diff --git a/.gitattributes b/.gitattributes index e69de29bb..dd3950f03 100644 --- a/.gitattributes +++ b/.gitattributes @@ -0,0 +1 @@ +platforms/android/test-app/runtime/src/main/libs/**/libv8_monolith.a filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 627aeb287..b0a5aa548 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,6 @@ jobs: IOS_BUILD_TIMEOUT_MS: "600000" IOS_TEST_TIMEOUT_MS: "600000" IOS_TEST_INACTIVITY_TIMEOUT_MS: "180000" - IOS_TEST_VERBOSE_SPECS: "1" + IOS_LOG_JUNIT: "1" IOS_SIMCTL_QUERY_TIMEOUT_MS: "10000" run: npm run test:ios diff --git a/.gitignore b/.gitignore index edc6abe58..95ef13f4b 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,8 @@ package-lock.json v8_build .npmrc /Frameworks/ +/.kiro/ +/opencode.json /llvm/ @@ -52,10 +54,10 @@ v8_build .cipd/ # project template -/templates/ios/.build_env_vars.sh -/templates/ios/__PROJECT_NAME__.xcodeproj/project.xcworkspace/xcshareddata/ -/templates/visionos/.build_env_vars.sh -/templates/visionos/__PROJECT_NAME__.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +/platforms/apple/templates/ios/.build_env_vars.sh +/platforms/apple/templates/ios/__PROJECT_NAME__.xcodeproj/project.xcworkspace/xcshareddata/ +/platforms/apple/templates/visionos/.build_env_vars.sh +/platforms/apple/templates/visionos/__PROJECT_NAME__.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist .cache/ @@ -65,11 +67,16 @@ packages/*/types SwiftBindgen # Generated Objective-C/C dispatch wrappers -NativeScript/ffi/napi/GeneratedSignatureDispatch.inc -NativeScript/ffi/napi/GeneratedSignatureDispatch.inc.stamp +NativeScript/ffi/**/GeneratedSignatureDispatch.inc +NativeScript/ffi/**/GeneratedSignatureDispatch.inc.stamp +NativeScript/ffi/**/GeneratedGsdSignatureDispatch.inc +NativeScript/ffi/**/GeneratedGsdSignatureDispatch.inc.stamp + +# Packaged native framework artifacts +packages/*/NativeScript.xcframework/ # React Native TurboModule package staging packages/react-native/dist/ packages/react-native/ios/vendor/ packages/react-native/metadata/ -packages/react-native/native-api-jsi/ +packages/react-native/native-api/ diff --git a/NativeScript/CMakeLists.txt b/NativeScript/CMakeLists.txt index b9386f462..518bf735a 100644 --- a/NativeScript/CMakeLists.txt +++ b/NativeScript/CMakeLists.txt @@ -20,12 +20,12 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMMON_FLAGS}") # Arguments set(TARGET_PLATFORM "macos" CACHE STRING "Target platform for the Objective-C bridge") set(TARGET_ENGINE "v8" CACHE STRING "Target JS engine for the NativeScript runtime") -set(NS_FFI_BACKEND "auto" CACHE STRING "FFI backend: auto, napi, or direct") +set(NS_FFI_BACKEND "auto" CACHE STRING "FFI backend: auto, napi, v8, jsc, quickjs, or hermes") set(NS_GSD_BACKEND "auto" CACHE STRING "Generated signature dispatch backend: auto, v8, jsc, quickjs, hermes, napi, or none") set(METADATA_SIZE 0 CACHE STRING "Size of embedded metadata in bytes") set(BUILD_CLI_BINARY OFF CACHE BOOL "Build the NativeScript CLI binary") set(BUILD_MACOS_NODE_API OFF CACHE BOOL "Build the NativeScript macOS Node API dylib") -set_property(CACHE NS_FFI_BACKEND PROPERTY STRINGS auto napi direct) +set_property(CACHE NS_FFI_BACKEND PROPERTY STRINGS auto napi v8 jsc quickjs hermes) set_property(CACHE NS_GSD_BACKEND PROPERTY STRINGS auto v8 jsc quickjs hermes napi none) if (BUILD_MACOS_NODE_API) @@ -139,36 +139,46 @@ message(STATUS "GENERIC_NAPI = ${GENERIC_NAPI}") if(NS_FFI_BACKEND STREQUAL "auto") if(GENERIC_NAPI OR TARGET_ENGINE_NONE) set(NS_EFFECTIVE_FFI_BACKEND "napi") - elseif(TARGET_ENGINE_HERMES OR TARGET_ENGINE_V8 OR TARGET_ENGINE_JSC OR TARGET_ENGINE_QUICKJS) - set(NS_EFFECTIVE_FFI_BACKEND "direct") + elseif(TARGET_ENGINE_HERMES) + set(NS_EFFECTIVE_FFI_BACKEND "hermes") + elseif(TARGET_ENGINE_V8) + set(NS_EFFECTIVE_FFI_BACKEND "v8") + elseif(TARGET_ENGINE_JSC) + set(NS_EFFECTIVE_FFI_BACKEND "jsc") + elseif(TARGET_ENGINE_QUICKJS) + set(NS_EFFECTIVE_FFI_BACKEND "quickjs") else() set(NS_EFFECTIVE_FFI_BACKEND "napi") endif() -elseif(NS_FFI_BACKEND STREQUAL "napi" OR NS_FFI_BACKEND STREQUAL "direct") +elseif(NS_FFI_BACKEND STREQUAL "napi" OR + NS_FFI_BACKEND STREQUAL "v8" OR + NS_FFI_BACKEND STREQUAL "jsc" OR + NS_FFI_BACKEND STREQUAL "quickjs" OR + NS_FFI_BACKEND STREQUAL "hermes") set(NS_EFFECTIVE_FFI_BACKEND "${NS_FFI_BACKEND}") else() message(FATAL_ERROR "Unknown NS_FFI_BACKEND: ${NS_FFI_BACKEND}") endif() -if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "direct" AND +if(NOT NS_EFFECTIVE_FFI_BACKEND STREQUAL "napi" AND (GENERIC_NAPI OR TARGET_ENGINE_NONE OR BUILD_MACOS_NODE_API)) - message(FATAL_ERROR "NS_FFI_BACKEND=direct requires an embedded JS runtime build") + message(FATAL_ERROR + "NS_FFI_BACKEND=${NS_EFFECTIVE_FFI_BACKEND} requires an embedded JS runtime build") endif() -message(STATUS "NS_FFI_BACKEND = ${NS_FFI_BACKEND} (${NS_EFFECTIVE_FFI_BACKEND})") - -if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "direct" AND - NOT (NS_GSD_BACKEND STREQUAL "auto" OR NS_GSD_BACKEND STREQUAL "none")) +if(NOT NS_EFFECTIVE_FFI_BACKEND STREQUAL "napi" AND + NOT NS_EFFECTIVE_FFI_BACKEND STREQUAL "${TARGET_ENGINE}") message(FATAL_ERROR - "NS_GSD_BACKEND is only used by the Node-API FFI backend. " - "Use NS_GSD_BACKEND=auto or none with NS_FFI_BACKEND=direct.") + "NS_FFI_BACKEND=${NS_EFFECTIVE_FFI_BACKEND} requires TARGET_ENGINE=${NS_EFFECTIVE_FFI_BACKEND}") endif() +message(STATUS "NS_FFI_BACKEND = ${NS_FFI_BACKEND} (${NS_EFFECTIVE_FFI_BACKEND})") + if(NS_GSD_BACKEND STREQUAL "auto") - if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "direct") - set(NS_EFFECTIVE_GSD_BACKEND "none") - else() + if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "napi") set(NS_EFFECTIVE_GSD_BACKEND "napi") + else() + set(NS_EFFECTIVE_GSD_BACKEND "${NS_EFFECTIVE_FFI_BACKEND}") endif() elseif(NS_GSD_BACKEND STREQUAL "v8" OR NS_GSD_BACKEND STREQUAL "jsc" OR @@ -200,81 +210,86 @@ if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "napi" AND "NS_FFI_BACKEND=napi is the pure Node-API FFI backend and only supports " "NS_GSD_BACKEND=napi or none.") endif() +if(NOT NS_EFFECTIVE_FFI_BACKEND STREQUAL "napi" AND + NS_EFFECTIVE_GSD_BACKEND STREQUAL "napi") + message(FATAL_ERROR + "NS_FFI_BACKEND=${NS_EFFECTIVE_FFI_BACKEND} cannot use NS_GSD_BACKEND=napi. " + "Use the matching engine backend or none.") +endif() message(STATUS "NS_GSD_BACKEND = ${NS_GSD_BACKEND} (${NS_EFFECTIVE_GSD_BACKEND})") # Set up sources include_directories( ./ - ffi/shared ../metadata-generator/include napi/common libffi/${LIBFFI_BUILD}/include ) set(FFI_SHARED_SOURCE_FILES - ffi/shared/Tasks.cpp + ffi/objc/shared/Tasks.cpp ) set(FFI_NAPI_SOURCE_FILES - ffi/napi/AutoreleasePool.mm - ffi/napi/Protocol.mm - ffi/napi/ObjCBridge.mm - ffi/napi/Block.mm - ffi/napi/Class.mm - ffi/napi/Closure.mm - ffi/napi/ClassMember.mm - ffi/napi/Cif.mm - ffi/napi/TypeConv.mm - ffi/napi/Util.mm - ffi/napi/Struct.mm - ffi/napi/ObjectRef.mm - ffi/napi/JSObject.mm - ffi/napi/Enum.mm - ffi/napi/Variable.mm - ffi/napi/Object.mm - ffi/napi/CFunction.mm - ffi/napi/Interop.mm - ffi/napi/InlineFunctions.mm - ffi/napi/ClassBuilder.mm + ffi/objc/napi/AutoreleasePool.mm + ffi/objc/napi/Protocol.mm + ffi/objc/napi/ObjCBridge.mm + ffi/objc/napi/Block.mm + ffi/objc/napi/Class.mm + ffi/objc/napi/Closure.mm + ffi/objc/napi/ClassMember.mm + ffi/objc/napi/Cif.mm + ffi/objc/napi/TypeConv.mm + ffi/objc/napi/Util.mm + ffi/objc/napi/Struct.mm + ffi/objc/napi/ObjectRef.mm + ffi/objc/napi/JSObject.mm + ffi/objc/napi/Enum.mm + ffi/objc/napi/Variable.mm + ffi/objc/napi/Object.mm + ffi/objc/napi/CFunction.mm + ffi/objc/napi/Interop.mm + ffi/objc/napi/InlineFunctions.mm + ffi/objc/napi/ClassBuilder.mm ) -set(FFI_DIRECT_SHARED_SOURCE_FILES - ffi/shared/direct/EmbeddedMetadata.mm +set(FFI_ENGINE_SHARED_SOURCE_FILES + ffi/objc/shared/MetadataState.mm ) -set(FFI_HERMES_DIRECT_SOURCE_FILES - ${FFI_DIRECT_SHARED_SOURCE_FILES} - ffi/hermes/jsi/NativeApiJsi.mm +set(FFI_HERMES_ENGINE_SOURCE_FILES + ${FFI_ENGINE_SHARED_SOURCE_FILES} + ffi/objc/hermes/NativeApiJsi.mm ) -set(FFI_V8_DIRECT_SOURCE_FILES - ${FFI_DIRECT_SHARED_SOURCE_FILES} - ffi/v8/NativeApiV8.mm - ffi/v8/NativeApiV8HostObjects.mm - ffi/v8/NativeApiV8Runtime.mm - ffi/v8/NativeApiV8Value.mm +set(FFI_V8_ENGINE_SOURCE_FILES + ${FFI_ENGINE_SHARED_SOURCE_FILES} + ffi/objc/v8/NativeApiV8.mm + ffi/objc/v8/NativeApiV8HostObjects.mm + ffi/objc/v8/NativeApiV8Runtime.mm + ffi/objc/v8/NativeApiV8Value.mm ) -set(FFI_JSC_DIRECT_SOURCE_FILES - ${FFI_DIRECT_SHARED_SOURCE_FILES} - ffi/jsc/NativeApiJSC.mm - ffi/jsc/NativeApiJSCHostObjects.mm - ffi/jsc/NativeApiJSCRuntime.mm - ffi/jsc/NativeApiJSCValue.mm +set(FFI_JSC_ENGINE_SOURCE_FILES + ${FFI_ENGINE_SHARED_SOURCE_FILES} + ffi/objc/jsc/NativeApiJSC.mm + ffi/objc/jsc/NativeApiJSCHostObjects.mm + ffi/objc/jsc/NativeApiJSCRuntime.mm + ffi/objc/jsc/NativeApiJSCValue.mm ) -set(FFI_QUICKJS_DIRECT_SOURCE_FILES - ${FFI_DIRECT_SHARED_SOURCE_FILES} - ffi/quickjs/NativeApiQuickJSHostObjects.mm - ffi/quickjs/NativeApiQuickJS.mm - ffi/quickjs/NativeApiQuickJSRuntime.mm - ffi/quickjs/NativeApiQuickJSValue.mm +set(FFI_QUICKJS_ENGINE_SOURCE_FILES + ${FFI_ENGINE_SHARED_SOURCE_FILES} + ffi/objc/quickjs/NativeApiQuickJSHostObjects.mm + ffi/objc/quickjs/NativeApiQuickJS.mm + ffi/objc/quickjs/NativeApiQuickJSRuntime.mm + ffi/objc/quickjs/NativeApiQuickJSValue.mm ) set(SOURCE_FILES ${FFI_SHARED_SOURCE_FILES} - runtime/NativeScriptException.mm + runtime/apple/NativeScriptException.mm ) if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "napi") @@ -287,28 +302,28 @@ endif() if(ENABLE_JS_RUNTIME) set(SOURCE_FILES ${SOURCE_FILES} - runtime/modules/console/Console.cpp - runtime/Runtime.cpp - runtime/modules/worker/Worker.mm - runtime/modules/worker/MessageJSON.cpp - runtime/modules/worker/MessageV8.cpp - runtime/modules/worker/ConcurrentQueue.cpp - 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/node/VM.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/apple/modules/console/Console.cpp + runtime/apple/Runtime.cpp + runtime/apple/modules/worker/Worker.mm + runtime/apple/modules/worker/MessageJSON.cpp + runtime/apple/modules/worker/MessageV8.cpp + runtime/apple/modules/worker/ConcurrentQueue.cpp + runtime/apple/modules/worker/WorkerImpl.mm + runtime/apple/modules/worker/WorkerImpl.mm + runtime/apple/modules/module/ModuleInternal.cpp + runtime/apple/modules/node/Node.cpp + runtime/apple/modules/node/FS.cpp + runtime/apple/modules/node/Path.cpp + runtime/apple/modules/node/Process.cpp + runtime/apple/modules/node/VM.cpp + runtime/apple/modules/performance/Performance.cpp + runtime/apple/ThreadSafeFunction.mm + runtime/apple/Bundle.mm + runtime/apple/modules/timers/Timers.mm + runtime/apple/modules/app/App.mm + runtime/apple/modules/web/Web.mm + runtime/apple/NativeScript.mm + runtime/apple/RuntimeConfig.cpp runtime/modules/url/ada/ada.cpp runtime/modules/url/URL.cpp runtime/modules/url/URLSearchParams.cpp @@ -328,10 +343,10 @@ if(ENABLE_JS_RUNTIME) napi/v8/SimpleAllocator.cpp ) - if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "direct") + if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "v8") set(SOURCE_FILES ${SOURCE_FILES} - ${FFI_V8_DIRECT_SOURCE_FILES} + ${FFI_V8_ENGINE_SOURCE_FILES} ) endif() @@ -359,10 +374,10 @@ if(ENABLE_JS_RUNTIME) napi/hermes/jsr.cpp ) - if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "direct") + if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "hermes") set(SOURCE_FILES ${SOURCE_FILES} - ${FFI_HERMES_DIRECT_SOURCE_FILES} + ${FFI_HERMES_ENGINE_SOURCE_FILES} ) endif() @@ -398,10 +413,10 @@ if(ENABLE_JS_RUNTIME) napi/quickjs/jsr.cpp ) - if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "direct") + if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "quickjs") set(SOURCE_FILES ${SOURCE_FILES} - ${FFI_QUICKJS_DIRECT_SOURCE_FILES} + ${FFI_QUICKJS_ENGINE_SOURCE_FILES} ) endif() @@ -417,10 +432,10 @@ if(ENABLE_JS_RUNTIME) napi/jsc/jsr.cpp ) - if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "direct") + if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "jsc") set(SOURCE_FILES ${SOURCE_FILES} - ${FFI_JSC_DIRECT_SOURCE_FILES} + ${FFI_JSC_ENGINE_SOURCE_FILES} ) endif() @@ -511,40 +526,48 @@ elseif(TARGET_ENGINE_JSC) target_compile_definitions(${NAME} PRIVATE TARGET_ENGINE_JSC) endif() -set(NS_GSD_BACKEND_V8_VALUE 0) -set(NS_GSD_BACKEND_JSC_VALUE 0) -set(NS_GSD_BACKEND_QUICKJS_VALUE 0) set(NS_GSD_BACKEND_HERMES_VALUE 0) set(NS_GSD_BACKEND_NAPI_VALUE 0) -set(NS_FFI_BACKEND_DIRECT_VALUE 0) +set(NS_GSD_BACKEND_PREPARED_VALUE 0) set(NS_FFI_BACKEND_NAPI_VALUE 0) +set(NS_FFI_BACKEND_V8_VALUE 0) +set(NS_FFI_BACKEND_JSC_VALUE 0) +set(NS_FFI_BACKEND_QUICKJS_VALUE 0) +set(NS_FFI_BACKEND_HERMES_VALUE 0) if(NS_EFFECTIVE_GSD_BACKEND STREQUAL "v8") - set(NS_GSD_BACKEND_V8_VALUE 1) + set(NS_GSD_BACKEND_PREPARED_VALUE 1) elseif(NS_EFFECTIVE_GSD_BACKEND STREQUAL "jsc") - set(NS_GSD_BACKEND_JSC_VALUE 1) + set(NS_GSD_BACKEND_PREPARED_VALUE 1) elseif(NS_EFFECTIVE_GSD_BACKEND STREQUAL "quickjs") - set(NS_GSD_BACKEND_QUICKJS_VALUE 1) + set(NS_GSD_BACKEND_PREPARED_VALUE 1) elseif(NS_EFFECTIVE_GSD_BACKEND STREQUAL "hermes") set(NS_GSD_BACKEND_HERMES_VALUE 1) elseif(NS_EFFECTIVE_GSD_BACKEND STREQUAL "napi") set(NS_GSD_BACKEND_NAPI_VALUE 1) endif() -if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "direct") - set(NS_FFI_BACKEND_DIRECT_VALUE 1) -elseif(NS_EFFECTIVE_FFI_BACKEND STREQUAL "napi") +if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "napi") set(NS_FFI_BACKEND_NAPI_VALUE 1) +elseif(NS_EFFECTIVE_FFI_BACKEND STREQUAL "v8") + set(NS_FFI_BACKEND_V8_VALUE 1) +elseif(NS_EFFECTIVE_FFI_BACKEND STREQUAL "jsc") + set(NS_FFI_BACKEND_JSC_VALUE 1) +elseif(NS_EFFECTIVE_FFI_BACKEND STREQUAL "quickjs") + set(NS_FFI_BACKEND_QUICKJS_VALUE 1) +elseif(NS_EFFECTIVE_FFI_BACKEND STREQUAL "hermes") + set(NS_FFI_BACKEND_HERMES_VALUE 1) endif() target_compile_definitions(${NAME} PRIVATE - NS_GSD_BACKEND_V8=${NS_GSD_BACKEND_V8_VALUE} - NS_GSD_BACKEND_JSC=${NS_GSD_BACKEND_JSC_VALUE} - NS_GSD_BACKEND_QUICKJS=${NS_GSD_BACKEND_QUICKJS_VALUE} NS_GSD_BACKEND_HERMES=${NS_GSD_BACKEND_HERMES_VALUE} NS_GSD_BACKEND_NAPI=${NS_GSD_BACKEND_NAPI_VALUE} - NS_FFI_BACKEND_DIRECT=${NS_FFI_BACKEND_DIRECT_VALUE} + NS_GSD_BACKEND_PREPARED=${NS_GSD_BACKEND_PREPARED_VALUE} NS_FFI_BACKEND_NAPI=${NS_FFI_BACKEND_NAPI_VALUE} + NS_FFI_BACKEND_V8=${NS_FFI_BACKEND_V8_VALUE} + NS_FFI_BACKEND_JSC=${NS_FFI_BACKEND_JSC_VALUE} + NS_FFI_BACKEND_QUICKJS=${NS_FFI_BACKEND_QUICKJS_VALUE} + NS_FFI_BACKEND_HERMES=${NS_FFI_BACKEND_HERMES_VALUE} ) set(FRAMEWORK_VERSION_VALUE "${VERSION}") @@ -657,13 +680,13 @@ if(TARGET_ENGINE_V8) # 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 + set(V8_SLICE_DIR "${V8_XCFRAMEWORK}/ios-arm64-simulator/libv8_monolith.framework") 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 + set(V8_SLICE_DIR "${V8_XCFRAMEWORK}/ios-arm64/libv8_monolith.framework") endif() elseif(TARGET_PLATFORM STREQUAL "visionos-sim") set(V8_SLICE_DIR "${V8_XCFRAMEWORK}/xrsimulator-arm64/libv8_monolith.framework") diff --git a/NativeScript/cli/main.cpp b/NativeScript/cli/main.cpp index f9ea139c0..f72be8600 100644 --- a/NativeScript/cli/main.cpp +++ b/NativeScript/cli/main.cpp @@ -5,12 +5,12 @@ #include #include -#include "runtime/NativeScriptException.h" -#include "runtime/Bundle.h" -#include "runtime/Runtime.h" -#include "runtime/RuntimeConfig.h" +#include "runtime/apple/NativeScriptException.h" +#include "runtime/apple/Bundle.h" +#include "runtime/apple/Runtime.h" +#include "runtime/apple/RuntimeConfig.h" #include "segappend.h" -#include "ffi/shared/Tasks.h" +#include "ffi/objc/shared/Tasks.h" #include "BundleLoader.h" using namespace nativescript; diff --git a/NativeScript/ffi/hermes/jsi/NativeApiJsi.h b/NativeScript/ffi/hermes/jsi/NativeApiJsi.h deleted file mode 100644 index 82df76143..000000000 --- a/NativeScript/ffi/hermes/jsi/NativeApiJsi.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef NATIVE_API_JSI_H -#define NATIVE_API_JSI_H - -#include -#include -#include - -#include - -namespace nativescript { - -class NativeApiJsiScheduler { - public: - virtual ~NativeApiJsiScheduler() = default; - virtual void invokeOnJS(std::function task) = 0; - virtual void invokeOnUI(std::function task) = 0; -}; - -struct NativeApiJsiConfig { - const char* metadataPath = nullptr; - const void* metadataPtr = nullptr; - const char* globalName = "__nativeScriptNativeApi"; - std::shared_ptr scheduler = nullptr; - std::function)> nativeInvocationInvoker = nullptr; - std::function)> nativeCallbackInvoker = nullptr; - std::function)> jsThreadCallbackInvoker = nullptr; - bool invokeCallbacksOnNativeCallerThread = false; - bool installGlobalSymbols = false; -}; - -facebook::jsi::Object CreateNativeApiJSI( - facebook::jsi::Runtime& runtime, - const NativeApiJsiConfig& config = NativeApiJsiConfig{}); - -void InstallNativeApiJSI( - facebook::jsi::Runtime& runtime, - const NativeApiJsiConfig& config = NativeApiJsiConfig{}); - -} // namespace nativescript - -extern "C" void NativeScriptInstallNativeApiJSI( - facebook::jsi::Runtime* runtime, const char* metadataPath); - -#endif // NATIVE_API_JSI_H diff --git a/NativeScript/ffi/hermes/jsi/NativeApiJsi.mm b/NativeScript/ffi/hermes/jsi/NativeApiJsi.mm deleted file mode 100644 index 70a80bbfe..000000000 --- a/NativeScript/ffi/hermes/jsi/NativeApiJsi.mm +++ /dev/null @@ -1,89 +0,0 @@ -#include "NativeApiJsi.h" - -#ifdef TARGET_ENGINE_HERMES - -#import -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Metadata.h" -#include "MetadataReader.h" -#include "ffi.h" - -@protocol NativeApiJsiClassBuilderProtocol -@end - -#ifdef EMBED_METADATA_SIZE -extern const unsigned char embedded_metadata[EMBED_METADATA_SIZE]; -#endif - -namespace nativescript { -namespace { - -using facebook::jsi::Array; -using facebook::jsi::ArrayBuffer; -using facebook::jsi::BigInt; -using facebook::jsi::Function; -using facebook::jsi::HostObject; -using facebook::jsi::MutableBuffer; -using facebook::jsi::Object; -using facebook::jsi::PropNameID; -using facebook::jsi::Runtime; -using facebook::jsi::String; -using facebook::jsi::StringBuffer; -using facebook::jsi::Value; -using metagen::MDMemberFlag; -using metagen::MDMetadataReader; -using metagen::MDSectionOffset; -using metagen::MDTypeKind; - -// clang-format off -#include "jsi/NativeApiJsiBridge.h" -#include "jsi/NativeApiJsiHostObjects.h" -#include "jsi/NativeApiJsiCallbacks.h" -#include "jsi/NativeApiJsiConversion.h" -#include "jsi/NativeApiJsiInvocation.h" -#include "jsi/NativeApiJsiClassBuilder.h" -#include "jsi/NativeApiJsiHostObject.h" -// clang-format on - -} // namespace - -#include "jsi/NativeApiJsiInstall.h" - -} // namespace nativescript - -extern "C" void NativeScriptInstallNativeApiJSI(facebook::jsi::Runtime* runtime, - const char* metadataPath) { - if (runtime == nullptr) { - return; - } - nativescript::NativeApiJsiConfig config; - config.metadataPath = metadataPath; - nativescript::InstallNativeApiJSI(*runtime, config); -} - -#endif // TARGET_ENGINE_HERMES diff --git a/NativeScript/ffi/hermes/jsi/README.md b/NativeScript/ffi/hermes/jsi/README.md deleted file mode 100644 index ca07222b5..000000000 --- a/NativeScript/ffi/hermes/jsi/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Native API JSI bridge - -This directory contains the Hermes-first JSI entrypoint for NativeScript Native -API access. - -The backend is split by FFI responsibility: - -- `../../shared/jsi/NativeApiJsiBridge.h` owns metadata indexing, symbol lookup, scheduler - state, and bridge lifetime caches. -- `../../shared/jsi/NativeApiJsiHostObjects.h` owns class, object, protocol, pointer, - reference, struct, and union host objects. -- `../../shared/jsi/NativeApiJsiCallbacks.h` owns signatures, libffi callback trampolines, - JS blocks, and native function pointer callback lifetime. -- `../../shared/jsi/NativeApiJsiConversion.h` owns JSI/native type conversion and the - `interop` helper surface. -- `../../shared/jsi/NativeApiJsiInvocation.h` owns constants, enums, C function calls, - function pointer calls, and Objective-C selector dispatch. -- `../../shared/jsi/NativeApiJsiHostObject.h` owns the public API host object exposed to JS. -- `../../shared/jsi/NativeApiJsiInstall.h` owns runtime/global installation. - -The core installer is engine-host agnostic: - -```cpp -nativescript::NativeApiJsiConfig config; -config.metadataPath = metadataPath; -config.metadataPtr = metadataPtr; -nativescript::InstallNativeApiJSI(runtime, config); -``` - -NativeScript's Hermes runtime installs this automatically as -`globalThis.__nativeScriptNativeApi`. - -React Native integrations should include `NativeApiJsiReactNative.h` from a -TurboModule implementation and pass the module's JS/UI `CallInvoker`s: - -```cpp -nativescript::InstallReactNativeNativeApiJSI( - runtime, jsInvoker, uiInvoker, metadataPath, metadataPtr); -``` - -The React Native adapter is intentionally only a scheduler/config shim. The -native API host object, metadata loading, primitive C function dispatch, -Objective-C class/object handles, and selector invocation live in the shared -JSI implementation so they can be used by both NativeScript Hermes and a React -Native TurboModule without going through Node-API. - -The direct JSI backend is still moving toward full NativeScript bridge parity. -It covers the metadata-backed Objective-C class/function/constant/enum paths -needed by the React Native TurboModule, plus metadata-backed structs/unions, -primitive array/vector value marshalling, JS blocks, C function pointer -callbacks, protocol wrappers, pointer/reference helpers, and the core `interop` -helpers (`Pointer`, `Reference`, `sizeof`, `alloc`, `free`, `adopt`, -`handleof`, `stringFromCString`, `bufferFromData`, and `addProtocol`). Struct -and union constructors, plus protocol symbols, are installed on `globalThis` -along with `interop` so common NativeScript-style calls such as -`CGRect({ origin, size })`, `interop.sizeof(CGRect)`, and -`interop.handleof(value)` work through JSI. - -The remaining RN FFI-suite skip is the explicit `interop.addMethod` decorator -hook. JavaScript-defined Objective-C subclasses created through `.extend(...)` -use the JSI class-builder path and are covered by the React Native compatibility -suite. diff --git a/NativeScript/ffi/jni/napi/callbackhandlers/CallbackHandlers.cpp b/NativeScript/ffi/jni/napi/callbackhandlers/CallbackHandlers.cpp new file mode 100644 index 000000000..ac74a4916 --- /dev/null +++ b/NativeScript/ffi/jni/napi/callbackhandlers/CallbackHandlers.cpp @@ -0,0 +1,1925 @@ +// +// Created by Ammar Ahmed on 20/09/2024. +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "JEnv.h" +#include "CallbackHandlers.h" +#include "Util.h" +#include "JniLocalRef.h" +#include "MetadataNode.h" +#include "MethodCache.h" +#include "ArgConverter.h" +#include "JsArgConverter.h" +#include "GlobalHelpers.h" +#include + +#ifdef USE_MIMALLOC + +#include "mimalloc.h" + +#endif + +using namespace std; +using namespace tns; + +void CallbackHandlers::Init(napi_env env) { + JEnv jEnv; + + JAVA_LANG_STRING = jEnv.FindClass("java/lang/String"); + assert(JAVA_LANG_STRING != nullptr); + + RUNTIME_CLASS = jEnv.FindClass("com/tns/Runtime"); + assert(RUNTIME_CLASS != nullptr); + + RESOLVE_CLASS_METHOD_ID = jEnv.GetMethodID(RUNTIME_CLASS, "resolveClass", + "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Z)Ljava/lang/Class;"); + assert(RESOLVE_CLASS_METHOD_ID != nullptr); + + CURRENT_OBJECTID_FIELD_ID = jEnv.GetFieldID(RUNTIME_CLASS, "currentObjectId", "I"); + assert(CURRENT_OBJECTID_FIELD_ID != nullptr); + + MAKE_INSTANCE_STRONG_ID = jEnv.GetMethodID(RUNTIME_CLASS, "makeInstanceStrong", + "(Ljava/lang/Object;I)V"); + assert(MAKE_INSTANCE_STRONG_ID != nullptr); + + GET_TYPE_METADATA = jEnv.GetStaticMethodID(RUNTIME_CLASS, "getTypeMetadata", + "(Ljava/lang/String;I)[Ljava/lang/String;"); + assert(GET_TYPE_METADATA != nullptr); + + ENABLE_VERBOSE_LOGGING_METHOD_ID = jEnv.GetMethodID(RUNTIME_CLASS, "enableVerboseLogging", + "()V"); + assert(ENABLE_VERBOSE_LOGGING_METHOD_ID != nullptr); + + DISABLE_VERBOSE_LOGGING_METHOD_ID = jEnv.GetMethodID(RUNTIME_CLASS, "disableVerboseLogging", + "()V"); + assert(ENABLE_VERBOSE_LOGGING_METHOD_ID != nullptr); + + INIT_WORKER_METHOD_ID = jEnv.GetStaticMethodID(RUNTIME_CLASS, "initWorker", + "(Ljava/lang/String;Ljava/lang/String;I)V"); + + assert(INIT_WORKER_METHOD_ID != nullptr); + + SEND_MESSAGE_TO_WORKER_METHOD_ID = jEnv.GetStaticMethodID(RUNTIME_CLASS, + "sendMessageFromMainToWorker", + "(ILjava/lang/String;)V"); + assert(SEND_MESSAGE_TO_WORKER_METHOD_ID != nullptr); + + SEND_MESSAGE_TO_MAIN_METHOD_ID = jEnv.GetStaticMethodID(RUNTIME_CLASS, + "sendMessageFromWorkerToMain", + "(Ljava/lang/String;)V"); + assert(SEND_MESSAGE_TO_MAIN_METHOD_ID != nullptr); + + TERMINATE_WORKER_METHOD_ID = jEnv.GetStaticMethodID(RUNTIME_CLASS, "workerObjectTerminate", + "(I)V"); + assert(TERMINATE_WORKER_METHOD_ID != nullptr); + + WORKER_SCOPE_CLOSE_METHOD_ID = jEnv.GetStaticMethodID(RUNTIME_CLASS, "workerScopeClose", "()V"); + assert(WORKER_SCOPE_CLOSE_METHOD_ID != nullptr); + + MetadataNode::Init(env); + + MethodCache::Init(); +} + +napi_value CallbackHandlers::CallJavaMethod(napi_env env, napi_value caller, const string &className, + const string &methodName, MetadataEntry *entry, + bool isFromInterface, bool isStatic, napi_callback_info info, size_t argc, napi_value* argv) { + + JEnv jEnv; + jclass clazz; + jmethodID mid; + string *sig = nullptr; + string *returnType = nullptr; + auto retType = MethodReturnType::Unknown; + MethodCache::CacheMethodInfo mi; + bool isSuper = false; + + if ((entry != nullptr) && entry->getIsResolved()) { + auto &entrySignature = entry->getSig(); + isStatic = entry->isStatic; + + if (entry->memberId == nullptr) { + clazz = jEnv.FindClass(className); + + if (clazz == nullptr) { + MetadataNode *callerNode = MetadataNode::GetNodeFromHandle(env, caller); + const string callerClassName = callerNode->GetName(); + + DEBUG_WRITE("Cannot resolve class: %s while calling method: %s callerClassName: %s", + className.c_str(), methodName.c_str(), callerClassName.c_str()); + clazz = jEnv.FindClass(callerClassName); + if (clazz == nullptr) { + //todo: plamen5kov: throw exception here + DEBUG_WRITE("Cannot resolve caller's class name: %s", callerClassName.c_str()); + return nullptr; + } + + if (isStatic) { + if (isFromInterface) { + auto methodAndClassPair = jEnv.GetInterfaceStaticMethodIDAndJClass( + className, + methodName, + entrySignature); + entry->memberId = methodAndClassPair.first; + clazz = methodAndClassPair.second; + } else { + entry->memberId = jEnv.GetStaticMethodID(clazz, methodName, entrySignature); + } + } else { + entry->memberId = jEnv.GetMethodID(clazz, methodName, entrySignature); + } + + if (entry->memberId == nullptr) { + //todo: plamen5kov: throw exception here + DEBUG_WRITE("Cannot resolve a method %s on caller class: %s", + methodName.c_str(), callerClassName.c_str()); + return nullptr; + } + } else { + if (isStatic) { + if (isFromInterface) { + auto methodAndClassPair = jEnv.GetInterfaceStaticMethodIDAndJClass( + className, + methodName, entrySignature); + entry->memberId = methodAndClassPair.first; + clazz = methodAndClassPair.second; + } else { + entry->memberId = jEnv.GetStaticMethodID(clazz, methodName, entrySignature); + } + } else { + entry->memberId = jEnv.GetMethodID(clazz, methodName, entrySignature); + } + + if (entry->memberId == nullptr) { + //todo: plamen5kov: throw exception here + DEBUG_WRITE("Cannot resolve a method %s on class: %s", methodName.c_str(), + className.c_str()); + return nullptr; + } + } + entry->clazz = clazz; + } + + mid = reinterpret_cast(entry->memberId); + clazz = entry->clazz; + sig = &entry->getSig(); + returnType = &entry->getReturnType(); + retType = entry->getRetType(); + } else { + DEBUG_WRITE("Resolving method: %s on className %s", methodName.c_str(), className.c_str()); + + clazz = jEnv.FindClass(className); + if (clazz != nullptr) { + mi = MethodCache::ResolveMethodSignature(env, className, methodName, argc, argv, isStatic); + if (mi.mid == nullptr) { + DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d", + className.c_str(), methodName.c_str(), isStatic, isSuper); + return nullptr; + } + } else { + MetadataNode *callerNode = MetadataNode::GetNodeFromHandle(env, caller); + const string callerClassName = callerNode->GetName(); + DEBUG_WRITE("Resolving method on caller class: %s.%s on className %s", + callerClassName.c_str(), methodName.c_str(), className.c_str()); + mi = MethodCache::ResolveMethodSignature(env, callerClassName, methodName, argc, argv, + isStatic); + if (mi.mid == nullptr) { + DEBUG_WRITE( + "Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d, callerClass=%s", + className.c_str(), methodName.c_str(), isStatic, isSuper, + callerClassName.c_str()); + return nullptr; + } + } + + clazz = mi.clazz; + mid = mi.mid; + sig = &mi.signature; + returnType = &mi.returnType; + retType = mi.retType; + } + + if (!isStatic) { + DEBUG_WRITE("CallJavaMethod on instance %s", methodName.c_str()); + } else { + DEBUG_WRITE("CallJavaMethod on class %s", methodName.c_str()); + } + + JsArgConverter argConverter = (entry != nullptr && entry->isExtensionFunction) + ? JsArgConverter(env, caller, argv, argc, *sig, entry) + : JsArgConverter(env, argv, argc, false, *sig, entry); + + + if (!argConverter.IsValid()) { + JsArgConverter::Error err = argConverter.GetError(); + throw NativeScriptException(err.msg); + } + + JniLocalRef callerJavaObject; + + jvalue *javaArgs = argConverter.ToArgs(); + + auto runtime = Runtime::GetRuntime(env); + auto objectManager = runtime->GetObjectManager(); + + if (!isStatic) { + int objectId = -1; + + callerJavaObject = objectManager->GetJavaObjectByJsObject(caller, &objectId); + isSuper = objectManager->GetIsSuper(objectId, caller); + + if (callerJavaObject.IsNull()) { + stringstream ss; + + napi_value new_target; + napi_get_new_target(env, info, &new_target); + if (!napi_util::is_null_or_undefined(env, new_target)) { + ss << "No java object found on which to call \"" << methodName + << "\" method. It is possible your Javascript object is not linked with the corresponding Java class. Try passing context(this) to the constructor function."; + } else { + ss << "Failed calling " << methodName << " on a " << className + << " instance. The JavaScript instance no longer has available Java instance counterpart."; + } + throw NativeScriptException(ss.str()); + } + } + + napi_value returnValue; + + switch (retType) { + case MethodReturnType::Void: { + if (isStatic) { + jEnv.CallStaticVoidMethodA(clazz, mid, javaArgs); + } else if (isSuper) { + jEnv.CallNonvirtualVoidMethodA(callerJavaObject, clazz, mid, javaArgs); + } else { + jEnv.CallVoidMethodA(callerJavaObject, mid, javaArgs); + } + returnValue = nullptr; + break; + } + case MethodReturnType::Boolean: { + jboolean result; + if (isStatic) { + result = jEnv.CallStaticBooleanMethodA(clazz, mid, javaArgs); + } else if (isSuper) { + result = jEnv.CallNonvirtualBooleanMethodA(callerJavaObject, clazz, mid, javaArgs); + } else { + result = jEnv.CallBooleanMethodA(callerJavaObject, mid, javaArgs); + } + + napi_get_boolean(env, result != 0, &returnValue); + break; + } + case MethodReturnType::Byte: { + jbyte result; + if (isStatic) { + result = jEnv.CallStaticByteMethodA(clazz, mid, javaArgs); + } else if (isSuper) { + result = jEnv.CallNonvirtualByteMethodA(callerJavaObject, clazz, mid, javaArgs); + } else { + result = jEnv.CallByteMethodA(callerJavaObject, mid, javaArgs); + } + + napi_create_int32(env, result, &returnValue); + break; + } + case MethodReturnType::Char: { + jchar result; + if (isStatic) { + result = jEnv.CallStaticCharMethodA(clazz, mid, javaArgs); + } else if (isSuper) { + result = jEnv.CallNonvirtualCharMethodA(callerJavaObject, clazz, mid, javaArgs); + } else { + result = jEnv.CallCharMethodA(callerJavaObject, mid, javaArgs); + } + + JniLocalRef str(jEnv.NewString(&result, 1)); + jboolean bol = true; + const char *resP = jEnv.GetStringUTFChars(str, &bol); + returnValue = ArgConverter::convertToJsString(env, resP, 1); + jEnv.ReleaseStringUTFChars(str, resP); + break; + } + case MethodReturnType::Short: { + jshort result; + if (isStatic) { + result = jEnv.CallStaticShortMethodA(clazz, mid, javaArgs); + } else if (isSuper) { + result = jEnv.CallNonvirtualShortMethodA(callerJavaObject, clazz, mid, javaArgs); + } else { + result = jEnv.CallShortMethodA(callerJavaObject, mid, javaArgs); + } + + napi_create_int32(env, result, &returnValue); + + break; + } + case MethodReturnType::Int: { + jint result; + if (isStatic) { + result = jEnv.CallStaticIntMethodA(clazz, mid, javaArgs); + } else if (isSuper) { + result = jEnv.CallNonvirtualIntMethodA(callerJavaObject, clazz, mid, javaArgs); + } else { + result = jEnv.CallIntMethodA(callerJavaObject, mid, javaArgs); + } + napi_create_int32(env, result, &returnValue); + break; + + } + case MethodReturnType::Long: { + jlong result; + if (isStatic) { + result = jEnv.CallStaticLongMethodA(clazz, mid, javaArgs); + } else if (isSuper) { + result = jEnv.CallNonvirtualLongMethodA(callerJavaObject, clazz, mid, javaArgs); + } else { + result = jEnv.CallLongMethodA(callerJavaObject, mid, javaArgs); + } + returnValue = ArgConverter::ConvertFromJavaLong(env, result); + break; + } + case MethodReturnType::Float: { + jfloat result; + if (isStatic) { + result = jEnv.CallStaticFloatMethodA(clazz, mid, javaArgs); + } else if (isSuper) { + result = jEnv.CallNonvirtualFloatMethodA(callerJavaObject, clazz, mid, javaArgs); + } else { + result = jEnv.CallFloatMethodA(callerJavaObject, mid, javaArgs); + } + napi_create_double(env, (double) result, &returnValue); + break; + } + case MethodReturnType::Double: { + jdouble result; + if (isStatic) { + result = jEnv.CallStaticDoubleMethodA(clazz, mid, javaArgs); + } else if (isSuper) { + result = jEnv.CallNonvirtualDoubleMethodA(callerJavaObject, clazz, mid, javaArgs); + } else { + result = jEnv.CallDoubleMethodA(callerJavaObject, mid, javaArgs); + } + napi_create_double(env, (double) result, &returnValue); + break; + } + case MethodReturnType::String: { + jobject result = nullptr; + bool exceptionOccurred; + + if (isStatic) { + result = jEnv.CallStaticObjectMethodA(clazz, mid, javaArgs); + } else if (isSuper) { + result = jEnv.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); + } else { + result = jEnv.CallObjectMethodA(callerJavaObject, mid, javaArgs); + } + + if (result != nullptr) { + returnValue = ArgConverter::jstringToJsString(env, static_cast(result)); + jEnv.DeleteLocalRef(result); + } else { + napi_get_null(env, &returnValue); + } + + break; + } + case MethodReturnType::Object: { + jobject result = nullptr; + bool exceptionOccurred; + + if (isStatic) { + result = jEnv.CallStaticObjectMethodA(clazz, mid, javaArgs); + } else if (isSuper) { + result = jEnv.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); + } else { + result = jEnv.CallObjectMethodA(callerJavaObject, mid, javaArgs); + } + + if (result != nullptr) { + auto isString = jEnv.IsInstanceOf(result, JAVA_LANG_STRING); + + if (isString) { + returnValue = ArgConverter::jstringToJsString(env, (jstring) result); + } else { + jint javaObjectID = objectManager->GetOrCreateObjectId(result); + returnValue = objectManager->GetJsObjectByJavaObject(javaObjectID); + + if (napi_util::is_null_or_undefined(env, returnValue)) { + returnValue = objectManager->CreateJSWrapper(javaObjectID, *returnType, + result); + } + } + + jEnv.DeleteLocalRef(result); + } else { + napi_get_null(env, &returnValue); + } + + break; + } + default: { + returnValue = napi_util::undefined(env); + assert(false); + break; + } + } + + + return returnValue; +} + + +bool CallbackHandlers::RegisterInstance(napi_env env, napi_value jsObject, + const std::string &fullClassName, + const ArgsWrapper &argWrapper, + napi_value implementationObject, + bool isInterface, + napi_value *jsThisProxy, + const std::string &baseClassName) { + bool success; + + DEBUG_WRITE("RegisterInstance called for '%s'", fullClassName.c_str()); + + auto runtime = Runtime::GetRuntime(env); + auto objectManager = runtime->GetObjectManager(); + + JEnv jEnv; + + jclass generatedJavaClass = ResolveClass(env, baseClassName, fullClassName, + implementationObject, + isInterface); + + int javaObjectID = objectManager->GenerateNewObjectID(); + + objectManager->Link(jsObject, javaObjectID, nullptr); + + // resolve constructor + auto mi = MethodCache::ResolveConstructorSignature(env, argWrapper, fullClassName, + generatedJavaClass, isInterface); + + // while the "instance" is being created, if an exception is thrown during the construction + // this scope will guarantee the "javaObjectID" will be set to -1 and won't have an invalid value + jobject instance; + { + JavaObjectIdScope objIdScope(jEnv, CURRENT_OBJECTID_FIELD_ID, runtime->GetJavaRuntime(), + javaObjectID); + + if (argWrapper.type == ArgType::Interface) { + instance = jEnv.NewObject(generatedJavaClass, mi.mid); + } else { + // resolve arguments before passing them on to the constructor + // JSToJavaConverter argConverter(isolate, argWrapper.args, mi.signature); + + + + JsArgConverter argConverter(env, argWrapper.argv, argWrapper.argc, mi.signature); + auto ctorArgs = argConverter.ToArgs(); + + instance = jEnv.NewObjectA(generatedJavaClass, mi.mid, ctorArgs); + } + } + + // Set runtimeId field on interface and extended classes + if (runtime->GetId() != 0 && (isInterface || implementationObject != nullptr)) { + jfieldID runtimeIdField; + auto itFound = jclass_to_runtimeId_cache.find(generatedJavaClass); + if (itFound != jclass_to_runtimeId_cache.end()) { + runtimeIdField = itFound->second; + } else { + runtimeIdField = jEnv.GetFieldID(generatedJavaClass, "runtimeId", "I"); + jclass_to_runtimeId_cache.emplace(generatedJavaClass, runtimeIdField); + } + if (runtimeIdField != nullptr) { + jint runtimeId = runtime->GetId(); // Assuming GetId() returns the current runtime's id + DEBUG_WRITE("Setting runtimeId %d on instance of %s", runtimeId, fullClassName.c_str()); + jEnv.SetIntField(instance, runtimeIdField, runtimeId); + } + } + + jEnv.CallVoidMethod(runtime->GetJavaRuntime(), MAKE_INSTANCE_STRONG_ID, instance, javaObjectID); + + AdjustAmountOfExternalAllocatedMemory(env); + + JniLocalRef localInstance(instance); + success = !localInstance.IsNull(); + + if (success) { + jclass instanceClass = jEnv.FindClass(fullClassName); + objectManager->SetJavaClass(jsObject, instanceClass); + *jsThisProxy = objectManager->GetOrCreateProxy(javaObjectID, jsObject); + } else { + DEBUG_WRITE_FORCE("RegisterInstance failed with null new instance class: %s", + fullClassName.c_str()); + } + + return success; +} + +jclass CallbackHandlers::ResolveClass(napi_env env, const string &baseClassName, + const string &fullClassName, + napi_value implementationObject, bool isInterface) { + JEnv jEnv; + jclass globalRefToGeneratedClass = jEnv.CheckForClassInCache(fullClassName); + + if (globalRefToGeneratedClass == nullptr) { + + // get needed arguments in order to load binding + JniLocalRef javaBaseClassName(jEnv.NewStringUTF(baseClassName.c_str())); + JniLocalRef javaFullClassName(jEnv.NewStringUTF(fullClassName.c_str())); + + jobjectArray methodOverrides = GetMethodOverrides(env, jEnv, implementationObject); + + jobjectArray implementedInterfaces = GetImplementedInterfaces(env, jEnv, + implementationObject); + + auto runtime = Runtime::GetRuntime(env); + + // create or load generated binding (java class) + jclass generatedClass = (jclass) jEnv.CallObjectMethod(runtime->GetJavaRuntime(), + RESOLVE_CLASS_METHOD_ID, + (jstring) javaBaseClassName, + (jstring) javaFullClassName, + methodOverrides, + implementedInterfaces, + isInterface); + + globalRefToGeneratedClass = jEnv.InsertClassIntoCache(fullClassName, generatedClass); + + jEnv.DeleteGlobalRef(methodOverrides); + jEnv.DeleteGlobalRef(implementedInterfaces); + } + + return globalRefToGeneratedClass; +} + +// Called by ExtendMethodCallback when extending a class +string CallbackHandlers::ResolveClassName(napi_env env, jclass &clazz) { + auto runtime = Runtime::GetRuntime(env); + auto objectManager = runtime->GetObjectManager(); + auto className = objectManager->GetClassName(clazz); + return className; +} + +napi_value CallbackHandlers::GetArrayElement(napi_env env, napi_value array, + uint32_t index, const string &arraySignature) { + return arrayElementAccessor.GetArrayElement(env, array, index, arraySignature); +} + +void CallbackHandlers::SetArrayElement(napi_env env, napi_value array, + uint32_t index, + const string &arraySignature, napi_value value) { + + arrayElementAccessor.SetArrayElement(env, array, index, arraySignature, value); +} + +napi_value CallbackHandlers::GetJavaField(napi_env env, napi_value caller, + FieldCallbackData *fieldData) { + return fieldAccessor.GetJavaField(env, caller, fieldData); +} + +void CallbackHandlers::SetJavaField(napi_env env, napi_value target, + napi_value value, FieldCallbackData *fieldData) { + fieldAccessor.SetJavaField(env, target, value, fieldData); +} + +void CallbackHandlers::AdjustAmountOfExternalAllocatedMemory(napi_env env) { + auto runtime = Runtime::GetRuntime(env); + runtime->AdjustAmountOfExternalAllocatedMemory(); + runtime->TryCallGC(); +} + +napi_value CallbackHandlers::CreateJSWrapper(napi_env env, jint javaObjectID, + const string &typeName) { + auto runtime = Runtime::GetRuntime(env); + auto objectManager = runtime->GetObjectManager(); + + return objectManager->CreateJSWrapper(javaObjectID, typeName); +} + +jobjectArray +CallbackHandlers::GetImplementedInterfaces(napi_env env, JEnv &jEnv, + napi_value implementationObject) { + if (implementationObject == nullptr || napi_util::is_undefined(env, implementationObject)) { + return CallbackHandlers::GetJavaStringArray(jEnv, 0); + } + + vector interfacesToImplement; + + napi_value prop; + napi_get_named_property(env, implementationObject, "interfaces", &prop); + bool isArray; + napi_is_array(env, prop, &isArray); + + if (isArray) { + uint32_t length; + napi_get_array_length(env, prop, &length); + + for (int j = 0; j < length; j++) { + napi_value element; + napi_get_element(env, prop, j, &element); + + if (napi_util::is_object(env, element)) { + auto node = MetadataNode::GetTypeMetadataName(env, element); + + node = Util::ReplaceAll(node, std::string("/"), std::string(".")); + + jstring value = jEnv.NewStringUTF(node.c_str()); + interfacesToImplement.push_back(value); + } + } + } + + int interfacesCount = interfacesToImplement.size(); + + jobjectArray implementedInterfaces = CallbackHandlers::GetJavaStringArray(jEnv, + interfacesCount); + for (int i = 0; i < interfacesCount; i++) { + jEnv.SetObjectArrayElement(implementedInterfaces, i, interfacesToImplement[i]); + } + + for (int i = 0; i < interfacesCount; i++) { + jEnv.DeleteLocalRef(interfacesToImplement[i]); + } + + return implementedInterfaces; +} + +jobjectArray +CallbackHandlers::GetMethodOverrides(napi_env env, JEnv &jEnv, napi_value implementationObject) { + if (implementationObject == nullptr || napi_util::is_undefined(env, implementationObject)) { + return CallbackHandlers::GetJavaStringArray(jEnv, 0); + } + + vector methodNames; + + napi_value propNames; + + napi_get_all_property_names(env, implementationObject, napi_key_own_only, + napi_key_all_properties, napi_key_numbers_to_strings, &propNames); + + uint32_t length; + napi_get_array_length(env, propNames, &length); + + for (int i = 0; i < length; i++) { + napi_value element; + napi_get_element(env, propNames, i, &element); + auto name = ArgConverter::ConvertToString(env, element); + + if (name == "super") { + continue; + } + + napi_value method; + + napi_get_property(env, implementationObject, element, &method); + + bool methodFound = napi_util::is_of_type(env, method, napi_function); + + if (methodFound) { + jstring value = jEnv.NewStringUTF(name.c_str()); + methodNames.push_back(value); + } + } + + int methodCount = methodNames.size(); + + jobjectArray methodOverrides = CallbackHandlers::GetJavaStringArray(jEnv, methodCount); + for (int i = 0; i < methodCount; i++) { + jEnv.SetObjectArrayElement(methodOverrides, i, methodNames[i]); + } + + for (int i = 0; i < methodCount; i++) { + jEnv.DeleteLocalRef(methodNames[i]); + } + + return methodOverrides; +} + +napi_value CallbackHandlers::RunOnMainThreadCallback(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + assert(argc == 1); + assert(napi_util::is_of_type(env, args[0], napi_function)); + + uint64_t key = ++count_; + bool inserted; + + std::tie(std::ignore, inserted) = cache_.try_emplace(key, env, args[0]); + assert(inserted && "Main thread callback ID should not be duplicated"); + + auto value = Callback(key); + auto size = sizeof(Callback); + auto wrote = write(Runtime::GetWriter(), &value, size); + + + + return nullptr; +} + +int CallbackHandlers::RunOnMainThreadFdCallback(int fd, int events, void *data) { + struct Callback value; + auto size = sizeof(Callback); + ssize_t nr = read(fd, &value, sizeof(value)); + + auto key = value.id_; + + auto it = cache_.find(key); + if (it == cache_.end()) { + return 1; + } + + napi_env env = it->second.env_; + napi_ref callback_ref = it->second.callback_; + + NapiScope scope(env); + + napi_value cb = napi_util::get_ref_value(env, callback_ref); + + napi_value global; + napi_get_global(env, &global); + + cache_.erase(it); + + napi_value result; + napi_status status = napi_call_function(env, global, cb, 0, nullptr, &result); + + if (status != napi_ok) { + napi_throw_error(env, nullptr, "Error calling JavaScript callback"); + } + + + return 1; +} + +napi_value CallbackHandlers::LogMethodCallback(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + try { + if (argc > 0) { + napi_valuetype valuetype; + napi_typeof(env, args[0], &valuetype); + if (valuetype == napi_string) { + size_t str_size; + napi_get_value_string_utf8(env, args[0], nullptr, 0, &str_size); + std::string message(str_size + 1, '\0'); + napi_get_value_string_utf8(env, args[0], &message[0], str_size + 1, &str_size); + DEBUG_WRITE("%s", message.c_str()); + } + } + } + catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } + catch (std::exception &e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what() << std::endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } + catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value CallbackHandlers::DrainMicrotaskCallback(napi_env env, napi_callback_info info) { + js_execute_pending_jobs(env); + return nullptr; +} + +napi_value CallbackHandlers::TimeCallback(napi_env env, napi_callback_info info) { + auto nano = std::chrono::time_point_cast( + std::chrono::system_clock::now()); + double duration = nano.time_since_epoch().count(); + napi_value result; + napi_create_double(env, duration, &result); + return result; +} + +napi_value +CallbackHandlers::ReleaseNativeCounterpartCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN_VARGS(); + + if (argc != 1) { + napi_throw_error(env, "0", "Unexpected arguments count!"); + return napi_util::undefined(env); + } + + if (!napi_util::is_of_type(env, argv[0], napi_object)) { + napi_throw_error(env, "0", "Argument is not an object!"); + return napi_util::undefined(env); + } + + + Runtime::GetRuntime(env)->GetObjectManager()->ReleaseNativeObject(env, argv[0]); + return napi_util::undefined(env); +} + +void CallbackHandlers::validateProvidedArgumentsLength(napi_env env, napi_callback_info info, + int expectedSize) { + size_t argc = 0; + napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr); + if ((int) argc != expectedSize) { + throw NativeScriptException("Unexpected arguments count!"); + } +} + +napi_value +CallbackHandlers::DumpReferenceTablesMethodCallback(napi_env env, napi_callback_info info) { + DumpReferenceTablesMethod(); + return nullptr; +} + +void CallbackHandlers::DumpReferenceTablesMethod() { + try { + JEnv jEnv; + jclass vmDbgClass = jEnv.FindClass("dalvik/system/VMDebug"); + if (vmDbgClass != nullptr) { + jmethodID mid = jEnv.GetStaticMethodID(vmDbgClass, "dumpReferenceTables", "()V"); + if (mid != 0) { + jEnv.CallStaticVoidMethod(vmDbgClass, mid); + } + } + } + catch (NativeScriptException &e) { + // e.ReThrowToNapi(env); + } + catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + // nsEx.ReThrowToV8(); + } + catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + // nsEx.ReThrowToV8(); + } +} + +napi_value +CallbackHandlers::EnableVerboseLoggingMethodCallback(napi_env env, napi_callback_info info) { + try { + tns::LogEnabled = true; + JEnv jEnv; + jEnv.CallVoidMethod(Runtime::GetRuntime(env)->GetJavaRuntime(), + ENABLE_VERBOSE_LOGGING_METHOD_ID); + } + catch (NativeScriptException &e) { + // e.ReThrowToNapi(env); + } + catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + // nsEx.ReThrowToV8(); + } + catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + // nsEx.ReThrowToV8(); + } + return nullptr; +} + +napi_value +CallbackHandlers::DisableVerboseLoggingMethodCallback(napi_env env, napi_callback_info info) { + try { + tns::LogEnabled = false; + JEnv jEnv; + jEnv.CallVoidMethod(Runtime::GetRuntime(env)->GetJavaRuntime(), + DISABLE_VERBOSE_LOGGING_METHOD_ID); + } + catch (NativeScriptException &e) { + // e.ReThrowToNapi(env); + } + catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + // nsEx.ReThrowToV8(); + } + catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + // nsEx.ReThrowToV8(); + } + return nullptr; +} + +napi_value CallbackHandlers::ExitMethodCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1); + auto msg = ArgConverter::ConvertToString(env, argv[0]); + DEBUG_WRITE_FATAL("FORCE EXIT: %s", msg.c_str()); + exit(-1); + return nullptr; +} + +void CallbackHandlers::CreateGlobalCastFunctions(napi_env env) { + napi_value global; + napi_get_global(env, &global); + castFunctions.CreateGlobalCastFunctions(env, global); +} + +vector CallbackHandlers::GetTypeMetadata(const string &name, int index) { + JEnv env; + + string canonicalName = Util::ConvertFromJniToCanonicalName(name); + + JniLocalRef className(env.NewStringUTF(canonicalName.c_str())); + jint idx = index; + + JniLocalRef pubApi( + env.CallStaticObjectMethod(RUNTIME_CLASS, GET_TYPE_METADATA, (jstring) className, idx)); + + jsize length = env.GetArrayLength(pubApi); + + assert(length > 0); + + vector result; + + for (jsize i = 0; i < length; i++) { + JniLocalRef s(env.GetObjectArrayElement(pubApi, i)); + const char *pc = env.GetStringUTFChars(s, nullptr); + result.push_back(string(pc)); + env.ReleaseStringUTFChars(s, pc); + } + + return result; +} + +napi_value CallbackHandlers::CallJSMethod(napi_env env, JNIEnv *_jEnv, + napi_value jsObject, jclass claz,const string &methodName,int javaObjectId, + jobjectArray args) { + JEnv jEnv(_jEnv); + napi_value result; + napi_value method; + +#ifndef __HERMES__ + auto runtime = Runtime::GetRuntime(env); + method = runtime->js_method_cache->getCachedMethod(javaObjectId, methodName); + if (!method) { +#endif + napi_get_named_property(env, jsObject, methodName.c_str(), &method); +#ifndef __HERMES__ + if (napi_util::is_of_type(env, method, napi_function)) { + runtime->js_method_cache->cacheMethod(javaObjectId, methodName, method); + } + } +#endif + + if (method == nullptr || !napi_util::is_of_type(env, method, napi_function)) { + stringstream ss; + ss << "Cannot find method '" << methodName << "' implementation"; + throw NativeScriptException(ss.str()); + } else { + DEBUG_WRITE("Calling JS Method %s", methodName.c_str()); + + bool exceptionPending; + napi_is_exception_pending(env, &exceptionPending); + + int argc = jEnv.GetArrayLength(args) / 3; + if (argc > 0) { + napi_value* jsArgs = nullptr; + napi_value stack_args[8]; + if (argc <= 8) { + jsArgs = stack_args; + } else { +#ifdef USE_MIMALLOC + jsArgs = (napi_value *) mi_malloc(sizeof(napi_value) * argc); +#else + jsArgs = (napi_value *) malloc(sizeof(napi_value) * argc); +#endif + } + ArgConverter::ConvertJavaArgsToJsArgs(env, args, argc, jsArgs); + napi_call_function(env, jsObject, method, argc, jsArgs, &result); + + if (argc > 8) { +#ifdef USE_MIMALLOC + mi_free(jsArgs); +#else + free(jsArgs); +#endif + } + } else { + napi_call_function(env, jsObject, method, 0, nullptr, &result); + } + + if (!exceptionPending) { + napi_is_exception_pending(env, &exceptionPending); + if (exceptionPending) { + napi_value error; + napi_get_and_clear_last_exception(env, &error); + throw NativeScriptException(env, error, "Error calling js method: " + methodName); + } + } + } + + + return result; +} + +napi_value CallbackHandlers::FindClass(napi_env env, const char *name) { + napi_value clazz = nullptr; + JEnv jEnv; + jclass javaClass = jEnv.FindClass(name); + if (jEnv.ExceptionCheck() == JNI_FALSE) { + auto runtime = Runtime::GetRuntime(env); + auto objectManager = runtime->GetObjectManager(); + + jint javaObjectID = objectManager->GetOrCreateObjectId(javaClass); + clazz = objectManager->GetJsObjectByJavaObject(javaObjectID); + + if (clazz == nullptr) { + clazz = objectManager->CreateJSWrapper(javaObjectID, "Ljava/lang/Class;", javaClass); + } + } + return clazz; +} + +int CallbackHandlers::GetArrayLength(napi_env env, napi_value arr) { + auto runtime = Runtime::GetRuntime(env); + auto objectManager = runtime->GetObjectManager(); + + JEnv jEnv; + + auto javaArr = objectManager->GetJavaObjectByJsObjectFast(arr); + + auto length = jEnv.GetArrayLength(javaArr); + + return length; +} + +jobjectArray CallbackHandlers::GetJavaStringArray(JEnv &jEnv, int length) { + if (length > CallbackHandlers::MAX_JAVA_STRING_ARRAY_LENGTH) { + stringstream ss; + ss << "You are trying to override more methods than the limit of " + << CallbackHandlers::MAX_JAVA_STRING_ARRAY_LENGTH; + throw NativeScriptException(ss.str()); + } + + JniLocalRef tmpArr(jEnv.NewObjectArray(length, JAVA_LANG_STRING, nullptr)); + return (jobjectArray) jEnv.NewGlobalRef(tmpArr); +} + +CallbackHandlers::func_AChoreographer_getInstance AChoreographer_getInstance_; + +CallbackHandlers::func_AChoreographer_postFrameCallback AChoreographer_postFrameCallback_; +CallbackHandlers::func_AChoreographer_postFrameCallbackDelayed AChoreographer_postFrameCallbackDelayed_; + +CallbackHandlers::func_AChoreographer_postFrameCallback64 AChoreographer_postFrameCallback64_; +CallbackHandlers::func_AChoreographer_postFrameCallbackDelayed64 AChoreographer_postFrameCallbackDelayed64_; + +void CallbackHandlers::PostCallback(napi_env env, napi_callback_info info, + CallbackHandlers::FrameCallbackCacheEntry *entry) { + size_t argc = 2; + napi_value args[2]; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + ALooper_prepare(0); + auto instance = AChoreographer_getInstance_(); + napi_value delay = args[1]; + napi_valuetype delayType; + napi_typeof(env, delay, &delayType); + + if (android_get_device_api_level() >= 29) { + if (delayType == napi_number) { + uint32_t delayValue; + napi_get_value_uint32(env, delay, &delayValue); + AChoreographer_postFrameCallbackDelayed64_(instance, entry->frameCallback64_, entry, + delayValue); + } else { + AChoreographer_postFrameCallback64_(instance, entry->frameCallback64_, entry); + } + } else { + if (delayType == napi_number) { + int64_t delayValue; + napi_get_value_int64(env, delay, &delayValue); + AChoreographer_postFrameCallbackDelayed_(instance, entry->frameCallback_, entry, + static_cast(delayValue)); + } else { + AChoreographer_postFrameCallback_(instance, entry->frameCallback_, entry); + } + } +} + +napi_value CallbackHandlers::PostFrameCallback(napi_env env, napi_callback_info info) { + if (android_get_device_api_level() >= 24) { + InitChoreographer(); + + size_t argc = 2; + napi_value args[2]; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + if (argc < 1) { + napi_throw_type_error(env, nullptr, "Frame callback argument is not a function"); + return nullptr; + } + + napi_valuetype argType; + napi_typeof(env, args[0], &argType); + if (argType != napi_function) { + napi_throw_type_error(env, nullptr, "Frame callback argument is not a function"); + return nullptr; + } + + napi_value func = args[0]; + + napi_value idKey; + napi_create_string_utf8(env, "_postFrameCallbackId", NAPI_AUTO_LENGTH, &idKey); + + napi_value pId; + napi_get_property(env, func, idKey, &pId); + + napi_valuetype pIdType; + napi_typeof(env, pId, &pIdType); + if (pIdType == napi_number) { + int32_t id; + napi_get_value_int32(env, pId, &id); + auto cb = frameCallbackCache_.find(id); + if (cb != frameCallbackCache_.end()) { + bool shouldReschedule = !cb->second.isScheduled(); + cb->second.markScheduled(); + if (shouldReschedule) { + PostCallback(env, info, &cb->second); + } + return nullptr; + } + } + + uint64_t key = ++frameCallbackCount_; + + napi_value keyValue; + napi_create_int64(env, key, &keyValue); + napi_set_property(env, func, idKey, keyValue); + + auto [val, inserted] = frameCallbackCache_.try_emplace(key, env, func, key); + assert(inserted && "Frame callback ID should not be duplicated"); + + val->second.markScheduled(); + PostCallback(env, info, &val->second); + + } + return nullptr; +} + +napi_value CallbackHandlers::RemoveFrameCallback(napi_env env, napi_callback_info info) { + if (android_get_device_api_level() >= 24) { + InitChoreographer(); + + size_t argc = 1; + napi_value args[1]; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + if (argc < 1) { + napi_throw_type_error(env, nullptr, "Frame callback argument is not a function"); + return nullptr; + } + + napi_valuetype argType; + napi_typeof(env, args[0], &argType); + if (argType != napi_function) { + napi_throw_type_error(env, nullptr, "Frame callback argument is not a function"); + return nullptr; + } + + napi_value func = args[0]; + + napi_value idKey; + napi_create_string_utf8(env, "_postFrameCallbackId", NAPI_AUTO_LENGTH, &idKey); + + napi_value pId; + napi_get_property(env, func, idKey, &pId); + + if (pId != nullptr && napi_util::is_of_type(env, pId, napi_number)) { + int32_t id; + napi_get_value_int32(env, pId, &id); + auto cb = frameCallbackCache_.find(id); + if (cb != frameCallbackCache_.end()) { + cb->second.markRemoved(); + } + } + } + return nullptr; +} + +void CallbackHandlers::InitChoreographer() { + if (AChoreographer_getInstance_ == nullptr) { + void *lib = dlopen("libandroid.so", RTLD_NOW | RTLD_LOCAL); + if (lib != nullptr) { + AChoreographer_getInstance_ = reinterpret_cast( + dlsym(lib, "AChoreographer_getInstance")); + AChoreographer_postFrameCallback_ = reinterpret_cast( + dlsym(lib, "AChoreographer_postFrameCallback")); + AChoreographer_postFrameCallbackDelayed_ = reinterpret_cast( + dlsym(lib, "AChoreographer_postFrameCallbackDelayed")); + + assert(AChoreographer_getInstance_); + assert(AChoreographer_postFrameCallback_); + assert(AChoreographer_postFrameCallbackDelayed_); + + if (android_get_device_api_level() >= 29) { + AChoreographer_postFrameCallback64_ = reinterpret_cast( + dlsym(lib, "AChoreographer_postFrameCallback64")); + AChoreographer_postFrameCallbackDelayed64_ = reinterpret_cast( + dlsym(lib, "AChoreographer_postFrameCallbackDelayed64")); + + assert(AChoreographer_postFrameCallback64_); + assert(AChoreographer_postFrameCallbackDelayed64_); + } + } + } +} + +void CallbackHandlers::RemoveEnvEntries(napi_env env) { + for (auto &item: cache_) { + if (item.second.env_ == env) { + cache_.erase(item.first); + } + } + + for (auto &item: frameCallbackCache_) { + if (item.second.env == env) { + frameCallbackCache_.erase(item.first); + } + } + +} + +// Worker + +napi_value CallbackHandlers::NewThreadCallback(napi_env env, napi_callback_info info) { + try { + NAPI_CALLBACK_BEGIN_VARGS() + + napi_value newTarget; + napi_get_new_target(env, info, &newTarget); + if (napi_util::is_null_or_undefined(env, newTarget)) { + throw NativeScriptException("Worker should be called as a constructor!"); + } + + if (argc != 1) { + throw NativeScriptException( + "Worker should be called with one parameter (name of file to run) or a URL/URL OBJECT to the file"); + } + + napi_valuetype value_type; + napi_typeof(env, argv[0], &value_type); + + if (value_type != napi_string && value_type != napi_object) { + throw NativeScriptException( + "Worker should be called with one parameter (name of file to run) or a URL/URL OBJECT to the file"); + } + + napi_value workerFilePath; + std::string baseurl_str; + if (value_type == napi_object) { + napi_get_named_property(env, argv[0], "href", &workerFilePath); + if (napi_util::is_null_or_undefined(env, workerFilePath)) { + throw NativeScriptException( + "Worker should be called with one parameter (name of file to run) or a URL to the file"); + } + } else { + workerFilePath = argv[0]; + } + + + + napi_value global; + napi_get_global(env, &global); + + auto frames = tns::BuildStacktraceFrames(env, nullptr, 1); + string currentExecutingScriptNameStr = + frames.size() < 3 ? frames[0].filename : frames[2].filename; + + auto lastForwardSlash = currentExecutingScriptNameStr.find_last_of("/"); + auto currentDir = currentExecutingScriptNameStr.substr(0, lastForwardSlash + 1); + std::string fileSchema("file://"); + if (currentDir.compare(0, fileSchema.length(), fileSchema) == 0) { + currentDir = currentDir.substr(fileSchema.length()); + } + + std::string workerPath = ArgConverter::ConvertToString(env, workerFilePath); + + if (workerPath.compare(0, fileSchema.length(), fileSchema) == 0) { + workerPath = workerPath.substr(fileSchema.length()); + auto workerPathPrefix = workerPath.substr(0, 1) == "/" ? "~" : "~/"; + workerPath = workerPathPrefix + workerPath; + } + + DEBUG_WRITE("Worker Path: %s, Current Dir: %s", workerPath.c_str(), currentDir.c_str()); + + + + // Will throw if path is invalid or doesn't exist + ModuleInternal::CheckFileExists(env, workerPath, currentDir); + + auto workerId = nextWorkerId++; + napi_value workerIdValue; + napi_create_int32(env, workerId, &workerIdValue); + napi_set_named_property(env, jsThis, "workerId", workerIdValue); + + id2WorkerMap.emplace(workerId, napi_util::make_ref(env, jsThis)); + + DEBUG_WRITE("Called Worker constructor id=%d", workerId); + + JEnv jEnv; + JniLocalRef filePath(jEnv.NewStringUTF(workerPath.c_str())); + JniLocalRef dirPath(jEnv.NewStringUTF(currentDir.c_str())); + jEnv.CallStaticVoidMethod(RUNTIME_CLASS, INIT_WORKER_METHOD_ID, (jstring) filePath, + (jstring) dirPath, workerId); + + napi_value stack; + napi_value error; + napi_value empty; + napi_create_string_utf8(env, "",0, &empty); + napi_create_error(env, empty, empty, &error); + napi_get_named_property(env, error, "stack", &stack); + napi_set_named_property(env, jsThis, "__stack__", stack); + + return jsThis; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + std::stringstream ss; + ss << "Error: c exception: " << e.what() << std::endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value +CallbackHandlers::WorkerObjectPostMessageCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN_VARGS(); + + try { + if (argc != 1) { + NativeScriptException exception( + "Failed to execute 'postMessage' on 'Worker': 1 argument required."); + throw exception; + } + + napi_value isTerminated; + napi_get_named_property(env, jsThis, "isTerminated", &isTerminated); + if (!napi_util::is_null_or_undefined(env, isTerminated)) { + bool terminated; + napi_get_value_bool(env, isTerminated, &terminated); + if (terminated) { + return nullptr; + } + } + + std::string msg = tns::JsonStringifyObject(env, argv[0], false); + + // get worker's ID that is associated on the other side - in Java + napi_value jsId; + napi_get_named_property(env, jsThis, "workerId", &jsId); + auto id = napi_util::get_int32(env, jsId); + + JEnv jEnv; + + jstring jmsg = jEnv.NewStringUTF(msg.c_str()); + JniLocalRef jmsgRef(jmsg); + + jEnv.CallStaticVoidMethod(RUNTIME_CLASS, SEND_MESSAGE_TO_WORKER_METHOD_ID, id, + (jstring) jmsgRef); + + DEBUG_WRITE( + "MAIN: WorkerObjectPostMessageCallback called postMessage on Worker object(id=%d)", + id); + } catch (NativeScriptException &ex) { + ex.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + return nullptr; +} + +void CallbackHandlers::WorkerGlobalOnMessageCallback(napi_env env, jstring message) { + NapiScope scope(env); + try { + napi_value globalObject; + napi_get_global(env, &globalObject); + + napi_value callback; + napi_get_named_property(env, globalObject, "onmessage", &callback); + + if (napi_util::is_of_type(env, callback, napi_function)) { + std::string msgString = ArgConverter::jstringToString(message); + napi_value dataObject = tns::JsonParseString(env, msgString.c_str()); + + napi_value obj; + napi_create_object(env, &obj); + if (napi_util::is_of_type(env, dataObject, napi_object)) { + napi_set_named_property(env, obj, "data", dataObject); + } + + napi_value args[1] = { + obj + }; + + napi_value result; + napi_status status = napi_call_function(env, globalObject, callback, 1, args, &result); + if (status == napi_pending_exception) { + napi_value error; + napi_get_and_clear_last_exception(env, &error); + CallWorkerScopeOnErrorHandle(env, error); + } + } else { + DEBUG_WRITE( + "WORKER: WorkerGlobalOnMessageCallback couldn't fire a worker's `onmessage` callback because it isn't implemented!"); + } + } catch (NativeScriptException &ex) { + ex.ReThrowToNapi(env); + } catch (std::exception e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what() << std::endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } +} + + +napi_value +CallbackHandlers::WorkerGlobalPostMessageCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1) + + try { + if (argc != 1) { + napi_throw_error(env, nullptr, + "Failed to execute 'postMessage' on WorkerGlobalScope: 1 argument required."); + return nullptr; + } + + bool pendingException; + napi_is_exception_pending(env, &pendingException); + if (pendingException) { + napi_value err; + napi_get_and_clear_last_exception(env, &err); + CallWorkerScopeOnErrorHandle(env, err); + } + + napi_value objToStringify = argv[0]; + std::string msg = tns::JsonStringifyObject(env, objToStringify, false); + + JEnv jenv; + auto jmsg = jenv.NewStringUTF(msg.c_str()); + JniLocalRef jmsgRef(jmsg); + + jenv.CallStaticVoidMethod(RUNTIME_CLASS, SEND_MESSAGE_TO_MAIN_METHOD_ID, (jstring) jmsgRef); + + DEBUG_WRITE("WORKER: WorkerGlobalPostMessageCallback called."); + } catch (NativeScriptException &ex) { + ex.ReThrowToNapi(env); + } catch (std::exception e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what() << std::endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +void CallbackHandlers::WorkerObjectOnMessageCallback(napi_env env, jint workerId, jstring message) { + NapiScope scope(env); + try { + + auto workerFound = CallbackHandlers::id2WorkerMap.find(workerId); + + if (workerFound == CallbackHandlers::id2WorkerMap.end()) { + DEBUG_WRITE( + "MAIN: WorkerObjectOnMessageCallback no worker instance was found with workerId=%d.", + workerId); + return; + } + + napi_ref workerPersistent = workerFound->second; + + napi_value worker; + napi_get_reference_value(env, workerPersistent, &worker); + + napi_value global; + napi_get_global(env, &global); + + napi_value callback; + napi_get_named_property(env, worker, "onmessage", &callback); + + if (napi_util::is_of_type(env, callback, napi_function)) { + std::string msgString = ArgConverter::jstringToString(message); + + napi_value dataObject = tns::JsonParseString(env, msgString.c_str()); + + napi_value obj; + napi_create_object(env, &obj); + napi_set_named_property(env, obj, "data", dataObject); + + napi_value args[1] = {obj}; + + napi_value result; + napi_status status = napi_call_function(env, worker, callback, 1, args, &result); + if (status != napi_ok) { + throw NativeScriptException("Error calling onmessage callback"); + } + } else { + DEBUG_WRITE( + "MAIN: WorkerObjectOnMessageCallback couldn't fire a worker(id=%d) object's `onmessage` callback because it isn't implemented.", + workerId); + } + } catch (NativeScriptException &ex) { + ex.ReThrowToNapi(env); + } catch (std::exception e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what() << std::endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } +} + +napi_value CallbackHandlers::WorkerObjectTerminateCallback(napi_env env, napi_callback_info info) { + size_t argc = 0; + napi_value thiz; + napi_get_cb_info(env, info, &argc, nullptr, &thiz, nullptr); + + DEBUG_WRITE("WORKER: WorkerObjectTerminateCallback called."); + + try { + napi_value global; + napi_get_global(env, &global); + + napi_value jsId; + napi_get_named_property(env, thiz, "workerId", &jsId); + + int32_t id; + napi_get_value_int32(env, jsId, &id); + + napi_value isTerminated; + napi_get_named_property(env, thiz, "isTerminated", &isTerminated); + if (!napi_util::is_null_or_undefined(env, isTerminated)) { + bool terminated; + napi_get_value_bool(env, isTerminated, &terminated); + if (terminated) { + return nullptr; + } + } + + napi_value trueValue; + napi_get_boolean(env, true, &trueValue); + napi_set_named_property(env, thiz, "isTerminated", trueValue); + + JEnv jenv; + jenv.CallStaticVoidMethod(RUNTIME_CLASS, TERMINATE_WORKER_METHOD_ID, id); + + CallbackHandlers::ClearWorkerPersistent(env, id); + } catch (NativeScriptException &ex) { + ex.ReThrowToNapi(env); + } catch (std::exception e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what() << std::endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value CallbackHandlers::WorkerGlobalCloseCallback(napi_env env, napi_callback_info info) { + size_t argc = 0; + napi_value thiz; + napi_get_cb_info(env, info, &argc, nullptr, &thiz, nullptr); + + DEBUG_WRITE("WORKER: WorkerThreadCloseCallback called."); + + try { + napi_value global; + napi_get_global(env, &global); + + napi_value isTerminated; + napi_get_named_property(env, global, "isTerminating", &isTerminated); + if (!napi_util::is_null_or_undefined(env, isTerminated)) { + bool terminated; + napi_get_value_bool(env, isTerminated, &terminated); + if (terminated) { + return nullptr; + } + } + + napi_value trueValue; + napi_get_boolean(env, true, &trueValue); + napi_set_named_property(env, global, "isTerminating", trueValue); + + napi_value callback; + napi_get_named_property(env, global, "onclose", &callback); + if (napi_util::is_of_type(env, callback, napi_function)) { + napi_value result; + napi_call_function(env, global, callback, 0, nullptr, &result); + } + + bool pendingException; + napi_is_exception_pending(env, &pendingException); + if (pendingException) { + napi_value err; + napi_get_and_clear_last_exception(env, &err); + CallWorkerScopeOnErrorHandle(env, err); + } + + JEnv jenv; + jenv.CallStaticVoidMethod(RUNTIME_CLASS, WORKER_SCOPE_CLOSE_METHOD_ID); + } catch (NativeScriptException &ex) { + ex.ReThrowToNapi(env); + } catch (std::exception e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what() << std::endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return napi_util::undefined(env); +} + +void CallbackHandlers::CallWorkerScopeOnErrorHandle(napi_env env, napi_value error) { + try { + napi_value global; + napi_get_global(env, &global); + + napi_value callback; + napi_get_named_property(env, global, "onerror", &callback); + + napi_value message = nullptr; + napi_value stack = nullptr; + std::vector frames; + if (napi_util::is_of_type(env, error, napi_object)) { + frames = tns::BuildStacktraceFrames(env, error, 1); + napi_get_named_property(env, error, "message", &message); + napi_get_named_property(env, error, "stack", &stack); + } else { + napi_coerce_to_string(env, error, &message); + napi_create_string_utf8(env, "", 0, &stack); + } + + if (napi_util::is_of_type(env, callback, napi_function)) { + napi_value args[1] = {error}; + napi_value result; + napi_call_function(env, global, callback, 1, args, &result); + + bool pendingException; + napi_is_exception_pending(env, &pendingException); + if (pendingException) { + napi_value perror = nullptr; + napi_value pmessage = nullptr; + napi_value pstack = nullptr; + napi_get_and_clear_last_exception(env, &perror); + + std::vector pframes; + if (napi_util::is_of_type(env, perror, napi_object)) { + pframes = tns::BuildStacktraceFrames(env, perror, 1); + napi_get_named_property(env, perror, "message", &pmessage); + napi_get_named_property(env, perror, "stack", &pstack); + } else { + napi_coerce_to_string(env, perror, &pmessage); + napi_create_string_utf8(env, "", 0, &pstack); + } + + auto line = 0; + std::string filename; + if (!pframes.empty()) { + line = pframes[0].line; + filename = pframes[0].filename; + } + Runtime::GetRuntime(env)->PassUncaughtExceptionFromWorkerToMainHandler( + pmessage, + pstack, + ArgConverter::convertToJsString(env, filename), + line + ); + } else if (!napi_util::is_null_or_undefined(env, result)) { + bool handled; + napi_get_value_bool(env, result, &handled); + if (handled) { + return; + } + } + } + + auto line = 0; + std::string filename; + if (!frames.empty()) { + line = frames[0].line; + filename = frames[0].filename; + } + Runtime::GetRuntime(env)->PassUncaughtExceptionFromWorkerToMainHandler( + message, + stack, + ArgConverter::convertToJsString(env, filename), + line + ); + + + } catch (NativeScriptException &ex) { + ex.ReThrowToNapi(env); + } catch (std::exception e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what() << std::endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } +} + +void CallbackHandlers::CallWorkerObjectOnErrorHandle(napi_env env, jint workerId, jstring message, + jstring stackTrace, jstring filename, + jint lineno, jstring threadName) { + NapiScope scope(env); + try { + auto workerFound = CallbackHandlers::id2WorkerMap.find(workerId); + + if (workerFound == CallbackHandlers::id2WorkerMap.end()) { + DEBUG_WRITE( + "MAIN: CallWorkerObjectOnErrorHandle no worker instance was found with workerId=%d.", + workerId); + return; + } + + napi_ref workerPersistent = workerFound->second; + + napi_value worker; + napi_get_reference_value(env, workerPersistent, &worker); + + napi_value callback; + napi_get_named_property(env, worker, "onerror", &callback); + + if (napi_util::is_of_type(env, callback, napi_function)) { + napi_value errEvent; + napi_value msgValue = ArgConverter::jstringToJsString(env, message); + napi_value codeValue; + napi_create_string_utf8(env, "",0, &codeValue); + napi_create_error(env,codeValue, msgValue, &errEvent); + napi_value stack_main_thread; + napi_get_named_property(env, worker, "__stack__", &stack_main_thread); + std::string main_stack = napi_util::get_string_value(env, stack_main_thread); + + std::string curr_stack = ArgConverter::jstringToString(stackTrace); + + std::string full_stack = curr_stack + "\n" + main_stack.substr(main_stack.find_first_of("\n") + 1) ; + + napi_value full_stack_value; + napi_create_string_utf8(env, full_stack.c_str(), full_stack.size(), &full_stack_value); + + napi_set_named_property(env, errEvent, "stack", full_stack_value); + + napi_value args[1] = {errEvent}; + + napi_value result; + napi_status status = napi_call_function(env, worker, callback, 1, args, &result); + if (status != napi_ok) { + napi_value exception; + napi_get_and_clear_last_exception(env, &exception); + if (!napi_util::is_null_or_undefined(env, exception)) { + throw NativeScriptException(env, exception, + "Error calling onerror on Worker Object"); + } else { + throw NativeScriptException("Error calling onerror on Worker Object"); + } + } + + bool handled; + napi_get_value_bool(env, result, &handled); + + if (handled) { + return; + } + } + + // Exception wasn't handled, or is critical -> Throw exception + std::string strMessage = ArgConverter::jstringToString(message); + std::string strFilename = ArgConverter::jstringToString(filename); + std::string strThreadname = ArgConverter::jstringToString(threadName); + std::string strStackTrace = ArgConverter::jstringToString(stackTrace); + + DEBUG_WRITE( + "Unhandled exception in '%s' thread. file: %s, line %d, message: %s\nStackTrace: %s", + strThreadname.c_str(), strFilename.c_str(), lineno, strMessage.c_str(), + strStackTrace.c_str()); + } catch (NativeScriptException &ex) { + ex.ReThrowToNapi(env); + } catch (std::exception e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what() << std::endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } +} + +void CallbackHandlers::ClearWorkerPersistent(napi_env env, int workerId) { + NapiScope scope(env); + DEBUG_WRITE("ClearWorkerPersistent called for workerId=%d", workerId); + + auto workerFound = CallbackHandlers::id2WorkerMap.find(workerId); + + if (workerFound == CallbackHandlers::id2WorkerMap.end()) { + DEBUG_WRITE( + "MAIN | WORKER: ClearWorkerPersistent no worker instance was found with workerId=%d ! The worker may already be terminated.", + workerId); + return; + } + + napi_ref workerPersistent = workerFound->second; + napi_delete_reference(env, workerPersistent); + + id2WorkerMap.erase(workerId); +} + +void CallbackHandlers::TerminateWorkerThread(napi_env env) { + JSEnterScope + try { + Runtime::GetRuntime(env)->DestroyRuntime(); + } catch (NativeScriptException &e) { + e.ReThrowToJava(nullptr); + } catch (std::exception e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what() << std::endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToJava(nullptr); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToJava(nullptr); + } + +} + +robin_hood::unordered_map CallbackHandlers::cache_; +robin_hood::unordered_map CallbackHandlers::jclass_to_runtimeId_cache; + +robin_hood::unordered_map CallbackHandlers::frameCallbackCache_; + +std::atomic_int64_t CallbackHandlers::count_ = {0}; +std::atomic_uint64_t CallbackHandlers::frameCallbackCount_ = {0}; + +int CallbackHandlers::nextWorkerId = 0; +int CallbackHandlers::lastCallId = -1; +napi_value CallbackHandlers::lastCallValue = nullptr; +robin_hood::unordered_map CallbackHandlers::id2WorkerMap; + +short CallbackHandlers::MAX_JAVA_STRING_ARRAY_LENGTH = 100; +jclass CallbackHandlers::RUNTIME_CLASS = nullptr; +jclass CallbackHandlers::JAVA_LANG_STRING = nullptr; +jfieldID CallbackHandlers::CURRENT_OBJECTID_FIELD_ID = nullptr; +jmethodID CallbackHandlers::RESOLVE_CLASS_METHOD_ID = nullptr; +jmethodID CallbackHandlers::MAKE_INSTANCE_STRONG_ID = nullptr; +jmethodID CallbackHandlers::GET_TYPE_METADATA = nullptr; +jmethodID CallbackHandlers::ENABLE_VERBOSE_LOGGING_METHOD_ID = nullptr; +jmethodID CallbackHandlers::DISABLE_VERBOSE_LOGGING_METHOD_ID = nullptr; +jmethodID CallbackHandlers::INIT_WORKER_METHOD_ID = nullptr; +jmethodID CallbackHandlers::SEND_MESSAGE_TO_MAIN_METHOD_ID = nullptr; +jmethodID CallbackHandlers::SEND_MESSAGE_TO_WORKER_METHOD_ID = nullptr; +jmethodID CallbackHandlers::TERMINATE_WORKER_METHOD_ID = nullptr; +jmethodID CallbackHandlers::WORKER_SCOPE_CLOSE_METHOD_ID = nullptr; + +NumericCasts CallbackHandlers::castFunctions; + +ArrayElementAccessor CallbackHandlers::arrayElementAccessor; +FieldAccessor CallbackHandlers::fieldAccessor; \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/callbackhandlers/CallbackHandlers.h b/NativeScript/ffi/jni/napi/callbackhandlers/CallbackHandlers.h new file mode 100644 index 000000000..4d7e4d1d5 --- /dev/null +++ b/NativeScript/ffi/jni/napi/callbackhandlers/CallbackHandlers.h @@ -0,0 +1,418 @@ +#ifndef CALLBACKHANDLERS_H_ +#define CALLBACKHANDLERS_H_ + +#include +#include +#include +#include "JEnv.h" +#include "ArgsWrapper.h" +#include "MetadataEntry.h" +#include "FieldCallbackData.h" +#include "MetadataTreeNode.h" +#include "NumericCasts.h" +#include "FieldAccessor.h" +#include "ArrayElementAccessor.h" +#include "ObjectManager.h" +#include "robin_hood.h" +#include +#include "NativeScriptAssert.h" +#include "NativeScriptException.h" +#include "Runtime.h" + +namespace tns { + class CallbackHandlers { + public: + /* + * Stores persistent handles of all 'Worker' objects initialized on the main thread + * Note: No isolates different than that of the main thread should access this map + */ + static robin_hood::unordered_map id2WorkerMap; + + static int nextWorkerId; + + static void Init(napi_env env); + + static napi_value + CreateJSWrapper(napi_env env, jint javaObjectID, const std::string &typeName); + + static bool RegisterInstance(napi_env env, napi_value jsObject, + const std::string &fullClassName, + const ArgsWrapper &argWrapper, + napi_value implementationObject, + bool isInterface, + napi_value *jsThisProxy, + const std::string &baseClassName = std::string()); + + static jclass ResolveClass(napi_env env, const std::string &baseClassName, + const std::string &fullClassName, + napi_value implementationObject, + bool isInterface); + + static std::string ResolveClassName(napi_env env, jclass &clazz); + + static napi_value + GetArrayElement(napi_env env, napi_value array, uint32_t index, + const std::string &arraySignature); + + static void + SetArrayElement(napi_env env, napi_value array, uint32_t index, + const std::string &arraySignature, napi_value value); + + static int GetArrayLength(napi_env env, napi_value arr); + + static napi_value + CallJavaMethod(napi_env env, napi_value caller, const std::string &className, + const std::string &methodName, MetadataEntry *entry, bool isFromInterface, + bool isStatic, napi_callback_info info, size_t argc, napi_value* argv); + + static napi_value + CallJSMethod(napi_env env, JNIEnv *jEnv, napi_value jsObject,jclass claz, + const std::string &methodName,int javaObjectId, jobjectArray args); + static napi_value + GetJavaField(napi_env env, napi_value caller, + FieldCallbackData *fieldData); + + static void SetJavaField(napi_env env, napi_value target, + napi_value value, FieldCallbackData *fieldData); + + static napi_value RunOnMainThreadCallback(napi_env env, napi_callback_info info); + + static int RunOnMainThreadFdCallback(int fd, int events, void *data); + + static napi_value LogMethodCallback(napi_env env, napi_callback_info info); + + static napi_value TimeCallback(napi_env env, napi_callback_info info); + + static napi_value + DumpReferenceTablesMethodCallback(napi_env env, napi_callback_info info); + + static napi_value DrainMicrotaskCallback(napi_env env, napi_callback_info info); + + static void DumpReferenceTablesMethod(); + + static napi_value ExitMethodCallback(napi_env env, napi_callback_info info); + + static void CreateGlobalCastFunctions(napi_env env); + + static std::vector GetTypeMetadata(const std::string &name, int index); + + /* + * Gets all methods in the implementation object, and packs them in a jobjectArray + * to pass them to Java Land, so that their corresponding Java callbacks are written when + * the dexFactory generates the class + */ + static jobjectArray + GetMethodOverrides(napi_env env, JEnv &jEnv, napi_value implementationObject); + + /* + * Gets all interfaces declared in the 'interfaces' array inside the implementation object, + * and packs them in a jobjectArray to pass them to Java Land, so that they may be + * implemented when the dexFactory generates the corresponding class + */ + static jobjectArray + GetImplementedInterfaces(napi_env env, JEnv &jEnv, napi_value implementationObject); + + static napi_value + EnableVerboseLoggingMethodCallback(napi_env env, napi_callback_info info); + + static napi_value + DisableVerboseLoggingMethodCallback(napi_env env, napi_callback_info info); + + static napi_value ReleaseNativeCounterpartCallback(napi_env env, napi_callback_info info); + + static napi_value FindClass(napi_env env, const char *name); + + static napi_value NewThreadCallback(napi_env env, napi_callback_info info); + + /* + * main -> worker messaging + * Fired when a Worker instance's postMessage is called + */ + static napi_value WorkerObjectPostMessageCallback(napi_env env, napi_callback_info info); + + /* + * main -> worker messaging + * Fired when worker object has "postMessage" and the worker has implemented "onMessage" handler + * In case "onMessage" handler isn't implemented no exception is thrown + */ + static void WorkerGlobalOnMessageCallback(napi_env env, jstring message); + + /* + * worker -> main thread messaging + * Fired when a Worker script's "postMessage" is called + */ + static napi_value WorkerGlobalPostMessageCallback(napi_env env, napi_callback_info info); + + /* + * worker -> main messaging + * Fired when worker has sent a message to main and the worker object has implemented "onMessage" handler + * In case "onMessage" handler isn't implemented no exception is thrown + */ + static void WorkerObjectOnMessageCallback(napi_env env, jint workerId, jstring message); + + /* + * Fired when a Worker instance's terminate is called (immediately stops execution of the thread) + */ + static napi_value WorkerObjectTerminateCallback(napi_env env, napi_callback_info info); + + /* + * Fired when a Worker script's close is called + */ + static napi_value WorkerGlobalCloseCallback(napi_env env, napi_callback_info info); + + /* + * Clears the persistent Worker object handle associated with a workerId + * Occurs when calling a worker object's `terminate` or a worker thread's global scope `close` + */ + static void ClearWorkerPersistent(napi_env env, int workerId); + + /* + * Terminates the currently executing Isolate. No scripts can be executed after this call + */ + static void TerminateWorkerThread(napi_env env); + + /* + * Is called when an unhandled exception is thrown inside the worker + * Will execute 'onerror' if one is provided inside the Worker Scope + * Will make the exception "bubble up" through to main, to be handled by the Worker Object + * if 'onerror' isn't implemented or returns false + */ + static void CallWorkerScopeOnErrorHandle(napi_env env, napi_value tc); + + /* + * Is called when an unhandled exception bubbles up from the worker scope to the main thread Worker Object + * Will execute `onerror` if one is implemented for the Worker Object instance + * Will throw a NativeScript Exception if 'onerror' isn't implemented or returns false + */ + static void CallWorkerObjectOnErrorHandle(napi_env env, jint workerId, jstring message, + jstring stackTrace, jstring filename, jint lineno, + jstring threadName); + + static napi_value PostFrameCallback(napi_env env, napi_callback_info info); + + static napi_value RemoveFrameCallback(napi_env env, napi_callback_info info); + + static void RemoveEnvEntries(napi_env env); + + struct AChoreographer; + + typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void *data); + + typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void *data); + + typedef AChoreographer *(*func_AChoreographer_getInstance)(); + + typedef void (*func_AChoreographer_postFrameCallback)( + AChoreographer *choreographer, AChoreographer_frameCallback callback, + void *data); + + typedef void (*func_AChoreographer_postFrameCallback64)( + AChoreographer *choreographer, AChoreographer_frameCallback64 callback, + void *data); + + typedef void (*func_AChoreographer_postFrameCallbackDelayed)( + AChoreographer *choreographer, AChoreographer_frameCallback callback, + void *data, long delayMillis); + + typedef void (*func_AChoreographer_postFrameCallbackDelayed64)( + AChoreographer *choreographer, AChoreographer_frameCallback64 callback, + void *data, uint32_t delayMillis); + + + static jint lastCallId; + static napi_value lastCallValue; + + private: + CallbackHandlers() { + } + + static void AdjustAmountOfExternalAllocatedMemory(napi_env napiEnv); + + /* + * Helper method that creates a java string array for sending strings over JNI + */ + static jobjectArray GetJavaStringArray(JEnv &jEnv, int length); + + static void + validateProvidedArgumentsLength(napi_env env, napi_callback_info info, int expectedSize); + + static short MAX_JAVA_STRING_ARRAY_LENGTH; + + static jclass RUNTIME_CLASS; + + static jclass JAVA_LANG_STRING; + + static jmethodID RESOLVE_CLASS_METHOD_ID; + + static jfieldID CURRENT_OBJECTID_FIELD_ID; + + static jmethodID MAKE_INSTANCE_STRONG_ID; + + static jmethodID GET_TYPE_METADATA; + + static jmethodID ENABLE_VERBOSE_LOGGING_METHOD_ID; + + static jmethodID DISABLE_VERBOSE_LOGGING_METHOD_ID; + + static jmethodID INIT_WORKER_METHOD_ID; + + static jmethodID SEND_MESSAGE_TO_WORKER_METHOD_ID; + static jmethodID SEND_MESSAGE_TO_MAIN_METHOD_ID; + static jmethodID TERMINATE_WORKER_METHOD_ID; + static jmethodID WORKER_SCOPE_CLOSE_METHOD_ID; + + static NumericCasts castFunctions; + + static ArrayElementAccessor arrayElementAccessor; + + static FieldAccessor fieldAccessor; + + struct JavaObjectIdScope { + JavaObjectIdScope(JEnv &_jEnv, jfieldID fieldId, jobject runtime, int javaObjectId) + : jEnv(_jEnv), _fieldID(fieldId), _runtime(runtime) { + jEnv.SetIntField(_runtime, _fieldID, javaObjectId); + } + + ~JavaObjectIdScope() { + jEnv.SetIntField(_runtime, _fieldID, -1); + } + + private: + JEnv jEnv; + jfieldID _fieldID; + jobject _runtime; + }; + + static std::atomic_int64_t count_; + + struct Callback { + Callback() {} + + Callback(uint64_t id) + : id_(id) { + } + + uint64_t id_; + }; + + struct CacheEntry { + CacheEntry(napi_env env, napi_value callback) + : env_(env) { + napi_create_reference(env, callback, 1, &callback_); + } + + ~CacheEntry() { + napi_delete_reference(env_, callback_); + } + + napi_env env_; + napi_ref callback_; + }; + + static robin_hood::unordered_map cache_; + + static robin_hood::unordered_map jclass_to_runtimeId_cache; + + static std::atomic_uint64_t frameCallbackCount_; + + struct FrameCallbackCacheEntry { + FrameCallbackCacheEntry(napi_env _env, napi_value callback_, uint64_t aId) + : env(_env), + id(aId) { + napi_create_reference(env, callback_, 1, &callback); + } + + ~FrameCallbackCacheEntry() { + napi_delete_reference(env, callback); + } + + napi_env env; + napi_ref callback; + uint64_t id; + + bool isScheduled() { + return scheduled; + } + + void markScheduled() { + scheduled = true; + removed = false; + } + + void markRemoved() { + // we can never unschedule a callback, so we just mark it as removed + removed = true; + uint32_t result; + } + + AChoreographer_frameCallback frameCallback_ = [](long ts, void *data) { + execute((double) ts, data); + }; + + AChoreographer_frameCallback64 frameCallback64_ = [](int64_t ts, void *data) { + execute((double) ts, data); + }; + + static void execute(double ts, void *data) { + if (data != nullptr) { + + auto entry = static_cast(data); + if (entry->shouldRemoveBeforeCall()) { + frameCallbackCache_.erase(entry->id); // invalidates *entry + return; + } + napi_env env = entry->env; + NapiScope scope(env); + napi_value cb = napi_util::get_ref_value(env, entry->callback); + + napi_value global; + napi_get_global(env, &global); + + entry->markUnscheduled(); + + napi_value args[1]; + napi_create_double(env, ts, &args[0]); + + napi_valuetype type; + napi_typeof(env, cb, &type); + + napi_value result; + napi_call_function(env, global, cb, 1, args, &result); + + + // check if we should remove it (it should be both unscheduled and removed) + if (entry->shouldRemoveAfterCall()) { + frameCallbackCache_.erase(entry->id); // invalidates *entry + } + } + } + + private: + bool removed = false; + bool scheduled = false; + + void markUnscheduled() { + scheduled = false; + removed = true; + } + + bool shouldRemoveBeforeCall() { + return removed; + } + + bool shouldRemoveAfterCall() { + return !scheduled && removed; + } + }; + + static robin_hood::unordered_map frameCallbackCache_; + + static void InitChoreographer(); + + static void PostCallback(napi_env env, napi_callback_info info, + FrameCallbackCacheEntry *entry); + + }; +} + +#endif /* CALLBACKHANDLERS_H_ */ \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/constants/Constants.cpp b/NativeScript/ffi/jni/napi/constants/Constants.cpp new file mode 100644 index 000000000..b957a7eea --- /dev/null +++ b/NativeScript/ffi/jni/napi/constants/Constants.cpp @@ -0,0 +1,12 @@ +/* + * Constants.cpp + * + * Created on: Nov 6, 2015 + * Author: gatanasov + */ + +#include "Constants.h" + +std::string Constants::APP_ROOT_FOLDER_PATH = ""; +bool Constants::CACHE_COMPILED_CODE = false; + diff --git a/NativeScript/ffi/jni/napi/constants/Constants.h b/NativeScript/ffi/jni/napi/constants/Constants.h new file mode 100644 index 000000000..7f41e29c5 --- /dev/null +++ b/NativeScript/ffi/jni/napi/constants/Constants.h @@ -0,0 +1,33 @@ +#ifndef CONSTANTS_H_ +#define CONSTANTS_H_ + +#include + +#define PROP_KEY_EXTEND "extend" +#define PROP_KEY_NULLOBJECT "null" +#define PROP_KEY_NULL_NODE_NAME "nullNode" +#define PROP_KEY_VALUEOF "valueOf" +#define PROP_KEY_CLASS "class" +#define PRIVATE_TYPE_NAME "#typename" +#define CLASS_IMPLEMENTATION_OBJECT "t::ClassImplementationObject" +#define PROP_KEY_SUPER "super" +#define PROP_KEY_SUPERVALUE "supervalue" +#define PRIVATE_JSINFO "#js_info" +#define PRIVATE_CALLSUPER "#supercall" +#define PRIVATE_IS_NAPI "#is_napi" +#define PROP_KEY_TOSTRING "toString" +#define PROP_KEY_IS_PROTOTYPE_IMPLEMENTATION_OBJECT "__isPrototypeImplementationObject" + +class Constants { + public: + const static char CLASS_NAME_LOCATION_SEPARATOR = '_'; + + static std::string APP_ROOT_FOLDER_PATH; + static bool CACHE_COMPILED_CODE; + + private: + Constants() { + } +}; + +#endif /* CONSTANTS_H_ */ diff --git a/NativeScript/ffi/jni/napi/conversion/ArgConverter.cpp b/NativeScript/ffi/jni/napi/conversion/ArgConverter.cpp new file mode 100644 index 000000000..f919a24a7 --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/ArgConverter.cpp @@ -0,0 +1,293 @@ +#include "ArgConverter.h" +#include "ObjectManager.h" +#include "Util.h" +#include "NativeScriptException.h" +#include "NumericCasts.h" +#include "NativeScriptAssert.h" +#include +#ifdef USE_MIMALLOC +#include "mimalloc.h" +#endif + + +using namespace std; +using namespace tns; + +namespace { +napi_value EnsurePlainConstructorThis(napi_env env, napi_value jsThis, napi_value prototype) { + if (!napi_util::is_null_or_undefined(env, jsThis)) { + return jsThis; + } + + napi_value receiver = nullptr; + if (napi_create_object(env, &receiver) != napi_ok || receiver == nullptr) { + return nullptr; + } + + if (!napi_util::is_null_or_undefined(env, prototype)) { + napi_util::setPrototypeOf(env, receiver, prototype); + } + + return receiver; +} +} + +void ArgConverter::Init(napi_env env) { + auto cache = GetTypeLongCache(env); + + napi_value longNumberCtorFunc; + napi_value valueOfFunc; + napi_value toStringFunc; + + napi_define_class(env, "NativeScriptLongNumber", NAPI_AUTO_LENGTH, ArgConverter::NativeScriptLongFunctionCallback,nullptr, 0, nullptr, &longNumberCtorFunc); + + napi_value longNumberPrototype = napi_util::get_prototype(env, longNumberCtorFunc); + + napi_create_function(env, "valueOf", strlen("valueOf"), + ArgConverter::NativeScriptLongValueOfFunctionCallback, nullptr, + &valueOfFunc); + + napi_create_function(env, "toString", strlen("toString"), + ArgConverter::NativeScriptLongToStringFunctionCallback, nullptr, + &toStringFunc); + + + napi_set_named_property(env, longNumberPrototype, "valueOf", valueOfFunc); + napi_set_named_property(env, longNumberPrototype, "toString", toStringFunc); + + cache->LongNumberCtorFunc = napi_util::make_ref(env, longNumberCtorFunc, 1); + napi_value nanValue; + napi_create_double(env, numeric_limits::quiet_NaN(), &nanValue); + + + napi_value global; + napi_get_global(env, &global); + + napi_value numCtor; + napi_get_named_property(env, global, "Number", &numCtor); + + napi_value nanObject; + napi_new_instance(env, numCtor, 1, &nanValue, &nanObject); + + cache->NanNumberObject = napi_util::make_ref(env, nanObject, 1); +} + +napi_value ArgConverter::NativeScriptLongValueOfFunctionCallback(napi_env env, napi_callback_info info) { + try { + napi_value result; + napi_create_double(env, numeric_limits::quiet_NaN(), &result); + return result; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + return nullptr; +} + +napi_value ArgConverter::NativeScriptLongToStringFunctionCallback(napi_env env, napi_callback_info info) { + try { + napi_value thisArg; + napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr); + + napi_value value; + napi_get_named_property(env, thisArg, "value", &value); + + return value; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + return nullptr; +} + +napi_value ArgConverter::NativeScriptLongFunctionCallback(napi_env env, napi_callback_info info) { + try { + NAPI_CALLBACK_BEGIN(1); + napi_value newTarget; + napi_get_new_target(env, info, &newTarget); + napi_value receiverPrototype = !napi_util::is_null_or_undefined(env, newTarget) + ? napi_util::get_prototype(env, newTarget) + : nullptr; + napi_value receiver = EnsurePlainConstructorThis(env, jsThis, receiverPrototype); + if (receiver == nullptr) { + return nullptr; + } + auto cache = GetTypeLongCache(env); + napi_value javaLong; + napi_get_boolean(env, true, &javaLong); + napi_set_named_property(env, receiver, "javaLong", javaLong); + + NumericCasts::MarkAsLong(env, receiver, argv[0]); + + napi_set_named_property(env, receiver, "prototype", napi_util::get_ref_value(env, cache->NanNumberObject)); + return receiver; + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + return nullptr; +} + +void ArgConverter::ConvertJavaArgsToJsArgs(napi_env env, jobjectArray args, size_t argc, napi_value* arr) { + JEnv jenv; + + auto runtime = Runtime::GetRuntime(env); + auto objectManager = runtime->GetObjectManager(); + + int jArrayIndex = 0; + for (int i = 0; i < argc; i++) { + JniLocalRef argTypeIDObj(jenv.GetObjectArrayElement(args, jArrayIndex++)); + JniLocalRef arg(jenv.GetObjectArrayElement(args, jArrayIndex++)); + JniLocalRef argJavaClassPath(jenv.GetObjectArrayElement(args, jArrayIndex++)); + + Type argTypeID = (Type) JType::IntValue(jenv, argTypeIDObj); + + napi_value jsArg; + switch (argTypeID) { + case Type::Boolean: + napi_get_boolean(env, JType::BooleanValue(jenv, arg), &jsArg); + break; + case Type::Char: + jsArg = jcharToJsString(env, JType::CharValue(jenv, arg)); + break; + case Type::Byte: + napi_create_int32(env, JType::ByteValue(jenv, arg), &jsArg); + break; + case Type::Short: + napi_create_int32(env, JType::ShortValue(jenv, arg), &jsArg); + break; + case Type::Int: + napi_create_int32(env, JType::IntValue(jenv, arg), &jsArg); + break; + case Type::Long: + napi_create_int64(env, JType::LongValue(jenv, arg), &jsArg); + break; + case Type::Float: + napi_create_double(env, JType::FloatValue(jenv, arg), &jsArg); + break; + case Type::Double: + napi_create_double(env, JType::DoubleValue(jenv, arg), &jsArg); + break; + case Type::String: + jsArg = jstringToJsString(env, (jstring) arg); + break; + case Type::JsObject: { + jint javaObjectID = JType::IntValue(jenv, arg); + jsArg = objectManager->GetJsObjectByJavaObject(javaObjectID); + + if (napi_util::is_null_or_undefined(env, jsArg)) { + string argClassName = jstringToString(ObjectToString(argJavaClassPath)); + argClassName = Util::ConvertFromCanonicalToJniName(argClassName); + jsArg = objectManager->CreateJSWrapper(javaObjectID, argClassName); + } + break; + } + case Type::Null: + napi_get_null(env, &jsArg); + break; + } + + arr[i] = jsArg; + } + +} + +napi_value ArgConverter::ConvertFromJavaLong(napi_env env, jlong value) { + napi_value convertedValue; + long long longValue = value; + + if ((-JS_LONG_LIMIT < longValue) && (longValue < JS_LONG_LIMIT)) { + napi_create_double(env, longValue, &convertedValue); + } else { + auto cache = GetTypeLongCache(env); + char strNumber[24]; + sprintf(strNumber, "%lld", longValue); + napi_value strValue; + napi_create_string_utf8(env, strNumber, NAPI_AUTO_LENGTH, &strValue); + napi_value args[1] = {strValue}; + + napi_new_instance(env, napi_util::get_ref_value(env, cache->LongNumberCtorFunc), 1, args, + &convertedValue); + } + + return convertedValue; +} + +int64_t ArgConverter::ConvertToJavaLong(napi_env env, napi_value value) { + napi_value valueProp; + napi_get_named_property(env, value, "value", &valueProp); + + size_t str_len; + napi_get_value_string_utf8(env, valueProp, nullptr, 0, &str_len); + string num(str_len, '\0'); + napi_get_value_string_utf8(env, valueProp, &num[0], str_len + 1, &str_len); + + int64_t longValue = atoll(num.c_str()); + + return longValue; +} + +ArgConverter::TypeLongOperationsCache *ArgConverter::GetTypeLongCache(napi_env env) { + TypeLongOperationsCache *cache; + auto itFound = s_type_long_operations_cache.find(env); + if (itFound == s_type_long_operations_cache.end()) { + cache = new TypeLongOperationsCache; + s_type_long_operations_cache.emplace(env, cache); + } else { + cache = itFound->second; + } + + return cache; +} + +u16string ArgConverter::ConvertToUtf16String(napi_env env, napi_value s) { + if (s == nullptr) { + return {}; + } else { + size_t str_len; + napi_get_value_string_utf8(env, s, nullptr, 0, &str_len); + string str(str_len, '\0'); + napi_get_value_string_utf8(env, s, &str[0], str_len + 1, &str_len); + auto utf16str = Util::ConvertFromUtf8ToUtf16(str); + + return utf16str; + } +} + +void ArgConverter::onDisposeEnv(napi_env env) { + auto itFound = s_type_long_operations_cache.find(env); + if (itFound != s_type_long_operations_cache.end()) { + if (itFound->second->LongNumberCtorFunc) { + napi_delete_reference(env, itFound->second->LongNumberCtorFunc); + } + if (itFound->second->NanNumberObject) { + napi_delete_reference(env, itFound->second->NanNumberObject); + } + delete itFound->second; + s_type_long_operations_cache.erase(itFound); + } +} + +robin_hood::unordered_map ArgConverter::s_type_long_operations_cache; diff --git a/NativeScript/ffi/jni/napi/conversion/ArgConverter.h b/NativeScript/ffi/jni/napi/conversion/ArgConverter.h new file mode 100644 index 000000000..a42a14cf2 --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/ArgConverter.h @@ -0,0 +1,137 @@ +/* + * ArgConverter.h + * + * Created on: Jan 29, 2014 + * Author: slavchev + */ + +#ifndef ARGCONVERTER_H_ +#define ARGCONVERTER_H_ + +#include "Runtime.h" +#include "NativeScriptAssert.h" +#include "JEnv.h" +#include +#include + +namespace tns { + + class ArgConverter { + public: + static void Init(napi_env env); + + static void ConvertJavaArgsToJsArgs(napi_env env, jobjectArray args, size_t length, napi_value* arr); + + static napi_value ConvertFromJavaLong(napi_env env, jlong value); + + static int64_t ConvertToJavaLong(napi_env env, napi_value value); + + static napi_value jstringToJsString(napi_env env, jstring value) { + if (value == nullptr) return napi_util::null(env); + + JEnv jenv; + auto chars = jenv.GetStringUTFChars(value,JNI_FALSE); + auto length = jenv.GetStringUTFLength(value); + auto jsString = convertToJsString(env, chars, length); + jenv.ReleaseStringUTFChars(value, chars); + + return jsString; + } + + static std::string jstringToString(jstring value) { + if (value == nullptr) { + return {}; + } + + JEnv jenv; + + jboolean f = JNI_FALSE; + auto chars = jenv.GetStringUTFChars(value, &f); + std::string s(chars); + jenv.ReleaseStringUTFChars(value, chars); + + return s; + } + + inline static std::string ConvertToString(napi_env env, napi_value s) { + if (s == nullptr) { + return {}; + } else { + return napi_util::get_string_value(env, s); + } + } + + static std::u16string ConvertToUtf16String(napi_env env, napi_value s); + + inline static jstring ConvertToJavaString(napi_env env, napi_value jsValue) { + JEnv jenv; + return jenv.NewStringUTF(napi_util::get_string_value(env, jsValue, 0)); + } + + inline static napi_value convertToJsString(napi_env env, const jchar *data, int length) { + napi_value result; + napi_create_string_utf16(env, reinterpret_cast(data), length, + &result); + return result; + } + + inline static napi_value convertToJsString(napi_env env, const std::string &s) { + napi_value result; + napi_create_string_utf8(env, s.c_str(), s.length(), &result); + return result; + } + + inline static napi_value convertToJsString(napi_env env, const char *data, int length) { + napi_value result; + napi_create_string_utf8(env, data, length, &result); + return result; + } + + inline static napi_value + ConvertToJsUTF16String(napi_env env, const std::u16string &utf16string) { + napi_value result; + napi_create_string_utf16(env, reinterpret_cast(utf16string.data()), + utf16string.length(), &result); + return result; + } + + static void onDisposeEnv(napi_env env); + + private: + + // TODO: plamen5kov: rewrite logic for java long number operations in javascript (java long -> javascript number operations check) + static const long long JS_LONG_LIMIT = ((long long) 1) << 53; + + struct TypeLongOperationsCache { + napi_ref LongNumberCtorFunc; + napi_ref NanNumberObject; + }; + + static TypeLongOperationsCache *GetTypeLongCache(napi_env env); + + inline static jstring ObjectToString(jobject object) { + return (jstring) object; + } + + inline static napi_value jcharToJsString(napi_env env, jchar value) { + auto v8String = convertToJsString(env, &value, 1); + return v8String; + } + + static napi_value NativeScriptLongFunctionCallback(napi_env env, napi_callback_info info); + + static napi_value + NativeScriptLongValueOfFunctionCallback(napi_env env, napi_callback_info info); + + static napi_value + NativeScriptLongToStringFunctionCallback(napi_env env, napi_callback_info info); + + /* + * "s_type_long_operations_cache" used to keep function + * dealing with operations concerning java long -> javascript number. + */ + static robin_hood::unordered_map s_type_long_operations_cache; + }; +} + +#endif /* ARGCONVERTER_H_ */ \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/conversion/ArgsWrapper.h b/NativeScript/ffi/jni/napi/conversion/ArgsWrapper.h new file mode 100644 index 000000000..70b96d8c8 --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/ArgsWrapper.h @@ -0,0 +1,30 @@ +/* + * ArgsWrapper.h + * + * Created on: Dec 20, 2013 + * Author: slavchev + */ + +#ifndef ARGSWRAPPER_H_ +#define ARGSWRAPPER_H_ +#include "js_native_api.h" + +namespace tns { +enum class ArgType { + Class, + Interface +}; + +struct ArgsWrapper { + public: + ArgsWrapper(napi_value* argv_, size_t argc_, ArgType t) + : + argv(argv_), argc(argc_), type(t) { + } + napi_value* argv; + size_t argc; + ArgType type; +}; +} + +#endif /* ARGSWRAPPER_H_ */ diff --git a/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.cpp b/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.cpp new file mode 100644 index 000000000..3d544248c --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.cpp @@ -0,0 +1,127 @@ +#include "ArrayBufferHelper.h" +#include "ArgConverter.h" +#include "NativeScriptException.h" +#include + +using namespace tns; + +ArrayBufferHelper::ArrayBufferHelper() + : m_objectManager(nullptr), m_ByteBufferClass(nullptr), m_isDirectMethodID(nullptr), + m_remainingMethodID(nullptr), m_getMethodID(nullptr) { +} + +void ArrayBufferHelper::CreateConvertFunctions(napi_env env, napi_value global, ObjectManager* objectManager) { + m_objectManager = objectManager; + napi_value fromFunc; + napi_create_function(env, "from", NAPI_AUTO_LENGTH, CreateFromCallbackStatic, this, &fromFunc); + + napi_value arrBufferCtorFunc; + napi_get_named_property(env, global, "ArrayBuffer", &arrBufferCtorFunc); + napi_set_named_property(env, arrBufferCtorFunc, "from", fromFunc); +} + +napi_value ArrayBufferHelper::CreateFromCallbackStatic(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1) + try { + auto thiz = reinterpret_cast(data); + return thiz->CreateFromCallbackImpl(env, argc, argv); + } catch (NativeScriptException& e) { + e.ReThrowToNapi(env); + } catch (std::exception& e) { + std::stringstream ss; + ss << "Error: c++ exception: " << e.what() << std::endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value ArrayBufferHelper::CreateFromCallbackImpl(napi_env env, size_t argc, napi_value* args) { + if (argc != 1) { + throw NativeScriptException("Wrong number of arguments (1 expected)"); + } + + napi_value arg = args[0]; + + bool isObject = napi_util::is_object(env, arg); + + if (!isObject) { + throw NativeScriptException("Wrong type of argument (object expected)"); + } + + auto argObj = arg; + + auto obj = m_objectManager->GetJavaObjectByJsObject(argObj); + + if (obj.IsNull()) { + throw NativeScriptException("Wrong type of argument (object expected)"); + } + + JEnv jEnv; + + if (m_ByteBufferClass == nullptr) { + m_ByteBufferClass = jEnv.FindClass("java/nio/ByteBuffer"); + assert(m_ByteBufferClass != nullptr); + } + + auto isByteBuffer = jEnv.IsInstanceOf(obj, m_ByteBufferClass); + + if (!isByteBuffer) { + throw NativeScriptException("Wrong type of argument (ByteBuffer expected)"); + } + + if (m_isDirectMethodID == nullptr) { + m_isDirectMethodID = jEnv.GetMethodID(m_ByteBufferClass, "isDirect", "()Z"); + assert(m_isDirectMethodID != nullptr); + } + + auto ret = jEnv.CallBooleanMethod(obj, m_isDirectMethodID); + + auto isDirectBuffer = ret == JNI_TRUE; + + napi_value arrayBuffer; + + if (isDirectBuffer) { + auto data = jEnv.GetDirectBufferAddress(obj); + auto size = jEnv.GetDirectBufferCapacity(obj); + + void* externalData = data; + napi_create_external_arraybuffer(env, externalData, size, nullptr, nullptr, &arrayBuffer); + } else { + if (m_remainingMethodID == nullptr) { + m_remainingMethodID = jEnv.GetMethodID(m_ByteBufferClass, "remaining", "()I"); + assert(m_remainingMethodID != nullptr); + } + + int bufferRemainingSize = jEnv.CallIntMethod(obj, m_remainingMethodID); + + if (m_getMethodID == nullptr) { + m_getMethodID = jEnv.GetMethodID(m_ByteBufferClass, "get", "([BII)Ljava/nio/ByteBuffer;"); + assert(m_getMethodID != nullptr); + } + + jbyteArray byteArray = jEnv.NewByteArray(bufferRemainingSize); + jEnv.CallObjectMethod(obj, m_getMethodID, byteArray, 0, bufferRemainingSize); + + auto byteArrayElements = jEnv.GetByteArrayElements(byteArray, 0); + + jbyte* data = new jbyte[bufferRemainingSize]; + memcpy(data, byteArrayElements, bufferRemainingSize); + + napi_create_external_arraybuffer(env, data, bufferRemainingSize, [](napi_env env, void* finalize_data, void* finalize_hint) { + delete[] static_cast(finalize_data); + }, nullptr, &arrayBuffer); + + jEnv.ReleaseByteArrayElements(byteArray, byteArrayElements, 0); + } + + napi_value nativeObjectKey; + napi_create_string_utf8(env, "nativeObject", NAPI_AUTO_LENGTH, &nativeObjectKey); + napi_set_property(env, arrayBuffer, nativeObjectKey, argObj); + + return arrayBuffer; +} \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.h b/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.h new file mode 100644 index 000000000..cccd172cd --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.h @@ -0,0 +1,29 @@ +#ifndef ARRAYBUFFERHELPER_H_ +#define ARRAYBUFFERHELPER_H_ + +#include "ObjectManager.h" + +namespace tns { + class ArrayBufferHelper { + public: + ArrayBufferHelper(); + + void CreateConvertFunctions(napi_env env, napi_value global, ObjectManager* objectManager); + + private: + + static napi_value CreateFromCallbackStatic(napi_env env, napi_callback_info info); + + napi_value CreateFromCallbackImpl(napi_env env, size_t argc, napi_value* args); + + ObjectManager* m_objectManager; + + jclass m_ByteBufferClass; + jmethodID m_isDirectMethodID; + jmethodID m_remainingMethodID; + jmethodID m_getMethodID; + }; +} + + +#endif /* ARRAYBUFFERHELPER_H_ */ diff --git a/NativeScript/ffi/jni/napi/conversion/ArrayElementAccessor.cpp b/NativeScript/ffi/jni/napi/conversion/ArrayElementAccessor.cpp new file mode 100644 index 000000000..f7cafee2e --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/ArrayElementAccessor.cpp @@ -0,0 +1,222 @@ +#include "ArrayElementAccessor.h" +#include "JsArgToArrayConverter.h" +#include "ArgConverter.h" +#include "Util.h" +#include "NativeScriptException.h" +#include "Runtime.h" + +using namespace std; +using namespace tns; + +napi_value ArrayElementAccessor::GetArrayElement(napi_env env, napi_value array, uint32_t index, const string& arraySignature) { + JEnv jenv; + + auto runtime = Runtime::GetRuntime(env); + auto objectManager = runtime->GetObjectManager(); + auto arr = objectManager->GetJavaObjectByJsObject(array); + + assertNonNullNativeArray(arr); + + napi_value value; + jsize startIndex = index; + const jsize length = 1; + + const string elementSignature = arraySignature.substr(1); + jboolean isCopy = false; + + if (elementSignature == "Z") { + jbooleanArray boolArr = static_cast(arr); + jboolean boolArrValue; + jenv.GetBooleanArrayRegion(boolArr, startIndex, length, &boolArrValue); + value = ConvertToJsValue(env, objectManager, jenv, elementSignature, &boolArrValue); + } else if (elementSignature == "B") { + jbyteArray byteArr = static_cast(arr); + jbyte byteArrValue; + jenv.GetByteArrayRegion(byteArr, startIndex, length, &byteArrValue); + value = ConvertToJsValue(env, objectManager, jenv, elementSignature, &byteArrValue); + } else if (elementSignature == "C") { + jcharArray charArr = static_cast(arr); + jchar charArrValue; + jenv.GetCharArrayRegion(charArr, startIndex, length, &charArrValue); + JniLocalRef s(jenv.NewString(&charArrValue, 1)); + const char* singleChar = jenv.GetStringUTFChars(s, &isCopy); + value = ConvertToJsValue(env, objectManager, jenv, elementSignature, singleChar); + jenv.ReleaseStringUTFChars(s, singleChar); + } else if (elementSignature == "S") { + jshortArray shortArr = static_cast(arr); + jshort shortArrValue; + jenv.GetShortArrayRegion(shortArr, startIndex, length, &shortArrValue); + value = ConvertToJsValue(env, objectManager, jenv, elementSignature, &shortArrValue); + } else if (elementSignature == "I") { + jintArray intArr = static_cast(arr); + jint intArrValue; + jenv.GetIntArrayRegion(intArr, startIndex, length, &intArrValue); + value = ConvertToJsValue(env, objectManager, jenv, elementSignature, &intArrValue); + } else if (elementSignature == "J") { + jlongArray longArr = static_cast(arr); + jlong longArrValue; + jenv.GetLongArrayRegion(longArr, startIndex, length, &longArrValue); + value = ConvertToJsValue(env, objectManager, jenv, elementSignature, &longArrValue); + } else if (elementSignature == "F") { + jfloatArray floatArr = static_cast(arr); + jfloat floatArrValue; + jenv.GetFloatArrayRegion(floatArr, startIndex, length, &floatArrValue); + value = ConvertToJsValue(env, objectManager, jenv, elementSignature, &floatArrValue); + } else if (elementSignature == "D") { + jdoubleArray doubleArr = static_cast(arr); + jdouble doubleArrValue; + jenv.GetDoubleArrayRegion(doubleArr, startIndex, length, &doubleArrValue); + value = ConvertToJsValue(env, objectManager, jenv, elementSignature, &doubleArrValue); + } else { + jobject result = jenv.GetObjectArrayElement(static_cast(arr), index); + value = ConvertToJsValue(env, objectManager, jenv, elementSignature, &result); + jenv.DeleteLocalRef(result); + } + + return value; +} + +void ArrayElementAccessor::SetArrayElement(napi_env env, napi_value array, uint32_t index, const string& arraySignature, napi_value value) { + JEnv jenv; + + + auto runtime = Runtime::GetRuntime(env); + auto objectManager = runtime->GetObjectManager(); + + tns::JniLocalRef arr = objectManager->GetJavaObjectByJsObject(array); + + assertNonNullNativeArray(arr); + + const string elementSignature = arraySignature.substr(1); + jboolean isCopy = false; + + if (elementSignature == "Z") { //bool + bool boolElementValue; + napi_get_value_bool(env, value, &boolElementValue); + jboolean jboolElementValue = static_cast(boolElementValue); + jbooleanArray boolArr = static_cast(arr); + jenv.SetBooleanArrayRegion(boolArr, index, 1, &jboolElementValue); + } else if (elementSignature == "B") { //byte + int32_t byteElementValue; + napi_get_value_int32(env, value, &byteElementValue); + jbyte jbyteElementValue = static_cast(byteElementValue); + jbyteArray byteArr = static_cast(arr); + jenv.SetByteArrayRegion(byteArr, index, 1, &jbyteElementValue); + } else if (elementSignature == "C") { //char + size_t str_len; + napi_get_value_string_utf8(env, value, nullptr, 0, &str_len); + string str(str_len, '\0'); + napi_get_value_string_utf8(env, value, &str[0], str_len + 1, &str_len); + JniLocalRef s(jenv.NewString(reinterpret_cast(str.c_str()), 1)); + const char* singleChar = jenv.GetStringUTFChars(s, &isCopy); + jchar charElementValue = *singleChar; + jenv.ReleaseStringUTFChars(s, singleChar); + jcharArray charArr = static_cast(arr); + jenv.SetCharArrayRegion(charArr, index, 1, &charElementValue); + } else if (elementSignature == "S") { //short + int32_t shortElementValue; + napi_get_value_int32(env, value, &shortElementValue); + jshort jshortElementValue = static_cast(shortElementValue); + jshortArray shortArr = static_cast(arr); + jenv.SetShortArrayRegion(shortArr, index, 1, &jshortElementValue); + } else if (elementSignature == "I") { //int + int32_t intElementValue; + napi_get_value_int32(env, value, &intElementValue); + jint jintElementValue = static_cast(intElementValue); + jintArray intArr = static_cast(arr); + jenv.SetIntArrayRegion(intArr, index, 1, &jintElementValue); + } else if (elementSignature == "J") { //long + int64_t longElementValue; + napi_get_value_int64(env, value, &longElementValue); + jlong jlongElementValue = static_cast(longElementValue); + jlongArray longArr = static_cast(arr); + jenv.SetLongArrayRegion(longArr, index, 1, &jlongElementValue); + } else if (elementSignature == "F") { //float + double floatElementValue; + napi_get_value_double(env, value, &floatElementValue); + jfloat jfloatElementValue = static_cast(floatElementValue); + jfloatArray floatArr = static_cast(arr); + jenv.SetFloatArrayRegion(floatArr, index, 1, &jfloatElementValue); + } else if (elementSignature == "D") { //double + double doubleElementValue; + napi_get_value_double(env, value, &doubleElementValue); + jdouble jdoubleElementValue = static_cast(doubleElementValue); + jdoubleArray doubleArr = static_cast(arr); + jenv.SetDoubleArrayRegion(doubleArr, index, 1, &jdoubleElementValue); + } else { //string or object + napi_valuetype ref_type; + + napi_typeof(env, value, &ref_type); + + if (ref_type == napi_object || ref_type == napi_function || ref_type == napi_string) { + JsArgToArrayConverter argConverter(env, value, false, (int) Type::Null); + if (argConverter.IsValid()) { + jobjectArray objArr = static_cast(arr); + jobject objectElementValue = argConverter.GetConvertedArg(); + jenv.SetObjectArrayElement(objArr, index, objectElementValue); + } else { + JsArgToArrayConverter::Error err = argConverter.GetError(); + throw NativeScriptException(string(err.msg)); + } + } else { + throw NativeScriptException(string("Cannot assign primitive value to array of objects.")); + } + } + +} + +napi_value ArrayElementAccessor::ConvertToJsValue(napi_env env, ObjectManager* objectManager, JEnv& jenv, const string& elementSignature, const void* value) { + napi_value jsValue; + + jint val = *(jint*) value; + + if (elementSignature == "Z") { + napi_get_boolean(env, *(jboolean*) value, &jsValue); + } else if (elementSignature == "B") { + napi_create_int32(env, *(jbyte*) value, &jsValue); + } else if (elementSignature == "C") { + napi_create_string_utf8(env, (const char*) value, 1, &jsValue); + } else if (elementSignature == "S") { + napi_create_int32(env, *(jshort*) value, &jsValue); + } else if (elementSignature == "I") { + napi_create_int32(env, *(jint*) value, &jsValue); + } else if (elementSignature == "J") { + napi_create_int64(env, *(jlong*) value, &jsValue); + } else if (elementSignature == "F") { + napi_create_double(env, *(jfloat*) value, &jsValue); + } else if (elementSignature == "D") { + napi_create_double(env, *(jdouble*) value, &jsValue); + } else { + if (nullptr != (*(jobject*) value)) { + bool isString = elementSignature == "Ljava/lang/String;"; + + if (isString) { + jsValue = ArgConverter::jstringToJsString(env, *(jstring *) value); + } else { + jint javaObjectID = objectManager->GetOrCreateObjectId(*(jobject*) value); + jsValue = objectManager->GetJsObjectByJavaObject(javaObjectID); + + if (napi_util::is_null_or_undefined(env, jsValue)) { + string className; + if (elementSignature[0] == '[') { + className = Util::JniClassPathToCanonicalName(elementSignature); + } else { + className = objectManager->GetClassName(*(jobject*) value); + } + + jsValue = objectManager->CreateJSWrapper(javaObjectID, className); + } + } + } else { + napi_get_null(env, &jsValue); + } + } + + return jsValue; +} + +void ArrayElementAccessor::assertNonNullNativeArray(tns::JniLocalRef& arrayReference) { + if(arrayReference.IsNull()){ + throw NativeScriptException("Failed calling indexer operator on native array. The JavaScript instance no longer has available Java instance counterpart."); + } +} \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/conversion/ArrayElementAccessor.h b/NativeScript/ffi/jni/napi/conversion/ArrayElementAccessor.h new file mode 100644 index 000000000..a17a687f6 --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/ArrayElementAccessor.h @@ -0,0 +1,24 @@ +#ifndef ARRAYELEMENTACCESSOR_H_ +#define ARRAYELEMENTACCESSOR_H_ + +#include "JEnv.h" +#include "JniLocalRef.h" +#include "js_native_api.h" +#include +#include "ObjectManager.h" + + +namespace tns { + class ArrayElementAccessor { + public: + napi_value GetArrayElement(napi_env env, napi_value array, uint32_t index, const std::string& arraySignature); + + void SetArrayElement(napi_env env, napi_value array, uint32_t index, const std::string& arraySignature, napi_value value); + + private: + napi_value ConvertToJsValue(napi_env env, ObjectManager* objectManager, JEnv& jEnv, const std::string& elementSignature, const void* value); + void assertNonNullNativeArray(tns::JniLocalRef& arrayReference); + }; +} + +#endif /* ARRAYELEMENTACCESSOR_H_ */ \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/conversion/ArrayHelper.cpp b/NativeScript/ffi/jni/napi/conversion/ArrayHelper.cpp new file mode 100644 index 000000000..415774843 --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/ArrayHelper.cpp @@ -0,0 +1,177 @@ +#include "ArrayHelper.h" +#include "ArgConverter.h" +#include "NativeScriptException.h" +#include "Runtime.h" +#include + +using namespace std; +using namespace tns; + +ArrayHelper::ArrayHelper() { +} + +void ArrayHelper::Init(napi_env env) { + JEnv jenv; + + RUNTIME_CLASS = jenv.FindClass("com/tns/Runtime"); + assert(RUNTIME_CLASS != nullptr); + + CREATE_ARRAY_HELPER = jenv.GetStaticMethodID(RUNTIME_CLASS, "createArrayHelper", "(Ljava/lang/String;I)Ljava/lang/Object;"); + assert(CREATE_ARRAY_HELPER != nullptr); + + napi_value global; + napi_get_global(env, &global); + + napi_value arrayConstructor; + napi_get_named_property(env, global, "Array", &arrayConstructor); + + napi_util::napi_set_function(env, arrayConstructor, "create", CreateJavaArrayCallback, nullptr); + +} + +napi_value ArrayHelper::CreateJavaArrayCallback(napi_env env, napi_callback_info info) { + try { + napi_value array = CreateJavaArray(env, info); + return array; + } catch (NativeScriptException& e) { + e.ReThrowToNapi(env); + } catch (std::exception& e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + return nullptr; +} + +napi_value ArrayHelper::CreateJavaArray(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN_VARGS() + + if (argc != 2) { + Throw(env, "Expect two parameters."); + return nullptr; + } + + napi_value type = argv[0]; + napi_value length = argv[1]; + + JniLocalRef array; + + auto runtime = Runtime::GetRuntime(env); + auto objectManager = runtime->GetObjectManager(); + + napi_valuetype typeType; + napi_typeof(env, type, &typeType); + + napi_valuetype lengthType; + napi_typeof(env, length, &lengthType); + + if (typeType == napi_string) { + if (lengthType != napi_number) { + Throw(env, "Expect integer value as a second argument."); + return nullptr; + } + + bool isFloat = napi_util::is_float(env, length); + + if (isFloat) { + Throw(env, "Expect integer value as a second argument. It is a float"); + return nullptr; + } + + int32_t len; + napi_get_value_int32(env, length, &len); + if (len < 0) { + Throw(env, "Expect non-negative integer value as a second argument."); + return nullptr; + } + + string typeName = ArgConverter::ConvertToString(env, type); + array = JniLocalRef(CreateArrayByClassName(typeName, len)); + } else if (typeType == napi_function || typeType == napi_object) { + if (lengthType != napi_number) { + Throw(env, "Expect integer value as a second argument."); + return nullptr; + } + + bool isFloat = napi_util::is_float(env, length); + + if (isFloat) { + Throw(env, "Expect integer value as a second argument."); + return nullptr; + } + + int32_t len; + napi_get_value_int32(env, length, &len); + if (len < 0) { + Throw(env, "Expect non-negative integer value as a second argument."); + return nullptr; + } + + napi_value classVal; + napi_get_named_property(env, type, "class", &classVal); + + napi_valuetype classValType; + napi_typeof(env, classVal, &classValType); + + if (classValType == napi_undefined) { + Throw(env, "Expect known class as a second argument."); + return nullptr; + } + + auto c = objectManager->GetJavaObjectByJsObject(classVal); + + JEnv jenv; + array = jenv.NewObjectArray(len, static_cast(c), nullptr); + } else { + Throw(env, "Expect primitive type name or class function as a first argument"); + return nullptr; + } + + jint javaObjectID = objectManager->GetOrCreateObjectId(array); + return objectManager->CreateJSWrapper(javaObjectID, "" /* ignored */, array); +} + +void ArrayHelper::Throw(napi_env env, const std::string& errorMessage) { + napi_value errMsg; + napi_create_string_utf8(env, errorMessage.c_str(), NAPI_AUTO_LENGTH, &errMsg); + + napi_value err; + napi_create_error(env, nullptr, errMsg, &err); + + napi_throw(env, err); +} + +jobject ArrayHelper::CreateArrayByClassName(const string& typeName, int length) { + JEnv jEnv; + jobject array; + + if (typeName == "char") { + array = jEnv.NewCharArray(length); + } else if (typeName == "boolean") { + array = jEnv.NewBooleanArray(length); + } else if (typeName == "byte") { + array = jEnv.NewByteArray(length); + } else if (typeName == "short") { + array = jEnv.NewShortArray(length); + } else if (typeName == "int") { + array = jEnv.NewIntArray(length); + } else if (typeName == "long") { + array = jEnv.NewLongArray(length); + } else if (typeName == "float") { + array = jEnv.NewFloatArray(length); + } else if (typeName == "double") { + array = jEnv.NewDoubleArray(length); + } else { + JniLocalRef s(jEnv.NewStringUTF(typeName.c_str())); + array = jEnv.CallStaticObjectMethod(RUNTIME_CLASS, CREATE_ARRAY_HELPER, (jstring)s, length); + } + + return array; +} + +jclass ArrayHelper::RUNTIME_CLASS = nullptr; +jmethodID ArrayHelper::CREATE_ARRAY_HELPER = nullptr; \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/conversion/ArrayHelper.h b/NativeScript/ffi/jni/napi/conversion/ArrayHelper.h new file mode 100644 index 000000000..bb234df26 --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/ArrayHelper.h @@ -0,0 +1,30 @@ +#ifndef ARRAYHELPER_H_ +#define ARRAYHELPER_H_ + +#include "js_native_api.h" +#include "ObjectManager.h" +#include + +namespace tns { +class ArrayHelper { + public: + static void Init(napi_env env); + + private: + ArrayHelper(); + + static napi_value CreateJavaArrayCallback(napi_env env, napi_callback_info info); + + static napi_value CreateJavaArray(napi_env env, napi_callback_info info); + + static void Throw(napi_env env, const std::string& errorMessage); + + static jobject CreateArrayByClassName(const std::string& typeName, int length); + + static jclass RUNTIME_CLASS; + + static jmethodID CREATE_ARRAY_HELPER; +}; +} + +#endif /* ARRAYHELPER_H_ */ \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/conversion/JsArgConverter.cpp b/NativeScript/ffi/jni/napi/conversion/JsArgConverter.cpp new file mode 100644 index 000000000..ec025b17f --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/JsArgConverter.cpp @@ -0,0 +1,830 @@ +#include "JsArgConverter.h" +#include "ObjectManager.h" +#include "JniSignatureParser.h" +#include "JsArgToArrayConverter.h" +#include "ArgConverter.h" +#include "NumericCasts.h" +#include "NativeScriptException.h" +#include + +using namespace std; +using namespace tns; + +JsArgConverter::JsArgConverter(napi_env env, napi_value caller, napi_value *args, size_t argc, + const std::string &methodSignature, MetadataEntry *entry) + : m_env(env), m_isValid(true), m_methodSignature(methodSignature), m_error(Error()) { + int napiProvidedArgumentsLength = argc; + m_argsLen = 1 + napiProvidedArgumentsLength; + + if (m_argsLen > 0) { + if ((entry != nullptr) && (entry->getIsResolved())) { + if (entry->parsedSig.empty()) { + JniSignatureParser parser(m_methodSignature); + entry->parsedSig = parser.Parse(); + } + m_tokens = entry->parsedSig; + } else { + JniSignatureParser parser(m_methodSignature); + m_tokens = parser.Parse(); + } + + m_isValid = ConvertArg(env, caller, 0); + + if (!m_isValid) { + throw NativeScriptException("Error while converting argument!"); + } + + for (size_t i = 0; i < napiProvidedArgumentsLength; i++) { + m_isValid = ConvertArg(env, args[i], i + 1); + + if (!m_isValid) { + break; + } + } + } +} + +JsArgConverter::JsArgConverter(napi_env env, napi_value *args, size_t argc, + bool hasImplementationObject, const std::string &methodSignature, + MetadataEntry *entry) + : m_env(env), m_isValid(true), m_methodSignature(methodSignature), m_error(Error()) { + m_argsLen = !hasImplementationObject ? argc : argc - 1; + + if (m_argsLen > 0) { + if ((entry != nullptr) && (entry->getIsResolved())) { + if (entry->parsedSig.empty()) { + JniSignatureParser parser(m_methodSignature); + entry->parsedSig = parser.Parse(); + } + m_tokens = entry->parsedSig; + } else { + JniSignatureParser parser(m_methodSignature); + m_tokens = parser.Parse(); + } + + for (size_t i = 0; i < m_argsLen; i++) { + m_isValid = ConvertArg(env, args[i], i); + + if (!m_isValid) { + break; + } + } + } +} + +JsArgConverter::JsArgConverter(napi_env env, napi_value *args, size_t argc, + const std::string &methodSignature) + : m_env(env), m_isValid(true), m_methodSignature(methodSignature), m_error(Error()) { + m_argsLen = argc; + + JniSignatureParser parser(m_methodSignature); + m_tokens = parser.Parse(); + + for (size_t i = 0; i < m_argsLen; i++) { + m_isValid = ConvertArg(env, args[i], i); + + if (!m_isValid) { + break; + } + } +} + +tns::BufferCastType JsArgConverter::GetCastType(napi_typedarray_type type) { + switch (type) { + case napi_uint16_array: + case napi_int16_array: + return tns::BufferCastType::Short; + case napi_uint32_array: + case napi_int32_array: + return tns::BufferCastType::Int; + case napi_float32_array: + return tns::BufferCastType::Float; + case napi_float64_array: + return tns::BufferCastType::Double; + case napi_bigint64_array: + case napi_biguint64_array: + return tns::BufferCastType::Long; + default: + return tns::BufferCastType::Byte; + } +} + +bool JsArgConverter::ConvertArg(napi_env env, napi_value arg, int index) { + bool success = false; + + char buff[1024]; + + const auto &typeSignature = m_tokens.at(index); + + if (arg == nullptr) { + SetConvertedObject(index, nullptr); + success = false; + } else { + napi_valuetype argType; + napi_typeof(m_env, arg, &argType); + + if (argType == napi_object || argType == napi_function) { + bool isArray; + napi_is_array(m_env, arg, &isArray); + + if (isArray) { + success = typeSignature[0] == '['; + + if (success) { + success = ConvertJavaScriptArray(env, arg, index); + } + + if (!success) { + sprintf(buff, "Cannot convert array to %s at index %d", typeSignature.c_str(), + index); + } + } else { + + CastType castType = CastType::None; + +#ifdef USE_HOST_OBJECT + void *data; + napi_get_host_object_data(env, arg, &data); + if (data) { + castType = CastType::None; + } else { + castType = NumericCasts::GetCastType(env, arg); + } +#else + castType = NumericCasts::GetCastType(m_env, arg); +#endif + + napi_value castValue; + napi_valuetype valueType = napi_undefined; + if (castType != CastType::None) { + castValue = NumericCasts::GetCastValue(m_env, arg); + if (castValue != nullptr) { + napi_typeof(env, castValue, &valueType); + } + + } + + JniLocalRef obj; + + auto runtime = Runtime::GetRuntime(m_env); + auto objectManager = runtime->GetObjectManager(); + + JEnv jEnv; + + switch (castType) { + case CastType::Char: + if (valueType == napi_string) { + string value = ArgConverter::ConvertToString(m_env, castValue); + m_args[index].c = (jchar) value[0]; + success = true; + } + break; + + case CastType::Byte: + if (valueType == napi_string) { + string strValue = ArgConverter::ConvertToString(m_env, castValue); + int byteArg = atoi(strValue.c_str()); + jbyte value = (jbyte) byteArg; + success = ConvertFromCastFunctionObject(value, index); + } else if (valueType == napi_number) { + int byteArg = napi_util::get_int32(env, castValue); + jbyte value = (jbyte) byteArg; + success = ConvertFromCastFunctionObject(value, index); + } + + break; + + case CastType::Short: + if (valueType == napi_string) { + string strValue = ArgConverter::ConvertToString(m_env, castValue); + int shortArg = atoi(strValue.c_str()); + jshort value = (jshort) shortArg; + success = ConvertFromCastFunctionObject(value, index); + } else if (valueType == napi_number) { + int shortArg; + napi_get_value_int32(m_env, castValue, &shortArg); + jshort value = (jshort) shortArg; + success = ConvertFromCastFunctionObject(value, index); + } + break; + + case CastType::Long: + if (valueType == napi_string) { + string strValue = ArgConverter::ConvertToString(m_env, castValue); + int64_t longArg = atoll(strValue.c_str()); + jlong value = (jlong) longArg; + success = ConvertFromCastFunctionObject(value, index); + } else if (valueType == napi_number) { + int64_t longArg; + napi_get_value_int64(m_env, castValue, &longArg); + jlong value = (jlong) longArg; + success = ConvertFromCastFunctionObject(value, index); + } + break; + + case CastType::Float: + if (valueType == napi_number) { + double floatArg; + napi_get_value_double(m_env, castValue, &floatArg); + jfloat value = (jfloat) floatArg; + success = ConvertFromCastFunctionObject(value, index); + } + break; + + case CastType::Double: + if (valueType == napi_number) { + double doubleArg; + napi_get_value_double(m_env, castValue, &doubleArg); + jdouble value = (jdouble) doubleArg; + success = ConvertFromCastFunctionObject(value, index); + } + break; + + case CastType::None: + obj = objectManager->GetJavaObjectByJsObject(arg); + + if (obj.IsNull()) { + bool isArrayBuffer = false; + bool isDataView = false; + bool isTypedArray = false; + + napi_is_arraybuffer(env, arg, &isArrayBuffer); + if (!isArrayBuffer) { + napi_is_typedarray(env, arg, &isTypedArray); + if (!isTypedArray) { + napi_is_dataview(env, arg, &isDataView); + } + } + + if (isArrayBuffer || isDataView || isTypedArray) { + obj = JsArgConverter::GetByteBuffer(env, arg, isArrayBuffer, + isTypedArray, isDataView); + } + } + +#ifdef USE_HOST_OBJECT + if (!data) { +#endif + napi_value nullNode; + napi_get_named_property(env, arg, PROP_KEY_NULL_NODE_NAME, &nullNode); + if (!napi_util::is_null_or_undefined(env, nullNode)) { + SetConvertedObject(index, nullptr); + success = true; + break; + } +#ifdef USE_HOST_OBJECT + } +#endif + + success = !obj.IsNull(); + + if (success) { + SetConvertedObject(index, obj.Move(), obj.IsGlobal()); + } else { + if (napi_util::is_number_object(env, arg)) { + success = ConvertJavaScriptNumber(env, arg, index, true); + break; + } else if (napi_util::is_string_object(env, arg)) { + napi_value stringValue = napi_util::valueOf(env, arg); + success = ConvertJavaScriptString(env, stringValue, index); + break; + } else if (napi_util::is_boolean_object(env, arg)) { + napi_value boolValue = napi_util::valueOf(env, arg); + success = ConvertJavaScriptBoolean(env, boolValue, index); + break; + } + + if (!success) { + sprintf(buff, "Cannot convert object to %s at index %d", + typeSignature.c_str(), index); + } + } + break; + + default: + throw NativeScriptException("Unsupported cast type"); + } + } + } else if (argType == napi_number) { + success = ConvertJavaScriptNumber(env, arg, index, false); + + if (!success) { + sprintf(buff, "Cannot convert number to %s at index %d", typeSignature.c_str(), + index); + } + } else if (argType == napi_boolean) { + success = ConvertJavaScriptBoolean(env, arg, index); + + if (!success) { + sprintf(buff, "Cannot convert boolean to %s at index %d", typeSignature.c_str(), + index); + } + } else if (argType == napi_string) { + success = ConvertJavaScriptString(env, arg, index); + + if (!success) { + sprintf(buff, "Cannot convert string to %s at index %d", typeSignature.c_str(), + index); + } + } else if (argType == napi_undefined || argType == napi_null) { + SetConvertedObject(index, nullptr); + success = true; + } else { + SetConvertedObject(index, nullptr); + success = false; + } + } + + if (!success) { + m_error.index = index; + m_error.msg = string(buff); + } + + return success; +} + + +void JsArgConverter::SetConvertedObject(int index, jobject obj, bool isGlobal) { + m_args[index].l = obj; + if ((obj != nullptr) && !isGlobal) { + m_args_refs[m_args_refs_size++] = index; + } +} + +bool JsArgConverter::ConvertJavaScriptNumber(napi_env env, napi_value jsValue, int index, + bool isNumberObject = false) { + bool success = true; + + jvalue value = {0}; + + const auto &typeSignature = m_tokens.at(index); + + const char typePrefix = typeSignature[0]; + + switch (typePrefix) { + case 'B': { // byte + int32_t intValue; + if (isNumberObject) { + napi_get_value_int32(env, napi_util::valueOf(env, jsValue), &intValue); + } else { + napi_get_value_int32(env, jsValue, &intValue); + } + value.b = (jbyte) intValue; + break; + } + case 'S': { // short + int intValue; + if (isNumberObject) { + napi_get_value_int32(env, napi_util::valueOf(env, jsValue), &intValue); + } else { + napi_get_value_int32(env, jsValue, &intValue); + } + value.s = (jshort) intValue; + break; + } + case 'I': { // int + int intValue; + if (isNumberObject) { + napi_get_value_int32(env, napi_util::valueOf(env, jsValue), &intValue); + } else { + napi_get_value_int32(env, jsValue, &intValue); + } + value.i = (jint) intValue; + break; + } + case 'J': { // long + int64_t intValue; + if (isNumberObject) { + napi_get_value_int64(env, napi_util::valueOf(env, jsValue), &intValue); + } else { + napi_get_value_int64(env, jsValue, &intValue); + } + value.j = (jlong) intValue; + break; + } + case 'F': { // float + double doubleValue; + if (isNumberObject) { + napi_get_value_double(env, napi_util::valueOf(env, jsValue), &doubleValue); + } else { + napi_get_value_double(env, jsValue, &doubleValue); + } + value.f = (jfloat) doubleValue; + break; + } + case 'D': { // double + double doubleValue; + if (isNumberObject) { + napi_get_value_double(env, napi_util::valueOf(env, jsValue), &doubleValue); + } else { + napi_get_value_double(env, jsValue, &doubleValue); + } + value.d = (jdouble) doubleValue; + break; + } + default: + success = false; + break; + } + + if (success) { + m_args[index] = value; + } + + return success; +} + +bool JsArgConverter::ConvertJavaScriptBoolean(napi_env env, napi_value jsValue, int index) { + bool success; + + const auto &typeSignature = m_tokens.at(index); + + if (typeSignature == "Z") { + bool argValue; + napi_get_value_bool(env, jsValue, &argValue); + + jboolean value = argValue ? JNI_TRUE : JNI_FALSE; + m_args[index].z = value; + success = true; + } else { + success = false; + } + + return success; +} + +bool JsArgConverter::ConvertJavaScriptString(napi_env env, napi_value jsValue, int index) { + jstring stringObject = ArgConverter::ConvertToJavaString(env, jsValue); + SetConvertedObject(index, stringObject); + return true; +} + +bool JsArgConverter::ConvertJavaScriptArray(napi_env env, napi_value jsArr, int index) { + bool success = true; + + jarray arr = nullptr; + + uint32_t jsLen; + napi_get_array_length(env, jsArr, &jsLen); + + const jsize arrLength = jsLen; + + const auto &arraySignature = m_tokens.at(index); + + std::string elementType = arraySignature.substr(1); + + const char elementTypePrefix = elementType[0]; + + jclass elementClass; + std::string strippedClassName; + + JEnv jenv; + switch (elementTypePrefix) { + case 'Z': { + arr = jenv.NewBooleanArray(arrLength); + std::vector bools(arrLength); + for (uint32_t i = 0; i < arrLength; i++) { + napi_value element; + napi_get_element(env, jsArr, i, &element); + + bool boolValue; + napi_get_value_bool(env, element, &boolValue); + bools[i] = (jboolean) boolValue; + } + jenv.SetBooleanArrayRegion((jbooleanArray) arr, 0, arrLength, bools.data()); + break; + } + case 'B': { + arr = jenv.NewByteArray(arrLength); + std::vector bytes(arrLength); + for (uint32_t i = 0; i < arrLength; i++) { + napi_value element; + napi_get_element(env, jsArr, i, &element); + int32_t intValue; + napi_get_value_int32(env, element, &intValue); + bytes[i] = (jbyte) intValue; + } + jenv.SetByteArrayRegion((jbyteArray) arr, 0, arrLength, bytes.data()); + break; + } + case 'C': { + arr = jenv.NewCharArray(arrLength); + std::vector chars(arrLength); + for (uint32_t i = 0; i < arrLength; i++) { + napi_value element; + napi_get_element(env, jsArr, i, &element); + size_t str_len; + napi_get_value_string_utf8(env, element, nullptr, 0, &str_len); + std::string str(str_len, '\0'); + napi_get_value_string_utf8(env, element, &str[0], str_len + 1, &str_len); + chars[i] = (jchar) str[0]; + } + jenv.SetCharArrayRegion((jcharArray) arr, 0, arrLength, chars.data()); + break; + } + case 'S': { + arr = jenv.NewShortArray(arrLength); + std::vector shorts(arrLength); + for (uint32_t i = 0; i < arrLength; i++) { + napi_value element; + napi_get_element(env, jsArr, i, &element); + int32_t intValue; + napi_get_value_int32(env, element, &intValue); + shorts[i] = (jshort) intValue; + } + jenv.SetShortArrayRegion((jshortArray) arr, 0, arrLength, shorts.data()); + break; + } + case 'I': { + arr = jenv.NewIntArray(arrLength); + std::vector ints(arrLength); + for (uint32_t i = 0; i < arrLength; i++) { + napi_value element; + napi_get_element(env, jsArr, i, &element); + int32_t intValue; + napi_get_value_int32(env, element, &intValue); + ints[i] = (jint) intValue; + } + jenv.SetIntArrayRegion((jintArray) arr, 0, arrLength, ints.data()); + break; + } + case 'J': { + arr = jenv.NewLongArray(arrLength); + std::vector longs(arrLength); + for (uint32_t i = 0; i < arrLength; i++) { + napi_value element; + napi_get_element(env, jsArr, i, &element); + int64_t intValue; + napi_get_value_int64(env, element, &intValue); + longs[i] = (jlong) intValue; + } + jenv.SetLongArrayRegion((jlongArray) arr, 0, arrLength, longs.data()); + break; + } + case 'F': { + arr = jenv.NewFloatArray(arrLength); + std::vector floats(arrLength); + for (uint32_t i = 0; i < arrLength; i++) { + napi_value element; + napi_get_element(env, jsArr, i, &element); + double doubleValue; + napi_get_value_double(env, element, &doubleValue); + floats[i] = (jfloat) doubleValue; + } + jenv.SetFloatArrayRegion((jfloatArray) arr, 0, arrLength, floats.data()); + break; + } + case 'D': { + arr = jenv.NewDoubleArray(arrLength); + std::vector doubles(arrLength); + for (uint32_t i = 0; i < arrLength; i++) { + napi_value element; + napi_get_element(env, jsArr, i, &element); + double doubleValue; + napi_get_value_double(env, element, &doubleValue); + doubles[i] = (jdouble) doubleValue; + } + jenv.SetDoubleArrayRegion((jdoubleArray) arr, 0, arrLength, doubles.data()); + break; + } + case 'L': + strippedClassName = elementType.substr(1, elementType.length() - 2); + elementClass = jenv.FindClass(strippedClassName); + arr = jenv.NewObjectArray(arrLength, elementClass, nullptr); + for (uint32_t i = 0; i < arrLength; i++) { + napi_value element; + napi_get_element(env, jsArr, i, &element); + JsArgToArrayConverter c(env, element, false, (int) Type::Null); + jobject o = c.GetConvertedArg(); + jenv.SetObjectArrayElement((jobjectArray) arr, (int) i, o); + } + break; + default: + success = false; + break; + } + + if (success) { + SetConvertedObject(index, arr); + } + + return success; +} + + +template +bool JsArgConverter::ConvertFromCastFunctionObject(T value, int index) { + bool success = false; + + const auto &typeSignature = m_tokens.at(index); + + const char typeSignaturePrefix = typeSignature[0]; + + switch (typeSignaturePrefix) { + case 'B': + m_args[index].b = (jbyte) value; + success = true; + break; + + case 'S': + m_args[index].s = (jshort) value; + success = true; + break; + + case 'I': + m_args[index].i = (jint) value; + success = true; + break; + + case 'J': + m_args[index].j = (jlong) value; + success = true; + break; + + case 'F': + m_args[index].f = (jfloat) value; + success = true; + break; + + case 'D': + m_args[index].d = (jdouble) value; + success = true; + break; + + default: + success = false; + break; + } + + return success; +} + +int JsArgConverter::Length() const { + return m_argsLen; +} + +bool JsArgConverter::IsValid() const { + return m_isValid; +} + +jvalue *JsArgConverter::ToArgs() { + return m_args; +} + +JsArgConverter::Error JsArgConverter::GetError() const { + return m_error; +} + +JsArgConverter::~JsArgConverter() { + if (m_argsLen > 0) { + JEnv env; + for (int i = 0; i < m_args_refs_size; i++) { + int index = m_args_refs[i]; + if (index != -1) { + env.DeleteLocalRef(m_args[index].l); + } + } + } +} + +JniLocalRef JsArgConverter::GetByteBuffer(napi_env env, napi_value object, bool isArrayBuffer, + bool isTypedArray, bool isDataView) { + JEnv jEnv; + + BufferCastType bufferCastType = tns::BufferCastType::Byte; + size_t offset = 0; + size_t length = 0; + void *data = nullptr; + + if (isTypedArray) { + napi_typedarray_type type; + napi_value arrayBuffer; + size_t byteOffset; + napi_get_typedarray_info(env, object, &type, nullptr, &data, + &arrayBuffer, &byteOffset); + + + + + napi_get_arraybuffer_info(env, arrayBuffer, nullptr, &length); + + offset = byteOffset; + bufferCastType = JsArgConverter::GetCastType(type); + } else if (isArrayBuffer) { + napi_get_arraybuffer_info(env, object, &data, &length); + } else if (isDataView) { + napi_get_dataview_info(env, object, &length, &data, nullptr, + &offset); + } + + jobject directBuffer; + + if (isDataView || isTypedArray) { + directBuffer =jEnv.NewDirectByteBuffer(static_cast(data) + offset, length); + } else { + directBuffer = jEnv.NewDirectByteBuffer(static_cast(data), length); + } + + + auto directBufferClazz = jEnv.GetObjectClass(directBuffer); + + auto byteOrderId = BYTE_ORDER_METHOD_ID; + + if (!BYTE_ORDER_METHOD_ID) { + byteOrderId = jEnv.GetMethodID(directBufferClazz, "order", + "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"); + BYTE_ORDER_METHOD_ID = byteOrderId; + } + + auto byteOrderClazz = jEnv.FindClass("java/nio/ByteOrder"); + + auto byteOrderEnumId = BYTE_ORDER_ENUM_ID; + + if (!byteOrderEnumId) { + byteOrderEnumId = jEnv.GetStaticMethodID(byteOrderClazz, + "nativeOrder", + "()Ljava/nio/ByteOrder;"); + BYTE_ORDER_ENUM_ID = byteOrderEnumId; + } + + auto nativeByteOrder = jEnv.CallStaticObjectMethodA(byteOrderClazz, + byteOrderEnumId, + nullptr); + + directBuffer = jEnv.CallObjectMethod(directBuffer, byteOrderId, + nativeByteOrder); + + jobject buffer; + + if (bufferCastType == BufferCastType::Short) { + auto id = AS_SHORT_BUFFER; + if (!id) { + id = jEnv.GetMethodID(directBufferClazz, "asShortBuffer", + "()Ljava/nio/ShortBuffer;"); + AS_SHORT_BUFFER = id; + } + + buffer = jEnv.CallObjectMethodA(directBuffer, id, nullptr); + } else if (bufferCastType == BufferCastType::Int) { + auto id = AS_INT_BUFFER; + + if (!id) { + id = jEnv.GetMethodID(directBufferClazz, "asIntBuffer", + "()Ljava/nio/IntBuffer;"); + AS_INT_BUFFER = id; + } + buffer = jEnv.CallObjectMethodA(directBuffer, id, nullptr); + } else if (bufferCastType == BufferCastType::Long) { + auto id = AS_LONG_BUFFER; + + if (!id) { + id = jEnv.GetMethodID(directBufferClazz, "asLongBuffer", + "()Ljava/nio/LongBuffer;"); + AS_LONG_BUFFER = id; + } + + buffer = jEnv.CallObjectMethodA(directBuffer, id, nullptr); + } else if (bufferCastType == BufferCastType::Float) { + + auto id = AS_FLOAT_BUFFER; + if (!id) { + id = jEnv.GetMethodID(directBufferClazz, "asFloatBuffer", + "()Ljava/nio/FloatBuffer;"); + AS_FLOAT_BUFFER = id; + } + + buffer = jEnv.CallObjectMethodA(directBuffer, id, nullptr); + } else if (bufferCastType == BufferCastType::Double) { + + auto id = AS_DOUBLE_BUFFER; + if (!id) { + id = jEnv.GetMethodID(directBufferClazz, "asDoubleBuffer", + "()Ljava/nio/DoubleBuffer;"); + AS_DOUBLE_BUFFER = id; + } + buffer = jEnv.CallObjectMethodA(directBuffer, id, nullptr); + } else { + buffer = directBuffer; + } + + buffer = jEnv.NewGlobalRef(buffer); + + ObjectManager *objectManager = Runtime::GetRuntime(env)->GetObjectManager(); + + int id = objectManager->GetOrCreateObjectId(buffer); + auto clazz = jEnv.GetObjectClass(buffer); + + ObjectManager::MarkObject(env, object); + + objectManager->Link(object, id, clazz); + + return objectManager->GetJavaObjectByJsObject(object); +} + +jmethodID JsArgConverter::BYTE_ORDER_METHOD_ID = nullptr; +jmethodID JsArgConverter::BYTE_ORDER_ENUM_ID = nullptr; +jmethodID JsArgConverter::AS_SHORT_BUFFER = nullptr; +jmethodID JsArgConverter::AS_LONG_BUFFER = nullptr; +jmethodID JsArgConverter::AS_FLOAT_BUFFER = nullptr; +jmethodID JsArgConverter::AS_INT_BUFFER = nullptr; +jmethodID JsArgConverter::AS_DOUBLE_BUFFER = nullptr; \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/conversion/JsArgConverter.h b/NativeScript/ffi/jni/napi/conversion/JsArgConverter.h new file mode 100644 index 000000000..33ce2049d --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/JsArgConverter.h @@ -0,0 +1,100 @@ +#ifndef JSARGCONVERTER_H_ +#define JSARGCONVERTER_H_ + +#include +#include +#include "JEnv.h" +#include "Runtime.h" +#include "MetadataEntry.h" + +namespace tns { + + enum class BufferCastType { + Byte, + Short, + Int, + Long, + Float, + Double + }; + + class JsArgConverter { + public: + + JsArgConverter(napi_env env, napi_value caller, napi_value* args, size_t argc, const std::string& methodSignature, MetadataEntry* entry); + + JsArgConverter(napi_env env, napi_value* args, size_t argc, bool hasImplementationObject, const std::string& methodSignature, MetadataEntry* entry); + + JsArgConverter(napi_env env, napi_value* args, size_t argc, const std::string& methodSignature); + + ~JsArgConverter(); + + jvalue* ToArgs(); + + int Length() const; + + bool IsValid() const; + + struct Error; + + Error GetError() const; + + struct Error { + Error() : + index(-1), msg(std::string()) { + } + + int index; + std::string msg; + }; + + static BufferCastType GetCastType(napi_typedarray_type type); + + static JniLocalRef GetByteBuffer(napi_env env, napi_value object, bool isArrayBuffer, bool isTypedArray, bool isDataView); + + + + static jmethodID BYTE_ORDER_METHOD_ID; + static jmethodID BYTE_ORDER_ENUM_ID; + static jmethodID AS_SHORT_BUFFER; + static jmethodID AS_INT_BUFFER; + static jmethodID AS_LONG_BUFFER; + static jmethodID AS_FLOAT_BUFFER; + static jmethodID AS_DOUBLE_BUFFER; + private: + + bool ConvertArg(napi_env env, napi_value arg, int index); + + bool ConvertJavaScriptArray(napi_env env, napi_value jsArr, int index); + + bool ConvertJavaScriptNumber(napi_env env, napi_value jsValue, int index, bool isNumberObject); + + bool ConvertJavaScriptBoolean(napi_env env, napi_value jsValue, int index); + + bool ConvertJavaScriptString(napi_env env, napi_value jsValue, int index); + + void SetConvertedObject(int index, jobject obj, bool isGlobal = false); + + + template + bool ConvertFromCastFunctionObject(T value, int index); + + napi_env m_env; + + int m_argsLen; + + bool m_isValid; + + jvalue m_args[255]; + int m_args_refs[255]; + int m_args_refs_size = 0; + + std::string m_methodSignature; + + std::vector m_tokens; + + Error m_error; + }; +} + +#endif /* JSARGCONVERTER_H_ */ \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/conversion/JsArgToArrayConverter.cpp b/NativeScript/ffi/jni/napi/conversion/JsArgToArrayConverter.cpp new file mode 100644 index 000000000..f71940a2b --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/JsArgToArrayConverter.cpp @@ -0,0 +1,429 @@ +#include "JsArgToArrayConverter.h" +#include +#include "ObjectManager.h" +#include "ArgConverter.h" +#include "NumericCasts.h" +#include "NativeScriptException.h" +#include "Runtime.h" +#include "MetadataNode.h" +#include "JsArgConverter.h" + +using namespace std; +using namespace tns; + +JsArgToArrayConverter::JsArgToArrayConverter(napi_env env, napi_value arg, + bool isImplementationObject, int classReturnType) + : m_arr(nullptr), m_argsAsObject(nullptr), m_argsLen(0), m_isValid(false), m_error(Error()), + m_return_type(classReturnType) { + if (!isImplementationObject) { + m_argsLen = 1; + m_argsAsObject = new jobject[m_argsLen]; + memset(m_argsAsObject, 0, m_argsLen * sizeof(jobject)); + + m_isValid = ConvertArg(env, arg, 0); + } +} + +JsArgToArrayConverter::JsArgToArrayConverter(napi_env env, size_t argc, napi_value *argv, + bool hasImplementationObject) + : m_arr(nullptr), m_argsAsObject(nullptr), m_argsLen(0), m_isValid(false), m_error(Error()), + m_return_type(static_cast(Type::Null)) { + m_argsLen = !hasImplementationObject ? argc : argc - 2; + + bool success = true; + + if (m_argsLen > 0) { + m_argsAsObject = new jobject[m_argsLen]; + memset(m_argsAsObject, 0, m_argsLen * sizeof(jobject)); + + for (int i = 0; i < m_argsLen; i++) { + success = ConvertArg(env, argv[i], i); + + if (!success) { + break; + } + } + } + + m_isValid = success; +} + +bool JsArgToArrayConverter::ConvertArg(napi_env env, napi_value arg, int index) { + bool success = false; + stringstream s; + + JEnv jEnv; + + Type returnType = JType::getClassType(m_return_type); + + napi_valuetype argType; + napi_typeof(env, arg, &argType); + + if (argType == napi_undefined || argType == napi_null) { + SetConvertedObject(jEnv, index, nullptr); + success = true; + } else if (argType == napi_number) { + double d; + napi_get_value_double(env, arg, &d); + int64_t i = (int64_t) d; + + bool isWholeNumber = d == i; + + if (isWholeNumber) { + jobject obj; + + if ((INT_MIN <= i) && (i <= INT_MAX) && + (returnType == Type::Int || returnType == Type::Null)) { + obj = JType::NewInt(jEnv, (jint) i); + } else { + obj = JType::NewLong(jEnv, (jlong) d); + } + + SetConvertedObject(jEnv, index, obj); + success = true; + } else { + jobject obj; + + if ((FLT_MIN <= d) && (d <= FLT_MAX) && + (returnType == Type::Float || returnType == Type::Null)) { + obj = JType::NewFloat(jEnv, (jfloat) d); + } else { + obj = JType::NewDouble(jEnv, (jdouble) d); + } + + SetConvertedObject(jEnv, index, obj); + success = true; + } + } else if (argType == napi_boolean) { + bool value; + napi_get_value_bool(env, arg, &value); + auto javaObject = JType::NewBoolean(jEnv, value); + SetConvertedObject(jEnv, index, javaObject); + success = true; + } else if (argType == napi_string) { + auto stringObject = ArgConverter::ConvertToJavaString(env, arg); + SetConvertedObject(jEnv, index, stringObject); + success = true; + } else if (argType == napi_object || argType == napi_function) { + napi_value jsObj = arg; + + + CastType castType = CastType::None; +#ifdef USE_HOST_OBJECT + void *data; + napi_get_host_object_data(env, jsObj, &data); + if (data) { + castType = CastType::None; + } else { + castType = NumericCasts::GetCastType(env, jsObj); + } +#else + castType = NumericCasts::GetCastType(env, jsObj); +#endif + + napi_value castValue; + jchar charValue; + jbyte byteValue; + jshort shortValue; + jlong longValue; + jfloat floatValue; + jdouble doubleValue; + jobject javaObject; + JniLocalRef obj; + + auto runtime = Runtime::GetRuntime(env); + auto objectManager = runtime->GetObjectManager(); + + switch (castType) { + case CastType::Char: + castValue = NumericCasts::GetCastValue(env, jsObj); + charValue = '\0'; + if (castValue != nullptr) { + string str = ArgConverter::ConvertToString(env, castValue); + charValue = (jchar) str[0]; + } + javaObject = JType::NewChar(jEnv, charValue); + SetConvertedObject(jEnv, index, javaObject); + success = true; + break; + + case CastType::Byte: + castValue = NumericCasts::GetCastValue(env, jsObj); + byteValue = 0; + + if (castValue != nullptr) { + if (napi_util::is_of_type(env, castValue, napi_string)) { + string value = ArgConverter::ConvertToString(env, castValue); + int byteArg = atoi(value.c_str()); + byteValue = (jbyte) byteArg; + } else { + int byteArg = napi_util::get_int32(env, castValue); + byteValue = (jbyte) byteArg; + } + } + + javaObject = JType::NewByte(jEnv, byteValue); + SetConvertedObject(jEnv, index, javaObject); + success = true; + break; + + case CastType::Short: + castValue = NumericCasts::GetCastValue(env, jsObj); + shortValue = 0; + if (castValue != nullptr) { + if (napi_util::is_of_type(env, castValue, napi_string)) { + string value = ArgConverter::ConvertToString(env, castValue); + int shortArg = atoi(value.c_str()); + shortValue = (jshort) shortArg; + } else { + int shortArg = napi_util::get_int32(env, castValue); + shortValue = (jshort) shortArg; + } + } + + javaObject = JType::NewShort(jEnv, shortValue); + + SetConvertedObject(jEnv, index, javaObject); + success = true; + break; + + case CastType::Long: + castValue = NumericCasts::GetCastValue(env, jsObj); + longValue = 0; + if (castValue != nullptr) { + if (napi_util::is_of_type(env, castValue, napi_string)) { + auto strValue = ArgConverter::ConvertToString(env, castValue); + longValue = atoll(strValue.c_str()); + } else { + int64_t longArg; + napi_get_value_int64(env, castValue, &longArg); + longValue = (jlong) longArg; + } + } + javaObject = JType::NewLong(jEnv, longValue); + SetConvertedObject(jEnv, index, javaObject); + success = true; + break; + + case CastType::Float: + castValue = NumericCasts::GetCastValue(env, jsObj); + floatValue = 0; + if (castValue != nullptr) { + double floatArg; + napi_get_value_double(env, castValue, &floatArg); + floatValue = (jfloat) floatArg; + } + javaObject = JType::NewFloat(jEnv, floatValue); + SetConvertedObject(jEnv, index, javaObject); + success = true; + break; + + case CastType::Double: + castValue = NumericCasts::GetCastValue(env, jsObj); + doubleValue = 0; + if (castValue != nullptr) { + double doubleArg; + napi_get_value_double(env, castValue, &doubleArg); + doubleValue = (jdouble) doubleArg; + } + javaObject = JType::NewDouble(jEnv, doubleValue); + SetConvertedObject(jEnv, index, javaObject); + success = true; + break; + + case CastType::None: + + obj = objectManager->GetJavaObjectByJsObject(jsObj); + + if (obj.IsNull()) { + bool isArrayBuffer = false; + bool isDataView = false; + bool isTypedArray = false; + + napi_is_arraybuffer(env, jsObj, &isArrayBuffer); + if (!isArrayBuffer) { + napi_is_typedarray(env, jsObj, &isTypedArray); + if (!isTypedArray) { + napi_is_dataview(env, jsObj, &isDataView); + } + } + + if (isArrayBuffer || isDataView || isTypedArray) { + obj = JsArgConverter::GetByteBuffer(env, jsObj, isArrayBuffer, isTypedArray, + isDataView); + } + } + + +#ifdef USE_HOST_OBJECT + if (!data) { +#endif + napi_value privateValue; + napi_get_named_property(env, jsObj, PROP_KEY_NULL_NODE_NAME, &privateValue); + if (!napi_util::is_null_or_undefined(env, privateValue)) { + void *data; + napi_get_value_external(env, privateValue, &data); + auto node = reinterpret_cast(data); + if (node == nullptr) { + s << "Cannot get type of the null argument at index " << index; + success = false; + break; + } + + auto type = node->GetName(); + auto nullObjName = "com/tns/NullObject"; + auto nullObjCtorSig = "(Ljava/lang/Class;)V"; + + jclass nullClazz = jEnv.FindClass(nullObjName); + jmethodID ctor = jEnv.GetMethodID(nullClazz, "", nullObjCtorSig); + jclass clazzToNull = jEnv.FindClass(type); + jobject nullObjType = jEnv.NewObject(nullClazz, ctor, clazzToNull); + + if (nullObjType != nullptr) { + SetConvertedObject(jEnv, index, nullObjType, false); + } else { + SetConvertedObject(jEnv, index, nullptr); + } + + success = true; + return success; + } + +#ifdef USE_HOST_OBJECT + } +#endif + + + success = !obj.IsNull(); + if (success) { + SetConvertedObject(jEnv, index, obj.Move(), obj.IsGlobal()); + } else { + if (napi_util::is_number_object(env, arg)) { + napi_value numValue = napi_util::valueOf(env, arg); + bool isFloat = napi_util::is_float(env, numValue); + if (isFloat) { + double floatArg; + napi_get_value_double(env, numValue, &floatArg); + jfloat value = (jfloat) floatArg; + javaObject = JType::NewFloat(jEnv, value); + SetConvertedObject(jEnv, index, javaObject); + success = true; + } else { + int intArg; + napi_get_value_int32(env, numValue, &intArg); + jint value = (jint) intArg; + javaObject = JType::NewInt(jEnv, value); + SetConvertedObject(jEnv, index, javaObject); + success = true; + } + break; + } else if (napi_util::is_string_object(env, arg)) { + napi_value stringValue = napi_util::valueOf(env, arg); + javaObject = ArgConverter::ConvertToJavaString(env, stringValue); + SetConvertedObject(jEnv, index, javaObject); + success = true; + break; + } else if (napi_util::is_boolean_object(env, arg)) { + napi_value boolValue = napi_util::valueOf(env, arg); + bool value = napi_util::get_bool(env, boolValue); + javaObject = JType::NewBoolean(jEnv, value); + SetConvertedObject(jEnv, index, javaObject); + success = true; + break; + } + + if (!success) { + napi_value objStr; + napi_coerce_to_string(env, jsObj, &objStr); + const char *objStrValue = napi_util::get_string_value(env, objStr); + s << "Cannot marshal JavaScript argument " << objStrValue << " at index " + << index + << " to Java type."; + } + } + break; + + default: + throw NativeScriptException("Unsupported cast type"); + } + } else { + s << "Cannot marshal JavaScript argument at index " << index << " to Java type."; + success = false; + } + + if (!success) { + m_error.index = index; + m_error.msg = s.str(); + } + + return success; +} + +jobject JsArgToArrayConverter::GetConvertedArg() { + return (m_argsLen > 0) ? m_argsAsObject[0] : nullptr; +} + +void JsArgToArrayConverter::SetConvertedObject(JEnv &env, int index, jobject obj, bool isGlobal) { + m_argsAsObject[index] = obj; + if ((obj != nullptr) && !isGlobal) { + m_storedIndexes.push_back(index); + } +} + +int JsArgToArrayConverter::Length() const { + return m_argsLen; +} + +bool JsArgToArrayConverter::IsValid() const { + return m_isValid; +} + +JsArgToArrayConverter::Error JsArgToArrayConverter::GetError() const { + return m_error; +} + +jobjectArray JsArgToArrayConverter::ToJavaArray() { + if ((m_arr == nullptr) && (m_argsLen > 0)) { + if (m_argsLen >= JsArgToArrayConverter::MAX_JAVA_PARAMS_COUNT) { + stringstream ss; + ss << "You are trying to override more than the MAX_JAVA_PARAMS_COUNT: " + << JsArgToArrayConverter::MAX_JAVA_PARAMS_COUNT; + throw NativeScriptException(ss.str()); + } + + JEnv jEnv; + + if (JsArgToArrayConverter::JAVA_LANG_OBJECT_CLASS == nullptr) { + JsArgToArrayConverter::JAVA_LANG_OBJECT_CLASS = jEnv.FindClass("java/lang/Object"); + } + + JniLocalRef tmpArr( + jEnv.NewObjectArray(m_argsLen, JsArgToArrayConverter::JAVA_LANG_OBJECT_CLASS, + nullptr)); + m_arr = (jobjectArray) jEnv.NewGlobalRef(tmpArr); + + for (int i = 0; i < m_argsLen; i++) { + jEnv.SetObjectArrayElement(m_arr, i, m_argsAsObject[i]); + } + } + + return m_arr; +} + +JsArgToArrayConverter::~JsArgToArrayConverter() { + if (m_argsLen > 0) { + JEnv env; + + env.DeleteGlobalRef(m_arr); + + int length = m_storedIndexes.size(); + for (int i = 0; i < length; i++) { + int index = m_storedIndexes[i]; + env.DeleteLocalRef(m_argsAsObject[index]); + } + + delete[] m_argsAsObject; + } +} + +jclass JsArgToArrayConverter::JAVA_LANG_OBJECT_CLASS = nullptr; \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/conversion/JsArgToArrayConverter.h b/NativeScript/ffi/jni/napi/conversion/JsArgToArrayConverter.h new file mode 100644 index 000000000..1d0c37590 --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/JsArgToArrayConverter.h @@ -0,0 +1,65 @@ +#ifndef JSARGTOARRAYCONVERTER_H_ +#define JSARGTOARRAYCONVERTER_H_ + +#include "JEnv.h" +#include "JniLocalRef.h" +#include "js_native_api.h" +#include +#include + +namespace tns { +class JsArgToArrayConverter { + public: + JsArgToArrayConverter(napi_env env, size_t argc, napi_value* argv, bool hasImplementationObject); + + JsArgToArrayConverter(napi_env env, napi_value arg, bool isImplementationObject, int classReturnType); + + ~JsArgToArrayConverter(); + + jobjectArray ToJavaArray(); + + jobject GetConvertedArg(); + + int Length() const; + + bool IsValid() const; + + struct Error; + + Error GetError() const; + + struct Error { + Error() : + index(-1), msg(std::string()) { + } + + int index; + std::string msg; + }; + + private: + bool ConvertArg(napi_env env, napi_value arg, int index); + + void SetConvertedObject(JEnv& env, int index, jobject obj, bool isGlobal = false); + + int m_argsLen; + + int m_return_type; + + bool m_isValid; + + Error m_error; + + std::vector m_storedIndexes; + + jobject* m_argsAsObject; + + jobjectArray m_arr; + + short MAX_JAVA_PARAMS_COUNT = 256; + + static jclass JAVA_LANG_OBJECT_CLASS; +}; +} + +#endif /* JSARGTOARRAYCONVERTER_H_ */ \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/conversion/NumericCasts.cpp b/NativeScript/ffi/jni/napi/conversion/NumericCasts.cpp new file mode 100644 index 000000000..148020676 --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/NumericCasts.cpp @@ -0,0 +1,218 @@ +#include "NumericCasts.h" +#include "NativeScriptAssert.h" +#include "Util.h" +#include "ArgConverter.h" +#include "NativeScriptException.h" +#include + +using namespace std; +using namespace tns; + +void NumericCasts::CreateGlobalCastFunctions(napi_env env, napi_value globalObject) { + + napi_value longFunc, byteFunc, shortFunc, doubleFunc, floatFunc, charFunc; + + napi_create_function(env, "long", NAPI_AUTO_LENGTH, NumericCasts::MarkAsLongCallback, nullptr, + &longFunc); + napi_create_function(env, "byte", NAPI_AUTO_LENGTH, NumericCasts::MarkAsByteCallback, nullptr, + &byteFunc); + napi_create_function(env, "short", NAPI_AUTO_LENGTH, NumericCasts::MarkAsShortCallback, nullptr, + &shortFunc); + napi_create_function(env, "double", NAPI_AUTO_LENGTH, NumericCasts::MarkAsDoubleCallback, + nullptr, + &doubleFunc); + napi_create_function(env, "float", NAPI_AUTO_LENGTH, NumericCasts::MarkAsFloatCallback, nullptr, + &floatFunc); + napi_create_function(env, "char", NAPI_AUTO_LENGTH, NumericCasts::MarkAsCharCallback, nullptr, + &charFunc); + + napi_set_named_property(env, globalObject, "long", longFunc); + napi_set_named_property(env, globalObject, "byte", byteFunc); + napi_set_named_property(env, globalObject, "short", shortFunc); + napi_set_named_property(env, globalObject, "double", doubleFunc); + napi_set_named_property(env, globalObject, "float", floatFunc); + napi_set_named_property(env, globalObject, "char", charFunc); +} + +void NumericCasts::MarkAsLong(napi_env env, napi_value object, napi_value value) { + MarkJsObject(env, object, CastType::Long, value); +} + + +napi_value NumericCasts::MarkAsLongCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1); + + if (argc != 1) { + napi_throw_error(env, nullptr, "long(x) should be called with single parameter"); + return nullptr; + } + + napi_valuetype type; + napi_typeof(env, argv[0], &type); + + if (type != napi_string && type != napi_number) { + napi_throw_error(env, nullptr, + "long(x) should be called with single parameter containing a long number representation"); + return nullptr; + } + + napi_value value = argv[0]; + + napi_value cast; + napi_create_object(env, &cast); + MarkJsObject(env, cast, CastType::Long, value); + return cast; +} + +napi_value NumericCasts::MarkAsByteCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1) + + if (argc != 1) { + napi_throw_error(env, nullptr, "byte(x) should be called with single parameter"); + return nullptr; + } + + napi_valuetype type; + napi_typeof(env, argv[0], &type); + + if (type != napi_string && type != napi_number && !napi_util::is_number_object(env, argv[0]) && !napi_util::is_string_object(env, argv[0])) { + napi_throw_error(env, nullptr, + "byte(x) should be called with single parameter containing a byte number representation"); + return nullptr; + } + napi_value value; + if (type == napi_number) { + value = argv[0]; + } else { + napi_coerce_to_string(env, argv[0], &value); + } + + napi_value cast; + napi_create_object(env, &cast); + MarkJsObject(env, cast, CastType::Byte, value); + return cast; +} + +napi_value NumericCasts::MarkAsShortCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1) + + if (argc != 1) { + napi_throw_error(env, nullptr, "short(x) should be called with single parameter"); + return nullptr; + } + + napi_valuetype type; + napi_typeof(env, argv[0], &type); + + if (type != napi_string && type != napi_number && !napi_util::is_number_object(env, argv[0]) && !napi_util::is_string_object(env, argv[0])) { + napi_throw_error(env, nullptr, + "short(x) should be called with single parameter containing a byte number representation"); + return nullptr; + } + napi_value value; + if (type == napi_number) { + value = argv[0]; + } else { + napi_coerce_to_string(env, argv[0], &value); + } + + napi_value cast; + napi_create_object(env, &cast); + MarkJsObject(env, cast, CastType::Short, value); + return cast; +} + +napi_value NumericCasts::MarkAsCharCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1) + + if (argc != 1) { + napi_throw_error(env, nullptr, "char(x) should be called with single parameter"); + return nullptr; + } + + napi_valuetype type; + napi_typeof(env, argv[0], &type); + + if (type != napi_string) { + napi_throw_error(env, nullptr, + "char(x) should be called with single parameter containing a char representation"); + return nullptr; + } + + size_t str_len; + napi_get_value_string_utf8(env, argv[0], nullptr, 0, &str_len); + if (str_len != 1) { + napi_throw_error(env, nullptr, + "char(x) should be called with single parameter containing a single char"); + return nullptr; + } + + + napi_value cast; + napi_create_object(env, &cast); + MarkJsObject(env, cast, CastType::Char, argv[0]); + return cast; +} + +napi_value NumericCasts::MarkAsFloatCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1); + + if (argc != 1) { + napi_throw_error(env, nullptr, "float(x) should be called with single parameter"); + return nullptr; + } + + napi_valuetype type; + napi_typeof(env, argv[0], &type); + + if (type != napi_number) { + napi_throw_error(env, nullptr, + "float(x) should be called with single parameter containing a float number representation"); + return nullptr; + } + + napi_value value = argv[0]; + + napi_value cast; + napi_create_object(env, &cast); + MarkJsObject(env, cast, CastType::Float, value); + return cast; +} + +napi_value NumericCasts::MarkAsDoubleCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1); + + if (argc != 1) { + napi_throw_error(env, nullptr, "double(x) should be called with single parameter"); + return nullptr; + } + + napi_valuetype type; + napi_typeof(env, argv[0], &type); + + if (type != napi_number) { + napi_throw_error(env, nullptr, + "double(x) should be called with single parameter containing a double number representation"); + return nullptr; + } + + napi_value value = argv[0]; + + napi_value cast; + napi_create_object(env, &cast); + MarkJsObject(env, cast, CastType::Double, value); + return cast; +} + +void +NumericCasts::MarkJsObject(napi_env env, napi_value object, CastType castType, napi_value value) { + napi_value type; + napi_create_int32(env, static_cast(castType), &type); + + napi_set_named_property(env, object, s_castMarker, type); + napi_set_named_property(env, object, "value", value); + +// DEBUG_WRITE("MarkJsObject: Marking js object with cast type: %d", castType); +} + +const char *NumericCasts::s_castMarker = "t::cast"; \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/conversion/NumericCasts.h b/NativeScript/ffi/jni/napi/conversion/NumericCasts.h new file mode 100644 index 000000000..27041e161 --- /dev/null +++ b/NativeScript/ffi/jni/napi/conversion/NumericCasts.h @@ -0,0 +1,75 @@ +#ifndef NUMERICCASTS_H_ +#define NUMERICCASTS_H_ + +#include "js_native_api.h" +#include "Runtime.h" +#include + +namespace tns { + enum class CastType { + None, + Char, + Byte, + Short, + Long, + Float, + Double + }; + + class NumericCasts { + public: + void CreateGlobalCastFunctions(napi_env env, napi_value globalObject); + + inline static CastType GetCastType(napi_env env, napi_value object) { + CastType ret = CastType::None; + +#ifdef USE_HOST_OBJECT + void *data; + napi_get_host_object_data(env, object, &data); + if (data != nullptr) return ret; +#endif + + napi_value hidden; + + bool hasProperty; + napi_has_named_property(env, object, s_castMarker, &hasProperty); + + if (hasProperty) { + napi_get_named_property(env, object, s_castMarker, &hidden); + int32_t castType; + napi_get_value_int32(env, hidden, &castType); + ret = static_cast(castType); + } + + return ret; + } + + inline static napi_value GetCastValue(napi_env env, napi_value object) { + napi_value value; + napi_get_named_property(env, object, "value", &value); + return value; + } + + static void MarkAsLong(napi_env env, napi_value object, napi_value value); + + private: + static napi_value MarkAsLongCallback(napi_env env, napi_callback_info info); + + static napi_value MarkAsByteCallback(napi_env env, napi_callback_info info); + + static napi_value MarkAsShortCallback(napi_env env, napi_callback_info info); + + static napi_value MarkAsCharCallback(napi_env env, napi_callback_info info); + + static napi_value MarkAsFloatCallback(napi_env env, napi_callback_info info); + + static napi_value MarkAsDoubleCallback(napi_env env, napi_callback_info info); + + static void + MarkJsObject(napi_env env, napi_value object, CastType castType, napi_value value); + + static const char *s_castMarker; + }; +} + +#endif /* NUMERICCASTS_H_ */ \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/exceptions/NativeScriptAssert.h b/NativeScript/ffi/jni/napi/exceptions/NativeScriptAssert.h new file mode 100644 index 000000000..0fe1ce5a8 --- /dev/null +++ b/NativeScript/ffi/jni/napi/exceptions/NativeScriptAssert.h @@ -0,0 +1,22 @@ +/* + * nativescriptassert.h + * + * Created on: 12.11.2013 + * Author: blagoev + */ + +#ifndef NATIVESCRIPTASSERT_H_ +#define NATIVESCRIPTASSERT_H_ + +#include + +namespace tns { +extern bool LogEnabled; + +#define DEBUG_WRITE(fmt, args...) if (tns::LogEnabled) __android_log_print(ANDROID_LOG_DEBUG, "TNS.Native", fmt, ##args) +//#define DEBUG_WRITE(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, "TNS.Native", fmt, ##args) +#define DEBUG_WRITE_FORCE(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, "TNS.Native", fmt, ##args) +#define DEBUG_WRITE_FATAL(fmt, args...) __android_log_print(ANDROID_LOG_FATAL, "TNS.Native", fmt, ##args) +} + +#endif /* NATIVESCRIPTASSERT_H_ */ diff --git a/NativeScript/ffi/jni/napi/exceptions/NativeScriptException.cpp b/NativeScript/ffi/jni/napi/exceptions/NativeScriptException.cpp new file mode 100644 index 000000000..df11c4901 --- /dev/null +++ b/NativeScript/ffi/jni/napi/exceptions/NativeScriptException.cpp @@ -0,0 +1,372 @@ +#include "Util.h" +#include "NativeScriptException.h" +#include "ArgConverter.h" +#include "NativeScriptAssert.h" +#include "Runtime.h" +#include + +using namespace std; +using namespace tns; + +NativeScriptException::NativeScriptException(JEnv& env) + : m_javascriptException(nullptr) { + jthrowable thrw = env.ExceptionOccurred(); + m_javaException = JniLocalRef(thrw); + env.ExceptionClear(); + DEBUG_WRITE("%s, %s", GetExceptionMessage(env, m_javaException).c_str(), GetExceptionStackTrace(env, m_javaException).c_str()); +} + +NativeScriptException::NativeScriptException(const string& message) + : m_javascriptException(nullptr), m_javaException(JniLocalRef()), m_message(message) { + + DEBUG_WRITE("%s", m_message.c_str()); +} + +NativeScriptException::NativeScriptException(const string& message, const string& stackTrace) + : m_javascriptException(nullptr), m_javaException(JniLocalRef()), m_message(message), m_stackTrace(stackTrace) { + + DEBUG_WRITE("%s, %s ", m_message.c_str(), m_stackTrace.c_str()); +} + +NativeScriptException::NativeScriptException(napi_env env, napi_value error, const string& message) + : m_javaException(JniLocalRef()) { + m_javascriptException = nullptr; + napi_create_reference(env, error, 1, &m_javascriptException); + m_message = GetErrorMessage(env, error, message); + m_stackTrace = GetErrorStackTrace(env, error); + m_fullMessage = GetFullMessage(env, error, m_message); +} + +void NativeScriptException::ReThrowToNapi(napi_env env) { + napi_value errObj; + + if (m_javascriptException != nullptr) { + napi_get_reference_value(env, m_javascriptException, &errObj); + if (napi_util::is_of_type(env, errObj, napi_object)) { + if (!m_fullMessage.empty()) { + napi_set_named_property(env, errObj, "fullMessage", ArgConverter::convertToJsString(env, m_fullMessage)); + } else if (!m_message.empty()) { + napi_set_named_property(env, errObj, "fullMessage", ArgConverter::convertToJsString(env, m_message)); + } + } + } else if (!m_fullMessage.empty()) { + napi_create_error(env, nullptr, ArgConverter::convertToJsString(env, m_fullMessage), &errObj); + } else if (!m_message.empty()) { + napi_create_error(env, nullptr, ArgConverter::convertToJsString(env, m_message), &errObj); + } else if (!m_javaException.IsNull()) { + errObj = WrapJavaToJsException(env); + } else { + napi_create_error(env, nullptr, ArgConverter::convertToJsString(env, "No javascript exception or message provided."), &errObj); + } + + napi_throw(env, errObj); + +// JSLeave +} + +void NativeScriptException::ReThrowToJava(napi_env env) { + if (env) { + NapiScope scope(env); + } + jthrowable ex = nullptr; + JEnv jEnv; + + if (!m_javaException.IsNull()) { + std::string excClassName; + if (env) { + auto objectManager = Runtime::GetRuntime(env)->GetObjectManager(); + excClassName = objectManager->GetClassName((jobject)m_javaException); + } + + if (excClassName == "com/tns/NativeScriptException") { + ex = m_javaException; + } else { + JniLocalRef msg(jEnv.NewStringUTF("Java Error!")); + JniLocalRef stack(jEnv.NewStringUTF("")); + ex = static_cast(jEnv.NewObject(NATIVESCRIPTEXCEPTION_CLASS, NATIVESCRIPTEXCEPTION_THROWABLE_CTOR_ID, (jstring)msg, (jstring)stack, (jobject)m_javaException)); + } + } else if (m_javascriptException != nullptr && env != nullptr) { + napi_value errObj; + napi_get_reference_value(env, m_javascriptException, &errObj); + if (napi_util::is_of_type(env, errObj, napi_object)) { + auto exObj = TryGetJavaThrowableObject(jEnv, env, errObj); + ex = (jthrowable)exObj.Move(); + } + + JniLocalRef msg(jEnv.NewStringUTF(m_message.c_str())); + JniLocalRef stackTrace(jEnv.NewStringUTF(m_stackTrace.c_str())); + + if (ex == nullptr) { + ex = static_cast(jEnv.NewObject(NATIVESCRIPTEXCEPTION_CLASS, NATIVESCRIPTEXCEPTION_JSVALUE_CTOR_ID, (jstring)msg, (jstring)stackTrace, reinterpret_cast(m_javascriptException))); + } else { + auto objectManager = Runtime::GetRuntime(env)->GetObjectManager(); + auto excClassName = objectManager->GetClassName(ex); + if (excClassName != "com/tns/NativeScriptException") { + ex = static_cast(jEnv.NewObject(NATIVESCRIPTEXCEPTION_CLASS, NATIVESCRIPTEXCEPTION_THROWABLE_CTOR_ID, (jstring)msg, (jstring)stackTrace, ex)); + } + } + } else if (!m_message.empty()) { + JniLocalRef msg(jEnv.NewStringUTF(m_message.c_str())); + JniLocalRef stackTrace(jEnv.NewStringUTF(m_stackTrace.c_str())); + ex = static_cast(jEnv.NewObject(NATIVESCRIPTEXCEPTION_CLASS, NATIVESCRIPTEXCEPTION_JSVALUE_CTOR_ID, (jstring)msg, (jstring)stackTrace, (jlong)0)); + } else { + JniLocalRef msg(jEnv.NewStringUTF("No java exception or message provided.")); + ex = static_cast(jEnv.NewObject(NATIVESCRIPTEXCEPTION_CLASS, NATIVESCRIPTEXCEPTION_JSVALUE_CTOR_ID, (jstring)msg, (jstring)nullptr, (jlong)0)); + } + jEnv.Throw(ex); +} + +void NativeScriptException::Init() { + JEnv jenv; + + RUNTIME_CLASS = jenv.FindClass("com/tns/Runtime"); + assert(RUNTIME_CLASS != nullptr); + + THROWABLE_CLASS = jenv.FindClass("java/lang/Throwable"); + assert(THROWABLE_CLASS != nullptr); + + NATIVESCRIPTEXCEPTION_CLASS = jenv.FindClass("com/tns/NativeScriptException"); + assert(NATIVESCRIPTEXCEPTION_CLASS != nullptr); + + NATIVESCRIPTEXCEPTION_JSVALUE_CTOR_ID = jenv.GetMethodID(NATIVESCRIPTEXCEPTION_CLASS, "", "(Ljava/lang/String;Ljava/lang/String;J)V"); + assert(NATIVESCRIPTEXCEPTION_JSVALUE_CTOR_ID != nullptr); + + NATIVESCRIPTEXCEPTION_THROWABLE_CTOR_ID = jenv.GetMethodID(NATIVESCRIPTEXCEPTION_CLASS, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V"); + assert(NATIVESCRIPTEXCEPTION_THROWABLE_CTOR_ID != nullptr); + + NATIVESCRIPTEXCEPTION_GET_STACK_TRACE_AS_STRING_METHOD_ID = jenv.GetStaticMethodID(NATIVESCRIPTEXCEPTION_CLASS, "getStackTraceAsString", "(Ljava/lang/Throwable;)Ljava/lang/String;"); + assert(NATIVESCRIPTEXCEPTION_GET_STACK_TRACE_AS_STRING_METHOD_ID != nullptr); + + NATIVESCRIPTEXCEPTION_GET_MESSAGE_METHOD_ID = jenv.GetStaticMethodID(NATIVESCRIPTEXCEPTION_CLASS, "getMessage", "(Ljava/lang/Throwable;)Ljava/lang/String;"); + assert(NATIVESCRIPTEXCEPTION_GET_MESSAGE_METHOD_ID != nullptr); +} + +// ON N-API UNCAUGHT EXCEPTION +void NativeScriptException::OnUncaughtError(napi_env env, napi_value error) { + string errorMessage = GetErrorMessage(env, error); + string stackTrace = GetErrorStackTrace(env, error); + + NativeScriptException e(errorMessage, stackTrace); + e.ReThrowToJava(env); +} + +void NativeScriptException::CallJsFuncWithErr(napi_env env, napi_value errObj, bool isDiscarded) { + napi_value global; + napi_get_global(env, &global); + + napi_value handler = nullptr; + if (isDiscarded) { + napi_get_named_property(env, global, "__onDiscardedError", &handler); + } else { + napi_get_named_property(env, global, "__onUncaughtError", &handler); + } + + if (napi_util::is_of_type(env, handler, napi_function)) { + napi_value result; + napi_call_function(env, global, handler, 1, &errObj, &result); + } +} + +napi_value NativeScriptException::WrapJavaToJsException(napi_env env) { + napi_value errObj; + + JEnv jenv; + + auto objectManager = Runtime::GetRuntime(env)->GetObjectManager(); + + string excClassName = objectManager->GetClassName((jobject)m_javaException); + if (excClassName == "com/tns/NativeScriptException") { + jfieldID fieldID = jenv.GetFieldID(jenv.GetObjectClass(m_javaException), "jsValueAddress", "J"); + jlong addr = jenv.GetLongField(m_javaException, fieldID); + + if (addr != 0) { + auto pv = reinterpret_cast(addr); + napi_get_reference_value(env, pv, &errObj); + napi_delete_reference(env, pv); + } else { + errObj = GetJavaExceptionFromEnv(env, m_javaException, jenv); + } + } else { + errObj = GetJavaExceptionFromEnv(env, m_javaException, jenv); + } + + return errObj; +} + +napi_value NativeScriptException::GetJavaExceptionFromEnv(napi_env env, const JniLocalRef& exc, JEnv& jenv) { + auto errMsg = GetExceptionMessage(jenv, exc); + auto stackTrace = GetExceptionStackTrace(jenv, exc); + DEBUG_WRITE("Error during java interop errorMessage: %s\n stackTrace:\n %s", errMsg.c_str(), stackTrace.c_str()); + + auto objectManager = Runtime::GetRuntime(env)->GetObjectManager(); + + napi_value msg = ArgConverter::convertToJsString(env, errMsg); + napi_value errObj; + napi_value code = ArgConverter::convertToJsString(env, "0", 1); + napi_create_error(env, code, msg, &errObj); + + jint javaObjectID = objectManager->GetOrCreateObjectId((jobject)exc); + auto nativeExceptionObject = objectManager->GetJsObjectByJavaObject(javaObjectID); + + if (napi_util::is_null_or_undefined(env, nativeExceptionObject)) { + string className = objectManager->GetClassName((jobject)exc); + nativeExceptionObject = objectManager->CreateJSWrapper(javaObjectID, className); + } + + napi_set_named_property(env, errObj, "nativeException", nativeExceptionObject); + + string jsStackTraceMessage = GetErrorStackTrace(env, errObj); + napi_set_named_property(env, errObj, "stack", ArgConverter::convertToJsString(env, jsStackTraceMessage)); + napi_set_named_property(env, errObj, "stackTrace", ArgConverter::convertToJsString(env, jsStackTraceMessage + stackTrace) ); + + return errObj; +} + +string NativeScriptException::GetFullMessage(napi_env env, napi_value error, const string& jsExceptionMessage) { + bool isError; + napi_is_error(env, error, &isError); + if (!isError) { + return jsExceptionMessage; + } + + stringstream ss; + ss << jsExceptionMessage; + + string stackTraceMessage = GetErrorStackTrace(env, error); + + ss << endl << "StackTrace: " << endl << stackTraceMessage << endl; + + string loggedMessage = ss.str(); + + PrintErrorMessage(loggedMessage); + + return loggedMessage; +} + +JniLocalRef NativeScriptException::TryGetJavaThrowableObject(JEnv& env, napi_env napiEnv, napi_value jsObj) { + JniLocalRef javaThrowableObject; + + auto objectManager = Runtime::GetRuntime(napiEnv)->GetObjectManager(); + + auto javaObj = objectManager->GetJavaObjectByJsObject(jsObj); + JniLocalRef objClass; + + if (!javaObj.IsNull()) { + objClass = JniLocalRef(env.GetObjectClass(javaObj)); + } else { + napi_value nativeEx; + napi_get_named_property(napiEnv, jsObj, "nativeException", &nativeEx); + if (napi_util::is_object(napiEnv, nativeEx)) { + javaObj = objectManager->GetJavaObjectByJsObject(nativeEx); + objClass = JniLocalRef(env.GetObjectClass(javaObj)); + } + } + + auto isThrowable = !objClass.IsNull() ? env.IsAssignableFrom(objClass, THROWABLE_CLASS) : JNI_FALSE; + + if (isThrowable == JNI_TRUE) { + javaThrowableObject = JniLocalRef(env.NewLocalRef(javaObj)); + } + + return javaThrowableObject; +} + +void NativeScriptException::PrintErrorMessage(const string& errorMessage) { + stringstream ss(errorMessage); + string line; + while (getline(ss, line, '\n')) { + DEBUG_WRITE("%s", line.c_str()); + } +} + +string NativeScriptException::GetErrorMessage(napi_env env, napi_value error, const string& prependMessage) { + bool isError; + napi_is_error(env, error, &isError); + + if (!isError) { + napi_value err; + napi_coerce_to_string(env, error, &err); + return napi_util::get_string_value(env, err); + } + + napi_value message; + napi_get_named_property(env, error, "message", &message); + + string mes = ArgConverter::ConvertToString(env, message); + + stringstream ss; + + if (!prependMessage.empty()) { + ss << prependMessage << endl; + } + + string errMessage; + bool hasFullErrorMessage = false; + napi_value fullMessage; + napi_get_named_property(env, error, "fullMessage", &fullMessage); + if (napi_util::is_of_type(env, fullMessage, napi_string)) { + hasFullErrorMessage = true; + errMessage = ArgConverter::ConvertToString(env, fullMessage); + ss << errMessage; + } + + if (!mes.empty()) { + if (hasFullErrorMessage) { + ss << endl; + } + ss << mes; + } + + return ss.str(); +} + +string NativeScriptException::GetErrorStackTrace(napi_env env, napi_value error) { + stringstream ss; + + bool isError; + napi_is_error(env, error, &isError); + if (!isError) return ""; + + napi_value stack; + napi_get_named_property(env, error, "stack", &stack); + + + string stackStr = ArgConverter::ConvertToString(env, stack); + ss << stackStr; + + return ss.str(); +} + +string NativeScriptException::GetExceptionMessage(JEnv& env, jthrowable exception) { + string errMsg; + JniLocalRef msg(env.CallStaticObjectMethod(NATIVESCRIPTEXCEPTION_CLASS, NATIVESCRIPTEXCEPTION_GET_MESSAGE_METHOD_ID, exception)); + + const char* msgStr = env.GetStringUTFChars(msg, nullptr); + + errMsg.append(msgStr); + + env.ReleaseStringUTFChars(msg, msgStr); + + return errMsg; +} + +string NativeScriptException::GetExceptionStackTrace(JEnv& env, jthrowable exception) { + string errStackTrace; + JniLocalRef msg(env.CallStaticObjectMethod(NATIVESCRIPTEXCEPTION_CLASS, NATIVESCRIPTEXCEPTION_GET_STACK_TRACE_AS_STRING_METHOD_ID, exception)); + + const char* msgStr = env.GetStringUTFChars(msg, nullptr); + + errStackTrace.append(msgStr); + + env.ReleaseStringUTFChars(msg, msgStr); + + return errStackTrace; +} + +jclass NativeScriptException::RUNTIME_CLASS = nullptr; +jclass NativeScriptException::THROWABLE_CLASS = nullptr; +jclass NativeScriptException::NATIVESCRIPTEXCEPTION_CLASS = nullptr; +jmethodID NativeScriptException::NATIVESCRIPTEXCEPTION_JSVALUE_CTOR_ID = nullptr; +jmethodID NativeScriptException::NATIVESCRIPTEXCEPTION_THROWABLE_CTOR_ID = nullptr; +jmethodID NativeScriptException::NATIVESCRIPTEXCEPTION_GET_MESSAGE_METHOD_ID = nullptr; +jmethodID NativeScriptException::NATIVESCRIPTEXCEPTION_GET_STACK_TRACE_AS_STRING_METHOD_ID = nullptr; \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/exceptions/NativeScriptException.h b/NativeScript/ffi/jni/napi/exceptions/NativeScriptException.h new file mode 100644 index 000000000..e7f82e22e --- /dev/null +++ b/NativeScript/ffi/jni/napi/exceptions/NativeScriptException.h @@ -0,0 +1,106 @@ +#ifndef NATIVESCRIPTEXCEPTION_H_ +#define NATIVESCRIPTEXCEPTION_H_ + +#include "js_native_api.h" +#include "JEnv.h" +#include "JniLocalRef.h" +#include "ObjectManager.h" + +namespace tns { +class NativeScriptException { + public: + /* + * Generates a NativeScriptException with java error from environment + */ + NativeScriptException(JEnv& env); + + /* + * Generates a NativeScriptException with given message + */ + NativeScriptException(const std::string& message); + + /* + * Generates a NativeScriptException with given message and stackTrace + */ + NativeScriptException(const std::string& message, const std::string& stackTrace); + + /* + * Generates a NativeScriptException with javascript error from napi_env and a prepend message if any + */ + NativeScriptException(napi_env env, napi_value error, const std::string& message = ""); + + void ReThrowToNapi(napi_env env); + void ReThrowToJava(napi_env env); + + static void Init(); + + /* + * This handler is attached to Node-API to handle uncaught javascript exceptions. + */ + static void OnUncaughtError(napi_env env, napi_value error); + + /* + * Calls the global "__onUncaughtError" or "__onDiscardedError" if such is provided + */ + static void CallJsFuncWithErr(napi_env env, napi_value errObj, bool isDiscarded); + + private: + /* + * Try to get native exception or NativeScriptException from js object + */ + JniLocalRef TryGetJavaThrowableObject(JEnv& env, napi_env napiEnv, napi_value jsObj); + + /* + * Gets java exception message from jthrowable + */ + std::string GetExceptionMessage(JEnv& env, jthrowable exception); + + /* + * Gets java exception stack trace from jthrowable + */ + std::string GetExceptionStackTrace(JEnv& env, jthrowable exception); + + /* + * Gets the member m_javaException, wraps it and creates a javascript error object from it + */ + napi_value WrapJavaToJsException(napi_env env); + + /* + * Gets all the information from a java exception and puts it in a javascript error object + */ + napi_value GetJavaExceptionFromEnv(napi_env env, const JniLocalRef& exc, JEnv& jenv); + + /* + * Gets all the information from a js message and an js error object and puts it in a string + */ + static std::string GetErrorMessage(napi_env env, napi_value error, const std::string& prependMessage = ""); + + /* + * Generates string stack trace from js StackTrace + */ + static std::string GetErrorStackTrace(napi_env env, napi_value stackTrace); + + /* + * Adds a prepend message to the normal message process + */ + std::string GetFullMessage(napi_env env, napi_value error, const std::string& jsExceptionMessage); + + napi_ref m_javascriptException; + JniLocalRef m_javaException; + std::string m_message; + std::string m_stackTrace; + std::string m_fullMessage; + + static jclass RUNTIME_CLASS; + static jclass THROWABLE_CLASS; + static jclass NATIVESCRIPTEXCEPTION_CLASS; + static jmethodID NATIVESCRIPTEXCEPTION_JSVALUE_CTOR_ID; + static jmethodID NATIVESCRIPTEXCEPTION_THROWABLE_CTOR_ID; + static jmethodID NATIVESCRIPTEXCEPTION_GET_MESSAGE_METHOD_ID; + static jmethodID NATIVESCRIPTEXCEPTION_GET_STACK_TRACE_AS_STRING_METHOD_ID; + + static void PrintErrorMessage(const std::string& errorMessage); +}; +} + +#endif /* NATIVESCRIPTEXCEPTION_H_ */ \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/global/GlobalHelpers.cpp b/NativeScript/ffi/jni/napi/global/GlobalHelpers.cpp new file mode 100644 index 000000000..c81812709 --- /dev/null +++ b/NativeScript/ffi/jni/napi/global/GlobalHelpers.cpp @@ -0,0 +1,195 @@ +#include "GlobalHelpers.h" +#include "ArgConverter.h" +#include "CallbackHandlers.h" +#include "JEnv.h" +#include "NativeScriptException.h" +#include +#include "robin_hood.h" +#include "Util.h" +#include + +using namespace std; + +static robin_hood::unordered_map envToPersistentSmartJSONStringify = robin_hood::unordered_map(); + +napi_value GetSmartJSONStringifyFunction(napi_env env) { + auto it = envToPersistentSmartJSONStringify.find(env); + if (it != envToPersistentSmartJSONStringify.end()) { + napi_value smartStringifyFunction; + napi_get_reference_value(env, it->second, &smartStringifyFunction); + return smartStringifyFunction; + } + + const char * smartStringifyFunctionScript = R"( + (function () { + function smartStringify(object, handleCirculars) { + if (!handleCirculars) { + return JSON.stringify(object, null, 2); + } + + const seen = []; + var replacer = function (key, value) { + if (value != null && typeof value == "object") { + if (seen.indexOf(value) >= 0) { + if (key) { + return "[Circular]"; + } + return; + } + seen.push(value); + } + return value; + }; + return JSON.stringify(object, replacer, 2); + } + return smartStringify; +})(); +)"; + + + napi_value source; + napi_create_string_utf8(env, smartStringifyFunctionScript, strlen(smartStringifyFunctionScript), &source); + + napi_value global; + napi_get_global(env, &global); + + napi_value result; + napi_status status = js_execute_script(env, source, "", &result); + if (status != napi_ok) { + return nullptr; + } + + if (!napi_util::is_of_type(env, result, napi_function)) { + return nullptr; + } + + napi_ref smartStringifyPersistentFunction; + napi_create_reference(env, result, 1, &smartStringifyPersistentFunction); + + envToPersistentSmartJSONStringify.emplace(env, smartStringifyPersistentFunction); + + return result; +} + + + +std::string tns::JsonStringifyObject(napi_env env, napi_value value, bool handleCircularReferences) { + if (value == nullptr) { + return ""; + } + + napi_value smartJSONStringifyFunction = GetSmartJSONStringifyFunction(env); + std::string result; + if (smartJSONStringifyFunction != nullptr) { + napi_value resultValue; + napi_value args[2]; + args[0] = value; + args[1] = handleCircularReferences ? napi_util::get_true(env) : napi_util::get_false(env); + napi_status status = napi_call_function(env, napi_util::global(env), smartJSONStringifyFunction, 2, args, &resultValue); + if (status != napi_ok) { + napi_value exception; + napi_get_and_clear_last_exception(env, &exception); + if (!napi_util::is_null_or_undefined(env, exception)) { + throw NativeScriptException(env, exception, "Error converting object to json"); + } else { + throw NativeScriptException("Error converting object to json"); + } + } + result = ArgConverter::ConvertToString(env, resultValue); + } + + return result; +} + +napi_value tns::JsonParseString(napi_env env, const std::string& value) { + napi_value global; + napi_value json; + napi_value parse; + + napi_get_global(env, &global); + napi_get_named_property(env, global, "JSON", &json); + napi_get_named_property(env, json, "parse", &parse); + + napi_value args[1]; + args[0] = ArgConverter::convertToJsString(env, value); + napi_value result; + napi_status status = napi_call_function(env, json, parse, 1, args, &result); + if (status != napi_ok) { + napi_value exception; + napi_get_and_clear_last_exception(env, &exception); + if (!napi_util::is_null_or_undefined(env, exception)) { + throw NativeScriptException(env, exception, "Error converting json string to object"); + } else { + throw NativeScriptException("Error converting json string to object"); + } + } + return result; +} + +std::vector tns::BuildStacktraceFrames(napi_env env, napi_value error, int size) { + std::vector frames; + napi_value stack; + if (error != nullptr) { + napi_get_named_property(env, error, "stack", &stack); + } else { +#ifndef __HERMES__ + napi_value err; + napi_value msg; + napi_create_string_utf8(env, "Error", strlen("Error"), &msg); + #ifdef __PRIMJS__ + napi_value error_ctor; + napi_get_named_property(env, napi_util::global(env), "Error", &error_ctor); + + napi_new_instance(env, error_ctor, 1, &msg, &err); + #else + napi_create_error(env, msg, msg, &err); + #endif + napi_get_named_property(env, err, "stack", &stack); +#else + napi_value global; + napi_get_global(env, &global); + napi_value getErrorStack; + napi_get_named_property(env, global, "getErrorStack", &getErrorStack); + napi_call_function(env, global, getErrorStack, 0, nullptr, &stack); +#endif + } + + if (napi_util::is_null_or_undefined(env, stack)) return frames; + + string stackTrace = napi_util::get_string_value(env, stack); + vector stackLines; + Util::SplitString(stackTrace, "\n", stackLines); + + int current = 0; + int count = 0; + for (auto &frame : stackLines) { + count++; +#ifdef __HERMES__ + if (error == nullptr && count < 3) continue; +#endif + +//#ifdef __JSC__ + regex frameRegex(R"((file:.*):(\d+):(\d+))"); +//#else +// regex frameRegex(R"(\((.*):(\d+):(\d+)\))"); +//#endif + smatch match; + if (regex_search(frame, match, frameRegex)) { + current++; + frames.emplace_back(stoi(match[2].str()), + stoi(match[3].str()), + match[1].str(), + frame); + if (current == size) break; + } + } + return frames; +} + +void tns::GlobalHelpers::onDisposeEnv(napi_env env) { + auto found = envToPersistentSmartJSONStringify.find(env); + if (found != envToPersistentSmartJSONStringify.end()) { + napi_delete_reference(env, found->second); + } + envToPersistentSmartJSONStringify.erase(env); +} \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/global/GlobalHelpers.h b/NativeScript/ffi/jni/napi/global/GlobalHelpers.h new file mode 100644 index 000000000..f2d9ce809 --- /dev/null +++ b/NativeScript/ffi/jni/napi/global/GlobalHelpers.h @@ -0,0 +1,34 @@ +#ifndef NAPI_GLOBALHELPERS_H_ +#define NAPI_GLOBALHELPERS_H_ + +#include "jni.h" +#include "js_native_api.h" +#include +#include +#include + +namespace tns { +std::string JsonStringifyObject(napi_env env, napi_value value, bool handleCircularReferences = true); + +napi_value JsonParseString(napi_env env, const std::string& value); + +struct JsStacktraceFrame { + JsStacktraceFrame(): line(0), col(0) {} + JsStacktraceFrame( + int _line, int _col, std::string _filename, std::string _text + ): line(_line), col(_col), filename(std::move(_filename)), text(std::move(_text)) {} + + int line; + int col; + std::string filename; + std::string text; +}; + +std::vector BuildStacktraceFrames(napi_env env, napi_value error, int size); + +namespace GlobalHelpers { + void onDisposeEnv(napi_env env); +} +} + +#endif /* NAPI_GLOBALHELPERS_H_ */ \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/jni/DesugaredInterfaceCompanionClassNameResolver.cpp b/NativeScript/ffi/jni/napi/jni/DesugaredInterfaceCompanionClassNameResolver.cpp new file mode 100644 index 000000000..fdbe50d60 --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/DesugaredInterfaceCompanionClassNameResolver.cpp @@ -0,0 +1,11 @@ +#include "DesugaredInterfaceCompanionClassNameResolver.h" + +std::string DesugaredInterfaceCompanionClassNameResolver::resolveD8InterfaceCompanionClassName( + const std::string& interfaceName) { + return interfaceName + D8_COMPANION_CLASS_SUFFIX; +} + +std::string DesugaredInterfaceCompanionClassNameResolver::resolveBazelInterfaceCompanionClassName( + const std::string& interfaceName) { + return interfaceName + BAZEL_COMPANION_CLASS_SUFFIX; +} diff --git a/NativeScript/ffi/jni/napi/jni/DesugaredInterfaceCompanionClassNameResolver.h b/NativeScript/ffi/jni/napi/jni/DesugaredInterfaceCompanionClassNameResolver.h new file mode 100644 index 000000000..052dc8952 --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/DesugaredInterfaceCompanionClassNameResolver.h @@ -0,0 +1,22 @@ +#ifndef TEST_APP_DESUGAREDINTERFACECOMPANIONCLASSNAMERESOLVER_H +#define TEST_APP_DESUGAREDINTERFACECOMPANIONCLASSNAMERESOLVER_H + + +#include + +class DesugaredInterfaceCompanionClassNameResolver { + +public: + std::string resolveD8InterfaceCompanionClassName(const std::string& interfaceName); + + std::string resolveBazelInterfaceCompanionClassName(const std::string& interfaceName); + +private: + const std::string BAZEL_COMPANION_CLASS_SUFFIX = "$$CC"; + const std::string D8_COMPANION_CLASS_SUFFIX = "$-CC"; + + +}; + + +#endif //TEST_APP_DESUGAREDINTERFACECOMPANIONCLASSNAMERESOLVER_H diff --git a/NativeScript/ffi/jni/napi/jni/DirectBuffer.cpp b/NativeScript/ffi/jni/napi/jni/DirectBuffer.cpp new file mode 100644 index 000000000..1b3e6f8a3 --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/DirectBuffer.cpp @@ -0,0 +1,56 @@ +#include "DirectBuffer.h" +#include "JniLocalRef.h" + +using namespace tns; + +DirectBuffer::DirectBuffer(uint32_t length) { + m_length = length; + + m_data = new int[m_length]; + + m_end = m_data + m_length; + + Reset(); + + int capacity = m_length * sizeof(int); + + JEnv env; + JniLocalRef buff(env.NewDirectByteBuffer(m_data, capacity)); + + m_buff = env.NewGlobalRef(buff); +} + +DirectBuffer::operator jobject() const { + return m_buff; +} + +int* DirectBuffer::GetData() const { + return m_data; +} + +int DirectBuffer::Length() const { + return m_length; +} + +int DirectBuffer::Size() const { + return m_pos - m_data; +} + +void DirectBuffer::Reset() { + m_pos = m_data; +} + +bool DirectBuffer::Write(int value) { + bool canWrite = m_pos < m_end; + if (canWrite) { + int bigEndianInt = __builtin_bswap32(value); + *(m_pos++) = bigEndianInt; + } + return canWrite; +} + +DirectBuffer::~DirectBuffer() { + JEnv env; + env.DeleteGlobalRef(m_buff); + delete[] m_data; +} diff --git a/NativeScript/ffi/jni/napi/jni/DirectBuffer.h b/NativeScript/ffi/jni/napi/jni/DirectBuffer.h new file mode 100644 index 000000000..4de8a8a63 --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/DirectBuffer.h @@ -0,0 +1,30 @@ +#ifndef DIRECTBUFFER_H_ +#define DIRECTBUFFER_H_ + +#include "JEnv.h" + +namespace tns { +class DirectBuffer { + public: + DirectBuffer(uint32_t capacity = 65536); + ~DirectBuffer(); + + operator jobject() const; + + int* GetData() const; + int Length() const; + int Size() const; + + void Reset(); + bool Write(int value); + + private: + jobject m_buff; + int* m_data; + jlong m_length; + int* m_pos; + int* m_end; +}; +} + +#endif /* DIRECTBUFFER_H_ */ diff --git a/NativeScript/ffi/jni/napi/jni/File.cpp b/NativeScript/ffi/jni/napi/jni/File.cpp new file mode 100644 index 000000000..0dad918da --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/File.cpp @@ -0,0 +1,108 @@ +/* + * File.cpp + * + * Created on: Jun 24, 2015 + * Author: gatanasov + */ + +#include "File.h" +#include +#include +#include +#include + +using namespace std; + +namespace tns { + +string File::ReadText(const string& filePath) { + int len; + bool isNew; + const char* content = ReadText(filePath, len, isNew); + + string s(content, len); + + if (isNew) { + delete[] content; + } + + return s; +} + +void* File::ReadBinary(const string& filePath, int& length) { + length = 0; + + auto file = fopen(filePath.c_str(), READ_BINARY); + if (!file) { + return nullptr; + } + + fseek(file, 0, SEEK_END); + length = ftell(file); + rewind(file); + + uint8_t* data = new uint8_t[length]; + fread(data, sizeof(uint8_t), length, file); + fclose(file); + + return data; +} + +bool File::WriteBinary(const string& filePath, const void* data, int length) { + auto file = fopen(filePath.c_str(), WRITE_BINARY); + if (!file) { + return false; + } + + auto writtenBytes = fwrite(data, sizeof(uint8_t), length, file); + fclose(file); + + return writtenBytes == length; +} + +const char* File::ReadText(const string& filePath, int& charLength, bool& isNew) { + FILE* file = fopen(filePath.c_str(), "rb"); + fseek(file, 0, SEEK_END); + + charLength = ftell(file); + isNew = charLength > BUFFER_SIZE; + + rewind(file); + + if (isNew) { + char* newBuffer = new char[charLength]; + fread(newBuffer, 1, charLength, file); + fclose(file); + + return newBuffer; + } + + fread(Buffer, 1, charLength, file); + fclose(file); + + return Buffer; +} + +std::unique_ptr File::ReadFile(const std::string &filePath, int &length, int extraBuffer) { + FILE *file = fopen(filePath.c_str(), "rb"); + if (!file) { + std::stringstream ss; + ss << "metadata file (" << filePath << ") couldn't be opened! (Error: " << errno << ") "; +// throw NativeScriptException(ss.str()); + } + + fseek(file, 0, SEEK_END); + length = ftell(file); + std::unique_ptr buffer(new char[length + extraBuffer]); + rewind(file); + fread(buffer.get(), 1, length, file); + fclose(file); + + return buffer; + } + +char* File::Buffer = new char[BUFFER_SIZE]; + +const char* File::WRITE_BINARY = "wb"; +const char* File::READ_BINARY = "rb"; +} diff --git a/NativeScript/ffi/jni/napi/jni/File.h b/NativeScript/ffi/jni/napi/jni/File.h new file mode 100644 index 000000000..de9509282 --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/File.h @@ -0,0 +1,29 @@ +/* + * File.h + * + * Created on: Jun 24, 2015 + * Author: gatanasov + */ + +#ifndef JNI_FILE_H_ +#define JNI_FILE_H_ + +#include + +namespace tns { +class File { + public: + static const char* ReadText(const std::string& filePath, int& length, bool& isNew); + static std::string ReadText(const std::string& filePath); + static bool WriteBinary(const std::string& filePath, const void* inData, int length); + static void* ReadBinary(const std::string& filePath, int& length); + static std::unique_ptr ReadFile(const std::string &filePath, int &length, int extraBuffer = 0); +private: + static const int BUFFER_SIZE = 1024 * 1024; + static char* Buffer; + static const char* WRITE_BINARY; + static const char* READ_BINARY; +}; +} + +#endif /* JNI_FILE_H_ */ diff --git a/NativeScript/ffi/jni/napi/jni/JEnv.cpp b/NativeScript/ffi/jni/napi/jni/JEnv.cpp new file mode 100644 index 000000000..190b2817a --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/JEnv.cpp @@ -0,0 +1,901 @@ +#include "JEnv.h" +#include +#include "Util.h" +#include "DesugaredInterfaceCompanionClassNameResolver.h" +#include "NativeScriptException.h" + +using namespace tns; +using namespace std; + +JEnv::JEnv() + : m_env(nullptr) { + JNIEnv *env = nullptr; + jint ret = s_jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + + if ((ret != JNI_OK) || (env == nullptr)) { + ret = s_jvm->AttachCurrentThread(&env, nullptr); + assert(ret == JNI_OK); + assert(env != nullptr); + } + + m_env = env; +} + +JEnv::JEnv(JNIEnv *jniEnv) { + jint ret = s_jvm->GetEnv(reinterpret_cast(&jniEnv), JNI_VERSION_1_6); + + if ((ret != JNI_OK) || (jniEnv == nullptr)) { + ret = s_jvm->AttachCurrentThread(&jniEnv, nullptr); + assert(ret == JNI_OK); + assert(jniEnv != nullptr); + } + + m_env = jniEnv; +} + +JEnv::~JEnv() { +} + +JEnv::operator JNIEnv* () const { + return m_env; +} + +jmethodID JEnv::GetMethodID(jclass clazz, const string &name, const string &sig) { + jmethodID mid = m_env->GetMethodID(clazz, name.c_str(), sig.c_str()); + CheckForJavaException(); + return mid; +} + +jmethodID JEnv::GetStaticMethodID(jclass clazz, const string &name, const string &sig) { + jmethodID mid = m_env->GetStaticMethodID(clazz, name.c_str(), sig.c_str()); + CheckForJavaException(); + return mid; +} + +jfieldID JEnv::GetFieldID(jclass clazz, const string &name, const string &sig) { + jfieldID fid = m_env->GetFieldID(clazz, name.c_str(), sig.c_str()); + CheckForJavaException(); + return fid; +} + +jfieldID JEnv::GetStaticFieldID(jclass clazz, const string &name, const string &sig) { + jfieldID fid = m_env->GetStaticFieldID(clazz, name.c_str(), sig.c_str()); + CheckForJavaException(); + return fid; +} + +void JEnv::CallStaticVoidMethodA(jclass clazz, jmethodID methodID, jvalue *args) { + m_env->CallStaticVoidMethodA(clazz, methodID, args); + CheckForJavaException(); +} + +void JEnv::CallNonvirtualVoidMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { + m_env->CallNonvirtualVoidMethodA(obj, clazz, methodID, args); + CheckForJavaException(); +} + +void JEnv::CallVoidMethodA(jobject obj, jmethodID methodID, jvalue *args) { + m_env->CallVoidMethodA(obj, methodID, args); + CheckForJavaException(); +} + +jboolean JEnv::CallStaticBooleanMethodA(jclass clazz, jmethodID methodID, jvalue *args) { + jboolean jbl = m_env->CallStaticBooleanMethodA(clazz, methodID, args); + CheckForJavaException(); + return jbl; +} + +jboolean +JEnv::CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { + jboolean jbl = m_env->CallNonvirtualBooleanMethodA(obj, clazz, methodID, args); + CheckForJavaException(); + return jbl; +} + +jboolean JEnv::CallBooleanMethodA(jobject obj, jmethodID methodID, jvalue *args) { + jboolean jbl = m_env->CallBooleanMethodA(obj, methodID, args); + CheckForJavaException(); + return jbl; +} + +jbyte JEnv::CallStaticByteMethodA(jclass clazz, jmethodID methodID, jvalue *args) { + jbyte jbt = m_env->CallStaticByteMethodA(clazz, methodID, args); + CheckForJavaException(); + return jbt; +} + +jbyte JEnv::CallNonvirtualByteMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { + jbyte jbt = m_env->CallNonvirtualByteMethodA(obj, clazz, methodID, args); + CheckForJavaException(); + return jbt; +} + +jbyte JEnv::CallByteMethodA(jobject obj, jmethodID methodID, jvalue *args) { + jbyte jbt = m_env->CallByteMethodA(obj, methodID, args); + CheckForJavaException(); + return jbt; +} + +jchar JEnv::CallStaticCharMethodA(jclass clazz, jmethodID methodID, jvalue *args) { + jchar jch = m_env->CallStaticCharMethodA(clazz, methodID, args); + CheckForJavaException(); + return jch; +} + +jchar JEnv::CallNonvirtualCharMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { + jchar jch = m_env->CallNonvirtualCharMethodA(obj, clazz, methodID, args); + CheckForJavaException(); + return jch; +} + +jchar JEnv::CallCharMethodA(jobject obj, jmethodID methodID, jvalue *args) { + jchar jch = m_env->CallCharMethodA(obj, methodID, args); + CheckForJavaException(); + return jch; +} + +jshort JEnv::CallStaticShortMethodA(jclass clazz, jmethodID methodID, jvalue *args) { + jshort jsh = m_env->CallStaticShortMethodA(clazz, methodID, args); + CheckForJavaException(); + return jsh; + +} + +jshort +JEnv::CallNonvirtualShortMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { + jshort jsh = m_env->CallNonvirtualShortMethodA(obj, clazz, methodID, args); + CheckForJavaException(); + return jsh; +} + +jshort JEnv::CallShortMethodA(jobject obj, jmethodID methodID, jvalue *args) { + jshort jsh = m_env->CallShortMethodA(obj, methodID, args); + CheckForJavaException(); + return jsh; +} + +jint JEnv::CallStaticIntMethodA(jclass clazz, jmethodID methodID, jvalue *args) { + jint ji = m_env->CallStaticIntMethodA(clazz, methodID, args); + CheckForJavaException(); + return ji; + +} + +jint JEnv::CallNonvirtualIntMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { + jint ji = m_env->CallNonvirtualIntMethodA(obj, clazz, methodID, args); + CheckForJavaException(); + return ji; +} + +jint JEnv::CallIntMethodA(jobject obj, jmethodID methodID, jvalue *args) { + jint ji = m_env->CallIntMethodA(obj, methodID, args); + CheckForJavaException(); + return ji; +} + +jlong JEnv::CallStaticLongMethodA(jclass clazz, jmethodID methodID, jvalue *args) { + jlong jl = m_env->CallStaticLongMethodA(clazz, methodID, args); + CheckForJavaException(); + return jl; +} + +jlong JEnv::CallNonvirtualLongMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { + jlong jl = m_env->CallNonvirtualLongMethodA(obj, clazz, methodID, args); + CheckForJavaException(); + return jl; +} + +jlong JEnv::CallLongMethodA(jobject obj, jmethodID methodID, jvalue *args) { + jlong jl = m_env->CallLongMethodA(obj, methodID, args); + CheckForJavaException(); + return jl; +} + +jfloat JEnv::CallStaticFloatMethodA(jclass clazz, jmethodID methodID, jvalue *args) { + jfloat jfl = m_env->CallStaticFloatMethodA(clazz, methodID, args); + CheckForJavaException(); + return jfl; +} + +jfloat +JEnv::CallNonvirtualFloatMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { + jfloat jfl = m_env->CallNonvirtualFloatMethodA(obj, clazz, methodID, args); + CheckForJavaException(); + return jfl; +} + +jfloat JEnv::CallFloatMethodA(jobject obj, jmethodID methodID, jvalue *args) { + jfloat jfl = m_env->CallFloatMethodA(obj, methodID, args); + CheckForJavaException(); + return jfl; +} + +jdouble JEnv::CallStaticDoubleMethodA(jclass clazz, jmethodID methodID, jvalue *args) { + jdouble jdb = m_env->CallStaticDoubleMethodA(clazz, methodID, args); + CheckForJavaException(); + return jdb; +} + +jdouble +JEnv::CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { + jdouble jdb = m_env->CallNonvirtualDoubleMethodA(obj, clazz, methodID, args); + CheckForJavaException(); + return jdb; +} + +jdouble JEnv::CallDoubleMethodA(jobject obj, jmethodID methodID, jvalue *args) { + jdouble jdb = m_env->CallDoubleMethodA(obj, methodID, args); + CheckForJavaException(); + return jdb; +} + +jobject JEnv::CallStaticObjectMethodA(jclass clazz, jmethodID methodID, jvalue *args) { + jobject jo = m_env->CallStaticObjectMethodA(clazz, methodID, args); + CheckForJavaException(); + return jo; +} + +jobject +JEnv::CallNonvirtualObjectMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { + jobject jo = m_env->CallNonvirtualObjectMethodA(obj, clazz, methodID, args); + CheckForJavaException(); + return jo; +} + +jobject JEnv::CallObjectMethodA(jobject obj, jmethodID methodID, jvalue *args) { + jobject jo = m_env->CallObjectMethodA(obj, methodID, args); + CheckForJavaException(); + return jo; +} + +jobject JEnv::GetStaticObjectField(jclass clazz, jfieldID fieldID) { + jobject jo = m_env->GetStaticObjectField(clazz, fieldID); + CheckForJavaException(); + return jo; +} + +jboolean JEnv::GetStaticBooleanField(jclass clazz, jfieldID fieldID) { + jboolean jbl = m_env->GetStaticBooleanField(clazz, fieldID); + CheckForJavaException(); + return jbl; +} + +jbyte JEnv::GetStaticByteField(jclass clazz, jfieldID fieldID) { + jbyte jbt = m_env->GetStaticByteField(clazz, fieldID); + CheckForJavaException(); + return jbt; +} + +jchar JEnv::GetStaticCharField(jclass clazz, jfieldID fieldID) { + jchar jch = m_env->GetStaticCharField(clazz, fieldID); + CheckForJavaException(); + return jch; +} + +jshort JEnv::GetStaticShortField(jclass clazz, jfieldID fieldID) { + jshort jsh = m_env->GetStaticShortField(clazz, fieldID); + CheckForJavaException(); + return jsh; +} + +jint JEnv::GetStaticIntField(jclass clazz, jfieldID fieldID) { + jint ji = m_env->GetStaticIntField(clazz, fieldID); + CheckForJavaException(); + return ji; +} + +jlong JEnv::GetStaticLongField(jclass clazz, jfieldID fieldID) { + jlong jl = m_env->GetStaticLongField(clazz, fieldID); + CheckForJavaException(); + return jl; +} + +jfloat JEnv::GetStaticFloatField(jclass clazz, jfieldID fieldID) { + jfloat jfl = m_env->GetStaticFloatField(clazz, fieldID); + CheckForJavaException(); + return jfl; +} + +jdouble JEnv::GetStaticDoubleField(jclass clazz, jfieldID fieldID) { + jdouble jd = m_env->GetStaticDoubleField(clazz, fieldID); + CheckForJavaException(); + return jd; +} + +void JEnv::SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value) { + m_env->SetStaticObjectField(clazz, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value) { + m_env->SetStaticBooleanField(clazz, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value) { + m_env->SetStaticByteField(clazz, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value) { + m_env->SetStaticCharField(clazz, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value) { + m_env->SetStaticShortField(clazz, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetStaticIntField(jclass clazz, jfieldID fieldID, jint value) { + m_env->SetStaticIntField(clazz, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value) { + m_env->SetStaticLongField(clazz, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value) { + m_env->SetStaticFloatField(clazz, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value) { + m_env->SetStaticDoubleField(clazz, fieldID, value); + CheckForJavaException(); +} + +jobject JEnv::GetObjectField(jobject obj, jfieldID fieldID) { + jobject jo = m_env->GetObjectField(obj, fieldID); + CheckForJavaException(); + return jo; +} + +jboolean JEnv::GetBooleanField(jobject obj, jfieldID fieldID) { + jboolean jbl = m_env->GetBooleanField(obj, fieldID); + CheckForJavaException(); + return jbl; +} + +jbyte JEnv::GetByteField(jobject obj, jfieldID fieldID) { + jbyte jbt = m_env->GetByteField(obj, fieldID); + CheckForJavaException(); + return jbt; +} + +jchar JEnv::GetCharField(jobject obj, jfieldID fieldID) { + jchar jch = m_env->GetCharField(obj, fieldID); + CheckForJavaException(); + return jch; +} + +jshort JEnv::GetShortField(jobject obj, jfieldID fieldID) { + jshort jsh = m_env->GetShortField(obj, fieldID); + CheckForJavaException(); + return jsh; +} + +jint JEnv::GetIntField(jobject obj, jfieldID fieldID) { + jint ji = m_env->GetIntField(obj, fieldID); + CheckForJavaException(); + return ji; +} + +jlong JEnv::GetLongField(jobject obj, jfieldID fieldID) { + jlong jl = m_env->GetLongField(obj, fieldID); + CheckForJavaException(); + return jl; +} + +jfloat JEnv::GetFloatField(jobject obj, jfieldID fieldID) { + jfloat jfl = m_env->GetFloatField(obj, fieldID); + CheckForJavaException(); + return jfl; +} + +jdouble JEnv::GetDoubleField(jobject obj, jfieldID fieldID) { + jdouble jd = m_env->GetDoubleField(obj, fieldID); + CheckForJavaException(); + return jd; +} + +void JEnv::SetObjectField(jobject obj, jfieldID fieldID, jobject value) { + m_env->SetObjectField(obj, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetBooleanField(jobject obj, jfieldID fieldID, jboolean value) { + m_env->SetBooleanField(obj, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetByteField(jobject obj, jfieldID fieldID, jbyte value) { + m_env->SetByteField(obj, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetCharField(jobject obj, jfieldID fieldID, jchar value) { + m_env->SetCharField(obj, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetShortField(jobject obj, jfieldID fieldID, jshort value) { + m_env->SetShortField(obj, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetIntField(jobject obj, jfieldID fieldID, jint value) { + m_env->SetIntField(obj, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetLongField(jobject obj, jfieldID fieldID, jlong value) { + m_env->SetLongField(obj, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetFloatField(jobject obj, jfieldID fieldID, jfloat value) { + m_env->SetFloatField(obj, fieldID, value); + CheckForJavaException(); +} + +void JEnv::SetDoubleField(jobject obj, jfieldID fieldID, jdouble value) { + m_env->SetDoubleField(obj, fieldID, value); + CheckForJavaException(); +} + +jstring JEnv::NewString(const jchar *unicodeChars, jsize len) { + jstring jst = m_env->NewString(unicodeChars, len); + CheckForJavaException(); + return jst; +} + +jstring JEnv::NewStringUTF(const char *bytes) { + jstring jst = m_env->NewStringUTF(bytes); + CheckForJavaException(); + return jst; +} + +jobjectArray JEnv::NewObjectArray(jsize length, jclass elementClass, jobject initialElement) { + jobjectArray joa = m_env->NewObjectArray(length, elementClass, initialElement); + CheckForJavaException(); + return joa; +} + +jobject JEnv::GetObjectArrayElement(jobjectArray array, jsize index) { + jobject jo = m_env->GetObjectArrayElement(array, index); + CheckForJavaException(); + return jo; +} + +void JEnv::SetObjectArrayElement(jobjectArray array, jsize index, jobject value) { + m_env->SetObjectArrayElement(array, index, value); + CheckForJavaException(); +} + +const char *JEnv::GetStringUTFChars(jstring str, jboolean *isCopy) { + const char *cc = m_env->GetStringUTFChars(str, isCopy); + CheckForJavaException(); + return cc; +} + +void JEnv::ReleaseStringUTFChars(jstring str, const char *utf) { + m_env->ReleaseStringUTFChars(str, utf); + CheckForJavaException(); +} + +const jchar *JEnv::GetStringChars(jstring str, jboolean *isCopy) { + const jchar *cjc = m_env->GetStringChars(str, isCopy); + CheckForJavaException(); + return cjc; +} + +void JEnv::ReleaseStringChars(jstring str, const jchar *chars) { + m_env->ReleaseStringChars(str, chars); + CheckForJavaException(); +} + +const int JEnv::GetStringLength(jstring str) { + const int ci = m_env->GetStringLength(str); + CheckForJavaException(); + return ci; +} + +const int JEnv::GetStringUTFLength(jstring str) { + const int ci = m_env->GetStringUTFLength(str); + CheckForJavaException(); + return ci; +} + +void JEnv::GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf) { + m_env->GetStringUTFRegion(str, start, len, buf); + CheckForJavaException(); +} + +jint JEnv::Throw(jthrowable obj) { + return m_env->Throw(obj); +} + +jint JEnv::ThrowNew(jclass clazz, const string &message) { + return m_env->ThrowNew(clazz, message.c_str()); +} + +jthrowable JEnv::ExceptionOccurred() { + jthrowable jt = m_env->ExceptionOccurred(); + return jt; +} + +void JEnv::ExceptionDescribe() { + m_env->ExceptionDescribe(); + CheckForJavaException(); +} + +void JEnv::ExceptionClear() { + m_env->ExceptionClear(); +} + +jboolean JEnv::IsInstanceOf(jobject obj, jclass clazz) { + jboolean jbl = m_env->IsInstanceOf(obj, clazz); + CheckForJavaException(); + return jbl; +} + +jobjectRefType JEnv::GetObjectRefType(jobject obj) { + jobjectRefType ort = m_env->GetObjectRefType(obj); + CheckForJavaException(); + return ort; +} + +jobject JEnv::NewGlobalRef(jobject obj) { + jobject jo = m_env->NewGlobalRef(obj); +// CheckForJavaException(); + return jo; +} + +jweak JEnv::NewWeakGlobalRef(jobject obj) { + jweak jw = m_env->NewWeakGlobalRef(obj); + CheckForJavaException(); + return jw; +} + +void JEnv::DeleteGlobalRef(jobject globalRef) { + m_env->DeleteGlobalRef(globalRef); + CheckForJavaException(); +} + +void JEnv::DeleteWeakGlobalRef(jweak obj) { + m_env->DeleteWeakGlobalRef(obj); + CheckForJavaException(); +} + +jobject JEnv::NewLocalRef(jobject ref) { + jobject jo = m_env->NewLocalRef(ref); + CheckForJavaException(); + return jo; +} + +void JEnv::DeleteLocalRef(jobject localRef) { + m_env->DeleteLocalRef(localRef); +} + +jbyteArray JEnv::NewByteArray(jsize length) { + jbyteArray jba = m_env->NewByteArray(length); + CheckForJavaException(); + return jba; +} + +jbooleanArray JEnv::NewBooleanArray(jsize length) { + jbooleanArray jba = m_env->NewBooleanArray(length); + CheckForJavaException(); + return jba; +} + +jcharArray JEnv::NewCharArray(jsize length) { + jcharArray jca = m_env->NewCharArray(length); + CheckForJavaException(); + return jca; +} + +jshortArray JEnv::NewShortArray(jsize length) { + jshortArray jsa = m_env->NewShortArray(length); + CheckForJavaException(); + return jsa; +} + +jintArray JEnv::NewIntArray(jsize length) { + jintArray jia = m_env->NewIntArray(length); + CheckForJavaException(); + return jia; +} + +jlongArray JEnv::NewLongArray(jsize length) { + jlongArray jla = m_env->NewLongArray(length); + CheckForJavaException(); + return jla; +} + +jfloatArray JEnv::NewFloatArray(jsize length) { + jfloatArray jfa = m_env->NewFloatArray(length); + CheckForJavaException(); + return jfa; +} + +jdoubleArray JEnv::NewDoubleArray(jsize length) { + jdoubleArray jda = m_env->NewDoubleArray(length); + CheckForJavaException(); + return jda; +} + +jbyte *JEnv::GetByteArrayElements(jbyteArray array, jboolean *isCopy) { + jbyte *jbt = m_env->GetByteArrayElements(array, isCopy); + CheckForJavaException(); + return jbt; +} + +void JEnv::ReleaseByteArrayElements(jbyteArray array, jbyte *elems, jint mode) { + m_env->ReleaseByteArrayElements(array, elems, mode); + CheckForJavaException(); +} + +void JEnv::GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean *buf) { + m_env->GetBooleanArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte *buf) { + m_env->GetByteArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar *buf) { + m_env->GetCharArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort *buf) { + m_env->GetShortArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::GetIntArrayRegion(jintArray array, jsize start, jsize len, jint *buf) { + m_env->GetIntArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +jint *JEnv::GetIntArrayElements(jintArray array, jboolean *isCopy) { + jint *jin = m_env->GetIntArrayElements(array, isCopy); + CheckForJavaException(); + return jin; +} + +void JEnv::GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong *buf) { + m_env->GetLongArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat *buf) { + m_env->GetFloatArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble *buf) { + m_env->GetDoubleArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::SetByteArrayRegion(jbyteArray array, jsize start, jsize len, const jbyte *buf) { + m_env->SetByteArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, const jboolean *buf) { + m_env->SetBooleanArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::SetCharArrayRegion(jcharArray array, jsize start, jsize len, const jchar *buf) { + m_env->SetCharArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::SetShortArrayRegion(jshortArray array, jsize start, jsize len, const jshort *buf) { + m_env->SetShortArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::SetIntArrayRegion(jintArray array, jsize start, jsize len, const jint *buf) { + m_env->SetIntArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::SetLongArrayRegion(jlongArray array, jsize start, jsize len, const jlong *buf) { + m_env->SetLongArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, const jfloat *buf) { + m_env->SetFloatArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +void JEnv::SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, const jdouble *buf) { + m_env->SetDoubleArrayRegion(array, start, len, buf); + CheckForJavaException(); +} + +jclass JEnv::FindClass(const string &className) { + jclass global_class = CheckForClassInCache(className); + + if (global_class == nullptr) { + auto classIsMissing = CheckForClassMissingCache(className); + // class is missing. Set the same JNI error we had when we tried to find it the first time + if (classIsMissing != nullptr) { + m_env->Throw(classIsMissing); + return nullptr; + } + jclass tmp = m_env->FindClass(className.c_str()); + + if (m_env->ExceptionCheck() == JNI_TRUE) { + m_env->ExceptionClear(); + string cannonicalClassName = Util::ConvertFromJniToCanonicalName(className); + jstring s = m_env->NewStringUTF(cannonicalClassName.c_str()); + tmp = static_cast(m_env->CallStaticObjectMethod(RUNTIME_CLASS, + GET_CACHED_CLASS_METHOD_ID, s)); + + m_env->DeleteLocalRef(s); + // we failed our static class check + // if we continue, we will crash (C++ level) + // so just return null and let the runtime deal with the NativeScriptException + if (m_env->ExceptionCheck() == JNI_TRUE) { + auto tmpException = m_env->ExceptionOccurred(); + m_env->ExceptionClear(); + m_env->Throw(InsertClassIntoMissingCache(className, tmpException)); + return nullptr; + } + } + + global_class = InsertClassIntoCache(className, tmp); + } + + return global_class; +} + +jclass JEnv::CheckForClassInCache(const string &className) { + jclass global_class = nullptr; + auto itFound = s_classCache.find(className); + + if (itFound != s_classCache.end()) { + global_class = itFound->second; + } + + return global_class; +} + +jclass JEnv::InsertClassIntoCache(const string &className, jclass &tmp) { + auto global_class = reinterpret_cast(m_env->NewGlobalRef(tmp)); + s_classCache.emplace(className, global_class); + m_env->DeleteLocalRef(tmp); + + return global_class; +} + +jthrowable JEnv::CheckForClassMissingCache(const string &className) { + jthrowable throwable = nullptr; + auto itFound = s_missingClasses.find(className); + + if (itFound != s_missingClasses.end()) { + throwable = itFound->second; + } + + return throwable; +} + +jthrowable JEnv::InsertClassIntoMissingCache(const string &className,const jthrowable &tmp) { + auto throwable = reinterpret_cast(m_env->NewGlobalRef(tmp)); + s_missingClasses.emplace(className, throwable); + m_env->DeleteLocalRef(tmp); + + return throwable; +} + +jobject JEnv::NewDirectByteBuffer(void *address, jlong capacity) { + jobject jo = m_env->NewDirectByteBuffer(address, capacity); + CheckForJavaException(); + return jo; +} + +void *JEnv::GetDirectBufferAddress(jobject buf) { + void *v = m_env->GetDirectBufferAddress(buf); + CheckForJavaException(); + return v; +} + +jlong JEnv::GetDirectBufferCapacity(jobject buf) { + jlong jl = m_env->GetDirectBufferCapacity(buf); + CheckForJavaException(); + return jl; +} + +jboolean JEnv::IsAssignableFrom(jclass clazz1, jclass clazz2) { + jboolean jbl = m_env->IsAssignableFrom(clazz1, clazz2); + CheckForJavaException(); + return jbl; +} + +void JEnv::Init(JavaVM *jvm) { + assert(jvm != nullptr); + s_jvm = jvm; + + JEnv env; + RUNTIME_CLASS = env.FindClass("com/tns/Runtime"); + assert(RUNTIME_CLASS != nullptr); + GET_CACHED_CLASS_METHOD_ID = env.GetStaticMethodID(RUNTIME_CLASS, "getCachedClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + assert(GET_CACHED_CLASS_METHOD_ID != nullptr); +} + +jclass JEnv::GetObjectClass(jobject obj) { + jclass jcl = m_env->GetObjectClass(obj); + CheckForJavaException(); + return jcl; +} + +jsize JEnv::GetArrayLength(jarray array) { + jsize jsz = m_env->GetArrayLength(array); + CheckForJavaException(); + return jsz; +} + +//recursion if we put: CheckForJavaException(); +//in this method +jboolean JEnv::ExceptionCheck() { + return m_env->ExceptionCheck(); +} + +void JEnv::CheckForJavaException() { + if (ExceptionCheck() == JNI_TRUE) { + throw NativeScriptException(*this); + } +} + +JavaVM *JEnv::s_jvm = nullptr; +robin_hood::unordered_map JEnv::s_classCache; +robin_hood::unordered_map JEnv::s_missingClasses; +jclass JEnv::RUNTIME_CLASS = nullptr; +jmethodID JEnv::GET_CACHED_CLASS_METHOD_ID = nullptr; + +std::pair +JEnv::GetInterfaceStaticMethodIDAndJClass(const std::string &interfaceName, + const std::string &methodName, + const std::string &sig) { + + DesugaredInterfaceCompanionClassNameResolver companionClassNameResolver; + std::string possibleCalleeNames[] = {interfaceName, + companionClassNameResolver.resolveBazelInterfaceCompanionClassName( + interfaceName), + companionClassNameResolver.resolveD8InterfaceCompanionClassName( + interfaceName)}; + + for (const std::string& calleeName: possibleCalleeNames) { + jclass clazz = this->FindClass(calleeName); + + if (clazz != NULL) { + jmethodID methodId = m_env->GetStaticMethodID(clazz, methodName.c_str(), sig.c_str()); + + if (ExceptionCheck() == JNI_FALSE) { + return std::make_pair(methodId, clazz); + } + + ExceptionClear(); + } + } + + throw NativeScriptException( + "Could not call static interface method with name: " + methodName + " and signature: " + + sig + " for interface: " + interfaceName); + +} + + diff --git a/NativeScript/ffi/jni/napi/jni/JEnv.h b/NativeScript/ffi/jni/napi/jni/JEnv.h new file mode 100644 index 000000000..d1de99d57 --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/JEnv.h @@ -0,0 +1,456 @@ +#ifndef JENV_H_ +#define JENV_H_ + +#include "jni.h" +#include "robin_hood.h" +#include + +namespace tns { + class JEnv { + public: + JEnv(); + + JEnv(JNIEnv *jniEnv); + + ~JEnv(); + + operator JNIEnv *() const; + + jclass GetObjectClass(jobject obj); + + jsize GetArrayLength(jarray array); + + jmethodID GetMethodID(jclass clazz, const std::string &name, const std::string &sig); + + jmethodID GetStaticMethodID(jclass clazz, const std::string &name, const std::string &sig); + + std::pair GetInterfaceStaticMethodIDAndJClass( + const std::string &interfaceName, const std::string &methodName, + const std::string &sig); + + jfieldID GetFieldID(jclass clazz, const std::string &name, const std::string &sig); + + jfieldID GetStaticFieldID(jclass clazz, const std::string &name, const std::string &sig); + + void CallStaticVoidMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + void CallVoidMethodA(jobject obj, jmethodID methodID, jvalue *args); + + jboolean CallStaticBooleanMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + jboolean + CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + jboolean CallBooleanMethodA(jobject obj, jmethodID methodID, jvalue *args); + + jbyte CallStaticByteMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + jbyte + CallNonvirtualByteMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + jbyte CallByteMethodA(jobject obj, jmethodID methodID, jvalue *args); + + jchar CallStaticCharMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + jchar + CallNonvirtualCharMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + jchar CallCharMethodA(jobject obj, jmethodID methodID, jvalue *args); + + jshort CallStaticShortMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + jshort + CallNonvirtualShortMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + jshort CallShortMethodA(jobject obj, jmethodID methodID, jvalue *args); + + jint CallStaticIntMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + jint CallNonvirtualIntMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + jint CallIntMethodA(jobject obj, jmethodID methodID, jvalue *args); + + jlong CallStaticLongMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + jlong + CallNonvirtualLongMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + jlong CallLongMethodA(jobject obj, jmethodID methodID, jvalue *args); + + jfloat CallStaticFloatMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + jfloat + CallNonvirtualFloatMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + jfloat CallFloatMethodA(jobject obj, jmethodID methodID, jvalue *args); + + jdouble CallStaticDoubleMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + jdouble + CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + jdouble CallDoubleMethodA(jobject obj, jmethodID methodID, jvalue *args); + + jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + jobject + CallNonvirtualObjectMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + jobject CallObjectMethodA(jobject obj, jmethodID methodID, jvalue *args); + + jobject GetStaticObjectField(jclass clazz, jfieldID fieldID); + + jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID); + + jbyte GetStaticByteField(jclass clazz, jfieldID fieldID); + + jchar GetStaticCharField(jclass clazz, jfieldID fieldID); + + jshort GetStaticShortField(jclass clazz, jfieldID fieldID); + + jint GetStaticIntField(jclass clazz, jfieldID fieldID); + + jlong GetStaticLongField(jclass clazz, jfieldID fieldID); + + jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID); + + jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID); + + void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value); + + void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value); + + void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value); + + void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value); + + void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value); + + void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value); + + void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value); + + void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value); + + void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value); + + jobject GetObjectField(jobject obj, jfieldID fieldID); + + jboolean GetBooleanField(jobject obj, jfieldID fieldID); + + jbyte GetByteField(jobject obj, jfieldID fieldID); + + jchar GetCharField(jobject obj, jfieldID fieldID); + + jshort GetShortField(jobject obj, jfieldID fieldID); + + jint GetIntField(jobject obj, jfieldID fieldID); + + jlong GetLongField(jobject obj, jfieldID fieldID); + + jfloat GetFloatField(jobject obj, jfieldID fieldID); + + jdouble GetDoubleField(jobject obj, jfieldID fieldID); + + void SetObjectField(jobject obj, jfieldID fieldID, jobject value); + + void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value); + + void SetByteField(jobject obj, jfieldID fieldID, jbyte value); + + void SetCharField(jobject obj, jfieldID fieldID, jchar value); + + void SetShortField(jobject obj, jfieldID fieldID, jshort value); + + void SetIntField(jobject obj, jfieldID fieldID, jint value); + + void SetLongField(jobject obj, jfieldID fieldID, jlong value); + + void SetFloatField(jobject obj, jfieldID fieldID, jfloat value); + + void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value); + + jstring NewString(const jchar *unicodeChars, jsize len); + + jstring NewStringUTF(const char *bytes); + + jobjectArray NewObjectArray(jsize length, jclass elementClass, jobject initialElement); + + jobject GetObjectArrayElement(jobjectArray array, jsize index); + + void SetObjectArrayElement(jobjectArray array, jsize index, jobject value); + + const char *GetStringUTFChars(jstring str, jboolean *isCopy); + + void ReleaseStringUTFChars(jstring str, const char *utf); + + const jchar *GetStringChars(jstring str, jboolean *isCopy); + + void ReleaseStringChars(jstring str, const jchar *chars); + + const int GetStringLength(jstring str); + + const int GetStringUTFLength(jstring str); + + void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf); + + jint Throw(jthrowable obj); + + jint ThrowNew(jclass clazz, const std::string &message); + + jboolean ExceptionCheck(); + + jthrowable ExceptionOccurred(); + + void ExceptionDescribe(); + + void ExceptionClear(); + + jboolean IsInstanceOf(jobject obj, jclass clazz); + + jobjectRefType GetObjectRefType(jobject obj); + + jobject NewGlobalRef(jobject obj); + + jweak NewWeakGlobalRef(jobject obj); + + void DeleteGlobalRef(jobject globalRef); + + void DeleteWeakGlobalRef(jweak obj); + + jobject NewLocalRef(jobject ref); + + void DeleteLocalRef(jobject localRef); + + jbyteArray NewByteArray(jsize length); + + jbooleanArray NewBooleanArray(jsize length); + + jcharArray NewCharArray(jsize length); + + jshortArray NewShortArray(jsize length); + + jintArray NewIntArray(jsize length); + + jlongArray NewLongArray(jsize length); + + jfloatArray NewFloatArray(jsize length); + + jdoubleArray NewDoubleArray(jsize length); + + jbyte *GetByteArrayElements(jbyteArray array, jboolean *isCopy); + + + void ReleaseByteArrayElements(jbyteArray array, jbyte *elems, jint mode); + + void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean *buf); + + void GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte *buf); + + void GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar *buf); + + void GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort *buf); + + void GetIntArrayRegion(jintArray array, jsize start, jsize len, jint *buf); + + jint *GetIntArrayElements(jintArray array, jboolean *isCopy); + + void GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong *buf); + + void GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat *buf); + + void GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble *buf); + + void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, const jbyte *buf); + + void + SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, const jboolean *buf); + + void SetCharArrayRegion(jcharArray array, jsize start, jsize len, const jchar *buf); + + void SetShortArrayRegion(jshortArray array, jsize start, jsize len, const jshort *buf); + + void SetIntArrayRegion(jintArray array, jsize start, jsize len, const jint *buf); + + void SetLongArrayRegion(jlongArray array, jsize start, jsize len, const jlong *buf); + + void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, const jfloat *buf); + + void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, const jdouble *buf); + + jclass FindClass(const std::string &className); + + /* + * The "CheckForClassInCache" will check if a class is loaded into the cache + * if it is: it returns a global reference of it + * if it is not: it will return "nullptr". + */ + jclass CheckForClassInCache(const std::string &className); + + /* + * "InsertClassIntoCache" will take care of deleting the LocalReference of passed "jclass& tmp". + * A new GlobalReference object will be created from "tmp". The function returns the global object. + */ + jclass InsertClassIntoCache(const std::string &className, jclass &tmp); + + + /* + * The "CheckForClassMissing" will check if a class has been checked and it was missing, if it is, it will return the original throwable + * this is useful for rethrowing exceptions if they were caught in the previous attempt of loading it. + * if it is not: it will return "nullptr". + */ + jthrowable CheckForClassMissingCache(const std::string &className); + + jthrowable InsertClassIntoMissingCache(const std::string &className, const jthrowable &tmp); + + jobject NewDirectByteBuffer(void *address, jlong capacity); + + void *GetDirectBufferAddress(jobject buf); + + jlong GetDirectBufferCapacity(jobject buf); + + jboolean IsAssignableFrom(jclass clazz1, jclass clazz2); + + template + void CallVoidMethod(jobject obj, jmethodID methodID, Args ... args) { + m_env->CallVoidMethod(obj, methodID, args...); + CheckForJavaException(); + } + + template + void CallStaticVoidMethod(jclass clazz, jmethodID methodID, Args ... args) { + m_env->CallStaticVoidMethod(clazz, methodID, args...); + CheckForJavaException(); + } + + template + void CallAppFail(jclass clazz, jmethodID methodID, Args ... args) { + m_env->CallStaticVoidMethod(clazz, methodID, args...); + } + + template + jint CallStaticIntMethod(jclass clazz, jmethodID methodID, Args ... args) { + jint ji = m_env->CallStaticIntMethod(clazz, methodID, args...); + CheckForJavaException(); + return ji; + } + + template + jlong CallStaticLongMethod(jclass clazz, jmethodID methodID, Args ... args) { + jlong jd = m_env->CallStaticLongMethod(clazz, methodID, args...); + CheckForJavaException(); + return jd; + } + + template + jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, Args ... args) { + jobject jo = m_env->CallStaticObjectMethod(clazz, methodID, args...); + CheckForJavaException(); + return jo; + } + + template + jboolean CallStaticBooleanMethod(jclass clazz, jmethodID methodID, Args ... args) { + jboolean jbl = m_env->CallStaticBooleanMethod(clazz, methodID, args...); + CheckForJavaException(); + return jbl; + } + + template + jobject CallObjectMethod(jobject obj, jmethodID methodID, Args ... args) { + jobject jo = m_env->CallObjectMethod(obj, methodID, args...); + CheckForJavaException(); + return jo; + } + + template + jboolean CallBooleanMethod(jobject obj, jmethodID methodID, Args ... args) { + jboolean jbl = m_env->CallBooleanMethod(obj, methodID, args...); + CheckForJavaException(); + return jbl; + } + + template + jchar CallCharMethod(jobject obj, jmethodID methodID, Args ... args) { + jchar jc = m_env->CallCharMethod(obj, methodID, args...); + CheckForJavaException(); + return jc; + } + + template + jbyte CallByteMethod(jobject obj, jmethodID methodID, Args ... args) { + jbyte jbt = m_env->CallByteMethod(obj, methodID, args...); + CheckForJavaException(); + return jbt; + } + + template + jshort CallShortMethod(jobject obj, jmethodID methodID, Args ... args) { + jshort jsh = m_env->CallShortMethod(obj, methodID, args...); + CheckForJavaException(); + return jsh; + } + + template + jint CallIntMethod(jobject obj, jmethodID methodID, Args ... args) { + jint ji = m_env->CallIntMethod(obj, methodID, args...); + CheckForJavaException(); + return ji; + } + + template + jlong CallLongMethod(jobject obj, jmethodID methodID, Args ... args) { + jlong jl = m_env->CallLongMethod(obj, methodID, args...); + CheckForJavaException(); + return jl; + } + + template + jfloat CallFloatMethod(jobject obj, jmethodID methodID, Args ... args) { + jfloat jf = m_env->CallFloatMethod(obj, methodID, args...); + CheckForJavaException(); + return jf; + } + + template + jdouble CallDoubleMethod(jobject obj, jmethodID methodID, Args ... args) { + jdouble jd = m_env->CallDoubleMethod(obj, methodID, args...); + CheckForJavaException(); + return jd; + } + + template + jobject NewObject(jclass clazz, jmethodID methodID, Args ... args) { + jobject jo = m_env->NewObject(clazz, methodID, args...); + CheckForJavaException(); + return jo; + + } + + jobject NewObjectA(jclass clazz, jmethodID methodID, jvalue *args) { + jobject jo = m_env->NewObjectA(clazz, methodID, args); + CheckForJavaException(); + return jo; + } + + static void Init(JavaVM *jvm); + + private: + void CheckForJavaException(); + + JNIEnv *m_env; + + static JavaVM *s_jvm; + + static jclass RUNTIME_CLASS; + + static jmethodID GET_CACHED_CLASS_METHOD_ID; + + static robin_hood::unordered_map s_classCache; + static robin_hood::unordered_map s_missingClasses; + }; +} + +#endif /* JENV_H_ */ diff --git a/NativeScript/ffi/jni/napi/jni/JType.cpp b/NativeScript/ffi/jni/napi/jni/JType.cpp new file mode 100644 index 000000000..479bfb0e3 --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/JType.cpp @@ -0,0 +1,155 @@ +#include "JType.h" +#include "NativeScriptAssert.h" + +namespace tns { +Type JType::getClassType(int retType) { + Type classReturnType = static_cast(retType); + return classReturnType; +} + +jobject JType::NewByte(JEnv env, jbyte value) { + EnsureInstance(env, &Byte, Type::Byte); + return env.NewObject(Byte->clazz, Byte->ctor, value); +} + +jobject JType::NewChar(JEnv env, jchar value) { + EnsureInstance(env, &Char, Type::Char); + return env.NewObject(Char->clazz, Char->ctor, value); +} + +jobject JType::NewBoolean(JEnv env, jboolean value) { + EnsureInstance(env, &Boolean, Type::Boolean); + return env.NewObject(Boolean->clazz, Boolean->ctor, value); +} + +jobject JType::NewShort(JEnv env, jshort value) { + EnsureInstance(env, &Short, Type::Short); + return env.NewObject(Short->clazz, Short->ctor, value); +} + +jobject JType::NewInt(JEnv env, jint value) { + EnsureInstance(env, &Int, Type::Int); + return env.NewObject(Int->clazz, Int->ctor, value); +} + +jobject JType::NewLong(JEnv env, jlong value) { + EnsureInstance(env, &Long, Type::Long); + return env.NewObject(Long->clazz, Long->ctor, value); +} + +jobject JType::NewFloat(JEnv env, jfloat value) { + EnsureInstance(env, &Float, Type::Float); + return env.NewObject(Float->clazz, Float->ctor, value); +} + +jobject JType::NewDouble(JEnv env, jdouble value) { + EnsureInstance(env, &Double, Type::Double); + return env.NewObject(Double->clazz, Double->ctor, value); +} + +jbyte JType::ByteValue(JEnv env, jobject value) { + EnsureInstance(env, &Byte, Type::Byte); + return env.CallByteMethod(value, Byte->valueMethodId); +} + +jchar JType::CharValue(JEnv env, jobject value) { + EnsureInstance(env, &Char, Type::Char); + return env.CallCharMethod(value, Char->valueMethodId); +} + +jboolean JType::BooleanValue(JEnv env, jobject value) { + EnsureInstance(env, &Boolean, Type::Boolean); + return env.CallBooleanMethod(value, Boolean->valueMethodId); +} + +jshort JType::ShortValue(JEnv env, jobject value) { + EnsureInstance(env, &Short, Type::Short); + return env.CallShortMethod(value, Short->valueMethodId); +} + +jint JType::IntValue(JEnv env, jobject value) { + EnsureInstance(env, &Int, Type::Int); + return env.CallIntMethod(value, Int->valueMethodId); +} + +jlong JType::LongValue(JEnv env, jobject value) { + EnsureInstance(env, &Long, Type::Long); + return env.CallLongMethod(value, Long->valueMethodId); +} + +jfloat JType::FloatValue(JEnv env, jobject value) { + EnsureInstance(env, &Float, Type::Float); + return env.CallFloatMethod(value, Float->valueMethodId); +} + +jdouble JType::DoubleValue(JEnv env, jobject value) { + EnsureInstance(env, &Double, Type::Double); + return env.CallDoubleMethod(value, Double->valueMethodId); +} + +void JType::EnsureInstance(JEnv env, JType** instance, Type type) { + if ((*instance) != nullptr) { + return; + } + + *instance = new JType(); + + (*instance)->Init(env, type); +} + +void JType::Init(JEnv env, Type type) { + switch (type) { + case Type::Byte: + this->clazz = env.FindClass("java/lang/Byte"); + this->ctor = env.GetMethodID(this->clazz, "", "(B)V"); + this->valueMethodId = env.GetMethodID(this->clazz, "byteValue", "()B"); + break; + case Type::Char: + this->clazz = env.FindClass("java/lang/Character"); + this->ctor = env.GetMethodID(this->clazz, "", "(C)V"); + this->valueMethodId = env.GetMethodID(this->clazz, "charValue", "()C"); + break; + case Type::Boolean: + this->clazz = env.FindClass("java/lang/Boolean"); + this->ctor = env.GetMethodID(this->clazz, "", "(Z)V"); + this->valueMethodId = env.GetMethodID(this->clazz, "booleanValue", "()Z"); + break; + case Type::Short: + this->clazz = env.FindClass("java/lang/Short"); + this->ctor = env.GetMethodID(this->clazz, "", "(S)V"); + this->valueMethodId = env.GetMethodID(this->clazz, "shortValue", "()S"); + break; + case Type::Int: + this->clazz = env.FindClass("java/lang/Integer"); + this->ctor = env.GetMethodID(this->clazz, "", "(I)V"); + this->valueMethodId = env.GetMethodID(this->clazz, "intValue", "()I"); + break; + case Type::Long: + this->clazz = env.FindClass("java/lang/Long"); + this->ctor = env.GetMethodID(this->clazz, "", "(J)V"); + this->valueMethodId = env.GetMethodID(this->clazz, "longValue", "()J"); + break; + case Type::Float: + this->clazz = env.FindClass("java/lang/Float"); + this->ctor = env.GetMethodID(this->clazz, "", "(F)V"); + this->valueMethodId = env.GetMethodID(this->clazz, "floatValue", "()F"); + break; + case Type::Double: + this->clazz = env.FindClass("java/lang/Double"); + this->ctor = env.GetMethodID(this->clazz, "", "(D)V"); + this->valueMethodId = env.GetMethodID(this->clazz, "doubleValue", "()D"); + break; + default: + break; + } +} + +JType* JType::Byte; +JType* JType::Char; +JType* JType::Boolean; +JType* JType::Short; +JType* JType::Int; +JType* JType::Long; +JType* JType::Float; +JType* JType::Double; +} diff --git a/NativeScript/ffi/jni/napi/jni/JType.h b/NativeScript/ffi/jni/napi/jni/JType.h new file mode 100644 index 000000000..d3bfcf2c9 --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/JType.h @@ -0,0 +1,66 @@ +#ifndef JNIPRIMITIVETYPE_H_ +#define JNIPRIMITIVETYPE_H_ + +#include "JEnv.h" + +namespace tns { +enum class Type + : int { + Boolean, + Char, + Byte, + Short, + Int, + Long, + Float, + Double, + String, + JsObject, + Null +}; + +class JType { + public: + static jobject NewByte(JEnv env, jbyte value); + static jobject NewChar(JEnv env, jchar value); + static jobject NewBoolean(JEnv env, jboolean value); + static jobject NewShort(JEnv env, jshort value); + static jobject NewInt(JEnv env, jint value); + static jobject NewLong(JEnv env, jlong value); + static jobject NewFloat(JEnv env, jfloat value); + static jobject NewDouble(JEnv env, jdouble value); + + static jbyte ByteValue(JEnv env, jobject value); + static jchar CharValue(JEnv env, jobject value); + static jboolean BooleanValue(JEnv env, jobject value); + static jshort ShortValue(JEnv env, jobject value); + static jint IntValue(JEnv env, jobject value); + static jlong LongValue(JEnv env, jobject value); + static jfloat FloatValue(JEnv env, jobject value); + static jdouble DoubleValue(JEnv env, jobject value); + + static Type getClassType(int retType); + + private: + JType() { + } + + void Init(JEnv env, Type type); + static void EnsureInstance(JEnv env, JType** instance, Type type); + + jclass clazz; + jmethodID ctor; + jmethodID valueMethodId; + + static JType* Byte; + static JType* Char; + static JType* Boolean; + static JType* Short; + static JType* Int; + static JType* Long; + static JType* Float; + static JType* Double; +}; +} + +#endif /* JNIPRIMITIVETYPE_H_ */ diff --git a/NativeScript/ffi/jni/napi/jni/JniLocalRef.h b/NativeScript/ffi/jni/napi/jni/JniLocalRef.h new file mode 100644 index 000000000..f950740cf --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/JniLocalRef.h @@ -0,0 +1,122 @@ +#ifndef JNILOCALREF_H_ +#define JNILOCALREF_H_ + +#include "JEnv.h" +#include "JType.h" + +namespace tns { +class JniLocalRef { + public: + JniLocalRef() + : m_obj(nullptr), m_isGlobal(false) { + } + + JniLocalRef(jobject obj, bool isGlobal = false) + : m_obj(obj), m_isGlobal(isGlobal) { + } + + JniLocalRef(jclass obj) + : m_obj(obj), m_isGlobal(false) { + } + + JniLocalRef(JniLocalRef&& rhs) + : m_obj(rhs.m_obj), m_isGlobal(rhs.m_isGlobal) { + rhs.m_obj = nullptr; + } + + bool IsNull() const { + return m_obj == nullptr; + } + + bool IsGlobal() const { + return m_isGlobal; + } + + jobject Move() { + auto value = m_obj; + m_obj = nullptr; + return value; + } + + JniLocalRef& operator=(JniLocalRef&& rhs) { + m_obj = rhs.m_obj; + m_isGlobal = rhs.m_isGlobal; + rhs.m_obj = nullptr; + return *this; + } + + operator jobject() const { + return m_obj; + } + + operator jstring() const { + return reinterpret_cast(m_obj); + } + + operator jclass() const { + return reinterpret_cast(m_obj); + } + + operator jboolean() const { + JEnv env; + return JType::BooleanValue(env, m_obj); + } + + operator jthrowable() const { + return reinterpret_cast(m_obj); + } + + operator jarray()const { + return reinterpret_cast(m_obj); + } + + operator jbyteArray() const { + return reinterpret_cast(m_obj); + } + + operator jshortArray() const { + return reinterpret_cast(m_obj); + } + + operator jintArray() const { + return reinterpret_cast(m_obj); + } + + operator jlongArray() const { + return reinterpret_cast(m_obj); + } + + operator jfloatArray() const { + return reinterpret_cast(m_obj); + } + + operator jdoubleArray() const { + return reinterpret_cast(m_obj); + } + + operator jbooleanArray() const { + return reinterpret_cast(m_obj); + } + + operator jcharArray() const { + return reinterpret_cast(m_obj); + } + + operator jobjectArray() const { + return reinterpret_cast(m_obj); + } + + ~JniLocalRef() { + if ((m_obj != nullptr) && !m_isGlobal) { + JEnv env; + env.DeleteLocalRef(m_obj); + } + } + + private: + jobject m_obj; + bool m_isGlobal; +}; +} + +#endif /* JNILOCALREF_H_ */ diff --git a/NativeScript/ffi/jni/napi/jni/JniSignatureParser.cpp b/NativeScript/ffi/jni/napi/jni/JniSignatureParser.cpp new file mode 100644 index 000000000..3d6df3559 --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/JniSignatureParser.cpp @@ -0,0 +1,102 @@ +#include "JniSignatureParser.h" + +#include + +using namespace std; +using namespace tns; + +JniSignatureParser::JniSignatureParser(const string& signature) + : m_signature(signature) { +} + +vector JniSignatureParser::Parse() { + size_t startIdx = m_signature.find_first_of('('); + + assert(startIdx != string::npos); + + size_t endIdx = m_signature.find_first_of(')'); + + assert(endIdx != string::npos); + + vector tokens = ParseParams(startIdx + 1, endIdx); + + return tokens; +} + +vector JniSignatureParser::ParseParams(int stardIdx, int endIdx) { + vector tokens; + + m_pos = stardIdx; + + while (m_pos < endIdx) { + string token = ReadNextToken(endIdx); + tokens.push_back(token); + } + + return tokens; +} + +string JniSignatureParser::ReadNextToken(int endIdx) { + string token; + + char currChar = m_signature[m_pos]; + + int idx; + bool endFound; + bool testNextChar = true; + + switch (currChar) { + case 'Z': + case 'B': + case 'C': + case 'S': + case 'I': + case 'J': + case 'F': + case 'D': + ++m_pos; + token.push_back(currChar); + break; + + case 'L': + idx = m_signature.find(';', m_pos); + assert(idx != string::npos); + token = m_signature.substr(m_pos, idx - m_pos + 1); + m_pos = idx + 1; + break; + + case '[': + idx = m_pos; + endFound = false; + while (!endFound && (idx < endIdx)) { + currChar = m_signature[idx++]; + if (testNextChar) { + switch (currChar) { + case 'Z': + case 'B': + case 'C': + case 'S': + case 'I': + case 'J': + case 'F': + case 'D': + endFound = true; + break; + } + testNextChar = currChar == '['; + } else { + endFound = currChar == ';'; + } + } + assert(endFound); + token = m_signature.substr(m_pos, idx - m_pos); + m_pos = idx; + break; + + default: + assert(false); + break; + } + + return token; +} diff --git a/NativeScript/ffi/jni/napi/jni/JniSignatureParser.h b/NativeScript/ffi/jni/napi/jni/JniSignatureParser.h new file mode 100644 index 000000000..9904884ca --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/JniSignatureParser.h @@ -0,0 +1,26 @@ +#ifndef JNISIGNATUREPARSER_H_ +#define JNISIGNATUREPARSER_H_ + +#include +#include + +namespace tns { +class JniSignatureParser { + public: + JniSignatureParser(const std::string& signature); + + std::vector Parse(); + + private: + + std::vector ParseParams(int stardIdx, int endIdx); + + std::string ReadNextToken(int endIdx); + + int m_pos; + + std::string m_signature; +}; +} + +#endif /* JNISIGNATUREPARSER_H_ */ diff --git a/NativeScript/ffi/jni/napi/jni/LRUCache.h b/NativeScript/ffi/jni/napi/jni/LRUCache.h new file mode 100644 index 000000000..c3318ff6d --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/LRUCache.h @@ -0,0 +1,158 @@ +#ifndef LRUCACHE_H_ +#define LRUCACHE_H_ + +/* + Copyright (c) 2010-2011, Tim Day + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +namespace tns { +// Class providing fixed-size (by number of records) +// LRU-replacement cache of a function with signature +// V f(K). +// MAP should be one of std::map or std::unordered_map. +// Variadic template args used to deal with the +// different type argument signatures of those +// containers; the default comparator/hash/allocator +// will be used. +template +class LRUCache { + public: + + typedef K key_type; + typedef V value_type; + + // Key access history, most recent at back + typedef std::list key_tracker_type; + + // Key to value and key history iterator + typedef std::unordered_map< key_type, std::pair > key_to_value_type; + + // Constuctor specifies the cached function and + // the maximum number of records to be stored + LRUCache(value_type (*loadCallback)(const key_type&, void*), void (*evictCallback)(const value_type&, void*), size_t capacity, void* state) + : m_loadCallback(loadCallback), m_capacity(capacity), m_evictCallback(evictCallback), m_state(state) { + assert(m_loadCallback != nullptr); + assert((0 < m_capacity) && (m_capacity < 10000)); + } + + // Obtain value of the cached function for k + value_type operator()(const key_type& k) { + + // Attempt to find existing record + auto it = m_key_to_value.find(k); + + if (it == m_key_to_value.end()) { + + // We don't have it: + + // Evaluate function and create new record + const value_type v = m_loadCallback(k, m_state); + insert(k,v); + + // Return the freshly computed value + return v; + + } else { + // We do have it: + + // Update access record by moving + // accessed key to back of list + m_key_tracker.splice(m_key_tracker.end(), m_key_tracker, (*it).second.second); + + // Return the retrieved value + return (*it).second.first; + } + } + + // Obtain the cached keys, most recently used element + // at head, least recently used at tail. + // This method is provided purely to support testing. + template void get_keys(IT dst) const { + auto src = m_key_tracker.rbegin(); + + while (src != m_key_tracker.rend()) { + *dst++ = *src++; + } + } + + void update(const key_type& key, const value_type& value) { + jweak ref = m_loadCallback(key, m_state); + insert(key, ref); + } + + private: + + // Record a fresh key-value pair in the cache + void insert(const key_type& k, const value_type& v) { + // Method is only called on cache misses + assert(m_key_to_value.find(k) == m_key_to_value.end()); + + // Make space if necessary + if (m_key_to_value.size() == m_capacity) { + evict(); + } + + // Record k as most-recently-used key + auto it = m_key_tracker.insert(m_key_tracker.end(), k); + + // Create the key-value entry, + // linked to the usage record. + m_key_to_value.insert(std::make_pair(k, std::make_pair(v, it))); + // No need to check return, + // given previous assert. + } + + // Purge the least-recently-used element in the cache + void evict() { + // Assert method is never called when cache is empty + assert(!m_key_tracker.empty()); + + // Identify least recently used key + auto it = m_key_to_value.find(m_key_tracker.front()); + assert(it != m_key_to_value.end()); + + if (m_evictCallback != nullptr) { + m_evictCallback((*it).second.first, m_state); + } + + // Erase both elements to completely purge record + m_key_to_value.erase(it); + m_key_tracker.pop_front(); + } + + // The function to be cached + value_type (*m_loadCallback)(const key_type&, void*); + + void (*m_evictCallback)(const value_type&, void*); + + // Maximum number of key-value pairs to be retained + const size_t m_capacity; + + // Key access history + key_tracker_type m_key_tracker; + + // user-defined state to pass to callback + void* m_state; + + // Key-to-value lookup + key_to_value_type m_key_to_value; +}; +} + +#endif /* LRUCACHE_H_ */ diff --git a/NativeScript/ffi/jni/napi/jni/Logger.cpp b/NativeScript/ffi/jni/napi/jni/Logger.cpp new file mode 100644 index 000000000..4e35d4cf9 --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/Logger.cpp @@ -0,0 +1,9 @@ +#include "Logger.h" + +using namespace tns; + +Logger::Logger() { +} + +void Logger::Write() { +} diff --git a/NativeScript/ffi/jni/napi/jni/Logger.h b/NativeScript/ffi/jni/napi/jni/Logger.h new file mode 100644 index 000000000..6b77c4f24 --- /dev/null +++ b/NativeScript/ffi/jni/napi/jni/Logger.h @@ -0,0 +1,14 @@ +#ifndef LOGGER_H_ +#define LOGGER_H_ + +namespace tns { +class Logger { + public: + Logger(); + + void Write(); + private: +}; +} + +#endif /* LOGGER_H_ */ diff --git a/NativeScript/ffi/jni/napi/jsonhelper/JSONObjectHelper.cpp b/NativeScript/ffi/jni/napi/jsonhelper/JSONObjectHelper.cpp new file mode 100644 index 000000000..4b2f356d6 --- /dev/null +++ b/NativeScript/ffi/jni/napi/jsonhelper/JSONObjectHelper.cpp @@ -0,0 +1,69 @@ +#include "NativeScriptException.h" +#include "JSONObjectHelper.h" +#include "ArgConverter.h" +#include +#include +#include + +using namespace tns; + +void JSONObjectHelper::RegisterFromFunction(napi_env env, napi_value value) { + napi_valuetype type; + napi_typeof(env, value, &type); + if (type != napi_function && type != napi_object) { + return; + } + + bool hasProperty; + napi_has_named_property(env, value, "from", &hasProperty); + if (hasProperty) { + return; + } + + napi_value from = CreateFromFunction(env); + napi_set_named_property(env, value, "from", from); +} + + +napi_value JSONObjectHelper::CreateFromFunction(napi_env env) { + static const char* source = R"((() => function from(data) { + if (!data) throw new Error("Expected one parameter"); + let store; + switch (typeof data) { + case "string": + case "boolean": + case "number": { + return data; + } + case "object": { + if (!data) { + return null; + } + + if (data instanceof Date) { + return data.toJSON(); + } + + if (Array.isArray(data)) { + store = new org.json.JSONArray(); + data.forEach((item) => store.put(from(item))); + return store; + } + + store = new org.json.JSONObject(); + Object.keys(data).forEach((key) => store.put(key, from(data[key]))); + return store; + } + default: + return null; + } + })();)"; + + napi_value script; + napi_create_string_utf8(env, source, NAPI_AUTO_LENGTH, &script); + + napi_value result; + js_execute_script(env, script, "", &result); + + return result; +} \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/jsonhelper/JSONObjectHelper.h b/NativeScript/ffi/jni/napi/jsonhelper/JSONObjectHelper.h new file mode 100644 index 000000000..cbe730398 --- /dev/null +++ b/NativeScript/ffi/jni/napi/jsonhelper/JSONObjectHelper.h @@ -0,0 +1,17 @@ +#ifndef JSONOBJECTHELPER_H_ +#define JSONOBJECTHELPER_H_ + +#include "js_native_api.h" + +namespace tns { + + class JSONObjectHelper { + public: + static void RegisterFromFunction(napi_env env, napi_value value); + private: + static napi_value CreateFromFunction(napi_env env); + }; + +} + +#endif //JSONOBJECTHELPER_H_ \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/metadata/FieldAccessor.cpp b/NativeScript/ffi/jni/napi/metadata/FieldAccessor.cpp new file mode 100644 index 000000000..7572a931b --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/FieldAccessor.cpp @@ -0,0 +1,366 @@ +#include "FieldAccessor.h" +#include "ArgConverter.h" +#include "NativeScriptException.h" +#include "Runtime.h" +#include + +using namespace std; +using namespace tns; + +napi_value +FieldAccessor::GetJavaField(napi_env env, napi_value target, FieldCallbackData *fieldData) { + JEnv jEnv; + + auto runtime = Runtime::GetRuntime(env); + auto objectManager = runtime->GetObjectManager(); + + napi_value fieldResult; + + JniLocalRef targetJavaObject; + + auto &fieldMetadata = fieldData->metadata; + + const auto &fieldTypeName = fieldMetadata.getSig(); + auto isStatic = fieldMetadata.isStatic; + + auto isPrimitiveType = fieldTypeName.size() == 1; + if (fieldData->fid == nullptr) { + auto isFieldArray = fieldTypeName[0] == '['; + auto fieldJniSig = isPrimitiveType + ? fieldTypeName + : (isFieldArray + ? fieldTypeName + : ("L" + fieldTypeName + ";")); + + if (isStatic) { + fieldData->clazz = jEnv.FindClass(fieldMetadata.getDeclaringType()); + fieldData->fid = jEnv.GetStaticFieldID(fieldData->clazz, fieldMetadata.getName(), + fieldJniSig); + } else { + fieldData->clazz = jEnv.FindClass(fieldMetadata.getDeclaringType()); + fieldData->fid = jEnv.GetFieldID(fieldData->clazz, fieldMetadata.getName(), + fieldJniSig); + } + } + + if (!isStatic) { + // Using fast, target is always the original *this* + targetJavaObject = objectManager->GetJavaObjectByJsObjectFast(target); + + if (targetJavaObject.IsNull()) { + stringstream ss; + ss << "Cannot access property '" << fieldMetadata.getName().c_str() + << "' because there is no corresponding Java object"; + throw NativeScriptException(ss.str()); + } + + } + + + auto fieldId = fieldData->fid; + auto clazz = fieldData->clazz; + + if (isPrimitiveType) { + switch (fieldTypeName[0]) { + case 'Z': { // bool + jboolean result; + if (isStatic) { + result = jEnv.GetStaticBooleanField(clazz, fieldId); + } else { + result = jEnv.GetBooleanField(targetJavaObject, fieldId); + } + fieldResult = + result == JNI_TRUE ? napi_util::get_true(env) : napi_util::get_false(env); + break; + } + case 'B': { // byte + jbyte result; + if (isStatic) { + result = jEnv.GetStaticByteField(clazz, fieldId); + } else { + result = jEnv.GetByteField(targetJavaObject, fieldId); + } + napi_create_int32(env, result, &fieldResult); + break; + } + case 'C': { // char + jchar result; + if (isStatic) { + result = jEnv.GetStaticCharField(clazz, fieldId); + } else { + result = jEnv.GetCharField(targetJavaObject, fieldId); + } + + JniLocalRef str(jEnv.NewString(&result, 1)); + jboolean bol = true; + const char *resP = jEnv.GetStringUTFChars(str, &bol); + fieldResult = ArgConverter::convertToJsString(env, resP, 1); + jEnv.ReleaseStringUTFChars(str, resP); + break; + } + case 'S': { // short + jshort result; + if (isStatic) { + result = jEnv.GetStaticShortField(clazz, fieldId); + } else { + result = jEnv.GetShortField(targetJavaObject, fieldId); + } + napi_create_int32(env, result, &fieldResult); + break; + } + case 'I': { // int + jint result; + if (isStatic) { + result = jEnv.GetStaticIntField(clazz, fieldId); + } else { + result = jEnv.GetIntField(targetJavaObject, fieldId); + } + + napi_create_int32(env, result, &fieldResult); + break; + } + case 'J': { // long + jlong result; + if (isStatic) { + result = jEnv.GetStaticLongField(clazz, fieldId); + } else { + result = jEnv.GetLongField(targetJavaObject, fieldId); + } + + fieldResult = ArgConverter::ConvertFromJavaLong(env, result); + break; + } + case 'F': { // float + jfloat result; + if (isStatic) { + result = jEnv.GetStaticFloatField(clazz, fieldId); + } else { + result = jEnv.GetFloatField(targetJavaObject, fieldId); + } + napi_create_double(env, (double) result, &fieldResult); + break; + } + case 'D': { // double + jdouble result; + if (isStatic) { + result = jEnv.GetStaticDoubleField(clazz, fieldId); + } else { + result = jEnv.GetDoubleField(targetJavaObject, fieldId); + } + napi_create_double(env, (double) result, &fieldResult); + break; + } + default: { + stringstream ss; + ss << "(InternalError): in FieldAccessor::GetJavaField: Unknown field type: '" + << fieldTypeName[0] << "'"; + throw NativeScriptException(ss.str()); + } + } + } else { + jobject result; + + if (isStatic) { + result = jEnv.GetStaticObjectField(clazz, fieldId); + } else { + result = jEnv.GetObjectField(targetJavaObject, fieldId); + } + + if (result != nullptr) { + + bool isString = fieldTypeName == "java/lang/String"; + if (isString) { + fieldResult = ArgConverter::jstringToJsString(env, (jstring) result); + } else { + int javaObjectID = objectManager->GetOrCreateObjectId(result); + auto objectResult = objectManager->GetJsObjectByJavaObject(javaObjectID); + + if (napi_util::is_null_or_undefined(env, objectResult)) { + objectResult = objectManager->CreateJSWrapper(javaObjectID, fieldTypeName, + result); + } + + fieldResult = objectResult; + } + jEnv.DeleteLocalRef(result); + } else { + napi_get_null(env, &fieldResult); + } + } + return fieldResult; +} + +void FieldAccessor::SetJavaField(napi_env env, napi_value target, napi_value value, + FieldCallbackData *fieldData) { + JEnv jEnv; + + auto runtime = Runtime::GetRuntime(env); + auto objectManager = runtime->GetObjectManager(); + + JniLocalRef targetJavaObject; + + auto &fieldMetadata = fieldData->metadata; + + const auto &fieldTypeName = fieldMetadata.getSig(); + auto isStatic = fieldMetadata.isStatic; + + auto isPrimitiveType = fieldTypeName.size() == 1; + auto isFieldArray = fieldTypeName[0] == '['; + + if (fieldData->fid == nullptr) { + auto fieldJniSig = isPrimitiveType + ? fieldTypeName + : (isFieldArray + ? fieldTypeName + : ("L" + fieldTypeName + ";")); + + if (isStatic) { + fieldData->clazz = jEnv.FindClass(fieldMetadata.getDeclaringType()); + assert(fieldData->clazz != nullptr); + fieldData->fid = jEnv.GetStaticFieldID(fieldData->clazz, fieldMetadata.getName(), + fieldJniSig); + assert(fieldData->fid != nullptr); + } else { + fieldData->clazz = jEnv.FindClass(fieldMetadata.getDeclaringType()); + assert(fieldData->clazz != nullptr); + fieldData->fid = jEnv.GetFieldID(fieldData->clazz, fieldMetadata.getName(), + fieldJniSig); + assert(fieldData->fid != nullptr); + } + } + + if (!isStatic) { + // Using fast, target is always the original *this* + targetJavaObject = objectManager->GetJavaObjectByJsObjectFast(target); + + if (targetJavaObject.IsNull()) { + stringstream ss; + ss << "Cannot access property '" << fieldMetadata.getName().c_str() + << "' because there is no corresponding Java object"; + throw NativeScriptException(ss.str()); + } + } + + auto fieldId = fieldData->fid; + auto clazz = fieldData->clazz; + + if (isPrimitiveType) { + switch (fieldTypeName[0]) { + case 'Z': { // bool + // TODO: validate value is a boolean before calling + bool boolValue = napi_util::is_of_type(env, value, napi_boolean) + ? napi_util::get_bool(env, value) : false; + if (isStatic) { + jEnv.SetStaticBooleanField(clazz, fieldId, boolValue); + } else { + jEnv.SetBooleanField(targetJavaObject, fieldId, + boolValue); + } + break; + } + case 'B': { // byte + // TODO: validate value is a byte before calling + jbyte intValue = !napi_util::is_of_type(env, value, napi_number) + ? napi_util::get_int32(env, value) : 0; + if (isStatic) { + jEnv.SetStaticByteField(clazz, fieldId, intValue); + } else { + jEnv.SetByteField(targetJavaObject, fieldId, intValue); + } + break; + } + case 'C': { // char + const char *stringValue = napi_util::get_string_value(env, value, 1); + JniLocalRef strValue(jEnv.NewStringUTF(stringValue)); + const char *chars = jEnv.GetStringUTFChars(strValue, 0); + + if (isStatic) { + jEnv.SetStaticCharField(clazz, fieldId, chars[0]); + } else { + jEnv.SetCharField(targetJavaObject, fieldId, chars[0]); + } + jEnv.ReleaseStringUTFChars(strValue, chars); + break; + } + case 'S': { // short + // TODO: validate value is a short before calling + short shortValue = !napi_util::is_of_type(env, value, napi_number) + ? napi_util::get_int32(env, value) : 0; + if (isStatic) { + jEnv.SetStaticShortField(clazz, fieldId, shortValue); + } else { + jEnv.SetShortField(targetJavaObject, fieldId, shortValue); + } + break; + } + case 'I': { // int + // TODO: validate value is a int before calling + int intValue = napi_util::is_of_type(env, value, napi_number) + ? napi_util::get_int32(env, value) : 0; + if (isStatic) { + jEnv.SetStaticIntField(clazz, fieldId, intValue); + } else { + jEnv.SetIntField(targetJavaObject, fieldId, intValue); + } + break; + } + case 'J': { // long + jlong longValue = static_cast(ArgConverter::ConvertToJavaLong(env, value)); + if (isStatic) { + jEnv.SetStaticLongField(clazz, fieldId, longValue); + } else { + jEnv.SetLongField(targetJavaObject, fieldId, longValue); + } + break; + } + case 'F': { // float + float floatValue = napi_util::is_of_type(env, value, napi_number) + ? napi_util::get_number(env, + value) : 0.0; + if (isStatic) { + jEnv.SetStaticFloatField(clazz, fieldId, + static_cast(floatValue)); + } else { + jEnv.SetFloatField(targetJavaObject, fieldId, + static_cast(floatValue)); + } + break; + } + case 'D': { // double + double doubleValue = napi_util::is_of_type(env, value, napi_number) + ? napi_util::get_number(env, + value) : 0.0; + if (isStatic) { + jEnv.SetStaticDoubleField(clazz, fieldId, doubleValue); + } else { + jEnv.SetDoubleField(targetJavaObject, fieldId, doubleValue); + } + break; + } + default: { + stringstream ss; + ss << "(InternalError): in FieldAccessor::SetJavaField: Unknown field type: '" + << fieldTypeName[0] << "'"; + throw NativeScriptException(ss.str()); + } + } + } else { + bool isString = fieldTypeName == "java/lang/String"; + JniLocalRef result; + + if (!napi_util::is_null(env, value) && !napi_util::is_undefined(env, value)) { + if (isString) { + // TODO: validate valie is a string; + result = ArgConverter::ConvertToJavaString(env, value); + } else { + result = objectManager->GetJavaObjectByJsObject(value); + } + } + + if (isStatic) { + jEnv.SetStaticObjectField(clazz, fieldId, result); + } else { + jEnv.SetObjectField(targetJavaObject, fieldId, result); + } + } +} diff --git a/NativeScript/ffi/jni/napi/metadata/FieldAccessor.h b/NativeScript/ffi/jni/napi/metadata/FieldAccessor.h new file mode 100644 index 000000000..cdc98675c --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/FieldAccessor.h @@ -0,0 +1,18 @@ +#ifndef FIELDACCESSOR_H_ +#define FIELDACCESSOR_H_ + +#include "JEnv.h" +#include +#include "ObjectManager.h" +#include "FieldCallbackData.h" + +namespace tns { +class FieldAccessor { + public: + napi_value GetJavaField(napi_env env, napi_value target, FieldCallbackData* fieldData); + + void SetJavaField(napi_env env, napi_value target, napi_value value, FieldCallbackData* fieldData); +}; +} + +#endif /* FIELDACCESSOR_H_ */ diff --git a/NativeScript/ffi/jni/napi/metadata/FieldCallbackData.h b/NativeScript/ffi/jni/napi/metadata/FieldCallbackData.h new file mode 100644 index 000000000..95476c78e --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/FieldCallbackData.h @@ -0,0 +1,22 @@ +#ifndef FIELDCALLBACKDATA_H_ +#define FIELDCALLBACKDATA_H_ + +#include "jni.h" +#include "MetadataEntry.h" + +namespace tns { + struct FieldCallbackData { + FieldCallbackData(MetadataEntry metadata) + : + metadata(metadata), fid(nullptr), clazz(nullptr) { + + } + + MetadataEntry metadata; + jfieldID fid; + jclass clazz; + }; + +} + +#endif /* FIELDCALLBACKDATA_H_ */ diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataBuilder.cpp b/NativeScript/ffi/jni/napi/metadata/MetadataBuilder.cpp new file mode 100644 index 000000000..a2fee850d --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MetadataBuilder.cpp @@ -0,0 +1,136 @@ +// +// Created by Ammar Ahmed on 28/09/2024. +// + +#include "MetadataBuilder.h" +#include +#include +#include +#include +#include +#include +#include +#include "NativeScriptException.h" +#include "NativeScriptAssert.h" +#include "File.h" +#include "CallbackHandlers.h" + + +using namespace tns; + +MetadataReader MetadataBuilder::BuildMetadata(const std::string &filesPath) { + timeval time1; + gettimeofday(&time1, nullptr); + + string baseDir = filesPath; + baseDir.append("/metadata"); + + DIR* dir = opendir(baseDir.c_str()); + + if(dir == nullptr){ + stringstream ss; + ss << "metadata folder couldn't be opened! (Error: "; + ss << errno; + ss << ") "; + + // TODO: Is there a way to detect if the screen is locked as verification + // We assume based on the error that this is the only way to get this specific error here at this point + if (errno == ENOENT || errno == EACCES) { + // Log the error with error code + __android_log_print(ANDROID_LOG_ERROR, "TNS.error", "%s", ss.str().c_str()); + + // While the screen is locked after boot; we cannot access our own apps directory on Android 9+ + // So the only thing to do at this point is just exit normally w/o crashing! + + // The only reason we should be in this specific path; is if: + // 1) android:directBootAware="true" flag is set on receiver + // 2) android.intent.action.LOCKED_BOOT_COMPLETED intent is set in manifest on above receiver + // See: https://developer.android.com/guide/topics/manifest/receiver-element + // and: https://developer.android.com/training/articles/direct-boot + // This specific path occurs if you using the NativeScript-Local-Notification plugin, the + // receiver code runs fine, but the app actually doesn't need to startup. The Native code tries to + // startup because the receiver is triggered. So even though we are exiting, the receiver will have + // done its job + + exit(0); + } + else { + throw NativeScriptException(ss.str()); + } + } + + string nodesFile = baseDir + "/treeNodeStream.dat"; + string namesFile = baseDir + "/treeStringsStream.dat"; + string valuesFile = baseDir + "/treeValueStream.dat"; + + FILE* f = fopen(nodesFile.c_str(), "rb"); + if (f == nullptr) { + stringstream ss; + ss << "metadata file (treeNodeStream.dat) couldn't be opened! (Error: "; + ss << errno; + ss << ") "; + + throw NativeScriptException(ss.str()); + } + fseek(f, 0, SEEK_END); + int lenNodes = ftell(f); + assert((lenNodes % sizeof(MetadataTreeNodeRawData)) == 0); + char* nodes = new char[lenNodes]; + rewind(f); + fread(nodes, 1, lenNodes, f); + fclose(f); + + const int _512KB = 524288; + + f = fopen(namesFile.c_str(), "rb"); + if (f == nullptr) { + stringstream ss; + ss << "metadata file (treeStringsStream.dat) couldn't be opened! (Error: "; + ss << errno; + ss << ") "; + throw NativeScriptException(ss.str()); + } + fseek(f, 0, SEEK_END); + int lenNames = ftell(f); + char* names = new char[lenNames + _512KB]; + rewind(f); + fread(names, 1, lenNames, f); + fclose(f); + + f = fopen(valuesFile.c_str(), "rb"); + if (f == nullptr) { + stringstream ss; + ss << "metadata file (treeValueStream.dat) couldn't be opened! (Error: "; + ss << errno; + ss << ") "; + throw NativeScriptException(ss.str()); + } + fseek(f, 0, SEEK_END); + int lenValues = ftell(f); + char* values = new char[lenValues + _512KB]; + rewind(f); + fread(values, 1, lenValues, f); + fclose(f); + + timeval time2; + gettimeofday(&time2, nullptr); + + DEBUG_WRITE("lenNodes=%d, lenNames=%d, lenValues=%d", lenNodes, lenNames, lenValues); + + long millis1 = (time1.tv_sec * 1000) + (time1.tv_usec / 1000); + long millis2 = (time2.tv_sec * 1000) + (time2.tv_usec / 1000); + + DEBUG_WRITE("time=%ld", (millis2 - millis1)); + + auto reader = BuildMetadata(lenNodes, reinterpret_cast(nodes), lenNames, reinterpret_cast(names), lenValues, reinterpret_cast(values)); + delete[] nodes; + return reader; +} + +MetadataReader MetadataBuilder::BuildMetadata(uint32_t nodesLength, uint8_t *nodeData, uint32_t nameLength, + uint8_t *nameData, uint32_t valueLength, uint8_t *valueData) { + return MetadataReader(nodesLength, nodeData, nameLength, nameData, valueLength, + valueData, CallbackHandlers::GetTypeMetadata); + + +} diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataBuilder.h b/NativeScript/ffi/jni/napi/metadata/MetadataBuilder.h new file mode 100644 index 000000000..d6e85baa0 --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MetadataBuilder.h @@ -0,0 +1,25 @@ +// +// Created by Ammar Ahmed on 28/09/2024. +// + +#ifndef TESTAPPNAPI_METADATABUILDER_H +#define TESTAPPNAPI_METADATABUILDER_H + +#include +#include "MetadataReader.h" + +namespace tns { + + class MetadataBuilder { + public: + static MetadataReader BuildMetadata(const std::string &filesPath); + + private: + static MetadataReader + BuildMetadata(uint32_t nodesLength, uint8_t *nodeData, uint32_t nameLength, + uint8_t *nameData, uint32_t valueLength, uint8_t *valueData); + }; + +} // tns + +#endif //TESTAPPNAPI_METADATABUILDER_H diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataEntry.cpp b/NativeScript/ffi/jni/napi/metadata/MetadataEntry.cpp new file mode 100644 index 000000000..7836dfbfd --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MetadataEntry.cpp @@ -0,0 +1,135 @@ +#include "MetadataNode.h" +#include "MetadataEntry.h" +#include "MetadataMethodInfo.h" +#include "MetadataReader.h" + +using namespace tns; + +MetadataEntry::MetadataEntry(MetadataTreeNode *m_treeNode, NodeType nodeType) : + treeNode(m_treeNode), type(nodeType), isExtensionFunction(false), isStatic(false), + isTypeMember(false), memberId(nullptr), clazz(nullptr), mi(nullptr),fi(nullptr), sfi(nullptr), + retType(MethodReturnType::Unknown), + paramCount(-1), isFinal(false), isResolved(false), retTypeParsed(false), + isFinalSet(false), isResolvedSet(false) {} + +std::string &MetadataEntry::getName() { + if (!name.empty()) return name; + + auto reader = MetadataNode::getMetadataReader(); + + if (type == NodeType::Field) { + name = reader->ReadName(fi->nameOffset); + } else if (type == NodeType::StaticField) { + name = reader->ReadName(sfi->nameOffset); + } else if (type == NodeType::Method) { + name = mi.GetName(); + } + + return name; +} + +std::string &MetadataEntry::getSig() { + if (!sig.empty()) return sig; + + auto reader = MetadataNode::getMetadataReader(); + + if (type == NodeType::Field) { + sig = reader->ReadTypeName(fi->nodeId); + } else if (type == NodeType::StaticField) { + sig = reader->ReadTypeName(sfi->nodeId); + } else if (type == NodeType::Method) { + uint8_t sigLength = mi.GetSignatureLength(); + if (sigLength > 0) + sig = mi.GetSignature(); + + } + + return sig; +} + +std::string &MetadataEntry::getReturnType() { + if (!returnType.empty()) return returnType; + + auto reader = MetadataNode::getMetadataReader(); + + if (type == NodeType::Method) { + if (mi.GetSignatureLength() > 0) { + returnType = MetadataReader::ParseReturnType(this->getSig()); + } + } else { + return returnType; + } + + return returnType; +} + +MethodReturnType MetadataEntry::getRetType() { + if (retTypeParsed) return retType; + auto reader = MetadataNode::getMetadataReader(); + + if (type == NodeType::Method && !this->getReturnType().empty()) { + retType = MetadataReader::GetReturnType(this->returnType); + } + + retTypeParsed = true; + + return retType; +} + +std::string &MetadataEntry::getDeclaringType() { + if (!declaringType.empty()) return declaringType; + + auto reader = MetadataNode::getMetadataReader(); + + if (type == NodeType::StaticField) { + declaringType = reader->ReadTypeName(sfi->declaringType); + } else if (type == NodeType::Method && isStatic) { + declaringType = mi.GetDeclaringType(); + } + + return declaringType; +} + +int MetadataEntry::getParamCount() { + if (paramCount != -1) return paramCount; + + auto reader = MetadataNode::getMetadataReader(); + + if (type == NodeType::Method) { + auto sigLength = mi.GetSignatureLength(); + if (sigLength > 0) { + paramCount = sigLength - 1; + } else { + paramCount = 0; + } + } + + return paramCount; +} + +bool MetadataEntry::getIsFinal() { + if (isFinalSet) return isFinal; + + if (type == NodeType::Field) { + isFinal = fi->finalModifier == MetadataTreeNode::FINAL; + } else if (type == NodeType::StaticField) { + isFinal = sfi->finalModifier == MetadataTreeNode::FINAL; + } + + isFinalSet = true; + + return isFinal; +} + +bool MetadataEntry::getIsResolved() { + if (isResolvedSet) return isResolved; + + auto reader = MetadataNode::getMetadataReader(); + if (type == NodeType::Method) { + isResolved = mi.CheckIsResolved() == 1; + } + + isResolvedSet = true; + + return isResolved; +} diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataEntry.h b/NativeScript/ffi/jni/napi/metadata/MetadataEntry.h new file mode 100644 index 000000000..6e708526c --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MetadataEntry.h @@ -0,0 +1,116 @@ +#ifndef METADATAENTRY_H_ +#define METADATAENTRY_H_ + +#include +#include "jni.h" +#include "MetadataTreeNode.h" +#include "MetadataMethodInfo.h" +#include "MetadataFieldInfo.h" + +namespace tns { + enum class NodeType { + Package, + Class, + Interface, + Method, + Field, + StaticField + }; + + enum class MethodReturnType { + Unknown, + Void, + Byte, + Short, + Int, + Long, + Float, + Double, + Char, + Boolean, + String, + Object + }; + + class MetadataEntry { + public: + + MetadataEntry(MetadataTreeNode *m_treeNode, NodeType nodeType); + + MetadataEntry(const MetadataEntry &other) = default; + + MetadataEntry &operator=(const MetadataEntry &other) { + if (this != &other) { + treeNode = other.treeNode; + type = other.type; + isExtensionFunction = other.isExtensionFunction; + isStatic = other.isStatic; + isTypeMember = other.isTypeMember; + memberId = other.memberId; + clazz = other.clazz; + parsedSig = other.parsedSig; + mi = other.mi; + fi = other.fi; + sfi = other.sfi; + name = other.name; + sig = other.sig; + returnType = other.returnType; + retType = other.retType; + declaringType = other.declaringType; + paramCount = other.paramCount; + isFinal = other.isFinal; + isResolved = other.isResolved; + isResolvedSet = other.isResolvedSet; + isFinalSet = other.isFinalSet; + } + return *this; + } + + std::string &getName(); + + std::string &getSig(); + + std::string &getReturnType(); + + MethodReturnType getRetType(); + + std::string &getDeclaringType(); + + int getParamCount(); + + bool getIsFinal(); + + bool getIsResolved(); + + MetadataTreeNode *treeNode; + NodeType type; + bool isExtensionFunction; + bool isStatic; + bool isTypeMember; + void *memberId; + jclass clazz; + std::vector parsedSig; + + MethodInfo mi; + FieldInfo *fi; + StaticFieldInfo *sfi; + + std::string name; + std::string sig; + std::string returnType; + MethodReturnType retType; + std::string declaringType; + int paramCount; + bool isFinal; + bool isResolved; + + private: + + bool retTypeParsed; + bool isFinalSet; + bool isResolvedSet; + + }; +} + +#endif /* METADATAENTRY_H_ */ diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataFieldInfo.h b/NativeScript/ffi/jni/napi/metadata/MetadataFieldInfo.h new file mode 100644 index 000000000..6c428960d --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MetadataFieldInfo.h @@ -0,0 +1,28 @@ +#ifndef METADATAFIELDINFO_H_ +#define METADATAFIELDINFO_H_ + +#include + +namespace tns { +struct __attribute__ ((__packed__)) FieldInfo { + FieldInfo() + : +nameOffset(0), nodeId(0), finalModifier(0) { +} + +uint32_t nameOffset; +uint16_t nodeId; +uint8_t finalModifier; +}; + +struct __attribute__ ((__packed__)) StaticFieldInfo: FieldInfo { + StaticFieldInfo() + : +FieldInfo(), declaringType(0) { +} + +uint16_t declaringType; +}; +} + +#endif /* METADATAFIELDINFO_H_ */ diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataMethodInfo.cpp b/NativeScript/ffi/jni/napi/metadata/MetadataMethodInfo.cpp new file mode 100644 index 000000000..42289ab89 --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MetadataMethodInfo.cpp @@ -0,0 +1,99 @@ +#include "MetadataMethodInfo.h" +#include "MetadataNode.h" + + +using namespace tns; + +std::string MethodInfo::GetName() { + string methodName = MetadataNode::getMetadataReader()->ReadName(nameOffset); + return methodName; +} + +uint8_t MethodInfo::CheckIsResolved() { + return resolvedData; +} + +uint16_t MethodInfo::GetSignatureLength() { + return m_signatureLength; +} + +std::string MethodInfo::GetSignature() { //use nodeId's to read the whole signature + auto m_reader = MetadataNode::getMetadataReader(); + string signature = "("; + string ret; + for (int i = 0; i < m_signatureLength; i++) { + uint16_t nodeId = nodeIds[i]; + string curArgTypeName = m_reader->ReadTypeName(nodeId); + MetadataTreeNode* node = m_reader->GetNodeById(nodeId); + + uint8_t nodeType = m_reader->GetNodeType(node); + bool isRefType = m_reader->IsNodeTypeClass(nodeType) || m_reader->IsNodeTypeInterface(nodeType); + if (i == 0) { + if ((curArgTypeName[0] != '[') && isRefType) { + ret.append("L"); + } + ret.append(curArgTypeName); + if ((curArgTypeName[0] != '[') && isRefType) { + ret.append(";"); + } + } else { + if ((curArgTypeName[0] != '[') && isRefType) { + signature.append("L"); + } + signature.append(curArgTypeName); + if ((curArgTypeName[0] != '[') && isRefType) { + signature.append(";"); + } + } + } + if (ret.empty()) { + ret = "V"; + } + signature += ")" + ret; + + return signature; +} + +std::string MethodInfo::GetDeclaringType() { + auto m_reader = MetadataNode::getMetadataReader(); + + return m_reader->ReadTypeName(declaringNodeId); +} + +int MethodInfo::GetSizeOfReadMethodInfo() { + + if (!sizeMeasured) { + sizeMeasured = true; + // name + nameOffset = *reinterpret_cast(m_pData); + m_pData += sizeof(uint32_t); + // resolved data + resolvedData = *reinterpret_cast(m_pData); + m_pData += sizeof(uint8_t); + // sig length + m_signatureLength = *reinterpret_cast(m_pData); + m_pData += sizeof(uint16_t); + + // signature + if (m_signatureLength > 0) { + uint16_t* nodeIdPtr = reinterpret_cast(m_pData); + nodeIds.resize(m_signatureLength); + for (int i = 0; i < m_signatureLength; i++) { + nodeIds[i] = *nodeIdPtr++; + } + m_pData += m_signatureLength * sizeof(uint16_t); + } + + // declaring type + if (isStatic) { + auto declaringTypePtr = reinterpret_cast(m_pData); + declaringNodeId = *declaringTypePtr; + m_pData += sizeof(uint16_t); + } + + + + } + + return m_pData - m_pStartData; +} \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataMethodInfo.h b/NativeScript/ffi/jni/napi/metadata/MetadataMethodInfo.h new file mode 100644 index 000000000..aadda536e --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MetadataMethodInfo.h @@ -0,0 +1,66 @@ +#ifndef METHODINFOSMARTPOINTER_H_ +#define METHODINFOSMARTPOINTER_H_ + +#include +#include +#include + +using namespace std; + +namespace tns { + class MethodInfo { + public: + + MethodInfo(uint8_t *pValue) + : isStatic(false), m_pData(pValue), m_pStartData(pValue), m_signatureLength(0), + sizeMeasured(false), nameOffset(0), resolvedData(0), + declaringNodeId(0){ + } + + MethodInfo(const MethodInfo& other) = default; + + MethodInfo& operator=(const MethodInfo& other) { + if (this != &other) { + isStatic = other.isStatic; + m_pData = other.m_pData; + m_pStartData = other.m_pStartData; + m_signatureLength = other.m_signatureLength; + sizeMeasured = other.sizeMeasured; + nameOffset = other.nameOffset; + resolvedData = other.resolvedData; + declaringNodeId = other.declaringNodeId; + nodeIds = other.nodeIds; + } + return *this; + } + + std::string GetName(); + + uint8_t CheckIsResolved(); + + uint16_t GetSignatureLength(); + + std::string GetSignature(); + + std::string GetDeclaringType(); //used only for static methods + + int GetSizeOfReadMethodInfo(); + + bool isStatic; + + private: + uint8_t *m_pData; //where we currently read + uint8_t *m_pStartData; // pointer to the beginning + uint16_t m_signatureLength; + bool sizeMeasured; + + uint32_t nameOffset; + uint8_t resolvedData; + uint16_t declaringNodeId; + std::vector nodeIds; + + + }; +} + +#endif /* METHODINFOSMARTPOINTER_H_ */ diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataNode.cpp b/NativeScript/ffi/jni/napi/metadata/MetadataNode.cpp new file mode 100644 index 000000000..8dacf23ab --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MetadataNode.cpp @@ -0,0 +1,2205 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "NativeScriptException.h" +#include "MetadataNode.h" +#include "CallbackHandlers.h" +#include "NativeScriptAssert.h" +#include "File.h" +#include "Runtime.h" +#include "ArgConverter.h" +#include "FieldCallbackData.h" +#include "MetadataBuilder.h" +#include "ArgsWrapper.h" +#include "Util.h" +#include "GlobalHelpers.h" +#include "JSONObjectHelper.h" + +using namespace std; + +namespace { +napi_value EnsureConstructorThis(napi_env env, napi_value jsThis, napi_value prototype) { + if (!napi_util::is_null_or_undefined(env, jsThis)) { + return jsThis; + } + + auto runtime = Runtime::GetRuntime(env); + auto receiver = runtime->GetObjectManager()->GetEmptyObject(); + if (!napi_util::is_null_or_undefined(env, receiver) && + !napi_util::is_null_or_undefined(env, prototype)) { + napi_util::setPrototypeOf(env, receiver, prototype); + } + + return receiver; +} +} + +void MetadataNode::Init(napi_env env) { + auto cache = GetMetadataNodeCache(env); +} + +napi_value MetadataNode::CreateArrayObjectConstructor(napi_env env) { + auto it = s_arrayObjects.find(env); + if (it != s_arrayObjects.end()) { + auto value = napi_util::get_ref_value(env, it->second); + if (!napi_util::is_null_or_undefined(env, value)) return value; + } + + auto node = GetOrCreate("java/lang/Object"); + auto objectConstructor = node->GetConstructorFunction(env); + + napi_value arrayConstructor; + const char *name = "ArrayObjectWrapper"; + napi_define_class(env, name, strlen(name), + [](napi_env env, napi_callback_info info) -> napi_value { + NAPI_CALLBACK_BEGIN(0) + napi_value newTarget; + napi_get_new_target(env, info, &newTarget); + napi_value receiverPrototype = !napi_util::is_null_or_undefined(env, newTarget) + ? napi_util::get_prototype(env, newTarget) + : nullptr; + return EnsureConstructorThis(env, jsThis, receiverPrototype); + }, nullptr, 0, nullptr, &arrayConstructor); + napi_value proto = napi_util::get_prototype(env, arrayConstructor); + ObjectManager::MarkObject(env, proto); + + napi_util::napi_set_function(env, proto, "setValueAtIndex", ArraySetterCallback, nullptr); + napi_util::napi_set_function(env, proto, "getValueAtIndex", ArrayGetterCallback, nullptr); + napi_util::napi_set_function(env, proto, "getAllValues", ArrayGetAllValuesCallback, nullptr); + napi_util::define_property(env, proto, "length", nullptr, ArrayLengthCallback); + + napi_util::napi_inherits(env, arrayConstructor, objectConstructor); + + s_arrayObjects.emplace(env, napi_util::make_ref(env, arrayConstructor)); + + return arrayConstructor; +} + +napi_value MetadataNode::CreateExtendedJSWrapper(napi_env env, ObjectManager *objectManager, + const std::string &proxyClassName, + int javaObjectID) { + napi_value extInstance = nullptr; + + auto cacheData = GetCachedExtendedClassData(env, proxyClassName); + + if (cacheData.node != nullptr) { + extInstance = objectManager->GetEmptyObject(); + if (napi_util::is_null_or_undefined(env, extInstance)) { + return nullptr; + } + ObjectManager::MarkSuperCall(env, extInstance); + napi_value extendedCtorFunc = napi_util::get_ref_value(env, + cacheData.extendedCtorFunction); + napi_value extendedPrototype = napi_util::get_prototype(env, extendedCtorFunc); + napi_util::setPrototypeOf(env, extInstance, extendedPrototype); + + napi_set_named_property(env, extInstance, CONSTRUCTOR, extendedCtorFunc); + + SetInstanceMetadata(env, extInstance, cacheData.node); + } + + return extInstance; +} + +string MetadataNode::GetTypeMetadataName(napi_env env, napi_value value) { + napi_value typeMetadataName; + napi_get_named_property(env, value, PRIVATE_TYPE_NAME, &typeMetadataName); + + return napi_util::get_string_value(env, typeMetadataName); +} + + +bool MetadataNode::isArray() { + return m_isArray; +} + +napi_value MetadataNode::CreateJSWrapper(napi_env env, ObjectManager *objectManager) { + napi_value obj; + + if (m_isArray) { + obj = CreateArrayWrapper(env); + } else { + obj = objectManager->GetEmptyObject(); + napi_value ctorFunc = GetConstructorFunction(env); + napi_set_named_property(env, obj, CONSTRUCTOR, ctorFunc); + napi_util::setPrototypeOf(env, obj, napi_util::get_prototype(env, ctorFunc)); + SetInstanceMetadata(env, obj, this); + } + + return obj; +} + +napi_value MetadataNode::ArrayGetterCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1); + + try { + + napi_value index = argv[0]; + int32_t indexValue; + napi_get_value_int32(env, index, &indexValue); + auto node = GetInstanceMetadata(env, jsThis); + + return CallbackHandlers::GetArrayElement(env, jsThis, indexValue, node->m_name); + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value MetadataNode::ArrayGetAllValuesCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(0); + try { + auto node = GetInstanceMetadata(env, jsThis); + auto length = CallbackHandlers::GetArrayLength(env, jsThis); + napi_value arr; + napi_create_array(env, &arr); + + for (int i = 0; i < length; i++) { + napi_value element = CallbackHandlers::GetArrayElement(env, jsThis, i, node->m_name); + napi_set_element(env, arr, i, element); + } + + return arr; + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value MetadataNode::ArraySetterCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(2); + + try { + + napi_value index = argv[0]; + napi_value value = argv[1]; + + int32_t indexValue; + napi_get_value_int32(env, index, &indexValue); + auto node = GetInstanceMetadata(env, jsThis); + + CallbackHandlers::SetArrayElement(env, jsThis, indexValue, node->m_name, value); + return value; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value MetadataNode::ArrayLengthCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(0) + + try { + int length = CallbackHandlers::GetArrayLength(env, jsThis); + + napi_value len; + napi_create_int32(env, length, &len); + return len; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value MetadataNode::CreateArrayWrapper(napi_env env) { + napi_value constructor = CreateArrayObjectConstructor(env); + napi_value instance; + napi_new_instance(env, constructor, 0, nullptr, &instance); + SetInstanceMetadata(env, instance, this); + return instance; +} + +napi_value MetadataNode::GetImplementationObject(napi_env env, napi_value object) { + auto target = object; + napi_value currentPrototype = target; + + napi_value implementationObject; + + napi_get_named_property(env, currentPrototype, CLASS_IMPLEMENTATION_OBJECT, + &implementationObject); + + if (implementationObject != nullptr && !napi_util::is_undefined(env, implementationObject)) { + return implementationObject; + } + + bool hasProperty; + + napi_value prototypeImplObjectKey; + napi_create_string_utf8(env, PROP_KEY_IS_PROTOTYPE_IMPLEMENTATION_OBJECT, NAPI_AUTO_LENGTH, + &prototypeImplObjectKey); + napi_has_own_property(env, object, prototypeImplObjectKey, &hasProperty); + + if (hasProperty) { + bool maybeHasOwnProperty; + napi_value prototypeKey; + napi_create_string_utf8(env, PROTOTYPE, NAPI_AUTO_LENGTH, &prototypeKey); + napi_has_own_property(env, object, prototypeKey, &maybeHasOwnProperty); + + if (!maybeHasOwnProperty) { + return nullptr; + } + + return napi_util::get_prototype(env, object); + } + + napi_value activityImplementationObject; + napi_get_named_property(env, object, "t::ActivityImplementationObject", + &activityImplementationObject); + + if (activityImplementationObject != nullptr && + !napi_util::is_undefined(env, activityImplementationObject)) { + return activityImplementationObject; + } + + napi_value lastPrototype; + + bool prototypeCycleDetected = false; + + bool foundImplementationObject = false; + + while (!foundImplementationObject) { + currentPrototype = napi_util::get_prototype(env, currentPrototype); + + if (napi_util::is_null(env, currentPrototype)) { + break; + } + + if (lastPrototype == currentPrototype) { + auto abovePrototype = napi_util::get_prototype(env, currentPrototype); + prototypeCycleDetected = abovePrototype == currentPrototype; + break; + } + + if (currentPrototype == nullptr || napi_util::is_null(env, currentPrototype) || + prototypeCycleDetected) { + return nullptr; + } else { + napi_value implObject; + napi_get_named_property(env, currentPrototype, CLASS_IMPLEMENTATION_OBJECT, + &implObject); + + if (implObject != nullptr && !napi_util::is_undefined(env, implObject)) { + foundImplementationObject = true; + return currentPrototype; + } + } + lastPrototype = currentPrototype; + } + + return implementationObject; +} + +void MetadataNode::SetInstanceMetadata(napi_env env, napi_value object, MetadataNode *node) { + auto cache = GetMetadataNodeCache(env); + napi_value external; + napi_create_external(env, node, [](napi_env env, void *d1, void *d2) {}, node, &external); + napi_set_named_property(env, object, "#instance_metadata", external); +// napi_wrap(env, object, node, nullptr, nullptr, nullptr); +} + + +napi_value MetadataNode::ExtendedClassConstructorCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN_VARGS() + + try { + napi_value newTarget; + napi_get_new_target(env, info, &newTarget); + if (napi_util::is_null_or_undefined(env, newTarget)) return nullptr; + napi_value receiver = EnsureConstructorThis(env, jsThis, napi_util::get_prototype(env, newTarget)); + if (napi_util::is_null_or_undefined(env, receiver)) return nullptr; + + auto extData = reinterpret_cast(data); + SetInstanceMetadata(env, receiver, extData->node); + + napi_value implementationObject = napi_util::get_ref_value(env, + extData->implementationObject); + ObjectManager::MarkSuperCall(env, receiver); + + string fullClassName = extData->fullClassName; + + ArgsWrapper argWrapper(argv.data(), argc, ArgType::Class); + napi_value jsThisProxy; + bool success = CallbackHandlers::RegisterInstance(env, receiver, fullClassName, argWrapper, + implementationObject, false, + &jsThisProxy, extData->node->m_name); + + return jsThisProxy; + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value MetadataNode::InterfaceConstructorCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN_VARGS() + + try { + + napi_valuetype arg1Type; + napi_valuetype arg2Type; + + napi_typeof(env, argv[0], &arg1Type); + + if (argc == 2) { + napi_typeof(env, argv[1], &arg2Type); + } + + napi_value implementationObject; + napi_value interfaceName; + + if (argc == 1) { + if (arg1Type != napi_object) { + throw NativeScriptException( + string("Invalid arguments provided, first argument must be an object if only one argument is provided")); + return nullptr; + } + implementationObject = argv[0]; + } else if (argc == 2) { + if (arg1Type != napi_string) { + throw NativeScriptException( + string("Invalid arguments provided, first argument must be a string if only two argument is provided")); + return nullptr; + } + + if (arg2Type != napi_object) { + throw NativeScriptException( + string("Invalid arguments provided, second argument must be an object if only one argument is provided")); + return nullptr; + } + + interfaceName = argv[0]; + implementationObject = argv[1]; + } else { + throw NativeScriptException( + string("Invalid arguments provided, first argument must be a string and second argument must be an object")); + } + + auto node = reinterpret_cast(data); + + auto className = node->m_implType; + napi_value newTarget; + napi_get_new_target(env, info, &newTarget); + napi_value receiverPrototype = !napi_util::is_null_or_undefined(env, newTarget) + ? napi_util::get_prototype(env, newTarget) + : nullptr; + napi_value receiver = EnsureConstructorThis(env, jsThis, receiverPrototype); + if (napi_util::is_null_or_undefined(env, receiver)) return nullptr; + + SetInstanceMetadata(env, receiver, node); + + ObjectManager::MarkSuperCall(env, receiver); + + + napi_util::setPrototypeOf(env, implementationObject, + napi_util::getPrototypeOf(env, receiver)); + + napi_util::setPrototypeOf(env, receiver, implementationObject); + + napi_set_named_property(env, receiver, CLASS_IMPLEMENTATION_OBJECT, implementationObject); + + ArgsWrapper argsWrapper(argv.data(), argc, ArgType::Interface); + + napi_value jsThisProxy; + auto success = CallbackHandlers::RegisterInstance(env, receiver, className, argsWrapper, + implementationObject, true, &jsThisProxy); + return jsThisProxy; + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value MetadataNode::ClassConstructorCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN_VARGS() + + try { + + auto node = reinterpret_cast(data); + napi_value newTarget; + napi_get_new_target(env, info, &newTarget); + napi_value receiverPrototype = !napi_util::is_null_or_undefined(env, newTarget) + ? napi_util::get_prototype(env, newTarget) + : nullptr; + napi_value receiver = EnsureConstructorThis(env, jsThis, receiverPrototype); + if (napi_util::is_null_or_undefined(env, receiver)) return nullptr; + + SetInstanceMetadata(env, receiver, node); + + string extendName; + auto className = node->m_name; + + string fullClassName = CreateFullClassName(className, extendName); + + ArgsWrapper argsWrapper(argv.data(), argc, ArgType::Class); + napi_value jsThisProxy; + bool success = CallbackHandlers::RegisterInstance(env, receiver, fullClassName, argsWrapper, + nullptr, false, &jsThisProxy, className); + + return jsThisProxy; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +string MetadataNode::CreateFullClassName(const std::string &className, + const std::string &extendNameAndLocation = "") { + string fullClassName = className; + + // create a class name consisting only of the base class name + last file name part + line + column + variable identifier + if (!extendNameAndLocation.empty()) { + string tempClassName = className; + fullClassName = Util::ReplaceAll(tempClassName, "$", "_"); + fullClassName += "_" + extendNameAndLocation; + } + + return fullClassName; +} + +bool MetadataNode::IsValidExtendName(napi_env env, napi_value name) { + string extendName = ArgConverter::ConvertToString(env, name); + + for (char currentSymbol: extendName) { + bool isValidExtendNameSymbol = isalpha(currentSymbol) || + isdigit(currentSymbol) || + currentSymbol == '_'; + if (!isValidExtendNameSymbol) { + return false; + } + } + + return true; +} + + +bool +MetadataNode::GetExtendLocation(napi_env env, string &extendLocation, bool isTypeScriptExtend) { + stringstream extendLocationStream; + + auto frames = tns::BuildStacktraceFrames(env, nullptr, 4); + tns::JsStacktraceFrame *frame; + if (isTypeScriptExtend) { + if (Util::Contains(frames[2].text, "call_super")) { + frame = &frames[3]; + } else { + frame = &frames[2]; // the _super.apply call to ts_helpers will always be the third call frame + } + } else { + frame = &frames[0]; + } + + if (frame == NULL) { + DEBUG_WRITE("%s", "FRAME IS NULL!"); + return true; + } + + string srcFileName = Util::ReplaceAll(frame->filename, "file://", ""); + + string fullPathToFile; + if (srcFileName == "" || srcFileName == "" || srcFileName == "JavaScript") { + fullPathToFile = "script"; + } else { + string hardcodedPathToSkip = Constants::APP_ROOT_FOLDER_PATH; + int startIndex = hardcodedPathToSkip.length(); + int strToTakeLen = srcFileName.length() - startIndex - 3; + fullPathToFile = srcFileName.substr(startIndex, strToTakeLen); + fullPathToFile = srcFileName; + replace(fullPathToFile.begin(), fullPathToFile.end(), '/', '_'); + replace(fullPathToFile.begin(), fullPathToFile.end(), '.', '_'); + replace(fullPathToFile.begin(), fullPathToFile.end(), '-', '_'); + replace(fullPathToFile.begin(), fullPathToFile.end(), ' ', '_'); + + vector pathParts; + Util::SplitString(fullPathToFile, "_", pathParts); + fullPathToFile = + pathParts.back() == "js" ? pathParts[pathParts.size() - 2] : pathParts.back(); + } + + if (frame->line < 0) { + extendLocationStream << fullPathToFile << " unknown line number"; + extendLocation = extendLocationStream.str(); + return false; + } + + if (frame->col < 0) { + extendLocationStream << fullPathToFile << " line:" << frame->line + << " unknown column number"; + extendLocation = extendLocationStream.str(); + return false; + } + int column = frame->col; + if (frame->line == 1) { + column -= ModuleInternal::MODULE_PROLOGUE_LENGTH; + } + +#ifdef __HERMES__ + column = column - 6; +#endif + + extendLocationStream << fullPathToFile << "_" << frame->line << "_" << column << "_"; + extendLocation = extendLocationStream.str(); + return true; +} + + +bool MetadataNode::ValidateExtendArguments(napi_env env, size_t argc, napi_value *argv, + bool extendLocationFound, string &extendLocation, + napi_value *extendName, napi_value *implementationObject, + bool isTypeScriptExtend) { + + if (argc == 1) { + if (!extendLocationFound) { + stringstream ss; + ss << "Invalid extend() call. No name specified for extend at location: " + << extendLocation.c_str(); + string exceptionMessage = ss.str(); + + throw NativeScriptException(exceptionMessage); + } + + if (!napi_util::is_object(env, argv[0])) { + stringstream ss; + ss << "Invalid extend() call. No implementation object specified at location: " + << extendLocation.c_str(); + string exceptionMessage = ss.str(); + + throw NativeScriptException(exceptionMessage); + } + + *implementationObject = argv[0]; + } else if (argc == 2 || isTypeScriptExtend) { + if (!napi_util::is_of_type(env, argv[0], napi_string)) { + stringstream ss; + ss << "Invalid extend() call. No name for extend specified at location: " + << extendLocation.c_str(); + string exceptionMessage = ss.str(); + + throw NativeScriptException(exceptionMessage); + } + + if (!napi_util::is_object(env, argv[1])) { + stringstream ss; + ss + << "Invalid extend() call. Named extend should be called with second object parameter containing overridden methods at location: " + << extendLocation.c_str(); + string exceptionMessage = ss.str(); + + throw NativeScriptException(exceptionMessage); + } + + DEBUG_WRITE("ExtendsCallMethodHandler: getting extend name"); + + *extendName = argv[0]; + bool isValidExtendName = IsValidExtendName(env, *extendName); + if (!isValidExtendName) { + stringstream ss; + ss << "The extend name \"" << ArgConverter::ConvertToString(env, *extendName) + << "\" you provided contains invalid symbols. Try using the symbols [a-z, A-Z, 0-9, _]." + << endl; + string exceptionMessage = ss.str(); + + throw NativeScriptException(exceptionMessage); + } + *implementationObject = argv[1]; + } else { + stringstream ss; + ss << "Invalid extend() call at location: " << extendLocation.c_str(); + string exceptionMessage = ss.str(); + throw NativeScriptException(exceptionMessage); + } + + return true; +} + +MetadataNode::ExtendedClassCacheData +MetadataNode::GetCachedExtendedClassData(napi_env env, const string &proxyClassName) { + auto cache = GetMetadataNodeCache(env); + ExtendedClassCacheData cacheData; + auto itFound = cache->ExtendedCtorFuncCache.find(proxyClassName); + if (itFound != cache->ExtendedCtorFuncCache.end()) { + cacheData = itFound->second; + } + + return cacheData; +} + +MetadataNode::MetadataNodeCache *MetadataNode::GetMetadataNodeCache(napi_env env) { + auto cache = s_metadata_node_cache.Get(env); + if (cache) return cache; + cache = new MetadataNodeCache; + s_metadata_node_cache.Insert(env, cache); + return cache; +} + +MetadataNode::MetadataNode(MetadataTreeNode *treeNode) : m_treeNode(treeNode) { + uint8_t nodeType = s_metadataReader.GetNodeType(treeNode); + + m_name = s_metadataReader.ReadTypeName(m_treeNode); + + uint8_t parentNodeType = s_metadataReader.GetNodeType(treeNode->parent); + + m_isArray = s_metadataReader.IsNodeTypeArray(parentNodeType); + + bool isInterface = s_metadataReader.IsNodeTypeInterface(nodeType); + + if (!m_isArray && isInterface) { + bool isPrefix; + auto impTypeName = s_metadataReader.ReadInterfaceImplementationTypeName(m_treeNode, + isPrefix); + m_implType = isPrefix + ? (impTypeName + m_name) + : impTypeName; + } +} + +void MetadataNode::CreateTopLevelNamespaces(napi_env env) { + napi_value global; + + napi_get_global(env, &global); + + auto root = s_metadataReader.GetRoot(); + + const auto &children = *root->children; + + for (auto treeNode: children) { + uint8_t nodeType = s_metadataReader.GetNodeType(treeNode); + + if (nodeType == MetadataTreeNode::PACKAGE) { + auto node = GetOrCreateInternal(treeNode); + + napi_value packageObj = node->CreateWrapper(env); + + string nameSpace = node->m_treeNode->name; + // if the namespaces matches a javascript keyword, prefix it with $ to avoid TypeScript and JavaScript errors + if (IsJavascriptKeyword(nameSpace)) { + nameSpace = "$" + nameSpace; + } + napi_set_named_property(env, global, nameSpace.c_str(), packageObj); + } + } +} + +MetadataTreeNode *MetadataNode::GetOrCreateTreeNodeByName(const string &className) { + MetadataTreeNode *result = nullptr; + + auto itFound = s_name2TreeNodeCache.find(className); + + if (itFound != s_name2TreeNodeCache.end()) { + result = itFound->second; + } else { + result = s_metadataReader.GetOrCreateTreeNodeByName(className); + + s_name2TreeNodeCache.emplace(className, result); + } + + return result; +} + +string MetadataNode::GetName() { + return m_name; +} + +MetadataNode *MetadataNode::GetOrCreate(const string &className) { + MetadataNode *node = nullptr; + + auto it = s_name2NodeCache.find(className); + + if (it == s_name2NodeCache.end()) { + MetadataTreeNode *treeNode = GetOrCreateTreeNodeByName(className); + + node = GetOrCreateInternal(treeNode); + + s_name2NodeCache.emplace(className, node); + } else { + node = it->second; + } + + return node; +} + +MetadataNode *MetadataNode::GetOrCreateInternal(MetadataTreeNode *treeNode) { + MetadataNode *result = nullptr; + + auto it = s_treeNode2NodeCache.find(treeNode); + + if (it != s_treeNode2NodeCache.end()) { + result = it->second; + } else { + auto name = GetJniClassName(treeNode); + if (!name.empty()) { + auto it2 = s_name2NodeCache.find(name); + if ( it2 != s_name2NodeCache.end()) { + result = it2->second; + } + } + + if (!result) { + result = new MetadataNode(treeNode); + s_treeNode2NodeCache.emplace(treeNode, result); + if (!result->m_name.empty()) { + s_name2NodeCache.emplace(result->m_name, result); + } + } + } + + auto found = s_treeNode2NodeCache.find(treeNode); + if (found == s_treeNode2NodeCache.end()) { + s_treeNode2NodeCache.emplace(treeNode, result); + } + + return result; +} + +MetadataEntry MetadataNode::GetChildMetadataForPackage(MetadataNode *node, const char *propName) { + assert(node->m_treeNode->children != nullptr); + + MetadataEntry child(nullptr, NodeType::Class); + + const auto &children = *node->m_treeNode->children; + + for (auto treeNodeChild: children) { + if (strcmp(treeNodeChild->name.c_str(), propName) == 0) { + child.name = propName; + child.treeNode = treeNodeChild; + child.type = static_cast(s_metadataReader.GetNodeType(treeNodeChild)); + + if (s_metadataReader.IsNodeTypeInterface((uint8_t) child.type)) { + bool isPrefix; + string declaringType = s_metadataReader.ReadInterfaceImplementationTypeName( + treeNodeChild, isPrefix); + child.declaringType = isPrefix + ? (declaringType + + s_metadataReader.ReadTypeName(child.treeNode)) + : declaringType; + } + } + } + + return child; +} + +bool MetadataNode::IsJavascriptKeyword(const std::string &word) { + static set keywords; + + if (keywords.empty()) { + string kw[]{"abstract", "arguments", "boolean", "break", "byte", "case", "catch", "char", + "class", "const", "continue", "debugger", "default", "delete", "do", + "double", "else", "enum", "eval", "export", "extends", "false", "final", + "finally", "float", "for", "function", "goto", "if", "implements", + "import", "in", "instanceof", "int", "interface", "let", "long", "native", + "new", "null", "package", "private", "protected", "public", "return", + "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", + "transient", "true", "try", "typeof", "var", "void", "volatile", "while", + "with", "yield"}; + + keywords = set(kw, kw + sizeof(kw) / sizeof(kw[0])); + } + + return keywords.find(word) != keywords.end(); +} + +napi_value MetadataNode::CreateWrapper(napi_env env) { + napi_value result; + uint8_t nodeType = s_metadataReader.GetNodeType(m_treeNode); + bool isClass = s_metadataReader.IsNodeTypeClass(nodeType), + isInterface = s_metadataReader.IsNodeTypeInterface(nodeType); + napi_status status; + + if (isClass || isInterface) { + result = GetConstructorFunction(env); + } else if (s_metadataReader.IsNodeTypePackage(nodeType)) { + result = CreatePackageObject(env); + } else { + std::stringstream ss; + ss << "(InternalError): Can't create proxy for this type=" << static_cast(nodeType); + throw NativeScriptException(ss.str()); + } + + return result; +} + +napi_value MetadataNode::PackageGetterCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(0) + try { + auto childTreeNode = static_cast(data); + auto node = GetOrCreateInternal(childTreeNode->parent); + DEBUG_WRITE("Get package item: %s", childTreeNode->name.c_str()); + auto hiddenPropName = "__" + childTreeNode->name; + napi_value hiddenValue; + napi_get_named_property(env, jsThis, hiddenPropName.c_str(), &hiddenValue); + + if (!napi_util::is_null_or_undefined(env, hiddenValue)) { + DEBUG_WRITE("Get cached package item: %s", hiddenPropName.c_str()); + return hiddenValue; + } + + auto childNode = MetadataNode::GetOrCreateInternal(childTreeNode); + hiddenValue = childNode->CreateWrapper(env); + + uint8_t childNodeType = s_metadataReader.GetNodeType(childTreeNode); + bool isInterface = s_metadataReader.IsNodeTypeInterface(childNodeType); + if (isInterface) { + // For all java interfaces we register the special Symbol.hasInstance property + // which is invoked by the instanceof operator (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/hasInstance). + // For example: + // + // Object.defineProperty(android.view.animation.Interpolator, Symbol.hasInstance, { + // value: function(obj) { + // return true; + // } + // }); + RegisterSymbolHasInstanceCallback(env, childTreeNode, hiddenValue); + } + auto parentNode = GetOrCreateInternal(childTreeNode->parent); + if (parentNode->m_name == "org/json" && childTreeNode->name == "JSONObject") { + JSONObjectHelper::RegisterFromFunction(env, hiddenValue); + } + + napi_set_named_property(env, jsThis, hiddenPropName.c_str(), hiddenValue); + + return hiddenValue; + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + return nullptr; +} + +void MetadataNode::RegisterSymbolHasInstanceCallback(napi_env env, const MetadataTreeNode *treeNode, + napi_value interface) { + if (napi_util::is_undefined(env, interface) || napi_util::is_null(env, interface)) { + return; + } + + JEnv jEnv; + + auto className = GetJniClassName(treeNode); + auto clazz = jEnv.FindClass(className); + if (clazz == nullptr) { + return; + } + + napi_value hasInstance; + napi_value symbol; + napi_value global; + napi_get_global(env, &global); + napi_get_named_property(env, global, "Symbol", &symbol); + napi_get_named_property(env, symbol, "hasInstance", &hasInstance); + napi_value method; + napi_create_function(env, "hasInstance", NAPI_AUTO_LENGTH, SymbolHasInstanceCallback, clazz, + &method); + + napi_property_descriptor desc = { + nullptr, // utf8name + hasInstance, // name + nullptr, // method + nullptr, // getter + nullptr, // setter + method, // value + napi_default, // attributes + nullptr // data + }; + napi_define_properties(env, interface, 1, &desc); +} + +napi_value MetadataNode::SymbolHasInstanceCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN_VARGS(); + if (argc != 1) { + throw NativeScriptException(string("Symbol.hasInstance must take exactly 1 argument")); + return nullptr; + } + + napi_value object = argv[0]; + + if (!napi_util::is_object(env, object)) { + return napi_util::get_false(env); + } + + auto clazz = reinterpret_cast(data); + auto runtime = Runtime::GetRuntime(env); + + auto objectManager = runtime->GetObjectManager(); + auto obj = objectManager->GetJavaObjectByJsObject(object); + + if (obj.IsNull()) { + // Couldn't find a corresponding java instance counterpart. This could happen + // if the "instanceof" operator is invoked on a pure javascript instance + return napi_util::get_false(env); + } + + JEnv jEnv; + auto isInstanceOf = jEnv.IsInstanceOf(obj, clazz); + + napi_value result; + napi_get_boolean(env, isInstanceOf, &result); + + return result; + +} + + +std::string MetadataNode::GetJniClassName(const MetadataTreeNode *node) { + std::stack s; + + while (node != nullptr && !node->name.empty()) { + s.push(node->name); + node = node->parent; + } + + string fullClassName; + while (!s.empty()) { + auto top = s.top(); + fullClassName = (fullClassName.empty()) ? top : fullClassName + "/" + top; + s.pop(); + } + + return fullClassName; +} + +napi_value MetadataNode::CreatePackageObject(napi_env env) { + napi_value packageObj; + napi_create_object(env, &packageObj); + + auto ptrChildren = this->m_treeNode->children; + + if (ptrChildren != nullptr) { + const auto &children = *ptrChildren; + auto lastChildName = ""; + for (auto childNode: children) { + if (strcmp(childNode->name.c_str(), lastChildName) == 0) { + continue; + } + lastChildName = childNode->name.c_str(); + napi_property_descriptor descriptor{ + childNode->name.c_str(), + nullptr, + nullptr, + PackageGetterCallback, + nullptr, + nullptr, + napi_default_jsproperty, + childNode}; + napi_define_properties(env, packageObj, 1, &descriptor); + } + } + + return packageObj; +} + +std::vector MetadataNode::SetClassMembers( + napi_env env, napi_value constructor, + std::vector &instanceMethodsCallbackData, + const std::vector &baseInstanceMethodsCallbackData, + MetadataTreeNode *treeNode) { + + if (treeNode->metadata != nullptr) { + return SetInstanceMembersFromRuntimeMetadata( + env, constructor, instanceMethodsCallbackData, + baseInstanceMethodsCallbackData, treeNode); + } + + return SetClassMembersFromStaticMetadata( + env, constructor, instanceMethodsCallbackData, + baseInstanceMethodsCallbackData, treeNode); +} + +std::vector MetadataNode::SetClassMembersFromStaticMetadata( + napi_env env, napi_value constructor, + std::vector &instanceMethodsCallbackData, + const std::vector &baseInstanceMethodsCallbackData, + MetadataTreeNode *treeNode) { + + std::vector instanceMethodData; + + uint8_t *curPtr = s_metadataReader.GetValueData() + treeNode->offsetValue + 1; + + auto nodeType = s_metadataReader.GetNodeType(treeNode); + auto curType = s_metadataReader.ReadTypeName(treeNode); + curPtr += sizeof(uint16_t /* baseClassId */); + + if (s_metadataReader.IsNodeTypeInterface(nodeType)) { + curPtr += sizeof(uint8_t) + sizeof(uint32_t); + } + + std::string lastMethodName; + MethodCallbackData *callbackData = nullptr; + + robin_hood::unordered_map collectedExtensionMethods; + + napi_value prototype = napi_util::get_prototype(env, constructor); + + auto extensionFunctionsCount = *reinterpret_cast(curPtr); + curPtr += sizeof(uint16_t); + collectedExtensionMethods.reserve(extensionFunctionsCount); + + for (auto i = 0; i < extensionFunctionsCount; i++) { + auto entry = MetadataReader::ReadExtensionFunctionEntry(&curPtr); + + auto &methodName = entry.getName(); + if (methodName != lastMethodName) { + callbackData = tryGetExtensionMethodCallbackData(collectedExtensionMethods, + methodName); + + if (callbackData == nullptr) { + callbackData = new MethodCallbackData(this); + + napi_value method; + napi_create_function(env, methodName.c_str(), methodName.size(), MethodCallback, + callbackData, &method); + + napi_util::define_property_value(env, prototype, methodName.c_str(), method, napi_default_method); + lastMethodName = methodName; + collectedExtensionMethods.emplace(methodName, callbackData); + + } + } + + callbackData->candidates.push_back(std::move(entry)); + } + + auto instanceMethodCount = *reinterpret_cast(curPtr); + collectedExtensionMethods.reserve(instanceMethodCount); + curPtr += sizeof(uint16_t); + + for (auto i = 0; i < instanceMethodCount; i++) { + auto entry = MetadataReader::ReadInstanceMethodEntry(&curPtr); + auto &methodName = entry.getName(); + if (methodName != lastMethodName) { + callbackData = tryGetExtensionMethodCallbackData(collectedExtensionMethods, + methodName); + + + if (callbackData == nullptr) { + callbackData = new MethodCallbackData(this); + napi_value method; + napi_create_function(env, methodName.c_str(), methodName.size(), MethodCallback, + callbackData, &method); + napi_util::define_property_value(env, prototype, methodName.c_str(), method, napi_default_method); + collectedExtensionMethods.emplace(methodName, callbackData); + } + + instanceMethodData.push_back(callbackData); + instanceMethodsCallbackData.push_back(callbackData); + + auto itFound = std::find_if(baseInstanceMethodsCallbackData.begin(), + baseInstanceMethodsCallbackData.end(), + [&methodName](MethodCallbackData *x) { + return x->candidates.front().name == methodName; + }); + if (itFound != baseInstanceMethodsCallbackData.end()) { + callbackData->parent = *itFound; + } + + lastMethodName = methodName; + } + + callbackData->candidates.push_back(std::move(entry)); + } + + auto instanceFieldCount = *reinterpret_cast(curPtr); + curPtr += sizeof(uint16_t); + for (auto i = 0; i < instanceFieldCount; i++) { + auto entry = MetadataReader::ReadInstanceFieldEntry(&curPtr); + auto &fieldName = entry.getName(); + auto fieldInfo = new FieldCallbackData(entry); + fieldInfo->metadata.declaringType = curType; + napi_util::define_property(env, prototype, fieldName.c_str(), nullptr, + FieldAccessorGetterCallback, FieldAccessorSetterCallback, + fieldInfo); + + MetadataNode::GetMetadataNodeCache(env)->fieldCallbackData.push_back(fieldInfo); + + } + + auto kotlinPropertiesCount = *reinterpret_cast(curPtr); + curPtr += sizeof(uint16_t); + for (int i = 0; i < kotlinPropertiesCount; ++i) { + uint32_t nameOffset = *reinterpret_cast(curPtr); + auto propertyName = s_metadataReader.ReadName(nameOffset); + curPtr += sizeof(uint32_t); + + auto hasGetter = *reinterpret_cast(curPtr); + curPtr += sizeof(uint16_t); + + std::string getterMethodName; + if (hasGetter >= 1) { + auto entry = MetadataReader::ReadInstanceMethodEntry(&curPtr); + getterMethodName = entry.getName(); + } + + auto hasSetter = *reinterpret_cast(curPtr); + curPtr += sizeof(uint16_t); + + std::string setterMethodName; + if (hasSetter >= 1) { + auto entry = MetadataReader::ReadInstanceMethodEntry(&curPtr); + setterMethodName = entry.getName(); + } + + auto propertyInfo = new PropertyCallbackData(propertyName, getterMethodName, + setterMethodName); + napi_util::define_property(env, prototype, propertyName.c_str(), nullptr, + PropertyAccessorGetterCallback, PropertyAccessorSetterCallback, + propertyInfo); + } + + // Set static class members on constructor + lastMethodName.clear(); + callbackData = nullptr; + + auto origin = Constants::APP_ROOT_FOLDER_PATH + this->m_name; + + // get candidates from static methods metadata + auto staticMethodCout = *reinterpret_cast(curPtr); + curPtr += sizeof(uint16_t); + for (auto i = 0; i < staticMethodCout; i++) { + auto entry = MetadataReader::ReadStaticMethodEntry(&curPtr); + // In java there can be multiple methods of same name with different parameters. + auto &methodName = entry.getName(); + if (methodName != lastMethodName) { + callbackData = new MethodCallbackData(this); + napi_value method; + napi_create_function(env, methodName.c_str(), methodName.size(), MethodCallback, + callbackData, &method); + + napi_util::define_property_value(env, constructor, methodName.c_str(), method, napi_default_method); + lastMethodName = methodName; + } + callbackData->candidates.push_back(std::move(entry)); + } + + napi_value extendMethod; + napi_create_function(env, PROP_KEY_EXTEND, sizeof(PROP_KEY_EXTEND), ExtendMethodCallback, this, + &extendMethod); + napi_set_named_property(env, constructor, PROP_KEY_EXTEND, extendMethod); + + // get candidates from static fields metadata + auto staticFieldCout = *reinterpret_cast(curPtr); + curPtr += sizeof(uint16_t); + for (auto i = 0; i < staticFieldCout; i++) { + auto entry = MetadataReader::ReadStaticFieldEntry(&curPtr); + auto &fieldName = entry.getName(); + auto fieldInfo = new FieldCallbackData(entry); + napi_value method; + napi_util::define_property(env, constructor, fieldName.c_str(), nullptr, + FieldAccessorGetterCallback, FieldAccessorSetterCallback, + fieldInfo); + MetadataNode::GetMetadataNodeCache(env)->fieldCallbackData.push_back(fieldInfo); + } + + + napi_util::define_property(env, constructor, PROP_KEY_NULLOBJECT, nullptr, + NullObjectAccessorGetterCallback, nullptr, this); + + + std::string tname = s_metadataReader.ReadTypeName(treeNode); + napi_set_named_property(env, constructor, PRIVATE_TYPE_NAME, + ArgConverter::convertToJsString(env, tname)); + + SetClassAccessor(env, constructor); + + return instanceMethodData; +} + +MetadataNode::MethodCallbackData *MetadataNode::tryGetExtensionMethodCallbackData( + const robin_hood::unordered_map &collectedMethodCallbackData, + const std::string &lookupName) { + + if (collectedMethodCallbackData.empty()) { + return nullptr; + } + + auto itFound = collectedMethodCallbackData.find(lookupName); + if (itFound != collectedMethodCallbackData.end()) { + return itFound->second; + } + + return nullptr; +} + +bool MetadataNode::IsNodeTypeInterface() { + uint8_t nodeType = s_metadataReader.GetNodeType(m_treeNode); + return s_metadataReader.IsNodeTypeInterface(nodeType); +} + +std::vector MetadataNode::SetInstanceMembersFromRuntimeMetadata( + napi_env env, napi_value constructor, + std::vector &instanceMethodsCallbackData, + const std::vector &baseInstanceMethodsCallbackData, + MetadataTreeNode *treeNode) { + assert(treeNode->metadata != nullptr); + + std::vector instanceMethodData; + + std::string line; + const std::string &metadata = *treeNode->metadata; + std::stringstream s(metadata); + + std::string kind; + std::string name; + std::string signature; + int paramCount; + + std::getline(s, line); // type line + std::getline(s, line); // base class line + + std::string lastMethodName; + MethodCallbackData *callbackData = nullptr; + + napi_value proto = napi_util::get_prototype(env, constructor); + while (std::getline(s, line)) { + std::stringstream tmp(line); + tmp >> kind >> name >> signature >> paramCount; + + char chKind = kind[0]; + + assert((chKind == 'M') || (chKind == 'F')); + + MetadataEntry entry(nullptr, NodeType::Field); + + entry.name = name; + entry.sig = signature; + entry.paramCount = paramCount; + entry.isStatic = false; + if (chKind == 'M') { + if (entry.name != lastMethodName) { + entry.type = NodeType::Method; + callbackData = new MethodCallbackData(this); + instanceMethodData.push_back(callbackData); + instanceMethodsCallbackData.push_back(callbackData); + + auto itFound = std::find_if(baseInstanceMethodsCallbackData.begin(), + baseInstanceMethodsCallbackData.end(), + [&entry](MethodCallbackData *x) { + return x->candidates.front().name == entry.name; + }); + if (itFound != baseInstanceMethodsCallbackData.end()) { + callbackData->parent = *itFound; + } + + napi_value method; + napi_create_function(env, entry.name.c_str(), NAPI_AUTO_LENGTH, MethodCallback, + callbackData, &method); + napi_set_named_property(env, proto, entry.name.c_str(), method); + + lastMethodName = entry.name; + } + callbackData->candidates.push_back(std::move(entry)); + } else if (chKind == 'F') { + entry.type = NodeType::Field; + auto *fieldInfo = new FieldCallbackData(entry); + napi_util::define_property(env, proto, entry.name.c_str(), nullptr, + FieldAccessorGetterCallback, FieldAccessorSetterCallback, + fieldInfo); + + MetadataNode::GetMetadataNodeCache(env)->fieldCallbackData.push_back(fieldInfo); + } + } + + return instanceMethodData; +} + +void MetadataNode::SetClassAccessor(napi_env env, napi_value constructor) { + napi_util::define_property(env, constructor, PROP_KEY_CLASS, nullptr, + ClassAccessorGetterCallback, nullptr, nullptr); +} + +napi_value MetadataNode::ClassAccessorGetterCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(0); + try { + napi_value name; + napi_get_named_property(env, jsThis, PRIVATE_TYPE_NAME, &name); + const char *nameValue = napi_util::get_string_value(env, name); + return CallbackHandlers::FindClass(env, nameValue); + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value MetadataNode::GetConstructorFunction(napi_env env) { + std::vector instanceMethodsCallbackData; + return GetConstructorFunctionInternal(env, m_treeNode, instanceMethodsCallbackData); +} + +napi_value MetadataNode::GetConstructorFunctionInternal(napi_env env, MetadataTreeNode *treeNode, + std::vector instanceMethodsCallbackData) { + + auto cache = GetMetadataNodeCache(env); + auto itFound = cache->CtorFuncCache.find(treeNode); + if (itFound != cache->CtorFuncCache.end()) { + if (itFound->second.constructorFunction != nullptr) { + auto value = napi_util::get_ref_value(env, itFound->second.constructorFunction); + if (!napi_util::is_null_or_undefined(env, value)) { + instanceMethodsCallbackData = itFound->second.instanceMethodCallbacks; + return value; + } + } + } + + if (itFound != cache->CtorFuncCache.end()) { +#ifndef __JSC__ + for (auto data: itFound->second.instanceMethodCallbacks) { + delete data; + } +#endif + itFound->second.instanceMethodCallbacks.clear(); + if (itFound->second.constructorFunction != nullptr) { + napi_delete_reference(env, itFound->second.constructorFunction); + } + cache->CtorFuncCache.erase(itFound); + } + + auto node = GetOrCreateInternal(treeNode); + + JEnv jEnv; + // if we already have an exception (which will be rethrown later) + // then we don't want to ignore the next exception + bool ignoreFindClassException = jEnv.ExceptionCheck() == JNI_FALSE; + auto currentClass = jEnv.FindClass(node->m_name); + if (ignoreFindClassException && jEnv.ExceptionCheck()) { + jEnv.ExceptionClear(); + // JNI found an exception looking up this class + // but we don't care, because this means this class doesn't exist + // like when you try to get a class that only exists in a higher API level + CtorCacheData ctorCacheItem(nullptr, instanceMethodsCallbackData); + cache->CtorFuncCache.emplace(treeNode, ctorCacheItem); + return nullptr; + }; + + auto currentNode = treeNode; + std::string finalName(currentNode->name); + while (currentNode->parent) { + if (!currentNode->parent->name.empty()) { + finalName = currentNode->parent->name + "." + finalName; + } + currentNode = currentNode->parent; + } + + // 1. Create the class and get the constructor + + napi_value constructor; + auto isInterface = s_metadataReader.IsNodeTypeInterface(treeNode->type); + napi_define_class(env, finalName.c_str(), NAPI_AUTO_LENGTH, + isInterface ? InterfaceConstructorCallback : ClassConstructorCallback, + node, 0, nullptr, &constructor); + + // Mark this constructor's prototype as a runtime object. + ObjectManager::MarkObject(env, napi_util::get_prototype(env, constructor)); + + // 2. Create the base constructor if it doesn't exist and inherit from it. + napi_value baseConstructor; + std::vector baseInstanceMethodsCallbackData; + auto tmpTreeNode = treeNode; + std::vector skippedBaseTypes; + + while (true) { + auto baseTreeNode = s_metadataReader.GetBaseClassNode(tmpTreeNode); + if (CheckClassHierarchy(jEnv, currentClass, treeNode, baseTreeNode, skippedBaseTypes)) { + tmpTreeNode = baseTreeNode; + continue; + } + + if ((baseTreeNode != treeNode) && (baseTreeNode != nullptr) && + (baseTreeNode->offsetValue > 0)) { + baseConstructor = GetConstructorFunctionInternal(env, baseTreeNode, + baseInstanceMethodsCallbackData); + + + if (baseConstructor != nullptr) { + napi_util::napi_inherits(env, constructor, baseConstructor); + } + } else { + baseConstructor = nullptr; + } + break; + } + + // 3. Define the class members now. + auto instanceMethodData = node->SetClassMembers(env, constructor, + instanceMethodsCallbackData, + baseInstanceMethodsCallbackData, treeNode); + + if (!skippedBaseTypes.empty()) { + // If there is a mismatch between base type of this class in metadata compared to the class + // at runtime, we will add methods of base class to this class's prototype. + node->SetMissingBaseMethods(env, skippedBaseTypes, instanceMethodData, constructor); + } + + + SetInnerTypes(env, constructor, treeNode); + + napi_ref constructorRef = napi_util::make_ref(env, constructor); + + if (baseConstructor != nullptr && !napi_util::is_undefined(env, baseConstructor)) { + napi_util::setPrototypeOf(env, constructor, baseConstructor); + } + + CtorCacheData ctorCacheItem(constructorRef, instanceMethodsCallbackData); + cache->CtorFuncCache.emplace(treeNode, ctorCacheItem); + + return constructor; +} + +void MetadataNode::SetInnerTypes(napi_env env, napi_value constructor, MetadataTreeNode *treeNode) { + if (treeNode->children != nullptr) { + const auto &children = *treeNode->children; + std::vector childNames(children.size()); + + for (auto curChild: children) { + bool hasOwnProperty = false; + napi_value childName; + napi_create_string_utf8(env, curChild->name.c_str(), curChild->name.size(), &childName); + napi_has_own_property(env, constructor, childName, &hasOwnProperty); + if (!hasOwnProperty) { + napi_util::define_property(env, constructor, curChild->name.c_str(), nullptr, + InnerTypeGetterCallback, nullptr, curChild); + } + } + } +} + +napi_value MetadataNode::InnerTypeGetterCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(0) + try { + auto curChild = reinterpret_cast(data); + auto childNode = GetOrCreateInternal(curChild); + auto cache = GetMetadataNodeCache(env); + auto itFound = cache->CtorFuncCache.find(childNode->m_treeNode); + if (itFound != cache->CtorFuncCache.end()) { + auto value = napi_util::get_ref_value(env, itFound->second.constructorFunction); + if (!napi_util::is_null_or_undefined(env, value)) return value; + } + napi_value constructor = childNode->GetConstructorFunction(env); + return constructor; + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +MetadataReader *MetadataNode::getMetadataReader() { + return &MetadataNode::s_metadataReader; +} + +napi_value MetadataNode::NullObjectAccessorGetterCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(0) + try { + + bool value; + napi_value nullNodeKey; + napi_create_string_utf8(env, PROP_KEY_NULL_NODE_NAME, NAPI_AUTO_LENGTH, &nullNodeKey); + napi_has_own_property(env, jsThis, nullNodeKey, &value); + + if (!value) { + auto node = reinterpret_cast(data); + napi_value external; + napi_create_external(env, node, [](napi_env env, void *d1, void *d2) {}, node, + &external); + napi_set_named_property(env, jsThis, PROP_KEY_NULL_NODE_NAME, external); + + napi_util::napi_set_function(env, + jsThis, + "valueOf", MetadataNode::NullValueOfCallback); + } + + return jsThis; + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value MetadataNode::NullValueOfCallback(napi_env env, napi_callback_info info) { + napi_value nullValue; + napi_get_null(env, &nullValue); + return nullValue; +} + +napi_value MetadataNode::FieldAccessorGetterCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(0); + try { + auto fieldData = reinterpret_cast(data); + auto &fieldMetadata = fieldData->metadata; + + if (fieldMetadata.getDeclaringType().empty()) { + return UNDEFINED; + } + + if (!fieldMetadata.isStatic) { + bool is__this__ = false; + napi_has_named_property(env, jsThis, "__napi::this", &is__this__); + if (!is__this__) { + napi_value constructor; + napi_value prototype; + napi_get_named_property(env, jsThis, "constructor", &constructor); + if (!napi_util::is_null_or_undefined(env, constructor)) { + napi_get_named_property(env, constructor, "prototype", &prototype); + bool isHolder; + napi_strict_equals(env, prototype, jsThis, &isHolder); + if (isHolder) { + return UNDEFINED; + } else { + napi_set_named_property(env, jsThis, "__napi::this", + napi_util::get_true(env)); + } + } + } + } + + return CallbackHandlers::GetJavaField(env, jsThis, fieldData); + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return UNDEFINED; +} +napi_ref propRef = nullptr; +napi_value MetadataNode::FieldAccessorSetterCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1); + + try { + auto fieldData = reinterpret_cast(data); + auto &fieldMetadata = fieldData->metadata; + + if (!fieldMetadata.isStatic) { + bool is__this__ = false; + napi_has_named_property(env, jsThis, "__napi::this", &is__this__); + if (!is__this__) { + napi_value constructor; + napi_value prototype; + napi_get_named_property(env, jsThis, "constructor", &constructor); + if (!napi_util::is_null_or_undefined(env, constructor)) { + napi_get_named_property(env, constructor, "prototype", &prototype); + bool isHolder; + napi_strict_equals(env, prototype, jsThis, &isHolder); + if (isHolder) { + return UNDEFINED; + } else { + napi_set_named_property(env, jsThis, "__napi::this", + napi_util::get_true(env)); + } + } + } + } + + if (fieldMetadata.getIsFinal()) { + stringstream ss; + ss << "You are trying to set \"" << fieldMetadata.getName() + << "\" which is a final field! Final fields can only be read."; + string exceptionMessage = ss.str(); + + throw NativeScriptException(exceptionMessage); + } else { + CallbackHandlers::SetJavaField(env, jsThis, argv[0], fieldData); + return argv[0]; + } + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return UNDEFINED; +} + +napi_value MetadataNode::PropertyAccessorGetterCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(0) + + try { + auto propertyCallbackData = reinterpret_cast(data); + + if (propertyCallbackData->getterMethodName.empty()) { + return nullptr; + } + + napi_value getter; + napi_get_named_property(env, jsThis, propertyCallbackData->getterMethodName.c_str(), + &getter); + + napi_value result; + napi_call_function(env, jsThis, getter, 0, nullptr, &result); + return result; + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value MetadataNode::PropertyAccessorSetterCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(1) + + try { + auto propertyCallbackData = reinterpret_cast(data); + + if (propertyCallbackData->setterMethodName.empty()) { + return nullptr; + } + + napi_value setter; + napi_get_named_property(env, jsThis, propertyCallbackData->setterMethodName.c_str(), + &setter); + + napi_value result; + napi_call_function(env, jsThis, setter, 1, &argv[0], &result); + + return result; + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value MetadataNode::ExtendMethodCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN_VARGS() + + try { + napi_value extendName; + napi_value implementationObject; + string extendLocation; + + auto hasDot = false; + auto isTypeScriptExtend = false; + + if (argc == 2) { + if (!napi_util::is_of_type(env, argv[0], napi_string)) { + stringstream ss; + ss << "Invalid extend() call. No name for extend specified at location: " + << extendLocation.c_str(); + string exceptionMessage = ss.str(); + + throw NativeScriptException(exceptionMessage); + } + + if (!napi_util::is_of_type(env, argv[1], napi_object)) { + stringstream ss; + ss << "Invalid extend() call. No implementation object specified at location: " + << extendLocation.c_str(); + string exceptionMessage = ss.str(); + + throw NativeScriptException(exceptionMessage); + } + + string strName = napi_util::get_string_value(env, argv[0]); + hasDot = strName.find('.') != string::npos; + } else if (argc == 3) { + if (napi_util::is_of_type(env, argv[2], napi_boolean)) { + napi_get_value_bool(env, argv[2], &isTypeScriptExtend); + }; + } + + auto node = reinterpret_cast(data); + + if (hasDot) { + extendName = argv[0]; + implementationObject = argv[1]; + } else { + bool validExtend = GetExtendLocation(env, extendLocation, isTypeScriptExtend); + napi_create_string_utf8(env, "", 0, &extendName); + auto validArgs = ValidateExtendArguments(env, argc, argv.data(), validExtend, + extendLocation, + &extendName, &implementationObject, + isTypeScriptExtend); + if (!validArgs) { + return nullptr; + } + } + + + string extendNameAndLocation = + extendLocation + ArgConverter::ConvertToString(env, extendName); + string fullClassName; + string baseClassName = node->m_name; + if (!hasDot) { + fullClassName = TNS_PREFIX + CreateFullClassName(baseClassName, extendNameAndLocation); + } else { + fullClassName = ArgConverter::ConvertToString(env, argv[0]); + } + + uint8_t nodeType = s_metadataReader.GetNodeType(node->m_treeNode); + bool isInterface = s_metadataReader.IsNodeTypeInterface(nodeType); + auto clazz = CallbackHandlers::ResolveClass(env, baseClassName, fullClassName, + implementationObject, isInterface); + auto fullExtendedName = CallbackHandlers::ResolveClassName(env, clazz); + + auto cachedData = GetCachedExtendedClassData(env, fullExtendedName); + if (cachedData.extendedCtorFunction != nullptr) { + auto value = napi_util::get_ref_value(env, cachedData.extendedCtorFunction); + if (!napi_util::is_null_or_undefined(env, value)) return value; + } + + napi_value implementationObjectName; + napi_get_named_property(env, implementationObject, CLASS_IMPLEMENTATION_OBJECT, + &implementationObjectName); + + if (napi_util::is_null_or_undefined(env, implementationObjectName)) { + napi_set_named_property(env, implementationObject, CLASS_IMPLEMENTATION_OBJECT, + ArgConverter::convertToJsString(env, fullExtendedName)); + } else { + string usedClassName = ArgConverter::ConvertToString(env, implementationObjectName); + stringstream s; + s << "This object is used to extend another class '" << usedClassName << "'"; + throw NativeScriptException(s.str()); + } + + auto baseClassCtorFunction = node->GetConstructorFunction(env); + + napi_value extendFuncCtor; + napi_define_class(env, fullExtendedName.c_str(), NAPI_AUTO_LENGTH, + MetadataNode::ExtendedClassConstructorCallback, + new ExtendedClassCallbackData(node, extendNameAndLocation, + napi_util::make_ref(env, + implementationObject), + fullClassName), 0, nullptr, + &extendFuncCtor); + napi_value extendFuncPrototype = napi_util::get_prototype(env, extendFuncCtor); + ObjectManager::MarkObject(env, extendFuncPrototype); + + napi_util::setPrototypeOf(env, implementationObject, + napi_util::get_prototype(env, baseClassCtorFunction)); + + napi_util::define_property( + env, implementationObject, PROP_KEY_SUPER, nullptr, SuperAccessorGetterCallback, + nullptr, nullptr); + + napi_util::setPrototypeOf(env, extendFuncPrototype, implementationObject); + + napi_util::setPrototypeOf(env, extendFuncCtor, baseClassCtorFunction); + + SetClassAccessor(env, extendFuncCtor); + + napi_set_named_property(env, extendFuncCtor, PRIVATE_TYPE_NAME, + ArgConverter::convertToJsString(env, fullExtendedName)); + + s_name2NodeCache.emplace(fullExtendedName, node); + + ExtendedClassCacheData cacheData(napi_util::make_ref(env, extendFuncCtor), fullExtendedName, + node); + auto cache = GetMetadataNodeCache(env); + cache->ExtendedCtorFuncCache.emplace(fullExtendedName, cacheData); + + return extendFuncCtor; + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + + +napi_value MetadataNode::SuperAccessorGetterCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(0) + + try { + + napi_value superValue; + napi_get_named_property(env, jsThis, PROP_KEY_SUPERVALUE, &superValue); + + if (napi_util::is_null_or_undefined(env, superValue)) { + auto objectManager = Runtime::GetRuntime(env)->GetObjectManager(); + superValue = objectManager->GetEmptyObject(); + + napi_delete_property(env, superValue, + ArgConverter::convertToJsString(env, PROP_KEY_TOSTRING), nullptr); + napi_delete_property(env, superValue, + ArgConverter::convertToJsString(env, PROP_KEY_VALUEOF), nullptr); + ObjectManager::MarkSuperCall(env, superValue); + + napi_value superProto = napi_util::getPrototypeOf(env, napi_util::getPrototypeOf(env, + napi_util::getPrototypeOf( + env, + jsThis))); + + napi_util::setPrototypeOf(env, superValue, superProto); + objectManager->CloneLink(jsThis, superValue); + auto node = GetInstanceMetadata(env, jsThis); + SetInstanceMetadata(env, superValue, node); + + int javaObjectID = -1; + objectManager->GetJavaObjectByJsObject(jsThis, &javaObjectID); + if (javaObjectID != -1) { + superValue = objectManager->GetOrCreateProxyWeak(javaObjectID, superValue); + } + napi_set_named_property(env, jsThis, PROP_KEY_SUPERVALUE, superValue); + } + + return superValue; + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +napi_value MetadataNode::MethodCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN_VARGS() + + try { + MetadataEntry *entry = nullptr; + + auto callbackData = reinterpret_cast(data); + auto initialCallbackData = reinterpret_cast(data); + + string *className; + auto &first = callbackData->candidates.front(); + auto &methodName = first.getName(); + + while ((callbackData != nullptr) && (entry == nullptr)) { + auto &candidates = callbackData->candidates; + + className = &callbackData->node->m_name; + + // Iterates through all methods and finds the best match based on the number of arguments + auto found = false; + for (auto &c: candidates) { + found = (!c.isExtensionFunction && c.getParamCount() == argc) || + (c.isExtensionFunction && c.getParamCount() == argc + 1); + if (found) { + if (c.isExtensionFunction) { + className = &c.getDeclaringType(); + } + entry = &c; + DEBUG_WRITE("MetaDataEntry Method %s's signature is: %s", + entry->getName().c_str(), + entry->getSig().c_str()); + break; + } + } + + // Iterates through the parent class's methods to find a good match + if (!found) { + callbackData = callbackData->parent; + } + } + + + if (argc == 0 && methodName == PROP_KEY_VALUEOF) { + return jsThis; + } else { +// Runtime::GetRuntime(env)->clearPendingError(); + bool isFromInterface = initialCallbackData->node->IsNodeTypeInterface(); + napi_value result = CallbackHandlers::CallJavaMethod(env, jsThis, *className, methodName, entry, + isFromInterface, first.isStatic, info, + argc, argv.data()); +// napi_value error; +// error = Runtime::GetRuntime(env)->getPendingError(); +// if (error) { +// throw NativeScriptException(env, error); +// } + return result; + } + + } catch (NativeScriptException &e) { + e.ReThrowToNapi(env); + } catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToNapi(env); + } catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToNapi(env); + } + + return nullptr; +} + +/** + * Compare class hierarchy in metadata with that at runtime. If a base class is missing + * at runtime, we must add all it's methods to the current class. + */ +bool +MetadataNode::CheckClassHierarchy(JEnv &env, jclass currentClass, MetadataTreeNode *currentTreeNode, + MetadataTreeNode *baseTreeNode, + std::vector &skippedBaseTypes) { + auto shouldSkipBaseClass = false; + if ((currentClass != nullptr) && (baseTreeNode != currentTreeNode) && + (baseTreeNode != nullptr) && + (baseTreeNode->offsetValue > 0)) { + auto baseNode = GetOrCreateInternal(baseTreeNode); + auto baseClass = env.FindClass(baseNode->m_name); + if (baseClass != nullptr) { + auto isBaseClass = env.IsAssignableFrom(currentClass, baseClass) == JNI_TRUE; + if (!isBaseClass) { + skippedBaseTypes.push_back(baseTreeNode); + shouldSkipBaseClass = true; + } + } + } + return shouldSkipBaseClass; +} + +void MetadataNode::SetMissingBaseMethods( + napi_env env, const std::vector &skippedBaseTypes, + const std::vector &instanceMethodData, + napi_value constructor) { + for (auto treeNode: skippedBaseTypes) { + uint8_t *curPtr = s_metadataReader.GetValueData() + treeNode->offsetValue + 1; + + auto nodeType = s_metadataReader.GetNodeType(treeNode); + auto curType = s_metadataReader.ReadTypeName(treeNode); + curPtr += sizeof(uint16_t /* baseClassId */); + + if (s_metadataReader.IsNodeTypeInterface(nodeType)) { + curPtr += sizeof(uint8_t) + sizeof(uint32_t); + } + + // Get candidates from instance methods metadata + auto instanceMethodCount = *reinterpret_cast(curPtr); + curPtr += sizeof(uint16_t); + MethodCallbackData *callbackData = nullptr; + + for (auto i = 0; i < instanceMethodCount; i++) { + auto entry = MetadataReader::ReadInstanceMethodEntry(&curPtr); + auto &methodName = entry.getName(); + auto isConstructor = methodName == ""; + if (isConstructor) { + continue; + } + + for (auto data: instanceMethodData) { + if (data->candidates.front().name == methodName) { + callbackData = data; + break; + } + } + + if (callbackData == nullptr) { + callbackData = new MethodCallbackData(this); + napi_value proto = napi_util::get_prototype(env, constructor); + napi_value method; + napi_create_function(env, methodName.c_str(), NAPI_AUTO_LENGTH, MethodCallback, + callbackData, &method); + napi_set_named_property(env, proto, methodName.c_str(), method); + } + + bool foundSameSig = false; + for (auto &m: callbackData->candidates) { + foundSameSig = m.getSig() == entry.getSig(); + if (foundSameSig) { + break; + } + } + + if (!foundSameSig) { + callbackData->candidates.push_back(std::move(entry)); + } + } + } +} + +void MetadataNode::BuildMetadata(const std::string &filesPath) { + s_metadataReader = MetadataBuilder::BuildMetadata(filesPath); +} + +void MetadataNode::onDisposeEnv(napi_env env) { + { + auto it = s_metadata_node_cache.Get(env); + if (it != nullptr) { + for (const auto &entry: it->CtorFuncCache) { + if (entry.second.constructorFunction == nullptr) { + napi_delete_reference(env, entry.second.constructorFunction); + } + for (const auto data: entry.second.instanceMethodCallbacks) { + delete data; + } + } + it->CtorFuncCache.clear(); + + for (const auto &entry: it->ExtendedCtorFuncCache) { + if (entry.second.extendedCtorFunction == nullptr) { + napi_delete_reference(env, entry.second.extendedCtorFunction); + } + } + it->ExtendedCtorFuncCache.clear(); + + for (const auto &entry: it->fieldCallbackData) { + delete entry; + } + } + s_metadata_node_cache.Remove(env); + delete it; + } + { + auto it = s_arrayObjects.find(env); + if (it != s_arrayObjects.end()) { + if (it->second != nullptr) { + napi_delete_reference(env, it->second); + } + s_arrayObjects.erase(it); + } + } +} + + +string MetadataNode::TNS_PREFIX = "com/tns/gen/"; +MetadataReader MetadataNode::s_metadataReader; +robin_hood::unordered_map MetadataNode::s_name2NodeCache; +robin_hood::unordered_map MetadataNode::s_name2TreeNodeCache; +robin_hood::unordered_map MetadataNode::s_treeNode2NodeCache; +tns::ConcurrentMap MetadataNode::s_metadata_node_cache; +robin_hood::unordered_map MetadataNode::s_arrayObjects; + +bool MetadataNode::s_profilerEnabled = false; diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataNode.h b/NativeScript/ffi/jni/napi/metadata/MetadataNode.h new file mode 100644 index 000000000..bdb6537c7 --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MetadataNode.h @@ -0,0 +1,304 @@ +#ifndef METADATA_NODE_H +#define METADATA_NODE_H + +#include +#include "MetadataTreeNode.h" +#include "MetadataEntry.h" +#include "robin_hood.h" +#include "MetadataReader.h" +#include "Runtime.h" + +#include "FieldCallbackData.h" +using namespace tns; + +class MetadataNode { +public: + static void Init(napi_env env); + + static void BuildMetadata(const std::string &filesPath); + + static void CreateTopLevelNamespaces(napi_env env); + + napi_value CreateWrapper(napi_env env); + + napi_value CreateJSWrapper(napi_env env, tns::ObjectManager *objectManager); + + napi_value CreateArrayWrapper(napi_env env); + + static MetadataNode *GetOrCreate(const std::string &className); + + static MetadataReader *getMetadataReader(); + + static napi_value GetImplementationObject(napi_env env, napi_value object); + + inline static MetadataNode* GetInstanceMetadata(napi_env env, napi_value object) { + void *node; + napi_value external; + napi_get_named_property(env, object, "#instance_metadata", &external); + + if (napi_util::is_null_or_undefined(env, external)) return nullptr; + + napi_get_value_external(env, external, &node); + if (node == nullptr) + return nullptr; + return reinterpret_cast(node); + } + + inline static MetadataNode* GetNodeFromHandle(napi_env env, napi_value value) { + auto node = GetInstanceMetadata(env, value); + return node; + } + + static string GetTypeMetadataName(napi_env env, napi_value value); + + static napi_value CreateExtendedJSWrapper(napi_env env, ObjectManager *objectManager, + const std::string &proxyClassName, int javaObjectID); + + std::string GetName(); + + static void onDisposeEnv(napi_env env); + + bool isArray(); + +private: + struct CtorCacheData; + struct PackageGetterMethodData; + struct MethodCallbackData; + struct ExtendedClassCallbackData; + struct ExtendedClassCacheData; + struct MetadataNodeCache; + + static string CreateFullClassName(const std::string& className, const std::string& extendNameAndLocation); + + static napi_value CreateArrayObjectConstructor(napi_env env); + + static void SetInstanceMetadata(napi_env env, napi_value object, MetadataNode* node); + + + static bool + CheckClassHierarchy(JEnv &env, jclass currentClass, MetadataTreeNode *currentTreeNode, + MetadataTreeNode *baseTreeNode, + std::vector &skippedBaseTypes); + + static MetadataNode *GetOrCreateInternal(MetadataTreeNode *treeNode); + + static MetadataNodeCache *GetMetadataNodeCache(napi_env env); + + explicit MetadataNode(MetadataTreeNode *treeNode); + + void SetMissingBaseMethods( + napi_env env, const std::vector &skippedBaseTypes, + const std::vector &instanceMethodData, + napi_value ctor); + + + + + napi_value GetConstructorFunction(napi_env env); + + napi_value GetConstructorFunctionInternal(napi_env env, MetadataTreeNode *treeNode, + std::vector instanceMethodsCallbackData); + + napi_value CreatePackageObject(napi_env env); + + + static bool IsValidExtendName(napi_env env, napi_value name); + static bool GetExtendLocation(napi_env env, std::string& extendLocation, bool isTypeScriptExtend); + static ExtendedClassCacheData GetCachedExtendedClassData(napi_env env, const std::string& proxyClassName); + static std::string GetJniClassName(const MetadataTreeNode* node); + + + static void SetClassAccessor(napi_env env, napi_value constructor); + + static MetadataEntry GetChildMetadataForPackage(MetadataNode *node, const char *propName); + + static MetadataTreeNode *GetOrCreateTreeNodeByName(const std::string &className); + + bool IsNodeTypeInterface(); + + std::vector SetClassMembersFromStaticMetadata( + napi_env env, napi_value constructor, + std::vector &instanceMethodsCallbackData, + const std::vector &baseInstanceMethodsCallbackData, + MetadataTreeNode *treeNode); + + std::vector SetInstanceMembersFromRuntimeMetadata( + napi_env env, napi_value constructor, + std::vector &instanceMethodsCallbackData, + const std::vector &baseInstanceMethodsCallbackData, + MetadataTreeNode *treeNode); + + + inline static MethodCallbackData *tryGetExtensionMethodCallbackData( + const robin_hood::unordered_map &collectedMethodCallbackData, + const std::string &lookupName); + + std::vector SetClassMembers( + napi_env env, napi_value constructor, + std::vector &instanceMethodsCallbackData, + const std::vector &baseInstanceMethodsCallbackData, + MetadataTreeNode *treeNode); + + + static napi_value NullObjectAccessorGetterCallback(napi_env env, napi_callback_info info); + + static napi_value FieldAccessorGetterCallback(napi_env env, napi_callback_info info); + + static napi_value FieldAccessorSetterCallback(napi_env env, napi_callback_info info); + + static napi_value ArraySetterCallback(napi_env env, napi_callback_info info); + + static napi_value ArrayGetterCallback(napi_env env, napi_callback_info info); + + static napi_value ArrayGetAllValuesCallback(napi_env env, napi_callback_info info); + + static napi_value ArrayLengthCallback(napi_env env, napi_callback_info info); + + static napi_value PropertyAccessorGetterCallback(napi_env env, napi_callback_info info); + + static napi_value PropertyAccessorSetterCallback(napi_env env, napi_callback_info info); + + static napi_value ExtendMethodCallback(napi_env env, napi_callback_info info); + + static napi_value MethodCallback(napi_env env, napi_callback_info info); + + static napi_value ClassAccessorGetterCallback(napi_env env, napi_callback_info info); + + static napi_value PackageGetterCallback(napi_env env, napi_callback_info info); + + static napi_value ExtendedClassConstructorCallback(napi_env env, napi_callback_info info); + + static napi_value InterfaceConstructorCallback(napi_env env, napi_callback_info info); + + static napi_value ClassConstructorCallback(napi_env env, napi_callback_info info); + + static void SetInnerTypes(napi_env env, napi_value constructor, MetadataTreeNode *treeNode); + + static napi_value InnerTypeGetterCallback(napi_env env, napi_callback_info info); + + static napi_value NullValueOfCallback(napi_env env, napi_callback_info info); + + + static void RegisterSymbolHasInstanceCallback(napi_env env, const MetadataTreeNode *treeNode, napi_value interface); + + static napi_value SymbolHasInstanceCallback(napi_env env, napi_callback_info info); + + static napi_value SuperAccessorGetterCallback(napi_env env, napi_callback_info info); + + static bool ValidateExtendArguments(napi_env env, size_t argc, napi_value * argv, bool extendLocationFound, string &extendLocation, napi_value* extendName, napi_value* implementationObject, bool isTypeScriptExtend); + + MetadataTreeNode *m_treeNode; + + std::string m_name; + std::string m_implType; + bool m_isArray; + + static bool IsJavascriptKeyword(const std::string &word); + + static std::string TNS_PREFIX; + static MetadataReader s_metadataReader; + + static robin_hood::unordered_map s_name2NodeCache; + static robin_hood::unordered_map s_name2TreeNodeCache; + static robin_hood::unordered_map s_treeNode2NodeCache; + static tns::ConcurrentMap s_metadata_node_cache; + static robin_hood::unordered_map s_arrayObjects; + + struct CtorCacheData { + CtorCacheData(napi_ref _constructorFunction, + std::vector _instanceMethodCallbacks) + : + constructorFunction(_constructorFunction), + instanceMethodCallbacks(_instanceMethodCallbacks) { + } + + napi_ref constructorFunction; + std::vector instanceMethodCallbacks; + }; + + struct MethodCallbackData { + MethodCallbackData() + : + node(nullptr), parent(nullptr), isSuper(false) { + } + + MethodCallbackData(MetadataNode *_node) + : + node(_node), parent(nullptr), isSuper(false) { + } + + std::vector candidates; + MetadataNode *node; + MethodCallbackData *parent; + bool isSuper; + }; + + struct PackageGetterMethodData { + PackageGetterMethodData() : utf8name(nullptr), node(nullptr), value(nullptr) {} + + PackageGetterMethodData(const char *_utf8name, MetadataNode *_node, napi_ref _value) + : utf8name(_utf8name), node(_node), value(_value) {} + + const char *utf8name; + MetadataNode *node; + napi_ref value; + }; + + struct ExtendedClassCacheData { + ExtendedClassCacheData() + : + extendedCtorFunction(nullptr), node(nullptr) { + } + + ExtendedClassCacheData(napi_ref extCtorFunc, const std::string &_extendedName, + MetadataNode *_node) + : + extendedName(_extendedName), node(_node) { + extendedCtorFunction = extCtorFunc; + } + + napi_ref extendedCtorFunction; + std::string extendedName; + MetadataNode *node; + }; + + struct PropertyCallbackData { + PropertyCallbackData(std::string _propertyName, std::string _getterMethodName, + std::string _setterMethodName) + : + propertyName(_propertyName), getterMethodName(_getterMethodName), + setterMethodName(_setterMethodName) { + + } + + std::string propertyName; + std::string getterMethodName; + std::string setterMethodName; + }; + + struct ExtendedClassCallbackData { + ExtendedClassCallbackData(MetadataNode *_node, const std::string &_extendedName, + napi_ref _implementationObject, std::string _fullClassName) + : + node(_node), extendedName(_extendedName), fullClassName(_fullClassName) { + implementationObject = _implementationObject; + } + + MetadataNode *node; + std::string extendedName; + napi_ref implementationObject; + + std::string fullClassName; + }; + + struct MetadataNodeCache { + robin_hood::unordered_map CtorFuncCache; + robin_hood::unordered_map ExtendedCtorFuncCache; + std::vector fieldCallbackData; + }; + + static bool s_profilerEnabled; + +}; + +#endif //METADATA_NODE_H \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataReader.cpp b/NativeScript/ffi/jni/napi/metadata/MetadataReader.cpp new file mode 100644 index 000000000..af2020493 --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MetadataReader.cpp @@ -0,0 +1,364 @@ +#include "MetadataReader.h" +#include "MetadataMethodInfo.h" +#include +#include "Util.h" +#include + +using namespace std; +using namespace tns; + +MetadataReader::MetadataReader() : m_root(nullptr), m_nodesLength(0), m_nameLength(0), + m_valueLength(0), + m_nodeData(nullptr), m_nameData(nullptr), m_valueData(nullptr), + m_getTypeMetadataCallback(nullptr) {} + +MetadataReader::MetadataReader(uint32_t nodesLength, uint8_t *nodeData, uint32_t nameLength, + uint8_t *nameData, uint32_t valueLength, uint8_t *valueData, + GetTypeMetadataCallback getTypeMetadataCallback) + : + m_nodesLength(nodesLength), m_nameLength(nameLength), + m_valueLength(valueLength), m_nodeData(nodeData), m_nameData(nameData), + m_valueData(valueData), + m_getTypeMetadataCallback(getTypeMetadataCallback) { + m_root = BuildTree(); +} + + + +// helper debug function when need to convert a metadata node to its full name +//std::string toFullName(MetadataTreeNode* p) { +// std::string final = p->name; +// while((p = p->parent) && !p->name.empty()) { +// final.insert(0,p->name + "."); +// }; +// return final; +//} + +MetadataTreeNode *MetadataReader::BuildTree() { + MetadataTreeNodeRawData *rootNodeData = reinterpret_cast(m_nodeData); + + MetadataTreeNodeRawData *curNodeData = rootNodeData; + + int len = m_nodesLength / sizeof(MetadataTreeNodeRawData); + + m_v.resize(len + 1000); + MetadataTreeNode *emptyNode = nullptr; + fill(m_v.begin(), m_v.end(), emptyNode); + + for (int i = 0; i < len; i++) { + MetadataTreeNode *node = GetNodeById(i); + if (nullptr == node) { + node = new MetadataTreeNode; + node->name = ReadName(curNodeData->offsetName); + node->offsetValue = curNodeData->offsetValue; + m_v[i] = node; + } + + uint16_t curNodeDataId = curNodeData - rootNodeData; + + if (curNodeDataId != curNodeData->firstChildId) { + node->children = new vector; + MetadataTreeNodeRawData *childNodeData = rootNodeData + curNodeData->firstChildId; + while (true) { + + uint16_t childNodeDataId = childNodeData - rootNodeData; + + MetadataTreeNode *childNode; + // node (and its next siblings) already visited, so we don't need to visit it again + if (m_v[childNodeDataId] != emptyNode) { + childNode = m_v[childNodeDataId]; + __android_log_print(ANDROID_LOG_ERROR, "TNS.error", + "Consistency error in metadata. A child should never have been visited before its parent. Parent: %s Child: %s. Child metadata id: %u", + node->name.c_str(), childNode->name.c_str(), + childNodeDataId); + break; + } else { + childNode = new MetadataTreeNode; + childNode->name = ReadName(childNodeData->offsetName); + childNode->offsetValue = childNodeData->offsetValue; + } + childNode->parent = node; + + node->children->push_back(childNode); + + m_v[childNodeDataId] = childNode; + + if (childNodeDataId == childNodeData->nextSiblingId) { + break; + } + + childNodeData = rootNodeData + childNodeData->nextSiblingId; + } + } + + curNodeData++; + } + + return GetNodeById(0); +} + +MetadataTreeNode *MetadataReader::GetNodeById(uint16_t nodeId) { + return m_v[nodeId]; +} + + +string MetadataReader::ReadTypeName(MetadataTreeNode *treeNode) { + string name; + + auto itFound = m_typeNameCache.find(treeNode); + + if (itFound != m_typeNameCache.end()) { + name = itFound->second; + } else { + name = ReadTypeNameInternal(treeNode); + + m_typeNameCache.emplace(treeNode, name); + } + + return name; +} + +string MetadataReader::ReadTypeNameInternal(MetadataTreeNode *treeNode) { + string name; + + uint8_t prevNodeType; + + while (treeNode->parent != nullptr) { + int curNodeType = GetNodeType(treeNode); + + bool isArrayElement = treeNode->offsetValue > ARRAY_OFFSET; + + if (isArrayElement) { + uint16_t forwardNodeId = treeNode->offsetValue - ARRAY_OFFSET; + MetadataTreeNode *forwardNode = GetNodeById(forwardNodeId); + name = ReadTypeName(forwardNode); + uint8_t forwardNodeType = GetNodeType(forwardNode); + if (IsNodeTypeInterface(forwardNodeType) || IsNodeTypeClass(forwardNodeType)) { + name = "L" + name + ";"; + } + } else { + if (!name.empty()) { + if (!IsNodeTypeArray(curNodeType)) { + if ((IsNodeTypeClass(prevNodeType) || IsNodeTypeInterface(prevNodeType)) + && (IsNodeTypeClass(curNodeType) || IsNodeTypeInterface(curNodeType))) { + name = "$" + name; + } else { + name = "/" + name; + } + } + } + + name = treeNode->name + name; + + prevNodeType = curNodeType; + } + + treeNode = treeNode->parent; + } + + return name; +} + +uint8_t *MetadataReader::GetValueData() const { + return m_valueData; +} + +uint16_t MetadataReader::GetNodeId(MetadataTreeNode *treeNode) { + auto itFound = find(m_v.begin(), m_v.end(), treeNode); + assert(itFound != m_v.end()); + uint16_t nodeId = itFound - m_v.begin(); + + return nodeId; +} + +MetadataTreeNode *MetadataReader::GetRoot() const { + return m_root; +} + +uint8_t MetadataReader::GetNodeType(MetadataTreeNode *treeNode) { + if (treeNode->type == MetadataTreeNode::INVALID_TYPE) { + uint8_t nodeType; + + uint32_t offsetValue = treeNode->offsetValue; + + if (offsetValue == 0) { + nodeType = MetadataTreeNode::PACKAGE; + } else if ((0 < offsetValue) && (offsetValue < ARRAY_OFFSET)) { + nodeType = *(m_valueData + offsetValue); + } else if (offsetValue == ARRAY_OFFSET) { + nodeType = MetadataTreeNode::ARRAY; + } else { + uint16_t nodeId = offsetValue - ARRAY_OFFSET; + MetadataTreeNode *arrElemNode = GetNodeById(nodeId); + nodeType = *(m_valueData + arrElemNode->offsetValue); + } + + treeNode->type = nodeType; + } + + return treeNode->type; +} + +MetadataTreeNode *MetadataReader::GetOrCreateTreeNodeByName(const string &className) { + MetadataTreeNode *treeNode = GetRoot(); + + int arrayIdx = -1; + string arrayName = "["; + + while (className[++arrayIdx] == '[') { + MetadataTreeNode *child = treeNode->GetChild(arrayName); + + if (child == nullptr) { + vector *children = treeNode->children; + if (children == nullptr) { + children = treeNode->children = new vector; + } + + child = new MetadataTreeNode; + child->name = "["; + child->parent = treeNode; + child->offsetValue = ARRAY_OFFSET; + + children->push_back(child); + m_v.push_back(child); + } + + treeNode = child; + } + + string cn = className.substr(arrayIdx); + + if (arrayIdx > 0) { + char last = *cn.rbegin(); + if (last == ';') { + cn = cn.substr(1, cn.length() - 2); + } + } + + vector names; + Util::SplitString(cn, "/$", names); + + if (arrayIdx > 0) { + bool found = false; + MetadataTreeNode *forwardedNode = GetOrCreateTreeNodeByName(cn); + + uint16_t forwardedNodeId = GetNodeId(forwardedNode); + if (treeNode->children == nullptr) { + treeNode->children = new vector(); + } + vector &children = *treeNode->children; + for (auto childNode: children) { + uint32_t childNodeId = (childNode->offsetValue >= ARRAY_OFFSET) + ? (childNode->offsetValue - ARRAY_OFFSET) + : + GetNodeId(childNode); + + if (childNodeId == forwardedNodeId) { + treeNode = childNode; + found = true; + break; + } + } + + if (!found) { + MetadataTreeNode *forwardNode = new MetadataTreeNode; + forwardNode->offsetValue = forwardedNodeId + ARRAY_OFFSET; + forwardNode->parent = treeNode; + + m_v.push_back(forwardNode); + children.push_back(forwardNode); + + treeNode = forwardNode; + } + + return treeNode; + } + + int curIdx = 0; + for (auto it = names.begin(); it != names.end(); ++it) { + MetadataTreeNode *child = treeNode->GetChild(*it); + + if (child == nullptr) { + vector api = m_getTypeMetadataCallback(cn, curIdx); + + for (const auto &part: api) { + vector *children = treeNode->children; + if (children == nullptr) { + children = treeNode->children = new vector; + } + + child = new MetadataTreeNode; + child->name = *it++; + child->parent = treeNode; + + string line; + string kind; + string name; + stringstream s(part); + + getline(s, line); + stringstream typeLine(line); + typeLine >> kind >> name; + auto cKind = kind[0]; + + // package, class, interface + assert((cKind == 'P') || (cKind == 'C') || (cKind == 'I')); + + if ((cKind == 'C') || (cKind == 'I')) { + child->metadata = new string(part); + child->type = (cKind == 'C') ? MetadataTreeNode::CLASS + : MetadataTreeNode::INTERFACE; + if (name == "S") { + child->type |= MetadataTreeNode::STATIC; + } + + getline(s, line); + stringstream baseClassLine(line); + baseClassLine >> kind >> name; + cKind = kind[0]; + + assert(cKind == 'B'); + auto baseClassTreeNode = GetOrCreateTreeNodeByName(name); + auto baseClassNodeId = GetNodeId(baseClassTreeNode); + + child->offsetValue = m_valueLength; + m_valueData[m_valueLength++] = child->type; + *reinterpret_cast(m_valueData + m_valueLength) = baseClassNodeId; + m_valueLength += sizeof(uint16_t); + } else { + child->type = MetadataTreeNode::PACKAGE; + } + + m_v.push_back(child); + children->push_back(child); + + treeNode = child; + } + + return treeNode; + } else { + treeNode = child; + } + ++curIdx; + } + + return treeNode; +} + +MetadataTreeNode *MetadataReader::GetBaseClassNode(MetadataTreeNode *treeNode) { + MetadataTreeNode *baseClassNode = nullptr; + + if (treeNode != nullptr) { + uint16_t baseClassNodeId = *reinterpret_cast(m_valueData + + treeNode->offsetValue + 1); + + size_t nodeCount = m_v.size(); + + assert(baseClassNodeId < nodeCount); + + baseClassNode = GetNodeById(baseClassNodeId); + } + + return baseClassNode; +} + diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataReader.h b/NativeScript/ffi/jni/napi/metadata/MetadataReader.h new file mode 100644 index 000000000..8d19683d7 --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MetadataReader.h @@ -0,0 +1,238 @@ +#ifndef METADATAREADER_H_ +#define METADATAREADER_H_ + +#include "MetadataEntry.h" +#include "MetadataFieldInfo.h" +#include +#include +#include +#include "robin_hood.h" + +namespace tns { + typedef std::vector (*GetTypeMetadataCallback)(const std::string &classname, + int index); + + class MetadataReader { + public: + MetadataReader(); + + MetadataReader(uint32_t nodesLength, uint8_t *nodeData, uint32_t nameLength, + uint8_t *nameData, uint32_t valueLength, uint8_t *valueData, + GetTypeMetadataCallback getTypeMetadataCallack); + + inline static MetadataEntry ReadInstanceFieldEntry(uint8_t **data) { + MetadataEntry entry(nullptr, NodeType::Field); + entry.fi = *reinterpret_cast(data); + entry.isStatic = false; + entry.isTypeMember = false; + + *data += sizeof(FieldInfo); + + return entry; + } + + inline static MetadataEntry ReadStaticFieldEntry(uint8_t **data) { + MetadataEntry entry(nullptr, NodeType::StaticField); + entry.sfi = *reinterpret_cast(data); + entry.isStatic = true; + entry.isTypeMember = false; + + *data += sizeof(StaticFieldInfo); + + return entry; + } + + inline static MetadataEntry ReadInstanceMethodEntry(uint8_t **data) { + MetadataEntry entry(nullptr, NodeType::Method); + entry.isTypeMember = true; + + entry.mi = MethodInfo(*data); // Assign MethodInfo object directly + *data += entry.mi.GetSizeOfReadMethodInfo(); + + return entry; + } + + inline static MetadataEntry ReadStaticMethodEntry(uint8_t **data) { + MetadataEntry entry(nullptr, NodeType::Method); + entry.isTypeMember = true; + + entry.mi = MethodInfo(*data); // Assign MethodInfo object directly + entry.mi.isStatic = true; + entry.isStatic = true; + + *data += entry.mi.GetSizeOfReadMethodInfo(); + + return entry; + } + + inline static MetadataEntry ReadExtensionFunctionEntry(uint8_t **data) { + MetadataEntry entry(nullptr, NodeType::Method); + + entry.mi = MethodInfo(*data); // Assign MethodInfo object directly + entry.mi.isStatic = true; + entry.isExtensionFunction = true; + entry.isStatic = true; + + *data += entry.mi.GetSizeOfReadMethodInfo(); + + return entry; + } + + inline std::string ReadTypeName(uint16_t nodeId) { + MetadataTreeNode *treeNode = GetNodeById(nodeId); + + return ReadTypeName(treeNode); + } + + std::string ReadTypeName(MetadataTreeNode *treeNode); + + inline std::string ReadName(uint32_t offset) { + uint16_t length = *reinterpret_cast(m_nameData + offset); + + std::string name(reinterpret_cast(m_nameData + offset + sizeof(uint16_t)), + length); + + return name; + } + + inline std::string + ReadInterfaceImplementationTypeName(MetadataTreeNode *treeNode, bool &isPrefix) { + uint8_t *data = + m_valueData + treeNode->offsetValue + sizeof(uint8_t) + sizeof(uint16_t); + + isPrefix = *data == 1; + + uint32_t pos = *reinterpret_cast(data + sizeof(uint8_t)); + + uint16_t len = *reinterpret_cast(m_nameData + pos); + + char *ptr = reinterpret_cast(m_nameData + pos + sizeof(uint16_t)); + + std::string name(ptr, len); + + assert(name.length() == len); + + return name; + } + + uint8_t *GetValueData() const; + + uint8_t GetNodeType(MetadataTreeNode *treeNode); + + uint16_t GetNodeId(MetadataTreeNode *treeNode); + + MetadataTreeNode *GetRoot() const; + + MetadataTreeNode *GetOrCreateTreeNodeByName(const std::string &className); + + MetadataTreeNode *GetBaseClassNode(MetadataTreeNode *treeNode); + + MetadataTreeNode *GetNodeById(uint16_t nodeId); + + inline bool IsNodeTypeArray(uint8_t type) { + bool isArray = (((type & MetadataTreeNode::PRIMITIVE) == 0) && + ((type & MetadataTreeNode::ARRAY) == MetadataTreeNode::ARRAY)); + + return isArray; + } + + inline bool IsNodeTypeStatic(uint8_t type) { + bool isStatic = (type & MetadataTreeNode::STATIC) == MetadataTreeNode::STATIC; + + return isStatic; + } + + inline bool IsNodeTypeClass(uint8_t type) { + bool isClass = (((type & MetadataTreeNode::PRIMITIVE) == 0) && + ((type & MetadataTreeNode::CLASS) == MetadataTreeNode::CLASS)); + + return isClass; + } + + inline bool IsNodeTypeInterface(uint8_t type) { + bool isInterface = (((type & MetadataTreeNode::PRIMITIVE) == 0) && + ((type & MetadataTreeNode::INTERFACE) == + MetadataTreeNode::INTERFACE)); + + return isInterface; + } + + inline bool IsNodeTypePackage(uint8_t type) { + bool isPackage = type == MetadataTreeNode::PACKAGE; + + return isPackage; + } + + inline static std::string ParseReturnType(const std::string &signature) { + int idx = signature.find(')'); + auto returnType = signature.substr(idx + 1); + return returnType; + } + + inline static MethodReturnType GetReturnType(const std::string &returnType) { + MethodReturnType retType; + char retTypePrefix = returnType[0]; + switch (retTypePrefix) { + case 'V': + retType = MethodReturnType::Void; + break; + case 'B': + retType = MethodReturnType::Byte; + break; + case 'S': + retType = MethodReturnType::Short; + break; + case 'I': + retType = MethodReturnType::Int; + break; + case 'J': + retType = MethodReturnType::Long; + break; + case 'F': + retType = MethodReturnType::Float; + break; + case 'D': + retType = MethodReturnType::Double; + break; + case 'C': + retType = MethodReturnType::Char; + break; + case 'Z': + retType = MethodReturnType::Boolean; + break; + case '[': + case 'L': + retType = (returnType == "Ljava/lang/String;") + ? MethodReturnType::String + : MethodReturnType::Object; + break; + default: + assert(false); + break; + } + return retType; + } + + private: +// static const uint32_t ARRAY_OFFSET = 1000000000; + static const uint32_t ARRAY_OFFSET = INT32_MAX; // 2147483647 + + MetadataTreeNode *BuildTree(); + + std::string ReadTypeNameInternal(MetadataTreeNode *treeNode); + + MetadataTreeNode *m_root; + uint32_t m_nodesLength; + uint32_t m_nameLength; + uint32_t m_valueLength; + uint8_t *m_nodeData; + uint8_t *m_nameData; + uint8_t *m_valueData; + std::vector m_v; + GetTypeMetadataCallback m_getTypeMetadataCallback; + + robin_hood::unordered_map m_typeNameCache; + }; +} + +#endif /* METADATAREADER_H_ */ diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataTreeNode.cpp b/NativeScript/ffi/jni/napi/metadata/MetadataTreeNode.cpp new file mode 100644 index 000000000..d8f909084 --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MetadataTreeNode.cpp @@ -0,0 +1,26 @@ +#include "MetadataTreeNode.h" + +using namespace std; +using namespace tns; + +MetadataTreeNode::MetadataTreeNode() + : + children(nullptr), parent(nullptr), metadata(nullptr), offsetValue(0), type(INVALID_TYPE) { +} + +MetadataTreeNode* MetadataTreeNode::GetChild(const string& childName) { + MetadataTreeNode* child = nullptr; + + if (children != nullptr) { + auto itEnd = children->end(); + auto itFound = find_if(children->begin(), itEnd, [&childName] (MetadataTreeNode *x) { + return x->name == childName; + }); + if (itFound != itEnd) { + child = *itFound; + } + } + + return child; +} + diff --git a/NativeScript/ffi/jni/napi/metadata/MetadataTreeNode.h b/NativeScript/ffi/jni/napi/metadata/MetadataTreeNode.h new file mode 100644 index 000000000..e30cfb1ea --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MetadataTreeNode.h @@ -0,0 +1,49 @@ +#ifndef TREENODE_H_ +#define TREENODE_H_ + +#include +#include + +namespace tns { +struct MetadataTreeNode { + MetadataTreeNode(); + + MetadataTreeNode* GetChild(const std::string& name); + + std::string name; + MetadataTreeNode* parent; + uint32_t offsetValue; + std::vector* children; + // + std::string* metadata; + uint8_t type; + + static const uint8_t PACKAGE = 0; + static const uint8_t CLASS = 1 << 0; + static const uint8_t INTERFACE = 1 << 1; + static const uint8_t STATIC = 1 << 2; + static const uint8_t ARRAY = 1 << 3; + static const uint8_t PRIMITIVE = 1 << 4; + + static const uint8_t FINAL = 1; + + static const uint8_t PRIMITIVE_BYTE = 1 + PRIMITIVE; + static const uint8_t PRIMITIVE_SHORT = 2 + PRIMITIVE; + static const uint8_t PRIMITIVE_INT = 3 + PRIMITIVE; + static const uint8_t PRIMITIVE_LONG = 4 + PRIMITIVE; + static const uint8_t PRIMITIVE_FLOAT = 5 + PRIMITIVE; + static const uint8_t PRIMITIVE_DOUBLE = 6 + PRIMITIVE; + static const uint8_t PRIMITIVE_BOOL = 7 + PRIMITIVE; + static const uint8_t PRIMITIVE_CHAR = 8 + PRIMITIVE; + static const uint8_t INVALID_TYPE = 0xFF; +}; + +struct MetadataTreeNodeRawData { + uint16_t firstChildId; + uint16_t nextSiblingId; + uint32_t offsetName; + uint32_t offsetValue; +}; +} + +#endif /* TREENODE_H_ */ diff --git a/NativeScript/ffi/jni/napi/metadata/MethodCache.cpp b/NativeScript/ffi/jni/napi/metadata/MethodCache.cpp new file mode 100644 index 000000000..24a1f0f4b --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MethodCache.cpp @@ -0,0 +1,34 @@ +#include "MethodCache.h" +#include "JniLocalRef.h" +#include "JsArgToArrayConverter.h" +#include "MetadataNode.h" +#include "NativeScriptAssert.h" +#include "Util.h" +#include "ArgConverter.h" +#include "NumericCasts.h" +#include "NativeScriptException.h" +#include "Runtime.h" +#include + +using namespace std; +using namespace tns; + +void MethodCache::Init() +{ + JEnv jEnv; + + RUNTIME_CLASS = jEnv.FindClass("com/tns/Runtime"); + assert(RUNTIME_CLASS != nullptr); + + RESOLVE_METHOD_OVERLOAD_METHOD_ID = jEnv.GetMethodID(RUNTIME_CLASS, "resolveMethodOverload", "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;"); + assert(RESOLVE_METHOD_OVERLOAD_METHOD_ID != nullptr); + + RESOLVE_CONSTRUCTOR_SIGNATURE_ID = jEnv.GetMethodID(RUNTIME_CLASS, "resolveConstructorSignature", "(Ljava/lang/Class;[Ljava/lang/Object;)Ljava/lang/String;"); + assert(RESOLVE_CONSTRUCTOR_SIGNATURE_ID != nullptr); +} + + +robin_hood::unordered_map MethodCache::s_method_ctor_signature_cache; +jclass MethodCache::RUNTIME_CLASS = nullptr; +jmethodID MethodCache::RESOLVE_METHOD_OVERLOAD_METHOD_ID = nullptr; +jmethodID MethodCache::RESOLVE_CONSTRUCTOR_SIGNATURE_ID = nullptr; diff --git a/NativeScript/ffi/jni/napi/metadata/MethodCache.h b/NativeScript/ffi/jni/napi/metadata/MethodCache.h new file mode 100644 index 000000000..bcf8e17d8 --- /dev/null +++ b/NativeScript/ffi/jni/napi/metadata/MethodCache.h @@ -0,0 +1,369 @@ +#ifndef METHODCACHE_H_ +#define METHODCACHE_H_ + +#include +#include +#include "JEnv.h" +#include "MetadataEntry.h" +#include "ArgsWrapper.h" +#include "NativeScriptAssert.h" +#include "MetadataReader.h" +#include "Runtime.h" +#include "MetadataNode.h" +#include "NumericCasts.h" +#include "NativeScriptException.h" +#include "JsArgToArrayConverter.h" +#include "Util.h" + +namespace tns { +/* + * MethodCache: class dealing with method/constructor resolution. + */ +class MethodCache { + public: + /* + * CacheMethodInfo: struct holding resolved methods/constructor resolution + */ + struct CacheMethodInfo { + CacheMethodInfo() + : + retType(MethodReturnType::Unknown), mid(nullptr), clazz(nullptr), isStatic(false) { + } + std::string signature; + std::string returnType; + MethodReturnType retType; + jmethodID mid; + jclass clazz; + bool isStatic; + }; + + static void Init(); + + inline static MethodCache::CacheMethodInfo ResolveMethodSignature(napi_env env, const string &className, const string &methodName, size_t argc, napi_value* argv, bool isStatic) + { + CacheMethodInfo method_info; + + auto encoded_method_signature = EncodeSignature(env, className, methodName,argc, argv, isStatic); + auto it = s_method_ctor_signature_cache.find(encoded_method_signature); + + if (it == s_method_ctor_signature_cache.end()) + { + auto signature = ResolveJavaMethod(env, argc, argv, className, methodName); + + DEBUG_WRITE("ResolveMethodSignature %s='%s'", encoded_method_signature.c_str(), signature.c_str()); + + if (!signature.empty()) + { + JEnv jEnv; + auto clazz = jEnv.FindClass(className); + assert(clazz != nullptr); + method_info.clazz = clazz; + method_info.signature = signature; + method_info.returnType = MetadataReader::ParseReturnType(method_info.signature); + method_info.retType = MetadataReader::GetReturnType(method_info.returnType); + method_info.isStatic = isStatic; + method_info.mid = isStatic + ? jEnv.GetStaticMethodID(clazz, methodName, signature) + : jEnv.GetMethodID(clazz, methodName, signature); + + s_method_ctor_signature_cache.emplace(encoded_method_signature, method_info); + } + } + else + { + method_info = (*it).second; + } + + return method_info; + } + + inline static MethodCache::CacheMethodInfo ResolveConstructorSignature(napi_env env, const ArgsWrapper &argWrapper, const string &fullClassName, jclass javaClass, bool isInterface) + { + CacheMethodInfo constructor_info; + + auto encoded_ctor_signature = EncodeSignature(env, fullClassName, "", argWrapper.argc, argWrapper.argv, false); + auto it = s_method_ctor_signature_cache.find(encoded_ctor_signature); + + if (it == s_method_ctor_signature_cache.end()) + { + auto signature = ResolveConstructor(env, argWrapper.argc, argWrapper.argv, javaClass, isInterface); + + DEBUG_WRITE("ResolveConstructorSignature %s='%s'", encoded_ctor_signature.c_str(), signature.c_str()); + + if (!signature.empty()) + { + JEnv jEnv; + constructor_info.clazz = javaClass; + constructor_info.signature = signature; + constructor_info.mid = jEnv.GetMethodID(javaClass, "", signature); + + s_method_ctor_signature_cache.emplace(encoded_ctor_signature, constructor_info); + } + } + else + { + constructor_info = (*it).second; + } + + return constructor_info; + } + +private: + MethodCache() { + } + + // Encoded signature .S/I....<...> + inline static string EncodeSignature(napi_env env, const string &className, const string &methodName, size_t argc, napi_value* argv, bool isStatic) + { + string sig(className); + sig.append("."); + if (isStatic) + { + sig.append("S."); + } + else + { + sig.append("I."); + } + sig.append(methodName); + sig.append("."); + + stringstream s; + s << argc; + sig.append(s.str()); + + for (int i = 0; i < argc; i++) + { + sig.append("."); + sig.append(GetType(env, argv[i])); + } + + return sig; + } + + inline static string GetType(napi_env env, napi_value value) + { + napi_valuetype valueType; + napi_typeof(env, value, &valueType); + string type = ""; + + if (valueType == napi_object || valueType == napi_function) + { + + napi_value nullNode; + napi_get_named_property(env, value, PROP_KEY_NULL_NODE_NAME, &nullNode); + + if (!napi_util::is_null_or_undefined(env, nullNode)) + { + void *data; + napi_get_value_external(env, nullNode, &data); + auto treeNode = reinterpret_cast(data); + + type = (treeNode != nullptr) ? treeNode->GetName() : ""; + + DEBUG_WRITE("Parameter of type %s with NULL value is passed to the method.", type.c_str()); + return type; + } + } + + + if (valueType == napi_string) { + type = "string"; + } else if (valueType == napi_null) { + type = "null"; + } else if (valueType == napi_undefined) { + type = "undefined"; + } else if (valueType == napi_number) { + type = "number"; + } else if (valueType == napi_object) { + type = "object"; + } else if (napi_util::is_array(env, value)) { + type = "array"; + } else if (valueType == napi_function) { + type = "function"; + } else if (napi_util::is_typedarray(env, value)) { + type = "typedarray"; + } else if (valueType == napi_boolean) { + type = "bool"; + } else if (napi_util::is_dataview(env, value)) { + type = "view"; + } else if (napi_util::is_date(env, value)) { + type = "date"; + } + + // Handle special cases for typed arrays + if (type == "typedarray") + { + napi_typedarray_type arrayType; + napi_get_typedarray_info(env, value, &arrayType, nullptr, nullptr, nullptr, nullptr); + switch (arrayType) + { + case napi_int8_array: + case napi_uint8_array: + case napi_uint8_clamped_array: + type = "bytebuffer"; + break; + case napi_int16_array: + case napi_uint16_array: + type = "shortbuffer"; + break; + case napi_int32_array: + case napi_uint32_array: + type = "intbuffer"; + break; + case napi_bigint64_array: + case napi_biguint64_array: + type = "longbuffer"; + break; + case napi_float32_array: + type = "floatbuffer"; + break; + case napi_float64_array: + type = "doublebuffer"; + break; + default: + type = ""; + } + } + + // Handle special cases for numbers + if (type == "number") + { + double d; + napi_get_value_double(env, value, &d); + int64_t i = (int64_t)d; + bool isInteger = d == i; + type = isInteger ? "intnumber" : "doublenumber"; + } + + // Handle special cases for objects + if (type == "object" || type == "function") + { + auto castType = NumericCasts::GetCastType(env, value); + MetadataNode *node; + + switch (castType) + { + case CastType::Char: + type = "char"; + break; + case CastType::Byte: + type = "byte"; + break; + case CastType::Short: + type = "short"; + break; + case CastType::Long: + type = "long"; + break; + case CastType::Float: + type = "float"; + break; + case CastType::Double: + type = "double"; + break; + case CastType::None: + node = MetadataNode::GetNodeFromHandle(env, value); + type = (node != nullptr) ? node->GetName() : ""; + + if (type == "") { + if (napi_util::is_number_object(env, value)) { + napi_value numValue = napi_util::valueOf(env, value); + bool isFloat = napi_util::is_float(env, numValue); + if (isFloat) { + type = "float"; + } else { + type = "int"; + } + } else if (napi_util::is_string_object(env, value)) { + type = "string"; + } else if (napi_util::is_number_object(env, value)) { + type = "bool"; + } + } + + break; + default: + throw NativeScriptException("Unsupported cast type"); + } + } + + if (type == "undefined") { + type = "null"; + } + + return type; + } + + inline static string ResolveJavaMethod(napi_env env , size_t argc, napi_value* argv, const string &className, const string &methodName) + { + JEnv jEnv; + + JsArgToArrayConverter argConverter(env, argc, argv, false); + + auto canonicalClassName = Util::ConvertFromJniToCanonicalName(className); + JniLocalRef jsClassName(jEnv.NewStringUTF(canonicalClassName.c_str())); + JniLocalRef jsMethodName(jEnv.NewStringUTF(methodName.c_str())); + + jobjectArray arrArgs = argConverter.ToJavaArray(); + + auto runtime = Runtime::GetRuntime(env); + + jstring signature = (jstring)jEnv.CallObjectMethod(runtime->GetJavaRuntime(), RESOLVE_METHOD_OVERLOAD_METHOD_ID, (jstring)jsClassName, (jstring)jsMethodName, arrArgs); + + string resolvedSignature; + + const char *str = jEnv.GetStringUTFChars(signature, nullptr); + resolvedSignature = string(str); + jEnv.ReleaseStringUTFChars(signature, str); + + jEnv.DeleteLocalRef(signature); + + return resolvedSignature; + } + + inline static string ResolveConstructor(napi_env env, size_t argc, napi_value* argv, jclass javaClass, bool isInterface) + { + JEnv jEnv; + string resolvedSignature; + + JsArgToArrayConverter argConverter(env, argc, argv, isInterface); + if (argConverter.IsValid()) + { + jobjectArray javaArgs = argConverter.ToJavaArray(); + + auto runtime = Runtime::GetRuntime(env); + + jstring signature = (jstring)jEnv.CallObjectMethod(runtime->GetJavaRuntime(), RESOLVE_CONSTRUCTOR_SIGNATURE_ID, javaClass, javaArgs); + + const char *str = jEnv.GetStringUTFChars(signature, nullptr); + resolvedSignature = string(str); + jEnv.ReleaseStringUTFChars(signature, str); + jEnv.DeleteLocalRef(signature); + } + else + { + JsArgToArrayConverter::Error err = argConverter.GetError(); + throw NativeScriptException(err.msg); + } + + return resolvedSignature; + } + + static jclass RUNTIME_CLASS; + + static jmethodID RESOLVE_METHOD_OVERLOAD_METHOD_ID; + + static jmethodID RESOLVE_CONSTRUCTOR_SIGNATURE_ID; + + /* + * "s_method_ctor_signature_cache" holding all resolved CacheMethodInfo against an encoded_signature string. + * Used for caching the resolved constructor or method signature. + * The encoded signature has template: .S/I....<...> + */ + static robin_hood::unordered_map s_method_ctor_signature_cache; +}; +} + +#endif /* METHODCACHE_H_ */ + diff --git a/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.cpp b/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.cpp new file mode 100644 index 000000000..d60735381 --- /dev/null +++ b/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.cpp @@ -0,0 +1,630 @@ +#include "ObjectManager.h" +#include "NativeScriptAssert.h" +#include "MetadataNode.h" +#include "ArgConverter.h" +#include "Util.h" +#include "NativeScriptException.h" +#include "Runtime.h" +#include +#include + +using namespace std; +using namespace tns; + +ObjectManager::ObjectManager(jobject javaRuntimeObject) : + m_javaRuntimeObject(javaRuntimeObject), + m_cache(NewWeakGlobalRefCallback, DeleteWeakGlobalRefCallback, 1000, this), + m_currentObjectId(0), + m_jsObjectProxyCreator(nullptr), + m_jsObjectCtor(nullptr), + m_env(nullptr) { + + JEnv env; + auto runtimeClass = env.FindClass("com/tns/Runtime"); + assert(runtimeClass != nullptr); + + GET_JAVAOBJECT_BY_ID_METHOD_ID = env.GetMethodID(runtimeClass, "getJavaObjectByID", + "(I)Ljava/lang/Object;"); + assert(GET_JAVAOBJECT_BY_ID_METHOD_ID != nullptr); + + GET_OR_CREATE_JAVA_OBJECT_ID_METHOD_ID = env.GetMethodID(runtimeClass, + "getOrCreateJavaObjectID", + "(Ljava/lang/Object;)I"); + assert(GET_OR_CREATE_JAVA_OBJECT_ID_METHOD_ID != nullptr); + + MAKE_INSTANCE_WEAK_METHOD_ID = env.GetMethodID(runtimeClass, "makeInstanceWeak", + "(I)V"); + assert(MAKE_INSTANCE_WEAK_METHOD_ID != nullptr); + + MAKE_INSTANCE_WEAK_BATCH_METHOD_ID = env.GetMethodID(runtimeClass, "makeInstanceWeak", + "(Ljava/nio/ByteBuffer;IZ)V"); + assert(MAKE_INSTANCE_WEAK_BATCH_METHOD_ID != nullptr); + + MAKE_INSTANCE_STRONG_METHOD_ID = env.GetMethodID(runtimeClass, "makeInstanceStrong", + "(I)V"); + assert(MAKE_INSTANCE_STRONG_METHOD_ID != nullptr); + + JAVA_LANG_CLASS = env.FindClass("java/lang/Class"); + assert(JAVA_LANG_CLASS != nullptr); + + GET_NAME_METHOD_ID = env.GetMethodID(JAVA_LANG_CLASS, "getName", "()Ljava/lang/String;"); + assert(GET_NAME_METHOD_ID != nullptr); +} + + +void ObjectManager::Init(napi_env env) { + m_env = env; + napi_value jsObjectCtor; + napi_define_class(env, "JSObject", NAPI_AUTO_LENGTH, JSObjectConstructorCallback, nullptr, + 0, + nullptr, &jsObjectCtor); + + napi_set_named_property(env, napi_util::get_prototype(env, jsObjectCtor), PRIVATE_IS_NAPI, + napi_util::get_true(env)); + m_jsObjectCtor = napi_util::make_ref(env, jsObjectCtor, 1); +} + + +void ObjectManager::OnDisposeEnv() { + JEnv jEnv; + if (this->m_jsObjectCtor) napi_delete_reference(m_env, this->m_jsObjectCtor); + if (this->m_jsObjectProxyCreator) napi_delete_reference(m_env, this->m_jsObjectProxyCreator); + + for (auto &entry: m_idToProxy) { + if (!entry.second) continue; + napi_delete_reference(m_env, entry.second); + } + m_idToProxy.clear(); + + for (auto &entry: m_idToObject) { + if (!entry.second) continue; + napi_delete_reference(m_env, entry.second); + } + m_idToObject.clear(); +} + +napi_value ObjectManager::GetOrCreateProxyWeak(jint javaObjectID, napi_value instance) { + napi_value proxy = nullptr; +#ifdef USE_HOST_OBJECT + bool is_array = false; + napi_value getter = nullptr; + napi_value setter = nullptr; + + napi_has_named_property(m_env, instance, "__is__javaArray", &is_array); + void* data; + napi_unwrap(m_env, instance, &data); + + if (is_array) { + napi_value global; + napi_get_global(m_env, &global); + napi_get_named_property(m_env, global, "getNativeArrayProp", &getter); + napi_get_named_property(m_env, global, "setNativeArrayProp", &setter); + } + + napi_create_host_object(m_env, instance, nullptr, data, is_array, getter, setter, &proxy); +#else + napi_value argv[2]; + argv[0] = instance; + napi_create_int32(m_env, javaObjectID, &argv[1]); + + if (!this->m_jsObjectProxyCreator) { + napi_value jsObjectProxyCreator; + napi_get_named_property(m_env, napi_util::global(m_env), "__createNativeProxy", + &jsObjectProxyCreator); + this->m_jsObjectProxyCreator = napi_util::make_ref(m_env, jsObjectProxyCreator); + } + + napi_call_function(m_env, napi_util::global(m_env), + napi_util::get_ref_value(m_env, this->m_jsObjectProxyCreator), + 2, argv, &proxy); + +#endif + return proxy; +} + +napi_value ObjectManager::GetOrCreateProxy(jint javaObjectID, napi_value instance) { + napi_value proxy = nullptr; + auto it = m_idToProxy.find(javaObjectID); + if (it != m_idToProxy.end() && it->second != nullptr) { + proxy = napi_util::get_ref_value(m_env, it->second); + if (!napi_util::is_null_or_undefined(m_env, proxy)) { + return proxy; + } else { + napi_delete_reference(m_env, it->second); + m_idToProxy.erase(javaObjectID); + } + } + + DEBUG_WRITE("%s %d", "Creating a new proxy for java object with id:", javaObjectID); + +#ifdef USE_HOST_OBJECT + bool is_array = false; + napi_value getter = nullptr; + napi_value setter = nullptr; + + napi_has_named_property(m_env, instance, "__is__javaArray", &is_array); + + auto data = new JSInstanceInfo(javaObjectID, nullptr); + + if (is_array) { + napi_value global; + napi_get_global(m_env, &global); + napi_get_named_property(m_env, global, "getNativeArrayProp", &getter); + napi_get_named_property(m_env, global, "setNativeArrayProp", &setter); + } + + napi_create_host_object(m_env, instance, JSObjectProxyFinalizerCallback, data, is_array, getter, setter, &proxy); + +#else + napi_value argv[2]; + argv[0] = instance; + napi_create_int32(m_env, javaObjectID, &argv[1]); + + if (!this->m_jsObjectProxyCreator) { + napi_value jsObjectProxyCreator; + napi_get_named_property(m_env, napi_util::global(m_env), "__createNativeProxy", + &jsObjectProxyCreator); + this->m_jsObjectProxyCreator = napi_util::make_ref(m_env, jsObjectProxyCreator); + } + + napi_call_function(m_env, napi_util::global(m_env), + napi_util::get_ref_value(m_env, this->m_jsObjectProxyCreator), + 2, argv, &proxy); + + if (!proxy) { + DEBUG_WRITE("Failed to create proxy for javaObjectId %d", javaObjectID); + return nullptr; + } + + + auto data = new JSInstanceInfo(javaObjectID, nullptr); + + napi_value external; + napi_create_external(m_env, data, JSObjectProxyFinalizerCallback, data, &external); + napi_set_named_property(m_env, proxy, "[[external]]", external); + + +#endif + + auto javaObjectIdFound = m_weakObjectIds.find(javaObjectID); + if (javaObjectIdFound != m_weakObjectIds.end()) { + m_weakObjectIds.erase(javaObjectID); + JEnv jenv; + jenv.CallVoidMethod(m_javaRuntimeObject, + MAKE_INSTANCE_STRONG_METHOD_ID, + javaObjectID); + DEBUG_WRITE("Making instance strong: %d", javaObjectID); + } + + m_idToProxy.emplace(javaObjectID, napi_util::make_ref(m_env, proxy, 0)); + + return proxy; +} + +JniLocalRef ObjectManager::GetJavaObjectByJsObject(napi_value object, int *objectId) { + int32_t javaObjectId = -1; + if (objectId) { + javaObjectId = *objectId; + } + +#ifdef USE_HOST_OBJECT + void* data = nullptr; + napi_get_host_object_data(m_env, object, &data); + if (data) { + auto info = (JSInstanceInfo *) data; + javaObjectId = info->JavaObjectID; + } else { + JSInstanceInfo *jsInstanceInfo = GetJSInstanceInfo(object); + if (jsInstanceInfo != nullptr) javaObjectId = jsInstanceInfo->JavaObjectID; + } +#else + if (javaObjectId == -1) { + JSInstanceInfo *jsInstanceInfo = GetJSInstanceInfo(object); + if (jsInstanceInfo != nullptr) javaObjectId = jsInstanceInfo->JavaObjectID; + } +#endif + + if (objectId) { + *objectId = javaObjectId; + } + + if (javaObjectId != -1) return {GetJavaObjectByID(javaObjectId), true}; + + return {}; +} + +JniLocalRef ObjectManager::GetJavaObjectByJsObjectFast(napi_value object) { + void *data = nullptr; + +#ifdef USE_HOST_OBJECT + napi_get_host_object_data(m_env, object, &data); +#endif + + if (!data) napi_unwrap(m_env, object, &data); + + if (data) { + auto info = reinterpret_cast(data); + return {GetJavaObjectByID(info->JavaObjectID), true}; + } + + return GetJavaObjectByJsObject(object); +} + +ObjectManager::JSInstanceInfo *ObjectManager::GetJSInstanceInfo(napi_value object) { + if (!IsRuntimeJsObject(object)) return nullptr; + + return GetJSInstanceInfoFromRuntimeObject(object); +} + +bool ObjectManager::IsHostObject(napi_value object) { +#ifdef USE_HOST_OBJECT + bool isHostObject; + napi_is_host_object(m_env, object, &isHostObject); + return isHostObject; +#endif + return false; +} + +ObjectManager::JSInstanceInfo * +ObjectManager::GetJSInstanceInfoFromRuntimeObject(napi_value object) { + napi_value jsInfo; + napi_get_named_property(m_env, object, PRIVATE_JSINFO, &jsInfo); + + if (napi_util::is_null_or_undefined(m_env, jsInfo)) { + napi_value proto = napi_util::get__proto__(m_env, object); + //Typescript object layout has an object instance as child of the actual registered instance. checking for that + if (!napi_util::is_null_or_undefined(m_env, proto)) { + if (IsRuntimeJsObject(proto)) { + napi_get_named_property(m_env, proto, PRIVATE_JSINFO, &jsInfo); + } + } + } + + if (!napi_util::is_null_or_undefined(m_env, jsInfo)) { + void *data; + napi_get_value_external(m_env, jsInfo, &data); + auto info = reinterpret_cast(data); + return info; + } + return nullptr; +} + +bool ObjectManager::IsRuntimeJsObject(napi_value object) { + if (object == nullptr) return false; + + bool result = false; + if (napi_has_named_property(m_env, object, PRIVATE_IS_NAPI, &result) != napi_ok) { + return false; + } + return result; +} + +jweak ObjectManager::GetJavaObjectByID(uint32_t javaObjectID) { + return m_cache(javaObjectID); +} + +jobject ObjectManager::GetJavaObjectByIDImpl(uint32_t javaObjectID) { + JEnv env; + jobject object = env.CallObjectMethod(m_javaRuntimeObject, GET_JAVAOBJECT_BY_ID_METHOD_ID, + javaObjectID); + return object; +} + +void ObjectManager::UpdateCache(int objectID, jobject obj) { + m_cache.update(objectID, obj); +} + +jclass ObjectManager::GetJavaClass(napi_value value) { + JSInstanceInfo *jsInfo = GetJSInstanceInfo(value); + jclass clazz = jsInfo->ObjectClazz; + + return clazz; +} + +void ObjectManager::SetJavaClass(napi_value value, jclass clazz) { + JSInstanceInfo *jsInfo = GetJSInstanceInfo(value); + jsInfo->ObjectClazz = clazz; +} + +int ObjectManager::GetOrCreateObjectId(jobject object) { + JEnv env; + jint javaObjectID = env.CallIntMethod(m_javaRuntimeObject, + GET_OR_CREATE_JAVA_OBJECT_ID_METHOD_ID, object); + return javaObjectID; +} + +napi_value ObjectManager::GetJsObjectByJavaObject(int javaObjectID) { + auto it = m_idToObject.find(javaObjectID); + if (it == m_idToObject.end()) { + return nullptr; + } + + napi_value instance = napi_util::get_ref_value(m_env, it->second); + if (napi_util::is_null_or_undefined(m_env, instance)) return nullptr; + return GetOrCreateProxy(javaObjectID, instance); +} + + +napi_value +ObjectManager::CreateJSWrapper(jint javaObjectID, const std::string &typeName) { + return CreateJSWrapperHelper(javaObjectID, typeName, nullptr); +} + +napi_value +ObjectManager::CreateJSWrapper(jint javaObjectID, const std::string &typeName, jobject instance) { + JEnv jenv; + JniLocalRef clazz(jenv.GetObjectClass(instance)); + + return CreateJSWrapperHelper(javaObjectID, typeName, clazz); +} + +napi_value +ObjectManager::CreateJSWrapperHelper(jint javaObjectID, const std::string &typeName, jclass clazz) { + auto className = (clazz != nullptr) ? GetClassName(clazz) : typeName; + + auto node = MetadataNode::GetOrCreate(className); + napi_value proxy = nullptr; + napi_value jsWrapper = node->CreateJSWrapper(m_env, this); + if (jsWrapper != nullptr) { + JEnv jenv; + auto claz = jenv.FindClass(className); + Link(jsWrapper, javaObjectID, claz); + if (node->isArray()) { + napi_set_named_property(m_env, jsWrapper, "__is__javaArray", + napi_util::get_true(m_env)); + } + proxy = GetOrCreateProxy(javaObjectID, jsWrapper); + } + + return proxy; +} + +void ObjectManager::Link(napi_value object, uint32_t javaObjectID, jclass clazz) { + if (!IsRuntimeJsObject(object)) { + std::string errMsg("Trying to link invalid 'this' to a Java object"); + throw NativeScriptException(errMsg); + } + + DEBUG_WRITE("Linking js object and java instance id: %d", javaObjectID); + + auto jsInstanceInfo = new JSInstanceInfo(javaObjectID, clazz); + + napi_ref objectHandle = napi_util::make_ref(m_env, object, 1); + + napi_value jsInfo; + napi_create_external(m_env, jsInstanceInfo, JSObjectFinalizerCallback, jsInstanceInfo, &jsInfo); + napi_set_named_property(m_env, object, PRIVATE_JSINFO, jsInfo); + + // Wrapped but does not handle data lifecycle. only used for fast access. + napi_wrap(m_env, object, jsInstanceInfo, [](napi_env env, void *data, void *hint) {}, jsInstanceInfo, + nullptr); + + m_idToObject.emplace(javaObjectID, objectHandle); +} + +bool ObjectManager::CloneLink(napi_value src, napi_value dest) { + auto jsInfo = GetJSInstanceInfo(src); + + auto success = jsInfo != nullptr; + + if (success) { + napi_value external; + napi_create_external(m_env, jsInfo, [](napi_env env, void* d1, void*d2) {}, jsInfo, &external); + napi_set_named_property(m_env, dest, PRIVATE_JSINFO, external); + napi_wrap(m_env, dest, jsInfo, [](napi_env env, void *data, void *hint) {}, jsInfo, + nullptr); + } + + return success; +} + +string ObjectManager::GetClassName(jobject javaObject) { + JEnv env; + JniLocalRef objectClass(env.GetObjectClass(javaObject)); + + return GetClassName((jclass) objectClass); +} + +bool ObjectManager::GetIsSuper(int objectId, napi_value value) { + auto it = m_idToSuper.find(objectId); + if (it != m_idToSuper.end()) return it->second; + napi_value superValue; + napi_get_named_property(m_env, value, PRIVATE_CALLSUPER, &superValue); + bool isSuper = napi_util::get_bool(m_env, superValue); + m_idToSuper.emplace(objectId, isSuper); + return isSuper; +} + +string ObjectManager::GetClassName(jclass clazz) { + JEnv env; + JniLocalRef javaCanonicalName(env.CallObjectMethod(clazz, GET_NAME_METHOD_ID)); + + string className = ArgConverter::jstringToString(javaCanonicalName); + + std::replace(className.begin(), className.end(), '.', '/'); + + return className; +} + +void +ObjectManager::JSObjectFinalizerCallback(napi_env env, void *finalizeData, void *finalizeHint) { + #ifdef __HERMES__ + if (finalizeHint == nullptr) return; + auto data = reinterpret_cast(finalizeHint); + #else + if (finalizeData == nullptr) return; + auto data = reinterpret_cast(finalizeData); + #endif + + DEBUG_WRITE("JS Object finalizer called for object id: %d", data->JavaObjectID); + delete data; +} + +void ObjectManager::JSObjectProxyFinalizerCallback(napi_env env, void *finalizeData, + void *finalizeHint) { + +#ifdef __HERMES__ + if (finalizeHint == nullptr) return; + auto state = reinterpret_cast(finalizeHint); +#else + if (finalizeData == nullptr) return; + auto state = reinterpret_cast(finalizeData); +#endif + + auto rt = Runtime::GetRuntimeUnchecked(env); + if (rt && !rt->is_destroying) { + + auto objManager = rt->GetObjectManager(); + auto itFound = objManager->m_weakObjectIds.find(state->JavaObjectID); + + DEBUG_WRITE("JS Proxy finalizer called for object id: %d", state->JavaObjectID); + if (itFound == objManager->m_weakObjectIds.end()) { + objManager->m_weakObjectIds.emplace(state->JavaObjectID); + JEnv jEnv; + jEnv.CallVoidMethod(objManager->m_javaRuntimeObject, + objManager->MAKE_INSTANCE_WEAK_METHOD_ID, + state->JavaObjectID); + + } + } + delete state; +} + +int ObjectManager::GenerateNewObjectID() { + const int one = 1; + int oldValue = __sync_fetch_and_add(&m_currentObjectId, one); + return oldValue; +} + +jweak ObjectManager::NewWeakGlobalRefCallback(const int &javaObjectID, void *state) { + auto objManager = reinterpret_cast(state); + JniLocalRef obj(objManager->GetJavaObjectByIDImpl(javaObjectID)); + JEnv jEnv; + jweak weakRef = jEnv.NewWeakGlobalRef(obj); + + return weakRef; +} + +void ObjectManager::DeleteWeakGlobalRefCallback(const jweak &object, void *state) { + JEnv jEnv; + jEnv.DeleteWeakGlobalRef(object); +} + +napi_value ObjectManager::GetEmptyObject() { + napi_value emptyObjCtorFunc = napi_util::get_ref_value(m_env, m_jsObjectCtor); + + napi_value ex; + napi_get_and_clear_last_exception(m_env, &ex); + + napi_value jsWrapper = nullptr; + auto status = napi_new_instance(m_env, emptyObjCtorFunc, 0, nullptr, &jsWrapper); + if (status == napi_ok && !napi_util::is_null_or_undefined(m_env, jsWrapper)) { + return jsWrapper; + } + + napi_get_and_clear_last_exception(m_env, &ex); + + status = napi_create_object(m_env, &jsWrapper); + if (status != napi_ok || jsWrapper == nullptr) return nullptr; + + MarkObject(m_env, jsWrapper); + auto prototype = napi_util::get_prototype(m_env, emptyObjCtorFunc); + if (!napi_util::is_null_or_undefined(m_env, prototype)) { + napi_util::setPrototypeOf(m_env, jsWrapper, prototype); + } + + if (napi_util::is_null_or_undefined(m_env, jsWrapper)) { + return nullptr; + } + + return jsWrapper; +} + +napi_value ObjectManager::JSObjectConstructorCallback(napi_env env, napi_callback_info info) { + NAPI_CALLBACK_BEGIN(0); + return jsThis; +} + +void ObjectManager::ReleaseObjectNow(napi_env env, int javaObjectId) { + auto rt = Runtime::GetRuntimeUnchecked(env); + if (!rt || rt->is_destroying) return; + ObjectManager *objMgr = rt->GetObjectManager(); + + auto itFound = objMgr->m_weakObjectIds.find(javaObjectId); + if (itFound == objMgr->m_weakObjectIds.end()) { + JEnv jEnv; + jEnv.CallVoidMethod(objMgr->m_javaRuntimeObject, objMgr->MAKE_INSTANCE_WEAK_METHOD_ID, + javaObjectId); + objMgr->m_weakObjectIds.emplace(javaObjectId); + } + + auto found = objMgr->m_idToProxy.find(javaObjectId); + if (found != objMgr->m_idToProxy.end()) { + napi_delete_reference(env, found->second); + objMgr->m_idToProxy.erase(javaObjectId); + } + + found = objMgr->m_idToObject.find(javaObjectId); + if (found != objMgr->m_idToObject.end()) { + napi_delete_reference(env, found->second); + objMgr->m_idToObject.erase(javaObjectId); + } + + Runtime::GetRuntime(env)->js_method_cache->cleanupObject(javaObjectId); +} + +void ObjectManager::ReleaseNativeObject(napi_env env, napi_value object) { + int32_t javaObjectId = -1; + JSInstanceInfo *jsInstanceInfo; + +#ifdef USE_HOST_OBJECT + void* data; + napi_get_host_object_data(env, object, &data); + if (data) { + jsInstanceInfo = reinterpret_cast(data); + } else { +#endif + jsInstanceInfo = GetJSInstanceInfo(object); +#ifdef USE_HOST_OBJECT + } +#endif + + if (jsInstanceInfo) { + javaObjectId = jsInstanceInfo->JavaObjectID; + } + + if (javaObjectId == -1) { + napi_throw_error(env, "0", "Trying to release a non native object!"); + return; + } + + ReleaseObjectNow(env, javaObjectId); +} + +void ObjectManager::OnGarbageCollected(JNIEnv *jEnv, jintArray object_ids) { + JEnv jenv(jEnv); + jsize length = jenv.GetArrayLength(object_ids); + int *cppArray = jenv.GetIntArrayElements(object_ids, nullptr); + for (jsize i = 0; i < length; i++) { + auto rt = Runtime::GetRuntimeUnchecked(m_env); + if (rt && rt->is_destroying) return; + int javaObjectId = cppArray[i]; + auto itFound = this->m_idToObject.find(javaObjectId); + if (itFound != this->m_idToObject.end()) { + napi_delete_reference(m_env, itFound->second); + this->m_idToObject.erase(javaObjectId); + + if (rt && !rt->is_destroying) { + rt->js_method_cache->cleanupObject(javaObjectId); + } + + DEBUG_WRITE("JS Object released for object id: %d", javaObjectId); + // auto found = this->m_idToProxy.find(javaObjectId); + // if (found != this->m_idToProxy.end()) { + // napi_delete_reference(m_env, found->second); + // this->m_idToProxy.erase(javaObjectId); + // } + } + + } +} diff --git a/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.h b/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.h new file mode 100644 index 000000000..4c5244918 --- /dev/null +++ b/NativeScript/ffi/jni/napi/objectmanager/ObjectManager.h @@ -0,0 +1,162 @@ +#ifndef OBJECTMANAGER_H_ +#define OBJECTMANAGER_H_ + +#include "js_native_api.h" +#include "JEnv.h" +#include "JniLocalRef.h" +#include "JniLocalRef.h" +#include "DirectBuffer.h" +#include "LRUCache.h" +#include +#include +#include +#include +#include +#include "Constants.h" + + +namespace tns { + class ObjectManager { + public: + ObjectManager(jobject javaRuntimeObject); + + void OnDisposeEnv(); + + void Init(napi_env env); + + JniLocalRef GetJavaObjectByJsObject(napi_value object, int *objectId = nullptr); + + + JniLocalRef GetJavaObjectByJsObjectFast(napi_value object); + + void UpdateCache(int objectID, jobject obj); + + jclass GetJavaClass(napi_value value); + + void SetJavaClass(napi_value instance, jclass clazz); + + int GetOrCreateObjectId(jobject object); + + napi_value GetJsObjectByJavaObject(int javaObjectID); + + napi_value + CreateJSWrapper(jint javaObjectID, const std::string &typeName); + + napi_value + CreateJSWrapper(jint javaObjectID, const std::string &typeName, jobject instance); + + napi_value GetOrCreateProxy(jint javaObjectID, napi_value instance); + + napi_value GetOrCreateProxyWeak(jint javaObjectID, napi_value instance); + + void Link(napi_value object, uint32_t javaObjectID, jclass clazz); + + bool CloneLink(napi_value src, napi_value dest); + + bool IsRuntimeJsObject(napi_value object); + + std::string GetClassName(jobject javaObject); + + std::string GetClassName(jclass clazz); + + int GenerateNewObjectID(); + + napi_value GetEmptyObject(); + + inline static void MarkObject(napi_env env, napi_value object) { + napi_value marker; + napi_get_boolean(env, true, &marker); + napi_set_named_property(env, object, PRIVATE_IS_NAPI, marker); + } + + inline static void MarkSuperCall(napi_env env, napi_value object) { + napi_value marker; + napi_get_boolean(env, true, &marker); + napi_set_named_property(env, object, PRIVATE_CALLSUPER, marker); + } + + void OnGarbageCollected(JNIEnv *jEnv, jintArray object_ids); + + void ReleaseNativeObject(napi_env env, napi_value object); + + inline static void ReleaseObjectNow(napi_env env, int javaObjectId); + + bool GetIsSuper(int objectId, napi_value value); + + bool IsHostObject(napi_value object); + + private: + static napi_value JSObjectConstructorCallback(napi_env env, napi_callback_info info); + + struct JSInstanceInfo { + public: + JSInstanceInfo(uint32_t javaObjectID, jclass claz) + : JavaObjectID(javaObjectID), ObjectClazz(claz) { + } + + uint32_t JavaObjectID; + jclass ObjectClazz; + }; + + + JSInstanceInfo *GetJSInstanceInfo(napi_value object); + + JSInstanceInfo *GetJSInstanceInfoFromRuntimeObject(napi_value object); + + napi_value + CreateJSWrapperHelper(jint javaObjectID, const std::string &typeName, jclass clazz); + + static void JSObjectFinalizerCallback(napi_env env, void *finalizeData, void *finalizeHint); + + static void + JSObjectProxyFinalizerCallback(napi_env env, void *finalizeData, void *finalizeHint); + + jweak GetJavaObjectByID(uint32_t javaObjectID); + + jobject GetJavaObjectByIDImpl(uint32_t javaObjectID); + + static jweak NewWeakGlobalRefCallback(const int &javaObjectID, void *state); + + static void DeleteWeakGlobalRefCallback(const jweak &object, void *state); + + jobject m_javaRuntimeObject; + + napi_env m_env; + + robin_hood::unordered_map m_idToProxy; + robin_hood::unordered_map m_idToObject; + robin_hood::unordered_map m_idToSuper; + robin_hood::unordered_set m_weakObjectIds; + robin_hood::unordered_set m_markedAsWeakIds; + + LRUCache m_cache; + + volatile int m_currentObjectId; + + DirectBuffer m_buff; + + DirectBuffer m_outBuff; + + jclass JAVA_LANG_CLASS; + + jmethodID GET_NAME_METHOD_ID; + + jmethodID GET_JAVAOBJECT_BY_ID_METHOD_ID; + + jmethodID GET_OR_CREATE_JAVA_OBJECT_ID_METHOD_ID; + + jmethodID MAKE_INSTANCE_WEAK_BATCH_METHOD_ID; + + jmethodID MAKE_INSTANCE_WEAK_METHOD_ID; + + jmethodID MAKE_INSTANCE_STRONG_METHOD_ID; + + napi_ref m_jsObjectCtor; + + napi_ref m_jsObjectProxyCreator; + + napi_ref jid; + }; +} + +#endif /* OBJECTMANAGER_H_ */ \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/weakref/WeakRef.cpp b/NativeScript/ffi/jni/napi/weakref/WeakRef.cpp new file mode 100644 index 000000000..3d15512e1 --- /dev/null +++ b/NativeScript/ffi/jni/napi/weakref/WeakRef.cpp @@ -0,0 +1,79 @@ +// +// Created by Ammar Ahmed on 03/12/2024. +// + +#include "WeakRef.h" +#include "native_api_util.h" + +using namespace tns; + +WeakRef::WeakRef(napi_env env, napi_value value) : env_(env), ref_(nullptr) { + napi_create_reference(env, value, 1, &ref_); +} + +WeakRef::~WeakRef() { + if (ref_ != nullptr) { + napi_delete_reference(env_, ref_); + } +} + +void WeakRef::Init(napi_env env) { + napi_value global; + napi_get_global(env, &global); + napi_property_descriptor properties[] = { + { "get", 0, Deref, 0, 0, 0, napi_default, 0 }, + { "deref", 0, Deref, 0, 0, 0, napi_default, 0 } + }; + + napi_value wr; + napi_get_named_property(env, global, "WeakRef", &wr); + if (napi_util::is_null_or_undefined(env, wr)) { + napi_value cons; + napi_define_class(env, "WeakRef", NAPI_AUTO_LENGTH, New, nullptr, 2, properties, &cons); + napi_set_named_property(env, global, "WeakRef", cons); + } +} + +napi_value WeakRef::New(napi_env env, napi_callback_info info) { + napi_value target; + napi_get_new_target(env, info, &target); + bool is_constructor = target != nullptr; + + if (!is_constructor) { + napi_throw_error(env, nullptr, "WeakRef must be called as a constructor"); + return nullptr; + } + + size_t arg_len; + napi_get_cb_info(env, info, &arg_len, nullptr, nullptr, nullptr); + + if (arg_len != 1) { + napi_throw_error(env, nullptr, "WeakRef constructor must be called with one argument"); + return nullptr; + } + + size_t argc = 1; + napi_value args[1]; + napi_value jsThis; + napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr); + + auto obj = new WeakRef(env, args[0]); + napi_wrap(env, jsThis, reinterpret_cast(obj), [](napi_env env, void* data, void* hint) { + delete reinterpret_cast(data); + }, nullptr, nullptr); + + return jsThis; +} + +napi_value WeakRef::Deref(napi_env env, napi_callback_info info) { + napi_value jsThis; + napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); + + WeakRef* obj; + napi_unwrap(env, jsThis, reinterpret_cast(&obj)); + + napi_value result; + napi_get_reference_value(env, obj->ref_, &result); + + return result; +} diff --git a/NativeScript/ffi/jni/napi/weakref/WeakRef.h b/NativeScript/ffi/jni/napi/weakref/WeakRef.h new file mode 100644 index 000000000..08579a40c --- /dev/null +++ b/NativeScript/ffi/jni/napi/weakref/WeakRef.h @@ -0,0 +1,28 @@ +// +// Created by Ammar Ahmed on 03/12/2024. +// + +#ifndef TEST_APP_WEAKREF_H +#define TEST_APP_WEAKREF_H + +#include "js_native_api.h" + +namespace tns { + class WeakRef { + public: + static void Init(napi_env env); + static napi_value New(napi_env env, napi_callback_info info); + + private: + explicit WeakRef(napi_env env, napi_value value); + ~WeakRef(); + + napi_env env_; + napi_ref ref_; + + static napi_value Get(napi_env env, napi_callback_info info); + static napi_value Deref(napi_env env, napi_callback_info info); + }; + +} +#endif //TEST_APP_WEAKREF_H diff --git a/NativeScript/ffi/jsc/NativeApiJSC.h b/NativeScript/ffi/jsc/NativeApiJSC.h deleted file mode 100644 index 0bf60c969..000000000 --- a/NativeScript/ffi/jsc/NativeApiJSC.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_H -#define NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_H - -#include "ffi/shared/direct/NativeApiDirect.h" -#include - -namespace nativescript { - -using NativeApiJSCConfig = NativeApiDirectConfig; - -void InstallNativeApiJSC(JSGlobalContextRef context, - const NativeApiJSCConfig& config = NativeApiJSCConfig{}); - -} // namespace nativescript - -extern "C" void NativeScriptInstallNativeApiJSC(JSGlobalContextRef context, - const char* metadataPath); - -#endif // NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_H diff --git a/NativeScript/ffi/jsc/NativeApiJSC.mm b/NativeScript/ffi/jsc/NativeApiJSC.mm deleted file mode 100644 index b1a6fa398..000000000 --- a/NativeScript/ffi/jsc/NativeApiJSC.mm +++ /dev/null @@ -1,70 +0,0 @@ -#include "NativeApiJSC.h" - -#ifdef TARGET_ENGINE_JSC - -#include "NativeApiJSCRuntime.h" - -namespace nativescript { - -using NativeApiJsiConfig = NativeApiDirectConfig; -using NativeApiJsiScheduler = NativeApiDirectScheduler; - -namespace { - -using facebook::jsi::Array; -using facebook::jsi::ArrayBuffer; -using facebook::jsi::BigInt; -using facebook::jsi::Function; -using facebook::jsi::HostObject; -using facebook::jsi::MutableBuffer; -using facebook::jsi::Object; -using facebook::jsi::PropNameID; -using facebook::jsi::Runtime; -using facebook::jsi::String; -using facebook::jsi::StringBuffer; -using facebook::jsi::Value; -using metagen::MDMemberFlag; -using metagen::MDMetadataReader; -using metagen::MDSectionOffset; -using metagen::MDTypeKind; - -// clang-format off -#include "jsi/NativeApiJsiBridge.h" -#include "jsi/NativeApiJsiHostObjects.h" -// clang-format on -#define NATIVESCRIPT_NATIVE_API_RETAIN_RUNTIME 1 - -std::shared_ptr retainNativeApiJsiRuntime(Runtime& runtime) { - return std::make_shared(runtime.state()); -} - -// clang-format off -#include "jsi/NativeApiJsiCallbacks.h" -#include "jsi/NativeApiJsiConversion.h" -#include "jsi/NativeApiJsiInvocation.h" -#include "jsi/NativeApiJsiClassBuilder.h" -#include "jsi/NativeApiJsiHostObject.h" -// clang-format on - -} // namespace - -#include "jsi/NativeApiJsiInstall.h" - -void InstallNativeApiJSC(JSGlobalContextRef context, const NativeApiJSCConfig& config) { - if (context == nullptr) { - return; - } - Runtime runtime(context); - InstallNativeApiJSI(runtime, config); -} - -} // namespace nativescript - -extern "C" void NativeScriptInstallNativeApiJSC(JSGlobalContextRef context, - const char* metadataPath) { - nativescript::NativeApiJSCConfig config; - config.metadataPath = metadataPath; - nativescript::InstallNativeApiJSC(context, config); -} - -#endif // TARGET_ENGINE_JSC diff --git a/NativeScript/ffi/jsc/NativeApiJSCHostObjects.mm b/NativeScript/ffi/jsc/NativeApiJSCHostObjects.mm deleted file mode 100644 index ab2da20a8..000000000 --- a/NativeScript/ffi/jsc/NativeApiJSCHostObjects.mm +++ /dev/null @@ -1,182 +0,0 @@ -#include "NativeApiJSCRuntime.h" - -#ifdef TARGET_ENGINE_JSC - -namespace facebook { -namespace jsi { - -namespace jscdirect { - -JSClassRef hostClass(Runtime& runtime); -JSClassRef functionClass(Runtime& runtime); - -JSValueRef hostGetProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, - JSValueRef* exception) { - auto* holder = static_cast(JSObjectGetPrivate(object)); - if (holder == nullptr || holder->hostObject == nullptr) { - return nullptr; - } - Runtime runtime(holder->state); - try { - Value result = holder->hostObject->get(runtime, PropNameID(stringToUtf8(propertyName))); - return result.isUndefined() ? nullptr : result.local(runtime); - } catch (const std::exception& error) { - setException(context, exception, error); - return JSValueMakeUndefined(context); - } -} - -bool hostSetProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, - JSValueRef value, JSValueRef* exception) { - auto* holder = static_cast(JSObjectGetPrivate(object)); - if (holder == nullptr || holder->hostObject == nullptr) { - return false; - } - Runtime runtime(holder->state); - try { - holder->hostObject->set(runtime, PropNameID(stringToUtf8(propertyName)), Value(runtime, value)); - return true; - } catch (const std::exception& error) { - setException(context, exception, error); - return true; - } -} - -void hostGetPropertyNames(JSContextRef, JSObjectRef object, - JSPropertyNameAccumulatorRef propertyNames) { - auto* holder = static_cast(JSObjectGetPrivate(object)); - if (holder == nullptr || holder->hostObject == nullptr) { - return; - } - Runtime runtime(holder->state); - try { - for (const auto& property : holder->hostObject->getPropertyNames(runtime)) { - JSStringRef name = makeJSString(property.utf8(runtime)); - JSPropertyNameAccumulatorAddName(propertyNames, name); - JSStringRelease(name); - } - } catch (const std::exception&) { - } -} - -void hostFinalize(JSObjectRef object) { - delete static_cast(JSObjectGetPrivate(object)); -} - -JSValueRef functionCall(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, - size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { - auto* holder = static_cast(JSObjectGetPrivate(function)); - if (holder == nullptr || !holder->callback) { - return JSValueMakeUndefined(context); - } - Runtime runtime(holder->state); - std::vector args; - args.reserve(argumentCount); - for (size_t i = 0; i < argumentCount; i++) { - args.emplace_back(runtime, arguments[i]); - } - try { - Value thisValue(runtime, thisObject); - Value result = - holder->callback(runtime, thisValue, args.empty() ? nullptr : args.data(), args.size()); - return result.local(runtime); - } catch (const std::exception& error) { - setException(context, exception, error); - return JSValueMakeUndefined(context); - } -} - -void functionFinalize(JSObjectRef object) { - delete static_cast(JSObjectGetPrivate(object)); -} - -JSClassRef hostClass(Runtime& runtime) { - auto state = runtime.state(); - if (state->hostClass == nullptr) { - JSClassDefinition definition = kJSClassDefinitionEmpty; - definition.className = "NativeScriptDirectHostObject"; - definition.getProperty = hostGetProperty; - definition.setProperty = hostSetProperty; - definition.getPropertyNames = hostGetPropertyNames; - definition.finalize = hostFinalize; - state->hostClass = JSClassCreate(&definition); - } - return state->hostClass; -} - -JSClassRef functionClass(Runtime& runtime) { - auto state = runtime.state(); - if (state->functionClass == nullptr) { - JSClassDefinition definition = kJSClassDefinitionEmpty; - definition.className = "NativeScriptDirectFunction"; - definition.callAsFunction = functionCall; - definition.finalize = functionFinalize; - state->functionClass = JSClassCreate(&definition); - } - return state->functionClass; -} - -void setFunctionPrototype(JSGlobalContextRef context, JSObjectRef function) { - if (context == nullptr || function == nullptr) { - return; - } - - JSValueRef exception = nullptr; - JSStringRef functionName = makeJSString("Function"); - JSValueRef functionValue = - JSObjectGetProperty(context, JSContextGetGlobalObject(context), functionName, &exception); - JSStringRelease(functionName); - if (exception != nullptr || functionValue == nullptr || - !JSValueIsObject(context, functionValue)) { - return; - } - - exception = nullptr; - JSObjectRef functionConstructor = JSValueToObject(context, functionValue, &exception); - if (exception != nullptr || functionConstructor == nullptr) { - return; - } - - JSStringRef prototypeName = makeJSString("prototype"); - JSValueRef prototypeValue = - JSObjectGetProperty(context, functionConstructor, prototypeName, &exception); - JSStringRelease(prototypeName); - if (exception != nullptr || prototypeValue == nullptr || - !JSValueIsObject(context, prototypeValue)) { - return; - } - - JSObjectSetPrototype(context, function, prototypeValue); -} - -} // namespace jscdirect - -Object Object::createFromHostObjectWithToken(Runtime& runtime, std::shared_ptr host, - const void* typeToken) { - auto* holder = new jscdirect::HostObjectHolder(runtime.state(), std::move(host), typeToken); - JSObjectRef object = JSObjectMake(runtime.context(), jscdirect::hostClass(runtime), holder); - return Object::fromValueStorage(Value(runtime, object).storage_); -} - -Function Function::createFromHostFunction(Runtime& runtime, const PropNameID& name, unsigned int, - HostFunctionType callback) { - auto* holder = new jscdirect::FunctionHolder(runtime.state(), std::move(callback)); - JSObjectRef function = JSObjectMake(runtime.context(), jscdirect::functionClass(runtime), holder); - jscdirect::setFunctionPrototype(runtime.context(), function); - std::string functionName = name.utf8(runtime); - if (!functionName.empty()) { - JSStringRef property = jscdirect::makeJSString("name"); - JSStringRef valueString = jscdirect::makeJSString(functionName); - JSValueRef value = JSValueMakeString(runtime.context(), valueString); - JSObjectSetProperty(runtime.context(), function, property, value, kJSPropertyAttributeReadOnly, - nullptr); - JSStringRelease(valueString); - JSStringRelease(property); - } - return Function(Object::fromValueStorage(Value(runtime, function).storage_)); -} - -} // namespace jsi -} // namespace facebook - -#endif // TARGET_ENGINE_JSC diff --git a/NativeScript/ffi/jsc/NativeApiJSCRuntime.h b/NativeScript/ffi/jsc/NativeApiJSCRuntime.h deleted file mode 100644 index 305399f41..000000000 --- a/NativeScript/ffi/jsc/NativeApiJSCRuntime.h +++ /dev/null @@ -1,839 +0,0 @@ -#ifndef NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_RUNTIME_H -#define NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_RUNTIME_H - -#ifdef TARGET_ENGINE_JSC - -#import -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Metadata.h" -#include "MetadataReader.h" -#include "ffi.h" - -@protocol NativeApiJsiClassBuilderProtocol -@end - -#ifdef EMBED_METADATA_SIZE -extern const unsigned char embedded_metadata[EMBED_METADATA_SIZE]; -#endif - -namespace facebook { -namespace jsi { - -class Runtime; -class Value; -class Object; -class Function; -class Array; -class String; -class BigInt; -class ArrayBuffer; - -class JSError : public std::runtime_error { - public: - JSError(Runtime&, const std::string& message) : std::runtime_error(message) {} - explicit JSError(const std::string& message) : std::runtime_error(message) {} -}; - -class StringBuffer { - public: - explicit StringBuffer(std::string value) : value_(std::move(value)) {} - const char* data() const { return value_.data(); } - size_t size() const { return value_.size(); } - - private: - std::string value_; -}; - -class MutableBuffer { - public: - virtual ~MutableBuffer() = default; - virtual size_t size() const = 0; - virtual uint8_t* data() = 0; -}; - -class PropNameID { - public: - PropNameID() = default; - explicit PropNameID(std::string value) : value_(std::move(value)) {} - - static PropNameID forAscii(Runtime&, const char* value) { - return PropNameID(value != nullptr ? value : ""); - } - - static PropNameID forAscii(Runtime&, const std::string& value) { return PropNameID(value); } - - std::string utf8(Runtime&) const { return value_; } - - private: - std::string value_; -}; - -class HostObject { - public: - virtual ~HostObject() = default; - virtual Value get(Runtime& runtime, const PropNameID& name); - virtual void set(Runtime& runtime, const PropNameID& name, const Value& value); - virtual std::vector getPropertyNames(Runtime& runtime); -}; - -using HostFunctionType = std::function; - -namespace jscdirect { - -inline std::string stringToUtf8(JSStringRef string) { - if (string == nullptr) { - return {}; - } - size_t capacity = JSStringGetMaximumUTF8CStringSize(string); - std::string result(capacity, '\0'); - size_t written = JSStringGetUTF8CString(string, result.data(), capacity); - if (written == 0) { - return {}; - } - result.resize(written - 1); - return result; -} - -inline JSStringRef makeJSString(const std::string& value) { - NSString* string = [[NSString alloc] initWithBytes:value.data() - length:value.size() - encoding:NSUTF8StringEncoding]; - if (string == nil) { - return JSStringCreateWithUTF8CString(value.c_str()); - } - - NSUInteger length = [string length]; - std::vector characters(length); - if (length > 0) { - [string getCharacters:characters.data() range:NSMakeRange(0, length)]; - } - [string release]; - return JSStringCreateWithCharacters(characters.data(), length); -} - -inline JSStringRef makeJSString(const char* value) { - return JSStringCreateWithUTF8CString(value != nullptr ? value : ""); -} - -inline std::string valueToUtf8(JSContextRef context, JSValueRef value) { - if (value == nullptr) { - return {}; - } - JSValueRef exception = nullptr; - JSStringRef string = JSValueToStringCopy(context, value, &exception); - if (string == nullptr || exception != nullptr) { - if (string != nullptr) { - JSStringRelease(string); - } - return {}; - } - std::string result = stringToUtf8(string); - JSStringRelease(string); - return result; -} - -inline JSValueRef makeError(JSContextRef context, const std::string& message) { - JSStringRef string = makeJSString(message); - JSValueRef argument = JSValueMakeString(context, string); - JSStringRelease(string); - JSValueRef exception = nullptr; - JSObjectRef error = JSObjectMakeError(context, 1, &argument, &exception); - if (error != nullptr && exception == nullptr) { - return error; - } - return argument; -} - -inline void setException(JSContextRef context, JSValueRef* exception, const std::exception& error) { - if (exception != nullptr) { - *exception = makeError(context, error.what()); - } -} - -struct RuntimeState { - explicit RuntimeState(JSGlobalContextRef context) : context(context) {} - - ~RuntimeState() { - if (hostClass != nullptr) { - JSClassRelease(hostClass); - } - if (functionClass != nullptr) { - JSClassRelease(functionClass); - } - } - - JSGlobalContextRef context = nullptr; - JSClassRef hostClass = nullptr; - JSClassRef functionClass = nullptr; -}; - -struct ValueStorage { - enum class Kind { - Undefined, - Null, - Bool, - Number, - JSC, - }; - - explicit ValueStorage(Kind kind) : kind(kind) {} - - ~ValueStorage() { - if (context != nullptr && value != nullptr) { - JSValueUnprotect(context, value); - } - } - - Kind kind = Kind::Undefined; - bool boolValue = false; - double numberValue = 0; - JSGlobalContextRef context = nullptr; - JSValueRef value = nullptr; -}; - -template -const void* hostObjectTypeToken() { - static int token = 0; - return &token; -} - -struct HostObjectHolder { - HostObjectHolder(std::shared_ptr state, std::shared_ptr hostObject, - const void* typeToken) - : state(std::move(state)), hostObject(std::move(hostObject)), typeToken(typeToken) {} - - std::shared_ptr state; - std::shared_ptr hostObject; - const void* typeToken = nullptr; -}; - -struct FunctionHolder { - FunctionHolder(std::shared_ptr state, HostFunctionType callback) - : state(std::move(state)), callback(std::move(callback)) {} - - std::shared_ptr state; - HostFunctionType callback; -}; - -struct ArrayBufferHolder { - explicit ArrayBufferHolder(std::shared_ptr buffer) : buffer(std::move(buffer)) {} - - std::shared_ptr buffer; -}; - -} // namespace jscdirect - -class Runtime { - public: - explicit Runtime(JSGlobalContextRef context) - : state_(std::make_shared(context)) {} - - explicit Runtime(std::shared_ptr state) : state_(std::move(state)) {} - - JSGlobalContextRef context() const { return state_->context; } - std::shared_ptr state() const { return state_; } - - Object global(); - Value evaluateJavaScript(std::shared_ptr buffer, const std::string& sourceURL); - void drainMicrotasks() {} - - private: - std::shared_ptr state_; -}; - -class String { - public: - String() = default; - String(Runtime& runtime, JSStringRef string); - - static String createFromUtf8(Runtime& runtime, const char* value) { - JSStringRef string = jscdirect::makeJSString(value); - String result(runtime, string); - JSStringRelease(string); - return result; - } - - static String createFromUtf8(Runtime& runtime, const std::string& value) { - JSStringRef string = jscdirect::makeJSString(value); - String result(runtime, string); - JSStringRelease(string); - return result; - } - - static String createFromUtf8(Runtime& runtime, const uint8_t* value, size_t length) { - std::string text(reinterpret_cast(value), length); - return createFromUtf8(runtime, text); - } - - std::string utf8(Runtime& runtime) const; - JSValueRef local(Runtime& runtime) const { return storage_->value; } - operator Value() const; - - private: - friend class Value; - std::shared_ptr storage_; -}; - -class Value { - public: - Value() - : storage_( - std::make_shared(jscdirect::ValueStorage::Kind::Undefined)) {} - - Value(bool value) - : storage_(std::make_shared(jscdirect::ValueStorage::Kind::Bool)) { - storage_->boolValue = value; - } - - Value(double value) - : storage_(std::make_shared(jscdirect::ValueStorage::Kind::Number)) { - storage_->numberValue = value; - } - - Value(int value) : Value(static_cast(value)) {} - Value(uint32_t value) : Value(static_cast(value)) {} - - Value(Runtime& runtime, const Value& value) : storage_(value.storage_) {} - Value(Runtime& runtime, Value&& value) : storage_(std::move(value.storage_)) {} - Value(Runtime& runtime, const String& value) : storage_(value.storage_) {} - Value(Runtime& runtime, const Object& object); - Value(Runtime& runtime, const Function& function); - Value(Runtime& runtime, const Array& array); - Value(Runtime& runtime, const ArrayBuffer& arrayBuffer); - Value(Runtime& runtime, const BigInt& bigint); - Value(Runtime& runtime, JSValueRef value) - : storage_(std::make_shared(jscdirect::ValueStorage::Kind::JSC)) { - storage_->context = runtime.context(); - storage_->value = value != nullptr ? value : JSValueMakeUndefined(runtime.context()); - JSValueProtect(runtime.context(), storage_->value); - } - - static Value undefined() { return Value(); } - static Value null() { - Value value; - value.storage_ = std::make_shared(jscdirect::ValueStorage::Kind::Null); - return value; - } - - bool isUndefined() const { - return storage_->kind == jscdirect::ValueStorage::Kind::Undefined || - (storage_->kind == jscdirect::ValueStorage::Kind::JSC && - JSValueIsUndefined(storage_->context, storage_->value)); - } - bool isNull() const { - return storage_->kind == jscdirect::ValueStorage::Kind::Null || - (storage_->kind == jscdirect::ValueStorage::Kind::JSC && - JSValueIsNull(storage_->context, storage_->value)); - } - bool isBool() const { - return storage_->kind == jscdirect::ValueStorage::Kind::Bool || - (storage_->kind == jscdirect::ValueStorage::Kind::JSC && - JSValueIsBoolean(storage_->context, storage_->value)); - } - bool getBool() const { - if (storage_->kind == jscdirect::ValueStorage::Kind::Bool) { - return storage_->boolValue; - } - if (storage_->kind == jscdirect::ValueStorage::Kind::JSC) { - return JSValueToBoolean(storage_->context, storage_->value); - } - return false; - } - bool isNumber() const { - return storage_->kind == jscdirect::ValueStorage::Kind::Number || - (storage_->kind == jscdirect::ValueStorage::Kind::JSC && - JSValueIsNumber(storage_->context, storage_->value)); - } - double getNumber() const { - if (storage_->kind == jscdirect::ValueStorage::Kind::Number) { - return storage_->numberValue; - } - if (storage_->kind == jscdirect::ValueStorage::Kind::JSC) { - return JSValueToNumber(storage_->context, storage_->value, nullptr); - } - return 0; - } - - bool isObject() const { - return storage_->kind == jscdirect::ValueStorage::Kind::JSC && - JSValueIsObject(storage_->context, storage_->value); - } - bool isString() const { - return storage_->kind == jscdirect::ValueStorage::Kind::JSC && - JSValueIsString(storage_->context, storage_->value); - } - bool isBigInt() const { - if (storage_->kind != jscdirect::ValueStorage::Kind::JSC) { - return false; - } - if (__builtin_available(macOS 15.0, iOS 18.0, *)) { - return JSValueIsBigInt(storage_->context, storage_->value); - } - return false; - } - bool isSymbol() const { - return storage_->kind == jscdirect::ValueStorage::Kind::JSC && - JSValueIsSymbol(storage_->context, storage_->value); - } - - Object asObject(Runtime& runtime) const; - String asString(Runtime& runtime) const; - BigInt getBigInt(Runtime& runtime) const; - - JSValueRef local(Runtime& runtime) const { - switch (storage_->kind) { - case jscdirect::ValueStorage::Kind::Undefined: - return JSValueMakeUndefined(runtime.context()); - case jscdirect::ValueStorage::Kind::Null: - return JSValueMakeNull(runtime.context()); - case jscdirect::ValueStorage::Kind::Bool: - return JSValueMakeBoolean(runtime.context(), storage_->boolValue); - case jscdirect::ValueStorage::Kind::Number: - return JSValueMakeNumber(runtime.context(), storage_->numberValue); - case jscdirect::ValueStorage::Kind::JSC: - return storage_->value; - } - } - - private: - friend class Runtime; - friend class Object; - friend class String; - friend class BigInt; - friend class ArrayBuffer; - friend class Function; - friend class Array; - std::shared_ptr storage_; -}; - -class Object { - public: - Object() = default; - explicit Object(Runtime& runtime) - : storage_(std::make_shared(jscdirect::ValueStorage::Kind::JSC)) { - storage_->context = runtime.context(); - storage_->value = JSObjectMake(runtime.context(), nullptr, nullptr); - JSValueProtect(runtime.context(), storage_->value); - } - - static Object fromValueStorage(std::shared_ptr storage) { - Object object; - object.storage_ = std::move(storage); - return object; - } - - template - static Object createFromHostObject(Runtime& runtime, std::shared_ptr host) { - auto baseHost = std::static_pointer_cast(std::move(host)); - return createFromHostObjectWithToken(runtime, std::move(baseHost), - jscdirect::hostObjectTypeToken()); - } - - Value getProperty(Runtime& runtime, const char* name) const { - JSStringRef property = jscdirect::makeJSString(name); - JSValueRef exception = nullptr; - JSValueRef result = - JSObjectGetProperty(runtime.context(), local(runtime), property, &exception); - JSStringRelease(property); - if (exception != nullptr) { - throw JSError(runtime, jscdirect::valueToUtf8(runtime.context(), exception)); - } - return Value(runtime, result); - } - - Value getProperty(Runtime& runtime, const std::string& name) const { - return getProperty(runtime, name.c_str()); - } - - Value getProperty(Runtime& runtime, const Value& key) const { - JSValueRef exception = nullptr; - JSValueRef result = JSObjectGetPropertyForKey(runtime.context(), local(runtime), - key.local(runtime), &exception); - if (exception != nullptr) { - throw JSError(runtime, jscdirect::valueToUtf8(runtime.context(), exception)); - } - return Value(runtime, result); - } - - Object getPropertyAsObject(Runtime& runtime, const char* name) const { - return getProperty(runtime, name).asObject(runtime); - } - - Function getPropertyAsFunction(Runtime& runtime, const char* name) const; - - void setProperty(Runtime& runtime, const char* name, const Value& value) { - JSStringRef property = jscdirect::makeJSString(name); - JSValueRef exception = nullptr; - JSObjectSetProperty(runtime.context(), local(runtime), property, value.local(runtime), - kJSPropertyAttributeNone, &exception); - JSStringRelease(property); - if (exception != nullptr) { - throw JSError(runtime, jscdirect::valueToUtf8(runtime.context(), exception)); - } - } - - void setProperty(Runtime& runtime, const char* name, const String& value) { - setProperty(runtime, name, Value(runtime, value)); - } - void setProperty(Runtime& runtime, const char* name, const Object& value) { - setProperty(runtime, name, Value(runtime, value)); - } - void setProperty(Runtime& runtime, const char* name, const Function& value); - void setProperty(Runtime& runtime, const char* name, const Array& value); - void setProperty(Runtime& runtime, const char* name, const ArrayBuffer& value); - void setProperty(Runtime& runtime, const char* name, bool value) { - setProperty(runtime, name, Value(value)); - } - void setProperty(Runtime& runtime, const char* name, double value) { - setProperty(runtime, name, Value(value)); - } - void setProperty(Runtime& runtime, const std::string& name, const Value& value) { - setProperty(runtime, name.c_str(), value); - } - void setProperty(Runtime& runtime, const Value& key, const Value& value) { - JSValueRef exception = nullptr; - JSObjectSetPropertyForKey(runtime.context(), local(runtime), key.local(runtime), - value.local(runtime), kJSPropertyAttributeNone, &exception); - if (exception != nullptr) { - throw JSError(runtime, jscdirect::valueToUtf8(runtime.context(), exception)); - } - } - - bool hasProperty(Runtime& runtime, const char* name) const { - JSStringRef property = jscdirect::makeJSString(name); - bool result = JSObjectHasProperty(runtime.context(), local(runtime), property); - JSStringRelease(property); - return result; - } - - bool isFunction(Runtime& runtime) const { - return JSObjectIsFunction(runtime.context(), local(runtime)); - } - - bool isArray(Runtime& runtime) const { - JSStringRef name = jscdirect::makeJSString("Array"); - JSValueRef constructorValue = JSObjectGetProperty( - runtime.context(), JSContextGetGlobalObject(runtime.context()), name, nullptr); - JSStringRelease(name); - if (constructorValue == nullptr || !JSValueIsObject(runtime.context(), constructorValue)) { - return false; - } - JSObjectRef constructor = JSValueToObject(runtime.context(), constructorValue, nullptr); - JSValueRef exception = nullptr; - bool result = - JSValueIsInstanceOfConstructor(runtime.context(), local(runtime), constructor, &exception); - return exception == nullptr && result; - } - - bool isArrayBuffer(Runtime& runtime) const { - JSValueRef exception = nullptr; - JSTypedArrayType type = - JSValueGetTypedArrayType(runtime.context(), storage_->value, &exception); - return exception == nullptr && type == kJSTypedArrayTypeArrayBuffer; - } - - Function asFunction(Runtime& runtime) const; - Array getArray(Runtime& runtime) const; - ArrayBuffer getArrayBuffer(Runtime& runtime) const; - Array getPropertyNames(Runtime& runtime) const; - - template - bool isHostObject(Runtime& runtime) const { - auto holder = hostObjectHolder(runtime); - return holder != nullptr && holder->typeToken == jscdirect::hostObjectTypeToken(); - } - - template - std::shared_ptr getHostObject(Runtime& runtime) const { - auto holder = hostObjectHolder(runtime); - if (holder == nullptr || holder->typeToken != jscdirect::hostObjectTypeToken()) { - return nullptr; - } - return std::static_pointer_cast(holder->hostObject); - } - - JSObjectRef local(Runtime& runtime) const { - return reinterpret_cast(const_cast(storage_->value)); - } - - operator Value() const { - Value value; - value.storage_ = storage_; - return value; - } - - protected: - friend class Value; - friend class Runtime; - friend class Function; - friend class Array; - friend class ArrayBuffer; - - explicit Object(std::shared_ptr storage) - : storage_(std::move(storage)) {} - - static Object createFromHostObjectWithToken(Runtime& runtime, std::shared_ptr host, - const void* typeToken); - - jscdirect::HostObjectHolder* hostObjectHolder(Runtime& runtime) const { - return static_cast(JSObjectGetPrivate(local(runtime))); - } - - std::shared_ptr storage_; -}; - -class Function : public Object { - public: - Function() = default; - explicit Function(Object object) : Object(std::move(object.storage_)) {} - - static Function createFromHostFunction(Runtime& runtime, const PropNameID& name, unsigned int, - HostFunctionType callback); - - Value call(Runtime& runtime, const Value* args, size_t count) const { - std::vector argv; - argv.reserve(count); - for (size_t i = 0; i < count; i++) { - argv.push_back(args[i].local(runtime)); - } - JSValueRef exception = nullptr; - JSValueRef result = JSObjectCallAsFunction( - runtime.context(), local(runtime), JSContextGetGlobalObject(runtime.context()), argv.size(), - argv.empty() ? nullptr : argv.data(), &exception); - if (exception != nullptr) { - throw JSError(runtime, jscdirect::valueToUtf8(runtime.context(), exception)); - } - return Value(runtime, result); - } - - Value call(Runtime& runtime) const { - return call(runtime, static_cast(nullptr), 0); - } - Value call(Runtime& runtime, std::nullptr_t, size_t) const { - return call(runtime, static_cast(nullptr), 0); - } - template - Value call(Runtime& runtime, const Value (&args)[N], size_t count) const { - return call(runtime, static_cast(args), count); - } - template - Value call(Runtime& runtime, Args&&... args) const { - Value argv[] = {Value(runtime, std::forward(args))...}; - return call(runtime, static_cast(argv), sizeof...(Args)); - } - - Value callWithThis(Runtime& runtime, const Object& thisObject, const Value* args = nullptr, - size_t count = 0) const { - std::vector argv; - argv.reserve(count); - for (size_t i = 0; i < count; i++) { - argv.push_back(args[i].local(runtime)); - } - JSValueRef exception = nullptr; - JSValueRef result = - JSObjectCallAsFunction(runtime.context(), local(runtime), thisObject.local(runtime), - argv.size(), argv.empty() ? nullptr : argv.data(), &exception); - if (exception != nullptr) { - throw JSError(runtime, jscdirect::valueToUtf8(runtime.context(), exception)); - } - return Value(runtime, result); - } - - Value callAsConstructor(Runtime& runtime, const Value* args, size_t count) const { - std::vector argv; - argv.reserve(count); - for (size_t i = 0; i < count; i++) { - argv.push_back(args[i].local(runtime)); - } - JSValueRef exception = nullptr; - JSValueRef result = JSObjectCallAsConstructor(runtime.context(), local(runtime), argv.size(), - argv.empty() ? nullptr : argv.data(), &exception); - if (exception != nullptr) { - throw JSError(runtime, jscdirect::valueToUtf8(runtime.context(), exception)); - } - return Value(runtime, result); - } - Value callAsConstructor(Runtime& runtime, std::nullptr_t, size_t) const { - return callAsConstructor(runtime, static_cast(nullptr), 0); - } - template - Value callAsConstructor(Runtime& runtime, const Value (&args)[N], size_t count) const { - return callAsConstructor(runtime, static_cast(args), count); - } - template - Value callAsConstructor(Runtime& runtime, Args&&... args) const { - Value argv[] = {Value(runtime, std::forward(args))...}; - return callAsConstructor(runtime, static_cast(argv), sizeof...(Args)); - } -}; - -class Array : public Object { - public: - explicit Array(Runtime& runtime, size_t size) - : Object(std::make_shared(jscdirect::ValueStorage::Kind::JSC)) { - std::vector initial(size, JSValueMakeUndefined(runtime.context())); - JSValueRef exception = nullptr; - storage_->context = runtime.context(); - storage_->value = - JSObjectMakeArray(runtime.context(), initial.size(), initial.data(), &exception); - if (exception != nullptr) { - throw JSError(runtime, jscdirect::valueToUtf8(runtime.context(), exception)); - } - JSValueProtect(runtime.context(), storage_->value); - } - - explicit Array(Object object) : Object(std::move(object.storage_)) {} - - size_t size(Runtime& runtime) const { - Value length = getProperty(runtime, "length"); - return length.isNumber() ? static_cast(std::max(0, length.getNumber())) : 0; - } - - Value getValueAtIndex(Runtime& runtime, size_t index) const { - JSValueRef exception = nullptr; - JSValueRef result = JSObjectGetPropertyAtIndex(runtime.context(), local(runtime), - static_cast(index), &exception); - if (exception != nullptr) { - throw JSError(runtime, jscdirect::valueToUtf8(runtime.context(), exception)); - } - return Value(runtime, result); - } - - void setValueAtIndex(Runtime& runtime, size_t index, const Value& value) { - JSValueRef exception = nullptr; - JSObjectSetPropertyAtIndex(runtime.context(), local(runtime), static_cast(index), - value.local(runtime), &exception); - if (exception != nullptr) { - throw JSError(runtime, jscdirect::valueToUtf8(runtime.context(), exception)); - } - } - void setValueAtIndex(Runtime& runtime, size_t index, const String& value) { - setValueAtIndex(runtime, index, Value(runtime, value)); - } -}; - -class BigInt { - public: - BigInt() = default; - BigInt(Runtime& runtime, JSValueRef value) - : storage_(std::make_shared(jscdirect::ValueStorage::Kind::JSC)) { - storage_->context = runtime.context(); - storage_->value = value; - JSValueProtect(runtime.context(), storage_->value); - } - - static BigInt fromInt64(Runtime& runtime, int64_t value) { - JSValueRef exception = nullptr; - JSValueRef result = nullptr; - if (__builtin_available(macOS 15.0, iOS 18.0, *)) { - result = JSBigIntCreateWithInt64(runtime.context(), value, &exception); - } - if (result == nullptr || exception != nullptr) { - result = JSValueMakeNumber(runtime.context(), static_cast(value)); - } - return BigInt(runtime, result); - } - - static BigInt fromUint64(Runtime& runtime, uint64_t value) { - JSValueRef exception = nullptr; - JSValueRef result = nullptr; - if (__builtin_available(macOS 15.0, iOS 18.0, *)) { - result = JSBigIntCreateWithUInt64(runtime.context(), value, &exception); - } - if (result == nullptr || exception != nullptr) { - result = JSValueMakeNumber(runtime.context(), static_cast(value)); - } - return BigInt(runtime, result); - } - - String toString(Runtime& runtime, int) const { - JSValueRef exception = nullptr; - JSStringRef string = JSValueToStringCopy(runtime.context(), local(runtime), &exception); - if (string == nullptr || exception != nullptr) { - throw JSError(runtime, jscdirect::valueToUtf8(runtime.context(), exception)); - } - String result(runtime, string); - JSStringRelease(string); - return result; - } - - JSValueRef local(Runtime& runtime) const { return storage_->value; } - - operator Value() const { - Value value; - value.storage_ = storage_; - return value; - } - - private: - friend class Value; - std::shared_ptr storage_; -}; - -class ArrayBuffer : public Object { - public: - ArrayBuffer(Runtime& runtime, std::shared_ptr buffer) - : Object(std::make_shared(jscdirect::ValueStorage::Kind::JSC)) { - auto* holder = new jscdirect::ArrayBufferHolder(std::move(buffer)); - JSValueRef exception = nullptr; - storage_->context = runtime.context(); - storage_->value = JSObjectMakeArrayBufferWithBytesNoCopy( - runtime.context(), holder->buffer->data(), holder->buffer->size(), - [](void*, void* deallocatorContext) { - delete static_cast(deallocatorContext); - }, - holder, &exception); - if (exception != nullptr) { - delete holder; - throw JSError(runtime, jscdirect::valueToUtf8(runtime.context(), exception)); - } - JSValueProtect(runtime.context(), storage_->value); - } - - explicit ArrayBuffer(Object object) : Object(std::move(object.storage_)) {} - - size_t size(Runtime& runtime) const { - JSValueRef exception = nullptr; - return JSObjectGetArrayBufferByteLength(runtime.context(), local(runtime), &exception); - } - - uint8_t* data(Runtime& runtime) const { - JSValueRef exception = nullptr; - return static_cast( - JSObjectGetArrayBufferBytesPtr(runtime.context(), local(runtime), &exception)); - } -}; -} // namespace jsi -} // namespace facebook - -#endif // TARGET_ENGINE_JSC - -#endif // NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_RUNTIME_H diff --git a/NativeScript/ffi/jsc/NativeApiJSCValue.mm b/NativeScript/ffi/jsc/NativeApiJSCValue.mm deleted file mode 100644 index ca4071567..000000000 --- a/NativeScript/ffi/jsc/NativeApiJSCValue.mm +++ /dev/null @@ -1,83 +0,0 @@ -#include "NativeApiJSCRuntime.h" - -#ifdef TARGET_ENGINE_JSC - -namespace facebook { -namespace jsi { - -Value HostObject::get(Runtime&, const PropNameID&) { return Value::undefined(); } -void HostObject::set(Runtime&, const PropNameID&, const Value&) {} -std::vector HostObject::getPropertyNames(Runtime&) { return {}; } - -String::String(Runtime& runtime, JSStringRef string) - : storage_(std::make_shared(jscdirect::ValueStorage::Kind::JSC)) { - storage_->context = runtime.context(); - storage_->value = JSValueMakeString(runtime.context(), string); - JSValueProtect(runtime.context(), storage_->value); -} - -std::string String::utf8(Runtime& runtime) const { - return jscdirect::valueToUtf8(runtime.context(), storage_->value); -} - -String::operator Value() const { - Value value; - value.storage_ = storage_; - return value; -} - -Value::Value(Runtime&, const Object& object) : storage_(object.storage_) {} -Value::Value(Runtime&, const Function& function) : storage_(function.storage_) {} -Value::Value(Runtime&, const Array& array) : storage_(array.storage_) {} -Value::Value(Runtime&, const ArrayBuffer& arrayBuffer) : storage_(arrayBuffer.storage_) {} -Value::Value(Runtime&, const BigInt& bigint) : storage_(bigint.storage_) {} - -Object Value::asObject(Runtime&) const { return Object::fromValueStorage(storage_); } - -String Value::asString(Runtime& runtime) const { - JSValueRef exception = nullptr; - JSStringRef string = JSValueToStringCopy(runtime.context(), local(runtime), &exception); - if (string == nullptr || exception != nullptr) { - throw JSError(runtime, jscdirect::valueToUtf8(runtime.context(), exception)); - } - String result(runtime, string); - JSStringRelease(string); - return result; -} - -BigInt Value::getBigInt(Runtime& runtime) const { return BigInt(runtime, local(runtime)); } - -Function Object::getPropertyAsFunction(Runtime& runtime, const char* name) const { - return getProperty(runtime, name).asObject(runtime).asFunction(runtime); -} -Function Object::asFunction(Runtime&) const { return Function(*this); } -Array Object::getArray(Runtime&) const { return Array(*this); } -ArrayBuffer Object::getArrayBuffer(Runtime&) const { return ArrayBuffer(*this); } - -Array Object::getPropertyNames(Runtime& runtime) const { - JSPropertyNameArrayRef propertyNames = - JSObjectCopyPropertyNames(runtime.context(), local(runtime)); - size_t count = JSPropertyNameArrayGetCount(propertyNames); - Array result(runtime, count); - for (size_t i = 0; i < count; i++) { - JSStringRef name = JSPropertyNameArrayGetNameAtIndex(propertyNames, i); - result.setValueAtIndex(runtime, i, String(runtime, name)); - } - JSPropertyNameArrayRelease(propertyNames); - return result; -} - -void Object::setProperty(Runtime& runtime, const char* name, const Function& value) { - setProperty(runtime, name, Value(runtime, value)); -} -void Object::setProperty(Runtime& runtime, const char* name, const Array& value) { - setProperty(runtime, name, Value(runtime, value)); -} -void Object::setProperty(Runtime& runtime, const char* name, const ArrayBuffer& value) { - setProperty(runtime, name, Value(runtime, value)); -} - -} // namespace jsi -} // namespace facebook - -#endif // TARGET_ENGINE_JSC diff --git a/NativeScript/ffi/napi/Cif.mm b/NativeScript/ffi/napi/Cif.mm deleted file mode 100644 index dfe18c8c3..000000000 --- a/NativeScript/ffi/napi/Cif.mm +++ /dev/null @@ -1,536 +0,0 @@ -#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" - -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: - case mdTypeString: - case mdTypePointer: - case mdTypeStruct: - case mdTypeArray: - case mdTypeBlock: - case mdTypeFunctionPointer: - case mdTypeVector: - case mdTypeExtVector: - case mdTypeComplex: - return true; - default: - return false; - } -} - -inline bool typeKindMayUseRoundTripCache(MDTypeKind kind) { - switch (kind) { - case mdTypeAnyObject: - case mdTypeProtocolObject: - case mdTypeClassObject: - case mdTypeInstanceObject: - case mdTypeNSStringObject: - case mdTypeNSMutableStringObject: - return true; - default: - return false; - } -} - -inline void updateGeneratedNapiDispatchCompatibility(Cif* cif) { - if (cif == nullptr) { - return; - } - - cif->skipGeneratedNapiDispatch = false; - cif->generatedDispatchHasRoundTripCacheArgument = false; - cif->generatedDispatchUsesObjectReturnStorage = false; - - if (cif->returnType != nullptr) { - cif->generatedDispatchUsesObjectReturnStorage = - typeKindMayUseRoundTripCache(cif->returnType->kind); - } - - cif->skipGeneratedNapiDispatch = typeRequiresSlowGeneratedNapiDispatch(cif->returnType); - if (cif->skipGeneratedNapiDispatch) { - return; - } - - for (const auto& argType : cif->argTypes) { - if (argType != nullptr && typeKindMayUseRoundTripCache(argType->kind)) { - cif->generatedDispatchHasRoundTripCacheArgument = true; - } - 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 -// invocation. -Cif* ObjCBridgeState::getMethodCif(napi_env env, Method method) { - auto encoding = std::string(method_getTypeEncoding(method)); - auto find = this->cifs[encoding]; - if (find != nullptr) { - return find; - } - - auto cif = new Cif(env, method); - this->cifs[encoding] = cif; - - return cif; -} - -Cif* ObjCBridgeState::getMethodCif(napi_env env, MDSectionOffset offset) { - auto find = this->mdMethodSignatureCache[offset]; - if (find != nullptr) { - return find; - } - - auto cif = new Cif(env, metadata, offset, true, false); - this->mdMethodSignatureCache[offset] = cif; - - return cif; -} - -Cif* ObjCBridgeState::getBlockCif(napi_env env, MDSectionOffset offset) { - auto find = this->mdBlockSignatureCache[offset]; - if (find != nullptr) { - return find; - } - - auto cif = new Cif(env, metadata, offset, false, true); - this->mdBlockSignatureCache[offset] = cif; - - return cif; -} - -Cif* ObjCBridgeState::getCFunctionCif(napi_env env, MDSectionOffset offset) { - auto find = this->mdFunctionSignatureCache[offset]; - if (find != nullptr) { - return find; - } - - auto cif = new Cif(env, metadata, offset, false, false); - this->mdFunctionSignatureCache[offset] = cif; - - return cif; -} - -Cif::Cif(napi_env env, std::string encoding, unsigned int implicitArgc) { - auto signature = [NSMethodSignature signatureWithObjCTypes:encoding.c_str()]; - unsigned long numberOfArguments = signature.numberOfArguments; - 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; - - const char* returnType = signature.methodReturnType; - this->returnType = TypeConv::Make(env, &returnType); - - ffi_type* rtype = this->returnType->type; - this->atypes = (ffi_type**)malloc(sizeof(ffi_type*) * totalArgc); - - unsigned long methodReturnLength = signature.methodReturnLength; - unsigned long frameLength = signature.frameLength; - - this->rvalue = malloc(methodReturnLength); - this->rvalueLength = methodReturnLength; - this->frameLength = frameLength; - - 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 = 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->ffiTypeForArgument(); - - if (i >= skippedArgs) { - 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 + 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; - - returnType = TypeConv::Make(env, reader, &offset); - - auto implicitArgs = isMethod ? 2 : isBlock ? 1 : 0; - - shouldFreeAny = false; - atypes = nullptr; - avaluesAllocStart = 0; - avaluesAllocCount = 0; - - if (next || isMethod || isBlock) { - while (next) { - auto argTypeKind = reader->getTypeKind(offset); - next = ((MDTypeFlag)argTypeKind & mdTypeFlagNext) != 0; - auto argTypeInfo = TypeConv::Make(env, reader, &offset); - std::string enc; - argTypeInfo->encode(&enc); - argTypes.push_back(argTypeInfo); - } - - argc = (int)argTypes.size(); - - auto totalArgc = argc + implicitArgs; - - argv = (napi_value*)malloc(sizeof(napi_value) * argc); - shouldFree = (bool*)malloc(sizeof(bool) * argc); - - atypes = (ffi_type**)malloc(sizeof(ffi_type*) * totalArgc); - avalues = (void**)malloc(sizeof(void*) * argc); - memset(avalues, 0, sizeof(void*) * argc); - - if (isMethod) { - atypes[0] = &ffi_type_pointer; - atypes[1] = &ffi_type_pointer; - } - - if (isBlock) { - atypes[0] = &ffi_type_pointer; - } - - for (int i = 0; i < argc; i++) { - atypes[i + implicitArgs] = argTypes[i]->ffiTypeForArgument(); - shouldFree[i] = false; - } - } else { - argc = 0; - argv = nullptr; - avalues = nullptr; - shouldFree = nullptr; - } - - ffi_status status = - ffi_prep_cif(&cif, FFI_DEFAULT_ABI, argc + implicitArgs, returnType->type, atypes); - - if (status != FFI_OK) { - std::cout << "Failed to prepare CIF, libffi returned error: " << status << std::endl; - return; - } - - for (int i = 0; i < argc; i++) { - avalues[i] = malloc(cif.arg_types[i + implicitArgs]->size); - avaluesAllocCount++; - } - - 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() { - if (rvalue != nullptr) { - free(rvalue); - } - if (argv != nullptr) { - free(argv); - } - if (avalues != nullptr) { - for (unsigned int i = 0; i < avaluesAllocCount; i++) { - auto index = avaluesAllocStart + i; - if (avalues[index] != nullptr) { - free(avalues[index]); - } - } - free(avalues); - } - if (atypes != nullptr) { - free(atypes); - } - if (shouldFree != nullptr) { - free(shouldFree); - } -} - -} // namespace nativescript diff --git a/NativeScript/ffi/napi/Closure.h b/NativeScript/ffi/napi/Closure.h deleted file mode 100644 index 7ceafd49c..000000000 --- a/NativeScript/ffi/napi/Closure.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef CLOSURE_H -#define CLOSURE_H - -#include - -#include -#include -#include - -#include "MetadataReader.h" -#include "TypeConv.h" -#include "ffi.h" -#include "node_api_util.h" -#include "objc/runtime.h" - -namespace nativescript { - -class ObjCBridgeState; - -class Closure { - public: - static void callBlockFromMainThread(napi_env env, napi_value js_cb, - void* context, void* data); - static void destroyOnOwningThread(Closure* closure); - - Closure(napi_env env, std::string typeEncoding, bool isBlock, bool isMethod = false); - Closure(napi_env env, 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 = nullptr; - ObjCBridgeState* bridgeState = nullptr; - uint64_t bridgeStateToken = 0; - napi_ref thisConstructor; - napi_ref func = nullptr; - bool isGetter = false; - bool isSetter = false; - std::string propertyName; - SEL selector = nullptr; - napi_threadsafe_function tsfn = nullptr; - - std::thread::id jsThreadId = std::this_thread::get_id(); - CFRunLoopRef jsRunLoop = CFRunLoopGetCurrent(); - std::atomic retainCount{1}; - - ffi_cif cif; - ffi_closure* closure; - void* fnptr; - ffi_type** atypes = nullptr; // Track malloc'd atypes array - - std::shared_ptr returnType; - std::vector> argTypes; -}; - -} // namespace nativescript - -#endif /* CLOSURE_H */ diff --git a/NativeScript/ffi/napi/ObjCBridge.mm b/NativeScript/ffi/napi/ObjCBridge.mm deleted file mode 100644 index 0c7ebd76a..000000000 --- a/NativeScript/ffi/napi/ObjCBridge.mm +++ /dev/null @@ -1,1142 +0,0 @@ -#include "ObjCBridge.h" -#include "AutoreleasePool.h" -#include "Block.h" -#include "Class.h" -#include "ClassMember.h" -#include "Enum.h" -#include "InlineFunctions.h" -#include "Interop.h" -#include "Metadata.h" -#include "MetadataReader.h" -#include "NativeScript.h" -#include "Object.h" -#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"))) -#if defined(__aarch64__) -embedded_metadata[EMBED_METADATA_SIZE] = "NSMDSectionHeaderARM"; -#else -embedded_metadata[EMBED_METADATA_SIZE] = "NSMDSectionHeaderX86"; -#endif -#endif - -namespace nativescript { -namespace { -std::mutex gLiveBridgeStatesMutex; -std::unordered_map gLiveBridgeStates; -std::atomic gNextBridgeStateToken{1}; -constexpr const char* kNativePointerProperty = "__ns_native_ptr"; - -inline void deleteReferenceNow(napi_env env, napi_ref ref, bool unrefFirst) { - if (env == nullptr || ref == nullptr) { - return; - } - - if (unrefFirst) { - uint32_t remaining = 0; - napi_reference_unref(env, ref, &remaining); - } - - napi_delete_reference(env, ref); -} - -inline void deleteReferenceOnOwningThread(napi_env env, ObjCBridgeState* bridgeState, - uint64_t bridgeStateToken, napi_ref ref, - bool unrefFirst) { - if (env == nullptr || ref == nullptr) { - return; - } - - if (bridgeState == nullptr) { - deleteReferenceNow(env, ref, unrefFirst); - return; - } - - if (!IsBridgeStateLive(bridgeState, bridgeStateToken)) { - return; - } - - if (bridgeState->jsThreadId == std::this_thread::get_id()) { -#if !defined(TARGET_ENGINE_QUICKJS) - deleteReferenceNow(env, ref, unrefFirst); - return; -#endif - } - - CFRunLoopRef runLoop = bridgeState->jsRunLoop; - if (runLoop == nullptr) { - runLoop = CFRunLoopGetMain(); - } - - if (runLoop == nullptr) { - if (bridgeState->jsThreadId == std::this_thread::get_id()) { - deleteReferenceNow(env, ref, unrefFirst); - } - return; - } - - CFRetain(runLoop); - CFRunLoopPerformBlock(runLoop, kCFRunLoopCommonModes, ^{ - if (IsBridgeStateLive(bridgeState, bridgeStateToken)) { - deleteReferenceNow(env, ref, unrefFirst); - } - CFRelease(runLoop); - }); - CFRunLoopWakeUp(runLoop); -} - -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 DeleteReferenceOnOwningThread(napi_env env, ObjCBridgeState* bridgeState, - uint64_t bridgeStateToken, napi_ref ref) { - deleteReferenceOnOwningThread(env, bridgeState, bridgeStateToken, ref, false); -} - -void ReleaseAndDeleteReferenceOnOwningThread(napi_env env, ObjCBridgeState* bridgeState, - uint64_t bridgeStateToken, napi_ref ref) { - deleteReferenceOnOwningThread(env, bridgeState, bridgeStateToken, ref, true); -} - -bool PostFinalizer(napi_env env, napi_finalize finalize_cb, void* finalize_data, - void* finalize_hint) { - if (env == nullptr || finalize_cb == nullptr) { - return false; - } - - ObjCBridgeState* bridgeState = ObjCBridgeState::InstanceData(env); - if (bridgeState != nullptr && bridgeState->jsThreadId == std::this_thread::get_id()) { -#if !defined(TARGET_ENGINE_QUICKJS) - finalize_cb(env, finalize_data, finalize_hint); - return true; -#endif - } - - CFRunLoopRef runLoop = bridgeState != nullptr ? bridgeState->jsRunLoop : CFRunLoopGetMain(); - if (runLoop == nullptr) { - return false; - } - - if (bridgeState == nullptr && [NSThread isMainThread]) { -#if !defined(TARGET_ENGINE_QUICKJS) - finalize_cb(env, finalize_data, finalize_hint); - return true; -#endif - } - - CFRetain(runLoop); - CFRunLoopPerformBlock(runLoop, kCFRunLoopCommonModes, ^{ - finalize_cb(env, finalize_data, finalize_hint); - CFRelease(runLoop); - }); - CFRunLoopWakeUp(runLoop); - return true; -} - -void finalize_bridge_data(napi_env env, void* data, void* hint) { - auto bridgeState = (ObjCBridgeState*)data; - delete bridgeState; -} - -MDMetadataReader* loadMetadataFromFile(const char* metadata_path) { - if (metadata_path == nullptr) { - metadata_path = "metadata.nsmd"; - } - - auto f = fopen(metadata_path == nullptr ? "metadata.nsmd" : metadata_path, "r"); - if (f == nullptr) { - fprintf(stderr, "metadata.nsmd not found\n"); - exit(1); - } - fseek(f, 0, SEEK_END); - auto size = ftell(f); - fseek(f, 0, SEEK_SET); - auto buffer = (uint8_t*)malloc(size); - fread(buffer, 1, size, f); - fclose(f); - 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; - } - if (valueType == napi_function) { - return true; - } - - if (valueType != napi_object) { - return false; - } - - napi_value instance = nullptr; - napi_status status = napi_new_instance(env, value, 0, nullptr, &instance); - if (status == napi_ok) { - return true; - } - - 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); - } - return false; -} - -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) { - 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 !== "undefined") { - globalObject.UIColor = globalObject.NSColor; - } - - const colorCtor = globalObject.UIColor || globalObject.NSColor; - if (!colorCtor || !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(env, std::string("v"), true); - 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); - trackedObjectLiveness = [[NSMutableSet alloc] init]; - - 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"); - metadata = new MDMetadataReader((void*)embedded_metadata); -#else - // NSLog(@"Using metadata from pointer: %p", metadata_ptr); - metadata = new MDMetadataReader((void*)metadata_ptr); -#endif - } else { -#ifdef EMBED_METADATA_SIZE - if (metadata_path != nullptr) { - // NSLog(@"Loading metadata from file: %s", metadata_path); - metadata = loadMetadataFromFile(metadata_path); - } else { - // NSLog(@"Using embedded metadata"); - metadata = new MDMetadataReader((void*)embedded_metadata); - } -#else - unsigned long segmentSize = 0; - auto segmentData = getsegmentdata((const mach_header_64*)_dyld_get_image_header(0), - "__objc_metadata", &segmentSize); - if (segmentData != nullptr) { - metadata = new MDMetadataReader(segmentData); - } else { - metadata = loadMetadataFromFile(metadata_path); - } -#endif - } - - // objc_autoreleasePool = objc_autoreleasePoolPush(); -} - -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) { - ObjCBridgeState::releaseRoundTripEntry(env, entry.second); - } - } - roundTripCacheFrames.clear(); - - for (auto& entry : recentRoundTripCache) { - ObjCBridgeState::releaseRoundTripEntry(env, entry.second); - } - recentRoundTripCache.clear(); - - for (auto& entry : handleObjectRefs) { - if (entry.second.ownsRef) { - deleteRef(entry.second.ref); - } - } - handleObjectRefs.clear(); - - for (auto& entry : recentObjectWrappers) { - deleteRef(entry.ref); - } - recentObjectWrappers.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; - } - cifs.clear(); - - for (auto& pair : mdMethodSignatureCache) { - delete pair.second; - } - mdMethodSignatureCache.clear(); - - for (auto& pair : mdBlockSignatureCache) { - delete pair.second; - } - mdBlockSignatureCache.clear(); - - // Clean up ObjCClass objects - for (auto& pair : classes) { - delete pair.second; - } - classes.clear(); - - // Clean up ObjCProtocol objects - for (auto& pair : protocols) { - delete pair.second; - } - protocols.clear(); - - // Clean up StructInfo objects - for (auto& pair : structInfoCache) { - delete pair.second; - } - structInfoCache.clear(); - - // Clean up CFunction objects - for (auto& pair : cFunctionCache) { - delete pair.second; - } - cFunctionCache.clear(); - - for (auto& pair : mdFunctionSignatureCache) { - delete pair.second; - } - mdFunctionSignatureCache.clear(); - - NSMutableSet* trackedObjectTable = static_cast(trackedObjectLiveness); - trackedObjectLiveness = nullptr; - [trackedObjectTable release]; - - // if (objc_autoreleasePool != nullptr) - // objc_autoreleasePoolPop(objc_autoreleasePool); - - delete metadata; - dlclose(self_dl); -} - -napi_value ObjCBridgeState::proxyNativeObject(napi_env env, napi_value object, id nativeObject) { - NAPI_PREAMBLE - - napi_value result = object; - const bool nativeIsArray = [nativeObject isKindOfClass:NSArray.class]; - bool shouldProxyArray = nativeIsArray; - if (shouldProxyArray) { - napi_value factory = get_ref_value(env, createNativeProxy); - napi_value transferOwnershipFunc = get_ref_value(env, this->transferOwnershipToNative); - napi_value global; - napi_value args[3] = {object, nullptr, transferOwnershipFunc}; - napi_get_boolean(env, true, &args[1]); - napi_get_global(env, &global); - napi_call_function(env, global, factory, 3, args, &result); - } - - napi_value nativePointer = Pointer::create(env, nativeObject); - if (nativePointer != nullptr) { - napi_set_named_property(env, result, kNativePointerProperty, nativePointer); - } - napi_wrap(env, result, nativeObject, nullptr, nullptr, nullptr); - - napi_ref ref = nullptr; - auto* finalizerContext = new JSObjectFinalizerContext{ - .bridgeState = this, - .bridgeStateToken = lifetimeToken, - .object = nativeObject, - .ref = nullptr, - }; - NAPI_GUARD( - napi_add_finalizer(env, result, finalizerContext, finalize_objc_object, nullptr, &ref)) { - delete finalizerContext; - NAPI_THROW_LAST_ERROR - return nullptr; - } - finalizerContext->ref = ref; - - storeObjectRef(nativeObject, ref); - cacheHandleObjectRef(env, nativeObject, ref); - cacheRecentObjectWrapper(env, nativeObject, result); - attachObjectLifecycleAssociation(env, nativeObject); - trackObject(nativeObject); - - return result; -} - -void ObjCBridgeState::trackObject(id object) noexcept { - if (object == nil) { - return; - } - - NSMutableSet* trackedObjectTable = static_cast(trackedObjectLiveness); - if (trackedObjectTable == nil) { - return; - } - - NSNumber* objectKey = [NSNumber numberWithUnsignedLongLong:NormalizeHandleKey((void*)object)]; - std::lock_guard lock(objectRefsMutex); - [trackedObjectTable addObject:objectKey]; -} - -bool ObjCBridgeState::isTrackedObjectAlive(id object) const noexcept { - if (object == nil) { - return false; - } - - NSMutableSet* trackedObjectTable = static_cast(trackedObjectLiveness); - if (trackedObjectTable == nil) { - return false; - } - - NSNumber* objectKey = [NSNumber numberWithUnsignedLongLong:NormalizeHandleKey((void*)object)]; - std::lock_guard lock(objectRefsMutex); - return [trackedObjectTable containsObject:objectKey]; -} - -} // namespace nativescript - -using namespace nativescript; - -NAPI_FUNCTION(getArrayBuffer) { - NAPI_CALLBACK_BEGIN(2) - - void* ptr = Pointer::unwrap(env, argv[0])->data; - int64_t length; - napi_get_value_int64(env, argv[1], &length); - - napi_value arrayBuffer; - if (length < 0) { - napi_throw_error(env, nullptr, "Invalid ArrayBuffer length"); - return nullptr; - } - - napi_create_external_arraybuffer(env, ptr, static_cast(length), nullptr, nullptr, - &arrayBuffer); - - return arrayBuffer; -} - -NAPI_FUNCTION(init) { - NAPI_CALLBACK_BEGIN(1) - napi_valuetype type; - napi_typeof(env, argv[0], &type); - const char* metadata_path = nullptr; - if (type == napi_string) { - size_t len; - napi_get_value_string_utf8(env, argv[0], nullptr, 0, &len); - metadata_path = (char*)malloc(len + 1); - napi_get_value_string_utf8(env, argv[0], (char*)metadata_path, len + 1, &len); - } - nativescript_init(env, metadata_path, nullptr); - return nullptr; -} - -NAPI_EXPORT NAPI_MODULE_REGISTER { - const napi_property_descriptor property = NAPI_FUNCTION_DESC(init); - napi_define_properties(env, exports, 1, &property); - return exports; -} - -NAPI_EXPORT void nativescript_init(void* _env, const char* metadata_path, - const void* metadata_ptr) { - napi_env env = (napi_env)_env; - - ObjCBridgeState* bridgeState = new ObjCBridgeState(env, metadata_path, metadata_ptr); - - napi_value objc; - napi_create_object(env, &objc); - - const napi_property_descriptor objcProperties[] = { - NAPI_FUNCTION_DESC(registerClass), NAPI_FUNCTION_DESC(registerBlock), - NAPI_FUNCTION_DESC(import), NAPI_FUNCTION_DESC(autoreleasepool), - NAPI_FUNCTION_DESC(getArrayBuffer), - }; - - napi_define_properties(env, objc, 5, objcProperties); - - napi_value global; - napi_get_global(env, &global); - - const napi_property_descriptor globalProperties[] = {{ - .utf8name = "objc", - .method = nullptr, - .getter = nullptr, - .setter = nullptr, - .value = objc, - .attributes = napi_enumerable, - .data = nullptr, - }, - { - .utf8name = "ObjectRef", - .method = nullptr, - .getter = nullptr, - .setter = nullptr, - .value = defineObjectRefClass(env), - .attributes = napi_enumerable, - .data = nullptr, - }, - { - .utf8name = "NativeClass", - .method = JS_registerClass, - .getter = nullptr, - .setter = nullptr, - .value = nullptr, - .attributes = napi_enumerable, - .data = nullptr, - }}; - - napi_define_properties(env, global, 3, globalProperties); - - setupObjCClassDecorator(env); - - initProxyFactory(env, bridgeState); - initFastEnumeratorIteratorFactory(env, bridgeState); - - registerInterop(env, global); - registerInlineFunctions(env); - - bridgeState->registerVarGlobals(env, global); - bridgeState->registerEnumGlobals(env, global); - bridgeState->registerStructGlobals(env, global); - bridgeState->registerUnionGlobals(env, global); - bridgeState->registerFunctionGlobals(env, global); - bridgeState->registerClassGlobals(env, global); - bridgeState->registerProtocolGlobals(env, global); - registerLegacyCompatGlobals(env, global, bridgeState); -} diff --git a/NativeScript/ffi/napi/SignatureDispatch.h b/NativeScript/ffi/napi/SignatureDispatch.h deleted file mode 100644 index c42a82376..000000000 --- a/NativeScript/ffi/napi/SignatureDispatch.h +++ /dev/null @@ -1,223 +0,0 @@ -#ifndef NS_FFI_NAPI_SIGNATURE_DISPATCH_H -#define NS_FFI_NAPI_SIGNATURE_DISPATCH_H - -#include - -#include -#include -#include - -#include "Cif.h" -#include "js_native_api.h" - -namespace nativescript { - -enum class SignatureCallKind : uint8_t { - ObjCMethod = 1, - CFunction = 2, - BlockInvoke = 3, -}; - -using ObjCPreparedInvoker = void (*)(void* fnptr, void** avalues, void* rvalue); -using CFunctionPreparedInvoker = void (*)(void* fnptr, void** avalues, - void* rvalue); -using BlockPreparedInvoker = 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 BlockDispatchEntry { - uint64_t dispatchId; - BlockPreparedInvoker 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_GSD_BACKEND_NAPI -#define NS_GSD_BACKEND_NAPI 1 -#endif - -#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 - -#ifndef NS_GSD_BACKEND_V8 -#define NS_GSD_BACKEND_V8 0 -#endif - -#ifndef NS_GSD_BACKEND_JSC -#define NS_GSD_BACKEND_JSC 0 -#endif - -#ifndef NS_GSD_BACKEND_QUICKJS -#define NS_GSD_BACKEND_QUICKJS 0 -#endif - -#ifndef NS_GSD_BACKEND_HERMES -#define NS_GSD_BACKEND_HERMES 0 -#endif - -#ifndef NS_GSD_BACKEND_ENGINE_DIRECT -#define NS_GSD_BACKEND_ENGINE_DIRECT 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}}; -inline constexpr BlockDispatchEntry kGeneratedBlockDispatchEntries[] = { - {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 BlockPreparedInvoker lookupBlockPreparedInvoker(uint64_t dispatchId) { - if (!isGeneratedDispatchEnabled()) { - return nullptr; - } - return lookupDispatchInvoker( - kGeneratedBlockDispatchEntries, 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_FFI_NAPI_SIGNATURE_DISPATCH_H diff --git a/NativeScript/ffi/objc/hermes/NativeApiJsi.h b/NativeScript/ffi/objc/hermes/NativeApiJsi.h new file mode 100644 index 000000000..e98ba0431 --- /dev/null +++ b/NativeScript/ffi/objc/hermes/NativeApiJsi.h @@ -0,0 +1,26 @@ +#ifndef NATIVE_API_JSI_H +#define NATIVE_API_JSI_H + +#include + +#include "ffi/objc/shared/NativeApiBackendConfig.h" + +namespace nativescript { + +using NativeApiJsiScheduler = NativeApiBackendScheduler; +using NativeApiJsiConfig = NativeApiBackendConfig; + +facebook::jsi::Object CreateNativeApiJSI( + facebook::jsi::Runtime& runtime, + const NativeApiJsiConfig& config = NativeApiJsiConfig{}); + +void InstallNativeApiJSI( + facebook::jsi::Runtime& runtime, + const NativeApiJsiConfig& config = NativeApiJsiConfig{}); + +} // namespace nativescript + +extern "C" void NativeScriptInstallNativeApiJSI( + facebook::jsi::Runtime* runtime, const char* metadataPath); + +#endif // NATIVE_API_JSI_H diff --git a/NativeScript/ffi/objc/hermes/NativeApiJsi.mm b/NativeScript/ffi/objc/hermes/NativeApiJsi.mm new file mode 100644 index 000000000..e7f508d7a --- /dev/null +++ b/NativeScript/ffi/objc/hermes/NativeApiJsi.mm @@ -0,0 +1,351 @@ +#include "NativeApiJsi.h" + +#ifdef TARGET_ENGINE_HERMES + +#import +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Metadata.h" +#include "MetadataReader.h" +#include "ffi.h" +#include "NativeApiJsiSignatureDispatch.h" + +@protocol NativeApiClassBuilderProtocol +@end + +#ifdef EMBED_METADATA_SIZE +extern const unsigned char embedded_metadata[EMBED_METADATA_SIZE]; +#endif + +namespace nativescript { +namespace { + +using facebook::jsi::Array; +using facebook::jsi::ArrayBuffer; +using facebook::jsi::BigInt; +using facebook::jsi::Function; +using facebook::jsi::HostObject; +using facebook::jsi::MutableBuffer; +using facebook::jsi::Object; +using facebook::jsi::PropNameID; +using facebook::jsi::Runtime; +using facebook::jsi::String; +using facebook::jsi::StringBuffer; +using facebook::jsi::Value; +using facebook::jsi::JSError; + +using NativeApiConfig = NativeApiJsiConfig; +using NativeApiScheduler = NativeApiJsiScheduler; +using metagen::MDMemberFlag; +using metagen::MDMetadataReader; +using metagen::MDSectionOffset; +using metagen::MDTypeKind; + +void SetNativeApiObjectPrototype(Runtime& runtime, Object& object, + const Object& prototype) { + Object objectConstructor = + runtime.global().getPropertyAsObject(runtime, "Object"); + Function setPrototypeOf = + objectConstructor.getPropertyAsFunction(runtime, "setPrototypeOf"); + setPrototypeOf.call(runtime, Value(runtime, object), Value(runtime, prototype)); +} + +// clang-format off +#define NATIVESCRIPT_NATIVE_API_RUNTIME_NAME "jsi" +#define NATIVESCRIPT_NATIVE_API_BACKEND_NAME "hermes" +#define NATIVESCRIPT_NATIVE_API_HOST_SET_VOID 1 +#define NATIVESCRIPT_NATIVE_API_HOST_EXPLICIT_OVERRIDE 1 +#define NATIVESCRIPT_NATIVE_API_HAS_ENGINE_SELECTOR_GROUP_FUNCTION 1 +#include "../shared/bridge/ObjCBridge.mm" +#include "../shared/bridge/HostObjects.mm" +#include "../shared/bridge/Callbacks.mm" +#include "../shared/bridge/TypeConv.mm" +#include "../shared/bridge/Invocation.mm" +#include "../shared/bridge/ClassBuilder.mm" +#include "../shared/bridge/HostObject.mm" +// clang-format on + +#include "NativeApiJsiGsd.mm" + + +void* lookupGeneratedEngineObjCGsdInvoker(uint64_t dispatchId) { + return reinterpret_cast(lookupObjCGsdInvoker(dispatchId)); +} + +bool tryCallGeneratedEngineObjCSelector( + Runtime& runtime, const std::shared_ptr& bridge, + id receiver, const NativeApiPreparedObjCInvocation& prepared, + const Value* args, size_t count, Class dispatchSuperClass, Value* result) { + if (result == nullptr || receiver == nil || + !prepared.gsdEngineCallable || dispatchSuperClass != Nil || + count != prepared.gsdEngineArgumentCount) { + return false; + } + + auto invoker = reinterpret_cast(prepared.engineInvoker); + GsdObjCContext ctx{runtime, bridge, receiver, prepared.selector, args, + prepared.signature.returnType}; + if (!invoker(ctx)) { + return false; + } + *result = std::move(ctx.result); + return true; +} + +Function CreateNativeApiSelectorGroupFunctionImpl( + Runtime& runtime, std::shared_ptr bridge, + Class lookupClass, bool receiverIsClass, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations, + std::weak_ptr boundReceiver = {}, + std::shared_ptr boundReceiverState = + nullptr) { + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "__nativeSelectorGroup"), 0, + [bridge = std::move(bridge), lookupClass, receiverIsClass, + selectors = std::move(selectors), + preparedInvocations = std::move(preparedInvocations), + boundReceiver = std::move(boundReceiver), + boundReceiverState = std::move(boundReceiverState), + cachedReceiverClass = Class(Nil), + cachedDispatchClass = Class(Nil)]( + Runtime& runtime, const Value& thisValue, const Value* args, + size_t count) mutable -> Value { + NativeApiRoundTripCacheFrameGuard roundTripFrame(bridge); + if (count >= selectors->size() || + (*selectors)[count].selectorName.empty()) { + throw JSError(runtime, + "Objective-C selector is not available for the provided " + "arguments count."); + } + + NativeApiSelectorGroupEntry& entry = (*selectors)[count]; + auto& prepared = (*preparedInvocations)[count]; + Class selectorLookupClass = lookupClass; + id receiver = receiverIsClass ? static_cast(lookupClass) : nil; + std::shared_ptr receiverHostObject; + if (!receiverIsClass) { + if (boundReceiverState != nullptr) { + receiver = boundReceiverState->object(); + if (receiver == nil) { + throw JSError(runtime, + "Objective-C selector requires a native receiver."); + } + } else if (thisValue.isObject()) { + Object receiverObject = thisValue.asObject(runtime); + if (receiverObject.isHostObject( + runtime)) { + receiverHostObject = + receiverObject.getHostObject( + runtime); + receiver = receiverHostObject->object(); + } + } + } + if (receiver == nil) { + throw JSError(runtime, + "Objective-C selector requires a native receiver."); + } + + const bool propertyGetterCall = + entry.hasMember && entry.member.property && count == 0; + const std::string* selectorNamePtr = &entry.selectorName; + const NativeApiMember* selectedMember = + entry.hasMember ? &entry.member : nullptr; + bool callTargetCanPrepare = true; + if (prepared == nullptr || propertyGetterCall) { + NativeApiSelectorGroupCallTarget callTarget = + selectorGroupCallTargetForEntry(receiver, selectorLookupClass, + receiverIsClass, entry, count); + selectorNamePtr = callTarget.selectorName; + selectedMember = callTarget.member; + callTargetCanPrepare = callTarget.canPrepare; + if (prepared != nullptr && prepared->selectorName != *selectorNamePtr) { + prepared = nullptr; + } + } + const std::string& selectorName = + prepared != nullptr && !propertyGetterCall ? prepared->selectorName + : *selectorNamePtr; + + if (receiverIsClass) { + Class methodClass = prepared != nullptr ? prepared->receiverClass : Nil; + if (methodClass == Nil) { + SEL selector = sel_registerName(selectorName.c_str()); + methodClass = + NativeApiClassHostObject::classRespondingToClassSelector( + lookupClass, selector); + } + if (methodClass == Nil) { + throw JSError(runtime, + "Objective-C selector is not available: " + + entry.selectorName); + } + selectorLookupClass = methodClass; + receiver = static_cast(methodClass); + } + if (propertyGetterCall && !callTargetCanPrepare) { + return callObjCSelector(runtime, bridge, receiver, receiverIsClass, + selectorName, selectedMember, nullptr, 0); + } + + if (prepared == nullptr) { + if (!receiverIsClass) { + SEL selector = sel_registerName(selectorName.c_str()); + if (class_getInstanceMethod(selectorLookupClass, selector) == nullptr) { + Class receiverClass = object_getClass(receiver); + if (class_getInstanceMethod(receiverClass, selector) != nullptr) { + selectorLookupClass = receiverClass; + } + } + } + prepared = prepareNativeApiObjCInvocation( + runtime, bridge, selectorLookupClass, receiverIsClass, selectorName, + selectedMember); + // Look up the engine-neutral GSD invoker for this signature. + if (prepared->engineInvoker == nullptr) { + uint64_t dispatchId = dispatchIdForEngineSignature( + prepared->signature, SignatureCallKind::ObjCMethod); + if (auto gsdInvoker = lookupObjCGsdInvoker(dispatchId)) { + prepared->engineInvoker = reinterpret_cast(gsdInvoker); + configureGeneratedEngineObjCInvocation(*prepared); + } + } + } + + // Memoized dispatch-superclass resolution (pure function of the + // receiver's class + lookupClass) — avoids a per-call + // class_conformsToProtocol probe. + Class gsdDispatchClass = Nil; + if (!receiverIsClass) { + Class receiverClass = object_getClass(receiver); + if (receiverClass == cachedReceiverClass) { + gsdDispatchClass = cachedDispatchClass; + } else { + gsdDispatchClass = + dispatchSuperclassForEngineDerivedReceiver(receiver, lookupClass); + cachedReceiverClass = receiverClass; + cachedDispatchClass = gsdDispatchClass; + } + } + // GSD fast path: read jsi args directly, call objc_msgSend with a + // typed cast, produce the jsi return value — bypassing all generic + // marshalling. Only engages for plain calls (no super dispatch, init + // disown handling, or implicit NSError-out argument). + if (prepared->gsdEngineCallable && gsdDispatchClass == Nil && + count == prepared->gsdEngineArgumentCount && + !(!receiverIsClass && prepared->isInitMethod)) { + auto invoker = + reinterpret_cast(prepared->engineInvoker); + GsdObjCContext ctx{runtime, bridge, receiver, prepared->selector, + args, prepared->signature.returnType}; + if (invoker(ctx)) { + return std::move(ctx.result); + } + } + + if (receiverIsClass) { + return callPreparedObjCSelector(runtime, bridge, receiver, true, + *prepared, args, count, Nil); + } + if (!receiverHostObject) { + if (boundReceiverState != nullptr) { + if (auto bound = boundReceiver.lock()) { + receiverHostObject = std::move(bound); + } + } else if (thisValue.isObject()) { + Object receiverObject = thisValue.asObject(runtime); + if (receiverObject.isHostObject( + runtime)) { + receiverHostObject = + receiverObject.getHostObject( + runtime); + } + } + } + if (!receiverHostObject) { + throw JSError(runtime, + "Objective-C selector requires a native receiver."); + } + return receiverHostObject->callPreparedObjectSelector( + runtime, *prepared, args, count, gsdDispatchClass); + }); +} + +Function CreateNativeApiSelectorGroupFunction( + Runtime& runtime, std::shared_ptr bridge, + Class lookupClass, bool receiverIsClass, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations) { + return CreateNativeApiSelectorGroupFunctionImpl( + runtime, std::move(bridge), lookupClass, receiverIsClass, + std::move(selectors), std::move(preparedInvocations), {}, nullptr); +} + +Function CreateNativeApiBoundSelectorGroupFunction( + Runtime& runtime, std::shared_ptr bridge, Class lookupClass, + std::shared_ptr receiverHostObject, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations) { + return CreateNativeApiSelectorGroupFunctionImpl( + runtime, std::move(bridge), lookupClass, false, std::move(selectors), + std::move(preparedInvocations), receiverHostObject, + receiverHostObject != nullptr ? receiverHostObject->lifetimeState() + : nullptr); +} + +} // namespace + +#include "../shared/bridge/Install.mm" + +Object CreateNativeApiJSI(Runtime& runtime, const NativeApiJsiConfig& config) { + return CreateNativeApi(runtime, config); +} + +void InstallNativeApiJSI(Runtime& runtime, const NativeApiJsiConfig& config) { + InstallNativeApi(runtime, config); +} + +} // namespace nativescript + +extern "C" void NativeScriptInstallNativeApiJSI(facebook::jsi::Runtime* runtime, + const char* metadataPath) { + if (runtime == nullptr) { + return; + } + nativescript::NativeApiJsiConfig config; + config.metadataPath = metadataPath; + nativescript::InstallNativeApiJSI(*runtime, config); +} + +#endif // TARGET_ENGINE_HERMES diff --git a/NativeScript/ffi/objc/hermes/NativeApiJsiGsd.mm b/NativeScript/ffi/objc/hermes/NativeApiJsiGsd.mm new file mode 100644 index 000000000..9bcf76829 --- /dev/null +++ b/NativeScript/ffi/objc/hermes/NativeApiJsiGsd.mm @@ -0,0 +1,169 @@ +// --- GSD (Generated Signature Dispatch) for Hermes/JSI --- +// GsdObjCContext is the engine-neutral interface the generated invokers use: +// it reads jsi::Value arguments and writes the jsi::Value return value using +// the shared engine-neutral conversion helpers (which already operate on the +// jsi value type). Readers require the fast representation; anything else +// makes a reader return false so the invoker falls back to the generic path. +struct GsdObjCContext; +using ObjCGsdInvoker = bool (*)(GsdObjCContext&); +struct ObjCGsdDispatchEntry { + uint64_t dispatchId; + ObjCGsdInvoker invoker; +}; + +struct GsdObjCContext { + Runtime& runtime; + const std::shared_ptr& bridge; + id self; + SEL selector; + const Value* arguments; + const NativeApiType& returnType; + Value result = Value::undefined(); + + template + void invokeNative(Invocation&& invocation) { + performGeneratedObjCInvocation(runtime, bridge, [&]() { invocation(); }); + } + + bool readNumber(size_t i, double* out) { + const Value& v = arguments[i]; + if (!v.isNumber()) return false; + *out = v.asNumber(); + return true; + } + bool readBool(size_t i, uint8_t* out) { + const Value& v = arguments[i]; + if (!v.isBool()) return false; + *out = v.getBool() ? 1 : 0; + return true; + } + template + bool readSigned(size_t i, T* out) { + double tmp = 0; + if (!readNumber(i, &tmp)) return false; + *out = static_cast(tmp); + return true; + } + template + bool readUnsigned(size_t i, T* out) { + double tmp = 0; + if (!readNumber(i, &tmp)) return false; + *out = static_cast(tmp); + return true; + } + bool readFloat(size_t i, float* out) { + double tmp = 0; + if (!readNumber(i, &tmp)) return false; + *out = static_cast(tmp); + return true; + } + bool readDouble(size_t i, double* out) { return readNumber(i, out); } + bool readSelector(size_t i, SEL* out) { + return readFastEngineSelectorArgument(runtime, arguments[i], out); + } + bool readClass(size_t i, Class* out) { + Class cls = classFromEngineValue(runtime, arguments[i]); + if (cls == Nil) return false; + *out = cls; + return true; + } + bool readObject(size_t i, id* out) { + const Value& v = arguments[i]; + if (v.isNull() || v.isUndefined()) { + *out = nil; + return true; + } + if (!v.isObject()) return false; + Object o = v.getObject(runtime); + if (o.isHostObject(runtime)) { + *out = o.getHostObject(runtime)->object(); + return true; + } + if (o.isHostObject(runtime)) { + *out = static_cast( + o.getHostObject(runtime)->nativeClass()); + return true; + } + Class cls = classFromEngineValue(runtime, v); + if (cls != Nil) { + *out = static_cast(cls); + return true; + } + if (o.isHostObject(runtime)) { + *out = static_cast( + o.getHostObject(runtime) + ->nativeProtocol()); + return true; + } + return false; + } + + void setVoid() { result = Value::undefined(); } + void setBool(bool v) { result = Value(v); } + void setInt32(int32_t v) { result = Value(static_cast(v)); } + void setUInt32(uint32_t v) { result = Value(static_cast(v)); } + void setUInt16(uint16_t v) { + if (v >= 32 && v <= 126) { + result = makeString(runtime, std::string(1, static_cast(v))); + } else { + result = Value(static_cast(v)); + } + } + void setInt64(int64_t v) { result = signedInteger64ToEngineValue(runtime, v); } + void setUInt64(uint64_t v) { + result = unsignedInteger64ToEngineValue(runtime, v); + } + void setDouble(double v) { result = Value(v); } + void setSelector(SEL v) { + const char* name = v != nullptr ? sel_getName(v) : nullptr; + result = name != nullptr ? makeString(runtime, name) : Value::null(); + } + void setClass(Class v) { + if (v == nil) { + result = Value::null(); + return; + } + const char* name = class_getName(v); + NativeApiSymbol symbol{ + .kind = NativeApiSymbolKind::Class, + .offset = MD_SECTION_OFFSET_NULL, + .name = name != nullptr ? name : "", + .runtimeName = name != nullptr ? name : "", + }; + if (const NativeApiSymbol* found = bridge->findClass(symbol.name)) { + symbol = *found; + } + result = makeNativeClassValue(runtime, bridge, std::move(symbol)); + } + void setObject(id obj) { + result = convertNativeReturnValue(runtime, bridge, returnType, &obj); + } +}; + +// Close the anonymous namespace so the generated dispatch table lives in +// namespace nativescript; GsdObjCContext/ObjCGsdDispatchEntry stay reachable +// via the unnamed namespace's implicit using-directive. +} // namespace (temporary close for GSD .inc) + +#if defined(__has_include) +#if __has_include("GeneratedGsdSignatureDispatch.inc") +#include "GeneratedGsdSignatureDispatch.inc" +#endif +#endif + +#ifndef NS_HAS_GENERATED_SIGNATURE_GSD_DISPATCH +inline constexpr ObjCGsdDispatchEntry kGeneratedObjCGsdDispatchEntries[] = { + {0, nullptr}}; +#endif + +ObjCGsdInvoker lookupObjCGsdInvoker(uint64_t dispatchId) { + if (!isGeneratedDispatchEnabled()) { + return nullptr; + } + return lookupDispatchInvoker( + kGeneratedObjCGsdDispatchEntries, dispatchId); +} + +namespace { // reopen anonymous namespace + +// --- End GSD --- diff --git a/NativeScript/ffi/hermes/jsi/NativeApiJsiReactNative.h b/NativeScript/ffi/objc/hermes/NativeApiJsiReactNative.h similarity index 100% rename from NativeScript/ffi/hermes/jsi/NativeApiJsiReactNative.h rename to NativeScript/ffi/objc/hermes/NativeApiJsiReactNative.h diff --git a/NativeScript/ffi/objc/hermes/NativeApiJsiSignatureDispatch.h b/NativeScript/ffi/objc/hermes/NativeApiJsiSignatureDispatch.h new file mode 100644 index 000000000..997e99ece --- /dev/null +++ b/NativeScript/ffi/objc/hermes/NativeApiJsiSignatureDispatch.h @@ -0,0 +1,14 @@ +#ifndef NS_FFI_HERMES_NATIVE_API_JSI_SIGNATURE_DISPATCH_H +#define NS_FFI_HERMES_NATIVE_API_JSI_SIGNATURE_DISPATCH_H + +#include "ffi/objc/shared/SignatureDispatchCore.h" + +#if defined(__has_include) +#if __has_include("GeneratedSignatureDispatch.inc") +#include "GeneratedSignatureDispatch.inc" +#endif +#endif + +#include "ffi/objc/shared/PreparedSignatureDispatch.h" + +#endif // NS_FFI_HERMES_NATIVE_API_JSI_SIGNATURE_DISPATCH_H diff --git a/NativeScript/ffi/objc/hermes/README.md b/NativeScript/ffi/objc/hermes/README.md new file mode 100644 index 000000000..35c04d97a --- /dev/null +++ b/NativeScript/ffi/objc/hermes/README.md @@ -0,0 +1,23 @@ +# Native API Hermes JSI backend + +This directory owns the Hermes-facing Native API entrypoint: + +- `NativeApiJsi.h` exposes the public JSI install/create API. +- `NativeApiJsi.mm` binds Hermes JSI types to the Hermes-owned bridge + implementation files in this directory. +- `NativeApiJsiReactNative.h` adapts React Native `CallInvoker`s to the JSI + scheduler config used by the TurboModule. +- `NativeApiJsiSignatureDispatch.h` wires Hermes generated signature dispatch + tables into native invocation. + +Hermes is the only backend that exposes the real `facebook::jsi` API. V8, JSC, +and QuickJS own their bridge implementations in their respective engine +directories. + +React Native integrations should include `NativeApiJsiReactNative.h` from a +TurboModule implementation and pass the module's JS/UI `CallInvoker`s: + +```cpp +nativescript::InstallReactNativeNativeApiJSI( + runtime, jsInvoker, uiInvoker, metadataPath, metadataPtr); +``` diff --git a/NativeScript/ffi/objc/jsc/NativeApiJSC.h b/NativeScript/ffi/objc/jsc/NativeApiJSC.h new file mode 100644 index 000000000..cd03fd630 --- /dev/null +++ b/NativeScript/ffi/objc/jsc/NativeApiJSC.h @@ -0,0 +1,20 @@ +#ifndef NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_H +#define NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_H + +#include "ffi/objc/shared/NativeApiBackendConfig.h" +#include + +namespace nativescript { + +using NativeApiScheduler = NativeApiBackendScheduler; +using NativeApiConfig = NativeApiBackendConfig; + +void InstallNativeApi(JSGlobalContextRef context, + const NativeApiConfig& config = NativeApiConfig{}); + +} // namespace nativescript + +extern "C" void NativeScriptInstallNativeApi(JSGlobalContextRef context, + const char* metadataPath); + +#endif // NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_H diff --git a/NativeScript/ffi/objc/jsc/NativeApiJSC.mm b/NativeScript/ffi/objc/jsc/NativeApiJSC.mm new file mode 100644 index 000000000..227af4c60 --- /dev/null +++ b/NativeScript/ffi/objc/jsc/NativeApiJSC.mm @@ -0,0 +1,71 @@ +#include "NativeApiJSC.h" + +#ifdef TARGET_ENGINE_JSC + +#include "NativeApiJSCRuntime.h" +#include "SignatureDispatch.h" + +namespace nativescript { + +namespace { + +using nativescript::engine::Array; +using nativescript::engine::ArrayBuffer; +using nativescript::engine::BigInt; +using nativescript::engine::Function; +using nativescript::engine::HostObject; +using nativescript::engine::MutableBuffer; +using nativescript::engine::Object; +using nativescript::engine::PropNameID; +using nativescript::engine::Runtime; +using nativescript::engine::String; +using nativescript::engine::StringBuffer; +using nativescript::engine::Value; +using nativescript::engine::JSError; +using metagen::MDMemberFlag; +using metagen::MDMetadataReader; +using metagen::MDSectionOffset; +using metagen::MDTypeKind; + +// clang-format off +#define NATIVESCRIPT_NATIVE_API_BACKEND_NAME "jsc" +#include "../shared/bridge/ObjCBridge.mm" +// clang-format on +#define NATIVESCRIPT_NATIVE_API_RETAIN_RUNTIME 1 +#define NATIVESCRIPT_NATIVE_API_HAS_ENGINE_SELECTOR_GROUP_FUNCTION 1 + +#include "NativeApiJSCRuntimeSupport.mm" + +// clang-format off +#include "../shared/bridge/HostObjects.mm" +#include "../shared/bridge/Callbacks.mm" +#include "../shared/bridge/TypeConv.mm" +#include "../shared/bridge/Invocation.mm" +#include "../shared/bridge/ClassBuilder.mm" +#include "../shared/bridge/HostObject.mm" +// clang-format on + +#include "NativeApiJSCSelectorGroups.mm" + +} // namespace + +#include "../shared/bridge/Install.mm" + +void InstallNativeApi(JSGlobalContextRef context, const NativeApiConfig& config) { + if (context == nullptr) { + return; + } + Runtime runtime(context); + InstallNativeApi(runtime, config); +} + +} // namespace nativescript + +extern "C" void NativeScriptInstallNativeApi(JSGlobalContextRef context, + const char* metadataPath) { + nativescript::NativeApiConfig config; + config.metadataPath = metadataPath; + nativescript::InstallNativeApi(context, config); +} + +#endif // TARGET_ENGINE_JSC diff --git a/NativeScript/ffi/objc/jsc/NativeApiJSCGsd.mm b/NativeScript/ffi/objc/jsc/NativeApiJSCGsd.mm new file mode 100644 index 000000000..a0e89498b --- /dev/null +++ b/NativeScript/ffi/objc/jsc/NativeApiJSCGsd.mm @@ -0,0 +1,316 @@ +// --- GSD (Generated Signature Dispatch) for JSC --- +// GsdObjCContext is the engine-neutral interface the generated invokers use: +// it reads JS arguments and writes the JS return value via the JSC API. The +// readers/setters mirror JSC's generic conversions exactly; any value not in +// the fast representation makes a reader return false so the invoker falls +// back to the fully correct generic path. Number readers require an actual +// JS number so coercion edge cases (numeric strings, single-char unichar +// arguments) defer to the generic path. +struct GsdObjCContext; +using ObjCGsdInvoker = bool (*)(GsdObjCContext&); +struct ObjCGsdDispatchEntry { + uint64_t dispatchId; + ObjCGsdInvoker invoker; +}; + +struct GsdObjCContext { + Runtime& runtime; + const std::shared_ptr& bridge; + id self; + SEL selector; + JSContextRef context; + const JSValueRef* arguments; + const NativeApiType& returnType; + JSValueRef result = nullptr; + const Value* valueArguments = nullptr; + bool materializeValueResult = false; + Value valueResult = Value::undefined(); + + template + void invokeNative(Invocation&& invocation) { + performGeneratedObjCInvocation(runtime, bridge, [&]() { invocation(); }); + } + + bool readNumber(size_t i, double* out) { + if (valueArguments != nullptr) { + const Value& v = valueArguments[i]; + if (!v.isNumber()) return false; + *out = v.getNumber(); + return true; + } + JSValueRef v = arguments[i]; + if (!JSValueIsNumber(context, v)) return false; + JSValueRef exception = nullptr; + double converted = JSValueToNumber(context, v, &exception); + if (exception != nullptr) return false; + *out = converted; + return true; + } + bool readBool(size_t i, uint8_t* out) { + if (valueArguments != nullptr) { + const Value& v = valueArguments[i]; + if (!v.isBool()) return false; + *out = v.getBool() ? 1 : 0; + return true; + } + JSValueRef v = arguments[i]; + if (!JSValueIsBoolean(context, v)) return false; + *out = JSValueToBoolean(context, v) ? 1 : 0; + return true; + } + template + bool readSigned(size_t i, T* out) { + double tmp = 0; + if (!readNumber(i, &tmp)) return false; + *out = static_cast(tmp); + return true; + } + template + bool readUnsigned(size_t i, T* out) { + double tmp = 0; + if (!readNumber(i, &tmp)) return false; + *out = static_cast(tmp); + return true; + } + bool readFloat(size_t i, float* out) { + double tmp = 0; + if (!readNumber(i, &tmp)) return false; + *out = static_cast(tmp); + return true; + } + bool readDouble(size_t i, double* out) { return readNumber(i, out); } + bool readSelector(size_t i, SEL* out) { + if (valueArguments != nullptr) { + return readFastEngineSelectorArgument(runtime, valueArguments[i], out); + } + return readJSCEngineSelectorArgument(runtime, arguments[i], out); + } + bool readClass(size_t i, Class* out) { + if (valueArguments != nullptr) { + Class cls = classFromEngineValue(runtime, valueArguments[i]); + if (cls == Nil) return false; + *out = cls; + return true; + } + if (auto* c = jscHostObjectRaw( + runtime, arguments[i])) { + *out = c->nativeClass(); + return true; + } + Class cls = jscNativeClassArgument(runtime, arguments[i]); + if (cls == Nil) return false; + *out = cls; + return true; + } + bool readObject(size_t i, id* out) { + if (valueArguments != nullptr) { + const Value& v = valueArguments[i]; + if (v.isNull() || v.isUndefined()) { + *out = nil; + return true; + } + if (!v.isObject()) return false; + Object object = v.asObject(runtime); + if (object.isHostObject(runtime)) { + *out = object.getHostObject(runtime)->object(); + return true; + } + if (object.isHostObject(runtime)) { + *out = static_cast( + object.getHostObject(runtime)->nativeClass()); + return true; + } + Class cls = classFromEngineValue(runtime, v); + if (cls != Nil) { + *out = static_cast(cls); + return true; + } + if (object.isHostObject(runtime)) { + *out = static_cast( + object.getHostObject(runtime) + ->nativeProtocol()); + return true; + } + return false; + } + JSValueRef v = arguments[i]; + if (v == nullptr || JSValueIsNull(context, v) || + JSValueIsUndefined(context, v)) { + *out = nil; + return true; + } + if (auto* h = jscHostObjectRaw(runtime, v)) { + *out = h->object(); + return true; + } + if (auto* c = jscHostObjectRaw(runtime, v)) { + *out = static_cast(c->nativeClass()); + return true; + } + if (JSValueIsObject(context, v)) { + Class cls = jscNativeClassArgument(runtime, v); + if (cls != Nil) { + *out = static_cast(cls); + return true; + } + } + if (auto* p = jscHostObjectRaw(runtime, v)) { + *out = static_cast(p->nativeProtocol()); + return true; + } + return false; + } + + void setVoid() { + if (materializeValueResult) { + valueResult = Value::undefined(); + return; + } + result = JSValueMakeUndefined(context); + } + void setBool(bool v) { + if (materializeValueResult) { + valueResult = Value(v); + return; + } + result = JSValueMakeBoolean(context, v); + } + void setInt32(int32_t v) { + if (materializeValueResult) { + valueResult = Value(static_cast(v)); + return; + } + result = JSValueMakeNumber(context, v); + } + void setUInt32(uint32_t v) { + if (materializeValueResult) { + valueResult = Value(static_cast(v)); + return; + } + result = JSValueMakeNumber(context, v); + } + void setUInt16(uint16_t v) { + if (materializeValueResult) { + if (v >= 32 && v <= 126) { + valueResult = makeString(runtime, std::string(1, static_cast(v))); + } else { + valueResult = Value(static_cast(v)); + } + return; + } + if (v >= 32 && v <= 126) { + char buffer[2] = {static_cast(v), '\0'}; + JSStringRef string = engine::jscengine::makeJSString(buffer); + result = JSValueMakeString(context, string); + JSStringRelease(string); + } else { + result = JSValueMakeNumber(context, v); + } + } + void setInt64(int64_t v) { + if (materializeValueResult) { + valueResult = signedInteger64ToEngineValue(runtime, v); + return; + } + result = jscInteger64Value(runtime, v); + } + void setUInt64(uint64_t v) { + if (materializeValueResult) { + valueResult = unsignedInteger64ToEngineValue(runtime, v); + return; + } + result = jscUnsignedInteger64Value(runtime, v); + } + void setDouble(double v) { + if (materializeValueResult) { + valueResult = Value(v); + return; + } + result = JSValueMakeNumber(context, v); + } + void setSelector(SEL v) { + const char* name = v != nullptr ? sel_getName(v) : nullptr; + if (materializeValueResult) { + valueResult = name != nullptr ? makeString(runtime, name) : Value::null(); + return; + } + if (name == nullptr) { + result = JSValueMakeNull(context); + return; + } + JSStringRef string = engine::jscengine::makeJSString(name); + result = JSValueMakeString(context, string); + JSStringRelease(string); + } + void setClass(Class v) { + if (materializeValueResult) { + if (v == nil) { + valueResult = Value::null(); + return; + } + const char* name = class_getName(v); + NativeApiSymbol symbol{ + .kind = NativeApiSymbolKind::Class, + .offset = MD_SECTION_OFFSET_NULL, + .name = name != nullptr ? name : "", + .runtimeName = name != nullptr ? name : "", + }; + if (const NativeApiSymbol* found = bridge->findClass(symbol.name)) { + symbol = *found; + } + valueResult = makeNativeClassValue(runtime, bridge, std::move(symbol)); + return; + } + if (v == nil) { + result = JSValueMakeNull(context); + return; + } + const char* name = class_getName(v); + NativeApiSymbol symbol{ + .kind = NativeApiSymbolKind::Class, + .offset = MD_SECTION_OFFSET_NULL, + .name = name != nullptr ? name : "", + .runtimeName = name != nullptr ? name : "", + }; + if (const NativeApiSymbol* found = bridge->findClass(symbol.name)) { + symbol = *found; + } + Value classValue = makeNativeClassValue(runtime, bridge, std::move(symbol)); + result = classValue.local(runtime); + } + void setObject(id obj) { + if (materializeValueResult) { + valueResult = convertNativeReturnValue(runtime, bridge, returnType, &obj); + return; + } + result = setJSCEngineObjectReturn(runtime, bridge, returnType, obj); + } +}; + +// Close the anonymous namespace so the generated dispatch table lives in +// namespace nativescript. GsdObjCContext/ObjCGsdDispatchEntry remain reachable +// via the unnamed namespace's implicit using-directive. +} // namespace (temporary close for GSD .inc) + +#if defined(__has_include) +#if __has_include("GeneratedGsdSignatureDispatch.inc") +#include "GeneratedGsdSignatureDispatch.inc" +#endif +#endif + +#ifndef NS_HAS_GENERATED_SIGNATURE_GSD_DISPATCH +inline constexpr ObjCGsdDispatchEntry kGeneratedObjCGsdDispatchEntries[] = { + {0, nullptr}}; +#endif + +ObjCGsdInvoker lookupObjCGsdInvoker(uint64_t dispatchId) { + if (!isGeneratedDispatchEnabled()) { + return nullptr; + } + return lookupDispatchInvoker( + kGeneratedObjCGsdDispatchEntries, dispatchId); +} + +namespace { // reopen anonymous namespace + +// --- End GSD --- diff --git a/NativeScript/ffi/objc/jsc/NativeApiJSCHostObjects.mm b/NativeScript/ffi/objc/jsc/NativeApiJSCHostObjects.mm new file mode 100644 index 000000000..7af17b3e4 --- /dev/null +++ b/NativeScript/ffi/objc/jsc/NativeApiJSCHostObjects.mm @@ -0,0 +1,271 @@ +#include "NativeApiJSCRuntime.h" + +#ifdef TARGET_ENGINE_JSC + +namespace nativescript { +class NativeApiObjectHostObject; +} + +namespace nativescript { +namespace engine { + +namespace jscengine { + +JSClassRef hostClass(Runtime& runtime); +JSClassRef functionClass(Runtime& runtime); +void setFunctionPrototype(JSGlobalContextRef context, JSObjectRef function); + +template +class StackValueArray { + public: + explicit StackValueArray(size_t count) : count_(count) { + if (count_ > InlineCount) { + values_ = static_cast(::operator new(sizeof(Value) * count_)); + } else { + values_ = reinterpret_cast(inlineStorage_); + } + } + + ~StackValueArray() { + for (size_t i = 0; i < constructed_; i++) { + values_[i].~Value(); + } + if (count_ > InlineCount) { + ::operator delete(values_); + } + } + + StackValueArray(const StackValueArray&) = delete; + StackValueArray& operator=(const StackValueArray&) = delete; + + void emplace(size_t index, Value&& value) { + new (&values_[index]) Value(std::move(value)); + constructed_++; + } + + Value* data() { return count_ == 0 ? nullptr : values_; } + size_t size() const { return count_; } + + private: + size_t count_ = 0; + size_t constructed_ = 0; + Value* values_ = nullptr; + alignas(Value) unsigned char inlineStorage_[sizeof(Value) * InlineCount]; +}; + +bool isNativeInstancePrototypeBypassExcluded(JSStringRef propertyName) { + return JSStringIsEqualToUTF8CString(propertyName, "kind") || + JSStringIsEqualToUTF8CString(propertyName, "className") || + JSStringIsEqualToUTF8CString(propertyName, "nativeAddress") || + JSStringIsEqualToUTF8CString(propertyName, "class") || + JSStringIsEqualToUTF8CString(propertyName, "constructor") || + JSStringIsEqualToUTF8CString(propertyName, "super") || + JSStringIsEqualToUTF8CString(propertyName, "invoke") || + JSStringIsEqualToUTF8CString(propertyName, "send") || + JSStringIsEqualToUTF8CString(propertyName, "takeRetainedValue") || + JSStringIsEqualToUTF8CString(propertyName, "takeUnretainedValue") || + JSStringIsEqualToUTF8CString(propertyName, "toString"); +} + +bool shouldDeferToNativeInstancePrototype(JSContextRef context, + JSObjectRef object, + JSStringRef propertyName, + HostObjectHolder* holder) { + if (context == nullptr || object == nullptr || propertyName == nullptr || + holder == nullptr || + holder->typeToken != hostObjectTypeToken() || + isNativeInstancePrototypeBypassExcluded(propertyName)) { + return false; + } + + JSValueRef prototypeValue = JSObjectGetPrototype(context, object); + if (prototypeValue == nullptr || !JSValueIsObject(context, prototypeValue)) { + return false; + } + + JSValueRef exception = nullptr; + JSObjectRef prototypeObject = + JSValueToObject(context, prototypeValue, &exception); + if (exception != nullptr || prototypeObject == nullptr) { + return false; + } + + exception = nullptr; + bool found = JSObjectHasProperty(context, prototypeObject, propertyName); + return exception == nullptr && found; +} + +JSValueRef hostGetProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, + JSValueRef* exception) { + auto* holder = static_cast(JSObjectGetPrivate(object)); + if (holder == nullptr || holder->hostObject == nullptr) { + return nullptr; + } + if (shouldDeferToNativeInstancePrototype(context, object, propertyName, + holder)) { + return nullptr; + } + Runtime runtime(holder->state); + try { + Value result = holder->hostObject->get(runtime, PropNameID(stringToUtf8(propertyName))); + return result.isUndefined() ? nullptr : result.local(runtime); + } catch (const std::exception& error) { + setException(context, exception, error); + return JSValueMakeUndefined(context); + } +} + +bool hostSetProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, + JSValueRef value, JSValueRef* exception) { + auto* holder = static_cast(JSObjectGetPrivate(object)); + if (holder == nullptr || holder->hostObject == nullptr) { + return false; + } + Runtime runtime(holder->state); + try { + return holder->hostObject->set(runtime, PropNameID(stringToUtf8(propertyName)), + Value::borrowed(runtime, value)); + } catch (const std::exception& error) { + setException(context, exception, error); + return true; + } +} + +void hostGetPropertyNames(JSContextRef, JSObjectRef object, + JSPropertyNameAccumulatorRef propertyNames) { + auto* holder = static_cast(JSObjectGetPrivate(object)); + if (holder == nullptr || holder->hostObject == nullptr) { + return; + } + Runtime runtime(holder->state); + try { + for (const auto& property : holder->hostObject->getPropertyNames(runtime)) { + JSStringRef name = makeJSString(property.utf8(runtime)); + JSPropertyNameAccumulatorAddName(propertyNames, name); + JSStringRelease(name); + } + } catch (const std::exception&) { + } +} + +void hostFinalize(JSObjectRef object) { + delete static_cast(JSObjectGetPrivate(object)); +} + +JSValueRef functionCall(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, + size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { + auto* holder = static_cast(JSObjectGetPrivate(function)); + if (holder == nullptr || !holder->callback) { + return JSValueMakeUndefined(context); + } + Runtime runtime(holder->state); + StackValueArray<8> args(argumentCount); + for (size_t i = 0; i < argumentCount; i++) { + args.emplace(i, Value::borrowed(runtime, arguments[i])); + } + try { + Value thisValue = Value::borrowed(runtime, thisObject); + Value result = + holder->callback(runtime, thisValue, args.size() == 0 ? nullptr : args.data(), + args.size()); + return result.local(runtime); + } catch (const std::exception& error) { + setException(context, exception, error); + return JSValueMakeUndefined(context); + } +} + +void functionFinalize(JSObjectRef object) { + delete static_cast(JSObjectGetPrivate(object)); +} + +JSClassRef hostClass(Runtime& runtime) { + auto state = runtime.state(); + if (state->hostClass == nullptr) { + JSClassDefinition definition = kJSClassDefinitionEmpty; + definition.className = "NativeScriptEngineHostObject"; + definition.getProperty = hostGetProperty; + definition.setProperty = hostSetProperty; + definition.getPropertyNames = hostGetPropertyNames; + definition.finalize = hostFinalize; + state->hostClass = JSClassCreate(&definition); + } + return state->hostClass; +} + +JSClassRef functionClass(Runtime& runtime) { + auto state = runtime.state(); + if (state->functionClass == nullptr) { + JSClassDefinition definition = kJSClassDefinitionEmpty; + definition.className = "NativeScriptEngineFunction"; + definition.callAsFunction = functionCall; + definition.finalize = functionFinalize; + state->functionClass = JSClassCreate(&definition); + } + return state->functionClass; +} + +void setFunctionPrototype(JSGlobalContextRef context, JSObjectRef function) { + if (context == nullptr || function == nullptr) { + return; + } + + JSValueRef exception = nullptr; + JSStringRef functionName = makeJSString("Function"); + JSValueRef functionValue = + JSObjectGetProperty(context, JSContextGetGlobalObject(context), functionName, &exception); + JSStringRelease(functionName); + if (exception != nullptr || functionValue == nullptr || + !JSValueIsObject(context, functionValue)) { + return; + } + + exception = nullptr; + JSObjectRef functionConstructor = JSValueToObject(context, functionValue, &exception); + if (exception != nullptr || functionConstructor == nullptr) { + return; + } + + JSStringRef prototypeName = makeJSString("prototype"); + JSValueRef prototypeValue = + JSObjectGetProperty(context, functionConstructor, prototypeName, &exception); + JSStringRelease(prototypeName); + if (exception != nullptr || prototypeValue == nullptr || + !JSValueIsObject(context, prototypeValue)) { + return; + } + + JSObjectSetPrototype(context, function, prototypeValue); +} + +} // namespace jscengine + +Object Object::createFromHostObjectWithToken(Runtime& runtime, std::shared_ptr host, + const void* typeToken) { + auto* holder = new jscengine::HostObjectHolder(runtime.state(), std::move(host), typeToken); + JSObjectRef object = JSObjectMake(runtime.context(), jscengine::hostClass(runtime), holder); + return Object::fromValueStorage(Value(runtime, object).storage_); +} + +Function Function::createFromHostFunction(Runtime& runtime, const PropNameID& name, unsigned int, + HostFunctionType callback) { + auto* holder = new jscengine::FunctionHolder(runtime.state(), std::move(callback)); + JSObjectRef function = JSObjectMake(runtime.context(), jscengine::functionClass(runtime), holder); + jscengine::setFunctionPrototype(runtime.context(), function); + std::string functionName = name.utf8(runtime); + if (!functionName.empty()) { + JSStringRef property = jscengine::makeJSString("name"); + JSStringRef valueString = jscengine::makeJSString(functionName); + JSValueRef value = JSValueMakeString(runtime.context(), valueString); + JSObjectSetProperty(runtime.context(), function, property, value, kJSPropertyAttributeReadOnly, + nullptr); + JSStringRelease(valueString); + JSStringRelease(property); + } + return Function(Object::fromValueStorage(Value(runtime, function).storage_)); +} + +} // namespace engine +} // namespace nativescript + +#endif // TARGET_ENGINE_JSC diff --git a/NativeScript/ffi/objc/jsc/NativeApiJSCMarshalling.mm b/NativeScript/ffi/objc/jsc/NativeApiJSCMarshalling.mm new file mode 100644 index 000000000..c5b5290e5 --- /dev/null +++ b/NativeScript/ffi/objc/jsc/NativeApiJSCMarshalling.mm @@ -0,0 +1,461 @@ +// Included by NativeApiJSCSelectorGroups.mm inside the NativeScript anonymous namespace. + +std::string jscValueToUtf8(Runtime& runtime, JSValueRef value) { + JSValueRef exception = nullptr; + JSStringRef string = JSValueToStringCopy(runtime.context(), value, &exception); + if (string == nullptr || exception != nullptr) { + if (string != nullptr) { + JSStringRelease(string); + } + return {}; + } + std::string result = engine::jscengine::stringToUtf8(string); + JSStringRelease(string); + return result; +} + +bool jscNumberValue(Runtime& runtime, JSValueRef value, double* result) { + if (result == nullptr) { + return false; + } + JSValueRef exception = nullptr; + double converted = JSValueToNumber(runtime.context(), value, &exception); + if (exception != nullptr) { + return false; + } + *result = converted; + return true; +} + +template +std::shared_ptr jscHostObject(Runtime& runtime, JSValueRef value) { + if (value == nullptr || !JSValueIsObject(runtime.context(), value)) { + return nullptr; + } + JSValueRef exception = nullptr; + JSObjectRef object = JSValueToObject(runtime.context(), value, &exception); + if (exception != nullptr || object == nullptr) { + return nullptr; + } + auto* holder = static_cast( + JSObjectGetPrivate(object)); + if (holder == nullptr || + holder->typeToken != engine::jscengine::hostObjectTypeToken()) { + return nullptr; + } + return std::static_pointer_cast(holder->hostObject); +} + +template +T* jscHostObjectRaw(Runtime& runtime, JSValueRef value) { + if (value == nullptr || !JSValueIsObject(runtime.context(), value)) { + return nullptr; + } + JSValueRef exception = nullptr; + JSObjectRef object = JSValueToObject(runtime.context(), value, &exception); + if (exception != nullptr || object == nullptr) { + return nullptr; + } + auto* holder = static_cast( + JSObjectGetPrivate(object)); + if (holder == nullptr || + holder->typeToken != engine::jscengine::hostObjectTypeToken()) { + return nullptr; + } + return static_cast(holder->hostObject.get()); +} + +id jscNativeObjectArgument(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiType& type, JSValueRef value, + NativeApiArgumentFrame& frame) { + if (value == nullptr || JSValueIsNull(runtime.context(), value) || + JSValueIsUndefined(runtime.context(), value)) { + return nil; + } + if (JSValueIsString(runtime.context(), value)) { + std::string utf8 = jscValueToUtf8(runtime, value); + id string = type.kind == metagen::mdTypeNSMutableStringObject + ? [[NSMutableString alloc] initWithBytes:utf8.data() + length:utf8.size() + encoding:NSUTF8StringEncoding] + : [[NSString alloc] initWithBytes:utf8.data() + length:utf8.size() + encoding:NSUTF8StringEncoding]; + if (string != nil) { + frame.addObject(string); + } + return string; + } + if (JSValueIsBoolean(runtime.context(), value)) { + return [NSNumber numberWithBool:JSValueToBoolean(runtime.context(), value)]; + } + if (JSValueIsNumber(runtime.context(), value)) { + double converted = 0; + if (jscNumberValue(runtime, value, &converted)) { + return [NSNumber numberWithDouble:converted]; + } + } + if (!JSValueIsObject(runtime.context(), value)) { + return nil; + } + if (auto objectHost = jscHostObject(runtime, value)) { + return objectHost->object(); + } + if (auto classHost = jscHostObject(runtime, value)) { + return static_cast(classHost->nativeClass()); + } + if (auto protocolHost = + jscHostObject(runtime, value)) { + return static_cast(protocolHost->nativeProtocol()); + } + if (auto pointerHost = + jscHostObject(runtime, value)) { + return static_cast(pointerHost->pointer()); + } + if (auto referenceHost = + jscHostObject(runtime, value)) { + return static_cast(referenceHost->data()); + } + if (auto structHost = + jscHostObject(runtime, value)) { + return static_cast(structHost->data()); + } + + JSValueRef exception = nullptr; + JSObjectRef object = JSValueToObject(runtime.context(), value, &exception); + if (exception == nullptr && object != nullptr) { + JSStringRef property = engine::jscengine::makeJSString("__nativeApiClass"); + JSValueRef wrappedClassValue = + JSObjectGetProperty(runtime.context(), object, property, nullptr); + JSStringRelease(property); + if (auto classHost = + jscHostObject(runtime, + wrappedClassValue)) { + return static_cast(classHost->nativeClass()); + } + } + + Value wrapped = Value::borrowed(runtime, value); + return objectFromEngineValue(runtime, bridge, wrapped, frame, + type.kind == + metagen::mdTypeNSMutableStringObject); +} + +Class jscNativeClassArgument(Runtime& runtime, JSValueRef value) { + if (value == nullptr || JSValueIsNull(runtime.context(), value) || + JSValueIsUndefined(runtime.context(), value)) { + return Nil; + } + if (auto classHost = jscHostObject(runtime, value)) { + return classHost->nativeClass(); + } + if (JSValueIsObject(runtime.context(), value)) { + JSValueRef exception = nullptr; + JSObjectRef object = JSValueToObject(runtime.context(), value, &exception); + if (exception == nullptr && object != nullptr) { + JSStringRef property = engine::jscengine::makeJSString("__nativeApiClass"); + JSValueRef wrappedClassValue = + JSObjectGetProperty(runtime.context(), object, property, nullptr); + JSStringRelease(property); + if (auto classHost = + jscHostObject(runtime, + wrappedClassValue)) { + return classHost->nativeClass(); + } + } + } + Value wrapped = Value::borrowed(runtime, value); + return classFromEngineValue(runtime, wrapped); +} + +bool readJSCEngineSelectorArgument(Runtime& runtime, JSValueRef value, + SEL* result) { + if (result == nullptr) { + return false; + } + if (value == nullptr || JSValueIsNull(runtime.context(), value) || + JSValueIsUndefined(runtime.context(), value)) { + *result = nullptr; + return true; + } + if (!JSValueIsString(runtime.context(), value)) { + return false; + } + std::string selectorName = jscValueToUtf8(runtime, value); + *result = sel_registerName(selectorName.c_str()); + return true; +} + +template +bool writeJSCNumber(Runtime& runtime, JSValueRef value, void* target) { + double converted = 0; + if (!jscNumberValue(runtime, value, &converted)) { + return false; + } + *static_cast(target) = static_cast(converted); + return true; +} + +bool prepareJSCEngineArgument( + Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, JSValueRef value, + NativeApiArgumentFrame& frame, size_t index) { + ffi_type* ffiType = ffiTypeForEngineArgument(type); + size_t size = + ffiType != nullptr && ffiType->size > 0 ? ffiType->size : nativeSizeForType(type); + void* target = frame.storageAt(index, size); + + switch (type.kind) { + case metagen::mdTypeBool: + if (!JSValueIsBoolean(runtime.context(), value)) { + return false; + } + *static_cast(target) = + JSValueToBoolean(runtime.context(), value) ? 1 : 0; + return true; + case metagen::mdTypeChar: + return writeJSCNumber(runtime, value, target); + case metagen::mdTypeUChar: + case metagen::mdTypeUInt8: + return writeJSCNumber(runtime, value, target); + case metagen::mdTypeSShort: + return writeJSCNumber(runtime, value, target); + case metagen::mdTypeUShort: + if (JSValueIsString(runtime.context(), value)) { + std::string text = jscValueToUtf8(runtime, value); + if (text.size() != 1) { + return false; + } + *static_cast(target) = + static_cast(static_cast(text[0])); + return true; + } + return writeJSCNumber(runtime, value, target); + case metagen::mdTypeSInt: + return writeJSCNumber(runtime, value, target); + case metagen::mdTypeUInt: + return writeJSCNumber(runtime, value, target); + case metagen::mdTypeSLong: + case metagen::mdTypeSInt64: + return writeJSCNumber(runtime, value, target); + case metagen::mdTypeULong: + case metagen::mdTypeUInt64: + return writeJSCNumber(runtime, value, target); + case metagen::mdTypeFloat: + return writeJSCNumber(runtime, value, target); + case metagen::mdTypeDouble: + return writeJSCNumber(runtime, value, target); + case metagen::mdTypeSelector: + return readJSCEngineSelectorArgument(runtime, value, + static_cast(target)); + case metagen::mdTypeClass: { + Class cls = jscNativeClassArgument(runtime, value); + if (cls == Nil) { + return false; + } + *static_cast(target) = cls; + return true; + } + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: + *static_cast(target) = + jscNativeObjectArgument(runtime, bridge, type, value, frame); + return true; + default: + break; + } + + Value wrapped = Value::borrowed(runtime, value); + convertEngineFfiArgument(runtime, bridge, type, wrapped, target, frame); + return true; +} + +JSValueRef jscInteger64Value(Runtime& runtime, int64_t value) { + constexpr int64_t maxSafeInteger = 9007199254740991LL; + constexpr int64_t minSafeInteger = -9007199254740991LL; + if (value >= minSafeInteger && value <= maxSafeInteger) { + return JSValueMakeNumber(runtime.context(), static_cast(value)); + } + Value bigint = BigInt::fromInt64(runtime, value); + return bigint.local(runtime); +} + +JSValueRef jscUnsignedInteger64Value(Runtime& runtime, uint64_t value) { + constexpr uint64_t maxSafeInteger = 9007199254740991ULL; + if (value <= maxSafeInteger) { + return JSValueMakeNumber(runtime.context(), static_cast(value)); + } + Value bigint = BigInt::fromUint64(runtime, value); + return bigint.local(runtime); +} + +JSValueRef setJSCEngineObjectReturn( + Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, id object) { + if (object == nil) { + return JSValueMakeNull(runtime.context()); + } + Value roundTrip = + findCachedNativeObjectReturn(runtime, bridge, type, object); + if (!roundTrip.isUndefined()) { + JSValueRef result = roundTrip.local(runtime); + if (type.returnOwned) { + [object release]; + } + return result; + } + if (nativeObjectReturnMayCoerceToString(type) && + nativeObjectIsStringLike(object)) { + std::string utf8 = utf8StringFromNSString(static_cast(object)); + if (type.returnOwned) { + [object release]; + } + JSStringRef string = engine::jscengine::makeJSString(utf8); + JSValueRef result = JSValueMakeString(runtime.context(), string); + JSStringRelease(string); + return result; + } + if ([object isKindOfClass:[NSNull class]]) { + if (type.returnOwned) { + [object release]; + } + return JSValueMakeNull(runtime.context()); + } + if ([object isKindOfClass:[NSNumber class]] && + ![object isKindOfClass:[NSDecimalNumber class]]) { + NSNumber* number = static_cast(object); + const char* objCType = [number objCType]; + bool isBool = CFGetTypeID((__bridge CFTypeRef)number) == + CFBooleanGetTypeID() || + (objCType != nullptr && + std::strcmp(objCType, @encode(BOOL)) == 0); + JSValueRef result = + isBool ? JSValueMakeBoolean(runtime.context(), [number boolValue]) + : JSValueMakeNumber(runtime.context(), [number doubleValue]); + if (type.returnOwned) { + [object release]; + } + return result; + } + + if (const NativeApiSymbol* classSymbol = + bridge->findClassForRuntimePointer((void*)object)) { + Value result = makeNativeClassValue(runtime, bridge, *classSymbol); + if (type.returnOwned) { + [object release]; + } + return result.local(runtime); + } + if (const NativeApiSymbol* protocolSymbol = + bridge->findProtocolForRuntimePointer((void*)object)) { + Value result = makeNativeProtocolValue(runtime, bridge, *protocolSymbol); + if (type.returnOwned) { + [object release]; + } + return result.local(runtime); + } + Value result = makeNativeObjectValue(runtime, bridge, object, type.returnOwned); + return result.local(runtime); +} + +JSValueRef setJSCEngineReturnValue( + Runtime& runtime, const std::shared_ptr& bridge, + NativeApiType type, void* value, const std::string& selectorName) { + switch (type.kind) { + case metagen::mdTypeVoid: + return JSValueMakeUndefined(runtime.context()); + case metagen::mdTypeBool: + return JSValueMakeBoolean(runtime.context(), + *static_cast(value) != 0); + case metagen::mdTypeChar: + return JSValueMakeNumber(runtime.context(), + *static_cast(value)); + case metagen::mdTypeUChar: + case metagen::mdTypeUInt8: + return JSValueMakeNumber(runtime.context(), + *static_cast(value)); + case metagen::mdTypeSShort: + return JSValueMakeNumber(runtime.context(), + *static_cast(value)); + case metagen::mdTypeUShort: { + uint16_t raw = *static_cast(value); + if (raw >= 32 && raw <= 126) { + char buffer[2] = {static_cast(raw), '\0'}; + JSStringRef string = engine::jscengine::makeJSString(buffer); + JSValueRef result = JSValueMakeString(runtime.context(), string); + JSStringRelease(string); + return result; + } + return JSValueMakeNumber(runtime.context(), raw); + } + case metagen::mdTypeSInt: + return JSValueMakeNumber(runtime.context(), + *static_cast(value)); + case metagen::mdTypeUInt: + return JSValueMakeNumber(runtime.context(), + *static_cast(value)); + case metagen::mdTypeSLong: + case metagen::mdTypeSInt64: + return jscInteger64Value(runtime, *static_cast(value)); + case metagen::mdTypeULong: + case metagen::mdTypeUInt64: + return jscUnsignedInteger64Value(runtime, + *static_cast(value)); + case metagen::mdTypeFloat: + return JSValueMakeNumber(runtime.context(), *static_cast(value)); + case metagen::mdTypeDouble: + return JSValueMakeNumber(runtime.context(), *static_cast(value)); + case metagen::mdTypeClass: { + Class cls = *static_cast(value); + if (cls == nil) { + return JSValueMakeNull(runtime.context()); + } + const char* name = class_getName(cls); + NativeApiSymbol symbol{ + .kind = NativeApiSymbolKind::Class, + .offset = MD_SECTION_OFFSET_NULL, + .name = name != nullptr ? name : "", + .runtimeName = name != nullptr ? name : "", + }; + if (const NativeApiSymbol* found = bridge->findClass(symbol.name)) { + symbol = *found; + } + Value result = makeNativeClassValue(runtime, bridge, std::move(symbol)); + return result.local(runtime); + } + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: + if ((selectorName == "valueForKey:" || + selectorName == "valueForKeyPath:") && + isObjectiveCObjectType(type)) { + type.kind = metagen::mdTypeAnyObject; + } + return setJSCEngineObjectReturn(runtime, bridge, type, + *static_cast(value)); + case metagen::mdTypeSelector: { + SEL selector = *static_cast(value); + const char* selectorNameValue = + selector != nullptr ? sel_getName(selector) : nullptr; + if (selectorNameValue == nullptr) { + return JSValueMakeNull(runtime.context()); + } + JSStringRef string = engine::jscengine::makeJSString(selectorNameValue); + JSValueRef result = JSValueMakeString(runtime.context(), string); + JSStringRelease(string); + return result; + } + default: + break; + } + Value result = convertNativeReturnValue(runtime, bridge, type, value); + return result.local(runtime); +} diff --git a/NativeScript/ffi/objc/jsc/NativeApiJSCRuntime.h b/NativeScript/ffi/objc/jsc/NativeApiJSCRuntime.h new file mode 100644 index 000000000..ecf8db1d3 --- /dev/null +++ b/NativeScript/ffi/objc/jsc/NativeApiJSCRuntime.h @@ -0,0 +1,875 @@ +#ifndef NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_RUNTIME_H +#define NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_RUNTIME_H + +#ifdef TARGET_ENGINE_JSC + +#import +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Metadata.h" +#include "MetadataReader.h" +#include "ffi.h" + +@protocol NativeApiClassBuilderProtocol +@end + +#ifdef EMBED_METADATA_SIZE +extern const unsigned char embedded_metadata[EMBED_METADATA_SIZE]; +#endif + +namespace nativescript { +namespace engine { + +class Runtime; +class Value; +class Object; +class Function; +class Array; +class String; +class BigInt; +class ArrayBuffer; + +class JSError : public std::runtime_error { + public: + JSError(Runtime&, const std::string& message) : std::runtime_error(message) {} + explicit JSError(const std::string& message) : std::runtime_error(message) {} +}; + +class StringBuffer { + public: + explicit StringBuffer(std::string value) : value_(std::move(value)) {} + const char* data() const { return value_.data(); } + size_t size() const { return value_.size(); } + + private: + std::string value_; +}; + +class MutableBuffer { + public: + virtual ~MutableBuffer() = default; + virtual size_t size() const = 0; + virtual uint8_t* data() = 0; +}; + +class PropNameID { + public: + PropNameID() = default; + explicit PropNameID(std::string value) : value_(std::move(value)) {} + + static PropNameID forAscii(Runtime&, const char* value) { + return PropNameID(value != nullptr ? value : ""); + } + + static PropNameID forAscii(Runtime&, const std::string& value) { return PropNameID(value); } + + std::string utf8(Runtime&) const { return value_; } + + private: + std::string value_; +}; + +class HostObject { + public: + virtual ~HostObject() = default; + virtual Value get(Runtime& runtime, const PropNameID& name); + virtual bool set(Runtime& runtime, const PropNameID& name, const Value& value); + virtual std::vector getPropertyNames(Runtime& runtime); +}; + +using HostFunctionType = std::function; + +namespace jscengine { + +inline std::string stringToUtf8(JSStringRef string) { + if (string == nullptr) { + return {}; + } + size_t capacity = JSStringGetMaximumUTF8CStringSize(string); + std::string result(capacity, '\0'); + size_t written = JSStringGetUTF8CString(string, result.data(), capacity); + if (written == 0) { + return {}; + } + result.resize(written - 1); + return result; +} + +inline JSStringRef makeJSString(const std::string& value) { + NSString* string = [[NSString alloc] initWithBytes:value.data() + length:value.size() + encoding:NSUTF8StringEncoding]; + if (string == nil) { + return JSStringCreateWithUTF8CString(value.c_str()); + } + + NSUInteger length = [string length]; + std::vector characters(length); + if (length > 0) { + [string getCharacters:characters.data() range:NSMakeRange(0, length)]; + } + [string release]; + return JSStringCreateWithCharacters(characters.data(), length); +} + +inline JSStringRef makeJSString(const char* value) { + return JSStringCreateWithUTF8CString(value != nullptr ? value : ""); +} + +inline std::string valueToUtf8(JSContextRef context, JSValueRef value) { + if (value == nullptr) { + return {}; + } + JSValueRef exception = nullptr; + JSStringRef string = JSValueToStringCopy(context, value, &exception); + if (string == nullptr || exception != nullptr) { + if (string != nullptr) { + JSStringRelease(string); + } + return {}; + } + std::string result = stringToUtf8(string); + JSStringRelease(string); + return result; +} + +inline JSValueRef makeError(JSContextRef context, const std::string& message) { + JSStringRef string = makeJSString(message); + JSValueRef argument = JSValueMakeString(context, string); + JSStringRelease(string); + JSValueRef exception = nullptr; + JSObjectRef error = JSObjectMakeError(context, 1, &argument, &exception); + if (error != nullptr && exception == nullptr) { + return error; + } + return argument; +} + +inline void setException(JSContextRef context, JSValueRef* exception, const std::exception& error) { + if (exception != nullptr) { + *exception = makeError(context, error.what()); + } +} + +struct RuntimeState { + explicit RuntimeState(JSGlobalContextRef context) : context(context) {} + + ~RuntimeState() { + if (hostClass != nullptr) { + JSClassRelease(hostClass); + } + if (functionClass != nullptr) { + JSClassRelease(functionClass); + } + if (selectorGroupFunctionClass != nullptr) { + JSClassRelease(selectorGroupFunctionClass); + } + } + + JSGlobalContextRef context = nullptr; + JSClassRef hostClass = nullptr; + JSClassRef functionClass = nullptr; + JSClassRef selectorGroupFunctionClass = nullptr; +}; + +struct ValueStorage { + enum class Kind { + Undefined, + Null, + Bool, + Number, + JSC, + JSCBorrowed, + }; + + explicit ValueStorage(Kind kind) : kind(kind) {} + + ~ValueStorage() { + if (kind == Kind::JSC && context != nullptr && value != nullptr) { + JSValueUnprotect(context, value); + } + } + + Kind kind = Kind::Undefined; + bool boolValue = false; + double numberValue = 0; + JSGlobalContextRef context = nullptr; + JSValueRef value = nullptr; +}; + +template +const void* hostObjectTypeToken() { + static int token = 0; + return &token; +} + +struct HostObjectHolder { + HostObjectHolder(std::shared_ptr state, std::shared_ptr hostObject, + const void* typeToken) + : state(std::move(state)), hostObject(std::move(hostObject)), typeToken(typeToken) {} + + std::shared_ptr state; + std::shared_ptr hostObject; + const void* typeToken = nullptr; +}; + +struct FunctionHolder { + FunctionHolder(std::shared_ptr state, HostFunctionType callback) + : state(std::move(state)), callback(std::move(callback)) {} + + std::shared_ptr state; + HostFunctionType callback; +}; + +struct ArrayBufferHolder { + explicit ArrayBufferHolder(std::shared_ptr buffer) : buffer(std::move(buffer)) {} + + std::shared_ptr buffer; +}; + +void setFunctionPrototype(JSGlobalContextRef context, JSObjectRef function); + +} // namespace jscengine + +class Runtime { + public: + explicit Runtime(JSGlobalContextRef context) + : state_(std::make_shared(context)) {} + + explicit Runtime(std::shared_ptr state) : state_(std::move(state)) {} + + JSGlobalContextRef context() const { return state_->context; } + std::shared_ptr state() const { return state_; } + + Object global(); + Value evaluateJavaScript(std::shared_ptr buffer, const std::string& sourceURL); + void drainMicrotasks() {} + + private: + std::shared_ptr state_; +}; + +class String { + public: + String() = default; + String(Runtime& runtime, JSStringRef string); + + static String createFromUtf8(Runtime& runtime, const char* value) { + JSStringRef string = jscengine::makeJSString(value); + String result(runtime, string); + JSStringRelease(string); + return result; + } + + static String createFromUtf8(Runtime& runtime, const std::string& value) { + JSStringRef string = jscengine::makeJSString(value); + String result(runtime, string); + JSStringRelease(string); + return result; + } + + static String createFromUtf8(Runtime& runtime, const uint8_t* value, size_t length) { + std::string text(reinterpret_cast(value), length); + return createFromUtf8(runtime, text); + } + + std::string utf8(Runtime& runtime) const; + JSValueRef local(Runtime& runtime) const { return storage_->value; } + operator Value() const; + + private: + friend class Value; + std::shared_ptr storage_; +}; + +class Value { + public: + Value() : kind_(jscengine::ValueStorage::Kind::Undefined) {} + + Value(bool value) : kind_(jscengine::ValueStorage::Kind::Bool), boolValue_(value) {} + + Value(double value) : kind_(jscengine::ValueStorage::Kind::Number), numberValue_(value) {} + + Value(int value) : Value(static_cast(value)) {} + Value(uint32_t value) : Value(static_cast(value)) {} + + Value(Runtime& runtime, const Value& value) { + if (value.kind_ == jscengine::ValueStorage::Kind::JSCBorrowed) { + // Promote borrowed to owned + storage_ = std::make_shared(jscengine::ValueStorage::Kind::JSC); + storage_->context = runtime.context(); + storage_->value = value.borrowedValue_ != nullptr ? value.borrowedValue_ + : JSValueMakeUndefined(runtime.context()); + JSValueProtect(runtime.context(), storage_->value); + kind_ = jscengine::ValueStorage::Kind::JSC; + return; + } + kind_ = value.kind_; + boolValue_ = value.boolValue_; + numberValue_ = value.numberValue_; + borrowedContext_ = value.borrowedContext_; + borrowedValue_ = value.borrowedValue_; + storage_ = value.storage_; + } + Value(Runtime& runtime, Value&& value) + : kind_(value.kind_), + boolValue_(value.boolValue_), + numberValue_(value.numberValue_), + borrowedContext_(value.borrowedContext_), + borrowedValue_(value.borrowedValue_), + storage_(std::move(value.storage_)) {} + Value(Runtime& runtime, const String& value); + Value(Runtime& runtime, const Object& object); + Value(Runtime& runtime, const Function& function); + Value(Runtime& runtime, const Array& array); + Value(Runtime& runtime, const ArrayBuffer& arrayBuffer); + Value(Runtime& runtime, const BigInt& bigint); + Value(Runtime& runtime, JSValueRef value) + : kind_(jscengine::ValueStorage::Kind::JSC), + storage_(std::make_shared(jscengine::ValueStorage::Kind::JSC)) { + storage_->context = runtime.context(); + storage_->value = value != nullptr ? value : JSValueMakeUndefined(runtime.context()); + JSValueProtect(runtime.context(), storage_->value); + } + + static Value borrowed(Runtime& runtime, JSValueRef value) { + Value result; + result.kind_ = jscengine::ValueStorage::Kind::JSCBorrowed; + result.borrowedContext_ = runtime.context(); + result.borrowedValue_ = value != nullptr ? value : JSValueMakeUndefined(runtime.context()); + return result; + } + + static Value undefined() { return Value(); } + static Value null() { + Value value; + value.kind_ = jscengine::ValueStorage::Kind::Null; + return value; + } + + bool isUndefined() const { + return kind_ == jscengine::ValueStorage::Kind::Undefined || + (isJSC() && JSValueIsUndefined(jscContext(), jscValue())); + } + bool isNull() const { + return kind_ == jscengine::ValueStorage::Kind::Null || + (isJSC() && JSValueIsNull(jscContext(), jscValue())); + } + bool isBool() const { + return kind_ == jscengine::ValueStorage::Kind::Bool || + (isJSC() && JSValueIsBoolean(jscContext(), jscValue())); + } + bool getBool() const { + if (kind_ == jscengine::ValueStorage::Kind::Bool) { + return boolValue_; + } + return isJSC() && JSValueToBoolean(jscContext(), jscValue()); + } + bool isNumber() const { + return kind_ == jscengine::ValueStorage::Kind::Number || + (isJSC() && JSValueIsNumber(jscContext(), jscValue())); + } + double getNumber() const { + if (kind_ == jscengine::ValueStorage::Kind::Number) { + return numberValue_; + } + return isJSC() ? JSValueToNumber(jscContext(), jscValue(), nullptr) : 0; + } + + bool isObject() const { return isJSC() && JSValueIsObject(jscContext(), jscValue()); } + bool isString() const { return isJSC() && JSValueIsString(jscContext(), jscValue()); } + bool isBigInt() const { + if (!isJSC()) { + return false; + } + if (__builtin_available(macOS 15.0, iOS 18.0, *)) { + return JSValueIsBigInt(jscContext(), jscValue()); + } + return false; + } + bool isSymbol() const { return isJSC() && JSValueIsSymbol(jscContext(), jscValue()); } + + Object asObject(Runtime& runtime) const; + String asString(Runtime& runtime) const; + BigInt getBigInt(Runtime& runtime) const; + + JSValueRef local(Runtime& runtime) const { + switch (kind_) { + case jscengine::ValueStorage::Kind::Undefined: + return JSValueMakeUndefined(runtime.context()); + case jscengine::ValueStorage::Kind::Null: + return JSValueMakeNull(runtime.context()); + case jscengine::ValueStorage::Kind::Bool: + return JSValueMakeBoolean(runtime.context(), boolValue_); + case jscengine::ValueStorage::Kind::Number: + return JSValueMakeNumber(runtime.context(), numberValue_); + case jscengine::ValueStorage::Kind::JSC: + return storage_->value; + case jscengine::ValueStorage::Kind::JSCBorrowed: + return borrowedValue_; + } + } + + // Access the shared storage (for Object/Function/Array interop) + std::shared_ptr storage() const { return storage_; } + + static Value fromStorage(std::shared_ptr s) { + Value v; + v.kind_ = s->kind; + v.boolValue_ = s->boolValue; + v.numberValue_ = s->numberValue; + v.storage_ = std::move(s); + return v; + } + + private: + friend class Runtime; + friend class Object; + friend class String; + friend class BigInt; + friend class ArrayBuffer; + friend class Function; + friend class Array; + + bool isJSC() const { + return kind_ == jscengine::ValueStorage::Kind::JSC || + kind_ == jscengine::ValueStorage::Kind::JSCBorrowed; + } + JSContextRef jscContext() const { + return kind_ == jscengine::ValueStorage::Kind::JSCBorrowed ? borrowedContext_ + : storage_->context; + } + JSValueRef jscValue() const { + return kind_ == jscengine::ValueStorage::Kind::JSCBorrowed ? borrowedValue_ : storage_->value; + } + + jscengine::ValueStorage::Kind kind_ = jscengine::ValueStorage::Kind::Undefined; + bool boolValue_ = false; + double numberValue_ = 0; + JSGlobalContextRef borrowedContext_ = nullptr; + JSValueRef borrowedValue_ = nullptr; + std::shared_ptr storage_; +}; + +class Object { + public: + Object() = default; + explicit Object(Runtime& runtime) + : storage_(std::make_shared(jscengine::ValueStorage::Kind::JSC)) { + storage_->context = runtime.context(); + storage_->value = JSObjectMake(runtime.context(), nullptr, nullptr); + JSValueProtect(runtime.context(), storage_->value); + } + + static Object fromValueStorage(std::shared_ptr storage) { + Object object; + object.storage_ = std::move(storage); + return object; + } + + template + static Object createFromHostObject(Runtime& runtime, std::shared_ptr host) { + auto baseHost = std::static_pointer_cast(std::move(host)); + return createFromHostObjectWithToken(runtime, std::move(baseHost), + jscengine::hostObjectTypeToken()); + } + + Value getProperty(Runtime& runtime, const char* name) const { + JSStringRef property = jscengine::makeJSString(name); + JSValueRef exception = nullptr; + JSValueRef result = + JSObjectGetProperty(runtime.context(), local(runtime), property, &exception); + JSStringRelease(property); + if (exception != nullptr) { + throw JSError(runtime, jscengine::valueToUtf8(runtime.context(), exception)); + } + return Value(runtime, result); + } + + Value getProperty(Runtime& runtime, const std::string& name) const { + return getProperty(runtime, name.c_str()); + } + + Value getProperty(Runtime& runtime, const Value& key) const { + JSValueRef exception = nullptr; + JSValueRef result = JSObjectGetPropertyForKey(runtime.context(), local(runtime), + key.local(runtime), &exception); + if (exception != nullptr) { + throw JSError(runtime, jscengine::valueToUtf8(runtime.context(), exception)); + } + return Value(runtime, result); + } + + Object getPropertyAsObject(Runtime& runtime, const char* name) const { + return getProperty(runtime, name).asObject(runtime); + } + + Function getPropertyAsFunction(Runtime& runtime, const char* name) const; + + void setProperty(Runtime& runtime, const char* name, const Value& value) { + JSStringRef property = jscengine::makeJSString(name); + JSValueRef exception = nullptr; + JSObjectSetProperty(runtime.context(), local(runtime), property, value.local(runtime), + kJSPropertyAttributeNone, &exception); + JSStringRelease(property); + if (exception != nullptr) { + throw JSError(runtime, jscengine::valueToUtf8(runtime.context(), exception)); + } + } + + void setProperty(Runtime& runtime, const char* name, const String& value) { + setProperty(runtime, name, Value(runtime, value)); + } + void setProperty(Runtime& runtime, const char* name, const Object& value) { + setProperty(runtime, name, Value(runtime, value)); + } + void setProperty(Runtime& runtime, const char* name, const Function& value); + void setProperty(Runtime& runtime, const char* name, const Array& value); + void setProperty(Runtime& runtime, const char* name, const ArrayBuffer& value); + void setProperty(Runtime& runtime, const char* name, bool value) { + setProperty(runtime, name, Value(value)); + } + void setProperty(Runtime& runtime, const char* name, double value) { + setProperty(runtime, name, Value(value)); + } + void setProperty(Runtime& runtime, const std::string& name, const Value& value) { + setProperty(runtime, name.c_str(), value); + } + void setProperty(Runtime& runtime, const Value& key, const Value& value) { + JSValueRef exception = nullptr; + JSObjectSetPropertyForKey(runtime.context(), local(runtime), key.local(runtime), + value.local(runtime), kJSPropertyAttributeNone, &exception); + if (exception != nullptr) { + throw JSError(runtime, jscengine::valueToUtf8(runtime.context(), exception)); + } + } + + bool hasProperty(Runtime& runtime, const char* name) const { + JSStringRef property = jscengine::makeJSString(name); + bool result = JSObjectHasProperty(runtime.context(), local(runtime), property); + JSStringRelease(property); + return result; + } + + bool isFunction(Runtime& runtime) const { + return JSObjectIsFunction(runtime.context(), local(runtime)); + } + + bool isArray(Runtime& runtime) const { + JSStringRef name = jscengine::makeJSString("Array"); + JSValueRef constructorValue = JSObjectGetProperty( + runtime.context(), JSContextGetGlobalObject(runtime.context()), name, nullptr); + JSStringRelease(name); + if (constructorValue == nullptr || !JSValueIsObject(runtime.context(), constructorValue)) { + return false; + } + JSObjectRef constructor = JSValueToObject(runtime.context(), constructorValue, nullptr); + JSValueRef exception = nullptr; + bool result = + JSValueIsInstanceOfConstructor(runtime.context(), local(runtime), constructor, &exception); + return exception == nullptr && result; + } + + bool isArrayBuffer(Runtime& runtime) const { + JSValueRef exception = nullptr; + JSTypedArrayType type = + JSValueGetTypedArrayType(runtime.context(), storage_->value, &exception); + return exception == nullptr && type == kJSTypedArrayTypeArrayBuffer; + } + + Function asFunction(Runtime& runtime) const; + Array getArray(Runtime& runtime) const; + ArrayBuffer getArrayBuffer(Runtime& runtime) const; + Array getPropertyNames(Runtime& runtime) const; + + template + bool isHostObject(Runtime& runtime) const { + auto holder = hostObjectHolder(runtime); + return holder != nullptr && holder->typeToken == jscengine::hostObjectTypeToken(); + } + + template + std::shared_ptr getHostObject(Runtime& runtime) const { + auto holder = hostObjectHolder(runtime); + if (holder == nullptr || holder->typeToken != jscengine::hostObjectTypeToken()) { + return nullptr; + } + return std::static_pointer_cast(holder->hostObject); + } + + JSObjectRef local(Runtime& runtime) const { + return reinterpret_cast(const_cast(storage_->value)); + } + + operator Value() const { return Value::fromStorage(storage_); } + + protected: + friend class Value; + friend class Runtime; + friend class Function; + friend class Array; + friend class ArrayBuffer; + + explicit Object(std::shared_ptr storage) + : storage_(std::move(storage)) {} + + static Object createFromHostObjectWithToken(Runtime& runtime, std::shared_ptr host, + const void* typeToken); + + jscengine::HostObjectHolder* hostObjectHolder(Runtime& runtime) const { + return static_cast(JSObjectGetPrivate(local(runtime))); + } + + std::shared_ptr storage_; +}; + +class Function : public Object { + public: + Function() = default; + explicit Function(Object object) : Object(std::move(object.storage_)) {} + + static Function createFromHostFunction(Runtime& runtime, const PropNameID& name, unsigned int, + HostFunctionType callback); + + Value call(Runtime& runtime, const Value* args, size_t count) const { + std::vector argv; + argv.reserve(count); + for (size_t i = 0; i < count; i++) { + argv.push_back(args[i].local(runtime)); + } + JSValueRef exception = nullptr; + JSValueRef result = JSObjectCallAsFunction( + runtime.context(), local(runtime), JSContextGetGlobalObject(runtime.context()), argv.size(), + argv.empty() ? nullptr : argv.data(), &exception); + if (exception != nullptr) { + throw JSError(runtime, jscengine::valueToUtf8(runtime.context(), exception)); + } + return Value(runtime, result); + } + + Value call(Runtime& runtime) const { + return call(runtime, static_cast(nullptr), 0); + } + Value call(Runtime& runtime, std::nullptr_t, size_t) const { + return call(runtime, static_cast(nullptr), 0); + } + template + Value call(Runtime& runtime, const Value (&args)[N], size_t count) const { + return call(runtime, static_cast(args), count); + } + template + Value call(Runtime& runtime, Args&&... args) const { + Value argv[] = {Value(runtime, std::forward(args))...}; + return call(runtime, static_cast(argv), sizeof...(Args)); + } + + Value callWithThis(Runtime& runtime, const Object& thisObject, const Value* args = nullptr, + size_t count = 0) const { + std::vector argv; + argv.reserve(count); + for (size_t i = 0; i < count; i++) { + argv.push_back(args[i].local(runtime)); + } + JSValueRef exception = nullptr; + JSValueRef result = + JSObjectCallAsFunction(runtime.context(), local(runtime), thisObject.local(runtime), + argv.size(), argv.empty() ? nullptr : argv.data(), &exception); + if (exception != nullptr) { + throw JSError(runtime, jscengine::valueToUtf8(runtime.context(), exception)); + } + return Value(runtime, result); + } + + Value callAsConstructor(Runtime& runtime, const Value* args, size_t count) const { + std::vector argv; + argv.reserve(count); + for (size_t i = 0; i < count; i++) { + argv.push_back(args[i].local(runtime)); + } + JSValueRef exception = nullptr; + JSValueRef result = JSObjectCallAsConstructor(runtime.context(), local(runtime), argv.size(), + argv.empty() ? nullptr : argv.data(), &exception); + if (exception != nullptr) { + throw JSError(runtime, jscengine::valueToUtf8(runtime.context(), exception)); + } + return Value(runtime, result); + } + Value callAsConstructor(Runtime& runtime, std::nullptr_t, size_t) const { + return callAsConstructor(runtime, static_cast(nullptr), 0); + } + template + Value callAsConstructor(Runtime& runtime, const Value (&args)[N], size_t count) const { + return callAsConstructor(runtime, static_cast(args), count); + } + template + Value callAsConstructor(Runtime& runtime, Args&&... args) const { + Value argv[] = {Value(runtime, std::forward(args))...}; + return callAsConstructor(runtime, static_cast(argv), sizeof...(Args)); + } +}; + +class Array : public Object { + public: + explicit Array(Runtime& runtime, size_t size) + : Object(std::make_shared(jscengine::ValueStorage::Kind::JSC)) { + std::vector initial(size, JSValueMakeUndefined(runtime.context())); + JSValueRef exception = nullptr; + storage_->context = runtime.context(); + storage_->value = + JSObjectMakeArray(runtime.context(), initial.size(), initial.data(), &exception); + if (exception != nullptr) { + throw JSError(runtime, jscengine::valueToUtf8(runtime.context(), exception)); + } + JSValueProtect(runtime.context(), storage_->value); + } + + explicit Array(Object object) : Object(std::move(object.storage_)) {} + + size_t size(Runtime& runtime) const { + Value length = getProperty(runtime, "length"); + return length.isNumber() ? static_cast(std::max(0, length.getNumber())) : 0; + } + + Value getValueAtIndex(Runtime& runtime, size_t index) const { + JSValueRef exception = nullptr; + JSValueRef result = JSObjectGetPropertyAtIndex(runtime.context(), local(runtime), + static_cast(index), &exception); + if (exception != nullptr) { + throw JSError(runtime, jscengine::valueToUtf8(runtime.context(), exception)); + } + return Value(runtime, result); + } + + void setValueAtIndex(Runtime& runtime, size_t index, const Value& value) { + JSValueRef exception = nullptr; + JSObjectSetPropertyAtIndex(runtime.context(), local(runtime), static_cast(index), + value.local(runtime), &exception); + if (exception != nullptr) { + throw JSError(runtime, jscengine::valueToUtf8(runtime.context(), exception)); + } + } + void setValueAtIndex(Runtime& runtime, size_t index, const String& value) { + setValueAtIndex(runtime, index, Value(runtime, value)); + } +}; + +class BigInt { + public: + BigInt() = default; + BigInt(Runtime& runtime, JSValueRef value) + : storage_(std::make_shared(jscengine::ValueStorage::Kind::JSC)) { + storage_->context = runtime.context(); + storage_->value = value; + JSValueProtect(runtime.context(), storage_->value); + } + + static BigInt fromInt64(Runtime& runtime, int64_t value) { + JSValueRef exception = nullptr; + JSValueRef result = nullptr; + if (__builtin_available(macOS 15.0, iOS 18.0, *)) { + result = JSBigIntCreateWithInt64(runtime.context(), value, &exception); + } + if (result == nullptr || exception != nullptr) { + result = JSValueMakeNumber(runtime.context(), static_cast(value)); + } + return BigInt(runtime, result); + } + + static BigInt fromUint64(Runtime& runtime, uint64_t value) { + JSValueRef exception = nullptr; + JSValueRef result = nullptr; + if (__builtin_available(macOS 15.0, iOS 18.0, *)) { + result = JSBigIntCreateWithUInt64(runtime.context(), value, &exception); + } + if (result == nullptr || exception != nullptr) { + result = JSValueMakeNumber(runtime.context(), static_cast(value)); + } + return BigInt(runtime, result); + } + + String toString(Runtime& runtime, int) const { + JSValueRef exception = nullptr; + JSStringRef string = JSValueToStringCopy(runtime.context(), local(runtime), &exception); + if (string == nullptr || exception != nullptr) { + throw JSError(runtime, jscengine::valueToUtf8(runtime.context(), exception)); + } + String result(runtime, string); + JSStringRelease(string); + return result; + } + + JSValueRef local(Runtime& runtime) const { return storage_->value; } + + operator Value() const { return Value::fromStorage(storage_); } + + private: + friend class Value; + std::shared_ptr storage_; +}; + +class ArrayBuffer : public Object { + public: + ArrayBuffer(Runtime& runtime, std::shared_ptr buffer) + : Object(std::make_shared(jscengine::ValueStorage::Kind::JSC)) { + auto* holder = new jscengine::ArrayBufferHolder(std::move(buffer)); + JSValueRef exception = nullptr; + storage_->context = runtime.context(); + storage_->value = JSObjectMakeArrayBufferWithBytesNoCopy( + runtime.context(), holder->buffer->data(), holder->buffer->size(), + [](void*, void* deallocatorContext) { + delete static_cast(deallocatorContext); + }, + holder, &exception); + if (exception != nullptr) { + delete holder; + throw JSError(runtime, jscengine::valueToUtf8(runtime.context(), exception)); + } + JSValueProtect(runtime.context(), storage_->value); + } + + explicit ArrayBuffer(Object object) : Object(std::move(object.storage_)) {} + + size_t size(Runtime& runtime) const { + JSValueRef exception = nullptr; + return JSObjectGetArrayBufferByteLength(runtime.context(), local(runtime), &exception); + } + + uint8_t* data(Runtime& runtime) const { + JSValueRef exception = nullptr; + return static_cast( + JSObjectGetArrayBufferBytesPtr(runtime.context(), local(runtime), &exception)); + } +}; +} // namespace engine +} // namespace nativescript + +#endif // TARGET_ENGINE_JSC + +#endif // NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_RUNTIME_H diff --git a/NativeScript/ffi/jsc/NativeApiJSCRuntime.mm b/NativeScript/ffi/objc/jsc/NativeApiJSCRuntime.mm similarity index 77% rename from NativeScript/ffi/jsc/NativeApiJSCRuntime.mm rename to NativeScript/ffi/objc/jsc/NativeApiJSCRuntime.mm index 3396af697..da5aa2ce8 100644 --- a/NativeScript/ffi/jsc/NativeApiJSCRuntime.mm +++ b/NativeScript/ffi/objc/jsc/NativeApiJSCRuntime.mm @@ -2,8 +2,8 @@ #ifdef TARGET_ENGINE_JSC -namespace facebook { -namespace jsi { +namespace nativescript { +namespace engine { Object Runtime::global() { return Object::fromValueStorage(Value(*this, JSContextGetGlobalObject(context())).storage_); @@ -13,18 +13,18 @@ const std::string& sourceURL) { JSStringRef source = JSStringCreateWithUTF8CString( buffer != nullptr ? std::string(buffer->data(), buffer->size()).c_str() : ""); - JSStringRef url = jscdirect::makeJSString(sourceURL); + JSStringRef url = jscengine::makeJSString(sourceURL); JSValueRef exception = nullptr; JSValueRef result = JSEvaluateScript(context(), source, nullptr, url, 1, &exception); JSStringRelease(source); JSStringRelease(url); if (exception != nullptr) { - throw JSError(*this, jscdirect::valueToUtf8(context(), exception)); + throw JSError(*this, jscengine::valueToUtf8(context(), exception)); } return Value(*this, result); } -} // namespace jsi -} // namespace facebook +} // namespace engine +} // namespace nativescript #endif // TARGET_ENGINE_JSC diff --git a/NativeScript/ffi/objc/jsc/NativeApiJSCRuntimeSupport.mm b/NativeScript/ffi/objc/jsc/NativeApiJSCRuntimeSupport.mm new file mode 100644 index 000000000..8d1849d19 --- /dev/null +++ b/NativeScript/ffi/objc/jsc/NativeApiJSCRuntimeSupport.mm @@ -0,0 +1,12 @@ +// Included by NativeApiJSC.mm inside the NativeScript anonymous namespace. + +std::shared_ptr retainNativeApiRuntime(Runtime& runtime) { + return std::make_shared(runtime.state()); +} + +void SetNativeApiObjectPrototype(Runtime& runtime, Object& object, + const Object& prototype) { + JSObjectSetPrototype(runtime.context(), object.local(runtime), + prototype.local(runtime)); +} + diff --git a/NativeScript/ffi/objc/jsc/NativeApiJSCSelectorGroups.mm b/NativeScript/ffi/objc/jsc/NativeApiJSCSelectorGroups.mm new file mode 100644 index 000000000..52f775102 --- /dev/null +++ b/NativeScript/ffi/objc/jsc/NativeApiJSCSelectorGroups.mm @@ -0,0 +1,458 @@ +// Included by NativeApiJSC.mm inside the NativeScript anonymous namespace. + +struct NativeApiSelectorGroupData { + NativeApiSelectorGroupData( + std::shared_ptr state, + std::shared_ptr bridge, Class lookupClass, + bool receiverIsClass, + std::shared_ptr> + selectors, + std::shared_ptr< + std::vector>> + preparedInvocations, + std::weak_ptr boundReceiver = {}, + std::shared_ptr boundReceiverState = + nullptr) + : state(state), + bridge(std::move(bridge)), + lookupClass(lookupClass), + receiverIsClass(receiverIsClass), + selectors(std::move(selectors)), + preparedInvocations(std::move(preparedInvocations)), + boundReceiver(std::move(boundReceiver)), + boundReceiverState(std::move(boundReceiverState)), + runtime(state) {} + + std::shared_ptr state; + std::shared_ptr bridge; + Class lookupClass = Nil; + bool receiverIsClass = false; + std::shared_ptr> selectors; + std::shared_ptr< + std::vector>> + preparedInvocations; + std::weak_ptr boundReceiver; + std::shared_ptr boundReceiverState; + // Reused per call (avoids per-call shared_ptr refcount + dispatch-superclass + // probe on the hot path). + Runtime runtime; + Class cachedReceiverClass = Nil; + Class cachedDispatchClass = Nil; +}; + +#include "NativeApiJSCMarshalling.mm" + +#include "NativeApiJSCGsd.mm" + + +void* lookupGeneratedEngineObjCGsdInvoker(uint64_t dispatchId) { + return reinterpret_cast(lookupObjCGsdInvoker(dispatchId)); +} + +bool tryCallGeneratedEngineObjCSelector( + Runtime& runtime, const std::shared_ptr& bridge, + id receiver, const NativeApiPreparedObjCInvocation& prepared, + const Value* args, size_t count, Class dispatchSuperClass, Value* result) { + if (result == nullptr || receiver == nil || + !prepared.gsdEngineCallable || dispatchSuperClass != Nil || + count != prepared.gsdEngineArgumentCount) { + return false; + } + + auto invoker = reinterpret_cast(prepared.engineInvoker); + GsdObjCContext ctx{runtime, bridge, receiver, prepared.selector, + runtime.context(), nullptr, prepared.signature.returnType}; + ctx.valueArguments = args; + ctx.materializeValueResult = true; + if (!invoker(ctx)) { + return false; + } + *result = std::move(ctx.valueResult); + return true; +} + +JSValueRef setJSCEnginePreparedObjCResult( + Runtime& runtime, const std::shared_ptr& bridge, + id receiver, const NativeApiPreparedObjCInvocation& prepared, + const std::shared_ptr& receiverHostObject, + const std::optional& initializerClassWrapper, + size_t providedCount, const JSValueRef arguments[], + Class dispatchSuperClass) { + const NativeApiSignature& signature = prepared.signature; + if (receiver == nil || signature.variadic || + unsupportedEngineType(signature.returnType)) { + throw JSError(runtime, + "Objective-C selector is not supported by JSC engine: " + + prepared.selectorName); + } + + const bool isNSErrorOutMethod = prepared.isNSErrorOutMethod; + if (isNSErrorOutMethod) { + size_t expected = signature.argumentTypes.size(); + if (providedCount > expected || providedCount + 1 < expected) { + throw JSError( + runtime, "Actual arguments count: \"" + std::to_string(providedCount) + + "\". Expected: \"" + std::to_string(expected) + "\"."); + } + } else if (providedCount != signature.argumentTypes.size()) { + throw JSError( + runtime, "Actual arguments count: \"" + std::to_string(providedCount) + + "\". Expected: \"" + + std::to_string(signature.argumentTypes.size()) + "\"."); + } + + // GSD fast path: the generated invoker reads args directly from the JSC + // arguments, calls objc_msgSend with a typed cast, and produces the JS + // return value — bypassing all generic marshalling. + if (prepared.gsdEngineCallable && dispatchSuperClass == Nil && + providedCount == prepared.gsdEngineArgumentCount && + !initializerClassWrapper && !isNSErrorOutMethod) { + auto invoker = reinterpret_cast(prepared.engineInvoker); + GsdObjCContext ctx{runtime, bridge, receiver, prepared.selector, + runtime.context(), arguments, signature.returnType}; + if (invoker(ctx)) { + return ctx.result; + } + } + + if (dispatchSuperClass == Nil && !initializerClassWrapper && + providedCount <= 2) { + Value fastArgs[2]; + for (size_t i = 0; i < providedCount; i++) { + fastArgs[i] = Value::borrowed(runtime, arguments[i]); + } + Value fastResult; + if (tryCallFastEngineObjCSelector(runtime, bridge, receiver, prepared, + fastArgs, providedCount, Nil, + &fastResult)) { + return fastResult.local(runtime); + } + } + + NativeApiArgumentFrame frame(signature.argumentTypes.size()); + for (size_t i = 0; i < providedCount; i++) { + if (!prepareJSCEngineArgument(runtime, bridge, signature.argumentTypes[i], + arguments[i], frame, i)) { + throw JSError(runtime, + "Objective-C argument is not supported by JSC engine: " + + prepared.selectorName); + } + } + + const bool hasImplicitNSErrorOutArg = + isNSErrorOutMethod && providedCount + 1 == signature.argumentTypes.size(); + NSError* implicitNSError = nil; + if (hasImplicitNSErrorOutArg) { + size_t outArgIndex = signature.argumentTypes.size() - 1; + void* target = frame.storageAt(outArgIndex, sizeof(NSError**)); + NSError** implicitNSErrorOutArg = &implicitNSError; + *static_cast(target) = implicitNSErrorOutArg; + } + + NativeApiPointerFrame values(signature.argumentTypes.size() + 2); + size_t valueIndex = 0; + struct objc_super superReceiver = {receiver, dispatchSuperClass}; + struct objc_super* superReceiverPtr = &superReceiver; + if (dispatchSuperClass != Nil) { + values.set(valueIndex++, &superReceiverPtr); + } else { + values.set(valueIndex++, &receiver); + } + values.set(valueIndex++, const_cast(&prepared.selector)); + for (size_t i = 0; i < signature.argumentTypes.size(); i++) { + values.set(valueIndex++, frame.values()[i]); + } + + NativeApiReturnStorage returnStorage( + nativeSizeForType(signature.returnType)); + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + if (prepared.preparedInvoker != nullptr && dispatchSuperClass == Nil) { + prepared.preparedInvoker(reinterpret_cast(objc_msgSend), + values.data(), returnStorage.data()); + } else { +#if defined(__x86_64__) + bool isStret = signature.returnType.ffiType->size > 16 && + signature.returnType.ffiType->type == FFI_TYPE_STRUCT; + void (*target)(void) = + dispatchSuperClass != Nil + ? (isStret ? FFI_FN(objc_msgSendSuper_stret) + : FFI_FN(objc_msgSendSuper)) + : (isStret ? FFI_FN(objc_msgSend_stret) : FFI_FN(objc_msgSend)); + ffi_call(const_cast(&signature.cif), target, + returnStorage.data(), values.data()); +#else + ffi_call(const_cast(&signature.cif), + dispatchSuperClass != Nil ? FFI_FN(objc_msgSendSuper) + : FFI_FN(objc_msgSend), + returnStorage.data(), values.data()); +#endif + } + }); + + NativeApiType returnType = signature.returnType; + if (hasImplicitNSErrorOutArg && implicitNSError != nil) { + const char* errorMessage = [[implicitNSError description] UTF8String]; + throw JSError( + runtime, errorMessage != nullptr ? errorMessage : "Unknown NSError"); + } + if (initializerClassWrapper) { + id resultObject = nil; + if (isObjectiveCObjectType(returnType)) { + resultObject = *static_cast(returnStorage.data()); + } + if (receiverHostObject != nullptr && resultObject != receiver) { + receiverHostObject->disownObject(receiver); + } + if (resultObject != nil) { + bridge->setObjectExpando(runtime, resultObject, + "__nativeApiClassWrapper", + Value(runtime, *initializerClassWrapper)); + } + } + return setJSCEngineReturnValue(runtime, bridge, returnType, + returnStorage.data(), prepared.selectorName); +} + +JSValueRef NativeApiSelectorGroupCall( + JSContextRef context, JSObjectRef function, JSObjectRef thisObject, + size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { + auto* data = + static_cast(JSObjectGetPrivate(function)); + if (data == nullptr || data->selectors == nullptr || + data->preparedInvocations == nullptr) { + return JSValueMakeUndefined(context); + } + + Runtime& runtime = data->runtime; + try { + NativeApiRoundTripCacheFrameGuard roundTripFrame(data->bridge); + if (argumentCount >= data->selectors->size() || + (*data->selectors)[argumentCount].selectorName.empty()) { + throw JSError(runtime, + "Objective-C selector is not available for the provided arguments " + "count."); + } + + NativeApiSelectorGroupEntry& entry = (*data->selectors)[argumentCount]; + auto& prepared = (*data->preparedInvocations)[argumentCount]; + Class selectorLookupClass = data->lookupClass; + id receiver = data->receiverIsClass ? static_cast(data->lookupClass) : nil; + std::shared_ptr receiverHostObject; + if (!data->receiverIsClass) { + if (data->boundReceiverState != nullptr) { + receiver = data->boundReceiverState->object(); + if (receiver == nil) { + throw JSError(runtime, + "Objective-C selector requires a native receiver."); + } + } else if (thisObject != nullptr) { + auto* holder = static_cast( + JSObjectGetPrivate(thisObject)); + if (holder != nullptr && + holder->typeToken == + engine::jscengine::hostObjectTypeToken< + NativeApiObjectHostObject>()) { + receiver = + static_cast(holder->hostObject.get()) + ->object(); + } + } + } + if (receiver == nil) { + throw JSError(runtime, + "Objective-C selector requires a native receiver."); + } + + const bool propertyGetterCall = + entry.hasMember && entry.member.property && argumentCount == 0; + const std::string* selectorNamePtr = &entry.selectorName; + const NativeApiMember* selectedMember = + entry.hasMember ? &entry.member : nullptr; + bool callTargetCanPrepare = true; + if (prepared == nullptr || propertyGetterCall) { + NativeApiSelectorGroupCallTarget callTarget = + selectorGroupCallTargetForEntry(receiver, selectorLookupClass, + data->receiverIsClass, entry, + argumentCount); + selectorNamePtr = callTarget.selectorName; + selectedMember = callTarget.member; + callTargetCanPrepare = callTarget.canPrepare; + if (prepared != nullptr && prepared->selectorName != *selectorNamePtr) { + prepared = nullptr; + } + } + const std::string& selectorName = + prepared != nullptr && !propertyGetterCall ? prepared->selectorName + : *selectorNamePtr; + + if (data->receiverIsClass) { + Class methodClass = prepared != nullptr ? prepared->receiverClass : Nil; + if (methodClass == Nil) { + SEL selector = sel_registerName(selectorName.c_str()); + methodClass = + NativeApiClassHostObject::classRespondingToClassSelector( + data->lookupClass, selector); + } + if (methodClass == Nil) { + throw JSError(runtime, + "Objective-C selector is not available: " + + entry.selectorName); + } + selectorLookupClass = methodClass; + receiver = static_cast(methodClass); + } + if (propertyGetterCall && !callTargetCanPrepare) { + return callObjCSelector(runtime, data->bridge, receiver, + data->receiverIsClass, selectorName, + selectedMember, nullptr, 0) + .local(runtime); + } + + if (prepared == nullptr) { + if (!data->receiverIsClass) { + SEL selector = sel_registerName(selectorName.c_str()); + if (class_getInstanceMethod(selectorLookupClass, selector) == nullptr) { + Class receiverClass = object_getClass(receiver); + if (class_getInstanceMethod(receiverClass, selector) != nullptr) { + selectorLookupClass = receiverClass; + } + } + } + prepared = prepareNativeApiObjCInvocation( + runtime, data->bridge, selectorLookupClass, data->receiverIsClass, + selectorName, selectedMember); + // Look up the engine-neutral GSD invoker for this signature. + if (prepared->engineInvoker == nullptr) { + uint64_t dispatchId = dispatchIdForEngineSignature( + prepared->signature, SignatureCallKind::ObjCMethod); + if (auto gsdInvoker = lookupObjCGsdInvoker(dispatchId)) { + prepared->engineInvoker = reinterpret_cast(gsdInvoker); + configureGeneratedEngineObjCInvocation(*prepared); + } + } + } + + std::optional initializerClassWrapper; + if (!data->receiverIsClass && prepared->isInitMethod) { + if (!receiverHostObject) { + if (data->boundReceiverState != nullptr) { + if (auto boundReceiver = data->boundReceiver.lock()) { + receiverHostObject = std::move(boundReceiver); + } + } else if (thisObject != nullptr) { + auto* holder = static_cast( + JSObjectGetPrivate(thisObject)); + if (holder != nullptr && + holder->typeToken == + engine::jscengine::hostObjectTypeToken< + NativeApiObjectHostObject>()) { + receiverHostObject = + std::static_pointer_cast( + holder->hostObject); + } + } + } + Value classWrapperValue = data->bridge->findObjectExpando( + runtime, receiver, "__nativeApiClassWrapper"); + if (classWrapperValue.isObject()) { + initializerClassWrapper.emplace(classWrapperValue.asObject(runtime)); + } + data->bridge->forgetRoundTripValue(receiver); + data->bridge->forgetObjectExpandos(receiver); + } + + Class dispatchClass = Nil; + if (!data->receiverIsClass) { + Class receiverClass = object_getClass(receiver); + if (receiverClass == data->cachedReceiverClass) { + dispatchClass = data->cachedDispatchClass; + } else { + dispatchClass = dispatchSuperclassForEngineDerivedReceiver( + receiver, data->lookupClass); + data->cachedReceiverClass = receiverClass; + data->cachedDispatchClass = dispatchClass; + } + } + return setJSCEnginePreparedObjCResult( + runtime, data->bridge, receiver, *prepared, receiverHostObject, + initializerClassWrapper, argumentCount, arguments, dispatchClass); + } catch (const std::exception& error) { + engine::jscengine::setException(context, exception, error); + return JSValueMakeUndefined(context); + } +} + +void NativeApiSelectorGroupFinalize(JSObjectRef function) { + delete static_cast( + JSObjectGetPrivate(function)); +} + +JSClassRef NativeApiSelectorGroupFunctionClass(Runtime& runtime) { + auto state = runtime.state(); + if (state->selectorGroupFunctionClass == nullptr) { + JSClassDefinition definition = kJSClassDefinitionEmpty; + definition.className = "NativeScriptEngineSelectorGroupFunction"; + definition.callAsFunction = NativeApiSelectorGroupCall; + definition.finalize = NativeApiSelectorGroupFinalize; + state->selectorGroupFunctionClass = JSClassCreate(&definition); + } + return state->selectorGroupFunctionClass; +} + +Function CreateNativeApiSelectorGroupFunctionImpl( + Runtime& runtime, std::shared_ptr bridge, + Class lookupClass, bool receiverIsClass, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations, + std::weak_ptr boundReceiver, + std::shared_ptr boundReceiverState = + nullptr) { + auto* data = new NativeApiSelectorGroupData( + runtime.state(), std::move(bridge), lookupClass, receiverIsClass, + std::move(selectors), std::move(preparedInvocations), + std::move(boundReceiver), std::move(boundReceiverState)); + JSObjectRef function = + JSObjectMake(runtime.context(), + NativeApiSelectorGroupFunctionClass(runtime), data); + engine::jscengine::setFunctionPrototype(runtime.context(), function); + + JSStringRef property = engine::jscengine::makeJSString("name"); + JSStringRef functionName = + engine::jscengine::makeJSString("__nativeSelectorGroup"); + JSValueRef value = JSValueMakeString(runtime.context(), functionName); + JSObjectSetProperty(runtime.context(), function, property, value, + kJSPropertyAttributeReadOnly, nullptr); + JSStringRelease(functionName); + JSStringRelease(property); + + Value functionValue(runtime, function); + return functionValue.asObject(runtime).asFunction(runtime); +} + +Function CreateNativeApiSelectorGroupFunction( + Runtime& runtime, std::shared_ptr bridge, + Class lookupClass, bool receiverIsClass, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations) { + return CreateNativeApiSelectorGroupFunctionImpl( + runtime, std::move(bridge), lookupClass, receiverIsClass, + std::move(selectors), std::move(preparedInvocations), {}, nullptr); +} + +Function CreateNativeApiBoundSelectorGroupFunction( + Runtime& runtime, std::shared_ptr bridge, Class lookupClass, + std::shared_ptr receiverHostObject, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations) { + return CreateNativeApiSelectorGroupFunctionImpl( + runtime, std::move(bridge), lookupClass, false, std::move(selectors), + std::move(preparedInvocations), receiverHostObject, + receiverHostObject != nullptr ? receiverHostObject->lifetimeState() + : nullptr); +} diff --git a/NativeScript/ffi/objc/jsc/NativeApiJSCValue.mm b/NativeScript/ffi/objc/jsc/NativeApiJSCValue.mm new file mode 100644 index 000000000..f66913761 --- /dev/null +++ b/NativeScript/ffi/objc/jsc/NativeApiJSCValue.mm @@ -0,0 +1,108 @@ +#include "NativeApiJSCRuntime.h" + +#ifdef TARGET_ENGINE_JSC + +namespace nativescript { +namespace engine { + +Value HostObject::get(Runtime&, const PropNameID&) { return Value::undefined(); } +bool HostObject::set(Runtime&, const PropNameID&, const Value&) { return true; } +std::vector HostObject::getPropertyNames(Runtime&) { return {}; } + +String::String(Runtime& runtime, JSStringRef string) + : storage_(std::make_shared(jscengine::ValueStorage::Kind::JSC)) { + storage_->context = runtime.context(); + storage_->value = JSValueMakeString(runtime.context(), string); + JSValueProtect(runtime.context(), storage_->value); +} + +std::string String::utf8(Runtime& runtime) const { + return jscengine::valueToUtf8(runtime.context(), storage_->value); +} + +String::operator Value() const { return Value::fromStorage(storage_); } + +Value::Value(Runtime&, const String& value) { + storage_ = value.storage_; + kind_ = storage_ ? storage_->kind : jscengine::ValueStorage::Kind::Undefined; +} +Value::Value(Runtime&, const Object& object) { + storage_ = object.storage_; + kind_ = storage_ ? storage_->kind : jscengine::ValueStorage::Kind::Undefined; +} +Value::Value(Runtime&, const Function& function) { + storage_ = function.storage_; + kind_ = storage_ ? storage_->kind : jscengine::ValueStorage::Kind::Undefined; +} +Value::Value(Runtime&, const Array& array) { + storage_ = array.storage_; + kind_ = storage_ ? storage_->kind : jscengine::ValueStorage::Kind::Undefined; +} +Value::Value(Runtime&, const ArrayBuffer& arrayBuffer) { + storage_ = arrayBuffer.storage_; + kind_ = storage_ ? storage_->kind : jscengine::ValueStorage::Kind::Undefined; +} +Value::Value(Runtime&, const BigInt& bigint) { + storage_ = bigint.storage_; + kind_ = storage_ ? storage_->kind : jscengine::ValueStorage::Kind::Undefined; +} + +Object Value::asObject(Runtime& runtime) const { + if (storage_) { + return Object::fromValueStorage(storage_); + } + // Promote borrowed to owned storage for Object. + auto s = std::make_shared(jscengine::ValueStorage::Kind::JSC); + s->context = runtime.context(); + s->value = borrowedValue_ != nullptr ? borrowedValue_ : JSValueMakeUndefined(runtime.context()); + JSValueProtect(runtime.context(), s->value); + return Object::fromValueStorage(std::move(s)); +} + +String Value::asString(Runtime& runtime) const { + JSValueRef exception = nullptr; + JSStringRef string = JSValueToStringCopy(runtime.context(), local(runtime), &exception); + if (string == nullptr || exception != nullptr) { + throw JSError(runtime, jscengine::valueToUtf8(runtime.context(), exception)); + } + String result(runtime, string); + JSStringRelease(string); + return result; +} + +BigInt Value::getBigInt(Runtime& runtime) const { return BigInt(runtime, local(runtime)); } + +Function Object::getPropertyAsFunction(Runtime& runtime, const char* name) const { + return getProperty(runtime, name).asObject(runtime).asFunction(runtime); +} +Function Object::asFunction(Runtime&) const { return Function(*this); } +Array Object::getArray(Runtime&) const { return Array(*this); } +ArrayBuffer Object::getArrayBuffer(Runtime&) const { return ArrayBuffer(*this); } + +Array Object::getPropertyNames(Runtime& runtime) const { + JSPropertyNameArrayRef propertyNames = + JSObjectCopyPropertyNames(runtime.context(), local(runtime)); + size_t count = JSPropertyNameArrayGetCount(propertyNames); + Array result(runtime, count); + for (size_t i = 0; i < count; i++) { + JSStringRef name = JSPropertyNameArrayGetNameAtIndex(propertyNames, i); + result.setValueAtIndex(runtime, i, String(runtime, name)); + } + JSPropertyNameArrayRelease(propertyNames); + return result; +} + +void Object::setProperty(Runtime& runtime, const char* name, const Function& value) { + setProperty(runtime, name, Value(runtime, value)); +} +void Object::setProperty(Runtime& runtime, const char* name, const Array& value) { + setProperty(runtime, name, Value(runtime, value)); +} +void Object::setProperty(Runtime& runtime, const char* name, const ArrayBuffer& value) { + setProperty(runtime, name, Value(runtime, value)); +} + +} // namespace engine +} // namespace nativescript + +#endif // TARGET_ENGINE_JSC diff --git a/NativeScript/ffi/objc/jsc/SignatureDispatch.h b/NativeScript/ffi/objc/jsc/SignatureDispatch.h new file mode 100644 index 000000000..02ac6e805 --- /dev/null +++ b/NativeScript/ffi/objc/jsc/SignatureDispatch.h @@ -0,0 +1,14 @@ +#ifndef NATIVESCRIPT_FFI_JSC_SIGNATURE_DISPATCH_H +#define NATIVESCRIPT_FFI_JSC_SIGNATURE_DISPATCH_H + +#include "ffi/objc/shared/SignatureDispatchCore.h" + +#if defined(__has_include) +#if __has_include("GeneratedSignatureDispatch.inc") +#include "GeneratedSignatureDispatch.inc" +#endif +#endif + +#include "ffi/objc/shared/PreparedSignatureDispatch.h" + +#endif // NATIVESCRIPT_FFI_JSC_SIGNATURE_DISPATCH_H diff --git a/NativeScript/ffi/napi/AutoreleasePool.h b/NativeScript/ffi/objc/napi/AutoreleasePool.h similarity index 100% rename from NativeScript/ffi/napi/AutoreleasePool.h rename to NativeScript/ffi/objc/napi/AutoreleasePool.h diff --git a/NativeScript/ffi/napi/AutoreleasePool.mm b/NativeScript/ffi/objc/napi/AutoreleasePool.mm similarity index 100% rename from NativeScript/ffi/napi/AutoreleasePool.mm rename to NativeScript/ffi/objc/napi/AutoreleasePool.mm diff --git a/NativeScript/ffi/napi/Block.h b/NativeScript/ffi/objc/napi/Block.h similarity index 100% rename from NativeScript/ffi/napi/Block.h rename to NativeScript/ffi/objc/napi/Block.h diff --git a/NativeScript/ffi/napi/Block.mm b/NativeScript/ffi/objc/napi/Block.mm similarity index 99% rename from NativeScript/ffi/napi/Block.mm rename to NativeScript/ffi/objc/napi/Block.mm index 3b8697afc..a99189a6a 100644 --- a/NativeScript/ffi/napi/Block.mm +++ b/NativeScript/ffi/objc/napi/Block.mm @@ -9,7 +9,7 @@ #include #include #include "Interop.h" -#include "runtime/NativeScriptException.h" +#include "runtime/apple/NativeScriptException.h" #include "ObjCBridge.h" #include "SignatureDispatch.h" #include "TypeConv.h" diff --git a/NativeScript/ffi/napi/CFunction.h b/NativeScript/ffi/objc/napi/CFunction.h similarity index 100% rename from NativeScript/ffi/napi/CFunction.h rename to NativeScript/ffi/objc/napi/CFunction.h diff --git a/NativeScript/ffi/napi/CFunction.mm b/NativeScript/ffi/objc/napi/CFunction.mm similarity index 99% rename from NativeScript/ffi/napi/CFunction.mm rename to NativeScript/ffi/objc/napi/CFunction.mm index 3f9d26714..e23a2b673 100644 --- a/NativeScript/ffi/napi/CFunction.mm +++ b/NativeScript/ffi/objc/napi/CFunction.mm @@ -12,8 +12,8 @@ #include "Interop.h" #include "ObjCBridge.h" #include "SignatureDispatch.h" -#include "runtime/NativeScriptException.h" -#include "Tasks.h" +#include "runtime/apple/NativeScriptException.h" +#include "ffi/objc/shared/Tasks.h" #ifdef ENABLE_JS_RUNTIME #include "jsr.h" #endif diff --git a/NativeScript/ffi/napi/CallbackThreading.h b/NativeScript/ffi/objc/napi/CallbackThreading.h similarity index 94% rename from NativeScript/ffi/napi/CallbackThreading.h rename to NativeScript/ffi/objc/napi/CallbackThreading.h index 63c17990f..08ffcea22 100644 --- a/NativeScript/ffi/napi/CallbackThreading.h +++ b/NativeScript/ffi/objc/napi/CallbackThreading.h @@ -4,6 +4,7 @@ #include "js_native_api.h" #include +#include #include #if defined(ENABLE_JS_RUNTIME) @@ -66,8 +67,9 @@ class NativeCallRuntimeUnlockScope final { jsr_->unlock(); } if (unlockedDepth_ == 0 && jsr_->runtime != nullptr) { - runtime_ = jsr_->runtime.get(); - runtime_->unlock(); + auto* runtime = jsr_->runtime.get(); + runtime->unlock(); + relockRuntime_ = [runtime]() { runtime->lock(); }; unlockedRuntime_ = true; } if (unlockedDepth_ > 0 || unlockedRuntime_) { @@ -91,8 +93,8 @@ class NativeCallRuntimeUnlockScope final { jsr_->lock(); } } - if (unlockedRuntime_ && runtime_ != nullptr) { - runtime_->lock(); + if (unlockedRuntime_ && relockRuntime_) { + relockRuntime_(); } #endif } @@ -104,7 +106,7 @@ class NativeCallRuntimeUnlockScope final { private: #if defined(ENABLE_JS_RUNTIME) && defined(TARGET_ENGINE_HERMES) JSR* jsr_ = nullptr; - facebook::jsi::ThreadSafeRuntime* runtime_ = nullptr; + std::function relockRuntime_; #endif int unlockedDepth_ = 0; bool unlockedRuntime_ = false; diff --git a/NativeScript/ffi/napi/Cif.h b/NativeScript/ffi/objc/napi/Cif.h similarity index 100% rename from NativeScript/ffi/napi/Cif.h rename to NativeScript/ffi/objc/napi/Cif.h diff --git a/NativeScript/ffi/objc/napi/Cif.mm b/NativeScript/ffi/objc/napi/Cif.mm new file mode 100644 index 000000000..4e1fe2004 --- /dev/null +++ b/NativeScript/ffi/objc/napi/Cif.mm @@ -0,0 +1,360 @@ +#include "Cif.h" +#include +#include +#include +#include +#include +#include +#include +#include "Metadata.h" +#include "MetadataReader.h" +#include "ObjCBridge.h" +#include "ffi/objc/shared/SignatureDispatchCore.h" +#include "TypeConv.h" +#include "Util.h" + +namespace nativescript { +namespace { + +inline bool typeRequiresSlowGeneratedNapiDispatch(const std::shared_ptr& type) { + if (type == nullptr) { + return false; + } + + switch (type->kind) { + case mdTypeUChar: + case mdTypeUInt8: + case mdTypeString: + case mdTypePointer: + case mdTypeStruct: + case mdTypeArray: + case mdTypeBlock: + case mdTypeFunctionPointer: + case mdTypeVector: + case mdTypeExtVector: + case mdTypeComplex: + return true; + default: + return false; + } +} + +inline bool typeKindMayUseRoundTripCache(MDTypeKind kind) { + switch (kind) { + case mdTypeAnyObject: + case mdTypeProtocolObject: + case mdTypeClassObject: + case mdTypeInstanceObject: + case mdTypeNSStringObject: + case mdTypeNSMutableStringObject: + return true; + default: + return false; + } +} + +inline void updateGeneratedNapiDispatchCompatibility(Cif* cif) { + if (cif == nullptr) { + return; + } + + cif->skipGeneratedNapiDispatch = false; + cif->generatedDispatchHasRoundTripCacheArgument = false; + cif->generatedDispatchUsesObjectReturnStorage = false; + + if (cif->returnType != nullptr) { + cif->generatedDispatchUsesObjectReturnStorage = + typeKindMayUseRoundTripCache(cif->returnType->kind); + } + + cif->skipGeneratedNapiDispatch = typeRequiresSlowGeneratedNapiDispatch(cif->returnType); + if (cif->skipGeneratedNapiDispatch) { + return; + } + + for (const auto& argType : cif->argTypes) { + if (argType != nullptr && typeKindMayUseRoundTripCache(argType->kind)) { + cif->generatedDispatchHasRoundTripCacheArgument = true; + } + if (typeRequiresSlowGeneratedNapiDispatch(argType)) { + cif->skipGeneratedNapiDispatch = true; + return; + } + } +} + +} // namespace + +// Essentially, we cache libffi structures per unique method signature, +// this helps us avoid the overhead of creating them on the fly for each +// invocation. +Cif* ObjCBridgeState::getMethodCif(napi_env env, Method method) { + auto encoding = std::string(method_getTypeEncoding(method)); + auto find = this->cifs[encoding]; + if (find != nullptr) { + return find; + } + + auto cif = new Cif(env, method); + this->cifs[encoding] = cif; + + return cif; +} + +Cif* ObjCBridgeState::getMethodCif(napi_env env, MDSectionOffset offset) { + auto find = this->mdMethodSignatureCache[offset]; + if (find != nullptr) { + return find; + } + + auto cif = new Cif(env, metadata, offset, true, false); + this->mdMethodSignatureCache[offset] = cif; + + return cif; +} + +Cif* ObjCBridgeState::getBlockCif(napi_env env, MDSectionOffset offset) { + auto find = this->mdBlockSignatureCache[offset]; + if (find != nullptr) { + return find; + } + + auto cif = new Cif(env, metadata, offset, false, true); + this->mdBlockSignatureCache[offset] = cif; + + return cif; +} + +Cif* ObjCBridgeState::getCFunctionCif(napi_env env, MDSectionOffset offset) { + auto find = this->mdFunctionSignatureCache[offset]; + if (find != nullptr) { + return find; + } + + auto cif = new Cif(env, metadata, offset, false, false); + this->mdFunctionSignatureCache[offset] = cif; + + return cif; +} + +Cif::Cif(napi_env env, std::string encoding, unsigned int implicitArgc) { + auto signature = [NSMethodSignature signatureWithObjCTypes:encoding.c_str()]; + unsigned long numberOfArguments = signature.numberOfArguments; + 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; + + const char* returnType = signature.methodReturnType; + this->returnType = TypeConv::Make(env, &returnType); + + ffi_type* rtype = this->returnType->type; + this->atypes = (ffi_type**)malloc(sizeof(ffi_type*) * totalArgc); + + unsigned long methodReturnLength = signature.methodReturnLength; + unsigned long frameLength = signature.frameLength; + + this->rvalue = malloc(methodReturnLength); + this->rvalueLength = methodReturnLength; + this->frameLength = frameLength; + + 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 = 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->ffiTypeForArgument(); + + if (i >= skippedArgs) { + 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 + 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; + + returnType = TypeConv::Make(env, reader, &offset); + + auto implicitArgs = isMethod ? 2 : isBlock ? 1 : 0; + + shouldFreeAny = false; + atypes = nullptr; + avaluesAllocStart = 0; + avaluesAllocCount = 0; + + if (next || isMethod || isBlock) { + while (next) { + auto argTypeKind = reader->getTypeKind(offset); + next = ((MDTypeFlag)argTypeKind & mdTypeFlagNext) != 0; + auto argTypeInfo = TypeConv::Make(env, reader, &offset); + std::string enc; + argTypeInfo->encode(&enc); + argTypes.push_back(argTypeInfo); + } + + argc = (int)argTypes.size(); + + auto totalArgc = argc + implicitArgs; + + argv = (napi_value*)malloc(sizeof(napi_value) * argc); + shouldFree = (bool*)malloc(sizeof(bool) * argc); + + atypes = (ffi_type**)malloc(sizeof(ffi_type*) * totalArgc); + avalues = (void**)malloc(sizeof(void*) * argc); + memset(avalues, 0, sizeof(void*) * argc); + + if (isMethod) { + atypes[0] = &ffi_type_pointer; + atypes[1] = &ffi_type_pointer; + } + + if (isBlock) { + atypes[0] = &ffi_type_pointer; + } + + for (int i = 0; i < argc; i++) { + atypes[i + implicitArgs] = argTypes[i]->ffiTypeForArgument(); + shouldFree[i] = false; + } + } else { + argc = 0; + argv = nullptr; + avalues = nullptr; + shouldFree = nullptr; + } + + ffi_status status = + ffi_prep_cif(&cif, FFI_DEFAULT_ABI, argc + implicitArgs, returnType->type, atypes); + + if (status != FFI_OK) { + std::cout << "Failed to prepare CIF, libffi returned error: " << status << std::endl; + return; + } + + for (int i = 0; i < argc; i++) { + avalues[i] = malloc(cif.arg_types[i + implicitArgs]->size); + avaluesAllocCount++; + } + + rvalue = malloc(cif.rtype->size); + rvalueLength = cif.rtype->size; + + signatureHash = metadataSignatureHash(reader, signatureStart); + + updateGeneratedNapiDispatchCompatibility(this); +} + +Cif::~Cif() { + if (rvalue != nullptr) { + free(rvalue); + } + if (argv != nullptr) { + free(argv); + } + if (avalues != nullptr) { + for (unsigned int i = 0; i < avaluesAllocCount; i++) { + auto index = avaluesAllocStart + i; + if (avalues[index] != nullptr) { + free(avalues[index]); + } + } + free(avalues); + } + if (atypes != nullptr) { + free(atypes); + } + if (shouldFree != nullptr) { + free(shouldFree); + } +} + +} // namespace nativescript diff --git a/NativeScript/ffi/napi/Class.h b/NativeScript/ffi/objc/napi/Class.h similarity index 100% rename from NativeScript/ffi/napi/Class.h rename to NativeScript/ffi/objc/napi/Class.h diff --git a/NativeScript/ffi/napi/Class.mm b/NativeScript/ffi/objc/napi/Class.mm similarity index 100% rename from NativeScript/ffi/napi/Class.mm rename to NativeScript/ffi/objc/napi/Class.mm diff --git a/NativeScript/ffi/napi/ClassBuilder.h b/NativeScript/ffi/objc/napi/ClassBuilder.h similarity index 100% rename from NativeScript/ffi/napi/ClassBuilder.h rename to NativeScript/ffi/objc/napi/ClassBuilder.h diff --git a/NativeScript/ffi/napi/ClassBuilder.mm b/NativeScript/ffi/objc/napi/ClassBuilder.mm similarity index 100% rename from NativeScript/ffi/napi/ClassBuilder.mm rename to NativeScript/ffi/objc/napi/ClassBuilder.mm diff --git a/NativeScript/ffi/napi/ClassMember.h b/NativeScript/ffi/objc/napi/ClassMember.h similarity index 100% rename from NativeScript/ffi/napi/ClassMember.h rename to NativeScript/ffi/objc/napi/ClassMember.h diff --git a/NativeScript/ffi/napi/ClassMember.mm b/NativeScript/ffi/objc/napi/ClassMember.mm similarity index 98% rename from NativeScript/ffi/napi/ClassMember.mm rename to NativeScript/ffi/objc/napi/ClassMember.mm index e7bc296a6..a18a566b8 100644 --- a/NativeScript/ffi/napi/ClassMember.mm +++ b/NativeScript/ffi/objc/napi/ClassMember.mm @@ -22,7 +22,7 @@ #include "Util.h" #include "Block.h" #include "Class.h" -#include "runtime/NativeScriptException.h" +#include "runtime/apple/NativeScriptException.h" #include "js_native_api.h" #include "js_native_api_types.h" #include "node_api_util.h" @@ -407,8 +407,13 @@ inline bool objcNativeCall(napi_env env, Cif* cif, id self, bool classMethod, bool isStret = cif->returnType->type->size > 16 && cif->returnType->type->type == FFI_TYPE_STRUCT; #endif + NapiNativeCallbackExceptionCapture callbackException; + ScopedNapiNativeCallbackExceptionCapture callbackExceptionCapture( + &callbackException); + @try { if (!supercall) { + bool preparedInvoked = false; if (cif != nullptr && cif->signatureHash != 0) { if (descriptor != nullptr && (!descriptor->dispatchLookupCached || @@ -432,22 +437,24 @@ inline bool objcNativeCall(napi_env env, Cif* cif, id self, bool classMethod, if (invoker != nullptr) { NativeCallRuntimeUnlockScope unlockRuntime(env); invoker((void*)objc_msgSend, avalues, rvalue); - return true; + preparedInvoked = true; } } + if (!preparedInvoked) { #if defined(__x86_64__) - if (isStret) { - NativeCallRuntimeUnlockScope unlockRuntime(env); - ffi_call(&cif->cif, FFI_FN(objc_msgSend_stret), rvalue, avalues); - } else { + if (isStret) { + NativeCallRuntimeUnlockScope unlockRuntime(env); + ffi_call(&cif->cif, FFI_FN(objc_msgSend_stret), rvalue, avalues); + } else { + NativeCallRuntimeUnlockScope unlockRuntime(env); + ffi_call(&cif->cif, FFI_FN(objc_msgSend), rvalue, avalues); + } +#else NativeCallRuntimeUnlockScope unlockRuntime(env); ffi_call(&cif->cif, FFI_FN(objc_msgSend), rvalue, avalues); - } -#else - NativeCallRuntimeUnlockScope unlockRuntime(env); - ffi_call(&cif->cif, FFI_FN(objc_msgSend), rvalue, avalues); #endif + } } else { Class superClass = classMethod ? class_getSuperclass(object_getClass((id)receiverClass)) : class_getSuperclass(receiverClass); @@ -475,6 +482,10 @@ inline bool objcNativeCall(napi_env env, Cif* cif, id self, bool classMethod, return false; } + if (rethrowNapiNativeCallbackException(env, callbackException)) { + return false; + } + return true; } diff --git a/NativeScript/ffi/objc/napi/Closure.h b/NativeScript/ffi/objc/napi/Closure.h new file mode 100644 index 000000000..d81a43f4d --- /dev/null +++ b/NativeScript/ffi/objc/napi/Closure.h @@ -0,0 +1,88 @@ +#ifndef CLOSURE_H +#define CLOSURE_H + +#include + +#include +#include +#include + +#include "MetadataReader.h" +#include "TypeConv.h" +#include "ffi.h" +#include "node_api_util.h" +#include "objc/runtime.h" + +namespace nativescript { + +class ObjCBridgeState; + +struct NapiNativeCallbackExceptionCapture { + napi_env env = nullptr; + napi_ref errorRef = nullptr; + + ~NapiNativeCallbackExceptionCapture(); + void clear(); +}; + +class ScopedNapiNativeCallbackExceptionCapture { + public: + explicit ScopedNapiNativeCallbackExceptionCapture( + NapiNativeCallbackExceptionCapture* capture); + ~ScopedNapiNativeCallbackExceptionCapture(); + + ScopedNapiNativeCallbackExceptionCapture( + const ScopedNapiNativeCallbackExceptionCapture&) = delete; + ScopedNapiNativeCallbackExceptionCapture& operator=( + const ScopedNapiNativeCallbackExceptionCapture&) = delete; + + private: + NapiNativeCallbackExceptionCapture* capture_ = nullptr; +}; + +bool recordNapiNativeCallbackException(napi_env env, napi_value error); +bool rethrowNapiNativeCallbackException( + napi_env env, NapiNativeCallbackExceptionCapture& capture); + +class Closure { + public: + static void callBlockFromMainThread(napi_env env, napi_value js_cb, + void* context, void* data); + static void destroyOnOwningThread(Closure* closure); + + Closure(napi_env env, std::string typeEncoding, bool isBlock, bool isMethod = false); + Closure(napi_env env, 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 = nullptr; + ObjCBridgeState* bridgeState = nullptr; + uint64_t bridgeStateToken = 0; + napi_ref thisConstructor; + napi_ref func = nullptr; + bool isGetter = false; + bool isSetter = false; + std::string propertyName; + SEL selector = nullptr; + napi_threadsafe_function tsfn = nullptr; + + std::thread::id jsThreadId = std::this_thread::get_id(); + CFRunLoopRef jsRunLoop = CFRunLoopGetCurrent(); + std::atomic retainCount{1}; + + ffi_cif cif; + ffi_closure* closure; + void* fnptr; + ffi_type** atypes = nullptr; // Track malloc'd atypes array + + std::shared_ptr returnType; + std::vector> argTypes; +}; + +} // namespace nativescript + +#endif /* CLOSURE_H */ diff --git a/NativeScript/ffi/napi/Closure.mm b/NativeScript/ffi/objc/napi/Closure.mm similarity index 86% rename from NativeScript/ffi/napi/Closure.mm rename to NativeScript/ffi/objc/napi/Closure.mm index ab90659ca..1e5be935b 100644 --- a/NativeScript/ffi/napi/Closure.mm +++ b/NativeScript/ffi/objc/napi/Closure.mm @@ -6,7 +6,7 @@ #include "ObjCBridge.h" #include "TypeConv.h" #include "Util.h" -#include "runtime/NativeScriptException.h" +#include "runtime/apple/NativeScriptException.h" #include "js_native_api.h" #include "js_native_api_types.h" #ifdef ENABLE_JS_RUNTIME @@ -28,6 +28,9 @@ namespace { +thread_local std::vector + gNativeCallbackExceptionCaptureStack; + inline void deleteClosureOnOwningThread(Closure* closure) { if (closure == nullptr) { return; @@ -61,6 +64,73 @@ inline void deleteClosureOnOwningThread(Closure* closure) { } // namespace +NapiNativeCallbackExceptionCapture::~NapiNativeCallbackExceptionCapture() { + clear(); +} + +void NapiNativeCallbackExceptionCapture::clear() { + if (env != nullptr && errorRef != nullptr) { + napi_delete_reference(env, errorRef); + } + env = nullptr; + errorRef = nullptr; +} + +ScopedNapiNativeCallbackExceptionCapture:: + ScopedNapiNativeCallbackExceptionCapture( + NapiNativeCallbackExceptionCapture* capture) + : capture_(capture) { + gNativeCallbackExceptionCaptureStack.push_back(capture_); +} + +ScopedNapiNativeCallbackExceptionCapture:: + ~ScopedNapiNativeCallbackExceptionCapture() { + if (!gNativeCallbackExceptionCaptureStack.empty() && + gNativeCallbackExceptionCaptureStack.back() == capture_) { + gNativeCallbackExceptionCaptureStack.pop_back(); + } +} + +bool recordNapiNativeCallbackException(napi_env env, napi_value error) { + if (env == nullptr || error == nullptr || + gNativeCallbackExceptionCaptureStack.empty()) { + return false; + } + + auto* capture = gNativeCallbackExceptionCaptureStack.back(); + if (capture == nullptr || capture->errorRef != nullptr) { + return capture != nullptr; + } + + capture->env = env; + return napi_create_reference(env, error, 1, &capture->errorRef) == napi_ok; +} + +bool rethrowNapiNativeCallbackException( + napi_env env, NapiNativeCallbackExceptionCapture& capture) { + if (env == nullptr || capture.errorRef == nullptr) { + return false; + } + + napi_value error = nullptr; + napi_ref errorRef = capture.errorRef; + capture.errorRef = nullptr; + napi_get_reference_value(env, errorRef, &error); + napi_delete_reference(env, errorRef); + capture.env = nullptr; + + if (error != nullptr) { + NativeScriptException nativeScriptException( + env, error, "JS implemented closure threw an exception"); + nativeScriptException.ReThrowToJS(env); + } else { + NativeScriptException nativeScriptException( + "Unable to obtain the error thrown by the JS implemented closure"); + nativeScriptException.ReThrowToJS(env); + } + return true; +} + void Closure::destroyOnOwningThread(Closure* closure) { deleteClosureOnOwningThread(closure); } inline bool selectorEndsWithErrorParam(SEL selector) { @@ -135,7 +205,11 @@ inline void JSCallbackInner(Closure* closure, napi_value func, napi_value thisAr napi_create_error(env, code, msg, &result); } - NativeScriptException::OnUncaughtError(env, result); + if (recordNapiNativeCallbackException(env, result)) { + napi_get_undefined(env, &result); + } else { + NativeScriptException::OnUncaughtError(env, result); + } } // Even if call was failed and result is just undefined, let's still try to @@ -251,7 +325,11 @@ void JSMethodCallback(ffi_cif* cif, void* ret, void* args[], void* data) { napi_create_error(env, code, msg, &result); } - NativeScriptException::OnUncaughtError(env, result); + if (recordNapiNativeCallbackException(env, result)) { + napi_get_undefined(env, &result); + } else { + NativeScriptException::OnUncaughtError(env, result); + } } bool shouldFree; diff --git a/NativeScript/ffi/napi/Enum.h b/NativeScript/ffi/objc/napi/Enum.h similarity index 100% rename from NativeScript/ffi/napi/Enum.h rename to NativeScript/ffi/objc/napi/Enum.h diff --git a/NativeScript/ffi/napi/Enum.mm b/NativeScript/ffi/objc/napi/Enum.mm similarity index 100% rename from NativeScript/ffi/napi/Enum.mm rename to NativeScript/ffi/objc/napi/Enum.mm diff --git a/NativeScript/ffi/napi/InlineFunctions.h b/NativeScript/ffi/objc/napi/InlineFunctions.h similarity index 100% rename from NativeScript/ffi/napi/InlineFunctions.h rename to NativeScript/ffi/objc/napi/InlineFunctions.h diff --git a/NativeScript/ffi/napi/InlineFunctions.mm b/NativeScript/ffi/objc/napi/InlineFunctions.mm similarity index 100% rename from NativeScript/ffi/napi/InlineFunctions.mm rename to NativeScript/ffi/objc/napi/InlineFunctions.mm diff --git a/NativeScript/ffi/napi/Interop.h b/NativeScript/ffi/objc/napi/Interop.h similarity index 100% rename from NativeScript/ffi/napi/Interop.h rename to NativeScript/ffi/objc/napi/Interop.h diff --git a/NativeScript/ffi/napi/Interop.mm b/NativeScript/ffi/objc/napi/Interop.mm similarity index 100% rename from NativeScript/ffi/napi/Interop.mm rename to NativeScript/ffi/objc/napi/Interop.mm diff --git a/NativeScript/ffi/napi/JSObject.h b/NativeScript/ffi/objc/napi/JSObject.h similarity index 100% rename from NativeScript/ffi/napi/JSObject.h rename to NativeScript/ffi/objc/napi/JSObject.h diff --git a/NativeScript/ffi/napi/JSObject.mm b/NativeScript/ffi/objc/napi/JSObject.mm similarity index 100% rename from NativeScript/ffi/napi/JSObject.mm rename to NativeScript/ffi/objc/napi/JSObject.mm diff --git a/NativeScript/ffi/napi/ObjCBridge.h b/NativeScript/ffi/objc/napi/ObjCBridge.h similarity index 100% rename from NativeScript/ffi/napi/ObjCBridge.h rename to NativeScript/ffi/objc/napi/ObjCBridge.h diff --git a/NativeScript/ffi/objc/napi/ObjCBridge.mm b/NativeScript/ffi/objc/napi/ObjCBridge.mm new file mode 100644 index 000000000..d3b250123 --- /dev/null +++ b/NativeScript/ffi/objc/napi/ObjCBridge.mm @@ -0,0 +1,1155 @@ +#include "ObjCBridge.h" +#include "AutoreleasePool.h" +#include "Block.h" +#include "Class.h" +#include "ClassMember.h" +#include "Enum.h" +#include "InlineFunctions.h" +#include "Interop.h" +#include "Metadata.h" +#include "MetadataReader.h" +#include "NativeScript.h" +#include "Object.h" +#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 +#include + +#ifdef EMBED_METADATA_SIZE +const unsigned char __attribute__((section("__objc_metadata,__objc_metadata"))) +#if defined(__aarch64__) +embedded_metadata[EMBED_METADATA_SIZE] = "NSMDSectionHeaderARM"; +#else +embedded_metadata[EMBED_METADATA_SIZE] = "NSMDSectionHeaderX86"; +#endif +#endif + +namespace nativescript { +namespace { +std::mutex gLiveBridgeStatesMutex; +std::unordered_map gLiveBridgeStates; +std::atomic gNextBridgeStateToken{1}; +constexpr const char* kNativePointerProperty = "__ns_native_ptr"; + +bool envFlagEnabled(const char* name) { + const char* value = std::getenv(name); + if (value == nullptr || value[0] == '\0') { + return false; + } + + return std::strcmp(value, "0") != 0 && std::strcmp(value, "false") != 0 && + std::strcmp(value, "FALSE") != 0 && std::strcmp(value, "False") != 0 && + std::strcmp(value, "off") != 0 && std::strcmp(value, "OFF") != 0 && + std::strcmp(value, "no") != 0 && std::strcmp(value, "NO") != 0; +} + +inline void deleteReferenceNow(napi_env env, napi_ref ref, bool unrefFirst) { + if (env == nullptr || ref == nullptr) { + return; + } + + if (unrefFirst) { + uint32_t remaining = 0; + napi_reference_unref(env, ref, &remaining); + } + + napi_delete_reference(env, ref); +} + +inline void deleteReferenceOnOwningThread(napi_env env, ObjCBridgeState* bridgeState, + uint64_t bridgeStateToken, napi_ref ref, + bool unrefFirst) { + if (env == nullptr || ref == nullptr) { + return; + } + + if (bridgeState == nullptr) { + deleteReferenceNow(env, ref, unrefFirst); + return; + } + + if (!IsBridgeStateLive(bridgeState, bridgeStateToken)) { + return; + } + + if (bridgeState->jsThreadId == std::this_thread::get_id()) { +#if !defined(TARGET_ENGINE_QUICKJS) + deleteReferenceNow(env, ref, unrefFirst); + return; +#endif + } + + CFRunLoopRef runLoop = bridgeState->jsRunLoop; + if (runLoop == nullptr) { + runLoop = CFRunLoopGetMain(); + } + + if (runLoop == nullptr) { + if (bridgeState->jsThreadId == std::this_thread::get_id()) { + deleteReferenceNow(env, ref, unrefFirst); + } + return; + } + + CFRetain(runLoop); + CFRunLoopPerformBlock(runLoop, kCFRunLoopCommonModes, ^{ + if (IsBridgeStateLive(bridgeState, bridgeStateToken)) { + deleteReferenceNow(env, ref, unrefFirst); + } + CFRelease(runLoop); + }); + CFRunLoopWakeUp(runLoop); +} + +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 DeleteReferenceOnOwningThread(napi_env env, ObjCBridgeState* bridgeState, + uint64_t bridgeStateToken, napi_ref ref) { + deleteReferenceOnOwningThread(env, bridgeState, bridgeStateToken, ref, false); +} + +void ReleaseAndDeleteReferenceOnOwningThread(napi_env env, ObjCBridgeState* bridgeState, + uint64_t bridgeStateToken, napi_ref ref) { + deleteReferenceOnOwningThread(env, bridgeState, bridgeStateToken, ref, true); +} + +bool PostFinalizer(napi_env env, napi_finalize finalize_cb, void* finalize_data, + void* finalize_hint) { + if (env == nullptr || finalize_cb == nullptr) { + return false; + } + + ObjCBridgeState* bridgeState = ObjCBridgeState::InstanceData(env); + if (bridgeState != nullptr && bridgeState->jsThreadId == std::this_thread::get_id()) { +#if !defined(TARGET_ENGINE_QUICKJS) + finalize_cb(env, finalize_data, finalize_hint); + return true; +#endif + } + + CFRunLoopRef runLoop = bridgeState != nullptr ? bridgeState->jsRunLoop : CFRunLoopGetMain(); + if (runLoop == nullptr) { + return false; + } + + if (bridgeState == nullptr && [NSThread isMainThread]) { +#if !defined(TARGET_ENGINE_QUICKJS) + finalize_cb(env, finalize_data, finalize_hint); + return true; +#endif + } + + CFRetain(runLoop); + CFRunLoopPerformBlock(runLoop, kCFRunLoopCommonModes, ^{ + finalize_cb(env, finalize_data, finalize_hint); + CFRelease(runLoop); + }); + CFRunLoopWakeUp(runLoop); + return true; +} + +void finalize_bridge_data(napi_env env, void* data, void* hint) { + auto bridgeState = (ObjCBridgeState*)data; + delete bridgeState; +} + +MDMetadataReader* loadMetadataFromFile(const char* metadata_path) { + if (metadata_path == nullptr) { + metadata_path = "metadata.nsmd"; + } + + auto f = fopen(metadata_path == nullptr ? "metadata.nsmd" : metadata_path, "r"); + if (f == nullptr) { + fprintf(stderr, "metadata.nsmd not found\n"); + exit(1); + } + fseek(f, 0, SEEK_END); + auto size = ftell(f); + fseek(f, 0, SEEK_SET); + auto buffer = (uint8_t*)malloc(size); + fread(buffer, 1, size, f); + fclose(f); + 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; + } + if (valueType == napi_function) { + return true; + } + + if (valueType != napi_object) { + return false; + } + + napi_value instance = nullptr; + napi_status status = napi_new_instance(env, value, 0, nullptr, &instance); + if (status == napi_ok) { + return true; + } + + 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); + } + return false; +} + +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) { + 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 !== "undefined") { + globalObject.UIColor = globalObject.NSColor; + } + + const colorCtor = globalObject.UIColor || globalObject.NSColor; + if (!colorCtor || !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(env, std::string("v"), true); + 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); + trackedObjectLiveness = [[NSMutableSet alloc] init]; + + 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"); + metadata = new MDMetadataReader((void*)embedded_metadata); +#else + // NSLog(@"Using metadata from pointer: %p", metadata_ptr); + metadata = new MDMetadataReader((void*)metadata_ptr); +#endif + } else { +#ifdef EMBED_METADATA_SIZE + if (metadata_path != nullptr) { + // NSLog(@"Loading metadata from file: %s", metadata_path); + metadata = loadMetadataFromFile(metadata_path); + } else { + // NSLog(@"Using embedded metadata"); + metadata = new MDMetadataReader((void*)embedded_metadata); + } +#else + unsigned long segmentSize = 0; + auto segmentData = getsegmentdata((const mach_header_64*)_dyld_get_image_header(0), + "__objc_metadata", &segmentSize); + if (segmentData != nullptr) { + metadata = new MDMetadataReader(segmentData); + } else { + metadata = loadMetadataFromFile(metadata_path); + } +#endif + } + + // objc_autoreleasePool = objc_autoreleasePoolPush(); +} + +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) { + ObjCBridgeState::releaseRoundTripEntry(env, entry.second); + } + } + roundTripCacheFrames.clear(); + + for (auto& entry : recentRoundTripCache) { + ObjCBridgeState::releaseRoundTripEntry(env, entry.second); + } + recentRoundTripCache.clear(); + + for (auto& entry : handleObjectRefs) { + if (entry.second.ownsRef) { + deleteRef(entry.second.ref); + } + } + handleObjectRefs.clear(); + + for (auto& entry : recentObjectWrappers) { + deleteRef(entry.ref); + } + recentObjectWrappers.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; + } + cifs.clear(); + + for (auto& pair : mdMethodSignatureCache) { + delete pair.second; + } + mdMethodSignatureCache.clear(); + + for (auto& pair : mdBlockSignatureCache) { + delete pair.second; + } + mdBlockSignatureCache.clear(); + + // Clean up ObjCClass objects + for (auto& pair : classes) { + delete pair.second; + } + classes.clear(); + + // Clean up ObjCProtocol objects + for (auto& pair : protocols) { + delete pair.second; + } + protocols.clear(); + + // Clean up StructInfo objects + for (auto& pair : structInfoCache) { + delete pair.second; + } + structInfoCache.clear(); + + // Clean up CFunction objects + for (auto& pair : cFunctionCache) { + delete pair.second; + } + cFunctionCache.clear(); + + for (auto& pair : mdFunctionSignatureCache) { + delete pair.second; + } + mdFunctionSignatureCache.clear(); + + NSMutableSet* trackedObjectTable = static_cast(trackedObjectLiveness); + trackedObjectLiveness = nullptr; + [trackedObjectTable release]; + + // if (objc_autoreleasePool != nullptr) + // objc_autoreleasePoolPop(objc_autoreleasePool); + + delete metadata; + dlclose(self_dl); +} + +napi_value ObjCBridgeState::proxyNativeObject(napi_env env, napi_value object, id nativeObject) { + NAPI_PREAMBLE + + napi_value result = object; + const bool nativeIsArray = [nativeObject isKindOfClass:NSArray.class]; + bool shouldProxyArray = nativeIsArray && !envFlagEnabled("NS_DISABLE_NAPI_ARRAY_PROXY"); + if (shouldProxyArray) { + napi_value factory = get_ref_value(env, createNativeProxy); + napi_value transferOwnershipFunc = get_ref_value(env, this->transferOwnershipToNative); + napi_value global; + napi_value args[3] = {object, nullptr, transferOwnershipFunc}; + napi_get_boolean(env, true, &args[1]); + napi_get_global(env, &global); + napi_call_function(env, global, factory, 3, args, &result); + } + + napi_value nativePointer = Pointer::create(env, nativeObject); + if (nativePointer != nullptr) { + napi_set_named_property(env, result, kNativePointerProperty, nativePointer); + } + napi_wrap(env, result, nativeObject, nullptr, nullptr, nullptr); + + napi_ref ref = nullptr; + auto* finalizerContext = new JSObjectFinalizerContext{ + .bridgeState = this, + .bridgeStateToken = lifetimeToken, + .object = nativeObject, + .ref = nullptr, + }; + NAPI_GUARD( + napi_add_finalizer(env, result, finalizerContext, finalize_objc_object, nullptr, &ref)) { + delete finalizerContext; + NAPI_THROW_LAST_ERROR + return nullptr; + } + finalizerContext->ref = ref; + + storeObjectRef(nativeObject, ref); + cacheHandleObjectRef(env, nativeObject, ref); + cacheRecentObjectWrapper(env, nativeObject, result); + attachObjectLifecycleAssociation(env, nativeObject); + trackObject(nativeObject); + + return result; +} + +void ObjCBridgeState::trackObject(id object) noexcept { + if (object == nil) { + return; + } + + NSMutableSet* trackedObjectTable = static_cast(trackedObjectLiveness); + if (trackedObjectTable == nil) { + return; + } + + NSNumber* objectKey = [NSNumber numberWithUnsignedLongLong:NormalizeHandleKey((void*)object)]; + std::lock_guard lock(objectRefsMutex); + [trackedObjectTable addObject:objectKey]; +} + +bool ObjCBridgeState::isTrackedObjectAlive(id object) const noexcept { + if (object == nil) { + return false; + } + + NSMutableSet* trackedObjectTable = static_cast(trackedObjectLiveness); + if (trackedObjectTable == nil) { + return false; + } + + NSNumber* objectKey = [NSNumber numberWithUnsignedLongLong:NormalizeHandleKey((void*)object)]; + std::lock_guard lock(objectRefsMutex); + return [trackedObjectTable containsObject:objectKey]; +} + +} // namespace nativescript + +using namespace nativescript; + +NAPI_FUNCTION(getArrayBuffer) { + NAPI_CALLBACK_BEGIN(2) + + void* ptr = Pointer::unwrap(env, argv[0])->data; + int64_t length; + napi_get_value_int64(env, argv[1], &length); + + napi_value arrayBuffer; + if (length < 0) { + napi_throw_error(env, nullptr, "Invalid ArrayBuffer length"); + return nullptr; + } + + napi_create_external_arraybuffer(env, ptr, static_cast(length), nullptr, nullptr, + &arrayBuffer); + + return arrayBuffer; +} + +NAPI_FUNCTION(init) { + NAPI_CALLBACK_BEGIN(1) + napi_valuetype type; + napi_typeof(env, argv[0], &type); + const char* metadata_path = nullptr; + if (type == napi_string) { + size_t len; + napi_get_value_string_utf8(env, argv[0], nullptr, 0, &len); + metadata_path = (char*)malloc(len + 1); + napi_get_value_string_utf8(env, argv[0], (char*)metadata_path, len + 1, &len); + } + nativescript_init(env, metadata_path, nullptr); + return nullptr; +} + +NAPI_EXPORT NAPI_MODULE_REGISTER { + const napi_property_descriptor property = NAPI_FUNCTION_DESC(init); + napi_define_properties(env, exports, 1, &property); + return exports; +} + +NAPI_EXPORT void nativescript_init(void* _env, const char* metadata_path, + const void* metadata_ptr) { + napi_env env = (napi_env)_env; + + ObjCBridgeState* bridgeState = new ObjCBridgeState(env, metadata_path, metadata_ptr); + + napi_value objc; + napi_create_object(env, &objc); + + const napi_property_descriptor objcProperties[] = { + NAPI_FUNCTION_DESC(registerClass), NAPI_FUNCTION_DESC(registerBlock), + NAPI_FUNCTION_DESC(import), NAPI_FUNCTION_DESC(autoreleasepool), + NAPI_FUNCTION_DESC(getArrayBuffer), + }; + + napi_define_properties(env, objc, 5, objcProperties); + + napi_value global; + napi_get_global(env, &global); + + const napi_property_descriptor globalProperties[] = {{ + .utf8name = "objc", + .method = nullptr, + .getter = nullptr, + .setter = nullptr, + .value = objc, + .attributes = napi_enumerable, + .data = nullptr, + }, + { + .utf8name = "ObjectRef", + .method = nullptr, + .getter = nullptr, + .setter = nullptr, + .value = defineObjectRefClass(env), + .attributes = napi_enumerable, + .data = nullptr, + }, + { + .utf8name = "NativeClass", + .method = JS_registerClass, + .getter = nullptr, + .setter = nullptr, + .value = nullptr, + .attributes = napi_enumerable, + .data = nullptr, + }}; + + napi_define_properties(env, global, 3, globalProperties); + + setupObjCClassDecorator(env); + + initProxyFactory(env, bridgeState); + initFastEnumeratorIteratorFactory(env, bridgeState); + + registerInterop(env, global); + registerInlineFunctions(env); + + bridgeState->registerVarGlobals(env, global); + bridgeState->registerEnumGlobals(env, global); + bridgeState->registerStructGlobals(env, global); + bridgeState->registerUnionGlobals(env, global); + bridgeState->registerFunctionGlobals(env, global); + bridgeState->registerClassGlobals(env, global); + bridgeState->registerProtocolGlobals(env, global); + registerLegacyCompatGlobals(env, global, bridgeState); +} diff --git a/NativeScript/ffi/napi/Object.h b/NativeScript/ffi/objc/napi/Object.h similarity index 100% rename from NativeScript/ffi/napi/Object.h rename to NativeScript/ffi/objc/napi/Object.h diff --git a/NativeScript/ffi/napi/Object.mm b/NativeScript/ffi/objc/napi/Object.mm similarity index 100% rename from NativeScript/ffi/napi/Object.mm rename to NativeScript/ffi/objc/napi/Object.mm diff --git a/NativeScript/ffi/napi/ObjectRef.h b/NativeScript/ffi/objc/napi/ObjectRef.h similarity index 100% rename from NativeScript/ffi/napi/ObjectRef.h rename to NativeScript/ffi/objc/napi/ObjectRef.h diff --git a/NativeScript/ffi/napi/ObjectRef.mm b/NativeScript/ffi/objc/napi/ObjectRef.mm similarity index 100% rename from NativeScript/ffi/napi/ObjectRef.mm rename to NativeScript/ffi/objc/napi/ObjectRef.mm diff --git a/NativeScript/ffi/napi/Protocol.h b/NativeScript/ffi/objc/napi/Protocol.h similarity index 100% rename from NativeScript/ffi/napi/Protocol.h rename to NativeScript/ffi/objc/napi/Protocol.h diff --git a/NativeScript/ffi/napi/Protocol.mm b/NativeScript/ffi/objc/napi/Protocol.mm similarity index 100% rename from NativeScript/ffi/napi/Protocol.mm rename to NativeScript/ffi/objc/napi/Protocol.mm diff --git a/NativeScript/ffi/objc/napi/SignatureDispatch.h b/NativeScript/ffi/objc/napi/SignatureDispatch.h new file mode 100644 index 000000000..5d4df5093 --- /dev/null +++ b/NativeScript/ffi/objc/napi/SignatureDispatch.h @@ -0,0 +1,138 @@ +#ifndef NS_FFI_NAPI_SIGNATURE_DISPATCH_H +#define NS_FFI_NAPI_SIGNATURE_DISPATCH_H + +#include + +#include "Cif.h" +#include "ffi/objc/shared/SignatureDispatchCore.h" +#include "js_native_api.h" + +namespace nativescript { + +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 ObjCNapiDispatchEntry { + uint64_t dispatchId; + ObjCNapiInvoker invoker; +}; + +struct CFunctionNapiDispatchEntry { + uint64_t dispatchId; + CFunctionNapiInvoker invoker; +}; + +} // namespace nativescript + +#ifndef NS_GSD_BACKEND_NAPI +#define NS_GSD_BACKEND_NAPI 1 +#endif + +#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 + +#ifndef NS_GSD_BACKEND_HERMES +#define NS_GSD_BACKEND_HERMES 0 +#endif + +#ifndef NS_GSD_BACKEND_PREPARED +#define NS_GSD_BACKEND_PREPARED 0 +#endif + +#define NS_REQUIRES_GENERATED_SIGNATURE_DISPATCH \ + (NS_GSD_BACKEND_HERMES || NS_GSD_BACKEND_NAPI || NS_GSD_BACKEND_PREPARED) + +#if defined(__has_include) +#if __has_include("GeneratedSignatureDispatch.inc") +#include "GeneratedSignatureDispatch.inc" +#elif NS_REQUIRES_GENERATED_SIGNATURE_DISPATCH +#error GeneratedSignatureDispatch.inc is required when generated signature dispatch is enabled. +#endif +#elif NS_REQUIRES_GENERATED_SIGNATURE_DISPATCH +#error __has_include is required to validate GeneratedSignatureDispatch.inc. +#endif + +#if NS_REQUIRES_GENERATED_SIGNATURE_DISPATCH && !NS_HAS_GENERATED_SIGNATURE_DISPATCH +#error GeneratedSignatureDispatch.inc did not enable this generated signature dispatch backend. +#endif + +#if NS_GSD_BACKEND_NAPI && !NS_HAS_GENERATED_SIGNATURE_NAPI_DISPATCH +#error GeneratedSignatureDispatch.inc did not enable Node-API generated signature dispatch. +#endif + +#if !NS_HAS_GENERATED_SIGNATURE_DISPATCH +namespace nativescript { +inline constexpr ObjCDispatchEntry kGeneratedObjCDispatchEntries[] = { + {0, nullptr}}; +inline constexpr CFunctionDispatchEntry kGeneratedCFunctionDispatchEntries[] = { + {0, nullptr}}; +inline constexpr BlockDispatchEntry kGeneratedBlockDispatchEntries[] = { + {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 { + +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 BlockPreparedInvoker lookupBlockPreparedInvoker(uint64_t dispatchId) { + if (!isGeneratedDispatchEnabled()) { + return nullptr; + } + return lookupDispatchInvoker( + kGeneratedBlockDispatchEntries, 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_FFI_NAPI_SIGNATURE_DISPATCH_H diff --git a/NativeScript/ffi/napi/Struct.h b/NativeScript/ffi/objc/napi/Struct.h similarity index 100% rename from NativeScript/ffi/napi/Struct.h rename to NativeScript/ffi/objc/napi/Struct.h diff --git a/NativeScript/ffi/napi/Struct.mm b/NativeScript/ffi/objc/napi/Struct.mm similarity index 100% rename from NativeScript/ffi/napi/Struct.mm rename to NativeScript/ffi/objc/napi/Struct.mm diff --git a/NativeScript/ffi/napi/TypeConv.h b/NativeScript/ffi/objc/napi/TypeConv.h similarity index 100% rename from NativeScript/ffi/napi/TypeConv.h rename to NativeScript/ffi/objc/napi/TypeConv.h diff --git a/NativeScript/ffi/napi/TypeConv.mm b/NativeScript/ffi/objc/napi/TypeConv.mm similarity index 100% rename from NativeScript/ffi/napi/TypeConv.mm rename to NativeScript/ffi/objc/napi/TypeConv.mm diff --git a/NativeScript/ffi/napi/Util.h b/NativeScript/ffi/objc/napi/Util.h similarity index 100% rename from NativeScript/ffi/napi/Util.h rename to NativeScript/ffi/objc/napi/Util.h diff --git a/NativeScript/ffi/napi/Util.mm b/NativeScript/ffi/objc/napi/Util.mm similarity index 100% rename from NativeScript/ffi/napi/Util.mm rename to NativeScript/ffi/objc/napi/Util.mm diff --git a/NativeScript/ffi/napi/Variable.h b/NativeScript/ffi/objc/napi/Variable.h similarity index 100% rename from NativeScript/ffi/napi/Variable.h rename to NativeScript/ffi/objc/napi/Variable.h diff --git a/NativeScript/ffi/napi/Variable.mm b/NativeScript/ffi/objc/napi/Variable.mm similarity index 100% rename from NativeScript/ffi/napi/Variable.mm rename to NativeScript/ffi/objc/napi/Variable.mm diff --git a/NativeScript/ffi/napi/node_api_util.h b/NativeScript/ffi/objc/napi/node_api_util.h similarity index 100% rename from NativeScript/ffi/napi/node_api_util.h rename to NativeScript/ffi/objc/napi/node_api_util.h diff --git a/NativeScript/ffi/objc/quickjs/NativeApiQuickJS.h b/NativeScript/ffi/objc/quickjs/NativeApiQuickJS.h new file mode 100644 index 000000000..c26fe8bfa --- /dev/null +++ b/NativeScript/ffi/objc/quickjs/NativeApiQuickJS.h @@ -0,0 +1,21 @@ +#ifndef NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_H +#define NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_H + +#include "ffi/objc/shared/NativeApiBackendConfig.h" +#include "quickjs.h" + +namespace nativescript { + +using NativeApiScheduler = NativeApiBackendScheduler; +using NativeApiConfig = NativeApiBackendConfig; + +void InstallNativeApi(JSContext* context, + const NativeApiConfig& config = + NativeApiConfig{}); + +} // namespace nativescript + +extern "C" void NativeScriptInstallNativeApi(JSContext* context, + const char* metadataPath); + +#endif // NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_H diff --git a/NativeScript/ffi/objc/quickjs/NativeApiQuickJS.mm b/NativeScript/ffi/objc/quickjs/NativeApiQuickJS.mm new file mode 100644 index 000000000..8f234d1bd --- /dev/null +++ b/NativeScript/ffi/objc/quickjs/NativeApiQuickJS.mm @@ -0,0 +1,75 @@ +#include "NativeApiQuickJS.h" + +#ifdef TARGET_ENGINE_QUICKJS + +#include "NativeApiQuickJSRuntime.h" +#include "SignatureDispatch.h" + +namespace nativescript { + +namespace { + +using nativescript::engine::Array; +using nativescript::engine::ArrayBuffer; +using nativescript::engine::BigInt; +using nativescript::engine::Function; +using nativescript::engine::HostObject; +using nativescript::engine::MutableBuffer; +using nativescript::engine::Object; +using nativescript::engine::PropNameID; +using nativescript::engine::Runtime; +using nativescript::engine::String; +using nativescript::engine::StringBuffer; +using nativescript::engine::Value; +using nativescript::engine::JSError; +using metagen::MDMemberFlag; +using metagen::MDMetadataReader; +using metagen::MDSectionOffset; +using metagen::MDTypeKind; + +// clang-format off +#define NATIVESCRIPT_NATIVE_API_HOST_EXPLICIT_OVERRIDE 1 +#define NATIVESCRIPT_NATIVE_API_BACKEND_NAME "quickjs" +#include "../shared/bridge/ObjCBridge.mm" +// clang-format on + +#define NATIVESCRIPT_NATIVE_API_HAS_ENGINE_LAZY_GLOBALS 1 +#define NATIVESCRIPT_NATIVE_API_RETAIN_RUNTIME 1 +#define NATIVESCRIPT_NATIVE_API_HAS_ENGINE_SELECTOR_GROUP_FUNCTION 1 + +#include "NativeApiQuickJSRuntimeSupport.mm" + +// clang-format off +#include "../shared/bridge/HostObjects.mm" +#include "../shared/bridge/Callbacks.mm" +#include "../shared/bridge/TypeConv.mm" +#include "../shared/bridge/Invocation.mm" +#include "../shared/bridge/ClassBuilder.mm" +#include "../shared/bridge/HostObject.mm" +// clang-format on + +#include "NativeApiQuickJSSelectorGroups.mm" + +} // namespace + +#include "../shared/bridge/Install.mm" + +void InstallNativeApi(JSContext* context, const NativeApiConfig& config) { + if (context == nullptr) { + return; + } + auto state = engine::quickjsengine::stateForContext(context); + nativescript::engine::Runtime runtime(state); + engine::quickjsengine::ensureClasses(runtime); + InstallNativeApi(runtime, config); +} + +} // namespace nativescript + +extern "C" void NativeScriptInstallNativeApi(JSContext* context, const char* metadataPath) { + nativescript::NativeApiConfig config; + config.metadataPath = metadataPath; + nativescript::InstallNativeApi(context, config); +} + +#endif // TARGET_ENGINE_QUICKJS diff --git a/NativeScript/ffi/objc/quickjs/NativeApiQuickJSGsd.mm b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSGsd.mm new file mode 100644 index 000000000..434920110 --- /dev/null +++ b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSGsd.mm @@ -0,0 +1,301 @@ +// --- GSD (Generated Signature Dispatch) for QuickJS --- +// GsdObjCContext is the engine-neutral interface the generated invokers use: +// it reads JS arguments and writes the JS return value via the QuickJS API. +// Readers require an actual JS number so coercion edge cases defer to the +// fully correct generic path. +struct GsdObjCContext; +using ObjCGsdInvoker = bool (*)(GsdObjCContext&); +struct ObjCGsdDispatchEntry { + uint64_t dispatchId; + ObjCGsdInvoker invoker; +}; + +struct GsdObjCContext { + Runtime& runtime; + const std::shared_ptr& bridge; + id self; + SEL selector; + JSContext* context; + JSValueConst* arguments; + const NativeApiType& returnType; + JSValue result = JS_UNDEFINED; + const Value* valueArguments = nullptr; + bool materializeValueResult = false; + Value valueResult = Value::undefined(); + + template + void invokeNative(Invocation&& invocation) { + performGeneratedObjCInvocation(runtime, bridge, [&]() { invocation(); }); + } + + bool readNumber(size_t i, double* out) { + if (valueArguments != nullptr) { + const Value& v = valueArguments[i]; + if (!v.isNumber()) return false; + *out = v.getNumber(); + return true; + } + JSValueConst v = arguments[i]; + if (!JS_IsNumber(v)) return false; + return quickJSNumberValue(context, v, out); + } + bool readBool(size_t i, uint8_t* out) { + if (valueArguments != nullptr) { + const Value& v = valueArguments[i]; + if (!v.isBool()) return false; + *out = v.getBool() ? 1 : 0; + return true; + } + JSValueConst v = arguments[i]; + if (!JS_IsBool(v)) return false; + *out = JS_ToBool(context, v) != 0 ? 1 : 0; + return true; + } + template + bool readSigned(size_t i, T* out) { + double tmp = 0; + if (!readNumber(i, &tmp)) return false; + *out = static_cast(tmp); + return true; + } + template + bool readUnsigned(size_t i, T* out) { + double tmp = 0; + if (!readNumber(i, &tmp)) return false; + *out = static_cast(tmp); + return true; + } + bool readFloat(size_t i, float* out) { + double tmp = 0; + if (!readNumber(i, &tmp)) return false; + *out = static_cast(tmp); + return true; + } + bool readDouble(size_t i, double* out) { return readNumber(i, out); } + bool readSelector(size_t i, SEL* out) { + if (valueArguments != nullptr) { + return readFastEngineSelectorArgument(runtime, valueArguments[i], out); + } + return readQuickJSEngineSelectorArgument(runtime, arguments[i], out); + } + bool readClass(size_t i, Class* out) { + if (valueArguments != nullptr) { + Class cls = classFromEngineValue(runtime, valueArguments[i]); + if (cls == Nil) return false; + *out = cls; + return true; + } + if (auto* c = quickJSHostObjectRaw( + runtime, arguments[i])) { + *out = c->nativeClass(); + return true; + } + Class cls = quickJSNativeClassArgument(runtime, arguments[i]); + if (cls == Nil) return false; + *out = cls; + return true; + } + bool readObject(size_t i, id* out) { + if (valueArguments != nullptr) { + const Value& v = valueArguments[i]; + if (v.isNull() || v.isUndefined()) { + *out = nil; + return true; + } + if (!v.isObject()) return false; + Object object = v.asObject(runtime); + if (object.isHostObject(runtime)) { + *out = object.getHostObject(runtime)->object(); + return true; + } + if (object.isHostObject(runtime)) { + *out = static_cast( + object.getHostObject(runtime)->nativeClass()); + return true; + } + Class cls = classFromEngineValue(runtime, v); + if (cls != Nil) { + *out = static_cast(cls); + return true; + } + if (object.isHostObject(runtime)) { + *out = static_cast( + object.getHostObject(runtime) + ->nativeProtocol()); + return true; + } + return false; + } + JSValueConst v = arguments[i]; + if (JS_IsNull(v) || JS_IsUndefined(v)) { + *out = nil; + return true; + } + if (auto* h = quickJSHostObjectRaw(runtime, v)) { + *out = h->object(); + return true; + } + if (auto* c = quickJSHostObjectRaw(runtime, v)) { + *out = static_cast(c->nativeClass()); + return true; + } + if (JS_IsObject(v)) { + Class cls = quickJSNativeClassArgument(runtime, v); + if (cls != Nil) { + *out = static_cast(cls); + return true; + } + } + if (auto* p = + quickJSHostObjectRaw(runtime, v)) { + *out = static_cast(p->nativeProtocol()); + return true; + } + return false; + } + + void setVoid() { + if (materializeValueResult) { + valueResult = Value::undefined(); + return; + } + result = JS_UNDEFINED; + } + void setBool(bool v) { + if (materializeValueResult) { + valueResult = Value(v); + return; + } + result = JS_NewBool(context, v); + } + void setInt32(int32_t v) { + if (materializeValueResult) { + valueResult = Value(static_cast(v)); + return; + } + result = JS_NewInt32(context, v); + } + void setUInt32(uint32_t v) { + if (materializeValueResult) { + valueResult = Value(static_cast(v)); + return; + } + result = JS_NewUint32(context, v); + } + void setUInt16(uint16_t v) { + if (materializeValueResult) { + if (v >= 32 && v <= 126) { + valueResult = makeString(runtime, std::string(1, static_cast(v))); + } else { + valueResult = Value(static_cast(v)); + } + return; + } + if (v >= 32 && v <= 126) { + char buffer[2] = {static_cast(v), '\0'}; + result = JS_NewStringLen(context, buffer, 1); + } else { + result = JS_NewUint32(context, v); + } + } + void setInt64(int64_t v) { + if (materializeValueResult) { + valueResult = signedInteger64ToEngineValue(runtime, v); + return; + } + result = quickJSInteger64Value(runtime, v); + } + void setUInt64(uint64_t v) { + if (materializeValueResult) { + valueResult = unsignedInteger64ToEngineValue(runtime, v); + return; + } + result = quickJSUnsignedInteger64Value(runtime, v); + } + void setDouble(double v) { + if (materializeValueResult) { + valueResult = Value(v); + return; + } + result = JS_NewFloat64(context, v); + } + void setSelector(SEL v) { + const char* name = v != nullptr ? sel_getName(v) : nullptr; + if (materializeValueResult) { + valueResult = name != nullptr ? makeString(runtime, name) : Value::null(); + return; + } + result = name == nullptr ? JS_NULL : JS_NewString(context, name); + } + void setClass(Class v) { + if (materializeValueResult) { + if (v == nil) { + valueResult = Value::null(); + return; + } + const char* name = class_getName(v); + NativeApiSymbol symbol{ + .kind = NativeApiSymbolKind::Class, + .offset = MD_SECTION_OFFSET_NULL, + .name = name != nullptr ? name : "", + .runtimeName = name != nullptr ? name : "", + }; + if (const NativeApiSymbol* found = bridge->findClass(symbol.name)) { + symbol = *found; + } + valueResult = makeNativeClassValue(runtime, bridge, std::move(symbol)); + return; + } + if (v == nil) { + result = JS_NULL; + return; + } + const char* name = class_getName(v); + NativeApiSymbol symbol{ + .kind = NativeApiSymbolKind::Class, + .offset = MD_SECTION_OFFSET_NULL, + .name = name != nullptr ? name : "", + .runtimeName = name != nullptr ? name : "", + }; + if (const NativeApiSymbol* found = bridge->findClass(symbol.name)) { + symbol = *found; + } + Value classValue = makeNativeClassValue(runtime, bridge, std::move(symbol)); + result = classValue.local(runtime); + } + void setObject(id obj) { + if (materializeValueResult) { + valueResult = convertNativeReturnValue(runtime, bridge, returnType, &obj); + return; + } + result = setQuickJSEngineObjectReturn(runtime, bridge, returnType, obj); + } +}; + +// Close the anonymous namespace so the generated dispatch table lives in +// namespace nativescript; GsdObjCContext/ObjCGsdDispatchEntry stay reachable +// via the unnamed namespace's implicit using-directive. +} // namespace (temporary close for GSD .inc) + +#if defined(__has_include) +#if __has_include("GeneratedGsdSignatureDispatch.inc") +#include "GeneratedGsdSignatureDispatch.inc" +#endif +#endif + +#ifndef NS_HAS_GENERATED_SIGNATURE_GSD_DISPATCH +inline constexpr ObjCGsdDispatchEntry kGeneratedObjCGsdDispatchEntries[] = { + {0, nullptr}}; +#endif + +ObjCGsdInvoker lookupObjCGsdInvoker(uint64_t dispatchId) { + if (!isGeneratedDispatchEnabled()) { + return nullptr; + } + return lookupDispatchInvoker( + kGeneratedObjCGsdDispatchEntries, dispatchId); +} + +namespace { // reopen anonymous namespace + +// --- End GSD --- diff --git a/NativeScript/ffi/objc/quickjs/NativeApiQuickJSHostObjects.mm b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSHostObjects.mm new file mode 100644 index 000000000..5917fd686 --- /dev/null +++ b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSHostObjects.mm @@ -0,0 +1,385 @@ +#include "NativeApiQuickJSRuntime.h" + +#ifdef TARGET_ENGINE_QUICKJS + +namespace nativescript { +class NativeApiObjectHostObject; +} + +namespace nativescript { +namespace engine { + +namespace quickjsengine { + +JSClassID gHostClassId = 0; +JSClassID gFunctionClassId = 0; + +namespace { +std::mutex& runtimeStatesMutex() { + static auto* mutex = new std::mutex(); + return *mutex; +} + +std::unordered_map>& runtimeStates() { + static auto* states = new std::unordered_map>(); + return *states; +} +} // namespace + +template +class StackValueArray { + public: + explicit StackValueArray(size_t count) : count_(count) { + if (count_ > InlineCount) { + values_ = static_cast(::operator new(sizeof(Value) * count_)); + } else { + values_ = reinterpret_cast(inlineStorage_); + } + } + + ~StackValueArray() { + for (size_t i = 0; i < constructed_; i++) { + values_[i].~Value(); + } + if (count_ > InlineCount) { + ::operator delete(values_); + } + } + + StackValueArray(const StackValueArray&) = delete; + StackValueArray& operator=(const StackValueArray&) = delete; + + void emplace(size_t index, Value&& value) { + new (&values_[index]) Value(std::move(value)); + constructed_++; + } + + Value* data() { return count_ == 0 ? nullptr : values_; } + size_t size() const { return count_; } + + private: + size_t count_ = 0; + size_t constructed_ = 0; + Value* values_ = nullptr; + alignas(Value) unsigned char inlineStorage_[sizeof(Value) * InlineCount]; +}; + +std::shared_ptr stateForContext(JSContext* context) { + std::lock_guard lock(runtimeStatesMutex()); + auto& states = runtimeStates(); + auto it = states.find(context); + if (it != states.end()) { + return it->second; + } + auto state = std::make_shared(context); + states[context] = state; + return state; +} + +static bool isNativeInstancePrototypeBypassExcluded(JSContext* ctx, + JSAtom atom) { + const char* name = JS_AtomToCString(ctx, atom); + if (name == nullptr) { + return true; + } + bool excluded = + std::strcmp(name, "kind") == 0 || + std::strcmp(name, "className") == 0 || + std::strcmp(name, "nativeAddress") == 0 || + std::strcmp(name, "class") == 0 || + std::strcmp(name, "constructor") == 0 || + std::strcmp(name, "super") == 0 || + std::strcmp(name, "invoke") == 0 || + std::strcmp(name, "send") == 0 || + std::strcmp(name, "takeRetainedValue") == 0 || + std::strcmp(name, "takeUnretainedValue") == 0 || + std::strcmp(name, "toString") == 0; + JS_FreeCString(ctx, name); + return excluded; +} + +static void freePropertyDescriptor(JSContext* ctx, + JSPropertyDescriptor& desc) { + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + JS_FreeValue(ctx, desc.value); +} + +static JSValue nativePrototypeProperty(JSContext* ctx, JSValueConst obj, + JSAtom atom, JSValueConst receiver, + HostObjectHolder* holder, + bool* handled) { + *handled = false; + if (holder == nullptr || + holder->typeToken != hostObjectTypeToken()) { + return JS_UNDEFINED; + } + + JSValue prototype = JS_GetPrototype(ctx, obj); + if (JS_IsException(prototype)) { + *handled = true; + return prototype; + } + + for (size_t depth = 0; depth < 64 && JS_IsObject(prototype); depth++) { + JSPropertyDescriptor desc = {}; + int found = JS_GetOwnProperty(ctx, &desc, prototype, atom); + if (found < 0) { + JS_FreeValue(ctx, prototype); + *handled = true; + return JS_EXCEPTION; + } + if (found > 0) { + if (isNativeInstancePrototypeBypassExcluded(ctx, atom)) { + freePropertyDescriptor(ctx, desc); + JS_FreeValue(ctx, prototype); + return JS_UNDEFINED; + } + + *handled = true; + JS_FreeValue(ctx, prototype); + if ((desc.flags & JS_PROP_GETSET) != 0) { + JSValue getter = desc.getter; + JS_FreeValue(ctx, desc.setter); + JS_FreeValue(ctx, desc.value); + if (JS_IsUndefined(getter)) { + JS_FreeValue(ctx, getter); + return JS_UNDEFINED; + } + JSValue result = JS_Call(ctx, getter, receiver, 0, nullptr); + JS_FreeValue(ctx, getter); + return result; + } + + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + return desc.value; + } + + JSValue nextPrototype = JS_GetPrototype(ctx, prototype); + JS_FreeValue(ctx, prototype); + if (JS_IsException(nextPrototype)) { + *handled = true; + return nextPrototype; + } + prototype = nextPrototype; + } + + JS_FreeValue(ctx, prototype); + return JS_UNDEFINED; +} + +static JSValue nativeHostGet(JSContext* ctx, JSValueConst obj, JSAtom atom, JSValueConst receiver) { + Runtime runtime(stateForContext(ctx)); + auto* holder = static_cast(JS_GetOpaque(obj, gHostClassId)); + if (holder == nullptr || holder->hostObject == nullptr) { + return JS_UNDEFINED; + } + try { + bool handledByPrototype = false; + JSValue prototypeResult = + nativePrototypeProperty(ctx, obj, atom, receiver, holder, + &handledByPrototype); + if (handledByPrototype) { + return prototypeResult; + } + + Value result = holder->hostObject->get(runtime, PropNameID(atomToUtf8(ctx, atom))); + if (!result.isUndefined()) { + return result.local(runtime); + } + return JS_UNDEFINED; + } catch (const std::exception& error) { + return throwError(ctx, error); + } +} + +static int nativeHostSet(JSContext* ctx, JSValueConst obj, JSAtom atom, JSValueConst value, + JSValueConst, int) { + Runtime runtime(stateForContext(ctx)); + auto* holder = static_cast(JS_GetOpaque(obj, gHostClassId)); + if (holder == nullptr || holder->hostObject == nullptr) { + return 0; + } + try { + bool handled = holder->hostObject->set( + runtime, PropNameID(atomToUtf8(ctx, atom)), + Value::borrowed(runtime, value)); + return handled ? 1 : 0; + } catch (const std::exception& error) { + throwError(ctx, error); + return -1; + } +} + +static int nativeHostHas(JSContext* ctx, JSValueConst obj, JSAtom atom) { + Runtime runtime(stateForContext(ctx)); + auto* holder = static_cast(JS_GetOpaque(obj, gHostClassId)); + if (holder == nullptr || holder->hostObject == nullptr) { + return 0; + } + try { + auto names = holder->hostObject->getPropertyNames(runtime); + std::string requested = atomToUtf8(ctx, atom); + for (const auto& name : names) { + if (name.utf8(runtime) == requested) { + return 1; + } + } + } catch (const std::exception&) { + } + return 0; +} + +static int nativeHostOwnNames(JSContext* ctx, JSPropertyEnum** ptab, uint32_t* plen, + JSValueConst obj) { + Runtime runtime(stateForContext(ctx)); + auto* holder = static_cast(JS_GetOpaque(obj, gHostClassId)); + if (holder == nullptr || holder->hostObject == nullptr) { + *ptab = nullptr; + *plen = 0; + return 0; + } + auto names = holder->hostObject->getPropertyNames(runtime); + *plen = static_cast(names.size()); + *ptab = static_cast(js_mallocz(ctx, sizeof(JSPropertyEnum) * names.size())); + for (uint32_t i = 0; i < *plen; i++) { + (*ptab)[i].is_enumerable = true; + (*ptab)[i].atom = JS_NewAtom(ctx, names[i].utf8(runtime).c_str()); + } + return 0; +} + +static void nativeHostFinalize(JSRuntime*, JSValue value) { + auto* holder = static_cast(JS_GetOpaque(value, gHostClassId)); + delete holder; +} + +static JSValue invokeFunctionHolder(JSContext* ctx, FunctionHolder* holder, JSValueConst thisValue, + int argc, JSValueConst* argv) { + Runtime runtime(stateForContext(ctx)); + if (holder == nullptr || !holder->callback) { + return JS_UNDEFINED; + } + StackValueArray<8> args(static_cast(argc)); + for (int i = 0; i < argc; i++) { + args.emplace(static_cast(i), Value::borrowed(runtime, argv[i])); + } + try { + Value self = Value::borrowed(runtime, thisValue); + Value result = + holder->callback(runtime, self, args.size() == 0 ? nullptr : args.data(), args.size()); + return result.local(runtime); + } catch (const std::exception& error) { + return throwError(ctx, error); + } +} + +static JSValue nativeFunctionCall(JSContext* ctx, JSValue function, JSValue thisValue, int argc, + JSValue* argv, int) { + auto* holder = static_cast(JS_GetOpaque(function, gFunctionClassId)); + return invokeFunctionHolder(ctx, holder, thisValue, argc, argv); +} + +static JSValue nativeFunctionCallData(JSContext* ctx, JSValue thisValue, int argc, JSValue* argv, + int, JSValue* data) { + auto* holder = static_cast(JS_GetOpaque(data[0], gFunctionClassId)); + return invokeFunctionHolder(ctx, holder, thisValue, argc, argv); +} + +static void nativeFunctionFinalize(JSRuntime*, JSValue value) { + auto* holder = static_cast(JS_GetOpaque(value, gFunctionClassId)); + delete holder; +} + +static JSClassExoticMethods hostExoticMethods = { + .get_own_property = nullptr, + .get_own_property_names = nativeHostOwnNames, + .delete_property = nullptr, + .define_own_property = nullptr, + .has_property = nativeHostHas, + .get_property = nativeHostGet, + .set_property = nativeHostSet, +}; + +void ensureClasses(Runtime& runtime) { + auto state = runtime.state(); + JSRuntime* rt = JS_GetRuntime(runtime.context()); + if (gHostClassId == 0) { + JS_NewClassID(rt, &gHostClassId); + } + if (!state->hostClassRegistered) { + JSClassDef def = {}; + def.class_name = "NativeScriptEngineHostObject"; + def.exotic = &hostExoticMethods; + def.finalizer = nativeHostFinalize; + JS_NewClass(rt, gHostClassId, &def); + JS_SetClassProto(runtime.context(), gHostClassId, JS_NewObject(runtime.context())); + state->hostClassRegistered = true; + } + if (gFunctionClassId == 0) { + JS_NewClassID(rt, &gFunctionClassId); + } + if (!state->functionClassRegistered) { + JSClassDef def = {}; + def.class_name = "NativeScriptEngineFunction"; + def.call = nativeFunctionCall; + def.finalizer = nativeFunctionFinalize; + JS_NewClass(rt, gFunctionClassId, &def); + JS_SetClassProto(runtime.context(), gFunctionClassId, JS_NewObject(runtime.context())); + state->functionClassRegistered = true; + } +} + +} // namespace quickjsengine + +quickjsengine::HostObjectHolder* Object::hostObjectHolder(Runtime& runtime) const { + quickjsengine::ensureClasses(runtime); + JSValue object = local(runtime); + auto* holder = static_cast( + JS_GetOpaque(object, quickjsengine::gHostClassId)); + JS_FreeValue(runtime.context(), object); + return holder; +} + +Object Object::createFromHostObjectWithToken(Runtime& runtime, std::shared_ptr host, + const void* typeToken) { + quickjsengine::ensureClasses(runtime); + auto* holder = new quickjsengine::HostObjectHolder(runtime.state(), std::move(host), typeToken); + JSValue object = JS_NewObjectClass(runtime.context(), quickjsengine::gHostClassId); + JS_SetOpaque(object, holder); + Object result = Object::fromValueStorage(Value(runtime, object).storage_); + JS_FreeValue(runtime.context(), object); + return result; +} + +Function Function::createFromHostFunction(Runtime& runtime, const PropNameID& name, + unsigned int parameterCount, HostFunctionType callback) { + quickjsengine::ensureClasses(runtime); + auto* holder = new quickjsengine::FunctionHolder(runtime.state(), std::move(callback)); + JSValue data = JS_NewObjectClass(runtime.context(), quickjsengine::gFunctionClassId); + if (JS_IsException(data)) { + delete holder; + throw JSError(runtime, "QuickJS host function data allocation failed."); + } + JS_SetOpaque(data, holder); + + JSValue function = JS_NewCFunctionData(runtime.context(), quickjsengine::nativeFunctionCallData, + static_cast(parameterCount), 0, 1, &data); + JS_FreeValue(runtime.context(), data); + if (JS_IsException(function)) { + throw JSError(runtime, "QuickJS host function allocation failed."); + } + + std::string functionName = name.utf8(runtime); + JSValue nameValue = JS_NewStringLen(runtime.context(), functionName.data(), functionName.size()); + JS_DefinePropertyValueStr(runtime.context(), function, "name", nameValue, JS_PROP_CONFIGURABLE); + Function result = Function(Object::fromValueStorage(Value(runtime, function).storage_)); + JS_FreeValue(runtime.context(), function); + return result; +} + +} // namespace engine +} // namespace nativescript + +#endif // TARGET_ENGINE_QUICKJS diff --git a/NativeScript/ffi/objc/quickjs/NativeApiQuickJSMarshalling.mm b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSMarshalling.mm new file mode 100644 index 000000000..fed137d9f --- /dev/null +++ b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSMarshalling.mm @@ -0,0 +1,461 @@ +// Included by NativeApiQuickJSSelectorGroups.mm inside the NativeScript anonymous namespace. + +std::string quickJSValueToUtf8(JSContext* context, JSValueConst value) { + size_t length = 0; + const char* text = JS_ToCStringLen(context, &length, value); + if (text == nullptr) { + return {}; + } + std::string result(text, length); + JS_FreeCString(context, text); + return result; +} + +bool quickJSNumberValue(JSContext* context, JSValueConst value, + double* result) { + if (result == nullptr) { + return false; + } + double converted = 0; + if (JS_ToFloat64(context, &converted, value) < 0) { + return false; + } + *result = converted; + return true; +} + +template +std::shared_ptr quickJSHostObject(Runtime& runtime, JSValueConst value) { + if (!JS_IsObject(value)) { + return nullptr; + } + engine::quickjsengine::ensureClasses(runtime); + auto* holder = static_cast( + JS_GetOpaque(value, engine::quickjsengine::gHostClassId)); + if (holder == nullptr || + holder->typeToken != engine::quickjsengine::hostObjectTypeToken()) { + return nullptr; + } + return std::static_pointer_cast(holder->hostObject); +} + +template +T* quickJSHostObjectRaw(Runtime& runtime, JSValueConst value) { + if (!JS_IsObject(value)) { + return nullptr; + } + engine::quickjsengine::ensureClasses(runtime); + auto* holder = static_cast( + JS_GetOpaque(value, engine::quickjsengine::gHostClassId)); + if (holder == nullptr || + holder->typeToken != engine::quickjsengine::hostObjectTypeToken()) { + return nullptr; + } + return static_cast(holder->hostObject.get()); +} + +id quickJSNativeObjectArgument( + Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, JSValueConst value, + NativeApiArgumentFrame& frame) { + JSContext* context = runtime.context(); + if (JS_IsNull(value) || JS_IsUndefined(value)) { + return nil; + } + if (JS_IsString(value)) { + std::string utf8 = quickJSValueToUtf8(context, value); + id string = type.kind == metagen::mdTypeNSMutableStringObject + ? [[NSMutableString alloc] initWithBytes:utf8.data() + length:utf8.size() + encoding:NSUTF8StringEncoding] + : [[NSString alloc] initWithBytes:utf8.data() + length:utf8.size() + encoding:NSUTF8StringEncoding]; + if (string != nil) { + frame.addObject(string); + } + return string; + } + if (JS_IsBool(value)) { + return [NSNumber numberWithBool:JS_ToBool(context, value) != 0]; + } + if (JS_IsNumber(value) || JS_IsBigInt(context, value)) { + double converted = 0; + if (quickJSNumberValue(context, value, &converted)) { + return [NSNumber numberWithDouble:converted]; + } + } + if (!JS_IsObject(value)) { + return nil; + } + if (auto objectHost = + quickJSHostObject(runtime, value)) { + return objectHost->object(); + } + if (auto classHost = + quickJSHostObject(runtime, value)) { + return static_cast(classHost->nativeClass()); + } + if (auto protocolHost = + quickJSHostObject(runtime, value)) { + return static_cast(protocolHost->nativeProtocol()); + } + if (auto pointerHost = + quickJSHostObject(runtime, value)) { + return static_cast(pointerHost->pointer()); + } + if (auto referenceHost = + quickJSHostObject(runtime, value)) { + return static_cast(referenceHost->data()); + } + if (auto structHost = + quickJSHostObject(runtime, value)) { + return static_cast(structHost->data()); + } + + JSValue wrappedClassValue = + JS_GetPropertyStr(context, value, "__nativeApiClass"); + if (!JS_IsException(wrappedClassValue)) { + if (auto classHost = quickJSHostObject( + runtime, wrappedClassValue)) { + JS_FreeValue(context, wrappedClassValue); + return static_cast(classHost->nativeClass()); + } + } + JS_FreeValue(context, wrappedClassValue); + + Value wrapped = Value::borrowed(runtime, value); + return objectFromEngineValue(runtime, bridge, wrapped, frame, + type.kind == + metagen::mdTypeNSMutableStringObject); +} + +Class quickJSNativeClassArgument(Runtime& runtime, JSValueConst value) { + if (JS_IsNull(value) || JS_IsUndefined(value)) { + return Nil; + } + if (auto classHost = + quickJSHostObject(runtime, value)) { + return classHost->nativeClass(); + } + if (JS_IsObject(value)) { + JSValue wrappedClassValue = + JS_GetPropertyStr(runtime.context(), value, "__nativeApiClass"); + if (!JS_IsException(wrappedClassValue)) { + if (auto classHost = quickJSHostObject( + runtime, wrappedClassValue)) { + JS_FreeValue(runtime.context(), wrappedClassValue); + return classHost->nativeClass(); + } + } + JS_FreeValue(runtime.context(), wrappedClassValue); + } + Value wrapped = Value::borrowed(runtime, value); + return classFromEngineValue(runtime, wrapped); +} + +bool readQuickJSEngineSelectorArgument(Runtime& runtime, JSValueConst value, + SEL* result) { + if (result == nullptr) { + return false; + } + if (JS_IsNull(value) || JS_IsUndefined(value)) { + *result = nullptr; + return true; + } + if (!JS_IsString(value)) { + return false; + } + std::string selectorName = quickJSValueToUtf8(runtime.context(), value); + *result = sel_registerName(selectorName.c_str()); + return true; +} + +template +bool writeQuickJSNumber(JSContext* context, JSValueConst value, void* target) { + double converted = 0; + if (!quickJSNumberValue(context, value, &converted)) { + return false; + } + *static_cast(target) = static_cast(converted); + return true; +} + +bool prepareQuickJSEngineArgument( + Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, JSValueConst value, + NativeApiArgumentFrame& frame, size_t index) { + ffi_type* ffiType = ffiTypeForEngineArgument(type); + size_t size = + ffiType != nullptr && ffiType->size > 0 ? ffiType->size : nativeSizeForType(type); + void* target = frame.storageAt(index, size); + JSContext* context = runtime.context(); + + switch (type.kind) { + case metagen::mdTypeBool: + if (!JS_IsBool(value)) { + return false; + } + *static_cast(target) = JS_ToBool(context, value) != 0 ? 1 : 0; + return true; + case metagen::mdTypeChar: + return writeQuickJSNumber(context, value, target); + case metagen::mdTypeUChar: + case metagen::mdTypeUInt8: + return writeQuickJSNumber(context, value, target); + case metagen::mdTypeSShort: + return writeQuickJSNumber(context, value, target); + case metagen::mdTypeUShort: + if (JS_IsString(value)) { + std::string text = quickJSValueToUtf8(context, value); + if (text.size() != 1) { + return false; + } + *static_cast(target) = + static_cast(static_cast(text[0])); + return true; + } + return writeQuickJSNumber(context, value, target); + case metagen::mdTypeSInt: { + int32_t converted = 0; + if (JS_ToInt32(context, &converted, value) < 0) { + return false; + } + *static_cast(target) = converted; + return true; + } + case metagen::mdTypeUInt: { + uint32_t converted = 0; + if (JS_ToUint32(context, &converted, value) < 0) { + return false; + } + *static_cast(target) = converted; + return true; + } + case metagen::mdTypeSLong: + case metagen::mdTypeSInt64: { + int64_t converted = 0; + if (JS_ToInt64Ext(context, &converted, value) < 0) { + return false; + } + *static_cast(target) = converted; + return true; + } + case metagen::mdTypeULong: + case metagen::mdTypeUInt64: { + uint64_t converted = 0; + if (JS_IsBigInt(context, value)) { + if (JS_ToBigUint64(context, &converted, value) < 0) { + return false; + } + } else { + int64_t signedValue = 0; + if (JS_ToInt64Ext(context, &signedValue, value) < 0) { + return false; + } + converted = static_cast(signedValue); + } + *static_cast(target) = converted; + return true; + } + case metagen::mdTypeFloat: + return writeQuickJSNumber(context, value, target); + case metagen::mdTypeDouble: + return writeQuickJSNumber(context, value, target); + case metagen::mdTypeSelector: + return readQuickJSEngineSelectorArgument(runtime, value, + static_cast(target)); + case metagen::mdTypeClass: { + Class cls = quickJSNativeClassArgument(runtime, value); + if (cls == Nil) { + return false; + } + *static_cast(target) = cls; + return true; + } + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: + *static_cast(target) = + quickJSNativeObjectArgument(runtime, bridge, type, value, frame); + return true; + default: + break; + } + + Value wrapped = Value::borrowed(runtime, value); + convertEngineFfiArgument(runtime, bridge, type, wrapped, target, frame); + return true; +} + +JSValue quickJSInteger64Value(Runtime& runtime, int64_t value) { + constexpr int64_t maxSafeInteger = 9007199254740991LL; + constexpr int64_t minSafeInteger = -9007199254740991LL; + if (value >= minSafeInteger && value <= maxSafeInteger) { + return JS_NewFloat64(runtime.context(), static_cast(value)); + } + return JS_NewBigInt64(runtime.context(), value); +} + +JSValue quickJSUnsignedInteger64Value(Runtime& runtime, uint64_t value) { + constexpr uint64_t maxSafeInteger = 9007199254740991ULL; + if (value <= maxSafeInteger) { + return JS_NewFloat64(runtime.context(), static_cast(value)); + } + return JS_NewBigUint64(runtime.context(), value); +} + +JSValue setQuickJSEngineObjectReturn( + Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, id object) { + JSContext* context = runtime.context(); + if (object == nil) { + return JS_NULL; + } + Value roundTrip = + findCachedNativeObjectReturn(runtime, bridge, type, object); + if (!roundTrip.isUndefined()) { + JSValue result = roundTrip.local(runtime); + if (type.returnOwned) { + [object release]; + } + return result; + } + if (nativeObjectReturnMayCoerceToString(type) && + nativeObjectIsStringLike(object)) { + std::string utf8 = utf8StringFromNSString(static_cast(object)); + if (type.returnOwned) { + [object release]; + } + return JS_NewStringLen(context, utf8.data(), utf8.size()); + } + if ([object isKindOfClass:[NSNull class]]) { + if (type.returnOwned) { + [object release]; + } + return JS_NULL; + } + if ([object isKindOfClass:[NSNumber class]] && + ![object isKindOfClass:[NSDecimalNumber class]]) { + NSNumber* number = static_cast(object); + const char* objCType = [number objCType]; + bool isBool = CFGetTypeID((__bridge CFTypeRef)number) == + CFBooleanGetTypeID() || + (objCType != nullptr && + std::strcmp(objCType, @encode(BOOL)) == 0); + JSValue result = isBool ? JS_NewBool(context, [number boolValue]) + : JS_NewFloat64(context, [number doubleValue]); + if (type.returnOwned) { + [object release]; + } + return result; + } + + if (const NativeApiSymbol* classSymbol = + bridge->findClassForRuntimePointer((void*)object)) { + Value result = makeNativeClassValue(runtime, bridge, *classSymbol); + if (type.returnOwned) { + [object release]; + } + return result.local(runtime); + } + if (const NativeApiSymbol* protocolSymbol = + bridge->findProtocolForRuntimePointer((void*)object)) { + Value result = makeNativeProtocolValue(runtime, bridge, *protocolSymbol); + if (type.returnOwned) { + [object release]; + } + return result.local(runtime); + } + Value result = makeNativeObjectValue(runtime, bridge, object, type.returnOwned); + return result.local(runtime); +} + +JSValue setQuickJSEngineReturnValue( + Runtime& runtime, const std::shared_ptr& bridge, + NativeApiType type, void* value, const std::string& selectorName) { + JSContext* context = runtime.context(); + switch (type.kind) { + case metagen::mdTypeVoid: + return JS_UNDEFINED; + case metagen::mdTypeBool: + return JS_NewBool(context, *static_cast(value) != 0); + case metagen::mdTypeChar: + return JS_NewInt32(context, *static_cast(value)); + case metagen::mdTypeUChar: + case metagen::mdTypeUInt8: + return JS_NewUint32(context, *static_cast(value)); + case metagen::mdTypeSShort: + return JS_NewInt32(context, *static_cast(value)); + case metagen::mdTypeUShort: { + uint16_t raw = *static_cast(value); + if (raw >= 32 && raw <= 126) { + char buffer[2] = {static_cast(raw), '\0'}; + return JS_NewStringLen(context, buffer, 1); + } + return JS_NewUint32(context, raw); + } + case metagen::mdTypeSInt: + return JS_NewInt32(context, *static_cast(value)); + case metagen::mdTypeUInt: + return JS_NewUint32(context, *static_cast(value)); + case metagen::mdTypeSLong: + case metagen::mdTypeSInt64: + return quickJSInteger64Value(runtime, *static_cast(value)); + case metagen::mdTypeULong: + case metagen::mdTypeUInt64: + return quickJSUnsignedInteger64Value(runtime, + *static_cast(value)); + case metagen::mdTypeFloat: + return JS_NewFloat64(context, *static_cast(value)); + case metagen::mdTypeDouble: + return JS_NewFloat64(context, *static_cast(value)); + case metagen::mdTypeClass: { + Class cls = *static_cast(value); + if (cls == nil) { + return JS_NULL; + } + const char* name = class_getName(cls); + NativeApiSymbol symbol{ + .kind = NativeApiSymbolKind::Class, + .offset = MD_SECTION_OFFSET_NULL, + .name = name != nullptr ? name : "", + .runtimeName = name != nullptr ? name : "", + }; + if (const NativeApiSymbol* found = bridge->findClass(symbol.name)) { + symbol = *found; + } + Value result = makeNativeClassValue(runtime, bridge, std::move(symbol)); + return result.local(runtime); + } + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: + if ((selectorName == "valueForKey:" || + selectorName == "valueForKeyPath:") && + isObjectiveCObjectType(type)) { + type.kind = metagen::mdTypeAnyObject; + } + return setQuickJSEngineObjectReturn(runtime, bridge, type, + *static_cast(value)); + case metagen::mdTypeSelector: { + SEL selector = *static_cast(value); + const char* selectorNameValue = + selector != nullptr ? sel_getName(selector) : nullptr; + if (selectorNameValue == nullptr) { + return JS_NULL; + } + return JS_NewString(context, selectorNameValue); + } + default: + break; + } + Value result = convertNativeReturnValue(runtime, bridge, type, value); + return result.local(runtime); +} diff --git a/NativeScript/ffi/objc/quickjs/NativeApiQuickJSRuntime.h b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSRuntime.h new file mode 100644 index 000000000..c20ad480a --- /dev/null +++ b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSRuntime.h @@ -0,0 +1,796 @@ +#ifndef NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_RUNTIME_H +#define NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_RUNTIME_H + +#ifdef TARGET_ENGINE_QUICKJS + +#import +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Metadata.h" +#include "MetadataReader.h" +#include "ffi.h" +#include "quickjs.h" + +@protocol NativeApiClassBuilderProtocol +@end + +#ifdef EMBED_METADATA_SIZE +extern const unsigned char embedded_metadata[EMBED_METADATA_SIZE]; +#endif + +namespace nativescript { +namespace engine { + +class Runtime; +class Value; +class Object; +class Function; +class Array; +class String; +class BigInt; +class ArrayBuffer; + +class JSError : public std::runtime_error { + public: + JSError(Runtime&, const std::string& message) : std::runtime_error(message) {} + explicit JSError(const std::string& message) : std::runtime_error(message) {} +}; + +class StringBuffer { + public: + explicit StringBuffer(std::string value) : value_(std::move(value)) {} + const char* data() const { return value_.data(); } + size_t size() const { return value_.size(); } + + private: + std::string value_; +}; + +class MutableBuffer { + public: + virtual ~MutableBuffer() = default; + virtual size_t size() const = 0; + virtual uint8_t* data() = 0; +}; + +class PropNameID { + public: + PropNameID() = default; + explicit PropNameID(std::string value) : value_(std::move(value)) {} + static PropNameID forAscii(Runtime&, const char* value) { + return PropNameID(value != nullptr ? value : ""); + } + static PropNameID forAscii(Runtime&, const std::string& value) { return PropNameID(value); } + std::string utf8(Runtime&) const { return value_; } + + private: + std::string value_; +}; + +class HostObject { + public: + virtual ~HostObject() = default; + virtual Value get(Runtime& runtime, const PropNameID& name); + virtual bool set(Runtime& runtime, const PropNameID& name, const Value& value); + virtual std::vector getPropertyNames(Runtime& runtime); +}; + +using HostFunctionType = std::function; + +namespace quickjsengine { + +template +const void* hostObjectTypeToken() { + static int token = 0; + return &token; +} + +struct RuntimeState { + explicit RuntimeState(JSContext* context) : context(context) {} + JSContext* context = nullptr; + bool hostClassRegistered = false; + bool functionClassRegistered = false; + bool selectorGroupDataClassRegistered = false; +}; + +extern JSClassID gHostClassId; +extern JSClassID gFunctionClassId; + +std::shared_ptr stateForContext(JSContext* context); + +struct ValueStorage { + enum class Kind { + Undefined, + Null, + Bool, + Number, + QuickJS, + QuickJSBorrowed, + }; + + explicit ValueStorage(Kind kind) : kind(kind) {} + ~ValueStorage() { + if (kind == Kind::QuickJS && context != nullptr && !JS_IsUninitialized(value)) { + JS_FreeValue(context, value); + } + } + + Kind kind = Kind::Undefined; + bool boolValue = false; + double numberValue = 0; + JSContext* context = nullptr; + JSValue value = JS_UNINITIALIZED; +}; + +struct HostObjectHolder { + HostObjectHolder(std::shared_ptr state, std::shared_ptr hostObject, + const void* typeToken) + : state(std::move(state)), hostObject(std::move(hostObject)), typeToken(typeToken) {} + std::shared_ptr state; + std::shared_ptr hostObject; + const void* typeToken = nullptr; +}; + +struct FunctionHolder { + FunctionHolder(std::shared_ptr state, HostFunctionType callback) + : state(std::move(state)), callback(std::move(callback)) {} + std::shared_ptr state; + HostFunctionType callback; +}; + +struct ArrayBufferHolder { + explicit ArrayBufferHolder(std::shared_ptr buffer) : buffer(std::move(buffer)) {} + std::shared_ptr buffer; +}; + +inline std::string valueToUtf8(JSContext* context, JSValueConst value) { + size_t length = 0; + const char* cString = JS_ToCStringLen(context, &length, value); + if (cString == nullptr) { + return {}; + } + std::string result(cString, length); + JS_FreeCString(context, cString); + return result; +} + +inline std::string currentExceptionMessage(JSContext* context) { + JSValue exception = JS_GetException(context); + std::string message = valueToUtf8(context, exception); + JS_FreeValue(context, exception); + return message.empty() ? std::string("QuickJS function call failed.") + : message; +} + +inline std::string atomToUtf8(JSContext* context, JSAtom atom) { + const char* cString = JS_AtomToCString(context, atom); + if (cString == nullptr) { + return {}; + } + std::string result(cString); + JS_FreeCString(context, cString); + return result; +} + +inline JSValue throwError(JSContext* context, const std::exception& error) { + return JS_ThrowTypeError(context, "%s", error.what()); +} + +void ensureClasses(Runtime& runtime); + +} // namespace quickjsengine + +class Runtime { + public: + explicit Runtime(JSContext* context) : state_(quickjsengine::stateForContext(context)) {} + explicit Runtime(std::shared_ptr state) : state_(std::move(state)) {} + JSContext* context() const { return state_->context; } + std::shared_ptr state() const { return state_; } + Object global(); + Value evaluateJavaScript(std::shared_ptr buffer, const std::string& sourceURL); + void drainMicrotasks() { + JSContext* ctx = context(); + JSRuntime* rt = JS_GetRuntime(ctx); + JSContext* jobCtx = nullptr; + while (JS_ExecutePendingJob(rt, &jobCtx) > 0) { + } + } + + private: + std::shared_ptr state_; +}; + +class String { + public: + String() = default; + String(Runtime& runtime, JSValue value); + static String createFromUtf8(Runtime& runtime, const char* value) { + return String(runtime, JS_NewString(runtime.context(), value != nullptr ? value : "")); + } + static String createFromUtf8(Runtime& runtime, const std::string& value) { + return String(runtime, JS_NewStringLen(runtime.context(), value.data(), value.size())); + } + static String createFromUtf8(Runtime& runtime, const uint8_t* value, size_t length) { + return String(runtime, + JS_NewStringLen(runtime.context(), reinterpret_cast(value), length)); + } + std::string utf8(Runtime& runtime) const; + JSValue local(Runtime& runtime) const; + operator Value() const; + + private: + friend class Value; + std::shared_ptr storage_; +}; + +class Value { + public: + Value() : kind_(quickjsengine::ValueStorage::Kind::Undefined) {} + + Value(bool value) : kind_(quickjsengine::ValueStorage::Kind::Bool), boolValue_(value) {} + + Value(double value) : kind_(quickjsengine::ValueStorage::Kind::Number), numberValue_(value) {} + + Value(int value) : Value(static_cast(value)) {} + Value(uint32_t value) : Value(static_cast(value)) {} + + Value(Runtime& runtime, const Value& value) { + if (value.kind_ == quickjsengine::ValueStorage::Kind::QuickJSBorrowed) { + // Promote borrowed to owned + storage_ = std::make_shared( + quickjsengine::ValueStorage::Kind::QuickJS); + storage_->context = runtime.context(); + storage_->value = JS_DupValue(runtime.context(), value.borrowedValue_); + kind_ = quickjsengine::ValueStorage::Kind::QuickJS; + return; + } + kind_ = value.kind_; + boolValue_ = value.boolValue_; + numberValue_ = value.numberValue_; + borrowedContext_ = value.borrowedContext_; + borrowedValue_ = value.borrowedValue_; + storage_ = value.storage_; + } + Value(Runtime& runtime, Value&& value) + : kind_(value.kind_), + boolValue_(value.boolValue_), + numberValue_(value.numberValue_), + borrowedContext_(value.borrowedContext_), + borrowedValue_(value.borrowedValue_), + storage_(std::move(value.storage_)) {} + Value(Runtime& runtime, const String& value) : storage_(value.storage_) { + kind_ = storage_ ? storage_->kind : quickjsengine::ValueStorage::Kind::Undefined; + } + Value(Runtime& runtime, const Object& object); + Value(Runtime& runtime, const Function& function); + Value(Runtime& runtime, const Array& array); + Value(Runtime& runtime, const ArrayBuffer& arrayBuffer); + Value(Runtime& runtime, const BigInt& bigint); + Value(Runtime& runtime, JSValue value) + : kind_(quickjsengine::ValueStorage::Kind::QuickJS), + storage_(std::make_shared( + quickjsengine::ValueStorage::Kind::QuickJS)) { + storage_->context = runtime.context(); + storage_->value = JS_DupValue(runtime.context(), value); + } + + static Value borrowed(Runtime& runtime, JSValueConst value) { + Value result; + result.kind_ = quickjsengine::ValueStorage::Kind::QuickJSBorrowed; + result.borrowedContext_ = runtime.context(); + result.borrowedValue_ = value; + return result; + } + + static Value undefined() { return Value(); } + static Value null() { + Value value; + value.kind_ = quickjsengine::ValueStorage::Kind::Null; + return value; + } + bool isUndefined() const { + if (kind_ == quickjsengine::ValueStorage::Kind::Undefined) { + return true; + } + return isQuickJS() && JS_IsUndefined(jsValue()); + } + bool isNull() const { + if (kind_ == quickjsengine::ValueStorage::Kind::Null) { + return true; + } + return isQuickJS() && JS_IsNull(jsValue()); + } + bool isBool() const { + if (kind_ == quickjsengine::ValueStorage::Kind::Bool) { + return true; + } + return isQuickJS() && JS_IsBool(jsValue()); + } + bool getBool() const { + if (kind_ == quickjsengine::ValueStorage::Kind::Bool) { + return boolValue_; + } + if (isQuickJS()) { + return JS_ToBool(jsContext(), jsValue()) != 0; + } + return false; + } + bool isNumber() const { + if (kind_ == quickjsengine::ValueStorage::Kind::Number) { + return true; + } + return isQuickJS() && JS_IsNumber(jsValue()); + } + double getNumber() const { + if (kind_ == quickjsengine::ValueStorage::Kind::Number) { + return numberValue_; + } + if (isQuickJS()) { + double value = 0; + JS_ToFloat64(jsContext(), &value, jsValue()); + return value; + } + return 0; + } + bool isObject() const { return isQuickJS() && JS_IsObject(jsValue()); } + bool isString() const { return isQuickJS() && JS_IsString(jsValue()); } + bool isBigInt() const { return isQuickJS() && JS_IsBigInt(jsContext(), jsValue()); } + bool isSymbol() const { return isQuickJS() && JS_IsSymbol(jsValue()); } + + Object asObject(Runtime& runtime) const; + String asString(Runtime& runtime) const; + BigInt getBigInt(Runtime& runtime) const; + + JSValue local(Runtime& runtime) const { + switch (kind_) { + case quickjsengine::ValueStorage::Kind::Undefined: + return JS_UNDEFINED; + case quickjsengine::ValueStorage::Kind::Null: + return JS_NULL; + case quickjsengine::ValueStorage::Kind::Bool: + return JS_NewBool(runtime.context(), boolValue_); + case quickjsengine::ValueStorage::Kind::Number: + return JS_NewFloat64(runtime.context(), numberValue_); + case quickjsengine::ValueStorage::Kind::QuickJS: + case quickjsengine::ValueStorage::Kind::QuickJSBorrowed: + return JS_DupValue(runtime.context(), jsValue()); + } + } + + // Access the shared storage (for Object/Function/Array interop) + std::shared_ptr storage() const { return storage_; } + + static Value fromStorage(std::shared_ptr s) { + Value v; + v.kind_ = s->kind; + v.boolValue_ = s->boolValue; + v.numberValue_ = s->numberValue; + v.storage_ = std::move(s); + return v; + } + + private: + friend class Runtime; + friend class Object; + friend class String; + friend class BigInt; + friend class ArrayBuffer; + friend class Function; + friend class Array; + + bool isQuickJS() const { + return kind_ == quickjsengine::ValueStorage::Kind::QuickJS || + kind_ == quickjsengine::ValueStorage::Kind::QuickJSBorrowed; + } + JSContext* jsContext() const { + return kind_ == quickjsengine::ValueStorage::Kind::QuickJSBorrowed ? borrowedContext_ + : storage_->context; + } + JSValue jsValue() const { + return kind_ == quickjsengine::ValueStorage::Kind::QuickJSBorrowed ? borrowedValue_ + : storage_->value; + } + + quickjsengine::ValueStorage::Kind kind_ = quickjsengine::ValueStorage::Kind::Undefined; + bool boolValue_ = false; + double numberValue_ = 0; + JSContext* borrowedContext_ = nullptr; + JSValue borrowedValue_ = JS_UNINITIALIZED; + std::shared_ptr storage_; +}; + +class Object { + public: + Object() = default; + explicit Object(Runtime& runtime) + : storage_(std::make_shared( + quickjsengine::ValueStorage::Kind::QuickJS)) { + storage_->context = runtime.context(); + storage_->value = JS_NewObject(runtime.context()); + } + static Object fromValueStorage(std::shared_ptr storage) { + Object object; + object.storage_ = std::move(storage); + return object; + } + template + static Object createFromHostObject(Runtime& runtime, std::shared_ptr host) { + auto baseHost = std::static_pointer_cast(std::move(host)); + return createFromHostObjectWithToken(runtime, std::move(baseHost), + quickjsengine::hostObjectTypeToken()); + } + + Value getProperty(Runtime& runtime, const char* name) const { + JSValue object = local(runtime); + JSValue result = JS_GetPropertyStr(runtime.context(), object, name != nullptr ? name : ""); + JS_FreeValue(runtime.context(), object); + if (JS_IsException(result)) { + throw JSError(runtime, "QuickJS property get failed."); + } + Value value(runtime, result); + JS_FreeValue(runtime.context(), result); + return value; + } + Value getProperty(Runtime& runtime, const std::string& name) const { + return getProperty(runtime, name.c_str()); + } + Value getProperty(Runtime& runtime, const Value& key) const { + JSValue object = local(runtime); + JSValue keyValue = key.local(runtime); + JSAtom atom = JS_ValueToAtom(runtime.context(), keyValue); + JS_FreeValue(runtime.context(), keyValue); + JSValue result = + atom == JS_ATOM_NULL ? JS_UNDEFINED : JS_GetProperty(runtime.context(), object, atom); + if (atom != JS_ATOM_NULL) { + JS_FreeAtom(runtime.context(), atom); + } + JS_FreeValue(runtime.context(), object); + if (JS_IsException(result)) { + throw JSError(runtime, "QuickJS property get failed."); + } + Value value(runtime, result); + JS_FreeValue(runtime.context(), result); + return value; + } + Object getPropertyAsObject(Runtime& runtime, const char* name) const { + return getProperty(runtime, name).asObject(runtime); + } + Function getPropertyAsFunction(Runtime& runtime, const char* name) const; + + void setProperty(Runtime& runtime, const char* name, const Value& value) { + JSValue object = local(runtime); + JSValue localValue = value.local(runtime); + int status = + JS_SetPropertyStr(runtime.context(), object, name != nullptr ? name : "", localValue); + JS_FreeValue(runtime.context(), object); + if (status < 0) { + throw JSError(runtime, "QuickJS property set failed."); + } + } + void setProperty(Runtime& runtime, const char* name, const String& value) { + setProperty(runtime, name, Value(runtime, value)); + } + void setProperty(Runtime& runtime, const char* name, const Object& value) { + setProperty(runtime, name, Value(runtime, value)); + } + void setProperty(Runtime& runtime, const char* name, const Function& value); + void setProperty(Runtime& runtime, const char* name, const Array& value); + void setProperty(Runtime& runtime, const char* name, const ArrayBuffer& value); + void setProperty(Runtime& runtime, const char* name, bool value) { + setProperty(runtime, name, Value(value)); + } + void setProperty(Runtime& runtime, const char* name, double value) { + setProperty(runtime, name, Value(value)); + } + void setProperty(Runtime& runtime, const std::string& name, const Value& value) { + setProperty(runtime, name.c_str(), value); + } + void setProperty(Runtime& runtime, const Value& key, const Value& value) { + JSValue object = local(runtime); + JSValue keyValue = key.local(runtime); + JSAtom atom = JS_ValueToAtom(runtime.context(), keyValue); + JS_FreeValue(runtime.context(), keyValue); + JSValue localValue = value.local(runtime); + int status = + atom == JS_ATOM_NULL ? -1 : JS_SetProperty(runtime.context(), object, atom, localValue); + if (atom != JS_ATOM_NULL) { + JS_FreeAtom(runtime.context(), atom); + } + JS_FreeValue(runtime.context(), object); + if (status < 0) { + throw JSError(runtime, "QuickJS property set failed."); + } + } + bool hasProperty(Runtime& runtime, const char* name) const { + JSValue object = local(runtime); + JSAtom atom = JS_NewAtom(runtime.context(), name != nullptr ? name : ""); + int result = JS_HasProperty(runtime.context(), object, atom); + JS_FreeAtom(runtime.context(), atom); + JS_FreeValue(runtime.context(), object); + return result > 0; + } + bool isFunction(Runtime& runtime) const { + JSValue object = local(runtime); + bool result = JS_IsFunction(runtime.context(), object); + JS_FreeValue(runtime.context(), object); + return result; + } + bool isArray(Runtime& runtime) const { + JSValue object = local(runtime); + int result = JS_IsArray(runtime.context(), object); + JS_FreeValue(runtime.context(), object); + return result > 0; + } + bool isArrayBuffer(Runtime& runtime) const { + JSValue object = local(runtime); + bool result = JS_IsArrayBuffer(object); + JS_FreeValue(runtime.context(), object); + return result; + } + Function asFunction(Runtime& runtime) const; + Array getArray(Runtime& runtime) const; + ArrayBuffer getArrayBuffer(Runtime& runtime) const; + Array getPropertyNames(Runtime& runtime) const; + + template + bool isHostObject(Runtime& runtime) const { + auto holder = hostObjectHolder(runtime); + return holder != nullptr && holder->typeToken == quickjsengine::hostObjectTypeToken(); + } + template + std::shared_ptr getHostObject(Runtime& runtime) const { + auto holder = hostObjectHolder(runtime); + if (holder == nullptr || holder->typeToken != quickjsengine::hostObjectTypeToken()) { + return nullptr; + } + return std::static_pointer_cast(holder->hostObject); + } + JSValue local(Runtime& runtime) const { return JS_DupValue(runtime.context(), storage_->value); } + operator Value() const { return Value::fromStorage(storage_); } + + protected: + friend class Value; + friend class Runtime; + friend class Function; + friend class Array; + friend class ArrayBuffer; + explicit Object(std::shared_ptr storage) + : storage_(std::move(storage)) {} + static Object createFromHostObjectWithToken(Runtime& runtime, std::shared_ptr host, + const void* typeToken); + quickjsengine::HostObjectHolder* hostObjectHolder(Runtime& runtime) const; + std::shared_ptr storage_; +}; + +class Function : public Object { + public: + Function() = default; + explicit Function(Object object) : Object(std::move(object.storage_)) {} + static Function createFromHostFunction(Runtime& runtime, const PropNameID& name, unsigned int, + HostFunctionType callback); + Value call(Runtime& runtime, const Value* args, size_t count) const { + JSValue function = local(runtime); + JSValue global = JS_GetGlobalObject(runtime.context()); + std::vector argv; + argv.reserve(count); + for (size_t i = 0; i < count; i++) { + argv.push_back(args[i].local(runtime)); + } + JSValue result = JS_Call(runtime.context(), function, global, static_cast(argv.size()), + argv.empty() ? nullptr : argv.data()); + for (auto& arg : argv) { + JS_FreeValue(runtime.context(), arg); + } + JS_FreeValue(runtime.context(), global); + JS_FreeValue(runtime.context(), function); + if (JS_IsException(result)) { + throw JSError(runtime, quickjsengine::currentExceptionMessage(runtime.context())); + } + Value value(runtime, result); + JS_FreeValue(runtime.context(), result); + return value; + } + Value call(Runtime& runtime) const { + return call(runtime, static_cast(nullptr), 0); + } + Value call(Runtime& runtime, std::nullptr_t, size_t) const { + return call(runtime, static_cast(nullptr), 0); + } + template + Value call(Runtime& runtime, const Value (&args)[N], size_t count) const { + return call(runtime, static_cast(args), count); + } + template + Value call(Runtime& runtime, Args&&... args) const { + Value argv[] = {Value(runtime, std::forward(args))...}; + return call(runtime, static_cast(argv), sizeof...(Args)); + } + Value callWithThis(Runtime& runtime, const Object& thisObject, const Value* args = nullptr, + size_t count = 0) const { + JSValue function = local(runtime); + JSValue thisValue = thisObject.local(runtime); + std::vector argv; + argv.reserve(count); + for (size_t i = 0; i < count; i++) { + argv.push_back(args[i].local(runtime)); + } + JSValue result = JS_Call(runtime.context(), function, thisValue, static_cast(argv.size()), + argv.empty() ? nullptr : argv.data()); + for (auto& arg : argv) { + JS_FreeValue(runtime.context(), arg); + } + JS_FreeValue(runtime.context(), thisValue); + JS_FreeValue(runtime.context(), function); + if (JS_IsException(result)) { + throw JSError(runtime, quickjsengine::currentExceptionMessage(runtime.context())); + } + Value value(runtime, result); + JS_FreeValue(runtime.context(), result); + return value; + } + Value callAsConstructor(Runtime& runtime, const Value* args, size_t count) const { + JSValue function = local(runtime); + std::vector argv; + argv.reserve(count); + for (size_t i = 0; i < count; i++) { + argv.push_back(args[i].local(runtime)); + } + JSValue result = JS_CallConstructor(runtime.context(), function, static_cast(argv.size()), + argv.empty() ? nullptr : argv.data()); + for (auto& arg : argv) { + JS_FreeValue(runtime.context(), arg); + } + JS_FreeValue(runtime.context(), function); + if (JS_IsException(result)) { + throw JSError(runtime, "QuickJS constructor call failed."); + } + Value value(runtime, result); + JS_FreeValue(runtime.context(), result); + return value; + } + Value callAsConstructor(Runtime& runtime, std::nullptr_t, size_t) const { + return callAsConstructor(runtime, static_cast(nullptr), 0); + } + template + Value callAsConstructor(Runtime& runtime, const Value (&args)[N], size_t count) const { + return callAsConstructor(runtime, static_cast(args), count); + } + template + Value callAsConstructor(Runtime& runtime, Args&&... args) const { + Value argv[] = {Value(runtime, std::forward(args))...}; + return callAsConstructor(runtime, static_cast(argv), sizeof...(Args)); + } +}; + +class Array : public Object { + public: + explicit Array(Runtime& runtime, size_t size) + : Object(std::make_shared( + quickjsengine::ValueStorage::Kind::QuickJS)) { + storage_->context = runtime.context(); + storage_->value = JS_NewArray(runtime.context()); + JS_SetPropertyStr(runtime.context(), storage_->value, "length", + JS_NewUint32(runtime.context(), static_cast(size))); + } + explicit Array(Object object) : Object(std::move(object.storage_)) {} + size_t size(Runtime& runtime) const { + Value length = getProperty(runtime, "length"); + return length.isNumber() ? static_cast(std::max(0, length.getNumber())) : 0; + } + Value getValueAtIndex(Runtime& runtime, size_t index) const { + JSValue object = local(runtime); + JSValue result = JS_GetPropertyUint32(runtime.context(), object, static_cast(index)); + JS_FreeValue(runtime.context(), object); + if (JS_IsException(result)) { + throw JSError(runtime, "QuickJS array get failed."); + } + Value value(runtime, result); + JS_FreeValue(runtime.context(), result); + return value; + } + void setValueAtIndex(Runtime& runtime, size_t index, const Value& value) { + JSValue object = local(runtime); + JSValue localValue = value.local(runtime); + int status = + JS_SetPropertyUint32(runtime.context(), object, static_cast(index), localValue); + JS_FreeValue(runtime.context(), object); + if (status < 0) { + throw JSError(runtime, "QuickJS array set failed."); + } + } + void setValueAtIndex(Runtime& runtime, size_t index, const String& value) { + setValueAtIndex(runtime, index, Value(runtime, value)); + } +}; + +class BigInt { + public: + BigInt() = default; + BigInt(Runtime& runtime, JSValue value) + : storage_(std::make_shared( + quickjsengine::ValueStorage::Kind::QuickJS)) { + storage_->context = runtime.context(); + storage_->value = JS_DupValue(runtime.context(), value); + } + static BigInt fromInt64(Runtime& runtime, int64_t value) { + JSValue result = JS_NewBigInt64(runtime.context(), value); + BigInt bigint(runtime, result); + JS_FreeValue(runtime.context(), result); + return bigint; + } + static BigInt fromUint64(Runtime& runtime, uint64_t value) { + JSValue result = JS_NewBigUint64(runtime.context(), value); + BigInt bigint(runtime, result); + JS_FreeValue(runtime.context(), result); + return bigint; + } + String toString(Runtime& runtime, int) const; + JSValue local(Runtime& runtime) const { return JS_DupValue(runtime.context(), storage_->value); } + operator Value() const { return Value::fromStorage(storage_); } + + private: + friend class Value; + std::shared_ptr storage_; +}; + +class ArrayBuffer : public Object { + public: + ArrayBuffer(Runtime& runtime, std::shared_ptr buffer) + : Object(std::make_shared( + quickjsengine::ValueStorage::Kind::QuickJS)) { + auto* holder = new quickjsengine::ArrayBufferHolder(std::move(buffer)); + storage_->context = runtime.context(); + storage_->value = JS_NewArrayBuffer( + runtime.context(), holder->buffer->data(), holder->buffer->size(), + [](JSRuntime*, void* opaque, void*) { + delete static_cast(opaque); + }, + holder, false); + } + explicit ArrayBuffer(Object object) : Object(std::move(object.storage_)) {} + size_t size(Runtime& runtime) const { + JSValue object = local(runtime); + size_t size = 0; + JS_GetArrayBuffer(runtime.context(), &size, object); + JS_FreeValue(runtime.context(), object); + return size; + } + uint8_t* data(Runtime& runtime) const { + JSValue object = local(runtime); + size_t size = 0; + uint8_t* data = JS_GetArrayBuffer(runtime.context(), &size, object); + JS_FreeValue(runtime.context(), object); + return data; + } +}; +} // namespace engine +} // namespace nativescript + +#endif // TARGET_ENGINE_QUICKJS + +#endif // NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_RUNTIME_H diff --git a/NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.mm b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSRuntime.mm similarity index 92% rename from NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.mm rename to NativeScript/ffi/objc/quickjs/NativeApiQuickJSRuntime.mm index 6a64b8e57..d38eb3ac6 100644 --- a/NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.mm +++ b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSRuntime.mm @@ -2,8 +2,8 @@ #ifdef TARGET_ENGINE_QUICKJS -namespace facebook { -namespace jsi { +namespace nativescript { +namespace engine { String BigInt::toString(Runtime& runtime, int) const { JSValue value = local(runtime); @@ -34,7 +34,7 @@ return value; } -} // namespace jsi -} // namespace facebook +} // namespace engine +} // namespace nativescript #endif // TARGET_ENGINE_QUICKJS diff --git a/NativeScript/ffi/objc/quickjs/NativeApiQuickJSRuntimeSupport.mm b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSRuntimeSupport.mm new file mode 100644 index 000000000..c755e5b97 --- /dev/null +++ b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSRuntimeSupport.mm @@ -0,0 +1,100 @@ +// Included by NativeApiQuickJS.mm inside the NativeScript anonymous namespace. + +static JSValue NativeApiLazyGlobalGetter(JSContext* context, JSValueConst, int, + JSValueConst*, int, JSValueConst* data) { + JSValue global = JS_GetGlobalObject(context); + JSValue resolver = JS_GetPropertyStr(context, global, "__nativeScriptResolveNativeApiLazyGlobal"); + if (!JS_IsFunction(context, resolver)) { + JS_FreeValue(context, resolver); + JS_FreeValue(context, global); + return JS_UNDEFINED; + } + + JSValueConst args[] = {data[0], data[1]}; + JSValue result = JS_Call(context, resolver, global, 2, args); + JS_FreeValue(context, resolver); + if (JS_IsException(result)) { + JS_FreeValue(context, global); + return result; + } + + JSAtom atom = JS_ValueToAtom(context, data[0]); + if (atom != JS_ATOM_NULL) { + JS_DefinePropertyValue(context, global, atom, JS_DupValue(context, result), + JS_PROP_CONFIGURABLE); + JS_FreeAtom(context, atom); + } + JS_FreeValue(context, global); + return result; +} + +bool InstallNativeApiLazyGlobal(Runtime& runtime, std::shared_ptr, + const std::string& name, const std::string& kind, + bool force) { + if (name.empty() || kind.empty()) { + return false; + } + + JSContext* context = runtime.context(); + JSValue global = JS_GetGlobalObject(context); + JSAtom atom = JS_NewAtomLen(context, name.data(), name.size()); + if (atom == JS_ATOM_NULL) { + JS_FreeValue(context, global); + return false; + } + + int hasProperty = JS_HasProperty(context, global, atom); + if (!force && hasProperty > 0) { + JS_FreeAtom(context, atom); + JS_FreeValue(context, global); + return false; + } + if (hasProperty < 0) { + JS_FreeAtom(context, atom); + JS_FreeValue(context, global); + return false; + } + + JSValue data[] = { + JS_NewStringLen(context, name.data(), name.size()), + JS_NewStringLen(context, kind.data(), kind.size()), + }; + if (JS_IsException(data[0]) || JS_IsException(data[1])) { + JS_FreeValue(context, data[0]); + JS_FreeValue(context, data[1]); + JS_FreeAtom(context, atom); + JS_FreeValue(context, global); + return false; + } + + JSValue getter = JS_NewCFunctionData(context, NativeApiLazyGlobalGetter, 0, 0, 2, data); + JS_FreeValue(context, data[0]); + JS_FreeValue(context, data[1]); + if (JS_IsException(getter)) { + JS_FreeAtom(context, atom); + JS_FreeValue(context, global); + return false; + } + + int status = + JS_DefinePropertyGetSet(context, global, atom, getter, JS_UNDEFINED, JS_PROP_CONFIGURABLE); + JS_FreeAtom(context, atom); + JS_FreeValue(context, global); + return status >= 0; +} + +void SetNativeApiObjectPrototype(Runtime& runtime, Object& object, + const Object& prototype) { + JSValue objectValue = object.local(runtime); + JSValue prototypeValue = prototype.local(runtime); + int status = JS_SetPrototype(runtime.context(), objectValue, prototypeValue); + JS_FreeValue(runtime.context(), prototypeValue); + JS_FreeValue(runtime.context(), objectValue); + if (status < 0) { + throw JSError(runtime, "QuickJS prototype assignment failed."); + } +} + +std::shared_ptr retainNativeApiRuntime(Runtime& runtime) { + return std::make_shared(runtime.state()); +} diff --git a/NativeScript/ffi/objc/quickjs/NativeApiQuickJSSelectorGroups.mm b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSSelectorGroups.mm new file mode 100644 index 000000000..26d0f1415 --- /dev/null +++ b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSSelectorGroups.mm @@ -0,0 +1,464 @@ +// Included by NativeApiQuickJS.mm inside the NativeScript anonymous namespace. + +struct NativeApiSelectorGroupData { + NativeApiSelectorGroupData( + std::shared_ptr state, + std::shared_ptr bridge, Class lookupClass, + bool receiverIsClass, + std::shared_ptr> + selectors, + std::shared_ptr< + std::vector>> + preparedInvocations, + std::weak_ptr boundReceiver = {}, + std::shared_ptr boundReceiverState = + nullptr) + : state(state), + bridge(std::move(bridge)), + lookupClass(lookupClass), + receiverIsClass(receiverIsClass), + selectors(std::move(selectors)), + preparedInvocations(std::move(preparedInvocations)), + boundReceiver(std::move(boundReceiver)), + boundReceiverState(std::move(boundReceiverState)), + runtime(state) {} + + std::shared_ptr state; + std::shared_ptr bridge; + Class lookupClass = Nil; + bool receiverIsClass = false; + std::shared_ptr> selectors; + std::shared_ptr< + std::vector>> + preparedInvocations; + std::weak_ptr boundReceiver; + std::shared_ptr boundReceiverState; + Runtime runtime; + Class cachedReceiverClass = Nil; + Class cachedDispatchClass = Nil; +}; + +#include "NativeApiQuickJSMarshalling.mm" + +#include "NativeApiQuickJSGsd.mm" + + +void* lookupGeneratedEngineObjCGsdInvoker(uint64_t dispatchId) { + return reinterpret_cast(lookupObjCGsdInvoker(dispatchId)); +} + +bool tryCallGeneratedEngineObjCSelector( + Runtime& runtime, const std::shared_ptr& bridge, + id receiver, const NativeApiPreparedObjCInvocation& prepared, + const Value* args, size_t count, Class dispatchSuperClass, Value* result) { + if (result == nullptr || receiver == nil || + !prepared.gsdEngineCallable || dispatchSuperClass != Nil || + count != prepared.gsdEngineArgumentCount) { + return false; + } + + auto invoker = reinterpret_cast(prepared.engineInvoker); + GsdObjCContext ctx{runtime, bridge, receiver, prepared.selector, + runtime.context(), nullptr, + prepared.signature.returnType}; + ctx.valueArguments = args; + ctx.materializeValueResult = true; + if (!invoker(ctx)) { + return false; + } + *result = std::move(ctx.valueResult); + return true; +} + +JSValue setQuickJSEnginePreparedObjCResult( + Runtime& runtime, const std::shared_ptr& bridge, + id receiver, const NativeApiPreparedObjCInvocation& prepared, + const std::shared_ptr& receiverHostObject, + const std::optional& initializerClassWrapper, + size_t providedCount, JSValueConst arguments[], + Class dispatchSuperClass) { + const NativeApiSignature& signature = prepared.signature; + if (receiver == nil || signature.variadic || + unsupportedEngineType(signature.returnType)) { + throw JSError(runtime, + "Objective-C selector is not supported by QuickJS engine: " + + prepared.selectorName); + } + + const bool isNSErrorOutMethod = prepared.isNSErrorOutMethod; + if (isNSErrorOutMethod) { + size_t expected = signature.argumentTypes.size(); + if (providedCount > expected || providedCount + 1 < expected) { + throw JSError( + runtime, "Actual arguments count: \"" + std::to_string(providedCount) + + "\". Expected: \"" + std::to_string(expected) + "\"."); + } + } else if (providedCount != signature.argumentTypes.size()) { + throw JSError( + runtime, "Actual arguments count: \"" + std::to_string(providedCount) + + "\". Expected: \"" + + std::to_string(signature.argumentTypes.size()) + "\"."); + } + + // GSD fast path: the generated invoker reads args directly from the QuickJS + // arguments, calls objc_msgSend with a typed cast, and produces the JS + // return value — bypassing all generic marshalling. + if (prepared.gsdEngineCallable && dispatchSuperClass == Nil && + providedCount == prepared.gsdEngineArgumentCount && + !initializerClassWrapper && !isNSErrorOutMethod) { + auto invoker = reinterpret_cast(prepared.engineInvoker); + GsdObjCContext ctx{runtime, bridge, receiver, prepared.selector, + runtime.context(), arguments, signature.returnType}; + if (invoker(ctx)) { + return ctx.result; + } + } + + if (dispatchSuperClass == Nil && !initializerClassWrapper && + providedCount <= 2) { + Value fastArgs[2]; + for (size_t i = 0; i < providedCount; i++) { + fastArgs[i] = Value::borrowed(runtime, arguments[i]); + } + Value fastResult; + if (tryCallFastEngineObjCSelector(runtime, bridge, receiver, prepared, + fastArgs, providedCount, Nil, + &fastResult)) { + return fastResult.local(runtime); + } + } + + NativeApiArgumentFrame frame(signature.argumentTypes.size()); + for (size_t i = 0; i < providedCount; i++) { + if (!prepareQuickJSEngineArgument(runtime, bridge, + signature.argumentTypes[i], + arguments[i], frame, i)) { + throw JSError(runtime, + "Objective-C argument is not supported by QuickJS engine: " + + prepared.selectorName); + } + } + + const bool hasImplicitNSErrorOutArg = + isNSErrorOutMethod && providedCount + 1 == signature.argumentTypes.size(); + NSError* implicitNSError = nil; + if (hasImplicitNSErrorOutArg) { + size_t outArgIndex = signature.argumentTypes.size() - 1; + void* target = frame.storageAt(outArgIndex, sizeof(NSError**)); + NSError** implicitNSErrorOutArg = &implicitNSError; + *static_cast(target) = implicitNSErrorOutArg; + } + + NativeApiPointerFrame values(signature.argumentTypes.size() + 2); + size_t valueIndex = 0; + struct objc_super superReceiver = {receiver, dispatchSuperClass}; + struct objc_super* superReceiverPtr = &superReceiver; + if (dispatchSuperClass != Nil) { + values.set(valueIndex++, &superReceiverPtr); + } else { + values.set(valueIndex++, &receiver); + } + values.set(valueIndex++, const_cast(&prepared.selector)); + for (size_t i = 0; i < signature.argumentTypes.size(); i++) { + values.set(valueIndex++, frame.values()[i]); + } + + NativeApiReturnStorage returnStorage( + nativeSizeForType(signature.returnType)); + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + if (prepared.preparedInvoker != nullptr && dispatchSuperClass == Nil) { + prepared.preparedInvoker(reinterpret_cast(objc_msgSend), + values.data(), returnStorage.data()); + } else { +#if defined(__x86_64__) + bool isStret = signature.returnType.ffiType->size > 16 && + signature.returnType.ffiType->type == FFI_TYPE_STRUCT; + void (*target)(void) = + dispatchSuperClass != Nil + ? (isStret ? FFI_FN(objc_msgSendSuper_stret) + : FFI_FN(objc_msgSendSuper)) + : (isStret ? FFI_FN(objc_msgSend_stret) : FFI_FN(objc_msgSend)); + ffi_call(const_cast(&signature.cif), target, + returnStorage.data(), values.data()); +#else + ffi_call(const_cast(&signature.cif), + dispatchSuperClass != Nil ? FFI_FN(objc_msgSendSuper) + : FFI_FN(objc_msgSend), + returnStorage.data(), values.data()); +#endif + } + }); + + NativeApiType returnType = signature.returnType; + if (hasImplicitNSErrorOutArg && implicitNSError != nil) { + const char* errorMessage = [[implicitNSError description] UTF8String]; + throw JSError( + runtime, errorMessage != nullptr ? errorMessage : "Unknown NSError"); + } + if (initializerClassWrapper) { + id resultObject = nil; + if (isObjectiveCObjectType(returnType)) { + resultObject = *static_cast(returnStorage.data()); + } + if (receiverHostObject != nullptr && resultObject != receiver) { + receiverHostObject->disownObject(receiver); + } + if (resultObject != nil) { + bridge->setObjectExpando(runtime, resultObject, + "__nativeApiClassWrapper", + Value(runtime, *initializerClassWrapper)); + } + } + return setQuickJSEngineReturnValue(runtime, bridge, returnType, + returnStorage.data(), + prepared.selectorName); +} + +static JSClassID gNativeApiSelectorGroupDataClassId = 0; + +void NativeApiSelectorGroupFinalize(JSRuntime*, JSValue value) { + auto* data = static_cast( + JS_GetOpaque(value, gNativeApiSelectorGroupDataClassId)); + delete data; +} + +void EnsureNativeApiSelectorGroupClass(Runtime& runtime) { + JSRuntime* jsRuntime = JS_GetRuntime(runtime.context()); + if (gNativeApiSelectorGroupDataClassId == 0) { + JS_NewClassID(jsRuntime, &gNativeApiSelectorGroupDataClassId); + } + + auto state = runtime.state(); + if (!state->selectorGroupDataClassRegistered) { + JSClassDef definition = {}; + definition.class_name = "NativeScriptEngineSelectorGroupData"; + definition.finalizer = NativeApiSelectorGroupFinalize; + JS_NewClass(jsRuntime, gNativeApiSelectorGroupDataClassId, + &definition); + state->selectorGroupDataClassRegistered = true; + } +} + +JSValue NativeApiSelectorGroupCall(JSContext* context, JSValue thisValue, + int argc, JSValue* argv, int, + JSValue* dataValues) { + auto* data = static_cast( + JS_GetOpaque(dataValues[0], gNativeApiSelectorGroupDataClassId)); + if (data == nullptr || data->selectors == nullptr || + data->preparedInvocations == nullptr) { + return JS_UNDEFINED; + } + + Runtime& runtime = data->runtime; + try { + NativeApiRoundTripCacheFrameGuard roundTripFrame(data->bridge); + size_t count = argc > 0 ? static_cast(argc) : 0; + if (count >= data->selectors->size() || + (*data->selectors)[count].selectorName.empty()) { + throw JSError(runtime, + "Objective-C selector is not available for the provided arguments " + "count."); + } + + NativeApiSelectorGroupEntry& entry = (*data->selectors)[count]; + auto& prepared = (*data->preparedInvocations)[count]; + Class selectorLookupClass = data->lookupClass; + id receiver = data->receiverIsClass ? static_cast(data->lookupClass) : nil; + std::shared_ptr receiverHostObject; + if (!data->receiverIsClass) { + if (data->boundReceiverState != nullptr) { + receiver = data->boundReceiverState->object(); + if (receiver == nil) { + throw JSError(runtime, + "Objective-C selector requires a native receiver."); + } + } else { + if (auto* rawHost = + quickJSHostObjectRaw(runtime, + thisValue)) { + receiver = rawHost->object(); + } + } + } + if (receiver == nil) { + throw JSError(runtime, + "Objective-C selector requires a native receiver."); + } + + const bool propertyGetterCall = + entry.hasMember && entry.member.property && count == 0; + const std::string* selectorNamePtr = &entry.selectorName; + const NativeApiMember* selectedMember = + entry.hasMember ? &entry.member : nullptr; + bool callTargetCanPrepare = true; + if (prepared == nullptr || propertyGetterCall) { + NativeApiSelectorGroupCallTarget callTarget = + selectorGroupCallTargetForEntry(receiver, selectorLookupClass, + data->receiverIsClass, entry, count); + selectorNamePtr = callTarget.selectorName; + selectedMember = callTarget.member; + callTargetCanPrepare = callTarget.canPrepare; + if (prepared != nullptr && prepared->selectorName != *selectorNamePtr) { + prepared = nullptr; + } + } + const std::string& selectorName = + prepared != nullptr && !propertyGetterCall ? prepared->selectorName + : *selectorNamePtr; + + if (data->receiverIsClass) { + Class methodClass = prepared != nullptr ? prepared->receiverClass : Nil; + if (methodClass == Nil) { + SEL selector = sel_registerName(selectorName.c_str()); + methodClass = + NativeApiClassHostObject::classRespondingToClassSelector( + data->lookupClass, selector); + } + if (methodClass == Nil) { + throw JSError(runtime, + "Objective-C selector is not available: " + + entry.selectorName); + } + selectorLookupClass = methodClass; + receiver = static_cast(methodClass); + } + if (propertyGetterCall && !callTargetCanPrepare) { + return callObjCSelector(runtime, data->bridge, receiver, + data->receiverIsClass, selectorName, + selectedMember, nullptr, 0) + .local(runtime); + } + + if (prepared == nullptr) { + if (!data->receiverIsClass) { + SEL selector = sel_registerName(selectorName.c_str()); + if (class_getInstanceMethod(selectorLookupClass, selector) == nullptr) { + Class receiverClass = object_getClass(receiver); + if (class_getInstanceMethod(receiverClass, selector) != nullptr) { + selectorLookupClass = receiverClass; + } + } + } + prepared = prepareNativeApiObjCInvocation( + runtime, data->bridge, selectorLookupClass, data->receiverIsClass, + selectorName, selectedMember); + // Look up the engine-neutral GSD invoker for this signature. + if (prepared->engineInvoker == nullptr) { + uint64_t dispatchId = dispatchIdForEngineSignature( + prepared->signature, SignatureCallKind::ObjCMethod); + if (auto gsdInvoker = lookupObjCGsdInvoker(dispatchId)) { + prepared->engineInvoker = reinterpret_cast(gsdInvoker); + configureGeneratedEngineObjCInvocation(*prepared); + } + } + } + + std::optional initializerClassWrapper; + if (!data->receiverIsClass && prepared->isInitMethod) { + if (!receiverHostObject) { + if (data->boundReceiverState != nullptr) { + if (auto boundReceiver = data->boundReceiver.lock()) { + receiverHostObject = std::move(boundReceiver); + } + } else { + receiverHostObject = + quickJSHostObject(runtime, thisValue); + } + } + Value classWrapperValue = data->bridge->findObjectExpando( + runtime, receiver, "__nativeApiClassWrapper"); + if (classWrapperValue.isObject()) { + initializerClassWrapper.emplace(classWrapperValue.asObject(runtime)); + } + data->bridge->forgetRoundTripValue(receiver); + data->bridge->forgetObjectExpandos(receiver); + } + + Class dispatchClass = Nil; + if (!data->receiverIsClass) { + Class receiverClass = object_getClass(receiver); + if (receiverClass == data->cachedReceiverClass) { + dispatchClass = data->cachedDispatchClass; + } else { + dispatchClass = dispatchSuperclassForEngineDerivedReceiver( + receiver, data->lookupClass); + data->cachedReceiverClass = receiverClass; + data->cachedDispatchClass = dispatchClass; + } + } + return setQuickJSEnginePreparedObjCResult( + runtime, data->bridge, receiver, *prepared, receiverHostObject, + initializerClassWrapper, count, argv, dispatchClass); + } catch (const std::exception& error) { + return engine::quickjsengine::throwError(context, error); + } +} + +Function CreateNativeApiSelectorGroupFunctionImpl( + Runtime& runtime, std::shared_ptr bridge, + Class lookupClass, bool receiverIsClass, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations, + std::weak_ptr boundReceiver, + std::shared_ptr boundReceiverState = + nullptr) { + EnsureNativeApiSelectorGroupClass(runtime); + auto* data = new NativeApiSelectorGroupData( + runtime.state(), std::move(bridge), lookupClass, receiverIsClass, + std::move(selectors), std::move(preparedInvocations), + std::move(boundReceiver), std::move(boundReceiverState)); + + JSValue dataObject = + JS_NewObjectClass(runtime.context(), + gNativeApiSelectorGroupDataClassId); + if (JS_IsException(dataObject)) { + delete data; + throw JSError(runtime, "QuickJS selector group allocation failed."); + } + JS_SetOpaque(dataObject, data); + + JSValue function = + JS_NewCFunctionData(runtime.context(), NativeApiSelectorGroupCall, + 0, 0, 1, &dataObject); + JS_FreeValue(runtime.context(), dataObject); + if (JS_IsException(function)) { + throw JSError(runtime, "QuickJS selector group function allocation failed."); + } + + JSValue nameValue = JS_NewStringLen(runtime.context(), "__nativeSelectorGroup", + std::strlen("__nativeSelectorGroup")); + JS_DefinePropertyValueStr(runtime.context(), function, "name", nameValue, + JS_PROP_CONFIGURABLE); + Value functionValue(runtime, function); + Function result = functionValue.asObject(runtime).asFunction(runtime); + JS_FreeValue(runtime.context(), function); + return result; +} + +Function CreateNativeApiSelectorGroupFunction( + Runtime& runtime, std::shared_ptr bridge, + Class lookupClass, bool receiverIsClass, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations) { + return CreateNativeApiSelectorGroupFunctionImpl( + runtime, std::move(bridge), lookupClass, receiverIsClass, + std::move(selectors), std::move(preparedInvocations), {}, nullptr); +} + +Function CreateNativeApiBoundSelectorGroupFunction( + Runtime& runtime, std::shared_ptr bridge, Class lookupClass, + std::shared_ptr receiverHostObject, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations) { + return CreateNativeApiSelectorGroupFunctionImpl( + runtime, std::move(bridge), lookupClass, false, std::move(selectors), + std::move(preparedInvocations), receiverHostObject, + receiverHostObject != nullptr ? receiverHostObject->lifetimeState() + : nullptr); +} diff --git a/NativeScript/ffi/objc/quickjs/NativeApiQuickJSValue.mm b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSValue.mm new file mode 100644 index 000000000..bb6852019 --- /dev/null +++ b/NativeScript/ffi/objc/quickjs/NativeApiQuickJSValue.mm @@ -0,0 +1,111 @@ +#include "NativeApiQuickJSRuntime.h" + +#ifdef TARGET_ENGINE_QUICKJS + +namespace nativescript { +namespace engine { + +Value HostObject::get(Runtime&, const PropNameID&) { return Value::undefined(); } +bool HostObject::set(Runtime&, const PropNameID&, const Value&) { return true; } +std::vector HostObject::getPropertyNames(Runtime&) { return {}; } +String::String(Runtime& runtime, JSValue value) + : storage_(std::make_shared( + quickjsengine::ValueStorage::Kind::QuickJS)) { + storage_->context = runtime.context(); + storage_->value = JS_DupValue(runtime.context(), value); +} +std::string String::utf8(Runtime& runtime) const { + JSValue value = local(runtime); + std::string result = quickjsengine::valueToUtf8(runtime.context(), value); + JS_FreeValue(runtime.context(), value); + return result; +} +JSValue String::local(Runtime& runtime) const { + return JS_DupValue(runtime.context(), storage_->value); +} +String::operator Value() const { return Value::fromStorage(storage_); } +Value::Value(Runtime&, const Object& object) { + storage_ = object.storage_; + kind_ = storage_ ? storage_->kind : quickjsengine::ValueStorage::Kind::Undefined; +} +Value::Value(Runtime&, const Function& function) { + storage_ = function.storage_; + kind_ = storage_ ? storage_->kind : quickjsengine::ValueStorage::Kind::Undefined; +} +Value::Value(Runtime&, const Array& array) { + storage_ = array.storage_; + kind_ = storage_ ? storage_->kind : quickjsengine::ValueStorage::Kind::Undefined; +} +Value::Value(Runtime&, const ArrayBuffer& arrayBuffer) { + storage_ = arrayBuffer.storage_; + kind_ = storage_ ? storage_->kind : quickjsengine::ValueStorage::Kind::Undefined; +} +Value::Value(Runtime&, const BigInt& bigint) { + storage_ = bigint.storage_; + kind_ = storage_ ? storage_->kind : quickjsengine::ValueStorage::Kind::Undefined; +} +Object Value::asObject(Runtime& runtime) const { + if (storage_) { + return Object::fromValueStorage(storage_); + } + // Promote to owned storage for Object. + auto s = std::make_shared(kind_); + if (kind_ == quickjsengine::ValueStorage::Kind::QuickJSBorrowed) { + s->kind = quickjsengine::ValueStorage::Kind::QuickJS; + s->context = borrowedContext_; + s->value = JS_DupValue(borrowedContext_, borrowedValue_); + } + return Object::fromValueStorage(std::move(s)); +} +String Value::asString(Runtime& runtime) const { + JSValue value = local(runtime); + String result(runtime, value); + JS_FreeValue(runtime.context(), value); + return result; +} +BigInt Value::getBigInt(Runtime& runtime) const { + JSValue value = local(runtime); + BigInt result(runtime, value); + JS_FreeValue(runtime.context(), value); + return result; +} +Function Object::getPropertyAsFunction(Runtime& runtime, const char* name) const { + return getProperty(runtime, name).asObject(runtime).asFunction(runtime); +} +Function Object::asFunction(Runtime&) const { return Function(*this); } +Array Object::getArray(Runtime&) const { return Array(*this); } +ArrayBuffer Object::getArrayBuffer(Runtime&) const { return ArrayBuffer(*this); } +Array Object::getPropertyNames(Runtime& runtime) const { + JSValue object = local(runtime); + JSPropertyEnum* properties = nullptr; + uint32_t count = 0; + int status = JS_GetOwnPropertyNames(runtime.context(), &properties, &count, object, + JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY); + JS_FreeValue(runtime.context(), object); + if (status < 0) { + throw JSError(runtime, "QuickJS property names failed."); + } + Array result(runtime, count); + for (uint32_t i = 0; i < count; i++) { + JSValue nameValue = JS_AtomToValue(runtime.context(), properties[i].atom); + result.setValueAtIndex(runtime, i, Value(runtime, nameValue)); + JS_FreeValue(runtime.context(), nameValue); + JS_FreeAtom(runtime.context(), properties[i].atom); + } + js_free(runtime.context(), properties); + return result; +} +void Object::setProperty(Runtime& runtime, const char* name, const Function& value) { + setProperty(runtime, name, Value(runtime, value)); +} +void Object::setProperty(Runtime& runtime, const char* name, const Array& value) { + setProperty(runtime, name, Value(runtime, value)); +} +void Object::setProperty(Runtime& runtime, const char* name, const ArrayBuffer& value) { + setProperty(runtime, name, Value(runtime, value)); +} + +} // namespace engine +} // namespace nativescript + +#endif // TARGET_ENGINE_QUICKJS diff --git a/NativeScript/ffi/objc/quickjs/SignatureDispatch.h b/NativeScript/ffi/objc/quickjs/SignatureDispatch.h new file mode 100644 index 000000000..2b9c0436b --- /dev/null +++ b/NativeScript/ffi/objc/quickjs/SignatureDispatch.h @@ -0,0 +1,14 @@ +#ifndef NATIVESCRIPT_FFI_QUICKJS_SIGNATURE_DISPATCH_H +#define NATIVESCRIPT_FFI_QUICKJS_SIGNATURE_DISPATCH_H + +#include "ffi/objc/shared/SignatureDispatchCore.h" + +#if defined(__has_include) +#if __has_include("GeneratedSignatureDispatch.inc") +#include "GeneratedSignatureDispatch.inc" +#endif +#endif + +#include "ffi/objc/shared/PreparedSignatureDispatch.h" + +#endif // NATIVESCRIPT_FFI_QUICKJS_SIGNATURE_DISPATCH_H diff --git a/NativeScript/ffi/shared/direct/EmbeddedMetadata.mm b/NativeScript/ffi/objc/shared/MetadataState.mm similarity index 100% rename from NativeScript/ffi/shared/direct/EmbeddedMetadata.mm rename to NativeScript/ffi/objc/shared/MetadataState.mm diff --git a/NativeScript/ffi/objc/shared/NativeApiBackendConfig.h b/NativeScript/ffi/objc/shared/NativeApiBackendConfig.h new file mode 100644 index 000000000..a6c2044e8 --- /dev/null +++ b/NativeScript/ffi/objc/shared/NativeApiBackendConfig.h @@ -0,0 +1,32 @@ +#ifndef NATIVESCRIPT_FFI_SHARED_NATIVE_API_BACKEND_CONFIG_H +#define NATIVESCRIPT_FFI_SHARED_NATIVE_API_BACKEND_CONFIG_H + +#include +#include + +namespace nativescript { + +class NativeApiBackendScheduler { + public: + virtual ~NativeApiBackendScheduler() = default; + virtual void invokeOnJS(std::function task) = 0; + virtual void invokeOnUI(std::function task) = 0; +}; + +struct NativeApiBackendConfig { + const char* metadataPath = nullptr; + const void* metadataPtr = nullptr; + const char* globalName = "__nativeScriptNativeApi"; + std::shared_ptr scheduler = nullptr; + std::function)> nativeInvocationInvoker = nullptr; + std::function)> nativeCallbackInvoker = nullptr; + std::function)> runtimeCallbackInvoker = nullptr; + std::function)> jsThreadCallbackInvoker = nullptr; + std::function)> jsThreadAsyncCallbackInvoker = nullptr; + bool invokeCallbacksOnNativeCallerThread = false; + bool installGlobalSymbols = false; +}; + +} // namespace nativescript + +#endif // NATIVESCRIPT_FFI_SHARED_NATIVE_API_BACKEND_CONFIG_H diff --git a/NativeScript/ffi/objc/shared/PreparedSignatureDispatch.h b/NativeScript/ffi/objc/shared/PreparedSignatureDispatch.h new file mode 100644 index 000000000..c941006fe --- /dev/null +++ b/NativeScript/ffi/objc/shared/PreparedSignatureDispatch.h @@ -0,0 +1,80 @@ +#ifndef NATIVESCRIPT_FFI_SHARED_PREPARED_SIGNATURE_DISPATCH_H +#define NATIVESCRIPT_FFI_SHARED_PREPARED_SIGNATURE_DISPATCH_H + +#include "SignatureDispatchCore.h" + +#ifndef NS_GSD_BACKEND_PREPARED +#define NS_GSD_BACKEND_PREPARED 0 +#endif + +#ifndef NS_GSD_BACKEND_HERMES +#define NS_GSD_BACKEND_HERMES 0 +#endif + +#ifndef NS_GSD_BACKEND_NAPI +#define NS_GSD_BACKEND_NAPI 0 +#endif + +#ifndef NS_HAS_GENERATED_SIGNATURE_DISPATCH +#define NS_HAS_GENERATED_SIGNATURE_DISPATCH 0 +#endif + +#define NS_REQUIRES_GENERATED_SIGNATURE_DISPATCH \ + (NS_GSD_BACKEND_HERMES || NS_GSD_BACKEND_NAPI || NS_GSD_BACKEND_PREPARED) + +#if NS_REQUIRES_GENERATED_SIGNATURE_DISPATCH && \ + !NS_HAS_GENERATED_SIGNATURE_DISPATCH +#error GeneratedSignatureDispatch.inc did not enable this generated signature dispatch backend. +#endif + +#if !NS_HAS_GENERATED_SIGNATURE_DISPATCH +namespace nativescript { +inline constexpr ObjCDispatchEntry kGeneratedObjCDispatchEntries[] = { + {0, nullptr}}; +inline constexpr CFunctionDispatchEntry kGeneratedCFunctionDispatchEntries[] = { + {0, nullptr}}; +inline constexpr BlockDispatchEntry kGeneratedBlockDispatchEntries[] = { + {0, nullptr}}; +} // namespace nativescript +#endif + +namespace nativescript { + +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 BlockPreparedInvoker lookupBlockPreparedInvoker(uint64_t dispatchId) { + if (!isGeneratedDispatchEnabled()) { + return nullptr; + } + return lookupDispatchInvoker( + kGeneratedBlockDispatchEntries, dispatchId); +} + +inline bool isPreparedGeneratedDispatchRequired() { +#if NS_HAS_GENERATED_SIGNATURE_DISPATCH && \ + (NS_GSD_BACKEND_PREPARED || NS_GSD_BACKEND_HERMES) + return isGeneratedDispatchEnabled(); +#else + return false; +#endif +} + +} // namespace nativescript + +#endif // NATIVESCRIPT_FFI_SHARED_PREPARED_SIGNATURE_DISPATCH_H diff --git a/NativeScript/ffi/objc/shared/SignatureDispatchCore.h b/NativeScript/ffi/objc/shared/SignatureDispatchCore.h new file mode 100644 index 000000000..229347a74 --- /dev/null +++ b/NativeScript/ffi/objc/shared/SignatureDispatchCore.h @@ -0,0 +1,298 @@ +#ifndef NS_FFI_SHARED_SIGNATURE_DISPATCH_CORE_H +#define NS_FFI_SHARED_SIGNATURE_DISPATCH_CORE_H + +#include +#include +#include +#include +#include + +#include "Metadata.h" +#include "MetadataReader.h" + +namespace nativescript { + +enum class SignatureCallKind : uint8_t { + ObjCMethod = 1, + CFunction = 2, + BlockInvoke = 3, +}; + +using ObjCPreparedInvoker = void (*)(void* fnptr, void** avalues, + void* rvalue); +using CFunctionPreparedInvoker = void (*)(void* fnptr, void** avalues, + void* rvalue); +using BlockPreparedInvoker = void (*)(void* fnptr, void** avalues, + void* rvalue); + +struct ObjCDispatchEntry { + uint64_t dispatchId; + ObjCPreparedInvoker invoker; +}; + +struct CFunctionDispatchEntry { + uint64_t dispatchId; + CFunctionPreparedInvoker invoker; +}; + +struct BlockDispatchEntry { + uint64_t dispatchId; + BlockPreparedInvoker invoker; +}; + +inline constexpr uint64_t kSignatureHashOffsetBasis = 14695981039346656037ull; +inline constexpr uint64_t kSignatureHashPrime = 1099511628211ull; +inline constexpr metagen::MDSectionOffset kNullMetadataSectionOffset = + static_cast(0xFFFFFFFFu >> 1); + +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); +} + +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"); + return disableFlag == nullptr || disableFlag[0] == '\0' || + (disableFlag[0] == '0' && disableFlag[1] == '\0'); + }(); + return enabled; +} + +namespace signature_dispatch_detail { + +inline metagen::MDTypeKind canonicalizeSignatureTypeKind( + metagen::MDTypeKind kind) { + switch (kind) { + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: + return metagen::mdTypeAnyObject; + default: + return kind; + } +} + +template +inline 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); + } +} + +inline metagen::MDTypeKind stripMetadataTypeFlags(metagen::MDTypeKind kind) { + uint8_t raw = static_cast(kind); + raw &= ~(metagen::mdTypeFlagNext | metagen::mdTypeFlagVariadic); + return static_cast(raw); +} + +inline bool appendMetadataSignatureHash( + metagen::MDMetadataReader* reader, metagen::MDSectionOffset signatureOffset, + std::unordered_set* activeSignatures, + uint64_t* hash); + +inline bool appendMetadataTypeHash( + metagen::MDMetadataReader* reader, metagen::MDSectionOffset* offset, + std::unordered_set* activeSignatures, + uint64_t* hash) { + if (reader == nullptr || offset == nullptr || hash == nullptr || + activeSignatures == nullptr) { + return false; + } + + const metagen::MDTypeKind kindWithFlags = reader->getTypeKind(*offset); + *offset += sizeof(metagen::MDTypeKind); + const metagen::MDTypeKind rawKind = stripMetadataTypeFlags(kindWithFlags); + + appendIntegralToHash(hash, 0xB0); + appendIntegralToHash( + hash, static_cast(canonicalizeSignatureTypeKind(rawKind))); + + switch (rawKind) { + case metagen::mdTypeArray: + case metagen::mdTypeVector: + case metagen::mdTypeExtVector: + case metagen::mdTypeComplex: { + const auto arraySize = reader->getArraySize(*offset); + *offset += sizeof(uint16_t); + appendIntegralToHash(hash, arraySize); + if (!appendMetadataTypeHash(reader, offset, activeSignatures, hash)) { + return false; + } + break; + } + + case metagen::mdTypeStruct: { + const auto structOffset = reader->getOffset(*offset); + *offset += sizeof(metagen::MDSectionOffset); + appendIntegralToHash(hash, structOffset); + break; + } + + case metagen::mdTypeClassObject: { + auto classOffset = reader->getOffset(*offset); + *offset += sizeof(metagen::MDSectionOffset); + bool hasNext = (classOffset & metagen::mdSectionOffsetNext) != 0; + while (hasNext) { + auto protocolOffset = reader->getOffset(*offset); + *offset += sizeof(metagen::MDSectionOffset); + hasNext = (protocolOffset & metagen::mdSectionOffsetNext) != 0; + } + break; + } + + case metagen::mdTypeProtocolObject: { + bool hasNext = true; + while (hasNext) { + auto protocolOffset = reader->getOffset(*offset); + *offset += sizeof(metagen::MDSectionOffset); + hasNext = (protocolOffset & metagen::mdSectionOffsetNext) != 0; + } + break; + } + + case metagen::mdTypePointer: + if (!appendMetadataTypeHash(reader, offset, activeSignatures, hash)) { + return false; + } + break; + + case metagen::mdTypeBlock: + case metagen::mdTypeFunctionPointer: { + const auto nestedSignatureOffset = reader->getOffset(*offset); + *offset += sizeof(metagen::MDSectionOffset); + if (nestedSignatureOffset != kNullMetadataSectionOffset) { + const auto nestedAbsoluteOffset = + reader->signaturesOffset + nestedSignatureOffset; + if (!appendMetadataSignatureHash(reader, nestedAbsoluteOffset, + activeSignatures, hash)) { + return false; + } + } + break; + } + + default: + break; + } + + appendIntegralToHash(hash, 0xBF); + return true; +} + +inline bool appendMetadataSignatureHash( + metagen::MDMetadataReader* reader, metagen::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); + + metagen::MDSectionOffset offset = signatureOffset; + const metagen::MDTypeKind returnTypeKind = reader->getTypeKind(offset); + bool next = + (static_cast(returnTypeKind) & metagen::mdTypeFlagNext) != 0; + const bool isVariadic = + (static_cast(returnTypeKind) & metagen::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 metagen::MDTypeKind argTypeKind = reader->getTypeKind(offset); + next = + (static_cast(argTypeKind) & metagen::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 signature_dispatch_detail + +inline uint64_t metadataSignatureHash( + metagen::MDMetadataReader* reader, + metagen::MDSectionOffset signatureOffset) { + if (reader == nullptr || signatureOffset == kNullMetadataSectionOffset) { + return 0; + } + + uint64_t hash = kSignatureHashOffsetBasis; + std::unordered_set activeSignatures; + if (!signature_dispatch_detail::appendMetadataSignatureHash( + reader, signatureOffset, &activeSignatures, &hash)) { + return 0; + } + return hash; +} + +} // namespace nativescript + +#endif // NS_FFI_SHARED_SIGNATURE_DISPATCH_CORE_H diff --git a/NativeScript/ffi/shared/Tasks.cpp b/NativeScript/ffi/objc/shared/Tasks.cpp similarity index 100% rename from NativeScript/ffi/shared/Tasks.cpp rename to NativeScript/ffi/objc/shared/Tasks.cpp diff --git a/NativeScript/ffi/shared/Tasks.h b/NativeScript/ffi/objc/shared/Tasks.h similarity index 100% rename from NativeScript/ffi/shared/Tasks.h rename to NativeScript/ffi/objc/shared/Tasks.h diff --git a/NativeScript/ffi/objc/shared/bridge/Callbacks.mm b/NativeScript/ffi/objc/shared/bridge/Callbacks.mm new file mode 100644 index 000000000..9f9d2c0cd --- /dev/null +++ b/NativeScript/ffi/objc/shared/bridge/Callbacks.mm @@ -0,0 +1,2274 @@ +bool isObjectiveCObjectType(const NativeApiType& type) { + switch (type.kind) { + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: + return true; + default: + return false; + } +} + +#ifndef NATIVESCRIPT_NATIVE_API_RETAIN_RUNTIME +std::shared_ptr retainNativeApiRuntime(Runtime& runtime) { + return std::shared_ptr(&runtime, [](Runtime*) {}); +} +#endif + +#ifndef NATIVESCRIPT_NATIVE_API_RUNTIME_SCOPE +class NativeApiRuntimeScope final { + public: + explicit NativeApiRuntimeScope(Runtime&) {} +}; +#endif + +struct NativeApiSignature { + ffi_cif cif = {}; + NativeApiType returnType; + std::vector argumentTypes; + std::vector ffiTypes; + std::string selectorName; + uint64_t signatureHash = 0; + uint8_t dispatchFlags = 0; + bool variadic = false; + bool prepared = false; + unsigned int implicitArgumentCount = 0; +}; + +enum class NativeApiCallbackThreadPolicy { + Default, + JS, + Runtime, +}; + +NativeApiCallbackThreadPolicy readEngineCallbackThreadPolicy( + Runtime& runtime, Object& functionObject) { + constexpr const char* propertyName = "__nativeScriptCallbackThread"; + try { + if (!functionObject.hasProperty(runtime, propertyName)) { + return NativeApiCallbackThreadPolicy::Default; + } + Value policyValue = functionObject.getProperty(runtime, propertyName); + if (!policyValue.isString()) { + return NativeApiCallbackThreadPolicy::Default; + } + std::string policy = policyValue.asString(runtime).utf8(runtime); + if (policy == "js") { + return NativeApiCallbackThreadPolicy::JS; + } + if (policy == "runtime" || policy == "worklet") { + return NativeApiCallbackThreadPolicy::Runtime; + } + } catch (const std::exception&) { + } + return NativeApiCallbackThreadPolicy::Default; +} + +bool selectorEndsWithNSErrorParam(const std::string& selectorName) { + constexpr const char* suffix = "error:"; + size_t suffixLength = std::strlen(suffix); + return selectorName.size() >= suffixLength && + selectorName.compare(selectorName.size() - suffixLength, suffixLength, + suffix) == 0; +} + +bool isNSErrorOutEngineMethodSignature(const NativeApiSignature& signature) { + if (signature.argumentTypes.empty() || signature.variadic || + !selectorEndsWithNSErrorParam(signature.selectorName)) { + return false; + } + + return signature.argumentTypes.back().kind == metagen::mdTypePointer; +} + +bool isNSErrorOutEngineMethodCallback(const NativeApiSignature& signature) { + return signature.returnType.kind == metagen::mdTypeBool && + signature.implicitArgumentCount >= 2 && + isNSErrorOutEngineMethodSignature(signature); +} + +class NativeApiArgumentFrame { + public: + explicit NativeApiArgumentFrame(size_t count) : count_(count) { + if (count_ > kInlineArgumentCount) { + heapStorage_.resize(count_); + heapValues_.resize(count_); + } + } + + ~NativeApiArgumentFrame() { + for (char* string : ownedCStrings_) { + free(string); + } + for (void* buffer : ownedBuffers_) { + free(buffer); + } + for (id object : ownedObjects_) { + [object release]; + } + for (const auto& entry : temporaryRoundTripValues_) { + if (entry.bridge != nullptr && entry.runtime != nullptr) { + entry.bridge->forgetRoundTripValue(*entry.runtime, entry.native); + } + } + ownedLifetimes_.clear(); + } + + void* storageAt(size_t index, size_t size) { + if (index >= count_) { + throw std::out_of_range("Native argument index out of range."); + } + + size = std::max(size, sizeof(void*)); + if (count_ <= kInlineArgumentCount && size <= kInlineStorageSize) { + std::memset(inlineStorage_[index], 0, kInlineStorageSize); + inlineValues_[index] = inlineStorage_[index]; + return inlineValues_[index]; + } + + if (count_ <= kInlineArgumentCount) { + overflowStorage_.emplace_back(size, 0); + inlineValues_[index] = overflowStorage_.back().data(); + return inlineValues_[index]; + } + + heapStorage_[index].assign(size, 0); + heapValues_[index] = heapStorage_[index].data(); + return heapValues_[index]; + } + + void addCString(char* value) { ownedCStrings_.push_back(value); } + void* addBuffer(size_t size) { + void* buffer = calloc(1, std::max(size, 1)); + if (buffer == nullptr) { + throw std::bad_alloc(); + } + ownedBuffers_.push_back(buffer); + return buffer; + } + void addObject(id value) { ownedObjects_.push_back(value); } + void retainObject(id value) { + if (value != nil) { + [value retain]; + ownedObjects_.push_back(value); + } + } + void addLifetime(std::shared_ptr value) { + if (value != nullptr) { + ownedLifetimes_.push_back(std::move(value)); + } + } + void rememberRoundTripValue( + const std::shared_ptr& bridge, Runtime& runtime, + const void* native, const Value& value) { + if (bridge == nullptr || native == nullptr) { + return; + } + bridge->rememberRoundTripValue(runtime, native, value); + temporaryRoundTripValues_.push_back({bridge, &runtime, native}); + } + void** values() { + if (count_ == 0) { + return nullptr; + } + return count_ <= kInlineArgumentCount ? inlineValues_ : heapValues_.data(); + } + + private: + static constexpr size_t kInlineArgumentCount = 8; + static constexpr size_t kInlineStorageSize = 32; + + size_t count_ = 0; + alignas(void*) unsigned char + inlineStorage_[kInlineArgumentCount][kInlineStorageSize] = {}; + void* inlineValues_[kInlineArgumentCount] = {}; + std::vector> heapStorage_; + std::vector heapValues_; + std::vector> overflowStorage_; + std::vector ownedCStrings_; + std::vector ownedBuffers_; + std::vector ownedObjects_; + std::vector> ownedLifetimes_; + struct TemporaryRoundTripValue { + std::shared_ptr bridge; + Runtime* runtime = nullptr; + const void* native = nullptr; + }; + std::vector temporaryRoundTripValues_; +}; + +class NativeApiMutableBuffer final : public MutableBuffer { + public: + explicit NativeApiMutableBuffer(size_t size) : data_(size) {} + NativeApiMutableBuffer(const void* data, size_t size) : data_(size) { + if (data != nullptr && size > 0) { + std::memcpy(data_.data(), data, size); + } + } + + size_t size() const override { return data_.size(); } + uint8_t* data() override { return data_.empty() ? nullptr : data_.data(); } + + private: + std::vector data_; +}; + +void convertEngineArgument(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiType& type, + const Value& value, void* target, + NativeApiArgumentFrame& frame); + +Value convertNativeReturnValue(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiType& type, void* value); + +Value wrapNativeFunctionPointer(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiType& type, void* pointer, + bool block); + +bool isObjectiveCObjectType(const NativeApiType& type); + +struct NativeApiBlockDescriptor { + unsigned long reserved = 0; + unsigned long size = 0; + void (*copyHelper)(void*, void*) = nullptr; + void (*disposeHelper)(void*) = nullptr; + const char* signature = nullptr; +}; + +struct NativeApiBlockLiteral { + void* isa = nullptr; + int flags = 0; + int reserved = 0; + void* invoke = nullptr; + NativeApiBlockDescriptor* descriptor = nullptr; + void* callback = nullptr; +}; + +constexpr int kNativeApiBlockNeedsFree = (1 << 24); +constexpr int kNativeApiBlockHasCopyDispose = (1 << 25); +constexpr int kNativeApiBlockRefCountOne = (1 << 1); +constexpr int kNativeApiBlockHasSignature = (1 << 30); + +void* nativeApiEngineMallocBlockIsa() { + static void* isa = dlsym(RTLD_DEFAULT, "_NSConcreteMallocBlock"); + return isa; +} + +void nativeApiEngineBlockCopy(void* dst, void* src); +void nativeApiEngineBlockDispose(void* src); + +std::string objcEncodingForEngineType(const NativeApiType& type) { + switch (type.kind) { + case metagen::mdTypeVoid: + return "v"; + case metagen::mdTypeBool: + return "B"; + case metagen::mdTypeChar: + return "c"; + case metagen::mdTypeUChar: + case metagen::mdTypeUInt8: + return "C"; + case metagen::mdTypeSShort: + return "s"; + case metagen::mdTypeUShort: + return "S"; + case metagen::mdTypeSInt: + return "i"; + case metagen::mdTypeUInt: + return "I"; + case metagen::mdTypeSLong: + case metagen::mdTypeSInt64: + return "q"; + case metagen::mdTypeULong: + case metagen::mdTypeUInt64: + return "Q"; + case metagen::mdTypeFloat: + return "f"; + case metagen::mdTypeDouble: + return "d"; + case metagen::mdTypeString: + return "*"; + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: + return "@"; + case metagen::mdTypeClass: + return "#"; + case metagen::mdTypeSelector: + return ":"; + case metagen::mdTypeBlock: + return "@?"; + case metagen::mdTypeFunctionPointer: + return "^?"; + case metagen::mdTypePointer: + case metagen::mdTypeOpaquePointer: + if (type.elementType != nullptr && + type.elementType->kind != metagen::mdTypeVoid) { + return "^" + objcEncodingForEngineType(*type.elementType); + } + return "^v"; + case metagen::mdTypeStruct: + return "{" + + (type.aggregateInfo != nullptr ? type.aggregateInfo->name + : std::string("?")) + + "=}"; + case metagen::mdTypeArray: + return "[" + std::to_string(type.arraySize) + + (type.elementType != nullptr ? objcEncodingForEngineType(*type.elementType) + : std::string("?")) + + "]"; + case metagen::mdTypeVector: + case metagen::mdTypeExtVector: + case metagen::mdTypeComplex: + return type.elementType != nullptr ? objcEncodingForEngineType(*type.elementType) + : "?"; + default: + return "?"; + } +} + +std::string objcBlockSignatureForEngineSignature( + const NativeApiSignature& signature) { + std::string encoding = objcEncodingForEngineType(signature.returnType); + encoding += "@?"; + for (const auto& argType : signature.argumentTypes) { + encoding += objcEncodingForEngineType(argType); + } + return encoding; +} + +std::string objcMethodSignatureForEngineSignature( + const NativeApiSignature& signature) { + std::string encoding = objcEncodingForEngineType(signature.returnType); + encoding += "@:"; + for (const auto& argType : signature.argumentTypes) { + encoding += objcEncodingForEngineType(argType); + } + return encoding; +} + +[[noreturn]] void throwNativeApiCallbackException( + const std::string& message) { + NSString* reason = [NSString stringWithUTF8String:message.c_str()]; + @throw [NSException exceptionWithName:@"NativeScriptEngineCallbackException" + reason:reason + userInfo:nil]; +} + +class NativeApiCallback; + +void nativeApiEngineCallbackTrampoline(ffi_cif* cif, void* ret, void* args[], + void* data); + +std::atomic gActiveNativeThreadEngineCallbacks{0}; + +// A callback can outlive the scope in which its function argument was created +// (e.g. a block invoked asynchronously). Round-trip the function through the +// engine value copy constructor so any scope-bound/borrowed handle is promoted +// to a persistent one before it is stored. +Function persistentEngineFunction(Runtime& runtime, const Function& function) { + Value shared(runtime, function); + Value persistent(runtime, shared); + return persistent.asObject(runtime).asFunction(runtime); +} + +class NativeApiCallback final + : public std::enable_shared_from_this { + public: + NativeApiCallback(Runtime& runtime, + std::shared_ptr bridge, + std::shared_ptr signature, + Function function, bool block, + NativeApiCallbackThreadPolicy threadPolicy = + NativeApiCallbackThreadPolicy::Default, + bool bindThis = false, + uintptr_t roundTripValidationKey = 0) + : runtimeOwner_(retainNativeApiRuntime(runtime)), + runtime_(runtimeOwner_.get()), + bridge_(std::move(bridge)), + signature_(std::move(signature)), + function_(std::make_shared( + persistentEngineFunction(runtime, function))), + block_(block), + threadPolicy_(threadPolicy), + bindThis_(bindThis), + roundTripValidationKey_(roundTripValidationKey) { + closure_ = static_cast( + ffi_closure_alloc(sizeof(ffi_closure), &executable_)); + if (closure_ == nullptr || executable_ == nullptr || + signature_ == nullptr || !signature_->prepared) { + throw JSError(runtime, + "Unable to allocate native callback."); + } + + ffi_status status = ffi_prep_closure_loc( + closure_, &signature_->cif, nativeApiEngineCallbackTrampoline, this, + executable_); + if (status != FFI_OK) { + ffi_closure_free(closure_); + closure_ = nullptr; + executable_ = nullptr; + throw JSError(runtime, + "Unable to prepare native callback."); + } + + if (block_) { + blockSignature_ = objcBlockSignatureForEngineSignature(*signature_); + descriptor_ = std::make_unique(); + descriptor_->reserved = 0; + descriptor_->size = sizeof(NativeApiBlockLiteral); + descriptor_->copyHelper = nativeApiEngineBlockCopy; + descriptor_->disposeHelper = nativeApiEngineBlockDispose; + descriptor_->signature = blockSignature_.c_str(); + + blockLiteral_ = static_cast( + calloc(1, sizeof(NativeApiBlockLiteral))); + if (blockLiteral_ == nullptr) { + throw JSError(runtime, "Unable to allocate native block callback."); + } + void* blockIsa = nativeApiEngineMallocBlockIsa(); + if (blockIsa == nullptr) { + free(blockLiteral_); + blockLiteral_ = nullptr; + throw JSError(runtime, + "Objective-C malloc block runtime is unavailable."); + } + blockLiteral_->isa = blockIsa; + blockLiteral_->flags = kNativeApiBlockNeedsFree | + kNativeApiBlockHasCopyDispose | + kNativeApiBlockRefCountOne | + kNativeApiBlockHasSignature; + blockLiteral_->invoke = executable_; + blockLiteral_->descriptor = descriptor_.get(); + blockLiteral_->callback = this; + } + } + + ~NativeApiCallback() { + if (closure_ != nullptr) { + ffi_closure_free(closure_); + closure_ = nullptr; + executable_ = nullptr; + } + } + + void* functionPointer() const { + return block_ && blockLiteral_ != nullptr + ? static_cast(blockLiteral_) + : executable_; + } + + const NativeApiSignature& signature() const { return *signature_; } + + void retainInitialBlockLifetime( + std::shared_ptr lifetime) { + if (block_) { + initialBlockLifetime_ = std::move(lifetime); + } + } + + void retainBlockCopy(const void* blockPointer) { + if (!block_) { + return; + } + auto self = shared_from_this(); + if (bridge_ != nullptr && runtime_ != nullptr && function_ != nullptr && + blockPointer != nullptr) { + bridge_->rememberRoundTripValue(*runtime_, blockPointer, + Value(*runtime_, *function_), false, + roundTripValidationKey_); + } + std::lock_guard lock(retainedBlockCopiesMutex_); + retainedBlockCopies_.push_back({blockPointer, std::move(self)}); + } + + bool releaseBlockCopy(const void* blockPointer) { + if (!block_) { + return false; + } + + bool canRelease = false; + { + std::lock_guard lock(retainedBlockCopiesMutex_); + auto it = retainedBlockCopies_.end(); + if (blockPointer != nullptr) { + it = std::find_if( + retainedBlockCopies_.begin(), retainedBlockCopies_.end(), + [blockPointer](const RetainedBlockCopy& retained) { + return retained.blockPointer == blockPointer; + }); + } + canRelease = + it != retainedBlockCopies_.end() || blockPointer == blockLiteral_; + } + // Forgetting the round-trip value touches the JS engine global/context. + // Block disposal can run during an autorelease-pool drain on an arbitrary + // thread (e.g. an NSOperationQueue worker). Keep the retained block entry + // in place until the JS-thread task runs so the callback and its engine + // function are also destroyed on the JS thread. + if (!canRelease) { + return false; + } + + auto bridge = bridge_; + auto* runtime = runtime_; + auto runtimeOwner = runtimeOwner_; + auto releaseOnJS = [this, bridge, runtime, runtimeOwner, blockPointer]() { + std::shared_ptr keepAlive; + try { + keepAlive = shared_from_this(); + } catch (const std::bad_weak_ptr&) { + return; + } + + const void* pointerToForget = nullptr; + { + std::lock_guard lock(retainedBlockCopiesMutex_); + auto it = retainedBlockCopies_.end(); + if (blockPointer != nullptr) { + it = std::find_if( + retainedBlockCopies_.begin(), retainedBlockCopies_.end(), + [blockPointer](const RetainedBlockCopy& retained) { + return retained.blockPointer == blockPointer; + }); + } + if (it != retainedBlockCopies_.end()) { + pointerToForget = it->blockPointer; + retainedBlockCopies_.erase(it); + } else if (blockPointer == blockLiteral_) { + pointerToForget = blockPointer; + blockLiteral_ = nullptr; + initialBlockLifetime_.reset(); + } + } + + if (bridge != nullptr && runtime != nullptr && + pointerToForget != nullptr) { + NativeApiRuntimeScope runtimeScope(*runtime); + bridge->forgetRoundTripValue(*runtime, pointerToForget); + } + }; + + if (bridge == nullptr) { + releaseOnJS(); + } else if (const auto& asyncInvoker = + bridge->jsThreadAsyncCallbackInvoker()) { + asyncInvoker(std::move(releaseOnJS)); + } else if (auto scheduler = bridge->scheduler()) { + scheduler->invokeOnJS(std::move(releaseOnJS)); + } else if (std::this_thread::get_id() == bridge->jsThreadId()) { + releaseOnJS(); + } else if (const auto& invoker = bridge->jsThreadCallbackInvoker()) { + invoker(std::move(releaseOnJS)); + } else { + releaseOnJS(); + } + return true; + } + + void invoke(void* ret, void* args[]) { + if (runtime_ == nullptr || function_ == nullptr || signature_ == nullptr) { + throwNativeApiCallbackException("Invalid callback."); + } + + std::string error; + auto call = [&]() { invokeOnCurrentThread(ret, args, &error); }; + const auto& nativeCallbackInvoker = bridge_->nativeCallbackInvoker(); + const auto& runtimeCallbackInvoker = bridge_->runtimeCallbackInvoker(); + const auto& jsThreadCallbackInvoker = bridge_->jsThreadCallbackInvoker(); + bool currentThreadIsJs = + std::this_thread::get_id() == bridge_->jsThreadId(); + + auto callOnNativeCallerThread = [&]() { + ScopedNativeCallerThreadEngineCallback callbackScope; + if (nativeCallbackInvoker) { + nativeCallbackInvoker(call); + } else { + call(); + } + }; + auto callOnJSThread = [&]() { + if (currentThreadIsJs) { + call(); + return; + } + if (jsThreadCallbackInvoker) { + jsThreadCallbackInvoker(call); + return; + } + if (auto scheduler = bridge_->scheduler()) { + dispatch_semaphore_t done = dispatch_semaphore_create(0); + scheduler->invokeOnJS([call, done]() mutable { + call(); + dispatch_semaphore_signal(done); + }); + dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER); + return; + } + error = "Native callback was invoked off the JS thread without a JS scheduler."; + }; + auto callOnRuntimeThread = [&]() { + if (currentThreadIsJs) { + call(); + return; + } + if (runtimeCallbackInvoker) { + runtimeCallbackInvoker(call); + return; + } + error = "Native callback was invoked off its owning runtime thread without a runtime scheduler."; + }; + + if (threadPolicy_ == NativeApiCallbackThreadPolicy::JS) { + callOnJSThread(); + if (!error.empty()) { + if (!recordNativeCallbackException(error)) { + throwNativeApiCallbackException(error); + } + } + return; + } + if (threadPolicy_ == NativeApiCallbackThreadPolicy::Runtime) { + callOnRuntimeThread(); + if (!error.empty()) { + if (!recordNativeCallbackException(error)) { + throwNativeApiCallbackException(error); + } + } + return; + } + + bool returnsVoid = signature_->returnType.kind == metagen::mdTypeVoid; + bool nativeCallerThreadCallbacks = + bridge_->invokeCallbacksOnNativeCallerThread(); + bool direct = currentThreadIsJs || + gSynchronousNativeInvocationDepth > 0; + bool waitForNativeThreadCallback = + currentThreadIsJs && nativeCallbackInvoker && + gActiveNativeThreadEngineCallbacks.load(std::memory_order_acquire) > 0; + auto dispatchZeroArgVoidBlockAsync = [&]() -> bool { + if (currentThreadIsJs || !returnsVoid || !block_ || + !signature_->argumentTypes.empty()) { + return false; + } + + std::shared_ptr keepAlive; + try { + keepAlive = shared_from_this(); + } catch (const std::bad_weak_ptr&) { + return false; + } + + auto asyncCall = [keepAlive = std::move(keepAlive)]() mutable { + std::string asyncError; + keepAlive->invokeOnCurrentThread(nullptr, nullptr, &asyncError); + if (!asyncError.empty()) { + recordNativeCallbackException(asyncError); + } + }; + + const auto& asyncInvoker = bridge_->jsThreadAsyncCallbackInvoker(); + if (asyncInvoker) { + asyncInvoker(std::move(asyncCall)); + return true; + } + if (auto scheduler = bridge_->scheduler()) { + scheduler->invokeOnJS(std::move(asyncCall)); + return true; + } + return false; + }; + + if (nativeCallerThreadCallbacks && !currentThreadIsJs) { + callOnNativeCallerThread(); + } else if (dispatchZeroArgVoidBlockAsync()) { + return; + } else if (direct && !waitForNativeThreadCallback) { + call(); + } else if (!currentThreadIsJs) { + callOnJSThread(); + } else if (nativeCallbackInvoker) { + bool nativeThreadCallback = !currentThreadIsJs; + if (nativeThreadCallback) { + gActiveNativeThreadEngineCallbacks.fetch_add(1, + std::memory_order_acq_rel); + } + try { + nativeCallbackInvoker(call); + } catch (...) { + if (nativeThreadCallback) { + gActiveNativeThreadEngineCallbacks.fetch_sub( + 1, std::memory_order_acq_rel); + } + throw; + } + if (nativeThreadCallback) { + gActiveNativeThreadEngineCallbacks.fetch_sub(1, + std::memory_order_acq_rel); + } + } else if (auto scheduler = bridge_->scheduler()) { + dispatch_semaphore_t done = dispatch_semaphore_create(0); + scheduler->invokeOnJS([call, done]() mutable { + call(); + dispatch_semaphore_signal(done); + }); + dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER); + } else { + error = "Native callback was invoked off the JS thread without a JS scheduler."; + } + + if (!error.empty()) { + if (!recordNativeCallbackException(error)) { + throwNativeApiCallbackException(error); + } + } + } + + private: + void invokeOnCurrentThread(void* ret, void* args[], std::string* error) { + try { + NativeApiRuntimeScope runtimeScope(*runtime_); + size_t nativeArgOffset = signature_->implicitArgumentCount; + std::vector jsArgs; + jsArgs.reserve(signature_->argumentTypes.size()); + for (size_t i = 0; i < signature_->argumentTypes.size(); i++) { + jsArgs.emplace_back(convertNativeReturnValue( + *runtime_, bridge_, signature_->argumentTypes[i], + args[i + nativeArgOffset])); + } + + Value result = Value::undefined(); + if (bindThis_ && nativeArgOffset >= 1) { + id self = *static_cast(args[0]); + Value thisValue = + makeNativeObjectValue(*runtime_, bridge_, self, false); + Object thisObject = thisValue.isObject() + ? thisValue.asObject(*runtime_) + : Object(*runtime_); + result = + jsArgs.empty() + ? function_->callWithThis(*runtime_, thisObject) + : function_->callWithThis( + *runtime_, thisObject, + static_cast(jsArgs.data()), + static_cast(jsArgs.size())); + } else { + result = + jsArgs.empty() + ? function_->call(*runtime_) + : function_->call(*runtime_, + static_cast(jsArgs.data()), + static_cast(jsArgs.size())); + } + storeReturnValue(result, ret); + if (std::this_thread::get_id() == bridge_->jsThreadId() && + gSynchronousNativeInvocationDepth == 0) { + runtime_->drainMicrotasks(); + } + } catch (const std::exception& exception) { + if (isNSErrorOutEngineMethodCallback(*signature_)) { + zeroReturnValue(ret); + populateNSErrorOutArgument(args, exception.what()); + return; + } + if (error != nullptr) { + *error = exception.what(); + } + zeroReturnValue(ret); + } catch (...) { + if (isNSErrorOutEngineMethodCallback(*signature_)) { + zeroReturnValue(ret); + populateNSErrorOutArgument(args, "Unknown exception in native callback."); + return; + } + if (error != nullptr) { + *error = "Unknown exception in native callback."; + } + zeroReturnValue(ret); + } + } + + void populateNSErrorOutArgument(void* args[], const char* message) { + if (args == nullptr || signature_ == nullptr || + signature_->argumentTypes.empty()) { + return; + } + + size_t outArgIndex = signature_->implicitArgumentCount + + signature_->argumentTypes.size() - 1; + void* outArgValue = args[outArgIndex]; + NSError** outError = + outArgValue != nullptr ? *reinterpret_cast(outArgValue) + : nullptr; + if (outError == nullptr) { + return; + } + + NSString* nsMessage = + message != nullptr ? [NSString stringWithUTF8String:message] : nil; + if (nsMessage == nil) { + nsMessage = @"JS error"; + } + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : nsMessage}; + *outError = [NSError errorWithDomain:@"TNSErrorDomain" + code:1 + userInfo:userInfo]; + } + + void zeroReturnValue(void* ret) { + if (ret == nullptr || signature_ == nullptr || + signature_->returnType.kind == metagen::mdTypeVoid) { + return; + } + size_t size = nativeSizeForType(signature_->returnType); + if (size > 0) { + std::memset(ret, 0, size); + } + } + + void storeReturnValue(const Value& result, void* ret) { + if (ret == nullptr || + signature_->returnType.kind == metagen::mdTypeVoid) { + return; + } + + zeroReturnValue(ret); + if (result.isUndefined() || result.isNull()) { + return; + } + const auto& returnType = signature_->returnType; + if (returnType.kind == metagen::mdTypeString && result.isString()) { + std::string utf8 = result.asString(*runtime_).utf8(*runtime_); + *static_cast(ret) = strdup(utf8.c_str()); + return; + } + if ((returnType.kind == metagen::mdTypePointer || + returnType.kind == metagen::mdTypeOpaquePointer) && + result.isString()) { + std::string utf8 = result.asString(*runtime_).utf8(*runtime_); + *static_cast(ret) = strdup(utf8.c_str()); + return; + } + + NativeApiArgumentFrame frame(1); + convertEngineArgument(*runtime_, bridge_, returnType, result, ret, frame); + if (isObjectiveCObjectType(returnType)) { + id object = *static_cast(ret); + if (object != nil) { + [object retain]; + [object autorelease]; + } + } + } + + std::shared_ptr runtimeOwner_; + Runtime* runtime_ = nullptr; + std::shared_ptr bridge_; + std::shared_ptr signature_; + std::shared_ptr function_; + bool block_ = false; + NativeApiCallbackThreadPolicy threadPolicy_ = + NativeApiCallbackThreadPolicy::Default; + bool bindThis_ = false; + uintptr_t roundTripValidationKey_ = 0; + ffi_closure* closure_ = nullptr; + void* executable_ = nullptr; + std::string blockSignature_; + std::unique_ptr descriptor_; + NativeApiBlockLiteral* blockLiteral_ = nullptr; + std::shared_ptr initialBlockLifetime_; + struct RetainedBlockCopy { + const void* blockPointer = nullptr; + std::shared_ptr lifetime; + }; + std::mutex retainedBlockCopiesMutex_; + std::vector retainedBlockCopies_; +}; + +void nativeApiEngineBlockCopy(void* dst, void* src) { + auto* dstBlock = static_cast(dst); + auto* srcBlock = static_cast(src); + if (dstBlock == nullptr || srcBlock == nullptr || + srcBlock->callback == nullptr) { + return; + } + dstBlock->callback = srcBlock->callback; + static_cast(srcBlock->callback) + ->retainBlockCopy(dstBlock); +} + +void nativeApiEngineBlockDispose(void* src) { + auto* block = static_cast(src); + if (block == nullptr || block->callback == nullptr) { + return; + } + bool released = + static_cast(block->callback)->releaseBlockCopy(block); + if (released) { + block->callback = nullptr; + } +} + +void nativeApiEngineCallbackTrampoline(ffi_cif*, void* ret, void* args[], + void* data) { + auto callback = static_cast(data); + if (callback == nullptr) { + return; + } + @try { + callback->invoke(ret, args); + } @catch (NSException* exception) { + const char* description = + exception.description != nil ? exception.description.UTF8String : nullptr; + std::string message = description != nullptr + ? description + : "Objective-C exception in native callback."; + if (!recordNativeCallbackException(message)) { + @throw; + } + } +} + +size_t nativeSizeForType(const NativeApiType& type) { + switch (type.kind) { + case metagen::mdTypeStruct: + if (type.aggregateInfo != nullptr) { + return type.aggregateInfo->size; + } + break; + case metagen::mdTypeArray: + if (type.elementType != nullptr) { + return nativeSizeForType(*type.elementType) * + static_cast(type.arraySize); + } + break; + case metagen::mdTypeVector: + case metagen::mdTypeExtVector: + case metagen::mdTypeComplex: + if (type.elementType != nullptr) { + size_t lanes = std::max(type.arraySize, 1); + size_t abiLanes = lanes == 3 ? 4 : lanes; + return nativeSizeForType(*type.elementType) * abiLanes; + } + break; + default: + break; + } + + if (type.ffiType != nullptr && type.ffiType->size > 0) { + return type.ffiType->size; + } + if (type.ffiType == &ffi_type_void) { + return 0; + } + return sizeof(void*); +} + +Value signedInteger64ToEngineValue(Runtime& runtime, int64_t value) { + constexpr int64_t maxSafeInteger = 9007199254740991LL; + constexpr int64_t minSafeInteger = -9007199254740991LL; + if (value >= minSafeInteger && value <= maxSafeInteger) { + return static_cast(value); + } + return BigInt::fromInt64(runtime, value); +} + +Value unsignedInteger64ToEngineValue(Runtime& runtime, uint64_t value) { + constexpr uint64_t maxSafeInteger = 9007199254740991ULL; + if (value <= maxSafeInteger) { + return static_cast(value); + } + return BigInt::fromUint64(runtime, value); +} + +bool parseIntegerTextToUintptr(const std::string& text, uintptr_t* address) { + if (address == nullptr) { + return false; + } + if (text.empty()) { + return false; + } + + char* end = nullptr; + if (text[0] == '-') { + long long signedValue = std::strtoll(text.c_str(), &end, 10); + if (end == nullptr || *end != '\0') { + return false; + } + *address = static_cast(static_cast(signedValue)); + return true; + } + + int base = 10; + const char* start = text.c_str(); + if (text.size() > 2 && text[0] == '0' && + (text[1] == 'x' || text[1] == 'X')) { + base = 16; + } + unsigned long long unsignedValue = std::strtoull(start, &end, base); + if (end == nullptr || *end != '\0') { + return false; + } + *address = static_cast(unsignedValue); + return true; +} + +bool parseBigIntToUintptr(Runtime& runtime, const BigInt& bigint, + uintptr_t* address) { + return parseIntegerTextToUintptr(bigint.toString(runtime, 10).utf8(runtime), + address); +} + +bool readEngineBuffer(Runtime& runtime, const Object& object, const uint8_t** data, + size_t* byteLength) { + if (data == nullptr || byteLength == nullptr) { + return false; + } + + if (object.isArrayBuffer(runtime)) { + ArrayBuffer buffer = object.getArrayBuffer(runtime); + *data = buffer.data(runtime); + *byteLength = buffer.size(runtime); + return true; + } + + Value bufferValue = object.getProperty(runtime, "buffer"); + if (!bufferValue.isObject()) { + return false; + } + Object bufferObject = bufferValue.asObject(runtime); + if (!bufferObject.isArrayBuffer(runtime)) { + return false; + } + + size_t byteOffset = 0; + size_t viewByteLength = 0; + Value offsetValue = object.getProperty(runtime, "byteOffset"); + if (offsetValue.isNumber()) { + byteOffset = static_cast(std::max(0, offsetValue.getNumber())); + } + Value lengthValue = object.getProperty(runtime, "byteLength"); + if (lengthValue.isNumber()) { + viewByteLength = static_cast(std::max(0, lengthValue.getNumber())); + } + + ArrayBuffer buffer = bufferObject.getArrayBuffer(runtime); + if (byteOffset > buffer.size(runtime)) { + return false; + } + if (viewByteLength == 0 || byteOffset + viewByteLength > buffer.size(runtime)) { + viewByteLength = buffer.size(runtime) - byteOffset; + } + *data = buffer.data(runtime) + byteOffset; + *byteLength = viewByteLength; + return true; +} + +uint32_t rawTypeKind(MDTypeKind kind) { + return static_cast(kind); +} + +MDTypeKind stripTypeFlags(MDTypeKind kind) { + uint32_t raw = rawTypeKind(kind); + raw &= ~static_cast(metagen::mdTypeFlagNext); + raw &= ~static_cast(metagen::mdTypeFlagVariadic); + return static_cast(raw); +} + +size_t alignUp(size_t value, size_t alignment) { + if (alignment == 0) { + return value; + } + return ((value + alignment - 1) / alignment) * alignment; +} + +ffi_type* ffiTypeForEngineKind(MDTypeKind kind) { + switch (kind) { + case metagen::mdTypeChar: + return &ffi_type_sint8; + case metagen::mdTypeUChar: + case metagen::mdTypeUInt8: + case metagen::mdTypeBool: + return &ffi_type_uint8; + case metagen::mdTypeSShort: + return &ffi_type_sint16; + case metagen::mdTypeUShort: + return &ffi_type_uint16; + case metagen::mdTypeSInt: + return &ffi_type_sint32; + case metagen::mdTypeUInt: + return &ffi_type_uint32; + case metagen::mdTypeSLong: + case metagen::mdTypeSInt64: + return &ffi_type_sint64; + case metagen::mdTypeULong: + case metagen::mdTypeUInt64: + return &ffi_type_uint64; + case metagen::mdTypeFloat: + return &ffi_type_float; + case metagen::mdTypeDouble: + return &ffi_type_double; + case metagen::mdTypeVoid: + return &ffi_type_void; + case metagen::mdTypeString: + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: + case metagen::mdTypeClass: + case metagen::mdTypeSelector: + case metagen::mdTypePointer: + case metagen::mdTypeOpaquePointer: + case metagen::mdTypeBlock: + case metagen::mdTypeFunctionPointer: + return &ffi_type_pointer; + default: + return nullptr; + } +} + +bool isSupportedEngineKind(MDTypeKind kind) { + switch (kind) { + default: + return ffiTypeForEngineKind(kind) != nullptr; + } +} + +void skipMetadataEngineTypePayload(MDMetadataReader* metadata, MDSectionOffset* offset, + MDTypeKind kind); + +void skipMetadataEngineType(MDMetadataReader* metadata, MDSectionOffset* offset) { + MDTypeKind kind = stripTypeFlags(metadata->getTypeKind(*offset)); + *offset += sizeof(MDTypeKind); + skipMetadataEngineTypePayload(metadata, offset, kind); +} + +void skipMetadataEngineTypePayload(MDMetadataReader* metadata, MDSectionOffset* offset, + MDTypeKind kind) { + switch (kind) { + case metagen::mdTypeClassObject: { + auto classOffset = metadata->getOffset(*offset); + *offset += sizeof(MDSectionOffset); + bool next = (classOffset & metagen::mdSectionOffsetNext) != 0; + while (next) { + auto protocolOffset = metadata->getOffset(*offset); + *offset += sizeof(MDSectionOffset); + next = (protocolOffset & metagen::mdSectionOffsetNext) != 0; + } + break; + } + case metagen::mdTypeProtocolObject: { + bool next = true; + while (next) { + auto protocolOffset = metadata->getOffset(*offset); + *offset += sizeof(MDSectionOffset); + next = (protocolOffset & metagen::mdSectionOffsetNext) != 0; + } + break; + } + case metagen::mdTypeArray: + case metagen::mdTypeVector: + case metagen::mdTypeExtVector: + case metagen::mdTypeComplex: + *offset += sizeof(uint16_t); + skipMetadataEngineType(metadata, offset); + break; + case metagen::mdTypeStruct: + *offset += sizeof(MDSectionOffset); + break; + case metagen::mdTypePointer: + skipMetadataEngineType(metadata, offset); + break; + case metagen::mdTypeBlock: + case metagen::mdTypeFunctionPointer: + *offset += sizeof(MDSectionOffset); + break; + default: + break; + } +} + +NativeApiType parseMetadataEngineType(MDMetadataReader* metadata, + MDSectionOffset* offset, + NativeApiBridge* bridge) { + MDTypeKind rawKind = metadata->getTypeKind(*offset); + MDTypeKind kind = stripTypeFlags(rawKind); + *offset += sizeof(MDTypeKind); + + NativeApiType type; + type.kind = kind; + + switch (kind) { + case metagen::mdTypeArray: { + type.arraySize = metadata->getArraySize(*offset); + *offset += sizeof(uint16_t); + type.elementType = + std::make_shared( + parseMetadataEngineType(metadata, offset, bridge)); + auto ffiOwner = std::make_shared(); + ffiOwner->elements.reserve(static_cast(type.arraySize) + 1); + ffi_type* elementFfiType = type.elementType->ffiType != nullptr + ? type.elementType->ffiType + : &ffi_type_pointer; + for (uint16_t i = 0; i < type.arraySize; i++) { + ffiOwner->elements.push_back(elementFfiType); + } + ffiOwner->finalize(); + type.ownedFfiType = ffiOwner; + type.ffiType = &ffiOwner->type; + type.supported = type.elementType->supported; + return type; + } + case metagen::mdTypeVector: + case metagen::mdTypeExtVector: + case metagen::mdTypeComplex: { + type.arraySize = metadata->getArraySize(*offset); + *offset += sizeof(uint16_t); + type.elementType = + std::make_shared( + parseMetadataEngineType(metadata, offset, bridge)); + auto ffiOwner = std::make_shared(); +#if defined(FFI_TYPE_EXT_VECTOR) + ffiOwner->type.type = + kind == metagen::mdTypeComplex ? FFI_TYPE_COMPLEX : FFI_TYPE_EXT_VECTOR; +#else + ffiOwner->type.type = + kind == metagen::mdTypeComplex ? FFI_TYPE_COMPLEX : FFI_TYPE_STRUCT; +#endif + ffi_type* elementFfiType = type.elementType->ffiType != nullptr + ? type.elementType->ffiType + : &ffi_type_float; + size_t lanes = std::max(type.arraySize, 1); + size_t abiLanes = lanes == 3 ? 4 : lanes; + size_t elementSize = std::max(elementFfiType->size, sizeof(float)); + size_t elementAlignment = + std::max(elementFfiType->alignment, static_cast(1)); + ffiOwner->elements.reserve(abiLanes + 1); + for (size_t i = 0; i < abiLanes; i++) { + ffiOwner->elements.push_back(elementFfiType); + } + ffiOwner->finalize(); + size_t vectorAlignment = elementAlignment; + if (kind != metagen::mdTypeComplex) { + size_t packedSize = abiLanes * elementSize; + size_t preferredAlignment = packedSize >= 16 ? 16 : packedSize; + vectorAlignment = std::max(vectorAlignment, preferredAlignment); + } + vectorAlignment = std::min(vectorAlignment, 16); + ffiOwner->type.alignment = static_cast(vectorAlignment); + ffiOwner->type.size = alignUp(abiLanes * elementSize, vectorAlignment); + type.ownedFfiType = ffiOwner; + type.ffiType = &ffiOwner->type; + type.supported = type.elementType->supported; + return type; + } + case metagen::mdTypeStruct: { + auto structOffset = metadata->getOffset(*offset); + *offset += sizeof(MDSectionOffset); + bool isUnion = (structOffset & metagen::mdSectionOffsetNext) != 0; + structOffset &= ~metagen::mdSectionOffsetNext; + if (structOffset == MD_SECTION_OFFSET_NULL || bridge == nullptr) { + type.kind = metagen::mdTypePointer; + type.ffiType = &ffi_type_pointer; + type.supported = true; + return type; + } + + MDSectionOffset absoluteOffset = + structOffset + (isUnion ? metadata->unionsOffset : metadata->structsOffset); + type.aggregateOffset = absoluteOffset; + type.aggregateIsUnion = isUnion; + type.aggregateInfo = bridge->aggregateInfoFor(absoluteOffset, isUnion); + type.ffiType = type.aggregateInfo != nullptr && type.aggregateInfo->ffi != nullptr + ? &type.aggregateInfo->ffi->type + : nullptr; + type.supported = type.ffiType != nullptr; + return type; + } + case metagen::mdTypePointer: + type.elementType = + std::make_shared( + parseMetadataEngineType(metadata, offset, bridge)); + type.ffiType = &ffi_type_pointer; + type.supported = true; + return type; + case metagen::mdTypeBlock: + case metagen::mdTypeFunctionPointer: + type.signatureOffset = metadata->getOffset(*offset) + metadata->signaturesOffset; + *offset += sizeof(MDSectionOffset); + type.ffiType = &ffi_type_pointer; + type.supported = true; + return type; + case metagen::mdTypeClassObject: { + auto classOffset = metadata->getOffset(*offset); + *offset += sizeof(MDSectionOffset); + bool next = (classOffset & metagen::mdSectionOffsetNext) != 0; + while (next) { + auto protocolOffset = metadata->getOffset(*offset); + *offset += sizeof(MDSectionOffset); + next = (protocolOffset & metagen::mdSectionOffsetNext) != 0; + } + break; + } + case metagen::mdTypeProtocolObject: { + bool next = true; + while (next) { + auto protocolOffset = metadata->getOffset(*offset); + *offset += sizeof(MDSectionOffset); + next = (protocolOffset & metagen::mdSectionOffsetNext) != 0; + } + break; + } + default: + break; + } + + type.ffiType = ffiTypeForEngineKind(kind); + type.supported = type.ffiType != nullptr && isSupportedEngineKind(kind); + return type; +} + +std::shared_ptr NativeApiBridge::aggregateInfoFor( + MDSectionOffset aggregateOffset, bool isUnion) { + if (metadata_ == nullptr || aggregateOffset == MD_SECTION_OFFSET_NULL) { + return nullptr; + } + + auto cached = aggregateInfoByOffset_.find(aggregateOffset); + if (cached != aggregateInfoByOffset_.end()) { + return cached->second; + } + + auto info = std::make_shared(); + info->offset = aggregateOffset; + info->isUnion = isUnion; + aggregateInfoByOffset_[aggregateOffset] = info; + + if (aggregateInfoInProgress_.find(aggregateOffset) != + aggregateInfoInProgress_.end()) { + auto ffiOwner = std::make_shared(); + ffiOwner->elements.push_back(&ffi_type_pointer); + ffiOwner->finalize(); + info->ffi = ffiOwner; + return info; + } + + aggregateInfoInProgress_.insert(aggregateOffset); + + MDSectionOffset offset = aggregateOffset; + const char* name = metadata_->getString(offset); + info->name = name != nullptr ? name : ""; + offset += sizeof(MDSectionOffset); + info->size = metadata_->getArraySize(offset); + offset += sizeof(uint16_t); + + bool next = true; + while (next) { + MDSectionOffset nameOffset = metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + next = (nameOffset & metagen::mdSectionOffsetNext) != 0; + nameOffset &= ~metagen::mdSectionOffsetNext; + if (nameOffset == MD_SECTION_OFFSET_NULL) { + break; + } + + NativeApiAggregateField field; + const char* fieldName = metadata_->resolveString(nameOffset); + field.name = fieldName != nullptr ? fieldName : ""; + if (!isUnion) { + field.offset = metadata_->getArraySize(offset); + offset += sizeof(uint16_t); + } + field.type = parseMetadataEngineType(metadata_.get(), &offset, this); + info->fields.push_back(std::move(field)); + } + + auto ffiOwner = std::make_shared(); + if (isUnion) { + ffi_type* largest = &ffi_type_uint8; + size_t largestSize = 0; + for (const auto& field : info->fields) { + size_t fieldSize = nativeSizeForType(field.type); + if (field.type.ffiType != nullptr && fieldSize >= largestSize) { + largest = field.type.ffiType; + largestSize = fieldSize; + } + } + ffiOwner->elements.push_back(largest); + } else { + for (const auto& field : info->fields) { + ffiOwner->elements.push_back(field.type.ffiType != nullptr + ? field.type.ffiType + : &ffi_type_pointer); + } + if (ffiOwner->elements.empty()) { + ffiOwner->elements.push_back(&ffi_type_uint8); + } + } + ffiOwner->finalize(); + info->ffi = ffiOwner; + aggregateInfoInProgress_.erase(aggregateOffset); + return info; +} + +ffi_type* ffiTypeForEngineArgument(const NativeApiType& type) { + switch (type.kind) { + case metagen::mdTypeArray: + return &ffi_type_pointer; + default: + return type.ffiType != nullptr ? type.ffiType : &ffi_type_pointer; + } +} + +std::optional parseMetadataEngineSignature( + MDMetadataReader* metadata, MDSectionOffset signatureOffset, + unsigned int implicitArgumentCount, NativeApiBridge* bridge, + bool returnOwned = false) { + if (metadata == nullptr || signatureOffset == MD_SECTION_OFFSET_NULL) { + return std::nullopt; + } + + NativeApiSignature signature; + signature.implicitArgumentCount = implicitArgumentCount; + signature.signatureHash = isPreparedGeneratedDispatchRequired() + ? metadataSignatureHash(metadata, signatureOffset) + : 0; + signature.dispatchFlags = returnOwned ? 1 : 0; + + MDSectionOffset offset = signatureOffset; + MDTypeKind returnKind = metadata->getTypeKind(offset); + uint32_t returnKindRaw = rawTypeKind(returnKind); + bool next = + (returnKindRaw & static_cast(metagen::mdTypeFlagNext)) != 0; + signature.variadic = + (returnKindRaw & static_cast(metagen::mdTypeFlagVariadic)) != 0; + signature.returnType = parseMetadataEngineType(metadata, &offset, bridge); + signature.returnType.returnOwned = returnOwned; + + while (next) { + MDTypeKind argKind = metadata->getTypeKind(offset); + next = (rawTypeKind(argKind) & + static_cast(metagen::mdTypeFlagNext)) != 0; + signature.argumentTypes.push_back(parseMetadataEngineType(metadata, &offset, bridge)); + } + + signature.ffiTypes.reserve(signature.argumentTypes.size() + + implicitArgumentCount); + for (unsigned int i = 0; i < implicitArgumentCount; i++) { + signature.ffiTypes.push_back(&ffi_type_pointer); + } + for (const auto& argType : signature.argumentTypes) { + signature.ffiTypes.push_back(ffiTypeForEngineArgument(argType)); + } + + ffi_status status = ffi_prep_cif( + &signature.cif, FFI_DEFAULT_ABI, + static_cast(signature.ffiTypes.size()), + signature.returnType.ffiType != nullptr ? signature.returnType.ffiType + : &ffi_type_void, + signature.ffiTypes.empty() ? nullptr : signature.ffiTypes.data()); + signature.prepared = status == FFI_OK; + return signature; +} + +bool prepareEngineCallbackSignature(NativeApiSignature* signature) { + if (signature == nullptr) { + return false; + } + + signature->ffiTypes.clear(); + signature->ffiTypes.reserve(signature->argumentTypes.size() + + signature->implicitArgumentCount); + for (unsigned int i = 0; i < signature->implicitArgumentCount; i++) { + signature->ffiTypes.push_back(&ffi_type_pointer); + } + for (const auto& argType : signature->argumentTypes) { + signature->ffiTypes.push_back(ffiTypeForEngineArgument(argType)); + } + + ffi_status status = ffi_prep_cif( + &signature->cif, FFI_DEFAULT_ABI, + static_cast(signature->ffiTypes.size()), + signature->returnType.ffiType != nullptr ? signature->returnType.ffiType + : &ffi_type_void, + signature->ffiTypes.empty() ? nullptr : signature->ffiTypes.data()); + signature->prepared = status == FFI_OK; + return signature->prepared; +} + +const char* skipObjCTypeQualifiers(const char* encoding) { + while (encoding != nullptr && *encoding != '\0' && + std::strchr("rnNoORV", *encoding) != nullptr) { + encoding++; + } + return encoding; +} + +const char* skipObjCTypeFrameOffset(const char* encoding) { + while (encoding != nullptr && *encoding >= '0' && *encoding <= '9') { + encoding++; + } + return encoding; +} + +const char* skipObjCTypeFieldName(const char* encoding, std::string* name) { + if (encoding == nullptr || *encoding != '"') { + return encoding; + } + + encoding++; + const char* start = encoding; + while (*encoding != '\0' && *encoding != '"') { + encoding++; + } + if (name != nullptr) { + *name = std::string(start, static_cast(encoding - start)); + } + return *encoding == '"' ? encoding + 1 : encoding; +} + +std::string normalizedObjCAggregateName(std::string name) { + if (!name.empty() && name.front() == '_') { + name.erase(name.begin()); + } + return name; +} + +std::vector knownObjCAggregateFieldNames( + const std::string& aggregateName, size_t fieldCount) { + std::string name = normalizedObjCAggregateName(aggregateName); + std::vector fields; + if (name == "CGPoint" || name == "NSPoint") { + fields = {"x", "y"}; + } else if (name == "CGSize" || name == "NSSize") { + fields = {"width", "height"}; + } else if (name == "CGRect" || name == "NSRect") { + fields = {"origin", "size"}; + } else if (name == "CGVector") { + fields = {"dx", "dy"}; + } else if (name == "UIEdgeInsets" || name == "NSEdgeInsets") { + fields = {"top", "left", "bottom", "right"}; + } else if (name == "NSDirectionalEdgeInsets") { + fields = {"top", "leading", "bottom", "trailing"}; + } else if (name == "NSRange" || name == "CFRange") { + fields = {"location", "length"}; + } else if (name == "CGAffineTransform") { + fields = {"a", "b", "c", "d", "tx", "ty"}; + } else if (name == "CATransform3D") { + fields = {"m11", "m12", "m13", "m14", "m21", "m22", "m23", "m24", + "m31", "m32", "m33", "m34", "m41", "m42", "m43", "m44"}; + } + + if (fields.size() != fieldCount) { + fields.clear(); + } + return fields; +} + +const NativeApiSymbol* findObjCAggregateSymbol( + NativeApiBridge* bridge, const std::string& name, bool isUnion) { + if (bridge == nullptr || name.empty()) { + return nullptr; + } + + std::vector candidates; + candidates.push_back(name); + std::string normalized = normalizedObjCAggregateName(name); + if (normalized != name) { + candidates.push_back(normalized); + } else { + candidates.push_back("_" + name); + } + constexpr const char* suffix = "Struct"; + if (normalized.size() > std::strlen(suffix) && + normalized.compare(normalized.size() - std::strlen(suffix), + std::strlen(suffix), suffix) == 0) { + candidates.push_back( + normalized.substr(0, normalized.size() - std::strlen(suffix))); + } else { + candidates.push_back(normalized + suffix); + } + + for (const auto& candidate : candidates) { + const NativeApiSymbol* symbol = + isUnion ? bridge->findUnion(candidate) : bridge->findStruct(candidate); + if (symbol == nullptr) { + symbol = bridge->findAggregate(candidate); + } + if (symbol != nullptr) { + return symbol; + } + } + + return nullptr; +} + +void applyObjCEncodingSizeAndAlignment(const char* encoding, + NativeApiFfiType* ffiType, + uint16_t* sizeOut = nullptr) { + if (encoding == nullptr || ffiType == nullptr) { + return; + } + + NSUInteger size = 0; + NSUInteger alignment = 0; + NSGetSizeAndAlignment(encoding, &size, &alignment); + if (size > 0) { + ffiType->type.size = static_cast(size); + if (sizeOut != nullptr) { + *sizeOut = static_cast(std::min( + size, static_cast(std::numeric_limits::max()))); + } + } + if (alignment > 0) { + ffiType->type.alignment = static_cast(alignment); + } +} + +NativeApiType parseObjCEncodedEngineType( + const char* encoding, NativeApiBridge* bridge = nullptr, + const char** endEncoding = nullptr); + +bool unsupportedEngineType(const NativeApiType& type); + +NativeApiType parseObjCEncodedAggregateEngineType( + const char* encoding, NativeApiBridge* bridge, const char** endEncoding) { + NativeApiType type; + type.kind = metagen::mdTypeStruct; + + const bool isUnion = *encoding == '('; + const char close = isUnion ? ')' : '}'; + const char* cursor = encoding + 1; + const char* nameStart = cursor; + while (*cursor != '\0' && *cursor != '=' && *cursor != close) { + cursor++; + } + std::string aggregateName(nameStart, static_cast(cursor - nameStart)); + + if (const NativeApiSymbol* symbol = + findObjCAggregateSymbol(bridge, aggregateName, isUnion)) { + type.aggregateOffset = symbol->offset; + type.aggregateIsUnion = symbol->kind == NativeApiSymbolKind::Union; + type.aggregateInfo = bridge->aggregateInfoFor(*symbol); + type.ffiType = type.aggregateInfo != nullptr && type.aggregateInfo->ffi != nullptr + ? &type.aggregateInfo->ffi->type + : nullptr; + type.supported = type.ffiType != nullptr; + + int depth = 0; + const char* end = encoding; + do { + if (*end == *encoding) { + depth++; + } else if (*end == close) { + depth--; + } + end++; + } while (*end != '\0' && depth > 0); + if (endEncoding != nullptr) { + *endEncoding = end; + } + return type; + } + + auto info = std::make_shared(); + info->name = aggregateName; + info->isUnion = isUnion; + info->offset = MD_SECTION_OFFSET_NULL; + + if (*cursor == '=') { + cursor++; + } + + size_t computedOffset = 0; + size_t maxFieldSize = 0; + size_t fieldIndex = 0; + while (*cursor != '\0' && *cursor != close) { + NativeApiAggregateField field; + std::string encodedFieldName; + cursor = skipObjCTypeFieldName(cursor, &encodedFieldName); + const char* fieldStart = cursor; + const char* fieldEnd = cursor; + field.type = parseObjCEncodedEngineType(cursor, bridge, &fieldEnd); + if (fieldEnd == fieldStart || unsupportedEngineType(field.type)) { + type.supported = false; + type.ffiType = nullptr; + if (endEncoding != nullptr) { + *endEncoding = fieldEnd; + } + return type; + } + + NSUInteger fieldSize = 0; + NSUInteger fieldAlignment = 0; + NSGetSizeAndAlignment(fieldStart, &fieldSize, &fieldAlignment); + size_t nativeFieldSize = + fieldSize > 0 ? static_cast(fieldSize) + : nativeSizeForType(field.type); + size_t nativeFieldAlignment = + fieldAlignment > 0 ? static_cast(fieldAlignment) + : std::max(1, field.type.ffiType != nullptr + ? field.type.ffiType->alignment + : 1); + if (isUnion) { + field.offset = 0; + maxFieldSize = std::max(maxFieldSize, nativeFieldSize); + } else { + computedOffset = alignUp(computedOffset, nativeFieldAlignment); + field.offset = static_cast(std::min( + computedOffset, std::numeric_limits::max())); + computedOffset += nativeFieldSize; + } + field.name = !encodedFieldName.empty() + ? encodedFieldName + : "field" + std::to_string(fieldIndex); + info->fields.push_back(std::move(field)); + fieldIndex++; + cursor = fieldEnd; + } + + if (*cursor == close) { + cursor++; + } + if (endEncoding != nullptr) { + *endEncoding = cursor; + } + + auto knownNames = knownObjCAggregateFieldNames(aggregateName, info->fields.size()); + for (size_t i = 0; i < knownNames.size(); i++) { + info->fields[i].name = knownNames[i]; + } + + auto ffiOwner = std::make_shared(); + if (isUnion) { + ffi_type* largest = &ffi_type_uint8; + size_t largestSize = 0; + for (const auto& field : info->fields) { + size_t fieldSize = nativeSizeForType(field.type); + if (field.type.ffiType != nullptr && fieldSize >= largestSize) { + largest = field.type.ffiType; + largestSize = fieldSize; + } + } + ffiOwner->elements.push_back(largest); + } else { + for (const auto& field : info->fields) { + ffiOwner->elements.push_back(field.type.ffiType != nullptr + ? field.type.ffiType + : &ffi_type_pointer); + } + } + if (ffiOwner->elements.empty()) { + ffiOwner->elements.push_back(&ffi_type_uint8); + } + ffiOwner->finalize(); + applyObjCEncodingSizeAndAlignment(encoding, ffiOwner.get(), &info->size); + if (info->size == 0) { + info->size = static_cast(std::min( + isUnion ? maxFieldSize : computedOffset, + std::numeric_limits::max())); + } + + info->ffi = ffiOwner; + type.aggregateInfo = info; + type.aggregateOffset = MD_SECTION_OFFSET_NULL; + type.aggregateIsUnion = isUnion; + type.ownedFfiType = ffiOwner; + type.ffiType = &ffiOwner->type; + type.supported = true; + return type; +} + +NativeApiType parseObjCEncodedArrayEngineType( + const char* encoding, NativeApiBridge* bridge, const char** endEncoding) { + NativeApiType type; + type.kind = metagen::mdTypeArray; + + const char* cursor = encoding + 1; + uint16_t count = 0; + while (*cursor >= '0' && *cursor <= '9') { + count = static_cast( + std::min(std::numeric_limits::max(), + (count * 10) + (*cursor - '0'))); + cursor++; + } + type.arraySize = count; + + const char* elementEnd = cursor; + type.elementType = std::make_shared( + parseObjCEncodedEngineType(cursor, bridge, &elementEnd)); + cursor = elementEnd; + if (*cursor == ']') { + cursor++; + } + if (endEncoding != nullptr) { + *endEncoding = cursor; + } + + auto ffiOwner = std::make_shared(); + ffi_type* elementFfiType = + type.elementType != nullptr && type.elementType->ffiType != nullptr + ? type.elementType->ffiType + : &ffi_type_pointer; + for (uint16_t i = 0; i < count; i++) { + ffiOwner->elements.push_back(elementFfiType); + } + if (ffiOwner->elements.empty()) { + ffiOwner->elements.push_back(&ffi_type_uint8); + } + ffiOwner->finalize(); + applyObjCEncodingSizeAndAlignment(encoding, ffiOwner.get()); + + type.ownedFfiType = ffiOwner; + type.ffiType = &ffiOwner->type; + type.supported = type.elementType != nullptr && type.elementType->supported; + return type; +} + +NativeApiType parseObjCEncodedEngineType( + const char* encoding, NativeApiBridge* bridge, const char** endEncoding) { + encoding = skipObjCTypeQualifiers(encoding); + NativeApiType type; + + if (encoding == nullptr || *encoding == '\0') { + type.kind = metagen::mdTypePointer; + type.ffiType = &ffi_type_pointer; + if (endEncoding != nullptr) { + *endEncoding = encoding; + } + return type; + } + + auto finishPrimitive = [&](const char* end) { + type.ffiType = ffiTypeForEngineKind(type.kind); + type.supported = type.ffiType != nullptr; + if (endEncoding != nullptr) { + *endEncoding = end; + } + return type; + }; + + switch (*encoding) { + case 'c': + type.kind = metagen::mdTypeChar; + break; + case 'i': + type.kind = metagen::mdTypeSInt; + break; + case 's': + type.kind = metagen::mdTypeSShort; + break; + case 'l': + case 'q': + type.kind = metagen::mdTypeSInt64; + break; + case 'C': + type.kind = metagen::mdTypeUInt8; + break; + case 'I': + type.kind = metagen::mdTypeUInt; + break; + case 'S': + type.kind = metagen::mdTypeUShort; + break; + case 'L': + case 'Q': + type.kind = metagen::mdTypeUInt64; + break; + case 'f': + type.kind = metagen::mdTypeFloat; + break; + case 'd': + type.kind = metagen::mdTypeDouble; + break; + case 'B': + type.kind = metagen::mdTypeBool; + break; + case 'v': + type.kind = metagen::mdTypeVoid; + break; + case '*': + type.kind = metagen::mdTypeString; + break; + case '@': + if (encoding[1] == '?') { + type.kind = metagen::mdTypeBlock; + return finishPrimitive(encoding + 2); + } + { + const char* objectEnd = encoding + 1; + if (*objectEnd == '"') { + objectEnd++; + while (*objectEnd != '\0' && *objectEnd != '"') { + objectEnd++; + } + if (*objectEnd == '"') { + objectEnd++; + } + } + if (std::strncmp(encoding, "@\"NSString\"", 11) == 0) { + type.kind = metagen::mdTypeNSStringObject; + } else if (std::strncmp(encoding, "@\"NSMutableString\"", 18) == 0) { + type.kind = metagen::mdTypeNSMutableStringObject; + } else { + type.kind = metagen::mdTypeAnyObject; + } + return finishPrimitive(objectEnd); + } + case '#': + type.kind = metagen::mdTypeClass; + break; + case ':': + type.kind = metagen::mdTypeSelector; + break; + case '^': + type.kind = metagen::mdTypePointer; + { + const char* elementEnd = encoding + 1; + type.elementType = std::make_shared( + parseObjCEncodedEngineType(encoding + 1, bridge, &elementEnd)); + type.ffiType = &ffi_type_pointer; + type.supported = true; + if (elementEnd == encoding + 1 && encoding[1] != '\0') { + elementEnd = encoding + 2; + } + if (endEncoding != nullptr) { + *endEncoding = elementEnd; + } + } + return type; + case '{': + case '(': + return parseObjCEncodedAggregateEngineType(encoding, bridge, endEncoding); + case '[': + return parseObjCEncodedArrayEngineType(encoding, bridge, endEncoding); + case 'b': { + type.kind = metagen::mdTypeUInt; + const char* cursor = encoding + 1; + while (*cursor >= '0' && *cursor <= '9') { + cursor++; + } + return finishPrimitive(cursor); + } + case '?': + type.kind = metagen::mdTypeOpaquePointer; + break; + default: + type.kind = metagen::mdTypePointer; + break; + } + + return finishPrimitive(encoding + 1); +} + +std::optional parseObjCCallbackEngineSignature( + const std::string& encodingString, bool block, NativeApiBridge* bridge) { + const char* cursor = skipObjCTypeQualifiers(encodingString.c_str()); + if (cursor == nullptr || *cursor == '\0') { + return std::nullopt; + } + + NativeApiSignature signature; + signature.implicitArgumentCount = block ? 1 : 0; + + const char* returnEnd = cursor; + signature.returnType = parseObjCEncodedEngineType(cursor, bridge, &returnEnd); + if (returnEnd == cursor) { + return std::nullopt; + } + cursor = skipObjCTypeFrameOffset(returnEnd); + + if (block) { + const char* blockSelf = skipObjCTypeQualifiers(cursor); + if (blockSelf != nullptr && blockSelf[0] == '@' && blockSelf[1] == '?') { + cursor = skipObjCTypeFrameOffset(blockSelf + 2); + } + } + + while (cursor != nullptr && *cursor != '\0') { + const char* argStart = skipObjCTypeQualifiers(cursor); + if (argStart == nullptr || *argStart == '\0') { + break; + } + const char* argEnd = argStart; + NativeApiType argType = parseObjCEncodedEngineType(argStart, bridge, &argEnd); + if (argEnd == argStart) { + return std::nullopt; + } + signature.argumentTypes.push_back(std::move(argType)); + cursor = skipObjCTypeFrameOffset(argEnd); + } + + prepareEngineCallbackSignature(&signature); + return signature; +} + +std::optional parseObjCMethodEngineSignature( + Method method, NativeApiBridge* bridge = nullptr) { + if (method == nullptr) { + return std::nullopt; + } + + NativeApiSignature signature; + signature.implicitArgumentCount = 2; + + char* returnEncoding = method_copyReturnType(method); + signature.returnType = parseObjCEncodedEngineType(returnEncoding, bridge); + if (returnEncoding != nullptr) { + free(returnEncoding); + } + + unsigned int totalArgc = method_getNumberOfArguments(method); + for (unsigned int i = 2; i < totalArgc; i++) { + char* argEncoding = method_copyArgumentType(method, i); + signature.argumentTypes.push_back(parseObjCEncodedEngineType(argEncoding, bridge)); + if (argEncoding != nullptr) { + free(argEncoding); + } + } + + signature.ffiTypes.reserve(totalArgc); + signature.ffiTypes.push_back(&ffi_type_pointer); + signature.ffiTypes.push_back(&ffi_type_pointer); + for (const auto& argType : signature.argumentTypes) { + signature.ffiTypes.push_back(ffiTypeForEngineArgument(argType)); + } + + ffi_status status = ffi_prep_cif( + &signature.cif, FFI_DEFAULT_ABI, + static_cast(signature.ffiTypes.size()), + signature.returnType.ffiType != nullptr ? signature.returnType.ffiType + : &ffi_type_void, + signature.ffiTypes.data()); + signature.prepared = status == FFI_OK; + return signature; +} + +bool prepareEngineMethodSignature(NativeApiSignature* signature) { + if (signature == nullptr) { + return false; + } + signature->implicitArgumentCount = 2; + signature->ffiTypes.clear(); + signature->ffiTypes.reserve(signature->argumentTypes.size() + 2); + signature->ffiTypes.push_back(&ffi_type_pointer); + signature->ffiTypes.push_back(&ffi_type_pointer); + for (const auto& argType : signature->argumentTypes) { + ffi_type* ffiType = ffiTypeForEngineArgument(argType); + if (ffiType == nullptr) { + signature->prepared = false; + return false; + } + signature->ffiTypes.push_back(ffiType); + } + ffi_type* returnFfiType = + signature->returnType.ffiType != nullptr ? signature->returnType.ffiType + : &ffi_type_void; + signature->prepared = + ffi_prep_cif(&signature->cif, FFI_DEFAULT_ABI, + static_cast(signature->ffiTypes.size()), + returnFfiType, signature->ffiTypes.data()) == FFI_OK; + return signature->prepared; +} + +bool isRuntimeAggregateType(const NativeApiType& type) { + switch (type.kind) { + case metagen::mdTypeStruct: + case metagen::mdTypeArray: + case metagen::mdTypeVector: + case metagen::mdTypeExtVector: + case metagen::mdTypeComplex: + return true; + default: + return false; + } +} + +bool reconcileObjCMethodRuntimeType(NativeApiType* metadataType, + const NativeApiType& runtimeType, + bool* abiChanged) { + if (metadataType == nullptr || unsupportedEngineType(runtimeType)) { + return false; + } + + if (runtimeType.kind == metagen::mdTypeBlock && + metadataType->kind == metagen::mdTypeFunctionPointer) { + metadataType->kind = metagen::mdTypeBlock; + metadataType->ffiType = runtimeType.ffiType; + metadataType->supported = runtimeType.supported; + return true; + } + + // Do not overwrite aggregate (struct/union) metadata types with the + // anonymous ObjC runtime encoding: the metadata type carries the real + // field names and layout that the runtime encoding (e.g. "{?=qqq}") lacks. + (void)abiChanged; + return false; +} + +bool reconcileObjCMethodRuntimeSignature(NativeApiSignature* signature, + const NativeApiSignature& runtime) { + if (signature == nullptr || + signature->argumentTypes.size() != runtime.argumentTypes.size()) { + return false; + } + + bool changed = false; + bool abiChanged = false; + changed |= reconcileObjCMethodRuntimeType(&signature->returnType, + runtime.returnType, &abiChanged); + for (size_t i = 0; i < signature->argumentTypes.size(); i++) { + changed |= reconcileObjCMethodRuntimeType(&signature->argumentTypes[i], + runtime.argumentTypes[i], + &abiChanged); + } + + if (abiChanged) { + signature->signatureHash = 0; + } + return !changed || prepareEngineMethodSignature(signature); +} + +bool unsupportedEngineType(const NativeApiType& type) { + if (type.kind == metagen::mdTypeStruct && type.aggregateInfo != nullptr && + type.aggregateInfo->ffi != nullptr) { + return false; + } + return !type.supported || type.ffiType == nullptr; +} + +bool signatureSupportedForEngineCallback(const NativeApiSignature& signature) { + if (!signature.prepared || signature.variadic || + unsupportedEngineType(signature.returnType)) { + return false; + } + for (const auto& argType : signature.argumentTypes) { + if (unsupportedEngineType(argType)) { + return false; + } + } + return true; +} + +std::shared_ptr createEngineCallback( + Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, Function function, bool block, + NativeApiCallbackThreadPolicy threadPolicy = + NativeApiCallbackThreadPolicy::Default) { + if (bridge == nullptr || bridge->metadata() == nullptr || + type.signatureOffset == MD_SECTION_OFFSET_NULL) { + throw JSError( + runtime, "Native callback metadata is unavailable."); + } + + auto parsed = parseMetadataEngineSignature( + bridge->metadata(), type.signatureOffset, block ? 1 : 0, bridge.get()); + if (!parsed || !signatureSupportedForEngineCallback(*parsed)) { + throw JSError( + runtime, "Native callback signature is not supported by backend."); + } + + auto signature = + std::make_shared(std::move(*parsed)); + uintptr_t roundTripValidationKey = + NativeApiBridge::callbackRoundTripValidationKey(type); + auto callback = std::make_shared( + runtime, bridge, std::move(signature), std::move(function), block, + threadPolicy, false, roundTripValidationKey); + if (block) { + callback->retainInitialBlockLifetime(callback); + } else { + bridge->retainEngineLifetime(callback); + } + return callback; +} + +std::shared_ptr createEngineCallback( + Runtime& runtime, const std::shared_ptr& bridge, + const std::string& objcSignatureEncoding, Function function, bool block, + NativeApiCallbackThreadPolicy threadPolicy = + NativeApiCallbackThreadPolicy::Default, + uintptr_t roundTripValidationKey = 0) { + if (bridge == nullptr || objcSignatureEncoding.empty()) { + throw JSError(runtime, "Native callback encoding is unavailable."); + } + + auto parsed = parseObjCCallbackEngineSignature( + objcSignatureEncoding, block, bridge.get()); + if (!parsed || !signatureSupportedForEngineCallback(*parsed)) { + throw JSError( + runtime, "Native callback signature is not supported by backend."); + } + + auto signature = + std::make_shared(std::move(*parsed)); + auto callback = std::make_shared( + runtime, bridge, std::move(signature), std::move(function), block, + threadPolicy, false, roundTripValidationKey); + if (block) { + callback->retainInitialBlockLifetime(callback); + } else { + bridge->retainEngineLifetime(callback); + } + return callback; +} + +std::shared_ptr createEngineMethodCallback( + Runtime& runtime, const std::shared_ptr& bridge, + const std::string& selectorName, MDSectionOffset signatureOffset, + Function function, bool returnOwned) { + if (bridge == nullptr || bridge->metadata() == nullptr || + signatureOffset == MD_SECTION_OFFSET_NULL) { + throw JSError( + runtime, "Native method callback metadata is unavailable."); + } + + auto parsed = parseMetadataEngineSignature( + bridge->metadata(), signatureOffset, 2, bridge.get(), returnOwned); + if (!parsed || !signatureSupportedForEngineCallback(*parsed)) { + throw JSError( + runtime, "Native method callback signature is not supported by backend."); + } + parsed->selectorName = selectorName; + + auto signature = + std::make_shared(std::move(*parsed)); + auto threadPolicy = readEngineCallbackThreadPolicy(runtime, function); + auto callback = std::make_shared( + runtime, bridge, std::move(signature), std::move(function), false, + threadPolicy, true); + bridge->retainEngineLifetime(callback); + return callback; +} + +std::shared_ptr createEngineMethodCallback( + Runtime& runtime, const std::shared_ptr& bridge, + const std::string& selectorName, NativeApiSignature signature, + Function function) { + signature.selectorName = selectorName; + prepareEngineMethodSignature(&signature); + if (!signatureSupportedForEngineCallback(signature)) { + throw JSError( + runtime, "Native method callback signature is not supported by backend."); + } + + auto sharedSignature = + std::make_shared(std::move(signature)); + auto threadPolicy = readEngineCallbackThreadPolicy(runtime, function); + auto callback = std::make_shared( + runtime, bridge, std::move(sharedSignature), std::move(function), false, + threadPolicy, true); + bridge->retainEngineLifetime(callback); + return callback; +} diff --git a/NativeScript/ffi/objc/shared/bridge/ClassBuilder.mm b/NativeScript/ffi/objc/shared/bridge/ClassBuilder.mm new file mode 100644 index 000000000..9c816f6e4 --- /dev/null +++ b/NativeScript/ffi/objc/shared/bridge/ClassBuilder.mm @@ -0,0 +1,792 @@ +std::string readOptionalStringProperty(Runtime& runtime, const Object& object, + const char* name) { + if (name == nullptr || !object.hasProperty(runtime, name)) { + return ""; + } + Value value = object.getProperty(runtime, name); + return value.isString() ? value.asString(runtime).utf8(runtime) : ""; +} + +struct NativeApiClassBuilderRegistration { + std::shared_ptr runtimeOwner; + Runtime* runtime = nullptr; + std::shared_ptr bridge; +}; + +std::mutex gNativeApiClassBuilderMutex; +std::unordered_map + gNativeApiClassBuilders; +struct NativeApiKnownExposedMethod { + std::string selectorName; + NativeApiSignature signature; +}; +std::mutex gNativeApiKnownExposedMethodsMutex; +std::unordered_map + gNativeApiKnownExposedMethods; + +void rememberNativeApiClassBuilder( + Runtime& runtime, const std::shared_ptr& bridge, + Class cls) { + if (cls == Nil) { + return; + } + std::lock_guard lock(gNativeApiClassBuilderMutex); + auto runtimeOwner = retainNativeApiRuntime(runtime); + gNativeApiClassBuilders[cls] = NativeApiClassBuilderRegistration{ + .runtimeOwner = runtimeOwner, + .runtime = runtimeOwner.get(), + .bridge = bridge, + }; +} + +void rememberNativeApiKnownExposedMethod( + const std::string& selectorName, const NativeApiSignature& signature) { + if (selectorName.empty()) { + return; + } + NativeApiKnownExposedMethod method{ + .selectorName = selectorName, + .signature = signature, + }; + std::lock_guard lock(gNativeApiKnownExposedMethodsMutex); + gNativeApiKnownExposedMethods[selectorName] = method; + gNativeApiKnownExposedMethods[jsifySelector(selectorName.c_str())] = + std::move(method); +} + +std::optional knownNativeApiExposedMethod( + const std::string& name) { + std::lock_guard lock(gNativeApiKnownExposedMethodsMutex); + auto it = gNativeApiKnownExposedMethods.find(name); + if (it == gNativeApiKnownExposedMethods.end()) { + return std::nullopt; + } + NativeApiKnownExposedMethod method = it->second; + prepareEngineMethodSignature(&method.signature); + return method; +} + +std::optional +findNativeApiClassBuilder(id object) { + Class cls = object != nil ? object_getClass(object) : Nil; + std::lock_guard lock(gNativeApiClassBuilderMutex); + while (cls != Nil) { + auto it = gNativeApiClassBuilders.find(cls); + if (it != gNativeApiClassBuilders.end()) { + return it->second; + } + cls = class_getSuperclass(cls); + } + return std::nullopt; +} + +const char* nativeApiEngineFastEnumerationEncoding() { + 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; +} + +NSUInteger nativeApiEngineSymbolIteratorCountByEnumerating( + id self, SEL, NSFastEnumerationState* state, + id __unsafe_unretained stackbuf[], NSUInteger len) { + if (len == 0 || state == nullptr || stackbuf == nullptr) { + return 0; + } + + auto registration = findNativeApiClassBuilder(self); + if (!registration || registration->runtime == nullptr || + registration->bridge == nullptr) { + return 0; + } + + Runtime& runtime = *registration->runtime; + NativeApiRuntimeScope runtimeScope(runtime); + auto bridge = registration->bridge; + try { + Value receiver = makeNativeObjectValue(runtime, bridge, self, false); + if (!receiver.isObject()) { + return 0; + } + + Value iteratorFactoryValue = + runtime.global().getProperty(runtime, + "__nativeScriptCreateNativeApiIterator"); + if (!iteratorFactoryValue.isObject() || + !iteratorFactoryValue.asObject(runtime).isFunction(runtime)) { + return 0; + } + + Function iteratorFactory = + iteratorFactoryValue.asObject(runtime).asFunction(runtime); + Value prototype = + bridge->findClassPrototype(runtime, object_getClass(self)); + Value iteratorValue = + prototype.isObject() + ? iteratorFactory.call(runtime, Value(runtime, receiver), + Value(runtime, prototype)) + : iteratorFactory.call(runtime, Value(runtime, receiver)); + if (!iteratorValue.isObject()) { + return 0; + } + Object iterator = iteratorValue.asObject(runtime); + Value nextValue = iterator.getProperty(runtime, "next"); + if (!nextValue.isObject() || + !nextValue.asObject(runtime).isFunction(runtime)) { + return 0; + } + Function next = nextValue.asObject(runtime).asFunction(runtime); + + auto callNext = [&]() -> Value { + return next.callWithThis(runtime, iterator); + }; + + for (unsigned long skipped = 0; skipped < state->state; skipped++) { + Value skippedResult = callNext(); + if (!skippedResult.isObject()) { + return 0; + } + Value doneValue = + skippedResult.asObject(runtime).getProperty(runtime, "done"); + if (doneValue.isBool() && doneValue.getBool()) { + return 0; + } + } + + NSUInteger count = 0; + while (count < len) { + Value nextResult = callNext(); + if (!nextResult.isObject()) { + break; + } + Object nextObject = nextResult.asObject(runtime); + Value doneValue = nextObject.getProperty(runtime, "done"); + if (doneValue.isBool() && doneValue.getBool()) { + break; + } + + Value value = nextObject.getProperty(runtime, "value"); + NativeApiArgumentFrame frame(1); + id nativeValue = objectFromEngineValue(runtime, bridge, value, frame, false); + if (nativeValue != nil) { + [nativeValue retain]; + [nativeValue autorelease]; + } + stackbuf[count++] = nativeValue; + } + + state->itemsPtr = stackbuf; + state->mutationsPtr = &state->extra[0]; + state->extra[0] = 0; + state->state += count; + return count; + } catch (const std::exception&) { + return 0; + } +} + +NativeApiSymbol runtimeSymbolForClass( + const std::shared_ptr& bridge, Class cls) { + if (bridge != nullptr) { + if (const NativeApiSymbol* symbol = bridge->findClassForRuntimeClass(cls)) { + return *symbol; + } + } + + const char* name = cls != Nil ? class_getName(cls) : ""; + return NativeApiSymbol{ + .kind = NativeApiSymbolKind::Class, + .offset = MD_SECTION_OFFSET_NULL, + .name = name != nullptr ? name : "", + .runtimeName = name != nullptr ? name : "", + }; +} + +std::string nextAvailableEngineClassName(const std::string& requestedName) { + if (requestedName.empty()) { + return ""; + } + if (objc_lookUpClass(requestedName.c_str()) == Nil) { + return requestedName; + } + + size_t suffix = 1; + std::string candidate; + do { + candidate = requestedName + std::to_string(suffix++); + } while (objc_lookUpClass(candidate.c_str()) != Nil); + return candidate; +} + +std::vector methodOverridesForName( + const std::vector& members, const std::string& name) { + std::vector result; + std::unordered_set selectors; + for (const auto& member : members) { + if (member.property || member.name != name || + (member.flags & metagen::mdMemberStatic) != 0 || + member.selectorName.empty()) { + continue; + } + if (selectors.insert(member.selectorName).second) { + result.push_back(member); + } + } + return result; +} + +const NativeApiMember* propertyOverrideForName( + const std::vector& members, const std::string& name) { + const NativeApiMember* propertyMember = nullptr; + for (const auto& member : members) { + if (member.property && member.name == name && + (member.flags & metagen::mdMemberStatic) == 0) { + if (propertyMember == nullptr) { + propertyMember = &member; + } + if (!member.readonly && !member.setterSelectorName.empty()) { + return &member; + } + } + } + return propertyMember; +} + +void addEngineOverrideMethod(Runtime& runtime, + const std::shared_ptr& bridge, + Class nativeClass, Class baseClass, + const std::string& selectorName, + MDSectionOffset signatureOffset, + bool returnOwned, Function function) { + if (selectorName.empty() || signatureOffset == MD_SECTION_OFFSET_NULL) { + return; + } + + auto callback = createEngineMethodCallback(runtime, bridge, selectorName, + signatureOffset, std::move(function), + returnOwned); + SEL selector = sel_registerName(selectorName.c_str()); + std::string metadataEncoding = + objcMethodSignatureForEngineSignature(callback->signature()); + class_replaceMethod(nativeClass, selector, + reinterpret_cast(callback->functionPointer()), + metadataEncoding.c_str()); +} + +Value getObjectPropertyOrUndefined(Runtime& runtime, const Object& object, + const std::string& name) { + return object.hasProperty(runtime, name.c_str()) + ? object.getProperty(runtime, name.c_str()) + : Value::undefined(); +} + +Class dispatchSuperclassForEngineDerivedReceiver(id receiver, + Class defaultSuperclass) { + if (receiver == nil) { + return Nil; + } + + Class receiverClass = object_getClass(receiver); + if (receiverClass == Nil || + !class_conformsToProtocol(receiverClass, + @protocol(NativeApiClassBuilderProtocol))) { + return Nil; + } + + Class superclass = class_getSuperclass(receiverClass); + return superclass != Nil ? superclass : defaultSuperclass; +} + +std::optional functionForSelector(Runtime& runtime, + const Object& methods, + const std::string& selectorName) { + Value value = getObjectPropertyOrUndefined(runtime, methods, selectorName); + if (!value.isObject() || !value.asObject(runtime).isFunction(runtime)) { + std::string jsName = jsifySelector(selectorName.c_str()); + if (jsName != selectorName) { + value = getObjectPropertyOrUndefined(runtime, methods, jsName); + } + } + if (!value.isObject() || !value.asObject(runtime).isFunction(runtime)) { + return std::nullopt; + } + return value.asObject(runtime).asFunction(runtime); +} + +std::optional readExposedType( + Runtime& runtime, const std::shared_ptr& bridge, + const Object& descriptor, const char* propertyName) { + if (!descriptor.hasProperty(runtime, propertyName)) { + return std::nullopt; + } + return interopTypeFromValue(runtime, bridge, + descriptor.getProperty(runtime, propertyName)); +} + +std::optional exposedMethodSignature( + Runtime& runtime, const std::shared_ptr& bridge, + const std::string& selectorName, const Object& descriptor) { + NativeApiSignature signature; + if (auto returnType = readExposedType(runtime, bridge, descriptor, "returns")) { + signature.returnType = *returnType; + } else { + signature.returnType = primitiveInteropType(metagen::mdTypeVoid); + } + + Value paramsValue = getObjectPropertyOrUndefined(runtime, descriptor, "params"); + if (!paramsValue.isUndefined() && !paramsValue.isNull()) { + if (!paramsValue.isObject() || !paramsValue.asObject(runtime).isArray(runtime)) { + throw JSError( + runtime, "exposedMethods params must be an array."); + } + Array params = paramsValue.asObject(runtime).getArray(runtime); + for (size_t i = 0; i < params.size(runtime); i++) { + Value typeValue = params.getValueAtIndex(runtime, i); + auto type = interopTypeFromValue(runtime, bridge, typeValue); + if (!type) { + throw JSError( + runtime, "exposedMethods contains an unsupported parameter type."); + } + signature.argumentTypes.push_back(*type); + } + } + + if (selectorArgumentCount(selectorName) != signature.argumentTypes.size()) { + throw JSError( + runtime, "exposedMethods selector argument count does not match params."); + } + + prepareEngineMethodSignature(&signature); + return signature; +} + +std::optional runtimeProtocolMethodSignature( + const char* types) { + if (types == nullptr) { + return std::nullopt; + } + + NSMethodSignature* methodSignature = + [NSMethodSignature signatureWithObjCTypes:types]; + if (methodSignature == nil || methodSignature.numberOfArguments < 2) { + return std::nullopt; + } + + NativeApiSignature signature; + signature.implicitArgumentCount = 2; + signature.returnType = + parseObjCEncodedEngineType(methodSignature.methodReturnType); + for (NSUInteger i = 2; i < methodSignature.numberOfArguments; i++) { + signature.argumentTypes.push_back( + parseObjCEncodedEngineType([methodSignature getArgumentTypeAtIndex:i])); + } + if (unsupportedEngineType(signature.returnType)) { + return std::nullopt; + } + for (const auto& argumentType : signature.argumentTypes) { + if (unsupportedEngineType(argumentType)) { + return std::nullopt; + } + } + return signature; +} + +std::optional protocolSymbolFromEngineValue( + Runtime& runtime, const std::shared_ptr& bridge, + const Value& value) { + if (value.isString()) { + std::string name = value.asString(runtime).utf8(runtime); + if (const NativeApiSymbol* symbol = bridge->findProtocol(name)) { + return *symbol; + } + return std::nullopt; + } + if (!value.isObject()) { + return std::nullopt; + } + + Object object = value.asObject(runtime); + if (object.isHostObject(runtime)) { + return object.getHostObject(runtime)->symbol(); + } + + if (stringPropertyOrEmpty(runtime, object, "kind") != "protocol") { + return std::nullopt; + } + + std::string runtimeName = stringPropertyOrEmpty(runtime, object, "runtimeName"); + if (!runtimeName.empty()) { + if (const NativeApiSymbol* symbol = bridge->findProtocol(runtimeName)) { + return *symbol; + } + } + + std::string name = stringPropertyOrEmpty(runtime, object, "name"); + if (!name.empty()) { + if (const NativeApiSymbol* symbol = bridge->findProtocol(name)) { + return *symbol; + } + } + + return std::nullopt; +} + +void addEngineExposedMethod(Runtime& runtime, + const std::shared_ptr& bridge, + Class nativeClass, const std::string& selectorName, + NativeApiSignature signature, Function function) { + if (selectorName.empty()) { + return; + } + auto callback = createEngineMethodCallback(runtime, bridge, selectorName, + std::move(signature), std::move(function)); + std::string encoding = objcMethodSignatureForEngineSignature(callback->signature()); + class_replaceMethod(nativeClass, sel_registerName(selectorName.c_str()), + reinterpret_cast(callback->functionPointer()), + encoding.c_str()); +} + +bool addRuntimeProtocolOverrideForName( + Runtime& runtime, const std::shared_ptr& bridge, + Class nativeClass, const std::vector& protocols, + const std::string& propertyName, Function function) { + std::unordered_set visited; + std::function visit = [&](Protocol* protocol) -> bool { + if (protocol == nullptr || !visited.insert(protocol).second) { + return false; + } + + Protocol** inherited = protocol_copyProtocolList(protocol, nullptr); + if (inherited != nullptr) { + unsigned int inheritedCount = 0; + free(inherited); + inherited = protocol_copyProtocolList(protocol, &inheritedCount); + for (unsigned int i = 0; i < inheritedCount; i++) { + if (visit(inherited[i])) { + free(inherited); + return true; + } + } + free(inherited); + } + + for (BOOL required : {YES, NO}) { + unsigned int count = 0; + objc_method_description* descriptions = + protocol_copyMethodDescriptionList(protocol, required, YES, &count); + for (unsigned int i = 0; i < count; i++) { + SEL selector = descriptions[i].name; + const char* selectorName = + selector != nullptr ? sel_getName(selector) : nullptr; + if (selectorName == nullptr || + jsifySelector(selectorName) != propertyName) { + continue; + } + auto signature = runtimeProtocolMethodSignature(descriptions[i].types); + if (signature) { + addEngineExposedMethod(runtime, bridge, nativeClass, selectorName, + std::move(*signature), std::move(function)); + free(descriptions); + return true; + } + } + free(descriptions); + } + return false; + }; + + for (Protocol* protocol : protocols) { + if (visit(protocol)) { + return true; + } + } + return false; +} + +Object getOwnPropertyDescriptor(Runtime& runtime, const Object& object, + const std::string& name) { + Object objectCtor = runtime.global().getPropertyAsObject(runtime, "Object"); + Function getOwnPropertyDescriptor = + objectCtor.getPropertyAsFunction(runtime, "getOwnPropertyDescriptor"); + Value args[] = {Value(runtime, object), makeString(runtime, name)}; + Value descriptorValue = + getOwnPropertyDescriptor.call(runtime, static_cast(args), + static_cast(2)); + return descriptorValue.isObject() ? descriptorValue.asObject(runtime) + : Object(runtime); +} + +Value extendNativeApiClass( + Runtime& runtime, const std::shared_ptr& bridge, + const Value* args, size_t count) { + if (count < 2 || !args[0].isObject() || !args[1].isObject()) { + throw JSError( + runtime, "extendClass expects a native class and method object."); + } + + Class baseClass = classFromEngineValue(runtime, args[0]); + if (baseClass == Nil) { + throw JSError( + runtime, "extendClass can only extend native class constructors."); + } + if (class_conformsToProtocol(baseClass, + @protocol(NativeApiClassBuilderProtocol))) { + throw JSError(runtime, + "Cannot extend an already extended class."); + } + + Object methods = args[1].asObject(runtime); + Object options = count >= 3 && args[2].isObject() + ? args[2].asObject(runtime) + : Object(runtime); + std::string requestedName = readOptionalStringProperty(runtime, options, "name"); + if (requestedName.empty()) { + const char* baseName = class_getName(baseClass); + requestedName = std::string(baseName != nullptr ? baseName : "NSObject") + + "_Extended_" + std::to_string(rand()); + } + + std::string className = nextAvailableEngineClassName(requestedName); + Class nativeClass = objc_allocateClassPair(baseClass, className.c_str(), 0); + if (nativeClass == Nil) { + throw JSError(runtime, "Failed to allocate Objective-C class."); + } + + markNativeApiExtendedClass(nativeClass); + class_addProtocol(nativeClass, @protocol(NativeApiClassBuilderProtocol)); + rememberNativeApiClassBuilder(runtime, bridge, nativeClass); + + NativeApiSymbol baseSymbol = runtimeSymbolForClass(bridge, baseClass); + std::vector extensionMembers = + bridge->membersForClass(baseSymbol); + std::vector optionProtocols; + Value protocolsValue = getObjectPropertyOrUndefined(runtime, options, "protocols"); + if (protocolsValue.isObject() && + protocolsValue.asObject(runtime).isArray(runtime)) { + Array protocols = protocolsValue.asObject(runtime).getArray(runtime); + for (size_t i = 0; i < protocols.size(runtime); i++) { + Value protocolValue = protocols.getValueAtIndex(runtime, i); + Protocol* protocol = protocolFromEngineValue(runtime, protocolValue); + std::optional protocolSymbol = + protocolSymbolFromEngineValue(runtime, bridge, protocolValue); + if (protocol != nullptr) { + optionProtocols.push_back(protocol); + class_addProtocol(nativeClass, protocol); + if (!protocolSymbol) { + if (const NativeApiSymbol* runtimeSymbol = + bridge->findProtocolForRuntimePointer(protocol)) { + protocolSymbol = *runtimeSymbol; + } + } + } + if (protocolSymbol) { + const auto& protocolMembers = bridge->membersForProtocol(*protocolSymbol); + extensionMembers.insert(extensionMembers.begin(), + protocolMembers.begin(), + protocolMembers.end()); + } + } + } + const auto& members = extensionMembers; + Array propertyNames = methods.getPropertyNames(runtime); + for (size_t i = 0; i < propertyNames.size(runtime); i++) { + Value propertyNameValue = propertyNames.getValueAtIndex(runtime, i); + if (!propertyNameValue.isString()) { + continue; + } + + std::string propertyName = propertyNameValue.asString(runtime).utf8(runtime); + Object descriptor = getOwnPropertyDescriptor(runtime, methods, propertyName); + + Value value = descriptor.getProperty(runtime, "value"); + if (value.isObject() && value.asObject(runtime).isFunction(runtime)) { + auto overrides = methodOverridesForName(members, propertyName); + bool addedOverride = false; + for (const auto& member : overrides) { + if (member.selectorName.empty() || + member.signatureOffset == MD_SECTION_OFFSET_NULL || + member.signatureOffset == 0) { + continue; + } + addEngineOverrideMethod( + runtime, bridge, nativeClass, baseClass, member.selectorName, + member.signatureOffset, + (member.flags & metagen::mdMemberReturnOwned) != 0, + value.asObject(runtime).asFunction(runtime)); + addedOverride = true; + } + if (!addedOverride) { + bool addedRuntimeProtocolOverride = addRuntimeProtocolOverrideForName( + runtime, bridge, nativeClass, optionProtocols, propertyName, + value.asObject(runtime).asFunction(runtime)); + if (!addedRuntimeProtocolOverride) { + if (auto known = knownNativeApiExposedMethod(propertyName)) { + addEngineExposedMethod(runtime, bridge, nativeClass, + known->selectorName, + std::move(known->signature), + value.asObject(runtime).asFunction(runtime)); + } + } + } + } + + const NativeApiMember* propertyMember = + propertyOverrideForName(members, propertyName); + + Value getter = descriptor.getProperty(runtime, "get"); + if (propertyMember != nullptr && getter.isObject() && + getter.asObject(runtime).isFunction(runtime)) { + addEngineOverrideMethod( + runtime, bridge, nativeClass, baseClass, + propertyMember->selectorName, propertyMember->signatureOffset, + (propertyMember->flags & metagen::mdMemberReturnOwned) != 0, + getter.asObject(runtime).asFunction(runtime)); + } else if (propertyMember == nullptr && getter.isObject() && + getter.asObject(runtime).isFunction(runtime)) { + auto overrides = methodOverridesForName(members, propertyName); + for (const auto& member : overrides) { + if (selectorArgumentCount(member.selectorName) != 0) { + continue; + } + addEngineOverrideMethod( + runtime, bridge, nativeClass, baseClass, member.selectorName, + member.signatureOffset, + (member.flags & metagen::mdMemberReturnOwned) != 0, + getter.asObject(runtime).asFunction(runtime)); + } + } + + Value setter = descriptor.getProperty(runtime, "set"); + if (propertyMember != nullptr && + setter.isObject() && setter.asObject(runtime).isFunction(runtime) && + !propertyMember->setterSelectorName.empty()) { + addEngineOverrideMethod(runtime, bridge, nativeClass, baseClass, + propertyMember->setterSelectorName, + propertyMember->setterSignatureOffset, false, + setter.asObject(runtime).asFunction(runtime)); + } + } + + Value exposedMethodsValue = + getObjectPropertyOrUndefined(runtime, options, "exposedMethods"); + if (!exposedMethodsValue.isObject()) { + exposedMethodsValue = + getObjectPropertyOrUndefined(runtime, methods, "ObjCExposedMethods"); + } + if (exposedMethodsValue.isObject()) { + Object exposedMethods = exposedMethodsValue.asObject(runtime); + Array exposedNames = exposedMethods.getPropertyNames(runtime); + for (size_t i = 0; i < exposedNames.size(runtime); i++) { + Value selectorValue = exposedNames.getValueAtIndex(runtime, i); + if (!selectorValue.isString()) { + continue; + } + std::string selectorName = selectorValue.asString(runtime).utf8(runtime); + Value descriptorValue = + getObjectPropertyOrUndefined(runtime, exposedMethods, selectorName); + if (!descriptorValue.isObject()) { + continue; + } + auto function = functionForSelector(runtime, methods, selectorName); + if (!function) { + continue; + } + auto signature = exposedMethodSignature( + runtime, bridge, selectorName, descriptorValue.asObject(runtime)); + if (signature) { + rememberNativeApiKnownExposedMethod(selectorName, *signature); + addEngineExposedMethod(runtime, bridge, nativeClass, selectorName, + std::move(*signature), std::move(*function)); + } + } + } + + Value hasIteratorValue = + getObjectPropertyOrUndefined(runtime, options, "__hasIterator"); + if (hasIteratorValue.isBool() && hasIteratorValue.getBool()) { + class_addProtocol(nativeClass, @protocol(NSFastEnumeration)); + if (const char* encoding = nativeApiEngineFastEnumerationEncoding()) { + class_replaceMethod( + nativeClass, + @selector(countByEnumeratingWithState:objects:count:), + reinterpret_cast(nativeApiEngineSymbolIteratorCountByEnumerating), + encoding); + } + } + + objc_registerClassPair(nativeClass); + + NativeApiSymbol newSymbol = baseSymbol; + newSymbol.name = className; + newSymbol.runtimeName = className; + newSymbol.superclassOffset = baseSymbol.offset; + return makeNativeClassValue(runtime, bridge, std::move(newSymbol)); + } + +Value invokeNativeApiBaseMethod( + Runtime& runtime, const std::shared_ptr& bridge, + const Value* args, size_t count) { + if (count < 3 || !args[0].isObject() || !args[1].isObject() || + !args[2].isString()) { + throw JSError( + runtime, "__invokeBase expects base class, receiver, and member name."); + } + + Class baseClass = classFromEngineValue(runtime, args[0]); + if (baseClass == Nil) { + throw JSError(runtime, "__invokeBase base class is invalid."); + } + + Object receiverObject = args[1].asObject(runtime); + if (!receiverObject.isHostObject(runtime)) { + throw JSError(runtime, "__invokeBase receiver is not native."); + } + + auto receiverHostObject = + receiverObject.getHostObject(runtime); + id receiver = receiverHostObject->object(); + std::string memberName = args[2].asString(runtime).utf8(runtime); + size_t actualArgc = count - 3; + + NativeApiSymbol baseSymbol = runtimeSymbolForClass(bridge, baseClass); + const auto& members = bridge->membersForClass(baseSymbol); + const NativeApiMember* member = + selectMethodMember(members, memberName, false, actualArgc); + if (member == nullptr) { + if (const NativeApiMember* propertyMember = + selectWritablePropertyMember(members, memberName, false)) { + if (actualArgc == 0) { + Class dispatchClass = + dispatchSuperclassForEngineDerivedReceiver(receiver, baseClass); + return receiverHostObject->callObjectSelector( + runtime, propertyMember->selectorName, propertyMember, nullptr, 0, + dispatchClass); + } + if (actualArgc == 1 && !propertyMember->setterSelectorName.empty() && + !propertyMember->readonly) { + Class dispatchClass = + dispatchSuperclassForEngineDerivedReceiver(receiver, baseClass); + NativeApiMember setterMember = *propertyMember; + setterMember.selectorName = propertyMember->setterSelectorName; + setterMember.signatureOffset = propertyMember->setterSignatureOffset; + return receiverHostObject->callObjectSelector( + runtime, setterMember.selectorName, &setterMember, args + 3, + actualArgc, dispatchClass); + } + } + } + if (member == nullptr) { + throw JSError( + runtime, "Objective-C base selector is not available: " + memberName); + } + + Class dispatchClass = + dispatchSuperclassForEngineDerivedReceiver(receiver, baseClass); + return receiverHostObject->callObjectSelector(runtime, member->selectorName, + member, args + 3, actualArgc, + dispatchClass); +} diff --git a/NativeScript/ffi/objc/shared/bridge/HostObject.mm b/NativeScript/ffi/objc/shared/bridge/HostObject.mm new file mode 100644 index 000000000..2e671ed32 --- /dev/null +++ b/NativeScript/ffi/objc/shared/bridge/HostObject.mm @@ -0,0 +1,623 @@ +#ifndef NATIVESCRIPT_NATIVE_API_BACKEND_NAME +#error Engine backends must define NATIVESCRIPT_NATIVE_API_BACKEND_NAME. +#endif + +#ifndef NATIVESCRIPT_NATIVE_API_RUNTIME_NAME +#define NATIVESCRIPT_NATIVE_API_RUNTIME_NAME NATIVESCRIPT_NATIVE_API_BACKEND_NAME +#endif + +#ifndef NATIVESCRIPT_NATIVE_API_HAS_ENGINE_LAZY_GLOBALS +inline bool InstallNativeApiLazyGlobal( + Runtime&, std::shared_ptr, const std::string&, + const std::string&, bool) { + return false; +} +#endif + +#ifndef NATIVESCRIPT_NATIVE_API_HAS_ENGINE_SELECTOR_GROUP_FUNCTION +#error Engine backends must provide an engine selector group function. +#endif + +Function CreateNativeApiSelectorGroupFunction( + Runtime& runtime, std::shared_ptr bridge, + Class lookupClass, bool receiverIsClass, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations); + +class NativeApiHostObject final : public HostObject { + public: + explicit NativeApiHostObject(std::shared_ptr bridge) + : bridge_(std::move(bridge)) {} + + Value get(Runtime& runtime, const PropNameID& name) override { + std::string property = name.utf8(runtime); + if (property == "runtime") { + return makeString(runtime, NATIVESCRIPT_NATIVE_API_RUNTIME_NAME); + } + if (property == "backend") { + return makeString(runtime, NATIVESCRIPT_NATIVE_API_BACKEND_NAME); + } + if (property == "metadata") { + return metadataObject(runtime); + } + if (property == "hasScheduler") { + return bridge_->scheduler() != nullptr; + } + if (property == "interop") { + return createInteropObject(runtime, bridge_); + } +#ifdef NATIVESCRIPT_NATIVE_API_HAS_ENGINE_LAZY_GLOBALS + if (property == "__defineLazyGlobal") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "__defineLazyGlobal"), 3, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + std::string name = readStringArg(runtime, args, count, 0, "name"); + std::string kind = readStringArg(runtime, args, count, 1, "kind"); + bool force = count > 2 && args[2].isBool() && args[2].getBool(); + return InstallNativeApiLazyGlobal(runtime, bridge, name, kind, + force); + }); + } +#endif + if (property == "__fastEnumeration") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "__fastEnumeration"), 1, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + if (count < 1 || !args[0].isObject()) { + throw JSError( + runtime, "Fast enumeration expects a native object."); + } + id object = NativeApiObjectHostObject::nativeObjectFromValue(runtime, args[0]); + if (object == nil) { + throw JSError( + runtime, "Fast enumeration expects a native object."); + } + if (![object conformsToProtocol:@protocol(NSFastEnumeration)]) { + throw JSError( + runtime, "Object does not conform to NSFastEnumeration."); + } + return Object::createFromHostObject( + runtime, + std::make_shared( + bridge, static_cast>(object))); + }); + } + if (property == "import") { + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "import"), 1, + [](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + std::string path = readStringArg(runtime, args, count, 0, "path"); + std::string frameworkPath = path; + if (!frameworkPath.empty() && frameworkPath[0] != '/') { + frameworkPath = "/System/Library/Frameworks/" + frameworkPath + + ".framework"; + } + + NSBundle* bundle = [NSBundle + bundleWithPath:[NSString stringWithUTF8String:frameworkPath.c_str()]]; + if (bundle == nil || ![bundle load]) { + throw JSError( + runtime, "Could not load bundle: " + frameworkPath); + } + return true; + }); + } + if (property == "lookup") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "lookup"), 1, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + std::string symbolName = + readStringArg(runtime, args, count, 0, "name"); + const NativeApiSymbol* symbol = bridge->find(symbolName); + if (symbol == nullptr) { + return Value::null(); + } + return symbolToObject(runtime, *symbol); + }); + } + if (property == "getClass") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "getClass"), 1, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + std::string className = + readStringArg(runtime, args, count, 0, "name"); + const NativeApiSymbol* symbol = bridge->findClass(className); + if (symbol == nullptr) { + Class cls = objc_lookUpClass(className.c_str()); + if (cls == nil) { + return Value::null(); + } + NativeApiSymbol runtimeSymbol{ + .kind = NativeApiSymbolKind::Class, + .offset = MD_SECTION_OFFSET_NULL, + .name = className, + .runtimeName = className, + }; + return makeNativeClassValue(runtime, bridge, + std::move(runtimeSymbol)); + } + + return makeNativeClassValue(runtime, bridge, *symbol); + }); + } + if (property == "__extendClass") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "__extendClass"), 2, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + return extendNativeApiClass(runtime, bridge, args, count); + }); + } + if (property == "__invokeBase") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "__invokeBase"), 3, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + return invokeNativeApiBaseMethod(runtime, bridge, args, count); + }); + } + if (property == "__makeSelectorGroupFunction") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "__makeSelectorGroupFunction"), + 3, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + if (count < 3 || !args[1].isBool() || !args[2].isObject() || + !args[2].asObject(runtime).isArray(runtime)) { + throw JSError( + runtime, + "__makeSelectorGroupFunction expects class, receiver kind, " + "and selector table."); + } + + Class lookupClass = classFromEngineValue(runtime, args[0]); + if (lookupClass == Nil) { + throw JSError(runtime, + "__makeSelectorGroupFunction class is invalid."); + } + + bool receiverIsClass = args[1].getBool(); + Array selectorTable = args[2].asObject(runtime).getArray(runtime); + size_t selectorCount = selectorTable.size(runtime); + auto selectors = + std::make_shared< + std::vector>( + selectorCount); + for (size_t i = 0; i < selectorCount; i++) { + Value selectorValue = selectorTable.getValueAtIndex(runtime, i); + if (selectorValue.isString()) { + (*selectors)[i].selectorName = + selectorValue.asString(runtime).utf8(runtime); + } else if (selectorValue.isObject()) { + Object descriptor = selectorValue.asObject(runtime); + Value selectorNameValue = + descriptor.getProperty(runtime, "selectorName"); + if (!selectorNameValue.isString()) { + continue; + } + NativeApiMember member; + member.selectorName = + selectorNameValue.asString(runtime).utf8(runtime); + Value nameValue = descriptor.getProperty(runtime, "name"); + if (nameValue.isString()) { + member.name = nameValue.asString(runtime).utf8(runtime); + } + Value setterSelectorNameValue = + descriptor.getProperty(runtime, "setterSelectorName"); + if (setterSelectorNameValue.isString()) { + member.setterSelectorName = + setterSelectorNameValue.asString(runtime).utf8(runtime); + } + Value signatureOffsetValue = + descriptor.getProperty(runtime, "signatureOffset"); + if (signatureOffsetValue.isNumber()) { + member.signatureOffset = static_cast( + signatureOffsetValue.getNumber()); + } + Value setterSignatureOffsetValue = + descriptor.getProperty(runtime, "setterSignatureOffset"); + if (setterSignatureOffsetValue.isNumber()) { + member.setterSignatureOffset = static_cast( + setterSignatureOffsetValue.getNumber()); + } + Value flagsValue = descriptor.getProperty(runtime, "flags"); + if (flagsValue.isNumber()) { + member.flags = static_cast( + static_cast(flagsValue.getNumber())); + } + Value propertyValue = descriptor.getProperty(runtime, "property"); + if (propertyValue.isBool()) { + member.property = propertyValue.getBool(); + } + Value readonlyValue = descriptor.getProperty(runtime, "readonly"); + if (readonlyValue.isBool()) { + member.readonly = readonlyValue.getBool(); + } + (*selectors)[i].selectorName = member.selectorName; + (*selectors)[i].member = std::move(member); + (*selectors)[i].hasMember = true; + } + } + + auto preparedInvocations = std::make_shared>>( + selectors->size()); + + return CreateNativeApiSelectorGroupFunction( + runtime, bridge, lookupClass, receiverIsClass, selectors, + preparedInvocations); + }); + } + if (property == "__rememberClassWrapper") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "__rememberClassWrapper"), 3, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + if (count < 2) { + return Value::undefined(); + } + Class cls = classFromEngineValue(runtime, args[0]); + if (cls == Nil) { + return Value::undefined(); + } + bridge->rememberClassValue(runtime, cls, args[1]); + if (count >= 3 && args[2].isObject()) { + bridge->rememberClassPrototype(runtime, cls, args[2]); + } + return Value::undefined(); + }); + } + if (property == "__rememberObjectClassWrapper") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "__rememberObjectClassWrapper"), + 2, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + if (count < 2) { + return Value::undefined(); + } + id object = NativeApiObjectHostObject::nativeObjectFromValue( + runtime, args[0]); + if (object == nil) { + return Value::undefined(); + } + // A factory/class method may return an instance of a different + // class (e.g. +[TNSSwiftLikeFactory create] returns a TNSSwiftLike). + // Only label the object with this wrapper when it actually is an + // instance of the wrapper's class, so `constructor` resolves to the + // object's real class instead of the calling class. + if (args[1].isObject()) { + Class wrapperClass = classFromEngineValue(runtime, args[1]); + if (wrapperClass != Nil && ![object isKindOfClass:wrapperClass]) { + return Value::undefined(); + } + } + bridge->setObjectExpando(runtime, object, + "__nativeApiClassWrapper", args[1]); + if (args[1].isObject()) { + Object classWrapper = args[1].asObject(runtime); + Value prototypeValue = + classWrapper.getProperty(runtime, "prototype"); + if (prototypeValue.isObject()) { + Object instanceObject = args[0].asObject(runtime); + Object prototype = prototypeValue.asObject(runtime); + SetNativeApiObjectPrototype(runtime, instanceObject, + prototype); + } + } + return Value::undefined(); + }); + } + if (property == "CC_SHA256") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "CC_SHA256"), 3, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + if (count < 3 || !args[1].isNumber()) { + throw JSError( + runtime, "CC_SHA256 expects data, length, and output."); + } + void* commonCrypto = + dlopen("/usr/lib/system/libcommonCrypto.dylib", + RTLD_NOW | RTLD_LOCAL); + void* symbol = commonCrypto != nullptr + ? dlsym(commonCrypto, "CC_SHA256") + : nullptr; + if (symbol == nullptr && commonCrypto != nullptr) { + symbol = dlsym(commonCrypto, "_CC_SHA256"); + } + if (symbol == nullptr) { + throw JSError(runtime, + "CC_SHA256 is not available."); + } + NativeApiArgumentFrame frame(3); + void* data = pointerFromEngineValue(runtime, bridge, args[0], frame); + void* output = + pointerFromEngineValue(runtime, bridge, args[2], frame); + using CC_SHA256_Fn = unsigned char* (*)(const void*, unsigned long, + unsigned char*); + auto fn = reinterpret_cast(symbol); + unsigned char* result = + fn(data, static_cast(args[1].getNumber()), + static_cast(output)); + return createPointer(runtime, bridge, result); + }); + } + if (property == "getFunction") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "getFunction"), 1, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + std::string functionName = + readStringArg(runtime, args, count, 0, "name"); + const NativeApiSymbol* symbol = bridge->findFunction(functionName); + if (symbol == nullptr) { + return Value::null(); + } + auto prepared = + std::make_shared(); + prepared->symbol = *symbol; + auto function = Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, symbol->name), 0, + [bridge, prepared](Runtime& runtime, const Value&, + const Value* args, + size_t count) -> Value { + return callCFunction(runtime, bridge, prepared, args, count); + }); + function.setProperty(runtime, "kind", makeString(runtime, "function")); + function.setProperty(runtime, "nativeName", + makeString(runtime, symbol->name)); + function.setProperty(runtime, "metadataOffset", + static_cast(symbol->offset)); + function.setProperty(runtime, "sizeof", + static_cast(sizeof(void*))); + return function; + }); + } + if (property == "getConstant") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "getConstant"), 1, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + std::string constantName = + readStringArg(runtime, args, count, 0, "name"); + const NativeApiSymbol* symbol = bridge->findConstant(constantName); + if (symbol == nullptr) { + return Value::undefined(); + } + return constantToValue(runtime, bridge, *symbol); + }); + } + if (property == "getEnum") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "getEnum"), 1, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + std::string enumName = readStringArg(runtime, args, count, 0, "name"); + const NativeApiSymbol* symbol = bridge->findEnum(enumName); + if (symbol == nullptr) { + return Value::undefined(); + } + return enumToObject(runtime, bridge->metadata(), *symbol); + }); + } + if (property == "getProtocol") { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "getProtocol"), 1, + [bridge](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + std::string protocolName = + readStringArg(runtime, args, count, 0, "name"); + const NativeApiSymbol* symbol = bridge->findProtocol(protocolName); + if (symbol == nullptr) { + Protocol* protocol = lookupProtocolByNativeName(protocolName); + if (protocol == nullptr) { + return Value::null(); + } + const char* runtimeName = protocol_getName(protocol); + NativeApiSymbol runtimeSymbol{ + .kind = NativeApiSymbolKind::Protocol, + .offset = MD_SECTION_OFFSET_NULL, + .name = protocolName, + .runtimeName = runtimeName != nullptr ? runtimeName : protocolName, + }; + return makeNativeProtocolValue(runtime, bridge, + std::move(runtimeSymbol)); + } + return makeNativeProtocolValue(runtime, bridge, *symbol); + }); + } + if (property == "getStruct" || property == "getUnion") { + auto bridge = bridge_; + bool isUnion = property == "getUnion"; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, property.c_str()), 1, + [bridge, isUnion](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + std::string aggregateName = + readStringArg(runtime, args, count, 0, "name"); + const NativeApiSymbol* symbol = + isUnion ? bridge->findUnion(aggregateName) + : bridge->findStruct(aggregateName); + if (symbol == nullptr) { + return Value::undefined(); + } + return makeAggregateConstructor(runtime, bridge, *symbol); + }); + } + + if (const NativeApiSymbol* classSymbol = bridge_->findClass(property)) { + return makeNativeClassValue(runtime, bridge_, *classSymbol); + } + + if (const NativeApiSymbol* functionSymbol = bridge_->findFunction(property)) { + auto prepared = + std::make_shared(); + prepared->symbol = *functionSymbol; + auto bridge = bridge_; + Function function = Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, property.c_str()), 0, + [bridge, prepared](Runtime& runtime, const Value&, + const Value* args, + size_t count) -> Value { + return callCFunction(runtime, bridge, prepared, args, count); + }); + function.setProperty(runtime, "kind", makeString(runtime, "function")); + function.setProperty(runtime, "nativeName", + makeString(runtime, functionSymbol->name)); + function.setProperty(runtime, "metadataOffset", + static_cast(functionSymbol->offset)); + function.setProperty(runtime, "sizeof", + static_cast(sizeof(void*))); + return function; + } + + if (const NativeApiSymbol* constantSymbol = bridge_->findConstant(property)) { + return constantToValue(runtime, bridge_, *constantSymbol); + } + + if (const NativeApiSymbol* enumSymbol = bridge_->findEnum(property)) { + return enumToObject(runtime, bridge_->metadata(), *enumSymbol); + } + + if (const NativeApiSymbol* protocolSymbol = + bridge_->findProtocol(property)) { + return makeNativeProtocolValue(runtime, bridge_, *protocolSymbol); + } + + if (const NativeApiSymbol* aggregateSymbol = + bridge_->findAggregate(property)) { + return makeAggregateConstructor(runtime, bridge_, *aggregateSymbol); + } + + return Value::undefined(); + } + + std::vector getPropertyNames(Runtime& runtime) override { + std::vector names; + names.reserve(11); + addPropertyName(runtime, names, "runtime"); + addPropertyName(runtime, names, "backend"); + addPropertyName(runtime, names, "metadata"); + addPropertyName(runtime, names, "hasScheduler"); + addPropertyName(runtime, names, "interop"); +#ifdef NATIVESCRIPT_NATIVE_API_HAS_ENGINE_LAZY_GLOBALS + addPropertyName(runtime, names, "__defineLazyGlobal"); +#endif + addPropertyName(runtime, names, "import"); + addPropertyName(runtime, names, "lookup"); + addPropertyName(runtime, names, "getClass"); + addPropertyName(runtime, names, "__extendClass"); + addPropertyName(runtime, names, "__invokeBase"); + addPropertyName(runtime, names, "__makeSelectorGroupFunction"); + addPropertyName(runtime, names, "__rememberClassWrapper"); + addPropertyName(runtime, names, "__rememberObjectClassWrapper"); + addPropertyName(runtime, names, "getFunction"); + addPropertyName(runtime, names, "getConstant"); + addPropertyName(runtime, names, "getEnum"); + addPropertyName(runtime, names, "getProtocol"); + addPropertyName(runtime, names, "getStruct"); + addPropertyName(runtime, names, "getUnion"); + return names; + } + + private: + Object metadataObject(Runtime& runtime) const { + Object metadata(runtime); + metadata.setProperty(runtime, "classes", + static_cast(bridge_->classCount())); + metadata.setProperty(runtime, "functions", + static_cast(bridge_->functionCount())); + metadata.setProperty(runtime, "constants", + static_cast(bridge_->constantCount())); + metadata.setProperty(runtime, "protocols", + static_cast(bridge_->protocolCount())); + metadata.setProperty(runtime, "enums", + static_cast(bridge_->enumCount())); + metadata.setProperty(runtime, "structs", + static_cast(bridge_->structCount())); + metadata.setProperty(runtime, "unions", + static_cast(bridge_->unionCount())); + + metadata.setProperty( + runtime, "classNames", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "classNames"), 0, + [bridge = bridge_](Runtime& runtime, const Value&, const Value*, + size_t) -> Value { + return namesToArray(runtime, bridge->classNames()); + })); + metadata.setProperty( + runtime, "functionNames", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "functionNames"), 0, + [bridge = bridge_](Runtime& runtime, const Value&, const Value*, + size_t) -> Value { + return namesToArray(runtime, bridge->functionNames()); + })); + metadata.setProperty( + runtime, "constantNames", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "constantNames"), 0, + [bridge = bridge_](Runtime& runtime, const Value&, const Value*, + size_t) -> Value { + return namesToArray(runtime, bridge->constantNames()); + })); + metadata.setProperty( + runtime, "protocolNames", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "protocolNames"), 0, + [bridge = bridge_](Runtime& runtime, const Value&, const Value*, + size_t) -> Value { + return namesToArray(runtime, bridge->protocolNames()); + })); + metadata.setProperty( + runtime, "enumNames", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "enumNames"), 0, + [bridge = bridge_](Runtime& runtime, const Value&, const Value*, + size_t) -> Value { + return namesToArray(runtime, bridge->enumNames()); + })); + metadata.setProperty( + runtime, "structNames", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "structNames"), 0, + [bridge = bridge_](Runtime& runtime, const Value&, const Value*, + size_t) -> Value { + return namesToArray(runtime, bridge->structNames()); + })); + metadata.setProperty( + runtime, "unionNames", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "unionNames"), 0, + [bridge = bridge_](Runtime& runtime, const Value&, const Value*, + size_t) -> Value { + return namesToArray(runtime, bridge->unionNames()); + })); + return metadata; + } + + std::shared_ptr bridge_; +}; diff --git a/NativeScript/ffi/objc/shared/bridge/HostObjects.mm b/NativeScript/ffi/objc/shared/bridge/HostObjects.mm new file mode 100644 index 000000000..e0ea2b3c0 --- /dev/null +++ b/NativeScript/ffi/objc/shared/bridge/HostObjects.mm @@ -0,0 +1,2520 @@ +// HostObject::set returns bool on engines whose interceptors can defer an +// unhandled set to the JS prototype chain. JSI's HostObject::set is void, so +// the Hermes backend defines NATIVESCRIPT_NATIVE_API_HOST_SET_VOID and the +// set overrides below collapse their return type/values accordingly. +#ifdef NATIVESCRIPT_NATIVE_API_HOST_SET_VOID +using NativeApiHostSetResult = void; +#define NATIVE_API_SET_RETURN(handled) return +#else +using NativeApiHostSetResult = bool; +#define NATIVE_API_SET_RETURN(handled) return (handled) +#endif + +// Engine-neutral factory for native object instance wrappers. V8 uses its +// kNonMasking native instance template (fast prototype-based property access); +// every other engine uses its standard host-object creation. Selected at +// compile time so the shared bridge code stays engine-agnostic. +template +Object createNativeInstanceHostObject(Runtime& runtime, std::shared_ptr host) { +#ifdef TARGET_ENGINE_V8 + return Object::createNativeInstanceHostObject(runtime, std::move(host)); +#else + return Object::createFromHostObject(runtime, std::move(host)); +#endif +} + +class NativeApiObjectLifetimeState final { + public: + explicit NativeApiObjectLifetimeState(id object) + : object_(reinterpret_cast(object)) {} + + id object() const { + return reinterpret_cast(object_.load(std::memory_order_relaxed)); + } + + void setObject(id object) { + object_.store(reinterpret_cast(object), std::memory_order_relaxed); + } + + void clear() { object_.store(nullptr, std::memory_order_relaxed); } + + private: + std::atomic object_{nullptr}; +}; + + +class NativeApiPointerHostObject final + : public HostObject, + public std::enable_shared_from_this { + public: + NativeApiPointerHostObject(std::shared_ptr bridge, + void* pointer, std::string kind = "pointer", + bool adopted = false, + std::shared_ptr backingValue = nullptr) + : bridge_(std::move(bridge)), + pointer_(pointer), + kind_(std::move(kind)), + adopted_(adopted), + backingValue_(std::move(backingValue)) {} + + ~NativeApiPointerHostObject() override { + if (adopted_ && pointer_ != nullptr) { + if (bridge_ != nullptr) { + bridge_->forgetPointerValue(pointer_); + } + free(pointer_); + pointer_ = nullptr; + } + } + + void* pointer() const { return pointer_; } + std::shared_ptr backingValue() const { return backingValue_; } + void setBackingValue(Runtime& runtime, const Value& value) { + backingValue_ = std::make_shared(runtime, value); + } + bool adopted() const { return adopted_; } + void adopt() { adopted_ = true; } + void clearWithoutFree() { + if (bridge_ != nullptr) { + bridge_->forgetPointerValue(pointer_); + } + pointer_ = nullptr; + adopted_ = false; + backingValue_.reset(); + } + + Value get(Runtime& runtime, const PropNameID& name) override { + std::string property = name.utf8(runtime); + if (property == "kind") { + return makeString(runtime, kind_); + } + if (property == "address") { + return static_cast(reinterpret_cast(pointer_)); + } + if (property == "adopted") { + return adopted_; + } + if (property == "takeRetainedValue" || property == "takeUnretainedValue") { + bool retained = property == "takeRetainedValue"; + std::weak_ptr weakSelf = shared_from_this(); + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, property.c_str()), 0, + [weakSelf, retained](Runtime& runtime, const Value&, const Value*, + size_t) -> Value { + auto self = weakSelf.lock(); + if (!self || self->pointer_ == nullptr || self->consumed_) { + throw JSError(runtime, "Unmanaged value has already been consumed."); + } + id object = static_cast(self->pointer_); + self->consumed_ = true; + self->pointer_ = nullptr; + self->adopted_ = false; + self->backingValue_.reset(); + return makeNativeObjectValue(runtime, self->bridge_, object, retained); + }); + } + if (property == "add" || property == "subtract") { + void* pointer = pointer_; + bool add = property == "add"; + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, property.c_str()), 1, + [bridge, pointer, add](Runtime& runtime, const Value&, + const Value* args, size_t count) -> Value { + if (count < 1 || !args[0].isNumber()) { + throw JSError(runtime, "Pointer offset must be a number."); + } + intptr_t offset = static_cast(args[0].getNumber()); + intptr_t base = reinterpret_cast(pointer); + void* result = reinterpret_cast(add ? base + offset : base - offset); + return createPointer(runtime, bridge, result); + }); + } + if (property == "toNumber") { + void* pointer = pointer_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "toNumber"), 0, + [pointer](Runtime&, const Value&, const Value*, size_t) -> Value { + return static_cast(reinterpret_cast(pointer)); + }); + } + if (property == "toBigInt") { + void* pointer = pointer_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "toBigInt"), 0, + [pointer](Runtime& runtime, const Value&, const Value*, size_t) -> Value { + return BigInt::fromUint64( + runtime, + static_cast(reinterpret_cast(pointer))); + }); + } + if (property == "toHexString" || property == "toDecimalString") { + void* pointer = pointer_; + bool hex = property == "toHexString"; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, property.c_str()), 0, + [pointer, hex](Runtime& runtime, const Value&, const Value*, size_t) -> Value { + if (hex) { + char text[2 + sizeof(uintptr_t) * 2 + 1] = {}; + snprintf(text, sizeof(text), "0x%llx", + static_cast( + reinterpret_cast(pointer))); + return makeString(runtime, text); + } else { + char text[32] = {}; + snprintf(text, sizeof(text), "%lld", + static_cast(reinterpret_cast(pointer))); + return makeString(runtime, text); + } + }); + } + if (property == "toString") { + void* pointer = pointer_; + std::string kind = kind_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "toString"), 0, + [pointer, kind](Runtime& runtime, const Value&, const Value*, + size_t) -> Value { + char address[32] = {}; + snprintf(address, sizeof(address), "%p", pointer); + if (kind == "pointer") { + return makeString(runtime, + ""); + } + return makeString(runtime, "[NativeApi " + kind + " " + + std::string(address) + "]"); + }); + } + return Value::undefined(); + } + + std::vector getPropertyNames(Runtime& runtime) override { + std::vector names; + names.reserve(3); + addPropertyName(runtime, names, "kind"); + addPropertyName(runtime, names, "address"); + addPropertyName(runtime, names, "adopted"); + addPropertyName(runtime, names, "takeRetainedValue"); + addPropertyName(runtime, names, "takeUnretainedValue"); + addPropertyName(runtime, names, "add"); + addPropertyName(runtime, names, "subtract"); + addPropertyName(runtime, names, "toNumber"); + addPropertyName(runtime, names, "toBigInt"); + addPropertyName(runtime, names, "toHexString"); + addPropertyName(runtime, names, "toDecimalString"); + addPropertyName(runtime, names, "toString"); + return names; + } + + private: + std::shared_ptr bridge_; + void* pointer_ = nullptr; + std::string kind_; + bool adopted_ = false; + bool consumed_ = false; + std::shared_ptr backingValue_; +}; + +class NativeApiReferenceHostObject final : public HostObject { + public: + NativeApiReferenceHostObject(std::shared_ptr bridge, + NativeApiType type, void* data, bool ownsData, + size_t byteLength = 0, + std::shared_ptr pendingValue = nullptr, + std::shared_ptr backingValue = nullptr) + : bridge_(std::move(bridge)), + type_(std::move(type)), + data_(data), + ownsData_(ownsData), + byteLength_(byteLength), + pendingValue_(std::move(pendingValue)), + backingValue_(std::move(backingValue)) {} + + ~NativeApiReferenceHostObject() override { + for (id object : retainedObjects_) { + [object release]; + } + if (ownsData_ && data_ != nullptr) { + free(data_); + data_ = nullptr; + } + } + + void* data() const { return data_; } + const NativeApiType& type() const { return type_; } + std::shared_ptr backingValue() const { return backingValue_; } + void ensureStorage(Runtime& runtime, NativeApiType type, + NativeApiArgumentFrame& frame, size_t elements = 1); + void retainObjectSlot(size_t index, id object); + + Value get(Runtime& runtime, const PropNameID& name) override; + NativeApiHostSetResult set(Runtime& runtime, const PropNameID& name, const Value& value) override; + std::vector getPropertyNames(Runtime& runtime) override { + std::vector names; + addPropertyName(runtime, names, "kind"); + addPropertyName(runtime, names, "value"); + addPropertyName(runtime, names, "address"); + addPropertyName(runtime, names, "toString"); + return names; + } + + private: + std::shared_ptr bridge_; + NativeApiType type_; + void* data_ = nullptr; + bool ownsData_ = false; + size_t byteLength_ = 0; + std::shared_ptr pendingValue_; + std::shared_ptr backingValue_; + std::vector retainedObjects_; +}; + +class NativeApiStructObjectHostObject final : public HostObject { + public: + NativeApiStructObjectHostObject( + std::shared_ptr bridge, + std::shared_ptr info, + const void* data = nullptr, bool ownsData = true, + std::shared_ptr> storageOwner = nullptr, + std::shared_ptr backingValue = nullptr) + : bridge_(std::move(bridge)), + info_(std::move(info)), + ownedData_(std::move(storageOwner)), + backingValue_(std::move(backingValue)), + ownsData_(ownsData) { + size_t size = info_ != nullptr ? info_->size : 0; + if (ownedData_ != nullptr) { + data_ = const_cast(data); + ownsData_ = false; + } else if (ownsData_) { + ownedData_ = std::make_shared>(size, 0); + if (data != nullptr && size > 0) { + std::memcpy(ownedData_->data(), data, size); + } + data_ = ownedData_->empty() ? nullptr : ownedData_->data(); + } else { + data_ = const_cast(data); + } + } + + void* data() const { return data_; } + std::shared_ptr info() const { return info_; } + std::shared_ptr> storageOwner() const { + return ownedData_; + } + std::shared_ptr backingValue() const { return backingValue_; } + + Value get(Runtime& runtime, const PropNameID& name) override; + NativeApiHostSetResult set(Runtime& runtime, const PropNameID& name, const Value& value) override; + std::vector getPropertyNames(Runtime& runtime) override; + + private: + std::shared_ptr bridge_; + std::shared_ptr info_; + std::shared_ptr> ownedData_; + std::shared_ptr backingValue_; + void* data_ = nullptr; + bool ownsData_ = true; +}; + +class NativeApiFastEnumerationIteratorHostObject final : public HostObject { + public: + NativeApiFastEnumerationIteratorHostObject( + std::shared_ptr bridge, id collection) + : bridge_(std::move(bridge)), collection_(collection) { + [(id)collection_ retain]; + } + + ~NativeApiFastEnumerationIteratorHostObject() override { + [(id)collection_ release]; + collection_ = nil; + } + + Value get(Runtime& runtime, const PropNameID& name) override { + std::string property = name.utf8(runtime); + if (property == "next") { + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "next"), 0, + [this](Runtime& runtime, const Value&, const Value*, size_t) -> Value { + return next(runtime); + }); + } + return Value::undefined(); + } + + std::vector getPropertyNames(Runtime& runtime) override { + std::vector names; + addPropertyName(runtime, names, "next"); + return names; + } + + private: + Value next(Runtime& runtime) { + Object result(runtime); + if (done_ || collection_ == nil) { + result.setProperty(runtime, "done", true); + return result; + } + + if (stackIndex_ >= stackLength_) { + stackLength_ = [collection_ countByEnumeratingWithState:&state_ + objects:stack_ + count:16]; + stackIndex_ = 0; + if (stackLength_ == 0) { + done_ = true; + result.setProperty(runtime, "done", true); + return result; + } + } + + id value = state_.itemsPtr[stackIndex_++]; + NativeApiType valueType = nativeObjectReturnTypeForClass(object_getClass(value)); + result.setProperty(runtime, "value", + convertNativeReturnValue(runtime, bridge_, valueType, &value)); + result.setProperty(runtime, "done", false); + return result; + } + + std::shared_ptr bridge_; + id collection_ = nil; + NSFastEnumerationState state_ = {}; + id __unsafe_unretained stack_[16] = {}; + NSUInteger stackLength_ = 0; + NSUInteger stackIndex_ = 0; + bool done_ = false; +}; + +NativeApiSymbol nativeApiSymbolForRuntimeClass( + const std::shared_ptr& bridge, Class cls) { + const char* name = cls != Nil ? class_getName(cls) : ""; + if (bridge != nullptr) { + if (const NativeApiSymbol* symbol = bridge->findClassForRuntimePointer(cls)) { + return *symbol; + } + if (const NativeApiSymbol* symbol = bridge->findClassForRuntimeClass(cls)) { + return *symbol; + } + if (name != nullptr) { + if (const NativeApiSymbol* symbol = bridge->findClass(name)) { + return *symbol; + } + } + } + + return NativeApiSymbol{ + .kind = NativeApiSymbolKind::Class, + .offset = MD_SECTION_OFFSET_NULL, + .name = name != nullptr ? name : "", + .runtimeName = name != nullptr ? name : "", + }; +} + +std::optional runtimeWritablePropertySetter(id object, + const std::string& property) { + if (object == nil || property.empty()) { + return std::nullopt; + } + + Class current = object_getClass(object); + while (current != Nil) { + objc_property_t prop = class_getProperty(current, property.c_str()); + if (prop != nullptr) { + if (char* readonly = property_copyAttributeValue(prop, "R")) { + free(readonly); + return std::nullopt; + } + + std::string setter = setterSelectorForProperty(property); + if (char* customSetter = property_copyAttributeValue(prop, "S")) { + setter = customSetter; + free(customSetter); + } + + SEL selector = sel_getUid(setter.c_str()); + if ([object respondsToSelector:selector]) { + return setter; + } + } + + current = class_getSuperclass(current); + } + + std::string setter = setterSelectorForProperty(property); + SEL selector = sel_getUid(setter.c_str()); + if ([object respondsToSelector:selector]) { + return setter; + } + + return std::nullopt; +} + +std::optional runtimeReadablePropertyGetter(id object, + const std::string& property) { + if (object == nil || property.empty()) { + return std::nullopt; + } + + Class current = object_getClass(object); + while (current != Nil) { + objc_property_t prop = class_getProperty(current, property.c_str()); + if (prop != nullptr) { + std::string getter = property; + if (char* customGetter = property_copyAttributeValue(prop, "G")) { + getter = customGetter; + free(customGetter); + } + + if (auto selector = + respondingPropertyGetterSelector(object, property, getter)) { + return selector; + } + } + + current = class_getSuperclass(current); + } + + return respondingPropertyGetterSelector(object, property, property); +} + +class NativeApiSuperHostObject final : public HostObject { + public: + NativeApiSuperHostObject(std::shared_ptr bridge, + id receiver, Class dispatchClass) + : bridge_(std::move(bridge)), + receiver_(receiver), + dispatchClass_(dispatchClass) { + if (receiver_ != nil) { + [receiver_ retain]; + } + } + + ~NativeApiSuperHostObject() override { + if (receiver_ != nil) { + [receiver_ release]; + receiver_ = nil; + } + } + + Value get(Runtime& runtime, const PropNameID& name) override { + std::string property = name.utf8(runtime); + if (property == "kind") { + return makeString(runtime, "super"); + } + if (property == "toString") { + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "toString"), 0, + [](Runtime& runtime, const Value&, const Value*, size_t) -> Value { + return makeString(runtime, "[NativeApiSuper]"); + }); + } + if (receiver_ == nil || dispatchClass_ == Nil) { + return Value::undefined(); + } + + if (const NativeApiSymbol* symbol = + bridge_->findClassForRuntimeClass(dispatchClass_)) { + const auto& members = bridge_->membersForClass(*symbol); + if (const NativeApiMember* propertyMember = + selectPropertyMember(members, property, false)) { + SEL selector = sel_getUid(propertyMember->selectorName.c_str()); + if (class_getInstanceMethod(dispatchClass_, selector) != nullptr) { + return callObjCSelector(runtime, bridge_, receiver_, false, + propertyMember->selectorName, propertyMember, + nullptr, 0, dispatchClass_); + } + } + + if (hasMethodMember(members, property, false)) { + auto bridge = bridge_; + id receiver = receiver_; + Class dispatchClass = dispatchClass_; + std::string memberName = property; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, property.c_str()), 0, + [bridge, receiver, dispatchClass, memberName]( + Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + const NativeApiSymbol* symbol = + bridge->findClassForRuntimeClass(dispatchClass); + if (symbol == nullptr) { + throw JSError( + runtime, "Objective-C metadata is not available for super."); + } + const NativeApiMember* selected = selectMethodMember( + bridge->membersForClass(*symbol), memberName, false, count); + if (selected == nullptr) { + throw JSError( + runtime, "Objective-C super selector is not available: " + + memberName); + } + return callObjCSelector(runtime, bridge, receiver, false, + selected->selectorName, selected, args, + count, dispatchClass); + }); + } + } + + return Value::undefined(); + } + + NativeApiHostSetResult set(Runtime& runtime, const PropNameID& name, const Value& value) override { + std::string property = name.utf8(runtime); + if (receiver_ == nil || dispatchClass_ == Nil) { + throw JSError(runtime, "Cannot set property on nil super."); + } + + if (const NativeApiSymbol* symbol = + bridge_->findClassForRuntimeClass(dispatchClass_)) { + const auto& members = bridge_->membersForClass(*symbol); + if (const NativeApiMember* propertyMember = + selectWritablePropertyMember(members, property, false)) { + if (propertyMember->readonly || + propertyMember->setterSelectorName.empty()) { + throw JSError( + runtime, "Attempted to assign to readonly property."); + } + NativeApiMember setterMember = *propertyMember; + setterMember.selectorName = propertyMember->setterSelectorName; + setterMember.signatureOffset = propertyMember->setterSignatureOffset; + Value args[] = {Value(runtime, value)}; + callObjCSelector(runtime, bridge_, receiver_, false, + setterMember.selectorName, &setterMember, args, 1, + dispatchClass_); + NATIVE_API_SET_RETURN(true); + } + } + + std::string setterSelectorName = setterSelectorForProperty(property); + SEL selector = sel_getUid(setterSelectorName.c_str()); + if (class_getInstanceMethod(dispatchClass_, selector) != nullptr) { + Value args[] = {Value(runtime, value)}; + callObjCSelector(runtime, bridge_, receiver_, false, setterSelectorName, + nullptr, args, 1, dispatchClass_); + NATIVE_API_SET_RETURN(true); + } + + throw JSError(runtime, + "No writable native super property: " + + property); + } + + std::vector getPropertyNames(Runtime& runtime) override { + std::vector names; + addPropertyName(runtime, names, "kind"); + addPropertyName(runtime, names, "toString"); + return names; + } + + private: + std::shared_ptr bridge_; + id receiver_ = nil; + Class dispatchClass_ = Nil; +}; + +struct NativeApiRuntimeMember { + std::string name; + std::string selectorName; + size_t argumentCount = 0; +}; + +using NativeApiRuntimeMembers = std::vector; + +struct NativeApiRuntimeMemberIndex { + NativeApiRuntimeMembers members; + std::unordered_set memberNames; + std::unordered_map> + selectorsByNameAndCount; +}; + +struct NativeApiRuntimeMembersCacheKey { + Class cls = Nil; + bool staticMembers = false; + + bool operator==(const NativeApiRuntimeMembersCacheKey& other) const { + return cls == other.cls && staticMembers == other.staticMembers; + } +}; + +struct NativeApiRuntimeMembersCacheKeyHash { + size_t operator()(const NativeApiRuntimeMembersCacheKey& key) const { + size_t classHash = std::hash{}(reinterpret_cast(key.cls)); + return classHash ^ (key.staticMembers ? 0x9e3779b97f4a7c15ULL : 0); + } +}; + +std::mutex& runtimeMembersCacheMutex() { + static std::mutex mutex; + return mutex; +} + +std::unordered_map, + NativeApiRuntimeMembersCacheKeyHash>& +runtimeMembersCache() { + static std::unordered_map, + NativeApiRuntimeMembersCacheKeyHash> + cache; + return cache; +} + +std::shared_ptr emptyRuntimeMembers() { + static auto empty = std::make_shared(); + return empty; +} + +NativeApiRuntimeMemberIndex buildRuntimeMembersForClass(Class cls, + bool staticMembers) { + NativeApiRuntimeMemberIndex index; + if (cls == Nil) { + return index; + } + + std::unordered_set seen; + Class current = staticMembers ? object_getClass(cls) : cls; + while (current != Nil) { + unsigned int methodCount = 0; + Method* methods = class_copyMethodList(current, &methodCount); + for (unsigned int i = 0; i < methodCount; i++) { + SEL selector = method_getName(methods[i]); + const char* selectorName = selector != nullptr ? sel_getName(selector) : nullptr; + if (selectorName == nullptr || selectorName[0] == '\0') { + continue; + } + + std::string selectorString(selectorName); + std::string name = jsifySelector(selectorString.c_str()); + if (name.empty()) { + continue; + } + + size_t argumentCount = selectorArgumentCount(selectorString); + std::string key = name + "\x1f" + std::to_string(argumentCount); + if (!seen.insert(key).second) { + continue; + } + + index.memberNames.insert(name); + index.selectorsByNameAndCount[name].emplace(argumentCount, selectorString); + index.members.push_back(NativeApiRuntimeMember{ + .name = std::move(name), + .selectorName = std::move(selectorString), + .argumentCount = argumentCount, + }); + } + if (methods != nullptr) { + free(methods); + } + current = class_getSuperclass(current); + } + + return index; +} + +std::shared_ptr runtimeMembersForClass( + Class cls, bool staticMembers) { + if (cls == Nil) { + return emptyRuntimeMembers(); + } + + NativeApiRuntimeMembersCacheKey key{.cls = cls, + .staticMembers = staticMembers}; + + { + std::lock_guard lock(runtimeMembersCacheMutex()); + auto& cache = runtimeMembersCache(); + auto cached = cache.find(key); + if (cached != cache.end()) { + return cached->second; + } + } + + auto members = + std::make_shared( + buildRuntimeMembersForClass(cls, staticMembers)); + + { + std::lock_guard lock(runtimeMembersCacheMutex()); + auto& cache = runtimeMembersCache(); + auto [cached, inserted] = cache.emplace(key, members); + return inserted ? members : cached->second; + } +} + +bool hasRuntimeMemberForName(Class cls, bool staticMembers, + const std::string& name) { + auto index = runtimeMembersForClass(cls, staticMembers); + return index->memberNames.find(name) != index->memberNames.end(); +} + +std::optional selectRuntimeSelectorForName( + Class cls, bool staticMembers, const std::string& name, size_t count) { + auto index = runtimeMembersForClass(cls, staticMembers); + auto selectorsForName = index->selectorsByNameAndCount.find(name); + if (selectorsForName == index->selectorsByNameAndCount.end()) { + return std::nullopt; + } + auto selector = selectorsForName->second.find(count); + if (selector == selectorsForName->second.end()) { + return std::nullopt; + } + return selector->second; +} + +Array runtimeMembersArray(Runtime& runtime, Class cls, bool staticMembers) { + auto index = runtimeMembersForClass(cls, staticMembers); + Array result(runtime, index->members.size()); + for (size_t i = 0; i < index->members.size(); i++) { + const auto& member = index->members[i]; + Object descriptor(runtime); + descriptor.setProperty(runtime, "name", makeString(runtime, member.name)); + descriptor.setProperty(runtime, "selectorName", + makeString(runtime, member.selectorName)); + descriptor.setProperty(runtime, "argumentCount", + static_cast(member.argumentCount)); + descriptor.setProperty(runtime, "property", false); + descriptor.setProperty(runtime, "readonly", false); + descriptor.setProperty(runtime, "setterSelectorName", makeString(runtime, "")); + result.setValueAtIndex(runtime, i, descriptor); + } + return result; +} + +class NativeApiObjectHostObject final + : public HostObject, + public std::enable_shared_from_this { + public: + NativeApiObjectHostObject(std::shared_ptr bridge, + id object, bool ownsObject) + : bridge_(std::move(bridge)), + object_(object), + ownsObject_(ownsObject), + lifetimeState_(std::make_shared(object)) { + if (bridge_ != nullptr && object_ != nil) { + bridge_->retainObjectExpandoOwner(object_); + } + if (object_ != nil && !ownsObject_) { + [object_ retain]; + ownsObject_ = true; + wrapperRetainedObject_ = true; + } + } + + ~NativeApiObjectHostObject() override { + if (bridge_ != nullptr && object_ != nil) { + bridge_->forgetRoundTripValue(object_); + bridge_->releaseObjectExpandoOwner( + object_, class_conformsToProtocol(object_getClass(object_), + @protocol(NativeApiClassBuilderProtocol))); + } + if (lifetimeState_ != nullptr) { + lifetimeState_->clear(); + } + if (ownsObject_ && object_ != nil) { + [object_ release]; + object_ = nil; + } + } + + id object() const { return object_; } + std::shared_ptr lifetimeState() const { + return lifetimeState_; + } + + // Store a JS-owned property as a bridge expando (read back by get()). Used by + // engine adapters whose exotic property storage doesn't fall back to own + // properties when the host set handler defers. + void storeOwnExpando(Runtime& runtime, const std::string& property, + const Value& value) { + if (object_ != nil) { + bridge_->setObjectExpando(runtime, object_, property, value); + } + } + + void disownObject(id expected, bool preserveExpandos = false) { + if (object_ == expected) { + if (bridge_ != nullptr && expected != nil) { + bridge_->forgetRoundTripValue(expected); + bridge_->releaseObjectExpandoOwner(expected, preserveExpandos); + } + ownsObject_ = false; + wrapperRetainedObject_ = false; + object_ = nil; + if (lifetimeState_ != nullptr) { + lifetimeState_->clear(); + } + } + } + + static bool isInitializerSelector(const std::string& selectorName) { + return selectorName.rfind("init", 0) == 0; + } + + static id nativeObjectFromValue(Runtime& runtime, const Value& value) { + if (!value.isObject()) { + return nil; + } + Object object = value.asObject(runtime); + if (!object.isHostObject(runtime)) { + return nil; + } + return object.getHostObject(runtime)->object(); + } + + static Value descriptionString(Runtime& runtime, id object) { + NSString* description = nil; + performDirectObjCInvocation(runtime, [&]() { + description = [(object != nil ? [object description] : @"") copy]; + }); + std::string text = description.UTF8String ?: ""; + [description release]; + return makeString(runtime, text); + } + + Value callObjectSelector(Runtime& runtime, const std::string& selectorName, + const NativeApiMember* member, const Value* args, + size_t count, Class dispatchSuperClass = Nil) { + id receiver = object_; + if (receiver == nil) { + throw JSError(runtime, + "Cannot send Objective-C selector to nil."); + } + + const bool initializer = isInitializerSelector(selectorName); + std::optional classWrapper; + if (initializer) { + Value classWrapperValue = bridge_->findObjectExpando( + runtime, receiver, "__nativeApiClassWrapper"); + if (classWrapperValue.isObject()) { + classWrapper.emplace(classWrapperValue.asObject(runtime)); + } + bridge_->forgetRoundTripValue(runtime, receiver); + } + + Value result = + callObjCSelector(runtime, bridge_, receiver, false, selectorName, member, + args, count, dispatchSuperClass); + if (initializer) { + id resultObject = nativeObjectFromValue(runtime, result); + disownObject(receiver, resultObject == receiver); + if (resultObject != nil) { + // Re-adopt the init result on this host object so that JS overrides + // returning `this` still have a valid native object. + object_ = resultObject; + ownsObject_ = true; + wrapperRetainedObject_ = true; + if (bridge_ != nullptr) { + bridge_->retainObjectExpandoOwner(object_); + } + if (lifetimeState_ != nullptr) { + lifetimeState_->setObject(object_); + } + [object_ retain]; + if (classWrapper) { + bridge_->setObjectExpando(runtime, resultObject, + "__nativeApiClassWrapper", + Value(runtime, *classWrapper)); + if (result.isObject()) { + Value prototypeValue = classWrapper->getProperty(runtime, "prototype"); + if (prototypeValue.isObject()) { + Object resultValue = result.asObject(runtime); + Object prototype = prototypeValue.asObject(runtime); + SetNativeApiObjectPrototype(runtime, resultValue, prototype); + } + } + } + } + } + return result; + } + + Value callPreparedObjectSelector( + Runtime& runtime, const NativeApiPreparedObjCInvocation& prepared, + const Value* args, size_t count, Class dispatchSuperClass = Nil) { + id receiver = object_; + if (receiver == nil) { + throw JSError(runtime, + "Cannot send Objective-C selector to nil."); + } + + const bool initializer = preparedObjCInvocationIsInit(prepared); + std::optional classWrapper; + if (initializer) { + Value classWrapperValue = bridge_->findObjectExpando( + runtime, receiver, "__nativeApiClassWrapper"); + if (classWrapperValue.isObject()) { + classWrapper.emplace(classWrapperValue.asObject(runtime)); + } + bridge_->forgetRoundTripValue(runtime, receiver); + } + + Value result = callPreparedObjCSelector( + runtime, bridge_, receiver, false, prepared, args, count, + dispatchSuperClass); + if (initializer) { + id resultObject = nativeObjectFromValue(runtime, result); + disownObject(receiver, resultObject == receiver); + if (resultObject != nil) { + // Re-adopt the init result on this host object so that JS overrides + // returning `this` still have a valid native object. + object_ = resultObject; + ownsObject_ = true; + wrapperRetainedObject_ = true; + if (bridge_ != nullptr) { + bridge_->retainObjectExpandoOwner(object_); + } + if (lifetimeState_ != nullptr) { + lifetimeState_->setObject(object_); + } + [object_ retain]; + if (classWrapper) { + bridge_->setObjectExpando(runtime, resultObject, + "__nativeApiClassWrapper", + Value(runtime, *classWrapper)); + if (result.isObject()) { + Value prototypeValue = classWrapper->getProperty(runtime, "prototype"); + if (prototypeValue.isObject()) { + Object resultValue = result.asObject(runtime); + Object prototype = prototypeValue.asObject(runtime); + SetNativeApiObjectPrototype(runtime, resultValue, prototype); + } + } + } + } + } + return result; + } + + Value classPrototypeForObject(Runtime& runtime) { + if (object_ == nil) { + return Value::undefined(); + } + + Value classWrapperValue = bridge_->findObjectExpando( + runtime, object_, "__nativeApiClassWrapper"); + if (!classWrapperValue.isObject()) { + classWrapperValue = bridge_->findClassValue(runtime, object_getClass(object_)); + } + if (!classWrapperValue.isObject()) { + if (const NativeApiSymbol* symbol = + bridge_->findClassForRuntimeClass(object_getClass(object_))) { + classWrapperValue = bridge_->findClassValue( + runtime, objc_lookUpClass(symbol->runtimeName.c_str())); + } + } + if (classWrapperValue.isObject()) { + Object classWrapper = classWrapperValue.asObject(runtime); + Value prototypeValue = classWrapper.getProperty(runtime, "prototype"); + if (prototypeValue.isObject()) { + return prototypeValue; + } + } + return bridge_->findClassPrototype(runtime, object_getClass(object_)); + } + + Value engineThisValueForObject(Runtime& runtime) { + Value thisValue = bridge_->findRoundTripValue(runtime, object_, + nullptr, true); + if (thisValue.isObject()) { + return thisValue; + } + return makeNativeObjectValue(runtime, bridge_, object_, false); + } + + Value prototypeFunctionForProperty(Runtime& runtime, + const std::string& property) { + if (property.empty()) { + return Value::undefined(); + } + + Value prototypeValue = classPrototypeForObject(runtime); + if (!prototypeValue.isObject()) { + return Value::undefined(); + } + + Object objectConstructor = + runtime.global().getPropertyAsObject(runtime, "Object"); + Function getOwnPropertyDescriptor = + objectConstructor.getPropertyAsFunction(runtime, + "getOwnPropertyDescriptor"); + Function getPrototypeOf = + objectConstructor.getPropertyAsFunction(runtime, "getPrototypeOf"); + Value propertyName = makeString(runtime, property); + Value currentValue(runtime, prototypeValue); + + for (size_t depth = 0; depth < 64 && currentValue.isObject(); depth++) { + Object current = currentValue.asObject(runtime); + Value descriptorValue = + getOwnPropertyDescriptor.call(runtime, Value(runtime, current), + propertyName); + if (descriptorValue.isObject()) { + Value functionValue = + descriptorValue.asObject(runtime).getProperty(runtime, "value"); + if (functionValue.isObject() && + functionValue.asObject(runtime).isFunction(runtime)) { + bridge_->setObjectExpando(runtime, object_, property, functionValue); + return functionValue; + } + return Value::undefined(); + } + currentValue = + getPrototypeOf.call(runtime, Value(runtime, current)); + } + + return Value::undefined(); + } + + // Invoke a JS-prototype getter accessor with this instance as the receiver. + // Sets *found and returns the resolved value. + Value resolveEnginePrototypeGetter(Runtime& runtime, + const std::string& property, bool* found) { + *found = false; + if (object_ == nil || property.empty()) { + return Value::undefined(); + } + Value prototypeValue = classPrototypeForObject(runtime); + if (!prototypeValue.isObject()) { + return Value::undefined(); + } + Object objectConstructor = + runtime.global().getPropertyAsObject(runtime, "Object"); + Function getOwnPropertyDescriptor = + objectConstructor.getPropertyAsFunction(runtime, "getOwnPropertyDescriptor"); + Function getPrototypeOf = + objectConstructor.getPropertyAsFunction(runtime, "getPrototypeOf"); + Value propertyName = makeString(runtime, property); + Value currentValue(runtime, prototypeValue); + for (size_t depth = 0; depth < 64 && currentValue.isObject(); depth++) { + Object current = currentValue.asObject(runtime); + Value descriptorValue = getOwnPropertyDescriptor.call( + runtime, Value(runtime, current), propertyName); + if (descriptorValue.isObject()) { + Object descriptor = descriptorValue.asObject(runtime); + Value getterValue = descriptor.getProperty(runtime, "get"); + if (getterValue.isObject() && + getterValue.asObject(runtime).isFunction(runtime)) { + Value thisValue = engineThisValueForObject(runtime); + if (thisValue.isObject()) { + *found = true; + return getterValue.asObject(runtime).asFunction(runtime).callWithThis( + runtime, thisValue.asObject(runtime), + static_cast(nullptr), static_cast(0)); + } + } + Value dataValue = descriptor.getProperty(runtime, "value"); + if (!dataValue.isUndefined()) { + *found = true; + return dataValue; + } + return Value::undefined(); + } + currentValue = getPrototypeOf.call(runtime, Value(runtime, current)); + } + return Value::undefined(); + } + + // Invoke a JS-prototype setter accessor with this instance as the receiver. + // Returns true when a setter was found and invoked. + bool invokeEnginePrototypeSetter(Runtime& runtime, const std::string& property, + const Value& value) { + if (object_ == nil || property.empty()) { + return false; + } + Value prototypeValue = classPrototypeForObject(runtime); + if (!prototypeValue.isObject()) { + return false; + } + Object objectConstructor = + runtime.global().getPropertyAsObject(runtime, "Object"); + Function getOwnPropertyDescriptor = + objectConstructor.getPropertyAsFunction(runtime, "getOwnPropertyDescriptor"); + Function getPrototypeOf = + objectConstructor.getPropertyAsFunction(runtime, "getPrototypeOf"); + Value propertyName = makeString(runtime, property); + Value currentValue(runtime, prototypeValue); + for (size_t depth = 0; depth < 64 && currentValue.isObject(); depth++) { + Object current = currentValue.asObject(runtime); + Value descriptorValue = getOwnPropertyDescriptor.call( + runtime, Value(runtime, current), propertyName); + if (descriptorValue.isObject()) { + Value setterValue = + descriptorValue.asObject(runtime).getProperty(runtime, "set"); + if (setterValue.isObject() && + setterValue.asObject(runtime).isFunction(runtime)) { + Value thisValue = engineThisValueForObject(runtime); + if (thisValue.isObject()) { + Value args[] = {Value(runtime, value)}; + setterValue.asObject(runtime).asFunction(runtime).callWithThis( + runtime, thisValue.asObject(runtime), + static_cast(args), static_cast(1)); + return true; + } + } + return false; + } + currentValue = getPrototypeOf.call(runtime, Value(runtime, current)); + } + return false; + } + + Value get(Runtime& runtime, const PropNameID& name) override { + std::string property = name.utf8(runtime); + + // Fast path: check expando cache first (hot path for method calls). + Value expando = bridge_->findObjectExpando(runtime, object_, property); + if (!expando.isUndefined()) { + return expando; + } + + // Fast path: cached metadata property-getter resolution. Skips the + // special-name chain + per-access metadata discovery for hot getters + // (hash/length/count/...). Only populated for genuine non-extended + // metadata property members below, so a hit is always safe to serve. + if (object_ != nil) { + if (const auto* cached = bridge_->findCachedPropertyGetter( + object_getClass(object_), property)) { + if (cached->preparedInvocation != nullptr) { + return callPreparedObjectSelector(runtime, + *cached->preparedInvocation, + nullptr, 0); + } + return callObjectSelector(runtime, cached->selectorName, cached->member, + nullptr, 0); + } + } + + if (property == "kind") { + return makeString(runtime, "object"); + } + if (property == "className") { + return makeString(runtime, object_ != nil ? object_getClassName(object_) : ""); + } + if (property == "nativeAddress") { + char address[32] = {}; + snprintf(address, sizeof(address), "%p", object_); + return makeString(runtime, address); + } + if (property == "class") { + auto bridge = bridge_; + id object = object_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "class"), 0, + [bridge, object](Runtime& runtime, const Value&, const Value*, + size_t) -> Value { + if (object == nil) { + return Value::undefined(); + } + Value classWrapper = bridge->findObjectExpando( + runtime, object, "__nativeApiClassWrapper"); + if (classWrapper.isObject()) { + return classWrapper; + } + NativeApiSymbol symbol = + nativeApiSymbolForRuntimeClass(bridge, object_getClass(object)); + return makeNativeClassValue(runtime, bridge, std::move(symbol)); + }); + } + if (property == "constructor") { + if (object_ == nil) { + return Value::undefined(); + } + // Check class wrapper expando first (set during class setup). + Value classWrapper = bridge_->findObjectExpando( + runtime, object_, "__nativeApiClassWrapper"); + if (classWrapper.isObject()) { + return classWrapper; + } + // Try cached class value. + Class objClass = object_getClass(object_); + Value cached = bridge_->findClassValue(runtime, objClass); + if (!cached.isUndefined()) { + return cached; + } + // Resolve through metadata and global. + NativeApiSymbol symbol = + nativeApiSymbolForRuntimeClass(bridge_, objClass); + // Try the global by the symbol's name (which may be the JS-friendly name + // from metadata, different from the ObjC runtime name for Swift classes). + if (!symbol.name.empty()) { + Object global = runtime.global(); + if (global.hasProperty(runtime, symbol.name.c_str())) { + Value globalClass = global.getProperty(runtime, symbol.name.c_str()); + if (!globalClass.isUndefined() && !globalClass.isNull()) { + return globalClass; + } + } + // Also try the runtime name if different. + if (symbol.runtimeName != symbol.name && + global.hasProperty(runtime, symbol.runtimeName.c_str())) { + Value globalClass = global.getProperty(runtime, symbol.runtimeName.c_str()); + if (!globalClass.isUndefined() && !globalClass.isNull()) { + return globalClass; + } + } + } + // For Swift classes: try findClass by runtime name which checks + // classSymbolsByRuntimeName_ and may return a different JS-friendly name. + if (bridge_ != nullptr) { + const char* runtimeName = class_getName(objClass); + if (runtimeName != nullptr) { + if (const NativeApiSymbol* found = bridge_->findClass(runtimeName)) { + if (found->name != symbol.name) { + Object global = runtime.global(); + if (global.hasProperty(runtime, found->name.c_str())) { + Value globalClass = global.getProperty(runtime, found->name.c_str()); + if (!globalClass.isUndefined() && !globalClass.isNull()) { + return globalClass; + } + } + } + } + } + } + return makeNativeClassValue(runtime, bridge_, std::move(symbol)); + } + if (property == "superclass") { + if (object_ == nil) { + return Value::undefined(); + } + Class superclass = class_getSuperclass(object_getClass(object_)); + if (superclass == Nil) { + return Value::null(); + } + // Try cached class value. + Value cached = bridge_->findClassValue(runtime, superclass); + if (!cached.isUndefined()) { + return cached; + } + // Try global lookup by class name. + const char* name = class_getName(superclass); + if (name != nullptr && name[0] != '\0') { + Object global = runtime.global(); + if (global.hasProperty(runtime, name)) { + Value globalClass = global.getProperty(runtime, name); + if (!globalClass.isUndefined()) { + return globalClass; + } + } + } + NativeApiSymbol symbol = nativeApiSymbolForRuntimeClass(bridge_, superclass); + return makeNativeClassValue(runtime, bridge_, std::move(symbol)); + } + if (property == "super") { + Class dispatchClass = + object_ != nil ? class_getSuperclass(object_getClass(object_)) : Nil; + return Object::createFromHostObject( + runtime, + std::make_shared(bridge_, object_, + dispatchClass)); + } + if (property == "invoke" || property == "send") { + auto bridge = bridge_; + id object = object_; + std::weak_ptr weakSelf = shared_from_this(); + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, property.c_str()), 1, + [bridge, object, weakSelf](Runtime& runtime, const Value&, + const Value* args, + size_t count) -> Value { + std::string selectorName = + readStringArg(runtime, args, count, 0, "selector"); + if (auto self = weakSelf.lock()) { + return self->callObjectSelector(runtime, selectorName, nullptr, + args + 1, count - 1); + } + return callObjCSelector(runtime, bridge, object, false, selectorName, + nullptr, args + 1, count - 1); + }); + } + if (property == "takeRetainedValue" || property == "takeUnretainedValue") { + bool retained = property == "takeRetainedValue"; + std::weak_ptr weakSelf = shared_from_this(); + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, property.c_str()), 0, + [weakSelf, retained](Runtime& runtime, const Value&, const Value*, + size_t) -> Value { + auto self = weakSelf.lock(); + if (!self || self->object_ == nil || self->consumed_) { + throw JSError(runtime, "Unmanaged value has already been consumed."); + } + + id object = self->object_; + bool ownsObject = self->ownsObject_; + bool wrapperRetainedObject = self->wrapperRetainedObject_; + if (self->bridge_ != nullptr) { + self->bridge_->forgetRoundTripValue(runtime, object); + self->bridge_->releaseObjectExpandoOwner(object); + } + self->object_ = nil; + self->ownsObject_ = false; + self->wrapperRetainedObject_ = false; + if (self->lifetimeState_ != nullptr) { + self->lifetimeState_->clear(); + } + self->consumed_ = true; + const bool releasePreviousOwnership = + ownsObject && (!retained || wrapperRetainedObject); + try { + Value result = + makeNativeObjectValue(runtime, self->bridge_, object, retained); + if (releasePreviousOwnership) { + [object release]; + } + return result; + } catch (...) { + if (releasePreviousOwnership) { + [object release]; + } + throw; + } + }); + } + if (property == "toString") { + id object = object_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "toString"), 0, + [object](Runtime& runtime, const Value&, const Value*, size_t) -> Value { + return NativeApiObjectHostObject::descriptionString(runtime, object); + }); + } + if (property == "description") { + return descriptionString(runtime, object_); + } + if (property == "URL" && object_ != nil && + [object_ respondsToSelector:@selector(URL)]) { + return callObjectSelector(runtime, "URL", nullptr, nullptr, 0); + } + if (property == "Symbol.iterator" || + property == "Symbol(Symbol.iterator)" || + property == "@@iterator") { + auto bridge = bridge_; + id object = object_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "Symbol.iterator"), 0, + [bridge, object](Runtime& runtime, const Value&, const Value*, + size_t) -> Value { + if (object == nil || + ![object conformsToProtocol:@protocol(NSFastEnumeration)]) { + throw JSError( + runtime, "Object does not conform to NSFastEnumeration."); + } + return Object::createFromHostObject( + runtime, + std::make_shared( + bridge, static_cast>(object))); + }); + } + +#if TARGET_OS_OSX + if (property == "initWithRedGreenBlueAlpha") { + Class nsColorClass = NSClassFromString(@"NSColor"); + if (object_ != nil && nsColorClass != Nil && + [object_ isKindOfClass:nsColorClass]) { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, property.c_str()), 4, + [bridge, nsColorClass](Runtime& runtime, const Value&, + const Value* args, size_t count) -> Value { + const char* selectors[] = { + "colorWithSRGBRed:green:blue:alpha:", + "colorWithCalibratedRed:green:blue:alpha:", + "colorWithDeviceRed:green:blue:alpha:", + }; + for (const char* selectorName : selectors) { + if (class_getClassMethod(nsColorClass, + sel_getUid(selectorName)) != nullptr) { + return callObjCSelector(runtime, bridge, + static_cast(nsColorClass), true, + selectorName, nullptr, args, count); + } + } + throw JSError( + runtime, "NSColor RGB initializer is not available."); + }); + } + } +#endif + + if (property == "initWithFireDateIntervalTargetSelectorUserInfoRepeats") { + Class timerClass = NSClassFromString(@"NSTimer"); + if (object_ != nil && timerClass != Nil && + [object_ isKindOfClass:timerClass]) { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, property.c_str()), 6, + [bridge, timerClass](Runtime& runtime, const Value&, + const Value* args, size_t count) -> Value { + if (count < 6) { + throw JSError( + runtime, "NSTimer initializer expects six arguments."); + } + return callObjCSelector( + runtime, bridge, static_cast(timerClass), true, + "timerWithTimeInterval:target:selector:userInfo:repeats:", + nullptr, args + 1, count - 1); + }); + } + } + + if (object_ != nil && [object_ isKindOfClass:[NSArray class]]) { + NSArray* array = static_cast(object_); + if (property == "length") { + return static_cast(array.count); + } + if (auto index = parseArrayIndexProperty(property)) { + if (*index >= array.count) { + return Value::undefined(); + } + id element = [array objectAtIndex:*index]; + NativeApiType elementType = nativeObjectReturnType(); + return convertNativeReturnValue(runtime, bridge_, elementType, &element); + } + } + + if (object_ != nil && property == "length" && + ![object_ respondsToSelector:@selector(length)]) { + return Value::undefined(); + } + if (object_ != nil && property == "count" && + ![object_ respondsToSelector:@selector(count)]) { + return Value::undefined(); + } + + // For JS-extended instances, metadata property accessors live on the + // prototype chain (native accessors plus any JS overrides), so defer to the + // engine instead of reading the native property here and shadowing a JS + // override. + bool isEngineExtendedInstance = + object_ != nil && + class_conformsToProtocol(object_getClass(object_), + @protocol(NativeApiClassBuilderProtocol)); + + if (object_ != nil && !isEngineExtendedInstance) { + if (const NativeApiSymbol* symbol = + bridge_->findClassForRuntimeClass(object_getClass(object_))) { + const auto& members = bridge_->membersForClass(*symbol); + if (const NativeApiMember* propertyMember = + selectPropertyMember(members, property, false)) { + if (auto getter = respondingPropertyGetterSelector( + object_, property, propertyMember->selectorName)) { + NativeApiMember getterMember = *propertyMember; + getterMember.selectorName = *getter; + std::shared_ptr preparedGetter; + try { + preparedGetter = prepareNativeApiObjCInvocation( + runtime, bridge_, object_getClass(object_), false, + getterMember.selectorName, &getterMember); + } catch (const std::exception&) { + } + bridge_->cachePropertyGetter(object_getClass(object_), property, + propertyMember, + getterMember.selectorName, + preparedGetter); + if (preparedGetter != nullptr) { + return callPreparedObjectSelector(runtime, *preparedGetter, + nullptr, 0); + } + return callObjectSelector(runtime, getterMember.selectorName, + &getterMember, nullptr, 0); + } + } + + // Resolve metadata methods to a bound selector-group function. The + // bound receiver keeps method-call semantics correct even on engines + // whose host-object interceptor does not preserve `this`, while the + // engine backend can still use its direct selector-group/GSD path. + if (hasMethodMember(members, property, false)) { + auto selectors = + selectorGroupEntriesForMethod(members, property, false); + if (selectors != nullptr) { + auto preparedInvocations = std::make_shared>>( + selectors->size()); + Value methodFunction = CreateNativeApiBoundSelectorGroupFunction( + runtime, bridge_, object_getClass(object_), shared_from_this(), + selectors, preparedInvocations); + // Cache the resolved host function so repeated method access does + // not reallocate it on every call (hot path). + bridge_->setObjectExpando(runtime, object_, property, + methodFunction); + return methodFunction; + } + } + } + } + + Value prototypeFunction = prototypeFunctionForProperty(runtime, property); + if (!prototypeFunction.isUndefined()) { + return prototypeFunction; + } + + // JS-subclassed instances own their members in JS (prototype accessors and + // methods); defer so the engine resolves them instead of the bridge + // returning a registered getter IMP as a raw callable. + if (isEngineExtendedInstance) { +#ifdef NATIVESCRIPT_NATIVE_API_HOST_EXPLICIT_OVERRIDE + // Engines whose exotic property handler invokes prototype accessors with + // the wrong receiver need the JS-prototype getter resolved here with this + // instance as the receiver. + bool found = false; + Value resolved = resolveEnginePrototypeGetter(runtime, property, &found); + if (found) { + return resolved; + } +#endif + if (auto selector = + runtimeReadablePropertyGetter(object_, property)) { + return callObjectSelector(runtime, *selector, nullptr, nullptr, 0); + } + return Value::undefined(); + } + + if (object_ != nil) { + // A runtime ObjC property (e.g. from a protocol the concrete, non-metadata + // class adopts) must be invoked as a getter, not returned as a callable. + if (objc_property_t prop = + class_getProperty(object_getClass(object_), property.c_str())) { + std::string getter = property; + if (char* customGetter = property_copyAttributeValue(prop, "G")) { + getter = customGetter; + free(customGetter); + } + if (auto selector = + respondingPropertyGetterSelector(object_, property, getter)) { + return callObjectSelector(runtime, *selector, nullptr, nullptr, 0); + } + } + } + + if (object_ != nil && + hasRuntimeMemberForName(object_getClass(object_), false, property)) { + std::weak_ptr weakSelf = shared_from_this(); + std::string memberName = property; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, property.c_str()), 0, + [weakSelf, memberName](Runtime& runtime, const Value&, + const Value* args, size_t count) -> Value { + auto self = weakSelf.lock(); + if (!self || self->object_ == nil) { + throw JSError(runtime, + "Cannot send Objective-C selector to nil."); + } + auto selectorName = selectRuntimeSelectorForName( + object_getClass(self->object_), false, memberName, count); + if (!selectorName) { + throw JSError(runtime, + "Objective-C selector is not available: " + + memberName); + } + return self->callObjectSelector(runtime, *selectorName, nullptr, + args, count); + }); + } + + return Value::undefined(); + } + + NativeApiHostSetResult set(Runtime& runtime, const PropNameID& name, const Value& value) override { + std::string property = name.utf8(runtime); + if (object_ == nil) { + throw JSError(runtime, "Cannot set property on nil object."); + } + + if (const NativeApiSymbol* symbol = + bridge_->findClassForRuntimeClass(object_getClass(object_))) { + const auto& members = bridge_->membersForClass(*symbol); + if (const NativeApiMember* propertyMember = + selectWritablePropertyMember(members, property, false)) { + if (propertyMember->readonly) { + throw JSError( + runtime, "Attempted to assign to readonly property."); + } + NativeApiMember setterMember = *propertyMember; + setterMember.selectorName = propertyMember->setterSelectorName; + setterMember.signatureOffset = propertyMember->setterSignatureOffset; + Value args[] = {Value(runtime, value)}; + callObjCSelector(runtime, bridge_, object_, false, + setterMember.selectorName, &setterMember, args, 1); + NATIVE_API_SET_RETURN(true); + } + } + + if (auto setterSelectorName = + runtimeWritablePropertySetter(object_, property)) { + Value args[] = {Value(runtime, value)}; + callObjCSelector(runtime, bridge_, object_, false, + *setterSelectorName, nullptr, args, 1); + NATIVE_API_SET_RETURN(true); + } + + // For JS-subclassed instances, an unknown property is owned by the JS + // prototype (e.g. a JS-defined accessor); defer so the engine runs it instead of + // shadowing it with a bridge expando. + if (class_conformsToProtocol(object_getClass(object_), + @protocol(NativeApiClassBuilderProtocol))) { +#ifdef NATIVESCRIPT_NATIVE_API_HOST_EXPLICIT_OVERRIDE + // Engines whose exotic property storage doesn't fall back to own + // properties need the JS-owned set resolved here: invoke a JS-prototype + // setter if present, otherwise store the value as a bridge expando. + bool invokedPrototypeSetter = + invokeEnginePrototypeSetter(runtime, property, value); + if (!invokedPrototypeSetter) { + storeOwnExpando(runtime, property, value); + } + NATIVE_API_SET_RETURN(true); +#else + NATIVE_API_SET_RETURN(false); +#endif + } + + bridge_->setObjectExpando(runtime, object_, property, value); + NATIVE_API_SET_RETURN(true); + } + + std::vector getPropertyNames(Runtime& runtime) override { + std::vector names; + names.reserve(6); + addPropertyName(runtime, names, "kind"); + addPropertyName(runtime, names, "className"); + addPropertyName(runtime, names, "nativeAddress"); + addPropertyName(runtime, names, "constructor"); + addPropertyName(runtime, names, "superclass"); + addPropertyName(runtime, names, "super"); + addPropertyName(runtime, names, "invoke"); + addPropertyName(runtime, names, "send"); + addPropertyName(runtime, names, "takeRetainedValue"); + addPropertyName(runtime, names, "takeUnretainedValue"); + addPropertyName(runtime, names, "toString"); + return names; + } + + private: + std::shared_ptr bridge_; + id object_ = nil; + bool ownsObject_ = false; + bool wrapperRetainedObject_ = false; + bool consumed_ = false; + std::shared_ptr lifetimeState_; +}; + +class NativeApiClassHostObject final : public HostObject { + public: + NativeApiClassHostObject(std::shared_ptr bridge, + NativeApiSymbol symbol) + : bridge_(std::move(bridge)), symbol_(std::move(symbol)) {} + + Class nativeClass() const { + return objc_lookUpClass(symbol_.runtimeName.c_str()); + } + + static Class classRespondingToClassSelector(Class cls, SEL selector) { + for (Class current = cls; current != Nil; + current = class_getSuperclass(current)) { + if (class_getClassMethod(current, selector) != nullptr) { + return current; + } + } + return Nil; + } + + Value get(Runtime& runtime, const PropNameID& name) override { + std::string property = name.utf8(runtime); + if (property == "kind") { + return makeString(runtime, "class"); + } + if (property == "name") { + return makeString(runtime, symbol_.name); + } + if (property == "runtimeName") { + return makeString(runtime, symbol_.runtimeName); + } + if (property == "available") { + return objc_lookUpClass(symbol_.runtimeName.c_str()) != nil; + } + if (property == "metadataOffset") { + return static_cast(symbol_.offset); + } + if (property == "__superclass") { + if (symbol_.superclassOffset == MD_SECTION_OFFSET_NULL) { + return Value::undefined(); + } + const NativeApiSymbol* superclass = + bridge_->findClassByOffset(symbol_.superclassOffset); + if (superclass == nullptr) { + return Value::undefined(); + } + return makeNativeClassValue(runtime, bridge_, *superclass); + } + if (property == "__runtimeStaticMembers" || + property == "__runtimeInstanceMembers") { + return runtimeMembersArray(runtime, nativeClass(), + property == "__runtimeStaticMembers"); + } + if (property == "__staticMembers" || property == "__instanceMembers") { + bool staticMembers = property == "__staticMembers"; + const auto& members = bridge_->surfaceMembersForClass(symbol_); + Array result(runtime, members.size()); + size_t index = 0; + for (const auto& member : members) { + bool memberIsStatic = + (member.flags & metagen::mdMemberStatic) != 0; + if (memberIsStatic != staticMembers) { + continue; + } + Object descriptor(runtime); + descriptor.setProperty(runtime, "name", makeString(runtime, member.name)); + descriptor.setProperty(runtime, "selectorName", + makeString(runtime, member.selectorName)); + descriptor.setProperty( + runtime, "argumentCount", + static_cast(selectorArgumentCount(member.selectorName))); + descriptor.setProperty(runtime, "property", member.property); + descriptor.setProperty(runtime, "readonly", member.readonly); + descriptor.setProperty(runtime, "signatureOffset", + static_cast(member.signatureOffset)); + descriptor.setProperty( + runtime, "setterSignatureOffset", + static_cast(member.setterSignatureOffset)); + descriptor.setProperty(runtime, "flags", + static_cast(member.flags)); + descriptor.setProperty(runtime, "setterSelectorName", + makeString(runtime, member.setterSelectorName)); + result.setValueAtIndex(runtime, index++, descriptor); + } + Array compact(runtime, index); + for (size_t i = 0; i < index; i++) { + compact.setValueAtIndex(runtime, i, result.getValueAtIndex(runtime, i)); + } + return compact; + } + if (property == "toString") { + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "toString"), 0, + [symbol = symbol_](Runtime& runtime, const Value&, + const Value*, size_t) -> Value { + return makeString(runtime, + "[NativeApiClass " + symbol.name + "]"); + }); + } + if (property == "construct" || property == "alloc" || property == "new") { + auto bridge = bridge_; + auto symbol = symbol_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, property), 0, + [bridge, symbol, property](Runtime& runtime, const Value&, + const Value* args, size_t count) -> Value { + Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); + if (cls == nil) { + throw JSError( + runtime, "Objective-C class is not available: " + symbol.name); + } + + id result = nil; + if (property == "construct" && count == 1) { + void* pointer = nullptr; + if (args[0].isNumber()) { + pointer = reinterpret_cast( + static_cast(args[0].getNumber())); + } else if (args[0].isObject()) { + Object object = args[0].asObject(runtime); + if (object.isHostObject(runtime)) { + auto pointerHost = + object.getHostObject( + runtime); + pointer = pointerHost->pointer(); + if (pointerHost->backingValue() != nullptr) { + Value backingValue(runtime, *pointerHost->backingValue()); + id backingObject = + NativeApiObjectHostObject::nativeObjectFromValue( + runtime, backingValue); + if (backingObject == static_cast(pointer) && + backingObject != nil && + [backingObject isKindOfClass:cls]) { + return backingValue; + } + } + } else if (object.isHostObject( + runtime)) { + auto referenceHost = + object.getHostObject( + runtime); + pointer = referenceHost->data(); + if (referenceHost->backingValue() != nullptr) { + Value backingValue(runtime, *referenceHost->backingValue()); + id backingObject = + NativeApiObjectHostObject::nativeObjectFromValue( + runtime, backingValue); + if (backingObject == static_cast(pointer) && + backingObject != nil && + [backingObject isKindOfClass:cls]) { + return backingValue; + } + } + } else if (object.isHostObject( + runtime)) { + pointer = object + .getHostObject( + runtime) + ->object(); + } + } + return makeNativeObjectValue(runtime, bridge, + static_cast(pointer), false); + } + + if (property == "new") { + if (count != 0) { + throw JSError( + runtime, "new does not take arguments; use invoke for an " + "explicit Objective-C selector."); + } + performDirectObjCInvocation(runtime, + [&]() { result = [[cls alloc] init]; }); + } else { + if (count != 0) { + throw JSError( + runtime, "alloc does not take arguments; call invoke on the " + "allocated object for an explicit init selector."); + } + performDirectObjCInvocation(runtime, + [&]() { result = [cls alloc]; }); + } + + return makeNativeObjectValue(runtime, bridge, result, true); + }); + } + if (property == "invoke" || property == "send") { + auto bridge = bridge_; + auto symbol = symbol_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, property.c_str()), 1, + [bridge, symbol](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + std::string selectorName = + readStringArg(runtime, args, count, 0, "selector"); + Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); + if (cls == nil) { + throw JSError( + runtime, "Objective-C class is not available: " + symbol.name); + } + return callObjCSelector(runtime, bridge, static_cast(cls), true, + selectorName, nullptr, args + 1, + count - 1); + }); + } + + Class cls = nativeClass(); + if (cls != Nil) { + Value expando = bridge_->findObjectExpando(runtime, cls, property); + if (!expando.isUndefined()) { + return expando; + } + } + + const auto& members = bridge_->membersForClass(symbol_); + if (const NativeApiMember* propertyMember = + selectWritablePropertyMember(members, property, true)) { + auto bridge = bridge_; + auto symbol = symbol_; + Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); + if (cls == nil) { + throw JSError( + runtime, "Objective-C class is not available: " + symbol.name); + } + SEL selector = sel_getUid(propertyMember->selectorName.c_str()); + Class dispatchClass = classRespondingToClassSelector(cls, selector); + if (dispatchClass != Nil) { + return callObjCSelector(runtime, bridge, static_cast(dispatchClass), true, + propertyMember->selectorName, propertyMember, + nullptr, 0); + } + } + + auto selectors = selectorGroupEntriesForMethod(members, property, true); + if (selectors != nullptr) { + if (cls == Nil) { + throw JSError( + runtime, "Objective-C class is not available: " + symbol_.name); + } + auto preparedInvocations = std::make_shared>>(selectors->size()); + Value methodFunction = CreateNativeApiSelectorGroupFunction( + runtime, bridge_, cls, true, selectors, preparedInvocations); + bridge_->setObjectExpando(runtime, cls, property, methodFunction); + return methodFunction; + } + + return Value::undefined(); + } + + NativeApiHostSetResult set(Runtime& runtime, const PropNameID& name, const Value& value) override { + std::string property = name.utf8(runtime); + Class cls = objc_lookUpClass(symbol_.runtimeName.c_str()); + if (cls == nil) { + throw JSError( + runtime, "Objective-C class is not available: " + symbol_.name); + } + + const auto& members = bridge_->membersForClass(symbol_); + if (const NativeApiMember* propertyMember = + selectPropertyMember(members, property, true)) { + if (propertyMember->readonly) { + throw JSError( + runtime, "Attempted to assign to readonly property."); + } + NativeApiMember setterMember = *propertyMember; + setterMember.selectorName = propertyMember->setterSelectorName; + setterMember.signatureOffset = propertyMember->setterSignatureOffset; + SEL selector = sel_getUid(setterMember.selectorName.c_str()); + Class dispatchClass = classRespondingToClassSelector(cls, selector); + if (dispatchClass == Nil) { + throw JSError(runtime, + "Objective-C selector is not available: " + + setterMember.selectorName); + } + Value args[] = {Value(runtime, value)}; + callObjCSelector(runtime, bridge_, static_cast(dispatchClass), true, + setterMember.selectorName, &setterMember, args, 1); + NATIVE_API_SET_RETURN(true); + } + + throw JSError(runtime, + "No writable native property: " + property); + } + + std::vector getPropertyNames(Runtime& runtime) override { + std::vector names; + names.reserve(8); + addPropertyName(runtime, names, "kind"); + addPropertyName(runtime, names, "name"); + addPropertyName(runtime, names, "runtimeName"); + addPropertyName(runtime, names, "available"); + addPropertyName(runtime, names, "metadataOffset"); + addPropertyName(runtime, names, "toString"); + addPropertyName(runtime, names, "construct"); + addPropertyName(runtime, names, "alloc"); + addPropertyName(runtime, names, "new"); + addPropertyName(runtime, names, "invoke"); + addPropertyName(runtime, names, "send"); + return names; + } + + private: + std::shared_ptr bridge_; + NativeApiSymbol symbol_; +}; + +Value makeNativeObjectValue(Runtime& runtime, + const std::shared_ptr& bridge, + id object, bool ownsObject) { + if (object == nil) { + return Value::null(); + } + + Value cached = bridge->findRoundTripValue(runtime, object, nullptr, true); + if (!cached.isUndefined()) { + // A consumed wrapper (e.g. an alloc'd placeholder singleton already passed + // to an initializer) must not be reused: drop the stale entry and re-wrap. + auto cachedHost = + cached.isObject() + ? cached.asObject(runtime).getHostObject(runtime) + : nullptr; + if (cachedHost != nullptr && cachedHost->object() != nil) { + if (ownsObject) { + [object release]; + } + return cached; + } + bridge->forgetRoundTripValue(runtime, object); + } + + Object result = createNativeInstanceHostObject( + runtime, + std::make_shared(bridge, object, ownsObject)); + Value prototypeValue = Value::undefined(); + Value classWrapperValue = + bridge->findObjectExpando(runtime, object, "__nativeApiClassWrapper"); + if (classWrapperValue.isObject()) { + Object classWrapper = classWrapperValue.asObject(runtime); + prototypeValue = classWrapper.getProperty(runtime, "prototype"); + } + if (!prototypeValue.isObject()) { + prototypeValue = bridge->findClassPrototype(runtime, object_getClass(object)); + } + if (!prototypeValue.isObject()) { + Value classWrapper = makeNativeClassValue( + runtime, bridge, + nativeApiSymbolForRuntimeClass(bridge, object_getClass(object))); + if (classWrapper.isObject()) { + prototypeValue = + classWrapper.asObject(runtime).getProperty(runtime, "prototype"); + } + } + if (prototypeValue.isObject()) { + Object prototype = prototypeValue.asObject(runtime); + SetNativeApiObjectPrototype(runtime, result, prototype); + } + bridge->rememberScopedRoundTripValue( + runtime, object, Value(runtime, result), + nativeObjectIsStringLike(object)); + return result; +} + +Value globalNativeSymbolValue(Runtime& runtime, const NativeApiSymbol& symbol, + const char* expectedKind) { + Object global = runtime.global(); + Value cacheValue = global.getProperty( + runtime, "__nativeScriptNativeApiGlobalCache"); + if (!cacheValue.isObject()) { + return Value::undefined(); + } + + Object cache = cacheValue.asObject(runtime); + auto readCache = [&](const std::string& name) -> Value { + if (name.empty()) { + return Value::undefined(); + } + + Value value = cache.getProperty(runtime, name.c_str()); + if (!value.isObject()) { + return Value::undefined(); + } + + try { + Object object = value.asObject(runtime); + Value kindValue = object.getProperty(runtime, "kind"); + if (kindValue.isString() && + kindValue.asString(runtime).utf8(runtime) == expectedKind) { + return value; + } + } catch (const std::exception&) { + } + + return Value::undefined(); + }; + + Value value = readCache(symbol.name); + if (!value.isUndefined()) { + return value; + } + if (symbol.runtimeName != symbol.name) { + value = readCache(symbol.runtimeName); + if (!value.isUndefined()) { + return value; + } + } + + try { + if (std::strcmp(expectedKind, "class") == 0) { + Value classResolverValue = global.getProperty( + runtime, "__nativeScriptResolveNativeApiClassWrapper"); + if (classResolverValue.isObject() && + classResolverValue.asObject(runtime).isFunction(runtime)) { + Function classResolver = + classResolverValue.asObject(runtime).asFunction(runtime); + auto resolveClassWrapper = [&](const std::string& name) -> Value { + if (name.empty()) { + return Value::undefined(); + } + Value resolved = classResolver.call(runtime, makeString(runtime, name)); + return resolved.isObject() ? std::move(resolved) : Value::undefined(); + }; + + value = resolveClassWrapper(symbol.name); + if (!value.isUndefined()) { + return value; + } + if (symbol.runtimeName != symbol.name) { + value = resolveClassWrapper(symbol.runtimeName); + if (!value.isUndefined()) { + return value; + } + } + } + } + + Value resolverValue = + global.getProperty(runtime, "__nativeScriptResolveNativeApiGlobal"); + if (resolverValue.isObject() && + resolverValue.asObject(runtime).isFunction(runtime)) { + Function resolver = resolverValue.asObject(runtime).asFunction(runtime); + auto resolveGlobal = [&](const std::string& name) -> Value { + if (name.empty()) { + return Value::undefined(); + } + Value resolved = resolver.call(runtime, makeString(runtime, name), + makeString(runtime, expectedKind)); + if (resolved.isObject()) { + return resolved; + } + return Value::undefined(); + }; + + value = resolveGlobal(symbol.name); + if (!value.isUndefined()) { + return value; + } + if (symbol.runtimeName != symbol.name) { + value = resolveGlobal(symbol.runtimeName); + if (!value.isUndefined()) { + return value; + } + } + } + } catch (const std::exception&) { + } + + return Value::undefined(); +} + +Value makeNativeClassValue(Runtime& runtime, + const std::shared_ptr& bridge, + NativeApiSymbol symbol) { + Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); + Value cachedClass = bridge->findClassValue(runtime, cls); + if (!cachedClass.isUndefined()) { + return cachedClass; + } + Value globalValue = globalNativeSymbolValue(runtime, symbol, "class"); + if (!globalValue.isUndefined()) { + return globalValue; + } + return Object::createFromHostObject( + runtime, + std::make_shared(bridge, std::move(symbol))); +} + +Protocol* lookupProtocolByNativeName(const std::string& name) { + Protocol* protocol = objc_getProtocol(name.c_str()); + if (protocol != nullptr) { + return protocol; + } + constexpr const char* suffix = "Protocol"; + size_t suffixLength = std::strlen(suffix); + if (name.size() > suffixLength && + name.compare(name.size() - suffixLength, suffixLength, suffix) == 0) { + protocol = objc_getProtocol( + name.substr(0, name.size() - suffixLength).c_str()); + } + return protocol; +} + +class NativeApiProtocolHostObject final : public HostObject { + public: + NativeApiProtocolHostObject(std::shared_ptr bridge, + NativeApiSymbol symbol) + : bridge_(std::move(bridge)), symbol_(std::move(symbol)) {} + + Protocol* nativeProtocol() const { + Protocol* protocol = lookupProtocolByNativeName(symbol_.runtimeName); + if (protocol == nullptr && symbol_.runtimeName != symbol_.name) { + protocol = lookupProtocolByNativeName(symbol_.name); + } + return protocol; + } + + const NativeApiSymbol& symbol() const { return symbol_; } + + Value get(Runtime& runtime, const PropNameID& name) override { + std::string property = name.utf8(runtime); + if (property == "kind") { + return makeString(runtime, "protocol"); + } + if (property == "name") { + return makeString(runtime, symbol_.name); + } + if (property == "runtimeName") { + return makeString(runtime, symbol_.runtimeName); + } + if (property == "available") { + return nativeProtocol() != nullptr; + } + if (property == "metadataOffset") { + return static_cast(symbol_.offset); + } + if (property == "nativeAddress") { + return static_cast( + reinterpret_cast(nativeProtocol())); + } + if (property == "prototype") { + Object prototype(runtime); + for (const auto& member : bridge_->membersForProtocol(symbol_)) { + if (prototype.hasProperty(runtime, member.name.c_str())) { + continue; + } + if (member.property) { + defineProtocolProperty(runtime, prototype, member, false); + } else { + prototype.setProperty(runtime, member.name.c_str(), + makeProtocolMemberFunction(runtime, member, + false)); + } + } + return prototype; + } + if (property == "toString") { + auto symbol = symbol_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "toString"), 0, + [symbol](Runtime& runtime, const Value&, const Value*, size_t) -> Value { + return makeString(runtime, + "[NativeApiProtocol " + symbol.name + "]"); + }); + } + const auto& members = bridge_->membersForProtocol(symbol_); + if (const NativeApiMember* propertyMember = + selectPropertyMember(members, property, true)) { + return makeProtocolPropertyGetter(runtime, *propertyMember, true); + } + if (const NativeApiMember* propertyMember = + selectPropertyMember(members, property, false)) { + return makeProtocolPropertyGetter(runtime, *propertyMember, true); + } + for (const auto& member : members) { + if (member.property || member.name != property) { + continue; + } + bool memberIsStatic = (member.flags & metagen::mdMemberStatic) != 0; + if (memberIsStatic) { + return makeProtocolMemberFunction(runtime, member, true); + } + } + for (const auto& member : members) { + if (member.property || member.name != property) { + continue; + } + bool memberIsStatic = (member.flags & metagen::mdMemberStatic) != 0; + if (!memberIsStatic) { + return makeProtocolMemberFunction(runtime, member, true); + } + } + return Value::undefined(); + } + + std::vector getPropertyNames(Runtime& runtime) override { + std::vector names; + addPropertyName(runtime, names, "kind"); + addPropertyName(runtime, names, "name"); + addPropertyName(runtime, names, "runtimeName"); + addPropertyName(runtime, names, "available"); + addPropertyName(runtime, names, "metadataOffset"); + addPropertyName(runtime, names, "nativeAddress"); + addPropertyName(runtime, names, "prototype"); + addPropertyName(runtime, names, "toString"); + for (const auto& member : bridge_->membersForProtocol(symbol_)) { + addPropertyName(runtime, names, member.name.c_str()); + } + return names; + } + + private: + static Class classReceiverFromThis(Runtime& runtime, const Value& thisValue) { + if (!thisValue.isObject()) { + return Nil; + } + + Object object = thisValue.asObject(runtime); + if (object.isHostObject(runtime)) { + return object.getHostObject(runtime)->nativeClass(); + } + + Value wrappedClass = object.getProperty(runtime, "__nativeApiClass"); + if (wrappedClass.isObject()) { + Object wrappedObject = wrappedClass.asObject(runtime); + if (wrappedObject.isHostObject(runtime)) { + return wrappedObject.getHostObject(runtime) + ->nativeClass(); + } + } + + Value kindValue = object.getProperty(runtime, "kind"); + if (kindValue.isString() && + kindValue.asString(runtime).utf8(runtime) == "class") { + Value runtimeNameValue = object.getProperty(runtime, "runtimeName"); + if (!runtimeNameValue.isString()) { + runtimeNameValue = object.getProperty(runtime, "name"); + } + if (runtimeNameValue.isString()) { + std::string runtimeName = + runtimeNameValue.asString(runtime).utf8(runtime); + return objc_lookUpClass(runtimeName.c_str()); + } + } + + return Nil; + } + + id objectReceiverFromThis(Runtime& runtime, const Value& thisValue) const { + if (!thisValue.isObject()) { + return nil; + } + + Object object = thisValue.asObject(runtime); + if (object.isHostObject(runtime)) { + return object.getHostObject(runtime)->object(); + } + + return nil; + } + + Value makeProtocolMemberFunction(Runtime& runtime, NativeApiMember member, + bool receiverIsClass) const { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, member.name.c_str()), 0, + [bridge, member, receiverIsClass](Runtime& runtime, + const Value& thisValue, + const Value* args, + size_t count) -> Value { + id receiver = nil; + if (receiverIsClass) { + receiver = static_cast( + classReceiverFromThis(runtime, thisValue)); + } else if (thisValue.isObject()) { + Object object = thisValue.asObject(runtime); + if (object.isHostObject(runtime)) { + receiver = object.getHostObject(runtime) + ->object(); + } + } + + if (receiver == nil) { + throw JSError( + runtime, "Protocol member requires a native receiver."); + } + return callObjCSelector(runtime, bridge, receiver, receiverIsClass, + member.selectorName, &member, args, count); + }); + } + + Value makeProtocolPropertyGetter(Runtime& runtime, NativeApiMember member, + bool receiverIsClass) const { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, member.name.c_str()), 0, + [bridge, member, receiverIsClass](Runtime& runtime, + const Value& thisValue, + const Value*, size_t) -> Value { + id receiver = nil; + if (receiverIsClass) { + receiver = static_cast( + classReceiverFromThis(runtime, thisValue)); + } else if (thisValue.isObject()) { + Object object = thisValue.asObject(runtime); + if (object.isHostObject(runtime)) { + receiver = object.getHostObject(runtime) + ->object(); + } + } + + if (receiver == nil) { + throw JSError( + runtime, "Protocol property requires a native receiver."); + } + NativeApiMember getterMember = member; + if (auto selector = respondingPropertyGetterSelector( + receiver, member.name, member.selectorName)) { + getterMember.selectorName = *selector; + } + return callObjCSelector(runtime, bridge, receiver, receiverIsClass, + getterMember.selectorName, &getterMember, + nullptr, 0); + }); + } + + Value makeProtocolPropertySetter(Runtime& runtime, NativeApiMember member, + bool receiverIsClass) const { + auto bridge = bridge_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, member.setterSelectorName.c_str()), + 1, + [bridge, member, receiverIsClass](Runtime& runtime, + const Value& thisValue, + const Value* args, + size_t count) -> Value { + id receiver = nil; + if (receiverIsClass) { + receiver = static_cast( + classReceiverFromThis(runtime, thisValue)); + } else if (thisValue.isObject()) { + Object object = thisValue.asObject(runtime); + if (object.isHostObject(runtime)) { + receiver = object.getHostObject(runtime) + ->object(); + } + } + + if (receiver == nil) { + throw JSError( + runtime, "Protocol property requires a native receiver."); + } + if (count < 1) { + throw JSError( + runtime, "Protocol property setter expects a value."); + } + + NativeApiMember setterMember = member; + setterMember.selectorName = member.setterSelectorName; + setterMember.signatureOffset = member.setterSignatureOffset; + return callObjCSelector(runtime, bridge, receiver, receiverIsClass, + setterMember.selectorName, &setterMember, + args, 1); + }); + } + + void defineProtocolProperty(Runtime& runtime, Object& target, + const NativeApiMember& member, + bool receiverIsClass) const { + try { + Object objectCtor = runtime.global().getPropertyAsObject(runtime, "Object"); + Function defineProperty = + objectCtor.getPropertyAsFunction(runtime, "defineProperty"); + Object descriptor(runtime); + descriptor.setProperty(runtime, "configurable", true); + descriptor.setProperty(runtime, "enumerable", true); + descriptor.setProperty(runtime, "get", + makeProtocolPropertyGetter(runtime, member, + receiverIsClass)); + if (!member.readonly && !member.setterSelectorName.empty()) { + descriptor.setProperty(runtime, "set", + makeProtocolPropertySetter(runtime, member, + receiverIsClass)); + } + defineProperty.call(runtime, target, makeString(runtime, member.name), + descriptor); + } catch (const std::exception&) { + } + } + + std::shared_ptr bridge_; + NativeApiSymbol symbol_; +}; + +Value makeNativeProtocolValue(Runtime& runtime, + const std::shared_ptr& bridge, + NativeApiSymbol symbol) { + Value globalValue = globalNativeSymbolValue(runtime, symbol, "protocol"); + if (!globalValue.isUndefined()) { + return globalValue; + } + return Object::createFromHostObject( + runtime, + std::make_shared(bridge, std::move(symbol))); +} + +Class nativeClassFromEngineObject(Runtime& runtime, const Object& object) { + if (object.isHostObject(runtime)) { + return object.getHostObject(runtime)->nativeClass(); + } + + Value wrappedClass = object.getProperty(runtime, "__nativeApiClass"); + if (wrappedClass.isObject()) { + Object wrappedObject = wrappedClass.asObject(runtime); + if (wrappedObject.isHostObject(runtime)) { + return wrappedObject.getHostObject(runtime) + ->nativeClass(); + } + } + return Nil; +} diff --git a/NativeScript/ffi/objc/shared/bridge/Install.mm b/NativeScript/ffi/objc/shared/bridge/Install.mm new file mode 100644 index 000000000..de8716fd9 --- /dev/null +++ b/NativeScript/ffi/objc/shared/bridge/Install.mm @@ -0,0 +1,1807 @@ +Object CreateNativeApi(Runtime& runtime, const NativeApiConfig& config) { + auto bridge = std::make_shared(config); + return Object::createFromHostObject(runtime, + std::make_shared(std::move(bridge))); +} + +void NativeApiWriteSmokeStage(const char* stage) { + const char* enabled = getenv("NATIVESCRIPT_RN_TURBO_SMOKE_MARKER"); + if (enabled == nullptr || enabled[0] == '\0') { + return; + } + + NSString* path = + [NSTemporaryDirectory() stringByAppendingPathComponent:@"NativeScriptNativeApiSmoke.marker"]; + NSString* content = [NSString stringWithFormat:@"stage=%s\n", stage != nullptr ? stage : ""]; + [content writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil]; +} + +void InstallAggregateGlobals(Runtime& runtime, Object& api, const char* namesFunction) { + Value metadataValue = api.getProperty(runtime, "metadata"); + if (!metadataValue.isObject()) { + return; + } + Object metadata = metadataValue.asObject(runtime); + Value namesValue = metadata.getProperty(runtime, namesFunction); + if (!namesValue.isObject()) { + return; + } + Object namesObject = namesValue.asObject(runtime); + if (!namesObject.isFunction(runtime)) { + return; + } + Value namesResult = namesObject.asFunction(runtime).call(runtime); + if (!namesResult.isObject() || !namesResult.asObject(runtime).isArray(runtime)) { + return; + } + Array names = namesResult.asObject(runtime).getArray(runtime); + Object global = runtime.global(); + for (size_t i = 0; i < names.size(runtime); i++) { + Value nameValue = names.getValueAtIndex(runtime, i); + if (!nameValue.isString()) { + continue; + } + std::string name = nameValue.asString(runtime).utf8(runtime); + if (name.empty() || global.hasProperty(runtime, name.c_str())) { + continue; + } + try { + Value aggregate = api.getProperty(runtime, name.c_str()); + if (!aggregate.isUndefined()) { + global.setProperty(runtime, name.c_str(), aggregate); + } + } catch (const std::exception&) { + // Some React Native globals are read-only even when hasProperty misses + // them. Keep NativeScript initialization resilient and skip collisions. + } + } +} + +std::string jsStringLiteral(const char* value) { + std::string result = "'"; + if (value != nullptr) { + for (const char* current = value; *current != '\0'; current++) { + switch (*current) { + case '\\': + result += "\\\\"; + break; + case '\'': + result += "\\'"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + default: + result += *current; + break; + } + } + } + result += "'"; + return result; +} + +void InstallNativeApiGlobalSymbols(Runtime& runtime, const char* globalName) { + NativeApiWriteSmokeStage("engine:globals:before-eval"); + static const char* GlobalInstaller = R"Engine_GLOBALS( +(function(nativeApiGlobalName) { + 'use strict'; + var api = globalThis[nativeApiGlobalName]; + var installedFlagName = '__nativeScriptNativeApiGlobalsInstalled'; + if (!api || globalThis[installedFlagName]) { + return; + } + + var cacheName = '__nativeScriptNativeApiGlobalCache'; + var typeCodeKey = '__nativeApiTypeCode'; + var classWrappers = typeof WeakMap === 'function' ? new WeakMap() : null; + var classWrappersByName = Object.create(null); + var resolvingGlobal = Object.create(null); + + function globalCache() { + var existing = globalThis[cacheName]; + if (existing && typeof existing === 'object') { + return existing; + } + var cache = Object.create(null); + Object.defineProperty(globalThis, cacheName, { + configurable: false, + enumerable: false, + writable: false, + value: cache + }); + return cache; + } + + function cacheGlobal(name, value) { + if (name && value !== undefined) { + globalCache()[name] = value; + } + } + + function resolveCachedGlobal(name, expectedKind) { + if (!name) { + return undefined; + } + var cached = globalCache()[name]; + if (cached && (typeof cached === 'object' || typeof cached === 'function') && cached.kind === expectedKind) { + return cached; + } + if (resolvingGlobal[name] || !Object.prototype.hasOwnProperty.call(globalThis, name)) { + return undefined; + } + resolvingGlobal[name] = true; + try { + var value = globalThis[name]; + if (value && (typeof value === 'object' || typeof value === 'function') && value.kind === expectedKind) { + cacheGlobal(name, value); + return value; + } + } finally { + delete resolvingGlobal[name]; + } + return undefined; + } + + function defineLazyGlobal(name, resolve, force, nativeKind) { + if (!name) { + return; + } + if (!force && Object.prototype.hasOwnProperty.call(globalThis, name)) { + try { + var existingDescriptor = Object.getOwnPropertyDescriptor(globalThis, name); + if (existingDescriptor && Object.prototype.hasOwnProperty.call(existingDescriptor, 'value')) { + cacheGlobal(name, existingDescriptor.value); + } + } catch (_) { + } + return; + } + var nativeDefineLazyGlobal = api.__defineLazyGlobal; + if (nativeKind && typeof nativeDefineLazyGlobal === 'function' && + typeof globalThis.__nativeScriptResolveNativeApiLazyGlobal === 'function') { + try { + if (nativeDefineLazyGlobal(name, nativeKind, !!force)) { + return; + } + } catch (_) { + } + } + try { + Object.defineProperty(globalThis, name, { + configurable: true, + enumerable: false, + get: function() { + var value = resolve(name); + cacheGlobal(name, value); + Object.defineProperty(globalThis, name, { + configurable: true, + enumerable: false, + writable: false, + value: value + }); + return value; + } + }); + } catch (_) { + var value = resolve(name); + if (value !== undefined) { + cacheGlobal(name, value); + Object.defineProperty(globalThis, name, { + configurable: true, + enumerable: false, + writable: false, + value: value + }); + } + } + } + + Object.defineProperty(globalThis, '__nativeScriptResolveNativeApiGlobal', { + configurable: false, + enumerable: false, + writable: false, + value: resolveCachedGlobal + }); + + Object.defineProperty(globalThis, '__nativeScriptResolveNativeApiClassWrapper', { + configurable: false, + enumerable: false, + writable: false, + value: function(name) { + return name ? classWrappersByName[name] : undefined; + } + }); + + function findPrototypeDescriptor(className, property) { + var prototype; + if (className && (typeof className === 'object' || typeof className === 'function')) { + prototype = className; + } else { + var wrapper = className ? classWrappersByName[className] : undefined; + prototype = wrapper && wrapper.prototype; + } + while (prototype != null) { + var descriptor = Object.getOwnPropertyDescriptor(prototype, property); + if (descriptor) { + return descriptor; + } + prototype = Object.getPrototypeOf(prototype); + } + return undefined; + } + + Object.defineProperty(globalThis, '__nativeScriptCreateNativeApiIterator', { + configurable: false, + enumerable: false, + writable: false, + value: function(receiver, prototype) { + if (!receiver || typeof Symbol !== 'function') { + return undefined; + } + var descriptor = findPrototypeDescriptor(prototype || receiver.className, Symbol.iterator); + if (descriptor && typeof descriptor.value === 'function') { + return descriptor.value.call(receiver); + } + if (descriptor && typeof descriptor.get === 'function') { + var getterValue = descriptor.get.call(receiver); + if (typeof getterValue === 'function') { + return getterValue.call(receiver); + } + } + var iteratorMethod = receiver[Symbol.iterator]; + return typeof iteratorMethod === 'function' + ? iteratorMethod.call(receiver) + : undefined; + } + }); + + function wrapAggregateConstructor(nativeConstructor) { + if (typeof nativeConstructor !== 'function') { + return nativeConstructor; + } + var aggregate = function NativeScriptAggregate(initialValue) { + return nativeConstructor(initialValue); + }; + try { + Object.defineProperty(aggregate, Symbol.hasInstance, { + configurable: true, + enumerable: false, + value: function(value) { + return !!value && + typeof value === 'object' && + value.kind === nativeConstructor.kind && + value.name === nativeConstructor.runtimeName; + } + }); + } catch (_) { + } + ['kind', 'runtimeName', 'metadataOffset', 'sizeof', 'fields', 'equals'].forEach(function(key) { + try { + Object.defineProperty(aggregate, key, { + configurable: true, + enumerable: false, + writable: false, + value: nativeConstructor[key] + }); + } catch (_) { + } + }); + return aggregate; + } + + function setDescriptorValue(target, property, receiver, value) { + for (var current = target; current; current = Object.getPrototypeOf(current)) { + var descriptor = Object.getOwnPropertyDescriptor(current, property); + if (!descriptor) { + continue; + } + if (typeof descriptor.set === 'function') { + descriptor.set.call(receiver, value); + return true; + } + if (descriptor.writable) { + if (receiver && receiver !== current) { + Object.defineProperty(receiver, property, { + configurable: true, + enumerable: true, + writable: true, + value: value + }); + } else { + current[property] = value; + } + return true; + } + return false; + } + return false; + } + + function setInheritedNativeClassValue(target, property, value) { + for (var current = Object.getPrototypeOf(target); + current && current !== Function.prototype; + current = Object.getPrototypeOf(current)) { + var nativeClassValue; + try { + nativeClassValue = current.__nativeApiClass; + } catch (_) { + nativeClassValue = null; + } + if (!nativeClassValue) { + continue; + } + try { + nativeClassValue[property] = value; + return true; + } catch (_) { + } + } + return false; + } + + function isConstructorOptions(value) { + if (!value || typeof value !== 'object' || Array.isArray(value)) { + return false; + } + if (value.kind || value.nativeAddress || value instanceof Date) { + return false; + } + return Object.getPrototypeOf(value) === Object.prototype || + Object.getPrototypeOf(value) === null; + } + + function capitalizeToken(value) { + value = String(value || ''); + return value ? value.charAt(0).toUpperCase() + value.slice(1) : value; + } + + function selectorCandidatesFromOptions(options) { + var keys = Object.keys(options || {}); + if (!keys.length) { + return []; + } + var first = capitalizeToken(keys[0]); + var tail = ''; + for (var i = 1; i < keys.length; i++) { + tail += keys[i] + ':'; + } + return [ + 'initWith' + first + ':' + tail, + 'init' + first + ':' + tail + ]; + } + + function valuesFromOptions(options) { + return Object.keys(options || {}).map(function(key) { + return options[key]; + }); + } + + function selectorScoreForArguments(selectorName, args) { + if (!selectorName || selectorName.indexOf('init') !== 0) { + return -1; + } + if (selectorName === 'init') { + return args.length === 0 ? 100 : -1; + } + if (args.length === 0) { + return -1; + } + var lower = selectorName.toLowerCase(); + var first = args[0]; + var score = 1; + if (Array.isArray(first)) { + if (lower.indexOf('array') !== -1) { + score += 40; + } + } else if (typeof first === 'string') { + if (lower.indexOf('string') !== -1) { + score += 40; + } + if (lower.indexOf('url') !== -1) { + score += 10; + } + } else if (typeof first === 'number') { + if (lower.indexOf('primitive') !== -1) { + score += 50; + } + if (lower.indexOf('int') !== -1 || + lower.indexOf('integer') !== -1 || + lower.indexOf('number') !== -1 || + lower.indexOf('float') !== -1 || + lower.indexOf('double') !== -1 || + lower.indexOf('long') !== -1 || + lower.indexOf('short') !== -1) { + score += 30; + } + } else if (isConstructorOptions(first)) { + if (lower.indexOf('struct') !== -1 || + lower.indexOf('structure') !== -1) { + score += 40; + } + if (lower.indexOf('dictionary') !== -1) { + score += 20; + } + } else if (first === null || typeof first === 'undefined') { + score += 5; + } else if (lower.indexOf('object') !== -1 || + lower.indexOf('url') !== -1 || + lower.indexOf('data') !== -1) { + score += 20; + } + + var allStrings = args.length > 1 && args.every(function(value) { + return typeof value === 'string'; + }); + var allNumbers = args.length > 1 && args.every(function(value) { + return typeof value === 'number'; + }); + if (allStrings && lower.indexOf('string') !== -1) { + score += 25; + } + if (allNumbers && + (lower.indexOf('int') !== -1 || lower.indexOf('number') !== -1)) { + score += 25; + } + return score; + } + + function initializerMembers(nativeClass, argumentCount) { + var metadataMembers = nativeClass.__instanceMembers || []; + var runtimeMembers = nativeClass.__runtimeInstanceMembers || []; + var members = metadataMembers.concat(runtimeMembers); + var result = []; + for (var i = 0; i < members.length; i++) { + var member = members[i]; + if (!member || member.property || !member.selectorName) { + continue; + } + if (member.selectorName.indexOf('init') !== 0) { + continue; + } + if (typeof argumentCount === 'number' && + member.argumentCount !== argumentCount) { + continue; + } + result.push(member); + } + return result; + } + + function chooseInitializer(nativeClass, args, optionSelectors) { + var members = initializerMembers(nativeClass, args.length); + if (!members.length) { + return null; + } + if (optionSelectors && optionSelectors.length) { + for (var i = 0; i < optionSelectors.length; i++) { + for (var j = 0; j < members.length; j++) { + if (members[j].selectorName === optionSelectors[i]) { + return members[j]; + } + } + } + } + + var best = null; + var bestScore = -1; + for (var k = 0; k < members.length; k++) { + var score = selectorScoreForArguments(members[k].selectorName, args); + if (score > bestScore) { + bestScore = score; + best = members[k]; + } + } + return bestScore >= 0 ? best : null; + } + + function chooseInitializerBySelectors(nativeClass, args, selectors) { + if (!selectors || !selectors.length) { + return null; + } + var members = initializerMembers(nativeClass, args.length); + for (var i = 0; i < selectors.length; i++) { + for (var j = 0; j < members.length; j++) { + if (members[j].selectorName === selectors[i]) { + return members[j]; + } + } + } + return null; + } + + function unavailableInitializerError(error) { + return error && + /Objective-C selector is not available/.test(String(error.message || error)); + } + + function constructNativeInstance(nativeClass, args, rememberInstance) { + if (args.length === 1 && + args[0] && + typeof args[0] === 'object' && + (args[0].kind === 'pointer' || args[0].kind === 'reference') && + typeof nativeClass.construct === 'function') { + return nativeClass.construct(args[0]); + } + + var actualArgs = args; + var initializer = null; + if (args.length === 1 && isConstructorOptions(args[0])) { + var optionSelectors = selectorCandidatesFromOptions(args[0]); + if (!optionSelectors.length) { + throw new Error('No initializer found that matches constructor invocation.'); + } + var optionArgs = valuesFromOptions(args[0]); + initializer = chooseInitializerBySelectors( + nativeClass, + optionArgs, + optionSelectors + ); + if (initializer) { + actualArgs = optionArgs; + } + } + if (!initializer) { + initializer = chooseInitializer(nativeClass, actualArgs, null); + } + if (!initializer) { + throw new Error('No initializer found that matches constructor invocation.'); + } + if (typeof nativeClass.alloc !== 'function') { + throw new Error('Native class cannot be allocated'); + } + var instance = nativeClass.alloc(); + if (typeof rememberInstance === 'function') { + instance = rememberInstance(instance); + } + if (initializer.selectorName === 'init') { + if (typeof instance.init !== 'function') { + throw new Error('No initializer found that matches constructor invocation.'); + } + return instance.init(); + } + try { + if (initializer.name && typeof instance[initializer.name] === 'function') { + return instance[initializer.name](...actualArgs); + } + var invokeArgs = [initializer.selectorName]; + for (var invokeArgIndex = 0; invokeArgIndex < actualArgs.length; invokeArgIndex++) { + invokeArgs.push(actualArgs[invokeArgIndex]); + } + return instance.invoke(...invokeArgs); + } catch (error) { + if (unavailableInitializerError(error)) { + throw new Error('No initializer found that matches constructor invocation.'); + } + throw error; + } + } + + function wrapNativeClass(nativeClass) { + if (!nativeClass || (typeof nativeClass !== 'object' && typeof nativeClass !== 'function')) { + return nativeClass; + } + var nativeClassName = nativeClass.runtimeName || nativeClass.name || ''; + if (nativeClassName && classWrappersByName[nativeClassName]) { + if (classWrappers) { + try { + classWrappers.set(nativeClass, classWrappersByName[nativeClassName]); + } catch (_) { + } + } + return classWrappersByName[nativeClassName]; + } + if (classWrappers) { + var cached = classWrappers.get(nativeClass); + if (cached) { + return cached; + } + } + var constructable = function NativeScriptNativeClass() { + var args = Array.prototype.slice.call(arguments); + var redirectConstructor = this && this.constructor; + if (redirectConstructor && + redirectConstructor !== constructable && + redirectConstructor !== wrapper && + typeof redirectConstructor.__nativeApiEnsureClass === 'function') { + var redirectedWrapper = redirectConstructor.__nativeApiEnsureClass(); + if (redirectedWrapper && + redirectedWrapper !== constructable && + redirectedWrapper !== wrapper && + typeof redirectedWrapper === 'function') { + return rememberClassOnInstance( + redirectedWrapper.call(this, ...args), + redirectConstructor + ); + } + } + if (args.length > 0) { + return rememberInstanceClass(constructNativeInstance(nativeClass, args, rememberInstanceClass)); + } + if (typeof nativeClass.new !== 'function') { + throw new Error('Native class cannot be initialized'); + } + return rememberInstanceClass(nativeClass.new()); + }; + function rememberInstanceClass(instance) { + return rememberClassOnInstance(instance, wrapper || constructable); + } + try { + Object.defineProperty(constructable, 'name', { + configurable: true, + enumerable: false, + value: nativeClassName || nativeClass.name || 'NativeScriptNativeClass' + }); + } catch (_) { + } + try { + Object.defineProperty(constructable, 'extend', { + configurable: true, + enumerable: false, + writable: false, + value: function(methods, options) { + if (methods == null || typeof methods !== 'object') { + throw new Error('extend() first parameter must be an object'); + } + var extendOptions = options || {}; + if (typeof Symbol === 'function' && + Object.prototype.hasOwnProperty.call(methods, Symbol.iterator)) { + try { + extendOptions = Object.assign({}, extendOptions, { + __hasIterator: true + }); + } catch (_) { + extendOptions.__hasIterator = true; + } + } + var extendedNativeClass = api.__extendClass(nativeClass, methods, extendOptions); + var extended = wrapNativeClass(extendedNativeClass); + try { + Object.setPrototypeOf(extended, wrapper || constructable); + } catch (_) { + } + var extendedPrototype = Object.create(constructable.prototype || null); + try { + Object.defineProperties(extendedPrototype, Object.getOwnPropertyDescriptors(methods)); + } catch (_) { + Object.keys(methods).forEach(function(key) { + extendedPrototype[key] = methods[key]; + }); + } + try { + Object.defineProperty(extendedPrototype, 'constructor', { + configurable: true, + enumerable: false, + writable: true, + value: extended + }); + } catch (_) { + } + extended.prototype = extendedPrototype; + try { + api.__rememberClassWrapper(extendedNativeClass, extended, extendedPrototype); + } catch (_) { + } + return extended; + } + }); + } catch (_) { + } + try { + Object.defineProperty(constructable, 'alloc', { + configurable: true, + enumerable: false, + writable: true, + value: function() { + if (arguments.length !== 0) { + throw new Error('alloc does not take arguments; use invoke for an explicit Objective-C selector.'); + } + return rememberInstanceClass(nativeClass.alloc()); + } + }); + } catch (_) { + } + try { + Object.defineProperty(constructable, 'new', { + configurable: true, + enumerable: false, + writable: false, + value: function() { + if (arguments.length !== 0) { + throw new Error('new does not take arguments; use invoke for an explicit Objective-C selector.'); + } + if (typeof nativeClass.new !== 'function') { + throw new Error('Native class cannot be initialized'); + } + return rememberInstanceClass(nativeClass.new()); + } + }); + } catch (_) { + } + try { + Object.defineProperty(constructable, 'caller', { + configurable: true, + enumerable: false, + writable: false, + value: null + }); + } catch (_) { + } + try { + Object.defineProperty(constructable, 'arguments', { + configurable: true, + enumerable: false, + writable: false, + value: null + }); + } catch (_) { + } + var basePrototypeTarget = {}; + var classMembersInstalled = false; + function selectorArgumentCount(selectorName) { + var count = 0; + if (typeof selectorName !== 'string') { + return count; + } + for (var i = 0; i < selectorName.length; i++) { + if (selectorName.charCodeAt(i) === 58) { + count++; + } + } + return count; + } + function selectorDescriptor(member, selectorName, signatureOffset, argumentCount, runtimeOnly) { + return { + name: member.name || '', + selectorName: selectorName || '', + setterSelectorName: member.setterSelectorName || '', + signatureOffset: typeof signatureOffset === 'number' ? signatureOffset : 0, + setterSignatureOffset: typeof member.setterSignatureOffset === 'number' + ? member.setterSignatureOffset + : 0, + flags: typeof member.flags === 'number' ? member.flags : 0, + property: !!member.property, + readonly: !!member.readonly, + argumentCount: typeof argumentCount === 'number' + ? argumentCount + : selectorArgumentCount(selectorName), + runtimeOnly: !!runtimeOnly + }; + } + function addSelectorGroups(groups, members, runtimeOnly) { + if (!groups || !members || typeof members.length !== 'number') { + return; + } + for (var i = 0; i < members.length; i++) { + var member = members[i]; + if (!member || member.property || !member.name || !member.selectorName) { + continue; + } + // Skip methods that need special interceptor handling with kNonMasking. + if (member.name === 'superclass' || member.name === 'class' || + member.name === 'constructor' || member.name === 'className') { + continue; + } + var argumentCount = typeof member.argumentCount === 'number' + ? member.argumentCount + : 0; + var group = groups[member.name]; + if (!group) { + group = []; + groups[member.name] = group; + } + if (group[argumentCount] === undefined) { + group[argumentCount] = selectorDescriptor( + member, + member.selectorName, + member.signatureOffset, + argumentCount, + runtimeOnly + ); + } + // Methods with a trailing NSError** out-parameter (selector ending in + // "error:") may be called with the error argument omitted, so register + // the error-omitted arity too. + if (argumentCount > 0 && + /error:$/.test(member.selectorName) && + group[argumentCount - 1] === undefined) { + group[argumentCount - 1] = selectorDescriptor( + member, + member.selectorName, + member.signatureOffset, + argumentCount - 1, + runtimeOnly + ); + } + } + } + function installSelectorGroups(target, groups, receiverIsClass) { + if (!target || !groups) { + return; + } + for (var name in groups) { + if (!Object.prototype.hasOwnProperty.call(groups, name) || + Object.prototype.hasOwnProperty.call(target, name)) { + continue; + } + var selectors = groups[name]; + if (!selectors || !selectors.length) { + continue; + } + var hasMetadataSelector = false; + for (var selectorIndex = 0; selectorIndex < selectors.length; selectorIndex++) { + if (selectors[selectorIndex] && !selectors[selectorIndex].runtimeOnly) { + hasMetadataSelector = true; + break; + } + } + if (!hasMetadataSelector && receiverIsClass && name in target) { + continue; + } + var selectorFunction = + api.__makeSelectorGroupFunction(nativeClass, !!receiverIsClass, selectors); + Object.defineProperty(target, name, { + configurable: true, + enumerable: false, + writable: true, + value: receiverIsClass + ? (function(fn, memberName) { + return function() { + if (this && typeof this === 'object' && this.kind === 'object') { + var baseArgs = [nativeClass, this, memberName]; + for (var baseArgIndex = 0; baseArgIndex < arguments.length; baseArgIndex++) { + baseArgs.push(arguments[baseArgIndex]); + } + return api.__invokeBase(...baseArgs); + } + var args = []; + for (var argIndex = 0; argIndex < arguments.length; argIndex++) { + args.push(arguments[argIndex]); + } + return rememberInstanceClass(fn(...args)); + }; + })(selectorFunction, name) + : selectorFunction + }); + } + } + function installClassMembers(target, members, receiverIsClass, runtimeMembers) { + var hasMetadataMembers = members && typeof members.length === 'number'; + var hasRuntimeMembers = runtimeMembers && typeof runtimeMembers.length === 'number'; + if (!target || (!hasMetadataMembers && !hasRuntimeMembers)) { + return; + } + var selectorGroups = Object.create(null); + addSelectorGroups(selectorGroups, members, false); + for (var i = 0; hasMetadataMembers && i < members.length; i++) { + var member = members[i]; + if (!member || !member.name) { + continue; + } + if (member.property) { + // Skip properties that need special interceptor handling (they + // return wrapped class constructors, not raw native values). + if (member.name === 'superclass' || member.name === 'class' || + member.name === 'constructor' || member.name === 'debugDescription' || + member.name === 'className') { + continue; + } + var existingDescriptor = Object.getOwnPropertyDescriptor(target, member.name); + if (existingDescriptor && + (typeof existingDescriptor.get === 'function' || + typeof existingDescriptor.set === 'function')) { + continue; + } + var getterFunction = member.selectorName + ? api.__makeSelectorGroupFunction( + nativeClass, + !!receiverIsClass, + [selectorDescriptor(member, member.selectorName, member.signatureOffset, 0)] + ) + : undefined; + var setterFunction = !member.readonly && member.setterSelectorName + ? api.__makeSelectorGroupFunction( + nativeClass, + !!receiverIsClass, + [ + null, + selectorDescriptor( + member, + member.setterSelectorName, + member.setterSignatureOffset, + 1 + ) + ] + ) + : undefined; + var descriptor = { + configurable: true, + enumerable: false, + get: receiverIsClass + ? (function(name, selectorName) { + return function() { + return selectorName + ? nativeClass.invoke(selectorName) + : nativeClass[name]; + }; + })(member.name, member.selectorName) + : (getterFunction || (function(name) { + return function() { + return api.__invokeBase(nativeClass, this, name); + }; + })(member.name)) + }; + if (!member.readonly) { + descriptor.set = receiverIsClass + ? (function(name, setterSelectorName) { + return function(value) { + if (setterSelectorName) { + return nativeClass.invoke(setterSelectorName, value); + } + nativeClass[name] = value; + }; + })(member.name, member.setterSelectorName) + : (setterFunction || (function(name) { + return function(value) { + return api.__invokeBase(nativeClass, this, name, value); + }; + })(member.name)); + } + Object.defineProperty(target, member.name, descriptor); + } else { + continue; + } + } + installSelectorGroups(target, selectorGroups, receiverIsClass); + } + function installNativeClassMembersIfNeeded() { + if (classMembersInstalled) { + return; + } + classMembersInstalled = true; + installClassMembers( + constructable, + nativeClass.__staticMembers, + true, + nativeClass.__runtimeStaticMembers + ); + installClassMembers( + basePrototypeTarget, + nativeClass.__instanceMembers, + false, + nativeClass.__runtimeInstanceMembers + ); + try { + delete constructable.__nativeApiInstallMembers; + } catch (_) { + } + } + try { + Object.defineProperty(constructable, '__nativeApiInstallMembers', { + configurable: true, + enumerable: false, + writable: false, + value: installNativeClassMembersIfNeeded + }); + } catch (_) { + } + try { + Object.defineProperty(basePrototypeTarget, 'constructor', { + configurable: true, + enumerable: false, + writable: true, + value: constructable + }); + } catch (_) { + } + try { + Object.defineProperty(basePrototypeTarget, 'toString', { + configurable: true, + enumerable: false, + writable: true, + value: function() { + return '[object NativeScriptObject]'; + } + }); + } catch (_) { + } + try { + if (typeof Symbol === 'function' && Symbol.iterator && + typeof api.__fastEnumeration === 'function') { + Object.defineProperty(basePrototypeTarget, Symbol.iterator, { + configurable: true, + enumerable: false, + writable: true, + value: function() { + return api.__fastEnumeration(this); + } + }); + } + } catch (_) { + } + constructable.prototype = basePrototypeTarget; + try { + Object.defineProperty(constructable, Symbol.hasInstance, { + configurable: true, + enumerable: false, + value: function(value) { + if (!value || typeof value !== 'object') { + return false; + } + var expectedName = nativeClass.runtimeName || nativeClass.name; + try { + if (typeof value.isKindOfClass === 'function' && + value.isKindOfClass(constructable)) { + return true; + } + } catch (_) { + } + try { + var current = typeof value.class === 'function' ? value.class() : null; + while (current) { + if (current === wrapper || current === constructable) { + return true; + } + var currentName = current.runtimeName || current.name; + if (typeof expectedName === 'string' && currentName === expectedName) { + return true; + } + var next = current.superclass || null; + if (typeof next === 'function' && next.kind !== 'class') { + next = next.call(current); + } + current = next || null; + } + } catch (_) { + } + return typeof expectedName === 'string' && value.className === expectedName; + } + }); + } catch (_) { + } + var cachedNativeFunctions = typeof Map === 'function' ? new Map() : null; + var wrapper = typeof Proxy === 'function' + ? new Proxy(constructable, { + get: function(target, property, receiver) { + if (property === '__nativeApiClass') { + return nativeClass; + } + if (property === 'toString') { + return function() { + return String(nativeClass); + }; + } + if (property === 'prototype') { + installNativeClassMembersIfNeeded(); + return Reflect.get(target, property, receiver); + } + if (property === 'hasOwnProperty') { + return function(key) { + installNativeClassMembersIfNeeded(); + return Object.prototype.hasOwnProperty.call(target, key); + }; + } + if (Object.prototype.hasOwnProperty.call(target, property) || + property === 'prototype' || + property === 'length' || + property === 'name') { + return Reflect.get(target, property, receiver); + } + installNativeClassMembersIfNeeded(); + if (Object.prototype.hasOwnProperty.call(target, property)) { + return Reflect.get(target, property, receiver); + } + if (cachedNativeFunctions && cachedNativeFunctions.has(property)) { + return cachedNativeFunctions.get(property); + } + var nativeValue = nativeClass[property]; + if (nativeValue !== undefined) { + if (typeof nativeValue === 'function') { + if (cachedNativeFunctions) { + cachedNativeFunctions.set(property, nativeValue); + } + try { + Object.defineProperty(target, property, { + configurable: true, + enumerable: false, + writable: false, + value: nativeValue + }); + } catch (_) { + } + } + return nativeValue; + } + var reflected = Reflect.get(target, property, receiver); + if (reflected !== undefined || property in target) { + return reflected; + } + installNativeClassMembersIfNeeded(); + reflected = Reflect.get(target, property, receiver); + if (reflected !== undefined || property in target) { + return reflected; + } + return reflected; + }, + set: function(target, property, value, receiver) { + if (property === 'prototype') { + target[property] = value; + return true; + } + installNativeClassMembersIfNeeded(); + if (setDescriptorValue(target, property, receiver, value)) { + return true; + } + if (setInheritedNativeClassValue(target, property, value)) { + return true; + } + try { + nativeClass[property] = value; + return true; + } catch (_) { + } + if (receiver && receiver !== target) { + Object.defineProperty(receiver, property, { + configurable: true, + enumerable: true, + writable: true, + value: value + }); + return true; + } + return Reflect.set(target, property, value, receiver); + }, + has: function(target, property) { + installNativeClassMembersIfNeeded(); + return property in target || property in nativeClass; + }, + ownKeys: function(target) { + installNativeClassMembersIfNeeded(); + return Reflect.ownKeys(target).filter(function(key) { + return key !== 'new' && + key !== 'hasOwnProperty' && + key !== '__nativeApiInstallMembers'; + }); + }, + getOwnPropertyDescriptor: function(target, property) { + installNativeClassMembersIfNeeded(); + return Reflect.getOwnPropertyDescriptor(target, property); + } + }) + : constructable; + if (classWrappers) { + classWrappers.set(nativeClass, wrapper); + } + try { + var nativeSuperclass = nativeClass.__superclass; + if (nativeSuperclass && nativeSuperclass !== nativeClass) { + var superclassWrapper = wrapNativeClass(nativeSuperclass); + if (superclassWrapper && superclassWrapper !== wrapper && + typeof Object.setPrototypeOf === 'function') { + Object.setPrototypeOf(wrapper, superclassWrapper); + if (superclassWrapper.prototype) { + Object.setPrototypeOf(constructable.prototype, superclassWrapper.prototype); + } + } + } + } catch (_) { + } + try { + api.__rememberClassWrapper(nativeClass, wrapper, constructable.prototype); + } catch (_) { + } + if (nativeClassName) { + classWrappersByName[nativeClassName] = wrapper; + cacheGlobal(nativeClassName, wrapper); + if (!Object.prototype.hasOwnProperty.call(globalThis, nativeClassName)) { + try { + Object.defineProperty(globalThis, nativeClassName, { + configurable: true, + enumerable: false, + writable: false, + value: wrapper + }); + } catch (_) { + } + } + } + if (nativeClass.name && nativeClass.name !== nativeClassName) { + classWrappersByName[nativeClass.name] = wrapper; + cacheGlobal(nativeClass.name, wrapper); + } + return wrapper; + } + + function rememberClassOnInstance(instance, classWrapper) { + if (instance && typeof instance === 'object' && classWrapper) { + try { + if (typeof classWrapper.__nativeApiInstallMembers === 'function') { + classWrapper.__nativeApiInstallMembers(); + } + if (typeof api.__rememberObjectClassWrapper === 'function') { + api.__rememberObjectClassWrapper(instance, classWrapper); + } else { + instance.__nativeApiClassWrapper = classWrapper; + } + } catch (_) { + } + } + return instance; + } + + function isNativeClassLike(value) { + if (!value || (typeof value !== 'object' && typeof value !== 'function')) { + return false; + } + if (value.kind === 'class') { + return true; + } + try { + return !!value.__nativeApiClass; + } catch (_) { + return false; + } + } + + function nativeClassLikeHandle(value) { + if (!value || (typeof value !== 'object' && typeof value !== 'function')) { + return value; + } + try { + if (typeof value.__nativeApiEnsureClass === 'function') { + value = value.__nativeApiEnsureClass(); + } + } catch (_) { + } + try { + return value.__nativeApiClass || value; + } catch (_) { + return value; + } + } + + function materializeTypeScriptNativeClass(constructor) { + if (!constructor || typeof constructor !== 'function') { + return undefined; + } + var state = constructor.__nativeApiTypeScriptState; + if (!state) { + return undefined; + } + if (state.wrapper) { + return state.wrapper; + } + if (state.materializing) { + return state.base; + } + + state.materializing = true; + try { + var baseWrapper = state.base; + if (baseWrapper && typeof baseWrapper.__nativeApiEnsureClass === 'function') { + baseWrapper = baseWrapper.__nativeApiEnsureClass(); + } + + var options = {}; + var className = constructor.ObjCClassName || constructor.name; + if (className) { + options.name = className; + } + if (constructor.ObjCProtocols) { + options.protocols = constructor.ObjCProtocols; + } + if (constructor.ObjCExposedMethods) { + options.exposedMethods = constructor.ObjCExposedMethods; + } + + var nativeBase = nativeClassLikeHandle(baseWrapper); + var nativeClass = api.__extendClass(nativeBase, constructor.prototype || {}, options); + var wrapper = wrapNativeClass(nativeClass); + state.wrapper = wrapper; + + try { + Object.setPrototypeOf(constructor, wrapper); + } catch (_) { + } + try { + api.__rememberClassWrapper(nativeClass, constructor, constructor.prototype || {}); + } catch (_) { + } + return wrapper; + } finally { + state.materializing = false; + } + } + + function defineTypeScriptStaticForwarder(constructor, name, isProperty, readonly) { + if (!name || name === 'length' || name === 'name' || name === 'prototype' || + Object.prototype.hasOwnProperty.call(constructor, name)) { + return; + } + + var descriptor = { + configurable: true, + enumerable: false + }; + + if (isProperty) { + descriptor.get = function() { + var wrapper = materializeTypeScriptNativeClass(constructor); + return wrapper ? wrapper[name] : undefined; + }; + if (!readonly) { + descriptor.set = function(value) { + var wrapper = materializeTypeScriptNativeClass(constructor); + if (wrapper) { + wrapper[name] = value; + } + }; + } + } else { + descriptor.writable = true; + descriptor.value = function() { + if (name === 'class') { + materializeTypeScriptNativeClass(constructor); + return constructor; + } + if (name === 'superclass') { + var state = constructor.__nativeApiTypeScriptState; + return state && state.base; + } + var wrapper = materializeTypeScriptNativeClass(constructor); + var member = wrapper && wrapper[name]; + if (typeof member !== 'function') { + throw new TypeError(String(name) + ' is not a function'); + } + var memberArgs = []; + for (var memberArgIndex = 0; memberArgIndex < arguments.length; memberArgIndex++) { + memberArgs.push(arguments[memberArgIndex]); + } + var result = wrapper[name](...memberArgs); + if (name === 'alloc' || name === 'new' || name === 'construct') { + return rememberClassOnInstance(result, constructor); + } + return result; + }; + } + + try { + Object.defineProperty(constructor, name, descriptor); + } catch (_) { + } + } + + function installTypeScriptNativeClassSupport(constructor, base) { + if (!constructor || typeof constructor !== 'function' || !isNativeClassLike(base)) { + return false; + } + if (constructor.__nativeApiTypeScriptState) { + return true; + } + + try { + Object.defineProperty(constructor, '__nativeApiTypeScriptState', { + configurable: false, + enumerable: false, + writable: false, + value: { + base: base, + wrapper: null, + materializing: false + } + }); + } catch (_) { + constructor.__nativeApiTypeScriptState = { + base: base, + wrapper: null, + materializing: false + }; + } + + try { + Object.defineProperty(constructor, '__nativeApiEnsureClass', { + configurable: false, + enumerable: false, + writable: false, + value: function() { + return materializeTypeScriptNativeClass(constructor); + } + }); + } catch (_) { + } + + try { + Object.defineProperty(constructor, '__nativeApiClass', { + configurable: true, + enumerable: false, + get: function() { + var wrapper = materializeTypeScriptNativeClass(constructor); + return wrapper && wrapper.__nativeApiClass; + } + }); + } catch (_) { + } + + ['alloc', 'new', 'class', 'superclass', 'extend'].forEach(function(name) { + defineTypeScriptStaticForwarder(constructor, name, false, false); + }); + + try { + var members = base.__staticMembers || []; + for (var i = 0; i < members.length; i++) { + var member = members[i]; + if (member && member.name) { + defineTypeScriptStaticForwarder( + constructor, + member.name, + !!member.property, + !!member.readonly + ); + } + } + } catch (_) { + } + + return true; + } + + function installTypeScriptNativeHelpers() { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function(d, b) { d.__proto__ = b; }) || + function(d, b) { + for (var p in b) { + if (Object.prototype.hasOwnProperty.call(b, p)) { + d[p] = b[p]; + } + } + }; + + globalThis.__extends = function(d, b) { + if (typeof b !== 'function' && b !== null) { + throw new TypeError('Class extends value ' + String(b) + ' is not a constructor or null'); + } + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + if (b !== null) { + installTypeScriptNativeClassSupport(d, b); + } + }; + + globalThis.NativeClass = function NativeClass(constructor) { + if (constructor && typeof constructor.__nativeApiEnsureClass === 'function') { + constructor.__nativeApiEnsureClass(); + } + return constructor; + }; + + globalThis.ObjCClass = function ObjCClass() { + var protocols = Array.prototype.slice.call(arguments); + return function(constructor) { + if (constructor.ObjCProtocols) { + for (var protocolIndex = 0; protocolIndex < protocols.length; protocolIndex++) { + constructor.ObjCProtocols.push(protocols[protocolIndex]); + } + } else { + constructor.ObjCProtocols = protocols; + } + if (typeof constructor.__nativeApiEnsureClass === 'function') { + constructor.__nativeApiEnsureClass(); + } + return constructor; + }; + }; + } + + function wrapInteropFactory(nativeFactory, properties) { + if (typeof nativeFactory !== 'function' || nativeFactory.__nativeScriptConstructable) { + return nativeFactory; + } + var constructable = function NativeScriptInteropValue() { + var factoryArgs = []; + for (var factoryArgIndex = 0; factoryArgIndex < arguments.length; factoryArgIndex++) { + factoryArgs.push(arguments[factoryArgIndex]); + } + return nativeFactory(...factoryArgs); + }; + try { + if (nativeFactory.prototype) { + constructable.prototype = nativeFactory.prototype; + } + } catch (_) { + } + try { + Object.defineProperty(constructable, Symbol.hasInstance, { + configurable: true, + enumerable: false, + value: function(value) { + return !!value && typeof value === 'object' && value.kind === properties.kind; + } + }); + } catch (_) { + } + Object.keys(properties).forEach(function(key) { + try { + Object.defineProperty(constructable, key, { + configurable: true, + enumerable: false, + writable: false, + value: properties[key] + }); + } catch (_) { + } + }); + Object.defineProperty(constructable, '__nativeScriptConstructable', { + configurable: false, + enumerable: false, + writable: false, + value: true + }); + return constructable; + } + + function installInteropConstructors() { + var interop = globalThis.interop; + if (!interop || typeof interop !== 'object') { + return; + } + var pointerSize; + try { + if (typeof interop.sizeof === 'function' && interop.types && interop.types.pointer !== undefined) { + pointerSize = interop.sizeof(interop.types.pointer); + } + } catch (_) { + pointerSize = undefined; + } + interop.Pointer = wrapInteropFactory(interop.Pointer, { kind: 'pointer', sizeof: pointerSize }); + interop.Reference = wrapInteropFactory(interop.Reference, { kind: 'reference', sizeof: pointerSize }); + interop.Block = wrapInteropFactory(interop.Block, { kind: 'block', sizeof: pointerSize }); + interop.FunctionReference = wrapInteropFactory( + interop.FunctionReference, + { kind: 'functionReference', sizeof: pointerSize } + ); + if (interop.types && typeof interop.types === 'object') { + Object.keys(interop.types).forEach(function(name) { + var value = interop.types[name]; + if (typeof value !== 'number') { + return; + } + var boxed = { + valueOf: function() { return value; }, + toString: function() { return String(value); } + }; + Object.defineProperty(boxed, typeCodeKey, { + configurable: false, + enumerable: false, + writable: false, + value: value + }); + interop.types[name] = boxed; + }); + } + } + + function defineInlineFunction(name, value) { + if (Object.prototype.hasOwnProperty.call(globalThis, name)) { + return; + } + Object.defineProperty(globalThis, name, { + configurable: true, + enumerable: false, + writable: true, + value: value + }); + } + + function installInlineFunctions() { + var makePoint = function(x, y) { return { x: x, y: y }; }; + var makeSize = function(width, height) { return { width: width, height: height }; }; + var makeRect = function(x, y, width, height) { + return { origin: { x: x, y: y }, size: { width: width, height: height } }; + }; + defineInlineFunction('CGPointMake', makePoint); + defineInlineFunction('NSMakePoint', makePoint); + defineInlineFunction('CGSizeMake', makeSize); + defineInlineFunction('NSMakeSize', makeSize); + defineInlineFunction('CGRectMake', makeRect); + defineInlineFunction('NSMakeRect', makeRect); + defineInlineFunction('NSMakeRange', function(location, length) { + return { location: location, length: length }; + }); + defineInlineFunction('UIEdgeInsetsMake', function(top, left, bottom, right) { + return { top: top, left: left, bottom: bottom, right: right }; + }); + } + + function names(kind) { + var metadata = api.metadata; + var fn = metadata && metadata[kind]; + return typeof fn === 'function' ? fn() : []; + } + + function nameSet(values) { + var result = Object.create(null); + (values || []).forEach(function(value) { + result[value] = true; + }); + return result; + } + + var classNameList = names('classNames'); + var functionNameList = names('functionNames'); + var constantNameList = names('constantNames'); + var protocolNameList = names('protocolNames'); + var enumNameList = names('enumNames'); + var functionNameSet = nameSet(functionNameList); + var constantNameSet = nameSet(constantNameList); + var classNameSet = nameSet(classNameList); + var protocolNameSet = nameSet(protocolNameList); + var enumNameSet = nameSet(enumNameList); + + function resolveNativeApiEnum(enumName) { + return (api.getEnum && api.getEnum(enumName)) || api[enumName]; + } + + Object.defineProperty(globalThis, '__nativeScriptResolveNativeApiLazyGlobal', { + configurable: false, + enumerable: false, + writable: false, + value: function(name, kind) { + var value; + if (kind === 'class') { + value = wrapNativeClass(api[name]); + } else if (kind === 'function' || kind === 'constant') { + value = api[name]; + } else if (kind === 'protocol') { + value = (api.getProtocol && api.getProtocol(name)) || api[name]; + } else if (kind === 'enum') { + value = resolveNativeApiEnum(name); + } else if (kind === 'struct') { + value = wrapAggregateConstructor((api.getStruct && api.getStruct(name)) || api[name]); + } else if (kind === 'union') { + value = wrapAggregateConstructor((api.getUnion && api.getUnion(name)) || api[name]); + } else if (kind && kind.indexOf('enumMember:') === 0) { + var enumValue = resolveNativeApiEnum(kind.slice('enumMember:'.length)); + value = enumValue && enumValue[name]; + } else { + value = api[name]; + } + cacheGlobal(name, value); + return value; + } + }); + + classNameList.forEach(function(name) { + defineLazyGlobal(name, function(className) { + return wrapNativeClass(api[className]); + }, false, 'class'); + }); + functionNameList.forEach(function(name) { + defineLazyGlobal(name, function(functionName) { + return api[functionName]; + }, false, 'function'); + }); + constantNameList.forEach(function(name) { + defineLazyGlobal(name, function(constantName) { + return api[constantName]; + }, false, 'constant'); + }); + protocolNameList.forEach(function(name) { + defineLazyGlobal(name, function(protocolName) { + return (api.getProtocol && api.getProtocol(protocolName)) || api[protocolName]; + }, false, 'protocol'); + }); + enumNameList.forEach(function(name) { + defineLazyGlobal(name, resolveNativeApiEnum, false, 'enum'); + var enumValue = resolveNativeApiEnum(name); + if (!enumValue || typeof enumValue !== 'object') { + return; + } + Object.keys(enumValue).forEach(function(memberName) { + if (/^-?\d+$/.test(memberName)) { + return; + } + defineLazyGlobal(memberName, function() { + return enumValue[memberName]; + }, false, 'enumMember:' + name); + }); + }); + names('structNames').forEach(function(name) { + var conflictsWithValue = + !!functionNameSet[name] || !!constantNameSet[name] || !!classNameSet[name] || + !!protocolNameSet[name] || !!enumNameSet[name]; + defineLazyGlobal(name, function(structName) { + return wrapAggregateConstructor((api.getStruct && api.getStruct(structName)) || api[structName]); + }, !conflictsWithValue, 'struct'); + }); + names('unionNames').forEach(function(name) { + var conflictsWithValue = + !!functionNameSet[name] || !!constantNameSet[name] || !!classNameSet[name] || + !!protocolNameSet[name] || !!enumNameSet[name]; + defineLazyGlobal(name, function(unionName) { + return wrapAggregateConstructor((api.getUnion && api.getUnion(unionName)) || api[unionName]); + }, !conflictsWithValue, 'union'); + }); + + if (typeof globalThis.UIColor === 'undefined' && + typeof globalThis.NSColor !== 'undefined') { + globalThis.UIColor = globalThis.NSColor; + cacheGlobal('UIColor', globalThis.UIColor); + } + var colorCtor = globalThis.UIColor || globalThis.NSColor; + if (colorCtor && colorCtor.prototype && + typeof colorCtor.prototype.initWithRedGreenBlueAlpha !== 'function') { + 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; + }; + } + defineLazyGlobal('CC_SHA256', function() { return api.CC_SHA256; }); + + installInteropConstructors(); + installTypeScriptNativeHelpers(); + installInlineFunctions(); + + try { + Object.defineProperty(globalThis, installedFlagName, { + configurable: false, + enumerable: false, + writable: false, + value: true + }); + } catch (_) { + } +}) +)Engine_GLOBALS"; + + std::string script(GlobalInstaller); + script += "("; + script += jsStringLiteral(globalName); + script += ");"; + runtime.evaluateJavaScript(std::make_shared(std::move(script)), + "NativeApiGlobals.js"); + NativeApiWriteSmokeStage("engine:globals:after-eval"); +} + +void InstallNativeApi(Runtime& runtime, const NativeApiConfig& config) { + const char* globalName = config.globalName != nullptr && config.globalName[0] != '\0' + ? config.globalName + : "__nativeScriptNativeApi"; + NativeApiWriteSmokeStage("engine:create-api"); + Object api = CreateNativeApi(runtime, config); + Object global = runtime.global(); + NativeApiWriteSmokeStage("engine:set-global"); + global.setProperty(runtime, globalName, api); + + NativeApiWriteSmokeStage("engine:set-interop"); + Value existingInterop = global.getProperty(runtime, "interop"); + if (existingInterop.isUndefined() || existingInterop.isNull()) { + global.setProperty(runtime, "interop", api.getProperty(runtime, "interop")); + } + if (config.installGlobalSymbols) { + NativeApiWriteSmokeStage("engine:install-globals"); + InstallNativeApiGlobalSymbols(runtime, globalName); + } else { + NativeApiWriteSmokeStage("engine:install-aggregate-globals"); + InstallAggregateGlobals(runtime, api, "protocolNames"); + } + NativeApiWriteSmokeStage("engine:installed"); +} diff --git a/NativeScript/ffi/objc/shared/bridge/Invocation.mm b/NativeScript/ffi/objc/shared/bridge/Invocation.mm new file mode 100644 index 000000000..af32e2d3e --- /dev/null +++ b/NativeScript/ffi/objc/shared/bridge/Invocation.mm @@ -0,0 +1,1764 @@ +bool isValidMetadataStringOffset(MDMetadataReader* metadata, + MDSectionOffset offset) { + if (metadata == nullptr || metadata->constantsOffset < metadata->stringsOffset) { + return false; + } + return offset < metadata->constantsOffset - metadata->stringsOffset; +} + +bool startsWith(const std::string& value, const std::string& prefix) { + return value.size() >= prefix.size() && + value.compare(0, prefix.size(), prefix) == 0; +} + +bool endsWith(const std::string& value, const std::string& suffix) { + return value.size() >= suffix.size() && + value.compare(value.size() - suffix.size(), suffix.size(), suffix) == 0; +} + +std::string stripEnumSuffix(const std::string& enumName) { + static const std::vector suffixes = { + "Options", "Option", "Enums", "Enum", "Result", "Enumeration", + "Orientation", "Style", "Mask", "Type", "Status", "Modes", "Mode", "s"}; + + for (const auto& suffix : suffixes) { + if (enumName.size() > suffix.size() && endsWith(enumName, suffix)) { + return enumName.substr(0, enumName.size() - suffix.size()); + } + } + + return enumName; +} + +bool isNSComparisonResultOrderingName(const std::string& enumName, + const std::string& member) { + if (enumName != "NSComparisonResult") { + return false; + } + return member == "Ascending" || member == "Same" || member == "Descending"; +} + +class NativeApiReturnStorage { + public: + explicit NativeApiReturnStorage(size_t size) + : size_(std::max(size, sizeof(void*))) { + if (size_ > kInlineSize) { + heap_.assign(size_, 0); + } else { + std::memset(inline_, 0, kInlineSize); + } + } + + void* data() { return heap_.empty() ? inline_ : heap_.data(); } + unsigned char* bytes() { return static_cast(data()); } + + private: + static constexpr size_t kInlineSize = 64; + + size_t size_ = 0; + alignas(std::max_align_t) unsigned char inline_[kInlineSize] = {}; + std::vector heap_; +}; + +class NativeApiPointerFrame { + public: + explicit NativeApiPointerFrame(size_t count) : count_(count) { + if (count_ > kInlineCount) { + heap_.resize(count_); + } + } + + void set(size_t index, void* value) { + if (index >= count_) { + throw std::out_of_range("Native invocation argument index out of range."); + } + if (count_ <= kInlineCount) { + inline_[index] = value; + } else { + heap_[index] = value; + } + } + + void** data() { + if (count_ == 0) { + return nullptr; + } + return count_ <= kInlineCount ? inline_ : heap_.data(); + } + + private: + static constexpr size_t kInlineCount = 10; + + size_t count_ = 0; + void* inline_[kInlineCount] = {}; + std::vector heap_; +}; + +Value enumToObject(Runtime& runtime, MDMetadataReader* metadata, + const NativeApiSymbol& symbol) { + Object result(runtime); + if (metadata == nullptr || symbol.offset == MD_SECTION_OFFSET_NULL) { + return result; + } + + std::string enumName = symbol.name; + std::string strippedPrefix = stripEnumSuffix(enumName); + MDSectionOffset offset = symbol.offset + sizeof(MDSectionOffset); + bool next = true; + while (next) { + auto nameOffset = metadata->getOffset(offset); + next = (nameOffset & metagen::mdSectionOffsetNext) != 0; + nameOffset &= ~metagen::mdSectionOffsetNext; + offset += sizeof(MDSectionOffset); + + const char* memberName = metadata->resolveString(nameOffset); + int64_t value = metadata->getEnumValue(offset); + offset += sizeof(int64_t); + + std::string canonicalName = memberName != nullptr ? memberName : ""; + std::vector aliases; + aliases.push_back(canonicalName); + + if (!strippedPrefix.empty() && startsWith(canonicalName, strippedPrefix) && + canonicalName.size() > strippedPrefix.size()) { + aliases.push_back(canonicalName.substr(strippedPrefix.size())); + } else if (!strippedPrefix.empty() && + !startsWith(canonicalName, strippedPrefix)) { + aliases.push_back(strippedPrefix + canonicalName); + } + + if (startsWith(enumName, "NS") && !startsWith(canonicalName, "NS")) { + aliases.push_back(std::string("NS") + canonicalName); + } + + if (enumName == "NSStringCompareOptions" && + !endsWith(canonicalName, "Search")) { + aliases.push_back(canonicalName + "Search"); + aliases.push_back(std::string("NS") + canonicalName + "Search"); + } + + if (!startsWith(canonicalName, "k")) { + aliases.push_back(std::string("k") + enumName + canonicalName); + } + + if (isNSComparisonResultOrderingName(enumName, canonicalName)) { + aliases.push_back(std::string("Ordered") + canonicalName); + aliases.push_back(std::string("NSOrdered") + canonicalName); + } + + std::vector uniqueAliases; + std::unordered_set seenAliases; + for (const auto& alias : aliases) { + if (!alias.empty() && seenAliases.insert(alias).second) { + uniqueAliases.push_back(alias); + } + } + + for (const auto& alias : uniqueAliases) { + result.setProperty(runtime, alias.c_str(), static_cast(value)); + } + + char valueKey[32] = {}; + snprintf(valueKey, sizeof(valueKey), "%lld", static_cast(value)); + if (!result.hasProperty(runtime, valueKey)) { + std::string reverseName = + uniqueAliases.size() > 1 ? uniqueAliases[1] : canonicalName; + result.setProperty(runtime, valueKey, makeString(runtime, reverseName)); + } + } + return result; +} + +Value constantToValue(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiSymbol& symbol) { + MDMetadataReader* metadata = bridge->metadata(); + if (metadata == nullptr || symbol.offset == MD_SECTION_OFFSET_NULL) { + return Value::undefined(); + } + + MDSectionOffset offset = symbol.offset + sizeof(MDSectionOffset); + auto evalKind = metadata->getVariableEvalKind(offset); + offset += sizeof(metagen::MDVariableEvalKind); + + switch (evalKind) { + case metagen::mdEvalInt64: + return static_cast(metadata->getInt64(offset)); + case metagen::mdEvalDouble: + return metadata->getDouble(offset); + case metagen::mdEvalString: { + if (isValidMetadataStringOffset(metadata, offset)) { + auto stringOffset = metadata->getOffset(offset); + return makeString(runtime, metadata->resolveString(stringOffset)); + } + + void* symbolPtr = dlsym(bridge->selfDl(), symbol.name.c_str()); + if (symbolPtr == nullptr) { + return Value::undefined(); + } + + NativeApiType stringObjectType; + stringObjectType.kind = metagen::mdTypeNSStringObject; + stringObjectType.ffiType = &ffi_type_pointer; + stringObjectType.supported = true; + return convertNativeReturnValue(runtime, bridge, stringObjectType, + symbolPtr); + } + case metagen::mdEvalNone: + break; + } + + MDSectionOffset typeOffset = offset; + NativeApiType type = parseMetadataEngineType(metadata, &typeOffset, bridge.get()); + if (unsupportedEngineType(type)) { + throw JSError( + runtime, "Native constant type is not supported by backend: " + + symbol.name); + } + + void* symbolPtr = dlsym(bridge->selfDl(), symbol.name.c_str()); + if (symbolPtr == nullptr) { + return Value::undefined(); + } + return convertNativeReturnValue(runtime, bridge, type, symbolPtr); +} + +void prepareEngineArgument(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiType& type, const Value& arg, + size_t index, NativeApiArgumentFrame& frame) { + ffi_type* ffiType = ffiTypeForEngineArgument(type); + size_t size = + ffiType != nullptr && ffiType->size > 0 ? ffiType->size : nativeSizeForType(type); + void* target = frame.storageAt(index, size); + convertEngineFfiArgument(runtime, bridge, type, arg, target, frame); +} + +void prepareEngineArguments(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiSignature& signature, + const Value* args, size_t count, + NativeApiArgumentFrame& frame) { + if (count != signature.argumentTypes.size()) { + throw JSError( + runtime, "Actual arguments count: \"" + std::to_string(count) + + "\". Expected: \"" + + std::to_string(signature.argumentTypes.size()) + "\"."); + } + + for (size_t i = 0; i < signature.argumentTypes.size(); i++) { + prepareEngineArgument(runtime, bridge, signature.argumentTypes[i], args[i], i, + frame); + } +} + +inline uint64_t dispatchIdForEngineSignature( + const NativeApiSignature& signature, SignatureCallKind kind) { + if (signature.signatureHash == 0) { + return 0; + } + return composeSignatureDispatchId(signature.signatureHash, kind, + signature.dispatchFlags); +} + +struct NativeApiPreparedCFunctionInvocation { + NativeApiSymbol symbol; + bool initialized = false; + void* function = nullptr; + NativeApiSignature signature; + CFunctionPreparedInvoker preparedInvoker = nullptr; +}; + +bool tryCallFastEngineCFunction( + Runtime& runtime, const std::shared_ptr& bridge, + void* function, const NativeApiSignature& signature, const Value* args, + size_t count, Value* result); + +Value callNativeFunctionPointer( + Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, void* pointer, bool block, const Value* args, + size_t count) { + if (pointer == nullptr) { + throw JSError(runtime, "Native function pointer is null."); + } + if (bridge == nullptr || bridge->metadata() == nullptr || + type.signatureOffset == MD_SECTION_OFFSET_NULL) { + throw JSError( + runtime, "Native function pointer metadata is unavailable."); + } + + auto signature = parseMetadataEngineSignature( + bridge->metadata(), type.signatureOffset, block ? 1 : 0, bridge.get()); + if (!signature || !signature->prepared || signature->variadic || + unsupportedEngineType(signature->returnType)) { + throw JSError( + runtime, + "Native function pointer signature is not supported by backend."); + } + + NativeApiArgumentFrame frame(signature->argumentTypes.size()); + prepareEngineArguments(runtime, bridge, *signature, args, count, frame); + + NativeApiPointerFrame values(signature->argumentTypes.size() + 1); + if (block) { + values.set(0, &pointer); + for (size_t i = 0; i < signature->argumentTypes.size(); i++) { + values.set(i + 1, frame.values()[i]); + } + } + + void* callable = pointer; + if (block) { + auto literal = static_cast(pointer); + if (literal == nullptr || literal->invoke == nullptr) { + throw JSError(runtime, "Native block invoke pointer is null."); + } + callable = literal->invoke; + } + + if (!block) { + Value fastResult; + if (tryCallFastEngineCFunction(runtime, bridge, callable, *signature, args, + count, &fastResult)) { + return fastResult; + } + } + + NativeApiReturnStorage returnStorage( + nativeSizeForType(signature->returnType)); + BlockPreparedInvoker blockPreparedInvoker = nullptr; + CFunctionPreparedInvoker functionPreparedInvoker = nullptr; + if (block) { + blockPreparedInvoker = lookupBlockPreparedInvoker(dispatchIdForEngineSignature( + *signature, SignatureCallKind::BlockInvoke)); + } else { + functionPreparedInvoker = lookupCFunctionPreparedInvoker( + dispatchIdForEngineSignature(*signature, SignatureCallKind::CFunction)); + } + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + if (block) { + if (blockPreparedInvoker != nullptr) { + blockPreparedInvoker(callable, values.data(), returnStorage.data()); + return; + } + } else { + if (functionPreparedInvoker != nullptr) { + functionPreparedInvoker(callable, frame.values(), returnStorage.data()); + return; + } + } + ffi_call(&signature->cif, FFI_FN(callable), returnStorage.data(), + block ? values.data() : frame.values()); + }); + + return convertNativeReturnValue(runtime, bridge, signature->returnType, + returnStorage.data()); +} + +Value wrapNativeFunctionPointer(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiType& type, void* pointer, + bool block) { + const char* functionName = block ? "NativeApiBlock" : "NativeApiFunctionPointer"; + auto function = Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, functionName), 0, + [bridge, type, pointer, block](Runtime& runtime, const Value&, + const Value* args, size_t count) -> Value { + return callNativeFunctionPointer(runtime, bridge, type, pointer, block, + args, count); + }); + function.setProperty(runtime, "kind", + makeString(runtime, block ? "block" : "functionPointer")); + function.setProperty( + runtime, "__nativeApiPointerObject", + createPointer(runtime, bridge, pointer)); + function.setProperty( + runtime, "__nativeApiPointer", + static_cast(reinterpret_cast(pointer))); + function.setProperty( + runtime, "nativeAddress", + static_cast(reinterpret_cast(pointer))); + function.setProperty(runtime, "sizeof", + static_cast(sizeof(void*))); + function.setProperty( + runtime, "toString", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "toString"), 0, + [pointer, block](Runtime& runtime, const Value&, const Value*, + size_t) -> Value { + char address[32] = {}; + snprintf(address, sizeof(address), "%p", pointer); + return makeString(runtime, + std::string("[NativeApi ") + + (block ? "Block " : "FunctionPointer ") + + address + "]"); + })); + return function; +} + +Value callCFunction(Runtime& runtime, + const std::shared_ptr& bridge, + const std::shared_ptr& prepared, + const Value* args, + size_t count) { + if (prepared == nullptr) { + throw JSError(runtime, "Native function state is unavailable."); + } + NativeApiRoundTripCacheFrameGuard roundTripFrame(bridge); + + MDMetadataReader* metadata = bridge->metadata(); + if (metadata == nullptr) { + throw JSError(runtime, "Native metadata is not loaded."); + } + + if (!prepared->initialized) { + void* fnptr = dlsym(bridge->selfDl(), prepared->symbol.name.c_str()); + if (fnptr == nullptr) { + throw JSError(runtime, + "Native function is not available: " + + prepared->symbol.name); + } + + MDSectionOffset signatureOffset = + metadata->signaturesOffset + + metadata->getOffset(prepared->symbol.offset + sizeof(MDSectionOffset)); + auto signature = parseMetadataEngineSignature( + metadata, signatureOffset, 0, bridge.get(), + (metadata->getFunctionFlag( + prepared->symbol.offset + sizeof(MDSectionOffset) * 2) & + metagen::mdFunctionReturnOwned) != 0); + if (!signature || !signature->prepared || signature->variadic || + unsupportedEngineType(signature->returnType)) { + throw JSError( + runtime, "Native function signature is not supported by backend: " + + prepared->symbol.name); + } + + prepared->function = fnptr; + prepared->signature = std::move(*signature); + prepared->preparedInvoker = lookupCFunctionPreparedInvoker( + dispatchIdForEngineSignature(prepared->signature, + SignatureCallKind::CFunction)); + prepared->initialized = true; + } + + NativeApiSignature& signature = prepared->signature; + Value fastResult; + if (tryCallFastEngineCFunction(runtime, bridge, prepared->function, signature, + args, count, &fastResult)) { + return fastResult; + } + + NativeApiArgumentFrame frame(signature.argumentTypes.size()); + prepareEngineArguments(runtime, bridge, signature, args, count, frame); + + if (prepared->symbol.name == "NSApplicationMain" || + prepared->symbol.name == "UIApplicationMain") { + runtime.drainMicrotasks(); + } + + NativeApiReturnStorage returnStorage( + nativeSizeForType(signature.returnType)); + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + if (prepared->preparedInvoker != nullptr) { + prepared->preparedInvoker(prepared->function, frame.values(), + returnStorage.data()); + } else { + ffi_call(&signature.cif, FFI_FN(prepared->function), returnStorage.data(), + frame.values()); + } + }); + + NativeApiType returnType = signature.returnType; + if (prepared->symbol.name == "CFBagContainsValue" && + (returnType.kind == metagen::mdTypeChar || + returnType.kind == metagen::mdTypeUChar || + returnType.kind == metagen::mdTypeUInt8)) { + return *returnStorage.bytes() != 0; + } + return convertNativeReturnValue(runtime, bridge, returnType, + returnStorage.data()); +} + +Value callCFunction(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiSymbol& symbol, const Value* args, + size_t count) { + auto prepared = std::make_shared(); + prepared->symbol = symbol; + return callCFunction(runtime, bridge, prepared, args, count); +} + +bool signatureSupportedForEngineInvocation( + const std::optional& signature) { + if (!signature || !signature->prepared || signature->variadic || + unsupportedEngineType(signature->returnType)) { + return false; + } + for (const auto& argType : signature->argumentTypes) { + if (unsupportedEngineType(argType)) { + return false; + } + } + return true; +} + +bool signatureSupportedForEngineInvocation( + const NativeApiSignature& signature) { + if (!signature.prepared || signature.variadic || + unsupportedEngineType(signature.returnType)) { + return false; + } + for (const auto& argType : signature.argumentTypes) { + if (unsupportedEngineType(argType)) { + return false; + } + } + return true; +} + +struct NativeApiPreparedObjCInvocation { + SEL selector = nullptr; + Class receiverClass = Nil; + std::string selectorName; + NativeApiSignature signature; + ObjCPreparedInvoker preparedInvoker = nullptr; + void* engineInvoker = nullptr; // Engine-neutral GSD invoker (ObjCGsdInvoker) + bool isNSErrorOutMethod = false; // Cached: avoids per-call selector scan. + bool isInitMethod = false; // Cached: avoids per-call "init" rfind. + bool gsdEngineCallable = false; + uint8_t gsdEngineArgumentCount = 0; + bool fastEngineCallable = false; + uint8_t fastEngineArgumentCount = 0; + uint8_t fastEngineFirstArgKind = 0; + uint8_t fastEngineSecondArgKind = 0; +}; + +bool preparedObjCInvocationIsInit( + const NativeApiPreparedObjCInvocation& prepared) { + return prepared.isInitMethod; +} + +bool isFastEngineObjectType(const NativeApiType& type) { + switch (type.kind) { + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: + return true; + default: + return false; + } +} + +bool isFastEngineSignedIntegerType(const NativeApiType& type) { + switch (type.kind) { + case metagen::mdTypeChar: + case metagen::mdTypeSShort: + case metagen::mdTypeSInt: + case metagen::mdTypeSLong: + case metagen::mdTypeSInt64: + return true; + default: + return false; + } +} + +bool isFastEngineUnsignedIntegerType(const NativeApiType& type) { + switch (type.kind) { + case metagen::mdTypeUChar: + case metagen::mdTypeUInt8: + case metagen::mdTypeUInt: + case metagen::mdTypeULong: + case metagen::mdTypeUInt64: + return true; + default: + return false; + } +} + +enum class NativeApiFastEngineArgKind : uint8_t { + Bool, + SignedInteger, + UnsignedInteger, + Float, + Double, + Object, + Class, + Selector, +}; + +std::optional fastEngineArgKind( + const NativeApiType& type) { + if (isFastEngineObjectType(type)) { + return NativeApiFastEngineArgKind::Object; + } + if (isFastEngineSignedIntegerType(type)) { + return NativeApiFastEngineArgKind::SignedInteger; + } + if (isFastEngineUnsignedIntegerType(type)) { + return NativeApiFastEngineArgKind::UnsignedInteger; + } + switch (type.kind) { + case metagen::mdTypeBool: + return NativeApiFastEngineArgKind::Bool; + case metagen::mdTypeFloat: + return NativeApiFastEngineArgKind::Float; + case metagen::mdTypeDouble: + return NativeApiFastEngineArgKind::Double; + case metagen::mdTypeClass: + return NativeApiFastEngineArgKind::Class; + case metagen::mdTypeSelector: + return NativeApiFastEngineArgKind::Selector; + default: + return std::nullopt; + } +} + +bool readFastEngineBoolArgument(Runtime& runtime, const Value& value, + BOOL* result) { + if (result == nullptr || !value.isBool()) { + return false; + } + *result = value.getBool() ? YES : NO; + return true; +} + +bool readFastEngineSignedIntegerArgument(Runtime& runtime, const Value& value, + NSInteger* result) { + if (result == nullptr) { + return false; + } + if (value.isNumber()) { + *result = static_cast(value.getNumber()); + return true; + } + return false; +} + +bool readFastEngineUnsignedIntegerArgument(Runtime& runtime, const Value& value, + NSUInteger* result) { + if (result == nullptr) { + return false; + } + if (value.isNumber()) { + *result = static_cast(value.getNumber()); + return true; + } + return false; +} + +bool readFastEngineFloatArgument(Runtime&, const Value& value, float* result) { + if (result == nullptr || !value.isNumber()) { + return false; + } + *result = static_cast(value.getNumber()); + return true; +} + +bool readFastEngineDoubleArgument(Runtime&, const Value& value, double* result) { + if (result == nullptr || !value.isNumber()) { + return false; + } + *result = value.getNumber(); + return true; +} + +bool readFastEngineObjectArgument( + Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, const Value& value, + NativeApiArgumentFrame& frame, id* result) { + if (result == nullptr) { + return false; + } + *result = objectFromEngineValue( + runtime, bridge, value, frame, + type.kind == metagen::mdTypeNSMutableStringObject); + if (valueIsNativeObjectHostObject(runtime, value)) { + frame.retainObject(*result); + } + return true; +} + +class NativeApiScopedObjCObjectRetain { + public: + explicit NativeApiScopedObjCObjectRetain(id object) : object_(object) { + if (object_ != nil) { + [object_ retain]; + } + } + + ~NativeApiScopedObjCObjectRetain() { + if (object_ != nil) { + [object_ release]; + } + } + + private: + id object_ = nil; +}; + +bool readFastEngineClassArgument(Runtime& runtime, const Value& value, + Class* result) { + if (result == nullptr) { + return false; + } + *result = classFromEngineValue(runtime, value); + return *result != Nil; +} + +bool readFastEngineSelectorArgument(Runtime& runtime, const Value& value, + SEL* result) { + if (result == nullptr) { + return false; + } + if (value.isNull() || value.isUndefined()) { + *result = nullptr; + return true; + } + if (!value.isString()) { + return false; + } + std::string selectorName = value.asString(runtime).utf8(runtime); + *result = sel_registerName(selectorName.c_str()); + return true; +} + +template +Value callFastEngineCFunctionWithReturn( + Runtime& runtime, const std::shared_ptr& bridge, + void* function, NativeApiType returnType, Args... nativeArgs) { + auto finalizeObjectReturn = [&](id object) -> Value { + return convertNativeReturnValue(runtime, bridge, returnType, &object); + }; + + switch (returnType.kind) { + case metagen::mdTypeVoid: { + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = void (*)(Args...); + reinterpret_cast(function)(nativeArgs...); + }); + return Value::undefined(); + } + case metagen::mdTypeBool: { + BOOL nativeResult = NO; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = BOOL (*)(Args...); + nativeResult = reinterpret_cast(function)(nativeArgs...); + }); + uint8_t storage = nativeResult ? 1 : 0; + return convertNativeReturnValue(runtime, bridge, returnType, &storage); + } + case metagen::mdTypeFloat: { + float nativeResult = 0; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = float (*)(Args...); + nativeResult = reinterpret_cast(function)(nativeArgs...); + }); + return convertNativeReturnValue(runtime, bridge, returnType, + &nativeResult); + } + case metagen::mdTypeDouble: { + double nativeResult = 0; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = double (*)(Args...); + nativeResult = reinterpret_cast(function)(nativeArgs...); + }); + return convertNativeReturnValue(runtime, bridge, returnType, + &nativeResult); + } + default: + break; + } + + if (isFastEngineObjectType(returnType) || + returnType.kind == metagen::mdTypeClass) { + id nativeResult = nil; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = id (*)(Args...); + nativeResult = reinterpret_cast(function)(nativeArgs...); + }); + return finalizeObjectReturn(nativeResult); + } + + if (returnType.kind == metagen::mdTypeSelector) { + SEL nativeResult = nullptr; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = SEL (*)(Args...); + nativeResult = reinterpret_cast(function)(nativeArgs...); + }); + return convertNativeReturnValue(runtime, bridge, returnType, + &nativeResult); + } + + if (isFastEngineSignedIntegerType(returnType)) { + int64_t nativeResult = 0; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = int64_t (*)(Args...); + nativeResult = reinterpret_cast(function)(nativeArgs...); + }); + return convertNativeReturnValue(runtime, bridge, returnType, + &nativeResult); + } + + if (isFastEngineUnsignedIntegerType(returnType)) { + uint64_t nativeResult = 0; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = uint64_t (*)(Args...); + nativeResult = reinterpret_cast(function)(nativeArgs...); + }); + return convertNativeReturnValue(runtime, bridge, returnType, + &nativeResult); + } + + throw JSError(runtime, "C function return type is not engine fast-callable."); +} + +bool isFastEngineCallableReturnType(const NativeApiType& returnType) { + return isFastEngineObjectType(returnType) || + returnType.kind == metagen::mdTypeVoid || + returnType.kind == metagen::mdTypeBool || + returnType.kind == metagen::mdTypeFloat || + returnType.kind == metagen::mdTypeDouble || + returnType.kind == metagen::mdTypeClass || + returnType.kind == metagen::mdTypeSelector || + isFastEngineSignedIntegerType(returnType) || + isFastEngineUnsignedIntegerType(returnType); +} + +bool tryCallFastEngineCFunction( + Runtime& runtime, const std::shared_ptr& bridge, + void* function, const NativeApiSignature& signature, const Value* args, + size_t count, Value* result) { + if (result == nullptr || function == nullptr || signature.variadic || + count != signature.argumentTypes.size() || count > 2 || + unsupportedEngineType(signature.returnType) || + !isFastEngineCallableReturnType(signature.returnType)) { + return false; + } + + std::optional firstArgKind; + std::optional secondArgKind; + if (count > 0) { + firstArgKind = fastEngineArgKind(signature.argumentTypes[0]); + if (!firstArgKind) { + return false; + } + } + if (count > 1) { + secondArgKind = fastEngineArgKind(signature.argumentTypes[1]); + if (!secondArgKind) { + return false; + } + } + + if (count == 0) { + *result = callFastEngineCFunctionWithReturn( + runtime, bridge, function, signature.returnType); + return true; + } + + NativeApiArgumentFrame frame(count); + auto callOne = [&](auto nativeArg0) -> Value { + return callFastEngineCFunctionWithReturn(runtime, bridge, function, + signature.returnType, nativeArg0); + }; + auto callTwo = [&](auto nativeArg0, auto nativeArg1) -> Value { + return callFastEngineCFunctionWithReturn(runtime, bridge, function, + signature.returnType, nativeArg0, + nativeArg1); + }; + + auto callWithSecondArg = [&](auto nativeArg0) -> bool { + switch (*secondArgKind) { + case NativeApiFastEngineArgKind::Bool: { + BOOL arg1 = NO; + if (!readFastEngineBoolArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::SignedInteger: { + NSInteger arg1 = 0; + if (!readFastEngineSignedIntegerArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::UnsignedInteger: { + NSUInteger arg1 = 0; + if (!readFastEngineUnsignedIntegerArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::Float: { + float arg1 = 0; + if (!readFastEngineFloatArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::Double: { + double arg1 = 0; + if (!readFastEngineDoubleArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::Object: { + id arg1 = nil; + if (!readFastEngineObjectArgument( + runtime, bridge, signature.argumentTypes[1], args[1], frame, + &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::Class: { + Class arg1 = Nil; + if (!readFastEngineClassArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::Selector: { + SEL arg1 = nullptr; + if (!readFastEngineSelectorArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + } + return false; + }; + + switch (*firstArgKind) { + case NativeApiFastEngineArgKind::Bool: { + BOOL arg0 = NO; + if (!readFastEngineBoolArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::SignedInteger: { + NSInteger arg0 = 0; + if (!readFastEngineSignedIntegerArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::UnsignedInteger: { + NSUInteger arg0 = 0; + if (!readFastEngineUnsignedIntegerArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::Float: { + float arg0 = 0; + if (!readFastEngineFloatArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::Double: { + double arg0 = 0; + if (!readFastEngineDoubleArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::Object: { + id arg0 = nil; + if (!readFastEngineObjectArgument(runtime, bridge, + signature.argumentTypes[0], args[0], + frame, &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::Class: { + Class arg0 = Nil; + if (!readFastEngineClassArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::Selector: { + SEL arg0 = nullptr; + if (!readFastEngineSelectorArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + } + + return false; +} + +template +Value callFastEngineObjCWithReturn( + Runtime& runtime, const std::shared_ptr& bridge, + id receiver, SEL selector, NativeApiType returnType, + const std::string& selectorName, Args... nativeArgs) { + auto finalizeObjectReturn = [&](id object) -> Value { + NativeApiType effectiveReturnType = returnType; + if ((selectorName == "valueForKey:" || selectorName == "valueForKeyPath:") && + isObjectiveCObjectType(effectiveReturnType)) { + effectiveReturnType.kind = metagen::mdTypeAnyObject; + } + if (startsWith(selectorName, "init") && + isObjectiveCObjectType(effectiveReturnType)) { + effectiveReturnType.kind = metagen::mdTypeInstanceObject; + } + return convertNativeReturnValue(runtime, bridge, effectiveReturnType, + &object); + }; + + switch (returnType.kind) { + case metagen::mdTypeVoid: { + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = void (*)(id, SEL, Args...); + reinterpret_cast(objc_msgSend)(receiver, selector, nativeArgs...); + }); + return Value::undefined(); + } + case metagen::mdTypeBool: { + BOOL nativeResult = NO; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = BOOL (*)(id, SEL, Args...); + nativeResult = + reinterpret_cast(objc_msgSend)(receiver, selector, nativeArgs...); + }); + uint8_t storage = nativeResult ? 1 : 0; + return convertNativeReturnValue(runtime, bridge, returnType, &storage); + } + case metagen::mdTypeFloat: { + float nativeResult = 0; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = float (*)(id, SEL, Args...); + nativeResult = + reinterpret_cast(objc_msgSend)(receiver, selector, nativeArgs...); + }); + return convertNativeReturnValue(runtime, bridge, returnType, + &nativeResult); + } + case metagen::mdTypeDouble: { + double nativeResult = 0; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = double (*)(id, SEL, Args...); + nativeResult = + reinterpret_cast(objc_msgSend)(receiver, selector, nativeArgs...); + }); + return convertNativeReturnValue(runtime, bridge, returnType, + &nativeResult); + } + default: + break; + } + + if (isFastEngineObjectType(returnType) || returnType.kind == metagen::mdTypeClass) { + id nativeResult = nil; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = id (*)(id, SEL, Args...); + nativeResult = + reinterpret_cast(objc_msgSend)(receiver, selector, nativeArgs...); + }); + return finalizeObjectReturn(nativeResult); + } + + if (isFastEngineSignedIntegerType(returnType)) { + int64_t nativeResult = 0; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = int64_t (*)(id, SEL, Args...); + nativeResult = + reinterpret_cast(objc_msgSend)(receiver, selector, nativeArgs...); + }); + return convertNativeReturnValue(runtime, bridge, returnType, + &nativeResult); + } + + if (isFastEngineUnsignedIntegerType(returnType)) { + uint64_t nativeResult = 0; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + using Fn = uint64_t (*)(id, SEL, Args...); + nativeResult = + reinterpret_cast(objc_msgSend)(receiver, selector, nativeArgs...); + }); + return convertNativeReturnValue(runtime, bridge, returnType, + &nativeResult); + } + + throw JSError(runtime, "Objective-C return type is not engine fast-callable."); +} + +template +Value callFastEngineObjC1( + Runtime& runtime, const std::shared_ptr& bridge, + id receiver, SEL selector, const NativeApiType& returnType, + const std::string& selectorName, A0 arg0) { + return callFastEngineObjCWithReturn(runtime, bridge, receiver, selector, + returnType, selectorName, arg0); +} + +template +Value callFastEngineObjC2( + Runtime& runtime, const std::shared_ptr& bridge, + id receiver, SEL selector, const NativeApiType& returnType, + const std::string& selectorName, A0 arg0, A1 arg1) { + return callFastEngineObjCWithReturn(runtime, bridge, receiver, selector, + returnType, selectorName, arg0, arg1); +} + +bool tryCallFastEngineObjCSelector( + Runtime& runtime, const std::shared_ptr& bridge, + id receiver, const NativeApiPreparedObjCInvocation& prepared, + const Value* args, size_t count, Class dispatchSuperClass, Value* result) { + if (result == nullptr || receiver == nil || dispatchSuperClass != Nil) { + return false; + } + + const NativeApiSignature& signature = prepared.signature; + if (!prepared.fastEngineCallable || + count != prepared.fastEngineArgumentCount) { + return false; + } + + NativeApiFastEngineArgKind firstArgKind = + static_cast(prepared.fastEngineFirstArgKind); + NativeApiFastEngineArgKind secondArgKind = + static_cast(prepared.fastEngineSecondArgKind); + + SEL selector = prepared.selector; + if (count == 0) { + *result = callFastEngineObjCWithReturn( + runtime, bridge, receiver, selector, signature.returnType, + prepared.selectorName); + return true; + } + + NativeApiArgumentFrame frame(count); + auto callOne = [&](auto nativeArg0) -> Value { + return callFastEngineObjC1(runtime, bridge, receiver, selector, + signature.returnType, prepared.selectorName, + nativeArg0); + }; + auto callTwo = [&](auto nativeArg0, auto nativeArg1) -> Value { + return callFastEngineObjC2(runtime, bridge, receiver, selector, + signature.returnType, prepared.selectorName, + nativeArg0, nativeArg1); + }; + auto callWithSecondArg = [&](auto nativeArg0) -> bool { + switch (secondArgKind) { + case NativeApiFastEngineArgKind::Bool: { + BOOL arg1 = NO; + if (!readFastEngineBoolArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::SignedInteger: { + NSInteger arg1 = 0; + if (!readFastEngineSignedIntegerArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::UnsignedInteger: { + NSUInteger arg1 = 0; + if (!readFastEngineUnsignedIntegerArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::Float: { + float arg1 = 0; + if (!readFastEngineFloatArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::Double: { + double arg1 = 0; + if (!readFastEngineDoubleArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::Object: { + id arg1 = nil; + if (!readFastEngineObjectArgument( + runtime, bridge, signature.argumentTypes[1], args[1], frame, + &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::Class: { + Class arg1 = Nil; + if (!readFastEngineClassArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + case NativeApiFastEngineArgKind::Selector: { + SEL arg1 = nullptr; + if (!readFastEngineSelectorArgument(runtime, args[1], &arg1)) { + return false; + } + *result = callTwo(nativeArg0, arg1); + return true; + } + } + return false; + }; + + switch (firstArgKind) { + case NativeApiFastEngineArgKind::Bool: { + BOOL arg0 = NO; + if (!readFastEngineBoolArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::SignedInteger: { + NSInteger arg0 = 0; + if (!readFastEngineSignedIntegerArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::UnsignedInteger: { + NSUInteger arg0 = 0; + if (!readFastEngineUnsignedIntegerArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::Float: { + float arg0 = 0; + if (!readFastEngineFloatArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::Double: { + double arg0 = 0; + if (!readFastEngineDoubleArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::Object: { + id arg0 = nil; + if (!readFastEngineObjectArgument(runtime, bridge, signature.argumentTypes[0], + args[0], frame, &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::Class: { + Class arg0 = Nil; + if (!readFastEngineClassArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + case NativeApiFastEngineArgKind::Selector: { + SEL arg0 = nullptr; + if (!readFastEngineSelectorArgument(runtime, args[0], &arg0)) { + return false; + } + if (count == 1) { + *result = callOne(arg0); + return true; + } + return callWithSecondArg(arg0); + } + } + + return false; +} + +bool isFastEngineObjCReturnType(const NativeApiType& returnType) { + return !unsupportedEngineType(returnType) && + (isFastEngineObjectType(returnType) || + returnType.kind == metagen::mdTypeVoid || + returnType.kind == metagen::mdTypeBool || + returnType.kind == metagen::mdTypeFloat || + returnType.kind == metagen::mdTypeDouble || + returnType.kind == metagen::mdTypeClass || + isFastEngineSignedIntegerType(returnType) || + isFastEngineUnsignedIntegerType(returnType)); +} + +void configureFastEngineObjCInvocation( + NativeApiPreparedObjCInvocation& prepared) { + prepared.fastEngineCallable = false; + prepared.fastEngineArgumentCount = 0; + prepared.fastEngineFirstArgKind = 0; + prepared.fastEngineSecondArgKind = 0; + + const NativeApiSignature& signature = prepared.signature; + if (signature.variadic || prepared.isNSErrorOutMethod || + signature.argumentTypes.size() > 2 || + !isFastEngineObjCReturnType(signature.returnType)) { + return; + } + + if (!signature.argumentTypes.empty()) { + std::optional firstArgKind = + fastEngineArgKind(signature.argumentTypes[0]); + if (!firstArgKind) { + return; + } + prepared.fastEngineFirstArgKind = static_cast(*firstArgKind); + } + if (signature.argumentTypes.size() > 1) { + std::optional secondArgKind = + fastEngineArgKind(signature.argumentTypes[1]); + if (!secondArgKind) { + return; + } + prepared.fastEngineSecondArgKind = static_cast(*secondArgKind); + } + + prepared.fastEngineArgumentCount = + static_cast(signature.argumentTypes.size()); + prepared.fastEngineCallable = true; +} + +void configureGeneratedEngineObjCInvocation( + NativeApiPreparedObjCInvocation& prepared) { + prepared.gsdEngineCallable = false; + prepared.gsdEngineArgumentCount = 0; + + const NativeApiSignature& signature = prepared.signature; + if (prepared.engineInvoker == nullptr || signature.variadic || + prepared.isNSErrorOutMethod || signature.argumentTypes.size() > 255) { + return; + } + + prepared.gsdEngineArgumentCount = + static_cast(signature.argumentTypes.size()); + prepared.gsdEngineCallable = true; +} + +std::shared_ptr +prepareNativeApiObjCInvocation( + Runtime& runtime, const std::shared_ptr& bridge, + Class lookupClass, bool receiverIsClass, const std::string& selectorName, + const NativeApiMember* member) { + if (lookupClass == Nil) { + throw JSError(runtime, + "Objective-C class is not available for selector: " + + selectorName); + } + + SEL selector = sel_registerName(selectorName.c_str()); + Method method = receiverIsClass ? class_getClassMethod(lookupClass, selector) + : class_getInstanceMethod(lookupClass, selector); + if (method == nullptr) { + throw JSError(runtime, + "Objective-C selector is not available: " + selectorName); + } + + std::optional signature; + std::optional runtimeSignature; + if (member != nullptr && + member->signatureOffset != MD_SECTION_OFFSET_NULL && + member->signatureOffset != 0) { + signature = parseMetadataEngineSignature( + bridge->metadata(), member->signatureOffset, 2, bridge.get(), + (member->flags & metagen::mdMemberReturnOwned) != 0); + } + if (method != nullptr) { + runtimeSignature = parseObjCMethodEngineSignature(method, bridge.get()); + } + if (signatureSupportedForEngineInvocation(signature) && + signatureSupportedForEngineInvocation(runtimeSignature)) { + reconcileObjCMethodRuntimeSignature(&*signature, *runtimeSignature); + } + if (!signatureSupportedForEngineInvocation(signature) && runtimeSignature) { + signature = std::move(runtimeSignature); + } + + if (!signatureSupportedForEngineInvocation(signature)) { + throw JSError( + runtime, "Objective-C signature is not supported by backend: " + + selectorName); + } + signature->selectorName = selectorName; + + auto prepared = std::make_shared(); + prepared->selector = selector; + prepared->receiverClass = receiverIsClass ? lookupClass : Nil; + prepared->selectorName = selectorName; + prepared->signature = std::move(*signature); + prepared->preparedInvoker = lookupObjCPreparedInvoker( + dispatchIdForEngineSignature(prepared->signature, + SignatureCallKind::ObjCMethod)); + prepared->engineInvoker = lookupGeneratedEngineObjCGsdInvoker( + dispatchIdForEngineSignature(prepared->signature, + SignatureCallKind::ObjCMethod)); + prepared->isNSErrorOutMethod = + isNSErrorOutEngineMethodSignature(prepared->signature); + prepared->isInitMethod = prepared->selectorName.rfind("init", 0) == 0; + configureGeneratedEngineObjCInvocation(*prepared); + configureFastEngineObjCInvocation(*prepared); + return prepared; +} + +Value callPreparedObjCSelector( + Runtime& runtime, const std::shared_ptr& bridge, + id receiver, bool receiverIsClass, + const NativeApiPreparedObjCInvocation& prepared, const Value* args, + size_t count, Class dispatchSuperClass) { + if (receiver == nil) { + throw JSError(runtime, + "Cannot send Objective-C selector to nil."); + } + NativeApiRoundTripCacheFrameGuard roundTripFrame(bridge); + + const NativeApiSignature& signature = prepared.signature; + Value fastResult; + if (tryCallGeneratedEngineObjCSelector(runtime, bridge, receiver, prepared, + args, count, dispatchSuperClass, + &fastResult)) { + return fastResult; + } + if (tryCallFastEngineObjCSelector(runtime, bridge, receiver, prepared, args, + count, dispatchSuperClass, &fastResult)) { + return fastResult; + } + + NativeApiArgumentFrame frame(signature.argumentTypes.size()); + frame.retainObject(receiver); + const bool isNSErrorOutMethod = prepared.isNSErrorOutMethod; + if (isNSErrorOutMethod) { + size_t expected = signature.argumentTypes.size(); + if (count > expected || count + 1 < expected) { + throw JSError( + runtime, "Actual arguments count: \"" + std::to_string(count) + + "\". Expected: \"" + std::to_string(expected) + "\"."); + } + } + + const bool hasImplicitNSErrorOutArg = + isNSErrorOutMethod && count + 1 == signature.argumentTypes.size(); + NSError* implicitNSError = nil; + if (hasImplicitNSErrorOutArg) { + for (size_t i = 0; i < count; i++) { + prepareEngineArgument(runtime, bridge, signature.argumentTypes[i], args[i], + i, frame); + } + + size_t outArgIndex = signature.argumentTypes.size() - 1; + void* target = frame.storageAt(outArgIndex, sizeof(NSError**)); + NSError** implicitNSErrorOutArg = &implicitNSError; + *static_cast(target) = implicitNSErrorOutArg; + } else { + prepareEngineArguments(runtime, bridge, signature, args, count, frame); + } + + NativeApiPointerFrame values(signature.argumentTypes.size() + 2); + size_t valueIndex = 0; + struct objc_super superReceiver = {receiver, dispatchSuperClass}; + struct objc_super* superReceiverPtr = &superReceiver; + if (dispatchSuperClass != Nil) { + values.set(valueIndex++, &superReceiverPtr); + } else { + values.set(valueIndex++, &receiver); + } + values.set(valueIndex++, const_cast(&prepared.selector)); + for (size_t i = 0; i < signature.argumentTypes.size(); i++) { + values.set(valueIndex++, frame.values()[i]); + } + + NativeApiReturnStorage returnStorage( + nativeSizeForType(signature.returnType)); + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + if (prepared.preparedInvoker != nullptr && dispatchSuperClass == Nil) { + prepared.preparedInvoker(reinterpret_cast(objc_msgSend), + values.data(), returnStorage.data()); + } else { +#if defined(__x86_64__) + bool isStret = signature.returnType.ffiType->size > 16 && + signature.returnType.ffiType->type == FFI_TYPE_STRUCT; + void (*target)(void) = + dispatchSuperClass != Nil + ? (isStret ? FFI_FN(objc_msgSendSuper_stret) + : FFI_FN(objc_msgSendSuper)) + : (isStret ? FFI_FN(objc_msgSend_stret) : FFI_FN(objc_msgSend)); + ffi_call(const_cast(&signature.cif), target, + returnStorage.data(), values.data()); +#else + ffi_call(const_cast(&signature.cif), + dispatchSuperClass != Nil ? FFI_FN(objc_msgSendSuper) + : FFI_FN(objc_msgSend), + returnStorage.data(), values.data()); +#endif + } + }); + + NativeApiType returnType = signature.returnType; + if ((prepared.selectorName == "valueForKey:" || + prepared.selectorName == "valueForKeyPath:") && + isObjectiveCObjectType(returnType)) { + returnType.kind = metagen::mdTypeAnyObject; + } + if (prepared.isInitMethod && + isObjectiveCObjectType(returnType)) { + returnType.kind = metagen::mdTypeInstanceObject; + } + if (hasImplicitNSErrorOutArg && implicitNSError != nil) { + const char* errorMessage = [[implicitNSError description] UTF8String]; + throw JSError( + runtime, errorMessage != nullptr ? errorMessage : "Unknown NSError"); + } + return convertNativeReturnValue(runtime, bridge, returnType, + returnStorage.data()); +} + +Value callObjCSelector(Runtime& runtime, + const std::shared_ptr& bridge, + id receiver, bool receiverIsClass, + const std::string& selectorName, + const NativeApiMember* member, + const Value* args, size_t count, + Class dispatchSuperClass) { + if (receiver == nil) { + throw JSError(runtime, + "Cannot send Objective-C selector to nil."); + } + NativeApiRoundTripCacheFrameGuard roundTripFrame(bridge); + + SEL selector = sel_registerName(selectorName.c_str()); + Class receiverClass = + receiverIsClass ? static_cast(receiver) : object_getClass(receiver); + Class lookupClass = dispatchSuperClass != Nil ? dispatchSuperClass : receiverClass; + Method method = receiverIsClass ? class_getClassMethod(lookupClass, selector) + : class_getInstanceMethod(lookupClass, selector); + if (method == nullptr && + (dispatchSuperClass != Nil || ![receiver respondsToSelector:selector])) { + throw JSError(runtime, + "Objective-C selector is not available: " + + selectorName); + } + + std::optional signature; + std::optional runtimeSignature; + if (member != nullptr && + member->signatureOffset != MD_SECTION_OFFSET_NULL && + member->signatureOffset != 0) { + signature = parseMetadataEngineSignature( + bridge->metadata(), member->signatureOffset, 2, bridge.get(), + (member->flags & metagen::mdMemberReturnOwned) != 0); + } + if (method != nullptr) { + runtimeSignature = parseObjCMethodEngineSignature(method, bridge.get()); + } + if (signatureSupportedForEngineInvocation(signature) && + signatureSupportedForEngineInvocation(runtimeSignature)) { + reconcileObjCMethodRuntimeSignature(&*signature, *runtimeSignature); + } + if (!signatureSupportedForEngineInvocation(signature) && runtimeSignature) { + signature = std::move(runtimeSignature); + } + + if (!signatureSupportedForEngineInvocation(signature)) { + throw JSError( + runtime, "Objective-C signature is not supported by backend: " + + selectorName); + } + signature->selectorName = selectorName; + + NativeApiPreparedObjCInvocation engineInvocation; + engineInvocation.selector = selector; + engineInvocation.selectorName = selectorName; + engineInvocation.signature = *signature; + engineInvocation.engineInvoker = lookupGeneratedEngineObjCGsdInvoker( + dispatchIdForEngineSignature(*signature, SignatureCallKind::ObjCMethod)); + engineInvocation.isNSErrorOutMethod = + isNSErrorOutEngineMethodSignature(*signature); + engineInvocation.isInitMethod = selectorName.rfind("init", 0) == 0; + configureGeneratedEngineObjCInvocation(engineInvocation); + configureFastEngineObjCInvocation(engineInvocation); + Value fastResult; + if (tryCallGeneratedEngineObjCSelector(runtime, bridge, receiver, + engineInvocation, args, count, + dispatchSuperClass, &fastResult)) { + return fastResult; + } + if (tryCallFastEngineObjCSelector(runtime, bridge, receiver, + engineInvocation, args, count, + dispatchSuperClass, &fastResult)) { + return fastResult; + } + + NativeApiArgumentFrame frame(signature->argumentTypes.size()); + const bool isNSErrorOutMethod = engineInvocation.isNSErrorOutMethod; + if (isNSErrorOutMethod) { + size_t expected = signature->argumentTypes.size(); + if (count > expected || count + 1 < expected) { + throw JSError( + runtime, "Actual arguments count: \"" + std::to_string(count) + + "\". Expected: \"" + std::to_string(expected) + "\"."); + } + } + + const bool hasImplicitNSErrorOutArg = + isNSErrorOutMethod && count + 1 == signature->argumentTypes.size(); + NSError* implicitNSError = nil; + if (hasImplicitNSErrorOutArg) { + for (size_t i = 0; i < count; i++) { + prepareEngineArgument(runtime, bridge, signature->argumentTypes[i], args[i], i, + frame); + } + + size_t outArgIndex = signature->argumentTypes.size() - 1; + void* target = frame.storageAt(outArgIndex, sizeof(NSError**)); + NSError** implicitNSErrorOutArg = &implicitNSError; + *static_cast(target) = implicitNSErrorOutArg; + } else { + prepareEngineArguments(runtime, bridge, *signature, args, count, frame); + } + + NativeApiPointerFrame values(signature->argumentTypes.size() + 2); + size_t valueIndex = 0; + struct objc_super superReceiver = {receiver, dispatchSuperClass}; + struct objc_super* superReceiverPtr = &superReceiver; + if (dispatchSuperClass != Nil) { + values.set(valueIndex++, &superReceiverPtr); + } else { + values.set(valueIndex++, &receiver); + } + values.set(valueIndex++, &selector); + for (size_t i = 0; i < signature->argumentTypes.size(); i++) { + values.set(valueIndex++, frame.values()[i]); + } + + NativeApiReturnStorage returnStorage( + nativeSizeForType(signature->returnType)); + auto preparedInvoker = + dispatchSuperClass == Nil + ? lookupObjCPreparedInvoker(dispatchIdForEngineSignature( + *signature, SignatureCallKind::ObjCMethod)) + : nullptr; + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + if (preparedInvoker != nullptr) { + preparedInvoker(reinterpret_cast(objc_msgSend), values.data(), + returnStorage.data()); + } else { +#if defined(__x86_64__) + bool isStret = signature->returnType.ffiType->size > 16 && + signature->returnType.ffiType->type == FFI_TYPE_STRUCT; + void (*target)(void) = + dispatchSuperClass != Nil + ? (isStret ? FFI_FN(objc_msgSendSuper_stret) + : FFI_FN(objc_msgSendSuper)) + : (isStret ? FFI_FN(objc_msgSend_stret) : FFI_FN(objc_msgSend)); + ffi_call(&signature->cif, target, returnStorage.data(), values.data()); +#else + ffi_call(&signature->cif, + dispatchSuperClass != Nil ? FFI_FN(objc_msgSendSuper) + : FFI_FN(objc_msgSend), + returnStorage.data(), values.data()); +#endif + } + }); + + NativeApiType returnType = signature->returnType; + if ((selectorName == "valueForKey:" || selectorName == "valueForKeyPath:") && + isObjectiveCObjectType(returnType)) { + returnType.kind = metagen::mdTypeAnyObject; + } + if (engineInvocation.isInitMethod && isObjectiveCObjectType(returnType)) { + returnType.kind = metagen::mdTypeInstanceObject; + } + if (hasImplicitNSErrorOutArg && implicitNSError != nil) { + const char* errorMessage = [[implicitNSError description] UTF8String]; + throw JSError( + runtime, errorMessage != nullptr ? errorMessage : "Unknown NSError"); + } + return convertNativeReturnValue(runtime, bridge, returnType, + returnStorage.data()); +} diff --git a/NativeScript/ffi/objc/shared/bridge/ObjCBridge.mm b/NativeScript/ffi/objc/shared/bridge/ObjCBridge.mm new file mode 100644 index 000000000..80688018c --- /dev/null +++ b/NativeScript/ffi/objc/shared/bridge/ObjCBridge.mm @@ -0,0 +1,2445 @@ +thread_local int gSynchronousNativeInvocationDepth = 0; +thread_local int gNativeCallerThreadEngineCallbackDepth = 0; +thread_local std::vector gNativeCallbackExceptionCaptureStack; +std::atomic gActiveSynchronousNativeInvocationDepth{0}; +static char gNativeApiExtendedClassKey; + +void markNativeApiExtendedClass(Class cls) { + if (cls == Nil) { + return; + } + objc_setAssociatedObject(cls, &gNativeApiExtendedClassKey, @YES, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +bool isNativeApiExtendedClass(Class cls) { + Class current = cls; + while (current != Nil) { + if (objc_getAssociatedObject(current, &gNativeApiExtendedClassKey) != nil) { + return true; + } + current = class_getSuperclass(current); + } + return false; +} + +class ScopedNativeApiSynchronousInvocation final { + public: + ScopedNativeApiSynchronousInvocation() { + gSynchronousNativeInvocationDepth += 1; + gActiveSynchronousNativeInvocationDepth.fetch_add(1, + std::memory_order_acq_rel); + } + + ~ScopedNativeApiSynchronousInvocation() { + gSynchronousNativeInvocationDepth -= 1; + gActiveSynchronousNativeInvocationDepth.fetch_sub(1, + std::memory_order_acq_rel); + } +}; + +class ScopedNativeCallerThreadEngineCallback final { + public: + ScopedNativeCallerThreadEngineCallback() { + gNativeCallerThreadEngineCallbackDepth += 1; + } + + ~ScopedNativeCallerThreadEngineCallback() { + gNativeCallerThreadEngineCallbackDepth -= 1; + } + + ScopedNativeCallerThreadEngineCallback( + const ScopedNativeCallerThreadEngineCallback&) = delete; + ScopedNativeCallerThreadEngineCallback& operator=( + const ScopedNativeCallerThreadEngineCallback&) = delete; +}; + +class ScopedNativeCallbackExceptionCapture final { + public: + explicit ScopedNativeCallbackExceptionCapture(std::string* message) + : message_(message) { + gNativeCallbackExceptionCaptureStack.push_back(message_); + } + + ~ScopedNativeCallbackExceptionCapture() { + if (!gNativeCallbackExceptionCaptureStack.empty() && + gNativeCallbackExceptionCaptureStack.back() == message_) { + gNativeCallbackExceptionCaptureStack.pop_back(); + } + } + + ScopedNativeCallbackExceptionCapture( + const ScopedNativeCallbackExceptionCapture&) = delete; + ScopedNativeCallbackExceptionCapture& operator=( + const ScopedNativeCallbackExceptionCapture&) = delete; + + private: + std::string* message_ = nullptr; +}; + +bool recordNativeCallbackException(const std::string& message) { + if (gNativeCallbackExceptionCaptureStack.empty()) { + return false; + } + + std::string* captured = gNativeCallbackExceptionCaptureStack.back(); + if (captured == nullptr) { + return false; + } + + if (captured->empty()) { + *captured = message; + } + return true; +} + +template +void performNativeInvocation(Runtime& runtime, + const std::function)>& + invoker, + Invocation&& invocation) { + NSString* exceptionDescription = nil; + std::string callbackException; + auto run = [&]() { + ScopedNativeApiSynchronousInvocation synchronousInvocation; + ScopedNativeCallbackExceptionCapture callbackExceptionCapture( + &callbackException); + @try { + invocation(); + } @catch (NSException* exception) { + exceptionDescription = [exception.description copy]; + } + }; + + bool skipInvoker = gNativeCallerThreadEngineCallbackDepth > 0; + if (invoker && !skipInvoker) { + invoker(run); + } else { + run(); + } + + if (exceptionDescription != nil) { + std::string message = exceptionDescription.UTF8String ?: ""; + [exceptionDescription release]; + throw JSError(runtime, message); + } + if (!callbackException.empty()) { + throw JSError(runtime, callbackException); + } +} + +template +void performDirectObjCInvocation(Runtime& runtime, Invocation&& invocation) { + NSString* exceptionDescription = nil; + auto run = [&]() { + @try { + invocation(); + } @catch (NSException* exception) { + exceptionDescription = [exception.description copy]; + } + }; + + run(); + + if (exceptionDescription != nil) { + std::string message = exceptionDescription.UTF8String ?: ""; + [exceptionDescription release]; + throw JSError(runtime, message); + } +} + +enum class NativeApiSymbolKind { + Class, + Function, + Constant, + Protocol, + Enum, + Struct, + Union, +}; + +struct NativeApiSymbol { + NativeApiSymbolKind kind; + MDSectionOffset offset = 0; + MDSectionOffset superclassOffset = MD_SECTION_OFFSET_NULL; + std::string name; + std::string runtimeName; +}; + +struct NativeApiMember { + std::string name; + std::string selectorName; + std::string setterSelectorName; + MDSectionOffset signatureOffset = MD_SECTION_OFFSET_NULL; + MDSectionOffset setterSignatureOffset = MD_SECTION_OFFSET_NULL; + MDMemberFlag flags = metagen::mdMemberFlagNull; + bool property = false; + bool readonly = false; +}; + +struct NativeApiSelectorGroupEntry { + std::string selectorName; + NativeApiMember member; + bool hasMember = false; + bool propertyGetterResolved = false; + bool propertyGetterCanPrepare = true; + bool propertyGetterHasAdjustedMember = false; + std::string propertyGetterSelectorName; + NativeApiMember propertyGetterMember; +}; + +struct NativeApiSelectorGroupCallTarget { + const std::string* selectorName = nullptr; + const NativeApiMember* member = nullptr; + bool canPrepare = true; +}; + +struct NativeApiAggregateInfo; + +struct NativeApiFfiType { + ffi_type type = {}; + std::vector elements; + + NativeApiFfiType() { + type.type = FFI_TYPE_STRUCT; + type.size = 0; + type.alignment = 0; + type.elements = nullptr; + } + + void finalize() { + elements.push_back(nullptr); + type.elements = elements.data(); + } +}; + +struct NativeApiType { + MDTypeKind kind = metagen::mdTypeVoid; + ffi_type* ffiType = &ffi_type_void; + bool supported = true; + bool returnOwned = false; + MDSectionOffset signatureOffset = MD_SECTION_OFFSET_NULL; + MDSectionOffset aggregateOffset = MD_SECTION_OFFSET_NULL; + bool aggregateIsUnion = false; + uint16_t arraySize = 0; + std::shared_ptr elementType; + std::shared_ptr aggregateInfo; + std::shared_ptr ownedFfiType; +}; + +struct NativeApiAggregateField { + std::string name; + uint16_t offset = 0; + NativeApiType type; +}; + +struct NativeApiAggregateInfo { + std::string name; + uint16_t size = 0; + bool isUnion = false; + MDSectionOffset offset = MD_SECTION_OFFSET_NULL; + std::vector fields; + std::shared_ptr ffi; +}; + +std::string jsifySelector(const char* selector) { + std::string jsifiedSelector; + bool nextUpper = false; + for (const char* c = selector; c != nullptr && *c != '\0'; c++) { + if (*c == ':') { + nextUpper = true; + } else if (nextUpper) { + jsifiedSelector += static_cast(toupper(*c)); + nextUpper = false; + } else { + jsifiedSelector += *c; + } + } + return jsifiedSelector; +} + +std::string booleanGetterSelectorForProperty(const std::string& property) { + if (property.empty()) { + return property; + } + + std::string selector = "is"; + selector += static_cast(toupper(property[0])); + selector += property.substr(1); + return selector; +} + +std::optional respondingPropertyGetterSelector( + id receiver, const std::string& property, + const std::string& preferredSelector) { + if (receiver == nil) { + return std::nullopt; + } + + auto respondsToSelectorName = [receiver](const std::string& selectorName) { + return !selectorName.empty() && + [receiver respondsToSelector:sel_getUid(selectorName.c_str())]; + }; + + if (respondsToSelectorName(preferredSelector)) { + return preferredSelector; + } + if (preferredSelector != property && respondsToSelectorName(property)) { + return property; + } + + std::string booleanSelector = booleanGetterSelectorForProperty(property); + if (booleanSelector != preferredSelector && booleanSelector != property && + respondsToSelectorName(booleanSelector)) { + return booleanSelector; + } + + return std::nullopt; +} + +std::string setterSelectorForProperty(const std::string& property) { + if (property.empty()) { + return property; + } + + std::string selector = "set"; + selector += static_cast(toupper(property[0])); + selector += property.substr(1); + selector += ":"; + return selector; +} + +size_t selectorArgumentCount(const std::string& selector) { + return static_cast( + std::count(selector.begin(), selector.end(), ':')); +} + +const NativeApiMember* selectMethodMember( + const std::vector& members, const std::string& property, + bool staticMethod, size_t argumentCount) { + for (const auto& member : members) { + if (member.property || member.name != property) { + continue; + } + + bool memberIsStatic = (member.flags & metagen::mdMemberStatic) != 0; + if (memberIsStatic != staticMethod) { + continue; + } + + if (selectorArgumentCount(member.selectorName) == argumentCount) { + return &member; + } + } + return nullptr; +} + +bool hasMethodMember(const std::vector& members, + const std::string& property, bool staticMethod) { + for (const auto& member : members) { + if (member.property || member.name != property) { + continue; + } + bool memberIsStatic = (member.flags & metagen::mdMemberStatic) != 0; + if (memberIsStatic == staticMethod) { + return true; + } + } + return false; +} + +std::shared_ptr> +selectorGroupEntriesForMethod(const std::vector& members, + const std::string& property, bool staticMethod) { + auto selectors = std::make_shared>(); + for (const auto& member : members) { + if (member.property || member.name != property || member.selectorName.empty()) { + continue; + } + + bool memberIsStatic = (member.flags & metagen::mdMemberStatic) != 0; + if (memberIsStatic != staticMethod) { + continue; + } + + size_t argumentCount = selectorArgumentCount(member.selectorName); + if (selectors->size() <= argumentCount) { + selectors->resize(argumentCount + 1); + } + if ((*selectors)[argumentCount].selectorName.empty()) { + (*selectors)[argumentCount].selectorName = member.selectorName; + (*selectors)[argumentCount].member = member; + (*selectors)[argumentCount].hasMember = true; + } + + if (argumentCount > 0 && member.selectorName.size() >= 6 && + member.selectorName.compare(member.selectorName.size() - 6, 6, + "error:") == 0) { + size_t omittedErrorCount = argumentCount - 1; + if (selectors->size() <= omittedErrorCount) { + selectors->resize(omittedErrorCount + 1); + } + if ((*selectors)[omittedErrorCount].selectorName.empty()) { + (*selectors)[omittedErrorCount].selectorName = member.selectorName; + (*selectors)[omittedErrorCount].member = member; + (*selectors)[omittedErrorCount].hasMember = true; + } + } + } + return selectors->empty() ? nullptr : selectors; +} + +bool selectorGroupCanPrepareSelector(id receiver, Class lookupClass, + bool receiverIsClass, + const std::string& selectorName) { + if (selectorName.empty()) { + return false; + } + SEL selector = sel_registerName(selectorName.c_str()); + if (receiverIsClass) { + return lookupClass != Nil && + class_getClassMethod(lookupClass, selector) != nullptr; + } + if (lookupClass != Nil && + class_getInstanceMethod(lookupClass, selector) != nullptr) { + return true; + } + return receiver != nil && + class_getInstanceMethod(object_getClass(receiver), selector) != nullptr; +} + +std::string selectorGroupPropertyGetterSelector( + id receiver, Class lookupClass, bool receiverIsClass, + const NativeApiMember& member) { + if (selectorGroupCanPrepareSelector(receiver, lookupClass, receiverIsClass, + member.selectorName)) { + return member.selectorName; + } + if (member.selectorName != member.name && + selectorGroupCanPrepareSelector(receiver, lookupClass, receiverIsClass, + member.name)) { + return member.name; + } + + std::string booleanSelector = booleanGetterSelectorForProperty(member.name); + if (booleanSelector != member.selectorName && booleanSelector != member.name && + selectorGroupCanPrepareSelector(receiver, lookupClass, receiverIsClass, + booleanSelector)) { + return booleanSelector; + } + + if (auto responding = respondingPropertyGetterSelector( + receiver, member.name, member.selectorName)) { + return *responding; + } + + return member.selectorName != member.name ? member.name : member.selectorName; +} + +NativeApiSelectorGroupCallTarget selectorGroupMemberForCall( + id receiver, Class lookupClass, bool receiverIsClass, + NativeApiSelectorGroupEntry& entry, size_t count) { + if (!entry.hasMember) { + return {&entry.selectorName, nullptr, true}; + } + if (count == 0 && entry.member.property) { + if (!entry.propertyGetterResolved) { + entry.propertyGetterSelectorName = selectorGroupPropertyGetterSelector( + receiver, lookupClass, receiverIsClass, entry.member); + entry.propertyGetterCanPrepare = selectorGroupCanPrepareSelector( + receiver, lookupClass, receiverIsClass, + entry.propertyGetterSelectorName); + if (entry.propertyGetterSelectorName != entry.member.selectorName) { + entry.propertyGetterMember = entry.member; + entry.propertyGetterMember.selectorName = + entry.propertyGetterSelectorName; + entry.propertyGetterHasAdjustedMember = true; + } + entry.propertyGetterResolved = true; + } + return {&entry.propertyGetterSelectorName, + entry.propertyGetterHasAdjustedMember ? &entry.propertyGetterMember + : &entry.member, + entry.propertyGetterCanPrepare}; + } + return {&entry.selectorName, &entry.member, true}; +} + +inline NativeApiSelectorGroupCallTarget selectorGroupCallTargetForEntry( + id receiver, Class lookupClass, bool receiverIsClass, + NativeApiSelectorGroupEntry& entry, size_t count) { + if (entry.hasMember && (!entry.member.property || count != 0)) { + return {&entry.selectorName, &entry.member, true}; + } + return selectorGroupMemberForCall(receiver, lookupClass, receiverIsClass, + entry, count); +} + +const NativeApiMember* selectPropertyMember( + const std::vector& members, const std::string& property, + bool staticMethod) { + for (const auto& member : members) { + if (!member.property || member.name != property) { + continue; + } + + bool memberIsStatic = (member.flags & metagen::mdMemberStatic) != 0; + if (memberIsStatic == staticMethod) { + return &member; + } + } + return nullptr; +} + +const NativeApiMember* selectWritablePropertyMember( + const std::vector& members, const std::string& property, + bool staticMethod) { + const NativeApiMember* propertyMember = nullptr; + for (const auto& member : members) { + if (!member.property || member.name != property) { + continue; + } + + bool memberIsStatic = (member.flags & metagen::mdMemberStatic) != 0; + if (memberIsStatic != staticMethod) { + continue; + } + + if (propertyMember == nullptr) { + propertyMember = &member; + } + if (!member.readonly && !member.setterSelectorName.empty()) { + return &member; + } + } + return propertyMember; +} + +void skipMetadataEngineType(MDMetadataReader* metadata, MDSectionOffset* offset); +Protocol* lookupProtocolByNativeName(const std::string& name); +struct NativeApiPreparedObjCInvocation; +bool preparedObjCInvocationIsInit( + const NativeApiPreparedObjCInvocation& prepared); + +inline uintptr_t normalizeRuntimePointer(uintptr_t pointer) { +#if INTPTR_MAX == INT64_MAX + return pointer & 0x0000FFFFFFFFFFFFULL; +#else + return pointer; +#endif +} + +class NativeApiBridge { + struct NativeApiRoundTripValue { + std::shared_ptr value; + bool stringLikeNative = false; + bool persistBeyondFrame = true; + uintptr_t validationKey = 0; + }; + using NativeApiRoundTripReleaseList = + std::vector; + using NativeApiRoundTripFrame = + std::unordered_map; + using NativeApiRoundTripFrameStack = std::vector; + + static constexpr size_t kRecentRoundTripValueLimit = 2; + + public: + explicit NativeApiBridge(const NativeApiConfig& config) + : metadata_(loadMetadata(config)), + scheduler_(config.scheduler), + nativeInvocationInvoker_(config.nativeInvocationInvoker), + nativeCallbackInvoker_(config.nativeCallbackInvoker), + runtimeCallbackInvoker_(config.runtimeCallbackInvoker), + jsThreadCallbackInvoker_(config.jsThreadCallbackInvoker), + jsThreadAsyncCallbackInvoker_(config.jsThreadAsyncCallbackInvoker), + invokeCallbacksOnNativeCallerThread_( + config.invokeCallbacksOnNativeCallerThread) { + selfDl_ = dlopen(nullptr, RTLD_NOW); + buildSymbolIndexes(); + } + + ~NativeApiBridge() { + if (selfDl_ != nullptr) { + dlclose(selfDl_); + } + } + + MDMetadataReader* metadata() const { return metadata_.get(); } + + void* selfDl() const { return selfDl_; } + + const NativeApiSymbol* find(const std::string& name) const { + auto it = symbolsByName_.find(name); + return it != symbolsByName_.end() ? &it->second : nullptr; + } + + const NativeApiSymbol* findClass(const std::string& name) const { + const NativeApiSymbol* symbol = find(name); + if (symbol != nullptr && symbol->kind == NativeApiSymbolKind::Class) { + return symbol; + } + auto it = classSymbolsByRuntimeName_.find(name); + return it != classSymbolsByRuntimeName_.end() ? &it->second : nullptr; + } + + const NativeApiSymbol* findClassByOffset(MDSectionOffset offset) const { + auto it = classSymbolsByOffset_.find(offset); + return it != classSymbolsByOffset_.end() ? &it->second : nullptr; + } + + const NativeApiSymbol* findClassForRuntimeClass(Class cls) const { + Class current = cls; + while (current != Nil) { + const char* name = class_getName(current); + if (name != nullptr) { + if (const NativeApiSymbol* symbol = findClass(name)) { + return symbol; + } + } + current = class_getSuperclass(current); + } + return nullptr; + } + + const NativeApiSymbol* findClassForRuntimePointer(void* pointer) const { + if (pointer == nullptr) { + return nullptr; + } + + auto it = classSymbolsByRuntimePointer_.find( + normalizeRuntimePointer(reinterpret_cast(pointer))); + return it != classSymbolsByRuntimePointer_.end() ? &it->second : nullptr; + } + + const NativeApiSymbol* findProtocolForRuntimePointer(void* pointer) const { + if (pointer == nullptr) { + return nullptr; + } + + auto it = protocolSymbolsByRuntimePointer_.find( + normalizeRuntimePointer(reinterpret_cast(pointer))); + return it != protocolSymbolsByRuntimePointer_.end() ? &it->second : nullptr; + } + + const NativeApiSymbol* findFunction(const std::string& name) const { + auto it = functionSymbolsByName_.find(name); + return it != functionSymbolsByName_.end() ? &it->second : nullptr; + } + + static uintptr_t callbackRoundTripValidationKey( + const NativeApiType& type) { + if (type.signatureOffset == 0 || + type.signatureOffset == MD_SECTION_OFFSET_NULL) { + return 0; + } + return (static_cast(type.signatureOffset) << 8) | + (static_cast(type.kind) & 0xff); + } + + void rememberRoundTripValue(Runtime& runtime, const void* native, + const Value& value, + bool stringLikeNative = false, + uintptr_t validationKey = 0) { + if (native == nullptr) { + return; + } + uintptr_t key = + normalizeRuntimePointer(reinterpret_cast(native)); + NativeApiRoundTripReleaseList releaseAfterUnlock; + { + std::lock_guard lock(roundTripValuesMutex_); + storeRoundTripEntry( + roundTripValues_, key, + NativeApiRoundTripValue{ + std::make_shared(runtime, value), stringLikeNative, true, + validationKey}, + releaseAfterUnlock); + roundTripValuesGeneration_.fetch_add(1, std::memory_order_release); + } +#ifdef TARGET_ENGINE_HERMES + rootRoundTripValue(runtime, key, value); +#endif + } + + void rememberScopedRoundTripValue(Runtime& runtime, const void* native, + const Value& value, + bool stringLikeNative = false, + bool persistBeyondFrame = true) { + rememberScopedRoundTripValueWithValidationKey( + runtime, native, value, stringLikeNative, persistBeyondFrame, + nativeObjectClassKey(native)); + } + + void rememberScopedRawRoundTripValue(Runtime& runtime, const void* native, + const Value& value, + bool stringLikeNative = false, + bool persistBeyondFrame = true) { + rememberScopedRoundTripValueWithValidationKey(runtime, native, value, + stringLikeNative, + persistBeyondFrame, 0); + } + + void rememberScopedRoundTripValueWithValidationKey(Runtime& runtime, + const void* native, + const Value& value, + bool stringLikeNative, + bool persistBeyondFrame, + uintptr_t validationKey) { + if (native == nullptr) { + return; + } + uintptr_t key = + normalizeRuntimePointer(reinterpret_cast(native)); + NativeApiRoundTripValue entry{ + std::make_shared(runtime, value), stringLikeNative, + persistBeyondFrame, validationKey}; + NativeApiRoundTripReleaseList releaseAfterUnlock; + { + std::lock_guard lock(roundTripValuesMutex_); + auto framesIt = + roundTripCacheFramesByThread_.find(std::this_thread::get_id()); + if (framesIt != roundTripCacheFramesByThread_.end() && + !framesIt->second.empty()) { + storeRoundTripEntry(framesIt->second.back(), key, std::move(entry), + releaseAfterUnlock); + } else if (persistBeyondFrame) { + rememberRecentRoundTripValue(key, std::move(entry), + releaseAfterUnlock); + } + roundTripValuesGeneration_.fetch_add(1, std::memory_order_release); + } + } + + Value findRoundTripValue(Runtime& runtime, const void* native, + bool* stringLikeNative = nullptr, + bool nativeIsObject = false, + uintptr_t validationKey = 0) { + if (stringLikeNative != nullptr) { + *stringLikeNative = false; + } + if (native == nullptr) { + return Value::undefined(); + } + uintptr_t key = + normalizeRuntimePointer(reinterpret_cast(native)); + const uintptr_t expectedValidationKey = + validationKey != 0 + ? validationKey + : (nativeIsObject ? nativeObjectClassKey(native) : 0); + struct RoundTripCacheEntry { + const NativeApiBridge* bridge = nullptr; + uintptr_t key = 0; + uint64_t generation = 0; + std::weak_ptr value; + bool miss = false; + bool stringLikeNative = false; + uintptr_t validationKey = 0; + }; + static thread_local RoundTripCacheEntry cache[4]; + const uint64_t generation = + roundTripValuesGeneration_.load(std::memory_order_acquire); + const size_t firstSlot = (key >> 4) & 3; + for (size_t i = 0; i < 4; i++) { + RoundTripCacheEntry& entry = cache[(firstSlot + i) & 3]; + if (entry.bridge == this && entry.key == key && + entry.generation == generation) { + if (entry.validationKey != expectedValidationKey) { + break; + } + if (entry.miss) { + return Value::undefined(); + } + if (auto cached = entry.value.lock()) { + if (roundTripValuesGeneration_.load(std::memory_order_acquire) == + generation) { + if (stringLikeNative != nullptr) { + *stringLikeNative = entry.stringLikeNative; + } + return Value(runtime, *cached); + } + } + break; + } + } + + std::shared_ptr storedValue; + bool cachedStringLike = false; + { + std::lock_guard lock(roundTripValuesMutex_); + auto findEntry = [&](const auto& map) -> const NativeApiRoundTripValue* { + auto it = map.find(key); + if (it == map.end() || it->second.value == nullptr) { + return nullptr; + } + if (it->second.validationKey != expectedValidationKey) { + return nullptr; + } + return &it->second; + }; + + const NativeApiRoundTripValue* entry = findEntry(roundTripValues_); + if (entry == nullptr) { + auto framesIt = + roundTripCacheFramesByThread_.find(std::this_thread::get_id()); + if (framesIt != roundTripCacheFramesByThread_.end()) { + for (auto frame = framesIt->second.rbegin(); + frame != framesIt->second.rend(); ++frame) { + entry = findEntry(*frame); + if (entry != nullptr) { + break; + } + } + } + } + if (entry == nullptr) { + entry = findEntry(recentRoundTripValues_); + } + if (entry == nullptr) { + cache[firstSlot] = RoundTripCacheEntry{ + this, key, generation, {}, true, false, expectedValidationKey}; + return Value::undefined(); + } + storedValue = entry->value; + cachedStringLike = entry->stringLikeNative; + cache[firstSlot] = RoundTripCacheEntry{ + this, key, generation, storedValue, false, cachedStringLike, + entry->validationKey}; + } + if (stringLikeNative != nullptr) { + *stringLikeNative = cachedStringLike; + } + return Value(runtime, *storedValue); + } + + void forgetRoundTripValue(Runtime& runtime, const void* native) { + if (native == nullptr) { + return; + } + uintptr_t key = + normalizeRuntimePointer(reinterpret_cast(native)); +#ifdef TARGET_ENGINE_HERMES + bool rooted = false; + NativeApiRoundTripReleaseList releaseAfterUnlock; + { + std::lock_guard lock(roundTripValuesMutex_); + eraseRoundTripMapKey(roundTripValues_, key, releaseAfterUnlock); + eraseRoundTripKeyFromScopedCaches(key, releaseAfterUnlock); + rooted = rootedRoundTripValues_.erase(key) > 0; + roundTripValuesGeneration_.fetch_add(1, std::memory_order_release); + } + if (rooted) { + unrootRoundTripValue(runtime, key); + } +#else + forgetRoundTripKey(key); +#endif + } + + void forgetRoundTripKey(uintptr_t key) { + if (key == 0) { + return; + } + NativeApiRoundTripReleaseList releaseAfterUnlock; + { + std::lock_guard lock(roundTripValuesMutex_); + eraseRoundTripMapKey(roundTripValues_, key, releaseAfterUnlock); + eraseRoundTripKeyFromScopedCaches(key, releaseAfterUnlock); + roundTripValuesGeneration_.fetch_add(1, std::memory_order_release); + } + } + + void forgetRoundTripValue(const void* native) { + if (native == nullptr) { + return; + } + uintptr_t key = + normalizeRuntimePointer(reinterpret_cast(native)); + NativeApiRoundTripReleaseList releaseAfterUnlock; + { + std::lock_guard lock(roundTripValuesMutex_); + eraseRoundTripMapKey(roundTripValues_, key, releaseAfterUnlock); + eraseRoundTripKeyFromScopedCaches(key, releaseAfterUnlock); + roundTripValuesGeneration_.fetch_add(1, std::memory_order_release); + } + } + + uint64_t roundTripValuesGeneration() const { + return roundTripValuesGeneration_.load(std::memory_order_acquire); + } + + void beginRoundTripCacheFrame() { + std::lock_guard lock(roundTripValuesMutex_); + roundTripCacheFramesByThread_[std::this_thread::get_id()].emplace_back(); + } + + void endRoundTripCacheFrame() { + NativeApiRoundTripReleaseList releaseAfterUnlock; + NativeApiRoundTripFrame frame; + { + std::lock_guard lock(roundTripValuesMutex_); + auto framesIt = + roundTripCacheFramesByThread_.find(std::this_thread::get_id()); + if (framesIt == roundTripCacheFramesByThread_.end() || + framesIt->second.empty()) { + return; + } + + auto& frames = framesIt->second; + frame = std::move(frames.back()); + frames.pop_back(); + if (!frames.empty()) { + auto& parent = frames.back(); + for (auto& entry : frame) { + storeRoundTripEntry(parent, entry.first, std::move(entry.second), + releaseAfterUnlock); + } + } else { + roundTripCacheFramesByThread_.erase(framesIt); + for (auto& entry : frame) { + if (entry.second.persistBeyondFrame) { + rememberRecentRoundTripValue(entry.first, std::move(entry.second), + releaseAfterUnlock); + } else { + releaseAfterUnlock.push_back(std::move(entry.second)); + } + } + } + roundTripValuesGeneration_.fetch_add(1, std::memory_order_release); + } + } + + void rememberClassValue(Runtime& runtime, Class cls, const Value& value) { + if (cls == Nil) { + return; + } + classValues_[normalizeRuntimePointer(reinterpret_cast(cls))] = + std::make_shared(runtime, value); + } + + Value findClassValue(Runtime& runtime, Class cls) const { + if (cls == Nil) { + return Value::undefined(); + } + auto it = classValues_.find( + normalizeRuntimePointer(reinterpret_cast(cls))); + if (it == classValues_.end() || it->second == nullptr) { + return Value::undefined(); + } + return Value(runtime, *it->second); + } + + void rememberClassPrototype(Runtime& runtime, Class cls, const Value& value) { + if (cls == Nil) { + return; + } + classPrototypes_[normalizeRuntimePointer(reinterpret_cast(cls))] = + std::make_shared(runtime, value); + } + + Value findClassPrototype(Runtime& runtime, Class cls) const { + if (cls == Nil) { + return Value::undefined(); + } + auto it = classPrototypes_.find( + normalizeRuntimePointer(reinterpret_cast(cls))); + if (it == classPrototypes_.end() || it->second == nullptr) { + return Value::undefined(); + } + return Value(runtime, *it->second); + } + + void setObjectExpando(Runtime& runtime, const void* native, + const std::string& property, const Value& value) { + if (native == nullptr || property.empty()) { + return; + } + objectExpandos_[normalizeRuntimePointer(reinterpret_cast(native))] + [property] = std::make_shared(runtime, value); + objectExpandosGeneration_.fetch_add(1, std::memory_order_release); + } + + void retainObjectExpandoOwner(const void* native) { + if (native == nullptr) { + return; + } + objectExpandoOwnerCounts_[ + normalizeRuntimePointer(reinterpret_cast(native))] += 1; + } + + void releaseObjectExpandoOwner(const void* native, + bool preserveExpandos = false) { + if (native == nullptr) { + return; + } + uintptr_t key = + normalizeRuntimePointer(reinterpret_cast(native)); + auto ownerIt = objectExpandoOwnerCounts_.find(key); + if (ownerIt != objectExpandoOwnerCounts_.end()) { + if (ownerIt->second > 1) { + ownerIt->second -= 1; + return; + } + objectExpandoOwnerCounts_.erase(ownerIt); + } + if (!preserveExpandos) { + forgetObjectExpandos(native); + } + } + + Value findObjectExpando(Runtime& runtime, const void* native, + const std::string& property) const { + if (native == nullptr || property.empty()) { + return Value::undefined(); + } + struct ObjectExpandoCacheEntry { + const NativeApiBridge* bridge = nullptr; + uintptr_t key = 0; + uint64_t generation = 0; + std::string property; + std::weak_ptr value; + bool miss = false; + }; + static thread_local ObjectExpandoCacheEntry cache[8]; + static thread_local size_t nextSlot = 0; + + const uintptr_t key = + normalizeRuntimePointer(reinterpret_cast(native)); + const uint64_t generation = + objectExpandosGeneration_.load(std::memory_order_acquire); + for (auto& entry : cache) { + if (entry.bridge == this && entry.key == key && + entry.generation == generation && entry.property == property) { + if (entry.miss) { + return Value::undefined(); + } + if (auto cached = entry.value.lock()) { + return Value(runtime, *cached); + } + break; + } + } + + auto objectIt = objectExpandos_.find(key); + const size_t slot = nextSlot++ & 7; + if (objectIt == objectExpandos_.end()) { + cache[slot] = + ObjectExpandoCacheEntry{this, key, generation, property, {}, true}; + return Value::undefined(); + } + auto propertyIt = objectIt->second.find(property); + if (propertyIt == objectIt->second.end() || propertyIt->second == nullptr) { + cache[slot] = + ObjectExpandoCacheEntry{this, key, generation, property, {}, true}; + return Value::undefined(); + } + cache[slot] = ObjectExpandoCacheEntry{ + this, key, generation, property, propertyIt->second, false}; + return Value(runtime, *propertyIt->second); + } + + void forgetObjectExpandos(const void* native) { + if (native == nullptr) { + return; + } + auto key = normalizeRuntimePointer(reinterpret_cast(native)); + objectExpandos_.erase( + key); + objectExpandosGeneration_.fetch_add(1, std::memory_order_release); + } + + // Per-class cache of resolved metadata property-getter members. Lets the + // instance property interceptor skip the special-name chain + metadata + // discovery on every `object.prop` access (the engines without V8's + // kNonMasking prototype fast path otherwise re-resolve on each access). + // membersByClassOffset_ vectors are permanent, so the member pointer is + // stable for the bridge's lifetime. + struct CachedPropertyGetter { + const NativeApiMember* member; + std::string selectorName; + std::shared_ptr preparedInvocation; + }; + const CachedPropertyGetter* findCachedPropertyGetter( + Class cls, const std::string& property) const { + if (cls == Nil || property.empty()) { + return nullptr; + } + struct PropertyGetterCacheEntry { + const NativeApiBridge* bridge = nullptr; + Class cls = Nil; + uint64_t generation = 0; + std::string property; + const CachedPropertyGetter* getter = nullptr; + bool miss = false; + }; + static thread_local PropertyGetterCacheEntry cache[8]; + static thread_local size_t nextSlot = 0; + + const uint64_t generation = + propertyGetterCacheGeneration_.load(std::memory_order_acquire); + for (auto& entry : cache) { + if (entry.bridge == this && entry.cls == cls && + entry.generation == generation && entry.property == property) { + return entry.miss ? nullptr : entry.getter; + } + } + + auto classIt = propertyGetterCache_.find(cls); + const size_t slot = nextSlot++ & 7; + if (classIt == propertyGetterCache_.end()) { + cache[slot] = PropertyGetterCacheEntry{ + this, cls, generation, property, nullptr, true}; + return nullptr; + } + auto propIt = classIt->second.find(property); + if (propIt == classIt->second.end()) { + cache[slot] = PropertyGetterCacheEntry{ + this, cls, generation, property, nullptr, true}; + return nullptr; + } + cache[slot] = + PropertyGetterCacheEntry{this, cls, generation, property, + &propIt->second, false}; + return &propIt->second; + } + void cachePropertyGetter(Class cls, const std::string& property, + const NativeApiMember* member, + const std::string& selectorName, + std::shared_ptr + preparedInvocation = nullptr) { + propertyGetterCache_[cls][property] = + CachedPropertyGetter{member, selectorName, + std::move(preparedInvocation)}; + propertyGetterCacheGeneration_.fetch_add(1, std::memory_order_release); + } + + void rememberPointerValue(Runtime& runtime, const void* native, + const Value& value) { + pointerValues_[reinterpret_cast(native)] = + std::make_shared(runtime, value); + } + + Value findPointerValue(Runtime& runtime, const void* native) const { + auto it = pointerValues_.find(reinterpret_cast(native)); + if (it == pointerValues_.end() || it->second == nullptr) { + return Value::undefined(); + } + return Value(runtime, *it->second); + } + + void forgetPointerValue(const void* native) { + if (native == nullptr) { + return; + } + pointerValues_.erase(reinterpret_cast(native)); + } + + const NativeApiSymbol* findConstant(const std::string& name) const { + auto it = constantSymbolsByName_.find(name); + return it != constantSymbolsByName_.end() ? &it->second : nullptr; + } + + const NativeApiSymbol* findProtocol(const std::string& name) const { + const NativeApiSymbol* symbol = find(name); + if (symbol != nullptr && symbol->kind == NativeApiSymbolKind::Protocol) { + return symbol; + } + auto it = protocolSymbolsByRuntimeName_.find(name); + return it != protocolSymbolsByRuntimeName_.end() ? &it->second : nullptr; + } + + const NativeApiSymbol* findEnum(const std::string& name) const { + auto it = enumSymbolsByName_.find(name); + return it != enumSymbolsByName_.end() ? &it->second : nullptr; + } + + const NativeApiSymbol* findStruct(const std::string& name) const { + auto it = structSymbolsByName_.find(name); + return it != structSymbolsByName_.end() ? &it->second : nullptr; + } + + const NativeApiSymbol* findUnion(const std::string& name) const { + auto it = unionSymbolsByName_.find(name); + return it != unionSymbolsByName_.end() ? &it->second : nullptr; + } + + const NativeApiSymbol* findAggregate(const std::string& name) const { + const NativeApiSymbol* symbol = findStruct(name); + if (symbol != nullptr) { + return symbol; + } + return findUnion(name); + } + + size_t classCount() const { return classNames_.size(); } + size_t functionCount() const { return functionNames_.size(); } + size_t constantCount() const { return constantNames_.size(); } + size_t protocolCount() const { return protocolNames_.size(); } + size_t enumCount() const { return enumNames_.size(); } + size_t structCount() const { return structNames_.size(); } + size_t unionCount() const { return unionNames_.size(); } + + const std::vector& classNames() const { return classNames_; } + const std::vector& functionNames() const { return functionNames_; } + const std::vector& constantNames() const { return constantNames_; } + const std::vector& protocolNames() const { return protocolNames_; } + const std::vector& enumNames() const { return enumNames_; } + const std::vector& structNames() const { return structNames_; } + const std::vector& unionNames() const { return unionNames_; } + std::shared_ptr scheduler() const { return scheduler_; } + const std::function)>& nativeInvocationInvoker() + const { + return nativeInvocationInvoker_; + } + const std::function)>& nativeCallbackInvoker() + const { + return nativeCallbackInvoker_; + } + const std::function)>& runtimeCallbackInvoker() + const { + return runtimeCallbackInvoker_; + } + const std::function)>& jsThreadCallbackInvoker() + const { + return jsThreadCallbackInvoker_; + } + const std::function)>& + jsThreadAsyncCallbackInvoker() const { + return jsThreadAsyncCallbackInvoker_; + } + bool invokeCallbacksOnNativeCallerThread() const { + return invokeCallbacksOnNativeCallerThread_; + } + + std::thread::id jsThreadId() const { return jsThreadId_; } + + void retainEngineLifetime(std::shared_ptr lifetime) { + if (lifetime == nullptr) { + return; + } + std::lock_guard lock(retainedLifetimesMutex_); + retainedLifetimes_.push_back(std::move(lifetime)); + } + +#ifdef TARGET_ENGINE_HERMES + std::string roundTripRootKey(uintptr_t key) const { + char buffer[32] = {}; + snprintf(buffer, sizeof(buffer), "p%llx", + static_cast(key)); + return buffer; + } + + Object roundTripRootObject(Runtime& runtime) { + if (roundTripRootCache_) { + return roundTripRootCache_->asObject(runtime); + } + static constexpr const char* kRootName = + "__nativeScriptNativeApiRoundTripValues"; + Object global = runtime.global(); + if (global.hasProperty(runtime, kRootName)) { + Value existing = global.getProperty(runtime, kRootName); + if (existing.isObject()) { + Object root = existing.asObject(runtime); + roundTripRootCache_ = std::make_shared(runtime, root); + return root; + } + } + + Object root(runtime); + global.setProperty(runtime, kRootName, root); + roundTripRootCache_ = std::make_shared(runtime, root); + return root; + } + + void rootRoundTripValue(Runtime& runtime, uintptr_t key, + const Value& value) { + roundTripRootObject(runtime) + .setProperty(runtime, roundTripRootKey(key).c_str(), value); + std::lock_guard lock(roundTripValuesMutex_); + rootedRoundTripValues_.insert(key); + } + + void unrootRoundTripValue(Runtime& runtime, uintptr_t key) { + roundTripRootObject(runtime) + .setProperty(runtime, roundTripRootKey(key).c_str(), + Value::undefined()); + } +#endif + + uintptr_t nativeObjectClassKey(const void* native) const { + if (native == nullptr) { + return 0; + } + return normalizeRuntimePointer( + reinterpret_cast(object_getClass(static_cast(native)))); + } + + void storeRoundTripEntry( + std::unordered_map& map, + uintptr_t key, NativeApiRoundTripValue&& entry, + NativeApiRoundTripReleaseList& releaseAfterUnlock) { + auto it = map.find(key); + if (it == map.end()) { + map.emplace(key, std::move(entry)); + return; + } + + releaseAfterUnlock.push_back(std::move(it->second)); + it->second = std::move(entry); + } + + void eraseRoundTripMapKey( + std::unordered_map& map, + uintptr_t key, NativeApiRoundTripReleaseList& releaseAfterUnlock) { + auto it = map.find(key); + if (it == map.end()) { + return; + } + + releaseAfterUnlock.push_back(std::move(it->second)); + map.erase(it); + } + + void eraseRoundTripKeyFromScopedCaches( + uintptr_t key, NativeApiRoundTripReleaseList& releaseAfterUnlock) { + eraseRoundTripMapKey(recentRoundTripValues_, key, releaseAfterUnlock); + recentRoundTripValueOrder_.erase( + std::remove(recentRoundTripValueOrder_.begin(), + recentRoundTripValueOrder_.end(), key), + recentRoundTripValueOrder_.end()); + for (auto& stackEntry : roundTripCacheFramesByThread_) { + for (auto& frame : stackEntry.second) { + eraseRoundTripMapKey(frame, key, releaseAfterUnlock); + } + } + } + + void rememberRecentRoundTripValue(uintptr_t key, + NativeApiRoundTripValue&& entry, + NativeApiRoundTripReleaseList& releaseAfterUnlock) { + if (recentRoundTripValues_.find(key) == recentRoundTripValues_.end()) { + recentRoundTripValueOrder_.push_back(key); + } + storeRoundTripEntry(recentRoundTripValues_, key, std::move(entry), + releaseAfterUnlock); + while (recentRoundTripValueOrder_.size() > kRecentRoundTripValueLimit) { + uintptr_t evicted = recentRoundTripValueOrder_.front(); + recentRoundTripValueOrder_.erase(recentRoundTripValueOrder_.begin()); + eraseRoundTripMapKey(recentRoundTripValues_, evicted, + releaseAfterUnlock); + } + } + + const std::vector& membersForClass( + const NativeApiSymbol& symbol) const { + auto cached = membersByClassOffset_.find(symbol.offset); + if (cached != membersByClassOffset_.end()) { + return cached->second; + } + + auto inserted = membersByClassOffset_.emplace( + symbol.offset, readMembersForClassHierarchy(symbol)); + return inserted.first->second; + } + + const std::vector& surfaceMembersForClass( + const NativeApiSymbol& symbol) const { + auto cached = surfaceMembersByClassOffset_.find(symbol.offset); + if (cached != surfaceMembersByClassOffset_.end()) { + return cached->second; + } + + auto inserted = surfaceMembersByClassOffset_.emplace( + symbol.offset, readSurfaceMembersForClass(symbol)); + return inserted.first->second; + } + + const std::vector& membersForProtocol( + const NativeApiSymbol& symbol) const { + auto cached = membersByProtocolOffset_.find(symbol.offset); + if (cached != membersByProtocolOffset_.end()) { + return cached->second; + } + + auto inserted = membersByProtocolOffset_.emplace( + symbol.offset, readMembersForProtocolHierarchy(symbol.offset)); + return inserted.first->second; + } + + std::shared_ptr aggregateInfoFor( + MDSectionOffset aggregateOffset, bool isUnion); + + std::shared_ptr aggregateInfoFor( + const NativeApiSymbol& symbol) { + return aggregateInfoFor(symbol.offset, + symbol.kind == NativeApiSymbolKind::Union); + } + + private: + static std::unique_ptr loadMetadataFromFile( + const char* metadataPath) { + const char* path = metadataPath != nullptr ? metadataPath : "metadata.nsmd"; + FILE* file = fopen(path, "rb"); + if (file == nullptr) { + throw std::runtime_error(std::string("metadata.nsmd not found: ") + path); + } + + fseek(file, 0, SEEK_END); + long size = ftell(file); + fseek(file, 0, SEEK_SET); + if (size <= 0) { + fclose(file); + throw std::runtime_error(std::string("metadata.nsmd is empty: ") + path); + } + + void* buffer = malloc(static_cast(size)); + if (buffer == nullptr) { + fclose(file); + throw std::bad_alloc(); + } + + size_t read = fread(buffer, 1, static_cast(size), file); + fclose(file); + if (read != static_cast(size)) { + free(buffer); + throw std::runtime_error(std::string("failed to read metadata: ") + path); + } + + return std::make_unique(buffer, true); + } + + static std::unique_ptr loadMetadata( + const NativeApiConfig& config) { + if (config.metadataPtr != nullptr && + *static_cast(config.metadataPtr) != '\0') { +#ifdef EMBED_METADATA_SIZE + return std::make_unique((void*)embedded_metadata); +#else + return std::make_unique( + const_cast(config.metadataPtr)); +#endif + } + +#ifdef EMBED_METADATA_SIZE + if (config.metadataPath == nullptr) { + return std::make_unique((void*)embedded_metadata); + } +#endif + + unsigned long segmentSize = 0; + auto segmentData = getsegmentdata( + reinterpret_cast(_dyld_get_image_header(0)), + "__objc_metadata", &segmentSize); + if (segmentData != nullptr && segmentSize > 0) { + return std::make_unique(segmentData); + } + + return loadMetadataFromFile(config.metadataPath); + } + + void addSymbol(NativeApiSymbolKind kind, MDSectionOffset offset, + const char* name, const char* runtimeName = nullptr, + MDSectionOffset superclassOffset = MD_SECTION_OFFSET_NULL) { + if (name == nullptr || name[0] == '\0') { + return; + } + + NativeApiSymbol symbol{ + .kind = kind, + .offset = offset, + .superclassOffset = superclassOffset, + .name = name, + .runtimeName = runtimeName != nullptr ? runtimeName : name, + }; + + switch (kind) { + case NativeApiSymbolKind::Class: + classNames_.push_back(symbol.name); + break; + case NativeApiSymbolKind::Function: + functionNames_.push_back(symbol.name); + functionSymbolsByName_[symbol.name] = symbol; + break; + case NativeApiSymbolKind::Constant: + constantNames_.push_back(symbol.name); + constantSymbolsByName_[symbol.name] = symbol; + break; + case NativeApiSymbolKind::Protocol: + protocolNames_.push_back(symbol.name); + break; + case NativeApiSymbolKind::Enum: + enumNames_.push_back(symbol.name); + enumSymbolsByName_[symbol.name] = symbol; + break; + case NativeApiSymbolKind::Struct: + structNames_.push_back(symbol.name); + structSymbolsByName_[symbol.name] = symbol; + break; + case NativeApiSymbolKind::Union: + unionNames_.push_back(symbol.name); + unionSymbolsByName_[symbol.name] = symbol; + break; + } + + symbolsByName_[symbol.name] = symbol; + if (kind == NativeApiSymbolKind::Class) { + classSymbolsByOffset_[symbol.offset] = symbol; + classSymbolsByRuntimeName_[symbol.runtimeName] = symbol; + Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); + if (cls != Nil) { + classSymbolsByRuntimePointer_[normalizeRuntimePointer( + reinterpret_cast(cls))] = symbol; + } + } else if (kind == NativeApiSymbolKind::Protocol) { + protocolSymbolsByOffset_[symbol.offset] = symbol; + protocolSymbolsByRuntimeName_[symbol.runtimeName] = symbol; + auto rememberProtocolRuntimeName = [&](const std::string& runtimeName) { + if (runtimeName.empty()) { + return; + } + protocolSymbolsByRuntimeName_[runtimeName] = symbol; + Protocol* runtimeProtocol = lookupProtocolByNativeName(runtimeName); + if (runtimeProtocol != nullptr) { + protocolSymbolsByRuntimePointer_[normalizeRuntimePointer( + reinterpret_cast(runtimeProtocol))] = symbol; + } + }; + if (symbol.name.size() > 9 && + std::isdigit(static_cast(symbol.name.back()))) { + size_t digitsStart = symbol.name.size(); + while (digitsStart > 0 && + std::isdigit(static_cast(symbol.name[digitsStart - 1]))) { + digitsStart--; + } + constexpr const char* protocolSuffix = "Protocol"; + size_t protocolSuffixLength = std::strlen(protocolSuffix); + if (digitsStart > protocolSuffixLength && + symbol.name.compare(digitsStart - protocolSuffixLength, + protocolSuffixLength, protocolSuffix) == 0) { + rememberProtocolRuntimeName( + symbol.name.substr(0, digitsStart - protocolSuffixLength)); + } + } + Protocol* protocol = lookupProtocolByNativeName(symbol.runtimeName); + if (protocol == nullptr && symbol.runtimeName != symbol.name) { + protocol = lookupProtocolByNativeName(symbol.name); + } + if (protocol != nullptr) { + protocolSymbolsByRuntimePointer_[normalizeRuntimePointer( + reinterpret_cast(protocol))] = symbol; + } + } else if (kind == NativeApiSymbolKind::Struct) { + structSymbolsByOffset_[symbol.offset] = symbol; + } else if (kind == NativeApiSymbolKind::Union) { + unionSymbolsByOffset_[symbol.offset] = symbol; + } + } + + void addAggregateAliases(NativeApiSymbolKind kind, MDSectionOffset offset, + const std::string& name) { + if (name.empty()) { + return; + } + + if (!name.empty() && name[0] == '_') { + std::string alias = name.substr(1); + if (!alias.empty() && symbolsByName_.find(alias) == symbolsByName_.end()) { + addSymbol(kind, offset, alias.c_str(), name.c_str()); + } + } + + constexpr const char* suffix = "Struct"; + if (name.size() < std::strlen(suffix) || + name.compare(name.size() - std::strlen(suffix), std::strlen(suffix), + suffix) != 0) { + std::string alias = name + suffix; + if (symbolsByName_.find(alias) == symbolsByName_.end()) { + addSymbol(kind, offset, alias.c_str(), name.c_str()); + } + } + } + + void buildSymbolIndexes() { + if (metadata_ == nullptr) { + return; + } + + indexConstants(); + indexEnums(); + indexFunctions(); + indexProtocols(); + indexClasses(); + indexStructs(); + indexUnions(); + } + + static void skipConstantValue(MDMetadataReader* metadata, + MDSectionOffset& offset, + metagen::MDVariableEvalKind evalKind) { + switch (evalKind) { + case metagen::mdEvalNone: + skipMetadataEngineType(metadata, &offset); + break; + case metagen::mdEvalInt64: + offset += sizeof(int64_t); + break; + case metagen::mdEvalDouble: + offset += sizeof(double); + break; + case metagen::mdEvalString: + offset += sizeof(MDSectionOffset); + break; + } + } + + void indexConstants() { + MDSectionOffset offset = metadata_->constantsOffset; + while (offset < metadata_->enumsOffset) { + MDSectionOffset originalOffset = offset; + addSymbol(NativeApiSymbolKind::Constant, originalOffset, + metadata_->getString(offset)); + offset += sizeof(MDSectionOffset); + auto evalKind = metadata_->getVariableEvalKind(offset); + offset += sizeof(metagen::MDVariableEvalKind); + skipConstantValue(metadata_.get(), offset, evalKind); + } + } + + void indexEnums() { + MDSectionOffset offset = metadata_->enumsOffset; + while (offset < metadata_->signaturesOffset) { + MDSectionOffset originalOffset = offset; + addSymbol(NativeApiSymbolKind::Enum, originalOffset, + metadata_->getString(offset)); + offset += sizeof(MDSectionOffset); + + bool next = true; + while (next) { + auto nameOffset = metadata_->getOffset(offset); + next = (nameOffset & metagen::mdSectionOffsetNext) != 0; + offset += sizeof(MDSectionOffset); + offset += sizeof(int64_t); + } + } + } + + void indexFunctions() { + MDSectionOffset offset = metadata_->functionsOffset; + while (offset < metadata_->protocolsOffset) { + MDSectionOffset originalOffset = offset; + addSymbol(NativeApiSymbolKind::Function, originalOffset, + metadata_->getString(offset)); + offset += sizeof(MDSectionOffset); + offset += sizeof(MDSectionOffset); + offset += sizeof(metagen::MDFunctionFlag); + } + } + + void indexProtocols() { + MDSectionOffset offset = metadata_->protocolsOffset; + while (offset < metadata_->classesOffset) { + MDSectionOffset originalOffset = offset; + auto nameOffset = metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + bool next = (nameOffset & metagen::mdSectionOffsetNext) != 0; + nameOffset &= ~metagen::mdSectionOffsetNext; + addSymbol(NativeApiSymbolKind::Protocol, originalOffset, + metadata_->resolveString(nameOffset)); + + while (next) { + auto protocolOffset = metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + next = (protocolOffset & metagen::mdSectionOffsetNext) != 0; + } + + next = true; + while (next) { + auto flags = metadata_->getMemberFlag(offset); + next = (flags & metagen::mdMemberNext) != 0; + offset += sizeof(flags); + if (flags == metagen::mdMemberFlagNull) { + break; + } + + skipMember(flags, offset); + } + } + } + + void indexClasses() { + MDSectionOffset offset = metadata_->classesOffset; + while (offset < metadata_->structsOffset) { + MDSectionOffset originalOffset = offset; + auto nameOffset = metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + auto runtimeNameOffset = metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + bool hasProtocols = (nameOffset & metagen::mdSectionOffsetNext) != 0; + nameOffset &= ~metagen::mdSectionOffsetNext; + + auto name = metadata_->resolveString(nameOffset); + const char* runtimeName = name; + if (runtimeNameOffset != MD_SECTION_OFFSET_NULL) { + runtimeName = metadata_->resolveString(runtimeNameOffset); + } + + while (hasProtocols) { + auto protocolOffset = metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + hasProtocols = (protocolOffset & metagen::mdSectionOffsetNext) != 0; + } + + auto superclass = metadata_->getOffset(offset); + offset += sizeof(superclass); + MDSectionOffset superclassOffset = + superclass & ~metagen::mdSectionOffsetNext; + if (superclassOffset != MD_SECTION_OFFSET_NULL) { + superclassOffset += metadata_->classesOffset; + } + + addSymbol(NativeApiSymbolKind::Class, originalOffset, name, runtimeName, + superclassOffset); + + bool next = (superclass & metagen::mdSectionOffsetNext) != 0; + while (next) { + auto flags = metadata_->getMemberFlag(offset); + next = (flags & metagen::mdMemberNext) != 0; + offset += sizeof(flags); + skipMember(flags, offset); + } + } + } + + void skipAggregateFields(MDSectionOffset& offset, bool isUnion) const { + bool next = true; + while (next) { + MDSectionOffset nameOffset = metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + next = (nameOffset & metagen::mdSectionOffsetNext) != 0; + nameOffset &= ~metagen::mdSectionOffsetNext; + if (nameOffset == MD_SECTION_OFFSET_NULL) { + break; + } + if (!isUnion) { + offset += sizeof(uint16_t); + } + skipMetadataEngineType(metadata_.get(), &offset); + } + } + + void indexStructs() { + MDSectionOffset offset = metadata_->structsOffset; + while (offset < metadata_->unionsOffset) { + if (metadata_->getOffset(offset) == 0) { + break; + } + MDSectionOffset originalOffset = offset; + const char* name = metadata_->getString(offset); + offset += sizeof(MDSectionOffset); + offset += sizeof(uint16_t); + addSymbol(NativeApiSymbolKind::Struct, originalOffset, name); + addAggregateAliases(NativeApiSymbolKind::Struct, originalOffset, + name != nullptr ? name : ""); + skipAggregateFields(offset, false); + } + } + + void indexUnions() { + MDSectionOffset offset = metadata_->unionsOffset; + while (metadata_->getOffset(offset) != 0) { + MDSectionOffset originalOffset = offset; + const char* name = metadata_->getString(offset); + offset += sizeof(MDSectionOffset); + offset += sizeof(uint16_t); + addSymbol(NativeApiSymbolKind::Union, originalOffset, name); + addAggregateAliases(NativeApiSymbolKind::Union, originalOffset, + name != nullptr ? name : ""); + skipAggregateFields(offset, true); + } + } + + void skipMember(MDMemberFlag flags, MDSectionOffset& offset) const { + if ((flags & metagen::mdMemberProperty) != 0) { + bool readonly = (flags & metagen::mdMemberReadonly) != 0; + offset += sizeof(MDSectionOffset); + offset += sizeof(MDSectionOffset); + offset += sizeof(MDSectionOffset); + if (!readonly) { + offset += sizeof(MDSectionOffset); + offset += sizeof(MDSectionOffset); + } + return; + } + + offset += sizeof(MDSectionOffset); + offset += sizeof(MDSectionOffset); + } + + std::vector readProtocolOffsetsForClass( + MDSectionOffset classOffset, MDSectionOffset* memberOffset = nullptr, + MDSectionOffset* superclassOffsetOut = nullptr) const { + std::vector protocols; + if (metadata_ == nullptr || classOffset == MD_SECTION_OFFSET_NULL) { + return protocols; + } + + MDSectionOffset offset = classOffset; + auto nameOffset = metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + offset += sizeof(MDSectionOffset); + bool hasProtocols = (nameOffset & metagen::mdSectionOffsetNext) != 0; + + while (hasProtocols) { + auto protocolOffset = metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + hasProtocols = (protocolOffset & metagen::mdSectionOffsetNext) != 0; + protocolOffset &= ~metagen::mdSectionOffsetNext; + if (protocolOffset != MD_SECTION_OFFSET_NULL) { + protocols.push_back(protocolOffset + metadata_->protocolsOffset); + } + } + + auto superclass = metadata_->getOffset(offset); + offset += sizeof(superclass); + const bool hasMembers = (superclass & metagen::mdSectionOffsetNext) != 0; + if (superclassOffsetOut != nullptr) { + MDSectionOffset superclassOffset = + superclass & ~metagen::mdSectionOffsetNext; + *superclassOffsetOut = + superclassOffset != MD_SECTION_OFFSET_NULL + ? superclassOffset + metadata_->classesOffset + : MD_SECTION_OFFSET_NULL; + } + if (memberOffset != nullptr) { + *memberOffset = hasMembers ? offset : MD_SECTION_OFFSET_NULL; + } + return protocols; + } + + std::vector readOwnMembersForClass( + MDSectionOffset classOffset) const { + std::vector members; + if (metadata_ == nullptr || classOffset == MD_SECTION_OFFSET_NULL) { + return members; + } + + MDSectionOffset memberOffset = MD_SECTION_OFFSET_NULL; + for (MDSectionOffset protocolOffset : + readProtocolOffsetsForClass(classOffset, &memberOffset)) { + auto protocol = protocolSymbolsByOffset_.find(protocolOffset); + if (protocol == protocolSymbolsByOffset_.end()) { + continue; + } + const auto& protocolMembers = membersForProtocol(protocol->second); + members.insert(members.end(), protocolMembers.begin(), + protocolMembers.end()); + } + + if (memberOffset != MD_SECTION_OFFSET_NULL) { + std::vector ownMembers = + readMembersAtOffset(memberOffset); + members.insert(members.end(), ownMembers.begin(), ownMembers.end()); + } + return members; + } + + std::vector readMembersForClass( + MDSectionOffset classOffset) const { + std::vector members; + if (metadata_ == nullptr || classOffset == MD_SECTION_OFFSET_NULL) { + return members; + } + + MDSectionOffset offset = classOffset; + auto nameOffset = metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + offset += sizeof(MDSectionOffset); + bool hasProtocols = (nameOffset & metagen::mdSectionOffsetNext) != 0; + + while (hasProtocols) { + auto protocolOffset = metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + hasProtocols = (protocolOffset & metagen::mdSectionOffsetNext) != 0; + } + + auto superclass = metadata_->getOffset(offset); + offset += sizeof(superclass); + + bool next = (superclass & metagen::mdSectionOffsetNext) != 0; + while (next) { + auto flags = metadata_->getMemberFlag(offset); + next = (flags & metagen::mdMemberNext) != 0; + offset += sizeof(flags); + if (flags == metagen::mdMemberFlagNull) { + break; + } + + NativeApiMember member; + member.flags = flags; + if ((flags & metagen::mdMemberProperty) != 0) { + member.property = true; + member.readonly = (flags & metagen::mdMemberReadonly) != 0; + member.name = metadata_->getString(offset); + offset += sizeof(MDSectionOffset); + member.selectorName = metadata_->getString(offset); + offset += sizeof(MDSectionOffset); + member.signatureOffset = + metadata_->signaturesOffset + metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + + if (!member.readonly) { + member.setterSelectorName = metadata_->getString(offset); + offset += sizeof(MDSectionOffset); + member.setterSignatureOffset = + metadata_->signaturesOffset + metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + } + } else { + member.selectorName = metadata_->getString(offset); + offset += sizeof(MDSectionOffset); + member.signatureOffset = + metadata_->signaturesOffset + metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + member.name = jsifySelector(member.selectorName.c_str()); + } + members.push_back(std::move(member)); + } + + return members; + } + + static bool memberIsStatic(const NativeApiMember& member) { + return (member.flags & metagen::mdMemberStatic) != 0; + } + + static bool sameMemberSlot(const NativeApiMember& lhs, + const NativeApiMember& rhs) { + return lhs.name == rhs.name && lhs.property == rhs.property && + memberIsStatic(lhs) == memberIsStatic(rhs); + } + + static bool sameMethodSelector(const NativeApiMember& lhs, + const NativeApiMember& rhs) { + return !lhs.property && !rhs.property && sameMemberSlot(lhs, rhs) && + lhs.selectorName == rhs.selectorName; + } + + static const NativeApiMember* findPropertyMember( + const std::vector& members, + const NativeApiMember& candidate) { + for (const auto& member : members) { + if (member.property && sameMemberSlot(member, candidate)) { + return &member; + } + } + return nullptr; + } + + static bool selectorExistsInMembers( + const std::vector& members, + const NativeApiMember& candidate) { + for (const auto& member : members) { + if (sameMethodSelector(member, candidate)) { + return true; + } + } + return false; + } + + static bool shouldSkipPropertyOverride( + const NativeApiMember* inherited, const NativeApiMember& member) { + if (inherited == nullptr || !inherited->property) { + return false; + } + + bool sameGetter = inherited->selectorName == member.selectorName; + bool sameSetter = + inherited->setterSelectorName == member.setterSelectorName; + if ((!inherited->readonly && member.readonly) || + (inherited->readonly == member.readonly && sameGetter && + (member.readonly || sameSetter))) { + return true; + } + return false; + } + + static void appendSurfaceMember( + std::vector& surface, + const std::vector& inheritedMembers, + const NativeApiMember& member) { + if (member.name.empty()) { + return; + } + + if (member.property) { + const NativeApiMember* inherited = + findPropertyMember(inheritedMembers, member); + if (shouldSkipPropertyOverride(inherited, member)) { + return; + } + + for (auto& existing : surface) { + if (!existing.property || !sameMemberSlot(existing, member)) { + continue; + } + if (existing.readonly && !member.readonly) { + existing = member; + } + return; + } + surface.push_back(member); + return; + } + + const bool keepInheritedMethod = + member.name == "alloc" || member.name == "toString" || + member.name == "superclass"; + if (!keepInheritedMethod && + selectorExistsInMembers(inheritedMembers, member)) { + return; + } + if (selectorExistsInMembers(surface, member)) { + return; + } + surface.push_back(member); + } + + std::vector readSurfaceMembersForClass( + const NativeApiSymbol& symbol) const { + std::vector inheritedMembers; + if (symbol.superclassOffset != MD_SECTION_OFFSET_NULL) { + auto superclass = classSymbolsByOffset_.find(symbol.superclassOffset); + if (superclass != classSymbolsByOffset_.end()) { + const auto& inherited = surfaceMembersForClass(superclass->second); + inheritedMembers.insert(inheritedMembers.end(), inherited.begin(), + inherited.end()); + } + } + + std::vector surface; + for (const auto& member : readOwnMembersForClass(symbol.offset)) { + appendSurfaceMember(surface, inheritedMembers, member); + } + return surface; + } + + std::vector readMembersAtOffset( + MDSectionOffset& offset) const { + std::vector members; + bool next = true; + while (next) { + auto flags = metadata_->getMemberFlag(offset); + next = (flags & metagen::mdMemberNext) != 0; + offset += sizeof(flags); + if (flags == metagen::mdMemberFlagNull) { + break; + } + + NativeApiMember member; + member.flags = flags; + if ((flags & metagen::mdMemberProperty) != 0) { + member.property = true; + member.readonly = (flags & metagen::mdMemberReadonly) != 0; + member.name = metadata_->getString(offset); + offset += sizeof(MDSectionOffset); + member.selectorName = metadata_->getString(offset); + offset += sizeof(MDSectionOffset); + member.signatureOffset = + metadata_->signaturesOffset + metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + + if (!member.readonly) { + member.setterSelectorName = metadata_->getString(offset); + offset += sizeof(MDSectionOffset); + member.setterSignatureOffset = + metadata_->signaturesOffset + metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + } + } else { + member.selectorName = metadata_->getString(offset); + offset += sizeof(MDSectionOffset); + member.signatureOffset = + metadata_->signaturesOffset + metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + member.name = jsifySelector(member.selectorName.c_str()); + } + members.push_back(std::move(member)); + } + return members; + } + + std::vector readMembersForProtocolHierarchy( + MDSectionOffset protocolOffset) const { + std::vector members; + if (metadata_ == nullptr || protocolOffset == MD_SECTION_OFFSET_NULL) { + return members; + } + + MDSectionOffset offset = protocolOffset; + auto nameOffset = metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + bool hasProtocols = (nameOffset & metagen::mdSectionOffsetNext) != 0; + + while (hasProtocols) { + auto inheritedOffset = metadata_->getOffset(offset); + offset += sizeof(MDSectionOffset); + hasProtocols = (inheritedOffset & metagen::mdSectionOffsetNext) != 0; + inheritedOffset &= ~metagen::mdSectionOffsetNext; + if (inheritedOffset == MD_SECTION_OFFSET_NULL) { + continue; + } + + MDSectionOffset absoluteOffset = + inheritedOffset + metadata_->protocolsOffset; + auto inheritedSymbol = protocolSymbolsByOffset_.find(absoluteOffset); + if (inheritedSymbol != protocolSymbolsByOffset_.end()) { + const auto& inheritedMembers = + membersForProtocol(inheritedSymbol->second); + members.insert(members.end(), inheritedMembers.begin(), + inheritedMembers.end()); + } + } + + std::vector ownMembers = readMembersAtOffset(offset); + members.insert(members.end(), ownMembers.begin(), ownMembers.end()); + return members; + } + + std::vector readMembersForClassHierarchy( + const NativeApiSymbol& symbol) const { + std::vector members = readOwnMembersForClass(symbol.offset); + if (symbol.superclassOffset == MD_SECTION_OFFSET_NULL) { + return members; + } + + auto superclass = classSymbolsByOffset_.find(symbol.superclassOffset); + if (superclass != classSymbolsByOffset_.end()) { + const auto& inheritedMembers = membersForClass(superclass->second); + members.insert(members.end(), inheritedMembers.begin(), + inheritedMembers.end()); + } + return members; + } + + std::unique_ptr metadata_; + void* selfDl_ = nullptr; + std::unordered_map symbolsByName_; + std::unordered_map functionSymbolsByName_; + std::unordered_map constantSymbolsByName_; + std::unordered_map enumSymbolsByName_; + std::unordered_map structSymbolsByName_; + std::unordered_map unionSymbolsByName_; + std::unordered_map classSymbolsByRuntimeName_; + std::unordered_map protocolSymbolsByRuntimeName_; + std::unordered_map classSymbolsByRuntimePointer_; + std::unordered_map protocolSymbolsByRuntimePointer_; + mutable std::mutex roundTripValuesMutex_; + std::unordered_map roundTripValues_; + std::unordered_map + roundTripCacheFramesByThread_; + std::unordered_map + recentRoundTripValues_; + std::vector recentRoundTripValueOrder_; +#ifdef TARGET_ENGINE_HERMES + std::unordered_set rootedRoundTripValues_; + std::shared_ptr roundTripRootCache_; +#endif + std::atomic roundTripValuesGeneration_{1}; + std::unordered_map> classValues_; + std::unordered_map> classPrototypes_; + std::unordered_map> pointerValues_; + std::unordered_map>> + objectExpandos_; + std::unordered_map objectExpandoOwnerCounts_; + std::atomic objectExpandosGeneration_{1}; + std::unordered_map> + propertyGetterCache_; + std::atomic propertyGetterCacheGeneration_{1}; + std::unordered_map classSymbolsByOffset_; + std::unordered_map protocolSymbolsByOffset_; + std::vector classNames_; + std::vector functionNames_; + std::vector constantNames_; + std::vector protocolNames_; + std::vector enumNames_; + std::vector structNames_; + std::vector unionNames_; + std::shared_ptr scheduler_; + std::function)> nativeInvocationInvoker_; + std::function)> nativeCallbackInvoker_; + std::function)> runtimeCallbackInvoker_; + std::function)> jsThreadCallbackInvoker_; + std::function)> jsThreadAsyncCallbackInvoker_; + bool invokeCallbacksOnNativeCallerThread_ = false; + mutable std::unordered_map> + membersByClassOffset_; + mutable std::unordered_map> + surfaceMembersByClassOffset_; + mutable std::unordered_map> + membersByProtocolOffset_; + std::unordered_map structSymbolsByOffset_; + std::unordered_map unionSymbolsByOffset_; + std::unordered_map> + aggregateInfoByOffset_; + std::unordered_set aggregateInfoInProgress_; + std::thread::id jsThreadId_ = std::this_thread::get_id(); + std::mutex retainedLifetimesMutex_; + std::vector> retainedLifetimes_; +}; + +class NativeApiRoundTripCacheFrameGuard final { + public: + explicit NativeApiRoundTripCacheFrameGuard( + const std::shared_ptr& bridge) + : bridge_(bridge) { + if (bridge_ != nullptr) { + bridge_->beginRoundTripCacheFrame(); + } + } + + ~NativeApiRoundTripCacheFrameGuard() { + if (bridge_ != nullptr) { + bridge_->endRoundTripCacheFrame(); + } + } + + NativeApiRoundTripCacheFrameGuard( + const NativeApiRoundTripCacheFrameGuard&) = delete; + NativeApiRoundTripCacheFrameGuard& operator=( + const NativeApiRoundTripCacheFrameGuard&) = delete; + + private: + std::shared_ptr bridge_; +}; + +template +void performGeneratedObjCInvocation( + Runtime& runtime, const std::shared_ptr& bridge, + Invocation&& invocation) { + const auto& invoker = bridge->nativeInvocationInvoker(); + if (invoker || bridge->invokeCallbacksOnNativeCallerThread()) { + performNativeInvocation(runtime, invoker, [&]() { invocation(); }); + } else { + invocation(); + } +} + +bool nativeObjectReturnMayCoerceToString(const NativeApiType& type) { + return type.kind == metagen::mdTypeAnyObject || + type.kind == metagen::mdTypeNSStringObject; +} + +bool nativeObjectIsStringLike(id object) { + if (object == nil) { + return false; + } + Class cls = object_getClass(object); + struct StringLikeClassCacheEntry { + Class cls = Nil; + bool stringLike = false; + }; + static thread_local StringLikeClassCacheEntry cache[4]; + const size_t firstSlot = (reinterpret_cast(cls) >> 4) & 3; + for (size_t i = 0; i < 4; i++) { + const auto& entry = cache[(firstSlot + i) & 3]; + if (entry.cls == cls) { + return entry.stringLike; + } + } + bool stringLike = [object isKindOfClass:[NSString class]]; + cache[firstSlot] = StringLikeClassCacheEntry{cls, stringLike}; + return stringLike; +} + +Value findCachedNativeObjectReturn(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiType& type, id object) { + bool roundTripStringLike = false; + const bool stringReturnCandidate = nativeObjectReturnMayCoerceToString(type); + // AnyObject/NSString returns intentionally coerce string-like native objects + // to JS strings, so cached identity is only valid for non-string wrappers. + if (stringReturnCandidate && nativeObjectIsStringLike(object)) { + return Value::undefined(); + } + Value roundTrip = bridge->findRoundTripValue( + runtime, object, stringReturnCandidate ? &roundTripStringLike : nullptr, + true); + if (!roundTrip.isUndefined() && !roundTripStringLike) { + return roundTrip; + } + return Value::undefined(); +} + +Value makeString(Runtime& runtime, const std::string& value) { + return String::createFromUtf8(runtime, value); +} + +std::string readStringArg(Runtime& runtime, const Value* args, size_t count, + size_t index, const char* argumentName) { + if (index >= count || !args[index].isString()) { + throw JSError( + runtime, std::string(argumentName) + " must be a string."); + } + return args[index].asString(runtime).utf8(runtime); +} + +const char* kindName(NativeApiSymbolKind kind) { + switch (kind) { + case NativeApiSymbolKind::Class: + return "class"; + case NativeApiSymbolKind::Function: + return "function"; + case NativeApiSymbolKind::Constant: + return "constant"; + case NativeApiSymbolKind::Protocol: + return "protocol"; + case NativeApiSymbolKind::Enum: + return "enum"; + case NativeApiSymbolKind::Struct: + return "struct"; + case NativeApiSymbolKind::Union: + return "union"; + } + return "unknown"; +} + +Array namesToArray(Runtime& runtime, const std::vector& names) { + Array result(runtime, names.size()); + for (size_t i = 0; i < names.size(); i++) { + result.setValueAtIndex(runtime, i, makeString(runtime, names[i])); + } + return result; +} + +void addPropertyName(Runtime& runtime, std::vector& names, + const char* name) { + names.push_back(PropNameID::forAscii(runtime, name)); +} + +class NativeApiPointerHostObject; +class NativeApiObjectHostObject; +class NativeApiClassHostObject; +class NativeApiProtocolHostObject; +class NativeApiArgumentFrame; +struct NativeApiPreparedCFunctionInvocation; +struct NativeApiPreparedObjCInvocation; + +Value callCFunction(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiSymbol& symbol, const Value* args, + size_t count); +Value callCFunction( + Runtime& runtime, const std::shared_ptr& bridge, + const std::shared_ptr& prepared, + const Value* args, size_t count); + +Value callObjCSelector(Runtime& runtime, + const std::shared_ptr& bridge, + id receiver, bool receiverIsClass, + const std::string& selectorName, + const NativeApiMember* member, + const Value* args, size_t count, + Class dispatchSuperClass = Nil); + +std::shared_ptr +prepareNativeApiObjCInvocation( + Runtime& runtime, const std::shared_ptr& bridge, + Class lookupClass, bool receiverIsClass, const std::string& selectorName, + const NativeApiMember* member); + +Value callPreparedObjCSelector( + Runtime& runtime, const std::shared_ptr& bridge, + id receiver, bool receiverIsClass, + const NativeApiPreparedObjCInvocation& prepared, const Value* args, + size_t count, Class dispatchSuperClass = Nil); + +void* lookupGeneratedEngineObjCGsdInvoker(uint64_t dispatchId); +bool tryCallGeneratedEngineObjCSelector( + Runtime& runtime, const std::shared_ptr& bridge, + id receiver, const NativeApiPreparedObjCInvocation& prepared, + const Value* args, size_t count, Class dispatchSuperClass, Value* result); + +Function CreateNativeApiSelectorGroupFunction( + Runtime& runtime, std::shared_ptr bridge, + Class lookupClass, bool receiverIsClass, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations); + +Function CreateNativeApiBoundSelectorGroupFunction( + Runtime& runtime, std::shared_ptr bridge, Class lookupClass, + std::shared_ptr receiverHostObject, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations); + +Value makeNativeObjectValue(Runtime& runtime, + const std::shared_ptr& bridge, + id object, bool ownsObject); + +Value makeNativeClassValue(Runtime& runtime, + const std::shared_ptr& bridge, + NativeApiSymbol symbol); + +Object symbolToObject(Runtime& runtime, const NativeApiSymbol& symbol) { + Object result(runtime); + result.setProperty(runtime, "kind", makeString(runtime, kindName(symbol.kind))); + result.setProperty(runtime, "name", makeString(runtime, symbol.name)); + result.setProperty(runtime, "runtimeName", + makeString(runtime, symbol.runtimeName)); + result.setProperty(runtime, "metadataOffset", + static_cast(symbol.offset)); + + if (symbol.kind == NativeApiSymbolKind::Class) { + Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); + result.setProperty(runtime, "available", cls != nil); + if (cls != nil) { + char address[32] = {}; + snprintf(address, sizeof(address), "%p", cls); + result.setProperty(runtime, "nativeAddress", makeString(runtime, address)); + } + } else if (symbol.kind == NativeApiSymbolKind::Struct || + symbol.kind == NativeApiSymbolKind::Union) { + result.setProperty(runtime, "available", true); + } + + return result; +} + +size_t nativeSizeForType(const NativeApiType& type); +std::optional parseArrayIndexProperty(const std::string& property); + +NativeApiType nativeObjectReturnType( + MDTypeKind kind = metagen::mdTypeAnyObject) { + NativeApiType type; + type.kind = kind; + type.ffiType = &ffi_type_pointer; + type.supported = true; + return type; +} + +NativeApiType nativeObjectReturnTypeForClass(Class cls) { + if (cls != Nil) { + const char* name = class_getName(cls); + if (name != nullptr && std::strcmp(name, "NSString") == 0) { + return nativeObjectReturnType(metagen::mdTypeNSStringObject); + } + if (name != nullptr && std::strcmp(name, "NSMutableString") == 0) { + return nativeObjectReturnType(metagen::mdTypeNSMutableStringObject); + } + } + return nativeObjectReturnType(metagen::mdTypeInstanceObject); +} + +Value convertNativeReturnValue(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiType& type, void* value); +Object createPointer(Runtime& runtime, + const std::shared_ptr& bridge, + void* pointer, bool adopted = false, + std::shared_ptr backingValue = nullptr); + +NativeApiType primitiveInteropType(MDTypeKind kind); diff --git a/NativeScript/ffi/objc/shared/bridge/TypeConv.mm b/NativeScript/ffi/objc/shared/bridge/TypeConv.mm new file mode 100644 index 000000000..24d6be0cc --- /dev/null +++ b/NativeScript/ffi/objc/shared/bridge/TypeConv.mm @@ -0,0 +1,2159 @@ +std::string stringPropertyOrEmpty(Runtime& runtime, const Object& object, const char* name); +void* pointerFromSymbolLikeObject(Runtime& runtime, const Object& object); + +id objectFromEngineValue(Runtime& runtime, const std::shared_ptr& bridge, + const Value& value, NativeApiArgumentFrame& frame, bool mutableString) { + if (value.isNull() || value.isUndefined()) { + return nil; + } + if (value.isString()) { + std::string utf8 = value.asString(runtime).utf8(runtime); + id string = mutableString ? [[NSMutableString alloc] initWithBytes:utf8.data() + length:utf8.size() + encoding:NSUTF8StringEncoding] + : [[NSString alloc] initWithBytes:utf8.data() + length:utf8.size() + encoding:NSUTF8StringEncoding]; + frame.addObject(string); + return string; + } + if (value.isBool()) { + return [NSNumber numberWithBool:value.getBool()]; + } + if (value.isNumber()) { + return [NSNumber numberWithDouble:value.getNumber()]; + } + if (value.isObject()) { + Object object = value.asObject(runtime); + if (object.isHostObject(runtime)) { + return object.getHostObject(runtime)->object(); + } + if (Class cls = nativeClassFromEngineObject(runtime, object)) { + return static_cast(cls); + } + if (object.isHostObject(runtime)) { + return static_cast( + object.getHostObject(runtime)->nativeProtocol()); + } + if (void* symbolPointer = pointerFromSymbolLikeObject(runtime, object)) { + return static_cast(symbolPointer); + } + if (object.isHostObject(runtime)) { + return static_cast(object.getHostObject(runtime)->pointer()); + } + if (object.isHostObject(runtime)) { + return static_cast(object.getHostObject(runtime)->data()); + } + if (object.isHostObject(runtime)) { + return static_cast( + object.getHostObject(runtime)->data()); + } + + Value getTimeValue = object.getProperty(runtime, "getTime"); + Value toISOStringValue = object.getProperty(runtime, "toISOString"); + if (getTimeValue.isObject() && getTimeValue.asObject(runtime).isFunction(runtime) && + toISOStringValue.isObject() && toISOStringValue.asObject(runtime).isFunction(runtime)) { + Value millisValue = getTimeValue.asObject(runtime).asFunction(runtime).callWithThis( + runtime, object, nullptr, 0); + if (millisValue.isNumber()) { + NSDate* date = [NSDate dateWithTimeIntervalSince1970:millisValue.getNumber() / 1000.0]; + bridge->rememberScopedRoundTripValue(runtime, date, value, false, true); + return date; + } + } + + Value valueOfValue = object.getProperty(runtime, "valueOf"); + if (valueOfValue.isObject() && valueOfValue.asObject(runtime).isFunction(runtime)) { + Value primitiveValue = valueOfValue.asObject(runtime).asFunction(runtime).callWithThis( + runtime, object, nullptr, 0); + if (primitiveValue.isString() || primitiveValue.isBool() || primitiveValue.isNumber()) { + return objectFromEngineValue(runtime, bridge, primitiveValue, frame, mutableString); + } + } + + const uint8_t* bytes = nullptr; + size_t byteLength = 0; + if (readEngineBuffer(runtime, object, &bytes, &byteLength)) { + NSData* data = [NSData dataWithBytes:bytes length:byteLength]; + bridge->rememberScopedRoundTripValue(runtime, data, value, false, false); + return data; + } + + if (object.isArray(runtime)) { + Array array = object.getArray(runtime); + NSMutableArray* nativeArray = [NSMutableArray arrayWithCapacity:array.size(runtime)]; + for (size_t i = 0; i < array.size(runtime); i++) { + id element = + objectFromEngineValue(runtime, bridge, array.getValueAtIndex(runtime, i), frame, false); + [nativeArray addObject:element != nil ? element : [NSNull null]]; + } + bridge->rememberScopedRoundTripValue(runtime, nativeArray, value, false, false); + return nativeArray; + } + + Value lengthValue = object.getProperty(runtime, "length"); + if (lengthValue.isNumber() && std::isfinite(lengthValue.getNumber()) && + lengthValue.getNumber() >= 0) { + size_t length = static_cast(std::floor(lengthValue.getNumber())); + NSMutableArray* nativeArray = [NSMutableArray arrayWithCapacity:length]; + for (size_t i = 0; i < length; i++) { + std::string key = std::to_string(i); + id element = objectFromEngineValue(runtime, bridge, + object.getProperty(runtime, key.c_str()), frame, false); + [nativeArray addObject:element != nil ? element : [NSNull null]]; + } + bridge->rememberScopedRoundTripValue(runtime, nativeArray, value, false, false); + return nativeArray; + } + + Value entriesValue = object.getProperty(runtime, "entries"); + Value sizeValue = object.getProperty(runtime, "size"); + Value getValue = object.getProperty(runtime, "get"); + if (entriesValue.isObject() && entriesValue.asObject(runtime).isFunction(runtime) && + sizeValue.isNumber() && getValue.isObject() && + getValue.asObject(runtime).isFunction(runtime)) { + Object arrayCtor = runtime.global().getPropertyAsObject(runtime, "Array"); + Function arrayFrom = arrayCtor.getPropertyAsFunction(runtime, "from"); + Value iterator = entriesValue.asObject(runtime).asFunction(runtime).callWithThis( + runtime, object, nullptr, 0); + Value pairsValue = arrayFrom.call(runtime, iterator); + if (pairsValue.isObject() && pairsValue.asObject(runtime).isArray(runtime)) { + Array pairs = pairsValue.asObject(runtime).getArray(runtime); + NSMutableDictionary* nativeMap = + [NSMutableDictionary dictionaryWithCapacity:pairs.size(runtime)]; + for (size_t i = 0; i < pairs.size(runtime); i++) { + Value pairValue = pairs.getValueAtIndex(runtime, i); + if (!pairValue.isObject() || !pairValue.asObject(runtime).isArray(runtime)) { + continue; + } + Array pair = pairValue.asObject(runtime).getArray(runtime); + if (pair.size(runtime) < 2) { + continue; + } + id key = objectFromEngineValue(runtime, bridge, pair.getValueAtIndex(runtime, 0), frame, + false); + id nativeValue = objectFromEngineValue(runtime, bridge, pair.getValueAtIndex(runtime, 1), + frame, false); + if (key != nil) { + [nativeMap setObject:nativeValue != nil ? nativeValue : [NSNull null] forKey:key]; + } + } + bridge->rememberScopedRoundTripValue(runtime, nativeMap, value, false, false); + return nativeMap; + } + } + + NSMutableDictionary* dictionary = [NSMutableDictionary dictionary]; + Array propertyNames = object.getPropertyNames(runtime); + for (size_t i = 0; i < propertyNames.size(runtime); i++) { + Value propertyNameValue = propertyNames.getValueAtIndex(runtime, i); + if (!propertyNameValue.isString()) { + continue; + } + std::string key = propertyNameValue.asString(runtime).utf8(runtime); + Value propertyValue = object.getProperty(runtime, key.c_str()); + if (propertyValue.isUndefined()) { + continue; + } + id nativeValue = objectFromEngineValue(runtime, bridge, propertyValue, frame, false); + NSString* nativeKey = [NSString stringWithUTF8String:key.c_str()]; + if (nativeKey != nil) { + [dictionary setObject:nativeValue != nil ? nativeValue : [NSNull null] forKey:nativeKey]; + } + } + bridge->rememberScopedRoundTripValue(runtime, dictionary, value, false, false); + return dictionary; + } + throw JSError(runtime, "Value cannot be converted to Objective-C object."); +} + +std::string utf8StringFromNSString(NSString* string) { + if (string == nil) { + return ""; + } + NSUInteger length = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + std::string result(length, '\0'); + NSUInteger usedLength = 0; + NSRange remainingRange = NSMakeRange(0, 0); + BOOL ok = [string getBytes:result.data() + maxLength:length + usedLength:&usedLength + encoding:NSUTF8StringEncoding + options:0 + range:NSMakeRange(0, string.length) + remainingRange:&remainingRange]; + if (!ok) { + return string.UTF8String ?: ""; + } + result.resize(usedLength); + return result; +} + +char* copyCStringForReference(const char* string, size_t* byteLength = nullptr) { + size_t length = string != nullptr ? std::strlen(string) + 1 : 1; + char* copy = static_cast(malloc(length)); + if (copy == nullptr) { + throw std::bad_alloc(); + } + if (string != nullptr) { + std::memcpy(copy, string, length); + } else { + copy[0] = '\0'; + } + if (byteLength != nullptr) { + *byteLength = length; + } + return copy; +} + +bool readNativePointerProperty(Runtime& runtime, const Object& object, void** pointer) { + if (pointer == nullptr) { + return false; + } + + Value nativePointerObjectValue = object.getProperty(runtime, "__nativeApiPointerObject"); + if (nativePointerObjectValue.isObject()) { + Object nativePointerObject = nativePointerObjectValue.asObject(runtime); + if (nativePointerObject.isHostObject(runtime)) { + *pointer = nativePointerObject.getHostObject(runtime)->pointer(); + return true; + } + } + + Value nativePointerValue = object.getProperty(runtime, "__nativeApiPointer"); + if (nativePointerValue.isNumber()) { + *pointer = reinterpret_cast(static_cast(nativePointerValue.getNumber())); + return true; + } + + Value nativeAddressValue = object.getProperty(runtime, "nativeAddress"); + if (nativeAddressValue.isNumber()) { + *pointer = reinterpret_cast(static_cast(nativeAddressValue.getNumber())); + return true; + } + + return false; +} + +std::string stringPropertyOrEmpty(Runtime& runtime, const Object& object, const char* name) { + if (name == nullptr || !object.hasProperty(runtime, name)) { + return ""; + } + Value value = object.getProperty(runtime, name); + return value.isString() ? value.asString(runtime).utf8(runtime) : ""; +} + +constexpr const char* kNativeApiCallbackEncodingProperty = "__nativeApiCallbackEncoding"; + +Function interopCallbackFromArguments(Runtime& runtime, const char* constructorName, + const char* kind, const Value* args, size_t count) { + if (count < 1) { + throw JSError(runtime, std::string(constructorName) + " expects a function."); + } + + std::optional callbackObject; + bool hasCallback = false; + std::string encoding; + + for (size_t i = 0; i < count; i++) { + const Value& arg = args[i]; + if (arg.isUndefined() || arg.isNull()) { + continue; + } + if (arg.isString()) { + if (!encoding.empty()) { + throw JSError(runtime, std::string(constructorName) + + " expects only one Objective-C encoding string."); + } + encoding = arg.asString(runtime).utf8(runtime); + continue; + } + if (arg.isObject()) { + Object object = arg.asObject(runtime); + if (object.isFunction(runtime)) { + if (hasCallback) { + throw JSError(runtime, std::string(constructorName) + " expects only one function."); + } + callbackObject.emplace(std::move(object)); + hasCallback = true; + continue; + } + } + + throw JSError(runtime, std::string(constructorName) + + " expects a function and an optional Objective-C encoding string."); + } + + if (!hasCallback) { + throw JSError(runtime, std::string(constructorName) + " expects a function."); + } + + Function function = callbackObject->asFunction(runtime); + function.setProperty(runtime, "kind", makeString(runtime, kind)); + function.setProperty(runtime, "sizeof", static_cast(sizeof(void*))); + if (!encoding.empty()) { + function.setProperty(runtime, kNativeApiCallbackEncodingProperty, + makeString(runtime, encoding)); + } + + return function; +} + +void* pointerFromSymbolLikeObject(Runtime& runtime, const Object& object) { + std::string kind = stringPropertyOrEmpty(runtime, object, "kind"); + if (kind != "class" && kind != "protocol") { + return nullptr; + } + + std::string runtimeName = stringPropertyOrEmpty(runtime, object, "runtimeName"); + if (runtimeName.empty()) { + runtimeName = stringPropertyOrEmpty(runtime, object, "name"); + } + if (runtimeName.empty()) { + return nullptr; + } + + if (kind == "class") { + return objc_lookUpClass(runtimeName.c_str()); + } + return lookupProtocolByNativeName(runtimeName); +} + +void* pointerFromEngineValue(Runtime& runtime, const std::shared_ptr& bridge, + const Value& value, NativeApiArgumentFrame& frame) { + if (value.isNull() || value.isUndefined()) { + return nullptr; + } + if (value.isNumber()) { + return reinterpret_cast(static_cast(value.getNumber())); + } + if (value.isObject()) { + Object object = value.asObject(runtime); + if (object.isHostObject(runtime)) { + return object.getHostObject(runtime)->pointer(); + } + if (object.isHostObject(runtime)) { + return object.getHostObject(runtime)->object(); + } + if (Class cls = nativeClassFromEngineObject(runtime, object)) { + return cls; + } + if (object.isHostObject(runtime)) { + return object.getHostObject(runtime)->nativeProtocol(); + } + if (void* symbolPointer = pointerFromSymbolLikeObject(runtime, object)) { + return symbolPointer; + } + if (object.isHostObject(runtime)) { + auto reference = object.getHostObject(runtime); + if (reference->data() == nullptr) { + reference->ensureStorage(runtime, reference->type(), frame); + } + return reference->data(); + } + if (object.isHostObject(runtime)) { + return object.getHostObject(runtime)->data(); + } + void* nativePointer = nullptr; + if (readNativePointerProperty(runtime, object, &nativePointer)) { + return nativePointer; + } + const uint8_t* bytes = nullptr; + size_t byteLength = 0; + if (readEngineBuffer(runtime, object, &bytes, &byteLength)) { + return const_cast(bytes); + } + } + if (value.isString()) { + std::string utf8 = value.asString(runtime).utf8(runtime); + char* string = strdup(utf8.c_str()); + if (string == nullptr) { + throw std::bad_alloc(); + } + if (bridge != nullptr) { + bridge->rememberScopedRawRoundTripValue(runtime, string, value, true, false); + } + frame.addCString(string); + return string; + } + throw JSError(runtime, "Value cannot be converted to pointer."); +} + +bool readPointerLikeValue(Runtime& runtime, const Value& value, void** pointer) { + if (pointer == nullptr || !value.isObject()) { + return false; + } + Object object = value.asObject(runtime); + if (object.isHostObject(runtime)) { + *pointer = object.getHostObject(runtime)->pointer(); + return true; + } + if (object.isHostObject(runtime)) { + *pointer = object.getHostObject(runtime)->data(); + return true; + } + if (object.isHostObject(runtime)) { + *pointer = object.getHostObject(runtime)->data(); + return true; + } + if (object.isHostObject(runtime)) { + *pointer = object.getHostObject(runtime)->object(); + return true; + } + if (Class cls = nativeClassFromEngineObject(runtime, object)) { + *pointer = cls; + return true; + } + if (object.isHostObject(runtime)) { + *pointer = object.getHostObject(runtime)->nativeProtocol(); + return true; + } + if (void* symbolPointer = pointerFromSymbolLikeObject(runtime, object)) { + *pointer = symbolPointer; + return true; + } + return readNativePointerProperty(runtime, object, pointer); +} + +template +void writeNumericArgument(Runtime& runtime, const Value& value, void* target, + const char* typeName) { + const Value* numericValue = &value; + Value primitiveValue = Value::undefined(); + if (value.isObject()) { + Object object = value.asObject(runtime); + Value valueOfValue = object.getProperty(runtime, "valueOf"); + if (valueOfValue.isObject() && valueOfValue.asObject(runtime).isFunction(runtime)) { + primitiveValue = valueOfValue.asObject(runtime).asFunction(runtime).callWithThis( + runtime, object, nullptr, 0); + numericValue = &primitiveValue; + } + } + + if (!numericValue->isNumber() && !numericValue->isBool()) { + throw JSError(runtime, std::string("Expected numeric ") + typeName + " argument."); + } + double number = + numericValue->isBool() ? (numericValue->getBool() ? 1.0 : 0.0) : numericValue->getNumber(); + *static_cast(target) = static_cast(number); +} + +void convertEngineArgument(Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, const Value& value, void* target, + NativeApiArgumentFrame& frame); + +Value convertNativeReturnValue(Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, void* value); + +Class classFromEngineValue(Runtime& runtime, const Value& value); +Protocol* protocolFromEngineValue(Runtime& runtime, const Value& value); + +bool valueIsNativeObjectHostObject(Runtime& runtime, const Value& value) { + if (!value.isObject()) { + return false; + } + return value.asObject(runtime).isHostObject(runtime); +} + +bool nativeTypeStoresObjectiveCObject(const NativeApiType& type) { + switch (type.kind) { + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: + return true; + default: + return false; + } +} + +void NativeApiReferenceHostObject::retainObjectSlot(size_t index, id object) { + if (retainedObjects_.size() <= index) { + retainedObjects_.resize(index + 1, nil); + } + id previous = retainedObjects_[index]; + if (previous == object) { + return; + } + [object retain]; + retainedObjects_[index] = object; + [previous release]; +} + +std::optional parseArrayIndexProperty(const std::string& property) { + if (property.empty()) { + return std::nullopt; + } + size_t index = 0; + for (char c : property) { + if (!std::isdigit(static_cast(c))) { + return std::nullopt; + } + size_t digit = static_cast(c - '0'); + if (index > (std::numeric_limits::max() - digit) / 10) { + return std::nullopt; + } + index = (index * 10) + digit; + } + return index; +} + +size_t referenceElementStride(const NativeApiType& type) { + return std::max(nativeSizeForType(type), 1); +} + +void convertAggregateArgument(Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, const Value& value, void* target, + NativeApiArgumentFrame& frame) { + size_t size = nativeSizeForType(type); + if (size == 0) { + return; + } + + std::memset(target, 0, size); + if (value.isNull() || value.isUndefined()) { + return; + } + + if (value.isObject()) { + Object object = value.asObject(runtime); + if (object.isHostObject(runtime)) { + auto structObject = object.getHostObject(runtime); + if (structObject->data() != nullptr) { + std::memcpy(target, structObject->data(), + std::min(size, static_cast(structObject->info()->size))); + } + return; + } + if (object.isHostObject(runtime)) { + void* data = object.getHostObject(runtime)->data(); + if (data != nullptr) { + std::memcpy(target, data, size); + } + return; + } + if (object.isHostObject(runtime)) { + void* data = object.getHostObject(runtime)->pointer(); + if (data != nullptr) { + std::memcpy(target, data, size); + } + return; + } + + const uint8_t* bytes = nullptr; + size_t byteLength = 0; + if (readEngineBuffer(runtime, object, &bytes, &byteLength)) { + if (bytes != nullptr) { + std::memcpy(target, bytes, std::min(byteLength, size)); + } + return; + } + } + + if (type.aggregateInfo == nullptr) { + throw JSError(runtime, "Missing native struct metadata."); + } + if (!value.isObject()) { + throw JSError(runtime, "Expected struct descriptor object."); + } + + Object object = value.asObject(runtime); + for (const auto& field : type.aggregateInfo->fields) { + bool hasField = object.hasProperty(runtime, field.name.c_str()); + if (!hasField) { + continue; + } + Value fieldValue = object.getProperty(runtime, field.name.c_str()); + void* fieldTarget = static_cast(target) + field.offset; + convertEngineArgument(runtime, bridge, field.type, fieldValue, fieldTarget, frame); + } +} + +void convertIndexedAggregateArgument(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiType& type, const Value& value, void* target, + NativeApiArgumentFrame& frame) { + size_t size = nativeSizeForType(type); + std::memset(target, 0, size); + if (value.isNull() || value.isUndefined()) { + return; + } + if (value.isObject()) { + const uint8_t* bytes = nullptr; + size_t byteLength = 0; + if (readEngineBuffer(runtime, value.asObject(runtime), &bytes, &byteLength)) { + if (bytes != nullptr) { + std::memcpy(target, bytes, std::min(byteLength, size)); + } + return; + } + } + if (!value.isObject() || !value.asObject(runtime).isArray(runtime)) { + throw JSError(runtime, "Expected array, ArrayBuffer, or typed array."); + } + + Array array = value.asObject(runtime).getArray(runtime); + size_t elementSize = type.elementType != nullptr ? nativeSizeForType(*type.elementType) : 0; + if (elementSize == 0 || type.elementType == nullptr) { + throw JSError(runtime, "Invalid native array element type."); + } + size_t count = std::min(type.arraySize, array.size(runtime)); + for (size_t i = 0; i < count; i++) { + void* slot = static_cast(target) + (i * elementSize); + convertEngineArgument(runtime, bridge, *type.elementType, array.getValueAtIndex(runtime, i), + slot, frame); + } +} + +void convertEngineFfiArgument(Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, const Value& value, void* target, + NativeApiArgumentFrame& frame) { + if (type.kind != metagen::mdTypeArray) { + convertEngineArgument(runtime, bridge, type, value, target, frame); + return; + } + + void* pointer = nullptr; + if (!value.isNull() && !value.isUndefined()) { + if (value.isObject()) { + Object object = value.asObject(runtime); + if (!readPointerLikeValue(runtime, value, &pointer)) { + const uint8_t* bytes = nullptr; + size_t byteLength = 0; + if (readEngineBuffer(runtime, object, &bytes, &byteLength)) { + pointer = const_cast(bytes); + } + } + } + + if (pointer == nullptr) { + size_t byteLength = nativeSizeForType(type); + void* buffer = frame.addBuffer(byteLength); + convertIndexedAggregateArgument(runtime, bridge, type, value, buffer, frame); + pointer = buffer; + } + } + + *static_cast(target) = pointer; +} + +void convertEngineArgument(Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, const Value& value, void* target, + NativeApiArgumentFrame& frame) { + if (unsupportedEngineType(type)) { + throw JSError(runtime, "This native signature is not supported by " + "the engine bridge yet."); + } + + switch (type.kind) { + case metagen::mdTypeBool: + if (!value.isNumber() && !value.isBool()) { + throw JSError(runtime, "Expected boolean or numeric argument."); + } + *static_cast(target) = value.isBool() + ? static_cast(value.getBool()) + : static_cast(value.getNumber() != 0); + break; + case metagen::mdTypeChar: + writeNumericArgument(runtime, value, target, "int8"); + break; + case metagen::mdTypeUChar: + case metagen::mdTypeUInt8: + writeNumericArgument(runtime, value, target, "uint8"); + break; + case metagen::mdTypeSShort: + writeNumericArgument(runtime, value, target, "int16"); + break; + case metagen::mdTypeUShort: + if (value.isString()) { + std::string text = value.asString(runtime).utf8(runtime); + if (text.size() != 1) { + throw JSError(runtime, "Expected a single-character string."); + } + *static_cast(target) = + static_cast(static_cast(text[0])); + } else { + writeNumericArgument(runtime, value, target, "uint16"); + } + break; + case metagen::mdTypeSInt: + writeNumericArgument(runtime, value, target, "int32"); + break; + case metagen::mdTypeUInt: + writeNumericArgument(runtime, value, target, "uint32"); + break; + case metagen::mdTypeSLong: + case metagen::mdTypeSInt64: + writeNumericArgument(runtime, value, target, "int64"); + break; + case metagen::mdTypeULong: + case metagen::mdTypeUInt64: + writeNumericArgument(runtime, value, target, "uint64"); + break; + case metagen::mdTypeFloat: + writeNumericArgument(runtime, value, target, "float"); + break; + case metagen::mdTypeDouble: + writeNumericArgument(runtime, value, target, "double"); + break; + case metagen::mdTypeString: { + if (value.isNull() || value.isUndefined()) { + *static_cast(target) = nullptr; + break; + } + if (value.isObject()) { + Object object = value.asObject(runtime); + void* pointer = nullptr; + if (readPointerLikeValue(runtime, value, &pointer)) { + if (bridge != nullptr) { + bridge->rememberScopedRawRoundTripValue(runtime, pointer, value, false, true); + } + *static_cast(target) = static_cast(pointer); + break; + } + const uint8_t* bytes = nullptr; + size_t byteLength = 0; + if (readEngineBuffer(runtime, object, &bytes, &byteLength)) { + if (bridge != nullptr) { + bridge->rememberScopedRawRoundTripValue(runtime, bytes, value, false, true); + } + *static_cast(target) = reinterpret_cast(const_cast(bytes)); + break; + } + Value valueOfValue = object.getProperty(runtime, "valueOf"); + if (valueOfValue.isObject() && valueOfValue.asObject(runtime).isFunction(runtime)) { + Value primitive = valueOfValue.asObject(runtime).asFunction(runtime).callWithThis( + runtime, object, nullptr, 0); + if (primitive.isString()) { + std::string utf8 = primitive.asString(runtime).utf8(runtime); + char* string = strdup(utf8.c_str()); + if (string == nullptr) { + throw std::bad_alloc(); + } + if (bridge != nullptr) { + bridge->rememberScopedRawRoundTripValue(runtime, string, value, true, false); + } + frame.addCString(string); + *static_cast(target) = string; + break; + } + } + } + if (!value.isString()) { + throw JSError(runtime, "Expected string argument."); + } + std::string utf8 = value.asString(runtime).utf8(runtime); + char* string = strdup(utf8.c_str()); + if (string == nullptr) { + throw std::bad_alloc(); + } + if (bridge != nullptr) { + bridge->rememberScopedRawRoundTripValue(runtime, string, value, true, false); + } + frame.addCString(string); + *static_cast(target) = string; + break; + } + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: { + id object = objectFromEngineValue(runtime, bridge, value, frame, + type.kind == metagen::mdTypeNSMutableStringObject); + if (valueIsNativeObjectHostObject(runtime, value)) { + frame.retainObject(object); + } + *static_cast(target) = object; + break; + } + case metagen::mdTypeClass: { + *static_cast(target) = classFromEngineValue(runtime, value); + break; + } + case metagen::mdTypeSelector: { + if (value.isNull() || value.isUndefined()) { + *static_cast(target) = nullptr; + break; + } + if (!value.isString()) { + throw JSError(runtime, "Expected selector string."); + } + std::string selectorName = value.asString(runtime).utf8(runtime); + *static_cast(target) = sel_registerName(selectorName.c_str()); + break; + } + case metagen::mdTypePointer: + if (value.isObject()) { + Object object = value.asObject(runtime); + if (object.isHostObject(runtime)) { + auto reference = object.getHostObject(runtime); + if (reference->data() == nullptr && type.elementType != nullptr) { + reference->ensureStorage(runtime, *type.elementType, frame); + } else if (reference->data() == nullptr) { + reference->ensureStorage(runtime, reference->type(), frame); + } + void* pointer = reference->data(); + frame.rememberRoundTripValue(bridge, runtime, pointer, value); + *static_cast(target) = pointer; + break; + } + if (object.isHostObject(runtime)) { + void* pointer = object.getHostObject(runtime)->data(); + frame.rememberRoundTripValue(bridge, runtime, pointer, value); + *static_cast(target) = pointer; + break; + } + const uint8_t* bytes = nullptr; + size_t byteLength = 0; + if (readEngineBuffer(runtime, object, &bytes, &byteLength)) { + void* pointer = const_cast(bytes); + frame.rememberRoundTripValue(bridge, runtime, pointer, value); + *static_cast(target) = pointer; + break; + } + } + *static_cast(target) = pointerFromEngineValue(runtime, bridge, value, frame); + break; + case metagen::mdTypeOpaquePointer: + *static_cast(target) = pointerFromEngineValue(runtime, bridge, value, frame); + break; + case metagen::mdTypeBlock: + case metagen::mdTypeFunctionPointer: { + if (value.isNull() || value.isUndefined()) { + *static_cast(target) = nullptr; + break; + } + if (value.isObject()) { + Object object = value.asObject(runtime); + void* nativePointer = nullptr; + if (object.isFunction(runtime)) { + std::string functionKind = stringPropertyOrEmpty(runtime, object, "kind"); + if (functionKind == "block" || functionKind == "functionPointer" || + functionKind == "functionReference") { + if (readNativePointerProperty(runtime, object, &nativePointer)) { + *static_cast(target) = nativePointer; + break; + } + } + + uintptr_t roundTripValidationKey = NativeApiBridge::callbackRoundTripValidationKey(type); + auto threadPolicy = readEngineCallbackThreadPolicy(runtime, object); + std::string callbackEncoding = + stringPropertyOrEmpty(runtime, object, kNativeApiCallbackEncodingProperty); + auto callback = + callbackEncoding.empty() + ? createEngineCallback(runtime, bridge, type, object.asFunction(runtime), + type.kind == metagen::mdTypeBlock, threadPolicy) + : createEngineCallback( + runtime, bridge, callbackEncoding, object.asFunction(runtime), + type.kind == metagen::mdTypeBlock, threadPolicy, roundTripValidationKey); + void* pointer = callback->functionPointer(); + if (type.kind == metagen::mdTypeBlock) { + frame.addObject(static_cast(pointer)); + frame.addLifetime(callback); + bridge->rememberRoundTripValue(runtime, pointer, value, false, roundTripValidationKey); + } else { + bridge->rememberRoundTripValue(runtime, pointer, value, false, roundTripValidationKey); + } + try { + object.setProperty(runtime, "__nativeApiPointerObject", + createPointer(runtime, bridge, pointer)); + object.setProperty(runtime, "__nativeApiPointer", + static_cast(reinterpret_cast(pointer))); + } catch (const std::exception&) { + } + *static_cast(target) = pointer; + break; + } + } + *static_cast(target) = pointerFromEngineValue(runtime, bridge, value, frame); + break; + } + case metagen::mdTypeStruct: + convertAggregateArgument(runtime, bridge, type, value, target, frame); + break; + case metagen::mdTypeArray: + case metagen::mdTypeVector: + case metagen::mdTypeExtVector: + case metagen::mdTypeComplex: + convertIndexedAggregateArgument(runtime, bridge, type, value, target, frame); + break; + default: + throw JSError(runtime, "Unsupported Engine argument type."); + } +} + +Value convertNativeReturnValue(Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, void* value) { + if (unsupportedEngineType(type)) { + throw JSError(runtime, "This native return type is not supported by " + "the engine bridge yet."); + } + + switch (type.kind) { + case metagen::mdTypeVoid: + return Value::undefined(); + case metagen::mdTypeBool: + return *static_cast(value) != 0; + case metagen::mdTypeChar: + return static_cast(*static_cast(value)); + case metagen::mdTypeUChar: + case metagen::mdTypeUInt8: + return static_cast(*static_cast(value)); + case metagen::mdTypeSShort: + return static_cast(*static_cast(value)); + case metagen::mdTypeUShort: { + uint16_t raw = *static_cast(value); + if (raw >= 32 && raw <= 126) { + char buffer[2] = {static_cast(raw), '\0'}; + return String::createFromUtf8(runtime, buffer); + } + return static_cast(raw); + } + case metagen::mdTypeSInt: + return static_cast(*static_cast(value)); + case metagen::mdTypeUInt: + return static_cast(*static_cast(value)); + case metagen::mdTypeSLong: + case metagen::mdTypeSInt64: + return signedInteger64ToEngineValue(runtime, *static_cast(value)); + case metagen::mdTypeULong: + case metagen::mdTypeUInt64: + return unsignedInteger64ToEngineValue(runtime, *static_cast(value)); + case metagen::mdTypeFloat: + return static_cast(*static_cast(value)); + case metagen::mdTypeDouble: + return *static_cast(value); + case metagen::mdTypeString: { + const char* string = *static_cast(value); + if (string == nullptr) { + return Value::null(); + } + NativeApiType cStringType = primitiveInteropType(metagen::mdTypeChar); + std::shared_ptr backingValue; + bool stringLikeNative = false; + if (bridge != nullptr) { + Value roundTrip = bridge->findRoundTripValue(runtime, string, &stringLikeNative); + if (!roundTrip.isUndefined()) { + backingValue = std::make_shared(runtime, roundTrip); + } + } + if (stringLikeNative) { + size_t byteLength = 0; + char* copy = copyCStringForReference(string, &byteLength); + return Object::createFromHostObject( + runtime, std::make_shared(bridge, cStringType, copy, true, + byteLength)); + } + return Object::createFromHostObject( + runtime, std::make_shared( + bridge, cStringType, const_cast(string), false, 0, nullptr, + std::move(backingValue))); + } + case metagen::mdTypeClass: { + Class cls = *static_cast(value); + if (cls == nil) { + return Value::null(); + } + const char* name = class_getName(cls); + NativeApiSymbol symbol{ + .kind = NativeApiSymbolKind::Class, + .offset = MD_SECTION_OFFSET_NULL, + .name = name != nullptr ? name : "", + .runtimeName = name != nullptr ? name : "", + }; + if (const NativeApiSymbol* found = bridge->findClass(symbol.name)) { + symbol = *found; + } + return makeNativeClassValue(runtime, bridge, std::move(symbol)); + } + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: { + id object = *static_cast(value); + if (object == nil) { + return Value::null(); + } + Value roundTrip = findCachedNativeObjectReturn(runtime, bridge, type, object); + if (!roundTrip.isUndefined()) { + if (type.returnOwned) { + [object release]; + } + return roundTrip; + } + if (nativeObjectReturnMayCoerceToString(type) && nativeObjectIsStringLike(object)) { + std::string utf8 = utf8StringFromNSString(static_cast(object)); + if (type.returnOwned) { + [object release]; + } + return makeString(runtime, utf8); + } + if ([object isKindOfClass:[NSNull class]]) { + if (type.returnOwned) { + [object release]; + } + return Value::null(); + } + if ([object isKindOfClass:[NSNumber class]] && + ![object isKindOfClass:[NSDecimalNumber class]]) { + NSNumber* number = static_cast(object); + const char* objCType = [number objCType]; + bool isBool = CFGetTypeID((__bridge CFTypeRef)number) == CFBooleanGetTypeID() || + (objCType != nullptr && std::strcmp(objCType, @encode(BOOL)) == 0); + Value result = + isBool ? Value(static_cast([number boolValue])) : Value([number doubleValue]); + if (type.returnOwned) { + [object release]; + } + return result; + } + if (const NativeApiSymbol* classSymbol = bridge->findClassForRuntimePointer((void*)object)) { + return makeNativeClassValue(runtime, bridge, *classSymbol); + } + if (const NativeApiSymbol* protocolSymbol = + bridge->findProtocolForRuntimePointer((void*)object)) { + return makeNativeProtocolValue(runtime, bridge, *protocolSymbol); + } + return makeNativeObjectValue(runtime, bridge, object, type.returnOwned); + } + case metagen::mdTypeSelector: { + SEL selector = *static_cast(value); + const char* selectorName = selector != nullptr ? sel_getName(selector) : nullptr; + return selectorName != nullptr ? makeString(runtime, selectorName) : Value::null(); + } + case metagen::mdTypePointer: + case metagen::mdTypeOpaquePointer: { + void* pointer = *static_cast(value); + if (pointer == nullptr) { + return Value::null(); + } + if (const NativeApiSymbol* classSymbol = bridge->findClassForRuntimePointer(pointer)) { + return makeNativeClassValue(runtime, bridge, *classSymbol); + } + if (const NativeApiSymbol* protocolSymbol = bridge->findProtocolForRuntimePointer(pointer)) { + return makeNativeProtocolValue(runtime, bridge, *protocolSymbol); + } + if (type.kind == metagen::mdTypePointer && type.elementType != nullptr) { + std::shared_ptr backingValue; + bool stringLikeNative = false; + Value roundTrip = bridge->findRoundTripValue(runtime, pointer, &stringLikeNative); + if (stringLikeNative) { + size_t byteLength = 0; + char* copy = copyCStringForReference(static_cast(pointer), &byteLength); + return Object::createFromHostObject( + runtime, std::make_shared(bridge, *type.elementType, + copy, true, byteLength)); + } + if (!roundTrip.isUndefined()) { + backingValue = std::make_shared(runtime, roundTrip); + } + return Object::createFromHostObject(runtime, std::make_shared( + bridge, *type.elementType, pointer, false, + 0, nullptr, std::move(backingValue))); + } + return createPointer(runtime, bridge, pointer); + } + case metagen::mdTypeBlock: + case metagen::mdTypeFunctionPointer: { + void* pointer = *static_cast(value); + if (pointer == nullptr) { + return Value::null(); + } + Value roundTrip = bridge->findRoundTripValue( + runtime, pointer, nullptr, false, NativeApiBridge::callbackRoundTripValidationKey(type)); + if (!roundTrip.isUndefined()) { + return roundTrip; + } + return wrapNativeFunctionPointer(runtime, bridge, type, pointer, + type.kind == metagen::mdTypeBlock); + } + case metagen::mdTypeStruct: + if (type.aggregateInfo == nullptr) { + return ArrayBuffer( + runtime, std::make_shared(value, nativeSizeForType(type))); + } + return Object::createFromHostObject( + runtime, std::make_shared(bridge, type.aggregateInfo, + value, true)); + case metagen::mdTypeArray: + case metagen::mdTypeVector: + case metagen::mdTypeExtVector: + case metagen::mdTypeComplex: { + Array result(runtime, type.arraySize); + if (type.elementType == nullptr) { + return result; + } + size_t elementSize = nativeSizeForType(*type.elementType); + auto base = static_cast(value); + for (uint16_t i = 0; i < type.arraySize; i++) { + result.setValueAtIndex( + runtime, i, + convertNativeReturnValue(runtime, bridge, *type.elementType, + base + (static_cast(i) * elementSize))); + } + return result; + } + default: + throw JSError(runtime, "Unsupported Engine return type."); + } +} + +void NativeApiReferenceHostObject::ensureStorage(Runtime& runtime, NativeApiType type, + NativeApiArgumentFrame& frame, size_t elements) { + size_t elementCount = std::max(elements, 1); + NativeApiType storageType = std::move(type); + size_t stride = std::max(nativeSizeForType(storageType), 1); + size_t required = std::max(stride * elementCount, sizeof(void*)); + type_ = std::move(storageType); + + if (data_ == nullptr) { + data_ = calloc(1, required); + ownsData_ = true; + byteLength_ = required; + } else if (ownsData_ && byteLength_ < required) { + void* expanded = realloc(data_, required); + if (expanded == nullptr) { + throw std::bad_alloc(); + } + std::memset(static_cast(expanded) + byteLength_, 0, required - byteLength_); + data_ = expanded; + byteLength_ = required; + } + + if (data_ != nullptr && pendingValue_ != nullptr) { + Value pending(runtime, *pendingValue_); + convertEngineArgument(runtime, bridge_, type_, pending, data_, frame); + if (nativeTypeStoresObjectiveCObject(type_)) { + retainObjectSlot(0, *static_cast(data_)); + } + pendingValue_.reset(); + } +} + +Value NativeApiReferenceHostObject::get(Runtime& runtime, const PropNameID& name) { + std::string property = name.utf8(runtime); + if (property == "kind") { + return makeString(runtime, "reference"); + } + if (property == "address") { + return static_cast(reinterpret_cast(data_)); + } + if (property == "value") { + if (data_ == nullptr) { + if (pendingValue_ != nullptr) { + return Value(runtime, *pendingValue_); + } + return Value::undefined(); + } + if (backingValue_ != nullptr && nativeTypeStoresObjectiveCObject(type_)) { + return Value(runtime, *backingValue_); + } + return convertNativeReturnValue(runtime, bridge_, type_, data_); + } + if (auto index = parseArrayIndexProperty(property)) { + if (data_ == nullptr) { + return Value::undefined(); + } + void* slot = static_cast(data_) + (*index * referenceElementStride(type_)); + return convertNativeReturnValue(runtime, bridge_, type_, slot); + } + if (property == "toString") { + void* data = data_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "toString"), 0, + [data](Runtime& runtime, const Value&, const Value*, size_t) -> Value { + char address[32] = {}; + snprintf(address, sizeof(address), "%p", data); + return makeString(runtime, ""); + }); + } + return Value::undefined(); +} + +NativeApiHostSetResult NativeApiReferenceHostObject::set(Runtime& runtime, const PropNameID& name, + const Value& value) { + std::string property = name.utf8(runtime); + auto index = parseArrayIndexProperty(property); + if (property != "value" && !index) { + NATIVE_API_SET_RETURN(true); + } + size_t slotIndex = index.value_or(0); + NativeApiArgumentFrame frame(1); + if (data_ == nullptr) { + if (slotIndex == 0) { + pendingValue_ = std::make_shared(runtime, value); + NATIVE_API_SET_RETURN(true); + } + ensureStorage(runtime, type_, frame, slotIndex + 1); + } + pendingValue_.reset(); + backingValue_.reset(); + void* slot = static_cast(data_) + (slotIndex * referenceElementStride(type_)); + convertEngineArgument(runtime, bridge_, type_, value, slot, frame); + if (nativeTypeStoresObjectiveCObject(type_)) { + retainObjectSlot(slotIndex, *static_cast(slot)); + } + NATIVE_API_SET_RETURN(true); +} + +Value NativeApiStructObjectHostObject::get(Runtime& runtime, const PropNameID& name) { + std::string property = name.utf8(runtime); + if (property == "kind") { + return makeString(runtime, info_ != nullptr && info_->isUnion ? "union" : "struct"); + } + if (property == "name") { + return makeString(runtime, info_ != nullptr ? info_->name : ""); + } + if (property == "sizeof") { + return static_cast(info_ != nullptr ? info_->size : 0); + } + if (property == "address") { + return static_cast(reinterpret_cast(data_)); + } + if (property == "toString") { + auto info = info_; + return Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "toString"), 0, + [info](Runtime& runtime, const Value&, const Value*, size_t) -> Value { + return makeString(runtime, std::string("[NativeApi ") + + (info != nullptr && info->isUnion ? "Union " : "Struct ") + + (info != nullptr ? info->name : "") + "]"); + }); + } + + if (info_ != nullptr && data_ != nullptr) { + for (const auto& field : info_->fields) { + if (field.name != property) { + continue; + } + void* fieldData = static_cast(data_) + field.offset; + if (field.type.kind == metagen::mdTypeStruct && field.type.aggregateInfo != nullptr) { + return Object::createFromHostObject( + runtime, + std::make_shared( + bridge_, field.type.aggregateInfo, fieldData, false, ownedData_, backingValue_)); + } + return convertNativeReturnValue(runtime, bridge_, field.type, fieldData); + } + } + return Value::undefined(); +} + +NativeApiHostSetResult NativeApiStructObjectHostObject::set(Runtime& runtime, + const PropNameID& name, + const Value& value) { + std::string property = name.utf8(runtime); + if (info_ == nullptr || data_ == nullptr) { + throw JSError(runtime, "Struct is not initialized."); + } + for (const auto& field : info_->fields) { + if (field.name != property) { + continue; + } + NativeApiArgumentFrame frame(1); + convertEngineArgument(runtime, bridge_, field.type, value, + static_cast(data_) + field.offset, frame); + NATIVE_API_SET_RETURN(true); + } + throw JSError(runtime, "No native struct field: " + property); +} + +std::vector NativeApiStructObjectHostObject::getPropertyNames(Runtime& runtime) { + std::vector names; + addPropertyName(runtime, names, "kind"); + addPropertyName(runtime, names, "name"); + addPropertyName(runtime, names, "sizeof"); + addPropertyName(runtime, names, "address"); + addPropertyName(runtime, names, "toString"); + if (info_ != nullptr) { + for (const auto& field : info_->fields) { + addPropertyName(runtime, names, field.name.c_str()); + } + } + return names; +} + +NativeApiType primitiveInteropType(MDTypeKind kind) { + NativeApiType type; + type.kind = kind; + type.ffiType = ffiTypeForEngineKind(kind); + type.supported = type.ffiType != nullptr; + return type; +} + +std::optional primitiveInteropTypeFromCode(int32_t code) { + MDTypeKind kind = static_cast(code); + switch (kind) { + case metagen::mdTypeVoid: + case metagen::mdTypeBool: + case metagen::mdTypeChar: + case metagen::mdTypeUChar: + case metagen::mdTypeUInt8: + case metagen::mdTypeSShort: + case metagen::mdTypeUShort: + case metagen::mdTypeSInt: + case metagen::mdTypeUInt: + case metagen::mdTypeSLong: + case metagen::mdTypeULong: + case metagen::mdTypeSInt64: + case metagen::mdTypeUInt64: + case metagen::mdTypeFloat: + case metagen::mdTypeDouble: + case metagen::mdTypeString: + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClass: + case metagen::mdTypeSelector: + case metagen::mdTypePointer: + case metagen::mdTypeOpaquePointer: + case metagen::mdTypeBlock: + case metagen::mdTypeFunctionPointer: + return primitiveInteropType(kind); + default: + return std::nullopt; + } +} + +std::optional interopTypeFromValue(Runtime& runtime, + const std::shared_ptr& bridge, + const Value& value) { + if (value.isNumber()) { + return primitiveInteropTypeFromCode(static_cast(value.getNumber())); + } + + if (!value.isObject()) { + return std::nullopt; + } + + Object object = value.asObject(runtime); + Value typeCodeValue = object.getProperty(runtime, "__nativeApiTypeCode"); + if (typeCodeValue.isNumber()) { + return primitiveInteropTypeFromCode(static_cast(typeCodeValue.getNumber())); + } + Value valueOfValue = object.getProperty(runtime, "valueOf"); + if (valueOfValue.isObject() && valueOfValue.asObject(runtime).isFunction(runtime)) { + Value primitive = valueOfValue.asObject(runtime).asFunction(runtime).callWithThis( + runtime, object, nullptr, 0); + if (primitive.isNumber()) { + return primitiveInteropTypeFromCode(static_cast(primitive.getNumber())); + } + } + + Class descriptorClass = nativeClassFromEngineObject(runtime, object); + if (descriptorClass == Nil && stringPropertyOrEmpty(runtime, object, "kind") == "class") { + descriptorClass = static_cast(pointerFromSymbolLikeObject(runtime, object)); + } + if (descriptorClass != Nil) { + return nativeObjectReturnTypeForClass(descriptorClass); + } + + if (object.isHostObject(runtime)) { + auto structObject = object.getHostObject(runtime); + NativeApiType type; + type.kind = metagen::mdTypeStruct; + type.aggregateInfo = structObject->info(); + type.aggregateOffset = + type.aggregateInfo != nullptr ? type.aggregateInfo->offset : MD_SECTION_OFFSET_NULL; + type.aggregateIsUnion = type.aggregateInfo != nullptr && type.aggregateInfo->isUnion; + type.ffiType = type.aggregateInfo != nullptr && type.aggregateInfo->ffi != nullptr + ? &type.aggregateInfo->ffi->type + : nullptr; + type.supported = type.ffiType != nullptr; + return type; + } + + Value kindValue = object.getProperty(runtime, "kind"); + if (kindValue.isString()) { + std::string kindName = kindValue.asString(runtime).utf8(runtime); + if (kindName == "pointer") { + return primitiveInteropType(metagen::mdTypePointer); + } + if (kindName == "reference") { + return primitiveInteropType(metagen::mdTypePointer); + } + if (kindName == "class") { + return nativeObjectReturnType(metagen::mdTypeInstanceObject); + } + if (kindName == "selector") { + return primitiveInteropType(metagen::mdTypeSelector); + } + if (kindName == "protocol") { + return primitiveInteropType(metagen::mdTypeProtocolObject); + } + if (kindName == "block") { + return primitiveInteropType(metagen::mdTypeBlock); + } + if (kindName == "functionPointer") { + return primitiveInteropType(metagen::mdTypeFunctionPointer); + } + if (kindName == "functionReference") { + return primitiveInteropType(metagen::mdTypeFunctionPointer); + } + } + Value offsetValue = object.getProperty(runtime, "metadataOffset"); + if (kindValue.isString() && offsetValue.isNumber()) { + std::string kindName = kindValue.asString(runtime).utf8(runtime); + if (kindName == "struct" || kindName == "union") { + bool isUnion = kindName == "union"; + auto info = + bridge->aggregateInfoFor(static_cast(offsetValue.getNumber()), isUnion); + NativeApiType type; + type.kind = metagen::mdTypeStruct; + type.aggregateInfo = info; + type.aggregateOffset = info != nullptr ? info->offset : MD_SECTION_OFFSET_NULL; + type.aggregateIsUnion = isUnion; + type.ffiType = info != nullptr && info->ffi != nullptr ? &info->ffi->type : nullptr; + type.supported = type.ffiType != nullptr; + return type; + } + } + + return std::nullopt; +} + +Value makeAggregateConstructor(Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiSymbol& symbol) { + auto info = bridge->aggregateInfoFor(symbol); + auto constructor = Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, symbol.name.c_str()), 1, + [bridge, symbol, info](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + if (info == nullptr) { + throw JSError(runtime, "Native aggregate metadata is unavailable: " + symbol.name); + } + + NativeApiType type; + type.kind = metagen::mdTypeStruct; + type.aggregateInfo = info; + type.aggregateOffset = info->offset; + type.aggregateIsUnion = info->isUnion; + type.ffiType = info->ffi != nullptr ? &info->ffi->type : nullptr; + type.supported = type.ffiType != nullptr; + + if (count > 0 && args[0].isObject()) { + void* pointer = nullptr; + if (readPointerLikeValue(runtime, args[0], &pointer) && pointer != nullptr) { + return Object::createFromHostObject(runtime, + std::make_shared( + bridge, info, pointer, false, nullptr, + std::make_shared(runtime, args[0]))); + } + } + + std::vector storage(info->size, 0); + if (count > 0) { + NativeApiArgumentFrame frame(1); + convertAggregateArgument(runtime, bridge, type, args[0], storage.data(), frame); + } + return Object::createFromHostObject( + runtime, + std::make_shared(bridge, info, storage.data(), true)); + }); + + constructor.setProperty( + runtime, "kind", + makeString(runtime, symbol.kind == NativeApiSymbolKind::Union ? "union" : "struct")); + constructor.setProperty(runtime, "runtimeName", makeString(runtime, symbol.runtimeName)); + constructor.setProperty(runtime, "metadataOffset", static_cast(symbol.offset)); + constructor.setProperty(runtime, "sizeof", static_cast(info != nullptr ? info->size : 0)); + constructor.setProperty( + runtime, "equals", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "equals"), 2, + [bridge, info](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + if (info == nullptr || count < 2) { + return false; + } + + NativeApiType type; + type.kind = metagen::mdTypeStruct; + type.aggregateInfo = info; + type.aggregateOffset = info->offset; + type.aggregateIsUnion = info->isUnion; + type.ffiType = info->ffi != nullptr ? &info->ffi->type : nullptr; + type.supported = type.ffiType != nullptr; + + std::vector left(info->size, 0); + std::vector right(info->size, 0); + try { + NativeApiArgumentFrame leftFrame(1); + convertAggregateArgument(runtime, bridge, type, args[0], left.data(), leftFrame); + NativeApiArgumentFrame rightFrame(1); + convertAggregateArgument(runtime, bridge, type, args[1], right.data(), rightFrame); + } catch (const std::exception&) { + return false; + } + + return std::memcmp(left.data(), right.data(), info->size) == 0; + })); + Array fields(runtime, info != nullptr ? info->fields.size() : 0); + if (info != nullptr) { + for (size_t i = 0; i < info->fields.size(); i++) { + fields.setValueAtIndex(runtime, i, makeString(runtime, info->fields[i].name)); + } + } + constructor.setProperty(runtime, "fields", fields); + return constructor; +} + +size_t sizeofInteropType(Runtime& runtime, const std::shared_ptr& bridge, + const Value& value) { + if (auto type = interopTypeFromValue(runtime, bridge, value)) { + return nativeSizeForType(*type); + } + + if (value.isObject()) { + Object object = value.asObject(runtime); + if (object.isHostObject(runtime) || + object.isHostObject(runtime) || + object.isHostObject(runtime) || + nativeClassFromEngineObject(runtime, object) != Nil) { + return sizeof(void*); + } + void* nativePointer = nullptr; + if (readNativePointerProperty(runtime, object, &nativePointer)) { + return sizeof(void*); + } + Value sizeValue = object.getProperty(runtime, "sizeof"); + if (sizeValue.isNumber()) { + return static_cast(sizeValue.getNumber()); + } + } + + throw JSError(runtime, "Invalid type for interop.sizeof."); +} + +Object createPointer(Runtime& runtime, const std::shared_ptr& bridge, + void* pointer, bool adopted, std::shared_ptr backingValue) { + if (!adopted && bridge != nullptr) { + Value cached = bridge->findPointerValue(runtime, pointer); + if (cached.isObject()) { + Object cachedObject = cached.asObject(runtime); + if (backingValue != nullptr && + cachedObject.isHostObject(runtime)) { + cachedObject.getHostObject(runtime)->setBackingValue( + runtime, *backingValue); + } + return cachedObject; + } + } + + Object result = Object::createFromHostObject( + runtime, std::make_shared(bridge, pointer, "pointer", adopted, + std::move(backingValue))); + if (!adopted && bridge != nullptr) { + bridge->rememberPointerValue(runtime, pointer, Value(runtime, result)); + } + return result; +} + +void installInteropHasInstance(Runtime& runtime, Function& constructor, const char* kind) { + Value symbolCtorValue = runtime.global().getProperty(runtime, "Symbol"); + if (!symbolCtorValue.isObject()) { + return; + } + + Object symbolCtor = symbolCtorValue.asObject(runtime); + Value hasInstanceValue = symbolCtor.getProperty(runtime, "hasInstance"); + if (!hasInstanceValue.isSymbol()) { + return; + } + + try { + Object objectCtor = runtime.global().getPropertyAsObject(runtime, "Object"); + Function defineProperty = objectCtor.getPropertyAsFunction(runtime, "defineProperty"); + Object descriptor(runtime); + descriptor.setProperty(runtime, "configurable", true); + descriptor.setProperty( + runtime, "value", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "Symbol.hasInstance"), 1, + [kind = std::string(kind)](Runtime& runtime, const Value&, const Value* args, + size_t count) -> Value { + if (count < 1 || !args[0].isObject()) { + return false; + } + + Object object = args[0].asObject(runtime); + Value kindValue = object.getProperty(runtime, "kind"); + return kindValue.isString() && kindValue.asString(runtime).utf8(runtime) == kind; + })); + defineProperty.call(runtime, constructor, hasInstanceValue, descriptor); + } catch (const std::exception&) { + } +} + +Class classFromEngineValue(Runtime& runtime, const Value& value) { + if (value.isString()) { + std::string name = value.asString(runtime).utf8(runtime); + return objc_lookUpClass(name.c_str()); + } + if (!value.isObject()) { + return Nil; + } + Object object = value.asObject(runtime); + if (Class cls = nativeClassFromEngineObject(runtime, object)) { + return cls; + } + if (stringPropertyOrEmpty(runtime, object, "kind") == "class") { + if (void* pointer = pointerFromSymbolLikeObject(runtime, object)) { + return static_cast(pointer); + } + } + if (object.isHostObject(runtime)) { + id nativeObject = object.getHostObject(runtime)->object(); + return nativeObject != nil ? object_getClass(nativeObject) : Nil; + } + return Nil; +} + +Protocol* protocolFromEngineValue(Runtime& runtime, const Value& value) { + if (value.isString()) { + std::string name = value.asString(runtime).utf8(runtime); + Protocol* protocol = objc_getProtocol(name.c_str()); + if (protocol == nullptr) { + constexpr const char* suffix = "Protocol"; + if (name.size() > std::strlen(suffix) && + name.compare(name.size() - std::strlen(suffix), std::strlen(suffix), suffix) == 0) { + protocol = objc_getProtocol(name.substr(0, name.size() - std::strlen(suffix)).c_str()); + } + } + return protocol; + } + if (!value.isObject()) { + return nullptr; + } + Object object = value.asObject(runtime); + if (object.isHostObject(runtime)) { + return object.getHostObject(runtime)->nativeProtocol(); + } + if (stringPropertyOrEmpty(runtime, object, "kind") == "protocol") { + return static_cast(pointerFromSymbolLikeObject(runtime, object)); + } + if (object.isHostObject(runtime)) { + return static_cast( + object.getHostObject(runtime)->pointer()); + } + void* nativePointer = nullptr; + if (readNativePointerProperty(runtime, object, &nativePointer)) { + return static_cast(nativePointer); + } + Value nameValue = object.getProperty(runtime, "name"); + if (nameValue.isString()) { + return protocolFromEngineValue(runtime, nameValue); + } + return nullptr; +} + +Object createInteropObject(Runtime& runtime, const std::shared_ptr& bridge) { + Object interop(runtime); + Object types(runtime); + auto setType = [&](const char* name, MDTypeKind kind) { + Object type(runtime); + double code = static_cast(kind); + type.setProperty(runtime, "__nativeApiTypeCode", code); + type.setProperty( + runtime, "valueOf", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "valueOf"), 0, + [code](Runtime&, const Value&, const Value*, size_t) -> Value { return code; })); + type.setProperty(runtime, "toString", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "toString"), 0, + [code](Runtime& runtime, const Value&, const Value*, size_t) -> Value { + char text[32] = {}; + snprintf(text, sizeof(text), "%d", static_cast(code)); + return makeString(runtime, text); + })); + types.setProperty(runtime, name, type); + }; + setType("void", metagen::mdTypeVoid); + setType("bool", metagen::mdTypeBool); + setType("int8", metagen::mdTypeChar); + setType("uint8", metagen::mdTypeUInt8); + setType("int16", metagen::mdTypeSShort); + setType("uint16", metagen::mdTypeUShort); + setType("int32", metagen::mdTypeSInt); + setType("uint32", metagen::mdTypeUInt); + setType("int64", metagen::mdTypeSInt64); + setType("uint64", metagen::mdTypeUInt64); + setType("float", metagen::mdTypeFloat); + setType("double", metagen::mdTypeDouble); + setType("UTF8CString", metagen::mdTypeString); + setType("unichar", metagen::mdTypeUShort); + setType("id", metagen::mdTypeAnyObject); + setType("class", metagen::mdTypeClass); + setType("protocol", metagen::mdTypeProtocolObject); + setType("SEL", metagen::mdTypeSelector); + setType("selector", metagen::mdTypeSelector); + setType("pointer", metagen::mdTypePointer); + setType("block", metagen::mdTypeBlock); + setType("functionPointer", metagen::mdTypeFunctionPointer); + interop.setProperty(runtime, "types", types); + + Function pointerConstructor = Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "Pointer"), 1, + [bridge](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + if (count > 0 && args[0].isObject()) { + Object object = args[0].asObject(runtime); + if (object.isHostObject(runtime)) { + return Value(runtime, object); + } + } + void* pointer = nullptr; + if (count > 0 && !args[0].isNull() && !args[0].isUndefined()) { + auto readAddress = [&](const Value& value, uintptr_t* address) -> bool { + auto readAddressFromString = [&](const Value& source) -> bool { + try { + Value stringCtorValue = runtime.global().getProperty(runtime, "String"); + if (!stringCtorValue.isObject() || + !stringCtorValue.asObject(runtime).isFunction(runtime)) { + return false; + } + Value stringValue = + stringCtorValue.asObject(runtime).asFunction(runtime).call(runtime, source); + if (!stringValue.isString()) { + return false; + } + return parseIntegerTextToUintptr(stringValue.asString(runtime).utf8(runtime), + address); + } catch (const std::exception&) { + return false; + } + }; + + if (value.isNumber()) { + double number = value.getNumber(); + if (!std::isfinite(number)) { + return false; + } + *address = static_cast(static_cast(number)); + return true; + } + if (value.isBigInt()) { + if (readAddressFromString(value)) { + return true; + } + BigInt bigint = value.getBigInt(runtime); + return parseBigIntToUintptr(runtime, bigint, address); + } + if (value.isObject()) { + Object object = value.asObject(runtime); + Value valueOfValue = object.getProperty(runtime, "valueOf"); + if (valueOfValue.isObject() && valueOfValue.asObject(runtime).isFunction(runtime)) { + Value primitive = valueOfValue.asObject(runtime).asFunction(runtime).callWithThis( + runtime, object, nullptr, 0); + if (primitive.isNumber()) { + double number = primitive.getNumber(); + if (!std::isfinite(number)) { + return false; + } + *address = static_cast(static_cast(number)); + return true; + } + if (primitive.isBigInt()) { + if (readAddressFromString(primitive)) { + return true; + } + BigInt bigint = primitive.getBigInt(runtime); + return parseBigIntToUintptr(runtime, bigint, address); + } + } + return readAddressFromString(value); + } + return false; + }; + + uintptr_t address = 0; + if (!readAddress(args[0], &address)) { + throw JSError(runtime, "Pointer expects a numeric address."); + } + pointer = reinterpret_cast(address); + } + return createPointer(runtime, bridge, pointer); + }); + Object pointerPrototype(runtime); + pointerPrototype.setProperty(runtime, "constructor", pointerConstructor); + pointerConstructor.setProperty(runtime, "prototype", pointerPrototype); + installInteropHasInstance(runtime, pointerConstructor, "pointer"); + pointerConstructor.setProperty(runtime, "kind", makeString(runtime, "pointer")); + pointerConstructor.setProperty(runtime, "sizeof", static_cast(sizeof(void*))); + interop.setProperty(runtime, "Pointer", pointerConstructor); + + Function blockConstructor = Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "Block"), 2, + [](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + return interopCallbackFromArguments(runtime, "Block", "block", args, count); + }); + Object blockPrototype(runtime); + blockPrototype.setProperty(runtime, "constructor", blockConstructor); + blockConstructor.setProperty(runtime, "prototype", blockPrototype); + installInteropHasInstance(runtime, blockConstructor, "block"); + blockConstructor.setProperty(runtime, "kind", makeString(runtime, "block")); + blockConstructor.setProperty(runtime, "sizeof", static_cast(sizeof(void*))); + interop.setProperty(runtime, "Block", blockConstructor); + + Function functionReferenceConstructor = Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "FunctionReference"), 2, + [](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + return interopCallbackFromArguments(runtime, "FunctionReference", "functionReference", args, + count); + }); + Object functionReferencePrototype(runtime); + functionReferencePrototype.setProperty(runtime, "constructor", functionReferenceConstructor); + functionReferenceConstructor.setProperty(runtime, "prototype", functionReferencePrototype); + installInteropHasInstance(runtime, functionReferenceConstructor, "functionReference"); + functionReferenceConstructor.setProperty(runtime, "kind", + makeString(runtime, "functionReference")); + functionReferenceConstructor.setProperty(runtime, "sizeof", static_cast(sizeof(void*))); + interop.setProperty(runtime, "FunctionReference", functionReferenceConstructor); + + Function referenceConstructor = Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "Reference"), 2, + [bridge](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + NativeApiType type = primitiveInteropType(metagen::mdTypePointer); + bool firstArgumentIsType = false; + if (count > 1) { + firstArgumentIsType = true; + } else if (count == 1 && args[0].isObject()) { + Object object = args[0].asObject(runtime); + Value typeCodeValue = object.getProperty(runtime, "__nativeApiTypeCode"); + Value kindValue = object.getProperty(runtime, "kind"); + firstArgumentIsType = + typeCodeValue.isNumber() || object.isFunction(runtime) || + nativeClassFromEngineObject(runtime, object) != Nil || + (kindValue.isString() && (kindValue.asString(runtime).utf8(runtime) == "class" || + kindValue.asString(runtime).utf8(runtime) == "protocol")); + } + std::optional requestedType = + firstArgumentIsType ? interopTypeFromValue(runtime, bridge, args[0]) : std::nullopt; + bool hasType = firstArgumentIsType && requestedType.has_value(); + if (hasType) { + type = *requestedType; + } + + void* data = nullptr; + bool ownsData = false; + size_t byteLength = 0; + std::shared_ptr pendingValue; + std::shared_ptr backingValue; + if (hasType) { + bool usesExternalStorage = false; + Value valueToStore = Value::undefined(); + if (count > 1) { + valueToStore = Value(runtime, args[1]); + if (args[1].isObject()) { + Object object = args[1].asObject(runtime); + if (object.isHostObject(runtime)) { + data = object.getHostObject(runtime)->pointer(); + usesExternalStorage = true; + } else if (object.isHostObject(runtime)) { + auto reference = object.getHostObject(runtime); + data = reference->data(); + if (data != nullptr) { + usesExternalStorage = true; + } else { + valueToStore = object.getProperty(runtime, "value"); + } + } else if (type.kind == metagen::mdTypeStruct && + object.isHostObject(runtime)) { + data = object.getHostObject(runtime)->data(); + usesExternalStorage = true; + } else if (type.kind == metagen::mdTypePointer || + type.kind == metagen::mdTypeOpaquePointer || + type.kind == metagen::mdTypeBlock || + type.kind == metagen::mdTypeFunctionPointer) { + void* nativePointer = nullptr; + if (readNativePointerProperty(runtime, object, &nativePointer)) { + data = nativePointer; + usesExternalStorage = true; + } + } + } + } + if (!usesExternalStorage) { + byteLength = std::max(nativeSizeForType(type), sizeof(void*)); + data = calloc(1, byteLength); + if (data == nullptr) { + throw std::bad_alloc(); + } + ownsData = true; + if (count > 1) { + NativeApiArgumentFrame frame(1); + convertEngineArgument(runtime, bridge, type, valueToStore, data, frame); + if (nativeTypeStoresObjectiveCObject(type) && valueToStore.isObject()) { + backingValue = std::make_shared(runtime, valueToStore); + } + } + } + } else if (count > 0) { + pendingValue = std::make_shared(runtime, args[0]); + } + + if (ownsData && data == nullptr) { + throw std::bad_alloc(); + } + return Object::createFromHostObject( + runtime, std::make_shared( + bridge, type, data, ownsData, byteLength, std::move(pendingValue), + std::move(backingValue))); + }); + Object referencePrototype(runtime); + referencePrototype.setProperty(runtime, "constructor", referenceConstructor); + referenceConstructor.setProperty(runtime, "prototype", referencePrototype); + installInteropHasInstance(runtime, referenceConstructor, "reference"); + referenceConstructor.setProperty(runtime, "kind", makeString(runtime, "reference")); + referenceConstructor.setProperty(runtime, "sizeof", static_cast(sizeof(void*))); + interop.setProperty(runtime, "Reference", referenceConstructor); + + interop.setProperty( + runtime, "sizeof", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "sizeof"), 1, + [bridge](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + if (count < 1) { + throw JSError(runtime, "sizeof expects a type."); + } + return static_cast(sizeofInteropType(runtime, bridge, args[0])); + })); + + interop.setProperty( + runtime, "alloc", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "alloc"), 1, + [bridge](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + if (count < 1 || !args[0].isNumber()) { + throw JSError(runtime, "alloc expects a byte size."); + } + size_t size = static_cast(std::max(0, args[0].getNumber())); + return createPointer(runtime, bridge, calloc(1, size), false); + })); + + interop.setProperty( + runtime, "free", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "free"), 1, + [](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + if (count < 1 || !args[0].isObject()) { + return Value::undefined(); + } + Object object = args[0].asObject(runtime); + if (!object.isHostObject(runtime)) { + return Value::undefined(); + } + auto pointer = object.getHostObject(runtime); + void* raw = pointer->pointer(); + if (raw != nullptr) { + free(raw); + pointer->clearWithoutFree(); + } + return Value::undefined(); + })); + + interop.setProperty( + runtime, "adopt", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "adopt"), 1, + [](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + if (count < 1 || !args[0].isObject()) { + throw JSError(runtime, "adopt expects a Pointer."); + } + Object object = args[0].asObject(runtime); + if (!object.isHostObject(runtime)) { + throw JSError(runtime, "adopt expects a Pointer."); + } + object.getHostObject(runtime)->adopt(); + return Value(runtime, object); + })); + + interop.setProperty( + runtime, "handleof", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "handleof"), 1, + [bridge](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + if (count < 1 || args[0].isNull() || args[0].isUndefined()) { + return Value::null(); + } + if (args[0].isString()) { + std::string utf8 = args[0].asString(runtime).utf8(runtime); + char* data = strdup(utf8.c_str()); + return createPointer(runtime, bridge, data); + } + if (!args[0].isObject()) { + return Value::null(); + } + Object object = args[0].asObject(runtime); + if (object.isHostObject(runtime)) { + return Value(runtime, object); + } + if (object.isHostObject(runtime)) { + auto reference = object.getHostObject(runtime); + void* data = reference->data(); + if (data == nullptr) { + throw JSError(runtime, "Cannot get handle of empty Reference."); + } + std::shared_ptr backingValue; + if (reference->backingValue() != nullptr && + nativeTypeStoresObjectiveCObject(reference->type())) { + backingValue = reference->backingValue(); + } + return createPointer(runtime, bridge, data, false, std::move(backingValue)); + } + if (object.isHostObject(runtime)) { + auto structObject = object.getHostObject(runtime); + if (structObject->backingValue() != nullptr) { + return Value(runtime, *structObject->backingValue()); + } + return createPointer(runtime, bridge, structObject->data()); + } + if (object.isHostObject(runtime)) { + id nativeObject = object.getHostObject(runtime)->object(); + return createPointer(runtime, bridge, nativeObject, false, + std::make_shared(runtime, args[0])); + } + if (Class cls = nativeClassFromEngineObject(runtime, object)) { + return createPointer(runtime, bridge, cls); + } + if (object.isHostObject(runtime)) { + return createPointer( + runtime, bridge, + object.getHostObject(runtime)->nativeProtocol()); + } + if (void* symbolPointer = pointerFromSymbolLikeObject(runtime, object)) { + return createPointer(runtime, bridge, symbolPointer); + } + void* nativePointer = nullptr; + if (readNativePointerProperty(runtime, object, &nativePointer)) { + return createPointer(runtime, bridge, nativePointer); + } + Value kindValue = object.getProperty(runtime, "kind"); + if (kindValue.isString()) { + std::string kind = kindValue.asString(runtime).utf8(runtime); + if (kind == "block" || kind == "functionPointer" || kind == "functionReference") { + throw JSError(runtime, "Cannot get handle of uninitialized native callback."); + } + } + Value nativeName = object.getProperty(runtime, "nativeName"); + if (nativeName.isString()) { + std::string name = nativeName.asString(runtime).utf8(runtime); + void* symbol = dlsym(bridge->selfDl(), name.c_str()); + if (symbol != nullptr) { + return createPointer(runtime, bridge, symbol); + } + } + return Value::null(); + })); + + interop.setProperty( + runtime, "object", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "object"), 1, + [bridge](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + if (count < 1 || args[0].isNull() || args[0].isUndefined()) { + return Value::null(); + } + + void* pointer = nullptr; + if (args[0].isString()) { + uintptr_t address = 0; + if (!parseIntegerTextToUintptr(args[0].asString(runtime).utf8(runtime), + &address)) { + throw JSError(runtime, + "interop.object expects an Objective-C object pointer."); + } + pointer = reinterpret_cast(address); + } else { + NativeApiArgumentFrame frame(1); + pointer = pointerFromEngineValue(runtime, bridge, args[0], frame); + } + + if (pointer == nullptr) { + return Value::null(); + } + + id object = static_cast(pointer); + NativeApiType type = nativeObjectReturnTypeForClass(object_getClass(object)); + return convertNativeReturnValue(runtime, bridge, type, &object); + })); + + interop.setProperty( + runtime, "stringFromCString", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "stringFromCString"), 2, + [bridge](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + if (count < 1 || args[0].isNull() || args[0].isUndefined()) { + return Value::null(); + } + NativeApiArgumentFrame frame(1); + const char* data = + static_cast(pointerFromEngineValue(runtime, bridge, args[0], frame)); + if (data == nullptr) { + return Value::null(); + } + if (count > 1 && args[1].isNumber()) { + size_t length = static_cast(std::max(0, args[1].getNumber())); + return String::createFromUtf8(runtime, reinterpret_cast(data), + length); + } + return makeString(runtime, data); + })); + + interop.setProperty( + runtime, "bufferFromData", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "bufferFromData"), 1, + [](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + if (count < 1 || !args[0].isObject()) { + throw JSError(runtime, "Invalid data."); + } + Object object = args[0].asObject(runtime); + if (object.isArrayBuffer(runtime)) { + return Value(runtime, object); + } + id native = nil; + if (object.isHostObject(runtime)) { + native = object.getHostObject(runtime)->object(); + } else if (object.isHostObject(runtime)) { + native = static_cast( + object.getHostObject(runtime)->pointer()); + } + if (native == nil || ![native isKindOfClass:[NSData class]]) { + throw JSError(runtime, "Invalid data."); + } + NSData* data = static_cast(native); + return ArrayBuffer(runtime, std::make_shared( + data.bytes, static_cast(data.length))); + })); + + interop.setProperty( + runtime, "addMethod", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "addMethod"), 2, + [](Runtime& runtime, const Value&, const Value*, size_t) -> Value { + throw JSError(runtime, "interop.addMethod requires the Engine class builder layer."); + })); + interop.setProperty( + runtime, "addProtocol", + Function::createFromHostFunction( + runtime, PropNameID::forAscii(runtime, "addProtocol"), 2, + [](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { + if (count < 2) { + throw JSError(runtime, "interop.addProtocol expects class and protocol."); + } + Class cls = classFromEngineValue(runtime, args[0]); + Protocol* protocol = protocolFromEngineValue(runtime, args[1]); + if (cls == Nil || protocol == nullptr) { + return false; + } + return class_addProtocol(cls, protocol); + })); + + return interop; +} diff --git a/NativeScript/ffi/objc/v8/NativeApiV8.h b/NativeScript/ffi/objc/v8/NativeApiV8.h new file mode 100644 index 000000000..c3d1761f6 --- /dev/null +++ b/NativeScript/ffi/objc/v8/NativeApiV8.h @@ -0,0 +1,22 @@ +#ifndef NATIVESCRIPT_FFI_V8_NATIVE_API_V8_H +#define NATIVESCRIPT_FFI_V8_NATIVE_API_V8_H + +#include "ffi/objc/shared/NativeApiBackendConfig.h" +#include "v8.h" + +namespace nativescript { + +using NativeApiScheduler = NativeApiBackendScheduler; +using NativeApiConfig = NativeApiBackendConfig; + +void InstallNativeApi(v8::Isolate* isolate, + v8::Local context, + const NativeApiConfig& config = NativeApiConfig{}); + +} // namespace nativescript + +extern "C" void NativeScriptInstallNativeApi(v8::Isolate* isolate, + v8::Local context, + const char* metadataPath); + +#endif // NATIVESCRIPT_FFI_V8_NATIVE_API_V8_H diff --git a/NativeScript/ffi/objc/v8/NativeApiV8.mm b/NativeScript/ffi/objc/v8/NativeApiV8.mm new file mode 100644 index 000000000..1ed1c20e0 --- /dev/null +++ b/NativeScript/ffi/objc/v8/NativeApiV8.mm @@ -0,0 +1,80 @@ +#include "NativeApiV8.h" + +#ifdef TARGET_ENGINE_V8 + +#include "NativeApiV8Runtime.h" +#include "SignatureDispatch.h" + +namespace nativescript { + +namespace { + +using nativescript::engine::Array; +using nativescript::engine::ArrayBuffer; +using nativescript::engine::BigInt; +using nativescript::engine::Function; +using nativescript::engine::HostObject; +using nativescript::engine::MutableBuffer; +using nativescript::engine::Object; +using nativescript::engine::PropNameID; +using nativescript::engine::Runtime; +using nativescript::engine::String; +using nativescript::engine::StringBuffer; +using nativescript::engine::Value; +using nativescript::engine::JSError; +using metagen::MDMemberFlag; +using metagen::MDMetadataReader; +using metagen::MDSectionOffset; +using metagen::MDTypeKind; + +// clang-format off +#define NATIVESCRIPT_NATIVE_API_BACKEND_NAME "v8" +#include "../shared/bridge/ObjCBridge.mm" +// clang-format on + +#define NATIVESCRIPT_NATIVE_API_HAS_ENGINE_LAZY_GLOBALS 1 +#define NATIVESCRIPT_NATIVE_API_HAS_ENGINE_SELECTOR_GROUP_FUNCTION 1 +#define NATIVESCRIPT_NATIVE_API_RETAIN_RUNTIME 1 +#define NATIVESCRIPT_NATIVE_API_RUNTIME_SCOPE 1 + +#include "NativeApiV8RuntimeSupport.mm" + +// clang-format off +#include "../shared/bridge/HostObjects.mm" +#include "../shared/bridge/Callbacks.mm" +#include "../shared/bridge/TypeConv.mm" +#include "../shared/bridge/Invocation.mm" +#include "../shared/bridge/ClassBuilder.mm" +#include "../shared/bridge/HostObject.mm" +// clang-format on + + +#include "NativeApiV8SelectorGroups.mm" + +} // namespace + +#include "../shared/bridge/Install.mm" + +void InstallNativeApi(v8::Isolate* isolate, v8::Local context, + const NativeApiConfig& config) { + if (isolate == nullptr || context.IsEmpty()) { + return; + } + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Context::Scope contextScope(context); + Runtime runtime(isolate, context); + InstallNativeApi(runtime, config); +} + +} // namespace nativescript + +extern "C" void NativeScriptInstallNativeApi(v8::Isolate* isolate, v8::Local context, + const char* metadataPath) { + nativescript::NativeApiConfig config; + config.metadataPath = metadataPath; + nativescript::InstallNativeApi(isolate, context, config); +} + +#endif // TARGET_ENGINE_V8 diff --git a/NativeScript/ffi/objc/v8/NativeApiV8Gsd.mm b/NativeScript/ffi/objc/v8/NativeApiV8Gsd.mm new file mode 100644 index 000000000..6b108acd9 --- /dev/null +++ b/NativeScript/ffi/objc/v8/NativeApiV8Gsd.mm @@ -0,0 +1,222 @@ +// --- GSD (Generated Signature Dispatch) for V8 --- +// GsdObjCContext is the engine-neutral interface the generated invokers use: +// it reads JS arguments and writes the JS return value via the V8 API. The +// readers mirror V8's generic argument/return conversions exactly; any value +// that is not in the fast representation makes a reader return false so the +// invoker falls back to the fully correct generic path. +struct GsdObjCContext; +using ObjCGsdInvoker = bool (*)(GsdObjCContext&); +struct ObjCGsdDispatchEntry { + uint64_t dispatchId; + ObjCGsdInvoker invoker; +}; + +struct GsdObjCContext { + Runtime& runtime; + const std::shared_ptr& bridge; + id self; + SEL selector; + const v8::FunctionCallbackInfo& info; + v8::Isolate* isolate; + v8::Local jsContext; + const NativeApiType& returnType; + + template + void invokeNative(Invocation&& invocation) { + performGeneratedObjCInvocation(runtime, bridge, [&]() { invocation(); }); + } + + v8::Local arg(size_t i) const { + return info[static_cast(i)]; + } + + bool readBool(size_t i, uint8_t* out) { + *out = arg(i)->BooleanValue(isolate) ? 1 : 0; + return true; + } + template + bool readSigned(size_t i, T* out) { + v8::Local v = arg(i); + if (v->IsInt32()) { + *out = static_cast(v.As()->Value()); + return true; + } + if constexpr (sizeof(T) <= 4) { + int32_t tmp = 0; + if (!v->Int32Value(jsContext).To(&tmp)) return false; + *out = static_cast(tmp); + } else { + if (v->IsBigInt()) { + bool lossless = false; + *out = static_cast(v.As()->Int64Value(&lossless)); + } else { + int64_t tmp = 0; + if (!v->IntegerValue(jsContext).To(&tmp)) return false; + *out = static_cast(tmp); + } + } + return true; + } + template + bool readUnsigned(size_t i, T* out) { + v8::Local v = arg(i); + if (v->IsUint32()) { + *out = static_cast(v.As()->Value()); + return true; + } + if (v->IsInt32()) { + *out = static_cast(v.As()->Value()); + return true; + } + if constexpr (sizeof(T) <= 4) { + uint32_t tmp = 0; + if (!v->Uint32Value(jsContext).To(&tmp)) return false; + *out = static_cast(tmp); + } else { + if (v->IsBigInt()) { + bool lossless = false; + *out = static_cast(v.As()->Uint64Value(&lossless)); + } else { + int64_t tmp = 0; + if (!v->IntegerValue(jsContext).To(&tmp)) return false; + *out = static_cast(static_cast(tmp)); + } + } + return true; + } + bool readFloat(size_t i, float* out) { + double tmp = 0.0; + if (!readDouble(i, &tmp)) return false; + *out = static_cast(tmp); + return true; + } + bool readDouble(size_t i, double* out) { + v8::Local v = arg(i); + if (v->IsNumber()) { + *out = v.As()->Value(); + return true; + } + return v->NumberValue(jsContext).To(out); + } + bool readSelector(size_t i, SEL* out) { + return readV8EngineSelectorArgument(runtime, arg(i), out); + } + bool readClass(size_t i, Class* out) { + Class cls = v8NativeClassArgument(runtime, arg(i)); + if (cls == Nil) return false; + *out = cls; + return true; + } + bool readObject(size_t i, id* out) { + v8::Local v = arg(i); + if (v.IsEmpty() || v->IsNullOrUndefined()) { + *out = nil; + return true; + } + if (!v->IsObject()) return false; + if (auto* h = v8HostObjectRaw(v)) { + *out = h->object(); + return true; + } + if (auto* c = v8HostObjectRaw(v)) { + *out = static_cast(c->nativeClass()); + return true; + } + Class cls = v8NativeClassArgument(runtime, v); + if (cls != Nil) { + *out = static_cast(cls); + return true; + } + if (auto* p = v8HostObjectRaw(v)) { + *out = static_cast(p->nativeProtocol()); + return true; + } + return false; + } + + void setVoid() {} + void setBool(bool v) { + info.GetReturnValue().Set(v8::Boolean::New(isolate, v)); + } + void setInt32(int32_t v) { + info.GetReturnValue().Set(v8::Integer::New(isolate, v)); + } + void setUInt32(uint32_t v) { + info.GetReturnValue().Set(v8::Integer::NewFromUnsigned(isolate, v)); + } + void setUInt16(uint16_t v) { + if (v >= 32 && v <= 126) { + char buffer[2] = {static_cast(v), '\0'}; + info.GetReturnValue().Set( + engine::v8engine::makeV8String(isolate, buffer)); + } else { + info.GetReturnValue().Set(v8::Integer::NewFromUnsigned(isolate, v)); + } + } + void setInt64(int64_t v) { + info.GetReturnValue().Set(v8Integer64Value(isolate, v)); + } + void setUInt64(uint64_t v) { + info.GetReturnValue().Set(v8UnsignedInteger64Value(isolate, v)); + } + void setDouble(double v) { + info.GetReturnValue().Set(v8::Number::New(isolate, v)); + } + void setSelector(SEL v) { + const char* name = v != nullptr ? sel_getName(v) : nullptr; + if (name == nullptr) { + info.GetReturnValue().Set(v8::Null(isolate)); + } else { + info.GetReturnValue().Set(engine::v8engine::makeV8String(isolate, name)); + } + } + void setClass(Class v) { + if (v == nil) { + info.GetReturnValue().Set(v8::Null(isolate)); + return; + } + const char* name = class_getName(v); + NativeApiSymbol symbol{ + .kind = NativeApiSymbolKind::Class, + .offset = MD_SECTION_OFFSET_NULL, + .name = name != nullptr ? name : "", + .runtimeName = name != nullptr ? name : "", + }; + if (const NativeApiSymbol* found = bridge->findClass(symbol.name)) { + symbol = *found; + } + Value result = makeNativeClassValue(runtime, bridge, std::move(symbol)); + info.GetReturnValue().Set(result.local(runtime)); + } + void setObject(id obj) { + setV8EngineObjectReturn(runtime, bridge, returnType, obj, info); + } +}; + +// Close the anonymous namespace so the generated dispatch table lives in +// namespace nativescript (visible to lookupObjCGsdInvoker). GsdObjCContext is +// reachable from there via the unnamed namespace's implicit using-directive. +} // namespace (temporary close for GSD .inc) + +#if defined(__has_include) +#if __has_include("GeneratedGsdSignatureDispatch.inc") +#include "GeneratedGsdSignatureDispatch.inc" +#endif +#endif + +#ifndef NS_HAS_GENERATED_SIGNATURE_GSD_DISPATCH +inline constexpr ObjCGsdDispatchEntry kGeneratedObjCGsdDispatchEntries[] = { + {0, nullptr}}; +#endif + +ObjCGsdInvoker lookupObjCGsdInvoker(uint64_t dispatchId) { + if (!isGeneratedDispatchEnabled()) { + return nullptr; + } + return lookupDispatchInvoker( + kGeneratedObjCGsdDispatchEntries, dispatchId); +} + +namespace { // reopen anonymous namespace + +// --- End GSD --- diff --git a/NativeScript/ffi/objc/v8/NativeApiV8HostObjects.mm b/NativeScript/ffi/objc/v8/NativeApiV8HostObjects.mm new file mode 100644 index 000000000..6064601ea --- /dev/null +++ b/NativeScript/ffi/objc/v8/NativeApiV8HostObjects.mm @@ -0,0 +1,411 @@ +#include "NativeApiV8Runtime.h" + +#ifdef TARGET_ENGINE_V8 + +namespace nativescript { +namespace engine { + +namespace v8engine { + +Value valueFromLocal(Runtime& runtime, v8::Local value) { return Value(runtime, value); } + +template +class StackValueArray { + public: + explicit StackValueArray(size_t count) : count_(count) { + if (count_ > InlineCount) { + values_ = static_cast(::operator new(sizeof(Value) * count_)); + } else { + values_ = reinterpret_cast(inlineStorage_); + } + } + + ~StackValueArray() { + for (size_t i = 0; i < constructed_; i++) { + values_[i].~Value(); + } + if (count_ > InlineCount) { + ::operator delete(values_); + } + } + + StackValueArray(const StackValueArray&) = delete; + StackValueArray& operator=(const StackValueArray&) = delete; + + void emplace(size_t index, Value&& value) { + new (&values_[index]) Value(std::move(value)); + constructed_++; + } + + Value* data() { return count_ == 0 ? nullptr : values_; } + size_t size() const { return count_; } + + private: + size_t count_ = 0; + size_t constructed_ = 0; + Value* values_ = nullptr; + alignas(Value) unsigned char inlineStorage_[sizeof(Value) * InlineCount]; +}; + +v8::Local hostObjectTemplate(Runtime& runtime) { + auto state = runtime.state(); + if (state->hostObjectTemplate.IsEmpty()) { + v8::Local objectTemplate = v8::ObjectTemplate::New(runtime.isolate()); + objectTemplate->SetInternalFieldCount(1); + // toString must be own property to override Object.prototype.toString + // when using kNonMasking interceptor. + objectTemplate->Set( + makeV8String(runtime.isolate(), "toString"), + v8::FunctionTemplate::New(runtime.isolate(), + [](const v8::FunctionCallbackInfo& info) { + v8::Local self = info.This(); + if (self.IsEmpty() || self->InternalFieldCount() < 1) return; + auto* holder = static_cast( + self->GetAlignedPointerFromInternalField(0)); + if (holder == nullptr || holder->hostObject == nullptr) return; + Runtime rt(holder->state); + try { + Value toStr = holder->hostObject->get(rt, PropNameID("toString")); + if (!toStr.isUndefined()) { + v8::Local v8Val = toStr.local(rt); + if (v8Val->IsFunction()) { + v8::Local result; + if (v8Val.As()->Call(rt.context(), self, 0, nullptr) + .ToLocal(&result)) { + info.GetReturnValue().Set(result); + return; + } + } + } + } catch (...) {} + }), + v8::DontEnum); + objectTemplate->SetHandler(v8::NamedPropertyHandlerConfiguration( + [](v8::Local property, + const v8::PropertyCallbackInfo& info) -> v8::Intercepted { + auto* holder = + static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); + if (holder == nullptr || holder->hostObject == nullptr) { + return v8::Intercepted::kNo; + } + // Fast path: skip symbols entirely (they never match our properties). + if (!property->IsString()) { + return v8::Intercepted::kNo; + } + Runtime runtime(holder->state); + try { + v8::Isolate* isolate = info.GetIsolate(); + v8::String::Utf8Value utf8(isolate, property); + if (*utf8 == nullptr) { + return v8::Intercepted::kNo; + } + Value result = holder->hostObject->get( + runtime, PropNameID(std::string(*utf8, utf8.length()))); + if (!result.isUndefined()) { + info.GetReturnValue().Set(result.local(runtime)); + return v8::Intercepted::kYes; + } + } catch (const std::exception& exception) { + throwV8Exception(info.GetIsolate(), exception); + return v8::Intercepted::kYes; + } + return v8::Intercepted::kNo; + }, + [](v8::Local property, v8::Local value, + const v8::PropertyCallbackInfo& info) -> v8::Intercepted { + auto* holder = + static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); + if (holder == nullptr || holder->hostObject == nullptr) { + return v8::Intercepted::kNo; + } + Runtime runtime(holder->state); + try { + bool handled = holder->hostObject->set( + runtime, PropNameID(propertyNameToUtf8(info.GetIsolate(), property)), + Value(runtime, value)); + return handled ? v8::Intercepted::kYes : v8::Intercepted::kNo; + } catch (const std::exception& exception) { + throwV8Exception(info.GetIsolate(), exception); + return v8::Intercepted::kYes; + } + }, + nullptr, nullptr, + [](const v8::PropertyCallbackInfo& info) { + auto* holder = + static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); + if (holder == nullptr || holder->hostObject == nullptr) { + return; + } + Runtime runtime(holder->state); + try { + auto propertyNames = holder->hostObject->getPropertyNames(runtime); + v8::Local result = + v8::Array::New(info.GetIsolate(), static_cast(propertyNames.size())); + for (size_t i = 0; i < propertyNames.size(); i++) { + std::string name = propertyNames[i].utf8(runtime); + result + ->Set(runtime.context(), static_cast(i), + makeV8String(info.GetIsolate(), name)) + .FromMaybe(false); + } + info.GetReturnValue().Set(result); + } catch (const std::exception& exception) { + throwV8Exception(info.GetIsolate(), exception); + } + }, + v8::Local(), v8::PropertyHandlerFlags::kNone)); + objectTemplate->SetHandler(v8::IndexedPropertyHandlerConfiguration( + [](uint32_t index, const v8::PropertyCallbackInfo& info) -> v8::Intercepted { + auto* holder = + static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); + if (holder == nullptr || holder->hostObject == nullptr) { + return v8::Intercepted::kNo; + } + Runtime runtime(holder->state); + try { + Value result = holder->hostObject->get(runtime, PropNameID(std::to_string(index))); + if (!result.isUndefined()) { + info.GetReturnValue().Set(result.local(runtime)); + return v8::Intercepted::kYes; + } + } catch (const std::exception& exception) { + throwV8Exception(info.GetIsolate(), exception); + return v8::Intercepted::kYes; + } + return v8::Intercepted::kNo; + }, + [](uint32_t index, v8::Local value, + const v8::PropertyCallbackInfo& info) -> v8::Intercepted { + auto* holder = + static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); + if (holder == nullptr || holder->hostObject == nullptr) { + return v8::Intercepted::kNo; + } + Runtime runtime(holder->state); + try { + holder->hostObject->set(runtime, PropNameID(std::to_string(index)), + Value(runtime, value)); + return v8::Intercepted::kYes; + } catch (const std::exception& exception) { + throwV8Exception(info.GetIsolate(), exception); + return v8::Intercepted::kYes; + } + }, + nullptr, nullptr, nullptr, v8::Local(), + v8::PropertyHandlerFlags::kNone)); + state->hostObjectTemplate.Reset(runtime.isolate(), objectTemplate); + } + return state->hostObjectTemplate.Get(runtime.isolate()); +} + +// Template for native object instances — uses kNonMasking so V8 checks +// prototype chain first (methods/properties installed there are found +// without calling the interceptor). +v8::Local nativeObjectTemplate(Runtime& runtime) { + auto state = runtime.state(); + if (state->nativeObjectTemplate.IsEmpty()) { + v8::Local objectTemplate = v8::ObjectTemplate::New(runtime.isolate()); + objectTemplate->SetInternalFieldCount(1); + // toString must be own property to override Object.prototype.toString + objectTemplate->Set( + makeV8String(runtime.isolate(), "toString"), + v8::FunctionTemplate::New(runtime.isolate(), + [](const v8::FunctionCallbackInfo& info) { + v8::Local self = info.This(); + if (self.IsEmpty() || self->InternalFieldCount() < 1) return; + auto* holder = static_cast( + self->GetAlignedPointerFromInternalField(0)); + if (holder == nullptr || holder->hostObject == nullptr) return; + Runtime rt(holder->state); + try { + Value toStr = holder->hostObject->get(rt, PropNameID("toString")); + if (!toStr.isUndefined()) { + v8::Local v8Val = toStr.local(rt); + if (v8Val->IsFunction()) { + v8::Local result; + if (v8Val.As()->Call(rt.context(), self, 0, nullptr) + .ToLocal(&result)) { + info.GetReturnValue().Set(result); + return; + } + } + } + } catch (...) {} + }), + v8::DontEnum); + objectTemplate->SetHandler(v8::NamedPropertyHandlerConfiguration( + [](v8::Local property, + const v8::PropertyCallbackInfo& info) -> v8::Intercepted { + auto* holder = + static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); + if (holder == nullptr || holder->hostObject == nullptr) { + return v8::Intercepted::kNo; + } + if (!property->IsString()) { + return v8::Intercepted::kNo; + } + Runtime runtime(holder->state); + try { + v8::Isolate* isolate = info.GetIsolate(); + v8::String::Utf8Value utf8(isolate, property); + if (*utf8 == nullptr) { + return v8::Intercepted::kNo; + } + Value result = holder->hostObject->get( + runtime, PropNameID(std::string(*utf8, utf8.length()))); + if (!result.isUndefined()) { + info.GetReturnValue().Set(result.local(runtime)); + return v8::Intercepted::kYes; + } + } catch (const std::exception& exception) { + throwV8Exception(info.GetIsolate(), exception); + return v8::Intercepted::kYes; + } + return v8::Intercepted::kNo; + }, + [](v8::Local property, v8::Local value, + const v8::PropertyCallbackInfo& info) -> v8::Intercepted { + auto* holder = + static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); + if (holder == nullptr || holder->hostObject == nullptr) { + return v8::Intercepted::kNo; + } + if (!property->IsString()) { + return v8::Intercepted::kNo; + } + Runtime runtime(holder->state); + try { + v8::Isolate* isolate = info.GetIsolate(); + v8::String::Utf8Value utf8(isolate, property); + if (*utf8 == nullptr) { + return v8::Intercepted::kNo; + } + bool handled = holder->hostObject->set( + runtime, PropNameID(std::string(*utf8, utf8.length())), + Value(runtime, value)); + return handled ? v8::Intercepted::kYes : v8::Intercepted::kNo; + } catch (const std::exception& exception) { + throwV8Exception(info.GetIsolate(), exception); + return v8::Intercepted::kYes; + } + }, + nullptr, nullptr, nullptr, v8::Local(), + v8::PropertyHandlerFlags::kNonMasking)); + objectTemplate->SetHandler(v8::IndexedPropertyHandlerConfiguration( + [](uint32_t index, const v8::PropertyCallbackInfo& info) -> v8::Intercepted { + auto* holder = + static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); + if (holder == nullptr || holder->hostObject == nullptr) { + return v8::Intercepted::kNo; + } + Runtime runtime(holder->state); + try { + Value result = holder->hostObject->get(runtime, PropNameID(std::to_string(index))); + if (!result.isUndefined()) { + info.GetReturnValue().Set(result.local(runtime)); + return v8::Intercepted::kYes; + } + } catch (const std::exception& exception) { + throwV8Exception(info.GetIsolate(), exception); + return v8::Intercepted::kYes; + } + return v8::Intercepted::kNo; + }, + [](uint32_t index, v8::Local value, + const v8::PropertyCallbackInfo& info) -> v8::Intercepted { + auto* holder = + static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); + if (holder == nullptr || holder->hostObject == nullptr) { + return v8::Intercepted::kNo; + } + Runtime runtime(holder->state); + try { + holder->hostObject->set(runtime, PropNameID(std::to_string(index)), + Value(runtime, value)); + return v8::Intercepted::kYes; + } catch (const std::exception& exception) { + throwV8Exception(info.GetIsolate(), exception); + return v8::Intercepted::kYes; + } + }, + nullptr, nullptr, nullptr, v8::Local(), + v8::PropertyHandlerFlags::kNonMasking)); + state->nativeObjectTemplate.Reset(runtime.isolate(), objectTemplate); + } + return state->nativeObjectTemplate.Get(runtime.isolate()); +} + +void hostObjectWeakCallback(const v8::WeakCallbackInfo& info) { + delete info.GetParameter(); +} + +void functionWeakCallback(const v8::WeakCallbackInfo& info) { + delete info.GetParameter(); +} + +} // namespace v8engine + +Object Object::createFromHostObjectWithToken(Runtime& runtime, std::shared_ptr host, + const void* typeToken) { + v8::Local object = + v8engine::hostObjectTemplate(runtime)->NewInstance(runtime.context()).ToLocalChecked(); + auto* holder = new v8engine::HostObjectHolder(runtime.state(), std::move(host), typeToken); + object->SetAlignedPointerInInternalField(0, holder); + holder->object.Reset(runtime.isolate(), object); + holder->object.SetWeak(holder, v8engine::hostObjectWeakCallback, + v8::WeakCallbackType::kParameter); + return Object::fromValueStorage(Value(runtime, object).storage_); +} + +Object Object::createNativeInstanceWithToken(Runtime& runtime, std::shared_ptr host, + const void* typeToken) { + v8::Local object = + v8engine::nativeObjectTemplate(runtime)->NewInstance(runtime.context()).ToLocalChecked(); + auto* holder = new v8engine::HostObjectHolder(runtime.state(), std::move(host), typeToken); + object->SetAlignedPointerInInternalField(0, holder); + holder->object.Reset(runtime.isolate(), object); + holder->object.SetWeak(holder, v8engine::hostObjectWeakCallback, + v8::WeakCallbackType::kParameter); + return Object::fromValueStorage(Value(runtime, object).storage_); +} + +Function Function::createFromHostFunction(Runtime& runtime, const PropNameID& name, unsigned int, + HostFunctionType callback) { + auto* holder = new v8engine::FunctionHolder(runtime.state(), std::move(callback)); + v8::Local data = v8::External::New(runtime.isolate(), holder); + v8::Local functionTemplate = v8::FunctionTemplate::New( + runtime.isolate(), + [](const v8::FunctionCallbackInfo& info) { + auto* holder = + static_cast(info.Data().As()->Value()); + Runtime runtime(holder->state); + v8engine::StackValueArray<8> args(static_cast(info.Length())); + for (int i = 0; i < info.Length(); i++) { + args.emplace(static_cast(i), Value::borrowed(runtime, info[i])); + } + try { + Value thisValue = Value::borrowed(runtime, info.This()); + Value result = holder->callback(runtime, thisValue, args.size() == 0 ? nullptr : args.data(), + args.size()); + info.GetReturnValue().Set(result.local(runtime)); + } catch (const std::exception& exception) { + v8engine::throwV8Exception(info.GetIsolate(), exception); + } + }, + data); + v8::Local function = + functionTemplate->GetFunction(runtime.context()).ToLocalChecked(); + std::string functionName = name.utf8(runtime); + if (!functionName.empty()) { + function->SetName(v8engine::makeV8String(runtime.isolate(), functionName)); + } + holder->function.Reset(runtime.isolate(), function); + holder->function.SetWeak(holder, v8engine::functionWeakCallback, + v8::WeakCallbackType::kParameter); + return Function(Object::fromValueStorage(Value(runtime, function).storage_)); +} + +} // namespace engine +} // namespace nativescript + +#endif // TARGET_ENGINE_V8 diff --git a/NativeScript/ffi/objc/v8/NativeApiV8Marshalling.mm b/NativeScript/ffi/objc/v8/NativeApiV8Marshalling.mm new file mode 100644 index 000000000..fc9de4db9 --- /dev/null +++ b/NativeScript/ffi/objc/v8/NativeApiV8Marshalling.mm @@ -0,0 +1,574 @@ +// Included by NativeApiV8SelectorGroups.mm inside the NativeScript anonymous namespace. + +std::string v8StringToUtf8(v8::Isolate* isolate, + v8::Local value) { + v8::String::Utf8Value utf8(isolate, value); + return *utf8 != nullptr ? std::string(*utf8, utf8.length()) : std::string(); +} + +template +std::shared_ptr v8HostObject(Runtime& runtime, v8::Local value) { + if (value.IsEmpty() || !value->IsObject()) { + return nullptr; + } + v8::Local object = value.As(); + if (object->InternalFieldCount() < 1) { + return nullptr; + } + auto* holder = static_cast( + object->GetAlignedPointerFromInternalField(0)); + if (holder == nullptr || + holder->typeToken != engine::v8engine::hostObjectTypeToken()) { + return nullptr; + } + return std::static_pointer_cast(holder->hostObject); +} + +// Fast version that returns raw pointer (no atomic ref count). +// Only safe when the caller guarantees the object stays alive. +template +T* v8HostObjectRaw(v8::Local value) { + if (value.IsEmpty() || !value->IsObject()) { + return nullptr; + } + v8::Local object = value.As(); + if (object->InternalFieldCount() < 1) { + return nullptr; + } + auto* holder = static_cast( + object->GetAlignedPointerFromInternalField(0)); + if (holder == nullptr || + holder->typeToken != engine::v8engine::hostObjectTypeToken()) { + return nullptr; + } + return static_cast(holder->hostObject.get()); +} + +id v8NativeObjectArgument(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiType& type, + v8::Local value, + NativeApiArgumentFrame& frame) { + v8::Isolate* isolate = runtime.isolate(); + if (value.IsEmpty() || value->IsNullOrUndefined()) { + return nil; + } + if (value->IsString()) { + std::string utf8 = v8StringToUtf8(isolate, value); + id string = type.kind == metagen::mdTypeNSMutableStringObject + ? [[NSMutableString alloc] initWithBytes:utf8.data() + length:utf8.size() + encoding:NSUTF8StringEncoding] + : [[NSString alloc] initWithBytes:utf8.data() + length:utf8.size() + encoding:NSUTF8StringEncoding]; + if (string != nil) { + frame.addObject(string); + } + return string; + } + if (value->IsBoolean()) { + return [NSNumber numberWithBool:value->BooleanValue(isolate)]; + } + if (value->IsNumber()) { + return [NSNumber numberWithDouble:value->NumberValue(runtime.context()) + .FromMaybe(0)]; + } + if (!value->IsObject()) { + return nil; + } + if (auto objectHost = + v8HostObject(runtime, value)) { + return objectHost->object(); + } + if (auto classHost = v8HostObject(runtime, value)) { + return static_cast(classHost->nativeClass()); + } + if (auto protocolHost = + v8HostObject(runtime, value)) { + return static_cast(protocolHost->nativeProtocol()); + } + if (auto pointerHost = + v8HostObject(runtime, value)) { + return static_cast(pointerHost->pointer()); + } + if (auto referenceHost = + v8HostObject(runtime, value)) { + return static_cast(referenceHost->data()); + } + if (auto structHost = + v8HostObject(runtime, value)) { + return static_cast(structHost->data()); + } + + v8::Local wrappedClassValue; + if (value.As() + ->Get(runtime.context(), + engine::v8engine::makeV8String(isolate, "__nativeApiClass")) + .ToLocal(&wrappedClassValue)) { + if (auto classHost = + v8HostObject(runtime, wrappedClassValue)) { + return static_cast(classHost->nativeClass()); + } + } + + Value wrapped = Value::borrowed(runtime, value); + return objectFromEngineValue(runtime, bridge, wrapped, frame, + type.kind == + metagen::mdTypeNSMutableStringObject); +} + +Class v8NativeClassArgument(Runtime& runtime, v8::Local value) { + if (value.IsEmpty() || value->IsNullOrUndefined()) { + return Nil; + } + auto* state = runtime.rawState(); + if (state != nullptr && value->IsObject()) { + if (state->nativeClassArgumentLast.nativeClass != Nil && + !state->nativeClassArgumentLast.value.IsEmpty() && + state->nativeClassArgumentLast.value.Get(runtime.isolate()) == value) { + return state->nativeClassArgumentLast.nativeClass; + } + for (auto& entry : state->nativeClassArgumentCache) { + if (entry.nativeClass != Nil && !entry.value.IsEmpty() && + entry.value.Get(runtime.isolate()) == value) { + state->nativeClassArgumentLast.value.Reset(runtime.isolate(), value); + state->nativeClassArgumentLast.nativeClass = entry.nativeClass; + return entry.nativeClass; + } + } + } + + Class result = Nil; + if (auto classHost = v8HostObject(runtime, value)) { + result = classHost->nativeClass(); + } else if (value->IsObject()) { + v8::Local wrappedClassValue; + if (value.As() + ->Get(runtime.context(), + engine::v8engine::makeV8String(runtime.isolate(), + "__nativeApiClass")) + .ToLocal(&wrappedClassValue)) { + if (auto classHost = + v8HostObject(runtime, + wrappedClassValue)) { + result = classHost->nativeClass(); + } + } + } + + if (result == Nil) { + Value wrapped = Value::borrowed(runtime, value); + result = classFromEngineValue(runtime, wrapped); + } + + if (result != Nil && state != nullptr && value->IsObject()) { + constexpr size_t cacheSize = + sizeof(state->nativeClassArgumentCache) / + sizeof(state->nativeClassArgumentCache[0]); + auto& entry = state->nativeClassArgumentCache[ + state->nativeClassArgumentCacheNext++ % cacheSize]; + entry.value.Reset(runtime.isolate(), value); + entry.nativeClass = result; + state->nativeClassArgumentLast.value.Reset(runtime.isolate(), value); + state->nativeClassArgumentLast.nativeClass = result; + } + return result; +} + +bool readV8EngineSelectorArgument(Runtime& runtime, v8::Local value, + SEL* result) { + if (result == nullptr) { + return false; + } + if (value.IsEmpty() || value->IsNullOrUndefined()) { + *result = nullptr; + return true; + } + if (!value->IsString()) { + return false; + } + auto* state = runtime.rawState(); + if (state != nullptr) { + if (state->nativeSelectorArgumentLast.selector != nullptr && + !state->nativeSelectorArgumentLast.value.IsEmpty() && + state->nativeSelectorArgumentLast.value.Get(runtime.isolate()) == + value) { + *result = state->nativeSelectorArgumentLast.selector; + return true; + } + for (auto& entry : state->nativeSelectorArgumentCache) { + if (entry.selector != nullptr && !entry.value.IsEmpty() && + entry.value.Get(runtime.isolate()) == value) { + *result = entry.selector; + state->nativeSelectorArgumentLast.value.Reset(runtime.isolate(), value); + state->nativeSelectorArgumentLast.selector = entry.selector; + return true; + } + } + } + + v8::Isolate* isolate = runtime.isolate(); + v8::Local string = value.As(); + char stackBuffer[128]; + if (string->Utf8LengthV2(isolate) + 1 <= sizeof(stackBuffer)) { + string->WriteUtf8V2(isolate, stackBuffer, sizeof(stackBuffer), + v8::String::WriteFlags::kNullTerminate); + *result = sel_registerName(stackBuffer); + } else { + std::string selectorName = v8StringToUtf8(isolate, value); + *result = sel_registerName(selectorName.c_str()); + } + if (*result != nullptr && state != nullptr) { + constexpr size_t cacheSize = + sizeof(state->nativeSelectorArgumentCache) / + sizeof(state->nativeSelectorArgumentCache[0]); + auto& entry = state->nativeSelectorArgumentCache[ + state->nativeSelectorArgumentCacheNext++ % cacheSize]; + entry.value.Reset(isolate, value); + entry.selector = *result; + state->nativeSelectorArgumentLast.value.Reset(isolate, value); + state->nativeSelectorArgumentLast.selector = *result; + } + return true; +} + +bool prepareV8EngineArgument( + Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, v8::Local value, + NativeApiArgumentFrame& frame, size_t index) { + ffi_type* ffiType = ffiTypeForEngineArgument(type); + size_t size = + ffiType != nullptr && ffiType->size > 0 ? ffiType->size : nativeSizeForType(type); + void* target = frame.storageAt(index, size); + + switch (type.kind) { + case metagen::mdTypeBool: + if (!value->IsBoolean()) { + return false; + } + *static_cast(target) = + value->BooleanValue(runtime.isolate()) ? 1 : 0; + return true; + case metagen::mdTypeChar: { + int32_t converted = 0; + if (!value->Int32Value(runtime.context()).To(&converted)) { + return false; + } + *static_cast(target) = static_cast(converted); + return true; + } + case metagen::mdTypeUChar: + case metagen::mdTypeUInt8: { + uint32_t converted = 0; + if (!value->Uint32Value(runtime.context()).To(&converted)) { + return false; + } + *static_cast(target) = static_cast(converted); + return true; + } + case metagen::mdTypeSShort: { + int32_t converted = 0; + if (!value->Int32Value(runtime.context()).To(&converted)) { + return false; + } + *static_cast(target) = static_cast(converted); + return true; + } + case metagen::mdTypeUShort: { + if (value->IsString()) { + std::string text = v8StringToUtf8(runtime.isolate(), value); + if (text.size() != 1) { + return false; + } + *static_cast(target) = + static_cast(static_cast(text[0])); + return true; + } + uint32_t converted = 0; + if (!value->Uint32Value(runtime.context()).To(&converted)) { + return false; + } + *static_cast(target) = static_cast(converted); + return true; + } + case metagen::mdTypeSInt: + return value->Int32Value(runtime.context()).To( + static_cast(target)); + case metagen::mdTypeUInt: + return value->Uint32Value(runtime.context()).To( + static_cast(target)); + case metagen::mdTypeSLong: + case metagen::mdTypeSInt64: { + if (value->IsBigInt()) { + bool lossless = false; + *static_cast(target) = + value.As()->Int64Value(&lossless); + return true; + } + return value->IntegerValue(runtime.context()).To( + static_cast(target)); + } + case metagen::mdTypeULong: + case metagen::mdTypeUInt64: { + if (value->IsBigInt()) { + bool lossless = false; + *static_cast(target) = + value.As()->Uint64Value(&lossless); + return true; + } + int64_t converted = 0; + if (!value->IntegerValue(runtime.context()).To(&converted)) { + return false; + } + *static_cast(target) = static_cast(converted); + return true; + } + case metagen::mdTypeFloat: { + double converted = 0; + if (!value->NumberValue(runtime.context()).To(&converted)) { + return false; + } + *static_cast(target) = static_cast(converted); + return true; + } + case metagen::mdTypeDouble: + return value->NumberValue(runtime.context()).To( + static_cast(target)); + case metagen::mdTypeSelector: + return readV8EngineSelectorArgument(runtime, value, + static_cast(target)); + case metagen::mdTypeClass: { + Class cls = v8NativeClassArgument(runtime, value); + if (cls == Nil) { + return false; + } + *static_cast(target) = cls; + return true; + } + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: + *static_cast(target) = + v8NativeObjectArgument(runtime, bridge, type, value, frame); + return true; + default: + break; + } + + Value wrapped = Value::borrowed(runtime, value); + convertEngineFfiArgument(runtime, bridge, type, wrapped, target, frame); + return true; +} + +v8::Local v8Integer64Value(v8::Isolate* isolate, int64_t value) { + constexpr int64_t maxSafeInteger = 9007199254740991LL; + constexpr int64_t minSafeInteger = -9007199254740991LL; + if (value >= minSafeInteger && value <= maxSafeInteger) { + return v8::Number::New(isolate, static_cast(value)); + } + return v8::BigInt::New(isolate, value); +} + +v8::Local v8UnsignedInteger64Value(v8::Isolate* isolate, + uint64_t value) { + constexpr uint64_t maxSafeInteger = 9007199254740991ULL; + if (value <= maxSafeInteger) { + return v8::Number::New(isolate, static_cast(value)); + } + return v8::BigInt::NewFromUnsigned(isolate, value); +} + +bool setV8EngineObjectReturn( + Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiType& type, id object, + const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = runtime.isolate(); + if (object == nil) { + info.GetReturnValue().Set(v8::Null(isolate)); + return true; + } + Value roundTrip = + findCachedNativeObjectReturn(runtime, bridge, type, object); + if (!roundTrip.isUndefined()) { + info.GetReturnValue().Set(roundTrip.local(runtime)); + if (type.returnOwned) { + [object release]; + } + return true; + } + if (nativeObjectReturnMayCoerceToString(type) && + nativeObjectIsStringLike(object)) { + std::string utf8 = utf8StringFromNSString(static_cast(object)); + if (type.returnOwned) { + [object release]; + } + info.GetReturnValue().Set(engine::v8engine::makeV8String(isolate, utf8)); + return true; + } + if ([object isKindOfClass:[NSNull class]]) { + if (type.returnOwned) { + [object release]; + } + info.GetReturnValue().Set(v8::Null(isolate)); + return true; + } + if ([object isKindOfClass:[NSNumber class]] && + ![object isKindOfClass:[NSDecimalNumber class]]) { + NSNumber* number = static_cast(object); + const char* objCType = [number objCType]; + bool isBool = CFGetTypeID((__bridge CFTypeRef)number) == + CFBooleanGetTypeID() || + (objCType != nullptr && + std::strcmp(objCType, @encode(BOOL)) == 0); + if (isBool) { + info.GetReturnValue().Set(v8::Boolean::New(isolate, [number boolValue])); + } else { + info.GetReturnValue().Set(v8::Number::New(isolate, [number doubleValue])); + } + if (type.returnOwned) { + [object release]; + } + return true; + } + + if (const NativeApiSymbol* classSymbol = + bridge->findClassForRuntimePointer((void*)object)) { + Value result = makeNativeClassValue(runtime, bridge, *classSymbol); + info.GetReturnValue().Set(result.local(runtime)); + if (type.returnOwned) { + [object release]; + } + return true; + } + if (const NativeApiSymbol* protocolSymbol = + bridge->findProtocolForRuntimePointer((void*)object)) { + Value result = makeNativeProtocolValue(runtime, bridge, *protocolSymbol); + info.GetReturnValue().Set(result.local(runtime)); + if (type.returnOwned) { + [object release]; + } + return true; + } + Value result = makeNativeObjectValue(runtime, bridge, object, type.returnOwned); + info.GetReturnValue().Set(result.local(runtime)); + return true; +} + +bool setV8EngineReturnValue( + Runtime& runtime, const std::shared_ptr& bridge, + NativeApiType type, void* value, const std::string& selectorName, + const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = runtime.isolate(); + switch (type.kind) { + case metagen::mdTypeVoid: + info.GetReturnValue().Set(v8::Undefined(isolate)); + return true; + case metagen::mdTypeBool: + info.GetReturnValue().Set( + v8::Boolean::New(isolate, *static_cast(value) != 0)); + return true; + case metagen::mdTypeChar: + info.GetReturnValue().Set( + v8::Integer::New(isolate, *static_cast(value))); + return true; + case metagen::mdTypeUChar: + case metagen::mdTypeUInt8: + info.GetReturnValue().Set(v8::Integer::NewFromUnsigned( + isolate, *static_cast(value))); + return true; + case metagen::mdTypeSShort: + info.GetReturnValue().Set( + v8::Integer::New(isolate, *static_cast(value))); + return true; + case metagen::mdTypeUShort: { + uint16_t raw = *static_cast(value); + if (raw >= 32 && raw <= 126) { + char buffer[2] = {static_cast(raw), '\0'}; + info.GetReturnValue().Set(engine::v8engine::makeV8String(isolate, buffer)); + } else { + info.GetReturnValue().Set(v8::Integer::NewFromUnsigned(isolate, raw)); + } + return true; + } + case metagen::mdTypeSInt: + info.GetReturnValue().Set( + v8::Integer::New(isolate, *static_cast(value))); + return true; + case metagen::mdTypeUInt: + info.GetReturnValue().Set(v8::Integer::NewFromUnsigned( + isolate, *static_cast(value))); + return true; + case metagen::mdTypeSLong: + case metagen::mdTypeSInt64: + info.GetReturnValue().Set( + v8Integer64Value(isolate, *static_cast(value))); + return true; + case metagen::mdTypeULong: + case metagen::mdTypeUInt64: + info.GetReturnValue().Set( + v8UnsignedInteger64Value(isolate, *static_cast(value))); + return true; + case metagen::mdTypeFloat: + info.GetReturnValue().Set( + v8::Number::New(isolate, *static_cast(value))); + return true; + case metagen::mdTypeDouble: + info.GetReturnValue().Set( + v8::Number::New(isolate, *static_cast(value))); + return true; + case metagen::mdTypeClass: { + Class cls = *static_cast(value); + if (cls == nil) { + info.GetReturnValue().Set(v8::Null(isolate)); + return true; + } + const char* name = class_getName(cls); + NativeApiSymbol symbol{ + .kind = NativeApiSymbolKind::Class, + .offset = MD_SECTION_OFFSET_NULL, + .name = name != nullptr ? name : "", + .runtimeName = name != nullptr ? name : "", + }; + if (const NativeApiSymbol* found = bridge->findClass(symbol.name)) { + symbol = *found; + } + Value result = makeNativeClassValue(runtime, bridge, std::move(symbol)); + info.GetReturnValue().Set(result.local(runtime)); + return true; + } + case metagen::mdTypeAnyObject: + case metagen::mdTypeProtocolObject: + case metagen::mdTypeClassObject: + case metagen::mdTypeInstanceObject: + case metagen::mdTypeNSStringObject: + case metagen::mdTypeNSMutableStringObject: + if ((selectorName == "valueForKey:" || + selectorName == "valueForKeyPath:") && + isObjectiveCObjectType(type)) { + type.kind = metagen::mdTypeAnyObject; + } + return setV8EngineObjectReturn(runtime, bridge, type, + *static_cast(value), info); + case metagen::mdTypeSelector: { + SEL selector = *static_cast(value); + const char* selectorNameValue = + selector != nullptr ? sel_getName(selector) : nullptr; + if (selectorNameValue == nullptr) { + info.GetReturnValue().Set(v8::Null(isolate)); + } else { + info.GetReturnValue().Set( + engine::v8engine::makeV8String(isolate, selectorNameValue)); + } + return true; + } + default: + break; + } + Value result = convertNativeReturnValue(runtime, bridge, type, value); + info.GetReturnValue().Set(result.local(runtime)); + return true; +} diff --git a/NativeScript/ffi/objc/v8/NativeApiV8Runtime.h b/NativeScript/ffi/objc/v8/NativeApiV8Runtime.h new file mode 100644 index 000000000..0d43fed55 --- /dev/null +++ b/NativeScript/ffi/objc/v8/NativeApiV8Runtime.h @@ -0,0 +1,814 @@ +#ifndef NATIVESCRIPT_FFI_V8_NATIVE_API_V8_RUNTIME_H +#define NATIVESCRIPT_FFI_V8_NATIVE_API_V8_RUNTIME_H + +#ifdef TARGET_ENGINE_V8 + +#import +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Metadata.h" +#include "MetadataReader.h" +#include "ffi.h" +#include "v8.h" + +@protocol NativeApiClassBuilderProtocol +@end + +#ifdef EMBED_METADATA_SIZE +extern const unsigned char embedded_metadata[EMBED_METADATA_SIZE]; +#endif + +namespace nativescript { +namespace engine { + +class Runtime; +class Value; +class Object; +class Function; +class Array; +class String; +class BigInt; +class ArrayBuffer; + +class JSError : public std::runtime_error { + public: + JSError(Runtime&, const std::string& message) : std::runtime_error(message) {} + explicit JSError(const std::string& message) : std::runtime_error(message) {} +}; + +class StringBuffer { + public: + explicit StringBuffer(std::string value) : value_(std::move(value)) {} + const char* data() const { return value_.data(); } + size_t size() const { return value_.size(); } + + private: + std::string value_; +}; + +class MutableBuffer { + public: + virtual ~MutableBuffer() = default; + virtual size_t size() const = 0; + virtual uint8_t* data() = 0; +}; + +class PropNameID { + public: + PropNameID() = default; + explicit PropNameID(std::string value) : value_(std::move(value)) {} + + static PropNameID forAscii(Runtime&, const char* value) { + return PropNameID(value != nullptr ? value : ""); + } + + static PropNameID forAscii(Runtime&, const std::string& value) { return PropNameID(value); } + + std::string utf8(Runtime&) const { return value_; } + + private: + std::string value_; +}; + +class HostObject { + public: + virtual ~HostObject() = default; + virtual Value get(Runtime& runtime, const PropNameID& name); + virtual bool set(Runtime& runtime, const PropNameID& name, const Value& value); + virtual std::vector getPropertyNames(Runtime& runtime); +}; + +using HostFunctionType = std::function; + +namespace v8engine { + +struct RuntimeState { + explicit RuntimeState(v8::Isolate* isolate, v8::Local context) : isolate(isolate) { + this->context.Reset(isolate, context); + } + + ~RuntimeState() { + nativeClassArgumentLast.value.Reset(); + nativeClassArgumentLast.nativeClass = Nil; + for (auto& entry : nativeClassArgumentCache) { + entry.value.Reset(); + entry.nativeClass = Nil; + } + nativeSelectorArgumentLast.value.Reset(); + nativeSelectorArgumentLast.selector = nullptr; + for (auto& entry : nativeSelectorArgumentCache) { + entry.value.Reset(); + entry.selector = nullptr; + } + context.Reset(); + } + + v8::Local localContext() const { + v8::Local ctx = context.Get(isolate); + return ctx.IsEmpty() ? isolate->GetCurrentContext() : ctx; + } + + v8::Isolate* isolate = nullptr; + v8::Global context; + v8::Global hostObjectTemplate; + v8::Global nativeObjectTemplate; // kNonMasking for instances + std::vector> retainedNativeData; + struct NativeClassArgumentCacheEntry { + v8::Global value; + Class nativeClass = Nil; + }; + NativeClassArgumentCacheEntry nativeClassArgumentLast; + NativeClassArgumentCacheEntry nativeClassArgumentCache[4]; + size_t nativeClassArgumentCacheNext = 0; + struct NativeSelectorArgumentCacheEntry { + v8::Global value; + SEL selector = nullptr; + }; + NativeSelectorArgumentCacheEntry nativeSelectorArgumentLast; + NativeSelectorArgumentCacheEntry nativeSelectorArgumentCache[4]; + size_t nativeSelectorArgumentCacheNext = 0; +}; + +struct ValueStorage { + enum class Kind : uint8_t { + Undefined, + Null, + Bool, + Number, + V8, + V8Borrowed, + }; + + explicit ValueStorage(Kind kind) : kind(kind) {} + + ~ValueStorage() { value.Reset(); } + + Kind kind = Kind::Undefined; + bool boolValue = false; + double numberValue = 0; + v8::Global value; + v8::Local borrowedValue; +}; + +template +const void* hostObjectTypeToken() { + static int token = 0; + return &token; +} + +struct HostObjectHolder { + HostObjectHolder(std::shared_ptr state, std::shared_ptr hostObject, + const void* typeToken) + : state(std::move(state)), hostObject(std::move(hostObject)), typeToken(typeToken) {} + + ~HostObjectHolder() { object.Reset(); } + + std::shared_ptr state; + std::shared_ptr hostObject; + const void* typeToken = nullptr; + v8::Global object; +}; + +struct FunctionHolder { + FunctionHolder(std::shared_ptr state, HostFunctionType callback) + : state(std::move(state)), callback(std::move(callback)) {} + + ~FunctionHolder() { function.Reset(); } + + std::shared_ptr state; + HostFunctionType callback; + v8::Global function; +}; + +struct ArrayBufferHolder { + explicit ArrayBufferHolder(std::shared_ptr buffer) : buffer(std::move(buffer)) {} + + std::shared_ptr buffer; + v8::Global object; +}; + +inline v8::Local makeV8String(v8::Isolate* isolate, const std::string& value) { + return v8::String::NewFromUtf8(isolate, value.c_str(), v8::NewStringType::kNormal, + static_cast(value.size())) + .ToLocalChecked(); +} + +inline std::string toUtf8(v8::Isolate* isolate, v8::Local value) { + if (value.IsEmpty()) { + return {}; + } + v8::String::Utf8Value utf8(isolate, value); + return *utf8 != nullptr ? std::string(*utf8, utf8.length()) : std::string(); +} + +inline std::string propertyNameToUtf8(v8::Isolate* isolate, v8::Local property) { + if (property->IsSymbol() && + property.As()->StrictEquals(v8::Symbol::GetIterator(isolate))) { + return "Symbol.iterator"; + } + return toUtf8(isolate, property); +} + +inline std::string currentExceptionMessage(v8::Isolate* isolate, v8::TryCatch& tryCatch) { + if (tryCatch.HasCaught()) { + return toUtf8(isolate, tryCatch.Exception()); + } + return "NativeScript V8 engine operation failed."; +} + +inline void throwV8Exception(v8::Isolate* isolate, const std::exception& exception) { + isolate->ThrowException(v8::Exception::Error(makeV8String(isolate, exception.what()))); +} + +} // namespace v8engine + +class Runtime { + public: + Runtime(v8::Isolate* isolate, v8::Local context) + : state_(std::make_shared(isolate, context)) {} + + explicit Runtime(std::shared_ptr state) : state_(std::move(state)) {} + + v8::Isolate* isolate() const { return state_->isolate; } + v8::Local context() const { return state_->localContext(); } + v8engine::RuntimeState* rawState() const { return state_.get(); } + std::shared_ptr state() const { return state_; } + + Object global(); + + Value evaluateJavaScript(std::shared_ptr buffer, const std::string& sourceURL); + + void drainMicrotasks() { isolate()->PerformMicrotaskCheckpoint(); } + + private: + std::shared_ptr state_; +}; + +class String { + public: + String() = default; + String(Runtime& runtime, v8::Local value); + + static String createFromUtf8(Runtime& runtime, const char* value) { + return String(runtime, + v8engine::makeV8String(runtime.isolate(), value != nullptr ? value : "")); + } + + static String createFromUtf8(Runtime& runtime, const std::string& value) { + return String(runtime, v8engine::makeV8String(runtime.isolate(), value)); + } + + static String createFromUtf8(Runtime& runtime, const uint8_t* value, size_t length) { + return String(runtime, v8::String::NewFromUtf8( + runtime.isolate(), + reinterpret_cast( + value != nullptr ? value : reinterpret_cast("")), + v8::NewStringType::kNormal, static_cast(length)) + .ToLocalChecked()); + } + + std::string utf8(Runtime& runtime) const { + return v8engine::toUtf8(runtime.isolate(), local(runtime)); + } + + v8::Local local(Runtime& runtime) const { + return storage_->value.Get(runtime.isolate()).As(); + } + + operator Value() const; + + private: + friend class Value; + std::shared_ptr storage_; +}; + +class Value { + public: + Value() : kind_(v8engine::ValueStorage::Kind::Undefined) {} + + Value(bool value) : kind_(v8engine::ValueStorage::Kind::Bool), boolValue_(value) {} + + Value(double value) : kind_(v8engine::ValueStorage::Kind::Number), numberValue_(value) {} + + Value(int value) : Value(static_cast(value)) {} + Value(uint32_t value) : Value(static_cast(value)) {} + + Value(Runtime& runtime, const Value& value) { + if (value.kind_ == v8engine::ValueStorage::Kind::V8Borrowed) { + // Promote borrowed to owned + storage_ = + std::make_shared(v8engine::ValueStorage::Kind::V8); + storage_->value.Reset(runtime.isolate(), value.borrowedValue_); + kind_ = v8engine::ValueStorage::Kind::V8; + return; + } + kind_ = value.kind_; + boolValue_ = value.boolValue_; + numberValue_ = value.numberValue_; + borrowedValue_ = value.borrowedValue_; + storage_ = value.storage_; + } + Value(Runtime& runtime, Value&& value) + : kind_(value.kind_), + boolValue_(value.boolValue_), + numberValue_(value.numberValue_), + borrowedValue_(value.borrowedValue_), + storage_(std::move(value.storage_)) {} + Value(Runtime& runtime, const String& value); + Value(Runtime& runtime, const Object& object); + Value(Runtime& runtime, const Function& function); + Value(Runtime& runtime, const Array& array); + Value(Runtime& runtime, const ArrayBuffer& arrayBuffer); + Value(Runtime& runtime, const BigInt& bigint); + + static Value undefined() { return Value(); } + + static Value null() { + Value value; + value.kind_ = v8engine::ValueStorage::Kind::Null; + return value; + } + + bool isUndefined() const; + bool isNull() const; + bool isBool() const; + bool getBool() const; + bool isNumber() const; + double getNumber() const; + + bool isObject() const; + bool isString() const; + bool isBigInt() const; + bool isSymbol() const; + + Object asObject(Runtime& runtime) const; + String asString(Runtime& runtime) const; + BigInt getBigInt(Runtime& runtime) const; + + v8::Local local(Runtime& runtime) const { + v8::Isolate* isolate = runtime.isolate(); + switch (kind_) { + case v8engine::ValueStorage::Kind::Undefined: + return v8::Undefined(isolate); + case v8engine::ValueStorage::Kind::Null: + return v8::Null(isolate); + case v8engine::ValueStorage::Kind::Bool: + return v8::Boolean::New(isolate, boolValue_); + case v8engine::ValueStorage::Kind::Number: + return v8::Number::New(isolate, numberValue_); + case v8engine::ValueStorage::Kind::V8: + return storage_->value.Get(isolate); + case v8engine::ValueStorage::Kind::V8Borrowed: + return borrowedValue_; + } + } + + Value(Runtime& runtime, v8::Local value) + : kind_(v8engine::ValueStorage::Kind::V8), + storage_(std::make_shared(v8engine::ValueStorage::Kind::V8)) { + storage_->value.Reset(runtime.isolate(), value); + } + + static Value borrowed(Runtime&, v8::Local value) { + Value result; + result.kind_ = v8engine::ValueStorage::Kind::V8Borrowed; + result.borrowedValue_ = value; + return result; + } + + // Access the shared storage (for Object/Function/Array interop) + std::shared_ptr storage() const { return storage_; } + + static Value fromStorage(std::shared_ptr s) { + Value v; + v.kind_ = s->kind; + v.boolValue_ = s->boolValue; + v.numberValue_ = s->numberValue; + v.borrowedValue_ = s->borrowedValue; + v.storage_ = std::move(s); + return v; + } + + private: + friend class Runtime; + friend class Object; + friend class String; + friend class BigInt; + friend class ArrayBuffer; + friend class Function; + friend class Array; + + v8engine::ValueStorage::Kind kind_ = v8engine::ValueStorage::Kind::Undefined; + bool boolValue_ = false; + double numberValue_ = 0; + v8::Local borrowedValue_; + std::shared_ptr storage_; +}; + +class Object { + public: + Object() = default; + explicit Object(Runtime& runtime) + : storage_(std::make_shared(v8engine::ValueStorage::Kind::V8)) { + storage_->value.Reset(runtime.isolate(), v8::Object::New(runtime.isolate())); + } + + static Object fromValueStorage(std::shared_ptr storage) { + Object object; + object.storage_ = std::move(storage); + return object; + } + + template + static Object createFromHostObject(Runtime& runtime, std::shared_ptr host) { + auto baseHost = std::static_pointer_cast(std::move(host)); + return createFromHostObjectWithToken(runtime, std::move(baseHost), + v8engine::hostObjectTypeToken()); + } + + // Create a native object instance using kNonMasking template for fast + // prototype-based property access. + template + static Object createNativeInstanceHostObject(Runtime& runtime, std::shared_ptr host) { + auto baseHost = std::static_pointer_cast(std::move(host)); + return createNativeInstanceWithToken(runtime, std::move(baseHost), + v8engine::hostObjectTypeToken()); + } + + static Object createNativeInstanceWithToken(Runtime& runtime, std::shared_ptr host, + const void* typeToken); + + Value getProperty(Runtime& runtime, const char* name) const { + return getProperty(runtime, + v8engine::makeV8String(runtime.isolate(), name != nullptr ? name : "")); + } + + Value getProperty(Runtime& runtime, const std::string& name) const { + return getProperty(runtime, name.c_str()); + } + + Value getProperty(Runtime& runtime, const Value& key) const { + return getProperty(runtime, key.local(runtime)); + } + + Value getProperty(Runtime& runtime, v8::Local key) const { + v8::TryCatch tryCatch(runtime.isolate()); + v8::Local result; + if (!local(runtime)->Get(runtime.context(), key).ToLocal(&result)) { + throw JSError(runtime, v8engine::currentExceptionMessage(runtime.isolate(), tryCatch)); + } + return Value(runtime, result); + } + + Object getPropertyAsObject(Runtime& runtime, const char* name) const { + return getProperty(runtime, name).asObject(runtime); + } + + Function getPropertyAsFunction(Runtime& runtime, const char* name) const; + + void setProperty(Runtime& runtime, const char* name, const Value& value) { + setProperty(runtime, v8engine::makeV8String(runtime.isolate(), name != nullptr ? name : ""), + value); + } + + void setProperty(Runtime& runtime, const char* name, const String& value) { + setProperty(runtime, name, Value(runtime, value)); + } + + void setProperty(Runtime& runtime, const char* name, const Object& value) { + setProperty(runtime, name, Value(runtime, value)); + } + + void setProperty(Runtime& runtime, const char* name, const Function& value); + void setProperty(Runtime& runtime, const char* name, const Array& value); + void setProperty(Runtime& runtime, const char* name, const ArrayBuffer& value); + void setProperty(Runtime& runtime, const char* name, bool value) { + setProperty(runtime, name, Value(value)); + } + void setProperty(Runtime& runtime, const char* name, double value) { + setProperty(runtime, name, Value(value)); + } + + void setProperty(Runtime& runtime, const std::string& name, const Value& value) { + setProperty(runtime, name.c_str(), value); + } + + void setProperty(Runtime& runtime, const Value& key, const Value& value) { + setProperty(runtime, key.local(runtime), value); + } + + void setProperty(Runtime& runtime, v8::Local key, const Value& value) { + v8::TryCatch tryCatch(runtime.isolate()); + if (!local(runtime)->Set(runtime.context(), key, value.local(runtime)).FromMaybe(false)) { + throw JSError(runtime, v8engine::currentExceptionMessage(runtime.isolate(), tryCatch)); + } + } + + bool hasProperty(Runtime& runtime, const char* name) const { + v8::TryCatch tryCatch(runtime.isolate()); + return local(runtime) + ->Has(runtime.context(), + v8engine::makeV8String(runtime.isolate(), name != nullptr ? name : "")) + .FromMaybe(false); + } + + bool isFunction(Runtime& runtime) const { return local(runtime)->IsFunction(); } + bool isArray(Runtime& runtime) const { return local(runtime)->IsArray(); } + bool isArrayBuffer(Runtime& runtime) const { return local(runtime)->IsArrayBuffer(); } + + Function asFunction(Runtime& runtime) const; + Array getArray(Runtime& runtime) const; + ArrayBuffer getArrayBuffer(Runtime& runtime) const; + Array getPropertyNames(Runtime& runtime) const; + + template + bool isHostObject(Runtime& runtime) const { + auto holder = hostObjectHolder(runtime); + return holder != nullptr && holder->typeToken == v8engine::hostObjectTypeToken(); + } + + template + std::shared_ptr getHostObject(Runtime& runtime) const { + auto holder = hostObjectHolder(runtime); + if (holder == nullptr || holder->typeToken != v8engine::hostObjectTypeToken()) { + return nullptr; + } + return std::static_pointer_cast(holder->hostObject); + } + + v8::Local local(Runtime& runtime) const { + if (storage_->kind == v8engine::ValueStorage::Kind::V8Borrowed) { + return storage_->borrowedValue.As(); + } + return storage_->value.Get(runtime.isolate()).As(); + } + + operator Value() const { + return Value::fromStorage(storage_); + } + + protected: + friend class Value; + friend class Runtime; + friend class Function; + friend class Array; + friend class ArrayBuffer; + + explicit Object(std::shared_ptr storage) : storage_(std::move(storage)) {} + + static Object createFromHostObjectWithToken(Runtime& runtime, std::shared_ptr host, + const void* typeToken); + + v8engine::HostObjectHolder* hostObjectHolder(Runtime& runtime) const { + v8::Local object = local(runtime); + if (object->InternalFieldCount() < 1) { + return nullptr; + } + return static_cast(object->GetAlignedPointerFromInternalField(0)); + } + + std::shared_ptr storage_; +}; + +class Function : public Object { + public: + Function() = default; + explicit Function(Object object) : Object(std::move(object.storage_)) {} + + static Function createFromHostFunction(Runtime& runtime, const PropNameID& name, unsigned int, + HostFunctionType callback); + + Value call(Runtime& runtime, const Value* args, size_t count) const { + v8::TryCatch tryCatch(runtime.isolate()); + std::vector> argv; + argv.reserve(count); + for (size_t i = 0; i < count; i++) { + argv.push_back(args[i].local(runtime)); + } + v8::Local result; + if (!local(runtime) + .As() + ->Call(runtime.context(), runtime.context()->Global(), static_cast(argv.size()), + argv.data()) + .ToLocal(&result)) { + throw JSError(runtime, v8engine::currentExceptionMessage(runtime.isolate(), tryCatch)); + } + return Value(runtime, result); + } + + Value call(Runtime& runtime) const { + return call(runtime, static_cast(nullptr), 0); + } + + Value call(Runtime& runtime, std::nullptr_t, size_t) const { + return call(runtime, static_cast(nullptr), 0); + } + + template + Value call(Runtime& runtime, const Value (&args)[N], size_t count) const { + return call(runtime, static_cast(args), count); + } + + template + Value call(Runtime& runtime, Args&&... args) const { + Value argv[] = {Value(runtime, std::forward(args))...}; + return call(runtime, static_cast(argv), sizeof...(Args)); + } + + Value callWithThis(Runtime& runtime, const Object& thisObject, const Value* args = nullptr, + size_t count = 0) const { + v8::TryCatch tryCatch(runtime.isolate()); + std::vector> argv; + argv.reserve(count); + for (size_t i = 0; i < count; i++) { + argv.push_back(args[i].local(runtime)); + } + v8::Local result; + if (!local(runtime) + .As() + ->Call(runtime.context(), thisObject.local(runtime), static_cast(argv.size()), + argv.data()) + .ToLocal(&result)) { + throw JSError(runtime, v8engine::currentExceptionMessage(runtime.isolate(), tryCatch)); + } + return Value(runtime, result); + } + + Value callAsConstructor(Runtime& runtime, const Value* args, size_t count) const { + v8::TryCatch tryCatch(runtime.isolate()); + std::vector> argv; + argv.reserve(count); + for (size_t i = 0; i < count; i++) { + argv.push_back(args[i].local(runtime)); + } + v8::Local result; + if (!local(runtime) + .As() + ->NewInstance(runtime.context(), static_cast(argv.size()), argv.data()) + .ToLocal(&result)) { + throw JSError(runtime, v8engine::currentExceptionMessage(runtime.isolate(), tryCatch)); + } + return Value(runtime, result); + } + + Value callAsConstructor(Runtime& runtime, std::nullptr_t, size_t) const { + return callAsConstructor(runtime, static_cast(nullptr), 0); + } + + template + Value callAsConstructor(Runtime& runtime, const Value (&args)[N], size_t count) const { + return callAsConstructor(runtime, static_cast(args), count); + } + + template + Value callAsConstructor(Runtime& runtime, Args&&... args) const { + Value argv[] = {Value(runtime, std::forward(args))...}; + return callAsConstructor(runtime, static_cast(argv), sizeof...(Args)); + } + + operator Value() const { + return Value::fromStorage(storage_); + } +}; + +class Array : public Object { + public: + explicit Array(Runtime& runtime, size_t size) + : Object(std::make_shared(v8engine::ValueStorage::Kind::V8)) { + storage_->value.Reset(runtime.isolate(), + v8::Array::New(runtime.isolate(), static_cast(size))); + } + + explicit Array(Object object) : Object(std::move(object.storage_)) {} + + size_t size(Runtime& runtime) const { return local(runtime).As()->Length(); } + + Value getValueAtIndex(Runtime& runtime, size_t index) const { + v8::TryCatch tryCatch(runtime.isolate()); + v8::Local result; + if (!local(runtime)->Get(runtime.context(), static_cast(index)).ToLocal(&result)) { + throw JSError(runtime, v8engine::currentExceptionMessage(runtime.isolate(), tryCatch)); + } + return Value(runtime, result); + } + + void setValueAtIndex(Runtime& runtime, size_t index, const Value& value) { + v8::TryCatch tryCatch(runtime.isolate()); + if (!local(runtime) + ->Set(runtime.context(), static_cast(index), value.local(runtime)) + .FromMaybe(false)) { + throw JSError(runtime, v8engine::currentExceptionMessage(runtime.isolate(), tryCatch)); + } + } + + void setValueAtIndex(Runtime& runtime, size_t index, const String& value) { + setValueAtIndex(runtime, index, Value(runtime, value)); + } + + operator Value() const { + return Value::fromStorage(storage_); + } +}; + +class BigInt { + public: + BigInt() = default; + BigInt(Runtime& runtime, v8::Local value) + : storage_(std::make_shared(v8engine::ValueStorage::Kind::V8)) { + storage_->value.Reset(runtime.isolate(), value); + } + + static BigInt fromInt64(Runtime& runtime, int64_t value) { + return BigInt(runtime, v8::BigInt::New(runtime.isolate(), value)); + } + + static BigInt fromUint64(Runtime& runtime, uint64_t value) { + return BigInt(runtime, v8::BigInt::NewFromUnsigned(runtime.isolate(), value)); + } + + String toString(Runtime& runtime, int radix) const { + v8::TryCatch tryCatch(runtime.isolate()); + v8::Local result; + (void)radix; + if (!local(runtime)->ToString(runtime.context()).ToLocal(&result)) { + throw JSError(runtime, v8engine::currentExceptionMessage(runtime.isolate(), tryCatch)); + } + return String(runtime, result); + } + + v8::Local local(Runtime& runtime) const { + return storage_->value.Get(runtime.isolate()).As(); + } + + operator Value() const { + return Value::fromStorage(storage_); + } + + private: + friend class Value; + std::shared_ptr storage_; +}; + +class ArrayBuffer : public Object { + public: + ArrayBuffer(Runtime& runtime, std::shared_ptr buffer) + : Object(std::make_shared(v8engine::ValueStorage::Kind::V8)) { + auto holder = new v8engine::ArrayBufferHolder(std::move(buffer)); + auto backingStore = v8::ArrayBuffer::NewBackingStore( + holder->buffer->data(), holder->buffer->size(), + [](void*, size_t, void* deleterData) { + auto* holder = static_cast(deleterData); + holder->object.Reset(); + delete holder; + }, + holder); + v8::Local arrayBuffer = + v8::ArrayBuffer::New(runtime.isolate(), std::move(backingStore)); + storage_->value.Reset(runtime.isolate(), arrayBuffer); + holder->object.Reset(runtime.isolate(), arrayBuffer); + } + + explicit ArrayBuffer(Object object) : Object(std::move(object.storage_)) {} + + size_t size(Runtime& runtime) const { return local(runtime).As()->ByteLength(); } + + uint8_t* data(Runtime& runtime) const { + auto backingStore = local(runtime).As()->GetBackingStore(); + return static_cast(backingStore->Data()); + } + + operator Value() const { + return Value::fromStorage(storage_); + } +}; +} // namespace engine +} // namespace nativescript + +#endif // TARGET_ENGINE_V8 + +#endif // NATIVESCRIPT_FFI_V8_NATIVE_API_V8_RUNTIME_H diff --git a/NativeScript/ffi/v8/NativeApiV8Runtime.mm b/NativeScript/ffi/objc/v8/NativeApiV8Runtime.mm similarity index 77% rename from NativeScript/ffi/v8/NativeApiV8Runtime.mm rename to NativeScript/ffi/objc/v8/NativeApiV8Runtime.mm index 681a211b0..10f8e3d86 100644 --- a/NativeScript/ffi/v8/NativeApiV8Runtime.mm +++ b/NativeScript/ffi/objc/v8/NativeApiV8Runtime.mm @@ -2,8 +2,8 @@ #ifdef TARGET_ENGINE_V8 -namespace facebook { -namespace jsi { +namespace nativescript { +namespace engine { Object Runtime::global() { return Object::fromValueStorage(Value(*this, context()->Global()).storage_); @@ -17,20 +17,20 @@ v8::NewStringType::kNormal, buffer != nullptr ? static_cast(buffer->size()) : 0) .ToLocalChecked(); - v8::Local resourceName = v8direct::makeV8String(isolate(), sourceURL); + v8::Local resourceName = v8engine::makeV8String(isolate(), sourceURL); v8::ScriptOrigin origin(resourceName); v8::Local script; if (!v8::Script::Compile(context(), source, &origin).ToLocal(&script)) { - throw JSError(*this, v8direct::currentExceptionMessage(isolate(), tryCatch)); + throw JSError(*this, v8engine::currentExceptionMessage(isolate(), tryCatch)); } v8::Local result; if (!script->Run(context()).ToLocal(&result)) { - throw JSError(*this, v8direct::currentExceptionMessage(isolate(), tryCatch)); + throw JSError(*this, v8engine::currentExceptionMessage(isolate(), tryCatch)); } return Value(*this, result); } -} // namespace jsi -} // namespace facebook +} // namespace engine +} // namespace nativescript #endif // TARGET_ENGINE_V8 diff --git a/NativeScript/ffi/objc/v8/NativeApiV8RuntimeSupport.mm b/NativeScript/ffi/objc/v8/NativeApiV8RuntimeSupport.mm new file mode 100644 index 000000000..558f6565d --- /dev/null +++ b/NativeScript/ffi/objc/v8/NativeApiV8RuntimeSupport.mm @@ -0,0 +1,121 @@ +// Included by NativeApiV8.mm inside the NativeScript anonymous namespace. + +struct NativeApiLazyGlobalData { + NativeApiLazyGlobalData(v8::Isolate* isolate, const std::string& name, + const std::string& kind) { + nameValue.Reset(isolate, engine::v8engine::makeV8String(isolate, name)); + kindValue.Reset(isolate, engine::v8engine::makeV8String(isolate, kind)); + } + + ~NativeApiLazyGlobalData() { + nameValue.Reset(); + kindValue.Reset(); + } + + v8::Global nameValue; + v8::Global kindValue; +}; + +std::shared_ptr retainNativeApiRuntime(Runtime& runtime) { + return std::make_shared(runtime.state()); +} + +class NativeApiRuntimeScope final { + public: + explicit NativeApiRuntimeScope(Runtime& runtime) + : locker_(runtime.isolate()), + isolateScope_(runtime.isolate()), + handleScope_(runtime.isolate()), + context_(runtime.context()), + contextScope_(context_) {} + + private: + v8::Locker locker_; + v8::Isolate::Scope isolateScope_; + v8::HandleScope handleScope_; + v8::Local context_; + v8::Context::Scope contextScope_; +}; + +void NativeApiLazyGlobalGetter(v8::Local, + const v8::PropertyCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + v8::HandleScope handleScope(isolate); + v8::Local context = isolate->GetCurrentContext(); + if (!info.Data()->IsExternal()) { + return; + } + + auto* data = static_cast(info.Data().As()->Value()); + if (data == nullptr) { + return; + } + v8::Local nameValue = data->nameValue.Get(isolate); + v8::Local kindValue = data->kindValue.Get(isolate); + + v8::Local global = context->Global(); + v8::Local resolverValue; + if (!global + ->Get(context, engine::v8engine::makeV8String( + isolate, "__nativeScriptResolveNativeApiLazyGlobal")) + .ToLocal(&resolverValue) || + !resolverValue->IsFunction()) { + return; + } + + v8::TryCatch tryCatch(isolate); + v8::Local args[] = {nameValue, kindValue}; + v8::Local result; + if (!resolverValue.As()->Call(context, global, 2, args).ToLocal(&result)) { + if (tryCatch.HasCaught()) { + isolate->ThrowException(tryCatch.Exception()); + } + return; + } + if (global->Delete(context, nameValue).FromMaybe(false)) { + global->DefineOwnProperty(context, nameValue, result, v8::DontEnum).FromMaybe(false); + } + info.GetReturnValue().Set(result); +} + +bool InstallNativeApiLazyGlobal(Runtime& runtime, std::shared_ptr, + const std::string& name, const std::string& kind, + bool force) { + if (name.empty() || kind.empty()) { + return false; + } + + v8::Isolate* isolate = runtime.isolate(); + v8::EscapableHandleScope handleScope(isolate); + v8::Local context = runtime.context(); + v8::Local global = context->Global(); + v8::Local property = engine::v8engine::makeV8String(isolate, name); + if (!force && global->HasOwnProperty(context, property).FromMaybe(false)) { + return false; + } + + auto data = std::make_shared(isolate, name, kind); + v8::Local external = v8::External::New(isolate, data.get()); + + bool installed = global + ->SetNativeDataProperty(context, property, NativeApiLazyGlobalGetter, + nullptr, external, v8::DontEnum) + .FromMaybe(false); + if (installed) { + runtime.state()->retainedNativeData.push_back(std::move(data)); + } + return installed; +} + +void SetNativeApiObjectPrototype(Runtime& runtime, Object& object, + const Object& prototype) { + v8::TryCatch tryCatch(runtime.isolate()); + if (!object.local(runtime) + ->SetPrototypeV2(runtime.context(), prototype.local(runtime)) + .FromMaybe(false)) { + throw JSError(runtime, + engine::v8engine::currentExceptionMessage(runtime.isolate(), + tryCatch)); + } +} + diff --git a/NativeScript/ffi/objc/v8/NativeApiV8SelectorGroups.mm b/NativeScript/ffi/objc/v8/NativeApiV8SelectorGroups.mm new file mode 100644 index 000000000..5b77573bd --- /dev/null +++ b/NativeScript/ffi/objc/v8/NativeApiV8SelectorGroups.mm @@ -0,0 +1,451 @@ +// Included by NativeApiV8.mm inside the NativeScript anonymous namespace. + +struct NativeApiSelectorGroupData { + NativeApiSelectorGroupData( + std::shared_ptr state, + std::shared_ptr bridge, Class lookupClass, + bool receiverIsClass, + std::shared_ptr> + selectors, + std::shared_ptr< + std::vector>> + preparedInvocations, + std::weak_ptr boundReceiver = {}, + std::shared_ptr boundReceiverState = + nullptr) + : state(state), + bridge(std::move(bridge)), + lookupClass(lookupClass), + receiverIsClass(receiverIsClass), + selectors(std::move(selectors)), + preparedInvocations(std::move(preparedInvocations)), + boundReceiver(std::move(boundReceiver)), + boundReceiverState(std::move(boundReceiverState)), + runtime(state) {} + + std::shared_ptr state; + std::shared_ptr bridge; + Class lookupClass = Nil; + bool receiverIsClass = false; + std::shared_ptr> selectors; + std::shared_ptr< + std::vector>> + preparedInvocations; + std::weak_ptr boundReceiver; + std::shared_ptr boundReceiverState; + // Cached Runtime wrapper reused per call (avoids per-call shared_ptr + // atomic refcount on the hot dispatch path). + Runtime runtime; + // 1-entry memo for dispatchSuperclassForEngineDerivedReceiver. + Class cachedReceiverClass = Nil; + Class cachedDispatchClass = Nil; +}; + +#include "NativeApiV8Marshalling.mm" + +#include "NativeApiV8Gsd.mm" + + +void* lookupGeneratedEngineObjCGsdInvoker(uint64_t dispatchId) { + return reinterpret_cast(lookupObjCGsdInvoker(dispatchId)); +} + +bool tryCallGeneratedEngineObjCSelector( + Runtime&, const std::shared_ptr&, id, + const NativeApiPreparedObjCInvocation&, const Value*, size_t, Class, + Value*) { + return false; +} + +void setV8EnginePreparedObjCResult( + Runtime& runtime, const std::shared_ptr& bridge, + id receiver, const NativeApiPreparedObjCInvocation& prepared, + const std::shared_ptr& receiverHostObject, + const std::optional& initializerClassWrapper, + const v8::FunctionCallbackInfo& info, + Class dispatchSuperClass) { + const NativeApiSignature& signature = prepared.signature; + if (receiver == nil || signature.variadic || + unsupportedEngineType(signature.returnType)) { + throw JSError(runtime, + "Objective-C selector is not supported by V8 engine: " + + prepared.selectorName); + } + + const bool isNSErrorOutMethod = prepared.isNSErrorOutMethod; + const size_t providedCount = static_cast(info.Length()); + if (isNSErrorOutMethod) { + size_t expected = signature.argumentTypes.size(); + if (providedCount > expected || providedCount + 1 < expected) { + throw JSError( + runtime, "Actual arguments count: \"" + std::to_string(providedCount) + + "\". Expected: \"" + std::to_string(expected) + "\"."); + } + } else if (providedCount != signature.argumentTypes.size()) { + throw JSError( + runtime, "Actual arguments count: \"" + std::to_string(providedCount) + + "\". Expected: \"" + + std::to_string(signature.argumentTypes.size()) + "\"."); + } + + // GSD fast path: the generated invoker reads args directly from + // FunctionCallbackInfo, calls objc_msgSend with a typed cast, and sets the + // return via the V8 API — all in one generated function. Bypasses all + // generic marshalling. + if (prepared.gsdEngineCallable && dispatchSuperClass == Nil && + providedCount == prepared.gsdEngineArgumentCount && + !initializerClassWrapper && !isNSErrorOutMethod) { + auto invoker = reinterpret_cast(prepared.engineInvoker); + GsdObjCContext ctx{runtime, + bridge, + receiver, + prepared.selector, + info, + runtime.isolate(), + runtime.context(), + signature.returnType}; + if (invoker(ctx)) { + return; + } + } + + if (dispatchSuperClass == Nil && !initializerClassWrapper && + providedCount <= 2) { + Value fastArgs[2]; + for (size_t i = 0; i < providedCount; i++) { + fastArgs[i] = Value::borrowed(runtime, info[static_cast(i)]); + } + Value fastResult; + if (tryCallFastEngineObjCSelector(runtime, bridge, receiver, prepared, + fastArgs, providedCount, Nil, + &fastResult)) { + info.GetReturnValue().Set(fastResult.local(runtime)); + return; + } + } + + NativeApiArgumentFrame frame(signature.argumentTypes.size()); + for (size_t i = 0; i < providedCount; i++) { + if (!prepareV8EngineArgument(runtime, bridge, signature.argumentTypes[i], + info[static_cast(i)], frame, i)) { + throw JSError(runtime, + "Objective-C argument is not supported by V8 engine: " + + prepared.selectorName); + } + } + + const bool hasImplicitNSErrorOutArg = + isNSErrorOutMethod && providedCount + 1 == signature.argumentTypes.size(); + NSError* implicitNSError = nil; + if (hasImplicitNSErrorOutArg) { + size_t outArgIndex = signature.argumentTypes.size() - 1; + void* target = frame.storageAt(outArgIndex, sizeof(NSError**)); + NSError** implicitNSErrorOutArg = &implicitNSError; + *static_cast(target) = implicitNSErrorOutArg; + } + + NativeApiPointerFrame values(signature.argumentTypes.size() + 2); + size_t valueIndex = 0; + struct objc_super superReceiver = {receiver, dispatchSuperClass}; + struct objc_super* superReceiverPtr = &superReceiver; + if (dispatchSuperClass != Nil) { + values.set(valueIndex++, &superReceiverPtr); + } else { + values.set(valueIndex++, &receiver); + } + values.set(valueIndex++, const_cast(&prepared.selector)); + for (size_t i = 0; i < signature.argumentTypes.size(); i++) { + values.set(valueIndex++, frame.values()[i]); + } + + NativeApiReturnStorage returnStorage( + nativeSizeForType(signature.returnType)); + performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + if (prepared.preparedInvoker != nullptr && dispatchSuperClass == Nil) { + prepared.preparedInvoker(reinterpret_cast(objc_msgSend), + values.data(), returnStorage.data()); + } else { +#if defined(__x86_64__) + bool isStret = signature.returnType.ffiType->size > 16 && + signature.returnType.ffiType->type == FFI_TYPE_STRUCT; + void (*target)(void) = + dispatchSuperClass != Nil + ? (isStret ? FFI_FN(objc_msgSendSuper_stret) + : FFI_FN(objc_msgSendSuper)) + : (isStret ? FFI_FN(objc_msgSend_stret) : FFI_FN(objc_msgSend)); + ffi_call(const_cast(&signature.cif), target, + returnStorage.data(), values.data()); +#else + ffi_call(const_cast(&signature.cif), + dispatchSuperClass != Nil ? FFI_FN(objc_msgSendSuper) + : FFI_FN(objc_msgSend), + returnStorage.data(), values.data()); +#endif + } + }); + + NativeApiType returnType = signature.returnType; + if (hasImplicitNSErrorOutArg && implicitNSError != nil) { + const char* errorMessage = [[implicitNSError description] UTF8String]; + throw JSError( + runtime, errorMessage != nullptr ? errorMessage : "Unknown NSError"); + } + if (initializerClassWrapper) { + id resultObject = nil; + if (isObjectiveCObjectType(returnType)) { + resultObject = *static_cast(returnStorage.data()); + } + if (receiverHostObject != nullptr && resultObject != receiver) { + receiverHostObject->disownObject(receiver); + } + if (resultObject != nil) { + bridge->setObjectExpando(runtime, resultObject, + "__nativeApiClassWrapper", + Value(runtime, *initializerClassWrapper)); + } + } + setV8EngineReturnValue(runtime, bridge, returnType, returnStorage.data(), + prepared.selectorName, info); +} + +void NativeApiSelectorGroupCallback( + const v8::FunctionCallbackInfo& info) { + auto* data = static_cast( + info.Data().As()->Value()); + if (data == nullptr || data->selectors == nullptr || + data->preparedInvocations == nullptr) { + return; + } + + Runtime& runtime = data->runtime; + v8::HandleScope handleScope(runtime.isolate()); + try { + NativeApiRoundTripCacheFrameGuard roundTripFrame(data->bridge); + size_t count = static_cast(info.Length()); + if (count >= data->selectors->size() || + (*data->selectors)[count].selectorName.empty()) { + throw JSError(runtime, + "Objective-C selector is not available for the provided arguments " + "count."); + } + + NativeApiSelectorGroupEntry& entry = (*data->selectors)[count]; + auto& prepared = (*data->preparedInvocations)[count]; + Class selectorLookupClass = data->lookupClass; + id receiver = data->receiverIsClass ? static_cast(data->lookupClass) : nil; + std::shared_ptr receiverHostObject; + if (!data->receiverIsClass) { + if (data->boundReceiverState != nullptr) { + receiver = data->boundReceiverState->object(); + if (receiver == nil) { + throw JSError(runtime, + "Objective-C selector requires a native receiver."); + } + } else { + // Use raw pointer for receiver lookup (avoids atomic ref count on hot path). + // The receiver host object is kept alive by the V8 GC handle. + auto* rawHost = v8HostObjectRaw(info.This()); + if (rawHost != nullptr) { + receiver = rawHost->object(); + // Only get shared_ptr if needed for init handling below. + } + } + } + if (receiver == nil) { + throw JSError(runtime, + "Objective-C selector requires a native receiver."); + } + + const bool propertyGetterCall = + entry.hasMember && entry.member.property && count == 0; + const std::string* selectorNamePtr = &entry.selectorName; + const NativeApiMember* selectedMember = + entry.hasMember ? &entry.member : nullptr; + bool callTargetCanPrepare = true; + if (prepared == nullptr || propertyGetterCall) { + NativeApiSelectorGroupCallTarget callTarget = + selectorGroupCallTargetForEntry(receiver, selectorLookupClass, + data->receiverIsClass, entry, count); + selectorNamePtr = callTarget.selectorName; + selectedMember = callTarget.member; + callTargetCanPrepare = callTarget.canPrepare; + if (prepared != nullptr && prepared->selectorName != *selectorNamePtr) { + prepared = nullptr; + } + } + const std::string& selectorName = + prepared != nullptr && !propertyGetterCall ? prepared->selectorName + : *selectorNamePtr; + + if (data->receiverIsClass) { + Class methodClass = prepared != nullptr ? prepared->receiverClass : Nil; + if (methodClass == Nil) { + SEL selector = sel_registerName(selectorName.c_str()); + methodClass = + NativeApiClassHostObject::classRespondingToClassSelector( + data->lookupClass, selector); + } + if (methodClass == Nil) { + throw JSError(runtime, + "Objective-C selector is not available: " + + entry.selectorName); + } + selectorLookupClass = methodClass; + receiver = static_cast(methodClass); + } + if (propertyGetterCall && !callTargetCanPrepare) { + Value result = callObjCSelector(runtime, data->bridge, receiver, + data->receiverIsClass, selectorName, + selectedMember, nullptr, 0); + info.GetReturnValue().Set(result.local(runtime)); + return; + } + + if (prepared == nullptr) { + // First call: resolve the method and cache the prepared invocation. + if (!data->receiverIsClass) { + SEL selector = sel_registerName(selectorName.c_str()); + if (class_getInstanceMethod(selectorLookupClass, selector) == nullptr) { + Class receiverClass = object_getClass(receiver); + if (class_getInstanceMethod(receiverClass, selector) != nullptr) { + selectorLookupClass = receiverClass; + } + } + } + prepared = prepareNativeApiObjCInvocation( + runtime, data->bridge, selectorLookupClass, data->receiverIsClass, + selectorName, selectedMember); + // Look up the engine-neutral GSD invoker for this signature. + if (prepared->engineInvoker == nullptr) { + uint64_t dispatchId = dispatchIdForEngineSignature( + prepared->signature, SignatureCallKind::ObjCMethod); + if (auto gsdInvoker = lookupObjCGsdInvoker(dispatchId)) { + prepared->engineInvoker = reinterpret_cast(gsdInvoker); + configureGeneratedEngineObjCInvocation(*prepared); + } + } + } + + std::optional initializerClassWrapper; + if (!data->receiverIsClass && prepared->isInitMethod) { + // Init methods need the shared_ptr for disown handling. + if (!receiverHostObject) { + if (data->boundReceiverState != nullptr) { + if (auto boundReceiver = data->boundReceiver.lock()) { + receiverHostObject = std::move(boundReceiver); + } + } + } + if (!receiverHostObject) { + receiverHostObject = + v8HostObject(runtime, info.This()); + } + Value classWrapperValue = data->bridge->findObjectExpando( + runtime, receiver, "__nativeApiClassWrapper"); + if (classWrapperValue.isObject()) { + initializerClassWrapper.emplace(classWrapperValue.asObject(runtime)); + } + data->bridge->forgetRoundTripValue(receiver); + data->bridge->forgetObjectExpandos(receiver); + } + + // For JS-extended receivers, dispatch from the immediate native + // superclass so native-derived overrides are honored (not the method's + // defining ancestor, which would skip intermediate native overrides). + // dispatchSuperclassForEngineDerivedReceiver is a pure function of the + // receiver's class + lookupClass, so memoize it (1-entry cache) to avoid a + // per-call class_conformsToProtocol on the hot path. + Class dispatchClass = Nil; + if (!data->receiverIsClass) { + Class receiverClass = object_getClass(receiver); + if (receiverClass == data->cachedReceiverClass) { + dispatchClass = data->cachedDispatchClass; + } else { + dispatchClass = dispatchSuperclassForEngineDerivedReceiver( + receiver, data->lookupClass); + data->cachedReceiverClass = receiverClass; + data->cachedDispatchClass = dispatchClass; + } + } + // Inline GSD fast path: skip the setV8EnginePreparedObjCResult call and its + // argument-count/NSError preamble entirely for the common case. The + // generated invoker reads args, calls objc_msgSend, and sets the return. + if (prepared->gsdEngineCallable && dispatchClass == Nil && + !prepared->isInitMethod && + count == prepared->gsdEngineArgumentCount) { + auto invoker = reinterpret_cast(prepared->engineInvoker); + GsdObjCContext ctx{runtime, + data->bridge, + receiver, + prepared->selector, + info, + runtime.isolate(), + runtime.context(), + prepared->signature.returnType}; + if (invoker(ctx)) { + return; + } + } + setV8EnginePreparedObjCResult(runtime, data->bridge, receiver, *prepared, + receiverHostObject, initializerClassWrapper, + info, dispatchClass); + } catch (const std::exception& exception) { + engine::v8engine::throwV8Exception(info.GetIsolate(), exception); + } +} + +Function CreateNativeApiSelectorGroupFunctionImpl( + Runtime& runtime, std::shared_ptr bridge, + Class lookupClass, bool receiverIsClass, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations, + std::weak_ptr boundReceiver, + std::shared_ptr boundReceiverState = + nullptr) { + auto data = std::make_shared( + runtime.state(), std::move(bridge), lookupClass, receiverIsClass, + std::move(selectors), std::move(preparedInvocations), + std::move(boundReceiver), std::move(boundReceiverState)); + auto* rawData = data.get(); + runtime.state()->retainedNativeData.push_back(std::move(data)); + + v8::Local external = + v8::External::New(runtime.isolate(), rawData); + v8::Local functionTemplate = + v8::FunctionTemplate::New(runtime.isolate(), + NativeApiSelectorGroupCallback, external); + v8::Local function = + functionTemplate->GetFunction(runtime.context()).ToLocalChecked(); + function->SetName( + engine::v8engine::makeV8String(runtime.isolate(), "__nativeSelectorGroup")); + Value functionValue(runtime, function); + return functionValue.asObject(runtime).asFunction(runtime); +} + +Function CreateNativeApiSelectorGroupFunction( + Runtime& runtime, std::shared_ptr bridge, + Class lookupClass, bool receiverIsClass, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations) { + return CreateNativeApiSelectorGroupFunctionImpl( + runtime, std::move(bridge), lookupClass, receiverIsClass, + std::move(selectors), std::move(preparedInvocations), {}, nullptr); +} + +Function CreateNativeApiBoundSelectorGroupFunction( + Runtime& runtime, std::shared_ptr bridge, Class lookupClass, + std::shared_ptr receiverHostObject, + std::shared_ptr> selectors, + std::shared_ptr< + std::vector>> + preparedInvocations) { + return CreateNativeApiSelectorGroupFunctionImpl( + runtime, std::move(bridge), lookupClass, false, std::move(selectors), + std::move(preparedInvocations), receiverHostObject, + receiverHostObject != nullptr ? receiverHostObject->lifetimeState() + : nullptr); +} diff --git a/NativeScript/ffi/objc/v8/NativeApiV8Value.mm b/NativeScript/ffi/objc/v8/NativeApiV8Value.mm new file mode 100644 index 000000000..dab1e0271 --- /dev/null +++ b/NativeScript/ffi/objc/v8/NativeApiV8Value.mm @@ -0,0 +1,242 @@ +#include "NativeApiV8Runtime.h" + +#ifdef TARGET_ENGINE_V8 + +namespace nativescript { +namespace engine { + +Value HostObject::get(Runtime&, const PropNameID&) { return Value::undefined(); } + +bool HostObject::set(Runtime&, const PropNameID&, const Value&) { return true; } + +std::vector HostObject::getPropertyNames(Runtime&) { return {}; } + +String::String(Runtime& runtime, v8::Local value) + : storage_(std::make_shared(v8engine::ValueStorage::Kind::V8)) { + storage_->value.Reset(runtime.isolate(), value); +} + +String::operator Value() const { + return Value::fromStorage(storage_); +} + +Value::Value(Runtime&, const String& value) { + storage_ = value.storage_; + kind_ = storage_->kind; +} +Value::Value(Runtime&, const Object& object) { + storage_ = object.storage_; + kind_ = storage_ ? storage_->kind : v8engine::ValueStorage::Kind::Undefined; +} +Value::Value(Runtime&, const Function& function) { + storage_ = function.storage_; + kind_ = storage_ ? storage_->kind : v8engine::ValueStorage::Kind::Undefined; +} +Value::Value(Runtime&, const Array& array) { + storage_ = array.storage_; + kind_ = storage_ ? storage_->kind : v8engine::ValueStorage::Kind::Undefined; +} +Value::Value(Runtime&, const ArrayBuffer& arrayBuffer) { + storage_ = arrayBuffer.storage_; + kind_ = storage_ ? storage_->kind : v8engine::ValueStorage::Kind::Undefined; +} +Value::Value(Runtime&, const BigInt& bigint) { + storage_ = bigint.storage_; + kind_ = storage_ ? storage_->kind : v8engine::ValueStorage::Kind::Undefined; +} + +bool Value::isObject() const { + if (kind_ == v8engine::ValueStorage::Kind::V8Borrowed) { + return !borrowedValue_.IsEmpty() && borrowedValue_->IsObject(); + } + if (kind_ != v8engine::ValueStorage::Kind::V8 || !storage_ || storage_->value.IsEmpty()) { + return false; + } + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + return isolate != nullptr && storage_->value.Get(isolate)->IsObject(); +} + +bool Value::isUndefined() const { + if (kind_ == v8engine::ValueStorage::Kind::Undefined) { + return true; + } + if (kind_ == v8engine::ValueStorage::Kind::V8Borrowed) { + return borrowedValue_.IsEmpty() || borrowedValue_->IsUndefined(); + } + if (kind_ != v8engine::ValueStorage::Kind::V8 || !storage_ || storage_->value.IsEmpty()) { + return false; + } + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + return isolate != nullptr && storage_->value.Get(isolate)->IsUndefined(); +} + +bool Value::isNull() const { + if (kind_ == v8engine::ValueStorage::Kind::Null) { + return true; + } + if (kind_ == v8engine::ValueStorage::Kind::V8Borrowed) { + return !borrowedValue_.IsEmpty() && borrowedValue_->IsNull(); + } + if (kind_ != v8engine::ValueStorage::Kind::V8 || !storage_ || storage_->value.IsEmpty()) { + return false; + } + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + return isolate != nullptr && storage_->value.Get(isolate)->IsNull(); +} + +bool Value::isBool() const { + if (kind_ == v8engine::ValueStorage::Kind::Bool) { + return true; + } + if (kind_ == v8engine::ValueStorage::Kind::V8Borrowed) { + return !borrowedValue_.IsEmpty() && borrowedValue_->IsBoolean(); + } + if (kind_ != v8engine::ValueStorage::Kind::V8 || !storage_ || storage_->value.IsEmpty()) { + return false; + } + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + return isolate != nullptr && storage_->value.Get(isolate)->IsBoolean(); +} + +bool Value::getBool() const { + if (kind_ == v8engine::ValueStorage::Kind::Bool) { + return boolValue_; + } + if (kind_ == v8engine::ValueStorage::Kind::V8Borrowed) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + return isolate != nullptr && !borrowedValue_.IsEmpty() + ? borrowedValue_->BooleanValue(isolate) + : false; + } + if (kind_ == v8engine::ValueStorage::Kind::V8 && storage_ && !storage_->value.IsEmpty()) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + if (isolate != nullptr) { + return storage_->value.Get(isolate)->BooleanValue(isolate); + } + } + return false; +} + +bool Value::isNumber() const { + if (kind_ == v8engine::ValueStorage::Kind::Number) { + return true; + } + if (kind_ == v8engine::ValueStorage::Kind::V8Borrowed) { + return !borrowedValue_.IsEmpty() && borrowedValue_->IsNumber(); + } + if (kind_ != v8engine::ValueStorage::Kind::V8 || !storage_ || storage_->value.IsEmpty()) { + return false; + } + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + return isolate != nullptr && storage_->value.Get(isolate)->IsNumber(); +} + +double Value::getNumber() const { + if (kind_ == v8engine::ValueStorage::Kind::Number) { + return numberValue_; + } + if (kind_ == v8engine::ValueStorage::Kind::V8Borrowed) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + if (isolate != nullptr && !borrowedValue_.IsEmpty()) { + return borrowedValue_->NumberValue(isolate->GetCurrentContext()).FromMaybe(0); + } + return 0; + } + if (kind_ == v8engine::ValueStorage::Kind::V8 && storage_ && !storage_->value.IsEmpty()) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + if (isolate != nullptr) { + return storage_->value.Get(isolate)->NumberValue(isolate->GetCurrentContext()).FromMaybe(0); + } + } + return 0; +} + +bool Value::isString() const { + if (kind_ == v8engine::ValueStorage::Kind::V8Borrowed) { + return !borrowedValue_.IsEmpty() && borrowedValue_->IsString(); + } + if (kind_ != v8engine::ValueStorage::Kind::V8 || !storage_ || storage_->value.IsEmpty()) { + return false; + } + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + return storage_->value.Get(isolate)->IsString(); +} + +bool Value::isBigInt() const { + if (kind_ == v8engine::ValueStorage::Kind::V8Borrowed) { + return !borrowedValue_.IsEmpty() && borrowedValue_->IsBigInt(); + } + if (kind_ != v8engine::ValueStorage::Kind::V8 || !storage_ || storage_->value.IsEmpty()) { + return false; + } + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + return storage_->value.Get(isolate)->IsBigInt(); +} + +bool Value::isSymbol() const { + if (kind_ == v8engine::ValueStorage::Kind::V8Borrowed) { + return !borrowedValue_.IsEmpty() && borrowedValue_->IsSymbol(); + } + if (kind_ != v8engine::ValueStorage::Kind::V8 || !storage_ || storage_->value.IsEmpty()) { + return false; + } + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + return storage_->value.Get(isolate)->IsSymbol(); +} + +Object Value::asObject(Runtime& runtime) const { + if (storage_) { + return Object::fromValueStorage(storage_); + } + // Need to promote to storage for Object + auto s = std::make_shared(kind_); + if (kind_ == v8engine::ValueStorage::Kind::V8Borrowed) { + s->kind = v8engine::ValueStorage::Kind::V8; + s->value.Reset(runtime.isolate(), borrowedValue_); + } + return Object::fromValueStorage(std::move(s)); +} + +String Value::asString(Runtime& runtime) const { + return String(runtime, local(runtime).As()); +} + +BigInt Value::getBigInt(Runtime& runtime) const { + return BigInt(runtime, local(runtime).As()); +} + +Function Object::getPropertyAsFunction(Runtime& runtime, const char* name) const { + return getProperty(runtime, name).asObject(runtime).asFunction(runtime); +} + +Function Object::asFunction(Runtime& runtime) const { return Function(*this); } + +Array Object::getArray(Runtime& runtime) const { return Array(*this); } + +ArrayBuffer Object::getArrayBuffer(Runtime& runtime) const { return ArrayBuffer(*this); } + +Array Object::getPropertyNames(Runtime& runtime) const { + v8::TryCatch tryCatch(runtime.isolate()); + v8::Local result; + if (!local(runtime)->GetPropertyNames(runtime.context()).ToLocal(&result)) { + throw JSError(runtime, v8engine::currentExceptionMessage(runtime.isolate(), tryCatch)); + } + return Array(Object::fromValueStorage(Value(runtime, result).storage_)); +} + +void Object::setProperty(Runtime& runtime, const char* name, const Function& value) { + setProperty(runtime, name, Value(runtime, value)); +} + +void Object::setProperty(Runtime& runtime, const char* name, const Array& value) { + setProperty(runtime, name, Value(runtime, value)); +} + +void Object::setProperty(Runtime& runtime, const char* name, const ArrayBuffer& value) { + setProperty(runtime, name, Value(runtime, value)); +} + +} // namespace engine +} // namespace nativescript + +#endif // TARGET_ENGINE_V8 diff --git a/NativeScript/ffi/objc/v8/SignatureDispatch.h b/NativeScript/ffi/objc/v8/SignatureDispatch.h new file mode 100644 index 000000000..7fd3b8533 --- /dev/null +++ b/NativeScript/ffi/objc/v8/SignatureDispatch.h @@ -0,0 +1,48 @@ +#ifndef NATIVESCRIPT_FFI_V8_SIGNATURE_DISPATCH_H +#define NATIVESCRIPT_FFI_V8_SIGNATURE_DISPATCH_H + +#include + +#include "ffi/objc/shared/SignatureDispatchCore.h" + +// Engine-neutral GSD (Generated Signature Dispatch). The GsdObjCContext struct, +// the ObjCGsdInvoker/ObjCGsdDispatchEntry types, the generated dispatch table, +// and lookupObjCGsdInvoker are all defined in NativeApiV8SelectorGroups.mm, +// which NativeApiV8.mm includes after the host object helpers the context +// relies on. Nothing GSD-related is declared here to avoid creating an +// ambiguous second GsdObjCContext. + +#ifndef NS_GSD_BACKEND_PREPARED +#define NS_GSD_BACKEND_PREPARED 1 +#endif + +#ifndef NS_GSD_BACKEND_NAPI +#define NS_GSD_BACKEND_NAPI 0 +#endif + +#ifndef NS_GSD_BACKEND_HERMES +#define NS_GSD_BACKEND_HERMES 0 +#endif + +#ifndef NS_HAS_GENERATED_SIGNATURE_DISPATCH +#define NS_HAS_GENERATED_SIGNATURE_DISPATCH 0 +#endif + +#ifndef NS_HAS_GENERATED_SIGNATURE_GSD_DISPATCH +#define NS_HAS_GENERATED_SIGNATURE_GSD_DISPATCH 0 +#endif + +// NOTE: GeneratedGsdSignatureDispatch.inc is included from +// NativeApiV8SelectorGroups.mm after GsdObjCContext is defined (avoids +// namespace ordering issues). + +// The main .inc (prepared invokers + tables) is included here. +#if defined(__has_include) +#if __has_include("GeneratedSignatureDispatch.inc") +#include "GeneratedSignatureDispatch.inc" +#endif +#endif + +#include "ffi/objc/shared/PreparedSignatureDispatch.h" + +#endif // NATIVESCRIPT_FFI_V8_SIGNATURE_DISPATCH_H diff --git a/NativeScript/ffi/quickjs/NativeApiQuickJS.h b/NativeScript/ffi/quickjs/NativeApiQuickJS.h deleted file mode 100644 index 73c31984d..000000000 --- a/NativeScript/ffi/quickjs/NativeApiQuickJS.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_H -#define NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_H - -#include "ffi/shared/direct/NativeApiDirect.h" -#include "quickjs.h" - -namespace nativescript { - -using NativeApiQuickJSConfig = NativeApiDirectConfig; - -void InstallNativeApiQuickJS(JSContext* context, - const NativeApiQuickJSConfig& config = - NativeApiQuickJSConfig{}); - -} // namespace nativescript - -extern "C" void NativeScriptInstallNativeApiQuickJS(JSContext* context, - const char* metadataPath); - -#endif // NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_H diff --git a/NativeScript/ffi/quickjs/NativeApiQuickJS.mm b/NativeScript/ffi/quickjs/NativeApiQuickJS.mm deleted file mode 100644 index 57988a26f..000000000 --- a/NativeScript/ffi/quickjs/NativeApiQuickJS.mm +++ /dev/null @@ -1,159 +0,0 @@ -#include "NativeApiQuickJS.h" - -#ifdef TARGET_ENGINE_QUICKJS - -#include "NativeApiQuickJSRuntime.h" - -namespace nativescript { - -using NativeApiJsiConfig = NativeApiDirectConfig; -using NativeApiJsiScheduler = NativeApiDirectScheduler; - -namespace { - -using facebook::jsi::Array; -using facebook::jsi::ArrayBuffer; -using facebook::jsi::BigInt; -using facebook::jsi::Function; -using facebook::jsi::HostObject; -using facebook::jsi::MutableBuffer; -using facebook::jsi::Object; -using facebook::jsi::PropNameID; -using facebook::jsi::Runtime; -using facebook::jsi::String; -using facebook::jsi::StringBuffer; -using facebook::jsi::Value; -using metagen::MDMemberFlag; -using metagen::MDMetadataReader; -using metagen::MDSectionOffset; -using metagen::MDTypeKind; - -// clang-format off -#include "jsi/NativeApiJsiBridge.h" -// clang-format on - -#define NATIVESCRIPT_NATIVE_API_HAS_ENGINE_LAZY_GLOBALS 1 -#define NATIVESCRIPT_NATIVE_API_RETAIN_RUNTIME 1 - -static JSValue NativeApiQuickJSLazyGlobalGetter(JSContext* context, JSValueConst, int, - JSValueConst*, int, JSValueConst* data) { - JSValue global = JS_GetGlobalObject(context); - JSValue resolver = JS_GetPropertyStr(context, global, "__nativeScriptResolveNativeApiLazyGlobal"); - if (!JS_IsFunction(context, resolver)) { - JS_FreeValue(context, resolver); - JS_FreeValue(context, global); - return JS_UNDEFINED; - } - - JSValueConst args[] = {data[0], data[1]}; - JSValue result = JS_Call(context, resolver, global, 2, args); - JS_FreeValue(context, resolver); - if (JS_IsException(result)) { - JS_FreeValue(context, global); - return result; - } - - JSAtom atom = JS_ValueToAtom(context, data[0]); - if (atom != JS_ATOM_NULL) { - JS_DefinePropertyValue(context, global, atom, JS_DupValue(context, result), - JS_PROP_CONFIGURABLE); - JS_FreeAtom(context, atom); - } - JS_FreeValue(context, global); - return result; -} - -bool InstallNativeApiEngineLazyGlobal(Runtime& runtime, std::shared_ptr, - const std::string& name, const std::string& kind, - bool force) { - if (name.empty() || kind.empty()) { - return false; - } - - JSContext* context = runtime.context(); - JSValue global = JS_GetGlobalObject(context); - JSAtom atom = JS_NewAtomLen(context, name.data(), name.size()); - if (atom == JS_ATOM_NULL) { - JS_FreeValue(context, global); - return false; - } - - int hasProperty = JS_HasProperty(context, global, atom); - if (!force && hasProperty > 0) { - JS_FreeAtom(context, atom); - JS_FreeValue(context, global); - return false; - } - if (hasProperty < 0) { - JS_FreeAtom(context, atom); - JS_FreeValue(context, global); - return false; - } - - JSValue data[] = { - JS_NewStringLen(context, name.data(), name.size()), - JS_NewStringLen(context, kind.data(), kind.size()), - }; - if (JS_IsException(data[0]) || JS_IsException(data[1])) { - JS_FreeValue(context, data[0]); - JS_FreeValue(context, data[1]); - JS_FreeAtom(context, atom); - JS_FreeValue(context, global); - return false; - } - - JSValue getter = JS_NewCFunctionData(context, NativeApiQuickJSLazyGlobalGetter, 0, 0, 2, data); - JS_FreeValue(context, data[0]); - JS_FreeValue(context, data[1]); - if (JS_IsException(getter)) { - JS_FreeAtom(context, atom); - JS_FreeValue(context, global); - return false; - } - - int status = - JS_DefinePropertyGetSet(context, global, atom, getter, JS_UNDEFINED, JS_PROP_CONFIGURABLE); - JS_FreeAtom(context, atom); - JS_FreeValue(context, global); - return status >= 0; -} - -// clang-format off -#include "jsi/NativeApiJsiHostObjects.h" -// clang-format on - -std::shared_ptr retainNativeApiJsiRuntime(Runtime& runtime) { - return std::make_shared(runtime.state()); -} - -// clang-format off -#include "jsi/NativeApiJsiCallbacks.h" -#include "jsi/NativeApiJsiConversion.h" -#include "jsi/NativeApiJsiInvocation.h" -#include "jsi/NativeApiJsiClassBuilder.h" -#include "jsi/NativeApiJsiHostObject.h" -// clang-format on - -} // namespace - -#include "jsi/NativeApiJsiInstall.h" - -void InstallNativeApiQuickJS(JSContext* context, const NativeApiQuickJSConfig& config) { - if (context == nullptr) { - return; - } - auto state = facebook::jsi::quickjsdirect::stateForContext(context); - facebook::jsi::Runtime runtime(state); - facebook::jsi::quickjsdirect::ensureClasses(runtime); - InstallNativeApiJSI(runtime, config); -} - -} // namespace nativescript - -extern "C" void NativeScriptInstallNativeApiQuickJS(JSContext* context, const char* metadataPath) { - nativescript::NativeApiQuickJSConfig config; - config.metadataPath = metadataPath; - nativescript::InstallNativeApiQuickJS(context, config); -} - -#endif // TARGET_ENGINE_QUICKJS diff --git a/NativeScript/ffi/quickjs/NativeApiQuickJSHostObjects.mm b/NativeScript/ffi/quickjs/NativeApiQuickJSHostObjects.mm deleted file mode 100644 index 1cd706a30..000000000 --- a/NativeScript/ffi/quickjs/NativeApiQuickJSHostObjects.mm +++ /dev/null @@ -1,239 +0,0 @@ -#include "NativeApiQuickJSRuntime.h" - -#ifdef TARGET_ENGINE_QUICKJS - -namespace facebook { -namespace jsi { - -namespace quickjsdirect { - -JSClassID gHostClassId = 0; -JSClassID gFunctionClassId = 0; - -namespace { -std::mutex& runtimeStatesMutex() { - static auto* mutex = new std::mutex(); - return *mutex; -} - -std::unordered_map>& runtimeStates() { - static auto* states = new std::unordered_map>(); - return *states; -} -} // namespace - -std::shared_ptr stateForContext(JSContext* context) { - std::lock_guard lock(runtimeStatesMutex()); - auto& states = runtimeStates(); - auto it = states.find(context); - if (it != states.end()) { - return it->second; - } - auto state = std::make_shared(context); - states[context] = state; - return state; -} - -static JSValue nativeHostGet(JSContext* ctx, JSValueConst obj, JSAtom atom, JSValueConst receiver) { - (void)receiver; - Runtime runtime(stateForContext(ctx)); - auto* holder = static_cast(JS_GetOpaque(obj, gHostClassId)); - if (holder == nullptr || holder->hostObject == nullptr) { - return JS_UNDEFINED; - } - try { - Value result = holder->hostObject->get(runtime, PropNameID(atomToUtf8(ctx, atom))); - return result.local(runtime); - } catch (const std::exception& error) { - return throwError(ctx, error); - } -} - -static int nativeHostSet(JSContext* ctx, JSValueConst obj, JSAtom atom, JSValueConst value, - JSValueConst, int) { - Runtime runtime(stateForContext(ctx)); - auto* holder = static_cast(JS_GetOpaque(obj, gHostClassId)); - if (holder == nullptr || holder->hostObject == nullptr) { - return 0; - } - try { - holder->hostObject->set(runtime, PropNameID(atomToUtf8(ctx, atom)), Value(runtime, value)); - return 1; - } catch (const std::exception& error) { - throwError(ctx, error); - return -1; - } -} - -static int nativeHostHas(JSContext* ctx, JSValueConst obj, JSAtom atom) { - Runtime runtime(stateForContext(ctx)); - auto* holder = static_cast(JS_GetOpaque(obj, gHostClassId)); - if (holder == nullptr || holder->hostObject == nullptr) { - return 0; - } - try { - auto names = holder->hostObject->getPropertyNames(runtime); - std::string requested = atomToUtf8(ctx, atom); - for (const auto& name : names) { - if (name.utf8(runtime) == requested) { - return 1; - } - } - } catch (const std::exception&) { - } - return 0; -} - -static int nativeHostOwnNames(JSContext* ctx, JSPropertyEnum** ptab, uint32_t* plen, - JSValueConst obj) { - Runtime runtime(stateForContext(ctx)); - auto* holder = static_cast(JS_GetOpaque(obj, gHostClassId)); - if (holder == nullptr || holder->hostObject == nullptr) { - *ptab = nullptr; - *plen = 0; - return 0; - } - auto names = holder->hostObject->getPropertyNames(runtime); - *plen = static_cast(names.size()); - *ptab = static_cast(js_mallocz(ctx, sizeof(JSPropertyEnum) * names.size())); - for (uint32_t i = 0; i < *plen; i++) { - (*ptab)[i].is_enumerable = true; - (*ptab)[i].atom = JS_NewAtom(ctx, names[i].utf8(runtime).c_str()); - } - return 0; -} - -static void nativeHostFinalize(JSRuntime*, JSValue value) { - auto* holder = static_cast(JS_GetOpaque(value, gHostClassId)); - delete holder; -} - -static JSValue invokeFunctionHolder(JSContext* ctx, FunctionHolder* holder, JSValueConst thisValue, - int argc, JSValueConst* argv) { - Runtime runtime(stateForContext(ctx)); - if (holder == nullptr || !holder->callback) { - return JS_UNDEFINED; - } - std::vector args; - args.reserve(argc); - for (int i = 0; i < argc; i++) { - args.emplace_back(runtime, argv[i]); - } - try { - Value self(runtime, thisValue); - Value result = - holder->callback(runtime, self, args.empty() ? nullptr : args.data(), args.size()); - return result.local(runtime); - } catch (const std::exception& error) { - return throwError(ctx, error); - } -} - -static JSValue nativeFunctionCall(JSContext* ctx, JSValue function, JSValue thisValue, int argc, - JSValue* argv, int) { - auto* holder = static_cast(JS_GetOpaque(function, gFunctionClassId)); - return invokeFunctionHolder(ctx, holder, thisValue, argc, argv); -} - -static JSValue nativeFunctionCallData(JSContext* ctx, JSValue thisValue, int argc, JSValue* argv, - int, JSValue* data) { - auto* holder = static_cast(JS_GetOpaque(data[0], gFunctionClassId)); - return invokeFunctionHolder(ctx, holder, thisValue, argc, argv); -} - -static void nativeFunctionFinalize(JSRuntime*, JSValue value) { - auto* holder = static_cast(JS_GetOpaque(value, gFunctionClassId)); - delete holder; -} - -static JSClassExoticMethods hostExoticMethods = { - .get_own_property = nullptr, - .get_own_property_names = nativeHostOwnNames, - .delete_property = nullptr, - .define_own_property = nullptr, - .has_property = nativeHostHas, - .get_property = nativeHostGet, - .set_property = nativeHostSet, -}; - -void ensureClasses(Runtime& runtime) { - auto state = runtime.state(); - JSRuntime* rt = JS_GetRuntime(runtime.context()); - if (gHostClassId == 0) { - JS_NewClassID(rt, &gHostClassId); - } - if (!state->hostClassRegistered) { - JSClassDef def = {}; - def.class_name = "NativeScriptDirectHostObject"; - def.exotic = &hostExoticMethods; - def.finalizer = nativeHostFinalize; - JS_NewClass(rt, gHostClassId, &def); - JS_SetClassProto(runtime.context(), gHostClassId, JS_NewObject(runtime.context())); - state->hostClassRegistered = true; - } - if (gFunctionClassId == 0) { - JS_NewClassID(rt, &gFunctionClassId); - } - if (!state->functionClassRegistered) { - JSClassDef def = {}; - def.class_name = "NativeScriptDirectFunction"; - def.call = nativeFunctionCall; - def.finalizer = nativeFunctionFinalize; - JS_NewClass(rt, gFunctionClassId, &def); - JS_SetClassProto(runtime.context(), gFunctionClassId, JS_NewObject(runtime.context())); - state->functionClassRegistered = true; - } -} - -} // namespace quickjsdirect - -quickjsdirect::HostObjectHolder* Object::hostObjectHolder(Runtime& runtime) const { - quickjsdirect::ensureClasses(runtime); - JSValue object = local(runtime); - auto* holder = static_cast( - JS_GetOpaque(object, quickjsdirect::gHostClassId)); - JS_FreeValue(runtime.context(), object); - return holder; -} - -Object Object::createFromHostObjectWithToken(Runtime& runtime, std::shared_ptr host, - const void* typeToken) { - quickjsdirect::ensureClasses(runtime); - auto* holder = new quickjsdirect::HostObjectHolder(runtime.state(), std::move(host), typeToken); - JSValue object = JS_NewObjectClass(runtime.context(), quickjsdirect::gHostClassId); - JS_SetOpaque(object, holder); - Object result = Object::fromValueStorage(Value(runtime, object).storage_); - JS_FreeValue(runtime.context(), object); - return result; -} - -Function Function::createFromHostFunction(Runtime& runtime, const PropNameID& name, - unsigned int parameterCount, HostFunctionType callback) { - quickjsdirect::ensureClasses(runtime); - auto* holder = new quickjsdirect::FunctionHolder(runtime.state(), std::move(callback)); - JSValue data = JS_NewObjectClass(runtime.context(), quickjsdirect::gFunctionClassId); - if (JS_IsException(data)) { - delete holder; - throw JSError(runtime, "QuickJS host function data allocation failed."); - } - JS_SetOpaque(data, holder); - - JSValue function = JS_NewCFunctionData(runtime.context(), quickjsdirect::nativeFunctionCallData, - static_cast(parameterCount), 0, 1, &data); - JS_FreeValue(runtime.context(), data); - if (JS_IsException(function)) { - throw JSError(runtime, "QuickJS host function allocation failed."); - } - - std::string functionName = name.utf8(runtime); - JSValue nameValue = JS_NewStringLen(runtime.context(), functionName.data(), functionName.size()); - JS_DefinePropertyValueStr(runtime.context(), function, "name", nameValue, JS_PROP_CONFIGURABLE); - Function result = Function(Object::fromValueStorage(Value(runtime, function).storage_)); - JS_FreeValue(runtime.context(), function); - return result; -} - -} // namespace jsi -} // namespace facebook - -#endif // TARGET_ENGINE_QUICKJS diff --git a/NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.h b/NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.h deleted file mode 100644 index 438a78166..000000000 --- a/NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.h +++ /dev/null @@ -1,745 +0,0 @@ -#ifndef NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_RUNTIME_H -#define NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_RUNTIME_H - -#ifdef TARGET_ENGINE_QUICKJS - -#import -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Metadata.h" -#include "MetadataReader.h" -#include "ffi.h" -#include "quickjs.h" - -@protocol NativeApiJsiClassBuilderProtocol -@end - -#ifdef EMBED_METADATA_SIZE -extern const unsigned char embedded_metadata[EMBED_METADATA_SIZE]; -#endif - -namespace facebook { -namespace jsi { - -class Runtime; -class Value; -class Object; -class Function; -class Array; -class String; -class BigInt; -class ArrayBuffer; - -class JSError : public std::runtime_error { - public: - JSError(Runtime&, const std::string& message) : std::runtime_error(message) {} - explicit JSError(const std::string& message) : std::runtime_error(message) {} -}; - -class StringBuffer { - public: - explicit StringBuffer(std::string value) : value_(std::move(value)) {} - const char* data() const { return value_.data(); } - size_t size() const { return value_.size(); } - - private: - std::string value_; -}; - -class MutableBuffer { - public: - virtual ~MutableBuffer() = default; - virtual size_t size() const = 0; - virtual uint8_t* data() = 0; -}; - -class PropNameID { - public: - PropNameID() = default; - explicit PropNameID(std::string value) : value_(std::move(value)) {} - static PropNameID forAscii(Runtime&, const char* value) { - return PropNameID(value != nullptr ? value : ""); - } - static PropNameID forAscii(Runtime&, const std::string& value) { return PropNameID(value); } - std::string utf8(Runtime&) const { return value_; } - - private: - std::string value_; -}; - -class HostObject { - public: - virtual ~HostObject() = default; - virtual Value get(Runtime& runtime, const PropNameID& name); - virtual void set(Runtime& runtime, const PropNameID& name, const Value& value); - virtual std::vector getPropertyNames(Runtime& runtime); -}; - -using HostFunctionType = std::function; - -namespace quickjsdirect { - -template -const void* hostObjectTypeToken() { - static int token = 0; - return &token; -} - -struct RuntimeState { - explicit RuntimeState(JSContext* context) : context(context) {} - JSContext* context = nullptr; - bool hostClassRegistered = false; - bool functionClassRegistered = false; -}; - -extern JSClassID gHostClassId; -extern JSClassID gFunctionClassId; - -std::shared_ptr stateForContext(JSContext* context); - -struct ValueStorage { - enum class Kind { - Undefined, - Null, - Bool, - Number, - QuickJS, - }; - - explicit ValueStorage(Kind kind) : kind(kind) {} - ~ValueStorage() { - if (context != nullptr && !JS_IsUninitialized(value)) { - JS_FreeValue(context, value); - } - } - - Kind kind = Kind::Undefined; - bool boolValue = false; - double numberValue = 0; - JSContext* context = nullptr; - JSValue value = JS_UNINITIALIZED; -}; - -struct HostObjectHolder { - HostObjectHolder(std::shared_ptr state, std::shared_ptr hostObject, - const void* typeToken) - : state(std::move(state)), hostObject(std::move(hostObject)), typeToken(typeToken) {} - std::shared_ptr state; - std::shared_ptr hostObject; - const void* typeToken = nullptr; -}; - -struct FunctionHolder { - FunctionHolder(std::shared_ptr state, HostFunctionType callback) - : state(std::move(state)), callback(std::move(callback)) {} - std::shared_ptr state; - HostFunctionType callback; -}; - -struct ArrayBufferHolder { - explicit ArrayBufferHolder(std::shared_ptr buffer) : buffer(std::move(buffer)) {} - std::shared_ptr buffer; -}; - -inline std::string valueToUtf8(JSContext* context, JSValueConst value) { - size_t length = 0; - const char* cString = JS_ToCStringLen(context, &length, value); - if (cString == nullptr) { - return {}; - } - std::string result(cString, length); - JS_FreeCString(context, cString); - return result; -} - -inline std::string atomToUtf8(JSContext* context, JSAtom atom) { - const char* cString = JS_AtomToCString(context, atom); - if (cString == nullptr) { - return {}; - } - std::string result(cString); - JS_FreeCString(context, cString); - return result; -} - -inline JSValue throwError(JSContext* context, const std::exception& error) { - return JS_ThrowTypeError(context, "%s", error.what()); -} - -void ensureClasses(Runtime& runtime); - -} // namespace quickjsdirect - -class Runtime { - public: - explicit Runtime(JSContext* context) : state_(quickjsdirect::stateForContext(context)) {} - explicit Runtime(std::shared_ptr state) : state_(std::move(state)) {} - JSContext* context() const { return state_->context; } - std::shared_ptr state() const { return state_; } - Object global(); - Value evaluateJavaScript(std::shared_ptr buffer, const std::string& sourceURL); - void drainMicrotasks() { - JSContext* ctx = context(); - JSRuntime* rt = JS_GetRuntime(ctx); - JSContext* jobCtx = nullptr; - while (JS_ExecutePendingJob(rt, &jobCtx) > 0) { - } - } - - private: - std::shared_ptr state_; -}; - -class String { - public: - String() = default; - String(Runtime& runtime, JSValue value); - static String createFromUtf8(Runtime& runtime, const char* value) { - return String(runtime, JS_NewString(runtime.context(), value != nullptr ? value : "")); - } - static String createFromUtf8(Runtime& runtime, const std::string& value) { - return String(runtime, JS_NewStringLen(runtime.context(), value.data(), value.size())); - } - static String createFromUtf8(Runtime& runtime, const uint8_t* value, size_t length) { - return String(runtime, - JS_NewStringLen(runtime.context(), reinterpret_cast(value), length)); - } - std::string utf8(Runtime& runtime) const; - JSValue local(Runtime& runtime) const; - operator Value() const; - - private: - friend class Value; - std::shared_ptr storage_; -}; - -class Value { - public: - Value() - : storage_(std::make_shared( - quickjsdirect::ValueStorage::Kind::Undefined)) {} - Value(bool value) - : storage_(std::make_shared( - quickjsdirect::ValueStorage::Kind::Bool)) { - storage_->boolValue = value; - } - Value(double value) - : storage_(std::make_shared( - quickjsdirect::ValueStorage::Kind::Number)) { - storage_->numberValue = value; - } - Value(int value) : Value(static_cast(value)) {} - Value(uint32_t value) : Value(static_cast(value)) {} - - Value(Runtime& runtime, const Value& value) : storage_(value.storage_) {} - Value(Runtime& runtime, Value&& value) : storage_(std::move(value.storage_)) {} - Value(Runtime& runtime, const String& value) : storage_(value.storage_) {} - Value(Runtime& runtime, const Object& object); - Value(Runtime& runtime, const Function& function); - Value(Runtime& runtime, const Array& array); - Value(Runtime& runtime, const ArrayBuffer& arrayBuffer); - Value(Runtime& runtime, const BigInt& bigint); - Value(Runtime& runtime, JSValue value) - : storage_(std::make_shared( - quickjsdirect::ValueStorage::Kind::QuickJS)) { - storage_->context = runtime.context(); - storage_->value = JS_DupValue(runtime.context(), value); - } - - static Value undefined() { return Value(); } - static Value null() { - Value value; - value.storage_ = - std::make_shared(quickjsdirect::ValueStorage::Kind::Null); - return value; - } - bool isUndefined() const { - return storage_->kind == quickjsdirect::ValueStorage::Kind::Undefined || - (storage_->kind == quickjsdirect::ValueStorage::Kind::QuickJS && - JS_IsUndefined(storage_->value)); - } - bool isNull() const { - return storage_->kind == quickjsdirect::ValueStorage::Kind::Null || - (storage_->kind == quickjsdirect::ValueStorage::Kind::QuickJS && - JS_IsNull(storage_->value)); - } - bool isBool() const { - return storage_->kind == quickjsdirect::ValueStorage::Kind::Bool || - (storage_->kind == quickjsdirect::ValueStorage::Kind::QuickJS && - JS_IsBool(storage_->value)); - } - bool getBool() const { - if (storage_->kind == quickjsdirect::ValueStorage::Kind::Bool) { - return storage_->boolValue; - } - if (storage_->kind == quickjsdirect::ValueStorage::Kind::QuickJS) { - return JS_ToBool(storage_->context, storage_->value) != 0; - } - return false; - } - bool isNumber() const { - return storage_->kind == quickjsdirect::ValueStorage::Kind::Number || - (storage_->kind == quickjsdirect::ValueStorage::Kind::QuickJS && - JS_IsNumber(storage_->value)); - } - double getNumber() const { - if (storage_->kind == quickjsdirect::ValueStorage::Kind::Number) { - return storage_->numberValue; - } - if (storage_->kind == quickjsdirect::ValueStorage::Kind::QuickJS) { - double value = 0; - JS_ToFloat64(storage_->context, &value, storage_->value); - return value; - } - return 0; - } - bool isObject() const { - return storage_->kind == quickjsdirect::ValueStorage::Kind::QuickJS && - JS_IsObject(storage_->value); - } - bool isString() const { - return storage_->kind == quickjsdirect::ValueStorage::Kind::QuickJS && - JS_IsString(storage_->value); - } - bool isBigInt() const { - return storage_->kind == quickjsdirect::ValueStorage::Kind::QuickJS && - JS_IsBigInt(storage_->context, storage_->value); - } - bool isSymbol() const { - return storage_->kind == quickjsdirect::ValueStorage::Kind::QuickJS && - JS_IsSymbol(storage_->value); - } - - Object asObject(Runtime& runtime) const; - String asString(Runtime& runtime) const; - BigInt getBigInt(Runtime& runtime) const; - - JSValue local(Runtime& runtime) const { - switch (storage_->kind) { - case quickjsdirect::ValueStorage::Kind::Undefined: - return JS_UNDEFINED; - case quickjsdirect::ValueStorage::Kind::Null: - return JS_NULL; - case quickjsdirect::ValueStorage::Kind::Bool: - return JS_NewBool(runtime.context(), storage_->boolValue); - case quickjsdirect::ValueStorage::Kind::Number: - return JS_NewFloat64(runtime.context(), storage_->numberValue); - case quickjsdirect::ValueStorage::Kind::QuickJS: - return JS_DupValue(runtime.context(), storage_->value); - } - } - - private: - friend class Runtime; - friend class Object; - friend class String; - friend class BigInt; - friend class ArrayBuffer; - friend class Function; - friend class Array; - std::shared_ptr storage_; -}; - -class Object { - public: - Object() = default; - explicit Object(Runtime& runtime) - : storage_(std::make_shared( - quickjsdirect::ValueStorage::Kind::QuickJS)) { - storage_->context = runtime.context(); - storage_->value = JS_NewObject(runtime.context()); - } - static Object fromValueStorage(std::shared_ptr storage) { - Object object; - object.storage_ = std::move(storage); - return object; - } - template - static Object createFromHostObject(Runtime& runtime, std::shared_ptr host) { - auto baseHost = std::static_pointer_cast(std::move(host)); - return createFromHostObjectWithToken(runtime, std::move(baseHost), - quickjsdirect::hostObjectTypeToken()); - } - - Value getProperty(Runtime& runtime, const char* name) const { - JSValue object = local(runtime); - JSValue result = JS_GetPropertyStr(runtime.context(), object, name != nullptr ? name : ""); - JS_FreeValue(runtime.context(), object); - if (JS_IsException(result)) { - throw JSError(runtime, "QuickJS property get failed."); - } - Value value(runtime, result); - JS_FreeValue(runtime.context(), result); - return value; - } - Value getProperty(Runtime& runtime, const std::string& name) const { - return getProperty(runtime, name.c_str()); - } - Value getProperty(Runtime& runtime, const Value& key) const { - JSValue object = local(runtime); - JSValue keyValue = key.local(runtime); - JSAtom atom = JS_ValueToAtom(runtime.context(), keyValue); - JS_FreeValue(runtime.context(), keyValue); - JSValue result = - atom == JS_ATOM_NULL ? JS_UNDEFINED : JS_GetProperty(runtime.context(), object, atom); - if (atom != JS_ATOM_NULL) { - JS_FreeAtom(runtime.context(), atom); - } - JS_FreeValue(runtime.context(), object); - if (JS_IsException(result)) { - throw JSError(runtime, "QuickJS property get failed."); - } - Value value(runtime, result); - JS_FreeValue(runtime.context(), result); - return value; - } - Object getPropertyAsObject(Runtime& runtime, const char* name) const { - return getProperty(runtime, name).asObject(runtime); - } - Function getPropertyAsFunction(Runtime& runtime, const char* name) const; - - void setProperty(Runtime& runtime, const char* name, const Value& value) { - JSValue object = local(runtime); - JSValue localValue = value.local(runtime); - int status = - JS_SetPropertyStr(runtime.context(), object, name != nullptr ? name : "", localValue); - JS_FreeValue(runtime.context(), object); - if (status < 0) { - throw JSError(runtime, "QuickJS property set failed."); - } - } - void setProperty(Runtime& runtime, const char* name, const String& value) { - setProperty(runtime, name, Value(runtime, value)); - } - void setProperty(Runtime& runtime, const char* name, const Object& value) { - setProperty(runtime, name, Value(runtime, value)); - } - void setProperty(Runtime& runtime, const char* name, const Function& value); - void setProperty(Runtime& runtime, const char* name, const Array& value); - void setProperty(Runtime& runtime, const char* name, const ArrayBuffer& value); - void setProperty(Runtime& runtime, const char* name, bool value) { - setProperty(runtime, name, Value(value)); - } - void setProperty(Runtime& runtime, const char* name, double value) { - setProperty(runtime, name, Value(value)); - } - void setProperty(Runtime& runtime, const std::string& name, const Value& value) { - setProperty(runtime, name.c_str(), value); - } - void setProperty(Runtime& runtime, const Value& key, const Value& value) { - JSValue object = local(runtime); - JSValue keyValue = key.local(runtime); - JSAtom atom = JS_ValueToAtom(runtime.context(), keyValue); - JS_FreeValue(runtime.context(), keyValue); - JSValue localValue = value.local(runtime); - int status = - atom == JS_ATOM_NULL ? -1 : JS_SetProperty(runtime.context(), object, atom, localValue); - if (atom != JS_ATOM_NULL) { - JS_FreeAtom(runtime.context(), atom); - } - JS_FreeValue(runtime.context(), object); - if (status < 0) { - throw JSError(runtime, "QuickJS property set failed."); - } - } - bool hasProperty(Runtime& runtime, const char* name) const { - JSValue object = local(runtime); - JSAtom atom = JS_NewAtom(runtime.context(), name != nullptr ? name : ""); - int result = JS_HasProperty(runtime.context(), object, atom); - JS_FreeAtom(runtime.context(), atom); - JS_FreeValue(runtime.context(), object); - return result > 0; - } - bool isFunction(Runtime& runtime) const { - JSValue object = local(runtime); - bool result = JS_IsFunction(runtime.context(), object); - JS_FreeValue(runtime.context(), object); - return result; - } - bool isArray(Runtime& runtime) const { - JSValue object = local(runtime); - int result = JS_IsArray(runtime.context(), object); - JS_FreeValue(runtime.context(), object); - return result > 0; - } - bool isArrayBuffer(Runtime& runtime) const { - JSValue object = local(runtime); - bool result = JS_IsArrayBuffer(object); - JS_FreeValue(runtime.context(), object); - return result; - } - Function asFunction(Runtime& runtime) const; - Array getArray(Runtime& runtime) const; - ArrayBuffer getArrayBuffer(Runtime& runtime) const; - Array getPropertyNames(Runtime& runtime) const; - - template - bool isHostObject(Runtime& runtime) const { - auto holder = hostObjectHolder(runtime); - return holder != nullptr && holder->typeToken == quickjsdirect::hostObjectTypeToken(); - } - template - std::shared_ptr getHostObject(Runtime& runtime) const { - auto holder = hostObjectHolder(runtime); - if (holder == nullptr || holder->typeToken != quickjsdirect::hostObjectTypeToken()) { - return nullptr; - } - return std::static_pointer_cast(holder->hostObject); - } - JSValue local(Runtime& runtime) const { return JS_DupValue(runtime.context(), storage_->value); } - operator Value() const { - Value value; - value.storage_ = storage_; - return value; - } - - protected: - friend class Value; - friend class Runtime; - friend class Function; - friend class Array; - friend class ArrayBuffer; - explicit Object(std::shared_ptr storage) - : storage_(std::move(storage)) {} - static Object createFromHostObjectWithToken(Runtime& runtime, std::shared_ptr host, - const void* typeToken); - quickjsdirect::HostObjectHolder* hostObjectHolder(Runtime& runtime) const; - std::shared_ptr storage_; -}; - -class Function : public Object { - public: - Function() = default; - explicit Function(Object object) : Object(std::move(object.storage_)) {} - static Function createFromHostFunction(Runtime& runtime, const PropNameID& name, unsigned int, - HostFunctionType callback); - Value call(Runtime& runtime, const Value* args, size_t count) const { - JSValue function = local(runtime); - JSValue global = JS_GetGlobalObject(runtime.context()); - std::vector argv; - argv.reserve(count); - for (size_t i = 0; i < count; i++) { - argv.push_back(args[i].local(runtime)); - } - JSValue result = JS_Call(runtime.context(), function, global, static_cast(argv.size()), - argv.empty() ? nullptr : argv.data()); - for (auto& arg : argv) { - JS_FreeValue(runtime.context(), arg); - } - JS_FreeValue(runtime.context(), global); - JS_FreeValue(runtime.context(), function); - if (JS_IsException(result)) { - throw JSError(runtime, "QuickJS function call failed."); - } - Value value(runtime, result); - JS_FreeValue(runtime.context(), result); - return value; - } - Value call(Runtime& runtime) const { - return call(runtime, static_cast(nullptr), 0); - } - Value call(Runtime& runtime, std::nullptr_t, size_t) const { - return call(runtime, static_cast(nullptr), 0); - } - template - Value call(Runtime& runtime, const Value (&args)[N], size_t count) const { - return call(runtime, static_cast(args), count); - } - template - Value call(Runtime& runtime, Args&&... args) const { - Value argv[] = {Value(runtime, std::forward(args))...}; - return call(runtime, static_cast(argv), sizeof...(Args)); - } - Value callWithThis(Runtime& runtime, const Object& thisObject, const Value* args = nullptr, - size_t count = 0) const { - JSValue function = local(runtime); - JSValue thisValue = thisObject.local(runtime); - std::vector argv; - argv.reserve(count); - for (size_t i = 0; i < count; i++) { - argv.push_back(args[i].local(runtime)); - } - JSValue result = JS_Call(runtime.context(), function, thisValue, static_cast(argv.size()), - argv.empty() ? nullptr : argv.data()); - for (auto& arg : argv) { - JS_FreeValue(runtime.context(), arg); - } - JS_FreeValue(runtime.context(), thisValue); - JS_FreeValue(runtime.context(), function); - if (JS_IsException(result)) { - throw JSError(runtime, "QuickJS function call failed."); - } - Value value(runtime, result); - JS_FreeValue(runtime.context(), result); - return value; - } - Value callAsConstructor(Runtime& runtime, const Value* args, size_t count) const { - JSValue function = local(runtime); - std::vector argv; - argv.reserve(count); - for (size_t i = 0; i < count; i++) { - argv.push_back(args[i].local(runtime)); - } - JSValue result = JS_CallConstructor(runtime.context(), function, static_cast(argv.size()), - argv.empty() ? nullptr : argv.data()); - for (auto& arg : argv) { - JS_FreeValue(runtime.context(), arg); - } - JS_FreeValue(runtime.context(), function); - if (JS_IsException(result)) { - throw JSError(runtime, "QuickJS constructor call failed."); - } - Value value(runtime, result); - JS_FreeValue(runtime.context(), result); - return value; - } - Value callAsConstructor(Runtime& runtime, std::nullptr_t, size_t) const { - return callAsConstructor(runtime, static_cast(nullptr), 0); - } - template - Value callAsConstructor(Runtime& runtime, const Value (&args)[N], size_t count) const { - return callAsConstructor(runtime, static_cast(args), count); - } - template - Value callAsConstructor(Runtime& runtime, Args&&... args) const { - Value argv[] = {Value(runtime, std::forward(args))...}; - return callAsConstructor(runtime, static_cast(argv), sizeof...(Args)); - } -}; - -class Array : public Object { - public: - explicit Array(Runtime& runtime, size_t size) - : Object(std::make_shared( - quickjsdirect::ValueStorage::Kind::QuickJS)) { - storage_->context = runtime.context(); - storage_->value = JS_NewArray(runtime.context()); - JS_SetPropertyStr(runtime.context(), storage_->value, "length", - JS_NewUint32(runtime.context(), static_cast(size))); - } - explicit Array(Object object) : Object(std::move(object.storage_)) {} - size_t size(Runtime& runtime) const { - Value length = getProperty(runtime, "length"); - return length.isNumber() ? static_cast(std::max(0, length.getNumber())) : 0; - } - Value getValueAtIndex(Runtime& runtime, size_t index) const { - JSValue object = local(runtime); - JSValue result = JS_GetPropertyUint32(runtime.context(), object, static_cast(index)); - JS_FreeValue(runtime.context(), object); - if (JS_IsException(result)) { - throw JSError(runtime, "QuickJS array get failed."); - } - Value value(runtime, result); - JS_FreeValue(runtime.context(), result); - return value; - } - void setValueAtIndex(Runtime& runtime, size_t index, const Value& value) { - JSValue object = local(runtime); - JSValue localValue = value.local(runtime); - int status = - JS_SetPropertyUint32(runtime.context(), object, static_cast(index), localValue); - JS_FreeValue(runtime.context(), object); - if (status < 0) { - throw JSError(runtime, "QuickJS array set failed."); - } - } - void setValueAtIndex(Runtime& runtime, size_t index, const String& value) { - setValueAtIndex(runtime, index, Value(runtime, value)); - } -}; - -class BigInt { - public: - BigInt() = default; - BigInt(Runtime& runtime, JSValue value) - : storage_(std::make_shared( - quickjsdirect::ValueStorage::Kind::QuickJS)) { - storage_->context = runtime.context(); - storage_->value = JS_DupValue(runtime.context(), value); - } - static BigInt fromInt64(Runtime& runtime, int64_t value) { - JSValue result = JS_NewBigInt64(runtime.context(), value); - BigInt bigint(runtime, result); - JS_FreeValue(runtime.context(), result); - return bigint; - } - static BigInt fromUint64(Runtime& runtime, uint64_t value) { - JSValue result = JS_NewBigUint64(runtime.context(), value); - BigInt bigint(runtime, result); - JS_FreeValue(runtime.context(), result); - return bigint; - } - String toString(Runtime& runtime, int) const; - JSValue local(Runtime& runtime) const { return JS_DupValue(runtime.context(), storage_->value); } - operator Value() const { - Value value; - value.storage_ = storage_; - return value; - } - - private: - friend class Value; - std::shared_ptr storage_; -}; - -class ArrayBuffer : public Object { - public: - ArrayBuffer(Runtime& runtime, std::shared_ptr buffer) - : Object(std::make_shared( - quickjsdirect::ValueStorage::Kind::QuickJS)) { - auto* holder = new quickjsdirect::ArrayBufferHolder(std::move(buffer)); - storage_->context = runtime.context(); - storage_->value = JS_NewArrayBuffer( - runtime.context(), holder->buffer->data(), holder->buffer->size(), - [](JSRuntime*, void* opaque, void*) { - delete static_cast(opaque); - }, - holder, false); - } - explicit ArrayBuffer(Object object) : Object(std::move(object.storage_)) {} - size_t size(Runtime& runtime) const { - JSValue object = local(runtime); - size_t size = 0; - JS_GetArrayBuffer(runtime.context(), &size, object); - JS_FreeValue(runtime.context(), object); - return size; - } - uint8_t* data(Runtime& runtime) const { - JSValue object = local(runtime); - size_t size = 0; - uint8_t* data = JS_GetArrayBuffer(runtime.context(), &size, object); - JS_FreeValue(runtime.context(), object); - return data; - } -}; -} // namespace jsi -} // namespace facebook - -#endif // TARGET_ENGINE_QUICKJS - -#endif // NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_RUNTIME_H diff --git a/NativeScript/ffi/quickjs/NativeApiQuickJSValue.mm b/NativeScript/ffi/quickjs/NativeApiQuickJSValue.mm deleted file mode 100644 index 94ae05e0e..000000000 --- a/NativeScript/ffi/quickjs/NativeApiQuickJSValue.mm +++ /dev/null @@ -1,88 +0,0 @@ -#include "NativeApiQuickJSRuntime.h" - -#ifdef TARGET_ENGINE_QUICKJS - -namespace facebook { -namespace jsi { - -Value HostObject::get(Runtime&, const PropNameID&) { return Value::undefined(); } -void HostObject::set(Runtime&, const PropNameID&, const Value&) {} -std::vector HostObject::getPropertyNames(Runtime&) { return {}; } -String::String(Runtime& runtime, JSValue value) - : storage_(std::make_shared( - quickjsdirect::ValueStorage::Kind::QuickJS)) { - storage_->context = runtime.context(); - storage_->value = JS_DupValue(runtime.context(), value); -} -std::string String::utf8(Runtime& runtime) const { - JSValue value = local(runtime); - std::string result = quickjsdirect::valueToUtf8(runtime.context(), value); - JS_FreeValue(runtime.context(), value); - return result; -} -JSValue String::local(Runtime& runtime) const { - return JS_DupValue(runtime.context(), storage_->value); -} -String::operator Value() const { - Value value; - value.storage_ = storage_; - return value; -} -Value::Value(Runtime&, const Object& object) : storage_(object.storage_) {} -Value::Value(Runtime&, const Function& function) : storage_(function.storage_) {} -Value::Value(Runtime&, const Array& array) : storage_(array.storage_) {} -Value::Value(Runtime&, const ArrayBuffer& arrayBuffer) : storage_(arrayBuffer.storage_) {} -Value::Value(Runtime&, const BigInt& bigint) : storage_(bigint.storage_) {} -Object Value::asObject(Runtime&) const { return Object::fromValueStorage(storage_); } -String Value::asString(Runtime& runtime) const { - JSValue value = local(runtime); - String result(runtime, value); - JS_FreeValue(runtime.context(), value); - return result; -} -BigInt Value::getBigInt(Runtime& runtime) const { - JSValue value = local(runtime); - BigInt result(runtime, value); - JS_FreeValue(runtime.context(), value); - return result; -} -Function Object::getPropertyAsFunction(Runtime& runtime, const char* name) const { - return getProperty(runtime, name).asObject(runtime).asFunction(runtime); -} -Function Object::asFunction(Runtime&) const { return Function(*this); } -Array Object::getArray(Runtime&) const { return Array(*this); } -ArrayBuffer Object::getArrayBuffer(Runtime&) const { return ArrayBuffer(*this); } -Array Object::getPropertyNames(Runtime& runtime) const { - JSValue object = local(runtime); - JSPropertyEnum* properties = nullptr; - uint32_t count = 0; - int status = JS_GetOwnPropertyNames(runtime.context(), &properties, &count, object, - JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY); - JS_FreeValue(runtime.context(), object); - if (status < 0) { - throw JSError(runtime, "QuickJS property names failed."); - } - Array result(runtime, count); - for (uint32_t i = 0; i < count; i++) { - JSValue nameValue = JS_AtomToValue(runtime.context(), properties[i].atom); - result.setValueAtIndex(runtime, i, Value(runtime, nameValue)); - JS_FreeValue(runtime.context(), nameValue); - JS_FreeAtom(runtime.context(), properties[i].atom); - } - js_free(runtime.context(), properties); - return result; -} -void Object::setProperty(Runtime& runtime, const char* name, const Function& value) { - setProperty(runtime, name, Value(runtime, value)); -} -void Object::setProperty(Runtime& runtime, const char* name, const Array& value) { - setProperty(runtime, name, Value(runtime, value)); -} -void Object::setProperty(Runtime& runtime, const char* name, const ArrayBuffer& value) { - setProperty(runtime, name, Value(runtime, value)); -} - -} // namespace jsi -} // namespace facebook - -#endif // TARGET_ENGINE_QUICKJS diff --git a/NativeScript/ffi/shared/direct/NativeApiDirect.h b/NativeScript/ffi/shared/direct/NativeApiDirect.h deleted file mode 100644 index ef55cda4f..000000000 --- a/NativeScript/ffi/shared/direct/NativeApiDirect.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef NATIVESCRIPT_FFI_SHARED_DIRECT_NATIVE_API_DIRECT_H -#define NATIVESCRIPT_FFI_SHARED_DIRECT_NATIVE_API_DIRECT_H - -#include -#include - -namespace nativescript { - -class NativeApiDirectScheduler { - public: - virtual ~NativeApiDirectScheduler() = default; - virtual void invokeOnJS(std::function task) = 0; - virtual void invokeOnUI(std::function task) = 0; -}; - -struct NativeApiDirectConfig { - const char* metadataPath = nullptr; - const void* metadataPtr = nullptr; - const char* globalName = "__nativeScriptNativeApi"; - std::shared_ptr scheduler = nullptr; - std::function)> nativeInvocationInvoker = nullptr; - std::function)> nativeCallbackInvoker = nullptr; - std::function)> jsThreadCallbackInvoker = nullptr; - bool invokeCallbacksOnNativeCallerThread = false; - bool installGlobalSymbols = false; -}; - -} // namespace nativescript - -#endif // NATIVESCRIPT_FFI_SHARED_DIRECT_NATIVE_API_DIRECT_H diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiBridge.h b/NativeScript/ffi/shared/jsi/NativeApiJsiBridge.h deleted file mode 100644 index d543b1ec3..000000000 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiBridge.h +++ /dev/null @@ -1,1705 +0,0 @@ -thread_local bool gDispatchNativeCallsToUI = false; -thread_local bool gExecutingDispatchedUINativeCall = false; -thread_local int gSynchronousNativeInvocationDepth = 0; -thread_local int gNativeCallerThreadJsiCallbackDepth = 0; -thread_local std::vector gNativeCallbackExceptionCaptureStack; -std::atomic gActiveSynchronousNativeInvocationDepth{0}; -static char gNativeApiJsiExtendedClassKey; - -void markNativeApiJsiExtendedClass(Class cls) { - if (cls == Nil) { - return; - } - objc_setAssociatedObject(cls, &gNativeApiJsiExtendedClassKey, @YES, - OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -bool isNativeApiJsiExtendedClass(Class cls) { - Class current = cls; - while (current != Nil) { - if (objc_getAssociatedObject(current, &gNativeApiJsiExtendedClassKey) != nil) { - return true; - } - current = class_getSuperclass(current); - } - return false; -} - -class ScopedNativeApiUINativeCallDispatch final { - public: - ScopedNativeApiUINativeCallDispatch() - : previous_(gDispatchNativeCallsToUI) { - gDispatchNativeCallsToUI = true; - } - - ~ScopedNativeApiUINativeCallDispatch() { - gDispatchNativeCallsToUI = previous_; - } - - private: - bool previous_ = false; -}; - -bool shouldDispatchNativeCallToUI() { - return gDispatchNativeCallsToUI && ![NSThread isMainThread]; -} - -class ScopedNativeApiSynchronousInvocation final { - public: - ScopedNativeApiSynchronousInvocation() { - gSynchronousNativeInvocationDepth += 1; - gActiveSynchronousNativeInvocationDepth.fetch_add(1, - std::memory_order_acq_rel); - } - - ~ScopedNativeApiSynchronousInvocation() { - gSynchronousNativeInvocationDepth -= 1; - gActiveSynchronousNativeInvocationDepth.fetch_sub(1, - std::memory_order_acq_rel); - } -}; - -class ScopedNativeCallerThreadJsiCallback final { - public: - ScopedNativeCallerThreadJsiCallback() { - gNativeCallerThreadJsiCallbackDepth += 1; - } - - ~ScopedNativeCallerThreadJsiCallback() { - gNativeCallerThreadJsiCallbackDepth -= 1; - } - - ScopedNativeCallerThreadJsiCallback( - const ScopedNativeCallerThreadJsiCallback&) = delete; - ScopedNativeCallerThreadJsiCallback& operator=( - const ScopedNativeCallerThreadJsiCallback&) = delete; -}; - -class ScopedNativeCallbackExceptionCapture final { - public: - explicit ScopedNativeCallbackExceptionCapture(std::string* message) - : message_(message) { - gNativeCallbackExceptionCaptureStack.push_back(message_); - } - - ~ScopedNativeCallbackExceptionCapture() { - if (!gNativeCallbackExceptionCaptureStack.empty() && - gNativeCallbackExceptionCaptureStack.back() == message_) { - gNativeCallbackExceptionCaptureStack.pop_back(); - } - } - - ScopedNativeCallbackExceptionCapture( - const ScopedNativeCallbackExceptionCapture&) = delete; - ScopedNativeCallbackExceptionCapture& operator=( - const ScopedNativeCallbackExceptionCapture&) = delete; - - private: - std::string* message_ = nullptr; -}; - -bool recordNativeCallbackException(const std::string& message) { - if (gNativeCallbackExceptionCaptureStack.empty()) { - return false; - } - - std::string* captured = gNativeCallbackExceptionCaptureStack.back(); - if (captured == nullptr) { - return false; - } - - if (captured->empty()) { - *captured = message; - } - return true; -} - -template -void performNativeInvocation(Runtime& runtime, - const std::function)>& - invoker, - Invocation&& invocation) { - NSString* exceptionDescription = nil; - std::string callbackException; - auto run = [&]() { - ScopedNativeApiSynchronousInvocation synchronousInvocation; - ScopedNativeCallbackExceptionCapture callbackExceptionCapture( - &callbackException); - @try { - invocation(); - } @catch (NSException* exception) { - exceptionDescription = [exception.description copy]; - } - }; - - bool skipInvoker = gNativeCallerThreadJsiCallbackDepth > 0; - if (shouldDispatchNativeCallToUI()) { - dispatch_sync(dispatch_get_main_queue(), ^{ - bool previous = gExecutingDispatchedUINativeCall; - gExecutingDispatchedUINativeCall = true; - if (invoker && !skipInvoker) { - invoker(run); - } else { - run(); - } - gExecutingDispatchedUINativeCall = previous; - }); - } else if (invoker && !skipInvoker) { - invoker(run); - } else { - run(); - } - - if (exceptionDescription != nil) { - std::string message = exceptionDescription.UTF8String ?: ""; - [exceptionDescription release]; - throw facebook::jsi::JSError(runtime, message); - } - if (!callbackException.empty()) { - throw facebook::jsi::JSError(runtime, callbackException); - } -} - -enum class NativeApiSymbolKind { - Class, - Function, - Constant, - Protocol, - Enum, - Struct, - Union, -}; - -struct NativeApiSymbol { - NativeApiSymbolKind kind; - MDSectionOffset offset = 0; - MDSectionOffset superclassOffset = MD_SECTION_OFFSET_NULL; - std::string name; - std::string runtimeName; -}; - -struct NativeApiMember { - std::string name; - std::string selectorName; - std::string setterSelectorName; - MDSectionOffset signatureOffset = MD_SECTION_OFFSET_NULL; - MDSectionOffset setterSignatureOffset = MD_SECTION_OFFSET_NULL; - MDMemberFlag flags = metagen::mdMemberFlagNull; - bool property = false; - bool readonly = false; -}; - -struct NativeApiJsiAggregateInfo; - -struct NativeApiJsiFfiType { - ffi_type type = {}; - std::vector elements; - - NativeApiJsiFfiType() { - type.type = FFI_TYPE_STRUCT; - type.size = 0; - type.alignment = 0; - type.elements = nullptr; - } - - void finalize() { - elements.push_back(nullptr); - type.elements = elements.data(); - } -}; - -struct NativeApiJsiType { - MDTypeKind kind = metagen::mdTypeVoid; - ffi_type* ffiType = &ffi_type_void; - bool supported = true; - bool returnOwned = false; - MDSectionOffset signatureOffset = MD_SECTION_OFFSET_NULL; - MDSectionOffset aggregateOffset = MD_SECTION_OFFSET_NULL; - bool aggregateIsUnion = false; - uint16_t arraySize = 0; - std::shared_ptr elementType; - std::shared_ptr aggregateInfo; - std::shared_ptr ownedFfiType; -}; - -struct NativeApiJsiAggregateField { - std::string name; - uint16_t offset = 0; - NativeApiJsiType type; -}; - -struct NativeApiJsiAggregateInfo { - std::string name; - uint16_t size = 0; - bool isUnion = false; - MDSectionOffset offset = MD_SECTION_OFFSET_NULL; - std::vector fields; - std::shared_ptr ffi; -}; - -std::string jsifySelector(const char* selector) { - std::string jsifiedSelector; - bool nextUpper = false; - for (const char* c = selector; c != nullptr && *c != '\0'; c++) { - if (*c == ':') { - nextUpper = true; - } else if (nextUpper) { - jsifiedSelector += static_cast(toupper(*c)); - nextUpper = false; - } else { - jsifiedSelector += *c; - } - } - return jsifiedSelector; -} - -std::string booleanGetterSelectorForProperty(const std::string& property) { - if (property.empty()) { - return property; - } - - std::string selector = "is"; - selector += static_cast(toupper(property[0])); - selector += property.substr(1); - return selector; -} - -std::optional runtimeBooleanGetterSelectorForProperty( - Class cls, bool staticMethod, const std::string& property) { - if (cls == nil || property.empty()) { - return std::nullopt; - } - - std::string selectorName = booleanGetterSelectorForProperty(property); - SEL selector = sel_getUid(selectorName.c_str()); - if ((!staticMethod && class_getInstanceMethod(cls, selector) != nullptr) || - (staticMethod && class_getClassMethod(cls, selector) != nullptr)) { - return selectorName; - } - return std::nullopt; -} - -std::optional runtimeSelectorNameForProperty( - Class cls, bool staticMethod, const std::string& property) { - if (cls == nil || property.empty()) { - return std::nullopt; - } - -#if TARGET_OS_OSX - if (property == "initWithRedGreenBlueAlpha") { - const char* candidates[] = { - "initWithSRGBRed:green:blue:alpha:", - "initWithCalibratedRed:green:blue:alpha:", - }; - for (const char* candidate : candidates) { - SEL selector = sel_getUid(candidate); - if ((!staticMethod && class_getInstanceMethod(cls, selector) != nullptr) || - (staticMethod && class_getClassMethod(cls, selector) != nullptr)) { - return std::string(candidate); - } - } - } else if (property == "colorWithRedGreenBlueAlpha") { - const char* candidates[] = { - "colorWithSRGBRed:green:blue:alpha:", - "colorWithCalibratedRed:green:blue:alpha:", - }; - for (const char* candidate : candidates) { - SEL selector = sel_getUid(candidate); - if ((!staticMethod && class_getInstanceMethod(cls, selector) != nullptr) || - (staticMethod && class_getClassMethod(cls, selector) != nullptr)) { - return std::string(candidate); - } - } - } -#endif - - if (auto selectorName = - runtimeBooleanGetterSelectorForProperty(cls, staticMethod, property)) { - return selectorName; - } - - Class scan = staticMethod ? object_getClass(cls) : cls; - while (scan != Nil) { - unsigned int methodCount = 0; - Method* methods = class_copyMethodList(scan, &methodCount); - for (unsigned int i = 0; i < methodCount; i++) { - SEL selector = method_getName(methods[i]); - const char* selectorName = selector != nullptr ? sel_getName(selector) : nullptr; - if (selectorName != nullptr && - (property == selectorName || jsifySelector(selectorName) == property)) { - std::string result(selectorName); - free(methods); - return result; - } - } - free(methods); - scan = class_getSuperclass(scan); - } - - return std::nullopt; -} - -std::string setterSelectorForProperty(const std::string& property) { - if (property.empty()) { - return property; - } - - std::string selector = "set"; - selector += static_cast(toupper(property[0])); - selector += property.substr(1); - selector += ":"; - return selector; -} - -bool hasRuntimeSetterForProperty(Class cls, bool staticMethod, - const std::string& property) { - if (cls == nil || property.empty()) { - return false; - } - - std::string setterSelectorName = setterSelectorForProperty(property); - SEL selector = sel_getUid(setterSelectorName.c_str()); - return staticMethod ? class_getClassMethod(cls, selector) != nullptr - : class_getInstanceMethod(cls, selector) != nullptr; -} - -size_t selectorArgumentCount(const std::string& selector) { - return static_cast( - std::count(selector.begin(), selector.end(), ':')); -} - -const NativeApiMember* selectMethodMember( - const std::vector& members, const std::string& property, - bool staticMethod, size_t argumentCount) { - const NativeApiMember* fallback = nullptr; - for (const auto& member : members) { - if (member.property || member.name != property) { - continue; - } - - bool memberIsStatic = (member.flags & metagen::mdMemberStatic) != 0; - if (memberIsStatic != staticMethod) { - continue; - } - - if (fallback == nullptr) { - fallback = &member; - } - if (selectorArgumentCount(member.selectorName) == argumentCount) { - return &member; - } - } - return fallback; -} - -const NativeApiMember* selectPropertyMember( - const std::vector& members, const std::string& property, - bool staticMethod) { - for (const auto& member : members) { - if (!member.property || member.name != property) { - continue; - } - - bool memberIsStatic = (member.flags & metagen::mdMemberStatic) != 0; - if (memberIsStatic == staticMethod) { - return &member; - } - } - return nullptr; -} - -const NativeApiMember* selectWritablePropertyMember( - const std::vector& members, const std::string& property, - bool staticMethod) { - const NativeApiMember* fallback = nullptr; - for (const auto& member : members) { - if (!member.property || member.name != property) { - continue; - } - - bool memberIsStatic = (member.flags & metagen::mdMemberStatic) != 0; - if (memberIsStatic != staticMethod) { - continue; - } - - if (fallback == nullptr) { - fallback = &member; - } - if (!member.readonly && !member.setterSelectorName.empty()) { - return &member; - } - } - return fallback; -} - -void skipMetadataJsiType(MDMetadataReader* metadata, MDSectionOffset* offset); -Protocol* lookupProtocolByNativeName(const std::string& name); - -inline uintptr_t normalizeRuntimePointer(uintptr_t pointer) { -#if INTPTR_MAX == INT64_MAX - return pointer & 0x0000FFFFFFFFFFFFULL; -#else - return pointer; -#endif -} - -class NativeApiJsiBridge { - public: - explicit NativeApiJsiBridge(const NativeApiJsiConfig& config) - : metadata_(loadMetadata(config)), - scheduler_(config.scheduler), - nativeInvocationInvoker_(config.nativeInvocationInvoker), - nativeCallbackInvoker_(config.nativeCallbackInvoker), - jsThreadCallbackInvoker_(config.jsThreadCallbackInvoker), - invokeCallbacksOnNativeCallerThread_( - config.invokeCallbacksOnNativeCallerThread) { - selfDl_ = dlopen(nullptr, RTLD_NOW); - buildSymbolIndexes(); - } - - ~NativeApiJsiBridge() { - if (selfDl_ != nullptr) { - dlclose(selfDl_); - } - } - - MDMetadataReader* metadata() const { return metadata_.get(); } - - void* selfDl() const { return selfDl_; } - - const NativeApiSymbol* find(const std::string& name) const { - auto it = symbolsByName_.find(name); - return it != symbolsByName_.end() ? &it->second : nullptr; - } - - const NativeApiSymbol* findClass(const std::string& name) const { - const NativeApiSymbol* symbol = find(name); - if (symbol != nullptr && symbol->kind == NativeApiSymbolKind::Class) { - return symbol; - } - auto it = classSymbolsByRuntimeName_.find(name); - return it != classSymbolsByRuntimeName_.end() ? &it->second : nullptr; - } - - const NativeApiSymbol* findClassByOffset(MDSectionOffset offset) const { - auto it = classSymbolsByOffset_.find(offset); - return it != classSymbolsByOffset_.end() ? &it->second : nullptr; - } - - const NativeApiSymbol* findClassForRuntimeClass(Class cls) const { - Class current = cls; - while (current != Nil) { - const char* name = class_getName(current); - if (name != nullptr) { - if (const NativeApiSymbol* symbol = findClass(name)) { - return symbol; - } - } - current = class_getSuperclass(current); - } - return nullptr; - } - - const NativeApiSymbol* findClassForRuntimePointer(void* pointer) const { - if (pointer == nullptr) { - return nullptr; - } - - auto it = classSymbolsByRuntimePointer_.find( - normalizeRuntimePointer(reinterpret_cast(pointer))); - return it != classSymbolsByRuntimePointer_.end() ? &it->second : nullptr; - } - - const NativeApiSymbol* findProtocolForRuntimePointer(void* pointer) const { - if (pointer == nullptr) { - return nullptr; - } - - auto it = protocolSymbolsByRuntimePointer_.find( - normalizeRuntimePointer(reinterpret_cast(pointer))); - return it != protocolSymbolsByRuntimePointer_.end() ? &it->second : nullptr; - } - - const NativeApiSymbol* findFunction(const std::string& name) const { - auto it = functionSymbolsByName_.find(name); - return it != functionSymbolsByName_.end() ? &it->second : nullptr; - } - - void rememberRoundTripValue(Runtime& runtime, const void* native, - const Value& value) { - if (native == nullptr) { - return; - } - std::lock_guard lock(roundTripValuesMutex_); - roundTripValues_[normalizeRuntimePointer( - reinterpret_cast(native))] = - std::make_shared(runtime, value); - } - - Value findRoundTripValue(Runtime& runtime, const void* native) const { - if (native == nullptr) { - return Value::undefined(); - } - std::lock_guard lock(roundTripValuesMutex_); - auto it = roundTripValues_.find( - normalizeRuntimePointer(reinterpret_cast(native))); - if (it == roundTripValues_.end() || it->second == nullptr) { - return Value::undefined(); - } - return Value(runtime, *it->second); - } - - void forgetRoundTripValue(const void* native) { - if (native == nullptr) { - return; - } - std::lock_guard lock(roundTripValuesMutex_); - roundTripValues_.erase( - normalizeRuntimePointer(reinterpret_cast(native))); - } - - void rememberClassValue(Runtime& runtime, Class cls, const Value& value) { - if (cls == Nil) { - return; - } - classValues_[normalizeRuntimePointer(reinterpret_cast(cls))] = - std::make_shared(runtime, value); - } - - Value findClassValue(Runtime& runtime, Class cls) const { - if (cls == Nil) { - return Value::undefined(); - } - auto it = classValues_.find( - normalizeRuntimePointer(reinterpret_cast(cls))); - if (it == classValues_.end() || it->second == nullptr) { - return Value::undefined(); - } - return Value(runtime, *it->second); - } - - void rememberClassPrototype(Runtime& runtime, Class cls, const Value& value) { - if (cls == Nil) { - return; - } - classPrototypes_[normalizeRuntimePointer(reinterpret_cast(cls))] = - std::make_shared(runtime, value); - } - - Value findClassPrototype(Runtime& runtime, Class cls) const { - if (cls == Nil) { - return Value::undefined(); - } - auto it = classPrototypes_.find( - normalizeRuntimePointer(reinterpret_cast(cls))); - if (it == classPrototypes_.end() || it->second == nullptr) { - return Value::undefined(); - } - return Value(runtime, *it->second); - } - - void setObjectExpando(Runtime& runtime, const void* native, - const std::string& property, const Value& value) { - if (native == nullptr || property.empty()) { - return; - } - objectExpandos_[normalizeRuntimePointer(reinterpret_cast(native))] - [property] = std::make_shared(runtime, value); - } - - Value findObjectExpando(Runtime& runtime, const void* native, - const std::string& property) const { - if (native == nullptr || property.empty()) { - return Value::undefined(); - } - auto objectIt = objectExpandos_.find( - normalizeRuntimePointer(reinterpret_cast(native))); - if (objectIt == objectExpandos_.end()) { - return Value::undefined(); - } - auto propertyIt = objectIt->second.find(property); - if (propertyIt == objectIt->second.end() || propertyIt->second == nullptr) { - return Value::undefined(); - } - return Value(runtime, *propertyIt->second); - } - - void forgetObjectExpandos(const void* native) { - if (native == nullptr) { - return; - } - objectExpandos_.erase( - normalizeRuntimePointer(reinterpret_cast(native))); - } - - void rememberPointerValue(Runtime& runtime, const void* native, - const Value& value) { - pointerValues_[reinterpret_cast(native)] = - std::make_shared(runtime, value); - } - - Value findPointerValue(Runtime& runtime, const void* native) const { - auto it = pointerValues_.find(reinterpret_cast(native)); - if (it == pointerValues_.end() || it->second == nullptr) { - return Value::undefined(); - } - return Value(runtime, *it->second); - } - - void forgetPointerValue(const void* native) { - if (native == nullptr) { - return; - } - pointerValues_.erase(reinterpret_cast(native)); - } - - const NativeApiSymbol* findConstant(const std::string& name) const { - auto it = constantSymbolsByName_.find(name); - return it != constantSymbolsByName_.end() ? &it->second : nullptr; - } - - const NativeApiSymbol* findProtocol(const std::string& name) const { - const NativeApiSymbol* symbol = find(name); - if (symbol != nullptr && symbol->kind == NativeApiSymbolKind::Protocol) { - return symbol; - } - auto it = protocolSymbolsByRuntimeName_.find(name); - return it != protocolSymbolsByRuntimeName_.end() ? &it->second : nullptr; - } - - const NativeApiSymbol* findEnum(const std::string& name) const { - auto it = enumSymbolsByName_.find(name); - return it != enumSymbolsByName_.end() ? &it->second : nullptr; - } - - const NativeApiSymbol* findStruct(const std::string& name) const { - auto it = structSymbolsByName_.find(name); - return it != structSymbolsByName_.end() ? &it->second : nullptr; - } - - const NativeApiSymbol* findUnion(const std::string& name) const { - auto it = unionSymbolsByName_.find(name); - return it != unionSymbolsByName_.end() ? &it->second : nullptr; - } - - const NativeApiSymbol* findAggregate(const std::string& name) const { - const NativeApiSymbol* symbol = findStruct(name); - if (symbol != nullptr) { - return symbol; - } - return findUnion(name); - } - - size_t classCount() const { return classNames_.size(); } - size_t functionCount() const { return functionNames_.size(); } - size_t constantCount() const { return constantNames_.size(); } - size_t protocolCount() const { return protocolNames_.size(); } - size_t enumCount() const { return enumNames_.size(); } - size_t structCount() const { return structNames_.size(); } - size_t unionCount() const { return unionNames_.size(); } - - const std::vector& classNames() const { return classNames_; } - const std::vector& functionNames() const { return functionNames_; } - const std::vector& constantNames() const { return constantNames_; } - const std::vector& protocolNames() const { return protocolNames_; } - const std::vector& enumNames() const { return enumNames_; } - const std::vector& structNames() const { return structNames_; } - const std::vector& unionNames() const { return unionNames_; } - std::shared_ptr scheduler() const { return scheduler_; } - const std::function)>& nativeInvocationInvoker() - const { - return nativeInvocationInvoker_; - } - const std::function)>& nativeCallbackInvoker() - const { - return nativeCallbackInvoker_; - } - const std::function)>& jsThreadCallbackInvoker() - const { - return jsThreadCallbackInvoker_; - } - bool invokeCallbacksOnNativeCallerThread() const { - return invokeCallbacksOnNativeCallerThread_; - } - std::thread::id jsThreadId() const { return jsThreadId_; } - - void retainJsiLifetime(std::shared_ptr lifetime) { - if (lifetime == nullptr) { - return; - } - std::lock_guard lock(retainedLifetimesMutex_); - retainedLifetimes_.push_back(std::move(lifetime)); - } - - const std::vector& membersForClass( - const NativeApiSymbol& symbol) const { - auto cached = membersByClassOffset_.find(symbol.offset); - if (cached != membersByClassOffset_.end()) { - return cached->second; - } - - auto inserted = membersByClassOffset_.emplace( - symbol.offset, readMembersForClassHierarchy(symbol)); - return inserted.first->second; - } - - const std::vector& surfaceMembersForClass( - const NativeApiSymbol& symbol) const { - auto cached = surfaceMembersByClassOffset_.find(symbol.offset); - if (cached != surfaceMembersByClassOffset_.end()) { - return cached->second; - } - - auto inserted = surfaceMembersByClassOffset_.emplace( - symbol.offset, readSurfaceMembersForClass(symbol)); - return inserted.first->second; - } - - const std::vector& membersForProtocol( - const NativeApiSymbol& symbol) const { - auto cached = membersByProtocolOffset_.find(symbol.offset); - if (cached != membersByProtocolOffset_.end()) { - return cached->second; - } - - auto inserted = membersByProtocolOffset_.emplace( - symbol.offset, readMembersForProtocolHierarchy(symbol.offset)); - return inserted.first->second; - } - - std::shared_ptr aggregateInfoFor( - MDSectionOffset aggregateOffset, bool isUnion); - - std::shared_ptr aggregateInfoFor( - const NativeApiSymbol& symbol) { - return aggregateInfoFor(symbol.offset, - symbol.kind == NativeApiSymbolKind::Union); - } - - private: - static std::unique_ptr loadMetadataFromFile( - const char* metadataPath) { - const char* path = metadataPath != nullptr ? metadataPath : "metadata.nsmd"; - FILE* file = fopen(path, "rb"); - if (file == nullptr) { - throw std::runtime_error(std::string("metadata.nsmd not found: ") + path); - } - - fseek(file, 0, SEEK_END); - long size = ftell(file); - fseek(file, 0, SEEK_SET); - if (size <= 0) { - fclose(file); - throw std::runtime_error(std::string("metadata.nsmd is empty: ") + path); - } - - void* buffer = malloc(static_cast(size)); - if (buffer == nullptr) { - fclose(file); - throw std::bad_alloc(); - } - - size_t read = fread(buffer, 1, static_cast(size), file); - fclose(file); - if (read != static_cast(size)) { - free(buffer); - throw std::runtime_error(std::string("failed to read metadata: ") + path); - } - - return std::make_unique(buffer, true); - } - - static std::unique_ptr loadMetadata( - const NativeApiJsiConfig& config) { - if (config.metadataPtr != nullptr && - *static_cast(config.metadataPtr) != '\0') { -#ifdef EMBED_METADATA_SIZE - return std::make_unique((void*)embedded_metadata); -#else - return std::make_unique( - const_cast(config.metadataPtr)); -#endif - } - -#ifdef EMBED_METADATA_SIZE - if (config.metadataPath == nullptr) { - return std::make_unique((void*)embedded_metadata); - } -#endif - - unsigned long segmentSize = 0; - auto segmentData = getsegmentdata( - reinterpret_cast(_dyld_get_image_header(0)), - "__objc_metadata", &segmentSize); - if (segmentData != nullptr && segmentSize > 0) { - return std::make_unique(segmentData); - } - - return loadMetadataFromFile(config.metadataPath); - } - - void addSymbol(NativeApiSymbolKind kind, MDSectionOffset offset, - const char* name, const char* runtimeName = nullptr, - MDSectionOffset superclassOffset = MD_SECTION_OFFSET_NULL) { - if (name == nullptr || name[0] == '\0') { - return; - } - - NativeApiSymbol symbol{ - .kind = kind, - .offset = offset, - .superclassOffset = superclassOffset, - .name = name, - .runtimeName = runtimeName != nullptr ? runtimeName : name, - }; - - switch (kind) { - case NativeApiSymbolKind::Class: - classNames_.push_back(symbol.name); - break; - case NativeApiSymbolKind::Function: - functionNames_.push_back(symbol.name); - functionSymbolsByName_[symbol.name] = symbol; - break; - case NativeApiSymbolKind::Constant: - constantNames_.push_back(symbol.name); - constantSymbolsByName_[symbol.name] = symbol; - break; - case NativeApiSymbolKind::Protocol: - protocolNames_.push_back(symbol.name); - break; - case NativeApiSymbolKind::Enum: - enumNames_.push_back(symbol.name); - enumSymbolsByName_[symbol.name] = symbol; - break; - case NativeApiSymbolKind::Struct: - structNames_.push_back(symbol.name); - structSymbolsByName_[symbol.name] = symbol; - break; - case NativeApiSymbolKind::Union: - unionNames_.push_back(symbol.name); - unionSymbolsByName_[symbol.name] = symbol; - break; - } - - symbolsByName_[symbol.name] = symbol; - if (kind == NativeApiSymbolKind::Class) { - classSymbolsByOffset_[symbol.offset] = symbol; - classSymbolsByRuntimeName_[symbol.runtimeName] = symbol; - Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); - if (cls != Nil) { - classSymbolsByRuntimePointer_[normalizeRuntimePointer( - reinterpret_cast(cls))] = symbol; - } - } else if (kind == NativeApiSymbolKind::Protocol) { - protocolSymbolsByOffset_[symbol.offset] = symbol; - protocolSymbolsByRuntimeName_[symbol.runtimeName] = symbol; - auto rememberProtocolRuntimeName = [&](const std::string& runtimeName) { - if (runtimeName.empty()) { - return; - } - protocolSymbolsByRuntimeName_[runtimeName] = symbol; - Protocol* runtimeProtocol = lookupProtocolByNativeName(runtimeName); - if (runtimeProtocol != nullptr) { - protocolSymbolsByRuntimePointer_[normalizeRuntimePointer( - reinterpret_cast(runtimeProtocol))] = symbol; - } - }; - if (symbol.name.size() > 9 && - std::isdigit(static_cast(symbol.name.back()))) { - size_t digitsStart = symbol.name.size(); - while (digitsStart > 0 && - std::isdigit(static_cast(symbol.name[digitsStart - 1]))) { - digitsStart--; - } - constexpr const char* protocolSuffix = "Protocol"; - size_t protocolSuffixLength = std::strlen(protocolSuffix); - if (digitsStart > protocolSuffixLength && - symbol.name.compare(digitsStart - protocolSuffixLength, - protocolSuffixLength, protocolSuffix) == 0) { - rememberProtocolRuntimeName( - symbol.name.substr(0, digitsStart - protocolSuffixLength)); - } - } - Protocol* protocol = lookupProtocolByNativeName(symbol.runtimeName); - if (protocol == nullptr && symbol.runtimeName != symbol.name) { - protocol = lookupProtocolByNativeName(symbol.name); - } - if (protocol != nullptr) { - protocolSymbolsByRuntimePointer_[normalizeRuntimePointer( - reinterpret_cast(protocol))] = symbol; - } - } else if (kind == NativeApiSymbolKind::Struct) { - structSymbolsByOffset_[symbol.offset] = symbol; - } else if (kind == NativeApiSymbolKind::Union) { - unionSymbolsByOffset_[symbol.offset] = symbol; - } - } - - void addAggregateAliases(NativeApiSymbolKind kind, MDSectionOffset offset, - const std::string& name) { - if (name.empty()) { - return; - } - - if (!name.empty() && name[0] == '_') { - std::string alias = name.substr(1); - if (!alias.empty() && symbolsByName_.find(alias) == symbolsByName_.end()) { - addSymbol(kind, offset, alias.c_str(), name.c_str()); - } - } - - constexpr const char* suffix = "Struct"; - if (name.size() < std::strlen(suffix) || - name.compare(name.size() - std::strlen(suffix), std::strlen(suffix), - suffix) != 0) { - std::string alias = name + suffix; - if (symbolsByName_.find(alias) == symbolsByName_.end()) { - addSymbol(kind, offset, alias.c_str(), name.c_str()); - } - } - } - - void buildSymbolIndexes() { - if (metadata_ == nullptr) { - return; - } - - indexConstants(); - indexEnums(); - indexFunctions(); - indexProtocols(); - indexClasses(); - indexStructs(); - indexUnions(); - } - - static void skipConstantValue(MDMetadataReader* metadata, - MDSectionOffset& offset, - metagen::MDVariableEvalKind evalKind) { - switch (evalKind) { - case metagen::mdEvalNone: - skipMetadataJsiType(metadata, &offset); - break; - case metagen::mdEvalInt64: - offset += sizeof(int64_t); - break; - case metagen::mdEvalDouble: - offset += sizeof(double); - break; - case metagen::mdEvalString: - offset += sizeof(MDSectionOffset); - break; - } - } - - void indexConstants() { - MDSectionOffset offset = metadata_->constantsOffset; - while (offset < metadata_->enumsOffset) { - MDSectionOffset originalOffset = offset; - addSymbol(NativeApiSymbolKind::Constant, originalOffset, - metadata_->getString(offset)); - offset += sizeof(MDSectionOffset); - auto evalKind = metadata_->getVariableEvalKind(offset); - offset += sizeof(metagen::MDVariableEvalKind); - skipConstantValue(metadata_.get(), offset, evalKind); - } - } - - void indexEnums() { - MDSectionOffset offset = metadata_->enumsOffset; - while (offset < metadata_->signaturesOffset) { - MDSectionOffset originalOffset = offset; - addSymbol(NativeApiSymbolKind::Enum, originalOffset, - metadata_->getString(offset)); - offset += sizeof(MDSectionOffset); - - bool next = true; - while (next) { - auto nameOffset = metadata_->getOffset(offset); - next = (nameOffset & metagen::mdSectionOffsetNext) != 0; - offset += sizeof(MDSectionOffset); - offset += sizeof(int64_t); - } - } - } - - void indexFunctions() { - MDSectionOffset offset = metadata_->functionsOffset; - while (offset < metadata_->protocolsOffset) { - MDSectionOffset originalOffset = offset; - addSymbol(NativeApiSymbolKind::Function, originalOffset, - metadata_->getString(offset)); - offset += sizeof(MDSectionOffset); - offset += sizeof(MDSectionOffset); - offset += sizeof(metagen::MDFunctionFlag); - } - } - - void indexProtocols() { - MDSectionOffset offset = metadata_->protocolsOffset; - while (offset < metadata_->classesOffset) { - MDSectionOffset originalOffset = offset; - auto nameOffset = metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - bool next = (nameOffset & metagen::mdSectionOffsetNext) != 0; - nameOffset &= ~metagen::mdSectionOffsetNext; - addSymbol(NativeApiSymbolKind::Protocol, originalOffset, - metadata_->resolveString(nameOffset)); - - while (next) { - auto protocolOffset = metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - next = (protocolOffset & metagen::mdSectionOffsetNext) != 0; - } - - next = true; - while (next) { - auto flags = metadata_->getMemberFlag(offset); - next = (flags & metagen::mdMemberNext) != 0; - offset += sizeof(flags); - if (flags == metagen::mdMemberFlagNull) { - break; - } - - skipMember(flags, offset); - } - } - } - - void indexClasses() { - MDSectionOffset offset = metadata_->classesOffset; - while (offset < metadata_->structsOffset) { - MDSectionOffset originalOffset = offset; - auto nameOffset = metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - auto runtimeNameOffset = metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - bool hasProtocols = (nameOffset & metagen::mdSectionOffsetNext) != 0; - nameOffset &= ~metagen::mdSectionOffsetNext; - - auto name = metadata_->resolveString(nameOffset); - const char* runtimeName = name; - if (runtimeNameOffset != MD_SECTION_OFFSET_NULL) { - runtimeName = metadata_->resolveString(runtimeNameOffset); - } - - while (hasProtocols) { - auto protocolOffset = metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - hasProtocols = (protocolOffset & metagen::mdSectionOffsetNext) != 0; - } - - auto superclass = metadata_->getOffset(offset); - offset += sizeof(superclass); - MDSectionOffset superclassOffset = - superclass & ~metagen::mdSectionOffsetNext; - if (superclassOffset != MD_SECTION_OFFSET_NULL) { - superclassOffset += metadata_->classesOffset; - } - - addSymbol(NativeApiSymbolKind::Class, originalOffset, name, runtimeName, - superclassOffset); - - bool next = (superclass & metagen::mdSectionOffsetNext) != 0; - while (next) { - auto flags = metadata_->getMemberFlag(offset); - next = (flags & metagen::mdMemberNext) != 0; - offset += sizeof(flags); - skipMember(flags, offset); - } - } - } - - void skipAggregateFields(MDSectionOffset& offset, bool isUnion) const { - bool next = true; - while (next) { - MDSectionOffset nameOffset = metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - next = (nameOffset & metagen::mdSectionOffsetNext) != 0; - nameOffset &= ~metagen::mdSectionOffsetNext; - if (nameOffset == MD_SECTION_OFFSET_NULL) { - break; - } - if (!isUnion) { - offset += sizeof(uint16_t); - } - skipMetadataJsiType(metadata_.get(), &offset); - } - } - - void indexStructs() { - MDSectionOffset offset = metadata_->structsOffset; - while (offset < metadata_->unionsOffset) { - if (metadata_->getOffset(offset) == 0) { - break; - } - MDSectionOffset originalOffset = offset; - const char* name = metadata_->getString(offset); - offset += sizeof(MDSectionOffset); - offset += sizeof(uint16_t); - addSymbol(NativeApiSymbolKind::Struct, originalOffset, name); - addAggregateAliases(NativeApiSymbolKind::Struct, originalOffset, - name != nullptr ? name : ""); - skipAggregateFields(offset, false); - } - } - - void indexUnions() { - MDSectionOffset offset = metadata_->unionsOffset; - while (metadata_->getOffset(offset) != 0) { - MDSectionOffset originalOffset = offset; - const char* name = metadata_->getString(offset); - offset += sizeof(MDSectionOffset); - offset += sizeof(uint16_t); - addSymbol(NativeApiSymbolKind::Union, originalOffset, name); - addAggregateAliases(NativeApiSymbolKind::Union, originalOffset, - name != nullptr ? name : ""); - skipAggregateFields(offset, true); - } - } - - void skipMember(MDMemberFlag flags, MDSectionOffset& offset) const { - if ((flags & metagen::mdMemberProperty) != 0) { - bool readonly = (flags & metagen::mdMemberReadonly) != 0; - offset += sizeof(MDSectionOffset); - offset += sizeof(MDSectionOffset); - offset += sizeof(MDSectionOffset); - if (!readonly) { - offset += sizeof(MDSectionOffset); - offset += sizeof(MDSectionOffset); - } - return; - } - - offset += sizeof(MDSectionOffset); - offset += sizeof(MDSectionOffset); - } - - std::vector readProtocolOffsetsForClass( - MDSectionOffset classOffset, MDSectionOffset* memberOffset = nullptr, - MDSectionOffset* superclassOffsetOut = nullptr) const { - std::vector protocols; - if (metadata_ == nullptr || classOffset == MD_SECTION_OFFSET_NULL) { - return protocols; - } - - MDSectionOffset offset = classOffset; - auto nameOffset = metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - offset += sizeof(MDSectionOffset); - bool hasProtocols = (nameOffset & metagen::mdSectionOffsetNext) != 0; - - while (hasProtocols) { - auto protocolOffset = metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - hasProtocols = (protocolOffset & metagen::mdSectionOffsetNext) != 0; - protocolOffset &= ~metagen::mdSectionOffsetNext; - if (protocolOffset != MD_SECTION_OFFSET_NULL) { - protocols.push_back(protocolOffset + metadata_->protocolsOffset); - } - } - - auto superclass = metadata_->getOffset(offset); - offset += sizeof(superclass); - const bool hasMembers = (superclass & metagen::mdSectionOffsetNext) != 0; - if (superclassOffsetOut != nullptr) { - MDSectionOffset superclassOffset = - superclass & ~metagen::mdSectionOffsetNext; - *superclassOffsetOut = - superclassOffset != MD_SECTION_OFFSET_NULL - ? superclassOffset + metadata_->classesOffset - : MD_SECTION_OFFSET_NULL; - } - if (memberOffset != nullptr) { - *memberOffset = hasMembers ? offset : MD_SECTION_OFFSET_NULL; - } - return protocols; - } - - std::vector readOwnMembersForClass( - MDSectionOffset classOffset) const { - std::vector members; - if (metadata_ == nullptr || classOffset == MD_SECTION_OFFSET_NULL) { - return members; - } - - MDSectionOffset memberOffset = MD_SECTION_OFFSET_NULL; - for (MDSectionOffset protocolOffset : - readProtocolOffsetsForClass(classOffset, &memberOffset)) { - auto protocol = protocolSymbolsByOffset_.find(protocolOffset); - if (protocol == protocolSymbolsByOffset_.end()) { - continue; - } - const auto& protocolMembers = membersForProtocol(protocol->second); - members.insert(members.end(), protocolMembers.begin(), - protocolMembers.end()); - } - - if (memberOffset != MD_SECTION_OFFSET_NULL) { - std::vector ownMembers = - readMembersAtOffset(memberOffset); - members.insert(members.end(), ownMembers.begin(), ownMembers.end()); - } - return members; - } - - std::vector readMembersForClass( - MDSectionOffset classOffset) const { - std::vector members; - if (metadata_ == nullptr || classOffset == MD_SECTION_OFFSET_NULL) { - return members; - } - - MDSectionOffset offset = classOffset; - auto nameOffset = metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - offset += sizeof(MDSectionOffset); - bool hasProtocols = (nameOffset & metagen::mdSectionOffsetNext) != 0; - - while (hasProtocols) { - auto protocolOffset = metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - hasProtocols = (protocolOffset & metagen::mdSectionOffsetNext) != 0; - } - - auto superclass = metadata_->getOffset(offset); - offset += sizeof(superclass); - - bool next = (superclass & metagen::mdSectionOffsetNext) != 0; - while (next) { - auto flags = metadata_->getMemberFlag(offset); - next = (flags & metagen::mdMemberNext) != 0; - offset += sizeof(flags); - if (flags == metagen::mdMemberFlagNull) { - break; - } - - NativeApiMember member; - member.flags = flags; - if ((flags & metagen::mdMemberProperty) != 0) { - member.property = true; - member.readonly = (flags & metagen::mdMemberReadonly) != 0; - member.name = metadata_->getString(offset); - offset += sizeof(MDSectionOffset); - member.selectorName = metadata_->getString(offset); - offset += sizeof(MDSectionOffset); - member.signatureOffset = - metadata_->signaturesOffset + metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - - if (!member.readonly) { - member.setterSelectorName = metadata_->getString(offset); - offset += sizeof(MDSectionOffset); - member.setterSignatureOffset = - metadata_->signaturesOffset + metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - } - } else { - member.selectorName = metadata_->getString(offset); - offset += sizeof(MDSectionOffset); - member.signatureOffset = - metadata_->signaturesOffset + metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - member.name = jsifySelector(member.selectorName.c_str()); - } - members.push_back(std::move(member)); - } - - return members; - } - - static bool memberIsStatic(const NativeApiMember& member) { - return (member.flags & metagen::mdMemberStatic) != 0; - } - - static bool sameMemberSlot(const NativeApiMember& lhs, - const NativeApiMember& rhs) { - return lhs.name == rhs.name && lhs.property == rhs.property && - memberIsStatic(lhs) == memberIsStatic(rhs); - } - - static bool sameMethodSelector(const NativeApiMember& lhs, - const NativeApiMember& rhs) { - return !lhs.property && !rhs.property && sameMemberSlot(lhs, rhs) && - lhs.selectorName == rhs.selectorName; - } - - static const NativeApiMember* findPropertyMember( - const std::vector& members, - const NativeApiMember& candidate) { - for (const auto& member : members) { - if (member.property && sameMemberSlot(member, candidate)) { - return &member; - } - } - return nullptr; - } - - static bool selectorExistsInMembers( - const std::vector& members, - const NativeApiMember& candidate) { - for (const auto& member : members) { - if (sameMethodSelector(member, candidate)) { - return true; - } - } - return false; - } - - static bool shouldSkipPropertyOverride( - const NativeApiMember* inherited, const NativeApiMember& member) { - if (inherited == nullptr || !inherited->property) { - return false; - } - - bool sameGetter = inherited->selectorName == member.selectorName; - bool sameSetter = - inherited->setterSelectorName == member.setterSelectorName; - if ((!inherited->readonly && member.readonly) || - (inherited->readonly == member.readonly && sameGetter && - (member.readonly || sameSetter))) { - return true; - } - return false; - } - - static void appendSurfaceMember( - std::vector& surface, - const std::vector& inheritedMembers, - const NativeApiMember& member) { - if (member.name.empty()) { - return; - } - - if (member.property) { - const NativeApiMember* inherited = - findPropertyMember(inheritedMembers, member); - if (shouldSkipPropertyOverride(inherited, member)) { - return; - } - - for (auto& existing : surface) { - if (!existing.property || !sameMemberSlot(existing, member)) { - continue; - } - if (existing.readonly && !member.readonly) { - existing = member; - } - return; - } - surface.push_back(member); - return; - } - - const bool keepInheritedMethod = - member.name == "alloc" || member.name == "toString" || - member.name == "superclass"; - if (!keepInheritedMethod && - selectorExistsInMembers(inheritedMembers, member)) { - return; - } - if (selectorExistsInMembers(surface, member)) { - return; - } - surface.push_back(member); - } - - std::vector readSurfaceMembersForClass( - const NativeApiSymbol& symbol) const { - std::vector inheritedMembers; - if (symbol.superclassOffset != MD_SECTION_OFFSET_NULL) { - auto superclass = classSymbolsByOffset_.find(symbol.superclassOffset); - if (superclass != classSymbolsByOffset_.end()) { - const auto& inherited = surfaceMembersForClass(superclass->second); - inheritedMembers.insert(inheritedMembers.end(), inherited.begin(), - inherited.end()); - } - } - - std::vector surface; - for (const auto& member : readOwnMembersForClass(symbol.offset)) { - appendSurfaceMember(surface, inheritedMembers, member); - } - return surface; - } - - std::vector readMembersAtOffset( - MDSectionOffset& offset) const { - std::vector members; - bool next = true; - while (next) { - auto flags = metadata_->getMemberFlag(offset); - next = (flags & metagen::mdMemberNext) != 0; - offset += sizeof(flags); - if (flags == metagen::mdMemberFlagNull) { - break; - } - - NativeApiMember member; - member.flags = flags; - if ((flags & metagen::mdMemberProperty) != 0) { - member.property = true; - member.readonly = (flags & metagen::mdMemberReadonly) != 0; - member.name = metadata_->getString(offset); - offset += sizeof(MDSectionOffset); - member.selectorName = metadata_->getString(offset); - offset += sizeof(MDSectionOffset); - member.signatureOffset = - metadata_->signaturesOffset + metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - - if (!member.readonly) { - member.setterSelectorName = metadata_->getString(offset); - offset += sizeof(MDSectionOffset); - member.setterSignatureOffset = - metadata_->signaturesOffset + metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - } - } else { - member.selectorName = metadata_->getString(offset); - offset += sizeof(MDSectionOffset); - member.signatureOffset = - metadata_->signaturesOffset + metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - member.name = jsifySelector(member.selectorName.c_str()); - } - members.push_back(std::move(member)); - } - return members; - } - - std::vector readMembersForProtocolHierarchy( - MDSectionOffset protocolOffset) const { - std::vector members; - if (metadata_ == nullptr || protocolOffset == MD_SECTION_OFFSET_NULL) { - return members; - } - - MDSectionOffset offset = protocolOffset; - auto nameOffset = metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - bool hasProtocols = (nameOffset & metagen::mdSectionOffsetNext) != 0; - - while (hasProtocols) { - auto inheritedOffset = metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - hasProtocols = (inheritedOffset & metagen::mdSectionOffsetNext) != 0; - inheritedOffset &= ~metagen::mdSectionOffsetNext; - if (inheritedOffset == MD_SECTION_OFFSET_NULL) { - continue; - } - - MDSectionOffset absoluteOffset = - inheritedOffset + metadata_->protocolsOffset; - auto inheritedSymbol = protocolSymbolsByOffset_.find(absoluteOffset); - if (inheritedSymbol != protocolSymbolsByOffset_.end()) { - const auto& inheritedMembers = - membersForProtocol(inheritedSymbol->second); - members.insert(members.end(), inheritedMembers.begin(), - inheritedMembers.end()); - } - } - - std::vector ownMembers = readMembersAtOffset(offset); - members.insert(members.end(), ownMembers.begin(), ownMembers.end()); - return members; - } - - std::vector readMembersForClassHierarchy( - const NativeApiSymbol& symbol) const { - std::vector members = readOwnMembersForClass(symbol.offset); - if (symbol.superclassOffset == MD_SECTION_OFFSET_NULL) { - return members; - } - - auto superclass = classSymbolsByOffset_.find(symbol.superclassOffset); - if (superclass != classSymbolsByOffset_.end()) { - const auto& inheritedMembers = membersForClass(superclass->second); - members.insert(members.end(), inheritedMembers.begin(), - inheritedMembers.end()); - } - return members; - } - - std::unique_ptr metadata_; - void* selfDl_ = nullptr; - std::unordered_map symbolsByName_; - std::unordered_map functionSymbolsByName_; - std::unordered_map constantSymbolsByName_; - std::unordered_map enumSymbolsByName_; - std::unordered_map structSymbolsByName_; - std::unordered_map unionSymbolsByName_; - std::unordered_map classSymbolsByRuntimeName_; - std::unordered_map protocolSymbolsByRuntimeName_; - std::unordered_map classSymbolsByRuntimePointer_; - std::unordered_map protocolSymbolsByRuntimePointer_; - mutable std::mutex roundTripValuesMutex_; - std::unordered_map> roundTripValues_; - std::unordered_map> classValues_; - std::unordered_map> classPrototypes_; - std::unordered_map> pointerValues_; - std::unordered_map>> - objectExpandos_; - std::unordered_map classSymbolsByOffset_; - std::unordered_map protocolSymbolsByOffset_; - std::vector classNames_; - std::vector functionNames_; - std::vector constantNames_; - std::vector protocolNames_; - std::vector enumNames_; - std::vector structNames_; - std::vector unionNames_; - std::shared_ptr scheduler_; - std::function)> nativeInvocationInvoker_; - std::function)> nativeCallbackInvoker_; - std::function)> jsThreadCallbackInvoker_; - bool invokeCallbacksOnNativeCallerThread_ = false; - mutable std::unordered_map> - membersByClassOffset_; - mutable std::unordered_map> - surfaceMembersByClassOffset_; - mutable std::unordered_map> - membersByProtocolOffset_; - std::unordered_map structSymbolsByOffset_; - std::unordered_map unionSymbolsByOffset_; - std::unordered_map> - aggregateInfoByOffset_; - std::unordered_set aggregateInfoInProgress_; - std::thread::id jsThreadId_ = std::this_thread::get_id(); - std::mutex retainedLifetimesMutex_; - std::vector> retainedLifetimes_; -}; - -Value makeString(Runtime& runtime, const std::string& value) { - return String::createFromUtf8(runtime, value); -} - -std::string readStringArg(Runtime& runtime, const Value* args, size_t count, - size_t index, const char* argumentName) { - if (index >= count || !args[index].isString()) { - throw facebook::jsi::JSError( - runtime, std::string(argumentName) + " must be a string."); - } - return args[index].asString(runtime).utf8(runtime); -} - -const char* kindName(NativeApiSymbolKind kind) { - switch (kind) { - case NativeApiSymbolKind::Class: - return "class"; - case NativeApiSymbolKind::Function: - return "function"; - case NativeApiSymbolKind::Constant: - return "constant"; - case NativeApiSymbolKind::Protocol: - return "protocol"; - case NativeApiSymbolKind::Enum: - return "enum"; - case NativeApiSymbolKind::Struct: - return "struct"; - case NativeApiSymbolKind::Union: - return "union"; - } - return "unknown"; -} - -Array namesToArray(Runtime& runtime, const std::vector& names) { - Array result(runtime, names.size()); - for (size_t i = 0; i < names.size(); i++) { - result.setValueAtIndex(runtime, i, makeString(runtime, names[i])); - } - return result; -} - -void addPropertyName(Runtime& runtime, std::vector& names, - const char* name) { - names.push_back(PropNameID::forAscii(runtime, name)); -} - -class NativeApiPointerHostObject; -class NativeApiObjectHostObject; -class NativeApiClassHostObject; -class NativeApiProtocolHostObject; -class NativeApiJsiArgumentFrame; - -Value callCFunction(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiSymbol& symbol, const Value* args, - size_t count); - -Value callObjCSelector(Runtime& runtime, - const std::shared_ptr& bridge, - id receiver, bool receiverIsClass, - const std::string& selectorName, - const NativeApiMember* member, - const Value* args, size_t count, - Class dispatchSuperClass = Nil); - -Value makeNativeObjectValue(Runtime& runtime, - const std::shared_ptr& bridge, - id object, bool ownsObject); - -Value makeNativeClassValue(Runtime& runtime, - const std::shared_ptr& bridge, - NativeApiSymbol symbol); - -Object symbolToObject(Runtime& runtime, const NativeApiSymbol& symbol) { - Object result(runtime); - result.setProperty(runtime, "kind", makeString(runtime, kindName(symbol.kind))); - result.setProperty(runtime, "name", makeString(runtime, symbol.name)); - result.setProperty(runtime, "runtimeName", - makeString(runtime, symbol.runtimeName)); - result.setProperty(runtime, "metadataOffset", - static_cast(symbol.offset)); - - if (symbol.kind == NativeApiSymbolKind::Class) { - Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); - result.setProperty(runtime, "available", cls != nil); - if (cls != nil) { - char address[32] = {}; - snprintf(address, sizeof(address), "%p", cls); - result.setProperty(runtime, "nativeAddress", makeString(runtime, address)); - } - } else if (symbol.kind == NativeApiSymbolKind::Struct || - symbol.kind == NativeApiSymbolKind::Union) { - result.setProperty(runtime, "available", true); - } - - return result; -} - -size_t nativeSizeForType(const NativeApiJsiType& type); -std::optional parseArrayIndexProperty(const std::string& property); - -NativeApiJsiType nativeObjectReturnType( - MDTypeKind kind = metagen::mdTypeAnyObject) { - NativeApiJsiType type; - type.kind = kind; - type.ffiType = &ffi_type_pointer; - type.supported = true; - return type; -} - -NativeApiJsiType nativeObjectReturnTypeForClass(Class cls) { - if (cls != Nil) { - const char* name = class_getName(cls); - if (name != nullptr && std::strcmp(name, "NSString") == 0) { - return nativeObjectReturnType(metagen::mdTypeNSStringObject); - } - if (name != nullptr && std::strcmp(name, "NSMutableString") == 0) { - return nativeObjectReturnType(metagen::mdTypeNSMutableStringObject); - } - } - return nativeObjectReturnType(metagen::mdTypeInstanceObject); -} - -Value convertNativeReturnValue(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* value); -Object createPointer(Runtime& runtime, - const std::shared_ptr& bridge, - void* pointer, bool adopted = false); - -NativeApiJsiType primitiveInteropType(MDTypeKind kind); diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiCallbacks.h b/NativeScript/ffi/shared/jsi/NativeApiJsiCallbacks.h deleted file mode 100644 index 7df14ed01..000000000 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiCallbacks.h +++ /dev/null @@ -1,1972 +0,0 @@ -bool isObjectiveCObjectType(const NativeApiJsiType& type) { - switch (type.kind) { - case metagen::mdTypeAnyObject: - case metagen::mdTypeProtocolObject: - case metagen::mdTypeClassObject: - case metagen::mdTypeInstanceObject: - case metagen::mdTypeNSStringObject: - case metagen::mdTypeNSMutableStringObject: - return true; - default: - return false; - } -} - -#ifndef NATIVESCRIPT_NATIVE_API_RETAIN_RUNTIME -std::shared_ptr retainNativeApiJsiRuntime(Runtime& runtime) { - return std::shared_ptr(&runtime, [](Runtime*) {}); -} -#endif - -#ifndef NATIVESCRIPT_NATIVE_API_RUNTIME_SCOPE -class NativeApiJsiRuntimeScope final { - public: - explicit NativeApiJsiRuntimeScope(Runtime&) {} -}; -#endif - -struct NativeApiJsiSignature { - ffi_cif cif = {}; - NativeApiJsiType returnType; - std::vector argumentTypes; - std::vector ffiTypes; - std::string selectorName; - bool variadic = false; - bool prepared = false; - unsigned int implicitArgumentCount = 0; -}; - -enum class NativeApiJsiCallbackThreadPolicy { - Default, - UI, - JS, -}; - -NativeApiJsiCallbackThreadPolicy readJsiCallbackThreadPolicy( - Runtime& runtime, Object& functionObject) { - constexpr const char* propertyName = "__nativeScriptCallbackThread"; - try { - if (!functionObject.hasProperty(runtime, propertyName)) { - return NativeApiJsiCallbackThreadPolicy::Default; - } - Value policyValue = functionObject.getProperty(runtime, propertyName); - if (!policyValue.isString()) { - return NativeApiJsiCallbackThreadPolicy::Default; - } - std::string policy = policyValue.asString(runtime).utf8(runtime); - if (policy == "ui") { - return NativeApiJsiCallbackThreadPolicy::UI; - } - if (policy == "js") { - return NativeApiJsiCallbackThreadPolicy::JS; - } - } catch (const std::exception&) { - } - return NativeApiJsiCallbackThreadPolicy::Default; -} - -bool selectorEndsWithNSErrorParam(const std::string& selectorName) { - constexpr const char* suffix = "error:"; - size_t suffixLength = std::strlen(suffix); - return selectorName.size() >= suffixLength && - selectorName.compare(selectorName.size() - suffixLength, suffixLength, - suffix) == 0; -} - -bool isNSErrorOutJsiMethodSignature(const NativeApiJsiSignature& signature) { - if (signature.argumentTypes.empty() || signature.variadic || - !selectorEndsWithNSErrorParam(signature.selectorName)) { - return false; - } - - return signature.argumentTypes.back().kind == metagen::mdTypePointer; -} - -bool isNSErrorOutJsiMethodCallback(const NativeApiJsiSignature& signature) { - return signature.returnType.kind == metagen::mdTypeBool && - signature.implicitArgumentCount >= 2 && - isNSErrorOutJsiMethodSignature(signature); -} - -class NativeApiJsiArgumentFrame { - public: - explicit NativeApiJsiArgumentFrame(size_t count) : storage_(count), values_(count) {} - - ~NativeApiJsiArgumentFrame() { - for (char* string : ownedCStrings_) { - free(string); - } - for (void* buffer : ownedBuffers_) { - free(buffer); - } - for (id object : ownedObjects_) { - [object release]; - } - for (const auto& entry : temporaryRoundTripValues_) { - if (entry.first != nullptr) { - entry.first->forgetRoundTripValue(entry.second); - } - } - ownedLifetimes_.clear(); - } - - void* storageAt(size_t index, size_t size) { - storage_[index].assign(std::max(size, sizeof(void*)), 0); - values_[index] = storage_[index].data(); - return values_[index]; - } - - void addCString(char* value) { ownedCStrings_.push_back(value); } - void* addBuffer(size_t size) { - void* buffer = calloc(1, std::max(size, 1)); - if (buffer == nullptr) { - throw std::bad_alloc(); - } - ownedBuffers_.push_back(buffer); - return buffer; - } - void addObject(id value) { ownedObjects_.push_back(value); } - void addLifetime(std::shared_ptr value) { - if (value != nullptr) { - ownedLifetimes_.push_back(std::move(value)); - } - } - void rememberRoundTripValue( - const std::shared_ptr& bridge, Runtime& runtime, - const void* native, const Value& value) { - if (bridge == nullptr || native == nullptr) { - return; - } - bridge->rememberRoundTripValue(runtime, native, value); - temporaryRoundTripValues_.push_back({bridge, native}); - } - void** values() { return values_.empty() ? nullptr : values_.data(); } - - private: - std::vector> storage_; - std::vector values_; - std::vector ownedCStrings_; - std::vector ownedBuffers_; - std::vector ownedObjects_; - std::vector> ownedLifetimes_; - std::vector, const void*>> - temporaryRoundTripValues_; -}; - -class NativeApiMutableBuffer final : public MutableBuffer { - public: - explicit NativeApiMutableBuffer(size_t size) : data_(size) {} - NativeApiMutableBuffer(const void* data, size_t size) : data_(size) { - if (data != nullptr && size > 0) { - std::memcpy(data_.data(), data, size); - } - } - - size_t size() const override { return data_.size(); } - uint8_t* data() override { return data_.empty() ? nullptr : data_.data(); } - - private: - std::vector data_; -}; - -void convertJsiArgument(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, - const Value& value, void* target, - NativeApiJsiArgumentFrame& frame); - -Value convertNativeReturnValue(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* value); - -Value wrapNativeFunctionPointer(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* pointer, - bool block); - -bool isObjectiveCObjectType(const NativeApiJsiType& type); - -struct NativeApiJsiBlockDescriptor { - unsigned long reserved = 0; - unsigned long size = 0; - void (*copyHelper)(void*, void*) = nullptr; - void (*disposeHelper)(void*) = nullptr; - const char* signature = nullptr; -}; - -struct NativeApiJsiBlockLiteral { - void* isa = nullptr; - int flags = 0; - int reserved = 0; - void* invoke = nullptr; - NativeApiJsiBlockDescriptor* descriptor = nullptr; - void* callback = nullptr; -}; - -constexpr int kNativeApiJsiBlockNeedsFree = (1 << 24); -constexpr int kNativeApiJsiBlockHasCopyDispose = (1 << 25); -constexpr int kNativeApiJsiBlockRefCountOne = (1 << 1); -constexpr int kNativeApiJsiBlockHasSignature = (1 << 30); - -void* nativeApiJsiStackBlockIsa() { - static void* isa = dlsym(RTLD_DEFAULT, "_NSConcreteStackBlock"); - return isa; -} - -void nativeApiJsiBlockCopy(void* dst, void* src); -void nativeApiJsiBlockDispose(void* src); - -std::string objcEncodingForJsiType(const NativeApiJsiType& type) { - switch (type.kind) { - case metagen::mdTypeVoid: - return "v"; - case metagen::mdTypeBool: - return "B"; - case metagen::mdTypeChar: - return "c"; - case metagen::mdTypeUChar: - case metagen::mdTypeUInt8: - return "C"; - case metagen::mdTypeSShort: - return "s"; - case metagen::mdTypeUShort: - return "S"; - case metagen::mdTypeSInt: - return "i"; - case metagen::mdTypeUInt: - return "I"; - case metagen::mdTypeSLong: - case metagen::mdTypeSInt64: - return "q"; - case metagen::mdTypeULong: - case metagen::mdTypeUInt64: - return "Q"; - case metagen::mdTypeFloat: - return "f"; - case metagen::mdTypeDouble: - return "d"; - case metagen::mdTypeString: - return "*"; - case metagen::mdTypeAnyObject: - case metagen::mdTypeProtocolObject: - case metagen::mdTypeClassObject: - case metagen::mdTypeInstanceObject: - case metagen::mdTypeNSStringObject: - case metagen::mdTypeNSMutableStringObject: - return "@"; - case metagen::mdTypeClass: - return "#"; - case metagen::mdTypeSelector: - return ":"; - case metagen::mdTypeBlock: - return "@?"; - case metagen::mdTypeFunctionPointer: - return "^?"; - case metagen::mdTypePointer: - case metagen::mdTypeOpaquePointer: - if (type.elementType != nullptr && - type.elementType->kind != metagen::mdTypeVoid) { - return "^" + objcEncodingForJsiType(*type.elementType); - } - return "^v"; - case metagen::mdTypeStruct: - return "{" + - (type.aggregateInfo != nullptr ? type.aggregateInfo->name - : std::string("?")) + - "=}"; - case metagen::mdTypeArray: - return "[" + std::to_string(type.arraySize) + - (type.elementType != nullptr ? objcEncodingForJsiType(*type.elementType) - : std::string("?")) + - "]"; - case metagen::mdTypeVector: - case metagen::mdTypeExtVector: - case metagen::mdTypeComplex: - return type.elementType != nullptr ? objcEncodingForJsiType(*type.elementType) - : "?"; - default: - return "?"; - } -} - -std::string objcBlockSignatureForJsiSignature( - const NativeApiJsiSignature& signature) { - std::string encoding = objcEncodingForJsiType(signature.returnType); - encoding += "@?"; - for (const auto& argType : signature.argumentTypes) { - encoding += objcEncodingForJsiType(argType); - } - return encoding; -} - -std::string objcMethodSignatureForJsiSignature( - const NativeApiJsiSignature& signature) { - std::string encoding = objcEncodingForJsiType(signature.returnType); - encoding += "@:"; - for (const auto& argType : signature.argumentTypes) { - encoding += objcEncodingForJsiType(argType); - } - return encoding; -} - -[[noreturn]] void throwNativeApiJsiCallbackException( - const std::string& message) { - NSString* reason = [NSString stringWithUTF8String:message.c_str()]; - @throw [NSException exceptionWithName:@"NativeScriptJSICallbackException" - reason:reason - userInfo:nil]; -} - -class NativeApiJsiCallback; - -void nativeApiJsiCallbackTrampoline(ffi_cif* cif, void* ret, void* args[], - void* data); - -std::atomic gActiveNativeThreadJsiCallbacks{0}; - -class NativeApiJsiCallback final - : public std::enable_shared_from_this { - public: - NativeApiJsiCallback(Runtime& runtime, - std::shared_ptr bridge, - std::shared_ptr signature, - Function function, bool block, - NativeApiJsiCallbackThreadPolicy threadPolicy = - NativeApiJsiCallbackThreadPolicy::Default, - bool bindThis = false) - : runtimeOwner_(retainNativeApiJsiRuntime(runtime)), - runtime_(runtimeOwner_.get()), - bridge_(std::move(bridge)), - signature_(std::move(signature)), - function_(std::make_shared(std::move(function))), - block_(block), - threadPolicy_(threadPolicy), - bindThis_(bindThis) { - closure_ = static_cast( - ffi_closure_alloc(sizeof(ffi_closure), &executable_)); - if (closure_ == nullptr || executable_ == nullptr || - signature_ == nullptr || !signature_->prepared) { - throw facebook::jsi::JSError(runtime, - "Unable to allocate native JSI callback."); - } - - ffi_status status = ffi_prep_closure_loc( - closure_, &signature_->cif, nativeApiJsiCallbackTrampoline, this, - executable_); - if (status != FFI_OK) { - ffi_closure_free(closure_); - closure_ = nullptr; - executable_ = nullptr; - throw facebook::jsi::JSError(runtime, - "Unable to prepare native JSI callback."); - } - - if (block_) { - blockSignature_ = objcBlockSignatureForJsiSignature(*signature_); - descriptor_ = std::make_unique(); - descriptor_->reserved = 0; - descriptor_->size = sizeof(NativeApiJsiBlockLiteral); - descriptor_->copyHelper = nativeApiJsiBlockCopy; - descriptor_->disposeHelper = nativeApiJsiBlockDispose; - descriptor_->signature = blockSignature_.c_str(); - - blockLiteral_ = std::make_unique(); - blockLiteral_->isa = nativeApiJsiStackBlockIsa(); - blockLiteral_->flags = kNativeApiJsiBlockHasCopyDispose | - kNativeApiJsiBlockHasSignature; - blockLiteral_->invoke = executable_; - blockLiteral_->descriptor = descriptor_.get(); - blockLiteral_->callback = this; - } - } - - ~NativeApiJsiCallback() { - if (closure_ != nullptr) { - ffi_closure_free(closure_); - closure_ = nullptr; - executable_ = nullptr; - } - } - - void* functionPointer() const { - return block_ && blockLiteral_ != nullptr - ? static_cast(blockLiteral_.get()) - : executable_; - } - - const NativeApiJsiSignature& signature() const { return *signature_; } - - void retainBlockCopy(const void* blockPointer) { - if (!block_) { - return; - } - auto self = shared_from_this(); - if (bridge_ != nullptr && runtime_ != nullptr && function_ != nullptr && - blockPointer != nullptr) { - bridge_->rememberRoundTripValue(*runtime_, blockPointer, - Value(*runtime_, *function_)); - } - std::lock_guard lock(retainedBlockCopiesMutex_); - retainedBlockCopies_.push_back({blockPointer, std::move(self)}); - } - - bool releaseBlockCopy(const void* blockPointer) { - if (!block_) { - return false; - } - std::shared_ptr keepAlive; - try { - keepAlive = shared_from_this(); - } catch (const std::bad_weak_ptr&) { - return false; - } - std::lock_guard lock(retainedBlockCopiesMutex_); - auto it = retainedBlockCopies_.end(); - if (blockPointer != nullptr) { - it = std::find_if( - retainedBlockCopies_.begin(), retainedBlockCopies_.end(), - [blockPointer](const RetainedBlockCopy& retained) { - return retained.blockPointer == blockPointer; - }); - } - if (it != retainedBlockCopies_.end()) { - if (bridge_ != nullptr && it->blockPointer != nullptr) { - bridge_->forgetRoundTripValue(it->blockPointer); - } - retainedBlockCopies_.erase(it); - return true; - } - return false; - } - - void invoke(void* ret, void* args[]) { - if (runtime_ == nullptr || function_ == nullptr || signature_ == nullptr) { - throwNativeApiJsiCallbackException("Invalid JSI callback."); - } - - std::string error; - auto call = [&]() { invokeOnCurrentThread(ret, args, &error); }; - const auto& nativeCallbackInvoker = bridge_->nativeCallbackInvoker(); - const auto& jsThreadCallbackInvoker = bridge_->jsThreadCallbackInvoker(); - bool currentThreadIsJs = - std::this_thread::get_id() == bridge_->jsThreadId(); - - auto callOnNativeCallerThread = [&]() { - ScopedNativeCallerThreadJsiCallback callbackScope; - if (nativeCallbackInvoker) { - nativeCallbackInvoker(call); - } else { - call(); - } - }; - auto callOnUIThread = [&]() { - auto runOnUIThread = [&]() { - bool previous = gExecutingDispatchedUINativeCall; - gExecutingDispatchedUINativeCall = true; - callOnNativeCallerThread(); - gExecutingDispatchedUINativeCall = previous; - }; - if ([NSThread isMainThread]) { - runOnUIThread(); - } else { - dispatch_sync(dispatch_get_main_queue(), ^{ - runOnUIThread(); - }); - } - }; - auto callOnJSThread = [&]() { - if (currentThreadIsJs) { - call(); - return; - } - if (jsThreadCallbackInvoker) { - jsThreadCallbackInvoker(call); - return; - } - if (auto scheduler = bridge_->scheduler()) { - dispatch_semaphore_t done = dispatch_semaphore_create(0); - scheduler->invokeOnJS([call, done]() mutable { - call(); - dispatch_semaphore_signal(done); - }); - dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER); - return; - } - error = "Native callback was invoked off the JS thread without a JS scheduler."; - }; - - if (threadPolicy_ == NativeApiJsiCallbackThreadPolicy::UI) { - callOnUIThread(); - if (!error.empty()) { - if (!recordNativeCallbackException(error)) { - throwNativeApiJsiCallbackException(error); - } - } - return; - } - if (threadPolicy_ == NativeApiJsiCallbackThreadPolicy::JS) { - callOnJSThread(); - if (!error.empty()) { - if (!recordNativeCallbackException(error)) { - throwNativeApiJsiCallbackException(error); - } - } - return; - } - - bool returnsVoid = signature_->returnType.kind == metagen::mdTypeVoid; - bool activeSynchronousNativeInvocation = - gActiveSynchronousNativeInvocationDepth.load( - std::memory_order_acquire) > 0; - bool nativeCallerThreadCallbacks = - bridge_->invokeCallbacksOnNativeCallerThread(); - bool nativeCallerThreadCallback = - nativeCallerThreadCallbacks && !currentThreadIsJs && - (block_ || bindThis_ || - (activeSynchronousNativeInvocation && !returnsVoid)); - bool direct = currentThreadIsJs || - gExecutingDispatchedUINativeCall || - gSynchronousNativeInvocationDepth > 0 || - nativeCallerThreadCallback || - (nativeCallerThreadCallbacks && !nativeCallbackInvoker && - activeSynchronousNativeInvocation); - bool waitForNativeThreadCallback = - currentThreadIsJs && nativeCallbackInvoker && - gActiveNativeThreadJsiCallbacks.load(std::memory_order_acquire) > 0; - if (direct && !waitForNativeThreadCallback) { - if (nativeCallerThreadCallback) { - callOnNativeCallerThread(); - } else { - call(); - } - } else if (!currentThreadIsJs && !nativeCallerThreadCallbacks) { - callOnJSThread(); - } else if (!currentThreadIsJs && returnsVoid && block_ && - jsThreadCallbackInvoker) { - jsThreadCallbackInvoker(call); - } else if (nativeCallbackInvoker) { - bool nativeThreadCallback = !currentThreadIsJs; - if (nativeThreadCallback) { - gActiveNativeThreadJsiCallbacks.fetch_add(1, - std::memory_order_acq_rel); - } - try { - nativeCallbackInvoker(call); - } catch (...) { - if (nativeThreadCallback) { - gActiveNativeThreadJsiCallbacks.fetch_sub( - 1, std::memory_order_acq_rel); - } - throw; - } - if (nativeThreadCallback) { - gActiveNativeThreadJsiCallbacks.fetch_sub(1, - std::memory_order_acq_rel); - } - } else if (auto scheduler = bridge_->scheduler()) { - dispatch_semaphore_t done = dispatch_semaphore_create(0); - scheduler->invokeOnJS([call, done]() mutable { - call(); - dispatch_semaphore_signal(done); - }); - dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER); - } else { - error = "Native callback was invoked off the JS thread without a JS scheduler."; - } - - if (!error.empty()) { - if (!recordNativeCallbackException(error)) { - throwNativeApiJsiCallbackException(error); - } - } - } - - private: - void invokeOnCurrentThread(void* ret, void* args[], std::string* error) { - try { - NativeApiJsiRuntimeScope runtimeScope(*runtime_); - size_t nativeArgOffset = signature_->implicitArgumentCount; - std::vector jsArgs; - jsArgs.reserve(signature_->argumentTypes.size()); - for (size_t i = 0; i < signature_->argumentTypes.size(); i++) { - jsArgs.emplace_back(convertNativeReturnValue( - *runtime_, bridge_, signature_->argumentTypes[i], - args[i + nativeArgOffset])); - } - - Value result = Value::undefined(); - if (bindThis_ && nativeArgOffset >= 1) { - id self = *static_cast(args[0]); - Value thisValue = - makeNativeObjectValue(*runtime_, bridge_, self, false); - Object thisObject = thisValue.isObject() - ? thisValue.asObject(*runtime_) - : Object(*runtime_); - result = - jsArgs.empty() - ? function_->callWithThis(*runtime_, thisObject) - : function_->callWithThis( - *runtime_, thisObject, - static_cast(jsArgs.data()), - static_cast(jsArgs.size())); - } else { - result = - jsArgs.empty() - ? function_->call(*runtime_) - : function_->call(*runtime_, - static_cast(jsArgs.data()), - static_cast(jsArgs.size())); - } - storeReturnValue(result, ret); - if (std::this_thread::get_id() == bridge_->jsThreadId()) { - runtime_->drainMicrotasks(); - } - } catch (const std::exception& exception) { - if (isNSErrorOutJsiMethodCallback(*signature_)) { - zeroReturnValue(ret); - populateNSErrorOutArgument(args, exception.what()); - return; - } - if (error != nullptr) { - *error = exception.what(); - } - zeroReturnValue(ret); - } catch (...) { - if (isNSErrorOutJsiMethodCallback(*signature_)) { - zeroReturnValue(ret); - populateNSErrorOutArgument(args, "Unknown exception in native JSI callback."); - return; - } - if (error != nullptr) { - *error = "Unknown exception in native JSI callback."; - } - zeroReturnValue(ret); - } - } - - void populateNSErrorOutArgument(void* args[], const char* message) { - if (args == nullptr || signature_ == nullptr || - signature_->argumentTypes.empty()) { - return; - } - - size_t outArgIndex = signature_->implicitArgumentCount + - signature_->argumentTypes.size() - 1; - void* outArgValue = args[outArgIndex]; - NSError** outError = - outArgValue != nullptr ? *reinterpret_cast(outArgValue) - : nullptr; - if (outError == nullptr) { - return; - } - - NSString* nsMessage = - message != nullptr ? [NSString stringWithUTF8String:message] : nil; - if (nsMessage == nil) { - nsMessage = @"JS error"; - } - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : nsMessage}; - *outError = [NSError errorWithDomain:@"TNSErrorDomain" - code:1 - userInfo:userInfo]; - } - - void zeroReturnValue(void* ret) { - if (ret == nullptr || signature_ == nullptr || - signature_->returnType.kind == metagen::mdTypeVoid) { - return; - } - size_t size = nativeSizeForType(signature_->returnType); - if (size > 0) { - std::memset(ret, 0, size); - } - } - - void storeReturnValue(const Value& result, void* ret) { - if (ret == nullptr || - signature_->returnType.kind == metagen::mdTypeVoid) { - return; - } - - zeroReturnValue(ret); - if (result.isUndefined() || result.isNull()) { - return; - } - const auto& returnType = signature_->returnType; - if (returnType.kind == metagen::mdTypeString && result.isString()) { - std::string utf8 = result.asString(*runtime_).utf8(*runtime_); - *static_cast(ret) = strdup(utf8.c_str()); - return; - } - if ((returnType.kind == metagen::mdTypePointer || - returnType.kind == metagen::mdTypeOpaquePointer) && - result.isString()) { - std::string utf8 = result.asString(*runtime_).utf8(*runtime_); - *static_cast(ret) = strdup(utf8.c_str()); - return; - } - - NativeApiJsiArgumentFrame frame(1); - convertJsiArgument(*runtime_, bridge_, returnType, result, ret, frame); - if (isObjectiveCObjectType(returnType)) { - id object = *static_cast(ret); - if (object != nil) { - [object retain]; - [object autorelease]; - } - } - } - - std::shared_ptr runtimeOwner_; - Runtime* runtime_ = nullptr; - std::shared_ptr bridge_; - std::shared_ptr signature_; - std::shared_ptr function_; - bool block_ = false; - NativeApiJsiCallbackThreadPolicy threadPolicy_ = - NativeApiJsiCallbackThreadPolicy::Default; - bool bindThis_ = false; - ffi_closure* closure_ = nullptr; - void* executable_ = nullptr; - std::string blockSignature_; - std::unique_ptr descriptor_; - std::unique_ptr blockLiteral_; - struct RetainedBlockCopy { - const void* blockPointer = nullptr; - std::shared_ptr lifetime; - }; - std::mutex retainedBlockCopiesMutex_; - std::vector retainedBlockCopies_; -}; - -void nativeApiJsiBlockCopy(void* dst, void* src) { - auto* dstBlock = static_cast(dst); - auto* srcBlock = static_cast(src); - if (dstBlock == nullptr || srcBlock == nullptr || - srcBlock->callback == nullptr) { - return; - } - dstBlock->callback = srcBlock->callback; - static_cast(srcBlock->callback) - ->retainBlockCopy(dstBlock); -} - -void nativeApiJsiBlockDispose(void* src) { - auto* block = static_cast(src); - if (block == nullptr || block->callback == nullptr) { - return; - } - bool released = - static_cast(block->callback)->releaseBlockCopy(block); - if (released) { - block->callback = nullptr; - } -} - -void nativeApiJsiCallbackTrampoline(ffi_cif*, void* ret, void* args[], - void* data) { - auto callback = static_cast(data); - if (callback == nullptr) { - return; - } - @try { - callback->invoke(ret, args); - } @catch (NSException* exception) { - const char* description = - exception.description != nil ? exception.description.UTF8String : nullptr; - std::string message = description != nullptr - ? description - : "Objective-C exception in native JSI callback."; - if (!recordNativeCallbackException(message)) { - @throw; - } - } -} - -size_t nativeSizeForType(const NativeApiJsiType& type) { - switch (type.kind) { - case metagen::mdTypeStruct: - if (type.aggregateInfo != nullptr) { - return type.aggregateInfo->size; - } - break; - case metagen::mdTypeArray: - if (type.elementType != nullptr) { - return nativeSizeForType(*type.elementType) * - static_cast(type.arraySize); - } - break; - case metagen::mdTypeVector: - case metagen::mdTypeExtVector: - case metagen::mdTypeComplex: - if (type.elementType != nullptr) { - size_t lanes = std::max(type.arraySize, 1); - size_t abiLanes = lanes == 3 ? 4 : lanes; - return nativeSizeForType(*type.elementType) * abiLanes; - } - break; - default: - break; - } - - if (type.ffiType != nullptr && type.ffiType->size > 0) { - return type.ffiType->size; - } - if (type.ffiType == &ffi_type_void) { - return 0; - } - return sizeof(void*); -} - -Value signedInteger64ToJsiValue(Runtime& runtime, int64_t value) { - constexpr int64_t maxSafeInteger = 9007199254740991LL; - constexpr int64_t minSafeInteger = -9007199254740991LL; - if (value >= minSafeInteger && value <= maxSafeInteger) { - return static_cast(value); - } - return BigInt::fromInt64(runtime, value); -} - -Value unsignedInteger64ToJsiValue(Runtime& runtime, uint64_t value) { - constexpr uint64_t maxSafeInteger = 9007199254740991ULL; - if (value <= maxSafeInteger) { - return static_cast(value); - } - return BigInt::fromUint64(runtime, value); -} - -bool parseIntegerTextToUintptr(const std::string& text, uintptr_t* address) { - if (address == nullptr) { - return false; - } - if (text.empty()) { - return false; - } - - char* end = nullptr; - if (text[0] == '-') { - long long signedValue = std::strtoll(text.c_str(), &end, 10); - if (end == nullptr || *end != '\0') { - return false; - } - *address = static_cast(static_cast(signedValue)); - return true; - } - - int base = 10; - const char* start = text.c_str(); - if (text.size() > 2 && text[0] == '0' && - (text[1] == 'x' || text[1] == 'X')) { - base = 16; - } - unsigned long long unsignedValue = std::strtoull(start, &end, base); - if (end == nullptr || *end != '\0') { - return false; - } - *address = static_cast(unsignedValue); - return true; -} - -bool parseBigIntToUintptr(Runtime& runtime, const BigInt& bigint, - uintptr_t* address) { - return parseIntegerTextToUintptr(bigint.toString(runtime, 10).utf8(runtime), - address); -} - -bool readJsiBuffer(Runtime& runtime, const Object& object, const uint8_t** data, - size_t* byteLength) { - if (data == nullptr || byteLength == nullptr) { - return false; - } - - if (object.isArrayBuffer(runtime)) { - ArrayBuffer buffer = object.getArrayBuffer(runtime); - *data = buffer.data(runtime); - *byteLength = buffer.size(runtime); - return true; - } - - Value bufferValue = object.getProperty(runtime, "buffer"); - if (!bufferValue.isObject()) { - return false; - } - Object bufferObject = bufferValue.asObject(runtime); - if (!bufferObject.isArrayBuffer(runtime)) { - return false; - } - - size_t byteOffset = 0; - size_t viewByteLength = 0; - Value offsetValue = object.getProperty(runtime, "byteOffset"); - if (offsetValue.isNumber()) { - byteOffset = static_cast(std::max(0, offsetValue.getNumber())); - } - Value lengthValue = object.getProperty(runtime, "byteLength"); - if (lengthValue.isNumber()) { - viewByteLength = static_cast(std::max(0, lengthValue.getNumber())); - } - - ArrayBuffer buffer = bufferObject.getArrayBuffer(runtime); - if (byteOffset > buffer.size(runtime)) { - return false; - } - if (viewByteLength == 0 || byteOffset + viewByteLength > buffer.size(runtime)) { - viewByteLength = buffer.size(runtime) - byteOffset; - } - *data = buffer.data(runtime) + byteOffset; - *byteLength = viewByteLength; - return true; -} - -uint32_t rawTypeKind(MDTypeKind kind) { - return static_cast(kind); -} - -MDTypeKind stripTypeFlags(MDTypeKind kind) { - uint32_t raw = rawTypeKind(kind); - raw &= ~static_cast(metagen::mdTypeFlagNext); - raw &= ~static_cast(metagen::mdTypeFlagVariadic); - return static_cast(raw); -} - -size_t alignUp(size_t value, size_t alignment) { - if (alignment == 0) { - return value; - } - return ((value + alignment - 1) / alignment) * alignment; -} - -ffi_type* ffiTypeForJsiKind(MDTypeKind kind) { - switch (kind) { - case metagen::mdTypeChar: - return &ffi_type_sint8; - case metagen::mdTypeUChar: - case metagen::mdTypeUInt8: - case metagen::mdTypeBool: - return &ffi_type_uint8; - case metagen::mdTypeSShort: - return &ffi_type_sint16; - case metagen::mdTypeUShort: - return &ffi_type_uint16; - case metagen::mdTypeSInt: - return &ffi_type_sint32; - case metagen::mdTypeUInt: - return &ffi_type_uint32; - case metagen::mdTypeSLong: - case metagen::mdTypeSInt64: - return &ffi_type_sint64; - case metagen::mdTypeULong: - case metagen::mdTypeUInt64: - return &ffi_type_uint64; - case metagen::mdTypeFloat: - return &ffi_type_float; - case metagen::mdTypeDouble: - return &ffi_type_double; - case metagen::mdTypeVoid: - return &ffi_type_void; - case metagen::mdTypeString: - case metagen::mdTypeAnyObject: - case metagen::mdTypeProtocolObject: - case metagen::mdTypeClassObject: - case metagen::mdTypeInstanceObject: - case metagen::mdTypeNSStringObject: - case metagen::mdTypeNSMutableStringObject: - case metagen::mdTypeClass: - case metagen::mdTypeSelector: - case metagen::mdTypePointer: - case metagen::mdTypeOpaquePointer: - case metagen::mdTypeBlock: - case metagen::mdTypeFunctionPointer: - return &ffi_type_pointer; - default: - return nullptr; - } -} - -bool isSupportedJsiKind(MDTypeKind kind) { - switch (kind) { - default: - return ffiTypeForJsiKind(kind) != nullptr; - } -} - -void skipMetadataJsiTypePayload(MDMetadataReader* metadata, MDSectionOffset* offset, - MDTypeKind kind); - -void skipMetadataJsiType(MDMetadataReader* metadata, MDSectionOffset* offset) { - MDTypeKind kind = stripTypeFlags(metadata->getTypeKind(*offset)); - *offset += sizeof(MDTypeKind); - skipMetadataJsiTypePayload(metadata, offset, kind); -} - -void skipMetadataJsiTypePayload(MDMetadataReader* metadata, MDSectionOffset* offset, - MDTypeKind kind) { - switch (kind) { - case metagen::mdTypeClassObject: { - auto classOffset = metadata->getOffset(*offset); - *offset += sizeof(MDSectionOffset); - bool next = (classOffset & metagen::mdSectionOffsetNext) != 0; - while (next) { - auto protocolOffset = metadata->getOffset(*offset); - *offset += sizeof(MDSectionOffset); - next = (protocolOffset & metagen::mdSectionOffsetNext) != 0; - } - break; - } - case metagen::mdTypeProtocolObject: { - bool next = true; - while (next) { - auto protocolOffset = metadata->getOffset(*offset); - *offset += sizeof(MDSectionOffset); - next = (protocolOffset & metagen::mdSectionOffsetNext) != 0; - } - break; - } - case metagen::mdTypeArray: - case metagen::mdTypeVector: - case metagen::mdTypeExtVector: - case metagen::mdTypeComplex: - *offset += sizeof(uint16_t); - skipMetadataJsiType(metadata, offset); - break; - case metagen::mdTypeStruct: - *offset += sizeof(MDSectionOffset); - break; - case metagen::mdTypePointer: - skipMetadataJsiType(metadata, offset); - break; - case metagen::mdTypeBlock: - case metagen::mdTypeFunctionPointer: - *offset += sizeof(MDSectionOffset); - break; - default: - break; - } -} - -NativeApiJsiType parseMetadataJsiType(MDMetadataReader* metadata, - MDSectionOffset* offset, - NativeApiJsiBridge* bridge) { - MDTypeKind rawKind = metadata->getTypeKind(*offset); - MDTypeKind kind = stripTypeFlags(rawKind); - *offset += sizeof(MDTypeKind); - - NativeApiJsiType type; - type.kind = kind; - - switch (kind) { - case metagen::mdTypeArray: { - type.arraySize = metadata->getArraySize(*offset); - *offset += sizeof(uint16_t); - type.elementType = - std::make_shared( - parseMetadataJsiType(metadata, offset, bridge)); - auto ffiOwner = std::make_shared(); - ffiOwner->elements.reserve(static_cast(type.arraySize) + 1); - ffi_type* elementFfiType = type.elementType->ffiType != nullptr - ? type.elementType->ffiType - : &ffi_type_pointer; - for (uint16_t i = 0; i < type.arraySize; i++) { - ffiOwner->elements.push_back(elementFfiType); - } - ffiOwner->finalize(); - type.ownedFfiType = ffiOwner; - type.ffiType = &ffiOwner->type; - type.supported = type.elementType->supported; - return type; - } - case metagen::mdTypeVector: - case metagen::mdTypeExtVector: - case metagen::mdTypeComplex: { - type.arraySize = metadata->getArraySize(*offset); - *offset += sizeof(uint16_t); - type.elementType = - std::make_shared( - parseMetadataJsiType(metadata, offset, bridge)); - auto ffiOwner = std::make_shared(); -#if defined(FFI_TYPE_EXT_VECTOR) - ffiOwner->type.type = - kind == metagen::mdTypeComplex ? FFI_TYPE_COMPLEX : FFI_TYPE_EXT_VECTOR; -#else - ffiOwner->type.type = - kind == metagen::mdTypeComplex ? FFI_TYPE_COMPLEX : FFI_TYPE_STRUCT; -#endif - ffi_type* elementFfiType = type.elementType->ffiType != nullptr - ? type.elementType->ffiType - : &ffi_type_float; - size_t lanes = std::max(type.arraySize, 1); - size_t abiLanes = lanes == 3 ? 4 : lanes; - size_t elementSize = std::max(elementFfiType->size, sizeof(float)); - size_t elementAlignment = - std::max(elementFfiType->alignment, static_cast(1)); - ffiOwner->elements.reserve(abiLanes + 1); - for (size_t i = 0; i < abiLanes; i++) { - ffiOwner->elements.push_back(elementFfiType); - } - ffiOwner->finalize(); - size_t vectorAlignment = elementAlignment; - if (kind != metagen::mdTypeComplex) { - size_t packedSize = abiLanes * elementSize; - size_t preferredAlignment = packedSize >= 16 ? 16 : packedSize; - vectorAlignment = std::max(vectorAlignment, preferredAlignment); - } - vectorAlignment = std::min(vectorAlignment, 16); - ffiOwner->type.alignment = static_cast(vectorAlignment); - ffiOwner->type.size = alignUp(abiLanes * elementSize, vectorAlignment); - type.ownedFfiType = ffiOwner; - type.ffiType = &ffiOwner->type; - type.supported = type.elementType->supported; - return type; - } - case metagen::mdTypeStruct: { - auto structOffset = metadata->getOffset(*offset); - *offset += sizeof(MDSectionOffset); - bool isUnion = (structOffset & metagen::mdSectionOffsetNext) != 0; - structOffset &= ~metagen::mdSectionOffsetNext; - if (structOffset == MD_SECTION_OFFSET_NULL || bridge == nullptr) { - type.kind = metagen::mdTypePointer; - type.ffiType = &ffi_type_pointer; - type.supported = true; - return type; - } - - MDSectionOffset absoluteOffset = - structOffset + (isUnion ? metadata->unionsOffset : metadata->structsOffset); - type.aggregateOffset = absoluteOffset; - type.aggregateIsUnion = isUnion; - type.aggregateInfo = bridge->aggregateInfoFor(absoluteOffset, isUnion); - type.ffiType = type.aggregateInfo != nullptr && type.aggregateInfo->ffi != nullptr - ? &type.aggregateInfo->ffi->type - : nullptr; - type.supported = type.ffiType != nullptr; - return type; - } - case metagen::mdTypePointer: - type.elementType = - std::make_shared( - parseMetadataJsiType(metadata, offset, bridge)); - type.ffiType = &ffi_type_pointer; - type.supported = true; - return type; - case metagen::mdTypeBlock: - case metagen::mdTypeFunctionPointer: - type.signatureOffset = metadata->getOffset(*offset) + metadata->signaturesOffset; - *offset += sizeof(MDSectionOffset); - type.ffiType = &ffi_type_pointer; - type.supported = true; - return type; - case metagen::mdTypeClassObject: { - auto classOffset = metadata->getOffset(*offset); - *offset += sizeof(MDSectionOffset); - bool next = (classOffset & metagen::mdSectionOffsetNext) != 0; - while (next) { - auto protocolOffset = metadata->getOffset(*offset); - *offset += sizeof(MDSectionOffset); - next = (protocolOffset & metagen::mdSectionOffsetNext) != 0; - } - break; - } - case metagen::mdTypeProtocolObject: { - bool next = true; - while (next) { - auto protocolOffset = metadata->getOffset(*offset); - *offset += sizeof(MDSectionOffset); - next = (protocolOffset & metagen::mdSectionOffsetNext) != 0; - } - break; - } - default: - break; - } - - type.ffiType = ffiTypeForJsiKind(kind); - type.supported = type.ffiType != nullptr && isSupportedJsiKind(kind); - return type; -} - -std::shared_ptr NativeApiJsiBridge::aggregateInfoFor( - MDSectionOffset aggregateOffset, bool isUnion) { - if (metadata_ == nullptr || aggregateOffset == MD_SECTION_OFFSET_NULL) { - return nullptr; - } - - auto cached = aggregateInfoByOffset_.find(aggregateOffset); - if (cached != aggregateInfoByOffset_.end()) { - return cached->second; - } - - auto info = std::make_shared(); - info->offset = aggregateOffset; - info->isUnion = isUnion; - aggregateInfoByOffset_[aggregateOffset] = info; - - if (aggregateInfoInProgress_.find(aggregateOffset) != - aggregateInfoInProgress_.end()) { - auto ffiOwner = std::make_shared(); - ffiOwner->elements.push_back(&ffi_type_pointer); - ffiOwner->finalize(); - info->ffi = ffiOwner; - return info; - } - - aggregateInfoInProgress_.insert(aggregateOffset); - - MDSectionOffset offset = aggregateOffset; - const char* name = metadata_->getString(offset); - info->name = name != nullptr ? name : ""; - offset += sizeof(MDSectionOffset); - info->size = metadata_->getArraySize(offset); - offset += sizeof(uint16_t); - - bool next = true; - while (next) { - MDSectionOffset nameOffset = metadata_->getOffset(offset); - offset += sizeof(MDSectionOffset); - next = (nameOffset & metagen::mdSectionOffsetNext) != 0; - nameOffset &= ~metagen::mdSectionOffsetNext; - if (nameOffset == MD_SECTION_OFFSET_NULL) { - break; - } - - NativeApiJsiAggregateField field; - const char* fieldName = metadata_->resolveString(nameOffset); - field.name = fieldName != nullptr ? fieldName : ""; - if (!isUnion) { - field.offset = metadata_->getArraySize(offset); - offset += sizeof(uint16_t); - } - field.type = parseMetadataJsiType(metadata_.get(), &offset, this); - info->fields.push_back(std::move(field)); - } - - auto ffiOwner = std::make_shared(); - if (isUnion) { - ffi_type* largest = &ffi_type_uint8; - size_t largestSize = 0; - for (const auto& field : info->fields) { - size_t fieldSize = nativeSizeForType(field.type); - if (field.type.ffiType != nullptr && fieldSize >= largestSize) { - largest = field.type.ffiType; - largestSize = fieldSize; - } - } - ffiOwner->elements.push_back(largest); - } else { - for (const auto& field : info->fields) { - ffiOwner->elements.push_back(field.type.ffiType != nullptr - ? field.type.ffiType - : &ffi_type_pointer); - } - if (ffiOwner->elements.empty()) { - ffiOwner->elements.push_back(&ffi_type_uint8); - } - } - ffiOwner->finalize(); - info->ffi = ffiOwner; - aggregateInfoInProgress_.erase(aggregateOffset); - return info; -} - -ffi_type* ffiTypeForJsiArgument(const NativeApiJsiType& type) { - switch (type.kind) { - case metagen::mdTypeArray: - return &ffi_type_pointer; - default: - return type.ffiType != nullptr ? type.ffiType : &ffi_type_pointer; - } -} - -std::optional parseMetadataJsiSignature( - MDMetadataReader* metadata, MDSectionOffset signatureOffset, - unsigned int implicitArgumentCount, NativeApiJsiBridge* bridge, - bool returnOwned = false) { - if (metadata == nullptr || signatureOffset == MD_SECTION_OFFSET_NULL) { - return std::nullopt; - } - - NativeApiJsiSignature signature; - signature.implicitArgumentCount = implicitArgumentCount; - - MDSectionOffset offset = signatureOffset; - MDTypeKind returnKind = metadata->getTypeKind(offset); - uint32_t returnKindRaw = rawTypeKind(returnKind); - bool next = - (returnKindRaw & static_cast(metagen::mdTypeFlagNext)) != 0; - signature.variadic = - (returnKindRaw & static_cast(metagen::mdTypeFlagVariadic)) != 0; - signature.returnType = parseMetadataJsiType(metadata, &offset, bridge); - signature.returnType.returnOwned = returnOwned; - - while (next) { - MDTypeKind argKind = metadata->getTypeKind(offset); - next = (rawTypeKind(argKind) & - static_cast(metagen::mdTypeFlagNext)) != 0; - signature.argumentTypes.push_back(parseMetadataJsiType(metadata, &offset, bridge)); - } - - signature.ffiTypes.reserve(signature.argumentTypes.size() + - implicitArgumentCount); - for (unsigned int i = 0; i < implicitArgumentCount; i++) { - signature.ffiTypes.push_back(&ffi_type_pointer); - } - for (const auto& argType : signature.argumentTypes) { - signature.ffiTypes.push_back(ffiTypeForJsiArgument(argType)); - } - - ffi_status status = ffi_prep_cif( - &signature.cif, FFI_DEFAULT_ABI, - static_cast(signature.ffiTypes.size()), - signature.returnType.ffiType != nullptr ? signature.returnType.ffiType - : &ffi_type_void, - signature.ffiTypes.empty() ? nullptr : signature.ffiTypes.data()); - signature.prepared = status == FFI_OK; - return signature; -} - -const char* skipObjCTypeQualifiers(const char* encoding) { - while (encoding != nullptr && *encoding != '\0' && - std::strchr("rnNoORV", *encoding) != nullptr) { - encoding++; - } - return encoding; -} - -const char* skipObjCTypeFieldName(const char* encoding, std::string* name) { - if (encoding == nullptr || *encoding != '"') { - return encoding; - } - - encoding++; - const char* start = encoding; - while (*encoding != '\0' && *encoding != '"') { - encoding++; - } - if (name != nullptr) { - *name = std::string(start, static_cast(encoding - start)); - } - return *encoding == '"' ? encoding + 1 : encoding; -} - -std::string normalizedObjCAggregateName(std::string name) { - if (!name.empty() && name.front() == '_') { - name.erase(name.begin()); - } - return name; -} - -std::vector knownObjCAggregateFieldNames( - const std::string& aggregateName, size_t fieldCount) { - std::string name = normalizedObjCAggregateName(aggregateName); - std::vector fields; - if (name == "CGPoint" || name == "NSPoint") { - fields = {"x", "y"}; - } else if (name == "CGSize" || name == "NSSize") { - fields = {"width", "height"}; - } else if (name == "CGRect" || name == "NSRect") { - fields = {"origin", "size"}; - } else if (name == "CGVector") { - fields = {"dx", "dy"}; - } else if (name == "UIEdgeInsets" || name == "NSEdgeInsets") { - fields = {"top", "left", "bottom", "right"}; - } else if (name == "NSDirectionalEdgeInsets") { - fields = {"top", "leading", "bottom", "trailing"}; - } else if (name == "NSRange" || name == "CFRange") { - fields = {"location", "length"}; - } else if (name == "CGAffineTransform") { - fields = {"a", "b", "c", "d", "tx", "ty"}; - } else if (name == "CATransform3D") { - fields = {"m11", "m12", "m13", "m14", "m21", "m22", "m23", "m24", - "m31", "m32", "m33", "m34", "m41", "m42", "m43", "m44"}; - } - - if (fields.size() != fieldCount) { - fields.clear(); - } - return fields; -} - -const NativeApiSymbol* findObjCAggregateSymbol( - NativeApiJsiBridge* bridge, const std::string& name, bool isUnion) { - if (bridge == nullptr || name.empty()) { - return nullptr; - } - - std::vector candidates; - candidates.push_back(name); - std::string normalized = normalizedObjCAggregateName(name); - if (normalized != name) { - candidates.push_back(normalized); - } else { - candidates.push_back("_" + name); - } - constexpr const char* suffix = "Struct"; - if (normalized.size() > std::strlen(suffix) && - normalized.compare(normalized.size() - std::strlen(suffix), - std::strlen(suffix), suffix) == 0) { - candidates.push_back( - normalized.substr(0, normalized.size() - std::strlen(suffix))); - } else { - candidates.push_back(normalized + suffix); - } - - for (const auto& candidate : candidates) { - const NativeApiSymbol* symbol = - isUnion ? bridge->findUnion(candidate) : bridge->findStruct(candidate); - if (symbol == nullptr) { - symbol = bridge->findAggregate(candidate); - } - if (symbol != nullptr) { - return symbol; - } - } - - return nullptr; -} - -void applyObjCEncodingSizeAndAlignment(const char* encoding, - NativeApiJsiFfiType* ffiType, - uint16_t* sizeOut = nullptr) { - if (encoding == nullptr || ffiType == nullptr) { - return; - } - - NSUInteger size = 0; - NSUInteger alignment = 0; - NSGetSizeAndAlignment(encoding, &size, &alignment); - if (size > 0) { - ffiType->type.size = static_cast(size); - if (sizeOut != nullptr) { - *sizeOut = static_cast(std::min( - size, static_cast(std::numeric_limits::max()))); - } - } - if (alignment > 0) { - ffiType->type.alignment = static_cast(alignment); - } -} - -NativeApiJsiType parseObjCEncodedJsiType( - const char* encoding, NativeApiJsiBridge* bridge = nullptr, - const char** endEncoding = nullptr); - -bool unsupportedJsiType(const NativeApiJsiType& type); - -NativeApiJsiType parseObjCEncodedAggregateJsiType( - const char* encoding, NativeApiJsiBridge* bridge, const char** endEncoding) { - NativeApiJsiType type; - type.kind = metagen::mdTypeStruct; - - const bool isUnion = *encoding == '('; - const char close = isUnion ? ')' : '}'; - const char* cursor = encoding + 1; - const char* nameStart = cursor; - while (*cursor != '\0' && *cursor != '=' && *cursor != close) { - cursor++; - } - std::string aggregateName(nameStart, static_cast(cursor - nameStart)); - - if (const NativeApiSymbol* symbol = - findObjCAggregateSymbol(bridge, aggregateName, isUnion)) { - type.aggregateOffset = symbol->offset; - type.aggregateIsUnion = symbol->kind == NativeApiSymbolKind::Union; - type.aggregateInfo = bridge->aggregateInfoFor(*symbol); - type.ffiType = type.aggregateInfo != nullptr && type.aggregateInfo->ffi != nullptr - ? &type.aggregateInfo->ffi->type - : nullptr; - type.supported = type.ffiType != nullptr; - - int depth = 0; - const char* end = encoding; - do { - if (*end == *encoding) { - depth++; - } else if (*end == close) { - depth--; - } - end++; - } while (*end != '\0' && depth > 0); - if (endEncoding != nullptr) { - *endEncoding = end; - } - return type; - } - - auto info = std::make_shared(); - info->name = aggregateName; - info->isUnion = isUnion; - info->offset = MD_SECTION_OFFSET_NULL; - - if (*cursor == '=') { - cursor++; - } - - size_t computedOffset = 0; - size_t maxFieldSize = 0; - size_t fieldIndex = 0; - while (*cursor != '\0' && *cursor != close) { - NativeApiJsiAggregateField field; - std::string encodedFieldName; - cursor = skipObjCTypeFieldName(cursor, &encodedFieldName); - const char* fieldStart = cursor; - const char* fieldEnd = cursor; - field.type = parseObjCEncodedJsiType(cursor, bridge, &fieldEnd); - if (fieldEnd == fieldStart || unsupportedJsiType(field.type)) { - type.supported = false; - type.ffiType = nullptr; - if (endEncoding != nullptr) { - *endEncoding = fieldEnd; - } - return type; - } - - NSUInteger fieldSize = 0; - NSUInteger fieldAlignment = 0; - NSGetSizeAndAlignment(fieldStart, &fieldSize, &fieldAlignment); - size_t nativeFieldSize = - fieldSize > 0 ? static_cast(fieldSize) - : nativeSizeForType(field.type); - size_t nativeFieldAlignment = - fieldAlignment > 0 ? static_cast(fieldAlignment) - : std::max(1, field.type.ffiType != nullptr - ? field.type.ffiType->alignment - : 1); - if (isUnion) { - field.offset = 0; - maxFieldSize = std::max(maxFieldSize, nativeFieldSize); - } else { - computedOffset = alignUp(computedOffset, nativeFieldAlignment); - field.offset = static_cast(std::min( - computedOffset, std::numeric_limits::max())); - computedOffset += nativeFieldSize; - } - field.name = !encodedFieldName.empty() - ? encodedFieldName - : "field" + std::to_string(fieldIndex); - info->fields.push_back(std::move(field)); - fieldIndex++; - cursor = fieldEnd; - } - - if (*cursor == close) { - cursor++; - } - if (endEncoding != nullptr) { - *endEncoding = cursor; - } - - auto knownNames = knownObjCAggregateFieldNames(aggregateName, info->fields.size()); - for (size_t i = 0; i < knownNames.size(); i++) { - info->fields[i].name = knownNames[i]; - } - - auto ffiOwner = std::make_shared(); - if (isUnion) { - ffi_type* largest = &ffi_type_uint8; - size_t largestSize = 0; - for (const auto& field : info->fields) { - size_t fieldSize = nativeSizeForType(field.type); - if (field.type.ffiType != nullptr && fieldSize >= largestSize) { - largest = field.type.ffiType; - largestSize = fieldSize; - } - } - ffiOwner->elements.push_back(largest); - } else { - for (const auto& field : info->fields) { - ffiOwner->elements.push_back(field.type.ffiType != nullptr - ? field.type.ffiType - : &ffi_type_pointer); - } - } - if (ffiOwner->elements.empty()) { - ffiOwner->elements.push_back(&ffi_type_uint8); - } - ffiOwner->finalize(); - applyObjCEncodingSizeAndAlignment(encoding, ffiOwner.get(), &info->size); - if (info->size == 0) { - info->size = static_cast(std::min( - isUnion ? maxFieldSize : computedOffset, - std::numeric_limits::max())); - } - - info->ffi = ffiOwner; - type.aggregateInfo = info; - type.aggregateOffset = MD_SECTION_OFFSET_NULL; - type.aggregateIsUnion = isUnion; - type.ownedFfiType = ffiOwner; - type.ffiType = &ffiOwner->type; - type.supported = true; - return type; -} - -NativeApiJsiType parseObjCEncodedArrayJsiType( - const char* encoding, NativeApiJsiBridge* bridge, const char** endEncoding) { - NativeApiJsiType type; - type.kind = metagen::mdTypeArray; - - const char* cursor = encoding + 1; - uint16_t count = 0; - while (*cursor >= '0' && *cursor <= '9') { - count = static_cast( - std::min(std::numeric_limits::max(), - (count * 10) + (*cursor - '0'))); - cursor++; - } - type.arraySize = count; - - const char* elementEnd = cursor; - type.elementType = std::make_shared( - parseObjCEncodedJsiType(cursor, bridge, &elementEnd)); - cursor = elementEnd; - if (*cursor == ']') { - cursor++; - } - if (endEncoding != nullptr) { - *endEncoding = cursor; - } - - auto ffiOwner = std::make_shared(); - ffi_type* elementFfiType = - type.elementType != nullptr && type.elementType->ffiType != nullptr - ? type.elementType->ffiType - : &ffi_type_pointer; - for (uint16_t i = 0; i < count; i++) { - ffiOwner->elements.push_back(elementFfiType); - } - if (ffiOwner->elements.empty()) { - ffiOwner->elements.push_back(&ffi_type_uint8); - } - ffiOwner->finalize(); - applyObjCEncodingSizeAndAlignment(encoding, ffiOwner.get()); - - type.ownedFfiType = ffiOwner; - type.ffiType = &ffiOwner->type; - type.supported = type.elementType != nullptr && type.elementType->supported; - return type; -} - -NativeApiJsiType parseObjCEncodedJsiType( - const char* encoding, NativeApiJsiBridge* bridge, const char** endEncoding) { - encoding = skipObjCTypeQualifiers(encoding); - NativeApiJsiType type; - - if (encoding == nullptr || *encoding == '\0') { - type.kind = metagen::mdTypePointer; - type.ffiType = &ffi_type_pointer; - if (endEncoding != nullptr) { - *endEncoding = encoding; - } - return type; - } - - auto finishPrimitive = [&](const char* end) { - type.ffiType = ffiTypeForJsiKind(type.kind); - type.supported = type.ffiType != nullptr; - if (endEncoding != nullptr) { - *endEncoding = end; - } - return type; - }; - - switch (*encoding) { - case 'c': - type.kind = metagen::mdTypeChar; - break; - case 'i': - type.kind = metagen::mdTypeSInt; - break; - case 's': - type.kind = metagen::mdTypeSShort; - break; - case 'l': - case 'q': - type.kind = metagen::mdTypeSInt64; - break; - case 'C': - type.kind = metagen::mdTypeUInt8; - break; - case 'I': - type.kind = metagen::mdTypeUInt; - break; - case 'S': - type.kind = metagen::mdTypeUShort; - break; - case 'L': - case 'Q': - type.kind = metagen::mdTypeUInt64; - break; - case 'f': - type.kind = metagen::mdTypeFloat; - break; - case 'd': - type.kind = metagen::mdTypeDouble; - break; - case 'B': - type.kind = metagen::mdTypeBool; - break; - case 'v': - type.kind = metagen::mdTypeVoid; - break; - case '*': - type.kind = metagen::mdTypeString; - break; - case '@': - if (encoding[1] == '?') { - type.kind = metagen::mdTypeBlock; - return finishPrimitive(encoding + 2); - } - { - const char* objectEnd = encoding + 1; - if (*objectEnd == '"') { - objectEnd++; - while (*objectEnd != '\0' && *objectEnd != '"') { - objectEnd++; - } - if (*objectEnd == '"') { - objectEnd++; - } - } - if (std::strncmp(encoding, "@\"NSString\"", 11) == 0) { - type.kind = metagen::mdTypeNSStringObject; - } else if (std::strncmp(encoding, "@\"NSMutableString\"", 18) == 0) { - type.kind = metagen::mdTypeNSMutableStringObject; - } else { - type.kind = metagen::mdTypeAnyObject; - } - return finishPrimitive(objectEnd); - } - case '#': - type.kind = metagen::mdTypeClass; - break; - case ':': - type.kind = metagen::mdTypeSelector; - break; - case '^': - type.kind = metagen::mdTypePointer; - { - const char* elementEnd = encoding + 1; - type.elementType = std::make_shared( - parseObjCEncodedJsiType(encoding + 1, bridge, &elementEnd)); - type.ffiType = &ffi_type_pointer; - type.supported = true; - if (elementEnd == encoding + 1 && encoding[1] != '\0') { - elementEnd = encoding + 2; - } - if (endEncoding != nullptr) { - *endEncoding = elementEnd; - } - } - return type; - case '{': - case '(': - return parseObjCEncodedAggregateJsiType(encoding, bridge, endEncoding); - case '[': - return parseObjCEncodedArrayJsiType(encoding, bridge, endEncoding); - case 'b': { - type.kind = metagen::mdTypeUInt; - const char* cursor = encoding + 1; - while (*cursor >= '0' && *cursor <= '9') { - cursor++; - } - return finishPrimitive(cursor); - } - case '?': - type.kind = metagen::mdTypeOpaquePointer; - break; - default: - type.kind = metagen::mdTypePointer; - break; - } - - return finishPrimitive(encoding + 1); -} - -std::optional parseObjCMethodJsiSignature( - Method method, NativeApiJsiBridge* bridge = nullptr) { - if (method == nullptr) { - return std::nullopt; - } - - NativeApiJsiSignature signature; - signature.implicitArgumentCount = 2; - - char* returnEncoding = method_copyReturnType(method); - signature.returnType = parseObjCEncodedJsiType(returnEncoding, bridge); - if (returnEncoding != nullptr) { - free(returnEncoding); - } - - unsigned int totalArgc = method_getNumberOfArguments(method); - for (unsigned int i = 2; i < totalArgc; i++) { - char* argEncoding = method_copyArgumentType(method, i); - signature.argumentTypes.push_back(parseObjCEncodedJsiType(argEncoding, bridge)); - if (argEncoding != nullptr) { - free(argEncoding); - } - } - - signature.ffiTypes.reserve(totalArgc); - signature.ffiTypes.push_back(&ffi_type_pointer); - signature.ffiTypes.push_back(&ffi_type_pointer); - for (const auto& argType : signature.argumentTypes) { - signature.ffiTypes.push_back(ffiTypeForJsiArgument(argType)); - } - - ffi_status status = ffi_prep_cif( - &signature.cif, FFI_DEFAULT_ABI, - static_cast(signature.ffiTypes.size()), - signature.returnType.ffiType != nullptr ? signature.returnType.ffiType - : &ffi_type_void, - signature.ffiTypes.data()); - signature.prepared = status == FFI_OK; - return signature; -} - -bool prepareJsiMethodSignature(NativeApiJsiSignature* signature) { - if (signature == nullptr) { - return false; - } - signature->implicitArgumentCount = 2; - signature->ffiTypes.clear(); - signature->ffiTypes.reserve(signature->argumentTypes.size() + 2); - signature->ffiTypes.push_back(&ffi_type_pointer); - signature->ffiTypes.push_back(&ffi_type_pointer); - for (const auto& argType : signature->argumentTypes) { - ffi_type* ffiType = ffiTypeForJsiArgument(argType); - if (ffiType == nullptr) { - signature->prepared = false; - return false; - } - signature->ffiTypes.push_back(ffiType); - } - ffi_type* returnFfiType = - signature->returnType.ffiType != nullptr ? signature->returnType.ffiType - : &ffi_type_void; - signature->prepared = - ffi_prep_cif(&signature->cif, FFI_DEFAULT_ABI, - static_cast(signature->ffiTypes.size()), - returnFfiType, signature->ffiTypes.data()) == FFI_OK; - return signature->prepared; -} - -bool reconcileObjCMethodRuntimeSignature(NativeApiJsiSignature* signature, - const NativeApiJsiSignature& runtime) { - if (signature == nullptr || - signature->argumentTypes.size() != runtime.argumentTypes.size()) { - return false; - } - - bool changed = false; - for (size_t i = 0; i < signature->argumentTypes.size(); i++) { - NativeApiJsiType& metadataType = signature->argumentTypes[i]; - const NativeApiJsiType& runtimeType = runtime.argumentTypes[i]; - if (runtimeType.kind == metagen::mdTypeBlock && - metadataType.kind == metagen::mdTypeFunctionPointer) { - metadataType.kind = metagen::mdTypeBlock; - metadataType.ffiType = runtimeType.ffiType; - metadataType.supported = runtimeType.supported; - changed = true; - } - } - - return !changed || prepareJsiMethodSignature(signature); -} - -bool unsupportedJsiType(const NativeApiJsiType& type) { - if (type.kind == metagen::mdTypeStruct && type.aggregateInfo != nullptr && - type.aggregateInfo->ffi != nullptr) { - return false; - } - return !type.supported || type.ffiType == nullptr; -} - -bool signatureSupportedForJsiCallback(const NativeApiJsiSignature& signature) { - if (!signature.prepared || signature.variadic || - unsupportedJsiType(signature.returnType)) { - return false; - } - for (const auto& argType : signature.argumentTypes) { - if (unsupportedJsiType(argType)) { - return false; - } - } - return true; -} - -std::shared_ptr createJsiCallback( - Runtime& runtime, const std::shared_ptr& bridge, - const NativeApiJsiType& type, Function function, bool block, - NativeApiJsiCallbackThreadPolicy threadPolicy = - NativeApiJsiCallbackThreadPolicy::Default) { - if (bridge == nullptr || bridge->metadata() == nullptr || - type.signatureOffset == MD_SECTION_OFFSET_NULL) { - throw facebook::jsi::JSError( - runtime, "Native callback metadata is unavailable."); - } - - auto parsed = parseMetadataJsiSignature( - bridge->metadata(), type.signatureOffset, block ? 1 : 0, bridge.get()); - if (!parsed || !signatureSupportedForJsiCallback(*parsed)) { - throw facebook::jsi::JSError( - runtime, "Native callback signature is not supported by pure JSI."); - } - - auto signature = - std::make_shared(std::move(*parsed)); - auto callback = std::make_shared( - runtime, bridge, std::move(signature), std::move(function), block, - threadPolicy); - if (!block) { - bridge->retainJsiLifetime(callback); - } - return callback; -} - -std::shared_ptr createJsiMethodCallback( - Runtime& runtime, const std::shared_ptr& bridge, - const std::string& selectorName, MDSectionOffset signatureOffset, - Function function, bool returnOwned) { - if (bridge == nullptr || bridge->metadata() == nullptr || - signatureOffset == MD_SECTION_OFFSET_NULL) { - throw facebook::jsi::JSError( - runtime, "Native method callback metadata is unavailable."); - } - - auto parsed = parseMetadataJsiSignature( - bridge->metadata(), signatureOffset, 2, bridge.get(), returnOwned); - if (!parsed || !signatureSupportedForJsiCallback(*parsed)) { - throw facebook::jsi::JSError( - runtime, "Native method callback signature is not supported by pure JSI."); - } - parsed->selectorName = selectorName; - - auto signature = - std::make_shared(std::move(*parsed)); - auto threadPolicy = readJsiCallbackThreadPolicy(runtime, function); - auto callback = std::make_shared( - runtime, bridge, std::move(signature), std::move(function), false, - threadPolicy, true); - bridge->retainJsiLifetime(callback); - return callback; -} - -std::shared_ptr createJsiMethodCallback( - Runtime& runtime, const std::shared_ptr& bridge, - const std::string& selectorName, NativeApiJsiSignature signature, - Function function) { - signature.selectorName = selectorName; - prepareJsiMethodSignature(&signature); - if (!signatureSupportedForJsiCallback(signature)) { - throw facebook::jsi::JSError( - runtime, "Native method callback signature is not supported by pure JSI."); - } - - auto sharedSignature = - std::make_shared(std::move(signature)); - auto threadPolicy = readJsiCallbackThreadPolicy(runtime, function); - auto callback = std::make_shared( - runtime, bridge, std::move(sharedSignature), std::move(function), false, - threadPolicy, true); - bridge->retainJsiLifetime(callback); - return callback; -} diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiClassBuilder.h b/NativeScript/ffi/shared/jsi/NativeApiJsiClassBuilder.h deleted file mode 100644 index bf4d3b2fc..000000000 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiClassBuilder.h +++ /dev/null @@ -1,789 +0,0 @@ -std::string readOptionalStringProperty(Runtime& runtime, const Object& object, - const char* name) { - if (name == nullptr || !object.hasProperty(runtime, name)) { - return ""; - } - Value value = object.getProperty(runtime, name); - return value.isString() ? value.asString(runtime).utf8(runtime) : ""; -} - -struct NativeApiJsiClassBuilderRegistration { - std::shared_ptr runtimeOwner; - Runtime* runtime = nullptr; - std::shared_ptr bridge; -}; - -std::mutex gNativeApiJsiClassBuilderMutex; -std::unordered_map - gNativeApiJsiClassBuilders; -struct NativeApiJsiKnownExposedMethod { - std::string selectorName; - NativeApiJsiSignature signature; -}; -std::mutex gNativeApiJsiKnownExposedMethodsMutex; -std::unordered_map - gNativeApiJsiKnownExposedMethods; - -void rememberNativeApiJsiClassBuilder( - Runtime& runtime, const std::shared_ptr& bridge, - Class cls) { - if (cls == Nil) { - return; - } - std::lock_guard lock(gNativeApiJsiClassBuilderMutex); - auto runtimeOwner = retainNativeApiJsiRuntime(runtime); - gNativeApiJsiClassBuilders[cls] = NativeApiJsiClassBuilderRegistration{ - .runtimeOwner = runtimeOwner, - .runtime = runtimeOwner.get(), - .bridge = bridge, - }; -} - -void rememberNativeApiJsiKnownExposedMethod( - const std::string& selectorName, const NativeApiJsiSignature& signature) { - if (selectorName.empty()) { - return; - } - NativeApiJsiKnownExposedMethod method{ - .selectorName = selectorName, - .signature = signature, - }; - std::lock_guard lock(gNativeApiJsiKnownExposedMethodsMutex); - gNativeApiJsiKnownExposedMethods[selectorName] = method; - gNativeApiJsiKnownExposedMethods[jsifySelector(selectorName.c_str())] = - std::move(method); -} - -std::optional knownNativeApiJsiExposedMethod( - const std::string& name) { - std::lock_guard lock(gNativeApiJsiKnownExposedMethodsMutex); - auto it = gNativeApiJsiKnownExposedMethods.find(name); - if (it == gNativeApiJsiKnownExposedMethods.end()) { - return std::nullopt; - } - NativeApiJsiKnownExposedMethod method = it->second; - prepareJsiMethodSignature(&method.signature); - return method; -} - -std::optional -findNativeApiJsiClassBuilder(id object) { - Class cls = object != nil ? object_getClass(object) : Nil; - std::lock_guard lock(gNativeApiJsiClassBuilderMutex); - while (cls != Nil) { - auto it = gNativeApiJsiClassBuilders.find(cls); - if (it != gNativeApiJsiClassBuilders.end()) { - return it->second; - } - cls = class_getSuperclass(cls); - } - return std::nullopt; -} - -const char* nativeApiJsiFastEnumerationEncoding() { - 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; -} - -NSUInteger nativeApiJsiSymbolIteratorCountByEnumerating( - id self, SEL, NSFastEnumerationState* state, - id __unsafe_unretained stackbuf[], NSUInteger len) { - if (len == 0 || state == nullptr || stackbuf == nullptr) { - return 0; - } - - auto registration = findNativeApiJsiClassBuilder(self); - if (!registration || registration->runtime == nullptr || - registration->bridge == nullptr) { - return 0; - } - - Runtime& runtime = *registration->runtime; - NativeApiJsiRuntimeScope runtimeScope(runtime); - auto bridge = registration->bridge; - try { - Value receiver = makeNativeObjectValue(runtime, bridge, self, false); - if (!receiver.isObject()) { - return 0; - } - - Value iteratorFactoryValue = - runtime.global().getProperty(runtime, - "__nativeScriptCreateNativeApiIterator"); - if (!iteratorFactoryValue.isObject() || - !iteratorFactoryValue.asObject(runtime).isFunction(runtime)) { - return 0; - } - - Function iteratorFactory = - iteratorFactoryValue.asObject(runtime).asFunction(runtime); - Value prototype = - bridge->findClassPrototype(runtime, object_getClass(self)); - Value iteratorValue = - prototype.isObject() - ? iteratorFactory.call(runtime, Value(runtime, receiver), - Value(runtime, prototype)) - : iteratorFactory.call(runtime, Value(runtime, receiver)); - if (!iteratorValue.isObject()) { - return 0; - } - Object iterator = iteratorValue.asObject(runtime); - Value nextValue = iterator.getProperty(runtime, "next"); - if (!nextValue.isObject() || - !nextValue.asObject(runtime).isFunction(runtime)) { - return 0; - } - Function next = nextValue.asObject(runtime).asFunction(runtime); - - auto callNext = [&]() -> Value { - return next.callWithThis(runtime, iterator); - }; - - for (unsigned long skipped = 0; skipped < state->state; skipped++) { - Value skippedResult = callNext(); - if (!skippedResult.isObject()) { - return 0; - } - Value doneValue = - skippedResult.asObject(runtime).getProperty(runtime, "done"); - if (doneValue.isBool() && doneValue.getBool()) { - return 0; - } - } - - NSUInteger count = 0; - while (count < len) { - Value nextResult = callNext(); - if (!nextResult.isObject()) { - break; - } - Object nextObject = nextResult.asObject(runtime); - Value doneValue = nextObject.getProperty(runtime, "done"); - if (doneValue.isBool() && doneValue.getBool()) { - break; - } - - Value value = nextObject.getProperty(runtime, "value"); - NativeApiJsiArgumentFrame frame(1); - id nativeValue = objectFromJsiValue(runtime, bridge, value, frame, false); - if (nativeValue != nil) { - [nativeValue retain]; - [nativeValue autorelease]; - } - stackbuf[count++] = nativeValue; - } - - state->itemsPtr = stackbuf; - state->mutationsPtr = &state->extra[0]; - state->extra[0] = 0; - state->state += count; - return count; - } catch (const std::exception&) { - return 0; - } -} - -NativeApiSymbol runtimeSymbolForClass( - const std::shared_ptr& bridge, Class cls) { - if (bridge != nullptr) { - if (const NativeApiSymbol* symbol = bridge->findClassForRuntimeClass(cls)) { - return *symbol; - } - } - - const char* name = cls != Nil ? class_getName(cls) : ""; - return NativeApiSymbol{ - .kind = NativeApiSymbolKind::Class, - .offset = MD_SECTION_OFFSET_NULL, - .name = name != nullptr ? name : "", - .runtimeName = name != nullptr ? name : "", - }; -} - -std::string nextAvailableJsiClassName(const std::string& requestedName) { - if (requestedName.empty()) { - return ""; - } - if (objc_lookUpClass(requestedName.c_str()) == Nil) { - return requestedName; - } - - size_t suffix = 1; - std::string candidate; - do { - candidate = requestedName + std::to_string(suffix++); - } while (objc_lookUpClass(candidate.c_str()) != Nil); - return candidate; -} - -std::vector methodOverridesForName( - const std::vector& members, const std::string& name) { - std::vector result; - std::unordered_set selectors; - for (const auto& member : members) { - if (member.property || member.name != name || - (member.flags & metagen::mdMemberStatic) != 0 || - member.selectorName.empty()) { - continue; - } - if (selectors.insert(member.selectorName).second) { - result.push_back(member); - } - } - return result; -} - -const NativeApiMember* propertyOverrideForName( - const std::vector& members, const std::string& name) { - const NativeApiMember* fallback = nullptr; - for (const auto& member : members) { - if (member.property && member.name == name && - (member.flags & metagen::mdMemberStatic) == 0) { - if (fallback == nullptr) { - fallback = &member; - } - if (!member.readonly && !member.setterSelectorName.empty()) { - return &member; - } - } - } - return fallback; -} - -void addJsiOverrideMethod(Runtime& runtime, - const std::shared_ptr& bridge, - Class nativeClass, Class baseClass, - const std::string& selectorName, - MDSectionOffset signatureOffset, - bool returnOwned, Function function) { - if (selectorName.empty() || signatureOffset == MD_SECTION_OFFSET_NULL) { - return; - } - - auto callback = createJsiMethodCallback(runtime, bridge, selectorName, - signatureOffset, std::move(function), - returnOwned); - SEL selector = sel_registerName(selectorName.c_str()); - std::string metadataEncoding = - objcMethodSignatureForJsiSignature(callback->signature()); - class_replaceMethod(nativeClass, selector, - reinterpret_cast(callback->functionPointer()), - metadataEncoding.c_str()); -} - -Value getObjectPropertyOrUndefined(Runtime& runtime, const Object& object, - const std::string& name) { - return object.hasProperty(runtime, name.c_str()) - ? object.getProperty(runtime, name.c_str()) - : Value::undefined(); -} - -Class dispatchSuperclassForJsiDerivedReceiver(id receiver, Class fallback) { - if (receiver == nil) { - return Nil; - } - - Class receiverClass = object_getClass(receiver); - if (receiverClass == Nil || - !class_conformsToProtocol(receiverClass, - @protocol(NativeApiJsiClassBuilderProtocol))) { - return Nil; - } - - Class superclass = class_getSuperclass(receiverClass); - return superclass != Nil ? superclass : fallback; -} - -std::optional functionForSelector(Runtime& runtime, - const Object& methods, - const std::string& selectorName) { - Value value = getObjectPropertyOrUndefined(runtime, methods, selectorName); - if (!value.isObject() || !value.asObject(runtime).isFunction(runtime)) { - std::string jsName = jsifySelector(selectorName.c_str()); - if (jsName != selectorName) { - value = getObjectPropertyOrUndefined(runtime, methods, jsName); - } - } - if (!value.isObject() || !value.asObject(runtime).isFunction(runtime)) { - return std::nullopt; - } - return value.asObject(runtime).asFunction(runtime); -} - -std::optional readExposedType( - Runtime& runtime, const std::shared_ptr& bridge, - const Object& descriptor, const char* propertyName) { - if (!descriptor.hasProperty(runtime, propertyName)) { - return std::nullopt; - } - return interopTypeFromValue(runtime, bridge, - descriptor.getProperty(runtime, propertyName)); -} - -std::optional exposedMethodSignature( - Runtime& runtime, const std::shared_ptr& bridge, - const std::string& selectorName, const Object& descriptor) { - NativeApiJsiSignature signature; - if (auto returnType = readExposedType(runtime, bridge, descriptor, "returns")) { - signature.returnType = *returnType; - } else { - signature.returnType = primitiveInteropType(metagen::mdTypeVoid); - } - - Value paramsValue = getObjectPropertyOrUndefined(runtime, descriptor, "params"); - if (!paramsValue.isUndefined() && !paramsValue.isNull()) { - if (!paramsValue.isObject() || !paramsValue.asObject(runtime).isArray(runtime)) { - throw facebook::jsi::JSError( - runtime, "exposedMethods params must be an array."); - } - Array params = paramsValue.asObject(runtime).getArray(runtime); - for (size_t i = 0; i < params.size(runtime); i++) { - Value typeValue = params.getValueAtIndex(runtime, i); - auto type = interopTypeFromValue(runtime, bridge, typeValue); - if (!type) { - throw facebook::jsi::JSError( - runtime, "exposedMethods contains an unsupported parameter type."); - } - signature.argumentTypes.push_back(*type); - } - } - - if (selectorArgumentCount(selectorName) != signature.argumentTypes.size()) { - throw facebook::jsi::JSError( - runtime, "exposedMethods selector argument count does not match params."); - } - - prepareJsiMethodSignature(&signature); - return signature; -} - -std::optional runtimeProtocolMethodSignature( - const char* types) { - if (types == nullptr) { - return std::nullopt; - } - - NSMethodSignature* methodSignature = - [NSMethodSignature signatureWithObjCTypes:types]; - if (methodSignature == nil || methodSignature.numberOfArguments < 2) { - return std::nullopt; - } - - NativeApiJsiSignature signature; - signature.implicitArgumentCount = 2; - signature.returnType = - parseObjCEncodedJsiType(methodSignature.methodReturnType); - for (NSUInteger i = 2; i < methodSignature.numberOfArguments; i++) { - signature.argumentTypes.push_back( - parseObjCEncodedJsiType([methodSignature getArgumentTypeAtIndex:i])); - } - if (unsupportedJsiType(signature.returnType)) { - return std::nullopt; - } - for (const auto& argumentType : signature.argumentTypes) { - if (unsupportedJsiType(argumentType)) { - return std::nullopt; - } - } - return signature; -} - -std::optional protocolSymbolFromJsiValue( - Runtime& runtime, const std::shared_ptr& bridge, - const Value& value) { - if (value.isString()) { - std::string name = value.asString(runtime).utf8(runtime); - if (const NativeApiSymbol* symbol = bridge->findProtocol(name)) { - return *symbol; - } - return std::nullopt; - } - if (!value.isObject()) { - return std::nullopt; - } - - Object object = value.asObject(runtime); - if (object.isHostObject(runtime)) { - return object.getHostObject(runtime)->symbol(); - } - - if (stringPropertyOrEmpty(runtime, object, "kind") != "protocol") { - return std::nullopt; - } - - std::string runtimeName = stringPropertyOrEmpty(runtime, object, "runtimeName"); - if (!runtimeName.empty()) { - if (const NativeApiSymbol* symbol = bridge->findProtocol(runtimeName)) { - return *symbol; - } - } - - std::string name = stringPropertyOrEmpty(runtime, object, "name"); - if (!name.empty()) { - if (const NativeApiSymbol* symbol = bridge->findProtocol(name)) { - return *symbol; - } - } - - return std::nullopt; -} - -void addJsiExposedMethod(Runtime& runtime, - const std::shared_ptr& bridge, - Class nativeClass, const std::string& selectorName, - NativeApiJsiSignature signature, Function function) { - if (selectorName.empty()) { - return; - } - auto callback = createJsiMethodCallback(runtime, bridge, selectorName, - std::move(signature), std::move(function)); - std::string encoding = objcMethodSignatureForJsiSignature(callback->signature()); - class_replaceMethod(nativeClass, sel_registerName(selectorName.c_str()), - reinterpret_cast(callback->functionPointer()), - encoding.c_str()); -} - -bool addRuntimeProtocolOverrideForName( - Runtime& runtime, const std::shared_ptr& bridge, - Class nativeClass, const std::vector& protocols, - const std::string& propertyName, Function function) { - std::unordered_set visited; - std::function visit = [&](Protocol* protocol) -> bool { - if (protocol == nullptr || !visited.insert(protocol).second) { - return false; - } - - Protocol** inherited = protocol_copyProtocolList(protocol, nullptr); - if (inherited != nullptr) { - unsigned int inheritedCount = 0; - free(inherited); - inherited = protocol_copyProtocolList(protocol, &inheritedCount); - for (unsigned int i = 0; i < inheritedCount; i++) { - if (visit(inherited[i])) { - free(inherited); - return true; - } - } - free(inherited); - } - - for (BOOL required : {YES, NO}) { - unsigned int count = 0; - objc_method_description* descriptions = - protocol_copyMethodDescriptionList(protocol, required, YES, &count); - for (unsigned int i = 0; i < count; i++) { - SEL selector = descriptions[i].name; - const char* selectorName = - selector != nullptr ? sel_getName(selector) : nullptr; - if (selectorName == nullptr || - jsifySelector(selectorName) != propertyName) { - continue; - } - auto signature = runtimeProtocolMethodSignature(descriptions[i].types); - if (signature) { - addJsiExposedMethod(runtime, bridge, nativeClass, selectorName, - std::move(*signature), std::move(function)); - free(descriptions); - return true; - } - } - free(descriptions); - } - return false; - }; - - for (Protocol* protocol : protocols) { - if (visit(protocol)) { - return true; - } - } - return false; -} - -Object getOwnPropertyDescriptor(Runtime& runtime, const Object& object, - const std::string& name) { - Object objectCtor = runtime.global().getPropertyAsObject(runtime, "Object"); - Function getOwnPropertyDescriptor = - objectCtor.getPropertyAsFunction(runtime, "getOwnPropertyDescriptor"); - Value args[] = {Value(runtime, object), makeString(runtime, name)}; - Value descriptorValue = - getOwnPropertyDescriptor.call(runtime, static_cast(args), - static_cast(2)); - return descriptorValue.isObject() ? descriptorValue.asObject(runtime) - : Object(runtime); -} - -Value extendNativeApiJsiClass( - Runtime& runtime, const std::shared_ptr& bridge, - const Value* args, size_t count) { - if (count < 2 || !args[0].isObject() || !args[1].isObject()) { - throw facebook::jsi::JSError( - runtime, "extendClass expects a native class and method object."); - } - - Class baseClass = classFromJsiValue(runtime, args[0]); - if (baseClass == Nil) { - throw facebook::jsi::JSError( - runtime, "extendClass can only extend native class constructors."); - } - if (class_conformsToProtocol(baseClass, - @protocol(NativeApiJsiClassBuilderProtocol))) { - throw facebook::jsi::JSError(runtime, - "Cannot extend an already extended class."); - } - - Object methods = args[1].asObject(runtime); - Object options = count >= 3 && args[2].isObject() - ? args[2].asObject(runtime) - : Object(runtime); - std::string requestedName = readOptionalStringProperty(runtime, options, "name"); - if (requestedName.empty()) { - const char* baseName = class_getName(baseClass); - requestedName = std::string(baseName != nullptr ? baseName : "NSObject") + - "_Extended_" + std::to_string(rand()); - } - - std::string className = nextAvailableJsiClassName(requestedName); - Class nativeClass = objc_allocateClassPair(baseClass, className.c_str(), 0); - if (nativeClass == Nil) { - throw facebook::jsi::JSError(runtime, "Failed to allocate Objective-C class."); - } - - markNativeApiJsiExtendedClass(nativeClass); - class_addProtocol(nativeClass, @protocol(NativeApiJsiClassBuilderProtocol)); - rememberNativeApiJsiClassBuilder(runtime, bridge, nativeClass); - - NativeApiSymbol baseSymbol = runtimeSymbolForClass(bridge, baseClass); - std::vector extensionMembers = - bridge->membersForClass(baseSymbol); - std::vector optionProtocols; - Value protocolsValue = getObjectPropertyOrUndefined(runtime, options, "protocols"); - if (protocolsValue.isObject() && - protocolsValue.asObject(runtime).isArray(runtime)) { - Array protocols = protocolsValue.asObject(runtime).getArray(runtime); - for (size_t i = 0; i < protocols.size(runtime); i++) { - Value protocolValue = protocols.getValueAtIndex(runtime, i); - Protocol* protocol = protocolFromJsiValue(runtime, protocolValue); - std::optional protocolSymbol = - protocolSymbolFromJsiValue(runtime, bridge, protocolValue); - if (protocol != nullptr) { - optionProtocols.push_back(protocol); - class_addProtocol(nativeClass, protocol); - if (!protocolSymbol) { - if (const NativeApiSymbol* runtimeSymbol = - bridge->findProtocolForRuntimePointer(protocol)) { - protocolSymbol = *runtimeSymbol; - } - } - } - if (protocolSymbol) { - const auto& protocolMembers = bridge->membersForProtocol(*protocolSymbol); - extensionMembers.insert(extensionMembers.begin(), - protocolMembers.begin(), - protocolMembers.end()); - } - } - } - const auto& members = extensionMembers; - Array propertyNames = methods.getPropertyNames(runtime); - for (size_t i = 0; i < propertyNames.size(runtime); i++) { - Value propertyNameValue = propertyNames.getValueAtIndex(runtime, i); - if (!propertyNameValue.isString()) { - continue; - } - - std::string propertyName = propertyNameValue.asString(runtime).utf8(runtime); - Object descriptor = getOwnPropertyDescriptor(runtime, methods, propertyName); - - Value value = descriptor.getProperty(runtime, "value"); - if (value.isObject() && value.asObject(runtime).isFunction(runtime)) { - auto overrides = methodOverridesForName(members, propertyName); - bool addedOverride = false; - for (const auto& member : overrides) { - if (member.selectorName.empty() || - member.signatureOffset == MD_SECTION_OFFSET_NULL || - member.signatureOffset == 0) { - continue; - } - addJsiOverrideMethod( - runtime, bridge, nativeClass, baseClass, member.selectorName, - member.signatureOffset, - (member.flags & metagen::mdMemberReturnOwned) != 0, - value.asObject(runtime).asFunction(runtime)); - addedOverride = true; - } - if (!addedOverride) { - bool addedRuntimeProtocolOverride = addRuntimeProtocolOverrideForName( - runtime, bridge, nativeClass, optionProtocols, propertyName, - value.asObject(runtime).asFunction(runtime)); - if (!addedRuntimeProtocolOverride) { - if (auto known = knownNativeApiJsiExposedMethod(propertyName)) { - addJsiExposedMethod(runtime, bridge, nativeClass, - known->selectorName, - std::move(known->signature), - value.asObject(runtime).asFunction(runtime)); - } - } - } - } - - const NativeApiMember* propertyMember = - propertyOverrideForName(members, propertyName); - - Value getter = descriptor.getProperty(runtime, "get"); - if (propertyMember != nullptr && getter.isObject() && - getter.asObject(runtime).isFunction(runtime)) { - addJsiOverrideMethod( - runtime, bridge, nativeClass, baseClass, - propertyMember->selectorName, propertyMember->signatureOffset, - (propertyMember->flags & metagen::mdMemberReturnOwned) != 0, - getter.asObject(runtime).asFunction(runtime)); - } else if (propertyMember == nullptr && getter.isObject() && - getter.asObject(runtime).isFunction(runtime)) { - auto overrides = methodOverridesForName(members, propertyName); - for (const auto& member : overrides) { - if (selectorArgumentCount(member.selectorName) != 0) { - continue; - } - addJsiOverrideMethod( - runtime, bridge, nativeClass, baseClass, member.selectorName, - member.signatureOffset, - (member.flags & metagen::mdMemberReturnOwned) != 0, - getter.asObject(runtime).asFunction(runtime)); - } - } - - Value setter = descriptor.getProperty(runtime, "set"); - if (propertyMember != nullptr && - setter.isObject() && setter.asObject(runtime).isFunction(runtime) && - !propertyMember->setterSelectorName.empty()) { - addJsiOverrideMethod(runtime, bridge, nativeClass, baseClass, - propertyMember->setterSelectorName, - propertyMember->setterSignatureOffset, false, - setter.asObject(runtime).asFunction(runtime)); - } - } - - Value exposedMethodsValue = - getObjectPropertyOrUndefined(runtime, options, "exposedMethods"); - if (!exposedMethodsValue.isObject()) { - exposedMethodsValue = - getObjectPropertyOrUndefined(runtime, methods, "ObjCExposedMethods"); - } - if (exposedMethodsValue.isObject()) { - Object exposedMethods = exposedMethodsValue.asObject(runtime); - Array exposedNames = exposedMethods.getPropertyNames(runtime); - for (size_t i = 0; i < exposedNames.size(runtime); i++) { - Value selectorValue = exposedNames.getValueAtIndex(runtime, i); - if (!selectorValue.isString()) { - continue; - } - std::string selectorName = selectorValue.asString(runtime).utf8(runtime); - Value descriptorValue = - getObjectPropertyOrUndefined(runtime, exposedMethods, selectorName); - if (!descriptorValue.isObject()) { - continue; - } - auto function = functionForSelector(runtime, methods, selectorName); - if (!function) { - continue; - } - auto signature = exposedMethodSignature( - runtime, bridge, selectorName, descriptorValue.asObject(runtime)); - if (signature) { - rememberNativeApiJsiKnownExposedMethod(selectorName, *signature); - addJsiExposedMethod(runtime, bridge, nativeClass, selectorName, - std::move(*signature), std::move(*function)); - } - } - } - - Value hasIteratorValue = - getObjectPropertyOrUndefined(runtime, options, "__hasIterator"); - if (hasIteratorValue.isBool() && hasIteratorValue.getBool()) { - class_addProtocol(nativeClass, @protocol(NSFastEnumeration)); - if (const char* encoding = nativeApiJsiFastEnumerationEncoding()) { - class_replaceMethod( - nativeClass, - @selector(countByEnumeratingWithState:objects:count:), - reinterpret_cast(nativeApiJsiSymbolIteratorCountByEnumerating), - encoding); - } - } - - objc_registerClassPair(nativeClass); - - NativeApiSymbol newSymbol = baseSymbol; - newSymbol.name = className; - newSymbol.runtimeName = className; - newSymbol.superclassOffset = baseSymbol.offset; - return makeNativeClassValue(runtime, bridge, std::move(newSymbol)); - } - -Value invokeNativeApiJsiBaseMethod( - Runtime& runtime, const std::shared_ptr& bridge, - const Value* args, size_t count) { - if (count < 3 || !args[0].isObject() || !args[1].isObject() || - !args[2].isString()) { - throw facebook::jsi::JSError( - runtime, "__invokeBase expects base class, receiver, and member name."); - } - - Class baseClass = classFromJsiValue(runtime, args[0]); - if (baseClass == Nil) { - throw facebook::jsi::JSError(runtime, "__invokeBase base class is invalid."); - } - - Object receiverObject = args[1].asObject(runtime); - if (!receiverObject.isHostObject(runtime)) { - throw facebook::jsi::JSError(runtime, "__invokeBase receiver is not native."); - } - - id receiver = - receiverObject.getHostObject(runtime)->object(); - std::string memberName = args[2].asString(runtime).utf8(runtime); - size_t actualArgc = count - 3; - - NativeApiSymbol baseSymbol = runtimeSymbolForClass(bridge, baseClass); - const auto& members = bridge->membersForClass(baseSymbol); - const NativeApiMember* member = - selectMethodMember(members, memberName, false, actualArgc); - if (member == nullptr) { - if (const NativeApiMember* propertyMember = - selectWritablePropertyMember(members, memberName, false)) { - if (actualArgc == 0) { - Class dispatchClass = - dispatchSuperclassForJsiDerivedReceiver(receiver, baseClass); - return callObjCSelector(runtime, bridge, receiver, false, - propertyMember->selectorName, propertyMember, - nullptr, 0, dispatchClass); - } - if (actualArgc == 1 && !propertyMember->setterSelectorName.empty() && - !propertyMember->readonly) { - Class dispatchClass = - dispatchSuperclassForJsiDerivedReceiver(receiver, baseClass); - NativeApiMember setterMember = *propertyMember; - setterMember.selectorName = propertyMember->setterSelectorName; - setterMember.signatureOffset = propertyMember->setterSignatureOffset; - return callObjCSelector(runtime, bridge, receiver, false, - setterMember.selectorName, &setterMember, - args + 3, actualArgc, dispatchClass); - } - } - } - if (member == nullptr) { - throw facebook::jsi::JSError( - runtime, "Objective-C base selector is not available: " + memberName); - } - - Class dispatchClass = - dispatchSuperclassForJsiDerivedReceiver(receiver, baseClass); - return callObjCSelector(runtime, bridge, receiver, false, member->selectorName, - member, args + 3, actualArgc, dispatchClass); -} diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiConversion.h b/NativeScript/ffi/shared/jsi/NativeApiJsiConversion.h deleted file mode 100644 index 9e37ec0a9..000000000 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiConversion.h +++ /dev/null @@ -1,2103 +0,0 @@ -std::string stringPropertyOrEmpty(Runtime& runtime, const Object& object, - const char* name); -void* pointerFromSymbolLikeObject(Runtime& runtime, const Object& object); - -id objectFromJsiValue(Runtime& runtime, - const std::shared_ptr& bridge, - const Value& value, NativeApiJsiArgumentFrame& frame, - bool mutableString) { - if (value.isNull() || value.isUndefined()) { - return nil; - } - if (value.isString()) { - std::string utf8 = value.asString(runtime).utf8(runtime); - id string = mutableString - ? [[NSMutableString alloc] initWithBytes:utf8.data() - length:utf8.size() - encoding:NSUTF8StringEncoding] - : [[NSString alloc] initWithBytes:utf8.data() - length:utf8.size() - encoding:NSUTF8StringEncoding]; - frame.addObject(string); - return string; - } - if (value.isBool()) { - return [NSNumber numberWithBool:value.getBool()]; - } - if (value.isNumber()) { - return [NSNumber numberWithDouble:value.getNumber()]; - } - if (value.isObject()) { - Object object = value.asObject(runtime); - if (object.isHostObject(runtime)) { - return object.getHostObject(runtime)->object(); - } - if (Class cls = nativeClassFromJsiObject(runtime, object)) { - return static_cast(cls); - } - if (object.isHostObject(runtime)) { - return static_cast( - object.getHostObject(runtime) - ->nativeProtocol()); - } - if (void* symbolPointer = pointerFromSymbolLikeObject(runtime, object)) { - return static_cast(symbolPointer); - } - if (object.isHostObject(runtime)) { - return static_cast( - object.getHostObject(runtime)->pointer()); - } - if (object.isHostObject(runtime)) { - return static_cast( - object.getHostObject(runtime)->data()); - } - if (object.isHostObject(runtime)) { - return static_cast( - object.getHostObject(runtime)->data()); - } - - Value getTimeValue = object.getProperty(runtime, "getTime"); - Value toISOStringValue = object.getProperty(runtime, "toISOString"); - if (getTimeValue.isObject() && - getTimeValue.asObject(runtime).isFunction(runtime) && - toISOStringValue.isObject() && - toISOStringValue.asObject(runtime).isFunction(runtime)) { - Value millisValue = getTimeValue.asObject(runtime) - .asFunction(runtime) - .callWithThis(runtime, object, nullptr, 0); - if (millisValue.isNumber()) { - NSDate* date = [NSDate dateWithTimeIntervalSince1970:millisValue.getNumber() / 1000.0]; - bridge->rememberRoundTripValue(runtime, date, value); - return date; - } - } - - Value valueOfValue = object.getProperty(runtime, "valueOf"); - if (valueOfValue.isObject() && - valueOfValue.asObject(runtime).isFunction(runtime)) { - Value primitiveValue = valueOfValue.asObject(runtime) - .asFunction(runtime) - .callWithThis(runtime, object, nullptr, 0); - if (primitiveValue.isString() || primitiveValue.isBool() || - primitiveValue.isNumber()) { - return objectFromJsiValue(runtime, bridge, primitiveValue, frame, - mutableString); - } - } - - const uint8_t* bytes = nullptr; - size_t byteLength = 0; - if (readJsiBuffer(runtime, object, &bytes, &byteLength)) { - NSData* data = [NSData dataWithBytes:bytes length:byteLength]; - bridge->rememberRoundTripValue(runtime, data, value); - return data; - } - - if (object.isArray(runtime)) { - Array array = object.getArray(runtime); - NSMutableArray* nativeArray = - [NSMutableArray arrayWithCapacity:array.size(runtime)]; - for (size_t i = 0; i < array.size(runtime); i++) { - id element = objectFromJsiValue(runtime, bridge, - array.getValueAtIndex(runtime, i), - frame, false); - [nativeArray addObject:element != nil ? element : [NSNull null]]; - } - bridge->rememberRoundTripValue(runtime, nativeArray, value); - return nativeArray; - } - - Value lengthValue = object.getProperty(runtime, "length"); - if (lengthValue.isNumber() && std::isfinite(lengthValue.getNumber()) && - lengthValue.getNumber() >= 0) { - size_t length = static_cast(std::floor(lengthValue.getNumber())); - NSMutableArray* nativeArray = [NSMutableArray arrayWithCapacity:length]; - for (size_t i = 0; i < length; i++) { - std::string key = std::to_string(i); - id element = objectFromJsiValue( - runtime, bridge, object.getProperty(runtime, key.c_str()), frame, - false); - [nativeArray addObject:element != nil ? element : [NSNull null]]; - } - bridge->rememberRoundTripValue(runtime, nativeArray, value); - return nativeArray; - } - - Value entriesValue = object.getProperty(runtime, "entries"); - Value sizeValue = object.getProperty(runtime, "size"); - Value getValue = object.getProperty(runtime, "get"); - if (entriesValue.isObject() && - entriesValue.asObject(runtime).isFunction(runtime) && - sizeValue.isNumber() && getValue.isObject() && - getValue.asObject(runtime).isFunction(runtime)) { - Object arrayCtor = runtime.global().getPropertyAsObject(runtime, "Array"); - Function arrayFrom = arrayCtor.getPropertyAsFunction(runtime, "from"); - Value iterator = entriesValue.asObject(runtime) - .asFunction(runtime) - .callWithThis(runtime, object, nullptr, 0); - Value pairsValue = arrayFrom.call(runtime, iterator); - if (pairsValue.isObject() && pairsValue.asObject(runtime).isArray(runtime)) { - Array pairs = pairsValue.asObject(runtime).getArray(runtime); - NSMutableDictionary* nativeMap = - [NSMutableDictionary dictionaryWithCapacity:pairs.size(runtime)]; - for (size_t i = 0; i < pairs.size(runtime); i++) { - Value pairValue = pairs.getValueAtIndex(runtime, i); - if (!pairValue.isObject() || - !pairValue.asObject(runtime).isArray(runtime)) { - continue; - } - Array pair = pairValue.asObject(runtime).getArray(runtime); - if (pair.size(runtime) < 2) { - continue; - } - id key = objectFromJsiValue(runtime, bridge, - pair.getValueAtIndex(runtime, 0), - frame, false); - id nativeValue = objectFromJsiValue(runtime, bridge, - pair.getValueAtIndex(runtime, 1), - frame, false); - if (key != nil) { - [nativeMap setObject:nativeValue != nil ? nativeValue : [NSNull null] - forKey:key]; - } - } - bridge->rememberRoundTripValue(runtime, nativeMap, value); - return nativeMap; - } - } - - NSMutableDictionary* dictionary = [NSMutableDictionary dictionary]; - Array propertyNames = object.getPropertyNames(runtime); - for (size_t i = 0; i < propertyNames.size(runtime); i++) { - Value propertyNameValue = propertyNames.getValueAtIndex(runtime, i); - if (!propertyNameValue.isString()) { - continue; - } - std::string key = propertyNameValue.asString(runtime).utf8(runtime); - Value propertyValue = object.getProperty(runtime, key.c_str()); - if (propertyValue.isUndefined()) { - continue; - } - id nativeValue = - objectFromJsiValue(runtime, bridge, propertyValue, frame, false); - NSString* nativeKey = [NSString stringWithUTF8String:key.c_str()]; - if (nativeKey != nil) { - [dictionary setObject:nativeValue != nil ? nativeValue : [NSNull null] - forKey:nativeKey]; - } - } - bridge->rememberRoundTripValue(runtime, dictionary, value); - return dictionary; - } - throw facebook::jsi::JSError(runtime, - "Value cannot be converted to Objective-C object."); -} - -std::string utf8StringFromNSString(NSString* string) { - if (string == nil) { - return ""; - } - NSUInteger length = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; - std::string result(length, '\0'); - NSUInteger usedLength = 0; - NSRange remainingRange = NSMakeRange(0, 0); - BOOL ok = [string getBytes:result.data() - maxLength:length - usedLength:&usedLength - encoding:NSUTF8StringEncoding - options:0 - range:NSMakeRange(0, string.length) - remainingRange:&remainingRange]; - if (!ok) { - return string.UTF8String ?: ""; - } - result.resize(usedLength); - return result; -} - -bool readNativePointerProperty(Runtime& runtime, const Object& object, - void** pointer) { - if (pointer == nullptr) { - return false; - } - - Value nativePointerObjectValue = - object.getProperty(runtime, "__nativeApiPointerObject"); - if (nativePointerObjectValue.isObject()) { - Object nativePointerObject = nativePointerObjectValue.asObject(runtime); - if (nativePointerObject.isHostObject( - runtime)) { - *pointer = nativePointerObject - .getHostObject(runtime) - ->pointer(); - return true; - } - } - - Value nativePointerValue = - object.getProperty(runtime, "__nativeApiPointer"); - if (nativePointerValue.isNumber()) { - *pointer = reinterpret_cast( - static_cast(nativePointerValue.getNumber())); - return true; - } - - Value nativeAddressValue = object.getProperty(runtime, "nativeAddress"); - if (nativeAddressValue.isNumber()) { - *pointer = reinterpret_cast( - static_cast(nativeAddressValue.getNumber())); - return true; - } - - return false; -} - -std::string stringPropertyOrEmpty(Runtime& runtime, const Object& object, - const char* name) { - if (name == nullptr || !object.hasProperty(runtime, name)) { - return ""; - } - Value value = object.getProperty(runtime, name); - return value.isString() ? value.asString(runtime).utf8(runtime) : ""; -} - -void* pointerFromSymbolLikeObject(Runtime& runtime, const Object& object) { - std::string kind = stringPropertyOrEmpty(runtime, object, "kind"); - if (kind != "class" && kind != "protocol") { - return nullptr; - } - - std::string runtimeName = stringPropertyOrEmpty(runtime, object, "runtimeName"); - if (runtimeName.empty()) { - runtimeName = stringPropertyOrEmpty(runtime, object, "name"); - } - if (runtimeName.empty()) { - return nullptr; - } - - if (kind == "class") { - return objc_lookUpClass(runtimeName.c_str()); - } - return lookupProtocolByNativeName(runtimeName); -} - -void* pointerFromJsiValue(Runtime& runtime, const Value& value, - NativeApiJsiArgumentFrame& frame) { - if (value.isNull() || value.isUndefined()) { - return nullptr; - } - if (value.isNumber()) { - return reinterpret_cast(static_cast(value.getNumber())); - } - if (value.isObject()) { - Object object = value.asObject(runtime); - if (object.isHostObject(runtime)) { - return object.getHostObject(runtime)->pointer(); - } - if (object.isHostObject(runtime)) { - return object.getHostObject(runtime)->object(); - } - if (Class cls = nativeClassFromJsiObject(runtime, object)) { - return cls; - } - if (object.isHostObject(runtime)) { - return object.getHostObject(runtime) - ->nativeProtocol(); - } - if (void* symbolPointer = pointerFromSymbolLikeObject(runtime, object)) { - return symbolPointer; - } - if (object.isHostObject(runtime)) { - auto reference = - object.getHostObject(runtime); - if (reference->data() == nullptr) { - reference->ensureStorage(runtime, reference->type(), frame); - } - return reference->data(); - } - if (object.isHostObject(runtime)) { - return object.getHostObject(runtime)->data(); - } - void* nativePointer = nullptr; - if (readNativePointerProperty(runtime, object, &nativePointer)) { - return nativePointer; - } - const uint8_t* bytes = nullptr; - size_t byteLength = 0; - if (readJsiBuffer(runtime, object, &bytes, &byteLength)) { - return const_cast(bytes); - } - } - if (value.isString()) { - std::string utf8 = value.asString(runtime).utf8(runtime); - char* string = strdup(utf8.c_str()); - return string; - } - throw facebook::jsi::JSError(runtime, "Value cannot be converted to pointer."); -} - -bool readPointerLikeValue(Runtime& runtime, const Value& value, void** pointer) { - if (pointer == nullptr || !value.isObject()) { - return false; - } - Object object = value.asObject(runtime); - if (object.isHostObject(runtime)) { - *pointer = object.getHostObject(runtime)->pointer(); - return true; - } - if (object.isHostObject(runtime)) { - *pointer = object.getHostObject(runtime)->data(); - return true; - } - if (object.isHostObject(runtime)) { - *pointer = object.getHostObject(runtime)->data(); - return true; - } - if (object.isHostObject(runtime)) { - *pointer = object.getHostObject(runtime)->object(); - return true; - } - if (Class cls = nativeClassFromJsiObject(runtime, object)) { - *pointer = cls; - return true; - } - if (object.isHostObject(runtime)) { - *pointer = - object.getHostObject(runtime)->nativeProtocol(); - return true; - } - if (void* symbolPointer = pointerFromSymbolLikeObject(runtime, object)) { - *pointer = symbolPointer; - return true; - } - return readNativePointerProperty(runtime, object, pointer); -} - -template -void writeNumericArgument(Runtime& runtime, const Value& value, void* target, - const char* typeName) { - const Value* numericValue = &value; - Value primitiveValue = Value::undefined(); - if (value.isObject()) { - Object object = value.asObject(runtime); - Value valueOfValue = object.getProperty(runtime, "valueOf"); - if (valueOfValue.isObject() && - valueOfValue.asObject(runtime).isFunction(runtime)) { - primitiveValue = valueOfValue.asObject(runtime) - .asFunction(runtime) - .callWithThis(runtime, object, nullptr, 0); - numericValue = &primitiveValue; - } - } - - if (!numericValue->isNumber() && !numericValue->isBool()) { - throw facebook::jsi::JSError(runtime, - std::string("Expected numeric ") + typeName + - " argument."); - } - double number = numericValue->isBool() ? (numericValue->getBool() ? 1.0 : 0.0) - : numericValue->getNumber(); - *static_cast(target) = static_cast(number); -} - -void convertJsiArgument(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, - const Value& value, void* target, - NativeApiJsiArgumentFrame& frame); - -Value convertNativeReturnValue(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* value); - -Class classFromJsiValue(Runtime& runtime, const Value& value); -Protocol* protocolFromJsiValue(Runtime& runtime, const Value& value); - -std::optional parseArrayIndexProperty(const std::string& property) { - if (property.empty()) { - return std::nullopt; - } - size_t index = 0; - for (char c : property) { - if (!std::isdigit(static_cast(c))) { - return std::nullopt; - } - size_t digit = static_cast(c - '0'); - if (index > (std::numeric_limits::max() - digit) / 10) { - return std::nullopt; - } - index = (index * 10) + digit; - } - return index; -} - -size_t referenceElementStride(const NativeApiJsiType& type) { - return std::max(nativeSizeForType(type), 1); -} - -void convertAggregateArgument(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, - const Value& value, void* target, - NativeApiJsiArgumentFrame& frame) { - size_t size = nativeSizeForType(type); - if (size == 0) { - return; - } - - std::memset(target, 0, size); - if (value.isNull() || value.isUndefined()) { - return; - } - - if (value.isObject()) { - Object object = value.asObject(runtime); - if (object.isHostObject(runtime)) { - auto structObject = object.getHostObject(runtime); - if (structObject->data() != nullptr) { - std::memcpy(target, structObject->data(), - std::min(size, static_cast(structObject->info()->size))); - } - return; - } - if (object.isHostObject(runtime)) { - void* data = object.getHostObject(runtime)->data(); - if (data != nullptr) { - std::memcpy(target, data, size); - } - return; - } - if (object.isHostObject(runtime)) { - void* data = object.getHostObject(runtime)->pointer(); - if (data != nullptr) { - std::memcpy(target, data, size); - } - return; - } - - const uint8_t* bytes = nullptr; - size_t byteLength = 0; - if (readJsiBuffer(runtime, object, &bytes, &byteLength)) { - if (bytes != nullptr) { - std::memcpy(target, bytes, std::min(byteLength, size)); - } - return; - } - } - - if (type.aggregateInfo == nullptr) { - throw facebook::jsi::JSError(runtime, "Missing native struct metadata."); - } - if (!value.isObject()) { - throw facebook::jsi::JSError(runtime, "Expected struct descriptor object."); - } - - Object object = value.asObject(runtime); - for (const auto& field : type.aggregateInfo->fields) { - bool hasField = object.hasProperty(runtime, field.name.c_str()); - if (!hasField) { - continue; - } - Value fieldValue = object.getProperty(runtime, field.name.c_str()); - void* fieldTarget = static_cast(target) + field.offset; - convertJsiArgument(runtime, bridge, field.type, fieldValue, fieldTarget, - frame); - } -} - -void convertIndexedAggregateArgument(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, - const Value& value, void* target, - NativeApiJsiArgumentFrame& frame) { - size_t size = nativeSizeForType(type); - std::memset(target, 0, size); - if (value.isNull() || value.isUndefined()) { - return; - } - if (value.isObject()) { - const uint8_t* bytes = nullptr; - size_t byteLength = 0; - if (readJsiBuffer(runtime, value.asObject(runtime), &bytes, &byteLength)) { - if (bytes != nullptr) { - std::memcpy(target, bytes, std::min(byteLength, size)); - } - return; - } - } - if (!value.isObject() || !value.asObject(runtime).isArray(runtime)) { - throw facebook::jsi::JSError(runtime, "Expected array, ArrayBuffer, or typed array."); - } - - Array array = value.asObject(runtime).getArray(runtime); - size_t elementSize = type.elementType != nullptr ? nativeSizeForType(*type.elementType) : 0; - if (elementSize == 0 || type.elementType == nullptr) { - throw facebook::jsi::JSError(runtime, "Invalid native array element type."); - } - size_t count = std::min(type.arraySize, array.size(runtime)); - for (size_t i = 0; i < count; i++) { - void* slot = static_cast(target) + (i * elementSize); - convertJsiArgument(runtime, bridge, *type.elementType, - array.getValueAtIndex(runtime, i), slot, frame); - } -} - -void convertJsiFfiArgument(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, const Value& value, - void* target, NativeApiJsiArgumentFrame& frame) { - if (type.kind != metagen::mdTypeArray) { - convertJsiArgument(runtime, bridge, type, value, target, frame); - return; - } - - void* pointer = nullptr; - if (!value.isNull() && !value.isUndefined()) { - if (value.isObject()) { - Object object = value.asObject(runtime); - if (!readPointerLikeValue(runtime, value, &pointer)) { - const uint8_t* bytes = nullptr; - size_t byteLength = 0; - if (readJsiBuffer(runtime, object, &bytes, &byteLength)) { - pointer = const_cast(bytes); - } - } - } - - if (pointer == nullptr) { - size_t byteLength = nativeSizeForType(type); - void* buffer = frame.addBuffer(byteLength); - convertIndexedAggregateArgument(runtime, bridge, type, value, buffer, - frame); - pointer = buffer; - } - } - - *static_cast(target) = pointer; -} - -void convertJsiArgument(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, - const Value& value, void* target, - NativeApiJsiArgumentFrame& frame) { - if (unsupportedJsiType(type)) { - throw facebook::jsi::JSError(runtime, - "This native signature is not supported by " - "the pure JSI bridge yet."); - } - - switch (type.kind) { - case metagen::mdTypeBool: - if (!value.isNumber() && !value.isBool()) { - throw facebook::jsi::JSError(runtime, - "Expected boolean or numeric argument."); - } - *static_cast(target) = - value.isBool() ? static_cast(value.getBool()) - : static_cast(value.getNumber() != 0); - break; - case metagen::mdTypeChar: - writeNumericArgument(runtime, value, target, "int8"); - break; - case metagen::mdTypeUChar: - case metagen::mdTypeUInt8: - writeNumericArgument(runtime, value, target, "uint8"); - break; - case metagen::mdTypeSShort: - writeNumericArgument(runtime, value, target, "int16"); - break; - case metagen::mdTypeUShort: - if (value.isString()) { - std::string text = value.asString(runtime).utf8(runtime); - if (text.size() != 1) { - throw facebook::jsi::JSError( - runtime, "Expected a single-character string."); - } - *static_cast(target) = - static_cast(static_cast(text[0])); - } else { - writeNumericArgument(runtime, value, target, "uint16"); - } - break; - case metagen::mdTypeSInt: - writeNumericArgument(runtime, value, target, "int32"); - break; - case metagen::mdTypeUInt: - writeNumericArgument(runtime, value, target, "uint32"); - break; - case metagen::mdTypeSLong: - case metagen::mdTypeSInt64: - writeNumericArgument(runtime, value, target, "int64"); - break; - case metagen::mdTypeULong: - case metagen::mdTypeUInt64: - writeNumericArgument(runtime, value, target, "uint64"); - break; - case metagen::mdTypeFloat: - writeNumericArgument(runtime, value, target, "float"); - break; - case metagen::mdTypeDouble: - writeNumericArgument(runtime, value, target, "double"); - break; - case metagen::mdTypeString: { - if (value.isNull() || value.isUndefined()) { - *static_cast(target) = nullptr; - break; - } - if (value.isObject()) { - Object object = value.asObject(runtime); - void* pointer = nullptr; - if (readPointerLikeValue(runtime, value, &pointer)) { - *static_cast(target) = static_cast(pointer); - break; - } - const uint8_t* bytes = nullptr; - size_t byteLength = 0; - if (readJsiBuffer(runtime, object, &bytes, &byteLength)) { - *static_cast(target) = - reinterpret_cast(const_cast(bytes)); - break; - } - Value valueOfValue = object.getProperty(runtime, "valueOf"); - if (valueOfValue.isObject() && - valueOfValue.asObject(runtime).isFunction(runtime)) { - Value primitive = valueOfValue.asObject(runtime) - .asFunction(runtime) - .callWithThis(runtime, object, nullptr, 0); - if (primitive.isString()) { - std::string utf8 = primitive.asString(runtime).utf8(runtime); - char* string = strdup(utf8.c_str()); - *static_cast(target) = string; - break; - } - } - } - if (!value.isString()) { - throw facebook::jsi::JSError(runtime, "Expected string argument."); - } - std::string utf8 = value.asString(runtime).utf8(runtime); - char* string = strdup(utf8.c_str()); - *static_cast(target) = string; - break; - } - case metagen::mdTypeAnyObject: - case metagen::mdTypeProtocolObject: - case metagen::mdTypeClassObject: - case metagen::mdTypeInstanceObject: - case metagen::mdTypeNSStringObject: - case metagen::mdTypeNSMutableStringObject: { - id object = objectFromJsiValue( - runtime, bridge, value, frame, - type.kind == metagen::mdTypeNSMutableStringObject); - *static_cast(target) = object; - break; - } - case metagen::mdTypeClass: { - *static_cast(target) = classFromJsiValue(runtime, value); - break; - } - case metagen::mdTypeSelector: { - if (value.isNull() || value.isUndefined()) { - *static_cast(target) = nullptr; - break; - } - if (!value.isString()) { - throw facebook::jsi::JSError(runtime, "Expected selector string."); - } - std::string selectorName = value.asString(runtime).utf8(runtime); - *static_cast(target) = sel_registerName(selectorName.c_str()); - break; - } - case metagen::mdTypePointer: - if (value.isObject()) { - Object object = value.asObject(runtime); - if (object.isHostObject(runtime)) { - auto reference = object.getHostObject(runtime); - if (reference->data() == nullptr && type.elementType != nullptr) { - reference->ensureStorage(runtime, *type.elementType, frame); - } else if (reference->data() == nullptr) { - reference->ensureStorage(runtime, reference->type(), frame); - } - void* pointer = reference->data(); - frame.rememberRoundTripValue(bridge, runtime, pointer, value); - *static_cast(target) = pointer; - break; - } - if (object.isHostObject(runtime)) { - void* pointer = - object.getHostObject(runtime) - ->data(); - frame.rememberRoundTripValue(bridge, runtime, pointer, value); - *static_cast(target) = pointer; - break; - } - const uint8_t* bytes = nullptr; - size_t byteLength = 0; - if (readJsiBuffer(runtime, object, &bytes, &byteLength)) { - void* pointer = const_cast(bytes); - frame.rememberRoundTripValue(bridge, runtime, pointer, value); - *static_cast(target) = pointer; - break; - } - } - *static_cast(target) = pointerFromJsiValue(runtime, value, frame); - break; - case metagen::mdTypeOpaquePointer: - *static_cast(target) = pointerFromJsiValue(runtime, value, frame); - break; - case metagen::mdTypeBlock: - case metagen::mdTypeFunctionPointer: { - if (value.isObject()) { - Object object = value.asObject(runtime); - void* nativePointer = nullptr; - if (object.isFunction(runtime)) { - std::string functionKind = stringPropertyOrEmpty(runtime, object, "kind"); - if (functionKind == "block" || functionKind == "functionPointer" || - functionKind == "functionReference") { - if (readNativePointerProperty(runtime, object, &nativePointer)) { - *static_cast(target) = nativePointer; - break; - } - } - - auto threadPolicy = readJsiCallbackThreadPolicy(runtime, object); - auto callback = - createJsiCallback(runtime, bridge, type, object.asFunction(runtime), - type.kind == metagen::mdTypeBlock, threadPolicy); - void* pointer = callback->functionPointer(); - if (type.kind == metagen::mdTypeBlock) { - frame.addLifetime(callback); - frame.rememberRoundTripValue(bridge, runtime, pointer, value); - } else { - bridge->rememberRoundTripValue(runtime, pointer, value); - } - try { - object.setProperty(runtime, "__nativeApiPointerObject", - createPointer(runtime, bridge, pointer)); - object.setProperty( - runtime, "__nativeApiPointer", - static_cast(reinterpret_cast(pointer))); - } catch (const std::exception&) { - } - *static_cast(target) = pointer; - break; - } - } - *static_cast(target) = pointerFromJsiValue(runtime, value, frame); - break; - } - case metagen::mdTypeStruct: - convertAggregateArgument(runtime, bridge, type, value, target, frame); - break; - case metagen::mdTypeArray: - case metagen::mdTypeVector: - case metagen::mdTypeExtVector: - case metagen::mdTypeComplex: - convertIndexedAggregateArgument(runtime, bridge, type, value, target, - frame); - break; - default: - throw facebook::jsi::JSError(runtime, "Unsupported JSI argument type."); - } -} - -Value convertNativeReturnValue(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* value) { - if (unsupportedJsiType(type)) { - throw facebook::jsi::JSError(runtime, - "This native return type is not supported by " - "the pure JSI bridge yet."); - } - - switch (type.kind) { - case metagen::mdTypeVoid: - return Value::undefined(); - case metagen::mdTypeBool: - return *static_cast(value) != 0; - case metagen::mdTypeChar: - return static_cast(*static_cast(value)); - case metagen::mdTypeUChar: - case metagen::mdTypeUInt8: - return static_cast(*static_cast(value)); - case metagen::mdTypeSShort: - return static_cast(*static_cast(value)); - case metagen::mdTypeUShort: { - uint16_t raw = *static_cast(value); - if (raw >= 32 && raw <= 126) { - char buffer[2] = {static_cast(raw), '\0'}; - return String::createFromUtf8(runtime, buffer); - } - return static_cast(raw); - } - case metagen::mdTypeSInt: - return static_cast(*static_cast(value)); - case metagen::mdTypeUInt: - return static_cast(*static_cast(value)); - case metagen::mdTypeSLong: - case metagen::mdTypeSInt64: - return signedInteger64ToJsiValue(runtime, *static_cast(value)); - case metagen::mdTypeULong: - case metagen::mdTypeUInt64: - return unsignedInteger64ToJsiValue(runtime, - *static_cast(value)); - case metagen::mdTypeFloat: - return static_cast(*static_cast(value)); - case metagen::mdTypeDouble: - return *static_cast(value); - case metagen::mdTypeString: { - const char* string = *static_cast(value); - if (string == nullptr) { - return Value::null(); - } - NativeApiJsiType cStringType = - primitiveInteropType(metagen::mdTypeChar); - return Object::createFromHostObject( - runtime, std::make_shared( - bridge, cStringType, const_cast(string), false)); - } - case metagen::mdTypeClass: { - Class cls = *static_cast(value); - if (cls == nil) { - return Value::null(); - } - const char* name = class_getName(cls); - NativeApiSymbol symbol{ - .kind = NativeApiSymbolKind::Class, - .offset = MD_SECTION_OFFSET_NULL, - .name = name != nullptr ? name : "", - .runtimeName = name != nullptr ? name : "", - }; - if (const NativeApiSymbol* found = bridge->findClass(symbol.name)) { - symbol = *found; - } - return makeNativeClassValue(runtime, bridge, std::move(symbol)); - } - case metagen::mdTypeAnyObject: - case metagen::mdTypeProtocolObject: - case metagen::mdTypeClassObject: - case metagen::mdTypeInstanceObject: - case metagen::mdTypeNSStringObject: - case metagen::mdTypeNSMutableStringObject: { - id object = *static_cast(value); - if (object == nil) { - return Value::null(); - } - if ([object isKindOfClass:[NSNull class]]) { - if (type.returnOwned) { - [object release]; - } - return Value::null(); - } - if ([object respondsToSelector:@selector(UTF8String)]) { - bool untypedObject = type.kind == metagen::mdTypeAnyObject; - bool explicitNSString = type.kind == metagen::mdTypeNSStringObject; - if (untypedObject || explicitNSString) { - std::string utf8 = utf8StringFromNSString(static_cast(object)); - if (type.returnOwned) { - [object release]; - } - return makeString(runtime, utf8); - } - } - if ([object isKindOfClass:[NSNumber class]] && - ![object isKindOfClass:[NSDecimalNumber class]]) { - NSNumber* number = static_cast(object); - const char* objCType = [number objCType]; - bool isBool = CFGetTypeID((__bridge CFTypeRef)number) == - CFBooleanGetTypeID() || - (objCType != nullptr && - std::strcmp(objCType, @encode(BOOL)) == 0); - Value result = isBool ? Value(static_cast([number boolValue])) - : Value([number doubleValue]); - if (type.returnOwned) { - [object release]; - } - return result; - } - Value roundTrip = bridge->findRoundTripValue(runtime, object); - if (!roundTrip.isUndefined()) { - if (type.returnOwned) { - [object release]; - } - return roundTrip; - } - if (const NativeApiSymbol* classSymbol = - bridge->findClassForRuntimePointer((void*)object)) { - return makeNativeClassValue(runtime, bridge, *classSymbol); - } - if (const NativeApiSymbol* protocolSymbol = - bridge->findProtocolForRuntimePointer((void*)object)) { - return makeNativeProtocolValue(runtime, bridge, *protocolSymbol); - } - return makeNativeObjectValue(runtime, bridge, object, type.returnOwned); - } - case metagen::mdTypeSelector: { - SEL selector = *static_cast(value); - const char* selectorName = selector != nullptr ? sel_getName(selector) : nullptr; - return selectorName != nullptr ? makeString(runtime, selectorName) - : Value::null(); - } - case metagen::mdTypePointer: - case metagen::mdTypeOpaquePointer: { - void* pointer = *static_cast(value); - if (pointer == nullptr) { - return Value::null(); - } - if (const NativeApiSymbol* classSymbol = - bridge->findClassForRuntimePointer(pointer)) { - return makeNativeClassValue(runtime, bridge, *classSymbol); - } - if (const NativeApiSymbol* protocolSymbol = - bridge->findProtocolForRuntimePointer(pointer)) { - return makeNativeProtocolValue(runtime, bridge, *protocolSymbol); - } - if (type.kind == metagen::mdTypePointer && type.elementType != nullptr) { - std::shared_ptr backingValue; - Value roundTrip = bridge->findRoundTripValue(runtime, pointer); - if (!roundTrip.isUndefined()) { - backingValue = std::make_shared(runtime, roundTrip); - } - return Object::createFromHostObject( - runtime, std::make_shared( - bridge, *type.elementType, pointer, false, 0, nullptr, - std::move(backingValue))); - } - return createPointer(runtime, bridge, pointer); - } - case metagen::mdTypeBlock: - case metagen::mdTypeFunctionPointer: { - void* pointer = *static_cast(value); - if (pointer == nullptr) { - return Value::null(); - } - Value roundTrip = bridge->findRoundTripValue(runtime, pointer); - if (!roundTrip.isUndefined()) { - return roundTrip; - } - return wrapNativeFunctionPointer(runtime, bridge, type, pointer, - type.kind == metagen::mdTypeBlock); - } - case metagen::mdTypeStruct: - if (type.aggregateInfo == nullptr) { - return ArrayBuffer( - runtime, std::make_shared( - value, nativeSizeForType(type))); - } - return Object::createFromHostObject( - runtime, std::make_shared( - bridge, type.aggregateInfo, value, true)); - case metagen::mdTypeArray: - case metagen::mdTypeVector: - case metagen::mdTypeExtVector: - case metagen::mdTypeComplex: { - Array result(runtime, type.arraySize); - if (type.elementType == nullptr) { - return result; - } - size_t elementSize = nativeSizeForType(*type.elementType); - auto base = static_cast(value); - for (uint16_t i = 0; i < type.arraySize; i++) { - result.setValueAtIndex( - runtime, i, - convertNativeReturnValue(runtime, bridge, *type.elementType, - base + (static_cast(i) * elementSize))); - } - return result; - } - default: - throw facebook::jsi::JSError(runtime, "Unsupported JSI return type."); - } -} - -void NativeApiReferenceHostObject::ensureStorage( - Runtime& runtime, NativeApiJsiType type, NativeApiJsiArgumentFrame& frame, - size_t elements) { - size_t elementCount = std::max(elements, 1); - NativeApiJsiType storageType = std::move(type); - size_t stride = std::max(nativeSizeForType(storageType), 1); - size_t required = std::max(stride * elementCount, sizeof(void*)); - type_ = std::move(storageType); - - if (data_ == nullptr) { - data_ = calloc(1, required); - ownsData_ = true; - byteLength_ = required; - } else if (ownsData_ && byteLength_ < required) { - void* expanded = realloc(data_, required); - if (expanded == nullptr) { - throw std::bad_alloc(); - } - std::memset(static_cast(expanded) + byteLength_, 0, - required - byteLength_); - data_ = expanded; - byteLength_ = required; - } - - if (data_ != nullptr && pendingValue_ != nullptr) { - Value pending(runtime, *pendingValue_); - convertJsiArgument(runtime, bridge_, type_, pending, data_, frame); - pendingValue_.reset(); - } -} - -Value NativeApiReferenceHostObject::get(Runtime& runtime, - const PropNameID& name) { - std::string property = name.utf8(runtime); - if (property == "kind") { - return makeString(runtime, "reference"); - } - if (property == "address") { - return static_cast(reinterpret_cast(data_)); - } - if (property == "value") { - if (data_ == nullptr) { - if (pendingValue_ != nullptr) { - return Value(runtime, *pendingValue_); - } - return Value::undefined(); - } - return convertNativeReturnValue(runtime, bridge_, type_, data_); - } - if (auto index = parseArrayIndexProperty(property)) { - if (data_ == nullptr) { - return Value::undefined(); - } - void* slot = static_cast(data_) + - (*index * referenceElementStride(type_)); - return convertNativeReturnValue(runtime, bridge_, type_, slot); - } - if (property == "toString") { - void* data = data_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "toString"), 0, - [data](Runtime& runtime, const Value&, const Value*, size_t) -> Value { - char address[32] = {}; - snprintf(address, sizeof(address), "%p", data); - return makeString(runtime, - ""); - }); - } - return Value::undefined(); -} - -void NativeApiReferenceHostObject::set(Runtime& runtime, - const PropNameID& name, - const Value& value) { - std::string property = name.utf8(runtime); - auto index = parseArrayIndexProperty(property); - if (property != "value" && !index) { - return; - } - size_t slotIndex = index.value_or(0); - NativeApiJsiArgumentFrame frame(1); - if (data_ == nullptr) { - if (slotIndex == 0) { - pendingValue_ = std::make_shared(runtime, value); - return; - } - ensureStorage(runtime, type_, frame, slotIndex + 1); - } - pendingValue_.reset(); - void* slot = static_cast(data_) + - (slotIndex * referenceElementStride(type_)); - convertJsiArgument(runtime, bridge_, type_, value, slot, frame); -} - -Value NativeApiStructObjectHostObject::get(Runtime& runtime, - const PropNameID& name) { - std::string property = name.utf8(runtime); - if (property == "kind") { - return makeString(runtime, info_ != nullptr && info_->isUnion ? "union" : "struct"); - } - if (property == "name") { - return makeString(runtime, info_ != nullptr ? info_->name : ""); - } - if (property == "sizeof") { - return static_cast(info_ != nullptr ? info_->size : 0); - } - if (property == "address") { - return static_cast(reinterpret_cast(data_)); - } - if (property == "toString") { - auto info = info_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "toString"), 0, - [info](Runtime& runtime, const Value&, const Value*, size_t) -> Value { - return makeString(runtime, - std::string("[NativeApiJsi ") + - (info != nullptr && info->isUnion ? "Union " : "Struct ") + - (info != nullptr ? info->name : "") + "]"); - }); - } - - if (info_ != nullptr && data_ != nullptr) { - for (const auto& field : info_->fields) { - if (field.name != property) { - continue; - } - void* fieldData = static_cast(data_) + field.offset; - if (field.type.kind == metagen::mdTypeStruct && - field.type.aggregateInfo != nullptr) { - return Object::createFromHostObject( - runtime, std::make_shared( - bridge_, field.type.aggregateInfo, fieldData, false, - ownedData_, backingValue_)); - } - return convertNativeReturnValue(runtime, bridge_, field.type, fieldData); - } - } - return Value::undefined(); -} - -void NativeApiStructObjectHostObject::set(Runtime& runtime, - const PropNameID& name, - const Value& value) { - std::string property = name.utf8(runtime); - if (info_ == nullptr || data_ == nullptr) { - throw facebook::jsi::JSError(runtime, "Struct is not initialized."); - } - for (const auto& field : info_->fields) { - if (field.name != property) { - continue; - } - NativeApiJsiArgumentFrame frame(1); - convertJsiArgument(runtime, bridge_, field.type, value, - static_cast(data_) + field.offset, frame); - return; - } - throw facebook::jsi::JSError(runtime, "No native struct field: " + property); -} - -std::vector NativeApiStructObjectHostObject::getPropertyNames( - Runtime& runtime) { - std::vector names; - addPropertyName(runtime, names, "kind"); - addPropertyName(runtime, names, "name"); - addPropertyName(runtime, names, "sizeof"); - addPropertyName(runtime, names, "address"); - addPropertyName(runtime, names, "toString"); - if (info_ != nullptr) { - for (const auto& field : info_->fields) { - addPropertyName(runtime, names, field.name.c_str()); - } - } - return names; -} - -NativeApiJsiType primitiveInteropType(MDTypeKind kind) { - NativeApiJsiType type; - type.kind = kind; - type.ffiType = ffiTypeForJsiKind(kind); - type.supported = type.ffiType != nullptr; - return type; -} - -std::optional primitiveInteropTypeFromCode(int32_t code) { - MDTypeKind kind = static_cast(code); - switch (kind) { - case metagen::mdTypeVoid: - case metagen::mdTypeBool: - case metagen::mdTypeChar: - case metagen::mdTypeUChar: - case metagen::mdTypeUInt8: - case metagen::mdTypeSShort: - case metagen::mdTypeUShort: - case metagen::mdTypeSInt: - case metagen::mdTypeUInt: - case metagen::mdTypeSLong: - case metagen::mdTypeULong: - case metagen::mdTypeSInt64: - case metagen::mdTypeUInt64: - case metagen::mdTypeFloat: - case metagen::mdTypeDouble: - case metagen::mdTypeString: - case metagen::mdTypeAnyObject: - case metagen::mdTypeProtocolObject: - case metagen::mdTypeClass: - case metagen::mdTypeSelector: - case metagen::mdTypePointer: - case metagen::mdTypeOpaquePointer: - case metagen::mdTypeBlock: - case metagen::mdTypeFunctionPointer: - return primitiveInteropType(kind); - default: - return std::nullopt; - } -} - -std::optional interopTypeFromValue( - Runtime& runtime, const std::shared_ptr& bridge, - const Value& value) { - if (value.isNumber()) { - return primitiveInteropTypeFromCode(static_cast(value.getNumber())); - } - - if (!value.isObject()) { - return std::nullopt; - } - - Object object = value.asObject(runtime); - Value typeCodeValue = object.getProperty(runtime, "__nativeApiTypeCode"); - if (typeCodeValue.isNumber()) { - return primitiveInteropTypeFromCode( - static_cast(typeCodeValue.getNumber())); - } - Value valueOfValue = object.getProperty(runtime, "valueOf"); - if (valueOfValue.isObject() && - valueOfValue.asObject(runtime).isFunction(runtime)) { - Value primitive = - valueOfValue.asObject(runtime).asFunction(runtime).callWithThis( - runtime, object, nullptr, 0); - if (primitive.isNumber()) { - return primitiveInteropTypeFromCode( - static_cast(primitive.getNumber())); - } - } - - Class descriptorClass = nativeClassFromJsiObject(runtime, object); - if (descriptorClass == Nil && - stringPropertyOrEmpty(runtime, object, "kind") == "class") { - descriptorClass = - static_cast(pointerFromSymbolLikeObject(runtime, object)); - } - if (descriptorClass != Nil) { - return nativeObjectReturnTypeForClass(descriptorClass); - } - - if (object.isHostObject(runtime)) { - auto structObject = object.getHostObject(runtime); - NativeApiJsiType type; - type.kind = metagen::mdTypeStruct; - type.aggregateInfo = structObject->info(); - type.aggregateOffset = type.aggregateInfo != nullptr - ? type.aggregateInfo->offset - : MD_SECTION_OFFSET_NULL; - type.aggregateIsUnion = type.aggregateInfo != nullptr && - type.aggregateInfo->isUnion; - type.ffiType = type.aggregateInfo != nullptr && type.aggregateInfo->ffi != nullptr - ? &type.aggregateInfo->ffi->type - : nullptr; - type.supported = type.ffiType != nullptr; - return type; - } - - Value kindValue = object.getProperty(runtime, "kind"); - if (kindValue.isString()) { - std::string kindName = kindValue.asString(runtime).utf8(runtime); - if (kindName == "pointer") { - return primitiveInteropType(metagen::mdTypePointer); - } - if (kindName == "reference") { - return primitiveInteropType(metagen::mdTypePointer); - } - if (kindName == "class") { - return nativeObjectReturnType(metagen::mdTypeInstanceObject); - } - if (kindName == "selector") { - return primitiveInteropType(metagen::mdTypeSelector); - } - if (kindName == "protocol") { - return primitiveInteropType(metagen::mdTypeProtocolObject); - } - if (kindName == "block") { - return primitiveInteropType(metagen::mdTypeBlock); - } - if (kindName == "functionPointer") { - return primitiveInteropType(metagen::mdTypeFunctionPointer); - } - if (kindName == "functionReference") { - return primitiveInteropType(metagen::mdTypeFunctionPointer); - } - } - Value offsetValue = object.getProperty(runtime, "metadataOffset"); - if (kindValue.isString() && offsetValue.isNumber()) { - std::string kindName = kindValue.asString(runtime).utf8(runtime); - if (kindName == "struct" || kindName == "union") { - bool isUnion = kindName == "union"; - auto info = bridge->aggregateInfoFor( - static_cast(offsetValue.getNumber()), isUnion); - NativeApiJsiType type; - type.kind = metagen::mdTypeStruct; - type.aggregateInfo = info; - type.aggregateOffset = info != nullptr ? info->offset : MD_SECTION_OFFSET_NULL; - type.aggregateIsUnion = isUnion; - type.ffiType = info != nullptr && info->ffi != nullptr ? &info->ffi->type : nullptr; - type.supported = type.ffiType != nullptr; - return type; - } - } - - return std::nullopt; -} - -Value makeAggregateConstructor(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiSymbol& symbol) { - auto info = bridge->aggregateInfoFor(symbol); - auto constructor = Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, symbol.name.c_str()), 1, - [bridge, symbol, info](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (info == nullptr) { - throw facebook::jsi::JSError(runtime, - "Native aggregate metadata is unavailable: " + - symbol.name); - } - - NativeApiJsiType type; - type.kind = metagen::mdTypeStruct; - type.aggregateInfo = info; - type.aggregateOffset = info->offset; - type.aggregateIsUnion = info->isUnion; - type.ffiType = info->ffi != nullptr ? &info->ffi->type : nullptr; - type.supported = type.ffiType != nullptr; - - if (count > 0 && args[0].isObject()) { - void* pointer = nullptr; - if (readPointerLikeValue(runtime, args[0], &pointer) && pointer != nullptr) { - return Object::createFromHostObject( - runtime, std::make_shared( - bridge, info, pointer, false, nullptr, - std::make_shared(runtime, args[0]))); - } - } - - std::vector storage(info->size, 0); - if (count > 0) { - NativeApiJsiArgumentFrame frame(1); - convertAggregateArgument(runtime, bridge, type, args[0], - storage.data(), frame); - } - return Object::createFromHostObject( - runtime, std::make_shared( - bridge, info, storage.data(), true)); - }); - - constructor.setProperty(runtime, "kind", - makeString(runtime, symbol.kind == NativeApiSymbolKind::Union - ? "union" - : "struct")); - constructor.setProperty(runtime, "runtimeName", makeString(runtime, symbol.runtimeName)); - constructor.setProperty(runtime, "metadataOffset", static_cast(symbol.offset)); - constructor.setProperty(runtime, "sizeof", - static_cast(info != nullptr ? info->size : 0)); - constructor.setProperty( - runtime, "equals", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "equals"), 2, - [bridge, info](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (info == nullptr || count < 2) { - return false; - } - - NativeApiJsiType type; - type.kind = metagen::mdTypeStruct; - type.aggregateInfo = info; - type.aggregateOffset = info->offset; - type.aggregateIsUnion = info->isUnion; - type.ffiType = info->ffi != nullptr ? &info->ffi->type : nullptr; - type.supported = type.ffiType != nullptr; - - std::vector left(info->size, 0); - std::vector right(info->size, 0); - try { - NativeApiJsiArgumentFrame leftFrame(1); - convertAggregateArgument(runtime, bridge, type, args[0], - left.data(), leftFrame); - NativeApiJsiArgumentFrame rightFrame(1); - convertAggregateArgument(runtime, bridge, type, args[1], - right.data(), rightFrame); - } catch (const std::exception&) { - return false; - } - - return std::memcmp(left.data(), right.data(), info->size) == 0; - })); - Array fields(runtime, info != nullptr ? info->fields.size() : 0); - if (info != nullptr) { - for (size_t i = 0; i < info->fields.size(); i++) { - fields.setValueAtIndex(runtime, i, makeString(runtime, info->fields[i].name)); - } - } - constructor.setProperty(runtime, "fields", fields); - return constructor; -} - -size_t sizeofInteropType(Runtime& runtime, - const std::shared_ptr& bridge, - const Value& value) { - if (auto type = interopTypeFromValue(runtime, bridge, value)) { - return nativeSizeForType(*type); - } - - if (value.isObject()) { - Object object = value.asObject(runtime); - if (object.isHostObject(runtime) || - object.isHostObject(runtime) || - object.isHostObject(runtime) || - nativeClassFromJsiObject(runtime, object) != Nil) { - return sizeof(void*); - } - void* nativePointer = nullptr; - if (readNativePointerProperty(runtime, object, &nativePointer)) { - return sizeof(void*); - } - Value sizeValue = object.getProperty(runtime, "sizeof"); - if (sizeValue.isNumber()) { - return static_cast(sizeValue.getNumber()); - } - } - - throw facebook::jsi::JSError(runtime, "Invalid type for interop.sizeof."); -} - -Object createPointer(Runtime& runtime, - const std::shared_ptr& bridge, - void* pointer, bool adopted) { - if (!adopted && bridge != nullptr) { - Value cached = bridge->findPointerValue(runtime, pointer); - if (cached.isObject()) { - return cached.asObject(runtime); - } - } - - Object result = Object::createFromHostObject( - runtime, - std::make_shared(bridge, pointer, "pointer", - adopted)); - if (!adopted && bridge != nullptr) { - bridge->rememberPointerValue(runtime, pointer, Value(runtime, result)); - } - return result; -} - -void installInteropHasInstance(Runtime& runtime, Function& constructor, - const char* kind) { - Value symbolCtorValue = runtime.global().getProperty(runtime, "Symbol"); - if (!symbolCtorValue.isObject()) { - return; - } - - Object symbolCtor = symbolCtorValue.asObject(runtime); - Value hasInstanceValue = symbolCtor.getProperty(runtime, "hasInstance"); - if (!hasInstanceValue.isSymbol()) { - return; - } - - try { - Object objectCtor = runtime.global().getPropertyAsObject(runtime, "Object"); - Function defineProperty = - objectCtor.getPropertyAsFunction(runtime, "defineProperty"); - Object descriptor(runtime); - descriptor.setProperty(runtime, "configurable", true); - descriptor.setProperty( - runtime, "value", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "Symbol.hasInstance"), 1, - [kind = std::string(kind)](Runtime& runtime, const Value&, - const Value* args, size_t count) -> Value { - if (count < 1 || !args[0].isObject()) { - return false; - } - - Object object = args[0].asObject(runtime); - Value kindValue = object.getProperty(runtime, "kind"); - return kindValue.isString() && - kindValue.asString(runtime).utf8(runtime) == kind; - })); - defineProperty.call(runtime, constructor, hasInstanceValue, descriptor); - } catch (const std::exception&) { - } -} - -Class classFromJsiValue(Runtime& runtime, const Value& value) { - if (value.isString()) { - std::string name = value.asString(runtime).utf8(runtime); - return objc_lookUpClass(name.c_str()); - } - if (!value.isObject()) { - return Nil; - } - Object object = value.asObject(runtime); - if (Class cls = nativeClassFromJsiObject(runtime, object)) { - return cls; - } - if (stringPropertyOrEmpty(runtime, object, "kind") == "class") { - if (void* pointer = pointerFromSymbolLikeObject(runtime, object)) { - return static_cast(pointer); - } - } - if (object.isHostObject(runtime)) { - id nativeObject = object.getHostObject(runtime)->object(); - return nativeObject != nil ? object_getClass(nativeObject) : Nil; - } - return Nil; -} - -Protocol* protocolFromJsiValue(Runtime& runtime, const Value& value) { - if (value.isString()) { - std::string name = value.asString(runtime).utf8(runtime); - Protocol* protocol = objc_getProtocol(name.c_str()); - if (protocol == nullptr) { - constexpr const char* suffix = "Protocol"; - if (name.size() > std::strlen(suffix) && - name.compare(name.size() - std::strlen(suffix), std::strlen(suffix), - suffix) == 0) { - protocol = objc_getProtocol( - name.substr(0, name.size() - std::strlen(suffix)).c_str()); - } - } - return protocol; - } - if (!value.isObject()) { - return nullptr; - } - Object object = value.asObject(runtime); - if (object.isHostObject(runtime)) { - return object.getHostObject(runtime) - ->nativeProtocol(); - } - if (stringPropertyOrEmpty(runtime, object, "kind") == "protocol") { - return static_cast(pointerFromSymbolLikeObject(runtime, object)); - } - if (object.isHostObject(runtime)) { - return static_cast( - object.getHostObject(runtime)->pointer()); - } - void* nativePointer = nullptr; - if (readNativePointerProperty(runtime, object, &nativePointer)) { - return static_cast(nativePointer); - } - Value nameValue = object.getProperty(runtime, "name"); - if (nameValue.isString()) { - return protocolFromJsiValue(runtime, nameValue); - } - return nullptr; -} - -Object createInteropObject(Runtime& runtime, - const std::shared_ptr& bridge) { - Object interop(runtime); - Object types(runtime); - auto setType = [&](const char* name, MDTypeKind kind) { - Object type(runtime); - double code = static_cast(kind); - type.setProperty(runtime, "__nativeApiTypeCode", code); - type.setProperty( - runtime, "valueOf", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "valueOf"), 0, - [code](Runtime&, const Value&, const Value*, size_t) -> Value { - return code; - })); - type.setProperty( - runtime, "toString", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "toString"), 0, - [code](Runtime& runtime, const Value&, const Value*, size_t) -> Value { - char text[32] = {}; - snprintf(text, sizeof(text), "%d", static_cast(code)); - return makeString(runtime, text); - })); - types.setProperty(runtime, name, type); - }; - setType("void", metagen::mdTypeVoid); - setType("bool", metagen::mdTypeBool); - setType("int8", metagen::mdTypeChar); - setType("uint8", metagen::mdTypeUInt8); - setType("int16", metagen::mdTypeSShort); - setType("uint16", metagen::mdTypeUShort); - setType("int32", metagen::mdTypeSInt); - setType("uint32", metagen::mdTypeUInt); - setType("int64", metagen::mdTypeSInt64); - setType("uint64", metagen::mdTypeUInt64); - setType("float", metagen::mdTypeFloat); - setType("double", metagen::mdTypeDouble); - setType("UTF8CString", metagen::mdTypeString); - setType("unichar", metagen::mdTypeUShort); - setType("id", metagen::mdTypeAnyObject); - setType("class", metagen::mdTypeClass); - setType("protocol", metagen::mdTypeProtocolObject); - setType("SEL", metagen::mdTypeSelector); - setType("selector", metagen::mdTypeSelector); - setType("pointer", metagen::mdTypePointer); - setType("block", metagen::mdTypeBlock); - setType("functionPointer", metagen::mdTypeFunctionPointer); - interop.setProperty(runtime, "types", types); - - Function pointerConstructor = Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "Pointer"), 1, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count > 0 && args[0].isObject()) { - Object object = args[0].asObject(runtime); - if (object.isHostObject(runtime)) { - return Value(runtime, object); - } - } - void* pointer = nullptr; - if (count > 0 && !args[0].isNull() && !args[0].isUndefined()) { - auto readAddress = [&](const Value& value, - uintptr_t* address) -> bool { - auto readAddressFromString = [&](const Value& source) -> bool { - try { - Value stringCtorValue = - runtime.global().getProperty(runtime, "String"); - if (!stringCtorValue.isObject() || - !stringCtorValue.asObject(runtime).isFunction(runtime)) { - return false; - } - Value stringValue = - stringCtorValue.asObject(runtime).asFunction(runtime) - .call(runtime, source); - if (!stringValue.isString()) { - return false; - } - return parseIntegerTextToUintptr( - stringValue.asString(runtime).utf8(runtime), address); - } catch (const std::exception&) { - return false; - } - }; - - if (value.isNumber()) { - double number = value.getNumber(); - if (!std::isfinite(number)) { - return false; - } - *address = static_cast( - static_cast(number)); - return true; - } - if (value.isBigInt()) { - if (readAddressFromString(value)) { - return true; - } - BigInt bigint = value.getBigInt(runtime); - return parseBigIntToUintptr(runtime, bigint, address); - } - if (value.isObject()) { - Object object = value.asObject(runtime); - Value valueOfValue = object.getProperty(runtime, "valueOf"); - if (valueOfValue.isObject() && - valueOfValue.asObject(runtime).isFunction(runtime)) { - Value primitive = valueOfValue.asObject(runtime) - .asFunction(runtime) - .callWithThis(runtime, object, nullptr, 0); - if (primitive.isNumber()) { - double number = primitive.getNumber(); - if (!std::isfinite(number)) { - return false; - } - *address = static_cast( - static_cast(number)); - return true; - } - if (primitive.isBigInt()) { - if (readAddressFromString(primitive)) { - return true; - } - BigInt bigint = primitive.getBigInt(runtime); - return parseBigIntToUintptr(runtime, bigint, address); - } - } - return readAddressFromString(value); - } - return false; - }; - - uintptr_t address = 0; - if (!readAddress(args[0], &address)) { - throw facebook::jsi::JSError(runtime, - "Pointer expects a numeric address."); - } - pointer = reinterpret_cast(address); - } - return createPointer(runtime, bridge, pointer); - }); - Object pointerPrototype(runtime); - pointerPrototype.setProperty(runtime, "constructor", pointerConstructor); - pointerConstructor.setProperty(runtime, "prototype", pointerPrototype); - installInteropHasInstance(runtime, pointerConstructor, "pointer"); - pointerConstructor.setProperty(runtime, "kind", makeString(runtime, "pointer")); - pointerConstructor.setProperty(runtime, "sizeof", - static_cast(sizeof(void*))); - interop.setProperty(runtime, "Pointer", pointerConstructor); - - Function functionReferenceConstructor = Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "FunctionReference"), 1, - [](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count < 1 || !args[0].isObject()) { - throw facebook::jsi::JSError( - runtime, "FunctionReference expects a function."); - } - - Object object = args[0].asObject(runtime); - if (!object.isFunction(runtime)) { - throw facebook::jsi::JSError( - runtime, "FunctionReference expects a function."); - } - - Function function = object.asFunction(runtime); - function.setProperty(runtime, "kind", - makeString(runtime, "functionReference")); - function.setProperty(runtime, "sizeof", - static_cast(sizeof(void*))); - return function; - }); - Object functionReferencePrototype(runtime); - functionReferencePrototype.setProperty(runtime, "constructor", - functionReferenceConstructor); - functionReferenceConstructor.setProperty(runtime, "prototype", - functionReferencePrototype); - installInteropHasInstance(runtime, functionReferenceConstructor, - "functionReference"); - functionReferenceConstructor.setProperty(runtime, "kind", - makeString(runtime, - "functionReference")); - functionReferenceConstructor.setProperty(runtime, "sizeof", - static_cast(sizeof(void*))); - interop.setProperty(runtime, "FunctionReference", - functionReferenceConstructor); - - Function referenceConstructor = Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "Reference"), 2, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - NativeApiJsiType type = primitiveInteropType(metagen::mdTypePointer); - bool firstArgumentIsType = false; - if (count > 1) { - firstArgumentIsType = true; - } else if (count == 1 && args[0].isObject()) { - Object object = args[0].asObject(runtime); - Value typeCodeValue = - object.getProperty(runtime, "__nativeApiTypeCode"); - Value kindValue = object.getProperty(runtime, "kind"); - firstArgumentIsType = - typeCodeValue.isNumber() || object.isFunction(runtime) || - nativeClassFromJsiObject(runtime, object) != Nil || - (kindValue.isString() && - (kindValue.asString(runtime).utf8(runtime) == "class" || - kindValue.asString(runtime).utf8(runtime) == "protocol")); - } - std::optional requestedType = - firstArgumentIsType - ? interopTypeFromValue(runtime, bridge, args[0]) - : std::nullopt; - bool hasType = firstArgumentIsType && requestedType.has_value(); - if (hasType) { - type = *requestedType; - } - - void* data = nullptr; - bool ownsData = false; - size_t byteLength = 0; - std::shared_ptr pendingValue; - if (hasType) { - bool usesExternalStorage = false; - Value valueToStore = Value::undefined(); - if (count > 1) { - valueToStore = Value(runtime, args[1]); - if (args[1].isObject()) { - Object object = args[1].asObject(runtime); - if (object.isHostObject(runtime)) { - data = object - .getHostObject( - runtime) - ->pointer(); - usesExternalStorage = true; - } else if (object.isHostObject( - runtime)) { - auto reference = - object.getHostObject( - runtime); - data = reference->data(); - if (data != nullptr) { - usesExternalStorage = true; - } else { - valueToStore = object.getProperty(runtime, "value"); - } - } else if (type.kind == metagen::mdTypeStruct && - object.isHostObject< - NativeApiStructObjectHostObject>(runtime)) { - data = object - .getHostObject< - NativeApiStructObjectHostObject>(runtime) - ->data(); - usesExternalStorage = true; - } else if (type.kind == metagen::mdTypePointer || - type.kind == metagen::mdTypeOpaquePointer || - type.kind == metagen::mdTypeBlock || - type.kind == metagen::mdTypeFunctionPointer) { - void* nativePointer = nullptr; - if (readNativePointerProperty(runtime, object, - &nativePointer)) { - data = nativePointer; - usesExternalStorage = true; - } - } - } - } - if (!usesExternalStorage) { - byteLength = std::max(nativeSizeForType(type), - sizeof(void*)); - data = calloc(1, byteLength); - if (data == nullptr) { - throw std::bad_alloc(); - } - ownsData = true; - if (count > 1) { - NativeApiJsiArgumentFrame frame(1); - convertJsiArgument(runtime, bridge, type, valueToStore, data, - frame); - } - } - } else if (count > 0) { - pendingValue = std::make_shared(runtime, args[0]); - } - - if (ownsData && data == nullptr) { - throw std::bad_alloc(); - } - return Object::createFromHostObject( - runtime, std::make_shared( - bridge, type, data, ownsData, byteLength, - std::move(pendingValue))); - }); - Object referencePrototype(runtime); - referencePrototype.setProperty(runtime, "constructor", referenceConstructor); - referenceConstructor.setProperty(runtime, "prototype", referencePrototype); - installInteropHasInstance(runtime, referenceConstructor, "reference"); - referenceConstructor.setProperty(runtime, "kind", - makeString(runtime, "reference")); - referenceConstructor.setProperty(runtime, "sizeof", - static_cast(sizeof(void*))); - interop.setProperty(runtime, "Reference", referenceConstructor); - - interop.setProperty( - runtime, "sizeof", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "sizeof"), 1, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count < 1) { - throw facebook::jsi::JSError(runtime, "sizeof expects a type."); - } - return static_cast(sizeofInteropType(runtime, bridge, args[0])); - })); - - interop.setProperty( - runtime, "alloc", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "alloc"), 1, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count < 1 || !args[0].isNumber()) { - throw facebook::jsi::JSError(runtime, "alloc expects a byte size."); - } - size_t size = static_cast(std::max(0, args[0].getNumber())); - return createPointer(runtime, bridge, calloc(1, size), false); - })); - - interop.setProperty( - runtime, "free", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "free"), 1, - [](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count < 1 || !args[0].isObject()) { - return Value::undefined(); - } - Object object = args[0].asObject(runtime); - if (!object.isHostObject(runtime)) { - return Value::undefined(); - } - auto pointer = object.getHostObject(runtime); - void* raw = pointer->pointer(); - if (raw != nullptr) { - free(raw); - pointer->clearWithoutFree(); - } - return Value::undefined(); - })); - - interop.setProperty( - runtime, "adopt", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "adopt"), 1, - [](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count < 1 || !args[0].isObject()) { - throw facebook::jsi::JSError(runtime, "adopt expects a Pointer."); - } - Object object = args[0].asObject(runtime); - if (!object.isHostObject(runtime)) { - throw facebook::jsi::JSError(runtime, "adopt expects a Pointer."); - } - object.getHostObject(runtime)->adopt(); - return Value(runtime, object); - })); - - interop.setProperty( - runtime, "handleof", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "handleof"), 1, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count < 1 || args[0].isNull() || args[0].isUndefined()) { - return Value::null(); - } - if (args[0].isString()) { - std::string utf8 = args[0].asString(runtime).utf8(runtime); - char* data = strdup(utf8.c_str()); - return createPointer(runtime, bridge, data); - } - if (!args[0].isObject()) { - return Value::null(); - } - Object object = args[0].asObject(runtime); - if (object.isHostObject(runtime)) { - return Value(runtime, object); - } - if (object.isHostObject(runtime)) { - void* data = - object.getHostObject(runtime)->data(); - if (data == nullptr) { - throw facebook::jsi::JSError( - runtime, "Cannot get handle of empty Reference."); - } - return createPointer(runtime, bridge, data); - } - if (object.isHostObject(runtime)) { - auto structObject = - object.getHostObject(runtime); - if (structObject->backingValue() != nullptr) { - return Value(runtime, *structObject->backingValue()); - } - return createPointer(runtime, bridge, structObject->data()); - } - if (object.isHostObject(runtime)) { - return createPointer( - runtime, bridge, - object.getHostObject(runtime) - ->object()); - } - if (Class cls = nativeClassFromJsiObject(runtime, object)) { - return createPointer(runtime, bridge, cls); - } - if (object.isHostObject(runtime)) { - return createPointer( - runtime, bridge, - object.getHostObject(runtime) - ->nativeProtocol()); - } - if (void* symbolPointer = pointerFromSymbolLikeObject(runtime, object)) { - return createPointer(runtime, bridge, symbolPointer); - } - void* nativePointer = nullptr; - if (readNativePointerProperty(runtime, object, &nativePointer)) { - return createPointer(runtime, bridge, nativePointer); - } - Value kindValue = object.getProperty(runtime, "kind"); - if (kindValue.isString() && - kindValue.asString(runtime).utf8(runtime) == "functionReference") { - throw facebook::jsi::JSError( - runtime, "Cannot get handle of uninitialized FunctionReference."); - } - Value nativeName = object.getProperty(runtime, "nativeName"); - if (nativeName.isString()) { - std::string name = nativeName.asString(runtime).utf8(runtime); - void* symbol = dlsym(bridge->selfDl(), name.c_str()); - if (symbol != nullptr) { - return createPointer(runtime, bridge, symbol); - } - } - return Value::null(); - })); - - interop.setProperty( - runtime, "stringFromCString", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "stringFromCString"), 2, - [](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count < 1 || args[0].isNull() || args[0].isUndefined()) { - return Value::null(); - } - NativeApiJsiArgumentFrame frame(1); - const char* data = - static_cast(pointerFromJsiValue(runtime, args[0], frame)); - if (data == nullptr) { - return Value::null(); - } - if (count > 1 && args[1].isNumber()) { - size_t length = static_cast(std::max(0, args[1].getNumber())); - return String::createFromUtf8(runtime, - reinterpret_cast(data), - length); - } - return makeString(runtime, data); - })); - - interop.setProperty( - runtime, "bufferFromData", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "bufferFromData"), 1, - [](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count < 1 || !args[0].isObject()) { - throw facebook::jsi::JSError(runtime, "Invalid data."); - } - Object object = args[0].asObject(runtime); - if (object.isArrayBuffer(runtime)) { - return Value(runtime, object); - } - id native = nil; - if (object.isHostObject(runtime)) { - native = object.getHostObject(runtime)->object(); - } else if (object.isHostObject(runtime)) { - native = static_cast( - object.getHostObject(runtime)->pointer()); - } - if (native == nil || ![native isKindOfClass:[NSData class]]) { - throw facebook::jsi::JSError(runtime, "Invalid data."); - } - NSData* data = static_cast(native); - return ArrayBuffer( - runtime, std::make_shared( - data.bytes, static_cast(data.length))); - })); - - interop.setProperty( - runtime, "addMethod", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "addMethod"), 2, - [](Runtime& runtime, const Value&, const Value*, size_t) -> Value { - throw facebook::jsi::JSError( - runtime, - "interop.addMethod requires the JSI class builder layer."); - })); - interop.setProperty( - runtime, "addProtocol", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "addProtocol"), 2, - [](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count < 2) { - throw facebook::jsi::JSError( - runtime, "interop.addProtocol expects class and protocol."); - } - Class cls = classFromJsiValue(runtime, args[0]); - Protocol* protocol = protocolFromJsiValue(runtime, args[1]); - if (cls == Nil || protocol == nullptr) { - return false; - } - return class_addProtocol(cls, protocol); - })); - - return interop; -} diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiHostObject.h b/NativeScript/ffi/shared/jsi/NativeApiJsiHostObject.h deleted file mode 100644 index 06b82774d..000000000 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiHostObject.h +++ /dev/null @@ -1,560 +0,0 @@ -#ifndef NATIVESCRIPT_NATIVE_API_HAS_ENGINE_LAZY_GLOBALS -inline bool InstallNativeApiEngineLazyGlobal( - Runtime&, std::shared_ptr, const std::string&, - const std::string&, bool) { - return false; -} -#endif - -class NativeApiHostObject final : public HostObject { - public: - explicit NativeApiHostObject(std::shared_ptr bridge) - : bridge_(std::move(bridge)) {} - - Value get(Runtime& runtime, const PropNameID& name) override { - std::string property = name.utf8(runtime); - if (property == "runtime") { - return makeString(runtime, "jsi"); - } - if (property == "backend") { - return makeString(runtime, "hermes"); - } - if (property == "metadata") { - return metadataObject(runtime); - } - if (property == "hasScheduler") { - return bridge_->scheduler() != nullptr; - } - if (property == "interop") { - return createInteropObject(runtime, bridge_); - } -#ifdef NATIVESCRIPT_NATIVE_API_HAS_ENGINE_LAZY_GLOBALS - if (property == "__defineLazyGlobal") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "__defineLazyGlobal"), 3, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - std::string name = readStringArg(runtime, args, count, 0, "name"); - std::string kind = readStringArg(runtime, args, count, 1, "kind"); - bool force = count > 2 && args[2].isBool() && args[2].getBool(); - return InstallNativeApiEngineLazyGlobal(runtime, bridge, name, kind, - force); - }); - } -#endif - if (property == "__fastEnumeration") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "__fastEnumeration"), 1, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count < 1 || !args[0].isObject()) { - throw facebook::jsi::JSError( - runtime, "Fast enumeration expects a native object."); - } - id object = NativeApiObjectHostObject::nativeObjectFromValue(runtime, args[0]); - if (object == nil) { - throw facebook::jsi::JSError( - runtime, "Fast enumeration expects a native object."); - } - if (![object conformsToProtocol:@protocol(NSFastEnumeration)]) { - throw facebook::jsi::JSError( - runtime, "Object does not conform to NSFastEnumeration."); - } - return Object::createFromHostObject( - runtime, - std::make_shared( - bridge, static_cast>(object))); - }); - } - if (property == "runOnUI") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "runOnUI"), 1, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - auto scheduler = bridge->scheduler(); - if (scheduler == nullptr) { - throw facebook::jsi::JSError( - runtime, - "NativeApiJsi was installed without a UI scheduler."); - } - - std::shared_ptr callback; - if (count > 0 && !args[0].isNull() && !args[0].isUndefined()) { - if (!args[0].isObject()) { - throw facebook::jsi::JSError( - runtime, "runOnUI expects a function callback."); - } - - Object callbackObject = args[0].asObject(runtime); - if (!callbackObject.isFunction(runtime)) { - throw facebook::jsi::JSError( - runtime, "runOnUI expects a function callback."); - } - callback = std::make_shared( - callbackObject.asFunction(runtime)); - } - - Runtime* runtimePtr = &runtime; - auto promiseCtor = - runtime.global().getPropertyAsFunction(runtime, "Promise"); - return promiseCtor.callAsConstructor( - runtime, - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "runOnUIPromise"), - 2, - [scheduler, runtimePtr, callback]( - Runtime& promiseRuntime, const Value&, - const Value* promiseArgs, - size_t promiseArgc) -> Value { - if (promiseArgc < 2 || !promiseArgs[0].isObject() || - !promiseArgs[1].isObject()) { - return Value::undefined(); - } - - auto resolve = std::make_shared( - promiseArgs[0].asObject(promiseRuntime) - .asFunction(promiseRuntime)); - auto reject = std::make_shared( - promiseArgs[1].asObject(promiseRuntime) - .asFunction(promiseRuntime)); - if (callback == nullptr) { - scheduler->invokeOnUI([scheduler, runtimePtr, resolve]() { - scheduler->invokeOnJS([runtimePtr, resolve]() { - resolve->call(*runtimePtr); - }); - }); - return Value::undefined(); - } - - scheduler->invokeOnJS([runtimePtr, callback, resolve, reject]() { - try { - { - ScopedNativeApiUINativeCallDispatch uiDispatch; - callback->call(*runtimePtr); - } - resolve->call(*runtimePtr); - } catch (const std::exception& error) { - reject->call( - *runtimePtr, - String::createFromUtf8(*runtimePtr, error.what())); - } - }); - - return Value::undefined(); - })); - }); - } - if (property == "import") { - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "import"), 1, - [](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - std::string path = readStringArg(runtime, args, count, 0, "path"); - std::string frameworkPath = path; - if (!frameworkPath.empty() && frameworkPath[0] != '/') { - frameworkPath = "/System/Library/Frameworks/" + frameworkPath + - ".framework"; - } - - NSBundle* bundle = [NSBundle - bundleWithPath:[NSString stringWithUTF8String:frameworkPath.c_str()]]; - if (bundle == nil || ![bundle load]) { - throw facebook::jsi::JSError( - runtime, "Could not load bundle: " + frameworkPath); - } - return true; - }); - } - if (property == "lookup") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "lookup"), 1, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - std::string symbolName = - readStringArg(runtime, args, count, 0, "name"); - const NativeApiSymbol* symbol = bridge->find(symbolName); - if (symbol == nullptr) { - return Value::null(); - } - return symbolToObject(runtime, *symbol); - }); - } - if (property == "getClass") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "getClass"), 1, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - std::string className = - readStringArg(runtime, args, count, 0, "name"); - const NativeApiSymbol* symbol = bridge->findClass(className); - if (symbol == nullptr) { - Class cls = objc_lookUpClass(className.c_str()); - if (cls == nil) { - return Value::null(); - } - NativeApiSymbol runtimeSymbol{ - .kind = NativeApiSymbolKind::Class, - .offset = MD_SECTION_OFFSET_NULL, - .name = className, - .runtimeName = className, - }; - return makeNativeClassValue(runtime, bridge, - std::move(runtimeSymbol)); - } - - return makeNativeClassValue(runtime, bridge, *symbol); - }); - } - if (property == "__extendClass") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "__extendClass"), 2, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - return extendNativeApiJsiClass(runtime, bridge, args, count); - }); - } - if (property == "__invokeBase") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "__invokeBase"), 3, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - return invokeNativeApiJsiBaseMethod(runtime, bridge, args, count); - }); - } - if (property == "__rememberClassWrapper") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "__rememberClassWrapper"), 3, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count < 2) { - return Value::undefined(); - } - Class cls = classFromJsiValue(runtime, args[0]); - if (cls == Nil) { - return Value::undefined(); - } - bridge->rememberClassValue(runtime, cls, args[1]); - if (count >= 3 && args[2].isObject()) { - bridge->rememberClassPrototype(runtime, cls, args[2]); - } - return Value::undefined(); - }); - } - if (property == "__rememberObjectClassWrapper") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "__rememberObjectClassWrapper"), - 2, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count < 2) { - return Value::undefined(); - } - id object = NativeApiObjectHostObject::nativeObjectFromValue( - runtime, args[0]); - if (object == nil) { - return Value::undefined(); - } - bridge->setObjectExpando(runtime, object, - "__nativeApiClassWrapper", args[1]); - return Value::undefined(); - }); - } - if (property == "CC_SHA256") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "CC_SHA256"), 3, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (count < 3 || !args[1].isNumber()) { - throw facebook::jsi::JSError( - runtime, "CC_SHA256 expects data, length, and output."); - } - void* commonCrypto = - dlopen("/usr/lib/system/libcommonCrypto.dylib", - RTLD_NOW | RTLD_LOCAL); - void* symbol = commonCrypto != nullptr - ? dlsym(commonCrypto, "CC_SHA256") - : nullptr; - if (symbol == nullptr && commonCrypto != nullptr) { - symbol = dlsym(commonCrypto, "_CC_SHA256"); - } - if (symbol == nullptr) { - throw facebook::jsi::JSError(runtime, - "CC_SHA256 is not available."); - } - NativeApiJsiArgumentFrame frame(3); - void* data = pointerFromJsiValue(runtime, args[0], frame); - void* output = pointerFromJsiValue(runtime, args[2], frame); - using CC_SHA256_Fn = unsigned char* (*)(const void*, unsigned long, - unsigned char*); - auto fn = reinterpret_cast(symbol); - unsigned char* result = - fn(data, static_cast(args[1].getNumber()), - static_cast(output)); - return createPointer(runtime, bridge, result); - }); - } - if (property == "getFunction") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "getFunction"), 1, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - std::string functionName = - readStringArg(runtime, args, count, 0, "name"); - const NativeApiSymbol* symbol = bridge->findFunction(functionName); - if (symbol == nullptr) { - return Value::null(); - } - auto function = Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, symbol->name), 0, - [bridge, symbol = *symbol](Runtime& runtime, const Value&, - const Value* args, - size_t count) -> Value { - return callCFunction(runtime, bridge, symbol, args, count); - }); - function.setProperty(runtime, "kind", makeString(runtime, "function")); - function.setProperty(runtime, "nativeName", - makeString(runtime, symbol->name)); - function.setProperty(runtime, "metadataOffset", - static_cast(symbol->offset)); - function.setProperty(runtime, "sizeof", - static_cast(sizeof(void*))); - return function; - }); - } - if (property == "getConstant") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "getConstant"), 1, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - std::string constantName = - readStringArg(runtime, args, count, 0, "name"); - const NativeApiSymbol* symbol = bridge->findConstant(constantName); - if (symbol == nullptr) { - return Value::undefined(); - } - return constantToValue(runtime, bridge, *symbol); - }); - } - if (property == "getEnum") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "getEnum"), 1, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - std::string enumName = readStringArg(runtime, args, count, 0, "name"); - const NativeApiSymbol* symbol = bridge->findEnum(enumName); - if (symbol == nullptr) { - return Value::undefined(); - } - return enumToObject(runtime, bridge->metadata(), *symbol); - }); - } - if (property == "getProtocol") { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "getProtocol"), 1, - [bridge](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - std::string protocolName = - readStringArg(runtime, args, count, 0, "name"); - const NativeApiSymbol* symbol = bridge->findProtocol(protocolName); - if (symbol == nullptr) { - Protocol* protocol = lookupProtocolByNativeName(protocolName); - if (protocol == nullptr) { - return Value::null(); - } - const char* runtimeName = protocol_getName(protocol); - NativeApiSymbol runtimeSymbol{ - .kind = NativeApiSymbolKind::Protocol, - .offset = MD_SECTION_OFFSET_NULL, - .name = protocolName, - .runtimeName = runtimeName != nullptr ? runtimeName : protocolName, - }; - return makeNativeProtocolValue(runtime, bridge, - std::move(runtimeSymbol)); - } - return makeNativeProtocolValue(runtime, bridge, *symbol); - }); - } - if (property == "getStruct" || property == "getUnion") { - auto bridge = bridge_; - bool isUnion = property == "getUnion"; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 1, - [bridge, isUnion](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - std::string aggregateName = - readStringArg(runtime, args, count, 0, "name"); - const NativeApiSymbol* symbol = - isUnion ? bridge->findUnion(aggregateName) - : bridge->findStruct(aggregateName); - if (symbol == nullptr) { - return Value::undefined(); - } - return makeAggregateConstructor(runtime, bridge, *symbol); - }); - } - - if (const NativeApiSymbol* classSymbol = bridge_->findClass(property)) { - return makeNativeClassValue(runtime, bridge_, *classSymbol); - } - - if (const NativeApiSymbol* functionSymbol = bridge_->findFunction(property)) { - auto bridge = bridge_; - Function function = Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 0, - [bridge, symbol = *functionSymbol](Runtime& runtime, const Value&, - const Value* args, - size_t count) -> Value { - return callCFunction(runtime, bridge, symbol, args, count); - }); - function.setProperty(runtime, "kind", makeString(runtime, "function")); - function.setProperty(runtime, "nativeName", - makeString(runtime, functionSymbol->name)); - function.setProperty(runtime, "metadataOffset", - static_cast(functionSymbol->offset)); - function.setProperty(runtime, "sizeof", - static_cast(sizeof(void*))); - return function; - } - - if (const NativeApiSymbol* constantSymbol = bridge_->findConstant(property)) { - return constantToValue(runtime, bridge_, *constantSymbol); - } - - if (const NativeApiSymbol* enumSymbol = bridge_->findEnum(property)) { - return enumToObject(runtime, bridge_->metadata(), *enumSymbol); - } - - if (const NativeApiSymbol* protocolSymbol = - bridge_->findProtocol(property)) { - return makeNativeProtocolValue(runtime, bridge_, *protocolSymbol); - } - - if (const NativeApiSymbol* aggregateSymbol = - bridge_->findAggregate(property)) { - return makeAggregateConstructor(runtime, bridge_, *aggregateSymbol); - } - - return Value::undefined(); - } - - std::vector getPropertyNames(Runtime& runtime) override { - std::vector names; - names.reserve(11); - addPropertyName(runtime, names, "runtime"); - addPropertyName(runtime, names, "backend"); - addPropertyName(runtime, names, "metadata"); - addPropertyName(runtime, names, "hasScheduler"); - addPropertyName(runtime, names, "interop"); -#ifdef NATIVESCRIPT_NATIVE_API_HAS_ENGINE_LAZY_GLOBALS - addPropertyName(runtime, names, "__defineLazyGlobal"); -#endif - addPropertyName(runtime, names, "runOnUI"); - addPropertyName(runtime, names, "import"); - addPropertyName(runtime, names, "lookup"); - addPropertyName(runtime, names, "getClass"); - addPropertyName(runtime, names, "__extendClass"); - addPropertyName(runtime, names, "__invokeBase"); - addPropertyName(runtime, names, "__rememberClassWrapper"); - addPropertyName(runtime, names, "__rememberObjectClassWrapper"); - addPropertyName(runtime, names, "getFunction"); - addPropertyName(runtime, names, "getConstant"); - addPropertyName(runtime, names, "getEnum"); - addPropertyName(runtime, names, "getProtocol"); - addPropertyName(runtime, names, "getStruct"); - addPropertyName(runtime, names, "getUnion"); - return names; - } - - private: - Object metadataObject(Runtime& runtime) const { - Object metadata(runtime); - metadata.setProperty(runtime, "classes", - static_cast(bridge_->classCount())); - metadata.setProperty(runtime, "functions", - static_cast(bridge_->functionCount())); - metadata.setProperty(runtime, "constants", - static_cast(bridge_->constantCount())); - metadata.setProperty(runtime, "protocols", - static_cast(bridge_->protocolCount())); - metadata.setProperty(runtime, "enums", - static_cast(bridge_->enumCount())); - metadata.setProperty(runtime, "structs", - static_cast(bridge_->structCount())); - metadata.setProperty(runtime, "unions", - static_cast(bridge_->unionCount())); - - metadata.setProperty( - runtime, "classNames", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "classNames"), 0, - [bridge = bridge_](Runtime& runtime, const Value&, const Value*, - size_t) -> Value { - return namesToArray(runtime, bridge->classNames()); - })); - metadata.setProperty( - runtime, "functionNames", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "functionNames"), 0, - [bridge = bridge_](Runtime& runtime, const Value&, const Value*, - size_t) -> Value { - return namesToArray(runtime, bridge->functionNames()); - })); - metadata.setProperty( - runtime, "constantNames", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "constantNames"), 0, - [bridge = bridge_](Runtime& runtime, const Value&, const Value*, - size_t) -> Value { - return namesToArray(runtime, bridge->constantNames()); - })); - metadata.setProperty( - runtime, "protocolNames", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "protocolNames"), 0, - [bridge = bridge_](Runtime& runtime, const Value&, const Value*, - size_t) -> Value { - return namesToArray(runtime, bridge->protocolNames()); - })); - metadata.setProperty( - runtime, "enumNames", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "enumNames"), 0, - [bridge = bridge_](Runtime& runtime, const Value&, const Value*, - size_t) -> Value { - return namesToArray(runtime, bridge->enumNames()); - })); - metadata.setProperty( - runtime, "structNames", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "structNames"), 0, - [bridge = bridge_](Runtime& runtime, const Value&, const Value*, - size_t) -> Value { - return namesToArray(runtime, bridge->structNames()); - })); - metadata.setProperty( - runtime, "unionNames", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "unionNames"), 0, - [bridge = bridge_](Runtime& runtime, const Value&, const Value*, - size_t) -> Value { - return namesToArray(runtime, bridge->unionNames()); - })); - return metadata; - } - - std::shared_ptr bridge_; -}; diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiHostObjects.h b/NativeScript/ffi/shared/jsi/NativeApiJsiHostObjects.h deleted file mode 100644 index a1dbf289d..000000000 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiHostObjects.h +++ /dev/null @@ -1,1759 +0,0 @@ -class NativeApiPointerHostObject final - : public HostObject, - public std::enable_shared_from_this { - public: - NativeApiPointerHostObject(std::shared_ptr bridge, - void* pointer, std::string kind = "pointer", - bool adopted = false) - : bridge_(std::move(bridge)), - pointer_(pointer), - kind_(std::move(kind)), - adopted_(adopted) {} - - ~NativeApiPointerHostObject() override { - if (adopted_ && pointer_ != nullptr) { - if (bridge_ != nullptr) { - bridge_->forgetPointerValue(pointer_); - } - free(pointer_); - pointer_ = nullptr; - } - } - - void* pointer() const { return pointer_; } - bool adopted() const { return adopted_; } - void adopt() { adopted_ = true; } - void clearWithoutFree() { - if (bridge_ != nullptr) { - bridge_->forgetPointerValue(pointer_); - } - pointer_ = nullptr; - adopted_ = false; - } - - Value get(Runtime& runtime, const PropNameID& name) override { - std::string property = name.utf8(runtime); - if (property == "kind") { - return makeString(runtime, kind_); - } - if (property == "address") { - return static_cast(reinterpret_cast(pointer_)); - } - if (property == "adopted") { - return adopted_; - } - if (property == "takeRetainedValue" || property == "takeUnretainedValue") { - bool retained = property == "takeRetainedValue"; - std::weak_ptr weakSelf = shared_from_this(); - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 0, - [weakSelf, retained](Runtime& runtime, const Value&, const Value*, - size_t) -> Value { - auto self = weakSelf.lock(); - if (!self || self->pointer_ == nullptr || self->consumed_) { - throw facebook::jsi::JSError(runtime, "Unmanaged value has already been consumed."); - } - id object = static_cast(self->pointer_); - self->consumed_ = true; - self->pointer_ = nullptr; - self->adopted_ = false; - return makeNativeObjectValue(runtime, self->bridge_, object, retained); - }); - } - if (property == "add" || property == "subtract") { - void* pointer = pointer_; - bool add = property == "add"; - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 1, - [bridge, pointer, add](Runtime& runtime, const Value&, - const Value* args, size_t count) -> Value { - if (count < 1 || !args[0].isNumber()) { - throw facebook::jsi::JSError(runtime, "Pointer offset must be a number."); - } - intptr_t offset = static_cast(args[0].getNumber()); - intptr_t base = reinterpret_cast(pointer); - void* result = reinterpret_cast(add ? base + offset : base - offset); - return createPointer(runtime, bridge, result); - }); - } - if (property == "toNumber") { - void* pointer = pointer_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "toNumber"), 0, - [pointer](Runtime&, const Value&, const Value*, size_t) -> Value { - return static_cast(reinterpret_cast(pointer)); - }); - } - if (property == "toBigInt") { - void* pointer = pointer_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "toBigInt"), 0, - [pointer](Runtime& runtime, const Value&, const Value*, size_t) -> Value { - return BigInt::fromUint64( - runtime, - static_cast(reinterpret_cast(pointer))); - }); - } - if (property == "toHexString" || property == "toDecimalString") { - void* pointer = pointer_; - bool hex = property == "toHexString"; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 0, - [pointer, hex](Runtime& runtime, const Value&, const Value*, size_t) -> Value { - if (hex) { - char text[2 + sizeof(uintptr_t) * 2 + 1] = {}; - snprintf(text, sizeof(text), "0x%llx", - static_cast( - reinterpret_cast(pointer))); - return makeString(runtime, text); - } else { - char text[32] = {}; - snprintf(text, sizeof(text), "%lld", - static_cast(reinterpret_cast(pointer))); - return makeString(runtime, text); - } - }); - } - if (property == "toString") { - void* pointer = pointer_; - std::string kind = kind_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "toString"), 0, - [pointer, kind](Runtime& runtime, const Value&, const Value*, - size_t) -> Value { - char address[32] = {}; - snprintf(address, sizeof(address), "%p", pointer); - if (kind == "pointer") { - return makeString(runtime, - ""); - } - return makeString(runtime, "[NativeApiJsi " + kind + " " + - std::string(address) + "]"); - }); - } - return Value::undefined(); - } - - std::vector getPropertyNames(Runtime& runtime) override { - std::vector names; - names.reserve(3); - addPropertyName(runtime, names, "kind"); - addPropertyName(runtime, names, "address"); - addPropertyName(runtime, names, "adopted"); - addPropertyName(runtime, names, "takeRetainedValue"); - addPropertyName(runtime, names, "takeUnretainedValue"); - addPropertyName(runtime, names, "add"); - addPropertyName(runtime, names, "subtract"); - addPropertyName(runtime, names, "toNumber"); - addPropertyName(runtime, names, "toBigInt"); - addPropertyName(runtime, names, "toHexString"); - addPropertyName(runtime, names, "toDecimalString"); - addPropertyName(runtime, names, "toString"); - return names; - } - - private: - std::shared_ptr bridge_; - void* pointer_ = nullptr; - std::string kind_; - bool adopted_ = false; - bool consumed_ = false; -}; - -class NativeApiReferenceHostObject final : public HostObject { - public: - NativeApiReferenceHostObject(std::shared_ptr bridge, - NativeApiJsiType type, void* data, bool ownsData, - size_t byteLength = 0, - std::shared_ptr pendingValue = nullptr, - std::shared_ptr backingValue = nullptr) - : bridge_(std::move(bridge)), - type_(std::move(type)), - data_(data), - ownsData_(ownsData), - byteLength_(byteLength), - pendingValue_(std::move(pendingValue)), - backingValue_(std::move(backingValue)) {} - - ~NativeApiReferenceHostObject() override { - if (ownsData_ && data_ != nullptr) { - free(data_); - data_ = nullptr; - } - } - - void* data() const { return data_; } - const NativeApiJsiType& type() const { return type_; } - void ensureStorage(Runtime& runtime, NativeApiJsiType type, - NativeApiJsiArgumentFrame& frame, size_t elements = 1); - - Value get(Runtime& runtime, const PropNameID& name) override; - void set(Runtime& runtime, const PropNameID& name, const Value& value) override; - std::vector getPropertyNames(Runtime& runtime) override { - std::vector names; - addPropertyName(runtime, names, "kind"); - addPropertyName(runtime, names, "value"); - addPropertyName(runtime, names, "address"); - addPropertyName(runtime, names, "toString"); - return names; - } - - private: - std::shared_ptr bridge_; - NativeApiJsiType type_; - void* data_ = nullptr; - bool ownsData_ = false; - size_t byteLength_ = 0; - std::shared_ptr pendingValue_; - std::shared_ptr backingValue_; -}; - -class NativeApiStructObjectHostObject final : public HostObject { - public: - NativeApiStructObjectHostObject( - std::shared_ptr bridge, - std::shared_ptr info, - const void* data = nullptr, bool ownsData = true, - std::shared_ptr> storageOwner = nullptr, - std::shared_ptr backingValue = nullptr) - : bridge_(std::move(bridge)), - info_(std::move(info)), - ownedData_(std::move(storageOwner)), - backingValue_(std::move(backingValue)), - ownsData_(ownsData) { - size_t size = info_ != nullptr ? info_->size : 0; - if (ownedData_ != nullptr) { - data_ = const_cast(data); - ownsData_ = false; - } else if (ownsData_) { - ownedData_ = std::make_shared>(size, 0); - if (data != nullptr && size > 0) { - std::memcpy(ownedData_->data(), data, size); - } - data_ = ownedData_->empty() ? nullptr : ownedData_->data(); - } else { - data_ = const_cast(data); - } - } - - void* data() const { return data_; } - std::shared_ptr info() const { return info_; } - std::shared_ptr> storageOwner() const { - return ownedData_; - } - std::shared_ptr backingValue() const { return backingValue_; } - - Value get(Runtime& runtime, const PropNameID& name) override; - void set(Runtime& runtime, const PropNameID& name, const Value& value) override; - std::vector getPropertyNames(Runtime& runtime) override; - - private: - std::shared_ptr bridge_; - std::shared_ptr info_; - std::shared_ptr> ownedData_; - std::shared_ptr backingValue_; - void* data_ = nullptr; - bool ownsData_ = true; -}; - -class NativeApiFastEnumerationIteratorHostObject final : public HostObject { - public: - NativeApiFastEnumerationIteratorHostObject( - std::shared_ptr bridge, id collection) - : bridge_(std::move(bridge)), collection_(collection) { - [(id)collection_ retain]; - } - - ~NativeApiFastEnumerationIteratorHostObject() override { - [(id)collection_ release]; - collection_ = nil; - } - - Value get(Runtime& runtime, const PropNameID& name) override { - std::string property = name.utf8(runtime); - if (property == "next") { - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "next"), 0, - [this](Runtime& runtime, const Value&, const Value*, size_t) -> Value { - return next(runtime); - }); - } - return Value::undefined(); - } - - std::vector getPropertyNames(Runtime& runtime) override { - std::vector names; - addPropertyName(runtime, names, "next"); - return names; - } - - private: - Value next(Runtime& runtime) { - Object result(runtime); - if (done_ || collection_ == nil) { - result.setProperty(runtime, "done", true); - return result; - } - - if (stackIndex_ >= stackLength_) { - stackLength_ = [collection_ countByEnumeratingWithState:&state_ - objects:stack_ - count:16]; - stackIndex_ = 0; - if (stackLength_ == 0) { - done_ = true; - result.setProperty(runtime, "done", true); - return result; - } - } - - id value = state_.itemsPtr[stackIndex_++]; - NativeApiJsiType valueType = nativeObjectReturnTypeForClass(object_getClass(value)); - result.setProperty(runtime, "value", - convertNativeReturnValue(runtime, bridge_, valueType, &value)); - result.setProperty(runtime, "done", false); - return result; - } - - std::shared_ptr bridge_; - id collection_ = nil; - NSFastEnumerationState state_ = {}; - id __unsafe_unretained stack_[16] = {}; - NSUInteger stackLength_ = 0; - NSUInteger stackIndex_ = 0; - bool done_ = false; -}; - -NativeApiSymbol nativeApiSymbolForRuntimeClass( - const std::shared_ptr& bridge, Class cls) { - const char* name = cls != Nil ? class_getName(cls) : ""; - if (bridge != nullptr) { - if (const NativeApiSymbol* symbol = bridge->findClassForRuntimePointer(cls)) { - return *symbol; - } - if (const NativeApiSymbol* symbol = bridge->findClassForRuntimeClass(cls)) { - return *symbol; - } - if (name != nullptr) { - if (const NativeApiSymbol* symbol = bridge->findClass(name)) { - return *symbol; - } - } - } - - return NativeApiSymbol{ - .kind = NativeApiSymbolKind::Class, - .offset = MD_SECTION_OFFSET_NULL, - .name = name != nullptr ? name : "", - .runtimeName = name != nullptr ? name : "", - }; -} - -class NativeApiSuperHostObject final : public HostObject { - public: - NativeApiSuperHostObject(std::shared_ptr bridge, - id receiver, Class dispatchClass) - : bridge_(std::move(bridge)), - receiver_(receiver), - dispatchClass_(dispatchClass) { - if (receiver_ != nil) { - [receiver_ retain]; - } - } - - ~NativeApiSuperHostObject() override { - if (receiver_ != nil) { - [receiver_ release]; - receiver_ = nil; - } - } - - Value get(Runtime& runtime, const PropNameID& name) override { - std::string property = name.utf8(runtime); - if (property == "kind") { - return makeString(runtime, "super"); - } - if (property == "toString") { - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "toString"), 0, - [](Runtime& runtime, const Value&, const Value*, size_t) -> Value { - return makeString(runtime, "[NativeApiJsiSuper]"); - }); - } - if (receiver_ == nil || dispatchClass_ == Nil) { - return Value::undefined(); - } - - if (const NativeApiSymbol* symbol = - bridge_->findClassForRuntimeClass(dispatchClass_)) { - const auto& members = bridge_->membersForClass(*symbol); - if (const NativeApiMember* propertyMember = - selectPropertyMember(members, property, false)) { - SEL selector = sel_getUid(propertyMember->selectorName.c_str()); - if (class_getInstanceMethod(dispatchClass_, selector) != nullptr) { - return callObjCSelector(runtime, bridge_, receiver_, false, - propertyMember->selectorName, propertyMember, - nullptr, 0, dispatchClass_); - } - } - - if (selectMethodMember(members, property, false, 0) != nullptr) { - auto bridge = bridge_; - id receiver = receiver_; - Class dispatchClass = dispatchClass_; - std::string memberName = property; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 0, - [bridge, receiver, dispatchClass, memberName]( - Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - const NativeApiSymbol* symbol = - bridge->findClassForRuntimeClass(dispatchClass); - if (symbol == nullptr) { - throw facebook::jsi::JSError( - runtime, "Objective-C metadata is not available for super."); - } - const NativeApiMember* selected = selectMethodMember( - bridge->membersForClass(*symbol), memberName, false, count); - if (selected == nullptr) { - throw facebook::jsi::JSError( - runtime, "Objective-C super selector is not available: " + - memberName); - } - return callObjCSelector(runtime, bridge, receiver, false, - selected->selectorName, selected, args, - count, dispatchClass); - }); - } - } - - if (auto selectorName = - runtimeSelectorNameForProperty(dispatchClass_, false, property)) { - auto bridge = bridge_; - id receiver = receiver_; - Class dispatchClass = dispatchClass_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 0, - [bridge, receiver, dispatchClass, selectorName = *selectorName]( - Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - return callObjCSelector(runtime, bridge, receiver, false, - selectorName, nullptr, args, count, - dispatchClass); - }); - } - - return Value::undefined(); - } - - void set(Runtime& runtime, const PropNameID& name, const Value& value) override { - std::string property = name.utf8(runtime); - if (receiver_ == nil || dispatchClass_ == Nil) { - throw facebook::jsi::JSError(runtime, "Cannot set property on nil super."); - } - - if (const NativeApiSymbol* symbol = - bridge_->findClassForRuntimeClass(dispatchClass_)) { - const auto& members = bridge_->membersForClass(*symbol); - if (const NativeApiMember* propertyMember = - selectWritablePropertyMember(members, property, false)) { - if (propertyMember->readonly || - propertyMember->setterSelectorName.empty()) { - throw facebook::jsi::JSError( - runtime, "Attempted to assign to readonly property."); - } - NativeApiMember setterMember = *propertyMember; - setterMember.selectorName = propertyMember->setterSelectorName; - setterMember.signatureOffset = propertyMember->setterSignatureOffset; - Value args[] = {Value(runtime, value)}; - callObjCSelector(runtime, bridge_, receiver_, false, - setterMember.selectorName, &setterMember, args, 1, - dispatchClass_); - return; - } - } - - std::string setterSelectorName = setterSelectorForProperty(property); - SEL selector = sel_getUid(setterSelectorName.c_str()); - if (class_getInstanceMethod(dispatchClass_, selector) != nullptr) { - Value args[] = {Value(runtime, value)}; - callObjCSelector(runtime, bridge_, receiver_, false, setterSelectorName, - nullptr, args, 1, dispatchClass_); - return; - } - - throw facebook::jsi::JSError(runtime, - "No writable native super property: " + - property); - } - - std::vector getPropertyNames(Runtime& runtime) override { - std::vector names; - addPropertyName(runtime, names, "kind"); - addPropertyName(runtime, names, "toString"); - return names; - } - - private: - std::shared_ptr bridge_; - id receiver_ = nil; - Class dispatchClass_ = Nil; -}; - -class NativeApiObjectHostObject final - : public HostObject, - public std::enable_shared_from_this { - public: - NativeApiObjectHostObject(std::shared_ptr bridge, - id object, bool ownsObject) - : bridge_(std::move(bridge)), object_(object), ownsObject_(ownsObject) { - if (object_ != nil && !ownsObject_) { - [object_ retain]; - ownsObject_ = true; - } - } - - ~NativeApiObjectHostObject() override { - if (ownsObject_ && object_ != nil) { - [object_ release]; - object_ = nil; - } - } - - id object() const { return object_; } - - void disownObject(id expected) { - if (object_ == expected) { - ownsObject_ = false; - object_ = nil; - } - } - - static bool isInitializerSelector(const std::string& selectorName) { - return selectorName.rfind("init", 0) == 0; - } - - static id nativeObjectFromValue(Runtime& runtime, const Value& value) { - if (!value.isObject()) { - return nil; - } - Object object = value.asObject(runtime); - if (!object.isHostObject(runtime)) { - return nil; - } - return object.getHostObject(runtime)->object(); - } - - Value callObjectSelector(Runtime& runtime, const std::string& selectorName, - const NativeApiMember* member, const Value* args, - size_t count, Class dispatchSuperClass = Nil) { - id receiver = object_; - if (receiver == nil) { - throw facebook::jsi::JSError(runtime, - "Cannot send Objective-C selector to nil."); - } - - const bool initializer = isInitializerSelector(selectorName); - std::optional classWrapper; - if (initializer) { - Value classWrapperValue = bridge_->findObjectExpando( - runtime, receiver, "__nativeApiClassWrapper"); - if (classWrapperValue.isObject()) { - classWrapper.emplace(classWrapperValue.asObject(runtime)); - } - bridge_->forgetRoundTripValue(receiver); - bridge_->forgetObjectExpandos(receiver); - } - - Value result = - callObjCSelector(runtime, bridge_, receiver, false, selectorName, member, - args, count, dispatchSuperClass); - if (initializer) { - if (nativeObjectFromValue(runtime, result) != receiver) { - disownObject(receiver); - } else if (classWrapper) { - bridge_->setObjectExpando(runtime, receiver, "__nativeApiClassWrapper", - Value(runtime, *classWrapper)); - } - } - return result; - } - - Value get(Runtime& runtime, const PropNameID& name) override { - std::string property = name.utf8(runtime); - if (property == "kind") { - return makeString(runtime, "object"); - } - if (property == "className") { - return makeString(runtime, object_ != nil ? object_getClassName(object_) : ""); - } - if (property == "nativeAddress") { - char address[32] = {}; - snprintf(address, sizeof(address), "%p", object_); - return makeString(runtime, address); - } - if (property == "class") { - auto bridge = bridge_; - id object = object_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "class"), 0, - [bridge, object](Runtime& runtime, const Value&, const Value*, - size_t) -> Value { - if (object == nil) { - return Value::undefined(); - } - Value classWrapper = bridge->findObjectExpando( - runtime, object, "__nativeApiClassWrapper"); - if (classWrapper.isObject()) { - return classWrapper; - } - NativeApiSymbol symbol = - nativeApiSymbolForRuntimeClass(bridge, object_getClass(object)); - return makeNativeClassValue(runtime, bridge, std::move(symbol)); - }); - } - if (property == "constructor") { - if (object_ == nil) { - return Value::undefined(); - } - Value classWrapper = bridge_->findObjectExpando( - runtime, object_, "__nativeApiClassWrapper"); - if (classWrapper.isObject()) { - return classWrapper; - } - NativeApiSymbol symbol = - nativeApiSymbolForRuntimeClass(bridge_, object_getClass(object_)); - return makeNativeClassValue(runtime, bridge_, std::move(symbol)); - } - if (property == "superclass") { - if (object_ == nil) { - return Value::undefined(); - } - Class superclass = class_getSuperclass(object_getClass(object_)); - if (superclass == Nil) { - return Value::null(); - } - NativeApiSymbol symbol = nativeApiSymbolForRuntimeClass(bridge_, superclass); - return makeNativeClassValue(runtime, bridge_, std::move(symbol)); - } - if (property == "super") { - Class dispatchClass = - object_ != nil ? class_getSuperclass(object_getClass(object_)) : Nil; - return Object::createFromHostObject( - runtime, - std::make_shared(bridge_, object_, - dispatchClass)); - } - if (property == "invoke" || property == "send") { - auto bridge = bridge_; - id object = object_; - std::weak_ptr weakSelf = shared_from_this(); - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 1, - [bridge, object, weakSelf](Runtime& runtime, const Value&, - const Value* args, - size_t count) -> Value { - std::string selectorName = - readStringArg(runtime, args, count, 0, "selector"); - if (auto self = weakSelf.lock()) { - return self->callObjectSelector(runtime, selectorName, nullptr, - args + 1, count - 1); - } - return callObjCSelector(runtime, bridge, object, false, selectorName, - nullptr, args + 1, count - 1); - }); - } - if (property == "takeRetainedValue" || property == "takeUnretainedValue") { - bool retained = property == "takeRetainedValue"; - std::weak_ptr weakSelf = shared_from_this(); - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 0, - [weakSelf, retained](Runtime& runtime, const Value&, const Value*, - size_t) -> Value { - auto self = weakSelf.lock(); - if (!self || self->object_ == nil || self->consumed_) { - throw facebook::jsi::JSError(runtime, "Unmanaged value has already been consumed."); - } - - id object = self->object_; - if (self->bridge_ != nullptr) { - self->bridge_->forgetRoundTripValue(object); - } - if (self->ownsObject_) { - [object release]; - } - self->object_ = nil; - self->ownsObject_ = false; - self->consumed_ = true; - return makeNativeObjectValue(runtime, self->bridge_, object, retained); - }); - } - if (property == "toString") { - id object = object_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "toString"), 0, - [object](Runtime& runtime, const Value&, const Value*, size_t) -> Value { - NSString* description = - object != nil ? [object description] : @""; - return makeString(runtime, description.UTF8String ?: ""); - }); - } - if (property == "URL" && object_ != nil && - [object_ respondsToSelector:@selector(URL)]) { - return callObjectSelector(runtime, "URL", nullptr, nullptr, 0); - } - if (property == "Symbol.iterator" || - property == "Symbol(Symbol.iterator)" || - property == "@@iterator") { - auto bridge = bridge_; - id object = object_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "Symbol.iterator"), 0, - [bridge, object](Runtime& runtime, const Value&, const Value*, - size_t) -> Value { - if (object == nil || - ![object conformsToProtocol:@protocol(NSFastEnumeration)]) { - throw facebook::jsi::JSError( - runtime, "Object does not conform to NSFastEnumeration."); - } - return Object::createFromHostObject( - runtime, - std::make_shared( - bridge, static_cast>(object))); - }); - } - -#if TARGET_OS_OSX - if (property == "initWithRedGreenBlueAlpha") { - Class nsColorClass = NSClassFromString(@"NSColor"); - if (object_ != nil && nsColorClass != Nil && - [object_ isKindOfClass:nsColorClass]) { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 4, - [bridge, nsColorClass](Runtime& runtime, const Value&, - const Value* args, size_t count) -> Value { - const char* selectors[] = { - "colorWithSRGBRed:green:blue:alpha:", - "colorWithCalibratedRed:green:blue:alpha:", - "colorWithDeviceRed:green:blue:alpha:", - }; - for (const char* selectorName : selectors) { - if (class_getClassMethod(nsColorClass, - sel_getUid(selectorName)) != nullptr) { - return callObjCSelector(runtime, bridge, - static_cast(nsColorClass), true, - selectorName, nullptr, args, count); - } - } - throw facebook::jsi::JSError( - runtime, "NSColor RGB initializer is not available."); - }); - } - } -#endif - - if (property == "initWithFireDateIntervalTargetSelectorUserInfoRepeats") { - Class timerClass = NSClassFromString(@"NSTimer"); - if (object_ != nil && timerClass != Nil && - [object_ isKindOfClass:timerClass]) { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 6, - [bridge, timerClass](Runtime& runtime, const Value&, - const Value* args, size_t count) -> Value { - if (count < 6) { - throw facebook::jsi::JSError( - runtime, "NSTimer initializer expects six arguments."); - } - return callObjCSelector( - runtime, bridge, static_cast(timerClass), true, - "timerWithTimeInterval:target:selector:userInfo:repeats:", - nullptr, args + 1, count - 1); - }); - } - } - - Value expando = bridge_->findObjectExpando(runtime, object_, property); - if (!expando.isUndefined()) { - return expando; - } - - if (object_ != nil) { - try { - Value receiver = bridge_->findRoundTripValue(runtime, object_); - Value resolverValue = runtime.global().getProperty( - runtime, "__nativeScriptGetNativeApiPrototypeProperty"); - if (receiver.isObject() && resolverValue.isObject() && - resolverValue.asObject(runtime).isFunction(runtime)) { - Value prototype = - bridge_->findClassPrototype(runtime, object_getClass(object_)); - Value prototypeOrName = prototype.isObject() - ? Value(runtime, prototype) - : Value::undefined(); - if (prototypeOrName.isUndefined()) { - Value classWrapper = bridge_->findObjectExpando( - runtime, object_, "__nativeApiClassWrapper"); - if (classWrapper.isObject()) { - Object wrapperObject = classWrapper.asObject(runtime); - Value wrapperPrototype = - wrapperObject.getProperty(runtime, "prototype"); - if (wrapperPrototype.isObject()) { - prototypeOrName = std::move(wrapperPrototype); - } - } - } - if (prototypeOrName.isUndefined()) { - const char* className = object_getClassName(object_); - prototypeOrName = makeString(runtime, - className != nullptr ? className : ""); - } - Value resolved = resolverValue.asObject(runtime) - .asFunction(runtime) - .call(runtime, std::move(prototypeOrName), - Value(runtime, receiver), - makeString(runtime, property)); - if (resolved.isObject()) { - Object result = resolved.asObject(runtime); - Value found = result.getProperty(runtime, "found"); - if (found.isBool() && found.getBool()) { - return result.getProperty(runtime, "value"); - } - } - } - } catch (const std::exception&) { - } - } - - if (object_ != nil && [object_ isKindOfClass:[NSArray class]]) { - NSArray* array = static_cast(object_); - if (property == "length") { - return static_cast(array.count); - } - if (auto index = parseArrayIndexProperty(property)) { - if (*index >= array.count) { - return Value::undefined(); - } - id element = [array objectAtIndex:*index]; - NativeApiJsiType elementType = nativeObjectReturnType(); - return convertNativeReturnValue(runtime, bridge_, elementType, &element); - } - } - - if (object_ != nil) { - if (const NativeApiSymbol* symbol = - bridge_->findClassForRuntimeClass(object_getClass(object_))) { - const auto& members = bridge_->membersForClass(*symbol); - if (const NativeApiMember* propertyMember = - selectPropertyMember(members, property, false)) { - SEL selector = sel_getUid(propertyMember->selectorName.c_str()); - if ([object_ respondsToSelector:selector]) { - return callObjectSelector(runtime, propertyMember->selectorName, - propertyMember, nullptr, 0); - } - std::string booleanSelectorName = - booleanGetterSelectorForProperty(property); - if (booleanSelectorName != propertyMember->selectorName) { - SEL booleanSelector = sel_getUid(booleanSelectorName.c_str()); - if ([object_ respondsToSelector:booleanSelector]) { - NativeApiMember getterMember = *propertyMember; - getterMember.selectorName = booleanSelectorName; - return callObjectSelector(runtime, getterMember.selectorName, - &getterMember, nullptr, 0); - } - } - } - - if (selectMethodMember(members, property, false, 0) != nullptr) { - auto bridge = bridge_; - id object = object_; - std::weak_ptr weakSelf = - shared_from_this(); - std::string memberName = property; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), - 0, - [bridge, object, weakSelf, memberName](Runtime& runtime, - const Value&, - const Value* args, - size_t count) -> Value { - const NativeApiSymbol* symbol = - bridge->findClassForRuntimeClass(object_getClass(object)); - if (symbol == nullptr) { - throw facebook::jsi::JSError( - runtime, "Objective-C metadata is not available for object."); - } - const NativeApiMember* selected = selectMethodMember( - bridge->membersForClass(*symbol), memberName, false, count); - if (selected == nullptr) { - throw facebook::jsi::JSError( - runtime, "Objective-C selector is not available: " + - memberName); - } - if (auto self = weakSelf.lock()) { - return self->callObjectSelector( - runtime, selected->selectorName, selected, args, count); - } - return callObjCSelector(runtime, bridge, object, false, - selected->selectorName, selected, args, - count); - }); - } - } - - if (auto selectorName = - runtimeSelectorNameForProperty(object_getClass(object_), false, - property)) { - if (selectorArgumentCount(*selectorName) == 0 && - hasRuntimeSetterForProperty(object_getClass(object_), false, - property)) { - return callObjectSelector(runtime, *selectorName, nullptr, nullptr, 0); - } - - auto bridge = bridge_; - id object = object_; - std::weak_ptr weakSelf = shared_from_this(); - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 0, - [bridge, object, weakSelf, selectorName = *selectorName]( - Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - if (auto self = weakSelf.lock()) { - return self->callObjectSelector(runtime, selectorName, nullptr, - args, count); - } - return callObjCSelector(runtime, bridge, object, false, - selectorName, nullptr, args, count); - }); - } - - if ([object_ isKindOfClass:[NSDictionary class]]) { - NSString* key = [NSString stringWithUTF8String:property.c_str()]; - if (key != nil) { - id value = [static_cast(object_) objectForKey:key]; - if (value != nil) { - NativeApiJsiType valueType = nativeObjectReturnType(); - return convertNativeReturnValue(runtime, bridge_, valueType, &value); - } - } - } - } - - return Value::undefined(); - } - - void set(Runtime& runtime, const PropNameID& name, const Value& value) override { - std::string property = name.utf8(runtime); - if (object_ == nil) { - throw facebook::jsi::JSError(runtime, "Cannot set property on nil object."); - } - - if (const NativeApiSymbol* symbol = - bridge_->findClassForRuntimeClass(object_getClass(object_))) { - const auto& members = bridge_->membersForClass(*symbol); - if (const NativeApiMember* propertyMember = - selectWritablePropertyMember(members, property, false)) { - if (propertyMember->readonly) { - throw facebook::jsi::JSError( - runtime, "Attempted to assign to readonly property."); - } - NativeApiMember setterMember = *propertyMember; - setterMember.selectorName = propertyMember->setterSelectorName; - setterMember.signatureOffset = propertyMember->setterSignatureOffset; - Value args[] = {Value(runtime, value)}; - callObjCSelector(runtime, bridge_, object_, false, - setterMember.selectorName, &setterMember, args, 1); - return; - } - } - - std::string setterSelectorName = setterSelectorForProperty(property); - SEL selector = sel_getUid(setterSelectorName.c_str()); - if ([object_ respondsToSelector:selector]) { - Value args[] = {Value(runtime, value)}; - callObjCSelector(runtime, bridge_, object_, false, setterSelectorName, - nullptr, args, 1); - return; - } - - bridge_->setObjectExpando(runtime, object_, property, value); - } - - std::vector getPropertyNames(Runtime& runtime) override { - std::vector names; - names.reserve(6); - addPropertyName(runtime, names, "kind"); - addPropertyName(runtime, names, "className"); - addPropertyName(runtime, names, "nativeAddress"); - addPropertyName(runtime, names, "constructor"); - addPropertyName(runtime, names, "superclass"); - addPropertyName(runtime, names, "super"); - addPropertyName(runtime, names, "invoke"); - addPropertyName(runtime, names, "send"); - addPropertyName(runtime, names, "takeRetainedValue"); - addPropertyName(runtime, names, "takeUnretainedValue"); - addPropertyName(runtime, names, "toString"); - return names; - } - - private: - std::shared_ptr bridge_; - id object_ = nil; - bool ownsObject_ = false; - bool consumed_ = false; -}; - -class NativeApiClassHostObject final : public HostObject { - public: - NativeApiClassHostObject(std::shared_ptr bridge, - NativeApiSymbol symbol) - : bridge_(std::move(bridge)), symbol_(std::move(symbol)) {} - - Class nativeClass() const { - return objc_lookUpClass(symbol_.runtimeName.c_str()); - } - - Value get(Runtime& runtime, const PropNameID& name) override { - std::string property = name.utf8(runtime); - if (property == "kind") { - return makeString(runtime, "class"); - } - if (property == "name") { - return makeString(runtime, symbol_.name); - } - if (property == "runtimeName") { - return makeString(runtime, symbol_.runtimeName); - } - if (property == "available") { - return objc_lookUpClass(symbol_.runtimeName.c_str()) != nil; - } - if (property == "metadataOffset") { - return static_cast(symbol_.offset); - } - if (property == "__superclass") { - if (symbol_.superclassOffset == MD_SECTION_OFFSET_NULL) { - return Value::undefined(); - } - const NativeApiSymbol* superclass = - bridge_->findClassByOffset(symbol_.superclassOffset); - if (superclass == nullptr) { - return Value::undefined(); - } - return makeNativeClassValue(runtime, bridge_, *superclass); - } - if (property == "__staticMembers" || property == "__instanceMembers") { - bool staticMembers = property == "__staticMembers"; - const auto& members = bridge_->surfaceMembersForClass(symbol_); - Array result(runtime, members.size()); - size_t index = 0; - for (const auto& member : members) { - bool memberIsStatic = - (member.flags & metagen::mdMemberStatic) != 0; - if (memberIsStatic != staticMembers) { - continue; - } - Object descriptor(runtime); - descriptor.setProperty(runtime, "name", makeString(runtime, member.name)); - descriptor.setProperty(runtime, "selectorName", - makeString(runtime, member.selectorName)); - descriptor.setProperty( - runtime, "argumentCount", - static_cast(selectorArgumentCount(member.selectorName))); - descriptor.setProperty(runtime, "property", member.property); - descriptor.setProperty(runtime, "readonly", member.readonly); - descriptor.setProperty(runtime, "setterSelectorName", - makeString(runtime, member.setterSelectorName)); - result.setValueAtIndex(runtime, index++, descriptor); - } - Array compact(runtime, index); - for (size_t i = 0; i < index; i++) { - compact.setValueAtIndex(runtime, i, result.getValueAtIndex(runtime, i)); - } - return compact; - } - if (property == "toString") { - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "toString"), 0, - [symbol = symbol_](Runtime& runtime, const Value&, - const Value*, size_t) -> Value { - return makeString(runtime, - "[NativeApiJsiClass " + symbol.name + "]"); - }); - } - if (property == "construct" || property == "alloc" || property == "new") { - auto bridge = bridge_; - auto symbol = symbol_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property), 0, - [bridge, symbol, property](Runtime& runtime, const Value&, - const Value* args, size_t count) -> Value { - Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); - if (cls == nil) { - throw facebook::jsi::JSError( - runtime, "Objective-C class is not available: " + symbol.name); - } - - id result = nil; - if (property == "construct" && count == 1) { - void* pointer = nullptr; - if (args[0].isNumber()) { - pointer = reinterpret_cast( - static_cast(args[0].getNumber())); - } else if (args[0].isObject()) { - Object object = args[0].asObject(runtime); - if (object.isHostObject(runtime)) { - pointer = object - .getHostObject( - runtime) - ->pointer(); - } else if (object.isHostObject( - runtime)) { - pointer = object - .getHostObject( - runtime) - ->data(); - } else if (object.isHostObject( - runtime)) { - pointer = object - .getHostObject( - runtime) - ->object(); - } - } - return makeNativeObjectValue(runtime, bridge, - static_cast(pointer), false); - } - - if (property == "new") { - if (count != 0) { - throw facebook::jsi::JSError( - runtime, "new does not take arguments; use invoke for an " - "explicit Objective-C selector."); - } - result = [[cls alloc] init]; - } else { - if (count != 0) { - throw facebook::jsi::JSError( - runtime, "alloc does not take arguments; call invoke on the " - "allocated object for an explicit init selector."); - } - result = [cls alloc]; - } - - return makeNativeObjectValue(runtime, bridge, result, true); - }); - } - if (property == "invoke" || property == "send") { - auto bridge = bridge_; - auto symbol = symbol_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 1, - [bridge, symbol](Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - std::string selectorName = - readStringArg(runtime, args, count, 0, "selector"); - Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); - if (cls == nil) { - throw facebook::jsi::JSError( - runtime, "Objective-C class is not available: " + symbol.name); - } - return callObjCSelector(runtime, bridge, static_cast(cls), true, - selectorName, nullptr, args + 1, - count - 1); - }); - } - - const auto& members = bridge_->membersForClass(symbol_); - if (const NativeApiMember* propertyMember = - selectWritablePropertyMember(members, property, true)) { - auto bridge = bridge_; - auto symbol = symbol_; - Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); - if (cls == nil) { - throw facebook::jsi::JSError( - runtime, "Objective-C class is not available: " + symbol.name); - } - SEL selector = sel_getUid(propertyMember->selectorName.c_str()); - if (class_getClassMethod(cls, selector) != nullptr) { - return callObjCSelector(runtime, bridge, static_cast(cls), true, - propertyMember->selectorName, propertyMember, - nullptr, 0); - } - } - - if (selectMethodMember(members, property, true, 0) != nullptr) { - auto bridge = bridge_; - auto symbol = symbol_; - std::string memberName = property; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 0, - [bridge, symbol, memberName](Runtime& runtime, const Value&, - const Value* args, - size_t count) -> Value { - Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); - if (cls == nil) { - throw facebook::jsi::JSError( - runtime, "Objective-C class is not available: " + symbol.name); - } - const NativeApiMember* selected = selectMethodMember( - bridge->membersForClass(symbol), memberName, true, count); - if (selected == nullptr) { - throw facebook::jsi::JSError( - runtime, "Objective-C selector is not available: " + - memberName); - } - return callObjCSelector(runtime, bridge, static_cast(cls), true, - selected->selectorName, selected, args, - count); - }); - } - - Class cls = objc_lookUpClass(symbol_.runtimeName.c_str()); - if (cls != nil) { - if (auto selectorName = - runtimeSelectorNameForProperty(cls, true, property)) { - if (selectorArgumentCount(*selectorName) == 0 && - hasRuntimeSetterForProperty(cls, true, property)) { - return callObjCSelector(runtime, bridge_, static_cast(cls), true, - *selectorName, nullptr, nullptr, 0); - } - - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, property.c_str()), 0, - [bridge, cls, selectorName = *selectorName]( - Runtime& runtime, const Value&, const Value* args, - size_t count) -> Value { - return callObjCSelector(runtime, bridge, static_cast(cls), - true, selectorName, nullptr, args, - count); - }); - } - } - - return Value::undefined(); - } - - void set(Runtime& runtime, const PropNameID& name, const Value& value) override { - std::string property = name.utf8(runtime); - Class cls = objc_lookUpClass(symbol_.runtimeName.c_str()); - if (cls == nil) { - throw facebook::jsi::JSError( - runtime, "Objective-C class is not available: " + symbol_.name); - } - - const auto& members = bridge_->membersForClass(symbol_); - if (const NativeApiMember* propertyMember = - selectPropertyMember(members, property, true)) { - if (propertyMember->readonly) { - throw facebook::jsi::JSError( - runtime, "Attempted to assign to readonly property."); - } - NativeApiMember setterMember = *propertyMember; - setterMember.selectorName = propertyMember->setterSelectorName; - setterMember.signatureOffset = propertyMember->setterSignatureOffset; - Value args[] = {Value(runtime, value)}; - callObjCSelector(runtime, bridge_, static_cast(cls), true, - setterMember.selectorName, &setterMember, args, 1); - return; - } - - std::string setterSelectorName = setterSelectorForProperty(property); - SEL selector = sel_getUid(setterSelectorName.c_str()); - if (class_getClassMethod(cls, selector) != nullptr) { - Value args[] = {Value(runtime, value)}; - callObjCSelector(runtime, bridge_, static_cast(cls), true, - setterSelectorName, nullptr, args, 1); - return; - } - - throw facebook::jsi::JSError(runtime, - "No writable native property: " + property); - } - - std::vector getPropertyNames(Runtime& runtime) override { - std::vector names; - names.reserve(8); - addPropertyName(runtime, names, "kind"); - addPropertyName(runtime, names, "name"); - addPropertyName(runtime, names, "runtimeName"); - addPropertyName(runtime, names, "available"); - addPropertyName(runtime, names, "metadataOffset"); - addPropertyName(runtime, names, "toString"); - addPropertyName(runtime, names, "construct"); - addPropertyName(runtime, names, "alloc"); - addPropertyName(runtime, names, "new"); - addPropertyName(runtime, names, "invoke"); - addPropertyName(runtime, names, "send"); - return names; - } - - private: - std::shared_ptr bridge_; - NativeApiSymbol symbol_; -}; - -Value makeNativeObjectValue(Runtime& runtime, - const std::shared_ptr& bridge, - id object, bool ownsObject) { - if (object == nil) { - return Value::null(); - } - - Value cached = bridge->findRoundTripValue(runtime, object); - if (!cached.isUndefined()) { - if (ownsObject) { - [object release]; - } - return cached; - } - - Object result = Object::createFromHostObject( - runtime, - std::make_shared(bridge, object, ownsObject)); - bridge->rememberRoundTripValue(runtime, object, Value(runtime, result)); - return result; -} - -Value globalNativeSymbolValue(Runtime& runtime, const NativeApiSymbol& symbol, - const char* expectedKind) { - Object global = runtime.global(); - Value cacheValue = global.getProperty( - runtime, "__nativeScriptNativeApiGlobalCache"); - if (!cacheValue.isObject()) { - return Value::undefined(); - } - - Object cache = cacheValue.asObject(runtime); - auto readCache = [&](const std::string& name) -> Value { - if (name.empty()) { - return Value::undefined(); - } - - Value value = cache.getProperty(runtime, name.c_str()); - if (!value.isObject()) { - return Value::undefined(); - } - - try { - Object object = value.asObject(runtime); - Value kindValue = object.getProperty(runtime, "kind"); - if (kindValue.isString() && - kindValue.asString(runtime).utf8(runtime) == expectedKind) { - return value; - } - } catch (const std::exception&) { - } - - return Value::undefined(); - }; - - Value value = readCache(symbol.name); - if (!value.isUndefined()) { - return value; - } - if (symbol.runtimeName != symbol.name) { - value = readCache(symbol.runtimeName); - if (!value.isUndefined()) { - return value; - } - } - - try { - if (std::strcmp(expectedKind, "class") == 0) { - Value classResolverValue = global.getProperty( - runtime, "__nativeScriptResolveNativeApiClassWrapper"); - if (classResolverValue.isObject() && - classResolverValue.asObject(runtime).isFunction(runtime)) { - Function classResolver = - classResolverValue.asObject(runtime).asFunction(runtime); - auto resolveClassWrapper = [&](const std::string& name) -> Value { - if (name.empty()) { - return Value::undefined(); - } - Value resolved = classResolver.call(runtime, makeString(runtime, name)); - return resolved.isObject() ? std::move(resolved) : Value::undefined(); - }; - - value = resolveClassWrapper(symbol.name); - if (!value.isUndefined()) { - return value; - } - if (symbol.runtimeName != symbol.name) { - value = resolveClassWrapper(symbol.runtimeName); - if (!value.isUndefined()) { - return value; - } - } - } - } - - Value resolverValue = - global.getProperty(runtime, "__nativeScriptResolveNativeApiGlobal"); - if (resolverValue.isObject() && - resolverValue.asObject(runtime).isFunction(runtime)) { - Function resolver = resolverValue.asObject(runtime).asFunction(runtime); - auto resolveGlobal = [&](const std::string& name) -> Value { - if (name.empty()) { - return Value::undefined(); - } - Value resolved = resolver.call(runtime, makeString(runtime, name), - makeString(runtime, expectedKind)); - if (resolved.isObject()) { - return resolved; - } - return Value::undefined(); - }; - - value = resolveGlobal(symbol.name); - if (!value.isUndefined()) { - return value; - } - if (symbol.runtimeName != symbol.name) { - value = resolveGlobal(symbol.runtimeName); - if (!value.isUndefined()) { - return value; - } - } - } - } catch (const std::exception&) { - } - - return Value::undefined(); -} - -Value makeNativeClassValue(Runtime& runtime, - const std::shared_ptr& bridge, - NativeApiSymbol symbol) { - Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); - Value cachedClass = bridge->findClassValue(runtime, cls); - if (!cachedClass.isUndefined()) { - return cachedClass; - } - Value globalValue = globalNativeSymbolValue(runtime, symbol, "class"); - if (!globalValue.isUndefined()) { - return globalValue; - } - return Object::createFromHostObject( - runtime, - std::make_shared(bridge, std::move(symbol))); -} - -Protocol* lookupProtocolByNativeName(const std::string& name) { - Protocol* protocol = objc_getProtocol(name.c_str()); - if (protocol != nullptr) { - return protocol; - } - constexpr const char* suffix = "Protocol"; - size_t suffixLength = std::strlen(suffix); - if (name.size() > suffixLength && - name.compare(name.size() - suffixLength, suffixLength, suffix) == 0) { - protocol = objc_getProtocol( - name.substr(0, name.size() - suffixLength).c_str()); - } - return protocol; -} - -class NativeApiProtocolHostObject final : public HostObject { - public: - NativeApiProtocolHostObject(std::shared_ptr bridge, - NativeApiSymbol symbol) - : bridge_(std::move(bridge)), symbol_(std::move(symbol)) {} - - Protocol* nativeProtocol() const { - Protocol* protocol = lookupProtocolByNativeName(symbol_.runtimeName); - if (protocol == nullptr && symbol_.runtimeName != symbol_.name) { - protocol = lookupProtocolByNativeName(symbol_.name); - } - return protocol; - } - - const NativeApiSymbol& symbol() const { return symbol_; } - - Value get(Runtime& runtime, const PropNameID& name) override { - std::string property = name.utf8(runtime); - if (property == "kind") { - return makeString(runtime, "protocol"); - } - if (property == "name") { - return makeString(runtime, symbol_.name); - } - if (property == "runtimeName") { - return makeString(runtime, symbol_.runtimeName); - } - if (property == "available") { - return nativeProtocol() != nullptr; - } - if (property == "metadataOffset") { - return static_cast(symbol_.offset); - } - if (property == "nativeAddress") { - return static_cast( - reinterpret_cast(nativeProtocol())); - } - if (property == "prototype") { - Object prototype(runtime); - for (const auto& member : bridge_->membersForProtocol(symbol_)) { - if (prototype.hasProperty(runtime, member.name.c_str())) { - continue; - } - if (member.property) { - defineProtocolProperty(runtime, prototype, member, false); - } else { - prototype.setProperty(runtime, member.name.c_str(), - makeProtocolMemberFunction(runtime, member, - false)); - } - } - return prototype; - } - if (property == "toString") { - auto symbol = symbol_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "toString"), 0, - [symbol](Runtime& runtime, const Value&, const Value*, size_t) -> Value { - return makeString(runtime, - "[NativeApiJsiProtocol " + symbol.name + "]"); - }); - } - const auto& members = bridge_->membersForProtocol(symbol_); - if (const NativeApiMember* propertyMember = - selectPropertyMember(members, property, true)) { - return makeProtocolPropertyGetter(runtime, *propertyMember, true); - } - if (const NativeApiMember* propertyMember = - selectPropertyMember(members, property, false)) { - return makeProtocolPropertyGetter(runtime, *propertyMember, true); - } - if (const NativeApiMember* methodMember = - selectMethodMember(members, property, true, 0)) { - return makeProtocolMemberFunction(runtime, *methodMember, true); - } - if (const NativeApiMember* methodMember = - selectMethodMember(members, property, false, 0)) { - return makeProtocolMemberFunction(runtime, *methodMember, true); - } - return Value::undefined(); - } - - std::vector getPropertyNames(Runtime& runtime) override { - std::vector names; - addPropertyName(runtime, names, "kind"); - addPropertyName(runtime, names, "name"); - addPropertyName(runtime, names, "runtimeName"); - addPropertyName(runtime, names, "available"); - addPropertyName(runtime, names, "metadataOffset"); - addPropertyName(runtime, names, "nativeAddress"); - addPropertyName(runtime, names, "prototype"); - addPropertyName(runtime, names, "toString"); - for (const auto& member : bridge_->membersForProtocol(symbol_)) { - addPropertyName(runtime, names, member.name.c_str()); - } - return names; - } - - private: - static Class classReceiverFromThis(Runtime& runtime, const Value& thisValue) { - if (!thisValue.isObject()) { - return Nil; - } - - Object object = thisValue.asObject(runtime); - if (object.isHostObject(runtime)) { - return object.getHostObject(runtime)->nativeClass(); - } - - Value wrappedClass = object.getProperty(runtime, "__nativeApiClass"); - if (wrappedClass.isObject()) { - Object wrappedObject = wrappedClass.asObject(runtime); - if (wrappedObject.isHostObject(runtime)) { - return wrappedObject.getHostObject(runtime) - ->nativeClass(); - } - } - - Value kindValue = object.getProperty(runtime, "kind"); - if (kindValue.isString() && - kindValue.asString(runtime).utf8(runtime) == "class") { - Value runtimeNameValue = object.getProperty(runtime, "runtimeName"); - if (!runtimeNameValue.isString()) { - runtimeNameValue = object.getProperty(runtime, "name"); - } - if (runtimeNameValue.isString()) { - std::string runtimeName = - runtimeNameValue.asString(runtime).utf8(runtime); - return objc_lookUpClass(runtimeName.c_str()); - } - } - - return Nil; - } - - id objectReceiverFromThis(Runtime& runtime, const Value& thisValue) const { - if (!thisValue.isObject()) { - return nil; - } - - Object object = thisValue.asObject(runtime); - if (object.isHostObject(runtime)) { - return object.getHostObject(runtime)->object(); - } - - return nil; - } - - Value makeProtocolMemberFunction(Runtime& runtime, NativeApiMember member, - bool receiverIsClass) const { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, member.name.c_str()), 0, - [bridge, member, receiverIsClass](Runtime& runtime, - const Value& thisValue, - const Value* args, - size_t count) -> Value { - id receiver = nil; - if (receiverIsClass) { - receiver = static_cast( - classReceiverFromThis(runtime, thisValue)); - } else if (thisValue.isObject()) { - Object object = thisValue.asObject(runtime); - if (object.isHostObject(runtime)) { - receiver = object.getHostObject(runtime) - ->object(); - } - } - - if (receiver == nil) { - throw facebook::jsi::JSError( - runtime, "Protocol member requires a native receiver."); - } - return callObjCSelector(runtime, bridge, receiver, receiverIsClass, - member.selectorName, &member, args, count); - }); - } - - Value makeProtocolPropertyGetter(Runtime& runtime, NativeApiMember member, - bool receiverIsClass) const { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, member.name.c_str()), 0, - [bridge, member, receiverIsClass](Runtime& runtime, - const Value& thisValue, - const Value*, size_t) -> Value { - id receiver = nil; - if (receiverIsClass) { - receiver = static_cast( - classReceiverFromThis(runtime, thisValue)); - } else if (thisValue.isObject()) { - Object object = thisValue.asObject(runtime); - if (object.isHostObject(runtime)) { - receiver = object.getHostObject(runtime) - ->object(); - } - } - - if (receiver == nil) { - throw facebook::jsi::JSError( - runtime, "Protocol property requires a native receiver."); - } - return callObjCSelector(runtime, bridge, receiver, receiverIsClass, - member.selectorName, &member, nullptr, 0); - }); - } - - Value makeProtocolPropertySetter(Runtime& runtime, NativeApiMember member, - bool receiverIsClass) const { - auto bridge = bridge_; - return Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, member.setterSelectorName.c_str()), - 1, - [bridge, member, receiverIsClass](Runtime& runtime, - const Value& thisValue, - const Value* args, - size_t count) -> Value { - id receiver = nil; - if (receiverIsClass) { - receiver = static_cast( - classReceiverFromThis(runtime, thisValue)); - } else if (thisValue.isObject()) { - Object object = thisValue.asObject(runtime); - if (object.isHostObject(runtime)) { - receiver = object.getHostObject(runtime) - ->object(); - } - } - - if (receiver == nil) { - throw facebook::jsi::JSError( - runtime, "Protocol property requires a native receiver."); - } - if (count < 1) { - throw facebook::jsi::JSError( - runtime, "Protocol property setter expects a value."); - } - - NativeApiMember setterMember = member; - setterMember.selectorName = member.setterSelectorName; - setterMember.signatureOffset = member.setterSignatureOffset; - return callObjCSelector(runtime, bridge, receiver, receiverIsClass, - setterMember.selectorName, &setterMember, - args, 1); - }); - } - - void defineProtocolProperty(Runtime& runtime, Object& target, - const NativeApiMember& member, - bool receiverIsClass) const { - try { - Object objectCtor = runtime.global().getPropertyAsObject(runtime, "Object"); - Function defineProperty = - objectCtor.getPropertyAsFunction(runtime, "defineProperty"); - Object descriptor(runtime); - descriptor.setProperty(runtime, "configurable", true); - descriptor.setProperty(runtime, "enumerable", true); - descriptor.setProperty(runtime, "get", - makeProtocolPropertyGetter(runtime, member, - receiverIsClass)); - if (!member.readonly && !member.setterSelectorName.empty()) { - descriptor.setProperty(runtime, "set", - makeProtocolPropertySetter(runtime, member, - receiverIsClass)); - } - defineProperty.call(runtime, target, makeString(runtime, member.name), - descriptor); - } catch (const std::exception&) { - } - } - - std::shared_ptr bridge_; - NativeApiSymbol symbol_; -}; - -Value makeNativeProtocolValue(Runtime& runtime, - const std::shared_ptr& bridge, - NativeApiSymbol symbol) { - Value globalValue = globalNativeSymbolValue(runtime, symbol, "protocol"); - if (!globalValue.isUndefined()) { - return globalValue; - } - return Object::createFromHostObject( - runtime, - std::make_shared(bridge, std::move(symbol))); -} - -Class nativeClassFromJsiObject(Runtime& runtime, const Object& object) { - if (object.isHostObject(runtime)) { - return object.getHostObject(runtime)->nativeClass(); - } - - Value wrappedClass = object.getProperty(runtime, "__nativeApiClass"); - if (wrappedClass.isObject()) { - Object wrappedObject = wrappedClass.asObject(runtime); - if (wrappedObject.isHostObject(runtime)) { - return wrappedObject.getHostObject(runtime) - ->nativeClass(); - } - } - return Nil; -} diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiInstall.h b/NativeScript/ffi/shared/jsi/NativeApiJsiInstall.h deleted file mode 100644 index 6e711d5be..000000000 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiInstall.h +++ /dev/null @@ -1,1675 +0,0 @@ -Object CreateNativeApiJSI(Runtime& runtime, const NativeApiJsiConfig& config) { - auto bridge = std::make_shared(config); - return Object::createFromHostObject( - runtime, std::make_shared(std::move(bridge))); -} - -void NativeApiJsiWriteSmokeStage(const char* stage) { - const char* enabled = getenv("NATIVESCRIPT_RN_TURBO_SMOKE_MARKER"); - if (enabled == nullptr || enabled[0] == '\0') { - return; - } - - NSString* path = [NSTemporaryDirectory() - stringByAppendingPathComponent:@"NativeScriptNativeApiSmoke.marker"]; - NSString* content = - [NSString stringWithFormat:@"stage=%s\n", stage != nullptr ? stage : ""]; - [content writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil]; -} - -void InstallAggregateGlobals(Runtime& runtime, Object& api, const char* namesFunction) { - Value metadataValue = api.getProperty(runtime, "metadata"); - if (!metadataValue.isObject()) { - return; - } - Object metadata = metadataValue.asObject(runtime); - Value namesValue = metadata.getProperty(runtime, namesFunction); - if (!namesValue.isObject()) { - return; - } - Object namesObject = namesValue.asObject(runtime); - if (!namesObject.isFunction(runtime)) { - return; - } - Value namesResult = namesObject.asFunction(runtime).call(runtime); - if (!namesResult.isObject() || !namesResult.asObject(runtime).isArray(runtime)) { - return; - } - Array names = namesResult.asObject(runtime).getArray(runtime); - Object global = runtime.global(); - for (size_t i = 0; i < names.size(runtime); i++) { - Value nameValue = names.getValueAtIndex(runtime, i); - if (!nameValue.isString()) { - continue; - } - std::string name = nameValue.asString(runtime).utf8(runtime); - if (name.empty() || global.hasProperty(runtime, name.c_str())) { - continue; - } - try { - Value aggregate = api.getProperty(runtime, name.c_str()); - if (!aggregate.isUndefined()) { - global.setProperty(runtime, name.c_str(), aggregate); - } - } catch (const std::exception&) { - // Some React Native globals are read-only even when hasProperty misses - // them. Keep NativeScript initialization resilient and skip collisions. - } - } -} - -std::string jsStringLiteral(const char* value) { - std::string result = "'"; - if (value != nullptr) { - for (const char* current = value; *current != '\0'; current++) { - switch (*current) { - case '\\': - result += "\\\\"; - break; - case '\'': - result += "\\'"; - break; - case '\n': - result += "\\n"; - break; - case '\r': - result += "\\r"; - break; - case '\t': - result += "\\t"; - break; - default: - result += *current; - break; - } - } - } - result += "'"; - return result; -} - -void InstallNativeApiJsiGlobalSymbols(Runtime& runtime, const char* globalName) { - NativeApiJsiWriteSmokeStage("jsi:globals:before-eval"); - static const char* GlobalInstaller = R"JSI_GLOBALS( -(function(nativeApiGlobalName) { - 'use strict'; - var api = globalThis[nativeApiGlobalName]; - var installedFlagName = '__nativeScriptNativeApiGlobalsInstalled'; - if (!api || globalThis[installedFlagName]) { - return; - } - - var cacheName = '__nativeScriptNativeApiGlobalCache'; - var typeCodeKey = '__nativeApiTypeCode'; - var classWrappers = typeof WeakMap === 'function' ? new WeakMap() : null; - var classWrappersByName = Object.create(null); - var resolvingGlobal = Object.create(null); - - function globalCache() { - var existing = globalThis[cacheName]; - if (existing && typeof existing === 'object') { - return existing; - } - var cache = Object.create(null); - Object.defineProperty(globalThis, cacheName, { - configurable: false, - enumerable: false, - writable: false, - value: cache - }); - return cache; - } - - function cacheGlobal(name, value) { - if (name && value !== undefined) { - globalCache()[name] = value; - } - } - - function resolveCachedGlobal(name, expectedKind) { - if (!name) { - return undefined; - } - var cached = globalCache()[name]; - if (cached && (typeof cached === 'object' || typeof cached === 'function') && cached.kind === expectedKind) { - return cached; - } - if (resolvingGlobal[name] || !Object.prototype.hasOwnProperty.call(globalThis, name)) { - return undefined; - } - resolvingGlobal[name] = true; - try { - var value = globalThis[name]; - if (value && (typeof value === 'object' || typeof value === 'function') && value.kind === expectedKind) { - cacheGlobal(name, value); - return value; - } - } finally { - delete resolvingGlobal[name]; - } - return undefined; - } - - function defineLazyGlobal(name, resolve, force, nativeKind) { - if (!name) { - return; - } - if (!force && Object.prototype.hasOwnProperty.call(globalThis, name)) { - try { - var existingDescriptor = Object.getOwnPropertyDescriptor(globalThis, name); - if (existingDescriptor && Object.prototype.hasOwnProperty.call(existingDescriptor, 'value')) { - cacheGlobal(name, existingDescriptor.value); - } - } catch (_) { - } - return; - } - var nativeDefineLazyGlobal = api.__defineLazyGlobal; - if (nativeKind && typeof nativeDefineLazyGlobal === 'function' && - typeof globalThis.__nativeScriptResolveNativeApiLazyGlobal === 'function') { - try { - if (nativeDefineLazyGlobal(name, nativeKind, !!force)) { - return; - } - } catch (_) { - } - } - try { - Object.defineProperty(globalThis, name, { - configurable: true, - enumerable: false, - get: function() { - var value = resolve(name); - cacheGlobal(name, value); - Object.defineProperty(globalThis, name, { - configurable: true, - enumerable: false, - writable: false, - value: value - }); - return value; - } - }); - } catch (_) { - var value = resolve(name); - if (value !== undefined) { - cacheGlobal(name, value); - Object.defineProperty(globalThis, name, { - configurable: true, - enumerable: false, - writable: false, - value: value - }); - } - } - } - - Object.defineProperty(globalThis, '__nativeScriptResolveNativeApiGlobal', { - configurable: false, - enumerable: false, - writable: false, - value: resolveCachedGlobal - }); - - Object.defineProperty(globalThis, '__nativeScriptResolveNativeApiClassWrapper', { - configurable: false, - enumerable: false, - writable: false, - value: function(name) { - return name ? classWrappersByName[name] : undefined; - } - }); - - function findPrototypeDescriptor(className, property) { - var prototype; - if (className && (typeof className === 'object' || typeof className === 'function')) { - prototype = className; - } else { - var wrapper = className ? classWrappersByName[className] : undefined; - prototype = wrapper && wrapper.prototype; - } - while (prototype != null) { - var descriptor = Object.getOwnPropertyDescriptor(prototype, property); - if (descriptor) { - return descriptor; - } - prototype = Object.getPrototypeOf(prototype); - } - return undefined; - } - - Object.defineProperty(globalThis, '__nativeScriptGetNativeApiPrototypeProperty', { - configurable: false, - enumerable: false, - writable: false, - value: function(className, receiver, property) { - var descriptor = findPrototypeDescriptor(className, property); - if (!descriptor) { - return { found: false }; - } - if (typeof descriptor.get === 'function') { - return { found: true, value: descriptor.get.call(receiver) }; - } - if (typeof descriptor.value === 'function') { - return { found: true, value: descriptor.value.bind(receiver) }; - } - if ('value' in descriptor) { - return { found: true, value: descriptor.value }; - } - return { found: true, value: undefined }; - } - }); - - Object.defineProperty(globalThis, '__nativeScriptCreateNativeApiIterator', { - configurable: false, - enumerable: false, - writable: false, - value: function(receiver, prototype) { - if (!receiver || typeof Symbol !== 'function') { - return undefined; - } - var descriptor = findPrototypeDescriptor(prototype || receiver.className, Symbol.iterator); - if (descriptor && typeof descriptor.value === 'function') { - return descriptor.value.call(receiver); - } - if (descriptor && typeof descriptor.get === 'function') { - var getterValue = descriptor.get.call(receiver); - if (typeof getterValue === 'function') { - return getterValue.call(receiver); - } - } - var iteratorMethod = receiver[Symbol.iterator]; - return typeof iteratorMethod === 'function' - ? iteratorMethod.call(receiver) - : undefined; - } - }); - - function wrapAggregateConstructor(nativeConstructor) { - if (typeof nativeConstructor !== 'function') { - return nativeConstructor; - } - var aggregate = function NativeScriptAggregate(initialValue) { - return nativeConstructor(initialValue); - }; - try { - Object.defineProperty(aggregate, Symbol.hasInstance, { - configurable: true, - enumerable: false, - value: function(value) { - return !!value && - typeof value === 'object' && - value.kind === nativeConstructor.kind && - value.name === nativeConstructor.runtimeName; - } - }); - } catch (_) { - } - ['kind', 'runtimeName', 'metadataOffset', 'sizeof', 'fields', 'equals'].forEach(function(key) { - try { - Object.defineProperty(aggregate, key, { - configurable: true, - enumerable: false, - writable: false, - value: nativeConstructor[key] - }); - } catch (_) { - } - }); - return aggregate; - } - - function setDescriptorValue(target, property, receiver, value) { - var descriptor = Object.getOwnPropertyDescriptor(target, property); - if (!descriptor) { - return false; - } - if (typeof descriptor.set === 'function') { - descriptor.set.call(receiver, value); - return true; - } - if (descriptor.writable) { - if (receiver && receiver !== target) { - Object.defineProperty(receiver, property, { - configurable: true, - enumerable: true, - writable: true, - value: value - }); - } else { - target[property] = value; - } - return true; - } - return false; - } - - function isConstructorOptions(value) { - if (!value || typeof value !== 'object' || Array.isArray(value)) { - return false; - } - if (value.kind || value.nativeAddress || value instanceof Date) { - return false; - } - return Object.getPrototypeOf(value) === Object.prototype || - Object.getPrototypeOf(value) === null; - } - - function capitalizeToken(value) { - value = String(value || ''); - return value ? value.charAt(0).toUpperCase() + value.slice(1) : value; - } - - function selectorCandidatesFromOptions(options) { - var keys = Object.keys(options || {}); - if (!keys.length) { - return []; - } - var first = capitalizeToken(keys[0]); - var tail = ''; - for (var i = 1; i < keys.length; i++) { - tail += keys[i] + ':'; - } - return [ - 'initWith' + first + ':' + tail, - 'init' + first + ':' + tail - ]; - } - - function valuesFromOptions(options) { - return Object.keys(options || {}).map(function(key) { - return options[key]; - }); - } - - function selectorScoreForArguments(selectorName, args) { - if (!selectorName || selectorName.indexOf('init') !== 0) { - return -1; - } - if (selectorName === 'init') { - return args.length === 0 ? 100 : -1; - } - if (args.length === 0) { - return -1; - } - var lower = selectorName.toLowerCase(); - var first = args[0]; - var score = 1; - if (Array.isArray(first)) { - if (lower.indexOf('array') !== -1) { - score += 40; - } - } else if (typeof first === 'string') { - if (lower.indexOf('string') !== -1) { - score += 40; - } - if (lower.indexOf('url') !== -1) { - score += 10; - } - } else if (typeof first === 'number') { - if (lower.indexOf('primitive') !== -1) { - score += 50; - } - if (lower.indexOf('int') !== -1 || - lower.indexOf('integer') !== -1 || - lower.indexOf('number') !== -1 || - lower.indexOf('float') !== -1 || - lower.indexOf('double') !== -1 || - lower.indexOf('long') !== -1 || - lower.indexOf('short') !== -1) { - score += 30; - } - } else if (isConstructorOptions(first)) { - if (lower.indexOf('struct') !== -1 || - lower.indexOf('structure') !== -1) { - score += 40; - } - if (lower.indexOf('dictionary') !== -1) { - score += 20; - } - } else if (first === null || typeof first === 'undefined') { - score += 5; - } else if (lower.indexOf('object') !== -1 || - lower.indexOf('url') !== -1 || - lower.indexOf('data') !== -1) { - score += 20; - } - - var allStrings = args.length > 1 && args.every(function(value) { - return typeof value === 'string'; - }); - var allNumbers = args.length > 1 && args.every(function(value) { - return typeof value === 'number'; - }); - if (allStrings && lower.indexOf('string') !== -1) { - score += 25; - } - if (allNumbers && - (lower.indexOf('int') !== -1 || lower.indexOf('number') !== -1)) { - score += 25; - } - return score; - } - - function initializerMembers(nativeClass, argumentCount) { - var members = nativeClass.__instanceMembers || []; - var result = []; - for (var i = 0; i < members.length; i++) { - var member = members[i]; - if (!member || member.property || !member.selectorName) { - continue; - } - if (member.selectorName.indexOf('init') !== 0) { - continue; - } - if (typeof argumentCount === 'number' && - member.argumentCount !== argumentCount) { - continue; - } - result.push(member); - } - return result; - } - - function chooseInitializer(nativeClass, args, optionSelectors) { - var members = initializerMembers(nativeClass, args.length); - if (!members.length) { - return null; - } - if (optionSelectors && optionSelectors.length) { - for (var i = 0; i < optionSelectors.length; i++) { - for (var j = 0; j < members.length; j++) { - if (members[j].selectorName === optionSelectors[i]) { - return members[j]; - } - } - } - } - - var best = null; - var bestScore = -1; - for (var k = 0; k < members.length; k++) { - var score = selectorScoreForArguments(members[k].selectorName, args); - if (score > bestScore) { - bestScore = score; - best = members[k]; - } - } - return bestScore >= 0 ? best : null; - } - - function chooseInitializerBySelectors(nativeClass, args, selectors) { - if (!selectors || !selectors.length) { - return null; - } - var members = initializerMembers(nativeClass, args.length); - for (var i = 0; i < selectors.length; i++) { - for (var j = 0; j < members.length; j++) { - if (members[j].selectorName === selectors[i]) { - return members[j]; - } - } - } - return null; - } - - function unavailableInitializerError(error) { - return error && - /Objective-C selector is not available/.test(String(error.message || error)); - } - - function constructNativeInstance(nativeClass, args) { - if (args.length === 1 && - args[0] && - typeof args[0] === 'object' && - (args[0].kind === 'pointer' || args[0].kind === 'reference') && - typeof nativeClass.construct === 'function') { - return nativeClass.construct(args[0]); - } - - var actualArgs = args; - var initializer = null; - if (args.length === 1 && isConstructorOptions(args[0])) { - var optionSelectors = selectorCandidatesFromOptions(args[0]); - if (!optionSelectors.length) { - throw new Error('No initializer found that matches constructor invocation.'); - } - var optionArgs = valuesFromOptions(args[0]); - initializer = chooseInitializerBySelectors( - nativeClass, - optionArgs, - optionSelectors - ); - if (initializer) { - actualArgs = optionArgs; - } - } - if (!initializer) { - initializer = chooseInitializer(nativeClass, actualArgs, null); - } - if (!initializer) { - throw new Error('No initializer found that matches constructor invocation.'); - } - if (typeof nativeClass.alloc !== 'function') { - throw new Error('Native class cannot be allocated'); - } - var instance = nativeClass.alloc(); - if (initializer.selectorName === 'init') { - if (typeof instance.init !== 'function') { - throw new Error('No initializer found that matches constructor invocation.'); - } - return instance.init(); - } - try { - if (initializer.name && typeof instance[initializer.name] === 'function') { - return instance[initializer.name].apply(instance, actualArgs); - } - var invokeArgs = [initializer.selectorName]; - Array.prototype.push.apply(invokeArgs, actualArgs); - return instance.invoke.apply(instance, invokeArgs); - } catch (error) { - if (unavailableInitializerError(error)) { - throw new Error('No initializer found that matches constructor invocation.'); - } - throw error; - } - } - - function wrapNativeClass(nativeClass) { - if (!nativeClass || (typeof nativeClass !== 'object' && typeof nativeClass !== 'function')) { - return nativeClass; - } - var nativeClassName = nativeClass.runtimeName || nativeClass.name || ''; - if (nativeClassName && classWrappersByName[nativeClassName]) { - if (classWrappers) { - try { - classWrappers.set(nativeClass, classWrappersByName[nativeClassName]); - } catch (_) { - } - } - return classWrappersByName[nativeClassName]; - } - if (classWrappers) { - var cached = classWrappers.get(nativeClass); - if (cached) { - return cached; - } - } - var constructable = function NativeScriptNativeClass() { - var args = Array.prototype.slice.call(arguments); - var redirectConstructor = this && this.constructor; - if (redirectConstructor && - redirectConstructor !== constructable && - redirectConstructor !== wrapper && - typeof redirectConstructor.__nativeApiEnsureClass === 'function') { - var redirectedWrapper = redirectConstructor.__nativeApiEnsureClass(); - if (redirectedWrapper && - redirectedWrapper !== constructable && - redirectedWrapper !== wrapper && - typeof redirectedWrapper.apply === 'function') { - return rememberClassOnInstance( - redirectedWrapper.apply(this, args), - redirectConstructor - ); - } - } - if (args.length > 0) { - return rememberInstanceClass(constructNativeInstance(nativeClass, args)); - } - if (typeof nativeClass.alloc !== 'function') { - throw new Error('Native class cannot be allocated'); - } - var instance = nativeClass.alloc(); - if (instance && typeof instance.init === 'function') { - return rememberInstanceClass(instance.init()); - } - return rememberInstanceClass(instance); - }; - function rememberInstanceClass(instance) { - return rememberClassOnInstance(instance, wrapper || constructable); - } - try { - Object.defineProperty(constructable, 'name', { - configurable: true, - enumerable: false, - value: nativeClassName || nativeClass.name || 'NativeScriptNativeClass' - }); - } catch (_) { - } - try { - Object.defineProperty(constructable, 'extend', { - configurable: true, - enumerable: false, - writable: false, - value: function(methods, options) { - if (methods == null || typeof methods !== 'object') { - throw new Error('extend() first parameter must be an object'); - } - var extendOptions = options || {}; - if (typeof Symbol === 'function' && - Object.prototype.hasOwnProperty.call(methods, Symbol.iterator)) { - try { - extendOptions = Object.assign({}, extendOptions, { - __hasIterator: true - }); - } catch (_) { - extendOptions.__hasIterator = true; - } - } - var extendedNativeClass = api.__extendClass(nativeClass, methods, extendOptions); - var extended = wrapNativeClass(extendedNativeClass); - try { - Object.setPrototypeOf(extended, wrapper || constructable); - } catch (_) { - } - var extendedPrototype = Object.create(constructable.prototype || null); - try { - Object.defineProperties(extendedPrototype, Object.getOwnPropertyDescriptors(methods)); - } catch (_) { - Object.keys(methods).forEach(function(key) { - extendedPrototype[key] = methods[key]; - }); - } - try { - Object.defineProperty(extendedPrototype, 'constructor', { - configurable: true, - enumerable: false, - writable: true, - value: extended - }); - } catch (_) { - } - extended.prototype = extendedPrototype; - try { - api.__rememberClassWrapper(extendedNativeClass, extended, extendedPrototype); - } catch (_) { - } - return extended; - } - }); - } catch (_) { - } - try { - Object.defineProperty(constructable, 'alloc', { - configurable: true, - enumerable: false, - writable: true, - value: function() { - return rememberInstanceClass(nativeClass.alloc.apply(nativeClass, arguments)); - } - }); - } catch (_) { - } - try { - Object.defineProperty(constructable, 'new', { - configurable: true, - enumerable: false, - writable: false, - value: function() { - if (arguments.length !== 0) { - throw new Error('new does not take arguments; use invoke for an explicit Objective-C selector.'); - } - if (typeof nativeClass.alloc !== 'function') { - throw new Error('Native class cannot be allocated'); - } - var instance = nativeClass.alloc(); - if (instance && typeof instance.init === 'function') { - return rememberInstanceClass(instance.init()); - } - return rememberInstanceClass(instance); - } - }); - } catch (_) { - } - try { - Object.defineProperty(constructable, 'caller', { - configurable: true, - enumerable: false, - writable: false, - value: null - }); - } catch (_) { - } - try { - Object.defineProperty(constructable, 'arguments', { - configurable: true, - enumerable: false, - writable: false, - value: null - }); - } catch (_) { - } - var basePrototypeTarget = {}; - var classMembersInstalled = false; - function installClassMembers(target, members, receiverIsClass) { - if (!target || !members || typeof members.length !== 'number') { - return; - } - for (var i = 0; i < members.length; i++) { - var member = members[i]; - if (!member || !member.name || Object.prototype.hasOwnProperty.call(target, member.name)) { - continue; - } - try { - if (member.property) { - var descriptor = { - configurable: true, - enumerable: false, - get: receiverIsClass - ? (function(name, selectorName) { - return function() { - return selectorName - ? nativeClass.invoke(selectorName) - : nativeClass[name]; - }; - })(member.name, member.selectorName) - : (function(name) { - return function() { - return api.__invokeBase(nativeClass, this, name); - }; - })(member.name) - }; - if (!member.readonly) { - descriptor.set = receiverIsClass - ? (function(name, setterSelectorName) { - return function(value) { - if (setterSelectorName) { - return nativeClass.invoke(setterSelectorName, value); - } - nativeClass[name] = value; - }; - })(member.name, member.setterSelectorName) - : (function(name) { - return function(value) { - return api.__invokeBase(nativeClass, this, name, value); - }; - })(member.name); - } - Object.defineProperty(target, member.name, descriptor); - } else { - Object.defineProperty(target, member.name, { - configurable: true, - enumerable: false, - writable: true, - value: receiverIsClass - ? (function(name) { - return function() { - if (this && typeof this === 'object' && this.kind === 'object') { - var baseArgs = [nativeClass, this, name]; - Array.prototype.push.apply(baseArgs, arguments); - return api.__invokeBase.apply(api, baseArgs); - } - return nativeClass[name].apply(nativeClass, arguments); - }; - })(member.name) - : (function(name) { - return function() { - var args = [nativeClass, this, name]; - Array.prototype.push.apply(args, arguments); - return api.__invokeBase.apply(api, args); - }; - })(member.name) - }); - } - } catch (_) { - } - } - } - function installNativeClassMembersIfNeeded() { - if (classMembersInstalled) { - return; - } - classMembersInstalled = true; - installClassMembers(constructable, nativeClass.__staticMembers, true); - installClassMembers(basePrototypeTarget, nativeClass.__instanceMembers, false); - try { - delete constructable.__nativeApiInstallMembers; - } catch (_) { - } - } - try { - Object.defineProperty(constructable, '__nativeApiInstallMembers', { - configurable: true, - enumerable: false, - writable: false, - value: installNativeClassMembersIfNeeded - }); - } catch (_) { - } - try { - Object.defineProperty(basePrototypeTarget, 'constructor', { - configurable: true, - enumerable: false, - writable: true, - value: constructable - }); - } catch (_) { - } - try { - Object.defineProperty(basePrototypeTarget, 'toString', { - configurable: true, - enumerable: false, - writable: true, - value: function() { - return '[object NativeScriptObject]'; - } - }); - } catch (_) { - } - try { - if (typeof Symbol === 'function' && Symbol.iterator && - typeof api.__fastEnumeration === 'function') { - Object.defineProperty(basePrototypeTarget, Symbol.iterator, { - configurable: true, - enumerable: false, - writable: true, - value: function() { - return api.__fastEnumeration(this); - } - }); - } - } catch (_) { - } - constructable.prototype = typeof Proxy === 'function' - ? new Proxy(basePrototypeTarget, { - get: function(target, property, receiver) { - installNativeClassMembersIfNeeded(); - if (property in target) { - return Reflect.get(target, property, receiver); - } - if (typeof property === 'symbol') { - return undefined; - } - return function() { - var args = [nativeClass, this, String(property)]; - Array.prototype.push.apply(args, arguments); - return api.__invokeBase.apply(api, args); - }; - }, - set: function(target, property, value, receiver) { - if (property === 'prototype') { - target[property] = value; - return true; - } - if (setDescriptorValue(target, property, receiver, value)) { - return true; - } - if (receiver && receiver !== target) { - Object.defineProperty(receiver, property, { - configurable: true, - enumerable: true, - writable: true, - value: value - }); - return true; - } - target[property] = value; - return true; - }, - has: function(target, property) { - installNativeClassMembersIfNeeded(); - return property in target; - }, - ownKeys: function(target) { - installNativeClassMembersIfNeeded(); - return Reflect.ownKeys(target); - }, - getOwnPropertyDescriptor: function(target, property) { - installNativeClassMembersIfNeeded(); - return Reflect.getOwnPropertyDescriptor(target, property); - } - }) - : basePrototypeTarget; - try { - Object.defineProperty(constructable, Symbol.hasInstance, { - configurable: true, - enumerable: false, - value: function(value) { - if (!value || typeof value !== 'object') { - return false; - } - var expectedName = nativeClass.runtimeName || nativeClass.name; - try { - if (typeof value.isKindOfClass === 'function' && - value.isKindOfClass(constructable)) { - return true; - } - } catch (_) { - } - try { - var current = typeof value.class === 'function' ? value.class() : null; - while (current) { - if (current === wrapper || current === constructable) { - return true; - } - var currentName = current.runtimeName || current.name; - if (typeof expectedName === 'string' && currentName === expectedName) { - return true; - } - var next = current.superclass || null; - if (typeof next === 'function' && next.kind !== 'class') { - next = next.call(current); - } - current = next || null; - } - } catch (_) { - } - return typeof expectedName === 'string' && value.className === expectedName; - } - }); - } catch (_) { - } - var cachedNativeFunctions = typeof Map === 'function' ? new Map() : null; - var wrapper = typeof Proxy === 'function' - ? new Proxy(constructable, { - get: function(target, property, receiver) { - if (property === '__nativeApiClass') { - return nativeClass; - } - if (property === 'toString') { - return function() { - return String(nativeClass); - }; - } - if (property === 'hasOwnProperty') { - return function(key) { - installNativeClassMembersIfNeeded(); - return Object.prototype.hasOwnProperty.call(target, key); - }; - } - if (Object.prototype.hasOwnProperty.call(target, property) || - property === 'prototype' || - property === 'length' || - property === 'name') { - return Reflect.get(target, property, receiver); - } - installNativeClassMembersIfNeeded(); - if (Object.prototype.hasOwnProperty.call(target, property)) { - return Reflect.get(target, property, receiver); - } - if (cachedNativeFunctions && cachedNativeFunctions.has(property)) { - return cachedNativeFunctions.get(property); - } - var nativeValue = nativeClass[property]; - if (nativeValue !== undefined) { - if (typeof nativeValue === 'function') { - if (cachedNativeFunctions) { - cachedNativeFunctions.set(property, nativeValue); - } - try { - Object.defineProperty(target, property, { - configurable: true, - enumerable: false, - writable: false, - value: nativeValue - }); - } catch (_) { - } - } - return nativeValue; - } - var reflected = Reflect.get(target, property, receiver); - if (reflected !== undefined || property in target) { - return reflected; - } - installNativeClassMembersIfNeeded(); - reflected = Reflect.get(target, property, receiver); - if (reflected !== undefined || property in target) { - return reflected; - } - return reflected; - }, - set: function(target, property, value, receiver) { - if (property === 'prototype') { - target[property] = value; - return true; - } - if (setDescriptorValue(target, property, receiver, value)) { - return true; - } - try { - nativeClass[property] = value; - return true; - } catch (_) { - } - if (receiver && receiver !== target) { - Object.defineProperty(receiver, property, { - configurable: true, - enumerable: true, - writable: true, - value: value - }); - return true; - } - return Reflect.set(target, property, value, receiver); - }, - has: function(target, property) { - installNativeClassMembersIfNeeded(); - return property in target || property in nativeClass; - }, - ownKeys: function(target) { - installNativeClassMembersIfNeeded(); - return Reflect.ownKeys(target).filter(function(key) { - return key !== 'new' && - key !== 'hasOwnProperty' && - key !== '__nativeApiInstallMembers'; - }); - }, - getOwnPropertyDescriptor: function(target, property) { - installNativeClassMembersIfNeeded(); - return Reflect.getOwnPropertyDescriptor(target, property); - } - }) - : constructable; - if (classWrappers) { - classWrappers.set(nativeClass, wrapper); - } - try { - var nativeSuperclass = nativeClass.__superclass; - if (nativeSuperclass && nativeSuperclass !== nativeClass) { - var superclassWrapper = wrapNativeClass(nativeSuperclass); - if (superclassWrapper && superclassWrapper !== wrapper && - typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(wrapper, superclassWrapper); - } - } - } catch (_) { - } - try { - api.__rememberClassWrapper(nativeClass, wrapper, constructable.prototype); - } catch (_) { - } - if (nativeClassName) { - classWrappersByName[nativeClassName] = wrapper; - cacheGlobal(nativeClassName, wrapper); - if (!Object.prototype.hasOwnProperty.call(globalThis, nativeClassName)) { - try { - Object.defineProperty(globalThis, nativeClassName, { - configurable: true, - enumerable: false, - writable: false, - value: wrapper - }); - } catch (_) { - } - } - } - if (nativeClass.name && nativeClass.name !== nativeClassName) { - classWrappersByName[nativeClass.name] = wrapper; - cacheGlobal(nativeClass.name, wrapper); - } - return wrapper; - } - - function rememberClassOnInstance(instance, classWrapper) { - if (instance && typeof instance === 'object' && classWrapper) { - try { - if (typeof api.__rememberObjectClassWrapper === 'function') { - api.__rememberObjectClassWrapper(instance, classWrapper); - } else { - instance.__nativeApiClassWrapper = classWrapper; - } - } catch (_) { - } - } - return instance; - } - - function isNativeClassLike(value) { - if (!value || (typeof value !== 'object' && typeof value !== 'function')) { - return false; - } - if (value.kind === 'class') { - return true; - } - try { - return !!value.__nativeApiClass; - } catch (_) { - return false; - } - } - - function nativeClassLikeHandle(value) { - if (!value || (typeof value !== 'object' && typeof value !== 'function')) { - return value; - } - try { - if (typeof value.__nativeApiEnsureClass === 'function') { - value = value.__nativeApiEnsureClass(); - } - } catch (_) { - } - try { - return value.__nativeApiClass || value; - } catch (_) { - return value; - } - } - - function materializeTypeScriptNativeClass(constructor) { - if (!constructor || typeof constructor !== 'function') { - return undefined; - } - var state = constructor.__nativeApiTypeScriptState; - if (!state) { - return undefined; - } - if (state.wrapper) { - return state.wrapper; - } - if (state.materializing) { - return state.base; - } - - state.materializing = true; - try { - var baseWrapper = state.base; - if (baseWrapper && typeof baseWrapper.__nativeApiEnsureClass === 'function') { - baseWrapper = baseWrapper.__nativeApiEnsureClass(); - } - - var options = {}; - var className = constructor.ObjCClassName || constructor.name; - if (className) { - options.name = className; - } - if (constructor.ObjCProtocols) { - options.protocols = constructor.ObjCProtocols; - } - if (constructor.ObjCExposedMethods) { - options.exposedMethods = constructor.ObjCExposedMethods; - } - - var nativeBase = nativeClassLikeHandle(baseWrapper); - var nativeClass = api.__extendClass(nativeBase, constructor.prototype || {}, options); - var wrapper = wrapNativeClass(nativeClass); - state.wrapper = wrapper; - - try { - Object.setPrototypeOf(constructor, wrapper); - } catch (_) { - } - try { - api.__rememberClassWrapper(nativeClass, constructor, constructor.prototype || {}); - } catch (_) { - } - return wrapper; - } finally { - state.materializing = false; - } - } - - function defineTypeScriptStaticForwarder(constructor, name, isProperty, readonly) { - if (!name || name === 'length' || name === 'name' || name === 'prototype' || - Object.prototype.hasOwnProperty.call(constructor, name)) { - return; - } - - var descriptor = { - configurable: true, - enumerable: false - }; - - if (isProperty) { - descriptor.get = function() { - var wrapper = materializeTypeScriptNativeClass(constructor); - return wrapper ? wrapper[name] : undefined; - }; - if (!readonly) { - descriptor.set = function(value) { - var wrapper = materializeTypeScriptNativeClass(constructor); - if (wrapper) { - wrapper[name] = value; - } - }; - } - } else { - descriptor.writable = true; - descriptor.value = function() { - if (name === 'class') { - materializeTypeScriptNativeClass(constructor); - return constructor; - } - if (name === 'superclass') { - var state = constructor.__nativeApiTypeScriptState; - return state && state.base; - } - var wrapper = materializeTypeScriptNativeClass(constructor); - var member = wrapper && wrapper[name]; - if (typeof member !== 'function') { - throw new TypeError(String(name) + ' is not a function'); - } - var result = member.apply(wrapper, arguments); - if (name === 'alloc' || name === 'new' || name === 'construct') { - return rememberClassOnInstance(result, constructor); - } - return result; - }; - } - - try { - Object.defineProperty(constructor, name, descriptor); - } catch (_) { - } - } - - function installTypeScriptNativeClassSupport(constructor, base) { - if (!constructor || typeof constructor !== 'function' || !isNativeClassLike(base)) { - return false; - } - if (constructor.__nativeApiTypeScriptState) { - return true; - } - - try { - Object.defineProperty(constructor, '__nativeApiTypeScriptState', { - configurable: false, - enumerable: false, - writable: false, - value: { - base: base, - wrapper: null, - materializing: false - } - }); - } catch (_) { - constructor.__nativeApiTypeScriptState = { - base: base, - wrapper: null, - materializing: false - }; - } - - try { - Object.defineProperty(constructor, '__nativeApiEnsureClass', { - configurable: false, - enumerable: false, - writable: false, - value: function() { - return materializeTypeScriptNativeClass(constructor); - } - }); - } catch (_) { - } - - try { - Object.defineProperty(constructor, '__nativeApiClass', { - configurable: true, - enumerable: false, - get: function() { - var wrapper = materializeTypeScriptNativeClass(constructor); - return wrapper && wrapper.__nativeApiClass; - } - }); - } catch (_) { - } - - ['alloc', 'new', 'class', 'superclass', 'extend'].forEach(function(name) { - defineTypeScriptStaticForwarder(constructor, name, false, false); - }); - - try { - var members = base.__staticMembers || []; - for (var i = 0; i < members.length; i++) { - var member = members[i]; - if (member && member.name) { - defineTypeScriptStaticForwarder( - constructor, - member.name, - !!member.property, - !!member.readonly - ); - } - } - } catch (_) { - } - - return true; - } - - function installTypeScriptNativeHelpers() { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function(d, b) { d.__proto__ = b; }) || - function(d, b) { - for (var p in b) { - if (Object.prototype.hasOwnProperty.call(b, p)) { - d[p] = b[p]; - } - } - }; - - globalThis.__extends = function(d, b) { - if (typeof b !== 'function' && b !== null) { - throw new TypeError('Class extends value ' + String(b) + ' is not a constructor or null'); - } - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - if (b !== null) { - installTypeScriptNativeClassSupport(d, b); - } - }; - - globalThis.NativeClass = function NativeClass(constructor) { - if (constructor && typeof constructor.__nativeApiEnsureClass === 'function') { - constructor.__nativeApiEnsureClass(); - } - return constructor; - }; - - globalThis.ObjCClass = function ObjCClass() { - var protocols = Array.prototype.slice.call(arguments); - return function(constructor) { - if (constructor.ObjCProtocols) { - Array.prototype.push.apply(constructor.ObjCProtocols, protocols); - } else { - constructor.ObjCProtocols = protocols; - } - if (typeof constructor.__nativeApiEnsureClass === 'function') { - constructor.__nativeApiEnsureClass(); - } - return constructor; - }; - }; - } - - function wrapInteropFactory(nativeFactory, properties) { - if (typeof nativeFactory !== 'function' || nativeFactory.__nativeScriptConstructable) { - return nativeFactory; - } - var constructable = function NativeScriptInteropValue() { - return nativeFactory.apply(undefined, arguments); - }; - try { - if (nativeFactory.prototype) { - constructable.prototype = nativeFactory.prototype; - } - } catch (_) { - } - try { - Object.defineProperty(constructable, Symbol.hasInstance, { - configurable: true, - enumerable: false, - value: function(value) { - return !!value && typeof value === 'object' && value.kind === properties.kind; - } - }); - } catch (_) { - } - Object.keys(properties).forEach(function(key) { - try { - Object.defineProperty(constructable, key, { - configurable: true, - enumerable: false, - writable: false, - value: properties[key] - }); - } catch (_) { - } - }); - Object.defineProperty(constructable, '__nativeScriptConstructable', { - configurable: false, - enumerable: false, - writable: false, - value: true - }); - return constructable; - } - - function installInteropConstructors() { - var interop = globalThis.interop; - if (!interop || typeof interop !== 'object') { - return; - } - var pointerSize; - try { - if (typeof interop.sizeof === 'function' && interop.types && interop.types.pointer !== undefined) { - pointerSize = interop.sizeof(interop.types.pointer); - } - } catch (_) { - pointerSize = undefined; - } - interop.Pointer = wrapInteropFactory(interop.Pointer, { kind: 'pointer', sizeof: pointerSize }); - interop.Reference = wrapInteropFactory(interop.Reference, { kind: 'reference', sizeof: pointerSize }); - interop.FunctionReference = wrapInteropFactory( - interop.FunctionReference, - { kind: 'functionReference', sizeof: pointerSize } - ); - if (interop.types && typeof interop.types === 'object') { - Object.keys(interop.types).forEach(function(name) { - var value = interop.types[name]; - if (typeof value !== 'number') { - return; - } - var boxed = { - valueOf: function() { return value; }, - toString: function() { return String(value); } - }; - Object.defineProperty(boxed, typeCodeKey, { - configurable: false, - enumerable: false, - writable: false, - value: value - }); - interop.types[name] = boxed; - }); - } - } - - function defineInlineFunction(name, value) { - if (Object.prototype.hasOwnProperty.call(globalThis, name)) { - return; - } - Object.defineProperty(globalThis, name, { - configurable: true, - enumerable: false, - writable: true, - value: value - }); - } - - function installInlineFunctions() { - var makePoint = function(x, y) { return { x: x, y: y }; }; - var makeSize = function(width, height) { return { width: width, height: height }; }; - var makeRect = function(x, y, width, height) { - return { origin: { x: x, y: y }, size: { width: width, height: height } }; - }; - defineInlineFunction('CGPointMake', makePoint); - defineInlineFunction('NSMakePoint', makePoint); - defineInlineFunction('CGSizeMake', makeSize); - defineInlineFunction('NSMakeSize', makeSize); - defineInlineFunction('CGRectMake', makeRect); - defineInlineFunction('NSMakeRect', makeRect); - defineInlineFunction('NSMakeRange', function(location, length) { - return { location: location, length: length }; - }); - defineInlineFunction('UIEdgeInsetsMake', function(top, left, bottom, right) { - return { top: top, left: left, bottom: bottom, right: right }; - }); - } - - function names(kind) { - var metadata = api.metadata; - var fn = metadata && metadata[kind]; - return typeof fn === 'function' ? fn() : []; - } - - function nameSet(values) { - var result = Object.create(null); - (values || []).forEach(function(value) { - result[value] = true; - }); - return result; - } - - var classNameList = names('classNames'); - var functionNameList = names('functionNames'); - var constantNameList = names('constantNames'); - var protocolNameList = names('protocolNames'); - var enumNameList = names('enumNames'); - var functionNameSet = nameSet(functionNameList); - var constantNameSet = nameSet(constantNameList); - var classNameSet = nameSet(classNameList); - var protocolNameSet = nameSet(protocolNameList); - var enumNameSet = nameSet(enumNameList); - - function resolveNativeApiEnum(enumName) { - return (api.getEnum && api.getEnum(enumName)) || api[enumName]; - } - - Object.defineProperty(globalThis, '__nativeScriptResolveNativeApiLazyGlobal', { - configurable: false, - enumerable: false, - writable: false, - value: function(name, kind) { - var value; - if (kind === 'class') { - value = wrapNativeClass(api[name]); - } else if (kind === 'function' || kind === 'constant') { - value = api[name]; - } else if (kind === 'protocol') { - value = (api.getProtocol && api.getProtocol(name)) || api[name]; - } else if (kind === 'enum') { - value = resolveNativeApiEnum(name); - } else if (kind === 'struct') { - value = wrapAggregateConstructor((api.getStruct && api.getStruct(name)) || api[name]); - } else if (kind === 'union') { - value = wrapAggregateConstructor((api.getUnion && api.getUnion(name)) || api[name]); - } else if (kind && kind.indexOf('enumMember:') === 0) { - var enumValue = resolveNativeApiEnum(kind.slice('enumMember:'.length)); - value = enumValue && enumValue[name]; - } else { - value = api[name]; - } - cacheGlobal(name, value); - return value; - } - }); - - classNameList.forEach(function(name) { - defineLazyGlobal(name, function(className) { - return wrapNativeClass(api[className]); - }, false, 'class'); - }); - functionNameList.forEach(function(name) { - defineLazyGlobal(name, function(functionName) { - return api[functionName]; - }, false, 'function'); - }); - constantNameList.forEach(function(name) { - defineLazyGlobal(name, function(constantName) { - return api[constantName]; - }, false, 'constant'); - }); - protocolNameList.forEach(function(name) { - defineLazyGlobal(name, function(protocolName) { - return (api.getProtocol && api.getProtocol(protocolName)) || api[protocolName]; - }, false, 'protocol'); - }); - enumNameList.forEach(function(name) { - defineLazyGlobal(name, resolveNativeApiEnum, false, 'enum'); - var enumValue = resolveNativeApiEnum(name); - if (!enumValue || typeof enumValue !== 'object') { - return; - } - Object.keys(enumValue).forEach(function(memberName) { - if (/^-?\d+$/.test(memberName)) { - return; - } - defineLazyGlobal(memberName, function() { - return enumValue[memberName]; - }, false, 'enumMember:' + name); - }); - }); - names('structNames').forEach(function(name) { - var conflictsWithValue = - !!functionNameSet[name] || !!constantNameSet[name] || !!classNameSet[name] || - !!protocolNameSet[name] || !!enumNameSet[name]; - defineLazyGlobal(name, function(structName) { - return wrapAggregateConstructor((api.getStruct && api.getStruct(structName)) || api[structName]); - }, !conflictsWithValue, 'struct'); - }); - names('unionNames').forEach(function(name) { - var conflictsWithValue = - !!functionNameSet[name] || !!constantNameSet[name] || !!classNameSet[name] || - !!protocolNameSet[name] || !!enumNameSet[name]; - defineLazyGlobal(name, function(unionName) { - return wrapAggregateConstructor((api.getUnion && api.getUnion(unionName)) || api[unionName]); - }, !conflictsWithValue, 'union'); - }); - - if (typeof globalThis.UIColor === 'undefined' && - typeof globalThis.NSColor !== 'undefined') { - globalThis.UIColor = globalThis.NSColor; - cacheGlobal('UIColor', globalThis.UIColor); - } - var colorCtor = globalThis.UIColor || globalThis.NSColor; - if (colorCtor && colorCtor.prototype && - typeof colorCtor.prototype.initWithRedGreenBlueAlpha !== 'function') { - 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; - }; - } - defineLazyGlobal('CC_SHA256', function() { return api.CC_SHA256; }); - - installInteropConstructors(); - installTypeScriptNativeHelpers(); - installInlineFunctions(); - - try { - Object.defineProperty(globalThis, installedFlagName, { - configurable: false, - enumerable: false, - writable: false, - value: true - }); - } catch (_) { - } -}) -)JSI_GLOBALS"; - - std::string script(GlobalInstaller); - script += "("; - script += jsStringLiteral(globalName); - script += ");"; - runtime.evaluateJavaScript(std::make_shared(std::move(script)), - "NativeApiJsiGlobals.js"); - NativeApiJsiWriteSmokeStage("jsi:globals:after-eval"); -} - -void InstallNativeApiJSI(Runtime& runtime, const NativeApiJsiConfig& config) { - const char* globalName = config.globalName != nullptr && config.globalName[0] != '\0' - ? config.globalName - : "__nativeScriptNativeApi"; - NativeApiJsiWriteSmokeStage("jsi:create-api"); - Object api = CreateNativeApiJSI(runtime, config); - Object global = runtime.global(); - NativeApiJsiWriteSmokeStage("jsi:set-global"); - global.setProperty(runtime, globalName, api); - - NativeApiJsiWriteSmokeStage("jsi:set-interop"); - Value existingInterop = global.getProperty(runtime, "interop"); - if (existingInterop.isUndefined() || existingInterop.isNull()) { - global.setProperty(runtime, "interop", api.getProperty(runtime, "interop")); - } - if (config.installGlobalSymbols) { - NativeApiJsiWriteSmokeStage("jsi:install-globals"); - InstallNativeApiJsiGlobalSymbols(runtime, globalName); - } else { - NativeApiJsiWriteSmokeStage("jsi:install-aggregate-globals"); - InstallAggregateGlobals(runtime, api, "protocolNames"); - } - NativeApiJsiWriteSmokeStage("jsi:installed"); -} diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiInvocation.h b/NativeScript/ffi/shared/jsi/NativeApiJsiInvocation.h deleted file mode 100644 index fe45f29fc..000000000 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiInvocation.h +++ /dev/null @@ -1,518 +0,0 @@ -bool isValidMetadataStringOffset(MDMetadataReader* metadata, - MDSectionOffset offset) { - if (metadata == nullptr || metadata->constantsOffset < metadata->stringsOffset) { - return false; - } - return offset < metadata->constantsOffset - metadata->stringsOffset; -} - -bool startsWith(const std::string& value, const std::string& prefix) { - return value.size() >= prefix.size() && - value.compare(0, prefix.size(), prefix) == 0; -} - -bool endsWith(const std::string& value, const std::string& suffix) { - return value.size() >= suffix.size() && - value.compare(value.size() - suffix.size(), suffix.size(), suffix) == 0; -} - -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() && endsWith(enumName, suffix)) { - return enumName.substr(0, enumName.size() - suffix.size()); - } - } - - return enumName; -} - -bool isNSComparisonResultOrderingName(const std::string& enumName, - const std::string& member) { - if (enumName != "NSComparisonResult") { - return false; - } - return member == "Ascending" || member == "Same" || member == "Descending"; -} - -Value enumToObject(Runtime& runtime, MDMetadataReader* metadata, - const NativeApiSymbol& symbol) { - Object result(runtime); - if (metadata == nullptr || symbol.offset == MD_SECTION_OFFSET_NULL) { - return result; - } - - std::string enumName = symbol.name; - std::string strippedPrefix = stripEnumSuffix(enumName); - MDSectionOffset offset = symbol.offset + sizeof(MDSectionOffset); - bool next = true; - while (next) { - auto nameOffset = metadata->getOffset(offset); - next = (nameOffset & metagen::mdSectionOffsetNext) != 0; - nameOffset &= ~metagen::mdSectionOffsetNext; - offset += sizeof(MDSectionOffset); - - const char* memberName = metadata->resolveString(nameOffset); - int64_t value = metadata->getEnumValue(offset); - offset += sizeof(int64_t); - - std::string canonicalName = memberName != nullptr ? memberName : ""; - std::vector aliases; - aliases.push_back(canonicalName); - - if (!strippedPrefix.empty() && startsWith(canonicalName, strippedPrefix) && - canonicalName.size() > strippedPrefix.size()) { - aliases.push_back(canonicalName.substr(strippedPrefix.size())); - } else if (!strippedPrefix.empty() && - !startsWith(canonicalName, strippedPrefix)) { - aliases.push_back(strippedPrefix + canonicalName); - } - - if (startsWith(enumName, "NS") && !startsWith(canonicalName, "NS")) { - aliases.push_back(std::string("NS") + canonicalName); - } - - if (enumName == "NSStringCompareOptions" && - !endsWith(canonicalName, "Search")) { - aliases.push_back(canonicalName + "Search"); - aliases.push_back(std::string("NS") + canonicalName + "Search"); - } - - if (!startsWith(canonicalName, "k")) { - aliases.push_back(std::string("k") + enumName + canonicalName); - } - - if (isNSComparisonResultOrderingName(enumName, canonicalName)) { - aliases.push_back(std::string("Ordered") + canonicalName); - aliases.push_back(std::string("NSOrdered") + canonicalName); - } - - std::vector uniqueAliases; - std::unordered_set seenAliases; - for (const auto& alias : aliases) { - if (!alias.empty() && seenAliases.insert(alias).second) { - uniqueAliases.push_back(alias); - } - } - - for (const auto& alias : uniqueAliases) { - result.setProperty(runtime, alias.c_str(), static_cast(value)); - } - - char valueKey[32] = {}; - snprintf(valueKey, sizeof(valueKey), "%lld", static_cast(value)); - if (!result.hasProperty(runtime, valueKey)) { - std::string reverseName = - uniqueAliases.size() > 1 ? uniqueAliases[1] : canonicalName; - result.setProperty(runtime, valueKey, makeString(runtime, reverseName)); - } - } - return result; -} - -Value constantToValue(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiSymbol& symbol) { - MDMetadataReader* metadata = bridge->metadata(); - if (metadata == nullptr || symbol.offset == MD_SECTION_OFFSET_NULL) { - return Value::undefined(); - } - - MDSectionOffset offset = symbol.offset + sizeof(MDSectionOffset); - auto evalKind = metadata->getVariableEvalKind(offset); - offset += sizeof(metagen::MDVariableEvalKind); - - switch (evalKind) { - case metagen::mdEvalInt64: - return static_cast(metadata->getInt64(offset)); - case metagen::mdEvalDouble: - return metadata->getDouble(offset); - case metagen::mdEvalString: { - if (isValidMetadataStringOffset(metadata, offset)) { - auto stringOffset = metadata->getOffset(offset); - return makeString(runtime, metadata->resolveString(stringOffset)); - } - - void* symbolPtr = dlsym(bridge->selfDl(), symbol.name.c_str()); - if (symbolPtr == nullptr) { - return Value::undefined(); - } - - NativeApiJsiType stringObjectType; - stringObjectType.kind = metagen::mdTypeNSStringObject; - stringObjectType.ffiType = &ffi_type_pointer; - stringObjectType.supported = true; - return convertNativeReturnValue(runtime, bridge, stringObjectType, - symbolPtr); - } - case metagen::mdEvalNone: - break; - } - - MDSectionOffset typeOffset = offset; - NativeApiJsiType type = parseMetadataJsiType(metadata, &typeOffset, bridge.get()); - if (unsupportedJsiType(type)) { - throw facebook::jsi::JSError( - runtime, "Native constant type is not supported by pure JSI: " + - symbol.name); - } - - void* symbolPtr = dlsym(bridge->selfDl(), symbol.name.c_str()); - if (symbolPtr == nullptr) { - return Value::undefined(); - } - return convertNativeReturnValue(runtime, bridge, type, symbolPtr); -} - -void prepareJsiArgument(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, const Value& arg, - size_t index, NativeApiJsiArgumentFrame& frame) { - ffi_type* ffiType = ffiTypeForJsiArgument(type); - size_t size = - ffiType != nullptr && ffiType->size > 0 ? ffiType->size : nativeSizeForType(type); - void* target = frame.storageAt(index, size); - convertJsiFfiArgument(runtime, bridge, type, arg, target, frame); -} - -void prepareJsiArguments(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiSignature& signature, - const Value* args, size_t count, - NativeApiJsiArgumentFrame& frame) { - if (count != signature.argumentTypes.size()) { - throw facebook::jsi::JSError( - runtime, "Actual arguments count: \"" + std::to_string(count) + - "\". Expected: \"" + - std::to_string(signature.argumentTypes.size()) + "\"."); - } - - for (size_t i = 0; i < signature.argumentTypes.size(); i++) { - prepareJsiArgument(runtime, bridge, signature.argumentTypes[i], args[i], i, - frame); - } -} - -Value callNativeFunctionPointer( - Runtime& runtime, const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* pointer, bool block, const Value* args, - size_t count) { - if (pointer == nullptr) { - throw facebook::jsi::JSError(runtime, "Native function pointer is null."); - } - if (bridge == nullptr || bridge->metadata() == nullptr || - type.signatureOffset == MD_SECTION_OFFSET_NULL) { - throw facebook::jsi::JSError( - runtime, "Native function pointer metadata is unavailable."); - } - - auto signature = parseMetadataJsiSignature( - bridge->metadata(), type.signatureOffset, block ? 1 : 0, bridge.get()); - if (!signature || !signature->prepared || signature->variadic || - unsupportedJsiType(signature->returnType)) { - throw facebook::jsi::JSError( - runtime, - "Native function pointer signature is not supported by pure JSI."); - } - - NativeApiJsiArgumentFrame frame(signature->argumentTypes.size()); - prepareJsiArguments(runtime, bridge, *signature, args, count, frame); - - std::vector values; - if (block) { - values.reserve(signature->argumentTypes.size() + 1); - values.push_back(&pointer); - for (size_t i = 0; i < signature->argumentTypes.size(); i++) { - values.push_back(frame.values()[i]); - } - } - - void* callable = pointer; - if (block) { - auto literal = static_cast(pointer); - if (literal == nullptr || literal->invoke == nullptr) { - throw facebook::jsi::JSError(runtime, "Native block invoke pointer is null."); - } - callable = literal->invoke; - } - - std::vector returnStorage( - std::max(nativeSizeForType(signature->returnType), sizeof(void*)), 0); - performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { - ffi_call(&signature->cif, FFI_FN(callable), returnStorage.data(), - block ? values.data() : frame.values()); - }); - - return convertNativeReturnValue(runtime, bridge, signature->returnType, - returnStorage.data()); -} - -Value wrapNativeFunctionPointer(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* pointer, - bool block) { - const char* functionName = block ? "NativeApiJsiBlock" : "NativeApiJsiFunctionPointer"; - auto function = Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, functionName), 0, - [bridge, type, pointer, block](Runtime& runtime, const Value&, - const Value* args, size_t count) -> Value { - return callNativeFunctionPointer(runtime, bridge, type, pointer, block, - args, count); - }); - function.setProperty(runtime, "kind", - makeString(runtime, block ? "block" : "functionPointer")); - function.setProperty( - runtime, "__nativeApiPointerObject", - createPointer(runtime, bridge, pointer)); - function.setProperty( - runtime, "__nativeApiPointer", - static_cast(reinterpret_cast(pointer))); - function.setProperty( - runtime, "nativeAddress", - static_cast(reinterpret_cast(pointer))); - function.setProperty(runtime, "sizeof", - static_cast(sizeof(void*))); - function.setProperty( - runtime, "toString", - Function::createFromHostFunction( - runtime, PropNameID::forAscii(runtime, "toString"), 0, - [pointer, block](Runtime& runtime, const Value&, const Value*, - size_t) -> Value { - char address[32] = {}; - snprintf(address, sizeof(address), "%p", pointer); - return makeString(runtime, - std::string("[NativeApiJsi ") + - (block ? "Block " : "FunctionPointer ") + - address + "]"); - })); - return function; -} - -Value callCFunction(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiSymbol& symbol, const Value* args, - size_t count) { - MDMetadataReader* metadata = bridge->metadata(); - if (metadata == nullptr) { - throw facebook::jsi::JSError(runtime, "Native metadata is not loaded."); - } - - void* fnptr = dlsym(bridge->selfDl(), symbol.name.c_str()); - if (fnptr == nullptr) { - throw facebook::jsi::JSError(runtime, - "Native function is not available: " + - symbol.name); - } - - MDSectionOffset signatureOffset = - metadata->signaturesOffset + - metadata->getOffset(symbol.offset + sizeof(MDSectionOffset)); - auto signature = parseMetadataJsiSignature( - metadata, signatureOffset, 0, bridge.get(), - (metadata->getFunctionFlag(symbol.offset + sizeof(MDSectionOffset) * 2) & - metagen::mdFunctionReturnOwned) != 0); - if (!signature || !signature->prepared || signature->variadic || - unsupportedJsiType(signature->returnType)) { - throw facebook::jsi::JSError( - runtime, "Native function signature is not supported by pure JSI: " + - symbol.name); - } - - NativeApiJsiArgumentFrame frame(signature->argumentTypes.size()); - prepareJsiArguments(runtime, bridge, *signature, args, count, frame); - - if (symbol.name == "NSApplicationMain" || - symbol.name == "UIApplicationMain") { - runtime.drainMicrotasks(); - } - - std::vector returnStorage( - std::max(nativeSizeForType(signature->returnType), sizeof(void*)), 0); - bool dispatchingNativeCallToUI = shouldDispatchNativeCallToUI(); - bool retainedReturn = false; - performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { - ffi_call(&signature->cif, FFI_FN(fnptr), returnStorage.data(), - frame.values()); - if (dispatchingNativeCallToUI && - !signature->returnType.returnOwned && - isObjectiveCObjectType(signature->returnType)) { - id object = *reinterpret_cast(returnStorage.data()); - if (object != nil) { - [object retain]; - retainedReturn = true; - } - } - }); - - NativeApiJsiType returnType = signature->returnType; - if (retainedReturn) { - returnType.returnOwned = true; - } - if (symbol.name == "CFBagContainsValue" && - (returnType.kind == metagen::mdTypeChar || - returnType.kind == metagen::mdTypeUChar || - returnType.kind == metagen::mdTypeUInt8)) { - return *returnStorage.data() != 0; - } - return convertNativeReturnValue(runtime, bridge, returnType, - returnStorage.data()); -} - -bool signatureSupportedForJsiInvocation( - const std::optional& signature) { - if (!signature || !signature->prepared || signature->variadic || - unsupportedJsiType(signature->returnType)) { - return false; - } - for (const auto& argType : signature->argumentTypes) { - if (unsupportedJsiType(argType)) { - return false; - } - } - return true; -} - -Value callObjCSelector(Runtime& runtime, - const std::shared_ptr& bridge, - id receiver, bool receiverIsClass, - const std::string& selectorName, - const NativeApiMember* member, - const Value* args, size_t count, - Class dispatchSuperClass) { - if (receiver == nil) { - throw facebook::jsi::JSError(runtime, - "Cannot send Objective-C selector to nil."); - } - - SEL selector = sel_registerName(selectorName.c_str()); - Class receiverClass = - receiverIsClass ? static_cast(receiver) : object_getClass(receiver); - Class lookupClass = dispatchSuperClass != Nil ? dispatchSuperClass : receiverClass; - Method method = receiverIsClass ? class_getClassMethod(lookupClass, selector) - : class_getInstanceMethod(lookupClass, selector); - if (method == nullptr && - (dispatchSuperClass != Nil || ![receiver respondsToSelector:selector])) { - throw facebook::jsi::JSError(runtime, - "Objective-C selector is not available: " + - selectorName); - } - - std::optional signature; - std::optional runtimeSignature; - if (member != nullptr && - member->signatureOffset != MD_SECTION_OFFSET_NULL && - member->signatureOffset != 0) { - signature = parseMetadataJsiSignature( - bridge->metadata(), member->signatureOffset, 2, bridge.get(), - (member->flags & metagen::mdMemberReturnOwned) != 0); - } - if (method != nullptr) { - runtimeSignature = parseObjCMethodJsiSignature(method, bridge.get()); - } - if (signatureSupportedForJsiInvocation(signature) && - signatureSupportedForJsiInvocation(runtimeSignature)) { - reconcileObjCMethodRuntimeSignature(&*signature, *runtimeSignature); - } - if (!signatureSupportedForJsiInvocation(signature) && runtimeSignature) { - signature = std::move(runtimeSignature); - } - - if (!signatureSupportedForJsiInvocation(signature)) { - throw facebook::jsi::JSError( - runtime, "Objective-C signature is not supported by pure JSI: " + - selectorName); - } - signature->selectorName = selectorName; - - NativeApiJsiArgumentFrame frame(signature->argumentTypes.size()); - const bool isNSErrorOutMethod = isNSErrorOutJsiMethodSignature(*signature); - if (isNSErrorOutMethod) { - size_t expected = signature->argumentTypes.size(); - if (count > expected || count + 1 < expected) { - throw facebook::jsi::JSError( - runtime, "Actual arguments count: \"" + std::to_string(count) + - "\". Expected: \"" + std::to_string(expected) + "\"."); - } - } - - const bool hasImplicitNSErrorOutArg = - isNSErrorOutMethod && count + 1 == signature->argumentTypes.size(); - NSError* implicitNSError = nil; - if (hasImplicitNSErrorOutArg) { - for (size_t i = 0; i < count; i++) { - prepareJsiArgument(runtime, bridge, signature->argumentTypes[i], args[i], i, - frame); - } - - size_t outArgIndex = signature->argumentTypes.size() - 1; - void* target = frame.storageAt(outArgIndex, sizeof(NSError**)); - NSError** implicitNSErrorOutArg = &implicitNSError; - *static_cast(target) = implicitNSErrorOutArg; - } else { - prepareJsiArguments(runtime, bridge, *signature, args, count, frame); - } - - std::vector values; - values.reserve(signature->argumentTypes.size() + 2); - struct objc_super superReceiver = {receiver, dispatchSuperClass}; - struct objc_super* superReceiverPtr = &superReceiver; - if (dispatchSuperClass != Nil) { - values.push_back(&superReceiverPtr); - } else { - values.push_back(&receiver); - } - values.push_back(&selector); - for (size_t i = 0; i < signature->argumentTypes.size(); i++) { - values.push_back(frame.values()[i]); - } - - std::vector returnStorage( - std::max(nativeSizeForType(signature->returnType), sizeof(void*)), 0); - bool dispatchingNativeCallToUI = shouldDispatchNativeCallToUI(); - bool retainedReturn = false; - performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { -#if defined(__x86_64__) - bool isStret = signature->returnType.ffiType->size > 16 && - signature->returnType.ffiType->type == FFI_TYPE_STRUCT; - void* target = dispatchSuperClass != Nil - ? (isStret ? FFI_FN(objc_msgSendSuper_stret) - : FFI_FN(objc_msgSendSuper)) - : (isStret ? FFI_FN(objc_msgSend_stret) - : FFI_FN(objc_msgSend)); - ffi_call(&signature->cif, target, returnStorage.data(), values.data()); -#else - ffi_call(&signature->cif, - dispatchSuperClass != Nil ? FFI_FN(objc_msgSendSuper) - : FFI_FN(objc_msgSend), - returnStorage.data(), values.data()); -#endif - if (dispatchingNativeCallToUI && - !signature->returnType.returnOwned && - isObjectiveCObjectType(signature->returnType)) { - id object = *reinterpret_cast(returnStorage.data()); - if (object != nil) { - [object retain]; - retainedReturn = true; - } - } - }); - - NativeApiJsiType returnType = signature->returnType; - if ((selectorName == "valueForKey:" || selectorName == "valueForKeyPath:") && - isObjectiveCObjectType(returnType)) { - returnType.kind = metagen::mdTypeAnyObject; - } - if (retainedReturn) { - returnType.returnOwned = true; - } - if (hasImplicitNSErrorOutArg && implicitNSError != nil) { - const char* errorMessage = [[implicitNSError description] UTF8String]; - throw facebook::jsi::JSError( - runtime, errorMessage != nullptr ? errorMessage : "Unknown NSError"); - } - return convertNativeReturnValue(runtime, bridge, returnType, - returnStorage.data()); -} diff --git a/NativeScript/ffi/v8/NativeApiV8.h b/NativeScript/ffi/v8/NativeApiV8.h deleted file mode 100644 index cf8c4054a..000000000 --- a/NativeScript/ffi/v8/NativeApiV8.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef NATIVESCRIPT_FFI_V8_NATIVE_API_V8_H -#define NATIVESCRIPT_FFI_V8_NATIVE_API_V8_H - -#include "ffi/shared/direct/NativeApiDirect.h" -#include "v8.h" - -namespace nativescript { - -using NativeApiV8Config = NativeApiDirectConfig; - -void InstallNativeApiV8(v8::Isolate* isolate, - v8::Local context, - const NativeApiV8Config& config = NativeApiV8Config{}); - -} // namespace nativescript - -extern "C" void NativeScriptInstallNativeApiV8(v8::Isolate* isolate, - v8::Local context, - const char* metadataPath); - -#endif // NATIVESCRIPT_FFI_V8_NATIVE_API_V8_H diff --git a/NativeScript/ffi/v8/NativeApiV8.mm b/NativeScript/ffi/v8/NativeApiV8.mm deleted file mode 100644 index 154a11947..000000000 --- a/NativeScript/ffi/v8/NativeApiV8.mm +++ /dev/null @@ -1,181 +0,0 @@ -#include "NativeApiV8.h" - -#ifdef TARGET_ENGINE_V8 - -#include "NativeApiV8Runtime.h" - -namespace nativescript { - -using NativeApiJsiConfig = NativeApiDirectConfig; -using NativeApiJsiScheduler = NativeApiDirectScheduler; - -namespace { - -using facebook::jsi::Array; -using facebook::jsi::ArrayBuffer; -using facebook::jsi::BigInt; -using facebook::jsi::Function; -using facebook::jsi::HostObject; -using facebook::jsi::MutableBuffer; -using facebook::jsi::Object; -using facebook::jsi::PropNameID; -using facebook::jsi::Runtime; -using facebook::jsi::String; -using facebook::jsi::StringBuffer; -using facebook::jsi::Value; -using metagen::MDMemberFlag; -using metagen::MDMetadataReader; -using metagen::MDSectionOffset; -using metagen::MDTypeKind; - -// clang-format off -#include "jsi/NativeApiJsiBridge.h" -// clang-format on - -#define NATIVESCRIPT_NATIVE_API_HAS_ENGINE_LAZY_GLOBALS 1 -#define NATIVESCRIPT_NATIVE_API_RETAIN_RUNTIME 1 -#define NATIVESCRIPT_NATIVE_API_RUNTIME_SCOPE 1 - -struct NativeApiV8LazyGlobalData { - NativeApiV8LazyGlobalData(v8::Isolate* isolate, const std::string& name, - const std::string& kind) { - nameValue.Reset(isolate, facebook::jsi::v8direct::makeV8String(isolate, name)); - kindValue.Reset(isolate, facebook::jsi::v8direct::makeV8String(isolate, kind)); - } - - ~NativeApiV8LazyGlobalData() { - nameValue.Reset(); - kindValue.Reset(); - } - - v8::Global nameValue; - v8::Global kindValue; -}; - -std::shared_ptr retainNativeApiJsiRuntime(Runtime& runtime) { - return std::make_shared(runtime.state()); -} - -class NativeApiJsiRuntimeScope final { - public: - explicit NativeApiJsiRuntimeScope(Runtime& runtime) - : locker_(runtime.isolate()), - isolateScope_(runtime.isolate()), - handleScope_(runtime.isolate()), - context_(runtime.context()), - contextScope_(context_) {} - - private: - v8::Locker locker_; - v8::Isolate::Scope isolateScope_; - v8::HandleScope handleScope_; - v8::Local context_; - v8::Context::Scope contextScope_; -}; - -void NativeApiV8LazyGlobalGetter(v8::Local, - const v8::PropertyCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - v8::HandleScope handleScope(isolate); - v8::Local context = isolate->GetCurrentContext(); - if (!info.Data()->IsExternal()) { - return; - } - - auto* data = static_cast(info.Data().As()->Value()); - if (data == nullptr) { - return; - } - v8::Local nameValue = data->nameValue.Get(isolate); - v8::Local kindValue = data->kindValue.Get(isolate); - - v8::Local global = context->Global(); - v8::Local resolverValue; - if (!global - ->Get(context, facebook::jsi::v8direct::makeV8String( - isolate, "__nativeScriptResolveNativeApiLazyGlobal")) - .ToLocal(&resolverValue) || - !resolverValue->IsFunction()) { - return; - } - - v8::TryCatch tryCatch(isolate); - v8::Local args[] = {nameValue, kindValue}; - v8::Local result; - if (!resolverValue.As()->Call(context, global, 2, args).ToLocal(&result)) { - if (tryCatch.HasCaught()) { - isolate->ThrowException(tryCatch.Exception()); - } - return; - } - if (global->Delete(context, nameValue).FromMaybe(false)) { - global->DefineOwnProperty(context, nameValue, result, v8::DontEnum).FromMaybe(false); - } - info.GetReturnValue().Set(result); -} - -bool InstallNativeApiEngineLazyGlobal(Runtime& runtime, std::shared_ptr, - const std::string& name, const std::string& kind, - bool force) { - if (name.empty() || kind.empty()) { - return false; - } - - v8::Isolate* isolate = runtime.isolate(); - v8::EscapableHandleScope handleScope(isolate); - v8::Local context = runtime.context(); - v8::Local global = context->Global(); - v8::Local property = facebook::jsi::v8direct::makeV8String(isolate, name); - if (!force && global->HasOwnProperty(context, property).FromMaybe(false)) { - return false; - } - - auto data = std::make_shared(isolate, name, kind); - v8::Local external = v8::External::New(isolate, data.get()); - - bool installed = global - ->SetNativeDataProperty(context, property, NativeApiV8LazyGlobalGetter, - nullptr, external, v8::DontEnum) - .FromMaybe(false); - if (installed) { - runtime.state()->retainedNativeData.push_back(std::move(data)); - } - return installed; -} - -// clang-format off -#include "jsi/NativeApiJsiHostObjects.h" -#include "jsi/NativeApiJsiCallbacks.h" -#include "jsi/NativeApiJsiConversion.h" -#include "jsi/NativeApiJsiInvocation.h" -#include "jsi/NativeApiJsiClassBuilder.h" -#include "jsi/NativeApiJsiHostObject.h" -// clang-format on - -} // namespace - -#include "jsi/NativeApiJsiInstall.h" - -void InstallNativeApiV8(v8::Isolate* isolate, v8::Local context, - const NativeApiV8Config& config) { - if (isolate == nullptr || context.IsEmpty()) { - return; - } - v8::Locker locker(isolate); - v8::Isolate::Scope isolateScope(isolate); - v8::HandleScope handleScope(isolate); - v8::Context::Scope contextScope(context); - Runtime runtime(isolate, context); - InstallNativeApiJSI(runtime, config); -} - -} // namespace nativescript - -extern "C" void NativeScriptInstallNativeApiV8(v8::Isolate* isolate, v8::Local context, - const char* metadataPath) { - nativescript::NativeApiV8Config config; - config.metadataPath = metadataPath; - nativescript::InstallNativeApiV8(isolate, context, config); -} - -#endif // TARGET_ENGINE_V8 diff --git a/NativeScript/ffi/v8/NativeApiV8HostObjects.mm b/NativeScript/ffi/v8/NativeApiV8HostObjects.mm deleted file mode 100644 index 8a11b15c4..000000000 --- a/NativeScript/ffi/v8/NativeApiV8HostObjects.mm +++ /dev/null @@ -1,187 +0,0 @@ -#include "NativeApiV8Runtime.h" - -#ifdef TARGET_ENGINE_V8 - -namespace facebook { -namespace jsi { - -namespace v8direct { - -Value valueFromLocal(Runtime& runtime, v8::Local value) { return Value(runtime, value); } - -v8::Local hostObjectTemplate(Runtime& runtime) { - auto state = runtime.state(); - if (state->hostObjectTemplate.IsEmpty()) { - v8::Local objectTemplate = v8::ObjectTemplate::New(runtime.isolate()); - objectTemplate->SetInternalFieldCount(1); - objectTemplate->SetHandler(v8::NamedPropertyHandlerConfiguration( - [](v8::Local property, - const v8::PropertyCallbackInfo& info) -> v8::Intercepted { - auto* holder = - static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); - if (holder == nullptr || holder->hostObject == nullptr) { - return v8::Intercepted::kNo; - } - Runtime runtime(holder->state); - try { - Value result = holder->hostObject->get( - runtime, PropNameID(propertyNameToUtf8(info.GetIsolate(), property))); - if (!result.isUndefined()) { - info.GetReturnValue().Set(result.local(runtime)); - return v8::Intercepted::kYes; - } - } catch (const std::exception& exception) { - throwV8Exception(info.GetIsolate(), exception); - return v8::Intercepted::kYes; - } - return v8::Intercepted::kNo; - }, - [](v8::Local property, v8::Local value, - const v8::PropertyCallbackInfo& info) -> v8::Intercepted { - auto* holder = - static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); - if (holder == nullptr || holder->hostObject == nullptr) { - return v8::Intercepted::kNo; - } - Runtime runtime(holder->state); - try { - holder->hostObject->set(runtime, - PropNameID(propertyNameToUtf8(info.GetIsolate(), property)), - Value(runtime, value)); - return v8::Intercepted::kYes; - } catch (const std::exception& exception) { - throwV8Exception(info.GetIsolate(), exception); - return v8::Intercepted::kYes; - } - }, - nullptr, nullptr, - [](const v8::PropertyCallbackInfo& info) { - auto* holder = - static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); - if (holder == nullptr || holder->hostObject == nullptr) { - return; - } - Runtime runtime(holder->state); - try { - auto propertyNames = holder->hostObject->getPropertyNames(runtime); - v8::Local result = - v8::Array::New(info.GetIsolate(), static_cast(propertyNames.size())); - for (size_t i = 0; i < propertyNames.size(); i++) { - std::string name = propertyNames[i].utf8(runtime); - result - ->Set(runtime.context(), static_cast(i), - makeV8String(info.GetIsolate(), name)) - .FromMaybe(false); - } - info.GetReturnValue().Set(result); - } catch (const std::exception& exception) { - throwV8Exception(info.GetIsolate(), exception); - } - }, - v8::Local(), v8::PropertyHandlerFlags::kNone)); - objectTemplate->SetHandler(v8::IndexedPropertyHandlerConfiguration( - [](uint32_t index, const v8::PropertyCallbackInfo& info) -> v8::Intercepted { - auto* holder = - static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); - if (holder == nullptr || holder->hostObject == nullptr) { - return v8::Intercepted::kNo; - } - Runtime runtime(holder->state); - try { - Value result = holder->hostObject->get(runtime, PropNameID(std::to_string(index))); - if (!result.isUndefined()) { - info.GetReturnValue().Set(result.local(runtime)); - return v8::Intercepted::kYes; - } - } catch (const std::exception& exception) { - throwV8Exception(info.GetIsolate(), exception); - return v8::Intercepted::kYes; - } - return v8::Intercepted::kNo; - }, - [](uint32_t index, v8::Local value, - const v8::PropertyCallbackInfo& info) -> v8::Intercepted { - auto* holder = - static_cast(info.Holder()->GetAlignedPointerFromInternalField(0)); - if (holder == nullptr || holder->hostObject == nullptr) { - return v8::Intercepted::kNo; - } - Runtime runtime(holder->state); - try { - holder->hostObject->set(runtime, PropNameID(std::to_string(index)), - Value(runtime, value)); - return v8::Intercepted::kYes; - } catch (const std::exception& exception) { - throwV8Exception(info.GetIsolate(), exception); - return v8::Intercepted::kYes; - } - }, - nullptr, nullptr, nullptr, v8::Local(), v8::PropertyHandlerFlags::kNone)); - state->hostObjectTemplate.Reset(runtime.isolate(), objectTemplate); - } - return state->hostObjectTemplate.Get(runtime.isolate()); -} - -void hostObjectWeakCallback(const v8::WeakCallbackInfo& info) { - delete info.GetParameter(); -} - -void functionWeakCallback(const v8::WeakCallbackInfo& info) { - delete info.GetParameter(); -} - -} // namespace v8direct - -Object Object::createFromHostObjectWithToken(Runtime& runtime, std::shared_ptr host, - const void* typeToken) { - v8::Local object = - v8direct::hostObjectTemplate(runtime)->NewInstance(runtime.context()).ToLocalChecked(); - auto* holder = new v8direct::HostObjectHolder(runtime.state(), std::move(host), typeToken); - object->SetAlignedPointerInInternalField(0, holder); - holder->object.Reset(runtime.isolate(), object); - holder->object.SetWeak(holder, v8direct::hostObjectWeakCallback, - v8::WeakCallbackType::kParameter); - return Object::fromValueStorage(Value(runtime, object).storage_); -} - -Function Function::createFromHostFunction(Runtime& runtime, const PropNameID& name, unsigned int, - HostFunctionType callback) { - auto* holder = new v8direct::FunctionHolder(runtime.state(), std::move(callback)); - v8::Local data = v8::External::New(runtime.isolate(), holder); - v8::Local functionTemplate = v8::FunctionTemplate::New( - runtime.isolate(), - [](const v8::FunctionCallbackInfo& info) { - auto* holder = - static_cast(info.Data().As()->Value()); - Runtime runtime(holder->state); - std::vector args; - args.reserve(info.Length()); - for (int i = 0; i < info.Length(); i++) { - args.push_back(Value(runtime, info[i])); - } - try { - Value thisValue(runtime, info.This()); - Value result = holder->callback(runtime, thisValue, args.empty() ? nullptr : args.data(), - args.size()); - info.GetReturnValue().Set(result.local(runtime)); - } catch (const std::exception& exception) { - v8direct::throwV8Exception(info.GetIsolate(), exception); - } - }, - data); - v8::Local function = - functionTemplate->GetFunction(runtime.context()).ToLocalChecked(); - std::string functionName = name.utf8(runtime); - if (!functionName.empty()) { - function->SetName(v8direct::makeV8String(runtime.isolate(), functionName)); - } - holder->function.Reset(runtime.isolate(), function); - holder->function.SetWeak(holder, v8direct::functionWeakCallback, - v8::WeakCallbackType::kParameter); - return Function(Object::fromValueStorage(Value(runtime, function).storage_)); -} - -} // namespace jsi -} // namespace facebook - -#endif // TARGET_ENGINE_V8 diff --git a/NativeScript/ffi/v8/NativeApiV8Runtime.h b/NativeScript/ffi/v8/NativeApiV8Runtime.h deleted file mode 100644 index 81c95a085..000000000 --- a/NativeScript/ffi/v8/NativeApiV8Runtime.h +++ /dev/null @@ -1,736 +0,0 @@ -#ifndef NATIVESCRIPT_FFI_V8_NATIVE_API_V8_RUNTIME_H -#define NATIVESCRIPT_FFI_V8_NATIVE_API_V8_RUNTIME_H - -#ifdef TARGET_ENGINE_V8 - -#import -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Metadata.h" -#include "MetadataReader.h" -#include "ffi.h" -#include "v8.h" - -@protocol NativeApiJsiClassBuilderProtocol -@end - -#ifdef EMBED_METADATA_SIZE -extern const unsigned char embedded_metadata[EMBED_METADATA_SIZE]; -#endif - -namespace facebook { -namespace jsi { - -class Runtime; -class Value; -class Object; -class Function; -class Array; -class String; -class BigInt; -class ArrayBuffer; - -class JSError : public std::runtime_error { - public: - JSError(Runtime&, const std::string& message) : std::runtime_error(message) {} - explicit JSError(const std::string& message) : std::runtime_error(message) {} -}; - -class StringBuffer { - public: - explicit StringBuffer(std::string value) : value_(std::move(value)) {} - const char* data() const { return value_.data(); } - size_t size() const { return value_.size(); } - - private: - std::string value_; -}; - -class MutableBuffer { - public: - virtual ~MutableBuffer() = default; - virtual size_t size() const = 0; - virtual uint8_t* data() = 0; -}; - -class PropNameID { - public: - PropNameID() = default; - explicit PropNameID(std::string value) : value_(std::move(value)) {} - - static PropNameID forAscii(Runtime&, const char* value) { - return PropNameID(value != nullptr ? value : ""); - } - - static PropNameID forAscii(Runtime&, const std::string& value) { return PropNameID(value); } - - std::string utf8(Runtime&) const { return value_; } - - private: - std::string value_; -}; - -class HostObject { - public: - virtual ~HostObject() = default; - virtual Value get(Runtime& runtime, const PropNameID& name); - virtual void set(Runtime& runtime, const PropNameID& name, const Value& value); - virtual std::vector getPropertyNames(Runtime& runtime); -}; - -using HostFunctionType = std::function; - -namespace v8direct { - -struct RuntimeState { - explicit RuntimeState(v8::Isolate* isolate, v8::Local context) : isolate(isolate) { - this->context.Reset(isolate, context); - } - - ~RuntimeState() { context.Reset(); } - - v8::Local localContext() const { return context.Get(isolate); } - - v8::Isolate* isolate = nullptr; - v8::Global context; - v8::Global hostObjectTemplate; - std::vector> retainedNativeData; -}; - -struct ValueStorage { - enum class Kind { - Undefined, - Null, - Bool, - Number, - V8, - }; - - explicit ValueStorage(Kind kind) : kind(kind) {} - - ~ValueStorage() { value.Reset(); } - - Kind kind = Kind::Undefined; - bool boolValue = false; - double numberValue = 0; - v8::Global value; -}; - -template -const void* hostObjectTypeToken() { - static int token = 0; - return &token; -} - -struct HostObjectHolder { - HostObjectHolder(std::shared_ptr state, std::shared_ptr hostObject, - const void* typeToken) - : state(std::move(state)), hostObject(std::move(hostObject)), typeToken(typeToken) {} - - ~HostObjectHolder() { object.Reset(); } - - std::shared_ptr state; - std::shared_ptr hostObject; - const void* typeToken = nullptr; - v8::Global object; -}; - -struct FunctionHolder { - FunctionHolder(std::shared_ptr state, HostFunctionType callback) - : state(std::move(state)), callback(std::move(callback)) {} - - ~FunctionHolder() { function.Reset(); } - - std::shared_ptr state; - HostFunctionType callback; - v8::Global function; -}; - -struct ArrayBufferHolder { - explicit ArrayBufferHolder(std::shared_ptr buffer) : buffer(std::move(buffer)) {} - - std::shared_ptr buffer; - v8::Global object; -}; - -inline v8::Local makeV8String(v8::Isolate* isolate, const std::string& value) { - return v8::String::NewFromUtf8(isolate, value.c_str(), v8::NewStringType::kNormal, - static_cast(value.size())) - .ToLocalChecked(); -} - -inline std::string toUtf8(v8::Isolate* isolate, v8::Local value) { - if (value.IsEmpty()) { - return {}; - } - v8::String::Utf8Value utf8(isolate, value); - return *utf8 != nullptr ? std::string(*utf8, utf8.length()) : std::string(); -} - -inline std::string propertyNameToUtf8(v8::Isolate* isolate, v8::Local property) { - if (property->IsSymbol() && - property.As()->StrictEquals(v8::Symbol::GetIterator(isolate))) { - return "Symbol.iterator"; - } - return toUtf8(isolate, property); -} - -inline std::string currentExceptionMessage(v8::Isolate* isolate, v8::TryCatch& tryCatch) { - if (tryCatch.HasCaught()) { - return toUtf8(isolate, tryCatch.Exception()); - } - return "NativeScript direct V8 operation failed."; -} - -inline void throwV8Exception(v8::Isolate* isolate, const std::exception& exception) { - isolate->ThrowException(v8::Exception::Error(makeV8String(isolate, exception.what()))); -} - -} // namespace v8direct - -class Runtime { - public: - Runtime(v8::Isolate* isolate, v8::Local context) - : state_(std::make_shared(isolate, context)) {} - - explicit Runtime(std::shared_ptr state) : state_(std::move(state)) {} - - v8::Isolate* isolate() const { return state_->isolate; } - v8::Local context() const { return state_->localContext(); } - std::shared_ptr state() const { return state_; } - - Object global(); - - Value evaluateJavaScript(std::shared_ptr buffer, const std::string& sourceURL); - - void drainMicrotasks() { isolate()->PerformMicrotaskCheckpoint(); } - - private: - std::shared_ptr state_; -}; - -class String { - public: - String() = default; - String(Runtime& runtime, v8::Local value); - - static String createFromUtf8(Runtime& runtime, const char* value) { - return String(runtime, - v8direct::makeV8String(runtime.isolate(), value != nullptr ? value : "")); - } - - static String createFromUtf8(Runtime& runtime, const std::string& value) { - return String(runtime, v8direct::makeV8String(runtime.isolate(), value)); - } - - static String createFromUtf8(Runtime& runtime, const uint8_t* value, size_t length) { - return String(runtime, v8::String::NewFromUtf8( - runtime.isolate(), - reinterpret_cast( - value != nullptr ? value : reinterpret_cast("")), - v8::NewStringType::kNormal, static_cast(length)) - .ToLocalChecked()); - } - - std::string utf8(Runtime& runtime) const { - return v8direct::toUtf8(runtime.isolate(), local(runtime)); - } - - v8::Local local(Runtime& runtime) const { - return storage_->value.Get(runtime.isolate()).As(); - } - - operator Value() const; - - private: - friend class Value; - std::shared_ptr storage_; -}; - -class Value { - public: - Value() - : storage_( - std::make_shared(v8direct::ValueStorage::Kind::Undefined)) {} - - Value(bool value) - : storage_(std::make_shared(v8direct::ValueStorage::Kind::Bool)) { - storage_->boolValue = value; - } - - Value(double value) - : storage_(std::make_shared(v8direct::ValueStorage::Kind::Number)) { - storage_->numberValue = value; - } - - Value(int value) : Value(static_cast(value)) {} - Value(uint32_t value) : Value(static_cast(value)) {} - - Value(Runtime& runtime, const Value& value) : storage_(value.storage_) {} - Value(Runtime& runtime, Value&& value) : storage_(std::move(value.storage_)) {} - Value(Runtime& runtime, const String& value) : storage_(value.storage_) {} - Value(Runtime& runtime, const Object& object); - Value(Runtime& runtime, const Function& function); - Value(Runtime& runtime, const Array& array); - Value(Runtime& runtime, const ArrayBuffer& arrayBuffer); - Value(Runtime& runtime, const BigInt& bigint); - - static Value undefined() { return Value(); } - - static Value null() { - Value value; - value.storage_ = std::make_shared(v8direct::ValueStorage::Kind::Null); - return value; - } - - bool isUndefined() const; - bool isNull() const; - bool isBool() const; - bool getBool() const; - bool isNumber() const; - double getNumber() const; - - bool isObject() const; - bool isString() const; - bool isBigInt() const; - bool isSymbol() const; - - Object asObject(Runtime& runtime) const; - String asString(Runtime& runtime) const; - BigInt getBigInt(Runtime& runtime) const; - - v8::Local local(Runtime& runtime) const { - v8::Isolate* isolate = runtime.isolate(); - switch (storage_->kind) { - case v8direct::ValueStorage::Kind::Undefined: - return v8::Undefined(isolate); - case v8direct::ValueStorage::Kind::Null: - return v8::Null(isolate); - case v8direct::ValueStorage::Kind::Bool: - return v8::Boolean::New(isolate, storage_->boolValue); - case v8direct::ValueStorage::Kind::Number: - return v8::Number::New(isolate, storage_->numberValue); - case v8direct::ValueStorage::Kind::V8: - return storage_->value.Get(isolate); - } - } - - Value(Runtime& runtime, v8::Local value) - : storage_(std::make_shared(v8direct::ValueStorage::Kind::V8)) { - storage_->value.Reset(runtime.isolate(), value); - } - - private: - friend class Runtime; - friend class Object; - friend class String; - friend class BigInt; - friend class ArrayBuffer; - friend class Function; - friend class Array; - - std::shared_ptr storage_; -}; - -class Object { - public: - Object() = default; - explicit Object(Runtime& runtime) - : storage_(std::make_shared(v8direct::ValueStorage::Kind::V8)) { - storage_->value.Reset(runtime.isolate(), v8::Object::New(runtime.isolate())); - } - - static Object fromValueStorage(std::shared_ptr storage) { - Object object; - object.storage_ = std::move(storage); - return object; - } - - template - static Object createFromHostObject(Runtime& runtime, std::shared_ptr host) { - auto baseHost = std::static_pointer_cast(std::move(host)); - return createFromHostObjectWithToken(runtime, std::move(baseHost), - v8direct::hostObjectTypeToken()); - } - - Value getProperty(Runtime& runtime, const char* name) const { - return getProperty(runtime, - v8direct::makeV8String(runtime.isolate(), name != nullptr ? name : "")); - } - - Value getProperty(Runtime& runtime, const std::string& name) const { - return getProperty(runtime, name.c_str()); - } - - Value getProperty(Runtime& runtime, const Value& key) const { - return getProperty(runtime, key.local(runtime)); - } - - Value getProperty(Runtime& runtime, v8::Local key) const { - v8::TryCatch tryCatch(runtime.isolate()); - v8::Local result; - if (!local(runtime)->Get(runtime.context(), key).ToLocal(&result)) { - throw JSError(runtime, v8direct::currentExceptionMessage(runtime.isolate(), tryCatch)); - } - return Value(runtime, result); - } - - Object getPropertyAsObject(Runtime& runtime, const char* name) const { - return getProperty(runtime, name).asObject(runtime); - } - - Function getPropertyAsFunction(Runtime& runtime, const char* name) const; - - void setProperty(Runtime& runtime, const char* name, const Value& value) { - setProperty(runtime, v8direct::makeV8String(runtime.isolate(), name != nullptr ? name : ""), - value); - } - - void setProperty(Runtime& runtime, const char* name, const String& value) { - setProperty(runtime, name, Value(runtime, value)); - } - - void setProperty(Runtime& runtime, const char* name, const Object& value) { - setProperty(runtime, name, Value(runtime, value)); - } - - void setProperty(Runtime& runtime, const char* name, const Function& value); - void setProperty(Runtime& runtime, const char* name, const Array& value); - void setProperty(Runtime& runtime, const char* name, const ArrayBuffer& value); - void setProperty(Runtime& runtime, const char* name, bool value) { - setProperty(runtime, name, Value(value)); - } - void setProperty(Runtime& runtime, const char* name, double value) { - setProperty(runtime, name, Value(value)); - } - - void setProperty(Runtime& runtime, const std::string& name, const Value& value) { - setProperty(runtime, name.c_str(), value); - } - - void setProperty(Runtime& runtime, const Value& key, const Value& value) { - setProperty(runtime, key.local(runtime), value); - } - - void setProperty(Runtime& runtime, v8::Local key, const Value& value) { - v8::TryCatch tryCatch(runtime.isolate()); - if (!local(runtime)->Set(runtime.context(), key, value.local(runtime)).FromMaybe(false)) { - throw JSError(runtime, v8direct::currentExceptionMessage(runtime.isolate(), tryCatch)); - } - } - - bool hasProperty(Runtime& runtime, const char* name) const { - v8::TryCatch tryCatch(runtime.isolate()); - return local(runtime) - ->Has(runtime.context(), - v8direct::makeV8String(runtime.isolate(), name != nullptr ? name : "")) - .FromMaybe(false); - } - - bool isFunction(Runtime& runtime) const { return local(runtime)->IsFunction(); } - bool isArray(Runtime& runtime) const { return local(runtime)->IsArray(); } - bool isArrayBuffer(Runtime& runtime) const { return local(runtime)->IsArrayBuffer(); } - - Function asFunction(Runtime& runtime) const; - Array getArray(Runtime& runtime) const; - ArrayBuffer getArrayBuffer(Runtime& runtime) const; - Array getPropertyNames(Runtime& runtime) const; - - template - bool isHostObject(Runtime& runtime) const { - auto holder = hostObjectHolder(runtime); - return holder != nullptr && holder->typeToken == v8direct::hostObjectTypeToken(); - } - - template - std::shared_ptr getHostObject(Runtime& runtime) const { - auto holder = hostObjectHolder(runtime); - if (holder == nullptr || holder->typeToken != v8direct::hostObjectTypeToken()) { - return nullptr; - } - return std::static_pointer_cast(holder->hostObject); - } - - v8::Local local(Runtime& runtime) const { - return storage_->value.Get(runtime.isolate()).As(); - } - - operator Value() const { - Value value; - value.storage_ = storage_; - return value; - } - - protected: - friend class Value; - friend class Runtime; - friend class Function; - friend class Array; - friend class ArrayBuffer; - - explicit Object(std::shared_ptr storage) : storage_(std::move(storage)) {} - - static Object createFromHostObjectWithToken(Runtime& runtime, std::shared_ptr host, - const void* typeToken); - - v8direct::HostObjectHolder* hostObjectHolder(Runtime& runtime) const { - v8::Local object = local(runtime); - if (object->InternalFieldCount() < 1) { - return nullptr; - } - return static_cast(object->GetAlignedPointerFromInternalField(0)); - } - - std::shared_ptr storage_; -}; - -class Function : public Object { - public: - Function() = default; - explicit Function(Object object) : Object(std::move(object.storage_)) {} - - static Function createFromHostFunction(Runtime& runtime, const PropNameID& name, unsigned int, - HostFunctionType callback); - - Value call(Runtime& runtime, const Value* args, size_t count) const { - v8::TryCatch tryCatch(runtime.isolate()); - std::vector> argv; - argv.reserve(count); - for (size_t i = 0; i < count; i++) { - argv.push_back(args[i].local(runtime)); - } - v8::Local result; - if (!local(runtime) - .As() - ->Call(runtime.context(), runtime.context()->Global(), static_cast(argv.size()), - argv.data()) - .ToLocal(&result)) { - throw JSError(runtime, v8direct::currentExceptionMessage(runtime.isolate(), tryCatch)); - } - return Value(runtime, result); - } - - Value call(Runtime& runtime) const { - return call(runtime, static_cast(nullptr), 0); - } - - Value call(Runtime& runtime, std::nullptr_t, size_t) const { - return call(runtime, static_cast(nullptr), 0); - } - - template - Value call(Runtime& runtime, const Value (&args)[N], size_t count) const { - return call(runtime, static_cast(args), count); - } - - template - Value call(Runtime& runtime, Args&&... args) const { - Value argv[] = {Value(runtime, std::forward(args))...}; - return call(runtime, static_cast(argv), sizeof...(Args)); - } - - Value callWithThis(Runtime& runtime, const Object& thisObject, const Value* args = nullptr, - size_t count = 0) const { - v8::TryCatch tryCatch(runtime.isolate()); - std::vector> argv; - argv.reserve(count); - for (size_t i = 0; i < count; i++) { - argv.push_back(args[i].local(runtime)); - } - v8::Local result; - if (!local(runtime) - .As() - ->Call(runtime.context(), thisObject.local(runtime), static_cast(argv.size()), - argv.data()) - .ToLocal(&result)) { - throw JSError(runtime, v8direct::currentExceptionMessage(runtime.isolate(), tryCatch)); - } - return Value(runtime, result); - } - - Value callAsConstructor(Runtime& runtime, const Value* args, size_t count) const { - v8::TryCatch tryCatch(runtime.isolate()); - std::vector> argv; - argv.reserve(count); - for (size_t i = 0; i < count; i++) { - argv.push_back(args[i].local(runtime)); - } - v8::Local result; - if (!local(runtime) - .As() - ->NewInstance(runtime.context(), static_cast(argv.size()), argv.data()) - .ToLocal(&result)) { - throw JSError(runtime, v8direct::currentExceptionMessage(runtime.isolate(), tryCatch)); - } - return Value(runtime, result); - } - - Value callAsConstructor(Runtime& runtime, std::nullptr_t, size_t) const { - return callAsConstructor(runtime, static_cast(nullptr), 0); - } - - template - Value callAsConstructor(Runtime& runtime, const Value (&args)[N], size_t count) const { - return callAsConstructor(runtime, static_cast(args), count); - } - - template - Value callAsConstructor(Runtime& runtime, Args&&... args) const { - Value argv[] = {Value(runtime, std::forward(args))...}; - return callAsConstructor(runtime, static_cast(argv), sizeof...(Args)); - } - - operator Value() const { - Value value; - value.storage_ = storage_; - return value; - } -}; - -class Array : public Object { - public: - explicit Array(Runtime& runtime, size_t size) - : Object(std::make_shared(v8direct::ValueStorage::Kind::V8)) { - storage_->value.Reset(runtime.isolate(), - v8::Array::New(runtime.isolate(), static_cast(size))); - } - - explicit Array(Object object) : Object(std::move(object.storage_)) {} - - size_t size(Runtime& runtime) const { return local(runtime).As()->Length(); } - - Value getValueAtIndex(Runtime& runtime, size_t index) const { - v8::TryCatch tryCatch(runtime.isolate()); - v8::Local result; - if (!local(runtime)->Get(runtime.context(), static_cast(index)).ToLocal(&result)) { - throw JSError(runtime, v8direct::currentExceptionMessage(runtime.isolate(), tryCatch)); - } - return Value(runtime, result); - } - - void setValueAtIndex(Runtime& runtime, size_t index, const Value& value) { - v8::TryCatch tryCatch(runtime.isolate()); - if (!local(runtime) - ->Set(runtime.context(), static_cast(index), value.local(runtime)) - .FromMaybe(false)) { - throw JSError(runtime, v8direct::currentExceptionMessage(runtime.isolate(), tryCatch)); - } - } - - void setValueAtIndex(Runtime& runtime, size_t index, const String& value) { - setValueAtIndex(runtime, index, Value(runtime, value)); - } - - operator Value() const { - Value value; - value.storage_ = storage_; - return value; - } -}; - -class BigInt { - public: - BigInt() = default; - BigInt(Runtime& runtime, v8::Local value) - : storage_(std::make_shared(v8direct::ValueStorage::Kind::V8)) { - storage_->value.Reset(runtime.isolate(), value); - } - - static BigInt fromInt64(Runtime& runtime, int64_t value) { - return BigInt(runtime, v8::BigInt::New(runtime.isolate(), value)); - } - - static BigInt fromUint64(Runtime& runtime, uint64_t value) { - return BigInt(runtime, v8::BigInt::NewFromUnsigned(runtime.isolate(), value)); - } - - String toString(Runtime& runtime, int radix) const { - v8::TryCatch tryCatch(runtime.isolate()); - v8::Local result; - (void)radix; - if (!local(runtime)->ToString(runtime.context()).ToLocal(&result)) { - throw JSError(runtime, v8direct::currentExceptionMessage(runtime.isolate(), tryCatch)); - } - return String(runtime, result); - } - - v8::Local local(Runtime& runtime) const { - return storage_->value.Get(runtime.isolate()).As(); - } - - operator Value() const { - Value value; - value.storage_ = storage_; - return value; - } - - private: - friend class Value; - std::shared_ptr storage_; -}; - -class ArrayBuffer : public Object { - public: - ArrayBuffer(Runtime& runtime, std::shared_ptr buffer) - : Object(std::make_shared(v8direct::ValueStorage::Kind::V8)) { - auto holder = new v8direct::ArrayBufferHolder(std::move(buffer)); - auto backingStore = v8::ArrayBuffer::NewBackingStore( - holder->buffer->data(), holder->buffer->size(), - [](void*, size_t, void* deleterData) { - auto* holder = static_cast(deleterData); - holder->object.Reset(); - delete holder; - }, - holder); - v8::Local arrayBuffer = - v8::ArrayBuffer::New(runtime.isolate(), std::move(backingStore)); - storage_->value.Reset(runtime.isolate(), arrayBuffer); - holder->object.Reset(runtime.isolate(), arrayBuffer); - } - - explicit ArrayBuffer(Object object) : Object(std::move(object.storage_)) {} - - size_t size(Runtime& runtime) const { return local(runtime).As()->ByteLength(); } - - uint8_t* data(Runtime& runtime) const { - auto backingStore = local(runtime).As()->GetBackingStore(); - return static_cast(backingStore->Data()); - } - - operator Value() const { - Value value; - value.storage_ = storage_; - return value; - } -}; -} // namespace jsi -} // namespace facebook - -#endif // TARGET_ENGINE_V8 - -#endif // NATIVESCRIPT_FFI_V8_NATIVE_API_V8_RUNTIME_H diff --git a/NativeScript/ffi/v8/NativeApiV8Value.mm b/NativeScript/ffi/v8/NativeApiV8Value.mm deleted file mode 100644 index d8b34428d..000000000 --- a/NativeScript/ffi/v8/NativeApiV8Value.mm +++ /dev/null @@ -1,177 +0,0 @@ -#include "NativeApiV8Runtime.h" - -#ifdef TARGET_ENGINE_V8 - -namespace facebook { -namespace jsi { - -Value HostObject::get(Runtime&, const PropNameID&) { return Value::undefined(); } - -void HostObject::set(Runtime&, const PropNameID&, const Value&) {} - -std::vector HostObject::getPropertyNames(Runtime&) { return {}; } - -String::String(Runtime& runtime, v8::Local value) - : storage_(std::make_shared(v8direct::ValueStorage::Kind::V8)) { - storage_->value.Reset(runtime.isolate(), value); -} - -String::operator Value() const { - Value value; - value.storage_ = storage_; - return value; -} - -Value::Value(Runtime&, const Object& object) : storage_(object.storage_) {} -Value::Value(Runtime&, const Function& function) : storage_(function.storage_) {} -Value::Value(Runtime&, const Array& array) : storage_(array.storage_) {} -Value::Value(Runtime&, const ArrayBuffer& arrayBuffer) : storage_(arrayBuffer.storage_) {} -Value::Value(Runtime&, const BigInt& bigint) : storage_(bigint.storage_) {} - -bool Value::isObject() const { - if (storage_->kind != v8direct::ValueStorage::Kind::V8 || storage_->value.IsEmpty()) { - return false; - } - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - return isolate != nullptr && storage_->value.Get(isolate)->IsObject(); -} - -bool Value::isUndefined() const { - if (storage_->kind == v8direct::ValueStorage::Kind::Undefined) { - return true; - } - if (storage_->kind != v8direct::ValueStorage::Kind::V8 || storage_->value.IsEmpty()) { - return false; - } - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - return isolate != nullptr && storage_->value.Get(isolate)->IsUndefined(); -} - -bool Value::isNull() const { - if (storage_->kind == v8direct::ValueStorage::Kind::Null) { - return true; - } - if (storage_->kind != v8direct::ValueStorage::Kind::V8 || storage_->value.IsEmpty()) { - return false; - } - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - return isolate != nullptr && storage_->value.Get(isolate)->IsNull(); -} - -bool Value::isBool() const { - if (storage_->kind == v8direct::ValueStorage::Kind::Bool) { - return true; - } - if (storage_->kind != v8direct::ValueStorage::Kind::V8 || storage_->value.IsEmpty()) { - return false; - } - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - return isolate != nullptr && storage_->value.Get(isolate)->IsBoolean(); -} - -bool Value::getBool() const { - if (storage_->kind == v8direct::ValueStorage::Kind::Bool) { - return storage_->boolValue; - } - if (storage_->kind == v8direct::ValueStorage::Kind::V8 && !storage_->value.IsEmpty()) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - if (isolate != nullptr) { - return storage_->value.Get(isolate)->BooleanValue(isolate); - } - } - return false; -} - -bool Value::isNumber() const { - if (storage_->kind == v8direct::ValueStorage::Kind::Number) { - return true; - } - if (storage_->kind != v8direct::ValueStorage::Kind::V8 || storage_->value.IsEmpty()) { - return false; - } - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - return isolate != nullptr && storage_->value.Get(isolate)->IsNumber(); -} - -double Value::getNumber() const { - if (storage_->kind == v8direct::ValueStorage::Kind::Number) { - return storage_->numberValue; - } - if (storage_->kind == v8direct::ValueStorage::Kind::V8 && !storage_->value.IsEmpty()) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - if (isolate != nullptr) { - return storage_->value.Get(isolate)->NumberValue(isolate->GetCurrentContext()).FromMaybe(0); - } - } - return 0; -} - -bool Value::isString() const { - if (storage_->kind != v8direct::ValueStorage::Kind::V8 || storage_->value.IsEmpty()) { - return false; - } - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - return storage_->value.Get(isolate)->IsString(); -} - -bool Value::isBigInt() const { - if (storage_->kind != v8direct::ValueStorage::Kind::V8 || storage_->value.IsEmpty()) { - return false; - } - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - return storage_->value.Get(isolate)->IsBigInt(); -} - -bool Value::isSymbol() const { - if (storage_->kind != v8direct::ValueStorage::Kind::V8 || storage_->value.IsEmpty()) { - return false; - } - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - return storage_->value.Get(isolate)->IsSymbol(); -} - -Object Value::asObject(Runtime& runtime) const { return Object::fromValueStorage(storage_); } - -String Value::asString(Runtime& runtime) const { - return String(runtime, local(runtime).As()); -} - -BigInt Value::getBigInt(Runtime& runtime) const { - return BigInt(runtime, local(runtime).As()); -} - -Function Object::getPropertyAsFunction(Runtime& runtime, const char* name) const { - return getProperty(runtime, name).asObject(runtime).asFunction(runtime); -} - -Function Object::asFunction(Runtime& runtime) const { return Function(*this); } - -Array Object::getArray(Runtime& runtime) const { return Array(*this); } - -ArrayBuffer Object::getArrayBuffer(Runtime& runtime) const { return ArrayBuffer(*this); } - -Array Object::getPropertyNames(Runtime& runtime) const { - v8::TryCatch tryCatch(runtime.isolate()); - v8::Local result; - if (!local(runtime)->GetPropertyNames(runtime.context()).ToLocal(&result)) { - throw JSError(runtime, v8direct::currentExceptionMessage(runtime.isolate(), tryCatch)); - } - return Array(Object::fromValueStorage(Value(runtime, result).storage_)); -} - -void Object::setProperty(Runtime& runtime, const char* name, const Function& value) { - setProperty(runtime, name, Value(runtime, value)); -} - -void Object::setProperty(Runtime& runtime, const char* name, const Array& value) { - setProperty(runtime, name, Value(runtime, value)); -} - -void Object::setProperty(Runtime& runtime, const char* name, const ArrayBuffer& value) { - setProperty(runtime, name, Value(runtime, value)); -} - -} // namespace jsi -} // namespace facebook - -#endif // TARGET_ENGINE_V8 diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/APICallbackFunction.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/APICallbackFunction.h new file mode 100644 index 000000000..e5283b5b4 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/APICallbackFunction.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef APICallbackFunction_h +#define APICallbackFunction_h + +#include "APICast.h" +#include "Error.h" +#include "JSCallbackConstructor.h" +#include "JSLock.h" +#include + +namespace JSC { + +struct APICallbackFunction { + +template static EncodedJSValue JSC_HOST_CALL call(ExecState*); +template static EncodedJSValue JSC_HOST_CALL construct(ExecState*); + +}; + +template +EncodedJSValue JSC_HOST_CALL APICallbackFunction::call(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSContextRef execRef = toRef(exec); + JSObjectRef functionRef = toRef(exec->jsCallee()); + JSObjectRef thisObjRef = toRef(jsCast(exec->thisValue().toThis(exec, NotStrictMode))); + + int argumentCount = static_cast(exec->argumentCount()); + Vector arguments; + arguments.reserveInitialCapacity(argumentCount); + for (int i = 0; i < argumentCount; i++) + arguments.uncheckedAppend(toRef(exec, exec->uncheckedArgument(i))); + + JSValueRef exception = 0; + JSValueRef result; + { + JSLock::DropAllLocks dropAllLocks(exec); + result = jsCast(toJS(functionRef))->functionCallback()(execRef, functionRef, thisObjRef, argumentCount, arguments.data(), &exception); + } + if (exception) + throwException(exec, scope, toJS(exec, exception)); + + // result must be a valid JSValue. + if (!result) + return JSValue::encode(jsUndefined()); + + return JSValue::encode(toJS(exec, result)); +} + +template +EncodedJSValue JSC_HOST_CALL APICallbackFunction::construct(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSObject* constructor = exec->jsCallee(); + JSContextRef ctx = toRef(exec); + JSObjectRef constructorRef = toRef(constructor); + + JSObjectCallAsConstructorCallback callback = jsCast(constructor)->constructCallback(); + if (callback) { + size_t argumentCount = exec->argumentCount(); + Vector arguments; + arguments.reserveInitialCapacity(argumentCount); + for (size_t i = 0; i < argumentCount; ++i) + arguments.uncheckedAppend(toRef(exec, exec->uncheckedArgument(i))); + + JSValueRef exception = 0; + JSObjectRef result; + { + JSLock::DropAllLocks dropAllLocks(exec); + result = callback(ctx, constructorRef, argumentCount, arguments.data(), &exception); + } + if (exception) { + throwException(exec, scope, toJS(exec, exception)); + return JSValue::encode(toJS(exec, exception)); + } + // result must be a valid JSValue. + if (!result) + return throwVMTypeError(exec, scope); + return JSValue::encode(toJS(result)); + } + + return JSValue::encode(toJS(JSObjectMake(ctx, jsCast(constructor)->classRef(), 0))); +} + +} // namespace JSC + +#endif // APICallbackFunction_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/APICast.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/APICast.h new file mode 100644 index 000000000..b2f3888b6 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/APICast.h @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2006-2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef APICast_h +#define APICast_h + +#include "JSAPIValueWrapper.h" +#include "JSCJSValue.h" +#include "JSCJSValueInlines.h" +#include "JSGlobalObject.h" +#include "HeapCellInlines.h" + +namespace JSC { + class ExecState; + class PropertyNameArray; + class VM; + class JSObject; + class JSValue; +} + +typedef const struct OpaqueJSContextGroup* JSContextGroupRef; +typedef const struct OpaqueJSContext* JSContextRef; +typedef struct OpaqueJSContext* JSGlobalContextRef; +typedef struct OpaqueJSPropertyNameAccumulator* JSPropertyNameAccumulatorRef; +typedef const struct OpaqueJSValue* JSValueRef; +typedef struct OpaqueJSValue* JSObjectRef; + +/* Opaque typing convenience methods */ + +inline JSC::ExecState* toJS(JSContextRef c) +{ + ASSERT(c); + return reinterpret_cast(const_cast(c)); +} + +inline JSC::ExecState* toJS(JSGlobalContextRef c) +{ + ASSERT(c); + return reinterpret_cast(c); +} + +inline JSC::JSGlobalObject* toJSGlobalObject(JSGlobalContextRef context) +{ + return toJS(context)->lexicalGlobalObject(); +} + +inline JSC::JSValue toJS(JSC::ExecState* exec, JSValueRef v) +{ + ASSERT_UNUSED(exec, exec); +#if !CPU(ADDRESS64) + JSC::JSCell* jsCell = reinterpret_cast(const_cast(v)); + if (!jsCell) + return JSC::jsNull(); + JSC::JSValue result; + if (jsCell->isAPIValueWrapper()) + result = JSC::jsCast(jsCell)->value(); + else + result = jsCell; +#else + JSC::JSValue result = bitwise_cast(v); +#endif + if (!result) + return JSC::jsNull(); + if (result.isCell()) + RELEASE_ASSERT(result.asCell()->methodTable(exec->vm())); + return result; +} + +inline JSC::JSValue toJSForGC(JSC::ExecState* exec, JSValueRef v) +{ + ASSERT_UNUSED(exec, exec); +#if !CPU(ADDRESS64) + JSC::JSCell* jsCell = reinterpret_cast(const_cast(v)); + if (!jsCell) + return JSC::JSValue(); + JSC::JSValue result = jsCell; +#else + JSC::JSValue result = bitwise_cast(v); +#endif + if (result && result.isCell()) + RELEASE_ASSERT(result.asCell()->methodTable(exec->vm())); + return result; +} + +// Used in JSObjectGetPrivate as that may be called during finalization +inline JSC::JSObject* uncheckedToJS(JSObjectRef o) +{ + return reinterpret_cast(o); +} + +inline JSC::JSObject* toJS(JSObjectRef o) +{ + JSC::JSObject* object = uncheckedToJS(o); + if (object) + RELEASE_ASSERT(object->methodTable(object->vm())); + return object; +} + +inline JSC::PropertyNameArray* toJS(JSPropertyNameAccumulatorRef a) +{ + return reinterpret_cast(a); +} + +inline JSC::VM* toJS(JSContextGroupRef g) +{ + return reinterpret_cast(const_cast(g)); +} + +inline JSValueRef toRef(JSC::VM& vm, JSC::JSValue v) +{ + ASSERT(vm.currentThreadIsHoldingAPILock()); +#if !CPU(ADDRESS64) + if (!v) + return 0; + if (!v.isCell()) + return reinterpret_cast(JSC::JSAPIValueWrapper::create(vm, v)); + return reinterpret_cast(v.asCell()); +#else + UNUSED_PARAM(vm); + return bitwise_cast(v); +#endif +} + +inline JSValueRef toRef(JSC::ExecState* exec, JSC::JSValue v) +{ + return toRef(exec->vm(), v); +} + +inline JSObjectRef toRef(JSC::JSObject* o) +{ + return reinterpret_cast(o); +} + +inline JSObjectRef toRef(const JSC::JSObject* o) +{ + return reinterpret_cast(const_cast(o)); +} + +inline JSContextRef toRef(JSC::ExecState* e) +{ + return reinterpret_cast(e); +} + +inline JSGlobalContextRef toGlobalRef(JSC::ExecState* e) +{ + ASSERT(e == e->lexicalGlobalObject()->globalExec()); + return reinterpret_cast(e); +} + +inline JSPropertyNameAccumulatorRef toRef(JSC::PropertyNameArray* l) +{ + return reinterpret_cast(l); +} + +inline JSContextGroupRef toRef(JSC::VM* g) +{ + return reinterpret_cast(g); +} + +#endif // APICast_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/APIUtils.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/APIUtils.h new file mode 100644 index 000000000..7a5e8ba8b --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/APIUtils.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef APIUtils_h +#define APIUtils_h + +#include "CatchScope.h" +#include "Exception.h" +#include "JSCJSValue.h" +#include "JSGlobalObjectInspectorController.h" +#include "JSValueRef.h" + +enum class ExceptionStatus { + DidThrow, + DidNotThrow +}; + +inline ExceptionStatus handleExceptionIfNeeded(JSC::CatchScope& scope, JSC::ExecState* exec, JSValueRef* returnedExceptionRef) +{ + if (UNLIKELY(scope.exception())) { + JSC::Exception* exception = scope.exception(); + if (returnedExceptionRef) + *returnedExceptionRef = toRef(exec, exception->value()); + scope.clearException(); +#if ENABLE(REMOTE_INSPECTOR) + scope.vm().vmEntryGlobalObject(exec)->inspectorController().reportAPIException(exec, exception); +#endif + return ExceptionStatus::DidThrow; + } + return ExceptionStatus::DidNotThrow; +} + +inline void setException(JSC::ExecState* exec, JSValueRef* returnedExceptionRef, JSC::JSValue exception) +{ + if (returnedExceptionRef) + *returnedExceptionRef = toRef(exec, exception); +#if ENABLE(REMOTE_INSPECTOR) + JSC::VM& vm = exec->vm(); + vm.vmEntryGlobalObject(exec)->inspectorController().reportAPIException(exec, JSC::Exception::create(vm, exception)); +#endif +} + +#endif /* APIUtils_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSAPIGlobalObject.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSAPIGlobalObject.h new file mode 100644 index 000000000..339e5e27f --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSAPIGlobalObject.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "JSGlobalObject.h" + +OBJC_CLASS JSScript; + +namespace JSC { + +class JSAPIGlobalObject : public JSGlobalObject { +public: + using Base = JSGlobalObject; + + DECLARE_EXPORT_INFO; + static const GlobalObjectMethodTable s_globalObjectMethodTable; + + static JSAPIGlobalObject* create(VM& vm, Structure* structure) + { + auto* object = new (NotNull, allocateCell(vm.heap)) JSAPIGlobalObject(vm, structure); + object->finishCreation(vm); + return object; + } + + static Structure* createStructure(VM& vm, JSValue prototype) + { + auto* result = Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), info()); + result->setTransitionWatchpointIsLikelyToBeFired(true); + return result; + } + + static JSInternalPromise* moduleLoaderImportModule(JSGlobalObject*, ExecState*, JSModuleLoader*, JSString* moduleNameValue, JSValue parameters, const SourceOrigin&); + static Identifier moduleLoaderResolve(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue keyValue, JSValue referrerValue, JSValue); + static JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue); + static JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSModuleRecord*, JSValue); + static JSValue moduleLoaderEvaluate(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue); + + JSValue loadAndEvaluateJSScriptModule(const JSLockHolder&, JSScript *); + +private: + JSAPIGlobalObject(VM& vm, Structure* structure) + : Base(vm, structure, &s_globalObjectMethodTable) + { } +}; + +} diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSAPIValueWrapper.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSAPIValueWrapper.h new file mode 100644 index 000000000..aa26082ff --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSAPIValueWrapper.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003-2019 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#pragma once + +#include "CallFrame.h" +#include "JSCJSValue.h" +#include "JSCast.h" +#include "Structure.h" + +namespace JSC { + +class JSAPIValueWrapper final : public JSCell { + friend JSValue jsAPIValueWrapper(ExecState*, JSValue); +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + JSValue value() const { return m_value.get(); } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(APIValueWrapperType, OverridesGetPropertyNames), info()); + } + + DECLARE_EXPORT_INFO; + + static JSAPIValueWrapper* create(VM& vm, JSValue value) + { + JSAPIValueWrapper* wrapper = new (NotNull, allocateCell(vm.heap)) JSAPIValueWrapper(vm); + wrapper->finishCreation(vm, value); + return wrapper; + } + +protected: + void finishCreation(VM& vm, JSValue value) + { + Base::finishCreation(vm); + m_value.set(vm, this, value); + ASSERT(!value.isCell()); + } + +private: + JSAPIValueWrapper(VM& vm) + : JSCell(vm, vm.apiWrapperStructure.get()) + { + } + + WriteBarrier m_value; +}; + +} // namespace JSC diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSAPIWrapperObject.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSAPIWrapperObject.h new file mode 100644 index 000000000..dd874dc65 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSAPIWrapperObject.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013-2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSAPIWrapperObject_h +#define JSAPIWrapperObject_h + +#include "JSBase.h" +#include "JSDestructibleObject.h" + +#if JSC_OBJC_API_ENABLED || defined(JSC_GLIB_API_ENABLED) + +namespace JSC { + +class JSAPIWrapperObject : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + void finishCreation(VM&); + static void visitChildren(JSCell*, JSC::SlotVisitor&); + + void* wrappedObject() { return m_wrappedObject; } + void setWrappedObject(void*); + +protected: + JSAPIWrapperObject(VM&, Structure*); + +private: + void* m_wrappedObject { nullptr }; +}; + +} // namespace JSC + +#endif // JSC_OBJC_API_ENABLED || defined(JSC_GLIB_API_ENABLED) + +#endif // JSAPIWrapperObject_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSBase.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSBase.h new file mode 100644 index 000000000..01c1b286e --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSBase.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2006 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSBase_h +#define JSBase_h + +#ifndef __cplusplus +#include +#endif + +#ifdef __OBJC__ +#import +#endif + +/* JavaScript engine interface */ + +/*! @typedef JSContextGroupRef A group that associates JavaScript contexts with one another. Contexts in the same group may share and exchange JavaScript objects. */ +typedef const struct OpaqueJSContextGroup* JSContextGroupRef; + +/*! @typedef JSContextRef A JavaScript execution context. Holds the global object and other execution state. */ +typedef const struct OpaqueJSContext* JSContextRef; + +/*! @typedef JSGlobalContextRef A global JavaScript execution context. A JSGlobalContext is a JSContext. */ +typedef struct OpaqueJSContext* JSGlobalContextRef; + +/*! @typedef JSStringRef A UTF16 character buffer. The fundamental string representation in JavaScript. */ +typedef struct OpaqueJSString* JSStringRef; + +/*! @typedef JSClassRef A JavaScript class. Used with JSObjectMake to construct objects with custom behavior. */ +typedef struct OpaqueJSClass* JSClassRef; + +/*! @typedef JSPropertyNameArrayRef An array of JavaScript property names. */ +typedef struct OpaqueJSPropertyNameArray* JSPropertyNameArrayRef; + +/*! @typedef JSPropertyNameAccumulatorRef An ordered set used to collect the names of a JavaScript object's properties. */ +typedef struct OpaqueJSPropertyNameAccumulator* JSPropertyNameAccumulatorRef; + +/*! @typedef JSTypedArrayBytesDeallocator A function used to deallocate bytes passed to a Typed Array constructor. The function should take two arguments. The first is a pointer to the bytes that were originally passed to the Typed Array constructor. The second is a pointer to additional information desired at the time the bytes are to be freed. */ +typedef void (*JSTypedArrayBytesDeallocator)(void* bytes, void* deallocatorContext); + +/* JavaScript data types */ + +/*! @typedef JSValueRef A JavaScript value. The base type for all JavaScript values, and polymorphic functions on them. */ +typedef const struct OpaqueJSValue* JSValueRef; + +/*! @typedef JSObjectRef A JavaScript object. A JSObject is a JSValue. */ +typedef struct OpaqueJSValue* JSObjectRef; + +/* Clang's __has_declspec_attribute emulation */ +/* https://clang.llvm.org/docs/LanguageExtensions.html#has-declspec-attribute */ + +#ifndef __has_declspec_attribute +#define __has_declspec_attribute(x) 0 +#endif + +/* JavaScript symbol exports */ +/* These rules should stay the same as in WebKit/Shared/API/c/WKDeclarationSpecifiers.h */ + +#undef JS_EXPORT +#if defined(JS_NO_EXPORT) +#define JS_EXPORT +#elif defined(WIN32) || defined(_WIN32) || defined(__CC_ARM) || defined(__ARMCC__) || (__has_declspec_attribute(dllimport) && __has_declspec_attribute(dllexport)) +#if defined(BUILDING_JavaScriptCore) || defined(STATICALLY_LINKED_WITH_JavaScriptCore) +#define JS_EXPORT __declspec(dllexport) +#else +#define JS_EXPORT __declspec(dllimport) +#endif +#elif defined(__GNUC__) +#define JS_EXPORT __attribute__((visibility("default"))) +#else /* !defined(JS_NO_EXPORT) */ +#define JS_EXPORT +#endif /* defined(JS_NO_EXPORT) */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Script Evaluation */ + +/*! +@function JSEvaluateScript +@abstract Evaluates a string of JavaScript. +@param ctx The execution context to use. +@param script A JSString containing the script to evaluate. +@param thisObject The object to use as "this," or NULL to use the global object as "this." +@param sourceURL A JSString containing a URL for the script's source file. This is used by debuggers and when reporting exceptions. Pass NULL if you do not care to include source file information. +@param startingLineNumber An integer value specifying the script's starting line number in the file located at sourceURL. This is only used when reporting exceptions. The value is one-based, so the first line is line 1 and invalid values are clamped to 1. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +@result The JSValue that results from evaluating script, or NULL if an exception is thrown. +*/ +JS_EXPORT JSValueRef JSEvaluateScript(JSContextRef ctx, JSStringRef script, JSObjectRef thisObject, JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception); + +/*! +@function JSCheckScriptSyntax +@abstract Checks for syntax errors in a string of JavaScript. +@param ctx The execution context to use. +@param script A JSString containing the script to check for syntax errors. +@param sourceURL A JSString containing a URL for the script's source file. This is only used when reporting exceptions. Pass NULL if you do not care to include source file information in exceptions. +@param startingLineNumber An integer value specifying the script's starting line number in the file located at sourceURL. This is only used when reporting exceptions. The value is one-based, so the first line is line 1 and invalid values are clamped to 1. +@param exception A pointer to a JSValueRef in which to store a syntax error exception, if any. Pass NULL if you do not care to store a syntax error exception. +@result true if the script is syntactically correct, otherwise false. +*/ +JS_EXPORT bool JSCheckScriptSyntax(JSContextRef ctx, JSStringRef script, JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception); + +/*! +@function JSGarbageCollect +@abstract Performs a JavaScript garbage collection. +@param ctx The execution context to use. +@discussion JavaScript values that are on the machine stack, in a register, + protected by JSValueProtect, set as the global object of an execution context, + or reachable from any such value will not be collected. + + During JavaScript execution, you are not required to call this function; the + JavaScript engine will garbage collect as needed. JavaScript values created + within a context group are automatically destroyed when the last reference + to the context group is released. +*/ +JS_EXPORT void JSGarbageCollect(JSContextRef ctx); + +#ifdef __cplusplus +} +#endif + +/* Enable the Objective-C API for platforms with a modern runtime. NOTE: This is duplicated in VM.h. */ +#if !defined(JSC_OBJC_API_ENABLED) +#if (defined(__clang__) && defined(__APPLE__) && ((defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && !defined(__i386__)) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE))) +#define JSC_OBJC_API_ENABLED 1 +#else +#define JSC_OBJC_API_ENABLED 0 +#endif +#endif + +#endif /* JSBase_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSBaseInternal.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSBaseInternal.h new file mode 100644 index 000000000..a274af96e --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSBaseInternal.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include + +namespace JSC { +class JSLockHolder; +class ExecState; +class SourceCode; +} + +extern "C" JSValueRef JSEvaluateScriptInternal(const JSC::JSLockHolder&, JSC::ExecState*, JSContextRef, JSObjectRef thisObject, const JSC::SourceCode&, JSValueRef* exception); diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSBasePrivate.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSBasePrivate.h new file mode 100644 index 000000000..2fc916b70 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSBasePrivate.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSBasePrivate_h +#define JSBasePrivate_h + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*! +@function +@abstract Reports an object's non-GC memory payload to the garbage collector. +@param ctx The execution context to use. +@param size The payload's size, in bytes. +@discussion Use this function to notify the garbage collector that a GC object +owns a large non-GC memory region. Calling this function will encourage the +garbage collector to collect soon, hoping to reclaim that large non-GC memory +region. +*/ +JS_EXPORT void JSReportExtraMemoryCost(JSContextRef ctx, size_t size) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + +JS_EXPORT void JSDisableGCTimer(void); + +#ifdef __cplusplus +} +#endif + +#endif /* JSBasePrivate_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCTestRunnerUtils.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCTestRunnerUtils.h new file mode 100644 index 000000000..c52da524b --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCTestRunnerUtils.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSCTestRunnerUtils_h +#define JSCTestRunnerUtils_h + +#include +#include + +namespace JSC { + +JS_EXPORT_PRIVATE JSValueRef failNextNewCodeBlock(JSContextRef); +JS_EXPORT_PRIVATE JSValueRef numberOfDFGCompiles(JSContextRef, JSValueRef theFunction); +JS_EXPORT_PRIVATE JSValueRef setNeverInline(JSContextRef, JSValueRef theFunction); +JS_EXPORT_PRIVATE JSValueRef setNeverOptimize(JSContextRef, JSValueRef theFunction); + +} // namespace JSC + +#endif // JSCTestRunnerUtils_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCallbackConstructor.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCallbackConstructor.h new file mode 100644 index 000000000..3c31e07f0 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCallbackConstructor.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSCallbackConstructor_h +#define JSCallbackConstructor_h + +#include "JSDestructibleObject.h" +#include "JSObjectRef.h" + +namespace JSC { + +class JSCallbackConstructor final : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + static const unsigned StructureFlags = Base::StructureFlags | ImplementsHasInstance | ImplementsDefaultHasInstance; + + static JSCallbackConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, JSClassRef classRef, JSObjectCallAsConstructorCallback callback) + { + VM& vm = exec->vm(); + JSCallbackConstructor* constructor = new (NotNull, allocateCell(vm.heap)) JSCallbackConstructor(globalObject, structure, classRef, callback); + constructor->finishCreation(globalObject, classRef); + return constructor; + } + + ~JSCallbackConstructor(); + static void destroy(JSCell*); + JSClassRef classRef() const { return m_class; } + JSObjectCallAsConstructorCallback callback() const { return m_callback; } + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(vm, globalObject, proto, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + JSCallbackConstructor(JSGlobalObject*, Structure*, JSClassRef, JSObjectCallAsConstructorCallback); + void finishCreation(JSGlobalObject*, JSClassRef); + +private: + friend struct APICallbackFunction; + + static ConstructType getConstructData(JSCell*, ConstructData&); + + JSObjectCallAsConstructorCallback constructCallback() { return m_callback; } + + JSClassRef m_class; + JSObjectCallAsConstructorCallback m_callback; +}; + +} // namespace JSC + +#endif // JSCallbackConstructor_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCallbackFunction.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCallbackFunction.h new file mode 100644 index 000000000..f9e4f9635 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCallbackFunction.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2006-2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSCallbackFunction_h +#define JSCallbackFunction_h + +#include "InternalFunction.h" +#include "JSObjectRef.h" + +namespace JSC { + +class JSCallbackFunction final : public InternalFunction { + friend struct APICallbackFunction; +public: + typedef InternalFunction Base; + + template + static IsoSubspace* subspaceFor(VM& vm) + { + return vm.callbackFunctionSpace(); + } + + static JSCallbackFunction* create(VM&, JSGlobalObject*, JSObjectCallAsFunctionCallback, const String& name); + + DECLARE_INFO; + + // InternalFunction mish-mashes constructor and function behavior -- we should + // refactor the code so this override isn't necessary + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(vm, globalObject, proto, TypeInfo(InternalFunctionType, StructureFlags), info()); + } + +private: + JSCallbackFunction(VM&, Structure*, JSObjectCallAsFunctionCallback); + void finishCreation(VM&, const String& name); + + JSObjectCallAsFunctionCallback functionCallback() { return m_callback; } + + JSObjectCallAsFunctionCallback m_callback { nullptr }; +}; + +} // namespace JSC + +#endif // JSCallbackFunction_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCallbackObject.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCallbackObject.h new file mode 100644 index 000000000..d0e625282 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCallbackObject.h @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2006-2019 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSCallbackObject_h +#define JSCallbackObject_h + +#include "JSObjectRef.h" +#include "JSValueRef.h" +#include "JSObject.h" + +namespace JSC { + +struct JSCallbackObjectData { + WTF_MAKE_FAST_ALLOCATED; +public: + JSCallbackObjectData(void* privateData, JSClassRef jsClass) + : privateData(privateData) + , jsClass(jsClass) + { + JSClassRetain(jsClass); + } + + ~JSCallbackObjectData() + { + JSClassRelease(jsClass); + } + + JSValue getPrivateProperty(const Identifier& propertyName) const + { + if (!m_privateProperties) + return JSValue(); + return m_privateProperties->getPrivateProperty(propertyName); + } + + void setPrivateProperty(VM& vm, JSCell* owner, const Identifier& propertyName, JSValue value) + { + if (!m_privateProperties) + m_privateProperties = makeUnique(); + m_privateProperties->setPrivateProperty(vm, owner, propertyName, value); + } + + void deletePrivateProperty(const Identifier& propertyName) + { + if (!m_privateProperties) + return; + m_privateProperties->deletePrivateProperty(propertyName); + } + + void visitChildren(SlotVisitor& visitor) + { + JSPrivatePropertyMap* properties = m_privateProperties.get(); + if (!properties) + return; + properties->visitChildren(visitor); + } + + void* privateData; + JSClassRef jsClass; + struct JSPrivatePropertyMap { + WTF_MAKE_FAST_ALLOCATED; + public: + JSValue getPrivateProperty(const Identifier& propertyName) const + { + PrivatePropertyMap::const_iterator location = m_propertyMap.find(propertyName.impl()); + if (location == m_propertyMap.end()) + return JSValue(); + return location->value.get(); + } + + void setPrivateProperty(VM& vm, JSCell* owner, const Identifier& propertyName, JSValue value) + { + LockHolder locker(m_lock); + WriteBarrier empty; + m_propertyMap.add(propertyName.impl(), empty).iterator->value.set(vm, owner, value); + } + + void deletePrivateProperty(const Identifier& propertyName) + { + LockHolder locker(m_lock); + m_propertyMap.remove(propertyName.impl()); + } + + void visitChildren(SlotVisitor& visitor) + { + LockHolder locker(m_lock); + for (auto& pair : m_propertyMap) { + if (pair.value) + visitor.append(pair.value); + } + } + + private: + typedef HashMap, WriteBarrier, IdentifierRepHash> PrivatePropertyMap; + PrivatePropertyMap m_propertyMap; + Lock m_lock; + }; + std::unique_ptr m_privateProperties; +}; + + +template +class JSCallbackObject final : public Parent { +protected: + JSCallbackObject(ExecState*, Structure*, JSClassRef, void* data); + JSCallbackObject(VM&, JSClassRef, Structure*); + + void finishCreation(ExecState*); + void finishCreation(VM&); + +public: + typedef Parent Base; + static const unsigned StructureFlags = Base::StructureFlags | ProhibitsPropertyCaching | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | ImplementsHasInstance | OverridesGetPropertyNames | OverridesGetCallData; + static_assert(!(StructureFlags & ImplementsDefaultHasInstance), "using customHasInstance"); + + ~JSCallbackObject(); + + static JSCallbackObject* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, JSClassRef classRef, void* data) + { + VM& vm = exec->vm(); + ASSERT_UNUSED(globalObject, !structure->globalObject() || structure->globalObject() == globalObject); + JSCallbackObject* callbackObject = new (NotNull, allocateCell(vm.heap)) JSCallbackObject(exec, structure, classRef, data); + callbackObject->finishCreation(exec); + return callbackObject; + } + static JSCallbackObject* create(VM&, JSClassRef, Structure*); + + static const bool needsDestruction; + static void destroy(JSCell* cell) + { + static_cast(cell)->JSCallbackObject::~JSCallbackObject(); + } + + void setPrivate(void* data); + void* getPrivate(); + + // FIXME: We should fix the warnings for extern-template in JSObject template classes: https://bugs.webkit.org/show_bug.cgi?id=161979 + IGNORE_CLANG_WARNINGS_BEGIN("undefined-var-template") + DECLARE_INFO; + IGNORE_CLANG_WARNINGS_END + + JSClassRef classRef() const { return m_callbackObjectData->jsClass; } + bool inherits(JSClassRef) const; + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + JSValue getPrivateProperty(const Identifier& propertyName) const + { + return m_callbackObjectData->getPrivateProperty(propertyName); + } + + void setPrivateProperty(VM& vm, const Identifier& propertyName, JSValue value) + { + m_callbackObjectData->setPrivateProperty(vm, this, propertyName, value); + } + + void deletePrivateProperty(const Identifier& propertyName) + { + m_callbackObjectData->deletePrivateProperty(propertyName); + } + + using Parent::methodTable; + +private: + static String className(const JSObject*, VM&); + static String toStringName(const JSObject*, ExecState*); + + static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); + + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); + + static bool put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + static bool putByIndex(JSCell*, ExecState*, unsigned, JSValue, bool shouldThrow); + + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned); + + static bool customHasInstance(JSObject*, ExecState*, JSValue); + + static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + + static void visitChildren(JSCell* cell, SlotVisitor& visitor) + { + JSCallbackObject* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS((static_cast(thisObject)), JSCallbackObject::info()); + Parent::visitChildren(thisObject, visitor); + thisObject->m_callbackObjectData->visitChildren(visitor); + } + + void init(ExecState*); + + static JSCallbackObject* asCallbackObject(JSValue); + static JSCallbackObject* asCallbackObject(EncodedJSValue); + + static EncodedJSValue JSC_HOST_CALL call(ExecState*); + static EncodedJSValue JSC_HOST_CALL construct(ExecState*); + + JSValue getStaticValue(ExecState*, PropertyName); + static EncodedJSValue staticFunctionGetter(ExecState*, EncodedJSValue, PropertyName); + static EncodedJSValue callbackGetter(ExecState*, EncodedJSValue, PropertyName); + + std::unique_ptr m_callbackObjectData; + const ClassInfo* m_classInfo { nullptr }; +}; + +} // namespace JSC + +// include the actual template class implementation +#include "JSCallbackObjectFunctions.h" + +#endif // JSCallbackObject_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCallbackObjectFunctions.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCallbackObjectFunctions.h new file mode 100644 index 000000000..c72363395 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSCallbackObjectFunctions.h @@ -0,0 +1,714 @@ +/* + * Copyright (C) 2006-2019 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "APICast.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSCallbackFunction.h" +#include "JSClassRef.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSLock.h" +#include "JSObjectRef.h" +#include "JSString.h" +#include "OpaqueJSString.h" +#include "PropertyNameArray.h" +#include + +namespace JSC { + +template +inline JSCallbackObject* JSCallbackObject::asCallbackObject(JSValue value) +{ + ASSERT(asObject(value)->inherits(value.getObject()->vm(), info())); + return jsCast(asObject(value)); +} + +template +inline JSCallbackObject* JSCallbackObject::asCallbackObject(EncodedJSValue encodedValue) +{ + JSValue value = JSValue::decode(encodedValue); + ASSERT(asObject(value)->inherits(value.getObject()->vm(), info())); + return jsCast(asObject(value)); +} + +template +JSCallbackObject::JSCallbackObject(ExecState* exec, Structure* structure, JSClassRef jsClass, void* data) + : Parent(exec->vm(), structure) + , m_callbackObjectData(makeUnique(data, jsClass)) +{ +} + +// Global object constructor. +// FIXME: Move this into a separate JSGlobalCallbackObject class derived from this one. +template +JSCallbackObject::JSCallbackObject(VM& vm, JSClassRef jsClass, Structure* structure) + : Parent(vm, structure) + , m_callbackObjectData(makeUnique(nullptr, jsClass)) +{ +} + +template +JSCallbackObject::~JSCallbackObject() +{ + VM& vm = this->HeapCell::vm(); + vm.currentlyDestructingCallbackObject = this; + ASSERT(m_classInfo); + vm.currentlyDestructingCallbackObjectClassInfo = m_classInfo; + JSObjectRef thisRef = toRef(static_cast(this)); + for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { + if (JSObjectFinalizeCallback finalize = jsClass->finalize) + finalize(thisRef); + } + vm.currentlyDestructingCallbackObject = nullptr; + vm.currentlyDestructingCallbackObjectClassInfo = nullptr; +} + +template +void JSCallbackObject::finishCreation(ExecState* exec) +{ + VM& vm = exec->vm(); + Base::finishCreation(vm); + ASSERT(Parent::inherits(vm, info())); + init(exec); +} + +// This is just for Global object, so we can assume that Base::finishCreation is JSGlobalObject::finishCreation. +template +void JSCallbackObject::finishCreation(VM& vm) +{ + ASSERT(Parent::inherits(vm, info())); + ASSERT(Parent::isGlobalObject()); + Base::finishCreation(vm); + init(jsCast(this)->globalExec()); +} + +template +void JSCallbackObject::init(ExecState* exec) +{ + ASSERT(exec); + + Vector initRoutines; + JSClassRef jsClass = classRef(); + do { + if (JSObjectInitializeCallback initialize = jsClass->initialize) + initRoutines.append(initialize); + } while ((jsClass = jsClass->parentClass)); + + // initialize from base to derived + for (int i = static_cast(initRoutines.size()) - 1; i >= 0; i--) { + JSLock::DropAllLocks dropAllLocks(exec); + JSObjectInitializeCallback initialize = initRoutines[i]; + initialize(toRef(exec), toRef(this)); + } + + m_classInfo = this->classInfo(); +} + +template +String JSCallbackObject::className(const JSObject* object, VM& vm) +{ + const JSCallbackObject* thisObject = jsCast(object); + String thisClassName = thisObject->classRef()->className(); + if (!thisClassName.isEmpty()) + return thisClassName; + + return Parent::className(object, vm); +} + +template +String JSCallbackObject::toStringName(const JSObject* object, ExecState* exec) +{ + VM& vm = exec->vm(); + const ClassInfo* info = object->classInfo(vm); + ASSERT(info); + return info->methodTable.className(object, vm); +} + +template +bool JSCallbackObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSCallbackObject* thisObject = jsCast(object); + JSContextRef ctx = toRef(exec); + JSObjectRef thisRef = toRef(thisObject); + RefPtr propertyNameRef; + + if (StringImpl* name = propertyName.uid()) { + for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { + // optional optimization to bypass getProperty in cases when we only need to know if the property exists + if (JSObjectHasPropertyCallback hasProperty = jsClass->hasProperty) { + if (!propertyNameRef) + propertyNameRef = OpaqueJSString::tryCreate(name); + JSLock::DropAllLocks dropAllLocks(exec); + if (hasProperty(ctx, thisRef, propertyNameRef.get())) { + slot.setCustom(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, callbackGetter); + return true; + } + } else if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) { + if (!propertyNameRef) + propertyNameRef = OpaqueJSString::tryCreate(name); + JSValueRef exception = 0; + JSValueRef value; + { + JSLock::DropAllLocks dropAllLocks(exec); + value = getProperty(ctx, thisRef, propertyNameRef.get(), &exception); + } + if (exception) { + throwException(exec, scope, toJS(exec, exception)); + slot.setValue(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, jsUndefined()); + return true; + } + if (value) { + slot.setValue(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, toJS(exec, value)); + return true; + } + } + + if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { + if (staticValues->contains(name)) { + JSValue value = thisObject->getStaticValue(exec, propertyName); + if (value) { + slot.setValue(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, value); + return true; + } + } + } + + if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { + if (staticFunctions->contains(name)) { + slot.setCustom(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, staticFunctionGetter); + return true; + } + } + } + } + + return Parent::getOwnPropertySlot(thisObject, exec, propertyName, slot); +} + +template +bool JSCallbackObject::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + VM& vm = exec->vm(); + return object->methodTable(vm)->getOwnPropertySlot(object, exec, Identifier::from(vm, propertyName), slot); +} + +template +JSValue JSCallbackObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + const JSCallbackObject* thisObject = jsCast(object); + JSContextRef ctx = toRef(exec); + JSObjectRef thisRef = toRef(thisObject); + ::JSType jsHint = hint == PreferString ? kJSTypeString : kJSTypeNumber; + + for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { + if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) { + JSValueRef exception = 0; + JSValueRef result = convertToType(ctx, thisRef, jsHint, &exception); + if (exception) { + throwException(exec, scope, toJS(exec, exception)); + return jsUndefined(); + } + if (result) + return toJS(exec, result); + } + } + + return Parent::defaultValue(object, exec, hint); +} + +template +bool JSCallbackObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSCallbackObject* thisObject = jsCast(cell); + JSContextRef ctx = toRef(exec); + JSObjectRef thisRef = toRef(thisObject); + RefPtr propertyNameRef; + JSValueRef valueRef = toRef(exec, value); + + if (StringImpl* name = propertyName.uid()) { + for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { + if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) { + if (!propertyNameRef) + propertyNameRef = OpaqueJSString::tryCreate(name); + JSValueRef exception = 0; + bool result; + { + JSLock::DropAllLocks dropAllLocks(exec); + result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception); + } + if (exception) + throwException(exec, scope, toJS(exec, exception)); + if (result || exception) + return result; + } + + if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { + if (StaticValueEntry* entry = staticValues->get(name)) { + if (entry->attributes & kJSPropertyAttributeReadOnly) + return false; + if (JSObjectSetPropertyCallback setProperty = entry->setProperty) { + JSValueRef exception = 0; + bool result; + { + JSLock::DropAllLocks dropAllLocks(exec); + result = setProperty(ctx, thisRef, entry->propertyNameRef.get(), valueRef, &exception); + } + if (exception) + throwException(exec, scope, toJS(exec, exception)); + if (result || exception) + return result; + } + } + } + + if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { + if (StaticFunctionEntry* entry = staticFunctions->get(name)) { + PropertySlot getSlot(thisObject, PropertySlot::InternalMethodType::VMInquiry); + if (Parent::getOwnPropertySlot(thisObject, exec, propertyName, getSlot)) + return Parent::put(thisObject, exec, propertyName, value, slot); + if (entry->attributes & kJSPropertyAttributeReadOnly) + return false; + return thisObject->JSCallbackObject::putDirect(vm, propertyName, value); // put as override property + } + } + } + } + + return Parent::put(thisObject, exec, propertyName, value, slot); +} + +template +bool JSCallbackObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyIndex, JSValue value, bool shouldThrow) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSCallbackObject* thisObject = jsCast(cell); + JSContextRef ctx = toRef(exec); + JSObjectRef thisRef = toRef(thisObject); + RefPtr propertyNameRef; + JSValueRef valueRef = toRef(exec, value); + Identifier propertyName = Identifier::from(vm, propertyIndex); + + for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { + if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) { + if (!propertyNameRef) + propertyNameRef = OpaqueJSString::tryCreate(propertyName.impl()); + JSValueRef exception = 0; + bool result; + { + JSLock::DropAllLocks dropAllLocks(exec); + result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception); + } + if (exception) + throwException(exec, scope, toJS(exec, exception)); + if (result || exception) + return result; + } + + if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { + if (StaticValueEntry* entry = staticValues->get(propertyName.impl())) { + if (entry->attributes & kJSPropertyAttributeReadOnly) + return false; + if (JSObjectSetPropertyCallback setProperty = entry->setProperty) { + JSValueRef exception = 0; + bool result; + { + JSLock::DropAllLocks dropAllLocks(exec); + result = setProperty(ctx, thisRef, entry->propertyNameRef.get(), valueRef, &exception); + } + if (exception) + throwException(exec, scope, toJS(exec, exception)); + if (result || exception) + return result; + } + } + } + + if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { + if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.impl())) { + if (entry->attributes & kJSPropertyAttributeReadOnly) + return false; + break; + } + } + } + + return Parent::putByIndex(thisObject, exec, propertyIndex, value, shouldThrow); +} + +template +bool JSCallbackObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSCallbackObject* thisObject = jsCast(cell); + JSContextRef ctx = toRef(exec); + JSObjectRef thisRef = toRef(thisObject); + RefPtr propertyNameRef; + + if (StringImpl* name = propertyName.uid()) { + for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { + if (JSObjectDeletePropertyCallback deleteProperty = jsClass->deleteProperty) { + if (!propertyNameRef) + propertyNameRef = OpaqueJSString::tryCreate(name); + JSValueRef exception = 0; + bool result; + { + JSLock::DropAllLocks dropAllLocks(exec); + result = deleteProperty(ctx, thisRef, propertyNameRef.get(), &exception); + } + if (exception) + throwException(exec, scope, toJS(exec, exception)); + if (result || exception) + return true; + } + + if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { + if (StaticValueEntry* entry = staticValues->get(name)) { + if (entry->attributes & kJSPropertyAttributeDontDelete) + return false; + return true; + } + } + + if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { + if (StaticFunctionEntry* entry = staticFunctions->get(name)) { + if (entry->attributes & kJSPropertyAttributeDontDelete) + return false; + return true; + } + } + } + } + + return Parent::deleteProperty(thisObject, exec, propertyName); +} + +template +bool JSCallbackObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName) +{ + VM& vm = exec->vm(); + JSCallbackObject* thisObject = jsCast(cell); + return thisObject->methodTable(vm)->deleteProperty(thisObject, exec, Identifier::from(vm, propertyName)); +} + +template +ConstructType JSCallbackObject::getConstructData(JSCell* cell, ConstructData& constructData) +{ + JSCallbackObject* thisObject = jsCast(cell); + for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { + if (jsClass->callAsConstructor) { + constructData.native.function = construct; + return ConstructType::Host; + } + } + return ConstructType::None; +} + +template +EncodedJSValue JSCallbackObject::construct(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSObject* constructor = exec->jsCallee(); + JSContextRef execRef = toRef(exec); + JSObjectRef constructorRef = toRef(constructor); + + for (JSClassRef jsClass = jsCast*>(constructor)->classRef(); jsClass; jsClass = jsClass->parentClass) { + if (JSObjectCallAsConstructorCallback callAsConstructor = jsClass->callAsConstructor) { + size_t argumentCount = exec->argumentCount(); + Vector arguments; + arguments.reserveInitialCapacity(argumentCount); + for (size_t i = 0; i < argumentCount; ++i) + arguments.uncheckedAppend(toRef(exec, exec->uncheckedArgument(i))); + JSValueRef exception = 0; + JSObject* result; + { + JSLock::DropAllLocks dropAllLocks(exec); + result = toJS(callAsConstructor(execRef, constructorRef, argumentCount, arguments.data(), &exception)); + } + if (exception) + throwException(exec, scope, toJS(exec, exception)); + return JSValue::encode(result); + } + } + + RELEASE_ASSERT_NOT_REACHED(); // getConstructData should prevent us from reaching here + return JSValue::encode(JSValue()); +} + +template +bool JSCallbackObject::customHasInstance(JSObject* object, ExecState* exec, JSValue value) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSCallbackObject* thisObject = jsCast(object); + JSContextRef execRef = toRef(exec); + JSObjectRef thisRef = toRef(thisObject); + + for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { + if (JSObjectHasInstanceCallback hasInstance = jsClass->hasInstance) { + JSValueRef valueRef = toRef(exec, value); + JSValueRef exception = 0; + bool result; + { + JSLock::DropAllLocks dropAllLocks(exec); + result = hasInstance(execRef, thisRef, valueRef, &exception); + } + if (exception) + throwException(exec, scope, toJS(exec, exception)); + return result; + } + } + return false; +} + +template +CallType JSCallbackObject::getCallData(JSCell* cell, CallData& callData) +{ + JSCallbackObject* thisObject = jsCast(cell); + for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { + if (jsClass->callAsFunction) { + callData.native.function = call; + return CallType::Host; + } + } + return CallType::None; +} + +template +EncodedJSValue JSCallbackObject::call(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSContextRef execRef = toRef(exec); + JSObjectRef functionRef = toRef(exec->jsCallee()); + JSObjectRef thisObjRef = toRef(jsCast(exec->thisValue().toThis(exec, NotStrictMode))); + + for (JSClassRef jsClass = jsCast*>(toJS(functionRef))->classRef(); jsClass; jsClass = jsClass->parentClass) { + if (JSObjectCallAsFunctionCallback callAsFunction = jsClass->callAsFunction) { + size_t argumentCount = exec->argumentCount(); + Vector arguments; + arguments.reserveInitialCapacity(argumentCount); + for (size_t i = 0; i < argumentCount; ++i) + arguments.uncheckedAppend(toRef(exec, exec->uncheckedArgument(i))); + JSValueRef exception = 0; + JSValue result; + { + JSLock::DropAllLocks dropAllLocks(exec); + result = toJS(exec, callAsFunction(execRef, functionRef, thisObjRef, argumentCount, arguments.data(), &exception)); + } + if (exception) + throwException(exec, scope, toJS(exec, exception)); + return JSValue::encode(result); + } + } + + RELEASE_ASSERT_NOT_REACHED(); // getCallData should prevent us from reaching here + return JSValue::encode(JSValue()); +} + +template +void JSCallbackObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + VM& vm = exec->vm(); + JSCallbackObject* thisObject = jsCast(object); + JSContextRef execRef = toRef(exec); + JSObjectRef thisRef = toRef(thisObject); + + for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) { + if (JSObjectGetPropertyNamesCallback getPropertyNames = jsClass->getPropertyNames) { + JSLock::DropAllLocks dropAllLocks(exec); + getPropertyNames(execRef, thisRef, toRef(&propertyNames)); + } + + if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { + typedef OpaqueJSClassStaticValuesTable::const_iterator iterator; + iterator end = staticValues->end(); + for (iterator it = staticValues->begin(); it != end; ++it) { + StringImpl* name = it->key.get(); + StaticValueEntry* entry = it->value.get(); + if (entry->getProperty && (!(entry->attributes & kJSPropertyAttributeDontEnum) || mode.includeDontEnumProperties())) { + ASSERT(!name->isSymbol()); + propertyNames.add(Identifier::fromString(vm, String(name))); + } + } + } + + if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { + typedef OpaqueJSClassStaticFunctionsTable::const_iterator iterator; + iterator end = staticFunctions->end(); + for (iterator it = staticFunctions->begin(); it != end; ++it) { + StringImpl* name = it->key.get(); + StaticFunctionEntry* entry = it->value.get(); + if (!(entry->attributes & kJSPropertyAttributeDontEnum) || mode.includeDontEnumProperties()) { + ASSERT(!name->isSymbol()); + propertyNames.add(Identifier::fromString(vm, String(name))); + } + } + } + } + + Parent::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); +} + +template +void JSCallbackObject::setPrivate(void* data) +{ + m_callbackObjectData->privateData = data; +} + +template +void* JSCallbackObject::getPrivate() +{ + return m_callbackObjectData->privateData; +} + +template +bool JSCallbackObject::inherits(JSClassRef c) const +{ + for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { + if (jsClass == c) + return true; + } + return false; +} + +template +JSValue JSCallbackObject::getStaticValue(ExecState* exec, PropertyName propertyName) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSObjectRef thisRef = toRef(this); + + if (StringImpl* name = propertyName.uid()) { + for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { + if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { + if (StaticValueEntry* entry = staticValues->get(name)) { + if (JSObjectGetPropertyCallback getProperty = entry->getProperty) { + JSValueRef exception = 0; + JSValueRef value; + { + JSLock::DropAllLocks dropAllLocks(exec); + value = getProperty(toRef(exec), thisRef, entry->propertyNameRef.get(), &exception); + } + if (exception) { + throwException(exec, scope, toJS(exec, exception)); + return jsUndefined(); + } + if (value) + return toJS(exec, value); + } + } + } + } + } + + return JSValue(); +} + +template +EncodedJSValue JSCallbackObject::staticFunctionGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName propertyName) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSCallbackObject* thisObj = asCallbackObject(thisValue); + + // Check for cached or override property. + PropertySlot slot2(thisObj, PropertySlot::InternalMethodType::VMInquiry); + if (Parent::getOwnPropertySlot(thisObj, exec, propertyName, slot2)) + return JSValue::encode(slot2.getValue(exec, propertyName)); + + if (StringImpl* name = propertyName.uid()) { + for (JSClassRef jsClass = thisObj->classRef(); jsClass; jsClass = jsClass->parentClass) { + if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { + if (StaticFunctionEntry* entry = staticFunctions->get(name)) { + if (JSObjectCallAsFunctionCallback callAsFunction = entry->callAsFunction) { + JSObject* o = JSCallbackFunction::create(vm, thisObj->globalObject(vm), callAsFunction, name); + thisObj->putDirect(vm, propertyName, o, entry->attributes); + return JSValue::encode(o); + } + } + } + } + } + + return JSValue::encode(throwException(exec, scope, createReferenceError(exec, "Static function property defined with NULL callAsFunction callback."_s))); +} + +template +EncodedJSValue JSCallbackObject::callbackGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName propertyName) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSCallbackObject* thisObj = asCallbackObject(thisValue); + + JSObjectRef thisRef = toRef(thisObj); + RefPtr propertyNameRef; + + if (StringImpl* name = propertyName.uid()) { + for (JSClassRef jsClass = thisObj->classRef(); jsClass; jsClass = jsClass->parentClass) { + if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) { + if (!propertyNameRef) + propertyNameRef = OpaqueJSString::tryCreate(name); + JSValueRef exception = 0; + JSValueRef value; + { + JSLock::DropAllLocks dropAllLocks(exec); + value = getProperty(toRef(exec), thisRef, propertyNameRef.get(), &exception); + } + if (exception) { + throwException(exec, scope, toJS(exec, exception)); + return JSValue::encode(jsUndefined()); + } + if (value) + return JSValue::encode(toJS(exec, value)); + } + } + } + + return JSValue::encode(throwException(exec, scope, createReferenceError(exec, "hasProperty callback returned true for a property that doesn't exist."_s))); +} + +} // namespace JSC diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSClassRef.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSClassRef.h new file mode 100644 index 000000000..0dd0dca54 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSClassRef.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2006 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSClassRef_h +#define JSClassRef_h + +#include "OpaqueJSString.h" +#include "Protect.h" +#include "Weak.h" +#include +#include +#include + +struct StaticValueEntry { + WTF_MAKE_FAST_ALLOCATED; +public: + StaticValueEntry(JSObjectGetPropertyCallback _getProperty, JSObjectSetPropertyCallback _setProperty, JSPropertyAttributes _attributes, String& propertyName) + : getProperty(_getProperty) + , setProperty(_setProperty) + , attributes(_attributes) + , propertyNameRef(OpaqueJSString::tryCreate(propertyName)) + { + } + + JSObjectGetPropertyCallback getProperty; + JSObjectSetPropertyCallback setProperty; + JSPropertyAttributes attributes; + RefPtr propertyNameRef; +}; + +struct StaticFunctionEntry { + WTF_MAKE_FAST_ALLOCATED; +public: + StaticFunctionEntry(JSObjectCallAsFunctionCallback _callAsFunction, JSPropertyAttributes _attributes) + : callAsFunction(_callAsFunction), attributes(_attributes) + { + } + + JSObjectCallAsFunctionCallback callAsFunction; + JSPropertyAttributes attributes; +}; + +typedef HashMap, std::unique_ptr> OpaqueJSClassStaticValuesTable; +typedef HashMap, std::unique_ptr> OpaqueJSClassStaticFunctionsTable; + +struct OpaqueJSClass; + +// An OpaqueJSClass (JSClass) is created without a context, so it can be used with any context, even across context groups. +// This structure holds data members that vary across context groups. +struct OpaqueJSClassContextData { + WTF_MAKE_NONCOPYABLE(OpaqueJSClassContextData); WTF_MAKE_FAST_ALLOCATED; +public: + OpaqueJSClassContextData(JSC::VM&, OpaqueJSClass*); + + // It is necessary to keep OpaqueJSClass alive because of the following rare scenario: + // 1. A class is created and used, so its context data is stored in VM hash map. + // 2. The class is released, and when all JS objects that use it are collected, OpaqueJSClass + // is deleted (that's the part prevented by this RefPtr). + // 3. Another class is created at the same address. + // 4. When it is used, the old context data is found in VM and used. + RefPtr m_class; + + std::unique_ptr staticValues; + std::unique_ptr staticFunctions; + JSC::Weak cachedPrototype; +}; + +struct OpaqueJSClass : public ThreadSafeRefCounted { + static Ref create(const JSClassDefinition*); + static Ref createNoAutomaticPrototype(const JSClassDefinition*); + JS_EXPORT_PRIVATE ~OpaqueJSClass(); + + String className(); + OpaqueJSClassStaticValuesTable* staticValues(JSC::ExecState*); + OpaqueJSClassStaticFunctionsTable* staticFunctions(JSC::ExecState*); + JSC::JSObject* prototype(JSC::ExecState*); + + OpaqueJSClass* parentClass; + OpaqueJSClass* prototypeClass; + + JSObjectInitializeCallback initialize; + JSObjectFinalizeCallback finalize; + JSObjectHasPropertyCallback hasProperty; + JSObjectGetPropertyCallback getProperty; + JSObjectSetPropertyCallback setProperty; + JSObjectDeletePropertyCallback deleteProperty; + JSObjectGetPropertyNamesCallback getPropertyNames; + JSObjectCallAsFunctionCallback callAsFunction; + JSObjectCallAsConstructorCallback callAsConstructor; + JSObjectHasInstanceCallback hasInstance; + JSObjectConvertToTypeCallback convertToType; + +private: + friend struct OpaqueJSClassContextData; + + OpaqueJSClass(); + OpaqueJSClass(const OpaqueJSClass&); + OpaqueJSClass(const JSClassDefinition*, OpaqueJSClass* protoClass); + + OpaqueJSClassContextData& contextData(JSC::ExecState*); + + // Strings in these data members should not be put into any AtomStringTable. + String m_className; + std::unique_ptr m_staticValues; + std::unique_ptr m_staticFunctions; +}; + +#endif // JSClassRef_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContext.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContext.h new file mode 100644 index 000000000..6b9c5d417 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContext.h @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2013-2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSContext_h +#define JSContext_h + +#include +#include + +#if JSC_OBJC_API_ENABLED + +@class JSScript, JSVirtualMachine, JSValue, JSContext; + +/*! +@interface +@discussion A JSContext is a JavaScript execution environment. All + JavaScript execution takes place within a context, and all JavaScript values + are tied to a context. +*/ +JSC_CLASS_AVAILABLE(macos(10.9), ios(7.0)) +@interface JSContext : NSObject + +/*! +@methodgroup Creating New JSContexts +*/ +/*! +@method +@abstract Create a JSContext. +@result The new context. +*/ +- (instancetype)init; + +/*! +@method +@abstract Create a JSContext in the specified virtual machine. +@param virtualMachine The JSVirtualMachine in which the context will be created. +@result The new context. +*/ +- (instancetype)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine; + +/*! +@methodgroup Evaluating Scripts +*/ +/*! +@method +@abstract Evaluate a string of JavaScript code. +@param script A string containing the JavaScript code to evaluate. +@result The last value generated by the script. +*/ +- (JSValue *)evaluateScript:(NSString *)script; + +/*! +@method +@abstract Evaluate a string of JavaScript code, with a URL for the script's source file. +@param script A string containing the JavaScript code to evaluate. +@param sourceURL A URL for the script's source file. Used by debuggers and when reporting exceptions. This parameter is informative only: it does not change the behavior of the script. +@result The last value generated by the script. +*/ +- (JSValue *)evaluateScript:(NSString *)script withSourceURL:(NSURL *)sourceURL JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +/*! +@methodgroup Callback Accessors +*/ +/*! +@method +@abstract Get the JSContext that is currently executing. +@discussion This method may be called from within an Objective-C block or method invoked + as a callback from JavaScript to retrieve the callback's context. Outside of + a callback from JavaScript this method will return nil. +@result The currently executing JSContext or nil if there isn't one. +*/ ++ (JSContext *)currentContext; + +/*! +@method +@abstract Get the JavaScript function that is currently executing. +@discussion This method may be called from within an Objective-C block or method invoked + as a callback from JavaScript to retrieve the callback's context. Outside of + a callback from JavaScript this method will return nil. +@result The currently executing JavaScript function or nil if there isn't one. +*/ ++ (JSValue *)currentCallee JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +/*! +@method +@abstract Get the this value of the currently executing method. +@discussion This method may be called from within an Objective-C block or method invoked + as a callback from JavaScript to retrieve the callback's this value. Outside + of a callback from JavaScript this method will return nil. +@result The current this value or nil if there isn't one. +*/ ++ (JSValue *)currentThis; + +/*! +@method +@abstract Get the arguments to the current callback. +@discussion This method may be called from within an Objective-C block or method invoked + as a callback from JavaScript to retrieve the callback's arguments, objects + in the returned array are instances of JSValue. Outside of a callback from + JavaScript this method will return nil. +@result An NSArray of the arguments nil if there is no current callback. +*/ ++ (NSArray *)currentArguments; + +/*! +@functiongroup Global Properties +*/ + +/*! +@property +@abstract Get the global object of the context. +@discussion This method retrieves the global object of the JavaScript execution context. + Instances of JSContext originating from WebKit will return a reference to the + WindowProxy object. +@result The global object. +*/ +@property (readonly, strong) JSValue *globalObject; + +/*! +@property +@discussion The exception property may be used to throw an exception to JavaScript. + + Before a callback is made from JavaScript to an Objective-C block or method, + the prior value of the exception property will be preserved and the property + will be set to nil. After the callback has completed the new value of the + exception property will be read, and prior value restored. If the new value + of exception is not nil, the callback will result in that value being thrown. + + This property may also be used to check for uncaught exceptions arising from + API function calls (since the default behaviour of exceptionHandler is to + assign an uncaught exception to this property). +*/ +@property (strong) JSValue *exception; + +/*! +@property +@discussion If a call to an API function results in an uncaught JavaScript exception, the + exceptionHandler block will be invoked. The default implementation for the + exception handler will store the exception to the exception property on + context. As a consequence the default behaviour is for uncaught exceptions + occurring within a callback from JavaScript to be rethrown upon return. + Setting this value to nil will cause all exceptions occurring + within a callback from JavaScript to be silently caught. +*/ +@property (copy) void(^exceptionHandler)(JSContext *context, JSValue *exception); + +/*! +@property +@discussion All instances of JSContext are associated with a JSVirtualMachine. +*/ +@property (readonly, strong) JSVirtualMachine *virtualMachine; + +/*! +@property +@discussion Name of the JSContext. Exposed when remote debugging the context. +*/ +@property (copy) NSString *name JSC_API_AVAILABLE(macos(10.10), ios(8.0)); +@end + +/*! +@category +@discussion Instances of JSContext implement the following methods in order to enable + support for subscript access by key and index, for example: + +@textblock + JSContext *context; + JSValue *v = context[@"X"]; // Get value for "X" from the global object. + context[@"Y"] = v; // Assign 'v' to "Y" on the global object. +@/textblock + + An object key passed as a subscript will be converted to a JavaScript value, + and then the value converted to a string used to resolve a property of the + global object. +*/ +@interface JSContext (SubscriptSupport) + +/*! +@method +@abstract Get a particular property on the global object. +@result The JSValue for the global object's property. +*/ +- (JSValue *)objectForKeyedSubscript:(id)key; + +/*! +@method +@abstract Set a particular property on the global object. +*/ +- (void)setObject:(id)object forKeyedSubscript:(NSObject *)key; + +@end + +/*! +@category +@discussion These functions are for bridging between the C API and the Objective-C API. +*/ +@interface JSContext (JSContextRefSupport) + +/*! +@method +@abstract Create a JSContext, wrapping its C API counterpart. +@result The JSContext equivalent of the provided JSGlobalContextRef. +*/ ++ (JSContext *)contextWithJSGlobalContextRef:(JSGlobalContextRef)jsGlobalContextRef; + +/*! +@property +@abstract Get the C API counterpart wrapped by a JSContext. +@result The C API equivalent of this JSContext. +*/ +@property (readonly) JSGlobalContextRef JSGlobalContextRef; + +@end + +#endif + +#endif // JSContext_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextInternal.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextInternal.h new file mode 100644 index 000000000..958c479f4 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextInternal.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013-2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#if JSC_OBJC_API_ENABLED + +#import + +struct CallbackData { + CallbackData* next; + JSContext *context; + JSValue *preservedException; + JSValueRef calleeValue; + JSValueRef thisValue; + size_t argumentCount; + const JSValueRef *arguments; + NSArray *currentArguments; +}; + +@class JSWrapperMap; + +@interface JSContext(Internal) + +- (void)notifyException:(JSValueRef)exception; +- (JSValue *)valueFromNotifyException:(JSValueRef)exception; +- (BOOL)boolFromNotifyException:(JSValueRef)exception; + +- (void)beginCallbackWithData:(CallbackData *)callbackData calleeValue:(JSValueRef)calleeValue thisValue:(JSValueRef)thisValue argumentCount:(size_t)argumentCount arguments:(const JSValueRef *)arguments; +- (void)endCallbackWithData:(CallbackData *)callbackData; + +- (JSWrapperMap *)wrapperMap; +- (JSValue *)wrapperForObjCObject:(id)object; +- (JSValue *)wrapperForJSObject:(JSValueRef)value; + +@end + +#endif diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextPrivate.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextPrivate.h new file mode 100644 index 000000000..75f526b97 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextPrivate.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSContextPrivate_h +#define JSContextPrivate_h + +#if JSC_OBJC_API_ENABLED + +#import + +@protocol JSModuleLoaderDelegate + +@required + +/*! @abstract Provides source code for any JS module that is actively imported. + @param context The context for which the module is being requested. + @param identifier The resolved identifier for the requested module. + @param resolve A JS function to call with the desired script for identifier. + @param reject A JS function to call when identifier cannot be fetched. + @discussion Currently, identifier will always be an absolute file URL computed from specifier of the requested module relative to the URL of the requesting script. If the requesting script does not have a URL and the module specifier is not an absolute path the module loader will fail to load the module. + + The first argument to resolve sholud always be a JSScript, otherwise the module loader will reject the module. + + Once an identifier has been resolved or rejected in a given context it will never be requested again. If a script is successfully evaluated it will not be re-evaluated on any subsequent import. + + The VM will retain all evaluated modules for the lifetime of the context. + */ +- (void)context:(JSContext *)context fetchModuleForIdentifier:(JSValue *)identifier withResolveHandler:(JSValue *)resolve andRejectHandler:(JSValue *)reject; + +@optional + +/*! @abstract This is called before the module with "key" is evaluated. + @param key The module key for the module that is about to be evaluated. + */ +- (void)willEvaluateModule:(NSURL *)key; + +/*! @abstract This is called after the module with "key" is evaluated. + @param key The module key for the module that was just evaluated. + */ +- (void)didEvaluateModule:(NSURL *)key; + +@end + +@interface JSContext(Private) + +/*! +@property +@discussion Remote inspection setting of the JSContext. Default value is YES. +*/ +@property (setter=_setRemoteInspectionEnabled:) BOOL _remoteInspectionEnabled JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +/*! +@property +@discussion Set whether or not the native call stack is included when reporting exceptions. Default value is YES. +*/ +@property (setter=_setIncludesNativeCallStackWhenReportingExceptions:) BOOL _includesNativeCallStackWhenReportingExceptions JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +/*! +@property +@discussion Set the run loop the Web Inspector debugger should use when evaluating JavaScript in the JSContext. +*/ +@property (setter=_setDebuggerRunLoop:) CFRunLoopRef _debuggerRunLoop JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +/*! @abstract The delegate the context will use when trying to load a module. Note, this delegate will be ignored for contexts returned by UIWebView. */ +@property (nonatomic, weak) id moduleLoaderDelegate JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! + @method + @abstract Run a JSScript. + @param script the JSScript to evaluate. + @discussion If the provided JSScript was created with kJSScriptTypeProgram, the script will run synchronously and return the result of evaluation. + + Otherwise, if the script was created with kJSScriptTypeModule, the module will be run asynchronously and will return a promise resolved when the module and any transitive dependencies are loaded. The module loader will treat the script as if it had been returned from a delegate call to moduleLoaderDelegate. This mirrors the JavaScript dynamic import operation. + */ +// FIXME: Before making this public need to fix: https://bugs.webkit.org/show_bug.cgi?id=199714 +- (JSValue *)evaluateJSScript:(JSScript *)script JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! + @method + @abstract Get the identifiers of the modules a JSScript depends on in this context. + @param script the JSScript to determine the dependencies of. + @result An Array containing all the identifiers of modules script depends on. + @discussion If the provided JSScript was not created with kJSScriptTypeModule, an exception will be thrown. Also, if the script has not already been evaluated in this context an error will be throw. + */ +- (JSValue *)dependencyIdentifiersForModuleJSScript:(JSScript *)script JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +@end + +#endif + +#endif // JSContextInternal_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextRef.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextRef.h new file mode 100644 index 000000000..1ce743580 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextRef.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2006 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSContextRef_h +#define JSContextRef_h + +#include +#include +#include + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/*! +@function +@abstract Creates a JavaScript context group. +@discussion A JSContextGroup associates JavaScript contexts with one another. + Contexts in the same group may share and exchange JavaScript objects. Sharing and/or exchanging + JavaScript objects between contexts in different groups will produce undefined behavior. + When objects from the same context group are used in multiple threads, explicit + synchronization is required. + + A JSContextGroup may need to run deferred tasks on a run loop, such as garbage collection + or resolving WebAssembly compilations. By default, calling JSContextGroupCreate will use + the run loop of the thread it was called on. Currently, there is no API to change a + JSContextGroup's run loop once it has been created. +@result The created JSContextGroup. +*/ +JS_EXPORT JSContextGroupRef JSContextGroupCreate(void) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + +/*! +@function +@abstract Retains a JavaScript context group. +@param group The JSContextGroup to retain. +@result A JSContextGroup that is the same as group. +*/ +JS_EXPORT JSContextGroupRef JSContextGroupRetain(JSContextGroupRef group) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + +/*! +@function +@abstract Releases a JavaScript context group. +@param group The JSContextGroup to release. +*/ +JS_EXPORT void JSContextGroupRelease(JSContextGroupRef group) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + +/*! +@function +@abstract Creates a global JavaScript execution context. +@discussion JSGlobalContextCreate allocates a global object and populates it with all the + built-in JavaScript objects, such as Object, Function, String, and Array. + + In WebKit version 4.0 and later, the context is created in a unique context group. + Therefore, scripts may execute in it concurrently with scripts executing in other contexts. + However, you may not use values created in the context in other contexts. +@param globalObjectClass The class to use when creating the global object. Pass + NULL to use the default object class. +@result A JSGlobalContext with a global object of class globalObjectClass. +*/ +JS_EXPORT JSGlobalContextRef JSGlobalContextCreate(JSClassRef globalObjectClass) JSC_API_AVAILABLE(macos(10.5), ios(7.0)); + +/*! +@function +@abstract Creates a global JavaScript execution context in the context group provided. +@discussion JSGlobalContextCreateInGroup allocates a global object and populates it with + all the built-in JavaScript objects, such as Object, Function, String, and Array. +@param globalObjectClass The class to use when creating the global object. Pass + NULL to use the default object class. +@param group The context group to use. The created global context retains the group. + Pass NULL to create a unique group for the context. +@result A JSGlobalContext with a global object of class globalObjectClass and a context + group equal to group. +*/ +JS_EXPORT JSGlobalContextRef JSGlobalContextCreateInGroup(JSContextGroupRef group, JSClassRef globalObjectClass) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + +/*! +@function +@abstract Retains a global JavaScript execution context. +@param ctx The JSGlobalContext to retain. +@result A JSGlobalContext that is the same as ctx. +*/ +JS_EXPORT JSGlobalContextRef JSGlobalContextRetain(JSGlobalContextRef ctx); + +/*! +@function +@abstract Releases a global JavaScript execution context. +@param ctx The JSGlobalContext to release. +*/ +JS_EXPORT void JSGlobalContextRelease(JSGlobalContextRef ctx); + +/*! +@function +@abstract Gets the global object of a JavaScript execution context. +@param ctx The JSContext whose global object you want to get. +@result ctx's global object. +*/ +JS_EXPORT JSObjectRef JSContextGetGlobalObject(JSContextRef ctx); + +/*! +@function +@abstract Gets the context group to which a JavaScript execution context belongs. +@param ctx The JSContext whose group you want to get. +@result ctx's group. +*/ +JS_EXPORT JSContextGroupRef JSContextGetGroup(JSContextRef ctx) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + +/*! +@function +@abstract Gets the global context of a JavaScript execution context. +@param ctx The JSContext whose global context you want to get. +@result ctx's global context. +*/ +JS_EXPORT JSGlobalContextRef JSContextGetGlobalContext(JSContextRef ctx) JSC_API_AVAILABLE(macos(10.7), ios(7.0)); + +/*! +@function +@abstract Gets a copy of the name of a context. +@param ctx The JSGlobalContext whose name you want to get. +@result The name for ctx. +@discussion A JSGlobalContext's name is exposed for remote debugging to make it +easier to identify the context you would like to attach to. +*/ +JS_EXPORT JSStringRef JSGlobalContextCopyName(JSGlobalContextRef ctx) JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +/*! +@function +@abstract Sets the remote debugging name for a context. +@param ctx The JSGlobalContext that you want to name. +@param name The remote debugging name to set on ctx. +*/ +JS_EXPORT void JSGlobalContextSetName(JSGlobalContextRef ctx, JSStringRef name) JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +#ifdef __cplusplus +} +#endif + +#endif /* JSContextRef_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextRefInspectorSupport.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextRefInspectorSupport.h new file mode 100644 index 000000000..a09d828bd --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextRefInspectorSupport.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSContextRefInspectorSupport_h +#define JSContextRefInspectorSupport_h + +#ifndef __cplusplus +#error Requires C++ Support. +#endif + +#include + +namespace Inspector { +class AugmentableInspectorController; +} + +extern "C" { +JS_EXPORT Inspector::AugmentableInspectorController* JSGlobalContextGetAugmentableInspectorController(JSGlobalContextRef); +} + +#endif // JSContextRefInspectorSupport_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextRefInternal.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextRefInternal.h new file mode 100644 index 000000000..149f70bad --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextRefInternal.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSContextRefInternal_h +#define JSContextRefInternal_h + +#include "JSContextRefPrivate.h" + +#if USE(CF) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if USE(CF) +/*! +@function +@abstract Gets the run loop used by the Web Inspector debugger when evaluating JavaScript in this context. +@param ctx The JSGlobalContext whose setting you want to get. +*/ +JS_EXPORT CFRunLoopRef JSGlobalContextGetDebuggerRunLoop(JSGlobalContextRef ctx) JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +/*! +@function +@abstract Sets the run loop used by the Web Inspector debugger when evaluating JavaScript in this context. +@param ctx The JSGlobalContext that you want to change. +@param runLoop The new value of the setting for the context. +*/ +JS_EXPORT void JSGlobalContextSetDebuggerRunLoop(JSGlobalContextRef ctx, CFRunLoopRef runLoop) JSC_API_AVAILABLE(macos(10.10), ios(8.0)); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // JSContextRefInternal_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextRefPrivate.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextRefPrivate.h new file mode 100644 index 000000000..6ae649ef3 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSContextRefPrivate.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSContextRefPrivate_h +#define JSContextRefPrivate_h + +#include +#include +#include + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/*! +@function +@abstract Gets a Backtrace for the existing context +@param ctx The JSContext whose backtrace you want to get +@result A string containing the backtrace +*/ +JS_EXPORT JSStringRef JSContextCreateBacktrace(JSContextRef ctx, unsigned maxStackSize) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + + +/*! +@typedef JSShouldTerminateCallback +@abstract The callback invoked when script execution has exceeded the allowed + time limit previously specified via JSContextGroupSetExecutionTimeLimit. +@param ctx The execution context to use. +@param context User specified context data previously passed to + JSContextGroupSetExecutionTimeLimit. +@discussion If you named your function Callback, you would declare it like this: + + bool Callback(JSContextRef ctx, void* context); + + If you return true, the timed out script will terminate. + If you return false, the script will run for another period of the allowed + time limit specified via JSContextGroupSetExecutionTimeLimit. + + Within this callback function, you may call JSContextGroupSetExecutionTimeLimit + to set a new time limit, or JSContextGroupClearExecutionTimeLimit to cancel the + timeout. +*/ +typedef bool +(*JSShouldTerminateCallback) (JSContextRef ctx, void* context); + +/*! +@function +@abstract Sets the script execution time limit. +@param group The JavaScript context group that this time limit applies to. +@param limit The time limit of allowed script execution time in seconds. +@param callback The callback function that will be invoked when the time limit + has been reached. This will give you a chance to decide if you want to + terminate the script or not. If you pass a NULL callback, the script will be + terminated unconditionally when the time limit has been reached. +@param context User data that you can provide to be passed back to you + in your callback. + + In order to guarantee that the execution time limit will take effect, you will + need to call JSContextGroupSetExecutionTimeLimit before you start executing + any scripts. +*/ +JS_EXPORT void JSContextGroupSetExecutionTimeLimit(JSContextGroupRef group, double limit, JSShouldTerminateCallback callback, void* context) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + +/*! +@function +@abstract Clears the script execution time limit. +@param group The JavaScript context group that the time limit is cleared on. +*/ +JS_EXPORT void JSContextGroupClearExecutionTimeLimit(JSContextGroupRef group) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + +/*! +@function +@abstract Gets a whether or not remote inspection is enabled on the context. +@param ctx The JSGlobalContext whose setting you want to get. +@result The value of the setting, true if remote inspection is enabled, otherwise false. +@discussion Remote inspection is true by default. +*/ +JS_EXPORT bool JSGlobalContextGetRemoteInspectionEnabled(JSGlobalContextRef ctx) JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +/*! +@function +@abstract Sets the remote inspection setting for a context. +@param ctx The JSGlobalContext that you want to change. +@param enabled The new remote inspection enabled setting for the context. +*/ +JS_EXPORT void JSGlobalContextSetRemoteInspectionEnabled(JSGlobalContextRef ctx, bool enabled) JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +/*! +@function +@abstract Gets the include native call stack when reporting exceptions setting for a context. +@param ctx The JSGlobalContext whose setting you want to get. +@result The value of the setting, true if remote inspection is enabled, otherwise false. +@discussion This setting is true by default. +*/ +JS_EXPORT bool JSGlobalContextGetIncludesNativeCallStackWhenReportingExceptions(JSGlobalContextRef ctx) JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +/*! +@function +@abstract Sets the include native call stack when reporting exceptions setting for a context. +@param ctx The JSGlobalContext that you want to change. +@param includesNativeCallStack The new value of the setting for the context. +*/ +JS_EXPORT void JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(JSGlobalContextRef ctx, bool includesNativeCallStack) JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +/*! +@function +@abstract Sets the unhandled promise rejection callback for a context. +@discussion Similar to window.addEventListener('unhandledrejection'), but for contexts not associated with a web view. +@param ctx The JSGlobalContext to set the callback on. +@param function The callback function to set, which receives the promise and rejection reason as arguments. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +*/ +JS_EXPORT void JSGlobalContextSetUnhandledRejectionCallback(JSGlobalContextRef ctx, JSObjectRef function, JSValueRef* exception) JSC_API_AVAILABLE(macos(JSC_MAC_TBA), ios(JSC_IOS_TBA)); + +#ifdef __cplusplus +} +#endif + +#endif /* JSContextRefPrivate_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSExport.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSExport.h new file mode 100644 index 000000000..5caace642 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSExport.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#if JSC_OBJC_API_ENABLED + +/*! +@protocol +@abstract JSExport provides a declarative way to export Objective-C objects and + classes -- including properties, instance methods, class methods, and + initializers -- to JavaScript. + +@discussion When an Objective-C object is exported to JavaScript, a JavaScript + wrapper object is created. + + In JavaScript, inheritance works via a chain of prototype objects. + For each Objective-C class in each JSContext, an object appropriate for use + as a prototype will be provided. For the class NSObject the prototype + will be the Object prototype. For all other Objective-C + classes a prototype will be created. The prototype for a given + Objective-C class will have its internal [Prototype] property set to point to + the prototype created for the Objective-C class's superclass. As such the + prototype chain for a JavaScript wrapper object will reflect the wrapped + Objective-C type's inheritance hierarchy. + + JavaScriptCore also produces a constructor for each Objective-C class. The + constructor has a property named 'prototype' that references the prototype, + and the prototype has a property named 'constructor' that references the + constructor. + + By default JavaScriptCore does not export any methods or properties from an + Objective-C class to JavaScript; however methods and properties may be exported + explicitly using JSExport. For each protocol that a class conforms to, if the + protocol incorporates the protocol JSExport, JavaScriptCore exports the methods + and properties in that protocol to JavaScript + + For each exported instance method JavaScriptCore will assign a corresponding + JavaScript function to the prototype. For each exported Objective-C property + JavaScriptCore will assign a corresponding JavaScript accessor to the prototype. + For each exported class method JavaScriptCore will assign a corresponding + JavaScript function to the constructor. For example: + +
+@textblock
+    @protocol MyClassJavaScriptMethods 
+    - (void)foo;
+    @end
+
+    @interface MyClass : NSObject 
+    - (void)foo;
+    - (void)bar;
+    @end
+@/textblock
+
+ + Data properties that are created on the prototype or constructor objects have + the attributes: writable:true, enumerable:false, configurable:true. + Accessor properties have the attributes: enumerable:false and configurable:true. + + If an instance of MyClass is converted to a JavaScript value, the resulting + wrapper object will (via its prototype) export the method foo to JavaScript, + since the class conforms to the MyClassJavaScriptMethods protocol, and this + protocol incorporates JSExport. bar will not be exported. + + JSExport supports properties, arguments, and return values of the following types: + + Primitive numbers: signed values up to 32-bits convert using JSValue's + valueWithInt32/toInt32. Unsigned values up to 32-bits convert using JSValue's + valueWithUInt32/toUInt32. All other numeric values convert using JSValue's + valueWithDouble/toDouble. + + BOOL: values convert using JSValue's valueWithBool/toBool. + + id: values convert using JSValue's valueWithObject/toObject. + + Objective-C instance pointers: Pointers convert using JSValue's + valueWithObjectOfClass/toObject. + + C structs: C structs for CGPoint, NSRange, CGRect, and CGSize convert using + JSValue's appropriate methods. Other C structs are not supported. + + Blocks: Blocks convert using JSValue's valueWithObject/toObject. + + All objects that conform to JSExport convert to JavaScript wrapper objects, + even if they subclass classes that would otherwise behave differently. For + example, if a subclass of NSString conforms to JSExport, it converts to + JavaScript as a wrapper object rather than a JavaScript string. +*/ +@protocol JSExport +@end + +/*! +@define +@abstract Rename a selector when it's exported to JavaScript. +@discussion When a selector that takes one or more arguments is converted to a JavaScript + property name, by default a property name will be generated by performing the + following conversion: + + - All colons are removed from the selector + + - Any lowercase letter that had followed a colon will be capitalized. + + Under the default conversion a selector doFoo:withBar: will be exported as + doFooWithBar. The default conversion may be overridden using the JSExportAs + macro, for example to export a method doFoo:withBar: as doFoo: + +
+@textblock
+    @protocol MyClassJavaScriptMethods 
+    JSExportAs(doFoo,
+    - (void)doFoo:(id)foo withBar:(id)bar
+    );
+    @end
+@/textblock
+
+ + Note that the JSExport macro may only be applied to a selector that takes one + or more argument. +*/ +#define JSExportAs(PropertyName, Selector) \ + @optional Selector __JS_EXPORT_AS__##PropertyName:(id)argument; @required Selector + +#endif diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSHeapFinalizerPrivate.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSHeapFinalizerPrivate.h new file mode 100644 index 000000000..8c9b1525e --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSHeapFinalizerPrivate.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSHeapFinalizerPrivate_h +#define JSHeapFinalizerPrivate_h + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*JSHeapFinalizer)(JSContextGroupRef, void *userData); + +JS_EXPORT void JSContextGroupAddHeapFinalizer(JSContextGroupRef, JSHeapFinalizer, void *userData); +JS_EXPORT void JSContextGroupRemoveHeapFinalizer(JSContextGroupRef, JSHeapFinalizer, void *userData); + +#ifdef __cplusplus +} +#endif + +#endif // JSHeapFinalizerPrivate_h + diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSManagedValue.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSManagedValue.h new file mode 100644 index 000000000..3ebc7a4bd --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSManagedValue.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSManagedValue_h +#define JSManagedValue_h + +#import +#import + +#if JSC_OBJC_API_ENABLED + +@class JSValue; +@class JSContext; + +/*! +@interface +@discussion JSManagedValue represents a "conditionally retained" JSValue. + "Conditionally retained" means that as long as the JSManagedValue's + JSValue is reachable through the JavaScript object graph, + or through the Objective-C object graph reported to the JSVirtualMachine using + addManagedReference:withOwner:, the corresponding JSValue will + be retained. However, if neither graph reaches the JSManagedValue, the + corresponding JSValue will be released and set to nil. + +The primary use for a JSManagedValue is to store a JSValue in an Objective-C +or Swift object that is exported to JavaScript. It is incorrect to store a JSValue +in an object that is exported to JavaScript, since doing so creates a retain cycle. +*/ +NS_CLASS_AVAILABLE(10_9, 7_0) +@interface JSManagedValue : NSObject + +/*! +@method +@abstract Create a JSManagedValue from a JSValue. +@result The new JSManagedValue. +*/ ++ (JSManagedValue *)managedValueWithValue:(JSValue *)value; ++ (JSManagedValue *)managedValueWithValue:(JSValue *)value andOwner:(id)owner JSC_API_AVAILABLE(macos(10.10), ios(8.0)); + +/*! +@method +@abstract Create a JSManagedValue. +@result The new JSManagedValue. +*/ +- (instancetype)initWithValue:(JSValue *)value; + +/*! +@property +@abstract Get the JSValue from the JSManagedValue. +@result The corresponding JSValue for this JSManagedValue or + nil if the JSValue has been collected. +*/ +@property (readonly, strong) JSValue *value; + +@end + +#endif // JSC_OBJC_API_ENABLED + +#endif // JSManagedValue_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSManagedValueInternal.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSManagedValueInternal.h new file mode 100644 index 000000000..2443fe5a9 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSManagedValueInternal.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSManagedValueInternal_h +#define JSManagedValueInternal_h + +#import + +#if JSC_OBJC_API_ENABLED + +@interface JSManagedValue(Internal) + +- (void)didAddOwner:(id)owner; +- (void)didRemoveOwner:(id)owner; + +@end + +#endif // JSC_OBJC_API_ENABLED + +#endif // JSManagedValueInternal_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSMarkingConstraintPrivate.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSMarkingConstraintPrivate.h new file mode 100644 index 000000000..aaf85cad1 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSMarkingConstraintPrivate.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSMarkingConstraintPrivate_h +#define JSMarkingConstraintPrivate_h + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct JSMarker; +typedef struct JSMarker JSMarker; +typedef JSMarker *JSMarkerRef; + +struct JSMarker { + bool (*IsMarked)(JSMarkerRef, JSObjectRef); + void (*Mark)(JSMarkerRef, JSObjectRef); +}; + +typedef void (*JSMarkingConstraint)(JSMarkerRef, void *userData); + +JS_EXPORT void JSContextGroupAddMarkingConstraint(JSContextGroupRef, JSMarkingConstraint, void *userData); + +#ifdef __cplusplus +} +#endif + +#endif // JSMarkingConstraintPrivate_h + diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSObjectRef.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSObjectRef.h new file mode 100644 index 000000000..330f5e3be --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSObjectRef.h @@ -0,0 +1,753 @@ +/* + * Copyright (C) 2006-2019 Apple Inc. All rights reserved. + * Copyright (C) 2008 Kelvin W Sherlock (ksherlock@gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSObjectRef_h +#define JSObjectRef_h + +#include +#include +#include + +#ifndef __cplusplus +#include +#endif +#include /* for size_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! +@enum JSPropertyAttribute +@constant kJSPropertyAttributeNone Specifies that a property has no special attributes. +@constant kJSPropertyAttributeReadOnly Specifies that a property is read-only. +@constant kJSPropertyAttributeDontEnum Specifies that a property should not be enumerated by JSPropertyEnumerators and JavaScript for...in loops. +@constant kJSPropertyAttributeDontDelete Specifies that the delete operation should fail on a property. +*/ +enum { + kJSPropertyAttributeNone = 0, + kJSPropertyAttributeReadOnly = 1 << 1, + kJSPropertyAttributeDontEnum = 1 << 2, + kJSPropertyAttributeDontDelete = 1 << 3 +}; + +/*! +@typedef JSPropertyAttributes +@abstract A set of JSPropertyAttributes. Combine multiple attributes by logically ORing them together. +*/ +typedef unsigned JSPropertyAttributes; + +/*! +@enum JSClassAttribute +@constant kJSClassAttributeNone Specifies that a class has no special attributes. +@constant kJSClassAttributeNoAutomaticPrototype Specifies that a class should not automatically generate a shared prototype for its instance objects. Use kJSClassAttributeNoAutomaticPrototype in combination with JSObjectSetPrototype to manage prototypes manually. +*/ +enum { + kJSClassAttributeNone = 0, + kJSClassAttributeNoAutomaticPrototype = 1 << 1 +}; + +/*! +@typedef JSClassAttributes +@abstract A set of JSClassAttributes. Combine multiple attributes by logically ORing them together. +*/ +typedef unsigned JSClassAttributes; + +/*! +@typedef JSObjectInitializeCallback +@abstract The callback invoked when an object is first created. +@param ctx The execution context to use. +@param object The JSObject being created. +@discussion If you named your function Initialize, you would declare it like this: + +void Initialize(JSContextRef ctx, JSObjectRef object); + +Unlike the other object callbacks, the initialize callback is called on the least +derived class (the parent class) first, and the most derived class last. +*/ +typedef void +(*JSObjectInitializeCallback) (JSContextRef ctx, JSObjectRef object); + +/*! +@typedef JSObjectFinalizeCallback +@abstract The callback invoked when an object is finalized (prepared for garbage collection). An object may be finalized on any thread. +@param object The JSObject being finalized. +@discussion If you named your function Finalize, you would declare it like this: + +void Finalize(JSObjectRef object); + +The finalize callback is called on the most derived class first, and the least +derived class (the parent class) last. + +You must not call any function that may cause a garbage collection or an allocation +of a garbage collected object from within a JSObjectFinalizeCallback. This includes +all functions that have a JSContextRef parameter. +*/ +typedef void +(*JSObjectFinalizeCallback) (JSObjectRef object); + +/*! +@typedef JSObjectHasPropertyCallback +@abstract The callback invoked when determining whether an object has a property. +@param ctx The execution context to use. +@param object The JSObject to search for the property. +@param propertyName A JSString containing the name of the property look up. +@result true if object has the property, otherwise false. +@discussion If you named your function HasProperty, you would declare it like this: + +bool HasProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName); + +If this function returns false, the hasProperty request forwards to object's statically declared properties, then its parent class chain (which includes the default object class), then its prototype chain. + +This callback enables optimization in cases where only a property's existence needs to be known, not its value, and computing its value would be expensive. + +If this callback is NULL, the getProperty callback will be used to service hasProperty requests. +*/ +typedef bool +(*JSObjectHasPropertyCallback) (JSContextRef ctx, JSObjectRef object, JSStringRef propertyName); + +/*! +@typedef JSObjectGetPropertyCallback +@abstract The callback invoked when getting a property's value. +@param ctx The execution context to use. +@param object The JSObject to search for the property. +@param propertyName A JSString containing the name of the property to get. +@param exception A pointer to a JSValueRef in which to return an exception, if any. +@result The property's value if object has the property, otherwise NULL. +@discussion If you named your function GetProperty, you would declare it like this: + +JSValueRef GetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception); + +If this function returns NULL, the get request forwards to object's statically declared properties, then its parent class chain (which includes the default object class), then its prototype chain. +*/ +typedef JSValueRef +(*JSObjectGetPropertyCallback) (JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception); + +/*! +@typedef JSObjectSetPropertyCallback +@abstract The callback invoked when setting a property's value. +@param ctx The execution context to use. +@param object The JSObject on which to set the property's value. +@param propertyName A JSString containing the name of the property to set. +@param value A JSValue to use as the property's value. +@param exception A pointer to a JSValueRef in which to return an exception, if any. +@result true if the property was set, otherwise false. +@discussion If you named your function SetProperty, you would declare it like this: + +bool SetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception); + +If this function returns false, the set request forwards to object's statically declared properties, then its parent class chain (which includes the default object class). +*/ +typedef bool +(*JSObjectSetPropertyCallback) (JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception); + +/*! +@typedef JSObjectDeletePropertyCallback +@abstract The callback invoked when deleting a property. +@param ctx The execution context to use. +@param object The JSObject in which to delete the property. +@param propertyName A JSString containing the name of the property to delete. +@param exception A pointer to a JSValueRef in which to return an exception, if any. +@result true if propertyName was successfully deleted, otherwise false. +@discussion If you named your function DeleteProperty, you would declare it like this: + +bool DeleteProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception); + +If this function returns false, the delete request forwards to object's statically declared properties, then its parent class chain (which includes the default object class). +*/ +typedef bool +(*JSObjectDeletePropertyCallback) (JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception); + +/*! +@typedef JSObjectGetPropertyNamesCallback +@abstract The callback invoked when collecting the names of an object's properties. +@param ctx The execution context to use. +@param object The JSObject whose property names are being collected. +@param propertyNames A JavaScript property name accumulator in which to accumulate the names of object's properties. +@discussion If you named your function GetPropertyNames, you would declare it like this: + +void GetPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames); + +Property name accumulators are used by JSObjectCopyPropertyNames and JavaScript for...in loops. + +Use JSPropertyNameAccumulatorAddName to add property names to accumulator. A class's getPropertyNames callback only needs to provide the names of properties that the class vends through a custom getProperty or setProperty callback. Other properties, including statically declared properties, properties vended by other classes, and properties belonging to object's prototype, are added independently. +*/ +typedef void +(*JSObjectGetPropertyNamesCallback) (JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames); + +/*! +@typedef JSObjectCallAsFunctionCallback +@abstract The callback invoked when an object is called as a function. +@param ctx The execution context to use. +@param function A JSObject that is the function being called. +@param thisObject A JSObject that is the 'this' variable in the function's scope. +@param argumentCount An integer count of the number of arguments in arguments. +@param arguments A JSValue array of the arguments passed to the function. +@param exception A pointer to a JSValueRef in which to return an exception, if any. +@result A JSValue that is the function's return value. +@discussion If you named your function CallAsFunction, you would declare it like this: + +JSValueRef CallAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); + +If your callback were invoked by the JavaScript expression 'myObject.myFunction()', function would be set to myFunction, and thisObject would be set to myObject. + +If this callback is NULL, calling your object as a function will throw an exception. +*/ +typedef JSValueRef +(*JSObjectCallAsFunctionCallback) (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); + +/*! +@typedef JSObjectCallAsConstructorCallback +@abstract The callback invoked when an object is used as a constructor in a 'new' expression. +@param ctx The execution context to use. +@param constructor A JSObject that is the constructor being called. +@param argumentCount An integer count of the number of arguments in arguments. +@param arguments A JSValue array of the arguments passed to the function. +@param exception A pointer to a JSValueRef in which to return an exception, if any. +@result A JSObject that is the constructor's return value. +@discussion If you named your function CallAsConstructor, you would declare it like this: + +JSObjectRef CallAsConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); + +If your callback were invoked by the JavaScript expression 'new myConstructor()', constructor would be set to myConstructor. + +If this callback is NULL, using your object as a constructor in a 'new' expression will throw an exception. +*/ +typedef JSObjectRef +(*JSObjectCallAsConstructorCallback) (JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); + +/*! +@typedef JSObjectHasInstanceCallback +@abstract hasInstance The callback invoked when an object is used as the target of an 'instanceof' expression. +@param ctx The execution context to use. +@param constructor The JSObject that is the target of the 'instanceof' expression. +@param possibleInstance The JSValue being tested to determine if it is an instance of constructor. +@param exception A pointer to a JSValueRef in which to return an exception, if any. +@result true if possibleInstance is an instance of constructor, otherwise false. +@discussion If you named your function HasInstance, you would declare it like this: + +bool HasInstance(JSContextRef ctx, JSObjectRef constructor, JSValueRef possibleInstance, JSValueRef* exception); + +If your callback were invoked by the JavaScript expression 'someValue instanceof myObject', constructor would be set to myObject and possibleInstance would be set to someValue. + +If this callback is NULL, 'instanceof' expressions that target your object will return false. + +Standard JavaScript practice calls for objects that implement the callAsConstructor callback to implement the hasInstance callback as well. +*/ +typedef bool +(*JSObjectHasInstanceCallback) (JSContextRef ctx, JSObjectRef constructor, JSValueRef possibleInstance, JSValueRef* exception); + +/*! +@typedef JSObjectConvertToTypeCallback +@abstract The callback invoked when converting an object to a particular JavaScript type. +@param ctx The execution context to use. +@param object The JSObject to convert. +@param type A JSType specifying the JavaScript type to convert to. +@param exception A pointer to a JSValueRef in which to return an exception, if any. +@result The objects's converted value, or NULL if the object was not converted. +@discussion If you named your function ConvertToType, you would declare it like this: + +JSValueRef ConvertToType(JSContextRef ctx, JSObjectRef object, JSType type, JSValueRef* exception); + +If this function returns false, the conversion request forwards to object's parent class chain (which includes the default object class). + +This function is only invoked when converting an object to number or string. An object converted to boolean is 'true.' An object converted to object is itself. +*/ +typedef JSValueRef +(*JSObjectConvertToTypeCallback) (JSContextRef ctx, JSObjectRef object, JSType type, JSValueRef* exception); + +/*! +@struct JSStaticValue +@abstract This structure describes a statically declared value property. +@field name A null-terminated UTF8 string containing the property's name. +@field getProperty A JSObjectGetPropertyCallback to invoke when getting the property's value. +@field setProperty A JSObjectSetPropertyCallback to invoke when setting the property's value. May be NULL if the ReadOnly attribute is set. +@field attributes A logically ORed set of JSPropertyAttributes to give to the property. +*/ +typedef struct { + const char* name; + JSObjectGetPropertyCallback getProperty; + JSObjectSetPropertyCallback setProperty; + JSPropertyAttributes attributes; +} JSStaticValue; + +/*! +@struct JSStaticFunction +@abstract This structure describes a statically declared function property. +@field name A null-terminated UTF8 string containing the property's name. +@field callAsFunction A JSObjectCallAsFunctionCallback to invoke when the property is called as a function. +@field attributes A logically ORed set of JSPropertyAttributes to give to the property. +*/ +typedef struct { + const char* name; + JSObjectCallAsFunctionCallback callAsFunction; + JSPropertyAttributes attributes; +} JSStaticFunction; + +/*! +@struct JSClassDefinition +@abstract This structure contains properties and callbacks that define a type of object. All fields other than the version field are optional. Any pointer may be NULL. +@field version The version number of this structure. The current version is 0. +@field attributes A logically ORed set of JSClassAttributes to give to the class. +@field className A null-terminated UTF8 string containing the class's name. +@field parentClass A JSClass to set as the class's parent class. Pass NULL use the default object class. +@field staticValues A JSStaticValue array containing the class's statically declared value properties. Pass NULL to specify no statically declared value properties. The array must be terminated by a JSStaticValue whose name field is NULL. +@field staticFunctions A JSStaticFunction array containing the class's statically declared function properties. Pass NULL to specify no statically declared function properties. The array must be terminated by a JSStaticFunction whose name field is NULL. +@field initialize The callback invoked when an object is first created. Use this callback to initialize the object. +@field finalize The callback invoked when an object is finalized (prepared for garbage collection). Use this callback to release resources allocated for the object, and perform other cleanup. +@field hasProperty The callback invoked when determining whether an object has a property. If this field is NULL, getProperty is called instead. The hasProperty callback enables optimization in cases where only a property's existence needs to be known, not its value, and computing its value is expensive. +@field getProperty The callback invoked when getting a property's value. +@field setProperty The callback invoked when setting a property's value. +@field deleteProperty The callback invoked when deleting a property. +@field getPropertyNames The callback invoked when collecting the names of an object's properties. +@field callAsFunction The callback invoked when an object is called as a function. +@field hasInstance The callback invoked when an object is used as the target of an 'instanceof' expression. +@field callAsConstructor The callback invoked when an object is used as a constructor in a 'new' expression. +@field convertToType The callback invoked when converting an object to a particular JavaScript type. +@discussion The staticValues and staticFunctions arrays are the simplest and most efficient means for vending custom properties. Statically declared properties autmatically service requests like getProperty, setProperty, and getPropertyNames. Property access callbacks are required only to implement unusual properties, like array indexes, whose names are not known at compile-time. + +If you named your getter function "GetX" and your setter function "SetX", you would declare a JSStaticValue array containing "X" like this: + +JSStaticValue StaticValueArray[] = { + { "X", GetX, SetX, kJSPropertyAttributeNone }, + { 0, 0, 0, 0 } +}; + +Standard JavaScript practice calls for storing function objects in prototypes, so they can be shared. The default JSClass created by JSClassCreate follows this idiom, instantiating objects with a shared, automatically generating prototype containing the class's function objects. The kJSClassAttributeNoAutomaticPrototype attribute specifies that a JSClass should not automatically generate such a prototype. The resulting JSClass instantiates objects with the default object prototype, and gives each instance object its own copy of the class's function objects. + +A NULL callback specifies that the default object callback should substitute, except in the case of hasProperty, where it specifies that getProperty should substitute. +*/ +typedef struct { + int version; /* current (and only) version is 0 */ + JSClassAttributes attributes; + + const char* className; + JSClassRef parentClass; + + const JSStaticValue* staticValues; + const JSStaticFunction* staticFunctions; + + JSObjectInitializeCallback initialize; + JSObjectFinalizeCallback finalize; + JSObjectHasPropertyCallback hasProperty; + JSObjectGetPropertyCallback getProperty; + JSObjectSetPropertyCallback setProperty; + JSObjectDeletePropertyCallback deleteProperty; + JSObjectGetPropertyNamesCallback getPropertyNames; + JSObjectCallAsFunctionCallback callAsFunction; + JSObjectCallAsConstructorCallback callAsConstructor; + JSObjectHasInstanceCallback hasInstance; + JSObjectConvertToTypeCallback convertToType; +} JSClassDefinition; + +/*! +@const kJSClassDefinitionEmpty +@abstract A JSClassDefinition structure of the current version, filled with NULL pointers and having no attributes. +@discussion Use this constant as a convenience when creating class definitions. For example, to create a class definition with only a finalize method: + +JSClassDefinition definition = kJSClassDefinitionEmpty; +definition.finalize = Finalize; +*/ +JS_EXPORT extern const JSClassDefinition kJSClassDefinitionEmpty; + +/*! +@function +@abstract Creates a JavaScript class suitable for use with JSObjectMake. +@param definition A JSClassDefinition that defines the class. +@result A JSClass with the given definition. Ownership follows the Create Rule. +*/ +JS_EXPORT JSClassRef JSClassCreate(const JSClassDefinition* definition); + +/*! +@function +@abstract Retains a JavaScript class. +@param jsClass The JSClass to retain. +@result A JSClass that is the same as jsClass. +*/ +JS_EXPORT JSClassRef JSClassRetain(JSClassRef jsClass); + +/*! +@function +@abstract Releases a JavaScript class. +@param jsClass The JSClass to release. +*/ +JS_EXPORT void JSClassRelease(JSClassRef jsClass); + +/*! +@function +@abstract Creates a JavaScript object. +@param ctx The execution context to use. +@param jsClass The JSClass to assign to the object. Pass NULL to use the default object class. +@param data A void* to set as the object's private data. Pass NULL to specify no private data. +@result A JSObject with the given class and private data. +@discussion The default object class does not allocate storage for private data, so you must provide a non-NULL jsClass to JSObjectMake if you want your object to be able to store private data. + +data is set on the created object before the intialize methods in its class chain are called. This enables the initialize methods to retrieve and manipulate data through JSObjectGetPrivate. +*/ +JS_EXPORT JSObjectRef JSObjectMake(JSContextRef ctx, JSClassRef jsClass, void* data); + +/*! +@function +@abstract Convenience method for creating a JavaScript function with a given callback as its implementation. +@param ctx The execution context to use. +@param name A JSString containing the function's name. This will be used when converting the function to string. Pass NULL to create an anonymous function. +@param callAsFunction The JSObjectCallAsFunctionCallback to invoke when the function is called. +@result A JSObject that is a function. The object's prototype will be the default function prototype. +*/ +JS_EXPORT JSObjectRef JSObjectMakeFunctionWithCallback(JSContextRef ctx, JSStringRef name, JSObjectCallAsFunctionCallback callAsFunction); + +/*! +@function +@abstract Convenience method for creating a JavaScript constructor. +@param ctx The execution context to use. +@param jsClass A JSClass that is the class your constructor will assign to the objects its constructs. jsClass will be used to set the constructor's .prototype property, and to evaluate 'instanceof' expressions. Pass NULL to use the default object class. +@param callAsConstructor A JSObjectCallAsConstructorCallback to invoke when your constructor is used in a 'new' expression. Pass NULL to use the default object constructor. +@result A JSObject that is a constructor. The object's prototype will be the default object prototype. +@discussion The default object constructor takes no arguments and constructs an object of class jsClass with no private data. +*/ +JS_EXPORT JSObjectRef JSObjectMakeConstructor(JSContextRef ctx, JSClassRef jsClass, JSObjectCallAsConstructorCallback callAsConstructor); + +/*! + @function + @abstract Creates a JavaScript Array object. + @param ctx The execution context to use. + @param argumentCount An integer count of the number of arguments in arguments. + @param arguments A JSValue array of data to populate the Array with. Pass NULL if argumentCount is 0. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSObject that is an Array. + @discussion The behavior of this function does not exactly match the behavior of the built-in Array constructor. Specifically, if one argument + is supplied, this function returns an array with one element. + */ +JS_EXPORT JSObjectRef JSObjectMakeArray(JSContextRef ctx, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + +/*! + @function + @abstract Creates a JavaScript Date object, as if by invoking the built-in Date constructor. + @param ctx The execution context to use. + @param argumentCount An integer count of the number of arguments in arguments. + @param arguments A JSValue array of arguments to pass to the Date Constructor. Pass NULL if argumentCount is 0. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSObject that is a Date. + */ +JS_EXPORT JSObjectRef JSObjectMakeDate(JSContextRef ctx, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + +/*! + @function + @abstract Creates a JavaScript Error object, as if by invoking the built-in Error constructor. + @param ctx The execution context to use. + @param argumentCount An integer count of the number of arguments in arguments. + @param arguments A JSValue array of arguments to pass to the Error Constructor. Pass NULL if argumentCount is 0. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSObject that is an Error. + */ +JS_EXPORT JSObjectRef JSObjectMakeError(JSContextRef ctx, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + +/*! + @function + @abstract Creates a JavaScript RegExp object, as if by invoking the built-in RegExp constructor. + @param ctx The execution context to use. + @param argumentCount An integer count of the number of arguments in arguments. + @param arguments A JSValue array of arguments to pass to the RegExp Constructor. Pass NULL if argumentCount is 0. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSObject that is a RegExp. + */ +JS_EXPORT JSObjectRef JSObjectMakeRegExp(JSContextRef ctx, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) JSC_API_AVAILABLE(macos(10.6), ios(7.0)); + +/*! + @function + @abstract Creates a JavaScript promise object by invoking the provided executor. + @param ctx The execution context to use. + @param resolve A pointer to a JSObjectRef in which to store the resolve function for the new promise. Pass NULL if you do not care to store the resolve callback. + @param reject A pointer to a JSObjectRef in which to store the reject function for the new promise. Pass NULL if you do not care to store the reject callback. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSObject that is a promise or NULL if an exception occurred. + */ +JS_EXPORT JSObjectRef JSObjectMakeDeferredPromise(JSContextRef ctx, JSObjectRef* resolve, JSObjectRef* reject, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! +@function +@abstract Creates a function with a given script as its body. +@param ctx The execution context to use. +@param name A JSString containing the function's name. This will be used when converting the function to string. Pass NULL to create an anonymous function. +@param parameterCount An integer count of the number of parameter names in parameterNames. +@param parameterNames A JSString array containing the names of the function's parameters. Pass NULL if parameterCount is 0. +@param body A JSString containing the script to use as the function's body. +@param sourceURL A JSString containing a URL for the script's source file. This is only used when reporting exceptions. Pass NULL if you do not care to include source file information in exceptions. +@param startingLineNumber An integer value specifying the script's starting line number in the file located at sourceURL. This is only used when reporting exceptions. The value is one-based, so the first line is line 1 and invalid values are clamped to 1. +@param exception A pointer to a JSValueRef in which to store a syntax error exception, if any. Pass NULL if you do not care to store a syntax error exception. +@result A JSObject that is a function, or NULL if either body or parameterNames contains a syntax error. The object's prototype will be the default function prototype. +@discussion Use this method when you want to execute a script repeatedly, to avoid the cost of re-parsing the script before each execution. +*/ +JS_EXPORT JSObjectRef JSObjectMakeFunction(JSContextRef ctx, JSStringRef name, unsigned parameterCount, const JSStringRef parameterNames[], JSStringRef body, JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception); + +/*! +@function +@abstract Gets an object's prototype. +@param ctx The execution context to use. +@param object A JSObject whose prototype you want to get. +@result A JSValue that is the object's prototype. +*/ +JS_EXPORT JSValueRef JSObjectGetPrototype(JSContextRef ctx, JSObjectRef object); + +/*! +@function +@abstract Sets an object's prototype. +@param ctx The execution context to use. +@param object The JSObject whose prototype you want to set. +@param value A JSValue to set as the object's prototype. +*/ +JS_EXPORT void JSObjectSetPrototype(JSContextRef ctx, JSObjectRef object, JSValueRef value); + +/*! +@function +@abstract Tests whether an object has a given property. +@param object The JSObject to test. +@param propertyName A JSString containing the property's name. +@result true if the object has a property whose name matches propertyName, otherwise false. +*/ +JS_EXPORT bool JSObjectHasProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName); + +/*! +@function +@abstract Gets a property from an object. +@param ctx The execution context to use. +@param object The JSObject whose property you want to get. +@param propertyName A JSString containing the property's name. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +@result The property's value if object has the property, otherwise the undefined value. +*/ +JS_EXPORT JSValueRef JSObjectGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception); + +/*! +@function +@abstract Sets a property on an object. +@param ctx The execution context to use. +@param object The JSObject whose property you want to set. +@param propertyName A JSString containing the property's name. +@param value A JSValueRef to use as the property's value. +@param attributes A logically ORed set of JSPropertyAttributes to give to the property. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +*/ +JS_EXPORT void JSObjectSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSPropertyAttributes attributes, JSValueRef* exception); + +/*! +@function +@abstract Deletes a property from an object. +@param ctx The execution context to use. +@param object The JSObject whose property you want to delete. +@param propertyName A JSString containing the property's name. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +@result true if the delete operation succeeds, otherwise false (for example, if the property has the kJSPropertyAttributeDontDelete attribute set). +*/ +JS_EXPORT bool JSObjectDeleteProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception); + +/*! + @function + @abstract Tests whether an object has a given property using a JSValueRef as the property key. + @param object The JSObject to test. + @param propertyKey A JSValueRef containing the property key to use when looking up the property. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result true if the object has a property whose name matches propertyKey, otherwise false. + @discussion This function is the same as performing "propertyKey in object" from JavaScript. + */ +JS_EXPORT bool JSObjectHasPropertyForKey(JSContextRef ctx, JSObjectRef object, JSValueRef propertyKey, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! + @function + @abstract Gets a property from an object using a JSValueRef as the property key. + @param ctx The execution context to use. + @param object The JSObject whose property you want to get. + @param propertyKey A JSValueRef containing the property key to use when looking up the property. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result The property's value if object has the property key, otherwise the undefined value. + @discussion This function is the same as performing "object[propertyKey]" from JavaScript. + */ +JS_EXPORT JSValueRef JSObjectGetPropertyForKey(JSContextRef ctx, JSObjectRef object, JSValueRef propertyKey, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! + @function + @abstract Sets a property on an object using a JSValueRef as the property key. + @param ctx The execution context to use. + @param object The JSObject whose property you want to set. + @param propertyKey A JSValueRef containing the property key to use when looking up the property. + @param value A JSValueRef to use as the property's value. + @param attributes A logically ORed set of JSPropertyAttributes to give to the property. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @discussion This function is the same as performing "object[propertyKey] = value" from JavaScript. + */ +JS_EXPORT void JSObjectSetPropertyForKey(JSContextRef ctx, JSObjectRef object, JSValueRef propertyKey, JSValueRef value, JSPropertyAttributes attributes, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! + @function + @abstract Deletes a property from an object using a JSValueRef as the property key. + @param ctx The execution context to use. + @param object The JSObject whose property you want to delete. + @param propertyKey A JSValueRef containing the property key to use when looking up the property. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result true if the delete operation succeeds, otherwise false (for example, if the property has the kJSPropertyAttributeDontDelete attribute set). + @discussion This function is the same as performing "delete object[propertyKey]" from JavaScript. + */ +JS_EXPORT bool JSObjectDeletePropertyForKey(JSContextRef ctx, JSObjectRef object, JSValueRef propertyKey, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! +@function +@abstract Gets a property from an object by numeric index. +@param ctx The execution context to use. +@param object The JSObject whose property you want to get. +@param propertyIndex An integer value that is the property's name. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +@result The property's value if object has the property, otherwise the undefined value. +@discussion Calling JSObjectGetPropertyAtIndex is equivalent to calling JSObjectGetProperty with a string containing propertyIndex, but JSObjectGetPropertyAtIndex provides optimized access to numeric properties. +*/ +JS_EXPORT JSValueRef JSObjectGetPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsigned propertyIndex, JSValueRef* exception); + +/*! +@function +@abstract Sets a property on an object by numeric index. +@param ctx The execution context to use. +@param object The JSObject whose property you want to set. +@param propertyIndex The property's name as a number. +@param value A JSValue to use as the property's value. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +@discussion Calling JSObjectSetPropertyAtIndex is equivalent to calling JSObjectSetProperty with a string containing propertyIndex, but JSObjectSetPropertyAtIndex provides optimized access to numeric properties. +*/ +JS_EXPORT void JSObjectSetPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsigned propertyIndex, JSValueRef value, JSValueRef* exception); + +/*! +@function +@abstract Gets an object's private data. +@param object A JSObject whose private data you want to get. +@result A void* that is the object's private data, if the object has private data, otherwise NULL. +*/ +JS_EXPORT void* JSObjectGetPrivate(JSObjectRef object); + +/*! +@function +@abstract Sets a pointer to private data on an object. +@param object The JSObject whose private data you want to set. +@param data A void* to set as the object's private data. +@result true if object can store private data, otherwise false. +@discussion The default object class does not allocate storage for private data. Only objects created with a non-NULL JSClass can store private data. +*/ +JS_EXPORT bool JSObjectSetPrivate(JSObjectRef object, void* data); + +/*! +@function +@abstract Tests whether an object can be called as a function. +@param ctx The execution context to use. +@param object The JSObject to test. +@result true if the object can be called as a function, otherwise false. +*/ +JS_EXPORT bool JSObjectIsFunction(JSContextRef ctx, JSObjectRef object); + +/*! +@function +@abstract Calls an object as a function. +@param ctx The execution context to use. +@param object The JSObject to call as a function. +@param thisObject The object to use as "this," or NULL to use the global object as "this." +@param argumentCount An integer count of the number of arguments in arguments. +@param arguments A JSValue array of arguments to pass to the function. Pass NULL if argumentCount is 0. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +@result The JSValue that results from calling object as a function, or NULL if an exception is thrown or object is not a function. +*/ +JS_EXPORT JSValueRef JSObjectCallAsFunction(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); + +/*! +@function +@abstract Tests whether an object can be called as a constructor. +@param ctx The execution context to use. +@param object The JSObject to test. +@result true if the object can be called as a constructor, otherwise false. +*/ +JS_EXPORT bool JSObjectIsConstructor(JSContextRef ctx, JSObjectRef object); + +/*! +@function +@abstract Calls an object as a constructor. +@param ctx The execution context to use. +@param object The JSObject to call as a constructor. +@param argumentCount An integer count of the number of arguments in arguments. +@param arguments A JSValue array of arguments to pass to the constructor. Pass NULL if argumentCount is 0. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +@result The JSObject that results from calling object as a constructor, or NULL if an exception is thrown or object is not a constructor. +*/ +JS_EXPORT JSObjectRef JSObjectCallAsConstructor(JSContextRef ctx, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); + +/*! +@function +@abstract Gets the names of an object's enumerable properties. +@param ctx The execution context to use. +@param object The object whose property names you want to get. +@result A JSPropertyNameArray containing the names object's enumerable properties. Ownership follows the Create Rule. +*/ +JS_EXPORT JSPropertyNameArrayRef JSObjectCopyPropertyNames(JSContextRef ctx, JSObjectRef object); + +/*! +@function +@abstract Retains a JavaScript property name array. +@param array The JSPropertyNameArray to retain. +@result A JSPropertyNameArray that is the same as array. +*/ +JS_EXPORT JSPropertyNameArrayRef JSPropertyNameArrayRetain(JSPropertyNameArrayRef array); + +/*! +@function +@abstract Releases a JavaScript property name array. +@param array The JSPropetyNameArray to release. +*/ +JS_EXPORT void JSPropertyNameArrayRelease(JSPropertyNameArrayRef array); + +/*! +@function +@abstract Gets a count of the number of items in a JavaScript property name array. +@param array The array from which to retrieve the count. +@result An integer count of the number of names in array. +*/ +JS_EXPORT size_t JSPropertyNameArrayGetCount(JSPropertyNameArrayRef array); + +/*! +@function +@abstract Gets a property name at a given index in a JavaScript property name array. +@param array The array from which to retrieve the property name. +@param index The index of the property name to retrieve. +@result A JSStringRef containing the property name. +*/ +JS_EXPORT JSStringRef JSPropertyNameArrayGetNameAtIndex(JSPropertyNameArrayRef array, size_t index); + +/*! +@function +@abstract Adds a property name to a JavaScript property name accumulator. +@param accumulator The accumulator object to which to add the property name. +@param propertyName The property name to add. +*/ +JS_EXPORT void JSPropertyNameAccumulatorAddName(JSPropertyNameAccumulatorRef accumulator, JSStringRef propertyName); + +#ifdef __cplusplus +} +#endif + +#endif /* JSObjectRef_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSObjectRefPrivate.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSObjectRefPrivate.h new file mode 100644 index 000000000..6e32612e3 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSObjectRefPrivate.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010-2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSObjectRefPrivate_h +#define JSObjectRefPrivate_h + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + @function + @abstract Sets a private property on an object. This private property cannot be accessed from within JavaScript. + @param ctx The execution context to use. + @param object The JSObject whose private property you want to set. + @param propertyName A JSString containing the property's name. + @param value A JSValue to use as the property's value. This may be NULL. + @result true if object can store private data, otherwise false. + @discussion This API allows you to store JS values directly an object in a way that will be ensure that they are kept alive without exposing them to JavaScript code and without introducing the reference cycles that may occur when using JSValueProtect. + + The default object class does not allocate storage for private data. Only objects created with a non-NULL JSClass can store private properties. + */ +JS_EXPORT bool JSObjectSetPrivateProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value); + +/*! + @function + @abstract Gets a private property from an object. + @param ctx The execution context to use. + @param object The JSObject whose private property you want to get. + @param propertyName A JSString containing the property's name. + @result The property's value if object has the property, otherwise NULL. + */ +JS_EXPORT JSValueRef JSObjectGetPrivateProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName); + +/*! + @function + @abstract Deletes a private property from an object. + @param ctx The execution context to use. + @param object The JSObject whose private property you want to delete. + @param propertyName A JSString containing the property's name. + @result true if object can store private data, otherwise false. + @discussion The default object class does not allocate storage for private data. Only objects created with a non-NULL JSClass can store private data. + */ +JS_EXPORT bool JSObjectDeletePrivateProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName); + +JS_EXPORT JSObjectRef JSObjectGetProxyTarget(JSObjectRef); + +JS_EXPORT JSGlobalContextRef JSObjectGetGlobalContext(JSObjectRef object); + +#ifdef __cplusplus +} +#endif + +#endif // JSObjectRefPrivate_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSRemoteInspector.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSRemoteInspector.h new file mode 100644 index 000000000..85768e4d6 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSRemoteInspector.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSRemoteInspector_h +#define JSRemoteInspector_h + +#include +#include + +#if defined(WIN32) || defined(_WIN32) +typedef int JSProcessID; +#else +#include +typedef pid_t JSProcessID; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +JS_EXPORT void JSRemoteInspectorDisableAutoStart(void) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); +JS_EXPORT void JSRemoteInspectorStart(void) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); +JS_EXPORT void JSRemoteInspectorSetParentProcessInformation(JSProcessID, const uint8_t* auditData, size_t auditLength) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); + +JS_EXPORT void JSRemoteInspectorSetLogToSystemConsole(bool) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); + +JS_EXPORT bool JSRemoteInspectorGetInspectionEnabledByDefault(void) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); +JS_EXPORT void JSRemoteInspectorSetInspectionEnabledByDefault(bool) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); + +#ifdef __cplusplus +} +#endif + +#endif /* JSRemoteInspector_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSRetainPtr.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSRetainPtr.h new file mode 100644 index 000000000..fd8412f3e --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSRetainPtr.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2005-2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include + +inline void JSRetain(JSStringRef string) { JSStringRetain(string); } +inline void JSRelease(JSStringRef string) { JSStringRelease(string); } +inline void JSRetain(JSGlobalContextRef context) { JSGlobalContextRetain(context); } +inline void JSRelease(JSGlobalContextRef context) { JSGlobalContextRelease(context); } + +enum AdoptTag { Adopt }; + +template class JSRetainPtr { +public: + JSRetainPtr() = default; + JSRetainPtr(T ptr) : m_ptr(ptr) { if (ptr) JSRetain(ptr); } + JSRetainPtr(const JSRetainPtr&); + JSRetainPtr(JSRetainPtr&&); + ~JSRetainPtr(); + + T get() const { return m_ptr; } + + void clear(); + T leakRef() WARN_UNUSED_RETURN; + + T operator->() const { return m_ptr; } + + bool operator!() const { return !m_ptr; } + explicit operator bool() const { return m_ptr; } + + JSRetainPtr& operator=(const JSRetainPtr&); + JSRetainPtr& operator=(JSRetainPtr&&); + JSRetainPtr& operator=(T); + + void swap(JSRetainPtr&); + + friend JSRetainPtr adopt(JSStringRef); + friend JSRetainPtr adopt(JSGlobalContextRef); + + // FIXME: Make this private once Apple's internal code is updated to not rely on it. + // https://bugs.webkit.org/show_bug.cgi?id=189644 + JSRetainPtr(AdoptTag, T); + +private: + T m_ptr { nullptr }; +}; + +JSRetainPtr adopt(JSStringRef); +JSRetainPtr adopt(JSGlobalContextRef); + +template inline JSRetainPtr::JSRetainPtr(AdoptTag, T ptr) + : m_ptr(ptr) +{ +} + +inline JSRetainPtr adopt(JSStringRef o) +{ + return JSRetainPtr(Adopt, o); +} + +inline JSRetainPtr adopt(JSGlobalContextRef o) +{ + return JSRetainPtr(Adopt, o); +} + +template inline JSRetainPtr::JSRetainPtr(const JSRetainPtr& o) + : m_ptr(o.m_ptr) +{ + if (m_ptr) + JSRetain(m_ptr); +} + +template inline JSRetainPtr::JSRetainPtr(JSRetainPtr&& o) + : m_ptr(o.leakRef()) +{ +} + +template inline JSRetainPtr::~JSRetainPtr() +{ + if (m_ptr) + JSRelease(m_ptr); +} + +template inline void JSRetainPtr::clear() +{ + if (T ptr = leakRef()) + JSRelease(ptr); +} + +template inline T JSRetainPtr::leakRef() +{ + return std::exchange(m_ptr, nullptr); +} + +template inline JSRetainPtr& JSRetainPtr::operator=(const JSRetainPtr& o) +{ + return operator=(o.get()); +} + +template inline JSRetainPtr& JSRetainPtr::operator=(JSRetainPtr&& o) +{ + if (T ptr = std::exchange(m_ptr, o.leakRef())) + JSRelease(ptr); + return *this; +} + +template inline JSRetainPtr& JSRetainPtr::operator=(T optr) +{ + if (optr) + JSRetain(optr); + if (T ptr = std::exchange(m_ptr, optr)) + JSRelease(ptr); + return *this; +} + +template inline void JSRetainPtr::swap(JSRetainPtr& o) +{ + std::swap(m_ptr, o.m_ptr); +} + +template inline void swap(JSRetainPtr& a, JSRetainPtr& b) +{ + a.swap(b); +} + +template inline bool operator==(const JSRetainPtr& a, const JSRetainPtr& b) +{ + return a.get() == b.get(); +} + +template inline bool operator==(const JSRetainPtr& a, U* b) +{ + return a.get() == b; +} + +template inline bool operator==(T* a, const JSRetainPtr& b) +{ + return a == b.get(); +} + +template inline bool operator!=(const JSRetainPtr& a, const JSRetainPtr& b) +{ + return a.get() != b.get(); +} + +template inline bool operator!=(const JSRetainPtr& a, U* b) +{ + return a.get() != b; +} + +template inline bool operator!=(T* a, const JSRetainPtr& b) +{ + return a != b.get(); +} diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSScript.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSScript.h new file mode 100644 index 000000000..12ccb0593 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSScript.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#if JSC_OBJC_API_ENABLED + +NS_ASSUME_NONNULL_BEGIN + +@class JSVirtualMachine; + +/*! + @enum JSScriptType + @abstract A constant identifying the execution type of a JSScript. + @constant kJSScriptTypeProgram The type of a normal JavaScript program. + @constant kJSScriptTypeModule The type of a module JavaScript program. + */ +typedef NS_ENUM(NSInteger, JSScriptType) { + kJSScriptTypeProgram, + kJSScriptTypeModule, +}; + + +JSC_CLASS_AVAILABLE(macos(10.15), ios(13.0)) +@interface JSScript : NSObject + +/*! + @method + @abstract Create a JSScript for the specified virtual machine. + @param type The type of JavaScript source. + @param source The source code to use when the script is evaluated by the JS vm. + @param sourceURL The source URL to associate with this script. For modules, this is the module identifier. + @param cachePath A URL containing the path where the VM should cache for future execution. On creation, we use this path to load the cached bytecode off disk. If the cached bytecode at this location is stale, you should delete that file before calling this constructor. + @param vm The JSVirtualMachine the script can be evaluated in. + @param error A description of why the script could not be created if the result is nil. + @result The new script. + @discussion The file at cachePath should not be externally modified for the lifecycle of vm. + */ ++ (nullable instancetype)scriptOfType:(JSScriptType)type withSource:(NSString *)source andSourceURL:(NSURL *)sourceURL andBytecodeCache:(nullable NSURL *)cachePath inVirtualMachine:(JSVirtualMachine *)vm error:(out NSError * _Nullable * _Nullable)error; + +/*! + @method + @abstract Create a JSScript for the specified virtual machine with a path to a codesigning and bytecode caching. + @param type The type of JavaScript source. + @param filePath A URL containing the path to a JS source code file on disk. + @param sourceURL The source URL to associate with this script. For modules, this is the module identifier. + @param cachePath A URL containing the path where the VM should cache for future execution. On creation, we use this path to load the cached bytecode off disk. If the cached bytecode at this location is stale, you should delete that file before calling this constructor. + @param vm The JSVirtualMachine the script can be evaluated in. + @param error A description of why the script could not be created if the result is nil. + @result The new script. + @discussion The files at filePath and cachePath should not be externally modified for the lifecycle of vm. This method will file back the memory for the source. + + If the file at filePath is not ascii this method will return nil. + */ ++ (nullable instancetype)scriptOfType:(JSScriptType)type memoryMappedFromASCIIFile:(NSURL *)filePath withSourceURL:(NSURL *)sourceURL andBytecodeCache:(nullable NSURL *)cachePath inVirtualMachine:(JSVirtualMachine *)vm error:(out NSError * _Nullable * _Nullable)error; + +/*! + @method + @abstract Cache the bytecode for this JSScript to disk at the path passed in during creation. + @param error A description of why the script could not be cached if the result is FALSE. + */ +- (BOOL)cacheBytecodeWithError:(out NSError * _Nullable * _Nullable)error; + +/*! + @method + @abstract Returns true when evaluating this JSScript will use the bytecode cache. Returns false otherwise. + */ +- (BOOL)isUsingBytecodeCache; + +/*! + @method + @abstract Returns the JSScriptType of this JSScript. + */ +- (JSScriptType)type; + +/*! + @method + @abstract Returns the sourceURL of this JSScript. + */ +- (NSURL *)sourceURL; + +@end + +NS_ASSUME_NONNULL_END + +#endif // JSC_OBJC_API_ENABLED diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSScriptInternal.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSScriptInternal.h new file mode 100644 index 000000000..4a9427d28 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSScriptInternal.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#import "JSScript.h" +#import "SourceCode.h" +#import + +#if JSC_OBJC_API_ENABLED + +NS_ASSUME_NONNULL_BEGIN + +namespace JSC { +class CachedBytecode; +class Identifier; +class JSSourceCode; +}; + +namespace WTF { +class String; +}; + +@interface JSScript(Internal) + +- (instancetype)init; +- (unsigned)hash; +- (const WTF::String&)source; +- (RefPtr)cachedBytecode; +- (JSC::JSSourceCode*)jsSourceCode; +- (JSC::SourceCode)sourceCode; +- (BOOL)writeCache:(String&)error; + +@end + +NS_ASSUME_NONNULL_END + +#endif // JSC_OBJC_API_ENABLED diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSScriptRefPrivate.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSScriptRefPrivate.h new file mode 100644 index 000000000..e6224ce37 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSScriptRefPrivate.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSScriptRefPrivate_h +#define JSScriptRefPrivate_h + +#include +#include +#include + +/*! @typedef JSScriptRef A JavaScript script reference. */ +typedef struct OpaqueJSScript* JSScriptRef; + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + @function + @abstract Creates a script reference from an ascii string, without copying or taking ownership of the string + @param contextGroup The context group the script is to be used in. + @param url The source url to be reported in errors and exceptions. + @param startingLineNumber An integer value specifying the script's starting line number in the file located at sourceURL. This is only used when reporting exceptions. The value is one-based, so the first line is line 1 and invalid values are clamped to 1. + @param source The source string. This is required to be pure ASCII and to never be deallocated. + @param length The length of the source string. + @param errorMessage A pointer to a JSStringRef in which to store the parse error message if the source is not valid. Pass NULL if you do not care to store an error message. + @param errorLine A pointer to an int in which to store the line number of a parser error. Pass NULL if you do not care to store an error line. + @result A JSScriptRef for the provided source, or NULL if any non-ASCII character is found in source or if the source is not a valid JavaScript program. Ownership follows the Create Rule. + @discussion Use this function to create a reusable script reference with a constant + buffer as the backing string. The source string must outlive the global context. + */ +JS_EXPORT JSScriptRef JSScriptCreateReferencingImmortalASCIIText(JSContextGroupRef contextGroup, JSStringRef url, int startingLineNumber, const char* source, size_t length, JSStringRef* errorMessage, int* errorLine); + +/*! + @function + @abstract Creates a script reference from a string + @param contextGroup The context group the script is to be used in. + @param url The source url to be reported in errors and exceptions. + @param startingLineNumber An integer value specifying the script's starting line number in the file located at sourceURL. This is only used when reporting exceptions. The value is one-based, so the first line is line 1 and invalid values are clamped to 1. + @param source The source string. + @param errorMessage A pointer to a JSStringRef in which to store the parse error message if the source is not valid. Pass NULL if you do not care to store an error message. + @param errorLine A pointer to an int in which to store the line number of a parser error. Pass NULL if you do not care to store an error line. + @result A JSScriptRef for the provided source, or NULL is the source is not a valid JavaScript program. Ownership follows the Create Rule. + */ +JS_EXPORT JSScriptRef JSScriptCreateFromString(JSContextGroupRef contextGroup, JSStringRef url, int startingLineNumber, JSStringRef source, JSStringRef* errorMessage, int* errorLine); + +/*! + @function + @abstract Retains a JavaScript script. + @param script The script to retain. + */ +JS_EXPORT void JSScriptRetain(JSScriptRef script); + +/*! + @function + @abstract Releases a JavaScript script. + @param script The script to release. + */ +JS_EXPORT void JSScriptRelease(JSScriptRef script); + +/*! + @function + @abstract Evaluates a JavaScript script. + @param ctx The execution context to use. + @param script The JSScript to evaluate. + @param thisValue The value to use as "this" when evaluating the script. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result The JSValue that results from evaluating script, or NULL if an exception is thrown. + */ +JS_EXPORT JSValueRef JSScriptEvaluate(JSContextRef ctx, JSScriptRef script, JSValueRef thisValue, JSValueRef* exception); + + +#ifdef __cplusplus +} +#endif + +#endif /* JSScriptRefPrivate_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSScriptSourceProvider.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSScriptSourceProvider.h new file mode 100644 index 000000000..09e4018dd --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSScriptSourceProvider.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if JSC_OBJC_API_ENABLED + +#import "SourceProvider.h" + +@class JSScript; + +class JSScriptSourceProvider : public JSC::SourceProvider { +public: + template + static Ref create(JSScript *script, Args&&... args) + { + return adoptRef(*new JSScriptSourceProvider(script, std::forward(args)...)); + } + + unsigned hash() const override; + StringView source() const override; + RefPtr cachedBytecode() const override; + +private: + template + JSScriptSourceProvider(JSScript *script, Args&&... args) + : SourceProvider(std::forward(args)...) + , m_script(script) + { } + + RetainPtr m_script; +}; + +#endif // JSC_OBJC_API_ENABLED diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSStringRef.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSStringRef.h new file mode 100644 index 000000000..bc03ed70d --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSStringRef.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2006 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSStringRef_h +#define JSStringRef_h + +#include + +#ifndef __cplusplus +#include +#endif +#include /* for size_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(_NATIVE_WCHAR_T_DEFINED) /* MSVC */ \ + && (!defined(__WCHAR_MAX__) || (__WCHAR_MAX__ > 0xffffU)) /* ISO C/C++ */ \ + && (!defined(WCHAR_MAX) || (WCHAR_MAX > 0xffffU)) /* RVCT */ +/*! +@typedef JSChar +@abstract A UTF-16 code unit. One, or a sequence of two, can encode any Unicode + character. As with all scalar types, endianness depends on the underlying + architecture. +*/ + typedef unsigned short JSChar; +#else + typedef wchar_t JSChar; +#endif + +/*! +@function +@abstract Creates a JavaScript string from a buffer of Unicode characters. +@param chars The buffer of Unicode characters to copy into the new JSString. +@param numChars The number of characters to copy from the buffer pointed to by chars. +@result A JSString containing chars. Ownership follows the Create Rule. +*/ +JS_EXPORT JSStringRef JSStringCreateWithCharacters(const JSChar* chars, size_t numChars); +/*! +@function +@abstract Creates a JavaScript string from a null-terminated UTF8 string. +@param string The null-terminated UTF8 string to copy into the new JSString. +@result A JSString containing string. Ownership follows the Create Rule. +*/ +JS_EXPORT JSStringRef JSStringCreateWithUTF8CString(const char* string); + +/*! +@function +@abstract Retains a JavaScript string. +@param string The JSString to retain. +@result A JSString that is the same as string. +*/ +JS_EXPORT JSStringRef JSStringRetain(JSStringRef string); +/*! +@function +@abstract Releases a JavaScript string. +@param string The JSString to release. +*/ +JS_EXPORT void JSStringRelease(JSStringRef string); + +/*! +@function +@abstract Returns the number of Unicode characters in a JavaScript string. +@param string The JSString whose length (in Unicode characters) you want to know. +@result The number of Unicode characters stored in string. +*/ +JS_EXPORT size_t JSStringGetLength(JSStringRef string); +/*! +@function +@abstract Returns a pointer to the Unicode character buffer that + serves as the backing store for a JavaScript string. +@param string The JSString whose backing store you want to access. +@result A pointer to the Unicode character buffer that serves as string's + backing store, which will be deallocated when string is deallocated. +*/ +JS_EXPORT const JSChar* JSStringGetCharactersPtr(JSStringRef string); + +/*! +@function +@abstract Returns the maximum number of bytes a JavaScript string will + take up if converted into a null-terminated UTF8 string. +@param string The JSString whose maximum converted size (in bytes) you + want to know. +@result The maximum number of bytes that could be required to convert string into a + null-terminated UTF8 string. The number of bytes that the conversion actually ends + up requiring could be less than this, but never more. +*/ +JS_EXPORT size_t JSStringGetMaximumUTF8CStringSize(JSStringRef string); +/*! +@function +@abstract Converts a JavaScript string into a null-terminated UTF8 string, + and copies the result into an external byte buffer. +@param string The source JSString. +@param buffer The destination byte buffer into which to copy a null-terminated + UTF8 representation of string. On return, buffer contains a UTF8 string + representation of string. If bufferSize is too small, buffer will contain only + partial results. If buffer is not at least bufferSize bytes in size, + behavior is undefined. +@param bufferSize The size of the external buffer in bytes. +@result The number of bytes written into buffer (including the null-terminator byte). +*/ +JS_EXPORT size_t JSStringGetUTF8CString(JSStringRef string, char* buffer, size_t bufferSize); + +/*! +@function +@abstract Tests whether two JavaScript strings match. +@param a The first JSString to test. +@param b The second JSString to test. +@result true if the two strings match, otherwise false. +*/ +JS_EXPORT bool JSStringIsEqual(JSStringRef a, JSStringRef b); +/*! +@function +@abstract Tests whether a JavaScript string matches a null-terminated UTF8 string. +@param a The JSString to test. +@param b The null-terminated UTF8 string to test. +@result true if the two strings match, otherwise false. +*/ +JS_EXPORT bool JSStringIsEqualToUTF8CString(JSStringRef a, const char* b); + +#ifdef __cplusplus +} +#endif + +#endif /* JSStringRef_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSStringRefBSTR.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSStringRefBSTR.h new file mode 100644 index 000000000..066c68d53 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSStringRefBSTR.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSStringRefBSTR_h +#define JSStringRefBSTR_h + +#include "JSBase.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* COM convenience methods */ + +/*! +@function +@abstract Creates a JavaScript string from a BSTR. +@param string The BSTR to copy into the new JSString. +@result A JSString containing string. Ownership follows the Create Rule. +*/ +JS_EXPORT JSStringRef JSStringCreateWithBSTR(const BSTR string); + +/*! +@function +@abstract Creates a BSTR from a JavaScript string. +@param string The JSString to copy into the new BSTR. +@result A BSTR containing string. Ownership follows the Create Rule. +*/ +JS_EXPORT BSTR JSStringCopyBSTR(const JSStringRef string); + +#ifdef __cplusplus +} +#endif + +#endif /* JSStringRefBSTR_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSStringRefCF.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSStringRefCF.h new file mode 100644 index 000000000..1e210c7a9 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSStringRefCF.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSStringRefCF_h +#define JSStringRefCF_h + +#include "JSBase.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* CFString convenience methods */ + +/*! +@function +@abstract Creates a JavaScript string from a CFString. +@discussion This function is optimized to take advantage of cases when + CFStringGetCharactersPtr returns a valid pointer. +@param string The CFString to copy into the new JSString. +@result A JSString containing string. Ownership follows the Create Rule. +*/ +JS_EXPORT JSStringRef JSStringCreateWithCFString(CFStringRef string); +/*! +@function +@abstract Creates a CFString from a JavaScript string. +@param alloc The alloc parameter to pass to CFStringCreate. +@param string The JSString to copy into the new CFString. +@result A CFString containing string. Ownership follows the Create Rule. +*/ +JS_EXPORT CFStringRef JSStringCopyCFString(CFAllocatorRef alloc, JSStringRef string) CF_RETURNS_RETAINED; + +#ifdef __cplusplus +} +#endif + +#endif /* JSStringRefCF_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSStringRefPrivate.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSStringRefPrivate.h new file mode 100644 index 000000000..f1db806ee --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSStringRefPrivate.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSStringRefPrivate_h +#define JSStringRefPrivate_h + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +JS_EXPORT JSStringRef JSStringCreateWithCharactersNoCopy(const JSChar* chars, size_t numChars); + +#ifdef __cplusplus +} +#endif + +#endif /* JSStringRefPrivate_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSTypedArray.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSTypedArray.h new file mode 100644 index 000000000..7eaf76c5e --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSTypedArray.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2015 Dominic Szablewski (dominic@phoboslab.org) + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSTypedArray_h +#define JSTypedArray_h + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// ------------- Typed Array functions -------------- + +/*! + @function + @abstract Creates a JavaScript Typed Array object with the given number of elements. + @param ctx The execution context to use. + @param arrayType A value identifying the type of array to create. If arrayType is kJSTypedArrayTypeNone or kJSTypedArrayTypeArrayBuffer then NULL will be returned. + @param length The number of elements to be in the new Typed Array. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSObjectRef that is a Typed Array with all elements set to zero or NULL if there was an error. + */ +JS_EXPORT JSObjectRef JSObjectMakeTypedArray(JSContextRef ctx, JSTypedArrayType arrayType, size_t length, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +/*! + @function + @abstract Creates a JavaScript Typed Array object from an existing pointer. + @param ctx The execution context to use. + @param arrayType A value identifying the type of array to create. If arrayType is kJSTypedArrayTypeNone or kJSTypedArrayTypeArrayBuffer then NULL will be returned. + @param bytes A pointer to the byte buffer to be used as the backing store of the Typed Array object. + @param byteLength The number of bytes pointed to by the parameter bytes. + @param bytesDeallocator The allocator to use to deallocate the external buffer when the JSTypedArrayData object is deallocated. + @param deallocatorContext A pointer to pass back to the deallocator. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSObjectRef Typed Array whose backing store is the same as the one pointed to by bytes or NULL if there was an error. + @discussion If an exception is thrown during this function the bytesDeallocator will always be called. + */ +JS_EXPORT JSObjectRef JSObjectMakeTypedArrayWithBytesNoCopy(JSContextRef ctx, JSTypedArrayType arrayType, void* bytes, size_t byteLength, JSTypedArrayBytesDeallocator bytesDeallocator, void* deallocatorContext, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +/*! + @function + @abstract Creates a JavaScript Typed Array object from an existing JavaScript Array Buffer object. + @param ctx The execution context to use. + @param arrayType A value identifying the type of array to create. If arrayType is kJSTypedArrayTypeNone or kJSTypedArrayTypeArrayBuffer then NULL will be returned. + @param buffer An Array Buffer object that should be used as the backing store for the created JavaScript Typed Array object. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSObjectRef that is a Typed Array or NULL if there was an error. The backing store of the Typed Array will be buffer. + */ +JS_EXPORT JSObjectRef JSObjectMakeTypedArrayWithArrayBuffer(JSContextRef ctx, JSTypedArrayType arrayType, JSObjectRef buffer, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +/*! + @function + @abstract Creates a JavaScript Typed Array object from an existing JavaScript Array Buffer object with the given offset and length. + @param ctx The execution context to use. + @param arrayType A value identifying the type of array to create. If arrayType is kJSTypedArrayTypeNone or kJSTypedArrayTypeArrayBuffer then NULL will be returned. + @param buffer An Array Buffer object that should be used as the backing store for the created JavaScript Typed Array object. + @param byteOffset The byte offset for the created Typed Array. byteOffset should aligned with the element size of arrayType. + @param length The number of elements to include in the Typed Array. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSObjectRef that is a Typed Array or NULL if there was an error. The backing store of the Typed Array will be buffer. + */ +JS_EXPORT JSObjectRef JSObjectMakeTypedArrayWithArrayBufferAndOffset(JSContextRef ctx, JSTypedArrayType arrayType, JSObjectRef buffer, size_t byteOffset, size_t length, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +/*! + @function + @abstract Returns a temporary pointer to the backing store of a JavaScript Typed Array object. + @param ctx The execution context to use. + @param object The Typed Array object whose backing store pointer to return. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A pointer to the raw data buffer that serves as object's backing store or NULL if object is not a Typed Array object. + @discussion The pointer returned by this function is temporary and is not guaranteed to remain valid across JavaScriptCore API calls. + */ +JS_EXPORT void* JSObjectGetTypedArrayBytesPtr(JSContextRef ctx, JSObjectRef object, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +/*! + @function + @abstract Returns the length of a JavaScript Typed Array object. + @param ctx The execution context to use. + @param object The Typed Array object whose length to return. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result The length of the Typed Array object or 0 if the object is not a Typed Array object. + */ +JS_EXPORT size_t JSObjectGetTypedArrayLength(JSContextRef ctx, JSObjectRef object, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +/*! + @function + @abstract Returns the byte length of a JavaScript Typed Array object. + @param ctx The execution context to use. + @param object The Typed Array object whose byte length to return. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result The byte length of the Typed Array object or 0 if the object is not a Typed Array object. + */ +JS_EXPORT size_t JSObjectGetTypedArrayByteLength(JSContextRef ctx, JSObjectRef object, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +/*! + @function + @abstract Returns the byte offset of a JavaScript Typed Array object. + @param ctx The execution context to use. + @param object The Typed Array object whose byte offset to return. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result The byte offset of the Typed Array object or 0 if the object is not a Typed Array object. + */ +JS_EXPORT size_t JSObjectGetTypedArrayByteOffset(JSContextRef ctx, JSObjectRef object, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +/*! + @function + @abstract Returns the JavaScript Array Buffer object that is used as the backing of a JavaScript Typed Array object. + @param ctx The execution context to use. + @param object The JSObjectRef whose Typed Array type data pointer to obtain. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSObjectRef with a JSTypedArrayType of kJSTypedArrayTypeArrayBuffer or NULL if object is not a Typed Array. + */ +JS_EXPORT JSObjectRef JSObjectGetTypedArrayBuffer(JSContextRef ctx, JSObjectRef object, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +// ------------- Array Buffer functions ------------- + +/*! + @function + @abstract Creates a JavaScript Array Buffer object from an existing pointer. + @param ctx The execution context to use. + @param bytes A pointer to the byte buffer to be used as the backing store of the Typed Array object. + @param byteLength The number of bytes pointed to by the parameter bytes. + @param bytesDeallocator The allocator to use to deallocate the external buffer when the Typed Array data object is deallocated. + @param deallocatorContext A pointer to pass back to the deallocator. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSObjectRef Array Buffer whose backing store is the same as the one pointed to by bytes or NULL if there was an error. + @discussion If an exception is thrown during this function the bytesDeallocator will always be called. + */ +JS_EXPORT JSObjectRef JSObjectMakeArrayBufferWithBytesNoCopy(JSContextRef ctx, void* bytes, size_t byteLength, JSTypedArrayBytesDeallocator bytesDeallocator, void* deallocatorContext, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +/*! + @function + @abstract Returns a pointer to the data buffer that serves as the backing store for a JavaScript Typed Array object. + @param object The Array Buffer object whose internal backing store pointer to return. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A pointer to the raw data buffer that serves as object's backing store or NULL if object is not an Array Buffer object. + @discussion The pointer returned by this function is temporary and is not guaranteed to remain valid across JavaScriptCore API calls. + */ +JS_EXPORT void* JSObjectGetArrayBufferBytesPtr(JSContextRef ctx, JSObjectRef object, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +/*! + @function + @abstract Returns the number of bytes in a JavaScript data object. + @param ctx The execution context to use. + @param object The JS Arary Buffer object whose length in bytes to return. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result The number of bytes stored in the data object. + */ +JS_EXPORT size_t JSObjectGetArrayBufferByteLength(JSContextRef ctx, JSObjectRef object, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +#ifdef __cplusplus +} +#endif + +#endif /* JSTypedArray_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSValue.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSValue.h new file mode 100644 index 000000000..1b5845e9b --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSValue.h @@ -0,0 +1,730 @@ +/* + * Copyright (C) 2013-2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSValue_h +#define JSValue_h + +#if JSC_OBJC_API_ENABLED + +#import + +@class JSContext; + +/*! +@interface +@discussion A JSValue is a reference to a JavaScript value. Every JSValue + originates from a JSContext and holds a strong reference to it. + When a JSValue instance method creates a new JSValue, the new value + originates from the same JSContext. + + All JSValues values also originate from a JSVirtualMachine + (available indirectly via the context property). It is an error to pass a + JSValue to a method or property of a JSValue or JSContext originating from a + different JSVirtualMachine. Doing so will raise an Objective-C exception. +*/ +NS_CLASS_AVAILABLE(10_9, 7_0) +@interface JSValue : NSObject + +/*! +@property +@abstract The JSContext that this value originates from. +*/ +@property (readonly, strong) JSContext *context; + +/*! +@methodgroup Creating JavaScript Values +*/ +/*! +@method +@abstract Create a JSValue by converting an Objective-C object. +@discussion The resulting JSValue retains the provided Objective-C object. +@param value The Objective-C object to be converted. +@result The new JSValue. +*/ ++ (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context; + +/*! +@method +@abstract Create a JavaScript value from a BOOL primitive. +@param context The JSContext in which the resulting JSValue will be created. +@result The new JSValue representing the equivalent boolean value. +*/ ++ (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context; + +/*! +@method +@abstract Create a JavaScript value from a double primitive. +@param context The JSContext in which the resulting JSValue will be created. +@result The new JSValue representing the equivalent boolean value. +*/ ++ (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context; + +/*! +@method +@abstract Create a JavaScript value from an int32_t primitive. +@param context The JSContext in which the resulting JSValue will be created. +@result The new JSValue representing the equivalent boolean value. +*/ ++ (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context; + +/*! +@method +@abstract Create a JavaScript value from a uint32_t primitive. +@param context The JSContext in which the resulting JSValue will be created. +@result The new JSValue representing the equivalent boolean value. +*/ ++ (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context; + +/*! +@method +@abstract Create a new, empty JavaScript object. +@param context The JSContext in which the resulting object will be created. +@result The new JavaScript object. +*/ ++ (JSValue *)valueWithNewObjectInContext:(JSContext *)context; + +/*! +@method +@abstract Create a new, empty JavaScript array. +@param context The JSContext in which the resulting array will be created. +@result The new JavaScript array. +*/ ++ (JSValue *)valueWithNewArrayInContext:(JSContext *)context; + +/*! +@method +@abstract Create a new JavaScript regular expression object. +@param pattern The regular expression pattern. +@param flags The regular expression flags. +@param context The JSContext in which the resulting regular expression object will be created. +@result The new JavaScript regular expression object. +*/ ++ (JSValue *)valueWithNewRegularExpressionFromPattern:(NSString *)pattern flags:(NSString *)flags inContext:(JSContext *)context; + +/*! +@method +@abstract Create a new JavaScript error object. +@param message The error message. +@param context The JSContext in which the resulting error object will be created. +@result The new JavaScript error object. +*/ ++ (JSValue *)valueWithNewErrorFromMessage:(NSString *)message inContext:(JSContext *)context; + +/*! +@method +@abstract Create a new promise object using the provided executor callback. +@param callback A callback block invoked while the promise object is being initialized. The resolve and reject parameters are functions that can be called to notify any pending reactions about the state of the new promise object. +@param context The JSContext to which the resulting JSValue belongs. +@result The JSValue representing a new promise JavaScript object. +@discussion This method is equivalent to calling the Promise constructor in JavaScript. the resolve and reject callbacks each normally take a single value, which they forward to all relevent pending reactions. While inside the executor callback context will act as if it were in any other callback, except calleeFunction will be nil. This also means means the new promise object may be accessed via [context thisValue]. +*/ ++ (JSValue *)valueWithNewPromiseInContext:(JSContext *)context fromExecutor:(void (^)(JSValue *resolve, JSValue *reject))callback JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! +@method +@abstract Create a new resolved promise object with the provided value. +@param result The result value to be passed to any reactions. +@param context The JSContext to which the resulting JSValue belongs. +@result The JSValue representing a new promise JavaScript object. +@discussion This method is equivalent to calling [JSValue valueWithNewPromiseFromExecutor:^(JSValue *resolve, JSValue *reject) { [resolve callWithArguments:@[result]]; } inContext:context] +*/ ++ (JSValue *)valueWithNewPromiseResolvedWithResult:(id)result inContext:(JSContext *)context JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! +@method +@abstract Create a new rejected promise object with the provided value. +@param reason The result value to be passed to any reactions. +@param context The JSContext to which the resulting JSValue belongs. +@result The JSValue representing a new promise JavaScript object. +@discussion This method is equivalent to calling [JSValue valueWithNewPromiseFromExecutor:^(JSValue *resolve, JSValue *reject) { [reject callWithArguments:@[reason]]; } inContext:context] +*/ ++ (JSValue *)valueWithNewPromiseRejectedWithReason:(id)reason inContext:(JSContext *)context JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! +@method +@abstract Create a new, unique, symbol object. +@param description The description of the symbol object being created. +@param context The JSContext to which the resulting JSValue belongs. +@result The JSValue representing a unique JavaScript value with type symbol. +*/ ++ (JSValue *)valueWithNewSymbolFromDescription:(NSString *)description inContext:(JSContext *)context JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! +@method +@abstract Create the JavaScript value null. +@param context The JSContext to which the resulting JSValue belongs. +@result The JSValue representing the JavaScript value null. +*/ ++ (JSValue *)valueWithNullInContext:(JSContext *)context; + +/*! +@method +@abstract Create the JavaScript value undefined. +@param context The JSContext to which the resulting JSValue belongs. +@result The JSValue representing the JavaScript value undefined. +*/ ++ (JSValue *)valueWithUndefinedInContext:(JSContext *)context; + +/*! +@methodgroup Converting to Objective-C Types +@discussion When converting between JavaScript values and Objective-C objects a copy is + performed. Values of types listed below are copied to the corresponding + types on conversion in each direction. For NSDictionaries, entries in the + dictionary that are keyed by strings are copied onto a JavaScript object. + For dictionaries and arrays, conversion is recursive, with the same object + conversion being applied to all entries in the collection. + +
+@textblock
+   Objective-C type  |   JavaScript type
+ --------------------+---------------------
+         nil         |     undefined
+        NSNull       |        null
+       NSString      |       string
+       NSNumber      |   number, boolean
+     NSDictionary    |   Object object
+       NSArray       |    Array object
+        NSDate       |     Date object
+       NSBlock (1)   |   Function object (1)
+          id (2)     |   Wrapper object (2)
+        Class (3)    | Constructor object (3)
+@/textblock
+
+ + (1) Instances of NSBlock with supported arguments types will be presented to + JavaScript as a callable Function object. For more information on supported + argument types see JSExport.h. If a JavaScript Function originating from an + Objective-C block is converted back to an Objective-C object the block will + be returned. All other JavaScript functions will be converted in the same + manner as a JavaScript object of type Object. + + (2) For Objective-C instances that do not derive from the set of types listed + above, a wrapper object to provide a retaining handle to the Objective-C + instance from JavaScript. For more information on these wrapper objects, see + JSExport.h. When a JavaScript wrapper object is converted back to Objective-C + the Objective-C instance being retained by the wrapper is returned. + + (3) For Objective-C Class objects a constructor object containing exported + class methods will be returned. See JSExport.h for more information on + constructor objects. + + For all methods taking arguments of type id, arguments will be converted + into a JavaScript value according to the above conversion. +*/ +/*! +@method +@abstract Convert this JSValue to an Objective-C object. +@discussion The JSValue is converted to an Objective-C object according + to the conversion rules specified above. +@result The Objective-C representation of this JSValue. +*/ +- (id)toObject; + +/*! +@method +@abstract Convert a JSValue to an Objective-C object of a specific class. +@discussion The JSValue is converted to an Objective-C object of the specified Class. + If the result is not of the specified Class then nil will be returned. +@result An Objective-C object of the specified Class or nil. +*/ +- (id)toObjectOfClass:(Class)expectedClass; + +/*! +@method +@abstract Convert a JSValue to a boolean. +@discussion The JSValue is converted to a boolean according to the rules specified + by the JavaScript language. +@result The boolean result of the conversion. +*/ +- (BOOL)toBool; + +/*! +@method +@abstract Convert a JSValue to a double. +@discussion The JSValue is converted to a number according to the rules specified + by the JavaScript language. +@result The double result of the conversion. +*/ +- (double)toDouble; + +/*! +@method +@abstract Convert a JSValue to an int32_t. +@discussion The JSValue is converted to an integer according to the rules specified + by the JavaScript language. +@result The int32_t result of the conversion. +*/ +- (int32_t)toInt32; + +/*! +@method +@abstract Convert a JSValue to a uint32_t. +@discussion The JSValue is converted to an integer according to the rules specified + by the JavaScript language. +@result The uint32_t result of the conversion. +*/ +- (uint32_t)toUInt32; + +/*! +@method +@abstract Convert a JSValue to a NSNumber. +@discussion If the JSValue represents a boolean, a NSNumber value of YES or NO + will be returned. For all other types the value will be converted to a number according + to the rules specified by the JavaScript language. +@result The NSNumber result of the conversion. +*/ +- (NSNumber *)toNumber; + +/*! +@method +@abstract Convert a JSValue to a NSString. +@discussion The JSValue is converted to a string according to the rules specified + by the JavaScript language. +@result The NSString containing the result of the conversion. +*/ +- (NSString *)toString; + +/*! +@method +@abstract Convert a JSValue to a NSDate. +@discussion The value is converted to a number representing a time interval + since 1970 which is then used to create a new NSDate instance. +@result The NSDate created using the converted time interval. +*/ +- (NSDate *)toDate; + +/*! +@method +@abstract Convert a JSValue to a NSArray. +@discussion If the value is null or undefined then nil is returned. + If the value is not an object then a JavaScript TypeError will be thrown. + The property length is read from the object, converted to an unsigned + integer, and an NSArray of this size is allocated. Properties corresponding + to indicies within the array bounds will be copied to the array, with + JSValues converted to equivalent Objective-C objects as specified. +@result The NSArray containing the recursively converted contents of the + converted JavaScript array. +*/ +- (NSArray *)toArray; + +/*! +@method +@abstract Convert a JSValue to a NSDictionary. +@discussion If the value is null or undefined then nil is returned. + If the value is not an object then a JavaScript TypeError will be thrown. + All enumerable properties of the object are copied to the dictionary, with + JSValues converted to equivalent Objective-C objects as specified. +@result The NSDictionary containing the recursively converted contents of + the converted JavaScript object. +*/ +- (NSDictionary *)toDictionary; + +/*! +@functiongroup Checking JavaScript Types +*/ + +/*! +@property +@abstract Check if a JSValue corresponds to the JavaScript value undefined. +*/ +@property (readonly) BOOL isUndefined; + +/*! +@property +@abstract Check if a JSValue corresponds to the JavaScript value null. +*/ +@property (readonly) BOOL isNull; + +/*! +@property +@abstract Check if a JSValue is a boolean. +*/ +@property (readonly) BOOL isBoolean; + +/*! +@property +@abstract Check if a JSValue is a number. +@discussion In JavaScript, there is no differentiation between types of numbers. + Semantically all numbers behave like doubles except in special cases like bit + operations. +*/ +@property (readonly) BOOL isNumber; + +/*! +@property +@abstract Check if a JSValue is a string. +*/ +@property (readonly) BOOL isString; + +/*! +@property +@abstract Check if a JSValue is an object. +*/ +@property (readonly) BOOL isObject; + +/*! +@property +@abstract Check if a JSValue is an array. +*/ +@property (readonly) BOOL isArray JSC_API_AVAILABLE(macos(10.11), ios(9.0)); + +/*! +@property +@abstract Check if a JSValue is a date. +*/ +@property (readonly) BOOL isDate JSC_API_AVAILABLE(macos(10.11), ios(9.0)); + +/*! + @property + @abstract Check if a JSValue is a symbol. + */ +@property (readonly) BOOL isSymbol JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! +@method +@abstract Compare two JSValues using JavaScript's === operator. +*/ +- (BOOL)isEqualToObject:(id)value; + +/*! +@method +@abstract Compare two JSValues using JavaScript's == operator. +*/ +- (BOOL)isEqualWithTypeCoercionToObject:(id)value; + +/*! +@method +@abstract Check if a JSValue is an instance of another object. +@discussion This method has the same function as the JavaScript operator instanceof. + If an object other than a JSValue is passed, it will first be converted according to + the aforementioned rules. +*/ +- (BOOL)isInstanceOf:(id)value; + +/*! +@methodgroup Calling Functions and Constructors +*/ +/*! +@method +@abstract Invoke a JSValue as a function. +@discussion In JavaScript, if a function doesn't explicitly return a value then it + implicitly returns the JavaScript value undefined. +@param arguments The arguments to pass to the function. +@result The return value of the function call. +*/ +- (JSValue *)callWithArguments:(NSArray *)arguments; + +/*! +@method +@abstract Invoke a JSValue as a constructor. +@discussion This is equivalent to using the new syntax in JavaScript. +@param arguments The arguments to pass to the constructor. +@result The return value of the constructor call. +*/ +- (JSValue *)constructWithArguments:(NSArray *)arguments; + +/*! +@method +@abstract Invoke a method on a JSValue. +@discussion Accesses the property named method from this value and + calls the resulting value as a function, passing this JSValue as the this + value along with the specified arguments. +@param method The name of the method to be invoked. +@param arguments The arguments to pass to the method. +@result The return value of the method call. +*/ +- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments; + +@end + +/*! +@category +@discussion Objective-C methods exported to JavaScript may have argument and/or return + values of struct types, provided that conversion to and from the struct is + supported by JSValue. Support is provided for any types where JSValue + contains both a class method valueWith:inContext:, and and instance + method to- where the string in these selector names match, + with the first argument to the former being of the same struct type as the + return type of the latter. + Support is provided for structs of type CGPoint, NSRange, CGRect and CGSize. +*/ +@interface JSValue (StructSupport) + +/*! +@method +@abstract Create a JSValue from a CGPoint. +@result A newly allocated JavaScript object containing properties + named x and y, with values from the CGPoint. +*/ ++ (JSValue *)valueWithPoint:(CGPoint)point inContext:(JSContext *)context; + +/*! +@method +@abstract Create a JSValue from a NSRange. +@result A newly allocated JavaScript object containing properties + named location and length, with values from the NSRange. +*/ ++ (JSValue *)valueWithRange:(NSRange)range inContext:(JSContext *)context; + +/*! +@method +@abstract +Create a JSValue from a CGRect. +@result A newly allocated JavaScript object containing properties + named x, y, width, and height, with values from the CGRect. +*/ ++ (JSValue *)valueWithRect:(CGRect)rect inContext:(JSContext *)context; + +/*! +@method +@abstract Create a JSValue from a CGSize. +@result A newly allocated JavaScript object containing properties + named width and height, with values from the CGSize. +*/ ++ (JSValue *)valueWithSize:(CGSize)size inContext:(JSContext *)context; + +/*! +@method +@abstract Convert a JSValue to a CGPoint. +@discussion Reads the properties named x and y from + this JSValue, and converts the results to double. +@result The new CGPoint. +*/ +- (CGPoint)toPoint; + +/*! +@method +@abstract Convert a JSValue to an NSRange. +@discussion Reads the properties named location and + length from this JSValue and converts the results to double. +@result The new NSRange. +*/ +- (NSRange)toRange; + +/*! +@method +@abstract Convert a JSValue to a CGRect. +@discussion Reads the properties named x, y, + width, and height from this JSValue and converts the results to double. +@result The new CGRect. +*/ +- (CGRect)toRect; + +/*! +@method +@abstract Convert a JSValue to a CGSize. +@discussion Reads the properties named width and + height from this JSValue and converts the results to double. +@result The new CGSize. +*/ +- (CGSize)toSize; + +@end + +/*! + @category + @discussion These methods enable querying properties on a JSValue. + */ +@interface JSValue (PropertyAccess) + +#if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500) || (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000) +typedef NSString *JSValueProperty; +#else +typedef id JSValueProperty; +#endif + +/*! + @method + @abstract Access a property of a JSValue. + @result The JSValue for the requested property or the JSValue undefined + if the property does not exist. + @discussion Corresponds to the JavaScript operation object[property]. Starting with macOS 10.15 and iOS 13, 'property' can be any 'id' and will be converted to a JSValue using the conversion rules of valueWithObject:inContext:. Prior to macOS 10.15 and iOS 13, 'property' was expected to be an NSString *. + */ +- (JSValue *)valueForProperty:(JSValueProperty)property; + +/*! + @method + @abstract Set a property on a JSValue. + @discussion Corresponds to the JavaScript operation object[property] = value. Starting with macOS 10.15 and iOS 13, 'property' can be any 'id' and will be converted to a JSValue using the conversion rules of valueWithObject:inContext:. Prior to macOS 10.15 and iOS 13, 'property' was expected to be an NSString *. + */ +- (void)setValue:(id)value forProperty:(JSValueProperty)property; + +/*! + @method + @abstract Delete a property from a JSValue. + @result YES if deletion is successful, NO otherwise. + @discussion Corresponds to the JavaScript operation delete object[property]. Starting with macOS 10.15 and iOS 13, 'property' can be any 'id' and will be converted to a JSValue using the conversion rules of valueWithObject:inContext:. Prior to macOS 10.15 and iOS 13, 'property' was expected to be an NSString *. + */ +- (BOOL)deleteProperty:(JSValueProperty)property; + +/*! + @method + @abstract Check if a JSValue has a property. + @discussion This method has the same function as the JavaScript operator in. + @result Returns YES if property is present on the value. + @discussion Corresponds to the JavaScript operation property in object. Starting with macOS 10.15 and iOS 13, 'property' can be any 'id' and will be converted to a JSValue using the conversion rules of valueWithObject:inContext:. Prior to macOS 10.15 and iOS 13, 'property' was expected to be an NSString *. + */ +- (BOOL)hasProperty:(JSValueProperty)property; + +/*! + @method + @abstract Define properties with custom descriptors on JSValues. + @discussion This method may be used to create a data or accessor property on an object. + This method operates in accordance with the Object.defineProperty method in the JavaScript language. Starting with macOS 10.15 and iOS 13, 'property' can be any 'id' and will be converted to a JSValue using the conversion rules of valueWithObject:inContext:. Prior to macOS 10.15 and iOS 13, 'property' was expected to be an NSString *. + */ +- (void)defineProperty:(JSValueProperty)property descriptor:(id)descriptor; + +/*! + @method + @abstract Access an indexed (numerical) property on a JSValue. + @result The JSValue for the property at the specified index. + Returns the JavaScript value undefined if no property exists at that index. + */ +- (JSValue *)valueAtIndex:(NSUInteger)index; + +/*! + @method + @abstract Set an indexed (numerical) property on a JSValue. + @discussion For JSValues that are JavaScript arrays, indices greater than + UINT_MAX - 1 will not affect the length of the array. + */ +- (void)setValue:(id)value atIndex:(NSUInteger)index; + +@end + +/*! +@category +@discussion Instances of JSValue implement the following methods in order to enable + support for subscript access by key and index, for example: + +@textblock + JSValue *objectA, *objectB; + JSValue *v1 = object[@"X"]; // Get value for property "X" from 'object'. + JSValue *v2 = object[42]; // Get value for index 42 from 'object'. + object[@"Y"] = v1; // Assign 'v1' to property "Y" of 'object'. + object[101] = v2; // Assign 'v2' to index 101 of 'object'. +@/textblock + + An object key passed as a subscript will be converted to a JavaScript value, + and then the value using the same rules as valueWithObject:inContext:. In macOS + 10.14 and iOS 12 and below, the key argument of + setObject:object forKeyedSubscript:key was restricted to an + NSObject * but that restriction was never used internally. +*/ +@interface JSValue (SubscriptSupport) + +- (JSValue *)objectForKeyedSubscript:(id)key; +- (JSValue *)objectAtIndexedSubscript:(NSUInteger)index; +- (void)setObject:(id)object forKeyedSubscript:(id)key; +- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index; + +@end + +/*! +@category +@discussion These functions are for bridging between the C API and the Objective-C API. +*/ +@interface JSValue (JSValueRefSupport) + +/*! +@method +@abstract Creates a JSValue, wrapping its C API counterpart. +@result The Objective-C API equivalent of the specified JSValueRef. +*/ ++ (JSValue *)valueWithJSValueRef:(JSValueRef)value inContext:(JSContext *)context; + +/*! +@property +@abstract Returns the C API counterpart wrapped by a JSContext. +@result The C API equivalent of this JSValue. +*/ +@property (readonly) JSValueRef JSValueRef; +@end + +#ifdef __cplusplus +extern "C" { +#endif + +/*! +@group Property Descriptor Constants +@discussion These keys may assist in creating a property descriptor for use with the + defineProperty method on JSValue. + Property descriptors must fit one of three descriptions: + + Data Descriptor: + - A descriptor containing one or both of the keys value and writable, + and optionally containing one or both of the keys enumerable and + configurable. A data descriptor may not contain either the get or + set key. + A data descriptor may be used to create or modify the attributes of a + data property on an object (replacing any existing accessor property). + + Accessor Descriptor: + - A descriptor containing one or both of the keys get and set, and + optionally containing one or both of the keys enumerable and + configurable. An accessor descriptor may not contain either the value + or writable key. + An accessor descriptor may be used to create or modify the attributes of + an accessor property on an object (replacing any existing data property). + + Generic Descriptor: + - A descriptor containing one or both of the keys enumerable and + configurable. A generic descriptor may not contain any of the keys + value, writable, get, or set. + A generic descriptor may be used to modify the attributes of an existing + data or accessor property, or to create a new data property. +*/ +/*! +@const +*/ +JS_EXPORT extern NSString * const JSPropertyDescriptorWritableKey; +/*! +@const +*/ +JS_EXPORT extern NSString * const JSPropertyDescriptorEnumerableKey; +/*! +@const +*/ +JS_EXPORT extern NSString * const JSPropertyDescriptorConfigurableKey; +/*! +@const +*/ +JS_EXPORT extern NSString * const JSPropertyDescriptorValueKey; +/*! +@const +*/ +JS_EXPORT extern NSString * const JSPropertyDescriptorGetKey; +/*! +@const +*/ +JS_EXPORT extern NSString * const JSPropertyDescriptorSetKey; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif + +#endif // JSValue_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSValueInternal.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSValueInternal.h new file mode 100644 index 000000000..54b755eff --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSValueInternal.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013-2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSValueInternal_h +#define JSValueInternal_h + +#import + +#if JSC_OBJC_API_ENABLED + +@interface JSValue(Internal) + +JSValueRef valueInternalValue(JSValue *); + +- (JSValue *)initWithValue:(JSValueRef)value inContext:(JSContext *)context; + +JSValueRef objectToValue(JSContext *, id); +id valueToObject(JSContext *, JSValueRef); +id valueToNumber(JSGlobalContextRef, JSValueRef, JSValueRef* exception); +id valueToString(JSGlobalContextRef, JSValueRef, JSValueRef* exception); +id valueToDate(JSGlobalContextRef, JSValueRef, JSValueRef* exception); +id valueToArray(JSGlobalContextRef, JSValueRef, JSValueRef* exception); +id valueToDictionary(JSGlobalContextRef, JSValueRef, JSValueRef* exception); + ++ (SEL)selectorForStructToValue:(const char *)structTag; ++ (SEL)selectorForValueToStruct:(const char *)structTag; + +@end + +NSInvocation *typeToValueInvocationFor(const char* encodedType); +NSInvocation *valueToTypeInvocationFor(const char* encodedType); + +#endif + +#endif // JSValueInternal_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSValuePrivate.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSValuePrivate.h new file mode 100644 index 000000000..35d3d1000 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSValuePrivate.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018-2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if JSC_OBJC_API_ENABLED + +#import + +@interface JSValue(JSPrivate) + +// Currently empty. May be used again in the future. + +@end + +#endif // JSC_OBJC_API_ENABLED diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSValueRef.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSValueRef.h new file mode 100644 index 000000000..911b4bfc4 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSValueRef.h @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2006-2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSValueRef_h +#define JSValueRef_h + +#include +#include + +#ifndef __cplusplus +#include +#endif + +/*! +@enum JSType +@abstract A constant identifying the type of a JSValue. +@constant kJSTypeUndefined The unique undefined value. +@constant kJSTypeNull The unique null value. +@constant kJSTypeBoolean A primitive boolean value, one of true or false. +@constant kJSTypeNumber A primitive number value. +@constant kJSTypeString A primitive string value. +@constant kJSTypeObject An object value (meaning that this JSValueRef is a JSObjectRef). +@constant kJSTypeSymbol A primitive symbol value. +*/ +typedef enum { + kJSTypeUndefined, + kJSTypeNull, + kJSTypeBoolean, + kJSTypeNumber, + kJSTypeString, + kJSTypeObject, + kJSTypeSymbol JSC_API_AVAILABLE(macos(10.15), ios(13.0)) +} JSType; + +/*! + @enum JSTypedArrayType + @abstract A constant identifying the Typed Array type of a JSObjectRef. + @constant kJSTypedArrayTypeInt8Array Int8Array + @constant kJSTypedArrayTypeInt16Array Int16Array + @constant kJSTypedArrayTypeInt32Array Int32Array + @constant kJSTypedArrayTypeUint8Array Uint8Array + @constant kJSTypedArrayTypeUint8ClampedArray Uint8ClampedArray + @constant kJSTypedArrayTypeUint16Array Uint16Array + @constant kJSTypedArrayTypeUint32Array Uint32Array + @constant kJSTypedArrayTypeFloat32Array Float32Array + @constant kJSTypedArrayTypeFloat64Array Float64Array + @constant kJSTypedArrayTypeArrayBuffer ArrayBuffer + @constant kJSTypedArrayTypeNone Not a Typed Array + + */ +typedef enum { + kJSTypedArrayTypeInt8Array, + kJSTypedArrayTypeInt16Array, + kJSTypedArrayTypeInt32Array, + kJSTypedArrayTypeUint8Array, + kJSTypedArrayTypeUint8ClampedArray, + kJSTypedArrayTypeUint16Array, + kJSTypedArrayTypeUint32Array, + kJSTypedArrayTypeFloat32Array, + kJSTypedArrayTypeFloat64Array, + kJSTypedArrayTypeArrayBuffer, + kJSTypedArrayTypeNone, +} JSTypedArrayType JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +#ifdef __cplusplus +extern "C" { +#endif + +/*! +@function +@abstract Returns a JavaScript value's type. +@param ctx The execution context to use. +@param value The JSValue whose type you want to obtain. +@result A value of type JSType that identifies value's type. +*/ +JS_EXPORT JSType JSValueGetType(JSContextRef ctx, JSValueRef value); + +/*! +@function +@abstract Tests whether a JavaScript value's type is the undefined type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the undefined type, otherwise false. +*/ +JS_EXPORT bool JSValueIsUndefined(JSContextRef ctx, JSValueRef value); + +/*! +@function +@abstract Tests whether a JavaScript value's type is the null type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the null type, otherwise false. +*/ +JS_EXPORT bool JSValueIsNull(JSContextRef ctx, JSValueRef value); + +/*! +@function +@abstract Tests whether a JavaScript value's type is the boolean type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the boolean type, otherwise false. +*/ +JS_EXPORT bool JSValueIsBoolean(JSContextRef ctx, JSValueRef value); + +/*! +@function +@abstract Tests whether a JavaScript value's type is the number type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the number type, otherwise false. +*/ +JS_EXPORT bool JSValueIsNumber(JSContextRef ctx, JSValueRef value); + +/*! +@function +@abstract Tests whether a JavaScript value's type is the string type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the string type, otherwise false. +*/ +JS_EXPORT bool JSValueIsString(JSContextRef ctx, JSValueRef value); + +/*! +@function +@abstract Tests whether a JavaScript value's type is the symbol type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the symbol type, otherwise false. +*/ +JS_EXPORT bool JSValueIsSymbol(JSContextRef ctx, JSValueRef value) JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/*! +@function +@abstract Tests whether a JavaScript value's type is the object type. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value's type is the object type, otherwise false. +*/ +JS_EXPORT bool JSValueIsObject(JSContextRef ctx, JSValueRef value); + + +/*! +@function +@abstract Tests whether a JavaScript value is an object with a given class in its class chain. +@param ctx The execution context to use. +@param value The JSValue to test. +@param jsClass The JSClass to test against. +@result true if value is an object and has jsClass in its class chain, otherwise false. +*/ +JS_EXPORT bool JSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassRef jsClass); + +/*! +@function +@abstract Tests whether a JavaScript value is an array. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value is an array, otherwise false. +*/ +JS_EXPORT bool JSValueIsArray(JSContextRef ctx, JSValueRef value) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); + +/*! +@function +@abstract Tests whether a JavaScript value is a date. +@param ctx The execution context to use. +@param value The JSValue to test. +@result true if value is a date, otherwise false. +*/ +JS_EXPORT bool JSValueIsDate(JSContextRef ctx, JSValueRef value) JSC_API_AVAILABLE(macos(10.11), ios(9.0)); + +/*! +@function +@abstract Returns a JavaScript value's Typed Array type. +@param ctx The execution context to use. +@param value The JSValue whose Typed Array type to return. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +@result A value of type JSTypedArrayType that identifies value's Typed Array type, or kJSTypedArrayTypeNone if the value is not a Typed Array object. + */ +JS_EXPORT JSTypedArrayType JSValueGetTypedArrayType(JSContextRef ctx, JSValueRef value, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.12), ios(10.0)); + +/* Comparing values */ + +/*! +@function +@abstract Tests whether two JavaScript values are equal, as compared by the JS == operator. +@param ctx The execution context to use. +@param a The first value to test. +@param b The second value to test. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +@result true if the two values are equal, false if they are not equal or an exception is thrown. +*/ +JS_EXPORT bool JSValueIsEqual(JSContextRef ctx, JSValueRef a, JSValueRef b, JSValueRef* exception); + +/*! +@function +@abstract Tests whether two JavaScript values are strict equal, as compared by the JS === operator. +@param ctx The execution context to use. +@param a The first value to test. +@param b The second value to test. +@result true if the two values are strict equal, otherwise false. +*/ +JS_EXPORT bool JSValueIsStrictEqual(JSContextRef ctx, JSValueRef a, JSValueRef b); + +/*! +@function +@abstract Tests whether a JavaScript value is an object constructed by a given constructor, as compared by the JS instanceof operator. +@param ctx The execution context to use. +@param value The JSValue to test. +@param constructor The constructor to test against. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +@result true if value is an object constructed by constructor, as compared by the JS instanceof operator, otherwise false. +*/ +JS_EXPORT bool JSValueIsInstanceOfConstructor(JSContextRef ctx, JSValueRef value, JSObjectRef constructor, JSValueRef* exception); + +/* Creating values */ + +/*! +@function +@abstract Creates a JavaScript value of the undefined type. +@param ctx The execution context to use. +@result The unique undefined value. +*/ +JS_EXPORT JSValueRef JSValueMakeUndefined(JSContextRef ctx); + +/*! +@function +@abstract Creates a JavaScript value of the null type. +@param ctx The execution context to use. +@result The unique null value. +*/ +JS_EXPORT JSValueRef JSValueMakeNull(JSContextRef ctx); + +/*! +@function +@abstract Creates a JavaScript value of the boolean type. +@param ctx The execution context to use. +@param boolean The bool to assign to the newly created JSValue. +@result A JSValue of the boolean type, representing the value of boolean. +*/ +JS_EXPORT JSValueRef JSValueMakeBoolean(JSContextRef ctx, bool boolean); + +/*! +@function +@abstract Creates a JavaScript value of the number type. +@param ctx The execution context to use. +@param number The double to assign to the newly created JSValue. +@result A JSValue of the number type, representing the value of number. +*/ +JS_EXPORT JSValueRef JSValueMakeNumber(JSContextRef ctx, double number); + +/*! +@function +@abstract Creates a JavaScript value of the string type. +@param ctx The execution context to use. +@param string The JSString to assign to the newly created JSValue. The + newly created JSValue retains string, and releases it upon garbage collection. +@result A JSValue of the string type, representing the value of string. +*/ +JS_EXPORT JSValueRef JSValueMakeString(JSContextRef ctx, JSStringRef string); + +/*! + @function + @abstract Creates a JavaScript value of the symbol type. + @param ctx The execution context to use. + @param description A description of the newly created symbol value. + @result A unique JSValue of the symbol type, whose description matches the one provided. + */ +JS_EXPORT JSValueRef JSValueMakeSymbol(JSContextRef ctx, JSStringRef description) JSC_API_AVAILABLE(macos(10.15), ios(13.0)); + +/* Converting to and from JSON formatted strings */ + +/*! + @function + @abstract Creates a JavaScript value from a JSON formatted string. + @param ctx The execution context to use. + @param string The JSString containing the JSON string to be parsed. + @result A JSValue containing the parsed value, or NULL if the input is invalid. + */ +JS_EXPORT JSValueRef JSValueMakeFromJSONString(JSContextRef ctx, JSStringRef string) JSC_API_AVAILABLE(macos(10.7), ios(7.0)); + +/*! + @function + @abstract Creates a JavaScript string containing the JSON serialized representation of a JS value. + @param ctx The execution context to use. + @param value The value to serialize. + @param indent The number of spaces to indent when nesting. If 0, the resulting JSON will not contains newlines. The size of the indent is clamped to 10 spaces. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSString with the result of serialization, or NULL if an exception is thrown. + */ +JS_EXPORT JSStringRef JSValueCreateJSONString(JSContextRef ctx, JSValueRef value, unsigned indent, JSValueRef* exception) JSC_API_AVAILABLE(macos(10.7), ios(7.0)); + +/* Converting to primitive values */ + +/*! +@function +@abstract Converts a JavaScript value to boolean and returns the resulting boolean. +@param ctx The execution context to use. +@param value The JSValue to convert. +@result The boolean result of conversion. +*/ +JS_EXPORT bool JSValueToBoolean(JSContextRef ctx, JSValueRef value); + +/*! +@function +@abstract Converts a JavaScript value to number and returns the resulting number. +@param ctx The execution context to use. +@param value The JSValue to convert. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +@result The numeric result of conversion, or NaN if an exception is thrown. +*/ +JS_EXPORT double JSValueToNumber(JSContextRef ctx, JSValueRef value, JSValueRef* exception); + +/*! +@function +@abstract Converts a JavaScript value to string and copies the result into a JavaScript string. +@param ctx The execution context to use. +@param value The JSValue to convert. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +@result A JSString with the result of conversion, or NULL if an exception is thrown. Ownership follows the Create Rule. +*/ +JS_EXPORT JSStringRef JSValueToStringCopy(JSContextRef ctx, JSValueRef value, JSValueRef* exception); + +/*! +@function +@abstract Converts a JavaScript value to object and returns the resulting object. +@param ctx The execution context to use. +@param value The JSValue to convert. +@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. +@result The JSObject result of conversion, or NULL if an exception is thrown. +*/ +JS_EXPORT JSObjectRef JSValueToObject(JSContextRef ctx, JSValueRef value, JSValueRef* exception); + +/* Garbage collection */ +/*! +@function +@abstract Protects a JavaScript value from garbage collection. +@param ctx The execution context to use. +@param value The JSValue to protect. +@discussion Use this method when you want to store a JSValue in a global or on the heap, where the garbage collector will not be able to discover your reference to it. + +A value may be protected multiple times and must be unprotected an equal number of times before becoming eligible for garbage collection. +*/ +JS_EXPORT void JSValueProtect(JSContextRef ctx, JSValueRef value); + +/*! +@function +@abstract Unprotects a JavaScript value from garbage collection. +@param ctx The execution context to use. +@param value The JSValue to unprotect. +@discussion A value may be protected multiple times and must be unprotected an + equal number of times before becoming eligible for garbage collection. +*/ +JS_EXPORT void JSValueUnprotect(JSContextRef ctx, JSValueRef value); + +#ifdef __cplusplus +} +#endif + +#endif /* JSValueRef_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSVirtualMachine.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSVirtualMachine.h new file mode 100644 index 000000000..e9c75da07 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSVirtualMachine.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#if JSC_OBJC_API_ENABLED + +/*! +@interface +@discussion An instance of JSVirtualMachine represents a single JavaScript "object space" + or set of execution resources. Thread safety is supported by locking the + virtual machine, with concurrent JavaScript execution supported by allocating + separate instances of JSVirtualMachine. + + A virtual machine may need to run deferred tasks on a run loop, such as garbage collection + or resolving WebAssembly compilations. By default, a virtual machine will use the run loop + of the thread it was initialized on. Currently, there is no API to change a + JSVirtualMachine's run loop once it has been initialized. +*/ +NS_CLASS_AVAILABLE(10_9, 7_0) +@interface JSVirtualMachine : NSObject + +/*! +@methodgroup Creating New Virtual Machines +*/ +/*! +@method +@abstract Create a new JSVirtualMachine. +*/ +- (instancetype)init; + +/*! +@methodgroup Memory Management +*/ +/*! +@method +@abstract Notify the JSVirtualMachine of an external object relationship. +@discussion Allows clients of JSVirtualMachine to make the JavaScript runtime aware of + arbitrary external Objective-C object graphs. The runtime can then use + this information to retain any JavaScript values that are referenced + from somewhere in said object graph. + + For correct behavior clients must make their external object graphs + reachable from within the JavaScript runtime. If an Objective-C object is + reachable from within the JavaScript runtime, all managed references + transitively reachable from it as recorded using + -addManagedReference:withOwner: will be scanned by the garbage collector. +@param object The object that the owner points to. +@param owner The object that owns the pointed to object. +*/ +- (void)addManagedReference:(id)object withOwner:(id)owner; + +/*! +@method +@abstract Notify the JSVirtualMachine that a previous object relationship no longer exists. +@discussion The JavaScript runtime will continue to scan any references that were + reported to it by -addManagedReference:withOwner: until those references are removed. +@param object The object that was formerly owned. +@param owner The former owner. +*/ +- (void)removeManagedReference:(id)object withOwner:(id)owner; + +@end + +#endif diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSVirtualMachineInternal.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSVirtualMachineInternal.h new file mode 100644 index 000000000..b533482ac --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSVirtualMachineInternal.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013, 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSVirtualMachineInternal_h +#define JSVirtualMachineInternal_h + +#if JSC_OBJC_API_ENABLED + +#import + +namespace JSC { +class VM; +class SlotVisitor; +} + +#if defined(__OBJC__) +@class NSMapTable; + +@interface JSVirtualMachine(Internal) + +JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *); + ++ (JSVirtualMachine *)virtualMachineWithContextGroupRef:(JSContextGroupRef)group; + +- (JSContext *)contextForGlobalContextRef:(JSGlobalContextRef)globalContext; +- (void)addContext:(JSContext *)wrapper forGlobalContextRef:(JSGlobalContextRef)globalContext; +- (JSC::VM&)vm; + +- (BOOL)isWebThreadAware; + +@end + +#endif // defined(__OBJC__) + +void scanExternalObjectGraph(JSC::VM&, JSC::SlotVisitor&, void* root); +void scanExternalRememberedSet(JSC::VM&, JSC::SlotVisitor&); + +#endif // JSC_OBJC_API_ENABLED + +#endif // JSVirtualMachineInternal_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSVirtualMachinePrivate.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSVirtualMachinePrivate.h new file mode 100644 index 000000000..950afc73f --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSVirtualMachinePrivate.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "JSExportMacros.h" +#include + +#if JSC_OBJC_API_ENABLED + +#import + +@interface JSVirtualMachine(JSPrivate) + +/*! +@method +@discussion Shrinks the memory footprint of the VM by deleting various internal caches, + running synchronous garbage collection, and releasing memory back to the OS. Note: this + API waits until no JavaScript is running on the stack before it frees any memory. It's + best to call this API when no JavaScript is running on the stack for this reason. However, if + you do call this API when JavaScript is running on the stack, the API will wait until all JavaScript + on the stack finishes running to free memory back to the OS. Therefore, calling this + API may not synchronously free memory. +*/ + +- (void)shrinkFootprintWhenIdle JSC_API_AVAILABLE(macos(10.14), ios(12.0)); + +#if ENABLE(DFG_JIT) + +/*! +@method +@abstract Set the number of threads to be used by the DFG JIT compiler. +@discussion If called after the VM has been initialized, it will terminate + threads until it meets the new limit or create new threads accordingly if the + new limit is higher than the previous limit. If called before initialization, + the Options value for the number of DFG threads will be updated to ensure the + DFG compiler already starts with the up-to-date limit. +@param numberOfThreads The number of threads the DFG compiler should use going forward +@result The previous number of threads being used by the DFG compiler +*/ ++ (NSUInteger)setNumberOfDFGCompilerThreads:(NSUInteger)numberOfThreads JSC_API_AVAILABLE(macos(10.14), ios(12.0)); + +/*! +@method +@abstract Set the number of threads to be used by the FTL JIT compiler. +@discussion If called after the VM has been initialized, it will terminate + threads until it meets the new limit or create new threads accordingly if the + new limit is higher than the previous limit. If called before initialization, + the Options value for the number of FTL threads will be updated to ensure the + FTL compiler already starts with the up-to-date limit. +@param numberOfThreads The number of threads the FTL compiler should use going forward +@result The previous number of threads being used by the FTL compiler +*/ ++ (NSUInteger)setNumberOfFTLCompilerThreads:(NSUInteger)numberOfThreads JSC_API_AVAILABLE(macos(10.14), ios(12.0)); + +/*! +@method +@abstract Allows embedders of JSC to specify that JSC should crash the process if a VM is created when unexpected. +@param shouldCrash Sets process-wide state that indicates whether VM creation should crash or not. +*/ ++ (void)setCrashOnVMCreation:(BOOL)shouldCrash; + +#endif // ENABLE(DFG_JIT) + +@end + +#endif // JSC_OBJC_API_ENABLED diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWeakObjectMapRefInternal.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWeakObjectMapRefInternal.h new file mode 100644 index 000000000..9037947d7 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWeakObjectMapRefInternal.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSWeakObjectMapRefInternal_h +#define JSWeakObjectMapRefInternal_h + +#include "WeakGCMap.h" +#include + +namespace JSC { + +class JSObject; + +} + +typedef void (*JSWeakMapDestroyedCallback)(struct OpaqueJSWeakObjectMap*, void*); + +typedef JSC::WeakGCMap WeakMapType; + +struct OpaqueJSWeakObjectMap : public RefCounted { +public: + static Ref create(JSC::VM& vm, void* data, JSWeakMapDestroyedCallback callback) + { + return adoptRef(*new OpaqueJSWeakObjectMap(vm, data, callback)); + } + + WeakMapType& map() { return m_map; } + + ~OpaqueJSWeakObjectMap() + { + m_callback(this, m_data); + } + +private: + OpaqueJSWeakObjectMap(JSC::VM& vm, void* data, JSWeakMapDestroyedCallback callback) + : m_map(vm) + , m_data(data) + , m_callback(callback) + { + } + WeakMapType m_map; + void* m_data; + JSWeakMapDestroyedCallback m_callback; +}; + + +#endif // JSWeakObjectMapInternal_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWeakObjectMapRefPrivate.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWeakObjectMapRefPrivate.h new file mode 100644 index 000000000..a335e23c9 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWeakObjectMapRefPrivate.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSWeakObjectMapRefPrivate_h +#define JSWeakObjectMapRefPrivate_h + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*! @typedef JSWeakObjectMapRef A weak map for storing JSObjectRefs */ +typedef struct OpaqueJSWeakObjectMap* JSWeakObjectMapRef; + +/*! + @typedef JSWeakMapDestroyedCallback + @abstract The callback invoked when a JSWeakObjectMapRef is being destroyed. + @param map The map that is being destroyed. + @param data The private data (if any) that was associated with the map instance. + */ +typedef void (*JSWeakMapDestroyedCallback)(JSWeakObjectMapRef map, void* data); + +/*! + @function + @abstract Creates a weak value map that can be used to reference user defined objects without preventing them from being collected. + @param ctx The execution context to use. + @param data A void* to set as the map's private data. Pass NULL to specify no private data. + @param destructor A function to call when the weak map is destroyed. + @result A JSWeakObjectMapRef bound to the given context, data and destructor. + @discussion The JSWeakObjectMapRef can be used as a storage mechanism to hold custom JS objects without forcing those objects to + remain live as JSValueProtect would. + */ +JS_EXPORT JSWeakObjectMapRef JSWeakObjectMapCreate(JSContextRef ctx, void* data, JSWeakMapDestroyedCallback destructor); + +/*! + @function + @abstract Associates a JSObjectRef with the given key in a JSWeakObjectMap. + @param ctx The execution context to use. + @param map The map to operate on. + @param key The key to associate a weak reference with. + @param object The user defined object to associate with the key. + */ +JS_EXPORT void JSWeakObjectMapSet(JSContextRef ctx, JSWeakObjectMapRef map, void* key, JSObjectRef object); + +/*! + @function + @abstract Retrieves the JSObjectRef associated with a key. + @param ctx The execution context to use. + @param map The map to query. + @param key The key to search for. + @result Either the live object associated with the provided key, or NULL. + */ +JS_EXPORT JSObjectRef JSWeakObjectMapGet(JSContextRef ctx, JSWeakObjectMapRef map, void* key); + +/*! + @function + @abstract Removes the entry for the given key if the key is present, otherwise it has no effect. + @param ctx The execution context to use. + @param map The map to use. + @param key The key to remove. + */ +JS_EXPORT void JSWeakObjectMapRemove(JSContextRef ctx, JSWeakObjectMapRef map, void* key); + +#ifdef __cplusplus +} +#endif + +#endif // JSWeakObjectMapPrivate_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWeakPrivate.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWeakPrivate.h new file mode 100644 index 000000000..9ca2ffa6d --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWeakPrivate.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSWeakPrivate_h +#define JSWeakPrivate_h + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef const struct OpaqueJSWeak* JSWeakRef; + +JS_EXPORT JSWeakRef JSWeakCreate(JSContextGroupRef, JSObjectRef); + +JS_EXPORT void JSWeakRetain(JSContextGroupRef, JSWeakRef); +JS_EXPORT void JSWeakRelease(JSContextGroupRef, JSWeakRef); + +JS_EXPORT JSObjectRef JSWeakGetObject(JSWeakRef); + +#ifdef __cplusplus +} +#endif + +#endif // JSWeakPrivate_h + diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWeakValue.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWeakValue.h new file mode 100644 index 000000000..177ef41cf --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWeakValue.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. + * Copyright (C) 2018 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "JSCJSValue.h" +#include "Weak.h" + +namespace JSC { + +class JSObject; +class JSString; +class WeakHandleOwner; + +class JSWeakValue { +public: + JSWeakValue() = default; + ~JSWeakValue(); + + void clear(); + bool isClear() const; + + bool isSet() const { return m_tag != WeakTypeTag::NotSet; } + bool isPrimitive() const { return m_tag == WeakTypeTag::Primitive; } + bool isObject() const { return m_tag == WeakTypeTag::Object; } + bool isString() const { return m_tag == WeakTypeTag::String; } + + void setPrimitive(JSValue); + void setObject(JSObject*, WeakHandleOwner&, void* context); + void setString(JSString*, WeakHandleOwner&, void* context); + + JSObject* object() const + { + ASSERT(isObject()); + return m_value.object.get(); + } + + JSValue primitive() const + { + ASSERT(isPrimitive()); + return m_value.primitive; + } + + JSString* string() const + { + ASSERT(isString()); + return m_value.string.get(); + } + +private: + enum class WeakTypeTag { NotSet, Primitive, Object, String }; + + WeakTypeTag m_tag { WeakTypeTag::NotSet }; + + union WeakValueUnion { + WeakValueUnion() + : primitive(JSValue()) + { + } + + ~WeakValueUnion() + { + ASSERT(!primitive); + } + + JSValue primitive; + Weak object; + Weak string; + } m_value; +}; + +} // namespace JSC diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWrapperMap.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWrapperMap.h new file mode 100644 index 000000000..6c18c64b4 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JSWrapperMap.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import +#import +#import + +#if JSC_OBJC_API_ENABLED + +@interface JSWrapperMap : NSObject + +- (instancetype)initWithGlobalContextRef:(JSGlobalContextRef)context; + +- (JSValue *)jsWrapperForObject:(id)object inContext:(JSContext *)context; + +- (JSValue *)objcWrapperForJSValueRef:(JSValueRef)value inContext:(JSContext *)context; + +@end + +id tryUnwrapObjcObject(JSGlobalContextRef, JSValueRef); + +bool supportsInitMethodConstructors(); +Protocol *getJSExportProtocol(); +Class getNSBlockClass(); + +#endif diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JavaScript.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JavaScript.h new file mode 100644 index 000000000..251e3937b --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JavaScript.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2006 Apple Inc. All rights reserved. + * Copyright (C) 2008 Alp Toker + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JavaScript_h +#define JavaScript_h + +#include +#include +#include +#include +#include +#include + +#endif /* JavaScript_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/JavaScriptCore.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/JavaScriptCore.h new file mode 100644 index 000000000..b2fde1dbe --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/JavaScriptCore.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JavaScriptCore_h +#define JavaScriptCore_h + +#include +#include + +#if defined(__OBJC__) && JSC_OBJC_API_ENABLED + +#import "JSContext.h" +#import "JSValue.h" +#import "JSManagedValue.h" +#import "JSVirtualMachine.h" +#import "JSExport.h" + +#endif + +#endif /* JavaScriptCore_h */ diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/ObjCCallbackFunction.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/ObjCCallbackFunction.h new file mode 100644 index 000000000..c30c15628 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/ObjCCallbackFunction.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef ObjCCallbackFunction_h +#define ObjCCallbackFunction_h + +#include + +#if JSC_OBJC_API_ENABLED + +#import + +#if defined(__OBJC__) +JSObjectRef objCCallbackFunctionForMethod(JSContext *, Class, Protocol *, BOOL isInstanceMethod, SEL, const char* types); +JSObjectRef objCCallbackFunctionForBlock(JSContext *, id); +JSObjectRef objCCallbackFunctionForInit(JSContext *, Class, Protocol *, SEL, const char* types); + +id tryUnwrapConstructor(JSC::VM*, JSObjectRef); +#endif + +namespace JSC { + +class ObjCCallbackFunctionImpl; + +class ObjCCallbackFunction : public InternalFunction { + friend struct APICallbackFunction; +public: + typedef InternalFunction Base; + + template + static IsoSubspace* subspaceFor(VM& vm) + { + return vm.objCCallbackFunctionSpace(); + } + + static ObjCCallbackFunction* create(VM&, JSGlobalObject*, const String& name, std::unique_ptr); + static void destroy(JSCell*); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + ASSERT(globalObject); + return Structure::create(vm, globalObject, prototype, TypeInfo(InternalFunctionType, StructureFlags), info()); + } + + DECLARE_EXPORT_INFO; + + ObjCCallbackFunctionImpl* impl() const { return m_impl.get(); } + +protected: + ObjCCallbackFunction(VM&, Structure*, JSObjectCallAsFunctionCallback, JSObjectCallAsConstructorCallback, std::unique_ptr); + +private: + JSObjectCallAsFunctionCallback functionCallback() { return m_functionCallback; } + JSObjectCallAsConstructorCallback constructCallback() { return m_constructCallback; } + + JSObjectCallAsFunctionCallback m_functionCallback; + JSObjectCallAsConstructorCallback m_constructCallback; + std::unique_ptr m_impl; +}; + +} // namespace JSC + +#endif + +#endif // ObjCCallbackFunction_h diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/ObjcRuntimeExtras.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/ObjcRuntimeExtras.h new file mode 100644 index 000000000..20d8b8584 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/ObjcRuntimeExtras.h @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2013, 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import +#import +#import +#import +#import +#import +#import + +template +inline std::unique_ptr> adoptSystem(U value) +{ + return std::unique_ptr>(value); +} + +inline bool protocolImplementsProtocol(Protocol *candidate, Protocol *target) +{ + unsigned protocolProtocolsCount; + auto protocolProtocols = adoptSystem<__unsafe_unretained Protocol*[]>(protocol_copyProtocolList(candidate, &protocolProtocolsCount)); + for (unsigned i = 0; i < protocolProtocolsCount; ++i) { + if (protocol_isEqual(protocolProtocols[i], target)) + return true; + } + return false; +} + +inline void forEachProtocolImplementingProtocol(Class cls, Protocol *target, void (^callback)(Protocol *, bool& stop)) +{ + ASSERT(cls); + ASSERT(target); + + Vector worklist; + HashSet visited; + + // Initially fill the worklist with the Class's protocols. + { + unsigned protocolsCount; + auto protocols = adoptSystem<__unsafe_unretained Protocol*[]>(class_copyProtocolList(cls, &protocolsCount)); + worklist.append(protocols.get(), protocolsCount); + } + + bool stop = false; + while (!worklist.isEmpty()) { + Protocol *protocol = worklist.last(); + worklist.removeLast(); + + // Are we encountering this Protocol for the first time? + if (!visited.add((__bridge void*)protocol).isNewEntry) + continue; + + // If it implements the protocol, make the callback. + if (protocolImplementsProtocol(protocol, target)) { + callback(protocol, stop); + if (stop) + break; + } + + // Add incorporated protocols to the worklist. + { + unsigned protocolsCount; + auto protocols = adoptSystem<__unsafe_unretained Protocol*[]>(protocol_copyProtocolList(protocol, &protocolsCount)); + worklist.append(protocols.get(), protocolsCount); + } + } +} + +inline void forEachMethodInClass(Class cls, void (^callback)(Method)) +{ + unsigned count; + auto methods = adoptSystem(class_copyMethodList(cls, &count)); + for (unsigned i = 0; i < count; ++i) + callback(methods[i]); +} + +inline void forEachMethodInProtocol(Protocol *protocol, BOOL isRequiredMethod, BOOL isInstanceMethod, void (^callback)(SEL, const char*)) +{ + unsigned count; + auto methods = adoptSystem(protocol_copyMethodDescriptionList(protocol, isRequiredMethod, isInstanceMethod, &count)); + for (unsigned i = 0; i < count; ++i) + callback(methods[i].name, methods[i].types); +} + +inline void forEachPropertyInProtocol(Protocol *protocol, void (^callback)(objc_property_t)) +{ + unsigned count; + auto properties = adoptSystem(protocol_copyPropertyList(protocol, &count)); + for (unsigned i = 0; i < count; ++i) + callback(properties[i]); +} + +template +void skipPair(const char*& position) +{ + size_t count = 1; + do { + char c = *position++; + if (!c) + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Malformed type encoding" userInfo:nil]; + if (c == open) + ++count; + else if (c == close) + --count; + } while (count); +} + +class StringRange { + WTF_MAKE_NONCOPYABLE(StringRange); +public: + StringRange(const char* begin, const char* end) + : m_string(begin, end - begin) + { } + operator const char*() const { return m_string.data(); } + const char* get() const { return m_string.data(); } + +private: + CString m_string; +}; + +class StructBuffer { + WTF_MAKE_NONCOPYABLE(StructBuffer); +public: + StructBuffer(const char* encodedType) + { + NSUInteger size, alignment; + NSGetSizeAndAlignment(encodedType, &size, &alignment); + m_buffer = fastAlignedMalloc(alignment, size); + } + + ~StructBuffer() { fastAlignedFree(m_buffer); } + operator void*() const { return m_buffer; } + +private: + void* m_buffer; +}; + +template +typename DelegateType::ResultType parseObjCType(const char*& position) +{ + ASSERT(*position); + + switch (*position++) { + case 'c': + return DelegateType::template typeInteger(); + case 'i': + return DelegateType::template typeInteger(); + case 's': + return DelegateType::template typeInteger(); + case 'l': + return DelegateType::template typeInteger(); + case 'q': + return DelegateType::template typeDouble(); + case 'C': + return DelegateType::template typeInteger(); + case 'I': + return DelegateType::template typeInteger(); + case 'S': + return DelegateType::template typeInteger(); + case 'L': + return DelegateType::template typeInteger(); + case 'Q': + return DelegateType::template typeDouble(); + case 'f': + return DelegateType::template typeDouble(); + case 'd': + return DelegateType::template typeDouble(); + case 'B': + return DelegateType::typeBool(); + case 'v': + return DelegateType::typeVoid(); + + case '@': { // An object (whether statically typed or typed id) + if (position[0] == '?' && position[1] == '<') { + position += 2; + const char* begin = position; + skipPair<'<','>'>(position); + return DelegateType::typeBlock(begin, position - 1); + } + + if (*position == '"') { + const char* begin = position + 1; + const char* protocolPosition = strchr(begin, '<'); + const char* endOfType = strchr(begin, '"'); + position = endOfType + 1; + + // There's no protocol involved in this type, so just handle the class name. + if (!protocolPosition || protocolPosition > endOfType) + return DelegateType::typeOfClass(begin, endOfType); + // We skipped the class name and went straight to the protocol, so this is an id type. + if (begin == protocolPosition) + return DelegateType::typeId(); + // We have a class name with a protocol. For now, ignore the protocol. + return DelegateType::typeOfClass(begin, protocolPosition); + } + + return DelegateType::typeId(); + } + + case '{': { // {name=type...} A structure + const char* begin = position - 1; + skipPair<'{','}'>(position); + return DelegateType::typeStruct(begin, position); + } + + // NOT supporting C strings, arrays, pointers, unions, bitfields, function pointers. + case '*': // A character string (char *) + case '[': // [array type] An array + case '(': // (name=type...) A union + case 'b': // bnum A bit field of num bits + case '^': // ^type A pointer to type + case '?': // An unknown type (among other things, this code is used for function pointers) + // NOT supporting Objective-C Class, SEL + case '#': // A class object (Class) + case ':': // A method selector (SEL) + default: + return nil; + } +} + +extern "C" { + // Forward declare some Objective-C runtime internal methods that are not API. + const char *_protocol_getMethodTypeEncoding(Protocol *, SEL, BOOL isRequiredMethod, BOOL isInstanceMethod); + id objc_initWeak(id *, id); + void objc_destroyWeak(id *); + bool _Block_has_signature(void *); + const char * _Block_signature(void *); +} diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/OpaqueJSString.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/OpaqueJSString.h new file mode 100644 index 000000000..4a4b5edfb --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/OpaqueJSString.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OpaqueJSString_h +#define OpaqueJSString_h + +#include +#include +#include + +namespace JSC { + class Identifier; + class VM; +} + +struct OpaqueJSString : public ThreadSafeRefCounted { + static Ref create() + { + return adoptRef(*new OpaqueJSString); + } + + static Ref create(const LChar* characters, unsigned length) + { + return adoptRef(*new OpaqueJSString(characters, length)); + } + + static Ref create(const UChar* characters, unsigned length) + { + return adoptRef(*new OpaqueJSString(characters, length)); + } + + JS_EXPORT_PRIVATE static RefPtr tryCreate(const String&); + JS_EXPORT_PRIVATE static RefPtr tryCreate(String&&); + + JS_EXPORT_PRIVATE ~OpaqueJSString(); + + bool is8Bit() { return m_string.is8Bit(); } + const LChar* characters8() { return m_string.characters8(); } + const UChar* characters16() { return m_string.characters16(); } + unsigned length() { return m_string.length(); } + + const UChar* characters(); + + JS_EXPORT_PRIVATE String string() const; + JSC::Identifier identifier(JSC::VM*) const; + + static bool equal(const OpaqueJSString*, const OpaqueJSString*); + +private: + friend class WTF::ThreadSafeRefCounted; + + OpaqueJSString() + : m_characters(nullptr) + { + } + + OpaqueJSString(const String& string) + : m_string(string.isolatedCopy()) + , m_characters(m_string.impl() && m_string.is8Bit() ? nullptr : const_cast(m_string.characters16())) + { + } + + explicit OpaqueJSString(String&& string) + : m_string(WTFMove(string)) + , m_characters(m_string.impl() && m_string.is8Bit() ? nullptr : const_cast(m_string.characters16())) + { + } + + OpaqueJSString(const LChar* characters, unsigned length) + : m_string(characters, length) + , m_characters(nullptr) + { + } + + OpaqueJSString(const UChar* characters, unsigned length) + : m_string(characters, length) + , m_characters(m_string.impl() && m_string.is8Bit() ? nullptr : const_cast(m_string.characters16())) + { + } + + String m_string; + + // This will be initialized on demand when characters() is called if the string needs up-conversion. + std::atomic m_characters; +}; + +#endif diff --git a/NativeScript/napi/android/jsc/include/JavaScriptCore/WebKitAvailability.h b/NativeScript/napi/android/jsc/include/JavaScriptCore/WebKitAvailability.h new file mode 100644 index 000000000..2a1ce7ce1 --- /dev/null +++ b/NativeScript/napi/android/jsc/include/JavaScriptCore/WebKitAvailability.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008, 2009, 2010, 2014 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __WebKitAvailability__ +#define __WebKitAvailability__ + +#if defined(__APPLE__) + +#include +#include + +#if defined(BUILDING_GTK__) +#undef JSC_API_AVAILABLE +#define JSC_API_AVAILABLE(...) +#endif + +#else +#define JSC_API_AVAILABLE(...) +#endif + +#endif /* __WebKitAvailability__ */ diff --git a/NativeScript/napi/android/jsc/jsc-api.cpp b/NativeScript/napi/android/jsc/jsc-api.cpp new file mode 100644 index 000000000..0f2a615d1 --- /dev/null +++ b/NativeScript/napi/android/jsc/jsc-api.cpp @@ -0,0 +1,2702 @@ +#include "jsc-api.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct napi_callback_info__ { + napi_value newTarget; + napi_value thisArg; + napi_value* argv; + void* data; + uint16_t argc; +}; + +namespace { +class JSString { + public: + JSString(const JSString&) = delete; + + JSString(JSString&& other) { + _string = other._string; + other._string = nullptr; + } + + JSString(const char* string, size_t length = NAPI_AUTO_LENGTH) + : _string{CreateUTF8(string, length)} { + } + + JSString(const JSChar* string, size_t length = NAPI_AUTO_LENGTH) + : _string{JSStringCreateWithCharacters(string, length == NAPI_AUTO_LENGTH ? std::char_traits::length(string) : length)} { + } + + ~JSString() { + if (_string != nullptr) { + JSStringRelease(_string); + } + } + + static JSString Attach(JSStringRef string) { + return {string}; + } + + operator JSStringRef() const { + return _string; + } + + size_t Length() const { + return JSStringGetLength(_string); + } + + size_t LengthUTF8() const { + std::vector buffer(JSStringGetMaximumUTF8CStringSize(_string)); + return JSStringGetUTF8CString(_string, buffer.data(), buffer.size()) - 1; + } + + size_t LengthLatin1() const { + // Latin1 has the same length as Unicode. + return JSStringGetLength(_string); + } + + void CopyTo(JSChar* buf, size_t bufsize, size_t* result) const { + size_t length{JSStringGetLength(_string)}; + const JSChar* chars{JSStringGetCharactersPtr(_string)}; + size_t size{std::min(length, bufsize - 1)}; + std::memcpy(buf, chars, size); + buf[size] = 0; + if (result != nullptr) { + *result = size; + } + } + + void CopyToUTF8(char* buf, size_t bufsize, size_t* result) const { + size_t size{JSStringGetUTF8CString(_string, buf, bufsize)}; + if (result != nullptr) { + // JSStringGetUTF8CString returns size with null terminator. + *result = size - 1; + } + } + + void CopyToLatin1(char* buf, size_t bufsize, size_t* result) const { + size_t length{JSStringGetLength(_string)}; + const JSChar* chars{JSStringGetCharactersPtr(_string)}; + size_t size{std::min(length, bufsize - 1)}; + for (int i = 0; i < size; ++i) { + const JSChar ch{chars[i]}; + buf[i] = (ch < 256) ? ch : '?'; + } + if (result != nullptr) { + *result = size; + } + } + + private: + static JSStringRef CreateUTF8(const char* string, size_t length) { + if (length == NAPI_AUTO_LENGTH) { + return JSStringCreateWithUTF8CString(string); + } + + std::u16string u16str{std::wstring_convert< + std::codecvt_utf8_utf16, char16_t>{}.from_bytes(string, string + length)}; + return JSStringCreateWithCharacters(reinterpret_cast(u16str.data()), u16str.size()); + } + + JSString(JSStringRef string) + : _string{string} { + } + + JSStringRef _string; +}; + +inline JSValueRef ToJSValue(const napi_value value) { + return reinterpret_cast(value); +} + +inline const JSValueRef* ToJSValues(const napi_value* values) { + return reinterpret_cast(values); +} + +inline JSObjectRef ToJSObject(napi_env env, const napi_value value) { + assert(value == nullptr || JSValueIsObject(env->context, reinterpret_cast(value))); + return reinterpret_cast(value); +} + +inline JSString ToJSString(napi_env env, napi_value value, JSValueRef* exception) { + return JSString::Attach(JSValueToStringCopy(env->context, ToJSValue(value), exception)); +} + +inline napi_value ToNapi(const JSValueRef value) { + return reinterpret_cast(const_cast(value)); +} + + static inline napi_value* ToNapi(const JSValueRef* values) { + return reinterpret_cast(const_cast(values)); +} + +napi_status napi_clear_last_error(napi_env env) { + env->last_error.error_code = napi_ok; + env->last_error.engine_error_code = 0; + env->last_error.engine_reserved = nullptr; + return napi_ok; +} + +napi_status napi_set_last_error(napi_env env, napi_status error_code, uint32_t engine_error_code = 0, void* engine_reserved = nullptr) { + env->last_error.error_code = error_code; + env->last_error.engine_error_code = engine_error_code; + env->last_error.engine_reserved = engine_reserved; + return error_code; +} + +napi_status napi_set_exception(napi_env env, JSValueRef exception) { + env->last_exception = exception; + return napi_set_last_error(env, napi_pending_exception); +} + +napi_status napi_set_error_code(napi_env env, + napi_value error, + napi_value code, + const char* code_cstring) { + napi_value code_value{code}; + if (code_value == nullptr) { + code_value = ToNapi(JSValueMakeString(env->context, JSString(code_cstring))); + } else { + RETURN_STATUS_IF_FALSE(env, JSValueIsString(env->context, ToJSValue(code_value)), napi_string_expected); + } + + CHECK_NAPI(napi_set_named_property(env, error, "code", code_value)); + return napi_ok; +} + +enum class NativeType { + Constructor, + External, + Function, + Reference, + Wrapper, +}; + +class NativeInfo { + public: + NativeType Type() const { + return _type; + } + + template + static T* Get(JSObjectRef obj) { + return reinterpret_cast(JSObjectGetPrivate(obj)); + } + + template + static T* FindInPrototypeChain(JSContextRef ctx, JSObjectRef obj) { + while (true) { + JSValueRef exception{}; + JSObjectRef prototype = JSValueToObject(ctx, JSObjectGetPrototype(ctx, obj), &exception); + if (exception != nullptr) { + return nullptr; + } + + NativeInfo* info = Get(prototype); + if (info != nullptr && info->Type() == T::StaticType) { + return reinterpret_cast(info); + } + + obj = prototype; + } + } + + template + static T* GetNativeInfo(JSContextRef ctx, JSObjectRef obj, const char * propertyKey) { + JSValueRef exception {}; + JSValueRef native_info = JSObjectGetProperty(ctx, obj, JSString(propertyKey), &exception); + + NativeInfo* info = Get(JSValueToObject(ctx, native_info, &exception)); + if (info != nullptr && info->Type() == T::StaticType) { + return reinterpret_cast(info); + } + return nullptr; + } + + static void SetNativeInfoKey(JSContextRef ctx, JSObjectRef obj, JSClassRef classRef,JSValueRef propertyKey, void * data) { + JSObjectRef info{JSObjectMake(ctx, classRef, data)}; + JSObjectSetPropertyForKey(ctx, obj, propertyKey, info, kJSPropertyAttributeDontEnum | kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, nullptr); + } + + static void SetNativeInfo(JSContextRef ctx, JSObjectRef obj, JSClassRef classRef, const char * propertyKey, void * data) { + JSObjectRef info{JSObjectMake(ctx, classRef, data)}; + JSObjectSetProperty(ctx, obj, JSString(propertyKey), info, kJSPropertyAttributeDontEnum | kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, nullptr); + } + + protected: + NativeInfo(NativeType type) + : _type{type} {} + + private: + NativeType _type; +}; + +class ConstructorInfo : public NativeInfo { + public: + static const NativeType StaticType = NativeType::Constructor; + + static napi_status Create(napi_env env, + const char* utf8name, + size_t length, + napi_callback cb, + void* data, + napi_value* result) { + ConstructorInfo* info{new ConstructorInfo(env, utf8name, length, cb, data)}; + if (info == nullptr) { + return napi_set_last_error(env, napi_generic_failure); + } + + JSObjectRef constructor{JSObjectMakeConstructor(env->context, info->_class, CallAsConstructor)}; + JSValueRef exception{}; + if (length) { + napi_value name; + napi_create_string_utf8(env, utf8name, length, &name); + JSObjectSetProperty(env->context, constructor, JSString("name"), ToJSValue(name), kJSPropertyAttributeNone, &exception); + } + JSObjectRef prototype{(JSObjectRef) JSObjectGetProperty(env->context, constructor, JSString("prototype"), &exception)}; + +// JSObjectSetPrototype(env->context, prototype, JSObjectGetPrototype(env->context, constructor)); +// JSObjectSetPrototype(env->context, constructor, prototype); + + NativeInfo::SetNativeInfo(env->context, constructor, info->_class, "[[jsc_constructor_info]]", info); + + JSObjectSetProperty(env->context, prototype, JSString("constructor"), constructor, + kJSPropertyAttributeNone, &exception); + CHECK_JSC(env, exception); + + *result = ToNapi(constructor); + return napi_ok; + } + + private: + ConstructorInfo(napi_env env, const char* name, size_t length, napi_callback cb, void* data) + : NativeInfo{NativeType::Constructor} + , _env{env} + , _name{name, (length == NAPI_AUTO_LENGTH ? std::strlen(name) : length)} + , _cb{cb} + , _data{data} { + + JSClassDefinition classDefinition{kJSClassDefinitionEmpty}; + classDefinition.className = _name.data(); + classDefinition.finalize = Finalize; + _class = JSClassCreate(&classDefinition); + } + + ~ConstructorInfo() { + JSClassRelease(_class); + } + + // JSObjectCallAsConstructorCallback + static JSObjectRef CallAsConstructor(JSContextRef ctx, + JSObjectRef constructor, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) { +// ConstructorInfo* info = NativeInfo::FindInPrototypeChain(ctx, constructor); + ConstructorInfo* info = NativeInfo::GetNativeInfo(ctx, constructor, "[[jsc_constructor_info]]"); + + // Make sure any errors encountered last time we were in N-API are gone. + napi_clear_last_error(info->_env); + + JSObjectRef instance{JSObjectMake(ctx, info->_class, nullptr)}; + +// JSObjectSetPrototype(ctx, instance, JSObjectGetPrototype(ctx, constructor)); +// +// JSObjectSetProperty(ctx, instance, JSString("prototype"), JSObjectGetProperty(ctx, constructor, JSString("prototype"), nullptr), kJSPropertyAttributeNone, +// nullptr); + + napi_callback_info__ cbinfo{}; + cbinfo.thisArg = ToNapi(instance); + cbinfo.newTarget = ToNapi(constructor); + cbinfo.argc = argumentCount; + cbinfo.argv = ToNapi(arguments); + cbinfo.data = info->_data; + + napi_value result = info->_cb(info->_env, &cbinfo); + + if (info->_env->last_exception != nullptr) { + *exception = info->_env->last_exception; + info->_env->last_exception = nullptr; + } + + return ToJSObject(info->_env, result); + } + + // JSObjectFinalizeCallback + static void Finalize(JSObjectRef object) { + ConstructorInfo* info = NativeInfo::Get(object); + if (!info) return; + assert(info->Type() == NativeType::Constructor); + delete info; + } + + private: + napi_env _env; + std::string _name; + napi_callback _cb; + void* _data; + JSClassRef _class; +}; + +namespace xyz { + static std::once_flag functionInfoOnceFlag; + JSClassRef functionInfoClass {}; +} + +class FunctionInfo : public NativeInfo { + public: + static const NativeType StaticType = NativeType::Function; + + static napi_status Create(napi_env env, + const char* utf8name, + size_t length, + napi_callback cb, + void* data, + napi_value* result) { + FunctionInfo* info{new FunctionInfo(env, cb, data)}; + if (info == nullptr) { + return napi_set_last_error(env, napi_generic_failure); + } + JSObjectRef function = JSObjectMake(env->context, xyz::functionInfoClass, info); + *result = ToNapi(function); + return napi_ok; + } + + + private: + FunctionInfo(napi_env env, napi_callback cb, void* data) + : NativeInfo{NativeType::Function} + , _env{env} + , _cb{cb} + , _data{data} { + std::call_once(xyz::functionInfoOnceFlag, []() { + JSClassDefinition definition{kJSClassDefinitionEmpty}; + definition.className = "NapiFunctionCallback"; + definition.callAsFunction = FunctionInfo::CallAsFunction; + definition.attributes = kJSClassAttributeNoAutomaticPrototype; + definition.initialize = FunctionInfo::initialize; + definition.finalize = Finalize; + xyz::functionInfoClass = JSClassCreate(&definition); + }); + + } + + ~FunctionInfo() {} + + static void initialize(JSContextRef ctx, JSObjectRef object) { + JSObjectRef global = JSContextGetGlobalObject(ctx); + JSValueRef value = + JSObjectGetProperty(ctx, global, JSString("Function"), nullptr); + JSObjectRef funcCtor = JSValueToObject(ctx, value, nullptr); + if (!funcCtor) { + // We can't do anything if Function is not an object + return; + } + JSValueRef funcProto = JSObjectGetPrototype(ctx, funcCtor); + JSObjectSetPrototype(ctx, object, funcProto); + } + + // JSObjectCallAsFunctionCallback + static JSValueRef CallAsFunction(JSContextRef ctx, + JSObjectRef function, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) { +// FunctionInfo* info = NativeInfo::Get(function); + FunctionInfo* info = reinterpret_cast(JSObjectGetPrivate(function)); + + // Make sure any errors encountered last time we were in N-API are gone. + napi_clear_last_error(info->_env); + + napi_callback_info__ cbinfo{}; + cbinfo.thisArg = ToNapi(thisObject); + cbinfo.newTarget = nullptr; + cbinfo.argc = argumentCount; + cbinfo.argv = ToNapi(arguments); + + cbinfo.data = info->_data; + + napi_value result = info->_cb(info->_env, &cbinfo); + + if (info->_env->last_exception != nullptr) { + *exception = info->_env->last_exception; + info->_env->last_exception = nullptr; + } + + return ToJSValue(result); + } + + // JSObjectFinalizeCallback + static void Finalize(JSObjectRef object) { + FunctionInfo* info = NativeInfo::Get(object); + assert(info->Type() == NativeType::Function); + delete info; + } + + napi_env _env; + napi_callback _cb; + void* _data; +}; + + +template +class BaseInfoT : public NativeInfo { + public: + static const NativeType StaticType = TType; + + ~BaseInfoT() { + JSClassRelease(_class); + } + + napi_env Env() const { + return _env; + } + + void Data(void* value) { + _data = value; + } + + void* Data() const { + return _data; + } + + using FinalizerT = std::function; + void AddFinalizer(FinalizerT finalizer) { + _finalizers.push_back(finalizer); + } + + protected: + BaseInfoT(napi_env env, const char* className) + : NativeInfo{TType} + , _env{env} { + JSClassDefinition definition{kJSClassDefinitionEmpty}; + definition.className = className; + definition.finalize = Finalize; + _class = JSClassCreate(&definition); + } + + // JSObjectFinalizeCallback + static void Finalize(JSObjectRef object) { + T* info = Get(object); + assert(info->Type() == TType); + for (const FinalizerT& finalizer : info->_finalizers) { + finalizer(info); + } + delete info; + } + + napi_env _env; + void* _data{}; + std::vector _finalizers{}; + JSClassRef _class{}; +}; + +class ExternalInfo: public BaseInfoT { + public: + static napi_status Create(napi_env env, + void* data, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result) { + ExternalInfo* info = new ExternalInfo(env); + if (info == nullptr) { + return napi_set_last_error(env, napi_generic_failure); + } + + info->Data(data); + + if (finalize_cb != nullptr) { + info->AddFinalizer([finalize_cb, finalize_hint](ExternalInfo* info) { + finalize_cb(info->Env(), info->Data(), finalize_hint); + }); + } + + *result = ToNapi(JSObjectMake(env->context, info->_class, info)); + return napi_ok; + } + + private: + ExternalInfo(napi_env env) + : BaseInfoT{env, "Native (External)"} { + } +}; + +class ReferenceInfo : public BaseInfoT { + public: + static napi_status Initialize(napi_env env, napi_value object, FinalizerT finalizer) { + + napi_valuetype type; + napi_typeof(env, object, &type); + + if (type == napi_object || type == napi_function) { + ReferenceInfo* info = new ReferenceInfo(env); + if (info == nullptr) { + return napi_set_last_error(env, napi_generic_failure); + } + + // JSObjectRef ref{JSObjectMake(env->context, info->_class, info)}; + // JSObjectSetPrototype(env->context, prototype, JSObjectGetPrototype(env->context, ToJSObject(env, object))); + // JSObjectSetPrototype(env->context, ToJSObject(env, object), prototype); + + NativeInfo::SetNativeInfo(env->context, ToJSObject(env, object), info->_class, "[[jsc_reference_info]]", info); + + info->AddFinalizer(finalizer); + } + + return napi_ok; + } + + private: + ReferenceInfo(napi_env env) + : BaseInfoT{env, "Native (Reference)"} { + } +}; + +class WrapperInfo : public BaseInfoT { + public: + static napi_status Wrap(napi_env env, napi_value object, WrapperInfo** result) { + WrapperInfo* info{}; + + napi_value propertyKey; + napi_create_string_utf8(env, "[[jsc_wrapper_info]]", NAPI_AUTO_LENGTH, &propertyKey); + bool hasOwnProperty; + napi_has_own_property(env, object, propertyKey, &hasOwnProperty); + + if (hasOwnProperty) { + return napi_generic_failure; +// CHECK_NAPI(Unwrap(env, object, &info)); + } + + if (info == nullptr) { + info = new WrapperInfo(env); + if (info == nullptr) { + return napi_set_last_error(env, napi_generic_failure); + } + + NativeInfo::SetNativeInfoKey(env->context, ToJSObject(env, object), info->_class, ToJSValue(propertyKey), info); + } + + *result = info; + return napi_ok; + } + + static napi_status Unwrap(napi_env env, napi_value object, WrapperInfo** result) { + *result = NativeInfo::GetNativeInfo(env->context, ToJSObject(env, object), "[[jsc_wrapper_info]]"); + return napi_ok; + } + + private: + WrapperInfo(napi_env env) + : BaseInfoT{env, "Native (Wrapper)"} { + } +}; + +class ExternalArrayBufferInfo { + public: + static napi_status Create(napi_env env, + void* external_data, + size_t byte_length, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result) { + ExternalArrayBufferInfo* info{new ExternalArrayBufferInfo(env, finalize_cb, finalize_hint)}; + if (info == nullptr) { + return napi_set_last_error(env, napi_generic_failure); + } + + JSValueRef exception{}; + *result = ToNapi(JSObjectMakeArrayBufferWithBytesNoCopy( + env->context, + external_data, + byte_length, + BytesDeallocator, + info, + &exception)); + CHECK_JSC(env, exception); + + return napi_ok; + } + + private: + ExternalArrayBufferInfo(napi_env env, napi_finalize finalize_cb, void* hint) + : _env{env} + , _cb{finalize_cb} + , _hint{hint} { + } + + // JSTypedArrayBytesDeallocator + static void BytesDeallocator(void* bytes, void* deallocatorContext) { + ExternalArrayBufferInfo* info{reinterpret_cast(deallocatorContext)}; + if (info->_cb != nullptr) { + info->_cb(info->_env, bytes, info->_hint); + } + delete info; + } + + napi_env _env; + napi_finalize _cb; + void* _hint; +}; +} + +struct napi_ref__ { + napi_ref__(napi_value value, uint32_t count) + : _value{value} + , _count{count} { + } + + napi_status init(napi_env env) { + // track the ref values to support weak refs + auto pair{env->active_ref_values.insert(_value)}; + if (pair.second) { + CHECK_NAPI(ReferenceInfo::Initialize(env, _value, [value = _value](ReferenceInfo* info) { + info->Env()->active_ref_values.erase(value); + })); + } + + if (_count != 0) { + protect(env); + } + + return napi_ok; + } + + void deinit(napi_env env) { + if (_count != 0) { + unprotect(env); + } + + _value = nullptr; + _count = 0; + } + + void ref(napi_env env) { + if (_count++ == 0) { + protect(env); + } + } + + void unref(napi_env env) { + if (--_count == 0) { + unprotect(env); + } + } + + uint32_t count() const { + return _count; + } + + napi_value value(napi_env env) const { + if (env->active_ref_values.find(_value) == env->active_ref_values.end()) { + return nullptr; + } + + return _value; + } + + private: + void protect(napi_env env) { + + _iter = env->strong_refs.insert(env->strong_refs.end(), this); + JSValueProtect(env->context, ToJSValue(_value)); + } + + void unprotect(napi_env env) { + env->strong_refs.erase(_iter); + JSValueUnprotect(env->context, ToJSValue(_value)); + } + + napi_value _value{}; + uint32_t _count{}; + std::list::iterator _iter{}; +}; + +void napi_env__::deinit_refs() { + while (!strong_refs.empty()) { + napi_ref ref{strong_refs.front()}; + ref->deinit(this); + } +} + +void napi_env__::init_symbol(JSValueRef &symbol, const char *description) { + symbol = JSValueMakeSymbol(context, JSString(description)); + JSValueProtect(context, symbol); +} + +void napi_env__::deinit_symbol(JSValueRef symbol) { + JSValueUnprotect(context, symbol); +} + +// Warning: Keep in-sync with napi_status enum +static const char* error_messages[] = { + nullptr, + "Invalid argument", + "An object was expected", + "A string was expected", + "A string or symbol was expected", + "A function was expected", + "A number was expected", + "A boolean was expected", + "An array was expected", + "Unknown failure", + "An exception is pending", + "The async work item was cancelled", + "napi_escape_handle already called on scope", + "Invalid handle scope usage", + "Invalid callback scope usage", + "Thread-safe function queue is full", + "Thread-safe function handle is closing", + "A bigint was expected", +}; + +napi_status napi_get_last_error_info(napi_env env, + const napi_extended_error_info** result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + // you must update this assert to reference the last message + // in the napi_status enum each time a new error message is added. + // We don't have a napi_status_last as this would result in an ABI + // change each time a message was added. + static_assert( + std::size(error_messages) == napi_bigint_expected + 1, + "Count of error messages must match count of error values"); + assert(env->last_error.error_code <= napi_callback_scope_mismatch); + + // Wait until someone requests the last error information to fetch the error + // message string + env->last_error.error_message = + error_messages[env->last_error.error_code]; + + *result = &env->last_error; + return napi_ok; +} + +napi_status napi_create_function(napi_env env, + const char* utf8name, + size_t length, + napi_callback cb, + void* callback_data, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + CHECK_NAPI(FunctionInfo::Create(env, utf8name, length, cb, callback_data, result)); + return napi_ok; +} + +napi_status napi_define_class(napi_env env, + const char* utf8name, + size_t length, + napi_callback cb, + void* data, + size_t property_count, + const napi_property_descriptor* properties, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + napi_value constructor{}; + CHECK_NAPI(ConstructorInfo::Create(env, utf8name, length, cb, data, &constructor)); + + int instancePropertyCount{0}; + int staticPropertyCount{0}; + for (size_t i = 0; i < property_count; i++) { + if ((properties[i].attributes & napi_static) != 0) { + staticPropertyCount++; + } else { + instancePropertyCount++; + } + } + + std::vector staticDescriptors{}; + std::vector instanceDescriptors{}; + staticDescriptors.reserve(staticPropertyCount); + instanceDescriptors.reserve(instancePropertyCount); + + for (size_t i = 0; i < property_count; i++) { + if ((properties[i].attributes & napi_static) != 0) { + staticDescriptors.push_back(properties[i]); + } else { + instanceDescriptors.push_back(properties[i]); + } + } + + if (staticPropertyCount > 0) { + CHECK_NAPI(napi_define_properties(env, + constructor, + staticDescriptors.size(), + staticDescriptors.data())); + } + + if (instancePropertyCount > 0) { + napi_value prototype{}; + CHECK_NAPI(napi_get_named_property(env, constructor, "prototype", &prototype)); +// CHECK_NAPI(napi_get_prototype(env, constructor, &prototype)); + + CHECK_NAPI(napi_define_properties(env, + prototype, + instanceDescriptors.size(), + instanceDescriptors.data())); + } + + *result = constructor; + return napi_ok; +} + +napi_status napi_get_property_names(napi_env env, + napi_value object, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + napi_value global{}, object_ctor{}, function{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Object", &object_ctor)); + CHECK_NAPI(napi_get_named_property(env, object_ctor, "getOwnPropertyNames", &function)); + CHECK_NAPI(napi_call_function(env, object_ctor, function, 0, nullptr, result)); + + return napi_ok; +} + +napi_status napi_set_property(napi_env env, + napi_value object, + napi_value key, + napi_value value) { + CHECK_ENV(env); + CHECK_ARG(env, key); + CHECK_ARG(env, value); + + JSValueRef exception{}; + JSString key_str{ToJSString(env, key, &exception)}; + CHECK_JSC(env, exception); + + JSObjectSetProperty( + env->context, + ToJSObject(env, object), + key_str, + ToJSValue(value), + kJSPropertyAttributeNone, + &exception); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_has_property(napi_env env, + napi_value object, + napi_value key, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + CHECK_ARG(env, key); + + JSValueRef exception{}; + JSString key_str{ToJSString(env, key, &exception)}; + CHECK_JSC(env, exception); + + *result = JSObjectHasProperty( + env->context, + ToJSObject(env, object), + key_str); + return napi_ok; +} + +napi_status napi_get_property(napi_env env, + napi_value object, + napi_value key, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, key); + CHECK_ARG(env, result); + + JSValueRef exception{}; + JSString key_str{ToJSString(env, key, &exception)}; + CHECK_JSC(env, exception); + + *result = ToNapi(JSObjectGetProperty( + env->context, + ToJSObject(env, object), + key_str, + &exception)); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_delete_property(napi_env env, + napi_value object, + napi_value key, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + JSValueRef exception{}; + JSString key_str{ToJSString(env, key, &exception)}; + CHECK_JSC(env, exception); + + *result = JSObjectDeleteProperty( + env->context, + ToJSObject(env, object), + key_str, + &exception); + CHECK_JSC(env, exception); + + return napi_ok; +} + +NAPI_EXTERN napi_status napi_has_own_property(napi_env env, +napi_value object, + napi_value key, +bool* result) { +CHECK_ENV(env); +CHECK_ARG(env, result); + +napi_value global{}, object_ctor{}, function{}, value{}; +CHECK_NAPI(napi_get_global(env, &global)); +CHECK_NAPI(napi_get_named_property(env, global, "Object", &object_ctor)); +CHECK_NAPI(napi_get_named_property(env, object_ctor, "hasOwnProperty", &function)); +CHECK_NAPI(napi_call_function(env, object_ctor, function, 0, nullptr, &value)); +*result = JSValueToBoolean(env->context, ToJSValue(value)); + +return napi_ok; +} + +napi_status napi_set_named_property(napi_env env, + napi_value object, + const char* utf8name, + napi_value value) { + CHECK_ENV(env); + CHECK_ARG(env, value); + + JSValueRef exception{}; + JSObjectSetProperty( + env->context, + ToJSObject(env, object), + JSString(utf8name), + ToJSValue(value), + kJSPropertyAttributeNone, + &exception); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_has_named_property(napi_env env, + napi_value object, + const char* utf8name, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, object); + + *result = JSObjectHasProperty( + env->context, + ToJSObject(env, object), + JSString(utf8name)); + + return napi_ok; +} + +napi_status napi_get_named_property(napi_env env, + napi_value object, + const char* utf8name, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, object); + + JSValueRef exception{}; + + *result = ToNapi(JSObjectGetProperty( + env->context, + ToJSObject(env, object), + JSString(utf8name), + &exception)); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_set_element(napi_env env, + napi_value object, + uint32_t index, + napi_value value) { + CHECK_ENV(env); + CHECK_ARG(env, value); + + JSValueRef exception{}; + JSObjectSetPropertyAtIndex( + env->context, + ToJSObject(env, object), + index, + ToJSValue(value), + &exception); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_has_element(napi_env env, + napi_value object, + uint32_t index, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + JSValueRef exception{}; + JSValueRef value{JSObjectGetPropertyAtIndex( + env->context, + ToJSObject(env, object), + index, + &exception)}; + CHECK_JSC(env, exception); + + *result = !JSValueIsUndefined(env->context, value); + return napi_ok; +} + +napi_status napi_get_element(napi_env env, + napi_value object, + uint32_t index, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + JSValueRef exception{}; + *result = ToNapi(JSObjectGetPropertyAtIndex( + env->context, + ToJSObject(env, object), + index, + &exception)); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_delete_element(napi_env env, + napi_value object, + uint32_t index, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + napi_value index_value{ToNapi(JSValueMakeNumber(env->context, index))}; + + JSValueRef exception{}; + JSString index_str{ToJSString(env, index_value, &exception)}; + CHECK_JSC(env, exception); + + *result = JSObjectDeleteProperty( + env->context, + ToJSObject(env, object), + index_str, + &exception); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_define_properties(napi_env env, + napi_value object, + size_t property_count, + const napi_property_descriptor* properties) { + CHECK_ENV(env); + if (property_count > 0) { + CHECK_ARG(env, properties); + } + + for (size_t i = 0; i < property_count; i++) { + const napi_property_descriptor* p{properties + i}; + + napi_value descriptor{}; + CHECK_NAPI(napi_create_object(env, &descriptor)); + + napi_value configurable{}; + CHECK_NAPI(napi_get_boolean(env, (p->attributes & napi_configurable), &configurable)); + CHECK_NAPI(napi_set_named_property(env, descriptor, "configurable", configurable)); + + napi_value enumerable{}; + CHECK_NAPI(napi_get_boolean(env, (p->attributes & napi_configurable), &enumerable)); + CHECK_NAPI(napi_set_named_property(env, descriptor, "enumerable", enumerable)); + + if (p->getter != nullptr || p->setter != nullptr) { + if (p->getter != nullptr) { + napi_value getter{}; + CHECK_NAPI(napi_create_function(env, p->utf8name, NAPI_AUTO_LENGTH, p->getter, p->data, &getter)); + CHECK_NAPI(napi_set_named_property(env, descriptor, "get", getter)); + } + if (p->setter != nullptr) { + napi_value setter{}; + CHECK_NAPI(napi_create_function(env, p->utf8name, NAPI_AUTO_LENGTH, p->setter, p->data, &setter)); + CHECK_NAPI(napi_set_named_property(env, descriptor, "set", setter)); + } + } else if (p->method != nullptr) { + napi_value method{}; + CHECK_NAPI(napi_create_function(env, p->utf8name, NAPI_AUTO_LENGTH, p->method, p->data, &method)); + CHECK_NAPI(napi_set_named_property(env, descriptor, "value", method)); + } else { + RETURN_STATUS_IF_FALSE(env, p->value != nullptr, napi_invalid_arg); + + napi_value writable{}; + CHECK_NAPI(napi_get_boolean(env, (p->attributes & napi_writable), &writable)); + CHECK_NAPI(napi_set_named_property(env, descriptor, "writable", writable)); + + CHECK_NAPI(napi_set_named_property(env, descriptor, "value", p->value)); + } + + napi_value propertyName{}; + if (p->utf8name == nullptr) { + propertyName = p->name; + } else { + CHECK_NAPI(napi_create_string_utf8(env, p->utf8name, NAPI_AUTO_LENGTH, &propertyName)); + } + + napi_value global{}, object_ctor{}, function{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Object", &object_ctor)); + CHECK_NAPI(napi_get_named_property(env, object_ctor, "defineProperty", &function)); + + napi_value args[] = { object, propertyName, descriptor }; + CHECK_NAPI(napi_call_function(env, object_ctor, function, 3, args, nullptr)); + } + + return napi_ok; +} + +napi_status napi_is_array(napi_env env, napi_value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + *result = JSValueIsArray( + env->context, + ToJSValue(value)); + return napi_ok; +} + +napi_status napi_get_array_length(napi_env env, + napi_value value, + uint32_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + JSValueRef exception{}; + JSValueRef length = JSObjectGetProperty( + env->context, + ToJSObject(env, value), + JSString("length"), + &exception); + CHECK_JSC(env, exception); + + *result = static_cast(JSValueToNumber(env->context, length, &exception)); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_strict_equals(napi_env env, + napi_value lhs, + napi_value rhs, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, lhs); + CHECK_ARG(env, rhs); + CHECK_ARG(env, result); + *result = JSValueIsStrictEqual( + env->context, + ToJSValue(lhs), + ToJSValue(rhs)); + return napi_ok; +} + +napi_status napi_get_prototype(napi_env env, + napi_value object, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + JSValueRef exception{}; + JSObjectRef prototype{JSValueToObject(env->context, JSObjectGetPrototype(env->context, ToJSObject(env, object)), &exception)}; + CHECK_JSC(env, exception); + + *result = ToNapi(prototype); + return napi_ok; +} + +napi_status napi_create_object(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = ToNapi(JSObjectMake(env->context, nullptr, nullptr)); + return napi_ok; +} + +napi_status napi_create_array(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + JSValueRef exception{}; + *result = ToNapi(JSObjectMakeArray(env->context, 0, nullptr, &exception)); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_create_array_with_length(napi_env env, + size_t length, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + JSValueRef exception{}; + JSObjectRef array = JSObjectMakeArray( + env->context, + 0, + nullptr, + &exception); + CHECK_JSC(env, exception); + + JSObjectSetProperty( + env->context, + array, + JSString("length"), + JSValueMakeNumber(env->context, static_cast(length)), + kJSPropertyAttributeNone, + &exception); + CHECK_JSC(env, exception); + + *result = ToNapi(array); + return napi_ok; +} + +napi_status napi_create_string_latin1(napi_env env, + const char* str, + size_t length, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = ToNapi(JSValueMakeString( + env->context, + JSString(str, length))); + return napi_ok; +} + +napi_status napi_create_string_utf8(napi_env env, + const char* str, + size_t length, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = ToNapi(JSValueMakeString( + env->context, + JSString(str, length))); + return napi_ok; +} + +napi_status napi_create_string_utf16(napi_env env, + const char16_t* str, + size_t length, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + static_assert(sizeof(char16_t) == sizeof(JSChar)); + *result = ToNapi(JSValueMakeString( + env->context, + JSString(reinterpret_cast(str), length))); + return napi_ok; +} + +napi_status napi_create_double(napi_env env, + double value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = ToNapi(JSValueMakeNumber(env->context, value)); + return napi_ok; +} + +napi_status napi_create_int32(napi_env env, + int32_t value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = ToNapi(JSValueMakeNumber(env->context, static_cast(value))); + return napi_ok; +} + +napi_status napi_create_uint32(napi_env env, + uint32_t value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = ToNapi(JSValueMakeNumber(env->context, static_cast(value))); + return napi_ok; +} + +napi_status napi_create_int64(napi_env env, + int64_t value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = ToNapi(JSValueMakeNumber(env->context, static_cast(value))); + return napi_ok; +} + +napi_status napi_get_boolean(napi_env env, bool value, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = ToNapi(JSValueMakeBoolean(env->context, value)); + return napi_ok; +} + +napi_status napi_create_symbol(napi_env env, + napi_value description, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + napi_value global{}, symbol_func{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Symbol", &symbol_func)); + CHECK_NAPI(napi_call_function(env, global, symbol_func, 1, &description, result)); + return napi_ok; +} + +napi_status napi_create_error(napi_env env, + napi_value code, + napi_value msg, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, msg); + CHECK_ARG(env, result); + + JSValueRef exception{}; + JSValueRef args[] = { ToJSValue(msg) }; + napi_value error = ToNapi(JSObjectMakeError(env->context, 1, args, &exception)); + CHECK_JSC(env, exception); + + CHECK_NAPI(napi_set_error_code(env, error, code, nullptr)); + + *result = error; + return napi_ok; +} + +napi_status napi_create_type_error(napi_env env, + napi_value code, + napi_value msg, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, msg); + CHECK_ARG(env, result); + + napi_value global{}, error_ctor{}, error{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "TypeError", &error_ctor)); + CHECK_NAPI(napi_new_instance(env, error_ctor, 1, &msg, &error)); + CHECK_NAPI(napi_set_error_code(env, error, code, nullptr)); + + *result = error; + return napi_ok; +} + +napi_status napi_create_range_error(napi_env env, + napi_value code, + napi_value msg, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, msg); + CHECK_ARG(env, result); + + napi_value global{}, error_ctor{}, error{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "RangeError", &error_ctor)); + CHECK_NAPI(napi_new_instance(env, error_ctor, 1, &msg, &error)); + CHECK_NAPI(napi_set_error_code(env, error, code, nullptr)); + + *result = error; + return napi_ok; +} + +napi_status napi_typeof(napi_env env, napi_value value, napi_valuetype* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + // JSC does not support BigInt + JSType valueType = JSValueGetType(env->context, ToJSValue(value)); + switch (valueType) { + case kJSTypeUndefined: *result = napi_undefined; break; + case kJSTypeNull: *result = napi_null; break; + case kJSTypeBoolean: *result = napi_boolean; break; + case kJSTypeNumber: *result = napi_number; break; + case kJSTypeString: *result = napi_string; break; + case kJSTypeSymbol: *result = napi_symbol; break; + default: + JSObjectRef object{ToJSObject(env, value)}; + if (JSObjectIsFunction(env->context, object)) { + *result = napi_function; + } else { + NativeInfo* info = NativeInfo::Get(object); + if (info != nullptr && info->Type() == NativeType::External) { + *result = napi_external; + } else { + *result = napi_object; + } + } + break; + } + + return napi_ok; +} + +napi_status napi_get_undefined(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = ToNapi(JSValueMakeUndefined(env->context)); + return napi_ok; +} + +napi_status napi_get_null(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = ToNapi(JSValueMakeNull(env->context)); + return napi_ok; +} + +napi_status napi_get_cb_info(napi_env env, // [in] NAPI environment handle + napi_callback_info cbinfo, // [in] Opaque callback-info handle + size_t* argc, // [in-out] Specifies the size of the provided argv array + // and receives the actual count of args. + napi_value* argv, // [out] Array of values + napi_value* this_arg, // [out] Receives the JS 'this' arg for the call + void** data) { // [out] Receives the data pointer for the callback. + CHECK_ENV(env); + CHECK_ARG(env, cbinfo); + + if (argv != nullptr) { + CHECK_ARG(env, argc); + + size_t i{0}; + size_t min{std::min(*argc, static_cast(cbinfo->argc))}; + + for (; i < min; i++) { + argv[i] = cbinfo->argv[i]; + } + + if (i < *argc) { + for (; i < *argc; i++) { + argv[i] = ToNapi(JSValueMakeUndefined(env->context)); + } + } + } + + if (argc != nullptr) { + *argc = cbinfo->argc; + } + + if (this_arg != nullptr) { + *this_arg = cbinfo->thisArg; + } + + if (data != nullptr) { + *data = cbinfo->data; + } + + return napi_ok; +} + +napi_status napi_get_new_target(napi_env env, + napi_callback_info cbinfo, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, cbinfo); + CHECK_ARG(env, result); + + *result = cbinfo->newTarget; + return napi_ok; +} + +napi_status napi_call_function(napi_env env, + napi_value recv, + napi_value func, + size_t argc, + const napi_value* argv, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, recv); + if (argc > 0) { + CHECK_ARG(env, argv); + } + + JSValueRef exception{}; + JSValueRef return_value{JSObjectCallAsFunction( + env->context, + ToJSObject(env, func), + JSValueIsUndefined(env->context, ToJSValue(recv)) ? nullptr : ToJSObject(env, recv), + argc, + ToJSValues(argv), + &exception)}; + CHECK_JSC(env, exception); + + if (result != nullptr) { + *result = ToNapi(return_value); + } + + return napi_ok; +} + +napi_status napi_get_global(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = ToNapi(JSContextGetGlobalObject(env->context)); + return napi_ok; +} + +napi_status napi_throw(napi_env env, napi_value error) { + CHECK_ENV(env); + napi_status status{napi_set_exception(env, ToJSValue(error))}; + assert(status == napi_pending_exception); + return napi_ok; +} + +napi_status napi_throw_error(napi_env env, + const char* code, + const char* msg) { + CHECK_ENV(env); + napi_value code_value{ToNapi(JSValueMakeString(env->context, JSString(code)))}; + napi_value msg_value{ToNapi(JSValueMakeString(env->context, JSString(msg)))}; + napi_value error{}; + CHECK_NAPI(napi_create_error(env, code_value, msg_value, &error)); + return napi_throw(env, error); +} + +napi_status napi_throw_type_error(napi_env env, + const char* code, + const char* msg) { + CHECK_ENV(env); + napi_value code_value{ToNapi(JSValueMakeString(env->context, JSString(code)))}; + napi_value msg_value{ToNapi(JSValueMakeString(env->context, JSString(msg)))}; + napi_value error{}; + CHECK_NAPI(napi_create_type_error(env, code_value, msg_value, &error)); + return napi_throw(env, error); +} + +napi_status napi_throw_range_error(napi_env env, + const char* code, + const char* msg) { + CHECK_ENV(env); + napi_value code_value{ToNapi(JSValueMakeString(env->context, JSString(code)))}; + napi_value msg_value{ToNapi(JSValueMakeString(env->context, JSString(msg)))}; + napi_value error{}; + CHECK_NAPI(napi_create_range_error(env, code_value, msg_value, &error)); + return napi_throw(env, error); +} + +napi_status napi_is_error(napi_env env, napi_value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + napi_value global{}, error_ctor{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Error", &error_ctor)); + CHECK_NAPI(napi_instanceof(env, value, error_ctor, result)); + + return napi_ok; +} + +napi_status napi_get_value_double(napi_env env, napi_value value, double* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + JSValueRef exception{}; + *result = JSValueToNumber(env->context, ToJSValue(value), &exception); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_get_value_int32(napi_env env, napi_value value, int32_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + JSValueRef exception{}; + double number = JSValueToNumber(env->context, ToJSValue(value), &exception); + + if (number > INT_MAX) { + *result = -1; + } else { + *result = static_cast(number); + } + + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_get_value_uint32(napi_env env, napi_value value, uint32_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + JSValueRef exception{}; + *result = static_cast(JSValueToNumber(env->context, ToJSValue(value), &exception)); + + double number = JSValueToNumber(env->context, ToJSValue(value), &exception); + if (number > UINT32_MAX) { + *result = -1; + } else { + *result = static_cast(number); + } + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_get_value_int64(napi_env env, napi_value value, int64_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + JSValueRef exception{}; + double number = JSValueToNumber(env->context, ToJSValue(value), &exception); + CHECK_JSC(env, exception); + + if (std::isfinite(number)) { + *result = static_cast(number); + } else { + *result = 0; + } + + return napi_ok; +} + +napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + *result = JSValueToBoolean(env->context, ToJSValue(value)); + return napi_ok; +} + +// Copies a JavaScript string into a LATIN-1 string buffer. The result is the +// number of bytes (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in bytes) +// via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_latin1(napi_env env, + napi_value value, + char* buf, + size_t bufsize, + size_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + + JSValueRef exception{}; + JSString string{ToJSString(env, value, &exception)}; + CHECK_JSC(env, exception); + + if (buf == nullptr) { + *result = string.LengthLatin1(); + } else { + string.CopyToLatin1(buf, bufsize, result); + } + + return napi_ok; +} + +// Copies a JavaScript string into a UTF-8 string buffer. The result is the +// number of bytes (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in bytes) +// via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_utf8(napi_env env, + napi_value value, + char* buf, + size_t bufsize, + size_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + + JSValueRef exception{}; + JSString string{ToJSString(env, value, &exception)}; + CHECK_JSC(env, exception); + + if (buf == nullptr) { + *result = string.LengthUTF8(); + } else { + string.CopyToUTF8(buf, bufsize, result); + } + + return napi_ok; +} + +// Copies a JavaScript string into a UTF-16 string buffer. The result is the +// number of 2-byte code units (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in 2-byte +// code units) via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_utf16(napi_env env, + napi_value value, + char16_t* buf, + size_t bufsize, + size_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + + JSValueRef exception{}; + JSString string{ToJSString(env, value, &exception)}; + CHECK_JSC(env, exception); + + if (buf == nullptr) { + *result = string.Length(); + } else { + static_assert(sizeof(char16_t) == sizeof(JSChar)); + string.CopyTo(reinterpret_cast(buf), bufsize, result); + } + + return napi_ok; +} + +napi_status napi_coerce_to_bool(napi_env env, + napi_value value, + napi_value* result) { + CHECK_ARG(env, result); + *result = ToNapi(JSValueMakeBoolean(env->context, + JSValueToBoolean(env->context, ToJSValue(value)))); + return napi_ok; +} + +napi_status napi_coerce_to_number(napi_env env, + napi_value value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + JSValueRef exception{}; + double number{JSValueToNumber(env->context, ToJSValue(value), &exception)}; + CHECK_JSC(env, exception); + + *result = ToNapi(JSValueMakeNumber(env->context, number)); + return napi_ok; +} + +napi_status napi_coerce_to_object(napi_env env, + napi_value value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + JSValueRef exception{}; + *result = ToNapi(JSValueToObject(env->context, ToJSValue(value), &exception)); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_coerce_to_string(napi_env env, + napi_value value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + JSValueRef exception{}; + JSString string{ToJSString(env, value, &exception)}; + CHECK_JSC(env, exception); + + *result = ToNapi(JSValueMakeString(env->context, string)); + return napi_ok; +} + +napi_status napi_wrap(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result) { + CHECK_ENV(env); + CHECK_ARG(env, js_object); + if (result != nullptr) { + CHECK_ARG(env, finalize_cb); + } + + WrapperInfo* info{}; + CHECK_NAPI(WrapperInfo::Wrap(env, js_object, &info)); + RETURN_STATUS_IF_FALSE(env, info->Data() == nullptr, napi_invalid_arg); + + info->Data(native_object); + + if (finalize_cb != nullptr) { + info->AddFinalizer([finalize_cb, finalize_hint](WrapperInfo* info) { + finalize_cb(info->Env(), info->Data(), finalize_hint); + }); + } + + if (result != nullptr) { + CHECK_NAPI(napi_create_reference(env, js_object, 0, result)); + } + + return napi_ok; +} + +napi_status napi_unwrap(napi_env env, napi_value js_object, void** result) { + CHECK_ENV(env); + CHECK_ARG(env, js_object); + + WrapperInfo* info{}; + CHECK_NAPI(WrapperInfo::Unwrap(env, js_object, &info)); + RETURN_STATUS_IF_FALSE(env, info != nullptr && info->Data() != nullptr, napi_invalid_arg); + + *result = info->Data(); + return napi_ok; +} + +napi_status napi_remove_wrap(napi_env env, napi_value js_object, void** result) { + CHECK_ENV(env); + CHECK_ARG(env, js_object); + + // Once an object is wrapped, it stays wrapped in order to support finalizer callbacks. + + WrapperInfo* info{}; + CHECK_NAPI(WrapperInfo::Unwrap(env, js_object, &info)); + RETURN_STATUS_IF_FALSE(env, info != nullptr && info->Data() != nullptr, napi_invalid_arg); + + *result = info->Data(); + info->Data(nullptr); + + return napi_ok; +} + +napi_status napi_create_external(napi_env env, + void* data, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + CHECK_NAPI(ExternalInfo::Create(env, data, finalize_cb, finalize_hint, result)); + return napi_ok; +} + +napi_status napi_get_value_external(napi_env env, napi_value value, void** result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + ExternalInfo* info = NativeInfo::Get(ToJSObject(env, value)); + *result = (info != nullptr && info->Type() == NativeType::External) ? info->Data() : nullptr; + return napi_ok; +} + +// Set initial_refcount to 0 for a weak reference, >0 for a strong reference. +napi_status napi_create_reference(napi_env env, + napi_value value, + uint32_t initial_refcount, + napi_ref* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + napi_ref__* ref{new napi_ref__{value, initial_refcount}}; + if (ref == nullptr) { + return napi_set_last_error(env, napi_generic_failure); + } + + ref->init(env); + *result = ref; + + return napi_ok; +} + +// Deletes a reference. The referenced value is released, and may be GC'd +// unless there are other references to it. +napi_status napi_delete_reference(napi_env env, napi_ref ref) { + CHECK_ENV(env); + CHECK_ARG(env, ref); + + ref->deinit(env); + delete ref; + + return napi_ok; +} + +// Increments the reference count, optionally returning the resulting count. +// After this call the reference will be a strong reference because its refcount +// is >0, and the referenced object is effectively "pinned". Calling this when +// the refcount is 0 and the target is unavailable results in an error. +napi_status napi_reference_ref(napi_env env, napi_ref ref, uint32_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, ref); + + ref->ref(env); + if (result != nullptr) { + *result = ref->count(); + } + + return napi_ok; +} + +// Decrements the reference count, optionally returning the resulting count. +// If the result is 0 the reference is now weak and the object may be GC'd at +// any time if there are no other references. Calling this when the refcount +// is already 0 results in an error. +napi_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, ref); + + ref->unref(env); + if (result != nullptr) { + *result = ref->count(); + } + + return napi_ok; +} + +// Attempts to get a referenced value. If the reference is weak, the value +// might no longer be available, in that case the call is still successful but +// the result is NULL. +napi_status napi_get_reference_value(napi_env env, + napi_ref ref, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, ref); + CHECK_ARG(env, result); + + *result = ref->value(env); + return napi_ok; +} + +// Stub implementation of handle scope apis for JSC. +napi_status napi_open_handle_scope(napi_env env, + napi_handle_scope* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = reinterpret_cast(1); + return napi_ok; +} + +// Stub implementation of handle scope apis for JSC. +napi_status napi_close_handle_scope(napi_env env, + napi_handle_scope scope) { + CHECK_ENV(env); + CHECK_ARG(env, scope); + return napi_ok; +} + +// Stub implementation of handle scope apis for JSC. +napi_status napi_open_escapable_handle_scope(napi_env env, + napi_escapable_handle_scope* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = reinterpret_cast(1); + return napi_ok; +} + +// Stub implementation of handle scope apis for JSC. +napi_status napi_close_escapable_handle_scope(napi_env env, + napi_escapable_handle_scope scope) { + CHECK_ENV(env); + CHECK_ARG(env, scope); + return napi_ok; +} + +// Stub implementation of handle scope apis for JSC. +// This one will return escapee value as this is called from leveldown db. +napi_status napi_escape_handle(napi_env env, + napi_escapable_handle_scope scope, + napi_value escapee, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, scope); + CHECK_ARG(env, escapee); + CHECK_ARG(env, result); + *result = escapee; + return napi_ok; +} + +napi_status napi_new_instance(napi_env env, + napi_value constructor, + size_t argc, + const napi_value* argv, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, constructor); + if (argc > 0) { + CHECK_ARG(env, argv); + } + CHECK_ARG(env, result); + + JSValueRef exception{}; + *result = ToNapi(JSObjectCallAsConstructor( + env->context, + ToJSObject(env, constructor), + argc, + ToJSValues(argv), + &exception)); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_instanceof(napi_env env, + napi_value object, + napi_value constructor, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, object); + CHECK_ARG(env, result); + + JSValueRef exception{}; + *result = JSValueIsInstanceOfConstructor( + env->context, + ToJSValue(object), + ToJSObject(env, constructor), + &exception); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_is_exception_pending(napi_env env, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = (env->last_exception != nullptr); + return napi_ok; +} + +napi_status napi_get_and_clear_last_exception(napi_env env, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + if (env->last_exception == nullptr) { + return napi_get_undefined(env, result); + } else { + *result = ToNapi(env->last_exception); + env->last_exception = nullptr; + } + + return napi_clear_last_error(env); +} + +napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + JSValueRef exception{}; + JSTypedArrayType type{JSValueGetTypedArrayType(env->context, ToJSValue(value), &exception)}; + CHECK_JSC(env, exception); + + *result = (type == kJSTypedArrayTypeArrayBuffer); + return napi_ok; +} + +napi_status napi_create_arraybuffer(napi_env env, + size_t byte_length, + void** data, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *data = malloc(byte_length); + JSValueRef exception{}; + *result = ToNapi(JSObjectMakeArrayBufferWithBytesNoCopy( + env->context, + *data, + byte_length, + [](void* bytes, void* deallocatorContext) { + free(bytes); + }, + nullptr, + &exception)); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_create_external_arraybuffer(napi_env env, + void* external_data, + size_t byte_length, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + CHECK_NAPI(ExternalArrayBufferInfo::Create(env, external_data, byte_length, finalize_cb, finalize_hint, result)); + return napi_ok; +} + +napi_status napi_get_arraybuffer_info(napi_env env, + napi_value arraybuffer, + void** data, + size_t* byte_length) { + CHECK_ENV(env); + CHECK_ARG(env, arraybuffer); + + JSValueRef exception{}; + + if (data != nullptr) { + *data = JSObjectGetArrayBufferBytesPtr(env->context, ToJSObject(env, arraybuffer), &exception); + CHECK_JSC(env, exception); + } + + if (byte_length != nullptr) { + *byte_length = JSObjectGetArrayBufferByteLength(env->context, ToJSObject(env, arraybuffer), &exception); + CHECK_JSC(env, exception); + } + + return napi_ok; +} + +napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + JSValueRef exception{}; + JSTypedArrayType type{JSValueGetTypedArrayType(env->context, ToJSValue(value), &exception)}; + CHECK_JSC(env, exception); + + *result = (type != kJSTypedArrayTypeNone && type != kJSTypedArrayTypeArrayBuffer); + return napi_ok; +} + +napi_status napi_create_typedarray(napi_env env, + napi_typedarray_type type, + size_t length, + napi_value arraybuffer, + size_t byte_offset, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, arraybuffer); + CHECK_ARG(env, result); + + JSTypedArrayType jsType{}; + switch (type) { + case napi_int8_array: + jsType = kJSTypedArrayTypeInt8Array; + break; + case napi_uint8_array: + jsType = kJSTypedArrayTypeUint8Array; + break; + case napi_uint8_clamped_array: + jsType = kJSTypedArrayTypeUint8ClampedArray; + break; + case napi_int16_array: + jsType = kJSTypedArrayTypeInt16Array; + break; + case napi_uint16_array: + jsType = kJSTypedArrayTypeUint16Array; + break; + case napi_int32_array: + jsType = kJSTypedArrayTypeInt32Array; + break; + case napi_uint32_array: + jsType = kJSTypedArrayTypeUint32Array; + break; + case napi_float32_array: + jsType = kJSTypedArrayTypeFloat32Array; + break; + case napi_float64_array: + jsType = kJSTypedArrayTypeFloat64Array; + break; + default: + return napi_set_last_error(env, napi_invalid_arg); + } + + JSValueRef exception{}; + *result = ToNapi(JSObjectMakeTypedArrayWithArrayBufferAndOffset( + env->context, + jsType, + ToJSObject(env, arraybuffer), + byte_offset, + length, + &exception)); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_get_typedarray_info(napi_env env, + napi_value typedarray, + napi_typedarray_type* type, + size_t* length, + void** data, + napi_value* arraybuffer, + size_t* byte_offset) { + CHECK_ENV(env); + CHECK_ARG(env, typedarray); + + JSValueRef exception{}; + + JSObjectRef object{ToJSObject(env, typedarray)}; + + if (type != nullptr) { + JSTypedArrayType typedArrayType{JSValueGetTypedArrayType(env->context, object, &exception)}; + CHECK_JSC(env, exception); + + switch (typedArrayType) { + case kJSTypedArrayTypeInt8Array: + *type = napi_int8_array; + break; + case kJSTypedArrayTypeUint8Array: + *type = napi_uint8_array; + break; + case kJSTypedArrayTypeUint8ClampedArray: + *type = napi_uint8_clamped_array; + break; + case kJSTypedArrayTypeInt16Array: + *type = napi_int16_array; + break; + case kJSTypedArrayTypeUint16Array: + *type = napi_uint16_array; + break; + case kJSTypedArrayTypeInt32Array: + *type = napi_int32_array; + break; + case kJSTypedArrayTypeUint32Array: + *type = napi_uint32_array; + break; + case kJSTypedArrayTypeFloat32Array: + *type = napi_float32_array; + break; + case kJSTypedArrayTypeFloat64Array: + *type = napi_float64_array; + break; + default: + return napi_set_last_error(env, napi_generic_failure); + } + } + + if (length != nullptr) { + *length = JSObjectGetTypedArrayLength(env->context, object, &exception); + CHECK_JSC(env, exception); + } + + if (data != nullptr || byte_offset != nullptr) { + size_t data_byte_offset{JSObjectGetTypedArrayByteOffset(env->context, object, &exception)}; + CHECK_JSC(env, exception); + + if (data != nullptr) { + *data = static_cast(JSObjectGetTypedArrayBytesPtr(env->context, object, &exception)) + data_byte_offset; + CHECK_JSC(env, exception); + } + + if (byte_offset != nullptr) { + *byte_offset = data_byte_offset; + } + } + + if (arraybuffer != nullptr) { + *arraybuffer = ToNapi(JSObjectGetTypedArrayBuffer(env->context, object, &exception)); + CHECK_JSC(env, exception); + } + + return napi_ok; +} + +napi_status napi_create_dataview(napi_env env, + size_t byte_length, + napi_value arraybuffer, + size_t byte_offset, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, arraybuffer); + CHECK_ARG(env, result); + + napi_value global{}, dataview_ctor{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "DataView", &dataview_ctor)); + + napi_value byte_offset_value{}, byte_length_value{}; + napi_create_double(env, static_cast(byte_offset), &byte_offset_value); + napi_create_double(env, static_cast(byte_length), &byte_length_value); + napi_value args[] = { arraybuffer, byte_offset_value, byte_length_value }; + CHECK_NAPI(napi_new_instance(env, dataview_ctor, 3, args, result)); + + return napi_ok; +} + +napi_status napi_is_dataview(napi_env env, napi_value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + napi_value global{}, dataview_ctor{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "DataView", &dataview_ctor)); + CHECK_NAPI(napi_instanceof(env, value, dataview_ctor, result)); + + return napi_ok; +} + +napi_status napi_get_dataview_info(napi_env env, + napi_value dataview, + size_t* byte_length, + void** data, + napi_value* arraybuffer, + size_t* byte_offset) { + CHECK_ENV(env); + CHECK_ARG(env, dataview); + + if (byte_length != nullptr) { + napi_value value{}; + double doubleValue{}; + CHECK_NAPI(napi_get_named_property(env, dataview, "byteLength", &value)); + CHECK_NAPI(napi_get_value_double(env, value, &doubleValue)); + *byte_length = static_cast(doubleValue); + } + + if (data != nullptr) { + napi_value value{}; + CHECK_NAPI(napi_get_named_property(env, dataview, "buffer", &value)); + CHECK_NAPI(napi_get_arraybuffer_info(env, value, data, nullptr)); + } + + if (arraybuffer != nullptr) { + CHECK_NAPI(napi_get_named_property(env, dataview, "buffer", arraybuffer)); + } + + if (byte_offset != nullptr) { + napi_value value{}; + double doubleValue{}; + CHECK_NAPI(napi_get_named_property(env, dataview, "byteOffset", &value)); + CHECK_NAPI(napi_get_value_double(env, value, &doubleValue)); + *byte_offset = static_cast(doubleValue); + } + + return napi_ok; +} + +napi_status napi_get_version(napi_env env, uint32_t* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = NAPI_VERSION; + return napi_ok; +} + +napi_status napi_create_promise(napi_env env, + napi_deferred* deferred, + napi_value* promise) { + CHECK_ENV(env); + CHECK_ARG(env, deferred); + CHECK_ARG(env, promise); + + napi_value global{}, promise_ctor{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Promise", &promise_ctor)); + + struct Wrapper { + napi_value resolve{}; + napi_value reject{}; + + static napi_value Callback(napi_env env, napi_callback_info cbinfo) { + Wrapper* wrapper = reinterpret_cast(cbinfo->data); + wrapper->resolve = cbinfo->argv[0]; + wrapper->reject = cbinfo->argv[1]; + return nullptr; + } + } wrapper; + + napi_value executor{}; + CHECK_NAPI(napi_create_function(env, "executor", NAPI_AUTO_LENGTH, Wrapper::Callback, &wrapper, &executor)); + CHECK_NAPI(napi_new_instance(env, promise_ctor, 1, &executor, promise)); + + napi_value deferred_value{}; + CHECK_NAPI(napi_create_object(env, &deferred_value)); + CHECK_NAPI(napi_set_named_property(env, deferred_value, "resolve", wrapper.resolve)); + CHECK_NAPI(napi_set_named_property(env, deferred_value, "reject", wrapper.reject)); + + napi_ref deferred_ref{}; + CHECK_NAPI(napi_create_reference(env, deferred_value, 1, &deferred_ref)); + *deferred = reinterpret_cast(deferred_ref); + + return napi_ok; +} + +napi_status napi_resolve_deferred(napi_env env, + napi_deferred deferred, + napi_value resolution) { + CHECK_ENV(env); + CHECK_ARG(env, deferred); + + napi_ref deferred_ref{reinterpret_cast(deferred)}; + napi_value undefined{}, deferred_value{}, resolve{}; + CHECK_NAPI(napi_get_undefined(env, &undefined)); + CHECK_NAPI(napi_get_reference_value(env, deferred_ref, &deferred_value)); + CHECK_NAPI(napi_get_named_property(env, deferred_value, "resolve", &resolve)); + CHECK_NAPI(napi_call_function(env, undefined, resolve, 1, &resolution, nullptr)); + CHECK_NAPI(napi_delete_reference(env, deferred_ref)); + + return napi_ok; +} + +napi_status napi_reject_deferred(napi_env env, + napi_deferred deferred, + napi_value rejection) { + CHECK_ENV(env); + CHECK_ARG(env, deferred); + + napi_ref deferred_ref{reinterpret_cast(deferred)}; + napi_value undefined{}, deferred_value{}, reject{}; + CHECK_NAPI(napi_get_undefined(env, &undefined)); + CHECK_NAPI(napi_get_reference_value(env, deferred_ref, &deferred_value)); + CHECK_NAPI(napi_get_named_property(env, deferred_value, "reject", &reject)); + CHECK_NAPI(napi_call_function(env, undefined, reject, 1, &rejection, nullptr)); + CHECK_NAPI(napi_delete_reference(env, deferred_ref)); + + return napi_ok; +} + +napi_status napi_is_promise(napi_env env, + napi_value promise, + bool* is_promise) { + CHECK_ENV(env); + CHECK_ARG(env, promise); + CHECK_ARG(env, is_promise); + + napi_value global{}, promise_ctor{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Promise", &promise_ctor)); + CHECK_NAPI(napi_instanceof(env, promise, promise_ctor, is_promise)); + + return napi_ok; +} + +napi_status napi_run_script(napi_env env, + napi_value script, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, script); + CHECK_ARG(env, result); + + JSValueRef exception{}; + + JSString script_str{ToJSString(env, script, &exception)}; + CHECK_JSC(env, exception); + + *result = ToNapi(JSEvaluateScript( + env->context, script_str, nullptr, nullptr, 0, &exception)); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_run_script_source(napi_env env, + napi_value script, + const char* source_url, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, script); + CHECK_ARG(env, result); + + JSValueRef exception{}; + + JSString script_str{ToJSString(env, script, &exception)}; + CHECK_JSC(env, exception); + + JSValueRef return_value{JSEvaluateScript( + env->context, script_str, nullptr, JSString(source_url), 0, &exception)}; + CHECK_JSC(env, exception); + + if (result != nullptr) { + *result = ToNapi(return_value); + } + + return napi_ok; +} + +napi_status napi_add_finalizer(napi_env env, + napi_value js_object, + void* finalize_data, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result) { + CHECK_ENV(env); + CHECK_ARG(env, js_object); + CHECK_ARG(env, finalize_cb); + + WrapperInfo* info{}; + CHECK_NAPI(WrapperInfo::Wrap(env, js_object, &info)); + + info->AddFinalizer([finalize_cb, finalize_data, finalize_hint](WrapperInfo* info) { + finalize_cb(info->Env(), finalize_data, finalize_hint); + }); + + if (result != nullptr) { + CHECK_NAPI(napi_create_reference(env, js_object, 0, result)); + } + + return napi_ok; +} + +napi_status napi_adjust_external_memory(napi_env env, + int64_t change_in_bytes, + int64_t* adjusted_value) { + CHECK_ENV(env); + CHECK_ARG(env, adjusted_value); + + // TODO: Determine if JSC needs or is able to do anything here + // For now, we can lie and say that we always adjusted more memory + *adjusted_value = change_in_bytes; + + return napi_ok; +} + +napi_status napi_create_date(napi_env env, + double time, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + auto jsTime = JSValueMakeNumber(env->context, time); + JSValueRef exception {}; + auto jsDate = JSObjectMakeDate(env->context, 1, &jsTime, &exception); + CHECK_JSC(env, exception); + + *result = ToNapi(jsDate); + return napi_ok; +} + +napi_status napi_is_date(napi_env env, + napi_value value, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + *result = JSValueIsDate(env->context, ToJSValue(value)); + + return napi_ok; +} + +napi_status napi_get_date_value(napi_env env, + napi_value value, + double* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + JSValueRef exception{}; + // we don't piggyback off of napi_get_value_double because that function + // SHOULDN'T coerce. + *result = JSValueToNumber(env->context, ToJSValue(value), &exception); + CHECK_JSC(env, exception); + + return napi_ok; +} + +napi_status napi_get_all_property_names(napi_env env, + napi_value object, + napi_key_collection_mode key_mode, + napi_key_filter key_filter, + napi_key_conversion key_conversion, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, object); + CHECK_ARG(env, result); + + napi_value array{}, push{}, global{}, object_ctor{}; + CHECK_NAPI(napi_create_array(env, &array)); + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Object", &object_ctor)); + CHECK_NAPI(napi_get_named_property(env, array, "push", &push)); + + std::vector getter_methods; + if (!(key_filter & napi_key_skip_strings)) { + napi_value method{}; + CHECK_NAPI(napi_get_named_property(env, object_ctor, "getOwnPropertyNames", &method)); + getter_methods.push_back(method); + } + if (!(key_filter & napi_key_skip_symbols)) { + napi_value method{}; + CHECK_NAPI(napi_get_named_property(env, object_ctor, "getOwnPropertySymbols", &method)); + getter_methods.push_back(method); + } + + napi_value current = object; + while (true) { + for (napi_value method : getter_methods) { + napi_value properties{}; + // Object.getOwnProperty[Names|Symbols](current) + CHECK_NAPI(napi_call_function(env, object_ctor, method, 1, ¤t, &properties)); + uint32_t length = 0; + CHECK_NAPI(napi_get_array_length(env, properties, &length)); + for (uint32_t i = 0; i != length; ++i) { + napi_value key{}; + CHECK_NAPI(napi_get_element(env, properties, i, &key)); + // TODO: coerce to number if napi_key_keep_numbers + // TODO: filter writable/enumerable/configurable +#if 0 + napi_value descriptor{}; + std::array args { current, key }; + // Object.getOwnPropertyDescriptor(current, key) + CHECK_NAPI(napi_call_function(env, object_ctor, getOwnPropertyDescriptor, 2, args.data(), &descriptor)); +#endif + CHECK_NAPI(napi_call_function(env, array, push, 1, &key, NULL)); + } + } + if (key_mode == napi_key_own_only) break; + napi_value next{}; + CHECK_NAPI(napi_get_prototype(env, current, &next)); + napi_valuetype next_type; + CHECK_NAPI(napi_typeof(env, next, &next_type)); + if (next_type == napi_null) break; + current = next; + }; + + *result = array; + + return napi_ok; +} + + +napi_status napi_object_freeze(napi_env env, + napi_value object) { + napi_value global{}, object_ctor{}, freeze{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Object", &object_ctor)); + CHECK_NAPI(napi_get_named_property(env, object_ctor, "freeze", &freeze)); + CHECK_NAPI(napi_call_function(env, object_ctor, freeze, 1, &object, nullptr)); + return napi_ok; +} + +napi_status napi_object_seal(napi_env env, + napi_value object) { + napi_value global{}, object_ctor{}, seal{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Object", &object_ctor)); + CHECK_NAPI(napi_get_named_property(env, object_ctor, "seal", &seal)); + CHECK_NAPI(napi_call_function(env, object_ctor, seal, 1, &object, nullptr)); + return napi_ok; +} \ No newline at end of file diff --git a/NativeScript/napi/android/jsc/jsc-api.h b/NativeScript/napi/android/jsc/jsc-api.h new file mode 100644 index 000000000..d22aa1534 --- /dev/null +++ b/NativeScript/napi/android/jsc/jsc-api.h @@ -0,0 +1,97 @@ +// +// Created by Ammar Ahmed on 01/12/2024. +// + +#ifndef TEST_APP_JSC_API_H +#define TEST_APP_JSC_API_H + +#include "js_native_api.h" +#include "js_native_api_types.h" +#include +#include +#include +#include +#include + +struct napi_env__ { + JSGlobalContextRef context{}; + JSValueRef last_exception{}; + napi_extended_error_info last_error{nullptr, nullptr, 0, napi_ok}; + std::unordered_set active_ref_values{}; + std::list strong_refs{}; + + JSValueRef constructor_info_symbol{}; + JSValueRef function_info_symbol{}; + JSValueRef reference_info_symbol{}; + JSValueRef wrapper_info_symbol{}; + + const std::thread::id thread_id{std::this_thread::get_id()}; + + napi_env__(JSGlobalContextRef context) : context{context} { + napi_envs[context] = this; + JSGlobalContextRetain(context); + init_symbol(constructor_info_symbol, "NS_ConstructorInfo"); + init_symbol(function_info_symbol, "NS_FunctionInfo"); + init_symbol(reference_info_symbol, "NS_ReferenceInfo"); + init_symbol(wrapper_info_symbol, "NS_WrapperInfo"); + } + + ~napi_env__() { + deinit_refs(); + JSGlobalContextRelease(context); + deinit_symbol(wrapper_info_symbol); + deinit_symbol(reference_info_symbol); + deinit_symbol(function_info_symbol); + deinit_symbol(constructor_info_symbol); + napi_envs.erase(context); + } + + static napi_env get(JSGlobalContextRef context) { + auto it = napi_envs.find(context); + if (it != napi_envs.end()) { + return it->second; + } else { + return nullptr; + } + } + +private: + static inline std::unordered_map napi_envs{}; + void deinit_refs(); + void init_symbol(JSValueRef& symbol, const char* description); + void deinit_symbol(JSValueRef symbol); +}; + +#define RETURN_STATUS_IF_FALSE(env, condition, status) \ + do { \ + if (!(condition)) { \ + return napi_set_last_error((env), (status)); \ + } \ + } while (0) + +#define CHECK_ENV(env) \ + do { \ + if ((env) == nullptr) { \ + return napi_invalid_arg; \ + } \ + } while (0) + +#define CHECK_ARG(env, arg) \ + RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg) + +#define CHECK_JSC(env, exception) \ + do { \ + if ((exception) != nullptr) { \ + return napi_set_exception(env, exception); \ + } \ + } while (0) + +// This does not call napi_set_last_error because the expression +// is assumed to be a NAPI function call that already did. +#define CHECK_NAPI(expr) \ + do { \ + napi_status status = (expr); \ + if (status != napi_ok) return status; \ + } while (0) + +#endif //TEST_APP_JSC_API_H diff --git a/NativeScript/napi/android/jsc/jsr.cpp b/NativeScript/napi/android/jsc/jsr.cpp new file mode 100644 index 000000000..d5b66d581 --- /dev/null +++ b/NativeScript/napi/android/jsc/jsr.cpp @@ -0,0 +1,90 @@ +#include "jsr.h" + +napi_status js_create_runtime(napi_runtime *runtime) { + if (!runtime) return napi_invalid_arg; + *runtime = (napi_runtime) JSGlobalContextCreateInGroup(nullptr, nullptr); + return napi_ok; +} + +napi_status js_create_napi_env(napi_env* env, napi_runtime runtime) { + if (env == nullptr) return napi_invalid_arg; + + *env = new napi_env__((JSGlobalContextRef) runtime); + JSGlobalContextRelease((JSGlobalContextRef) runtime); + + napi_value gc; + napi_create_function(*env, "gc", strlen("gc"), [](napi_env env, napi_callback_info info) -> napi_value { + JSGarbageCollect(env->context); + napi_value undefined; + napi_get_undefined(env, &undefined); + return undefined; + }, nullptr, &gc); + napi_value global; + napi_get_global(*env, &global); + napi_set_named_property(*env, global, "gc", gc); + + + return napi_ok; + +} + +napi_status js_set_runtime_flags(const char* flags) { + return napi_ok; +} + +napi_status js_lock_env(napi_env env) { + return napi_ok; +} + +napi_status js_unlock_env(napi_env env) { + return napi_ok; +} + +napi_status js_free_napi_env(napi_env env) { + if (env == nullptr) return napi_invalid_arg; + delete env; + return napi_ok; +} + +napi_status js_free_runtime(napi_runtime runtime) { +// JSContextGroupRelease((JSContextGroupRef) runtime); + return napi_ok; +} + +napi_status js_execute_script(napi_env env, + napi_value script, + const char *file, + napi_value *result) { + + return napi_run_script_source(env, script, file, result); + +} + +napi_status js_execute_pending_jobs(napi_env env) { + return napi_ok; +} + +napi_status js_get_engine_ptr(napi_env env, int64_t *engine_ptr) { + *engine_ptr = (int64_t) 0; + return napi_ok; +} + +napi_status js_adjust_external_memory(napi_env env, int64_t changeInBytes, int64_t *externalMemory) { + return napi_ok; +} + +napi_status js_cache_script(napi_env env, const char *source, const char *file) { + return napi_ok; +} + +napi_status js_run_cached_script(napi_env env, const char *file, napi_value script, void *cache, + napi_value *result) { + return napi_ok; +} + + +napi_status js_get_runtime_version(napi_env env, napi_value* version) { + napi_create_string_utf8(env, "JSC", NAPI_AUTO_LENGTH, version); + + return napi_ok; +} diff --git a/NativeScript/napi/android/jsc/jsr.h b/NativeScript/napi/android/jsc/jsr.h new file mode 100644 index 000000000..3bbc53130 --- /dev/null +++ b/NativeScript/napi/android/jsc/jsr.h @@ -0,0 +1,32 @@ +// +// Created by Ammar Ahmed on 01/12/2024. +// + +#ifndef TEST_APP_JSR_H +#define TEST_APP_JSR_H + +#include "jsr_common.h" +#include "jsc-api.h" + +typedef struct napi_runtime__ *napi_runtime; + +class NapiScope { +public: + explicit NapiScope(napi_env env, bool openHandle = true) + : env_(env) + { +// napi_open_handle_scope(env_, &napiHandleScope_); + } + + ~NapiScope() { +// napi_close_handle_scope(env_, napiHandleScope_); + } + +private: + napi_env env_; + napi_handle_scope napiHandleScope_; +}; + +#define JSEnterScope + +#endif //TEST_APP_JSR_H diff --git a/NativeScript/napi/android/primjs/code_cache.cc b/NativeScript/napi/android/primjs/code_cache.cc new file mode 100644 index 000000000..1194bda05 --- /dev/null +++ b/NativeScript/napi/android/primjs/code_cache.cc @@ -0,0 +1,261 @@ +/** + * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. + * + * Use of this source code is governed by a MIT license that can be + * found in the LICENSE file in the root of the source tree. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#include "code_cache.h" + +#include + +#include +#include + +#if OS_ANDROID +#include "basic/log/logging.h" +#else +#define VLOGD(...) void((__VA_ARGS__)) +#endif // OS_ANDROID + +#ifdef PROFILE_CODECACHE +#define INCREASE(target) ++(target) +#else +#define INCREASE(target) +#endif // PROFILE_CODECACHE + +constexpr double CacheBlob::MAGIC; + +bool CacheBlob::insert(const std::string& filename, const uint8_t* data, + int length) { + // too large (more than half of MAX) cached data must be discarded + if (length == 0 || data == nullptr) { + INCREASE(expired_query_); + return false; + } + CachedData* target = nullptr; + const uint8_t* old_data = nullptr; + { + std::unique_lock lock(write_mutex_); + auto it = cache_map_.find(filename); + if (it != cache_map_.end()) { + target = it->second; + current_size_ -= target->length_; + remove_from_ranking_list(target); + + if (get_enough_space(length)) { + if (mode_ == kAppending) mode_ = kWriting; + target->length_ = length; + } else { + cache_map_.erase(it); + return false; + } + + } else if (get_enough_space(length)) { + target = new CachedData(length, nullptr, filename); + cache_map_[filename] = target; + if (mode_ == kAppending) { + if (append_vec_ == nullptr) { + append_vec_ = new CacheVector(); + } + append_vec_->push_back(target); + } + } else { + return false; + } + + old_data = target->data_; + target->data_ = data; + } + + heat_ranking_.push_back(target); + current_size_ += length; + if (old_data) { + delete[] old_data; + } + INCREASE(target->used_times_); + + return true; +} + +// That len will be not nullptr is ensured by the context. +const CachedData* CacheBlob::find(const std::string& filename, int* len) const { + if (!write_mutex_.try_lock()) { + INCREASE(missed_query_); + INCREASE(total_query_); + return empty_cache_.get(); + } + auto it = cache_map_.find(filename); + write_mutex_.unlock(); + CachedData* result = nullptr; + if (it != cache_map_.end()) { + // every time a cache is searched, its heat-ranking + // rises and thus the ranking list changes + result = it->second; + *len = result->length_; + INCREASE(result->used_times_); + } else { + *len = 0; + INCREASE(missed_query_); + } + INCREASE(total_query_); + + // NOTE: + // The result cached data is safe beyond this scope, + // because JS engines got this data and then use it + // consecutively in one thread. Tasks that insert new + // data for the same filename will only be posted after + // that cached data has been already used up. + return result; +} + +void CacheBlob::remove(const std::string& filename) { + auto it = cache_map_.find(filename); + if (it != cache_map_.end()) { + current_size_ -= it->second->length_; + delete it->second; + cache_map_.erase(it); + if (mode_ == kAppending) mode_ = kWriting; + } +} + +// cache file structure: +// Header: +// | magic number | --> 8 bytes +// Body : +// | file name size | --> 2 bytes +// | file name | --> x bytes +// | cache data length | --> 4 bytes +// | cache data | --> y bytes +// ... +void CacheBlob::output() { + if (mode_ == kAppending && append_vec_ == nullptr) return; + bool appending = mode_ == kAppending && append_vec_ != nullptr; + + FILE* file_out = appending ? fopen(target_path_.c_str(), "ab") + : fopen(target_path_.c_str(), "wb"); + if (file_out) { + if (appending) { + for (auto it : *append_vec_) write_cache_unit(file_out, it); + } else { + fwrite(&MAGIC, DOUBLE_SIZE, 1, file_out); + for (auto& it : cache_map_) write_cache_unit(file_out, it.second); + } + fclose(file_out); + VLOGD("codecache: output cache file %s succeed.\n", target_path_.c_str()); + } +} + +// steps to rebuild blob: +// 1. read and check magic number; +// 2. read 2 bytes to get the size (x) of file name; +// 3. read x bytes to get the file name; +// 4. read 4 bytes to get the size (y) of cache data; +// 5. read y bytes to get the actual content of cache data; +// 6. go back to step 2 until EOF +bool CacheBlob::input() { + FILE* file_in = fopen(target_path_.c_str(), "rb"); + + bool succeeded = false; + if (file_in) { + double maybe_magic; + fread(reinterpret_cast(&maybe_magic), DOUBLE_SIZE, 1, file_in); + // check whether file is valid. + if (maybe_magic == MAGIC) { + int c; + while ((c = fgetc(file_in)) != EOF) { + ungetc(c, file_in); + read_cache_unit(file_in); + } + mode_ = kAppending; + succeeded = true; + } + fclose(file_in); + } + return succeeded; +} + +#ifdef PROFILE_CODECACHE +void CacheBlob::dump_status(void* p) { + std::vector >* status_vec = + reinterpret_cast >*>(p); + std::sort(heat_ranking_.begin(), heat_ranking_.end(), CachedData::compare); + status_vec->push_back(std::pair("Total", total_query_)); + status_vec->push_back(std::pair("Missed", missed_query_)); + status_vec->push_back(std::pair("Expired", expired_query_)); + status_vec->push_back(std::pair( + "Updated", mode_ == kAppending && append_vec_ == nullptr ? 0 : 1)); + status_vec->push_back(std::pair("Size", current_size_)); + status_vec->push_back(std::pair("Heat Ranking, total ", + heat_ranking_.size())); + for (size_t i = 0; i < heat_ranking_.size(); ++i) { + CachedData* dt = heat_ranking_[i]; + status_vec->push_back( + std::pair(dt->file_name_, dt->used_times_)); + } +} +#endif // PROFILE_CODECACHE + +void CacheBlob::write_cache_unit(FILE* file_out, const CachedData* unit) { + // write file name + uint16_t size = static_cast(unit->file_name_.size()); + fwrite(static_cast(&size), SHORT_SIZE, 1, file_out); + fwrite(unit->file_name_.c_str(), 1, unit->file_name_.size(), file_out); + + uint32_t length = unit->length_; + fwrite(static_cast(&length), INT_SIZE, 1, file_out); + fwrite(unit->data_, 1, unit->length_, file_out); +} + +void CacheBlob::read_cache_unit(FILE* file_in) { + uint16_t filename_length; + fread(&filename_length, SHORT_SIZE, 1, file_in); + std::string name(filename_length, '\0'); + fread(&name[0], 1, filename_length, file_in); + + uint32_t data_length; + fread(&data_length, INT_SIZE, 1, file_in); + uint8_t* data = new uint8_t[data_length]; + fread(data, 1, data_length, file_in); + + CachedData* cd = new CachedData(static_cast(data_length), data, name); + + current_size_ += data_length; + heat_ranking_.push_back(cd); + cache_map_[name] = cd; +} + +void CacheBlob::remove_from_ranking_list(CachedData* target) { + for (size_t i = 0; i < heat_ranking_.size(); ++i) { + if (target == heat_ranking_[i]) { + heat_ranking_.erase(heat_ranking_.begin() + i); + break; + } + } +} + +// This is a vast-time-costing function +bool CacheBlob::get_enough_space(int data_size) { + // 0. check whether available space is enough + if (current_size_ + data_size <= max_capacity_) return true; + int size_needed = data_size + current_size_ - max_capacity_; + // 1. sort the heat_ranking_ + std::sort(heat_ranking_.begin(), heat_ranking_.end(), CachedData::compare); + // 2. pick CachedDatas + for (int i = heat_ranking_.size() - 1; i >= 0; --i) { + size_needed -= heat_ranking_[i]->length_; + if (size_needed <= 0) { + for (size_t j = i; j < heat_ranking_.size(); ++j) { + CachedData* it = heat_ranking_[j]; + remove(it->file_name_); + } + heat_ranking_.erase(heat_ranking_.begin() + i, heat_ranking_.end()); + return true; + } + } + return false; +} diff --git a/NativeScript/napi/android/primjs/code_cache.h b/NativeScript/napi/android/primjs/code_cache.h new file mode 100644 index 000000000..f10a44ee6 --- /dev/null +++ b/NativeScript/napi/android/primjs/code_cache.h @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. + * + * Use of this source code is governed by a MIT license that can be + * found in the LICENSE file in the root of the source tree. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_NAPI_COMMON_CODE_CACHE_H_ +#define SRC_NAPI_COMMON_CODE_CACHE_H_ + +#include +#include +#include +#include +#include + +struct CachedData { + CachedData() : length_(0), data_(nullptr), file_name_("") {} + + CachedData(int length, const uint8_t* data, const std::string& name) + : length_(length), data_(data), file_name_(name) {} + + // carefully copy and move objects of this class + ~CachedData() { + if (data_) delete[] data_; + } + + static bool compare(CachedData* left, CachedData* right) { + if (left->used_times_ > right->used_times_) { + return true; + } else if (left->used_times_ < right->used_times_) { + return false; + } else { + return left->length_ > right->length_; + } + } + + int used_times_ = 0; + int length_; + const uint8_t* data_; + const std::string file_name_; +}; + +typedef std::unordered_map CacheMap; +typedef std::vector CacheVector; +#define SHORT_SIZE 2 +#define INT_SIZE 4 +#define DOUBLE_SIZE 8 + +class CacheBlob { + private: + enum CacheMode { kWriting, kAppending }; + static constexpr double MAGIC = 3.14159265; + + public: + explicit CacheBlob(const std::string& path, int max_cap = 1 << 20) + : current_size_(0), + target_path_(path), + max_capacity_(max_cap), + empty_cache_(new CachedData) {} + + virtual ~CacheBlob() { + for (auto it : cache_map_) delete it.second; + if (append_vec_) delete append_vec_; + } + + // NOTE: modifications on CacheBlob can only happen in worker Thread. + bool insert(const std::string& filename, const uint8_t* data, int length); + const CachedData* find(const std::string& filename, int* len) const; + void remove(const std::string& filename); + void output(); + bool input(); + int size() const { return current_size_; } + +#ifdef PROFILE_CODECACHE + void dump_status(void* p); +#endif // PROFILE_CODECACHE + + private: + void write_cache_unit(FILE* file_out, const CachedData* unit); + void read_cache_unit(FILE* file_in); + void remove_from_ranking_list(CachedData* target); + // This is a vast-time-costing function + bool get_enough_space(int data_size); + + CacheMap cache_map_; + // ranking list for caches' frequency of being used. + CacheVector heat_ranking_; + int current_size_; + const std::string target_path_; + int max_capacity_; + mutable std::mutex write_mutex_; + std::unique_ptr empty_cache_; + +#ifdef PROFILE_CODECACHE + mutable int total_query_ = 0; + mutable int missed_query_ = 0; + mutable int expired_query_ = 0; +#endif // PROFILE_CODECACHE + CacheMode mode_ = kWriting; + CacheVector* append_vec_ = nullptr; +}; + +#endif // SRC_NAPI_COMMON_CODE_CACHE_H_ diff --git a/NativeScript/napi/android/primjs/include/basic/log/logging.h b/NativeScript/napi/android/primjs/include/basic/log/logging.h new file mode 100644 index 000000000..2cb633ee2 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/basic/log/logging.h @@ -0,0 +1,171 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_BASIC_LOG_LOGGING_H_ +#define SRC_BASIC_LOG_LOGGING_H_ + +#include +#include +#include + +#include "quickjs/include/base_export.h" + +#if defined(OS_ANDROID) +#include + +#define VLOGW(...) __android_log_print(ANDROID_LOG_WARN, "PRIMJS", __VA_ARGS__) +#define VLOGE(...) __android_log_print(ANDROID_LOG_ERROR, "PRIMJS", __VA_ARGS__) +#define VLOGI(...) __android_log_print(ANDROID_LOG_INFO, "PRIMJS", __VA_ARGS__) +#define VLOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PRIMJS", __VA_ARGS__) +#else +#include + +#define VLOGW(format, ...) \ + fprintf(stderr, "[PRIMJS] " format "\n", ##__VA_ARGS__) +#define VLOGE(format, ...) \ + fprintf(stderr, "[PRIMJS] " format "\n", ##__VA_ARGS__) +#define VLOGI(format, ...) \ + fprintf(stderr, "[PRIMJS] " format "\n", ##__VA_ARGS__) +#define VLOGD(format, ...) \ + fprintf(stderr, "[PRIMJS] " format "\n", ##__VA_ARGS__) +#endif + +namespace primjs { +namespace general { +namespace logging { + +class LogMessage; +void Log(LogMessage *msg); + +QJS_HIDE void SetMinLogLevel(int level); + +QJS_EXPORT int GetMinAllLogLevel(); + +#define PRIMJS_LOG_LEVEL_VERBOSE -1 +#define PRIMJS_LOG_LEVEL_INFO 0 +#define PRIMJS_LOG_LEVEL_WARNING 1 +#define PRIMJS_LOG_LEVEL_ERROR 2 +#define PRIMJS_LOG_LEVEL_FATAL 3 +#define PRIMJS_LOG_LEVEL_NUM 4 + +typedef int LogSeverity; +const LogSeverity LOG_VERBOSE = PRIMJS_LOG_LEVEL_VERBOSE; +const LogSeverity LOG_INFO = PRIMJS_LOG_LEVEL_INFO; +const LogSeverity LOG_WARNING = PRIMJS_LOG_LEVEL_WARNING; +const LogSeverity LOG_ERROR = PRIMJS_LOG_LEVEL_ERROR; +const LogSeverity LOG_FATAL = PRIMJS_LOG_LEVEL_FATAL; +const LogSeverity LOG_NUM_SEVERITIES = PRIMJS_LOG_LEVEL_NUM; + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". +class LogMessageVoidify { + public: + LogMessageVoidify() = default; + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream &) {} +}; + +#define LOG_IS_ON(severity) \ + ((primjs::general::logging::LOG_##severity) >= \ + primjs::general::logging::GetMinAllLogLevel()) + +#define LOG_STREAM(severity) \ + primjs::general::logging::LogMessage( \ + __FILE__, __LINE__, primjs::general::logging::LOG_##severity) \ + .stream() + +#define LAZY_STREAM(stream, condition) \ + !(condition) ? (void)0 \ + : primjs::general::logging::LogMessageVoidify() & (stream) + +// Use this macro to suppress warning if the variable in log is not used. +#define UNUSED_LOG_VARIABLE __attribute__((unused)) + +#ifndef PRIMJS_MIN_LOG_LEVEL +#define PRIMJS_MIN_LOG_LEVEL PRIMJS_LOG_LEVEL_VERBOSE +#endif + +// TODO(zhixuan): Currently, the usage of log macros is like "LOGI("abc" << +// variable)", which is mixed of stream pattern and format string pattern. +// Change the loggin fashion entirely to format string pattern in future. +#if PRIMJS_MIN_LOG_LEVEL <= PRIMJS_LOG_LEVEL_VERBOSE +#define LOGV(msg) LAZY_STREAM(LOG_STREAM(VERBOSE), LOG_IS_ON(VERBOSE)) << msg +#define DLOGV(msg) LAZY_STREAM(LOG_STREAM(VERBOSE), LOG_IS_ON(VERBOSE)) << msg +#else +#define LOGV(msg) +#define DLOGV(msg) +#endif + +#if PRIMJS_MIN_LOG_LEVEL <= PRIMJS_LOG_LEVEL_INFO +#define LOGI(msg) LAZY_STREAM(LOG_STREAM(INFO), LOG_IS_ON(INFO)) << msg +#define DLOGI(msg) LAZY_STREAM(LOG_STREAM(INFO), LOG_IS_ON(INFO)) << msg +#else +#define LOGI(msg) +#define DLOGI(msg) +#endif + +#if PRIMJS_MIN_LOG_LEVEL <= PRIMJS_LOG_LEVEL_WARNING +#define LOGW(msg) LAZY_STREAM(LOG_STREAM(WARNING), LOG_IS_ON(WARNING)) << msg +#define DLOGW(msg) LAZY_STREAM(LOG_STREAM(WARNING), LOG_IS_ON(WARNING)) << msg +#else +#define LOGW(msg) +#define DLOGW(msg) +#endif + +#if PRIMJS_MIN_LOG_LEVEL <= PRIMJS_LOG_LEVEL_ERROR +#define LOGE(msg) LAZY_STREAM(LOG_STREAM(ERROR), LOG_IS_ON(ERROR)) << msg +#define DLOGE(msg) LAZY_STREAM(LOG_STREAM(ERROR), LOG_IS_ON(ERROR)) << msg +#else +#define LOGE(msg) +#define DLOGE(msg) +#endif + +#if PRIMJS_MIN_LOG_LEVEL <= PRIMJS_LOG_LEVEL_FATAL +#define LOGF(msg) LAZY_STREAM(LOG_STREAM(FATAL), LOG_IS_ON(FATAL)) << msg +#define DLOGF(msg) LAZY_STREAM(LOG_STREAM(FATAL), LOG_IS_ON(FATAL)) << msg +#else +#define LOGF(msg) +#define DLOGF(msg) +#endif + +#ifndef DCHECK +// for debug, if check failed, log fatal and abort +#if !defined(NDEBUG) +#define DCHECK(condition) \ + LAZY_STREAM(LOG_STREAM(FATAL), !(condition)) \ + << "Check failed: " #condition ". " +#else +// for release, do nothing +#define DCHECK(condition) !(condition) ? (void)0 : (void)0 +#endif +#endif + +#define NOTREACHED() LOGF("") + +class QJS_EXPORT LogMessage { + public: + LogMessage(const char *file, int line, LogSeverity severity); + ~LogMessage(); + std::ostringstream &stream(); + LogSeverity severity() { return severity_; } + + private: + void Init(const char *file, int line); + + LogSeverity severity_; + std::ostringstream stream_; + + const char *file_; + const int line_; + LogMessage(const LogMessage &) = delete; + LogMessage &operator=(const LogMessage &) = delete; +}; + +} // namespace logging +} // namespace general +} // namespace primjs + +#endif // SRC_BASIC_LOG_LOGGING_H_ diff --git a/NativeScript/napi/android/primjs/include/gc/allocator.h b/NativeScript/napi/android/primjs/include/gc/allocator.h new file mode 100644 index 000000000..bcf6f0d90 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/gc/allocator.h @@ -0,0 +1,282 @@ +/* + This is a version of malloc/free/realloc written by + Doug Lea and released to the public domain, as explained at + http://creativecommons.org/publicdomain/zero/1.0/ Send questions, + comments, complaints, performance data, etc to dl@cs.oswego.edu +* Version 2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea + Note: There may be an updated version of this malloc obtainable at + ftp://gee.cs.oswego.edu/pub/misc/malloc.c + Check before installing! + */ +/* + * PackageLicenseDeclared: CC0-1.0 + */ + +// Copyright 2023 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_GC_ALLOCATOR_H_ +#define SRC_GC_ALLOCATOR_H_ +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#include + +#if defined(ANDROID) || defined(__ANDROID__) || defined(OS_IOS) +#define THREAD_NUM 3 +#define CREATE_THREAD_NUM 2 +#else +#define THREAD_NUM 6 +#define CREATE_THREAD_NUM 6 +#endif + +typedef uint64_t binmap_t; +typedef uint64_t bindex_t; +typedef uint64_t flag_t; +struct malloc_tree_chunk { + size_t prev_foot; + size_t head; + struct malloc_tree_chunk* fd; + struct malloc_tree_chunk* bk; + + struct malloc_tree_chunk* child[2]; + struct malloc_tree_chunk* parent; + bindex_t index; +}; +typedef struct malloc_tree_chunk* tbinptr; +struct malloc_segment { + char* base; + size_t size; + struct malloc_segment* next; + flag_t sflags; +}; +typedef struct malloc_segment msegment; +typedef struct malloc_segment* msegmentptr; +struct malloc_chunk { + size_t prev_foot; + size_t head; + struct malloc_chunk* fd; + struct malloc_chunk* bk; +}; + +typedef struct malloc_chunk mchunk; +typedef struct malloc_chunk* mchunkptr; + +#ifndef USE_LOCKS +#define USE_LOCKS 0 +#endif +#define NSMALLBINS (32U) +#define NTREEBINS (32U) +struct malloc_state { + binmap_t smallmap; + binmap_t local_smallmap[THREAD_NUM]; + binmap_t treemap; + binmap_t local_treemap[THREAD_NUM]; + size_t dvsize; + size_t topsize; + char* least_addr; + mchunkptr dv; + mchunkptr top; + size_t trim_check; + char* mmap_cache; + size_t mmap_cache_size; + size_t release_checks; + size_t magic; + mchunkptr* smallbins; + mchunkptr* local_smallbins[THREAD_NUM]; + tbinptr* treebins; + tbinptr* local_treebins[THREAD_NUM]; + size_t footprint; + size_t max_footprint; + size_t footprint_limit; + flag_t mflags; +#if USE_LOCKS + MLOCK_T mutex; +#endif + msegment seg; + size_t exts; + void** mmap_array; + uint32_t mmap_free_index; + uint32_t mmap_size; + uint32_t mmap_count; + size_t cur_malloc_size; // malloc size after gc + size_t footprint_before_gc; +#ifdef ENABLE_TRACING_GC_LOG + size_t malloc_size_before_gc; + size_t malloc_size_after_gc; + size_t finalizer_time; + size_t free_time; + size_t free_set_bit_time; + size_t free_gene_freelist_time; + size_t free_mmap_chunk_time; + size_t release_time; + size_t release_seg_num; +#endif + void* runtime; + void* pool; + int gc_flag[THREAD_NUM]; + int local_idx_flag[THREAD_NUM]; +#ifndef _WIN32 + pthread_mutex_t mtx; +#endif + size_t seg_count; + bool open_madvise; +#if defined(ANDROID) || defined(__ANDROID__) || defined(OS_IOS) + char mem_name[30]; +#endif +}; +typedef struct malloc_state* mstate; +void* allocate(mstate m, size_t bytes); +void gcfree(mstate m, void* mem); +void* reallocate(mstate m, void* oldmem, size_t bytes); +size_t allocate_usable_size(void* mem); +size_t allocate_usable_size_debug(void* mem); +size_t allocate_usable_size_debug_protect(void* mem); +void destroy_allocate_instance(mstate m); + +int atomic_acqurie_local_idx(mstate m); +void atomic_release_local_idx(mstate m, int local_idx); + +void set_mark_multi(void* ptr); +bool is_marked_multi(void* ptr); +void set_alloc_tag(void* ptr, int alloc_tag); +int get_alloc_tag(void* ptr); +void set_hash_size(void* ptr, int hash_size); +int get_hash_size(void* ptr); +void set_heap_obj_len(void* ptr, int len); +int get_heap_obj_len(void* ptr); + +#ifdef ENABLE_GC_DEBUG_TOOLS +#ifdef __cplusplus +extern "C" { +#endif +void delete_cur_mems(void* runtime, void* ptr); +void multi_delete_cur_mems(void* runtime, void* ptr, int local_idx); +void merge_mems(void* runtime); +void add_cur_mems(void* runtime, void* ptr); +#ifdef __cplusplus +} +#endif +#endif + +int64_t get_daytime(); +#ifdef ENABLE_TRACING_GC_LOG +size_t get_malloc_size(mstate m); +#endif + +#define SIZE_T_SIZE (sizeof(size_t)) +#define INT_SIZE (sizeof(int)) +#define SIZE_T_BITSIZE (sizeof(size_t) << 3) + +#define SIZE_T_ZERO ((size_t)0) +#define SIZE_T_ONE ((size_t)1) +#define SIZE_T_TWO ((size_t)2) +#define SIZE_T_FOUR ((size_t)4) +#define TWO_SIZE_T_SIZES (SIZE_T_SIZE << 1) +#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE << 2) +#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES + TWO_SIZE_T_SIZES) +#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) + +#define PINUSE_BIT (SIZE_T_ONE) +#define CINUSE_BIT (SIZE_T_TWO) +#define FLAG4_BIT (SIZE_T_FOUR) +#define INUSE_BITS (PINUSE_BIT | CINUSE_BIT) +#define FLAG_BITS (PINUSE_BIT | CINUSE_BIT | FLAG4_BIT) + +#define FENCEPOST_HEAD (INUSE_BITS | SIZE_T_SIZE) + +#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) +#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) + +#define next_chunk(p) ((mchunkptr)(((char*)(p)) + ((p)->head & ~FLAG_BITS))) +#define prev_chunk(p) ((mchunkptr)(((char*)(p)) - ((p)->prev_foot))) + +#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) + +#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) + +#define set_size_and_pinuse_of_free_chunk(p, s) \ + ((p)->head = (s | PINUSE_BIT), set_foot(p, s)) + +#define set_free_with_pinuse(p, s, n) \ + (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) + +#define overhead_for(p) (is_mmapped(p) ? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) + +#if defined(DARWIN) || defined(_DARWIN) +#define HAVE_MMAP 1 +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)16U) +#endif +#endif /* DARWIN */ + +#ifndef MALLOC_ALIGNMENT +#if defined(__x86_64__) || defined(__aarch64__) +#define MALLOC_ALIGNMENT ((size_t)(1 * sizeof(void*))) +#else +#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void*))) +#endif +#endif /* MALLOC_ALIGNMENT */ + +#ifndef FOOTERS +#define FOOTERS 0 +#endif /* FOOTERS */ + +#if FOOTERS +#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES + 2 * INT_SIZE) +#else /* FOOTERS */ +#define CHUNK_OVERHEAD (SIZE_T_SIZE + 2 * INT_SIZE) +#endif /* FOOTERS */ + +#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) + +#define pad_request(req) \ + (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +#define align_offset(A) \ + ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0) \ + ? 0 \ + : ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & \ + CHUNK_ALIGN_MASK)) +#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) + +#define chunk2mem(p) ((void*)((char*)(p) + (TWO_SIZE_T_SIZES + 2 * INT_SIZE))) + +#define segment_holds(S, A) \ + ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) + +#define cinuse(p) ((p)->head & CINUSE_BIT) +#define pinuse(p) ((p)->head & PINUSE_BIT) +#define flag4inuse(p) ((p)->head & FLAG4_BIT) +#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) +#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) + +#define chunksize(p) ((p)->head & ~(FLAG_BITS)) + +#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) +#define set_flag4(p) ((p)->head |= FLAG4_BIT) +#define clear_flag4(p) ((p)->head &= ~FLAG4_BIT) + +#define mem2chunk(mem) \ + ((mchunkptr)((char*)(mem) - (TWO_SIZE_T_SIZES + 2 * INT_SIZE))) + +#define IS_UNUSED_BIT (16U) +void init_bins(mstate m); +void local_gcfree(mstate fm, void* mem, int local_idx); +void* mmap_set_free(uint32_t v); + +size_t release_unused_segments(mstate m); + +bool mmap_is_free(const void* p); + +bool is_marked(void* ptr); +void clear_mark(void* ptr); +int get_tag(void* ptr); +void local_insert_chunk(mstate m, mchunkptr mchunk, size_t size, int local_idx); + +#endif // SRC_GC_ALLOCATOR_H_ diff --git a/NativeScript/napi/android/primjs/include/gc/base-global-handles.h b/NativeScript/napi/android/primjs/include/gc/base-global-handles.h new file mode 100644 index 000000000..348e87242 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/gc/base-global-handles.h @@ -0,0 +1,73 @@ +// Copyright 2009 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. +// +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_GC_BASE_GLOBAL_HANDLES_H_ +#define SRC_GC_BASE_GLOBAL_HANDLES_H_ + +#define ITERATOR(NODETYPE) \ + NodeIterator final { \ + public: \ + explicit NodeIterator(NodeBlock* block) : block_(block) {} \ + NodeIterator(NodeIterator&& other) \ + : block_(other.block_), index_(other.index_) {} \ + NodeIterator(const NodeIterator&) = delete; \ + NodeIterator& operator=(const NodeIterator&) = delete; \ + bool operator==(const NodeIterator& other) const { \ + return block_ == other.block_; \ + } \ + bool operator!=(const NodeIterator& other) const { \ + return block_ != other.block_; \ + } \ + NodeIterator& operator++() { \ + if (++index_ < kBlockSize) return *this; \ + index_ = 0; \ + block_ = block_->next_used(); \ + return *this; \ + } \ + NODETYPE* operator*() { return block_->at(index_); } \ + NODETYPE* operator->() { return block_->at(index_); } \ + \ + private: \ + NodeBlock* block_ = nullptr; \ + size_t index_ = 0; \ + }; + +#define NODEBLOCK(HANDLETYPE, NODETYPE) \ + NodeBlock final { \ + public: \ + inline static const NodeBlock* From(const NODETYPE* node); \ + inline static NodeBlock* From(NODETYPE* node); \ + NodeBlock(HANDLETYPE* global_handles, HANDLETYPE::NodeSpace* space, \ + NodeBlock* next) \ + : next_(next), global_handles_(global_handles), space_(space) {} \ + ~NodeBlock() = default; \ + NodeBlock(const NodeBlock&) = delete; \ + NodeBlock& operator=(const NodeBlock&) = delete; \ + NODETYPE* at(size_t index) { return &nodes_[index]; } \ + const NODETYPE* at(size_t index) const { return &nodes_[index]; } \ + HANDLETYPE::NodeSpace* space() const { return space_; } \ + HANDLETYPE* global_handles() const { return global_handles_; } \ + inline bool IncreaseUsage(); \ + inline bool DecreaseUsage(); \ + inline void ListAdd(NodeBlock** top); \ + inline void ListRemove(NodeBlock** top); \ + NodeBlock* next() const { return next_; } \ + NodeBlock* next_used() const { return next_used_; } \ + const void* begin_address() const { return nodes_; } \ + const void* end_address() const { return &nodes_[kBlockSize]; } \ + \ + private: \ + NODETYPE nodes_[kBlockSize]; \ + NodeBlock* const next_; \ + HANDLETYPE* const global_handles_; \ + HANDLETYPE::NodeSpace* const space_; \ + NodeBlock* next_used_ = nullptr; \ + NodeBlock* prev_used_ = nullptr; \ + uint32_t used_nodes_ = 0; \ + }; +#endif // SRC_GC_BASE_GLOBAL_HANDLES_H_ diff --git a/NativeScript/napi/android/primjs/include/gc/collector.h b/NativeScript/napi/android/primjs/include/gc/collector.h new file mode 100644 index 000000000..ada95c1a1 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/gc/collector.h @@ -0,0 +1,314 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_GC_COLLECTOR_H_ +#define SRC_GC_COLLECTOR_H_ + +#include + +#include "gc/trace-gc.h" +#include "quickjs/include/quickjs-inner.h" + +enum HandleType; +class Visitor; +class Finalizer; +class Sweeper; +typedef struct JSString JSAtomStruct; +struct LEPUSRuntime; +typedef struct malloc_state *mstate; +struct JSAsyncFunctionState; +struct JSProperty; +struct JSRegExp; +struct BCReaderState; +struct JSToken; +struct ValueBuffer; +class GarbageCollector { + public: + GarbageCollector(LEPUSRuntime *rt, mstate) noexcept; + ~GarbageCollector(); + // gc + void CollectGarbage(size_t size = 0) noexcept; + void DoOnlyFinalizer() noexcept; + + void Init(LEPUSRuntime *rt); + Visitor *GetVisitor() { return visitor; } + Finalizer *GetFinalizer() { return finalizer; } + // gc pause suppression mode + void SetGCPauseSuppressionMode(bool mode) { + gc_pause_suppression_mode_ = mode; + } + bool GetGCPauseSuppressionMode() { return gc_pause_suppression_mode_; } + // forbid_gc + void SetForbidGC() { forbid_gc_++; } + void ResetForbidGC() { forbid_gc_--; } + + // for debug +#ifdef ENABLE_GC_DEBUG_TOOLS + size_t mem_order_cnt; + std::unordered_map cur_mems; + std::unordered_set delete_mems[THREAD_NUM]; + size_t delete_order_cnt; + std::unordered_map del_mems; + + size_t handle_order_cnt; + std::unordered_map cur_handles; + size_t qjsvalue_order_cnt; + std::unordered_map cur_qjsvalues; +#endif + + size_t GetHandleSize() { +#ifdef ENABLE_GC_DEBUG_TOOLS + return cur_handles.size(); +#else + return 0; +#endif + } + + size_t GetQjsValueSize() { +#ifdef ENABLE_GC_DEBUG_TOOLS + return cur_qjsvalues.size(); +#else + return 0; +#endif + } + + void SetMaxLimit(size_t limit); + size_t GetMaxLimit(); + void AddGCDuration(int64_t gc_time) { total_duration += gc_time; } + int64_t GetGCDuration() { return total_duration; } + int js_ref_count; + + private: + // gc + void MarkLiveObjects() noexcept; + void SweepDeadObjects() noexcept; +#ifdef ENABLE_TRACING_GC_LOG + void PrintGCLog(int64_t mark_begin, int64_t mark_end) noexcept; +#endif + void UpdateFootprintLimit(size_t size) noexcept; + void UpdateNGFootprintLimit(size_t size) noexcept; + void UpdateGCInfo(size_t heapsize_before, int64_t duration); + + // field + LEPUSRuntime *rt_; + int forbid_gc_; + bool gc_pause_suppression_mode_ = false; + Visitor *visitor; + Finalizer *finalizer; + Sweeper *sweeper; + size_t max_limit; +#ifdef ENABLE_TRACING_GC_LOG + int64_t gc_begin_time; + int64_t last_gc_time; +#endif + int64_t total_duration; + std::stringstream gc_info; + int info_size; +}; + +class Visitor { + public: + Visitor(LEPUSRuntime *rt) noexcept + : rt_(rt), objs(nullptr), objs_len(0), idx(0) { + for (int i = 0; i < THREAD_NUM; i++) { + queue[i] = new Queue(rt_); + } + } + ~Visitor() { + for (int i = 0; i < THREAD_NUM; i++) { + delete queue[i]; + } + if (objs != nullptr) { + system_free(objs); + } + } + void ScanRoots(); + void VisitRootLEPUSValue(LEPUSValue *val, int local_idx) noexcept; + void VisitRootLEPUSValue(LEPUSValue &val, int local_idx) noexcept; + void AddObjectDuringGC(void *ptr) { + if (objs == nullptr) { + objs = static_cast(system_malloc(16 * sizeof(void *))); + objs_len = 16; + } + if (idx >= objs_len) { + int new_len = objs_len * 2; + void **new_objs = + static_cast(system_realloc(objs, new_len * sizeof(void *))); + if (!new_objs) abort(); + objs = new_objs; + objs_len = new_len; + } + objs[idx] = ptr; + idx++; + } + void VisitObjectDuringGC() { + for (int i = 0; i < idx; i++) { + VisitRootHeapObj(objs[i], 0); + } + idx = 0; + } + + private: + // private scanner + void ScanStack() noexcept; + void ScanRuntime() noexcept; + void ScanHandles() noexcept; + void ScanContext(LEPUSContext *ctx) noexcept; + + // visit root + void VisitRoot(void *ptr, HandleType type, int local_idx) noexcept; + void VisitRootHeapObj(void *ptr, int local_idx) noexcept; + void VisitRootHeapObjForTask(Queue *q, void *ptr) noexcept; + void VisitRootCString(char *cstr, int local_idx) noexcept; + void VisitRootJSToken(JSToken *token, int local_idx) noexcept; + void VisitRootBCReaderState(BCReaderState *s, int local_idx) noexcept; + void VisitRootValueBuffer(ValueBuffer *b, int local_idx) noexcept; + void VisitJSAtom(JSAtom atom, int local_idx) noexcept; + + // visitor + /* LEPUSValue with tag -> begin */ + void VisitEntry(void *ptr, int local_idx) noexcept; + void VisitLEPUSLepusRef(void *ptr, int local_idx) noexcept; + void VisitJShape(void *ptr, int local_idx) noexcept; + void VisitJSVarRef(void *ptr, int local_idx) noexcept; + void VisitJSFunctionBytecode(void *ptr, int local_idx) noexcept; + void VisitJSObject(void *ptr, int local_idx) noexcept; + /* LEPUSValue with tag -> end */ + // LEPUS_TAG_BIG_INT + // LEPUS_TAG_BIG_FLOAT + /* LEPUSObject with class_id -> begin */ + void VisitJSBoundFunction(void *ptr, + int local_idx) noexcept; // normal_free + void VisitJSCFunctionDataRecord(void *ptr, + int local_idx) noexcept; // normal_free + void VisitJSForInIterator(void *ptr, + int local_idx) noexcept; // normal_free + void VisitJSArrayBuffer(void *ptr, int local_idx) noexcept; + void VisitJSTypedArray(void *ptr, int local_idx) noexcept; + void VisitJSMapState(void *ptr, int local_idx) noexcept; + void VisitJSMapIteratorData(void *ptr, int local_idx) noexcept; + void VisitJSArrayIteratorData(void *ptr, + int local_idx) noexcept; // normal_free + void VisitJSRegExpStringIteratorData(void *ptr, + int local_idx) noexcept; // normal_free + void VisitJSGeneratorData(void *ptr, int local_idx) noexcept; + void VisitJSProxyData(void *ptr, int local_idx) noexcept; // normal_free + void VisitJSPromiseData(void *ptr, int local_idx) noexcept; // normal_free + void VisitJSPromiseReactionData(void *ptr, + int local_idx) noexcept; // normal_free + void VisitJSPromiseFunctionData(void *ptr, + int local_idx) noexcept; // normal_free + void VisitJSAsyncFunctionData(void *ptr, int local_idx) noexcept; + void VisitJSAsyncFromSyncIteratorData(void *ptr, + int local_idx) noexcept; // normal_free + void VisitJSAsyncGeneratorData(void *ptr, int local_idx) noexcept; + /* LEPUSObject with class_id -> end */ + // scan context +#ifdef ENABLE_QUICKJS_DEBUGGER + void VisitJSScriptSource(void *ptr, int local_idx) noexcept; +#endif + // other + void VisitJSPropertyEnum(void *ptr, + int local_idx) noexcept; // normal_free + void VisitJSModuleDef(void *ptr, int local_idx) noexcept; + void VisitJSFunctionDef(void *ptr, int local_idx) noexcept; + void VisitJSValueArray(void *ptr, int local_idx) noexcept; + void VisitValueSlot(void *ptr, int local_idx) noexcept; + void VisitJsonStrArray(void *ptr, int local_idx) noexcept; + + void VisitSeparableString(void *ptr, int local_idx) noexcept; + void VisitDebuggerInfo(void *, int32_t) noexcept; + void VisitFinalizationRegistryData(void *ptr, int local_idx) noexcept; + + // push ptr + void PushObjLEPUSValue(LEPUSValue &val, int local_idx) noexcept; + void PushObjLEPUSValue(LEPUSValue *val, int local_idx) noexcept; + void PushObjAtom(JSAtom atom, int local_idx) noexcept; + void PushObjJSAsyncFunctionState(JSAsyncFunctionState *s, + int local_idx) noexcept; + void PushObjJStackFrame(LEPUSStackFrame *sf, int local_idx) noexcept; + void PushBytecodeAtoms(const uint8_t *bc_buf, int bc_len, + int use_short_opcodes, int local_idx) noexcept; + void PushObjJSRegExp(JSRegExp *re, int local_idx) noexcept; + void PushObjFunc(LEPUSObject *obj, int local_idx) noexcept; + void PushObjArray(LEPUSObject *obj, int local_idx) noexcept; + void PushObjRegExp(LEPUSObject *obj, int local_idx) noexcept; + void PushObjProperty(JSProperty *pr, int prop_flags, int local_idx) noexcept; + + // tools + bool IsConstString(void *ptr) { + return get_alloc_tag(ptr) == ALLOC_TAG_JSConstString; + } + // field + LEPUSRuntime *rt_; + Queue *queue[THREAD_NUM]; // for visit + void **objs; + int objs_len; + int idx; +}; + +class Finalizer { + public: + Finalizer(LEPUSRuntime *rt) noexcept : rt_(rt) {} + void close_var_refs(LEPUSStackFrame *sf) noexcept; + void free_atom(LEPUSRuntime *rt, JSAtomStruct *p) noexcept; + // do finalizer + void DoFinalizer(void *ptr) noexcept; + void DoFinalizer2(void *ptr) noexcept; +#ifdef ENABLE_LEPUSNG + void JSLepusRefFinalizer(void *ptr) noexcept; +#endif +#ifdef CONFIG_BIGNUM + void JSBigFloatFinalizer(void *ptr) noexcept; +#endif + void JSObjectFinalizer(void *ptr) noexcept; + void JSObjectOnlyFinalizer(void *ptr) noexcept; + void JSStringFinalizer(void *ptr) noexcept; +#ifdef ENABLE_LEPUSNG + void JSStringOnlyFinalizer(void *ptr) noexcept; +#endif + void JSSymbolFinalizer(void *ptr) noexcept; + void JSShapeFinalizer(void *ptr) noexcept; + void JSVarRefFinalizer(void *ptr) noexcept; + void JSFunctionBytecodeFinalizer(void *ptr) noexcept; + void JSArrayBufferFinalizer(void *ptr) noexcept; + void JSTypedArrayFinalizer(void *ptr) noexcept; + void JSMapStateFinalizer(void *ptr) noexcept; + void JSMapIteratorDataFinalizer(void *ptr) noexcept; + void JSGeneratorDataFinalizer(void *ptr) noexcept; + void JSAsyncFunctionDataFinalizer(void *ptr) noexcept; + void JSAsyncGeneratorDataFinalizer(void *ptr) noexcept; + void JSModuleDefFinalizer(void *ptr) noexcept; + void JSFunctionDefFinalizer(void *ptr) noexcept; + void JSSeparableStringFinalizer(void *ptr) noexcept {} + void FinalizationRegistryDataFinalizer(void *ptr) noexcept; + void WeakRefDataFinalizer(void *ptr) noexcept; + + private: + LEPUSRuntime *rt_; +}; + +class MlockScope { + public: + MlockScope(Queue **q) : queue(q) { +#ifndef _WIN32 + for (int i = 0; i < THREAD_NUM; i++) { + SYSCALL_CHECK( + mlock(queue[i]->GetQueue(), queue[i]->GetSize() * sizeof(uintptr_t))) + } +#endif + } + ~MlockScope() { +#ifndef _WIN32 + for (int i = 0; i < THREAD_NUM; i++) { + SYSCALL_CHECK(munlock(queue[i]->GetQueue(), + queue[i]->GetSize() * sizeof(uintptr_t))); + } +#endif + } + + private: + Queue **queue; +}; +#endif // SRC_GC_COLLECTOR_H_ diff --git a/NativeScript/napi/android/primjs/include/gc/global-handles.h b/NativeScript/napi/android/primjs/include/gc/global-handles.h new file mode 100644 index 000000000..430151f11 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/gc/global-handles.h @@ -0,0 +1,62 @@ +// Copyright 2009 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. +// +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_GC_GLOBAL_HANDLES_H_ +#define SRC_GC_GLOBAL_HANDLES_H_ + +// #include + +#include "gc/persistent-handle.h" + +typedef uintptr_t Addr; + +// Global handles hold handles that are independent of stack-state and can have +// callbacks and finalizers attached to them. +class GlobalHandles final { + public: + static void EnableMarkingBarrier(LEPUSRuntime* runtime); + static void DisableMarkingBarrier(LEPUSRuntime* runtime); + + GlobalHandles(const GlobalHandles&) = delete; + GlobalHandles& operator=(const GlobalHandles&) = delete; + + class NodeBlock; + + static void Destroy(LEPUSValue* location); + + explicit GlobalHandles(LEPUSRuntime* runtime); + ~GlobalHandles(); + + // Creates a new global handle that is alive until Destroy is called. + LEPUSValue* Create(LEPUSValue value, bool is_weak); + + void IterateAllRoots(int local_idx, int offset); + void GlobalRootsFinalizer(); + bool IsMarkedLEPUSValue(LEPUSValue* val); + LEPUSRuntime* runtime() const { return runtime_; } + + size_t TotalSize() const; + size_t UsedSize() const; + // Number of global handles. + size_t handles_count() const; + void SetWeak(LEPUSValue* location, void* data, void (*cb)(void*)); + void ClearWeak(LEPUSValue* location); + void SetWeakState(LEPUSValue* location); + + private: + // Internal node structures. + class NodeIterator; + class NodeSpace; + + LEPUSRuntime* const runtime_; + bool is_marking_ = false; + + NodeSpace* regular_nodes_; +}; + +#endif // SRC_GC_GLOBAL_HANDLES_H_ diff --git a/NativeScript/napi/android/primjs/include/gc/persistent-handle.h b/NativeScript/napi/android/primjs/include/gc/persistent-handle.h new file mode 100644 index 000000000..c67bd0fa6 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/gc/persistent-handle.h @@ -0,0 +1,222 @@ +// Copyright 2009 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. +// +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_GC_PERSISTENT_HANDLE_H_ +#define SRC_GC_PERSISTENT_HANDLE_H_ +extern "C" { +#include "quickjs/include/quickjs.h" +} +#ifdef ENABLE_GC_DEBUG_TOOLS +#define DCHECK2(condition) \ + if (!(condition)) abort(); +#if defined(ANDROID) || defined(__ANDROID__) +#include +#endif +#else +#define DCHECK2(condition) ((void)0) +#endif + +const int kApiSystemPointerSize = sizeof(void*); +static const int kNodeClassIdOffset = 1 * kApiSystemPointerSize; + +class PersistentBase { + public: + /** + * If non-empty, destroy the underlying storage cell + * IsEmpty() will return true after this call. + */ + inline void Reset(LEPUSRuntime* runtime); + inline void Reset(LEPUSContext* ctx); + + /** + * If non-empty, destroy the underlying storage cell + * and create a new one with the contents of other if other is non empty + */ + inline void Reset(LEPUSRuntime* runtime, LEPUSValue other, + bool is_weak = false); + + inline void Reset(LEPUSContext* ctx, LEPUSValue other, bool is_weak = false); + + inline bool IsEmpty() const { return val_ == nullptr; } + inline void Empty() { val_ = nullptr; } + + inline LEPUSValue Get() const { + if (val_) return *val_; + return LEPUS_UNDEFINED; + } + + inline bool IsWeak() const; + + PersistentBase(const PersistentBase& other) = delete; + void operator=(const PersistentBase&) = delete; + + explicit inline PersistentBase(LEPUSValue* val) : val_(val) {} + inline static LEPUSValue* New(LEPUSRuntime* runtime, LEPUSValue that, + bool is_weak); + + LEPUSValue* val_; +}; + +class GCPersistent : public PersistentBase { + public: + /** + * A GCPersistent with no storage cell. + */ + inline GCPersistent() : PersistentBase(nullptr) {} + /** + * Construct a GCPersistent from a Local. + * When the Local is non-empty, a new storage cell is created + * pointing to the same object, and no flags are set. + */ + inline GCPersistent(LEPUSRuntime* runtime, LEPUSValue that, + bool is_weak = false) + : PersistentBase(PersistentBase::New(runtime, that, is_weak)) {} + + inline GCPersistent(LEPUSContext* ctx, LEPUSValue that, bool is_weak = false) + : PersistentBase( + PersistentBase::New(LEPUS_GetRuntime(ctx), that, is_weak)) {} + + /** + * The copy constructors and assignment operator create a GCPersistent + * exactly as the GCPersistent constructor, but the Copy function from the + * traits class is called, allowing the setting of flags based on the + * copied GCPersistent. + */ + GCPersistent(const GCPersistent& that) = delete; + + GCPersistent& operator=(const GCPersistent& that) = delete; + + /** + * The destructor will dispose the GCPersistent based on the + * kResetInDestructor flags in the traits class. Since not calling dispose + * can result in a memory leak, it is recommended to always set this flag. + */ + inline ~GCPersistent() { + // if (false) this->Reset(); + } + void SetWeak(LEPUSRuntime* runtime) { SetWeakState(runtime, this->val_); } + + private: + friend class Utils; + friend class GCPersistent; + inline LEPUSValue* operator*() const { return this->val_; } +}; + +class WASMGCPersistent : public PersistentBase { + public: + inline WASMGCPersistent() + : PersistentBase(nullptr), rt(nullptr), val(LEPUS_UNDEFINED) {} + inline WASMGCPersistent(LEPUSValue that) + : PersistentBase(nullptr), rt(nullptr), val(that) {} + + inline WASMGCPersistent(LEPUSRuntime* runtime, LEPUSValue that, + bool is_weak = false) + : PersistentBase(PersistentBase::New(runtime, that, is_weak)), + rt(runtime), + val(that) {} + + inline WASMGCPersistent(LEPUSContext* ctx, LEPUSValue that, + bool is_weak = false) + : PersistentBase( + PersistentBase::New(LEPUS_GetRuntime(ctx), that, is_weak)), + rt(LEPUS_GetRuntime(ctx)), + val(that) {} + + WASMGCPersistent(const WASMGCPersistent& that) + : PersistentBase(PersistentBase::New(that.GetRT(), that.Get(), false)) { + rt = that.GetRT(); + val = that.Get(); + } + + WASMGCPersistent& operator=(const WASMGCPersistent& that) { + if (val_) { + *val_ = that.Get(); + } else { + val_ = PersistentBase::New(that.GetRT(), that.Get(), false); + } + rt = that.GetRT(); + val = that.Get(); + return *this; + } + void Reset(LEPUSContext* ctx, LEPUSValue value) { + PersistentBase::Reset(LEPUS_GetRuntime(ctx), value, false); + rt = LEPUS_GetRuntime(ctx); + val = value; + } + + LEPUSValue Get() const { + if (val_) { + return *val_; + } else { + return val; + } + } + + LEPUSValue* GetPtr() const { + if (val_) { + return val_; + } else { + return const_cast(&val); + } + } + + virtual inline ~WASMGCPersistent() { PersistentBase::Reset(rt); } + LEPUSRuntime* GetRT() const { return rt; } + + private: + inline LEPUSValue* operator*() const { return this->val_; } + LEPUSRuntime* rt; + LEPUSValue val; +}; + +LEPUSValue* PersistentBase::New(LEPUSRuntime* runtime, LEPUSValue that, + bool is_weak) { + if (!runtime || LEPUS_IsUndefined(that)) return nullptr; + return GlobalizeReference(runtime, that, is_weak); +} + +bool PersistentBase::IsWeak() const { + *reinterpret_cast(0xdead) = 0; + return false; +} + +void PersistentBase::Reset(LEPUSRuntime* runtime) { + if (this->IsEmpty() || !runtime) return; + DisposeGlobal(runtime, this->val_); + val_ = nullptr; +} + +void PersistentBase::Reset(LEPUSContext* ctx) { Reset(LEPUS_GetRuntime(ctx)); } + +/** + * If non-empty, destroy the underlying storage cell + * and create a new one with the contents of other if other is non empty + */ + +void PersistentBase::Reset(LEPUSRuntime* runtime, LEPUSValue other, + bool is_weak) { + Reset(runtime); + if (LEPUS_IsUndefined(other)) return; + this->val_ = New(runtime, other, is_weak); +} + +void PersistentBase::Reset(LEPUSContext* ctx, LEPUSValue other, bool is_weak) { + Reset(LEPUS_GetRuntime(ctx), other, is_weak); +} + +class QJSValueValueAllocator { + public: + static void* New(LEPUSRuntime* runtime) { + return AllocateQJSValueValue(runtime); + } + static void Delete(LEPUSRuntime* runtime, void* instance) { + FreeQJSValueValue(runtime, reinterpret_cast(instance)); + } +}; + +#endif // SRC_GC_PERSISTENT_HANDLE_H_ diff --git a/NativeScript/napi/android/primjs/include/gc/qjsvaluevalue-space.h b/NativeScript/napi/android/primjs/include/gc/qjsvaluevalue-space.h new file mode 100644 index 000000000..09668f55e --- /dev/null +++ b/NativeScript/napi/android/primjs/include/gc/qjsvaluevalue-space.h @@ -0,0 +1,34 @@ +// Copyright 2009 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. +// +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_GC_QJSVALUEVALUE_SPACE_H_ +#define SRC_GC_QJSVALUEVALUE_SPACE_H_ + +#include "gc/persistent-handle.h" +class QJSValueValueSpace final { + public: + QJSValueValueSpace(const QJSValueValueSpace&) = delete; + QJSValueValueSpace& operator=(const QJSValueValueSpace&) = delete; + + class NodeBlock; + class NodeIterator; + class NodeSpace; + + static void Destroy(void* location); + explicit QJSValueValueSpace(LEPUSRuntime* runtime); + ~QJSValueValueSpace(); + void* Create(); + void IterateAllRoots(int local_idx); + LEPUSRuntime* runtime() const { return runtime_; } + + private: + LEPUSRuntime* runtime_; + NodeSpace* regular_nodes_; +}; + +#endif // SRC_GC_QJSVALUEVALUE_SPACE_H_ diff --git a/NativeScript/napi/android/primjs/include/gc/sweeper.h b/NativeScript/napi/android/primjs/include/gc/sweeper.h new file mode 100644 index 000000000..df7dacab2 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/gc/sweeper.h @@ -0,0 +1,58 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_GC_SWEEPER_H_ +#define SRC_GC_SWEEPER_H_ +#include + +#include "quickjs/include/quickjs-inner.h" + +class Sweeper { + public: + Sweeper(mstate state) : m(state) {} + void sweep_finalizer(); + void sweep_free(); + void traverse_finalizer(bool is_only, int64_t begin_time); + void traverse_chunk_for_finalizer(bool is_only = false); + void free_mmap_objects(); + void traverse_chunk_for_free(); + void reinit_freelist(); + int calculate_task_granularity(); + static void generate_freelist(mstate m, msegmentptr sp_begin, + msegmentptr sp_end); + + private: + mstate m; +}; + +void do_finalizer(void* runtime, void* ptr, bool is_only); + +void do_global_finalizer(void* rt); + +void merge_dead_objs(mstate m, msegmentptr sp_begin, msegmentptr sp_end); + +class AcquireIdxScope { + private: + int local_idx; + mstate m_; + + public: + AcquireIdxScope(mstate m) { + local_idx = atomic_acqurie_local_idx(m); + m_ = m; + while (local_idx == -1) { +#ifndef _WIN32 + sched_yield(); +#endif + local_idx = atomic_acqurie_local_idx(m); + } + } + ~AcquireIdxScope() { atomic_release_local_idx(m_, local_idx); } + operator int() { return local_idx; } +}; +void parallel_traverse_heap_segment( + mstate m, size_t segs_in_thread, ByteThreadPool* workerThreadPool, + std::function func); + +#endif // SRC_GC_SWEEPER_H_ diff --git a/NativeScript/napi/android/primjs/include/gc/thread_pool.h b/NativeScript/napi/android/primjs/include/gc/thread_pool.h new file mode 100644 index 000000000..b6b12c3b7 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/gc/thread_pool.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) [2020] Huawei Technologies Co.,Ltd.All rights reserved. + * + * OpenArkCompiler is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan + * PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the + * Mulan PSL v2 for more details. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_GC_THREAD_POOL_H_ +#define SRC_GC_THREAD_POOL_H_ + +#ifndef _WIN32 +#include +#endif + +#include +#include +#include +#include +#include + +class ByteTask { + public: + ByteTask() = default; + virtual ~ByteTask() = default; + virtual void Execute(size_t threadId) = 0; +}; + +class ByteLambdaTask : public ByteTask { + public: + explicit ByteLambdaTask(const std::function &function) + : func(function) {} + ~ByteLambdaTask() = default; + void Execute(size_t threadId) override { func(threadId); } + + private: + std::function func; +}; + +class ByteThreadPool; + +class BytePoolThread { + public: + // use for profiling + std::vector *schedCores; + + BytePoolThread(ByteThreadPool *threadPool, const char *threadName, + size_t threadId, size_t stackSize); + ~BytePoolThread(); + + void SetPriority(int32_t prior); + +#ifndef _WIN32 + // get pthread of thread + pthread_t GetThread() const { return pthread; } + + // get thread id of thread + pid_t GetTid() const { return tid; } +#endif + + static void *WorkerFunc(void *param); + + private: + size_t id; +#ifndef _WIN32 + pthread_t pthread; + pid_t tid; +#endif + std::string name; + ByteThreadPool *pool; +}; + +// manual +// new (SetMaxActiveThreadNum(optional) addTask startPool waitFinish)^. Exit +// delete if need to change MaxActiveThreadNum, should waitFinish or stop pool +// at first +class ByteThreadPool { + public: + // Constructor for thread pool, 1) Create threads, 2) wait all thread created + // & sleep name is the thread pool name. thread name = + // Pool_$(poolname)_ThreadId_$(threadId) maxThreadNum is the max thread number + // in pool. prior is the priority of threads in pool. + ByteThreadPool(const char *name, int32_t maxThreadNum, int32_t prior); + + // Destructor for thread pool, 1) close pool 2) wait thread in pool to exit, + // 3) release resources of class + ~ByteThreadPool(); + + // Set priority of each thread in pool. + void SetPriority(int32_t prior); + + // Set max active thread number of pool, redundant thread hangup in sleep + // condition var. notify more waitting thread get to work when pool is + // running. Range [1 - maxThreadNum]. + void SetMaxActiveThreadNum(int32_t num); + + // Get max active thread number of pool. + int32_t GetMaxActiveThreadNum() const { return maxActiveThreadNum; } + + // Get max thread number of pool, defalut = maxThreadNum. + int32_t GetMaxThreadNum() const { return maxThreadNum; } + + // Add new task to task queue , task should inherit from ByteTask. + void AddTask(ByteTask *task); + + // Add task to thread , func indicate Lambda statement. + void AddTask(std::function func); + + // Start thread pool, notify all sleep threads to get to work + void Start(); + + // Wait all task in task queue finished, if pool stopped, only wait until + // current excuting task finish after all task finished, stop pool + // addToExecute indicate whether the caller thread excute task + void WaitFinish(bool addToExecute, + std::vector *schedCores = nullptr); + + // used in none-parallel concurrent mark + void DrainTaskQueue(); + + // Notify & Wait all thread waitting for task to sleep + void Stop(); + + // Notify all thread in pool to exit , notify all waitFinish thread to + // return,nonblock ^. + void Exit(); + + // Remove all task in task queue + void ClearAllTask(); + + // Get task count in queue + size_t GetTaskNumber() { + std::unique_lock taskLock(taskMutex); + return taskQueue.size(); + } + + // Get all BytePoolThread in pool + const std::vector &GetThreads() const { return threads; } + + private: + // thread default stack size 512 KB. + static const size_t kDefaultStackSize = (512 * 1024); + // int32_t priority; + + std::string name; + // pool stop or running state + std::atomic running; + // is pool exit + std::atomic exit; + // all task put in task queue + std::queue taskQueue; + + // active thread 0 ..... maxActiveThreadNum .....maxThreadNum + // max thread number in pool + int32_t maxThreadNum; + + // max active thread number, redundant thread hang up in threadSleepingCondVar + int32_t maxActiveThreadNum; + + // current active thread, when equals to zero, no thread running, all thread + // slept + std::atomic currActiveThreadNum; + + // current waitting thread, when equals to currActiveThreadNum + // no thread excuting, all task finished + std::atomic currWaittingThreadNum; + + // single lock + std::mutex taskMutex; + + // hangup when no task available + std::condition_variable taskEmptyCondVar; + + // hangup when to much active thread or pool stopped + std::condition_variable threadSleepingCondVar; + + // hangup when there is thread excuting + std::condition_variable allWorkDoneCondVar; + + // hangup when there is thread active + std::condition_variable allThreadStopped; + + // use for profiling + std::vector threads; + + // is pool running or stopped + bool IsRunning() const { return running.load(std::memory_order_relaxed); } + + bool IsExited() const { return exit.load(std::memory_order_relaxed); } + + friend class BytePoolThread; +}; + +#endif // SRC_GC_THREAD_POOL_H_ diff --git a/NativeScript/napi/android/primjs/include/gc/trace-gc.h b/NativeScript/napi/android/primjs/include/gc/trace-gc.h new file mode 100644 index 000000000..c45f27920 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/gc/trace-gc.h @@ -0,0 +1,137 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_GC_TRACE_GC_H_ +#define SRC_GC_TRACE_GC_H_ +#ifdef __cplusplus +extern "C" { +#endif +#include "quickjs/include/quickjs.h" +#ifdef __cplusplus +} +#endif + +struct LEPUSRuntime; + +typedef struct napi_env__ *napi_env; +typedef struct napi_value__ *napi_value; +class napi_handle_scope__; +typedef void napi_func(napi_env env, napi_handle_scope__ *scope); + +enum HandleType { + HANDLE_TYPE_UNDEFINED, + HANDLE_TYPE_HEAP_OBJ, + HANDLE_TYPE_DIR_HEAP_OBJ, + HANDLE_TYPE_LEPUS_VALUE, + HANDLE_TYPE_CSTRING, + HANDLE_TYPE_LEPUS_TOKEN, + HANDLE_TYPE_BC_READER_STATE, + HANDLE_TYPE_VALUE_BUFFER +}; + +typedef struct { + void *ptr; + HandleType type; +} HeapStruct; + +class PtrHandles { + public: + PtrHandles(LEPUSRuntime *rt); + ~PtrHandles(); + // handles + void PushHandle(void *ptr, HandleType type); + void ResetHandle(void *val, HandleType type); + void PushLEPUSValueArrayHandle(LEPUSValue *array, int size, + bool need_init = true); + // special type + void PushLEPUSAtom(uint32_t atom); + void PushLEPUSPropertyDescriptor(LEPUSPropertyDescriptor *desc); + void PushLEPUSValuePtr(LEPUSValue val); + // tools + HeapStruct *GetHandles() const { return handles; } + void SetHeapObjIdx(int idx) { handle_idx = idx; } + int GetHeapObjIdx() const { return handle_idx; } + + private: + int handle_idx; + int handle_size; + HeapStruct *handles; + LEPUSRuntime *rt_; + void InitialHandles(); + void ResizeHandles(); +}; + +class HandleScope { + public: + HandleScope(LEPUSRuntime *rt); + HandleScope(LEPUSContext *ctx); + HandleScope(LEPUSContext *ctx, void *ptr, HandleType type); + ~HandleScope(); + void PushHandle(void *ptr, HandleType type); + void PushLEPUSAtom(JSAtom atom); + void PushLEPUSValueArrayHandle(LEPUSValue *array, int size, + bool need_init = true); + void ResetHandle(void *ptr, HandleType type); + void PushLEPUSPropertyDescriptor(LEPUSPropertyDescriptor *desc); + + private: + PtrHandles *ptr_handles; + int handle_prev_idx; +}; + +class napi_handle_scope__ { + public: + napi_handle_scope__(napi_env env, LEPUSContext *ctx, napi_func *func = nullptr); + explicit napi_handle_scope__(LEPUSContext *ctx) + : env_(nullptr), + ctx_(ctx), + handle_tail_(nullptr), + reset_napi_env(nullptr) { + is_gc = ctx_ == nullptr ? false : LEPUS_IsGCMode(ctx_); + if (is_gc) { + prev_ = reinterpret_cast(GetNapiScope(ctx_)); + SetNapiScope(ctx_, this); + } + } + + inline ~napi_handle_scope__() { + Handle *curr = handle_tail_; + while (curr) { + Handle *temp = curr; + if (!is_gc) { + LEPUS_FreeValue(ctx_, curr->value); + } + curr = curr->prev; + delete temp; + } + if (is_gc) { + SetNapiScope(ctx_, prev_); + } else { + reset_napi_env(env_, prev_); + } + } + + napi_handle_scope__(const napi_handle_scope__ &) = delete; + void operator=(const napi_handle_scope__ &) = delete; + + napi_value Escape(napi_value v); + + napi_value CreateHandle(LEPUSValue v); + struct Handle { + LEPUSValue value; + Handle *prev; + }; + Handle *GetHandle() { return handle_tail_; } + napi_handle_scope__ *GetPrevScope() { return prev_; } + + napi_env env_; + LEPUSContext *ctx_; + bool is_gc; + napi_handle_scope__ *prev_; + Handle *handle_tail_; + napi_func *reset_napi_env; +}; + + +#endif // SRC_GC_TRACE_GC_H_ diff --git a/NativeScript/napi/android/primjs/include/quickjs/BUILD.gn b/NativeScript/napi/android/primjs/include/quickjs/BUILD.gn new file mode 100644 index 000000000..0c3cb42c7 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright 2024 The Lynx Authors. All rights reserved. +# Licensed under the Apache License Version 2.0 that can be found in the +# LICENSE file in the root directory of this source tree. +import("//Primjs.gni") +primjs_source_set("quickjs") { + sources = [ + "source/cutils.cc", + "source/libbf.cc", + "source/libregexp.cc", + "source/libunicode.cc", + "source/primjs_monitor.cc", + "source/quickjs-libc.cc", + "source/quickjs.cc", + "source/quickjs_gc.cc", + "source/quickjs_queue.cc", + "source/quickjs_version.cc", + ] + if (use_bignum) { + sources += [ "source/libbf.cc" ] + } +} diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/base_export.h b/NativeScript/napi/android/primjs/include/quickjs/include/base_export.h new file mode 100644 index 000000000..81216f05d --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/base_export.h @@ -0,0 +1,18 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_BASE_EXPORT_H_ +#define SRC_INTERPRETER_QUICKJS_INCLUDE_BASE_EXPORT_H_ + +#if defined(WIN32) +#define QJS_EXPORT __declspec(dllimport) +#define QJS_EXPORT_FOR_DEVTOOL __declspec(dllimport) +#define QJS_HIDE +#else // defined(WIN32) +#define QJS_EXPORT __attribute__((visibility("default"))) +#define QJS_EXPORT_FOR_DEVTOOL __attribute__((visibility("default"))) +#define QJS_HIDE __attribute__((visibility("hidden"))) +#endif // defined(WIN32) + +#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_BASE_EXPORT_H_ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/cutils.h b/NativeScript/napi/android/primjs/include/quickjs/include/cutils.h new file mode 100644 index 000000000..97fd0b478 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/cutils.h @@ -0,0 +1,255 @@ +/* + * C utilities + * + * Copyright (c) 2017 Fabrice Bellard + * Copyright (c) 2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_CUTILS_H_ +#define SRC_INTERPRETER_QUICKJS_INCLUDE_CUTILS_H_ + +#include +#include + +#include "base_export.h" + +/* set if CPU is big endian */ +#undef WORDS_BIGENDIAN + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#define force_inline inline __attribute__((always_inline)) +#define no_inline __attribute__((noinline)) +#define __maybe_unused __attribute__((unused)) + +#define xglue(x, y) x##y +#define glue(x, y) xglue(x, y) +#define stringify(s) tostring(s) +#define tostring(s) #s + +#ifndef offsetof +#define offsetof(type, field) ((size_t) & ((type *)0)->field) +#endif +#ifndef countof +#define countof(x) (sizeof(x) / sizeof((x)[0])) +#endif + +typedef int BOOL; + +#ifndef FALSE +enum { + FALSE = 0, + TRUE = 1, +}; +#endif + +QJS_HIDE void pstrcpy(char *buf, int buf_size, const char *str); +QJS_HIDE char *pstrcat(char *buf, int buf_size, const char *s); +QJS_HIDE int strstart(const char *str, const char *val, const char **ptr); +QJS_HIDE int has_suffix(const char *str, const char *suffix); + +static inline int max_int(int a, int b) { + if (a > b) + return a; + else + return b; +} + +static inline int min_int(int a, int b) { + if (a < b) + return a; + else + return b; +} + +static inline uint32_t max_uint32(uint32_t a, uint32_t b) { + if (a > b) + return a; + else + return b; +} + +static inline uint32_t min_uint32(uint32_t a, uint32_t b) { + if (a < b) + return a; + else + return b; +} + +static inline int64_t max_int64(int64_t a, int64_t b) { + if (a > b) + return a; + else + return b; +} + +static inline int64_t min_int64(int64_t a, int64_t b) { + if (a < b) + return a; + else + return b; +} + +/* WARNING: undefined if a = 0 */ +static inline int clz32(unsigned int a) { return __builtin_clz(a); } + +/* WARNING: undefined if a = 0 */ +static inline int clz64(uint64_t a) { return __builtin_clzll(a); } + +/* WARNING: undefined if a = 0 */ +static inline int ctz32(unsigned int a) { return __builtin_ctz(a); } + +/* WARNING: undefined if a = 0 */ +static inline int ctz64(uint64_t a) { return __builtin_ctzll(a); } + +struct __attribute__((packed)) packed_u64 { + uint64_t v; +}; + +struct __attribute__((packed)) packed_u32 { + uint32_t v; +}; + +struct __attribute__((packed)) packed_u16 { + uint16_t v; +}; + +static inline uint64_t get_u64(const uint8_t *tab) { + return ((const struct packed_u64 *)tab)->v; +} + +static inline int64_t get_i64(const uint8_t *tab) { + return (int64_t)((const struct packed_u64 *)tab)->v; +} + +static inline void put_u64(uint8_t *tab, uint64_t val) { + ((struct packed_u64 *)tab)->v = val; +} + +static inline uint32_t get_u32(const uint8_t *tab) { + return ((const struct packed_u32 *)tab)->v; +} + +static inline int32_t get_i32(const uint8_t *tab) { + return (int32_t)((const struct packed_u32 *)tab)->v; +} + +static inline void put_u32(uint8_t *tab, uint32_t val) { + ((struct packed_u32 *)tab)->v = val; +} + +static inline uint32_t get_u16(const uint8_t *tab) { + return ((const struct packed_u16 *)tab)->v; +} + +static inline int32_t get_i16(const uint8_t *tab) { + return (int16_t)((const struct packed_u16 *)tab)->v; +} + +static inline void put_u16(uint8_t *tab, uint16_t val) { + ((struct packed_u16 *)tab)->v = val; +} + +static inline uint32_t get_u8(const uint8_t *tab) { return *tab; } + +static inline int32_t get_i8(const uint8_t *tab) { return (int8_t)*tab; } + +static inline void put_u8(uint8_t *tab, uint8_t val) { *tab = val; } + +static inline uint16_t bswap16(uint16_t x) { return (x >> 8) | (x << 8); } + +static inline uint32_t bswap32(uint32_t v) { + return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | + ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); +} + +static inline uint64_t bswap64(uint64_t v) { + return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | + ((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) | + ((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) | + ((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) | + ((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) | + ((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) | + ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | + ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); +} + +/* XXX: should take an extra argument to pass slack information to the caller */ +typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size, + int alloc_tag); + +typedef struct DynBuf { + uint8_t *buf; + size_t size; + size_t allocated_size; + BOOL error; /* true if a memory allocation error occurred */ + DynBufReallocFunc *realloc_func; + void *opaque; /* for realloc_func */ +} DynBuf; + +QJS_HIDE void dbuf_init(DynBuf *s); +QJS_HIDE void dbuf_init2(DynBuf *s, void *opaque, + DynBufReallocFunc *realloc_func); +QJS_HIDE int dbuf_realloc(DynBuf *s, size_t new_size, int alloc_tag = 1); +QJS_HIDE int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, + size_t len); +QJS_HIDE int dbuf_put(DynBuf *s, const uint8_t *data, size_t len); +QJS_HIDE int dbuf_put_self(DynBuf *s, size_t offset, size_t len); +QJS_HIDE int dbuf_putc(DynBuf *s, uint8_t c); +QJS_HIDE int dbuf_putstr(DynBuf *s, const char *str); +QJS_HIDE static inline int dbuf_put_u16(DynBuf *s, uint16_t val) { + return dbuf_put(s, (uint8_t *)&val, 2); +} +QJS_HIDE static inline int dbuf_put_u32(DynBuf *s, uint32_t val) { + return dbuf_put(s, (uint8_t *)&val, 4); +} +QJS_HIDE static inline int dbuf_put_u64(DynBuf *s, uint64_t val) { + return dbuf_put(s, (uint8_t *)&val, 8); +} +int __attribute__((format(printf, 2, 3))) QJS_HIDE dbuf_printf(DynBuf *s, + const char *fmt, + ...); +QJS_HIDE void dbuf_free(DynBuf *s); +static inline BOOL dbuf_error(DynBuf *s) { return s->error; } + +#define UTF8_CHAR_LEN_MAX 6 + +int unicode_to_utf8(uint8_t *buf, unsigned int c); +int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp); + +static inline int from_hex(int c) { + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else + return -1; +} + +void rqsort(void *base, size_t nmemb, size_t size, + int (*cmp)(const void *, const void *, void *), void *arg); + +#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_CUTILS_H_ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/libbf.h b/NativeScript/napi/android/primjs/include/quickjs/include/libbf.h new file mode 100644 index 000000000..d1e4256e2 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/libbf.h @@ -0,0 +1,343 @@ +/* + * Tiny arbitrary precision floating point library + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_LIBBF_H_ +#define SRC_INTERPRETER_QUICKJS_INCLUDE_LIBBF_H_ + +#include +#include + +#if defined(__x86_64__) +#define LIMB_LOG2_BITS 6 +#else +#define LIMB_LOG2_BITS 5 +#endif + +#define LIMB_BITS (1 << LIMB_LOG2_BITS) + +#if LIMB_BITS == 64 +typedef __int128 int128_t; +typedef unsigned __int128 uint128_t; +typedef int64_t slimb_t; +typedef uint64_t limb_t; +typedef uint128_t dlimb_t; +#define EXP_MIN INT64_MIN +#define EXP_MAX INT64_MAX + +#else + +typedef int32_t slimb_t; +typedef uint32_t limb_t; +typedef uint64_t dlimb_t; +#define EXP_MIN INT32_MIN +#define EXP_MAX INT32_MAX + +#endif + +/* in bits */ +#define BF_EXP_BITS_MIN 3 +#define BF_EXP_BITS_MAX (LIMB_BITS - 2) +#define BF_PREC_MIN 2 +#define BF_PREC_MAX (((limb_t)1 << BF_EXP_BITS_MAX) - 2) +#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */ + +#if LIMB_BITS == 64 +#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197)) +#else +#define BF_CHKSUM_MOD 975620677U +#endif + +#define BF_EXP_ZERO EXP_MIN +#define BF_EXP_INF (EXP_MAX - 1) +#define BF_EXP_NAN EXP_MAX + +/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0, + +/-infinity is represented with expn = BF_EXP_INF and len = 0, + NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored) + */ +typedef struct { + struct bf_context_t *ctx; + int sign; + slimb_t expn; + limb_t len; + limb_t *tab; +} bf_t; + +typedef enum { + BF_RNDN, /* round to nearest, ties to even */ + BF_RNDZ, /* round to zero */ + BF_RNDD, /* round to -inf */ + BF_RNDU, /* round to +inf */ + BF_RNDNA, /* round to nearest, ties away from zero */ + BF_RNDNU, /* round to nearest, ties to +inf */ + BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU, + inexact flag is always set) */ +} bf_rnd_t; + +/* allow subnormal numbers (only available if the number of exponent + bits is < BF_EXP_BITS_MAX and prec != BF_PREC_INF) */ +#define BF_FLAG_SUBNORMAL (1 << 3) + +#define BF_RND_MASK 0x7 +#define BF_EXP_BITS_SHIFT 4 +#define BF_EXP_BITS_MASK 0x3f + +/* contains the rounding mode and number of exponents bits */ +typedef uint32_t bf_flags_t; + +typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size); + +typedef struct { + bf_t val; + limb_t prec; +} BFConstCache; + +typedef struct bf_context_t { + void *realloc_opaque; + bf_realloc_func_t *realloc_func; + BFConstCache log2_cache; + BFConstCache pi_cache; + struct BFNTTState *ntt_state; +} bf_context_t; + +static inline int bf_get_exp_bits(bf_flags_t flags) { + return BF_EXP_BITS_MAX - ((flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK); +} + +static inline bf_flags_t bf_set_exp_bits(int n) { + return (BF_EXP_BITS_MAX - n) << BF_EXP_BITS_SHIFT; +} + +/* returned status */ +#define BF_ST_INVALID_OP (1 << 0) +#define BF_ST_DIVIDE_ZERO (1 << 1) +#define BF_ST_OVERFLOW (1 << 2) +#define BF_ST_UNDERFLOW (1 << 3) +#define BF_ST_INEXACT (1 << 4) +/* not used yet, indicate that a memory allocation error occured. NaN + is returned */ +#define BF_ST_MEM_ERROR (1 << 5) + +#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */ + +static inline slimb_t bf_max(slimb_t a, slimb_t b) { + if (a > b) + return a; + else + return b; +} + +static inline slimb_t bf_min(slimb_t a, slimb_t b) { + if (a < b) + return a; + else + return b; +} + +void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, + void *realloc_opaque); +void bf_context_end(bf_context_t *s); +/* free memory allocated for the bf cache data */ +void bf_clear_cache(bf_context_t *s); + +static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size) { + return realloc(ptr, size); +} + +void bf_init(bf_context_t *s, bf_t *r); + +static inline void bf_delete(bf_t *r) { + bf_context_t *s = r->ctx; + /* we accept to delete a zeroed bf_t structure */ + if (s) { + bf_realloc(s, r->tab, 0); + } +} + +static inline void bf_neg(bf_t *r) { r->sign ^= 1; } + +static inline int bf_is_finite(const bf_t *a) { return (a->expn < BF_EXP_INF); } + +static inline int bf_is_nan(const bf_t *a) { return (a->expn == BF_EXP_NAN); } + +static inline int bf_is_zero(const bf_t *a) { return (a->expn == BF_EXP_ZERO); } + +void bf_set_ui(bf_t *r, uint64_t a); +void bf_set_si(bf_t *r, int64_t a); +void bf_set_nan(bf_t *r); +void bf_set_zero(bf_t *r, int is_neg); +void bf_set_inf(bf_t *r, int is_neg); +void bf_set(bf_t *r, const bf_t *a); +void bf_move(bf_t *r, bf_t *a); +int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode); +void bf_set_float64(bf_t *a, double d); + +int bf_cmpu(const bf_t *a, const bf_t *b); +int bf_cmp_full(const bf_t *a, const bf_t *b); +int bf_cmp_eq(const bf_t *a, const bf_t *b); +int bf_cmp_le(const bf_t *a, const bf_t *b); +int bf_cmp_lt(const bf_t *a, const bf_t *b); +int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); +int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); +int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); +int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, + bf_flags_t flags); +int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags); +int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); +#define BF_DIVREM_EUCLIDIAN BF_RNDF +int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +int bf_fmod(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); +int bf_remainder(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); +int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); +int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec, bf_flags_t flags); +int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b, limb_t prec, bf_flags_t flags); +int bf_rint(bf_t *r, limb_t prec, bf_flags_t flags); +int bf_round(bf_t *r, limb_t prec, bf_flags_t flags); +int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a); +int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +slimb_t bf_get_exp_min(const bf_t *a); +void bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b); +void bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b); +void bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b); + +/* additional flags for bf_atof */ +/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */ +#define BF_ATOF_NO_HEX (1 << 16) +/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */ +#define BF_ATOF_BIN_OCT (1 << 17) +/* Only accept integers (no decimal point, no exponent, no infinity nor NaN */ +#define BF_ATOF_INT_ONLY (1 << 18) +/* Do not accept radix prefix after sign */ +#define BF_ATOF_NO_PREFIX_AFTER_SIGN (1 << 19) +/* Do not parse NaN and parse case sensitive 'Infinity' */ +#define BF_ATOF_JS_QUIRKS (1 << 20) +/* Do not round integers to the indicated precision */ +#define BF_ATOF_INT_PREC_INF (1 << 21) +/* Support legacy octal syntax for well formed numbers */ +#define BF_ATOF_LEGACY_OCTAL (1 << 22) +/* accept _ between digits as a digit separator */ +#define BF_ATOF_UNDERSCORE_SEP (1 << 23) +/* if a 'n' suffix is present, force integer parsing (XXX: remove) */ +#define BF_ATOF_INT_N_SUFFIX (1 << 24) +/* if set return NaN if empty number string (instead of 0) */ +#define BF_ATOF_NAN_IF_EMPTY (1 << 25) +/* only accept decimal floating point if radix = 0 */ +#define BF_ATOF_ONLY_DEC_FLOAT (1 << 26) + +/* additional return flags */ +/* indicate that the parsed number is an integer (only set when the + flags BF_ATOF_INT_PREC_INF or BF_ATOF_INT_N_SUFFIX are used) */ +#define BF_ATOF_ST_INTEGER (1 << 5) +/* integer parsed as legacy octal */ +#define BF_ATOF_ST_LEGACY_OCTAL (1 << 6) + +int bf_atof(bf_t *a, const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags); +/* this version accepts prec = BF_PREC_INF and returns the radix + exponent */ +int bf_atof2(bf_t *r, slimb_t *pexponent, const char *str, const char **pnext, + int radix, limb_t prec, bf_flags_t flags); +int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, slimb_t expn, + limb_t prec, bf_flags_t flags); + +#define BF_FTOA_FORMAT_MASK (3 << 16) +/* fixed format: prec significant digits rounded with (flags & + BF_RND_MASK). Exponential notation is used if too many zeros are + needed. */ +#define BF_FTOA_FORMAT_FIXED (0 << 16) +/* fractional format: prec digits after the decimal point rounded with + (flags & BF_RND_MASK) */ +#define BF_FTOA_FORMAT_FRAC (1 << 16) +/* free format: use as many digits as necessary so that bf_atof() + return the same number when using precision 'prec', rounding to + nearest and the subnormal+exponent configuration of 'flags'. The + result is meaningful only if 'a' is already rounded to the wanted + precision. + + Infinite precision (BF_PREC_INF) is supported when the radix is a + power of two. */ +#define BF_FTOA_FORMAT_FREE (2 << 16) +/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits + (takes more computation time). */ +#define BF_FTOA_FORMAT_FREE_MIN (3 << 16) + +/* force exponential notation for fixed or free format */ +#define BF_FTOA_FORCE_EXP (1 << 20) +/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for + base 2 if non zero value */ +#define BF_FTOA_ADD_PREFIX (1 << 21) +#define BF_FTOA_JS_QUIRKS (1 << 22) + +size_t bf_ftoa(char **pbuf, const bf_t *a, int radix, limb_t prec, + bf_flags_t flags); + +/* modulo 2^n instead of saturation. NaN and infinity return 0 */ +#define BF_GET_INT_MOD (1 << 0) +int bf_get_int32(int *pres, const bf_t *a, int flags); +int bf_get_int64(int64_t *pres, const bf_t *a, int flags); + +/* the following functions are exported for testing only. */ +void bf_print_str(const char *str, const bf_t *a); +void bf_resize(bf_t *r, limb_t len); +int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len); +void bf_recip(bf_t *r, const bf_t *a, limb_t prec); +void bf_rsqrt(bf_t *a, const bf_t *x, limb_t prec); +int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags); +int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k); +slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, + int is_ceil1); + +/* transcendental functions */ +int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags); +int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags); +int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +#define BF_POW_JS_QUICKS (1 << 16) +int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, + bf_flags_t flags); +int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, limb_t prec, + bf_flags_t flags); +int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); + +#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_LIBBF_H_ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/libregexp-opcode.h b/NativeScript/napi/android/primjs/include/quickjs/include/libregexp-opcode.h new file mode 100644 index 000000000..480c23df5 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/libregexp-opcode.h @@ -0,0 +1,61 @@ +/* + * Regular Expression Engine + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +#ifdef DEF + +DEF(invalid, 1) /* never used */ +DEF(char, 3) +DEF(char32, 5) +DEF(dot, 1) +DEF(any, 1) /* same as dot but match any character including line terminator */ +DEF(line_start, 1) +DEF(line_end, 1) +DEF(goto, 5) +DEF(split_goto_first, 5) +DEF(split_next_first, 5) +DEF(match, 1) +DEF(save_start, 2) /* save start position */ +DEF(save_end, 2) /* save end position, must come after saved_start */ +DEF(save_reset, 3) /* reset save positions */ +DEF(loop, 5) /* decrement the top the stack and goto if != 0 */ +DEF(push_i32, 5) /* push integer on the stack */ +DEF(drop, 1) +DEF(word_boundary, 1) +DEF(not_word_boundary, 1) +DEF(back_reference, 2) +DEF(backward_back_reference, 2) /* must come after back_reference */ +DEF(range, 3) /* variable length */ +DEF(range32, 3) /* variable length */ +DEF(lookahead, 5) +DEF(negative_lookahead, 5) +DEF(push_char_pos, 1) /* push the character position on the stack */ +DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character + position */ +DEF(prev, 1) /* go to the previous char */ +DEF(simple_greedy_quant, 17) + +#endif /* DEF */ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/libregexp.h b/NativeScript/napi/android/primjs/include/quickjs/include/libregexp.h new file mode 100644 index 000000000..1065fcfb2 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/libregexp.h @@ -0,0 +1,97 @@ +/* + * Regular Expression Engine + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_LIBREGEXP_H_ +#define SRC_INTERPRETER_QUICKJS_INCLUDE_LIBREGEXP_H_ + +#include + +#include "base_export.h" +#include "libunicode.h" + +#define LRE_BOOL int /* for documentation purposes */ + +#define LRE_FLAG_GLOBAL (1 << 0) +#define LRE_FLAG_IGNORECASE (1 << 1) +#define LRE_FLAG_MULTILINE (1 << 2) +#define LRE_FLAG_DOTALL (1 << 3) +#define LRE_FLAG_UTF16 (1 << 4) +#define LRE_FLAG_STICKY (1 << 5) + +#define LRE_FLAG_NAMED_GROUPS \ + (1 << 7) /* named groups are present in the regexp */ + +uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, + const char *buf, size_t buf_len, int re_flags, + void *opaque); +int lre_get_capture_count(const uint8_t *bc_buf); +QJS_HIDE int lre_get_flags(const uint8_t *bc_buf); +int lre_exec(uint8_t **capture, const uint8_t *bc_buf, const uint8_t *cbuf, + int cindex, int clen, int cbuf_type, void *opaque); + +QJS_HIDE int lre_parse_escape(const uint8_t **pp, int allow_utf16); +QJS_HIDE LRE_BOOL lre_is_space(int c); + +/* must be provided by the user */ +QJS_HIDE LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); +void *lre_realloc(__attribute__((unused)) void *opaque, void *ptr, size_t size, + __attribute__((unused)) int alloc_tag); + +/* LEPUS identifier test */ +extern uint32_t const lre_id_start_table_ascii[4]; +extern uint32_t const lre_id_continue_table_ascii[4]; + +static inline int lre_js_is_ident_first(int c) { + if ((uint32_t)c < 128) { + return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { +#ifdef CONFIG_ALL_UNICODE + return lre_is_id_start(c); +#else + return !lre_is_space(c); +#endif + } +} + +void lre_free(void *); + +static inline int lre_js_is_ident_next(int c) { + if ((uint32_t)c < 128) { + return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { + /* ZWNJ and ZWJ are accepted in identifiers */ +#ifdef CONFIG_ALL_UNICODE + return lre_is_id_continue(c) || c == 0x200C || c == 0x200D; +#else + return !lre_is_space(c) || c == 0x200C || c == 0x200D; +#endif + } +} + +#undef LRE_BOOL + +#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_LIBREGEXP_H_ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/libunicode-table.h b/NativeScript/napi/android/primjs/include/quickjs/include/libunicode-table.h new file mode 100644 index 000000000..9b4a5b4b8 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/libunicode-table.h @@ -0,0 +1,3495 @@ +/* + * Regular Expression Engine + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +/* Compressed unicode tables */ +/* Automatically generated file - do not edit */ + +#include + +static const uint32_t case_conv_table1[359] = { + 0x00209a30, 0x00309a00, 0x005a8173, 0x00601730, 0x006c0730, 0x006f81b3, + 0x00701700, 0x007c0700, 0x007f8100, 0x00803040, 0x009801c3, 0x00988190, + 0x00990640, 0x009c9040, 0x00a481b4, 0x00a52e40, 0x00bc0130, 0x00bc8640, + 0x00bf8170, 0x00c00100, 0x00c08130, 0x00c10440, 0x00c30130, 0x00c38240, + 0x00c48230, 0x00c58240, 0x00c70130, 0x00c78130, 0x00c80130, 0x00c88240, + 0x00c98130, 0x00ca0130, 0x00ca8100, 0x00cb0130, 0x00cb8130, 0x00cc0240, + 0x00cd0100, 0x00ce0130, 0x00ce8130, 0x00cf0100, 0x00cf8130, 0x00d00640, + 0x00d30130, 0x00d38240, 0x00d48130, 0x00d60240, 0x00d70130, 0x00d78240, + 0x00d88230, 0x00d98440, 0x00db8130, 0x00dc0240, 0x00de0240, 0x00df8100, + 0x00e20350, 0x00e38350, 0x00e50350, 0x00e69040, 0x00ee8100, 0x00ef1240, + 0x00f801b4, 0x00f88350, 0x00fa0240, 0x00fb0130, 0x00fb8130, 0x00fc2840, + 0x01100130, 0x01111240, 0x011d0131, 0x011d8240, 0x011e8130, 0x011f0131, + 0x011f8201, 0x01208240, 0x01218130, 0x01220130, 0x01228130, 0x01230a40, + 0x01280101, 0x01288101, 0x01290101, 0x01298100, 0x012a0100, 0x012b0200, + 0x012c8100, 0x012d8100, 0x012e0101, 0x01300100, 0x01308101, 0x01318100, + 0x01328101, 0x01330101, 0x01340100, 0x01348100, 0x01350101, 0x01358101, + 0x01360101, 0x01378100, 0x01388101, 0x01390100, 0x013a8100, 0x013e8101, + 0x01400100, 0x01410101, 0x01418100, 0x01438101, 0x01440100, 0x01448100, + 0x01450200, 0x01460100, 0x01490100, 0x014e8101, 0x014f0101, 0x01a28173, + 0x01b80440, 0x01bb0240, 0x01bd8300, 0x01bf8130, 0x01c30130, 0x01c40330, + 0x01c60130, 0x01c70230, 0x01c801d0, 0x01c89130, 0x01d18930, 0x01d60100, + 0x01d68300, 0x01d801d3, 0x01d89100, 0x01e10173, 0x01e18900, 0x01e60100, + 0x01e68200, 0x01e78130, 0x01e80173, 0x01e88173, 0x01ea8173, 0x01eb0173, + 0x01eb8100, 0x01ec1840, 0x01f80173, 0x01f88173, 0x01f90100, 0x01f98100, + 0x01fa01a0, 0x01fa8173, 0x01fb8240, 0x01fc8130, 0x01fd0240, 0x01fe8330, + 0x02001030, 0x02082030, 0x02182000, 0x02281000, 0x02302240, 0x02453640, + 0x02600130, 0x02608e40, 0x02678100, 0x02686040, 0x0298a630, 0x02b0a600, + 0x02c381b5, 0x08502631, 0x08638131, 0x08668131, 0x08682b00, 0x087e8300, + 0x09d05011, 0x09f80610, 0x09fc0620, 0x0e400174, 0x0e408174, 0x0e410174, + 0x0e418174, 0x0e420174, 0x0e428174, 0x0e430174, 0x0e438180, 0x0e440180, + 0x0e482b30, 0x0e5e8330, 0x0ebc8101, 0x0ebe8101, 0x0ec70101, 0x0f007e40, + 0x0f3f1840, 0x0f4b01b5, 0x0f4b81b6, 0x0f4c01b6, 0x0f4c81b6, 0x0f4d01b7, + 0x0f4d8180, 0x0f4f0130, 0x0f506040, 0x0f800800, 0x0f840830, 0x0f880600, + 0x0f8c0630, 0x0f900800, 0x0f940830, 0x0f980800, 0x0f9c0830, 0x0fa00600, + 0x0fa40630, 0x0fa801b0, 0x0fa88100, 0x0fa901d3, 0x0fa98100, 0x0faa01d3, + 0x0faa8100, 0x0fab01d3, 0x0fab8100, 0x0fac8130, 0x0fad8130, 0x0fae8130, + 0x0faf8130, 0x0fb00800, 0x0fb40830, 0x0fb80200, 0x0fb90400, 0x0fbb0200, + 0x0fbc0201, 0x0fbd0201, 0x0fbe0201, 0x0fc008b7, 0x0fc40867, 0x0fc808b8, + 0x0fcc0868, 0x0fd008b8, 0x0fd40868, 0x0fd80200, 0x0fd901b9, 0x0fd981b1, + 0x0fda01b9, 0x0fdb01b1, 0x0fdb81d7, 0x0fdc0230, 0x0fdd0230, 0x0fde0161, + 0x0fdf0173, 0x0fe101b9, 0x0fe181b2, 0x0fe201ba, 0x0fe301b2, 0x0fe381d8, + 0x0fe40430, 0x0fe60162, 0x0fe80200, 0x0fe901d0, 0x0fe981d0, 0x0feb01b0, + 0x0feb81d0, 0x0fec0230, 0x0fed0230, 0x0ff00201, 0x0ff101d3, 0x0ff181d3, + 0x0ff201ba, 0x0ff28101, 0x0ff301b0, 0x0ff381d3, 0x0ff40230, 0x0ff50230, + 0x0ff60131, 0x0ff901ba, 0x0ff981b2, 0x0ffa01bb, 0x0ffb01b2, 0x0ffb81d9, + 0x0ffc0230, 0x0ffd0230, 0x0ffe0162, 0x109301a0, 0x109501a0, 0x109581a0, + 0x10990131, 0x10a70101, 0x10b01031, 0x10b81001, 0x10c18240, 0x125b1a31, + 0x12681a01, 0x16002f31, 0x16182f01, 0x16300240, 0x16310130, 0x16318130, + 0x16320130, 0x16328100, 0x16330100, 0x16338640, 0x16368130, 0x16370130, + 0x16378130, 0x16380130, 0x16390240, 0x163a8240, 0x163f0230, 0x16406440, + 0x16758440, 0x16790240, 0x16802600, 0x16938100, 0x16968100, 0x53202e40, + 0x53401c40, 0x53910e40, 0x53993e40, 0x53bc8440, 0x53be8130, 0x53bf0a40, + 0x53c58240, 0x53c68130, 0x53c80440, 0x53ca0101, 0x53cb1440, 0x53d50130, + 0x53d58130, 0x53d60130, 0x53d68130, 0x53d70130, 0x53d80130, 0x53d88130, + 0x53d90130, 0x53d98131, 0x53da0c40, 0x53e10240, 0x53e20131, 0x53e28130, + 0x53e30130, 0x55a98101, 0x55b85020, 0x7d8001b2, 0x7d8081b2, 0x7d8101b2, + 0x7d8181da, 0x7d8201da, 0x7d8281b3, 0x7d8301b3, 0x7d8981bb, 0x7d8a01bb, + 0x7d8a81bb, 0x7d8b01bc, 0x7d8b81bb, 0x7f909a31, 0x7fa09a01, 0x82002831, + 0x82142801, 0x82582431, 0x826c2401, 0x86403331, 0x86603301, 0x8c502031, + 0x8c602001, 0xb7202031, 0xb7302001, 0xf4802231, 0xf4912201, +}; + +static const uint8_t case_conv_table2[359] = { + 0x01, 0x00, 0x9c, 0x06, 0x07, 0x4d, 0x03, 0x04, 0x10, 0x00, 0x8f, 0x0b, + 0x00, 0x00, 0x11, 0x00, 0x08, 0x00, 0x53, 0x4a, 0x51, 0x00, 0x52, 0x00, + 0x53, 0x00, 0x3a, 0x54, 0x55, 0x00, 0x57, 0x59, 0x3f, 0x5d, 0x5c, 0x00, + 0x46, 0x61, 0x63, 0x42, 0x64, 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, + 0x6c, 0x00, 0x6e, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, + 0x93, 0x00, 0x00, 0x20, 0x35, 0x00, 0x27, 0x00, 0x21, 0x00, 0x24, 0x22, + 0x2a, 0x00, 0x13, 0x6b, 0x6d, 0x00, 0x26, 0x24, 0x27, 0x14, 0x16, 0x18, + 0x1b, 0x1c, 0x3e, 0x1e, 0x3f, 0x1f, 0x39, 0x3d, 0x22, 0x21, 0x41, 0x1e, + 0x40, 0x25, 0x25, 0x26, 0x28, 0x20, 0x2a, 0x49, 0x2c, 0x43, 0x2e, 0x4b, + 0x30, 0x4c, 0x32, 0x44, 0x42, 0x99, 0x00, 0x00, 0x95, 0x8f, 0x7d, 0x7e, + 0x83, 0x84, 0x12, 0x80, 0x82, 0x76, 0x77, 0x12, 0x7b, 0xa3, 0x7c, 0x78, + 0x79, 0x8a, 0x92, 0x98, 0xa6, 0xa0, 0x85, 0x00, 0x9a, 0xa1, 0x93, 0x75, + 0x33, 0x95, 0x00, 0x8e, 0x00, 0x74, 0x99, 0x98, 0x97, 0x96, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0xa1, 0xa0, 0x15, 0x2e, 0x2f, 0x30, 0xb4, 0xb5, + 0x4c, 0xaa, 0xa9, 0x12, 0x14, 0x1e, 0x21, 0x22, 0x22, 0x2a, 0x34, 0x35, + 0xa6, 0xa7, 0x36, 0x1f, 0x4a, 0x00, 0x00, 0x97, 0x01, 0x5a, 0xda, 0x1d, + 0x36, 0x05, 0x00, 0xc4, 0xc3, 0xc6, 0xc5, 0xc8, 0xc7, 0xca, 0xc9, 0xcc, + 0xcb, 0xc4, 0xd5, 0x45, 0xd6, 0x42, 0xd7, 0x46, 0xd8, 0xce, 0xd0, 0xd2, + 0xd4, 0xda, 0xd9, 0xee, 0xf6, 0xfe, 0x0e, 0x07, 0x0f, 0x80, 0x9f, 0x00, + 0x21, 0x80, 0xa3, 0xed, 0x00, 0xc0, 0x40, 0xc6, 0x60, 0xe7, 0xdb, 0xe6, + 0x99, 0xc0, 0x00, 0x00, 0x06, 0x60, 0xdc, 0x29, 0xfd, 0x15, 0x12, 0x06, + 0x16, 0xf8, 0xdd, 0x06, 0x15, 0x12, 0x84, 0x08, 0xc6, 0x16, 0xff, 0xdf, + 0x03, 0xc0, 0x40, 0x00, 0x46, 0x60, 0xde, 0xe0, 0x6d, 0x37, 0x38, 0x39, + 0x15, 0x14, 0x17, 0x16, 0x00, 0x1a, 0x19, 0x1c, 0x1b, 0x00, 0x5f, 0xb7, + 0x65, 0x44, 0x47, 0x00, 0x4f, 0x62, 0x4e, 0x50, 0x00, 0x00, 0x48, 0x00, + 0x00, 0x00, 0xa3, 0xa4, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00, + 0x00, 0x5a, 0x00, 0x48, 0x00, 0x5b, 0x56, 0x58, 0x60, 0x5e, 0x70, 0x69, + 0x6f, 0x4b, 0x00, 0x00, 0x3b, 0x67, 0xb8, 0x45, 0xa8, 0x8a, 0x8b, 0x8c, + 0xab, 0xac, 0x58, 0x58, 0xaf, 0x94, 0xb0, 0x6f, 0xb2, 0x5a, 0x59, 0x5c, + 0x5b, 0x5e, 0x5d, 0x60, 0x5f, 0x62, 0x61, 0x64, 0x63, 0x66, 0x65, +}; + +static const uint16_t case_conv_ext[58] = { + 0x0399, 0x0308, 0x0301, 0x03a5, 0x0313, 0x0300, 0x0342, 0x0391, 0x0397, + 0x03a9, 0x0046, 0x0049, 0x004c, 0x0053, 0x0069, 0x0307, 0x02bc, 0x004e, + 0x004a, 0x030c, 0x0535, 0x0552, 0x0048, 0x0331, 0x0054, 0x0057, 0x030a, + 0x0059, 0x0041, 0x02be, 0x1f08, 0x1f80, 0x1f28, 0x1f90, 0x1f68, 0x1fa0, + 0x1fba, 0x0386, 0x1fb3, 0x1fca, 0x0389, 0x1fc3, 0x03a1, 0x1ffa, 0x038f, + 0x1ff3, 0x0544, 0x0546, 0x053b, 0x054e, 0x053d, 0x03b8, 0x0462, 0xa64a, + 0x1e60, 0x03c9, 0x006b, 0x00e5, +}; + +static const uint8_t unicode_prop_Cased1_table[172] = { + 0x40, 0xa9, 0x80, 0x8e, 0x80, 0xfc, 0x80, 0xd3, 0x80, 0x8c, 0x80, 0x8d, + 0x81, 0x8d, 0x02, 0x80, 0xe1, 0x80, 0x91, 0x85, 0x9a, 0x01, 0x00, 0x01, + 0x11, 0x00, 0x01, 0x04, 0x08, 0x01, 0x08, 0x30, 0x08, 0x01, 0x15, 0x20, + 0x00, 0x39, 0x99, 0x31, 0x9d, 0x84, 0x40, 0x94, 0x80, 0xd6, 0x82, 0xa6, + 0x80, 0x41, 0x62, 0x80, 0xa6, 0x80, 0x57, 0x76, 0xf8, 0x02, 0x80, 0x8f, + 0x80, 0xb0, 0x40, 0xdb, 0x08, 0x80, 0x41, 0xd0, 0x80, 0x8c, 0x80, 0x8f, + 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, 0x14, 0x28, 0x10, 0x11, 0x02, 0x01, + 0x18, 0x0b, 0x24, 0x4b, 0x26, 0x01, 0x01, 0x86, 0xe5, 0x80, 0x60, 0x79, + 0xb6, 0x81, 0x40, 0x91, 0x81, 0xbd, 0x88, 0x94, 0x05, 0x80, 0x98, 0x80, + 0xc7, 0x82, 0x43, 0x34, 0xa2, 0x06, 0x80, 0x8b, 0x61, 0x28, 0x97, 0xd4, + 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, + 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80, 0x41, 0x53, + 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x59, 0x63, 0x99, + 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Cased1_index[18] = { + 0xb9, 0x02, 0xe0, 0xa0, 0x1e, 0x40, 0x9e, 0xa6, 0x40, + 0xba, 0xd4, 0x01, 0x89, 0xd7, 0x01, 0x8a, 0xf1, 0x01, +}; + +static const uint8_t unicode_prop_Case_Ignorable_table[678] = { + 0xa6, 0x05, 0x80, 0x8a, 0x80, 0xa2, 0x00, 0x80, 0xc6, 0x03, 0x00, 0x03, + 0x01, 0x81, 0x41, 0xf6, 0x40, 0xbf, 0x19, 0x18, 0x88, 0x08, 0x80, 0x40, + 0xfa, 0x86, 0x40, 0xce, 0x80, 0xb6, 0xac, 0x00, 0x01, 0x01, 0x00, 0xab, + 0x80, 0x8a, 0x85, 0x89, 0x8a, 0x00, 0xa2, 0x80, 0x89, 0x94, 0x8f, 0x80, + 0xe4, 0x38, 0x89, 0x03, 0xa0, 0x00, 0x80, 0x9d, 0x9a, 0xda, 0x8a, 0xb9, + 0x8a, 0x18, 0x08, 0x97, 0x97, 0xaa, 0x82, 0xf6, 0xaf, 0xb6, 0x00, 0x03, + 0x3b, 0x02, 0x86, 0x89, 0x81, 0x8c, 0x80, 0x8e, 0x80, 0xb9, 0x03, 0x1f, + 0x80, 0x93, 0x81, 0x99, 0x01, 0x81, 0xb8, 0x03, 0x0b, 0x09, 0x12, 0x80, + 0x9d, 0x0a, 0x80, 0x8a, 0x81, 0xb8, 0x03, 0x20, 0x0b, 0x80, 0x93, 0x81, + 0x95, 0x28, 0x80, 0xb9, 0x01, 0x00, 0x1f, 0x07, 0x80, 0x8a, 0x81, 0x9d, + 0x80, 0xbc, 0x80, 0x8b, 0x80, 0xb1, 0x02, 0x80, 0xb8, 0x14, 0x10, 0x1e, + 0x81, 0x8a, 0x81, 0x9c, 0x80, 0xb9, 0x01, 0x05, 0x04, 0x81, 0x93, 0x81, + 0x9b, 0x81, 0xb8, 0x0b, 0x1f, 0x80, 0x93, 0x81, 0xe5, 0x06, 0x10, 0x80, + 0xd9, 0x01, 0x86, 0x8a, 0x88, 0xe1, 0x01, 0x88, 0x88, 0x00, 0x85, 0xc9, + 0x81, 0x9a, 0x00, 0x00, 0x80, 0xb6, 0x8d, 0x04, 0x01, 0x84, 0x8a, 0x80, + 0xa3, 0x88, 0x80, 0xe5, 0x18, 0x28, 0x09, 0x81, 0x98, 0x0b, 0x82, 0x8f, + 0x83, 0x8c, 0x01, 0x0d, 0x80, 0x8e, 0x80, 0xdd, 0x80, 0x42, 0x5f, 0x82, + 0x43, 0xb1, 0x82, 0x9c, 0x82, 0x9c, 0x81, 0x9d, 0x81, 0xbf, 0x08, 0x37, + 0x01, 0x8a, 0x10, 0x20, 0xac, 0x83, 0xb3, 0x80, 0xc0, 0x81, 0xa1, 0x80, + 0xf5, 0x13, 0x81, 0x88, 0x05, 0x82, 0x40, 0xda, 0x09, 0x80, 0xb9, 0x00, + 0x30, 0x00, 0x01, 0x3d, 0x89, 0x08, 0xa6, 0x07, 0x8e, 0xc0, 0x83, 0xaf, + 0x00, 0x20, 0x04, 0x80, 0xa7, 0x88, 0x8b, 0x81, 0x9f, 0x19, 0x08, 0x82, + 0xb7, 0x00, 0x0a, 0x00, 0x82, 0xb9, 0x39, 0x81, 0xbf, 0x85, 0xd1, 0x10, + 0x8c, 0x06, 0x18, 0x28, 0x11, 0xb1, 0xbe, 0x8c, 0x80, 0xa1, 0xde, 0x04, + 0x41, 0xbc, 0x00, 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c, 0x82, 0x8c, 0x81, + 0x8b, 0x27, 0x81, 0x89, 0x01, 0x01, 0x84, 0xb0, 0x20, 0x89, 0x00, 0x8c, + 0x80, 0x8f, 0x8c, 0xb2, 0xa0, 0x4b, 0x8a, 0x81, 0xf0, 0x82, 0xfc, 0x80, + 0x8e, 0x80, 0xdf, 0x9f, 0xae, 0x80, 0x41, 0xd4, 0x80, 0xa3, 0x1a, 0x24, + 0x80, 0xdc, 0x85, 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80, 0x44, 0xe1, 0x85, + 0x41, 0x0d, 0x80, 0xe1, 0x18, 0x89, 0x00, 0x9b, 0x83, 0xcf, 0x81, 0x8d, + 0xa1, 0xcd, 0x80, 0x96, 0x82, 0xec, 0x0f, 0x02, 0x03, 0x80, 0x98, 0x81, + 0x40, 0x9c, 0x81, 0x99, 0x91, 0x8c, 0x80, 0xa5, 0x87, 0x98, 0x8a, 0xad, + 0x82, 0xaf, 0x01, 0x19, 0x81, 0x90, 0x80, 0x94, 0x81, 0xc1, 0x29, 0x09, + 0x81, 0x8b, 0x07, 0x80, 0xa2, 0x80, 0x8a, 0x80, 0xb2, 0x00, 0x11, 0x0c, + 0x08, 0x80, 0x9a, 0x80, 0x8d, 0x0c, 0x08, 0x80, 0xe3, 0x84, 0x40, 0x84, + 0x01, 0x03, 0x80, 0x60, 0x4f, 0x2f, 0x80, 0x40, 0x92, 0x8f, 0x42, 0x3d, + 0x8f, 0x10, 0x8b, 0x8f, 0xa1, 0x01, 0x80, 0x40, 0xa8, 0x06, 0x05, 0x80, + 0x8a, 0x80, 0xa2, 0x00, 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2, 0x80, 0x94, + 0x82, 0x42, 0x00, 0x80, 0x40, 0xe1, 0x80, 0x40, 0x94, 0x84, 0x46, 0x85, + 0x10, 0x0c, 0x83, 0xa7, 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x83, + 0x42, 0x1d, 0x8a, 0x40, 0xaf, 0x80, 0xb5, 0x8e, 0xb7, 0x82, 0xb0, 0x19, + 0x09, 0x80, 0x8e, 0x80, 0xb1, 0x82, 0xa3, 0x20, 0x87, 0xbd, 0x80, 0x8b, + 0x81, 0xb3, 0x88, 0x89, 0x83, 0xe1, 0x11, 0x00, 0x0d, 0x80, 0x40, 0x9f, + 0x02, 0x87, 0x94, 0x81, 0xb8, 0x0a, 0x80, 0xa4, 0x32, 0x84, 0x40, 0xc2, + 0x39, 0x10, 0x80, 0x96, 0x80, 0xd3, 0x28, 0x03, 0x08, 0x81, 0x40, 0xed, + 0x1d, 0x08, 0x81, 0x9a, 0x81, 0xd4, 0x39, 0x00, 0x81, 0xe9, 0x00, 0x01, + 0x28, 0x80, 0xe4, 0x11, 0x18, 0x84, 0x41, 0x02, 0x88, 0x01, 0x41, 0x98, + 0x19, 0x0b, 0x80, 0x9f, 0x89, 0xa7, 0x29, 0x1f, 0x80, 0x88, 0x29, 0x82, + 0xad, 0x8c, 0x01, 0x41, 0x95, 0x30, 0x28, 0x80, 0xd1, 0x95, 0x0e, 0x01, + 0x01, 0xf9, 0x2a, 0x00, 0x08, 0x30, 0x80, 0xc7, 0x0a, 0x00, 0x80, 0x41, + 0x5a, 0x81, 0x55, 0x3a, 0x88, 0x60, 0x36, 0xb6, 0x84, 0xba, 0x86, 0x88, + 0x83, 0x44, 0x0a, 0x80, 0xbe, 0x90, 0xbf, 0x08, 0x80, 0x60, 0x4c, 0xb8, + 0x08, 0x83, 0x54, 0xc2, 0x82, 0x88, 0x8f, 0x0e, 0x9d, 0x83, 0x40, 0x93, + 0x82, 0x47, 0xba, 0xb6, 0x83, 0xb1, 0x38, 0x8d, 0x80, 0x95, 0x20, 0x8e, + 0x45, 0x4f, 0x30, 0x90, 0x0e, 0x01, 0x04, 0x41, 0x04, 0x8d, 0x41, 0xad, + 0x83, 0x45, 0xdf, 0x86, 0xec, 0x87, 0x4a, 0xae, 0x84, 0x6c, 0x0c, 0x00, + 0x80, 0x9d, 0xdf, 0xff, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_Case_Ignorable_index[66] = { + 0xc0, 0x05, 0x00, 0x2e, 0x08, 0x20, 0x52, 0x0a, 0x00, 0x05, 0x0c, + 0x00, 0x4f, 0x0e, 0x20, 0x75, 0x10, 0x20, 0x44, 0x18, 0x00, 0x43, + 0x1b, 0x00, 0x00, 0x1e, 0x00, 0x7e, 0x2c, 0x00, 0x7e, 0xa6, 0x40, + 0x83, 0xa9, 0x20, 0xf7, 0xaa, 0x00, 0x41, 0xff, 0x20, 0x28, 0x0d, + 0x01, 0x3f, 0x12, 0x41, 0xde, 0x15, 0x21, 0x5c, 0x1a, 0x01, 0xf5, + 0x6a, 0x21, 0x37, 0xda, 0x01, 0x02, 0x00, 0x2e, 0xf0, 0x01, 0x0e, +}; + +static const uint8_t unicode_prop_ID_Start_table[1024] = { + 0xc0, 0x99, 0x85, 0x99, 0xae, 0x80, 0x89, 0x03, 0x04, 0x96, 0x80, 0x9e, + 0x80, 0x41, 0xc9, 0x83, 0x8b, 0x8d, 0x26, 0x00, 0x80, 0x40, 0x80, 0x20, + 0x09, 0x18, 0x05, 0x00, 0x10, 0x00, 0x93, 0x80, 0xd2, 0x80, 0x40, 0x8a, + 0x87, 0x40, 0xa5, 0x80, 0xa5, 0x08, 0x85, 0xa8, 0xc6, 0x9a, 0x1b, 0xac, + 0xaa, 0xa2, 0x08, 0xe2, 0x00, 0x8e, 0x0e, 0x81, 0x89, 0x11, 0x80, 0x8f, + 0x00, 0x9d, 0x9c, 0xd8, 0x8a, 0x80, 0x97, 0xa0, 0x88, 0x0b, 0x04, 0x95, + 0x18, 0x88, 0x02, 0x80, 0x96, 0x98, 0x86, 0x8a, 0xb4, 0x94, 0x07, 0xc5, + 0xb5, 0x10, 0x91, 0x06, 0x89, 0x8e, 0x8f, 0x1f, 0x09, 0x81, 0x95, 0x06, + 0x00, 0x13, 0x10, 0x8f, 0x80, 0x8c, 0x08, 0x82, 0x8d, 0x81, 0x89, 0x07, + 0x2b, 0x09, 0x95, 0x06, 0x01, 0x01, 0x01, 0x9e, 0x18, 0x80, 0x92, 0x82, + 0x8f, 0x88, 0x02, 0x80, 0x95, 0x06, 0x01, 0x04, 0x10, 0x91, 0x80, 0x8e, + 0x81, 0x96, 0x80, 0x8a, 0x39, 0x09, 0x95, 0x06, 0x01, 0x04, 0x10, 0x9d, + 0x08, 0x82, 0x8e, 0x80, 0x90, 0x00, 0x2a, 0x10, 0x1a, 0x08, 0x00, 0x0a, + 0x0a, 0x12, 0x8b, 0x95, 0x80, 0xb3, 0x38, 0x10, 0x96, 0x80, 0x8f, 0x10, + 0x99, 0x14, 0x81, 0x9d, 0x03, 0x38, 0x10, 0x96, 0x80, 0x89, 0x04, 0x10, + 0x9f, 0x00, 0x81, 0x8e, 0x81, 0x91, 0x38, 0x10, 0xa8, 0x08, 0x8f, 0x04, + 0x17, 0x82, 0x97, 0x2c, 0x91, 0x82, 0x97, 0x80, 0x88, 0x00, 0x0e, 0xb9, + 0xaf, 0x01, 0x8b, 0x86, 0xb9, 0x08, 0x00, 0x20, 0x97, 0x00, 0x80, 0x89, + 0x01, 0x88, 0x01, 0x20, 0x80, 0x94, 0x83, 0x9f, 0x80, 0xbe, 0x38, 0xa3, + 0x9a, 0x84, 0xf2, 0xaa, 0x93, 0x80, 0x8f, 0x2b, 0x1a, 0x02, 0x0e, 0x13, + 0x8c, 0x8b, 0x80, 0x90, 0xa5, 0x00, 0x20, 0x81, 0xaa, 0x80, 0x41, 0x4c, + 0x03, 0x0e, 0x00, 0x03, 0x81, 0xa8, 0x03, 0x81, 0xa0, 0x03, 0x0e, 0x00, + 0x03, 0x81, 0x8e, 0x80, 0xb8, 0x03, 0x81, 0xc2, 0xa4, 0x8f, 0x8f, 0xd5, + 0x0d, 0x82, 0x42, 0x6b, 0x81, 0x90, 0x80, 0x99, 0x84, 0xca, 0x82, 0x8a, + 0x86, 0x8c, 0x03, 0x8d, 0x91, 0x8d, 0x91, 0x8d, 0x8c, 0x02, 0x8e, 0xb3, + 0xa2, 0x03, 0x80, 0xc2, 0xd8, 0x86, 0xa8, 0x00, 0x84, 0xc5, 0x89, 0x9e, + 0xb0, 0x9d, 0x0c, 0x8a, 0xab, 0x83, 0x99, 0xb5, 0x96, 0x88, 0xb4, 0xd1, + 0x80, 0xdc, 0xae, 0x90, 0x86, 0xb6, 0x9d, 0x8c, 0x81, 0x89, 0xab, 0x99, + 0xa3, 0xa8, 0x82, 0x89, 0xa3, 0x81, 0x88, 0x86, 0xaa, 0x0a, 0xa8, 0x18, + 0x28, 0x0a, 0x04, 0x40, 0xbf, 0xbf, 0x41, 0x15, 0x0d, 0x81, 0xa5, 0x0d, + 0x0f, 0x00, 0x00, 0x00, 0x80, 0x9e, 0x81, 0xb4, 0x06, 0x00, 0x12, 0x06, + 0x13, 0x0d, 0x83, 0x8c, 0x22, 0x06, 0xf3, 0x80, 0x8c, 0x80, 0x8f, 0x8c, + 0xe4, 0x03, 0x01, 0x89, 0x00, 0x0d, 0x28, 0x00, 0x00, 0x80, 0x8f, 0x0b, + 0x24, 0x18, 0x90, 0xa8, 0x4a, 0x76, 0xae, 0x80, 0xae, 0x80, 0x40, 0x84, + 0x2b, 0x11, 0x8b, 0xa5, 0x00, 0x20, 0x81, 0xb7, 0x30, 0x8f, 0x96, 0x88, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x86, 0x42, 0x25, 0x82, 0x98, + 0x88, 0x34, 0x0c, 0x83, 0xd5, 0x1c, 0x80, 0xd9, 0x03, 0x84, 0xaa, 0x80, + 0xdd, 0x90, 0x9a, 0xb4, 0x8f, 0x41, 0xff, 0x59, 0xb5, 0xc9, 0x60, 0x51, + 0xef, 0x8f, 0x44, 0x8c, 0xc2, 0xad, 0x81, 0x41, 0x0c, 0x82, 0x8f, 0x89, + 0x81, 0x93, 0xae, 0x8f, 0x9e, 0x81, 0xcf, 0xa6, 0x88, 0x81, 0xe6, 0x81, + 0xb4, 0x0c, 0xaf, 0x8a, 0x02, 0x03, 0x80, 0x96, 0x9c, 0xb3, 0x8d, 0xb1, + 0xbd, 0x2a, 0x00, 0x81, 0x8a, 0x9b, 0x89, 0x96, 0x98, 0x9c, 0x86, 0xae, + 0x9b, 0x80, 0x8f, 0x20, 0x89, 0x89, 0x20, 0xa8, 0x96, 0x10, 0x87, 0x93, + 0x96, 0x10, 0x82, 0xb1, 0x00, 0x11, 0x0c, 0x08, 0x00, 0x97, 0x11, 0x8a, + 0x32, 0x8b, 0x29, 0x29, 0x85, 0x88, 0x30, 0x30, 0xaa, 0x80, 0x8b, 0x87, + 0xf2, 0x9c, 0x60, 0x2b, 0xa3, 0x8b, 0x96, 0x83, 0xb0, 0x60, 0x21, 0x03, + 0x41, 0x6d, 0x81, 0xe9, 0xa5, 0x86, 0x8b, 0x24, 0x00, 0x89, 0x80, 0x8c, + 0x04, 0x00, 0x01, 0x01, 0x80, 0xeb, 0xa0, 0x41, 0x6a, 0x91, 0xbf, 0x81, + 0xb5, 0xa7, 0x8b, 0xf3, 0x20, 0x40, 0x86, 0xa3, 0x99, 0x85, 0x99, 0x8a, + 0xd8, 0x15, 0x0d, 0x0d, 0x0a, 0xa2, 0x8b, 0x80, 0x99, 0x80, 0x92, 0x01, + 0x80, 0x8e, 0x81, 0x8d, 0xa1, 0xfa, 0xc4, 0xb4, 0x41, 0x0a, 0x9c, 0x82, + 0xb0, 0xae, 0x9f, 0x8c, 0x9d, 0x84, 0xa5, 0x89, 0x9d, 0x81, 0xa3, 0x1f, + 0x04, 0xa9, 0x40, 0x9d, 0x91, 0xa3, 0x83, 0xa3, 0x83, 0xa7, 0x87, 0xb3, + 0x40, 0x9b, 0x41, 0x36, 0x88, 0x95, 0x89, 0x87, 0x40, 0x97, 0x29, 0x00, + 0xab, 0x01, 0x10, 0x81, 0x96, 0x89, 0x96, 0x88, 0x9e, 0xc0, 0x92, 0x01, + 0x89, 0x95, 0x89, 0x99, 0xc5, 0xb7, 0x29, 0xbf, 0x80, 0x8e, 0x18, 0x10, + 0x9c, 0xa9, 0x9c, 0x82, 0x9c, 0xa2, 0x38, 0x9b, 0x9a, 0xb5, 0x89, 0x95, + 0x89, 0x92, 0x8c, 0x91, 0xed, 0xc8, 0xb6, 0xb2, 0x8c, 0xb2, 0x8c, 0xa3, + 0x41, 0xdb, 0x9c, 0x89, 0x07, 0x95, 0x40, 0x99, 0x96, 0x8b, 0xb4, 0xca, + 0xac, 0x9f, 0x98, 0x99, 0xa3, 0x9c, 0x80, 0x8a, 0xa2, 0x10, 0x8b, 0xaf, + 0x8d, 0x83, 0x94, 0x00, 0x80, 0xa2, 0x91, 0x80, 0x98, 0xd3, 0x30, 0x00, + 0x18, 0x8e, 0x80, 0x89, 0x86, 0xae, 0xa5, 0x39, 0x09, 0x95, 0x06, 0x01, + 0x04, 0x10, 0x91, 0x80, 0x8b, 0x84, 0x40, 0x9d, 0xb4, 0x91, 0x83, 0x93, + 0x80, 0x9f, 0xaf, 0x93, 0x08, 0x80, 0x40, 0xb7, 0xae, 0xa8, 0x83, 0xa3, + 0xaf, 0x93, 0x80, 0xba, 0xaa, 0x8c, 0x80, 0xc6, 0x9a, 0x40, 0xe4, 0xab, + 0xf3, 0xbf, 0x9e, 0x80, 0x40, 0x9f, 0x39, 0xa6, 0x8f, 0x00, 0x80, 0x9b, + 0x80, 0x89, 0xa7, 0x30, 0x94, 0x80, 0x8a, 0xad, 0x92, 0x80, 0xa1, 0xb8, + 0x41, 0x06, 0x88, 0x80, 0xa4, 0x90, 0x80, 0xb0, 0x9d, 0xef, 0x30, 0x08, + 0xa5, 0x94, 0x80, 0x98, 0x28, 0x08, 0x9f, 0x8d, 0x80, 0x41, 0x46, 0x92, + 0x41, 0x0c, 0x43, 0x99, 0xe5, 0xee, 0x90, 0x40, 0xc3, 0x4a, 0xbb, 0x44, + 0x2e, 0x4f, 0xd0, 0x42, 0x46, 0x60, 0x21, 0xb8, 0x42, 0x38, 0x86, 0x9e, + 0xf0, 0x9d, 0x91, 0xaf, 0x8f, 0x83, 0x9e, 0x94, 0x84, 0x92, 0x42, 0xaf, + 0xbf, 0xff, 0xca, 0x20, 0xc1, 0x8c, 0xbf, 0x08, 0x80, 0x9b, 0x57, 0xf7, + 0x87, 0x42, 0xf2, 0x60, 0x25, 0x0c, 0x41, 0x1e, 0xb0, 0x82, 0x90, 0x1f, + 0x41, 0x8b, 0x49, 0x03, 0xea, 0x84, 0x8c, 0x82, 0x88, 0x86, 0x89, 0x57, + 0x65, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, + 0x80, 0xc0, 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80, + 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x49, + 0x33, 0xac, 0x89, 0x86, 0x8f, 0x80, 0x41, 0x70, 0xab, 0x45, 0x13, 0x40, + 0xc4, 0xba, 0xc3, 0x30, 0x44, 0xb3, 0x18, 0x9a, 0x01, 0x00, 0x08, 0x80, + 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, 0x00, 0x02, 0x01, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, 0x80, + 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, 0x51, 0x43, 0x60, 0xa6, 0xd6, + 0xa8, 0x50, 0x34, 0x8a, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, + 0x4c, 0x1e, 0x42, 0x1d, +}; + +static const uint8_t unicode_prop_ID_Start_index[96] = { + 0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xb1, 0x09, 0x00, 0xba, 0x0a, 0x00, + 0xd1, 0x0b, 0x20, 0x62, 0x0d, 0x40, 0x01, 0x0f, 0x20, 0x5e, 0x12, 0x00, + 0xf9, 0x16, 0x00, 0x17, 0x1a, 0x20, 0xc0, 0x1d, 0x20, 0x9d, 0x20, 0x00, + 0x68, 0x2d, 0x00, 0x00, 0x32, 0x20, 0xc0, 0xa7, 0x20, 0x29, 0xaa, 0x00, + 0xa4, 0xd7, 0x20, 0xc8, 0xfd, 0x20, 0x75, 0x01, 0x01, 0x37, 0x07, 0x01, + 0x36, 0x0a, 0x21, 0xf7, 0x0f, 0x21, 0xa9, 0x12, 0x01, 0x30, 0x16, 0x21, + 0x8a, 0x1a, 0x01, 0x9a, 0x23, 0x01, 0x80, 0x6e, 0x21, 0x89, 0xbc, 0x21, + 0xc1, 0xd6, 0x01, 0xc5, 0xe8, 0x21, 0x73, 0xee, 0x01, 0x1e, 0xfa, 0x02, +}; + +static const uint8_t unicode_prop_ID_Continue1_table[607] = { + 0xaf, 0x89, 0xa4, 0x80, 0xd6, 0x80, 0x42, 0x47, 0xef, 0x96, 0x80, 0x40, + 0xfa, 0x84, 0x41, 0x08, 0xac, 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, + 0x9e, 0x28, 0xe4, 0x31, 0x29, 0x08, 0x19, 0x89, 0x96, 0x80, 0x9d, 0x9a, + 0xda, 0x8a, 0x8e, 0x89, 0xa0, 0x88, 0x88, 0x80, 0x97, 0x18, 0x88, 0x02, + 0x04, 0xaa, 0x82, 0xf6, 0x8e, 0x80, 0xa0, 0xb5, 0x10, 0x91, 0x06, 0x89, + 0x09, 0x89, 0x90, 0x82, 0xb7, 0x00, 0x31, 0x09, 0x82, 0x88, 0x80, 0x89, + 0x09, 0x89, 0x8d, 0x01, 0x82, 0xb7, 0x00, 0x23, 0x09, 0x12, 0x80, 0x93, + 0x8b, 0x10, 0x8a, 0x82, 0xb7, 0x00, 0x38, 0x10, 0x82, 0x93, 0x09, 0x89, + 0x89, 0x28, 0x82, 0xb7, 0x00, 0x31, 0x09, 0x17, 0x81, 0x89, 0x09, 0x89, + 0x91, 0x80, 0xba, 0x22, 0x10, 0x83, 0x88, 0x80, 0x8d, 0x89, 0x8f, 0x84, + 0xb8, 0x30, 0x10, 0x1e, 0x81, 0x8a, 0x09, 0x89, 0x90, 0x82, 0xb7, 0x00, + 0x30, 0x10, 0x1e, 0x81, 0x8a, 0x09, 0x89, 0x8f, 0x83, 0xb6, 0x08, 0x30, + 0x10, 0x83, 0x88, 0x80, 0x89, 0x09, 0x89, 0x91, 0x81, 0xc5, 0x03, 0x28, + 0x00, 0x3d, 0x89, 0x09, 0xbc, 0x01, 0x86, 0x8b, 0x38, 0x89, 0xd6, 0x01, + 0x88, 0x8a, 0x29, 0x89, 0xbd, 0x0d, 0x89, 0x8a, 0x00, 0x00, 0x03, 0x81, + 0xb0, 0x93, 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, 0x80, 0xe3, 0x93, 0x80, + 0x89, 0x8b, 0x1b, 0x10, 0x11, 0x32, 0x83, 0x8c, 0x8b, 0x80, 0x8e, 0x42, + 0xbe, 0x82, 0x88, 0x88, 0x43, 0x9f, 0x82, 0x9c, 0x82, 0x9c, 0x81, 0x9d, + 0x81, 0xbf, 0x9f, 0x88, 0x01, 0x89, 0xa0, 0x11, 0x89, 0x40, 0x8e, 0x80, + 0xf5, 0x8b, 0x83, 0x8b, 0x89, 0x89, 0xff, 0x8a, 0xbb, 0x84, 0xb8, 0x89, + 0x80, 0x9c, 0x81, 0x8a, 0x85, 0x89, 0x95, 0x8d, 0xc1, 0x84, 0xae, 0x90, + 0x8a, 0x89, 0x90, 0x88, 0x8b, 0x82, 0x9d, 0x8c, 0x81, 0x89, 0xab, 0x8d, + 0xaf, 0x93, 0x87, 0x89, 0x85, 0x89, 0xf5, 0x10, 0x94, 0x18, 0x28, 0x0a, + 0x40, 0xc5, 0xb9, 0x04, 0x42, 0x3e, 0x81, 0x92, 0x80, 0xfa, 0x8c, 0x18, + 0x82, 0x8b, 0x4b, 0xfd, 0x82, 0x40, 0x8c, 0x80, 0xdf, 0x9f, 0x42, 0x29, + 0x85, 0xe8, 0x81, 0x60, 0x75, 0x84, 0x89, 0xc4, 0x03, 0x89, 0x9f, 0x81, + 0xcf, 0x81, 0x41, 0x0f, 0x02, 0x03, 0x80, 0x96, 0x84, 0xd7, 0x81, 0xb1, + 0x91, 0x89, 0x89, 0x85, 0x91, 0x8c, 0x8a, 0x9b, 0x87, 0x98, 0x8c, 0xab, + 0x83, 0xae, 0x8d, 0x8e, 0x89, 0x8a, 0x80, 0x89, 0x89, 0xae, 0x8d, 0x8b, + 0x07, 0x09, 0x89, 0xa0, 0x82, 0xb1, 0x00, 0x11, 0x0c, 0x08, 0x80, 0xa8, + 0x24, 0x81, 0x40, 0xeb, 0x38, 0x09, 0x89, 0x60, 0x4f, 0x23, 0x80, 0x42, + 0xe0, 0x8f, 0x8f, 0x8f, 0x11, 0x97, 0x82, 0x40, 0xbf, 0x89, 0xa4, 0x80, + 0x42, 0xbc, 0x80, 0x40, 0xe1, 0x80, 0x40, 0x94, 0x84, 0x41, 0x24, 0x89, + 0x45, 0x56, 0x10, 0x0c, 0x83, 0xa7, 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, + 0x3c, 0x1f, 0x89, 0x42, 0x0b, 0x8a, 0x40, 0xae, 0x82, 0xb4, 0x8e, 0x9e, + 0x89, 0x8e, 0x83, 0xac, 0x8a, 0xb4, 0x89, 0x2a, 0xa3, 0x8d, 0x80, 0x89, + 0x21, 0xab, 0x80, 0x8b, 0x82, 0xaf, 0x8d, 0x3b, 0x82, 0x89, 0xd1, 0x8b, + 0x28, 0x40, 0x9f, 0x8b, 0x84, 0x89, 0x2b, 0xb6, 0x08, 0x31, 0x09, 0x82, + 0x88, 0x80, 0x89, 0x09, 0x32, 0x84, 0x40, 0xbf, 0x91, 0x88, 0x89, 0x18, + 0xd0, 0x93, 0x8b, 0x89, 0x40, 0xd4, 0x31, 0x88, 0x9a, 0x81, 0xd1, 0x90, + 0x8e, 0x89, 0xd0, 0x8c, 0x87, 0x89, 0xd2, 0x8e, 0x83, 0x89, 0x40, 0xf1, + 0x8e, 0x40, 0xa4, 0x89, 0x40, 0xe6, 0x31, 0x32, 0x80, 0x9b, 0x89, 0xa7, + 0x30, 0x1f, 0x80, 0x88, 0x8a, 0xad, 0x8f, 0x41, 0x94, 0x38, 0x87, 0x8f, + 0x89, 0xb7, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, 0x30, 0x07, 0x89, + 0xaf, 0x20, 0x08, 0x27, 0x89, 0x41, 0x48, 0x83, 0x60, 0x4b, 0x68, 0x89, + 0x40, 0x85, 0x84, 0xba, 0x86, 0x98, 0x89, 0x43, 0xf4, 0x00, 0xb6, 0x33, + 0x60, 0x4d, 0x09, 0x81, 0x54, 0xc5, 0x22, 0x2f, 0x39, 0x86, 0x9d, 0x83, + 0x40, 0x93, 0x82, 0x45, 0x88, 0xb1, 0x41, 0xff, 0xb6, 0x83, 0xb1, 0x38, + 0x8d, 0x80, 0x95, 0x20, 0x8e, 0x45, 0x4f, 0x30, 0x90, 0x0e, 0x01, 0x04, + 0x41, 0x04, 0x86, 0x88, 0x89, 0x41, 0xa1, 0x8d, 0x45, 0xd5, 0x86, 0xec, + 0x34, 0x89, 0x6c, 0x17, 0xa5, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_ID_Continue1_index[57] = { + 0xfa, 0x06, 0x00, 0x84, 0x09, 0x00, 0xf0, 0x0a, 0x00, 0x70, 0x0c, 0x00, + 0xf4, 0x0d, 0x00, 0x4a, 0x10, 0x20, 0x1a, 0x18, 0x20, 0x74, 0x1b, 0x00, + 0xe2, 0x20, 0x00, 0x28, 0xa8, 0x20, 0x7e, 0xaa, 0x20, 0x40, 0xff, 0x00, + 0x03, 0x10, 0x21, 0xeb, 0x12, 0x01, 0x41, 0x16, 0x01, 0x40, 0x1c, 0x61, + 0x37, 0x6b, 0x21, 0x76, 0xda, 0x01, 0xf0, 0x01, 0x0e, +}; + +#ifdef CONFIG_ALL_UNICODE + +static const uint8_t unicode_cc_table[831] = { + 0xb2, 0xcf, 0xd4, 0x00, 0xe8, 0x03, 0xdc, 0x00, 0xe8, 0x00, 0xd8, 0x04, + 0xdc, 0x01, 0xca, 0x03, 0xdc, 0x01, 0xca, 0x0a, 0xdc, 0x04, 0x01, 0x03, + 0xdc, 0xc7, 0x00, 0xf0, 0xc0, 0x02, 0xdc, 0xc2, 0x01, 0xdc, 0x80, 0xc2, + 0x03, 0xdc, 0xc0, 0x00, 0xe8, 0x01, 0xdc, 0xc0, 0x41, 0xe9, 0x00, 0xea, + 0x41, 0xe9, 0x00, 0xea, 0x00, 0xe9, 0xcc, 0xb0, 0xe2, 0xc4, 0xb0, 0xd8, + 0x00, 0xdc, 0xc3, 0x00, 0xdc, 0xc2, 0x00, 0xde, 0x00, 0xdc, 0xc5, 0x05, + 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xde, 0x00, 0xe4, 0xc0, 0x49, 0x0a, + 0x43, 0x13, 0x80, 0x00, 0x17, 0x80, 0x41, 0x18, 0x80, 0xc0, 0x00, 0xdc, + 0x80, 0x00, 0x12, 0xb0, 0x17, 0xc7, 0x42, 0x1e, 0xaf, 0x47, 0x1b, 0xc1, + 0x01, 0xdc, 0xc4, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0x8f, 0x00, 0x23, 0xb0, + 0x34, 0xc6, 0x81, 0xc3, 0x00, 0xdc, 0xc0, 0x81, 0xc1, 0x80, 0x00, 0xdc, + 0xc1, 0x00, 0xdc, 0xa2, 0x00, 0x24, 0x9d, 0xc0, 0x00, 0xdc, 0xc1, 0x00, + 0xdc, 0xc1, 0x02, 0xdc, 0xc0, 0x01, 0xdc, 0xc0, 0x00, 0xdc, 0xc2, 0x00, + 0xdc, 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, + 0x6f, 0xc6, 0x00, 0xdc, 0xc0, 0x88, 0x00, 0xdc, 0x97, 0xc3, 0x80, 0xc8, + 0x80, 0xc2, 0x80, 0xc4, 0xaa, 0x02, 0xdc, 0xb0, 0x46, 0x00, 0xdc, 0xcd, + 0x80, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xc2, 0x02, 0xdc, + 0x42, 0x1b, 0xc2, 0x00, 0xdc, 0xc1, 0x01, 0xdc, 0xc4, 0xb0, 0x0b, 0x00, + 0x07, 0x8f, 0x00, 0x09, 0x82, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, 0x36, 0x00, + 0x07, 0x8f, 0x00, 0x09, 0xaf, 0xc0, 0xb0, 0x0c, 0x00, 0x07, 0x8f, 0x00, + 0x09, 0xb0, 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3d, 0x00, 0x07, + 0x8f, 0x00, 0x09, 0xb0, 0x4e, 0x00, 0x09, 0xb0, 0x4e, 0x00, 0x09, 0x86, + 0x00, 0x54, 0x00, 0x5b, 0xb0, 0x34, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, + 0x3c, 0x01, 0x09, 0x8f, 0x00, 0x09, 0xb0, 0x4b, 0x00, 0x09, 0xb0, 0x3c, + 0x01, 0x67, 0x00, 0x09, 0x8c, 0x03, 0x6b, 0xb0, 0x3b, 0x01, 0x76, 0x00, + 0x09, 0x8c, 0x03, 0x7a, 0xb0, 0x1b, 0x01, 0xdc, 0x9a, 0x00, 0xdc, 0x80, + 0x00, 0xdc, 0x80, 0x00, 0xd8, 0xb0, 0x06, 0x41, 0x81, 0x80, 0x00, 0x84, + 0x84, 0x03, 0x82, 0x81, 0x00, 0x82, 0x80, 0xc1, 0x00, 0x09, 0x80, 0xc1, + 0xb0, 0x0d, 0x00, 0xdc, 0xb0, 0x3f, 0x00, 0x07, 0x80, 0x01, 0x09, 0xb0, + 0x21, 0x00, 0xdc, 0xb2, 0x9e, 0xc2, 0xb3, 0x83, 0x00, 0x09, 0x9e, 0x00, + 0x09, 0xb0, 0x6c, 0x00, 0x09, 0x89, 0xc0, 0xb0, 0x9a, 0x00, 0xe4, 0xb0, + 0x5e, 0x00, 0xde, 0xc0, 0x00, 0xdc, 0xb0, 0xaa, 0xc0, 0x00, 0xdc, 0xb0, + 0x16, 0x00, 0x09, 0x93, 0xc7, 0x81, 0x00, 0xdc, 0xaf, 0xc4, 0x05, 0xdc, + 0xc1, 0x00, 0xdc, 0xb0, 0x45, 0x00, 0x07, 0x8e, 0x00, 0x09, 0xa5, 0xc0, + 0x00, 0xdc, 0xc6, 0xb0, 0x05, 0x01, 0x09, 0xb0, 0x09, 0x00, 0x07, 0x8a, + 0x01, 0x09, 0xb0, 0x12, 0x00, 0x07, 0xb0, 0x67, 0xc2, 0x41, 0x00, 0x04, + 0xdc, 0xc1, 0x03, 0xdc, 0xc0, 0x41, 0x00, 0x05, 0x01, 0x83, 0x00, 0xdc, + 0x85, 0xc0, 0x82, 0xc1, 0xb0, 0x95, 0xc1, 0x00, 0xdc, 0xc6, 0x00, 0xdc, + 0xc1, 0x00, 0xea, 0x00, 0xd6, 0x00, 0xdc, 0x00, 0xca, 0xe4, 0x00, 0xe8, + 0x01, 0xe4, 0x00, 0xdc, 0x80, 0xc0, 0x00, 0xe9, 0x00, 0xdc, 0xc0, 0x00, + 0xdc, 0xb2, 0x9f, 0xc1, 0x01, 0x01, 0xc3, 0x02, 0x01, 0xc1, 0x83, 0xc0, + 0x82, 0x01, 0x01, 0xc0, 0x00, 0xdc, 0xc0, 0x01, 0x01, 0x03, 0xdc, 0xc0, + 0xb8, 0x03, 0xcd, 0xc2, 0xb0, 0x5c, 0x00, 0x09, 0xb0, 0x2f, 0xdf, 0xb1, + 0xf9, 0x00, 0xda, 0x00, 0xe4, 0x00, 0xe8, 0x00, 0xde, 0x01, 0xe0, 0xb0, + 0x38, 0x01, 0x08, 0xb8, 0x6d, 0xa3, 0xc0, 0x83, 0xc9, 0x9f, 0xc1, 0xb0, + 0x1f, 0xc1, 0xb0, 0xe3, 0x00, 0x09, 0xb0, 0x8c, 0x00, 0x09, 0x9a, 0xd1, + 0xb0, 0x08, 0x02, 0xdc, 0xa4, 0x00, 0x09, 0xb0, 0x2e, 0x00, 0x07, 0x8b, + 0x00, 0x09, 0xb0, 0xbe, 0xc0, 0x80, 0xc1, 0x00, 0xdc, 0x81, 0xc1, 0x84, + 0xc1, 0x80, 0xc0, 0xb0, 0x03, 0x00, 0x09, 0xb0, 0xc5, 0x00, 0x09, 0xb8, + 0x46, 0xff, 0x00, 0x1a, 0xb2, 0xd0, 0xc6, 0x06, 0xdc, 0xc1, 0xb3, 0x9c, + 0x00, 0xdc, 0xb0, 0xb1, 0x00, 0xdc, 0xb0, 0x64, 0xc4, 0xb6, 0x61, 0x00, + 0xdc, 0x80, 0xc0, 0xa7, 0xc0, 0x00, 0x01, 0x00, 0xdc, 0x83, 0x00, 0x09, + 0xb0, 0x74, 0xc0, 0x00, 0xdc, 0xb2, 0x0c, 0xc3, 0xb1, 0xed, 0x01, 0xdc, + 0xc2, 0x00, 0xdc, 0xc0, 0x03, 0xdc, 0xb0, 0xc4, 0x00, 0x09, 0xb0, 0x07, + 0x00, 0x09, 0xb0, 0x08, 0x00, 0x09, 0x00, 0x07, 0xb0, 0x14, 0xc2, 0xaf, + 0x01, 0x09, 0xb0, 0x0d, 0x00, 0x07, 0xb0, 0x1b, 0x00, 0x09, 0x88, 0x00, + 0x07, 0xb0, 0x39, 0x00, 0x09, 0x00, 0x07, 0xb0, 0x81, 0x00, 0x07, 0x00, + 0x09, 0xb0, 0x1f, 0x01, 0x07, 0x8f, 0x00, 0x09, 0x97, 0xc6, 0x82, 0xc4, + 0xb0, 0x9c, 0x00, 0x09, 0x82, 0x00, 0x07, 0x96, 0xc0, 0xb0, 0x32, 0x00, + 0x09, 0x00, 0x07, 0xb0, 0xca, 0x00, 0x09, 0x00, 0x07, 0xb0, 0x4d, 0x00, + 0x09, 0xb0, 0x45, 0x00, 0x09, 0x00, 0x07, 0xb0, 0x42, 0x00, 0x09, 0xb0, + 0xdc, 0x00, 0x09, 0x00, 0x07, 0xb1, 0x74, 0x00, 0x09, 0xb0, 0x22, 0x00, + 0x09, 0x91, 0x00, 0x09, 0xb0, 0x20, 0x00, 0x09, 0xb1, 0x74, 0x00, 0x09, + 0xb0, 0xd1, 0x00, 0x07, 0x80, 0x01, 0x09, 0xb0, 0x20, 0x00, 0x09, 0xb8, + 0x45, 0x27, 0x04, 0x01, 0xb0, 0x0a, 0xc6, 0xb8, 0x49, 0x36, 0x00, 0x01, + 0xb8, 0x0c, 0x95, 0x01, 0xd8, 0x02, 0x01, 0x82, 0x00, 0xe2, 0x04, 0xd8, + 0x87, 0x07, 0xdc, 0x81, 0xc4, 0x01, 0xdc, 0x9d, 0xc3, 0xb0, 0x63, 0xc2, + 0xb8, 0x05, 0x8a, 0xc6, 0x80, 0xd0, 0x81, 0xc6, 0x80, 0xc1, 0x80, 0xc4, + 0xb0, 0xd4, 0xc6, 0xb1, 0x84, 0xc3, 0xb5, 0xaf, 0x06, 0xdc, 0xb0, 0x3c, + 0xc5, 0x00, 0x07, +}; + +static const uint8_t unicode_cc_index[78] = { + 0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05, 0x00, 0xe7, 0x06, 0x00, + 0x45, 0x07, 0x00, 0xe2, 0x08, 0x00, 0x53, 0x09, 0x00, 0xcd, 0x0b, 0x20, + 0x38, 0x0e, 0x00, 0x73, 0x0f, 0x20, 0x5d, 0x13, 0x20, 0x60, 0x1a, 0x20, + 0xe6, 0x1b, 0x20, 0xfa, 0x1c, 0x00, 0x00, 0x1e, 0x20, 0x80, 0x2d, 0x00, + 0x06, 0xa8, 0x00, 0xbe, 0xaa, 0x00, 0x76, 0x03, 0x01, 0x4d, 0x0f, 0x01, + 0xcb, 0x11, 0x21, 0x5e, 0x14, 0x01, 0x3b, 0x18, 0x21, 0xf0, 0x6a, 0x41, + 0xaa, 0xd1, 0x01, 0x4b, 0xe9, 0x01, +}; + +static const uint32_t unicode_decomp_table1[687] = { + 0x00280081, 0x002a0097, 0x002a8081, 0x002bc097, 0x002c8115, 0x002d0097, + 0x002d4081, 0x002e0097, 0x002e4115, 0x002f0199, 0x00302016, 0x00400842, + 0x00448a42, 0x004a0442, 0x004c0096, 0x004c8117, 0x004d0242, 0x004e4342, + 0x004fc12f, 0x0050c342, 0x005240bf, 0x00530342, 0x00550942, 0x005a0842, + 0x005e0096, 0x005e4342, 0x005fc081, 0x00680142, 0x006bc142, 0x00710185, + 0x0071c317, 0x00734844, 0x00778344, 0x00798342, 0x007b02be, 0x007c4197, + 0x007d0142, 0x007e0444, 0x00800e42, 0x00878142, 0x00898744, 0x00ac0483, + 0x00b60317, 0x00b80283, 0x00d00214, 0x00d10096, 0x00dd0080, 0x00de8097, + 0x00df8080, 0x00e10097, 0x00e1413e, 0x00e1c080, 0x00e204be, 0x00ea83ae, + 0x00f282ae, 0x00f401ad, 0x00f4c12e, 0x00f54103, 0x00fc0303, 0x00fe4081, + 0x0100023e, 0x0101c0be, 0x010301be, 0x010640be, 0x010e40be, 0x0114023e, + 0x0115c0be, 0x011701be, 0x011d8144, 0x01304144, 0x01340244, 0x01358144, + 0x01368344, 0x01388344, 0x013a8644, 0x013e0144, 0x0161c085, 0x018882ae, + 0x019d422f, 0x01b00184, 0x01b4c084, 0x024a4084, 0x024c4084, 0x024d0084, + 0x0256042e, 0x0272c12e, 0x02770120, 0x0277c084, 0x028cc084, 0x028d8084, + 0x029641ae, 0x02978084, 0x02d20084, 0x02d2c12e, 0x02d70120, 0x02e50084, + 0x02f281ae, 0x03120084, 0x03300084, 0x0331c122, 0x0332812e, 0x035281ae, + 0x03768084, 0x037701ae, 0x038cc085, 0x03acc085, 0x03b7012f, 0x03c30081, + 0x03d0c084, 0x03d34084, 0x03d48084, 0x03d5c084, 0x03d70084, 0x03da4084, + 0x03dcc084, 0x03dd412e, 0x03ddc085, 0x03de0084, 0x03de4085, 0x03e04084, + 0x03e4c084, 0x03e74084, 0x03e88084, 0x03e9c084, 0x03eb0084, 0x03ee4084, + 0x04098084, 0x043f0081, 0x06c18484, 0x06c48084, 0x06cec184, 0x06d00120, + 0x06d0c084, 0x074b0383, 0x074cc41f, 0x074f1783, 0x075e0081, 0x0766d283, + 0x07801d44, 0x078e8942, 0x07931844, 0x079f0d42, 0x07a58216, 0x07a68085, + 0x07a6c0be, 0x07a80d44, 0x07aea044, 0x07c00122, 0x07c08344, 0x07c20122, + 0x07c28344, 0x07c40122, 0x07c48244, 0x07c60122, 0x07c68244, 0x07c8113e, + 0x07d08244, 0x07d20122, 0x07d28244, 0x07d40122, 0x07d48344, 0x07d64c3e, + 0x07dc4080, 0x07dc80be, 0x07dcc080, 0x07dd00be, 0x07dd4080, 0x07dd80be, + 0x07ddc080, 0x07de00be, 0x07de4080, 0x07de80be, 0x07dec080, 0x07df00be, + 0x07df4080, 0x07e00820, 0x07e40820, 0x07e80820, 0x07ec05be, 0x07eec080, + 0x07ef00be, 0x07ef4097, 0x07ef8080, 0x07efc117, 0x07f0443e, 0x07f24080, + 0x07f280be, 0x07f2c080, 0x07f303be, 0x07f4c080, 0x07f582ae, 0x07f6c080, + 0x07f7433e, 0x07f8c080, 0x07f903ae, 0x07fac080, 0x07fb013e, 0x07fb8102, + 0x07fc83be, 0x07fe4080, 0x07fe80be, 0x07fec080, 0x07ff00be, 0x07ff4080, + 0x07ff8097, 0x0800011e, 0x08008495, 0x08044081, 0x0805c097, 0x08090081, + 0x08094097, 0x08098099, 0x080bc081, 0x080cc085, 0x080d00b1, 0x080d8085, + 0x080dc0b1, 0x080f0197, 0x0811c197, 0x0815c0b3, 0x0817c081, 0x081c0595, + 0x081ec081, 0x081f0215, 0x0820051f, 0x08228583, 0x08254415, 0x082a0097, + 0x08400119, 0x08408081, 0x0840c0bf, 0x08414119, 0x0841c081, 0x084240bf, + 0x0842852d, 0x08454081, 0x08458097, 0x08464295, 0x08480097, 0x08484099, + 0x08488097, 0x08490081, 0x08498080, 0x084a0081, 0x084a8102, 0x084b0495, + 0x084d421f, 0x084e4081, 0x084ec099, 0x084f0283, 0x08514295, 0x08540119, + 0x0854809b, 0x0854c619, 0x0857c097, 0x08580081, 0x08584097, 0x08588099, + 0x0858c097, 0x08590081, 0x08594097, 0x08598099, 0x0859c09b, 0x085a0097, + 0x085a4081, 0x085a8097, 0x085ac099, 0x085b0295, 0x085c4097, 0x085c8099, + 0x085cc097, 0x085d0081, 0x085d4097, 0x085d8099, 0x085dc09b, 0x085e0097, + 0x085e4081, 0x085e8097, 0x085ec099, 0x085f0215, 0x08624099, 0x0866813e, + 0x086b80be, 0x087341be, 0x088100be, 0x088240be, 0x088300be, 0x088901be, + 0x088b0085, 0x088b40b1, 0x088bc085, 0x088c00b1, 0x089040be, 0x089100be, + 0x0891c1be, 0x089801be, 0x089b42be, 0x089d0144, 0x089e0144, 0x08a00144, + 0x08a10144, 0x08a20144, 0x08ab023e, 0x08b80244, 0x08ba8220, 0x08ca411e, + 0x0918049f, 0x091a4523, 0x091cc097, 0x091d04a5, 0x091f452b, 0x0921c09b, + 0x092204a1, 0x09244525, 0x0926c099, 0x09270d25, 0x092d8d1f, 0x09340d1f, + 0x093a8081, 0x0a8300b3, 0x0a9d0099, 0x0a9d4097, 0x0a9d8099, 0x0ab700be, + 0x0b1f0115, 0x0b5bc081, 0x0ba7c081, 0x0bbcc081, 0x0bc004ad, 0x0bc244ad, + 0x0bc484ad, 0x0bc6f383, 0x0be0852d, 0x0be31d03, 0x0bf1882d, 0x0c000081, + 0x0c0d8283, 0x0c130b84, 0x0c194284, 0x0c1c0122, 0x0c1cc122, 0x0c1d8122, + 0x0c1e4122, 0x0c1f0122, 0x0c250084, 0x0c26c123, 0x0c278084, 0x0c27c085, + 0x0c2b0b84, 0x0c314284, 0x0c340122, 0x0c34c122, 0x0c358122, 0x0c364122, + 0x0c370122, 0x0c3d0084, 0x0c3dc220, 0x0c3f8084, 0x0c3fc085, 0x0c4c4a2d, + 0x0c51451f, 0x0c53ca9f, 0x0c5915ad, 0x0c648703, 0x0c800741, 0x0c838089, + 0x0c83c129, 0x0c8441a9, 0x0c850089, 0x0c854129, 0x0c85c2a9, 0x0c870089, + 0x0c87408f, 0x0c87808d, 0x0c881241, 0x0c910203, 0x0c940099, 0x0c9444a3, + 0x0c968323, 0x0c98072d, 0x0c9b84af, 0x0c9dc2a1, 0x0c9f00b5, 0x0c9f40b3, + 0x0c9f8085, 0x0ca01883, 0x0cac4223, 0x0cad4523, 0x0cafc097, 0x0cb004a1, + 0x0cb241a5, 0x0cb30097, 0x0cb34099, 0x0cb38097, 0x0cb3c099, 0x0cb417ad, + 0x0cbfc085, 0x0cc001b3, 0x0cc0c0b1, 0x0cc100b3, 0x0cc14131, 0x0cc1c0b5, + 0x0cc200b3, 0x0cc241b1, 0x0cc30133, 0x0cc38131, 0x0cc40085, 0x0cc440b1, + 0x0cc48133, 0x0cc50085, 0x0cc540b5, 0x0cc580b7, 0x0cc5c0b5, 0x0cc600b1, + 0x0cc64135, 0x0cc6c0b3, 0x0cc701b1, 0x0cc7c0b3, 0x0cc800b5, 0x0cc840b3, + 0x0cc881b1, 0x0cc9422f, 0x0cca4131, 0x0ccac0b5, 0x0ccb00b1, 0x0ccb40b3, + 0x0ccb80b5, 0x0ccbc0b1, 0x0ccc012f, 0x0ccc80b5, 0x0cccc0b3, 0x0ccd00b5, + 0x0ccd40b1, 0x0ccd80b5, 0x0ccdc085, 0x0cce02b1, 0x0ccf40b3, 0x0ccf80b1, + 0x0ccfc085, 0x0cd001b1, 0x0cd0c0b3, 0x0cd101b1, 0x0cd1c0b5, 0x0cd200b3, + 0x0cd24085, 0x0cd280b5, 0x0cd2c085, 0x0cd30133, 0x0cd381b1, 0x0cd440b3, + 0x0cd48085, 0x0cd4c0b1, 0x0cd500b3, 0x0cd54085, 0x0cd580b5, 0x0cd5c0b1, + 0x0cd60521, 0x0cd88525, 0x0cdb02a5, 0x0cdc4099, 0x0cdc8117, 0x0cdd0099, + 0x0cdd4197, 0x0cde0127, 0x0cde8285, 0x0cdfc089, 0x0ce0043f, 0x0ce20099, + 0x0ce2409b, 0x0ce283bf, 0x0ce44219, 0x0ce54205, 0x0ce6433f, 0x0ce7c131, + 0x0ce84085, 0x0ce881b1, 0x0ce94085, 0x0ce98107, 0x0cea0089, 0x0cea4097, + 0x0cea8219, 0x0ceb809d, 0x0cebc08d, 0x0cec083f, 0x0cf00105, 0x0cf0809b, + 0x0cf0c197, 0x0cf1809b, 0x0cf1c099, 0x0cf20517, 0x0cf48099, 0x0cf4c117, + 0x0cf54119, 0x0cf5c097, 0x0cf6009b, 0x0cf64099, 0x0cf68217, 0x0cf78119, + 0x0cf804a1, 0x0cfa4525, 0x0cfcc525, 0x0cff4125, 0x0cffc099, 0x29a70103, + 0x29dc0081, 0x29fe0103, 0x2ad70203, 0x3e401482, 0x3e4a7f82, 0x3e6a3f82, + 0x3e8aa102, 0x3e9b0110, 0x3e9c2f82, 0x3eb3c590, 0x3ec00197, 0x3ec0c119, + 0x3ec1413f, 0x3ec4c2af, 0x3ec74184, 0x3ec804ad, 0x3eca4081, 0x3eca8304, + 0x3ecc03a0, 0x3ece02a0, 0x3ecf8084, 0x3ed00120, 0x3ed0c120, 0x3ed184ae, + 0x3ed3c085, 0x3ed4312d, 0x3ef4cbad, 0x3efa892f, 0x3eff022d, 0x3f002f2f, + 0x3f1782a5, 0x3f18c0b1, 0x3f1907af, 0x3f1cffaf, 0x3f3c81a5, 0x3f3d64af, + 0x3f542031, 0x3f649b31, 0x3f7c0131, 0x3f7c83b3, 0x3f7e40b1, 0x3f7e80bd, + 0x3f7ec0bb, 0x3f7f00b3, 0x3f840503, 0x3f8c01ad, 0x3f8cc315, 0x3f8e462d, + 0x3f91cc03, 0x3f97c695, 0x3f9c01af, 0x3f9d0085, 0x3f9d852f, 0x3fa03aad, + 0x3fbd442f, 0x3fc06f1f, 0x3fd7c11f, 0x3fd85fad, 0x3fe80081, 0x3fe84f1f, + 0x3ff0831f, 0x3ff2831f, 0x3ff4831f, 0x3ff6819f, 0x3ff80783, 0x44268192, + 0x442ac092, 0x444b8112, 0x44d2c112, 0x452ec212, 0x456e8112, 0x74578392, + 0x746ec312, 0x75000d1f, 0x75068d1f, 0x750d0d1f, 0x7513839f, 0x7515891f, + 0x751a0d1f, 0x75208d1f, 0x75271015, 0x752f439f, 0x7531459f, 0x75340d1f, + 0x753a8d1f, 0x75410395, 0x7543441f, 0x7545839f, 0x75478d1f, 0x754e0795, + 0x7552839f, 0x75548d1f, 0x755b0d1f, 0x75618d1f, 0x75680d1f, 0x756e8d1f, + 0x75750d1f, 0x757b8d1f, 0x75820d1f, 0x75888d1f, 0x758f0d1f, 0x75958d1f, + 0x759c0d1f, 0x75a28d1f, 0x75a90103, 0x75aa089f, 0x75ae4081, 0x75ae839f, + 0x75b04081, 0x75b08c9f, 0x75b6c081, 0x75b7032d, 0x75b8889f, 0x75bcc081, + 0x75bd039f, 0x75bec081, 0x75bf0c9f, 0x75c54081, 0x75c5832d, 0x75c7089f, + 0x75cb4081, 0x75cb839f, 0x75cd4081, 0x75cd8c9f, 0x75d3c081, 0x75d4032d, + 0x75d5889f, 0x75d9c081, 0x75da039f, 0x75dbc081, 0x75dc0c9f, 0x75e24081, + 0x75e2832d, 0x75e4089f, 0x75e84081, 0x75e8839f, 0x75ea4081, 0x75ea8c9f, + 0x75f0c081, 0x75f1042d, 0x75f3851f, 0x75f6051f, 0x75f8851f, 0x75fb051f, + 0x75fd851f, 0x7b80022d, 0x7b814dad, 0x7b884203, 0x7b89c081, 0x7b8a452d, + 0x7b8d0403, 0x7b908081, 0x7b91dc03, 0x7ba0052d, 0x7ba2c8ad, 0x7ba84483, + 0x7baac8ad, 0x7c400097, 0x7c404521, 0x7c440d25, 0x7c4a8087, 0x7c4ac115, + 0x7c4b4117, 0x7c4c0d1f, 0x7c528217, 0x7c538099, 0x7c53c097, 0x7c5a8197, + 0x7c640097, 0x7c80012f, 0x7c808081, 0x7c841603, 0x7c9004c1, 0x7c940103, + 0xbe0001ac, 0xbe00d110, 0xbe0947ac, 0xbe0d3910, 0xbe29872c, 0xbe2d022c, + 0xbe2e3790, 0xbe49ff90, 0xbe69bc10, +}; + +static const uint16_t unicode_decomp_table2[687] = { + 0x0020, 0x0000, 0x0061, 0x0002, 0x0004, 0x0006, 0x03bc, 0x0008, 0x000a, + 0x000c, 0x0015, 0x0095, 0x00a5, 0x00b9, 0x00c1, 0x00c3, 0x00c7, 0x00cb, + 0x00d1, 0x00d7, 0x00dd, 0x00e0, 0x00e6, 0x00f8, 0x0108, 0x010a, 0x0073, + 0x0110, 0x0112, 0x0114, 0x0120, 0x012c, 0x0144, 0x014d, 0x0153, 0x0162, + 0x0168, 0x016a, 0x0176, 0x0192, 0x0194, 0x01a9, 0x01bb, 0x01c7, 0x01d1, + 0x01d5, 0x02b9, 0x01d7, 0x003b, 0x01d9, 0x01db, 0x00b7, 0x01e1, 0x01fc, + 0x020c, 0x0218, 0x021d, 0x0223, 0x0227, 0x03a3, 0x0233, 0x023f, 0x0242, + 0x024b, 0x024e, 0x0251, 0x025d, 0x0260, 0x0269, 0x026c, 0x026f, 0x0275, + 0x0278, 0x0281, 0x028a, 0x029c, 0x029f, 0x02a3, 0x02af, 0x02b9, 0x02c5, + 0x02c9, 0x02cd, 0x02d1, 0x02d5, 0x02e7, 0x02ed, 0x02f1, 0x02f5, 0x02f9, + 0x02fd, 0x0305, 0x0309, 0x030d, 0x0313, 0x0317, 0x031b, 0x0323, 0x0327, + 0x032b, 0x032f, 0x0335, 0x033d, 0x0341, 0x0349, 0x034d, 0x0351, 0x0f0b, + 0x0357, 0x035b, 0x035f, 0x0363, 0x0367, 0x036b, 0x036f, 0x0373, 0x0379, + 0x037d, 0x0381, 0x0385, 0x0389, 0x038d, 0x0391, 0x0395, 0x0399, 0x039d, + 0x03a1, 0x10dc, 0x03a5, 0x03c9, 0x03cd, 0x03d9, 0x03dd, 0x03e1, 0x03ef, + 0x03f1, 0x043d, 0x044f, 0x0499, 0x04f0, 0x0502, 0x054a, 0x0564, 0x056c, + 0x0570, 0x0573, 0x059a, 0x05fa, 0x05fe, 0x0607, 0x060b, 0x0614, 0x0618, + 0x061e, 0x0622, 0x0628, 0x068e, 0x0694, 0x0698, 0x069e, 0x06a2, 0x06ab, + 0x03ac, 0x06f3, 0x03ad, 0x06f6, 0x03ae, 0x06f9, 0x03af, 0x06fc, 0x03cc, + 0x06ff, 0x03cd, 0x0702, 0x03ce, 0x0705, 0x0709, 0x070d, 0x0711, 0x0386, + 0x0732, 0x0735, 0x03b9, 0x0737, 0x073b, 0x0388, 0x0753, 0x0389, 0x0756, + 0x0390, 0x076b, 0x038a, 0x0777, 0x03b0, 0x0789, 0x038e, 0x0799, 0x079f, + 0x07a3, 0x038c, 0x07b8, 0x038f, 0x07bb, 0x00b4, 0x07be, 0x07c0, 0x07c2, + 0x2010, 0x07cb, 0x002e, 0x07cd, 0x07cf, 0x0020, 0x07d2, 0x07d6, 0x07db, + 0x07df, 0x07e4, 0x07ea, 0x07f0, 0x0020, 0x07f6, 0x2212, 0x0801, 0x0805, + 0x0807, 0x081d, 0x0825, 0x0827, 0x0043, 0x082d, 0x0830, 0x0190, 0x0836, + 0x0839, 0x004e, 0x0845, 0x0847, 0x084c, 0x084e, 0x0851, 0x005a, 0x03a9, + 0x005a, 0x0853, 0x0857, 0x0860, 0x0069, 0x0862, 0x0865, 0x086f, 0x0874, + 0x087a, 0x087e, 0x08a2, 0x0049, 0x08a4, 0x08a6, 0x08a9, 0x0056, 0x08ab, + 0x08ad, 0x08b0, 0x08b4, 0x0058, 0x08b6, 0x08b8, 0x08bb, 0x08c0, 0x08c2, + 0x08c5, 0x0076, 0x08c7, 0x08c9, 0x08cc, 0x08d0, 0x0078, 0x08d2, 0x08d4, + 0x08d7, 0x08db, 0x08de, 0x08e4, 0x08e7, 0x08f0, 0x08f3, 0x08f6, 0x08f9, + 0x0902, 0x0906, 0x090b, 0x090f, 0x0914, 0x0917, 0x091a, 0x0923, 0x092c, + 0x093b, 0x093e, 0x0941, 0x0944, 0x0947, 0x094a, 0x0956, 0x095c, 0x0960, + 0x0962, 0x0964, 0x0968, 0x096a, 0x0970, 0x0978, 0x097c, 0x0980, 0x0986, + 0x0989, 0x098f, 0x0991, 0x0030, 0x0993, 0x0999, 0x099c, 0x099e, 0x09a1, + 0x09a4, 0x2d61, 0x6bcd, 0x9f9f, 0x09a6, 0x09b1, 0x09bc, 0x09c7, 0x0a95, + 0x0aa1, 0x0b15, 0x0020, 0x0b27, 0x0b31, 0x0b8d, 0x0ba1, 0x0ba5, 0x0ba9, + 0x0bad, 0x0bb1, 0x0bb5, 0x0bb9, 0x0bbd, 0x0bc1, 0x0bc5, 0x0c21, 0x0c35, + 0x0c39, 0x0c3d, 0x0c41, 0x0c45, 0x0c49, 0x0c4d, 0x0c51, 0x0c55, 0x0c59, + 0x0c6f, 0x0c71, 0x0c73, 0x0ca0, 0x0cbc, 0x0cdc, 0x0ce4, 0x0cec, 0x0cf4, + 0x0cfc, 0x0d04, 0x0d0c, 0x0d14, 0x0d22, 0x0d2e, 0x0d7a, 0x0d82, 0x0d85, + 0x0d89, 0x0d8d, 0x0d9d, 0x0db1, 0x0db5, 0x0dbc, 0x0dc2, 0x0dc6, 0x0e28, + 0x0e2c, 0x0e30, 0x0e32, 0x0e36, 0x0e3c, 0x0e3e, 0x0e41, 0x0e43, 0x0e46, + 0x0e77, 0x0e7b, 0x0e89, 0x0e8e, 0x0e94, 0x0e9c, 0x0ea3, 0x0ea9, 0x0eb4, + 0x0ebe, 0x0ec6, 0x0eca, 0x0ecf, 0x0ed9, 0x0edd, 0x0ee4, 0x0eec, 0x0ef3, + 0x0ef8, 0x0f04, 0x0f0a, 0x0f15, 0x0f1b, 0x0f22, 0x0f28, 0x0f33, 0x0f3d, + 0x0f45, 0x0f4c, 0x0f51, 0x0f57, 0x0f5e, 0x0f63, 0x0f69, 0x0f70, 0x0f76, + 0x0f7d, 0x0f82, 0x0f89, 0x0f8d, 0x0f9e, 0x0fa4, 0x0fa9, 0x0fad, 0x0fb8, + 0x0fbe, 0x0fc9, 0x0fd0, 0x0fd6, 0x0fda, 0x0fe1, 0x0fe5, 0x0fef, 0x0ffa, + 0x1000, 0x1004, 0x1009, 0x100f, 0x1013, 0x101a, 0x101f, 0x1023, 0x1029, + 0x102f, 0x1032, 0x1036, 0x1039, 0x103f, 0x1045, 0x1059, 0x1061, 0x1079, + 0x107c, 0x1080, 0x1095, 0x10a1, 0x10b1, 0x10c3, 0x10cb, 0x10cf, 0x10da, + 0x10de, 0x10ea, 0x10f2, 0x10f4, 0x1100, 0x1105, 0x1111, 0x1141, 0x1149, + 0x114d, 0x1153, 0x1157, 0x115a, 0x116e, 0x1171, 0x1175, 0x117b, 0x117d, + 0x1181, 0x1184, 0x118c, 0x1192, 0x1196, 0x119c, 0x11a2, 0x11a8, 0x11ab, + 0xa76f, 0x11af, 0x11b3, 0x11bb, 0x120d, 0x130b, 0x1409, 0x148d, 0x1492, + 0x1550, 0x1569, 0x156f, 0x1575, 0x157b, 0x1587, 0x1593, 0x002b, 0x159e, + 0x15b6, 0x15ba, 0x15be, 0x15c2, 0x15c6, 0x15ca, 0x15de, 0x15e2, 0x1646, + 0x165f, 0x1685, 0x168b, 0x1749, 0x174f, 0x1754, 0x1774, 0x1874, 0x187a, + 0x190e, 0x19d0, 0x1a74, 0x1a7c, 0x1a9a, 0x1a9f, 0x1ab3, 0x1abd, 0x1ac3, + 0x1ad7, 0x1adc, 0x1ae2, 0x1af0, 0x1b20, 0x1b2d, 0x1b35, 0x1b39, 0x1b4f, + 0x1bc6, 0x1bd8, 0x1bda, 0x1bdc, 0x3164, 0x1c1d, 0x1c1f, 0x1c21, 0x1c23, + 0x1c25, 0x1c27, 0x1c45, 0x1c53, 0x1c58, 0x1c61, 0x1c6a, 0x1c7c, 0x1c85, + 0x1ca5, 0x1cc0, 0x1cc2, 0x1cc4, 0x1cc6, 0x1cc8, 0x1cca, 0x1ccc, 0x1cce, + 0x1cee, 0x1cf0, 0x1cf2, 0x1cf4, 0x1cf6, 0x1cfd, 0x1cff, 0x1d01, 0x1d03, + 0x1d12, 0x1d14, 0x1d16, 0x1d18, 0x1d1a, 0x1d1c, 0x1d1e, 0x1d20, 0x1d22, + 0x1d24, 0x1d26, 0x1d28, 0x1d2a, 0x1d2c, 0x1d2e, 0x1d32, 0x03f4, 0x1d34, + 0x2207, 0x1d36, 0x2202, 0x1d38, 0x1d40, 0x03f4, 0x1d42, 0x2207, 0x1d44, + 0x2202, 0x1d46, 0x1d4e, 0x03f4, 0x1d50, 0x2207, 0x1d52, 0x2202, 0x1d54, + 0x1d5c, 0x03f4, 0x1d5e, 0x2207, 0x1d60, 0x2202, 0x1d62, 0x1d6a, 0x03f4, + 0x1d6c, 0x2207, 0x1d6e, 0x2202, 0x1d70, 0x1d7a, 0x1d7c, 0x1d7e, 0x1d80, + 0x1d82, 0x1d84, 0x1d8a, 0x1da7, 0x062d, 0x1daf, 0x1dbb, 0x062c, 0x1dcb, + 0x1e3b, 0x1e47, 0x1e5a, 0x1e6c, 0x1e7f, 0x1e81, 0x1e85, 0x1e8b, 0x1e91, + 0x1e93, 0x1e97, 0x1e99, 0x1ea1, 0x1ea4, 0x1ea6, 0x1eac, 0x1eae, 0x30b5, + 0x1eb4, 0x1f0c, 0x1f22, 0x1f26, 0x1f2b, 0x1f78, 0x1f89, 0x208a, 0x209a, + 0x20a0, 0x219a, 0x22b8, +}; + +static const uint8_t unicode_decomp_data[9158] = { + 0x20, 0x88, 0x20, 0x84, 0x32, 0x33, 0x20, 0x81, 0x20, 0xa7, 0x31, 0x6f, + 0x31, 0xd0, 0x34, 0x31, 0xd0, 0x32, 0x33, 0xd0, 0x34, 0x41, 0x80, 0x41, + 0x81, 0x41, 0x82, 0x41, 0x83, 0x41, 0x88, 0x41, 0x8a, 0x00, 0x00, 0x43, + 0xa7, 0x45, 0x80, 0x45, 0x81, 0x45, 0x82, 0x45, 0x88, 0x49, 0x80, 0x49, + 0x81, 0x49, 0x82, 0x49, 0x88, 0x00, 0x00, 0x4e, 0x83, 0x4f, 0x80, 0x4f, + 0x81, 0x4f, 0x82, 0x4f, 0x83, 0x4f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x55, + 0x80, 0x55, 0x81, 0x55, 0x82, 0x55, 0x88, 0x59, 0x81, 0x00, 0x00, 0x00, + 0x00, 0x61, 0x80, 0x61, 0x81, 0x61, 0x82, 0x61, 0x83, 0x61, 0x88, 0x61, + 0x8a, 0x00, 0x00, 0x63, 0xa7, 0x65, 0x80, 0x65, 0x81, 0x65, 0x82, 0x65, + 0x88, 0x69, 0x80, 0x69, 0x81, 0x69, 0x82, 0x69, 0x88, 0x00, 0x00, 0x6e, + 0x83, 0x6f, 0x80, 0x6f, 0x81, 0x6f, 0x82, 0x6f, 0x83, 0x6f, 0x88, 0x00, + 0x00, 0x00, 0x00, 0x75, 0x80, 0x75, 0x81, 0x75, 0x82, 0x75, 0x88, 0x79, + 0x81, 0x00, 0x00, 0x79, 0x88, 0x41, 0x84, 0x41, 0x86, 0x41, 0xa8, 0x43, + 0x81, 0x43, 0x82, 0x43, 0x87, 0x43, 0x8c, 0x44, 0x8c, 0x45, 0x84, 0x45, + 0x86, 0x45, 0x87, 0x45, 0xa8, 0x45, 0x8c, 0x47, 0x82, 0x47, 0x86, 0x47, + 0x87, 0x47, 0xa7, 0x48, 0x82, 0x49, 0x83, 0x49, 0x84, 0x49, 0x86, 0x49, + 0xa8, 0x49, 0x87, 0x49, 0x4a, 0x69, 0x6a, 0x4a, 0x82, 0x4b, 0xa7, 0x4c, + 0x81, 0x4c, 0xa7, 0x4c, 0x8c, 0x4c, 0x00, 0x00, 0x6b, 0x20, 0x6b, 0x4e, + 0x81, 0x4e, 0xa7, 0x4e, 0x8c, 0xbc, 0x02, 0x6e, 0x4f, 0x84, 0x4f, 0x86, + 0x4f, 0x8b, 0x52, 0x81, 0x52, 0xa7, 0x52, 0x8c, 0x53, 0x81, 0x53, 0x82, + 0x53, 0xa7, 0x53, 0x8c, 0x54, 0xa7, 0x54, 0x8c, 0x55, 0x83, 0x55, 0x84, + 0x55, 0x86, 0x55, 0x8a, 0x55, 0x8b, 0x55, 0xa8, 0x57, 0x82, 0x59, 0x82, + 0x59, 0x88, 0x5a, 0x81, 0x5a, 0x87, 0x5a, 0x8c, 0x4f, 0x9b, 0x55, 0x9b, + 0x44, 0x00, 0x7d, 0x01, 0x44, 0x00, 0x7e, 0x01, 0x64, 0x00, 0x7e, 0x01, + 0x4c, 0x4a, 0x4c, 0x6a, 0x6c, 0x6a, 0x4e, 0x4a, 0x4e, 0x6a, 0x6e, 0x6a, + 0x41, 0x00, 0x8c, 0x49, 0x00, 0x8c, 0x4f, 0x00, 0x8c, 0x55, 0x00, 0x8c, + 0xdc, 0x00, 0x84, 0xdc, 0x00, 0x81, 0xdc, 0x00, 0x8c, 0xdc, 0x00, 0x80, + 0xc4, 0x00, 0x84, 0x26, 0x02, 0x84, 0xc6, 0x00, 0x84, 0x47, 0x8c, 0x4b, + 0x8c, 0x4f, 0xa8, 0xea, 0x01, 0x84, 0xeb, 0x01, 0x84, 0xb7, 0x01, 0x8c, + 0x92, 0x02, 0x8c, 0x6a, 0x00, 0x8c, 0x44, 0x5a, 0x44, 0x7a, 0x64, 0x7a, + 0x47, 0x81, 0x4e, 0x00, 0x80, 0xc5, 0x00, 0x81, 0xc6, 0x00, 0x81, 0xd8, + 0x00, 0x81, 0x41, 0x8f, 0x41, 0x91, 0x45, 0x8f, 0x45, 0x91, 0x49, 0x8f, + 0x49, 0x91, 0x4f, 0x8f, 0x4f, 0x91, 0x52, 0x8f, 0x52, 0x91, 0x55, 0x8f, + 0x55, 0x91, 0x53, 0xa6, 0x54, 0xa6, 0x48, 0x8c, 0x41, 0x00, 0x87, 0x45, + 0x00, 0xa7, 0xd6, 0x00, 0x84, 0xd5, 0x00, 0x84, 0x4f, 0x00, 0x87, 0x2e, + 0x02, 0x84, 0x59, 0x00, 0x84, 0x68, 0x00, 0x66, 0x02, 0x6a, 0x00, 0x72, + 0x00, 0x79, 0x02, 0x7b, 0x02, 0x81, 0x02, 0x77, 0x00, 0x79, 0x00, 0x20, + 0x86, 0x20, 0x87, 0x20, 0x8a, 0x20, 0xa8, 0x20, 0x83, 0x20, 0x8b, 0x63, + 0x02, 0x6c, 0x00, 0x73, 0x00, 0x78, 0x00, 0x95, 0x02, 0x80, 0x81, 0x00, + 0x93, 0x88, 0x81, 0x20, 0xc5, 0x20, 0x81, 0xa8, 0x00, 0x81, 0x91, 0x03, + 0x81, 0x95, 0x03, 0x81, 0x97, 0x03, 0x81, 0x99, 0x03, 0x81, 0x00, 0x00, + 0x00, 0x9f, 0x03, 0x81, 0x00, 0x00, 0x00, 0xa5, 0x03, 0x81, 0xa9, 0x03, + 0x81, 0xca, 0x03, 0x81, 0x01, 0x03, 0x98, 0x07, 0xa4, 0x07, 0xb0, 0x00, + 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xca, 0x00, 0x01, 0x03, 0xb8, 0x07, + 0xc4, 0x07, 0xbe, 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xa5, 0x03, 0x0d, 0x13, + 0x00, 0x01, 0x03, 0xd1, 0x00, 0xd1, 0x07, 0xc6, 0x03, 0xc0, 0x03, 0xba, + 0x03, 0xc1, 0x03, 0xc2, 0x03, 0x00, 0x00, 0x98, 0x03, 0xb5, 0x03, 0x15, + 0x04, 0x80, 0x15, 0x04, 0x88, 0x00, 0x00, 0x00, 0x13, 0x04, 0x81, 0x06, + 0x04, 0x88, 0x1a, 0x04, 0x81, 0x18, 0x04, 0x80, 0x23, 0x04, 0x86, 0x18, + 0x04, 0x86, 0x38, 0x04, 0x86, 0x35, 0x04, 0x80, 0x35, 0x04, 0x88, 0x00, + 0x00, 0x00, 0x33, 0x04, 0x81, 0x56, 0x04, 0x88, 0x3a, 0x04, 0x81, 0x38, + 0x04, 0x80, 0x43, 0x04, 0x86, 0x74, 0x04, 0x8f, 0x16, 0x04, 0x86, 0x10, + 0x04, 0x86, 0x10, 0x04, 0x88, 0x15, 0x04, 0x86, 0xd8, 0x04, 0x88, 0x16, + 0x04, 0x88, 0x17, 0x04, 0x88, 0x18, 0x04, 0x84, 0x18, 0x04, 0x88, 0x1e, + 0x04, 0x88, 0xe8, 0x04, 0x88, 0x2d, 0x04, 0x88, 0x23, 0x04, 0x84, 0x23, + 0x04, 0x88, 0x23, 0x04, 0x8b, 0x27, 0x04, 0x88, 0x2b, 0x04, 0x88, 0x65, + 0x05, 0x82, 0x05, 0x27, 0x06, 0x00, 0x2c, 0x00, 0x2d, 0x21, 0x2d, 0x00, + 0x2e, 0x23, 0x2d, 0x27, 0x06, 0x00, 0x4d, 0x21, 0x4d, 0xa0, 0x4d, 0x23, + 0x4d, 0xd5, 0x06, 0x54, 0x06, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x06, 0x54, + 0x06, 0xd2, 0x06, 0x54, 0x06, 0x28, 0x09, 0x3c, 0x09, 0x30, 0x09, 0x3c, + 0x09, 0x33, 0x09, 0x3c, 0x09, 0x15, 0x09, 0x00, 0x27, 0x01, 0x27, 0x02, + 0x27, 0x07, 0x27, 0x0c, 0x27, 0x0d, 0x27, 0x16, 0x27, 0x1a, 0x27, 0xbe, + 0x09, 0x09, 0x00, 0x09, 0x19, 0xa1, 0x09, 0xbc, 0x09, 0xaf, 0x09, 0xbc, + 0x09, 0x32, 0x0a, 0x3c, 0x0a, 0x38, 0x0a, 0x3c, 0x0a, 0x16, 0x0a, 0x00, + 0x26, 0x01, 0x26, 0x06, 0x26, 0x2b, 0x0a, 0x3c, 0x0a, 0x47, 0x0b, 0x56, + 0x0b, 0x3e, 0x0b, 0x09, 0x00, 0x09, 0x19, 0x21, 0x0b, 0x3c, 0x0b, 0x92, + 0x0b, 0xd7, 0x0b, 0xbe, 0x0b, 0x08, 0x00, 0x09, 0x00, 0x08, 0x19, 0x46, + 0x0c, 0x56, 0x0c, 0xbf, 0x0c, 0xd5, 0x0c, 0xc6, 0x0c, 0xd5, 0x0c, 0xc2, + 0x0c, 0x04, 0x00, 0x08, 0x13, 0x3e, 0x0d, 0x08, 0x00, 0x09, 0x00, 0x08, + 0x19, 0xd9, 0x0d, 0xca, 0x0d, 0xca, 0x0d, 0x0f, 0x05, 0x12, 0x00, 0x0f, + 0x15, 0x4d, 0x0e, 0x32, 0x0e, 0xcd, 0x0e, 0xb2, 0x0e, 0x99, 0x0e, 0x12, + 0x00, 0x12, 0x08, 0x42, 0x0f, 0xb7, 0x0f, 0x4c, 0x0f, 0xb7, 0x0f, 0x51, + 0x0f, 0xb7, 0x0f, 0x56, 0x0f, 0xb7, 0x0f, 0x5b, 0x0f, 0xb7, 0x0f, 0x40, + 0x0f, 0xb5, 0x0f, 0x71, 0x0f, 0x72, 0x0f, 0x71, 0x0f, 0x00, 0x03, 0x41, + 0x0f, 0xb2, 0x0f, 0x81, 0x0f, 0xb3, 0x0f, 0x80, 0x0f, 0xb3, 0x0f, 0x81, + 0x0f, 0x71, 0x0f, 0x80, 0x0f, 0x92, 0x0f, 0xb7, 0x0f, 0x9c, 0x0f, 0xb7, + 0x0f, 0xa1, 0x0f, 0xb7, 0x0f, 0xa6, 0x0f, 0xb7, 0x0f, 0xab, 0x0f, 0xb7, + 0x0f, 0x90, 0x0f, 0xb5, 0x0f, 0x25, 0x10, 0x2e, 0x10, 0x05, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1b, 0x35, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x09, 0x1b, 0x35, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x1b, 0x35, 0x1b, 0x11, 0x1b, 0x35, + 0x1b, 0x3a, 0x1b, 0x35, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x1b, 0x35, + 0x1b, 0x3e, 0x1b, 0x35, 0x1b, 0x42, 0x1b, 0x35, 0x1b, 0x41, 0x00, 0xc6, + 0x00, 0x42, 0x00, 0x00, 0x00, 0x44, 0x00, 0x45, 0x00, 0x8e, 0x01, 0x47, + 0x00, 0x4f, 0x00, 0x22, 0x02, 0x50, 0x00, 0x52, 0x00, 0x54, 0x00, 0x55, + 0x00, 0x57, 0x00, 0x61, 0x00, 0x50, 0x02, 0x51, 0x02, 0x02, 0x1d, 0x62, + 0x00, 0x64, 0x00, 0x65, 0x00, 0x59, 0x02, 0x5b, 0x02, 0x5c, 0x02, 0x67, + 0x00, 0x00, 0x00, 0x6b, 0x00, 0x6d, 0x00, 0x4b, 0x01, 0x6f, 0x00, 0x54, + 0x02, 0x16, 0x1d, 0x17, 0x1d, 0x70, 0x00, 0x74, 0x00, 0x75, 0x00, 0x1d, + 0x1d, 0x6f, 0x02, 0x76, 0x00, 0x25, 0x1d, 0xb2, 0x03, 0xb3, 0x03, 0xb4, + 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x69, 0x00, 0x72, 0x00, 0x75, 0x00, 0x76, + 0x00, 0xb2, 0x03, 0xb3, 0x03, 0xc1, 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x52, + 0x02, 0x63, 0x00, 0x55, 0x02, 0xf0, 0x00, 0x5c, 0x02, 0x66, 0x00, 0x5f, + 0x02, 0x61, 0x02, 0x65, 0x02, 0x68, 0x02, 0x69, 0x02, 0x6a, 0x02, 0x7b, + 0x1d, 0x9d, 0x02, 0x6d, 0x02, 0x85, 0x1d, 0x9f, 0x02, 0x71, 0x02, 0x70, + 0x02, 0x72, 0x02, 0x73, 0x02, 0x74, 0x02, 0x75, 0x02, 0x78, 0x02, 0x82, + 0x02, 0x83, 0x02, 0xab, 0x01, 0x89, 0x02, 0x8a, 0x02, 0x1c, 0x1d, 0x8b, + 0x02, 0x8c, 0x02, 0x7a, 0x00, 0x90, 0x02, 0x91, 0x02, 0x92, 0x02, 0xb8, + 0x03, 0x41, 0x00, 0xa5, 0x42, 0x00, 0x87, 0x42, 0x00, 0xa3, 0x42, 0x00, + 0xb1, 0xc7, 0x00, 0x81, 0x44, 0x00, 0x87, 0x44, 0x00, 0xa3, 0x44, 0x00, + 0xb1, 0x44, 0x00, 0xa7, 0x44, 0x00, 0xad, 0x12, 0x01, 0x80, 0x12, 0x01, + 0x81, 0x45, 0x00, 0xad, 0x45, 0x00, 0xb0, 0x28, 0x02, 0x86, 0x46, 0x00, + 0x87, 0x47, 0x00, 0x84, 0x48, 0x00, 0x87, 0x48, 0x00, 0xa3, 0x48, 0x00, + 0x88, 0x48, 0x00, 0xa7, 0x48, 0x00, 0xae, 0x49, 0x00, 0xb0, 0xcf, 0x00, + 0x81, 0x4b, 0x00, 0x81, 0x4b, 0x00, 0xa3, 0x4b, 0x00, 0xb1, 0x4c, 0x00, + 0xa3, 0x36, 0x1e, 0x84, 0x4c, 0xb1, 0x4c, 0xad, 0x4d, 0x81, 0x4d, 0x87, + 0x4d, 0xa3, 0x4e, 0x87, 0x4e, 0xa3, 0x4e, 0xb1, 0x4e, 0xad, 0xd5, 0x00, + 0x81, 0xd5, 0x00, 0x88, 0x4c, 0x01, 0x80, 0x4c, 0x01, 0x81, 0x50, 0x00, + 0x81, 0x50, 0x00, 0x87, 0x52, 0x00, 0x87, 0x52, 0x00, 0xa3, 0x5a, 0x1e, + 0x84, 0x52, 0x00, 0xb1, 0x53, 0x00, 0x87, 0x53, 0x00, 0xa3, 0x5a, 0x01, + 0x87, 0x60, 0x01, 0x87, 0x62, 0x1e, 0x87, 0x54, 0x00, 0x87, 0x54, 0x00, + 0xa3, 0x54, 0x00, 0xb1, 0x54, 0x00, 0xad, 0x55, 0x00, 0xa4, 0x55, 0x00, + 0xb0, 0x55, 0x00, 0xad, 0x68, 0x01, 0x81, 0x6a, 0x01, 0x88, 0x56, 0x83, + 0x56, 0xa3, 0x57, 0x80, 0x57, 0x81, 0x57, 0x88, 0x57, 0x87, 0x57, 0xa3, + 0x58, 0x87, 0x58, 0x88, 0x59, 0x87, 0x5a, 0x82, 0x5a, 0xa3, 0x5a, 0xb1, + 0x68, 0xb1, 0x74, 0x88, 0x77, 0x8a, 0x79, 0x8a, 0x61, 0x00, 0xbe, 0x02, + 0x7f, 0x01, 0x87, 0x41, 0x00, 0xa3, 0x41, 0x00, 0x89, 0xc2, 0x00, 0x81, + 0xc2, 0x00, 0x80, 0xc2, 0x00, 0x89, 0xc2, 0x00, 0x83, 0xa0, 0x1e, 0x82, + 0x02, 0x01, 0x81, 0x02, 0x01, 0x80, 0x02, 0x01, 0x89, 0x02, 0x01, 0x83, + 0xa0, 0x1e, 0x86, 0x45, 0x00, 0xa3, 0x45, 0x00, 0x89, 0x45, 0x00, 0x83, + 0xca, 0x00, 0x81, 0xca, 0x00, 0x80, 0xca, 0x00, 0x89, 0xca, 0x00, 0x83, + 0xb8, 0x1e, 0x82, 0x49, 0x00, 0x89, 0x49, 0x00, 0xa3, 0x4f, 0x00, 0xa3, + 0x4f, 0x00, 0x89, 0xd4, 0x00, 0x81, 0xd4, 0x00, 0x80, 0xd4, 0x00, 0x89, + 0xd4, 0x00, 0x83, 0xcc, 0x1e, 0x82, 0xa0, 0x01, 0x81, 0xa0, 0x01, 0x80, + 0xa0, 0x01, 0x89, 0xa0, 0x01, 0x83, 0xa0, 0x01, 0xa3, 0x55, 0x00, 0xa3, + 0x55, 0x00, 0x89, 0xaf, 0x01, 0x81, 0xaf, 0x01, 0x80, 0xaf, 0x01, 0x89, + 0xaf, 0x01, 0x83, 0xaf, 0x01, 0xa3, 0x59, 0x00, 0x80, 0x59, 0x00, 0xa3, + 0x59, 0x00, 0x89, 0x59, 0x00, 0x83, 0xb1, 0x03, 0x13, 0x03, 0x00, 0x1f, + 0x80, 0x00, 0x1f, 0x81, 0x00, 0x1f, 0xc2, 0x91, 0x03, 0x13, 0x03, 0x08, + 0x1f, 0x80, 0x08, 0x1f, 0x81, 0x08, 0x1f, 0xc2, 0xb5, 0x03, 0x13, 0x03, + 0x10, 0x1f, 0x80, 0x10, 0x1f, 0x81, 0x95, 0x03, 0x13, 0x03, 0x18, 0x1f, + 0x80, 0x18, 0x1f, 0x81, 0xb7, 0x03, 0x93, 0xb7, 0x03, 0x94, 0x20, 0x1f, + 0x80, 0x21, 0x1f, 0x80, 0x20, 0x1f, 0x81, 0x21, 0x1f, 0x81, 0x20, 0x1f, + 0xc2, 0x21, 0x1f, 0xc2, 0x97, 0x03, 0x93, 0x97, 0x03, 0x94, 0x28, 0x1f, + 0x80, 0x29, 0x1f, 0x80, 0x28, 0x1f, 0x81, 0x29, 0x1f, 0x81, 0x28, 0x1f, + 0xc2, 0x29, 0x1f, 0xc2, 0xb9, 0x03, 0x93, 0xb9, 0x03, 0x94, 0x30, 0x1f, + 0x80, 0x31, 0x1f, 0x80, 0x30, 0x1f, 0x81, 0x31, 0x1f, 0x81, 0x30, 0x1f, + 0xc2, 0x31, 0x1f, 0xc2, 0x99, 0x03, 0x93, 0x99, 0x03, 0x94, 0x38, 0x1f, + 0x80, 0x39, 0x1f, 0x80, 0x38, 0x1f, 0x81, 0x39, 0x1f, 0x81, 0x38, 0x1f, + 0xc2, 0x39, 0x1f, 0xc2, 0xbf, 0x03, 0x93, 0xbf, 0x03, 0x94, 0x40, 0x1f, + 0x80, 0x40, 0x1f, 0x81, 0x9f, 0x03, 0x13, 0x03, 0x48, 0x1f, 0x80, 0x48, + 0x1f, 0x81, 0xc5, 0x03, 0x13, 0x03, 0x50, 0x1f, 0x80, 0x50, 0x1f, 0x81, + 0x50, 0x1f, 0xc2, 0xa5, 0x03, 0x94, 0x00, 0x00, 0x00, 0x59, 0x1f, 0x80, + 0x00, 0x00, 0x00, 0x59, 0x1f, 0x81, 0x00, 0x00, 0x00, 0x59, 0x1f, 0xc2, + 0xc9, 0x03, 0x93, 0xc9, 0x03, 0x94, 0x60, 0x1f, 0x80, 0x61, 0x1f, 0x80, + 0x60, 0x1f, 0x81, 0x61, 0x1f, 0x81, 0x60, 0x1f, 0xc2, 0x61, 0x1f, 0xc2, + 0xa9, 0x03, 0x93, 0xa9, 0x03, 0x94, 0x68, 0x1f, 0x80, 0x69, 0x1f, 0x80, + 0x68, 0x1f, 0x81, 0x69, 0x1f, 0x81, 0x68, 0x1f, 0xc2, 0x69, 0x1f, 0xc2, + 0xb1, 0x03, 0x80, 0xb5, 0x03, 0x80, 0xb7, 0x03, 0x80, 0xb9, 0x03, 0x80, + 0xbf, 0x03, 0x80, 0xc5, 0x03, 0x80, 0xc9, 0x03, 0x80, 0x00, 0x1f, 0x45, + 0x03, 0x20, 0x1f, 0x45, 0x03, 0x60, 0x1f, 0x45, 0x03, 0xb1, 0x03, 0x86, + 0xb1, 0x03, 0x84, 0x70, 0x1f, 0xc5, 0xb1, 0x03, 0xc5, 0xac, 0x03, 0xc5, + 0x00, 0x00, 0x00, 0xb1, 0x03, 0xc2, 0xb6, 0x1f, 0xc5, 0x91, 0x03, 0x86, + 0x91, 0x03, 0x84, 0x91, 0x03, 0x80, 0x91, 0x03, 0xc5, 0x20, 0x93, 0x20, + 0x93, 0x20, 0xc2, 0xa8, 0x00, 0xc2, 0x74, 0x1f, 0xc5, 0xb7, 0x03, 0xc5, + 0xae, 0x03, 0xc5, 0x00, 0x00, 0x00, 0xb7, 0x03, 0xc2, 0xc6, 0x1f, 0xc5, + 0x95, 0x03, 0x80, 0x97, 0x03, 0x80, 0x97, 0x03, 0xc5, 0xbf, 0x1f, 0x80, + 0xbf, 0x1f, 0x81, 0xbf, 0x1f, 0xc2, 0xb9, 0x03, 0x86, 0xb9, 0x03, 0x84, + 0xca, 0x03, 0x80, 0x00, 0x03, 0xb9, 0x42, 0xca, 0x42, 0x99, 0x06, 0x99, + 0x04, 0x99, 0x00, 0xfe, 0x1f, 0x80, 0xfe, 0x1f, 0x81, 0xfe, 0x1f, 0xc2, + 0xc5, 0x03, 0x86, 0xc5, 0x03, 0x84, 0xcb, 0x03, 0x80, 0x00, 0x03, 0xc1, + 0x13, 0xc1, 0x14, 0xc5, 0x42, 0xcb, 0x42, 0xa5, 0x06, 0xa5, 0x04, 0xa5, + 0x00, 0xa1, 0x03, 0x94, 0xa8, 0x00, 0x80, 0x85, 0x03, 0x60, 0x00, 0x7c, + 0x1f, 0xc5, 0xc9, 0x03, 0xc5, 0xce, 0x03, 0xc5, 0x00, 0x00, 0x00, 0xc9, + 0x03, 0xc2, 0xf6, 0x1f, 0xc5, 0x9f, 0x03, 0x80, 0xa9, 0x03, 0x80, 0xa9, + 0x03, 0xc5, 0x20, 0x94, 0x02, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xb3, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x32, 0x20, + 0x32, 0x20, 0x32, 0x20, 0x00, 0x00, 0x00, 0x35, 0x20, 0x35, 0x20, 0x35, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x21, 0x00, 0x00, 0x20, 0x85, 0x3f, 0x3f, + 0x3f, 0x21, 0x21, 0x3f, 0x32, 0x20, 0x00, 0x00, 0x00, 0x00, 0x30, 0x69, + 0x00, 0x00, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x3d, 0x28, 0x29, + 0x6e, 0x30, 0x00, 0x2b, 0x00, 0x12, 0x22, 0x3d, 0x00, 0x28, 0x00, 0x29, + 0x00, 0x00, 0x00, 0x61, 0x00, 0x65, 0x00, 0x6f, 0x00, 0x78, 0x00, 0x59, + 0x02, 0x68, 0x6b, 0x6c, 0x6d, 0x6e, 0x70, 0x73, 0x74, 0x52, 0x73, 0x61, + 0x2f, 0x63, 0x61, 0x2f, 0x73, 0xb0, 0x00, 0x43, 0x63, 0x2f, 0x6f, 0x63, + 0x2f, 0x75, 0xb0, 0x00, 0x46, 0x48, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, + 0xdf, 0x01, 0x01, 0x04, 0x24, 0x4e, 0x6f, 0x50, 0x51, 0x52, 0x52, 0x52, + 0x53, 0x4d, 0x54, 0x45, 0x4c, 0x54, 0x4d, 0x4b, 0x00, 0xc5, 0x00, 0x42, + 0x43, 0x00, 0x65, 0x45, 0x46, 0x00, 0x4d, 0x6f, 0xd0, 0x05, 0x46, 0x41, + 0x58, 0xc0, 0x03, 0xb3, 0x03, 0x93, 0x03, 0xa0, 0x03, 0x11, 0x22, 0x44, + 0x64, 0x65, 0x69, 0x6a, 0x31, 0xd0, 0x37, 0x31, 0xd0, 0x39, 0x31, 0xd0, + 0x31, 0x30, 0x31, 0xd0, 0x33, 0x32, 0xd0, 0x33, 0x31, 0xd0, 0x35, 0x32, + 0xd0, 0x35, 0x33, 0xd0, 0x35, 0x34, 0xd0, 0x35, 0x31, 0xd0, 0x36, 0x35, + 0xd0, 0x36, 0x31, 0xd0, 0x38, 0x33, 0xd0, 0x38, 0x35, 0xd0, 0x38, 0x37, + 0xd0, 0x38, 0x31, 0xd0, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x56, 0x56, + 0x49, 0x56, 0x49, 0x49, 0x56, 0x49, 0x49, 0x49, 0x49, 0x58, 0x58, 0x49, + 0x58, 0x49, 0x49, 0x4c, 0x43, 0x44, 0x4d, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x76, 0x76, 0x69, 0x76, 0x69, 0x69, 0x76, 0x69, 0x69, 0x69, + 0x69, 0x78, 0x78, 0x69, 0x78, 0x69, 0x69, 0x6c, 0x63, 0x64, 0x6d, 0x30, + 0xd0, 0x33, 0x90, 0x21, 0xb8, 0x92, 0x21, 0xb8, 0x94, 0x21, 0xb8, 0xd0, + 0x21, 0xb8, 0xd4, 0x21, 0xb8, 0xd2, 0x21, 0xb8, 0x03, 0x22, 0xb8, 0x08, + 0x22, 0xb8, 0x0b, 0x22, 0xb8, 0x23, 0x22, 0xb8, 0x00, 0x00, 0x00, 0x25, + 0x22, 0xb8, 0x2b, 0x22, 0x2b, 0x22, 0x2b, 0x22, 0x00, 0x00, 0x00, 0x2e, + 0x22, 0x2e, 0x22, 0x2e, 0x22, 0x00, 0x00, 0x00, 0x3c, 0x22, 0xb8, 0x43, + 0x22, 0xb8, 0x45, 0x22, 0xb8, 0x00, 0x00, 0x00, 0x48, 0x22, 0xb8, 0x3d, + 0x00, 0xb8, 0x00, 0x00, 0x00, 0x61, 0x22, 0xb8, 0x4d, 0x22, 0xb8, 0x3c, + 0x00, 0xb8, 0x3e, 0x00, 0xb8, 0x64, 0x22, 0xb8, 0x65, 0x22, 0xb8, 0x72, + 0x22, 0xb8, 0x76, 0x22, 0xb8, 0x7a, 0x22, 0xb8, 0x82, 0x22, 0xb8, 0x86, + 0x22, 0xb8, 0xa2, 0x22, 0xb8, 0xa8, 0x22, 0xb8, 0xa9, 0x22, 0xb8, 0xab, + 0x22, 0xb8, 0x7c, 0x22, 0xb8, 0x91, 0x22, 0xb8, 0xb2, 0x22, 0x38, 0x03, + 0x08, 0x30, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00, 0x32, 0x30, 0x28, 0x00, + 0x31, 0x00, 0x29, 0x00, 0x28, 0x00, 0x31, 0x00, 0x30, 0x00, 0x29, 0x00, + 0x28, 0x32, 0x30, 0x29, 0x31, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, + 0x2e, 0x00, 0x32, 0x30, 0x2e, 0x28, 0x00, 0x61, 0x00, 0x29, 0x00, 0x41, + 0x00, 0x61, 0x00, 0x2b, 0x22, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x3a, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0xdd, 0x2a, 0xb8, 0x6a, 0x56, 0x00, 0x4e, + 0x00, 0x28, 0x36, 0x3f, 0x59, 0x85, 0x8c, 0xa0, 0xba, 0x3f, 0x51, 0x00, + 0x26, 0x2c, 0x43, 0x57, 0x6c, 0xa1, 0xb6, 0xc1, 0x9b, 0x52, 0x00, 0x5e, + 0x7a, 0x7f, 0x9d, 0xa6, 0xc1, 0xce, 0xe7, 0xb6, 0x53, 0xc8, 0x53, 0xe3, + 0x53, 0xd7, 0x56, 0x1f, 0x57, 0xeb, 0x58, 0x02, 0x59, 0x0a, 0x59, 0x15, + 0x59, 0x27, 0x59, 0x73, 0x59, 0x50, 0x5b, 0x80, 0x5b, 0xf8, 0x5b, 0x0f, + 0x5c, 0x22, 0x5c, 0x38, 0x5c, 0x6e, 0x5c, 0x71, 0x5c, 0xdb, 0x5d, 0xe5, + 0x5d, 0xf1, 0x5d, 0xfe, 0x5d, 0x72, 0x5e, 0x7a, 0x5e, 0x7f, 0x5e, 0xf4, + 0x5e, 0xfe, 0x5e, 0x0b, 0x5f, 0x13, 0x5f, 0x50, 0x5f, 0x61, 0x5f, 0x73, + 0x5f, 0xc3, 0x5f, 0x08, 0x62, 0x36, 0x62, 0x4b, 0x62, 0x2f, 0x65, 0x34, + 0x65, 0x87, 0x65, 0x97, 0x65, 0xa4, 0x65, 0xb9, 0x65, 0xe0, 0x65, 0xe5, + 0x65, 0xf0, 0x66, 0x08, 0x67, 0x28, 0x67, 0x20, 0x6b, 0x62, 0x6b, 0x79, + 0x6b, 0xb3, 0x6b, 0xcb, 0x6b, 0xd4, 0x6b, 0xdb, 0x6b, 0x0f, 0x6c, 0x14, + 0x6c, 0x34, 0x6c, 0x6b, 0x70, 0x2a, 0x72, 0x36, 0x72, 0x3b, 0x72, 0x3f, + 0x72, 0x47, 0x72, 0x59, 0x72, 0x5b, 0x72, 0xac, 0x72, 0x84, 0x73, 0x89, + 0x73, 0xdc, 0x74, 0xe6, 0x74, 0x18, 0x75, 0x1f, 0x75, 0x28, 0x75, 0x30, + 0x75, 0x8b, 0x75, 0x92, 0x75, 0x76, 0x76, 0x7d, 0x76, 0xae, 0x76, 0xbf, + 0x76, 0xee, 0x76, 0xdb, 0x77, 0xe2, 0x77, 0xf3, 0x77, 0x3a, 0x79, 0xb8, + 0x79, 0xbe, 0x79, 0x74, 0x7a, 0xcb, 0x7a, 0xf9, 0x7a, 0x73, 0x7c, 0xf8, + 0x7c, 0x36, 0x7f, 0x51, 0x7f, 0x8a, 0x7f, 0xbd, 0x7f, 0x01, 0x80, 0x0c, + 0x80, 0x12, 0x80, 0x33, 0x80, 0x7f, 0x80, 0x89, 0x80, 0xe3, 0x81, 0x00, + 0x07, 0x10, 0x19, 0x29, 0x38, 0x3c, 0x8b, 0x8f, 0x95, 0x4d, 0x86, 0x6b, + 0x86, 0x40, 0x88, 0x4c, 0x88, 0x63, 0x88, 0x7e, 0x89, 0x8b, 0x89, 0xd2, + 0x89, 0x00, 0x8a, 0x37, 0x8c, 0x46, 0x8c, 0x55, 0x8c, 0x78, 0x8c, 0x9d, + 0x8c, 0x64, 0x8d, 0x70, 0x8d, 0xb3, 0x8d, 0xab, 0x8e, 0xca, 0x8e, 0x9b, + 0x8f, 0xb0, 0x8f, 0xb5, 0x8f, 0x91, 0x90, 0x49, 0x91, 0xc6, 0x91, 0xcc, + 0x91, 0xd1, 0x91, 0x77, 0x95, 0x80, 0x95, 0x1c, 0x96, 0xb6, 0x96, 0xb9, + 0x96, 0xe8, 0x96, 0x51, 0x97, 0x5e, 0x97, 0x62, 0x97, 0x69, 0x97, 0xcb, + 0x97, 0xed, 0x97, 0xf3, 0x97, 0x01, 0x98, 0xa8, 0x98, 0xdb, 0x98, 0xdf, + 0x98, 0x96, 0x99, 0x99, 0x99, 0xac, 0x99, 0xa8, 0x9a, 0xd8, 0x9a, 0xdf, + 0x9a, 0x25, 0x9b, 0x2f, 0x9b, 0x32, 0x9b, 0x3c, 0x9b, 0x5a, 0x9b, 0xe5, + 0x9c, 0x75, 0x9e, 0x7f, 0x9e, 0xa5, 0x9e, 0x00, 0x16, 0x1e, 0x28, 0x2c, + 0x54, 0x58, 0x69, 0x6e, 0x7b, 0x96, 0xa5, 0xad, 0xe8, 0xf7, 0xfb, 0x12, + 0x30, 0x00, 0x00, 0x41, 0x53, 0x44, 0x53, 0x45, 0x53, 0x4b, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, 0x00, 0x51, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x53, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x55, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, 0x00, 0x57, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x59, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x61, 0x30, 0x99, 0x30, 0x64, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, 0x00, 0x68, 0x30, 0x99, + 0x30, 0x6f, 0x30, 0x99, 0x30, 0x72, 0x30, 0x99, 0x30, 0x75, 0x30, 0x99, + 0x30, 0x78, 0x30, 0x99, 0x30, 0x7b, 0x30, 0x99, 0x30, 0x46, 0x30, 0x99, + 0x30, 0x20, 0x00, 0x99, 0x30, 0x9d, 0x30, 0x99, 0x30, 0x88, 0x30, 0x8a, + 0x30, 0xab, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, 0x00, 0xad, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xb1, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xb7, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xbd, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x30, 0x99, 0x30, 0xc4, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xc8, 0x30, 0x99, 0x30, 0xcf, 0x30, 0x99, 0x30, 0xd2, 0x30, 0x99, + 0x30, 0xd5, 0x30, 0x99, 0x30, 0xd8, 0x30, 0x99, 0x30, 0xdb, 0x30, 0x99, + 0x30, 0xa6, 0x30, 0x99, 0x30, 0xef, 0x30, 0x99, 0x30, 0xfd, 0x30, 0x99, + 0x30, 0xb3, 0x30, 0xc8, 0x30, 0x00, 0x11, 0x00, 0x01, 0xaa, 0x02, 0xac, + 0xad, 0x03, 0x04, 0x05, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0x1a, 0x06, + 0x07, 0x08, 0x21, 0x09, 0x11, 0x61, 0x11, 0x14, 0x11, 0x4c, 0x00, 0x01, + 0xb3, 0xb4, 0xb8, 0xba, 0xbf, 0xc3, 0xc5, 0x08, 0xc9, 0xcb, 0x09, 0x0a, + 0x0c, 0x0e, 0x0f, 0x13, 0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x22, + 0x2c, 0x33, 0x38, 0xdd, 0xde, 0x43, 0x44, 0x45, 0x70, 0x71, 0x74, 0x7d, + 0x7e, 0x80, 0x8a, 0x8d, 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, + 0x0a, 0x4e, 0x2d, 0x4e, 0x0b, 0x4e, 0x32, 0x75, 0x59, 0x4e, 0x19, 0x4e, + 0x01, 0x4e, 0x29, 0x59, 0x30, 0x57, 0xba, 0x4e, 0x28, 0x00, 0x29, 0x00, + 0x00, 0x11, 0x02, 0x11, 0x03, 0x11, 0x05, 0x11, 0x06, 0x11, 0x07, 0x11, + 0x09, 0x11, 0x0b, 0x11, 0x0c, 0x11, 0x0e, 0x11, 0x0f, 0x11, 0x10, 0x11, + 0x11, 0x11, 0x12, 0x11, 0x28, 0x00, 0x00, 0x11, 0x61, 0x11, 0x29, 0x00, + 0x28, 0x00, 0x02, 0x11, 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x05, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x09, 0x11, 0x61, 0x11, 0x29, 0x00, + 0x28, 0x00, 0x0b, 0x11, 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0e, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0c, 0x11, 0x6e, 0x11, 0x29, 0x00, + 0x28, 0x00, 0x0b, 0x11, 0x69, 0x11, 0x0c, 0x11, 0x65, 0x11, 0xab, 0x11, + 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, 0x69, 0x11, 0x12, 0x11, 0x6e, 0x11, + 0x29, 0x00, 0x28, 0x00, 0x29, 0x00, 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, + 0xdb, 0x56, 0x94, 0x4e, 0x6d, 0x51, 0x03, 0x4e, 0x6b, 0x51, 0x5d, 0x4e, + 0x41, 0x53, 0x08, 0x67, 0x6b, 0x70, 0x34, 0x6c, 0x28, 0x67, 0xd1, 0x91, + 0x1f, 0x57, 0xe5, 0x65, 0x2a, 0x68, 0x09, 0x67, 0x3e, 0x79, 0x0d, 0x54, + 0x79, 0x72, 0xa1, 0x8c, 0x5d, 0x79, 0xb4, 0x52, 0xe3, 0x4e, 0x7c, 0x54, + 0x66, 0x5b, 0xe3, 0x76, 0x01, 0x4f, 0xc7, 0x8c, 0x54, 0x53, 0x6d, 0x79, + 0x11, 0x4f, 0xea, 0x81, 0xf3, 0x81, 0x4f, 0x55, 0x7c, 0x5e, 0x87, 0x65, + 0x8f, 0x7b, 0x50, 0x54, 0x45, 0x32, 0x00, 0x31, 0x00, 0x33, 0x00, 0x30, + 0x00, 0x00, 0x11, 0x00, 0x02, 0x03, 0x05, 0x06, 0x07, 0x09, 0x0b, 0x0c, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x00, 0x11, 0x00, 0x61, 0x02, 0x61, 0x03, + 0x61, 0x05, 0x61, 0x06, 0x61, 0x07, 0x61, 0x09, 0x61, 0x0b, 0x61, 0x0c, + 0x61, 0x0e, 0x11, 0x61, 0x11, 0x00, 0x11, 0x0e, 0x61, 0xb7, 0x00, 0x69, + 0x0b, 0x11, 0x01, 0x63, 0x00, 0x69, 0x0b, 0x11, 0x6e, 0x11, 0x00, 0x4e, + 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, 0x94, 0x4e, 0x6d, 0x51, 0x03, 0x4e, + 0x6b, 0x51, 0x5d, 0x4e, 0x41, 0x53, 0x08, 0x67, 0x6b, 0x70, 0x34, 0x6c, + 0x28, 0x67, 0xd1, 0x91, 0x1f, 0x57, 0xe5, 0x65, 0x2a, 0x68, 0x09, 0x67, + 0x3e, 0x79, 0x0d, 0x54, 0x79, 0x72, 0xa1, 0x8c, 0x5d, 0x79, 0xb4, 0x52, + 0xd8, 0x79, 0x37, 0x75, 0x73, 0x59, 0x69, 0x90, 0x2a, 0x51, 0x70, 0x53, + 0xe8, 0x6c, 0x05, 0x98, 0x11, 0x4f, 0x99, 0x51, 0x63, 0x6b, 0x0a, 0x4e, + 0x2d, 0x4e, 0x0b, 0x4e, 0xe6, 0x5d, 0xf3, 0x53, 0x3b, 0x53, 0x97, 0x5b, + 0x66, 0x5b, 0xe3, 0x76, 0x01, 0x4f, 0xc7, 0x8c, 0x54, 0x53, 0x1c, 0x59, + 0x33, 0x00, 0x36, 0x00, 0x34, 0x00, 0x30, 0x00, 0x35, 0x30, 0x31, 0x00, + 0x08, 0x67, 0x31, 0x00, 0x30, 0x00, 0x08, 0x67, 0x48, 0x67, 0x65, 0x72, + 0x67, 0x65, 0x56, 0x4c, 0x54, 0x44, 0xa2, 0x30, 0x00, 0x02, 0x04, 0x06, + 0x08, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, + 0x1f, 0x22, 0x24, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x30, 0x33, + 0x36, 0x39, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x42, 0x44, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0xe4, 0x4e, 0x8c, 0x54, 0xa1, + 0x30, 0x01, 0x30, 0x5b, 0x27, 0x01, 0x4a, 0x34, 0x00, 0x01, 0x52, 0x39, + 0x01, 0xa2, 0x30, 0x00, 0x5a, 0x49, 0xa4, 0x30, 0x00, 0x27, 0x4f, 0x0c, + 0xa4, 0x30, 0x00, 0x4f, 0x1d, 0x02, 0x05, 0x4f, 0xa8, 0x30, 0x00, 0x11, + 0x07, 0x54, 0x21, 0xa8, 0x30, 0x00, 0x54, 0x03, 0x54, 0xa4, 0x30, 0x06, + 0x4f, 0x15, 0x06, 0x58, 0x3c, 0x07, 0x00, 0x46, 0xab, 0x30, 0x00, 0x3e, + 0x18, 0x1d, 0x00, 0x42, 0x3f, 0x51, 0xac, 0x30, 0x00, 0x41, 0x47, 0x00, + 0x47, 0x32, 0xae, 0x30, 0xac, 0x30, 0xae, 0x30, 0x00, 0x1d, 0x4e, 0xad, + 0x30, 0x00, 0x38, 0x3d, 0x4f, 0x01, 0x3e, 0x13, 0x4f, 0xad, 0x30, 0xed, + 0x30, 0xad, 0x30, 0x00, 0x40, 0x03, 0x3c, 0x33, 0xad, 0x30, 0x00, 0x40, + 0x34, 0x4f, 0x1b, 0x3e, 0xad, 0x30, 0x00, 0x40, 0x42, 0x16, 0x1b, 0xb0, + 0x30, 0x00, 0x39, 0x30, 0xa4, 0x30, 0x0c, 0x45, 0x3c, 0x24, 0x4f, 0x0b, + 0x47, 0x18, 0x00, 0x49, 0xaf, 0x30, 0x00, 0x3e, 0x4d, 0x1e, 0xb1, 0x30, + 0x00, 0x4b, 0x08, 0x02, 0x3a, 0x19, 0x02, 0x4b, 0x2c, 0xa4, 0x30, 0x11, + 0x00, 0x0b, 0x47, 0xb5, 0x30, 0x00, 0x3e, 0x0c, 0x47, 0x2b, 0xb0, 0x30, + 0x07, 0x3a, 0x43, 0x00, 0xb9, 0x30, 0x02, 0x3a, 0x08, 0x02, 0x3a, 0x0f, + 0x07, 0x43, 0x00, 0xb7, 0x30, 0x10, 0x00, 0x12, 0x34, 0x11, 0x3c, 0x13, + 0x17, 0xa4, 0x30, 0x2a, 0x1f, 0x24, 0x2b, 0x00, 0x20, 0xbb, 0x30, 0x16, + 0x41, 0x00, 0x38, 0x0d, 0xc4, 0x30, 0x0d, 0x38, 0x00, 0xd0, 0x30, 0x00, + 0x2c, 0x1c, 0x1b, 0xa2, 0x30, 0x32, 0x00, 0x17, 0x26, 0x49, 0xaf, 0x30, + 0x25, 0x00, 0x3c, 0xb3, 0x30, 0x21, 0x00, 0x20, 0x38, 0xa1, 0x30, 0x34, + 0x00, 0x48, 0x22, 0x28, 0xa3, 0x30, 0x32, 0x00, 0x59, 0x25, 0xa7, 0x30, + 0x2f, 0x1c, 0x10, 0x00, 0x44, 0xd5, 0x30, 0x00, 0x14, 0x1e, 0xaf, 0x30, + 0x29, 0x00, 0x10, 0x4d, 0x3c, 0xda, 0x30, 0xbd, 0x30, 0xb8, 0x30, 0x22, + 0x13, 0x1a, 0x20, 0x33, 0x0c, 0x22, 0x3b, 0x01, 0x22, 0x44, 0x00, 0x21, + 0x44, 0x07, 0xa4, 0x30, 0x39, 0x00, 0x4f, 0x24, 0xc8, 0x30, 0x14, 0x23, + 0x00, 0xdb, 0x30, 0xf3, 0x30, 0xc9, 0x30, 0x14, 0x2a, 0x00, 0x12, 0x33, + 0x22, 0x12, 0x33, 0x2a, 0xa4, 0x30, 0x3a, 0x00, 0x0b, 0x49, 0xa4, 0x30, + 0x3a, 0x00, 0x47, 0x3a, 0x1f, 0x2b, 0x3a, 0x47, 0x0b, 0xb7, 0x30, 0x27, + 0x3c, 0x00, 0x30, 0x3c, 0xaf, 0x30, 0x30, 0x00, 0x3e, 0x44, 0xdf, 0x30, + 0xea, 0x30, 0xd0, 0x30, 0x0f, 0x1a, 0x00, 0x2c, 0x1b, 0xe1, 0x30, 0xac, + 0x30, 0xac, 0x30, 0x35, 0x00, 0x1c, 0x47, 0x35, 0x50, 0x1c, 0x3f, 0xa2, + 0x30, 0x42, 0x5a, 0x27, 0x42, 0x5a, 0x49, 0x44, 0x00, 0x51, 0xc3, 0x30, + 0x27, 0x00, 0x05, 0x28, 0xea, 0x30, 0xe9, 0x30, 0xd4, 0x30, 0x17, 0x00, + 0x28, 0xd6, 0x30, 0x15, 0x26, 0x00, 0x15, 0xec, 0x30, 0xe0, 0x30, 0xb2, + 0x30, 0x3a, 0x41, 0x16, 0x00, 0x41, 0xc3, 0x30, 0x2c, 0x00, 0x05, 0x30, + 0x00, 0xb9, 0x70, 0x31, 0x00, 0x30, 0x00, 0xb9, 0x70, 0x32, 0x00, 0x30, + 0x00, 0xb9, 0x70, 0x68, 0x50, 0x61, 0x64, 0x61, 0x41, 0x55, 0x62, 0x61, + 0x72, 0x6f, 0x56, 0x70, 0x63, 0x64, 0x6d, 0x64, 0x00, 0x6d, 0x00, 0xb2, + 0x00, 0x49, 0x00, 0x55, 0x00, 0x73, 0x5e, 0x10, 0x62, 0x2d, 0x66, 0x8c, + 0x54, 0x27, 0x59, 0x63, 0x6b, 0x0e, 0x66, 0xbb, 0x6c, 0x2a, 0x68, 0x0f, + 0x5f, 0x1a, 0x4f, 0x3e, 0x79, 0x70, 0x00, 0x41, 0x6e, 0x00, 0x41, 0xbc, + 0x03, 0x41, 0x6d, 0x00, 0x41, 0x6b, 0x00, 0x41, 0x4b, 0x00, 0x42, 0x4d, + 0x00, 0x42, 0x47, 0x00, 0x42, 0x63, 0x61, 0x6c, 0x6b, 0x63, 0x61, 0x6c, + 0x70, 0x00, 0x46, 0x6e, 0x00, 0x46, 0xbc, 0x03, 0x46, 0xbc, 0x03, 0x67, + 0x6d, 0x00, 0x67, 0x6b, 0x00, 0x67, 0x48, 0x00, 0x7a, 0x6b, 0x48, 0x7a, + 0x4d, 0x48, 0x7a, 0x47, 0x48, 0x7a, 0x54, 0x48, 0x7a, 0xbc, 0x03, 0x13, + 0x21, 0x6d, 0x00, 0x13, 0x21, 0x64, 0x00, 0x13, 0x21, 0x6b, 0x00, 0x13, + 0x21, 0x66, 0x00, 0x6d, 0x6e, 0x00, 0x6d, 0xbc, 0x03, 0x6d, 0x6d, 0x00, + 0x6d, 0x63, 0x00, 0x6d, 0x6b, 0x00, 0x6d, 0x63, 0x00, 0x0a, 0x0a, 0x4f, + 0x00, 0x0a, 0x4f, 0x6d, 0x00, 0xb2, 0x00, 0x63, 0x00, 0x08, 0x0a, 0x4f, + 0x0a, 0x0a, 0x50, 0x00, 0x0a, 0x50, 0x6d, 0x00, 0xb3, 0x00, 0x6b, 0x00, + 0x6d, 0x00, 0xb3, 0x00, 0x6d, 0x00, 0x15, 0x22, 0x73, 0x00, 0x6d, 0x00, + 0x15, 0x22, 0x73, 0x00, 0xb2, 0x00, 0x50, 0x61, 0x6b, 0x50, 0x61, 0x4d, + 0x50, 0x61, 0x47, 0x50, 0x61, 0x72, 0x61, 0x64, 0x72, 0x61, 0x64, 0xd1, + 0x73, 0x72, 0x00, 0x61, 0x00, 0x64, 0x00, 0x15, 0x22, 0x73, 0x00, 0xb2, + 0x00, 0x70, 0x00, 0x73, 0x6e, 0x00, 0x73, 0xbc, 0x03, 0x73, 0x6d, 0x00, + 0x73, 0x70, 0x00, 0x56, 0x6e, 0x00, 0x56, 0xbc, 0x03, 0x56, 0x6d, 0x00, + 0x56, 0x6b, 0x00, 0x56, 0x4d, 0x00, 0x56, 0x70, 0x00, 0x57, 0x6e, 0x00, + 0x57, 0xbc, 0x03, 0x57, 0x6d, 0x00, 0x57, 0x6b, 0x00, 0x57, 0x4d, 0x00, + 0x57, 0x6b, 0x00, 0xa9, 0x03, 0x4d, 0x00, 0xa9, 0x03, 0x61, 0x2e, 0x6d, + 0x2e, 0x42, 0x71, 0x63, 0x63, 0x63, 0x64, 0x43, 0xd1, 0x6b, 0x67, 0x43, + 0x6f, 0x2e, 0x64, 0x42, 0x47, 0x79, 0x68, 0x61, 0x48, 0x50, 0x69, 0x6e, + 0x4b, 0x4b, 0x4b, 0x4d, 0x6b, 0x74, 0x6c, 0x6d, 0x6c, 0x6e, 0x6c, 0x6f, + 0x67, 0x6c, 0x78, 0x6d, 0x62, 0x6d, 0x69, 0x6c, 0x6d, 0x6f, 0x6c, 0x50, + 0x48, 0x70, 0x2e, 0x6d, 0x2e, 0x50, 0x50, 0x4d, 0x50, 0x52, 0x73, 0x72, + 0x53, 0x76, 0x57, 0x62, 0x56, 0xd1, 0x6d, 0x41, 0xd1, 0x6d, 0x31, 0x00, + 0xe5, 0x65, 0x31, 0x00, 0x30, 0x00, 0xe5, 0x65, 0x32, 0x00, 0x30, 0x00, + 0xe5, 0x65, 0x33, 0x00, 0x30, 0x00, 0xe5, 0x65, 0x67, 0x61, 0x6c, 0x4a, + 0x04, 0x4c, 0x04, 0x26, 0x01, 0x53, 0x01, 0x27, 0xa7, 0x37, 0xab, 0x6b, + 0x02, 0x52, 0xab, 0x48, 0x8c, 0xf4, 0x66, 0xca, 0x8e, 0xc8, 0x8c, 0xd1, + 0x6e, 0x32, 0x4e, 0xe5, 0x53, 0x9c, 0x9f, 0x9c, 0x9f, 0x51, 0x59, 0xd1, + 0x91, 0x87, 0x55, 0x48, 0x59, 0xf6, 0x61, 0x69, 0x76, 0x85, 0x7f, 0x3f, + 0x86, 0xba, 0x87, 0xf8, 0x88, 0x8f, 0x90, 0x02, 0x6a, 0x1b, 0x6d, 0xd9, + 0x70, 0xde, 0x73, 0x3d, 0x84, 0x6a, 0x91, 0xf1, 0x99, 0x82, 0x4e, 0x75, + 0x53, 0x04, 0x6b, 0x1b, 0x72, 0x2d, 0x86, 0x1e, 0x9e, 0x50, 0x5d, 0xeb, + 0x6f, 0xcd, 0x85, 0x64, 0x89, 0xc9, 0x62, 0xd8, 0x81, 0x1f, 0x88, 0xca, + 0x5e, 0x17, 0x67, 0x6a, 0x6d, 0xfc, 0x72, 0xce, 0x90, 0x86, 0x4f, 0xb7, + 0x51, 0xde, 0x52, 0xc4, 0x64, 0xd3, 0x6a, 0x10, 0x72, 0xe7, 0x76, 0x01, + 0x80, 0x06, 0x86, 0x5c, 0x86, 0xef, 0x8d, 0x32, 0x97, 0x6f, 0x9b, 0xfa, + 0x9d, 0x8c, 0x78, 0x7f, 0x79, 0xa0, 0x7d, 0xc9, 0x83, 0x04, 0x93, 0x7f, + 0x9e, 0xd6, 0x8a, 0xdf, 0x58, 0x04, 0x5f, 0x60, 0x7c, 0x7e, 0x80, 0x62, + 0x72, 0xca, 0x78, 0xc2, 0x8c, 0xf7, 0x96, 0xd8, 0x58, 0x62, 0x5c, 0x13, + 0x6a, 0xda, 0x6d, 0x0f, 0x6f, 0x2f, 0x7d, 0x37, 0x7e, 0x4b, 0x96, 0xd2, + 0x52, 0x8b, 0x80, 0xdc, 0x51, 0xcc, 0x51, 0x1c, 0x7a, 0xbe, 0x7d, 0xf1, + 0x83, 0x75, 0x96, 0x80, 0x8b, 0xcf, 0x62, 0x02, 0x6a, 0xfe, 0x8a, 0x39, + 0x4e, 0xe7, 0x5b, 0x12, 0x60, 0x87, 0x73, 0x70, 0x75, 0x17, 0x53, 0xfb, + 0x78, 0xbf, 0x4f, 0xa9, 0x5f, 0x0d, 0x4e, 0xcc, 0x6c, 0x78, 0x65, 0x22, + 0x7d, 0xc3, 0x53, 0x5e, 0x58, 0x01, 0x77, 0x49, 0x84, 0xaa, 0x8a, 0xba, + 0x6b, 0xb0, 0x8f, 0x88, 0x6c, 0xfe, 0x62, 0xe5, 0x82, 0xa0, 0x63, 0x65, + 0x75, 0xae, 0x4e, 0x69, 0x51, 0xc9, 0x51, 0x81, 0x68, 0xe7, 0x7c, 0x6f, + 0x82, 0xd2, 0x8a, 0xcf, 0x91, 0xf5, 0x52, 0x42, 0x54, 0x73, 0x59, 0xec, + 0x5e, 0xc5, 0x65, 0xfe, 0x6f, 0x2a, 0x79, 0xad, 0x95, 0x6a, 0x9a, 0x97, + 0x9e, 0xce, 0x9e, 0x9b, 0x52, 0xc6, 0x66, 0x77, 0x6b, 0x62, 0x8f, 0x74, + 0x5e, 0x90, 0x61, 0x00, 0x62, 0x9a, 0x64, 0x23, 0x6f, 0x49, 0x71, 0x89, + 0x74, 0xca, 0x79, 0xf4, 0x7d, 0x6f, 0x80, 0x26, 0x8f, 0xee, 0x84, 0x23, + 0x90, 0x4a, 0x93, 0x17, 0x52, 0xa3, 0x52, 0xbd, 0x54, 0xc8, 0x70, 0xc2, + 0x88, 0xaa, 0x8a, 0xc9, 0x5e, 0xf5, 0x5f, 0x7b, 0x63, 0xae, 0x6b, 0x3e, + 0x7c, 0x75, 0x73, 0xe4, 0x4e, 0xf9, 0x56, 0xe7, 0x5b, 0xba, 0x5d, 0x1c, + 0x60, 0xb2, 0x73, 0x69, 0x74, 0x9a, 0x7f, 0x46, 0x80, 0x34, 0x92, 0xf6, + 0x96, 0x48, 0x97, 0x18, 0x98, 0x8b, 0x4f, 0xae, 0x79, 0xb4, 0x91, 0xb8, + 0x96, 0xe1, 0x60, 0x86, 0x4e, 0xda, 0x50, 0xee, 0x5b, 0x3f, 0x5c, 0x99, + 0x65, 0x02, 0x6a, 0xce, 0x71, 0x42, 0x76, 0xfc, 0x84, 0x7c, 0x90, 0x8d, + 0x9f, 0x88, 0x66, 0x2e, 0x96, 0x89, 0x52, 0x7b, 0x67, 0xf3, 0x67, 0x41, + 0x6d, 0x9c, 0x6e, 0x09, 0x74, 0x59, 0x75, 0x6b, 0x78, 0x10, 0x7d, 0x5e, + 0x98, 0x6d, 0x51, 0x2e, 0x62, 0x78, 0x96, 0x2b, 0x50, 0x19, 0x5d, 0xea, + 0x6d, 0x2a, 0x8f, 0x8b, 0x5f, 0x44, 0x61, 0x17, 0x68, 0x87, 0x73, 0x86, + 0x96, 0x29, 0x52, 0x0f, 0x54, 0x65, 0x5c, 0x13, 0x66, 0x4e, 0x67, 0xa8, + 0x68, 0xe5, 0x6c, 0x06, 0x74, 0xe2, 0x75, 0x79, 0x7f, 0xcf, 0x88, 0xe1, + 0x88, 0xcc, 0x91, 0xe2, 0x96, 0x3f, 0x53, 0xba, 0x6e, 0x1d, 0x54, 0xd0, + 0x71, 0x98, 0x74, 0xfa, 0x85, 0xa3, 0x96, 0x57, 0x9c, 0x9f, 0x9e, 0x97, + 0x67, 0xcb, 0x6d, 0xe8, 0x81, 0xcb, 0x7a, 0x20, 0x7b, 0x92, 0x7c, 0xc0, + 0x72, 0x99, 0x70, 0x58, 0x8b, 0xc0, 0x4e, 0x36, 0x83, 0x3a, 0x52, 0x07, + 0x52, 0xa6, 0x5e, 0xd3, 0x62, 0xd6, 0x7c, 0x85, 0x5b, 0x1e, 0x6d, 0xb4, + 0x66, 0x3b, 0x8f, 0x4c, 0x88, 0x4d, 0x96, 0x8b, 0x89, 0xd3, 0x5e, 0x40, + 0x51, 0xc0, 0x55, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x58, 0x00, 0x00, 0x74, + 0x66, 0x00, 0x00, 0x00, 0x00, 0xde, 0x51, 0x2a, 0x73, 0xca, 0x76, 0x3c, + 0x79, 0x5e, 0x79, 0x65, 0x79, 0x8f, 0x79, 0x56, 0x97, 0xbe, 0x7c, 0xbd, + 0x7f, 0x00, 0x00, 0x12, 0x86, 0x00, 0x00, 0xf8, 0x8a, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x90, 0xfd, 0x90, 0xef, 0x98, 0xfc, 0x98, 0x28, 0x99, 0xb4, + 0x9d, 0xde, 0x90, 0xb7, 0x96, 0xae, 0x4f, 0xe7, 0x50, 0x4d, 0x51, 0xc9, + 0x52, 0xe4, 0x52, 0x51, 0x53, 0x9d, 0x55, 0x06, 0x56, 0x68, 0x56, 0x40, + 0x58, 0xa8, 0x58, 0x64, 0x5c, 0x6e, 0x5c, 0x94, 0x60, 0x68, 0x61, 0x8e, + 0x61, 0xf2, 0x61, 0x4f, 0x65, 0xe2, 0x65, 0x91, 0x66, 0x85, 0x68, 0x77, + 0x6d, 0x1a, 0x6e, 0x22, 0x6f, 0x6e, 0x71, 0x2b, 0x72, 0x22, 0x74, 0x91, + 0x78, 0x3e, 0x79, 0x49, 0x79, 0x48, 0x79, 0x50, 0x79, 0x56, 0x79, 0x5d, + 0x79, 0x8d, 0x79, 0x8e, 0x79, 0x40, 0x7a, 0x81, 0x7a, 0xc0, 0x7b, 0xf4, + 0x7d, 0x09, 0x7e, 0x41, 0x7e, 0x72, 0x7f, 0x05, 0x80, 0xed, 0x81, 0x79, + 0x82, 0x79, 0x82, 0x57, 0x84, 0x10, 0x89, 0x96, 0x89, 0x01, 0x8b, 0x39, + 0x8b, 0xd3, 0x8c, 0x08, 0x8d, 0xb6, 0x8f, 0x38, 0x90, 0xe3, 0x96, 0xff, + 0x97, 0x3b, 0x98, 0x75, 0x60, 0xee, 0x42, 0x18, 0x82, 0x02, 0x26, 0x4e, + 0xb5, 0x51, 0x68, 0x51, 0x80, 0x4f, 0x45, 0x51, 0x80, 0x51, 0xc7, 0x52, + 0xfa, 0x52, 0x9d, 0x55, 0x55, 0x55, 0x99, 0x55, 0xe2, 0x55, 0x5a, 0x58, + 0xb3, 0x58, 0x44, 0x59, 0x54, 0x59, 0x62, 0x5a, 0x28, 0x5b, 0xd2, 0x5e, + 0xd9, 0x5e, 0x69, 0x5f, 0xad, 0x5f, 0xd8, 0x60, 0x4e, 0x61, 0x08, 0x61, + 0x8e, 0x61, 0x60, 0x61, 0xf2, 0x61, 0x34, 0x62, 0xc4, 0x63, 0x1c, 0x64, + 0x52, 0x64, 0x56, 0x65, 0x74, 0x66, 0x17, 0x67, 0x1b, 0x67, 0x56, 0x67, + 0x79, 0x6b, 0xba, 0x6b, 0x41, 0x6d, 0xdb, 0x6e, 0xcb, 0x6e, 0x22, 0x6f, + 0x1e, 0x70, 0x6e, 0x71, 0xa7, 0x77, 0x35, 0x72, 0xaf, 0x72, 0x2a, 0x73, + 0x71, 0x74, 0x06, 0x75, 0x3b, 0x75, 0x1d, 0x76, 0x1f, 0x76, 0xca, 0x76, + 0xdb, 0x76, 0xf4, 0x76, 0x4a, 0x77, 0x40, 0x77, 0xcc, 0x78, 0xb1, 0x7a, + 0xc0, 0x7b, 0x7b, 0x7c, 0x5b, 0x7d, 0xf4, 0x7d, 0x3e, 0x7f, 0x05, 0x80, + 0x52, 0x83, 0xef, 0x83, 0x79, 0x87, 0x41, 0x89, 0x86, 0x89, 0x96, 0x89, + 0xbf, 0x8a, 0xf8, 0x8a, 0xcb, 0x8a, 0x01, 0x8b, 0xfe, 0x8a, 0xed, 0x8a, + 0x39, 0x8b, 0x8a, 0x8b, 0x08, 0x8d, 0x38, 0x8f, 0x72, 0x90, 0x99, 0x91, + 0x76, 0x92, 0x7c, 0x96, 0xe3, 0x96, 0x56, 0x97, 0xdb, 0x97, 0xff, 0x97, + 0x0b, 0x98, 0x3b, 0x98, 0x12, 0x9b, 0x9c, 0x9f, 0x4a, 0x28, 0x44, 0x28, + 0xd5, 0x33, 0x9d, 0x3b, 0x18, 0x40, 0x39, 0x40, 0x49, 0x52, 0xd0, 0x5c, + 0xd3, 0x7e, 0x43, 0x9f, 0x8e, 0x9f, 0x2a, 0xa0, 0x02, 0x66, 0x66, 0x66, + 0x69, 0x66, 0x6c, 0x66, 0x66, 0x69, 0x66, 0x66, 0x6c, 0x7f, 0x01, 0x74, + 0x73, 0x00, 0x74, 0x65, 0x05, 0x0f, 0x11, 0x0f, 0x00, 0x0f, 0x06, 0x19, + 0x11, 0x0f, 0x08, 0xd9, 0x05, 0xb4, 0x05, 0x00, 0x00, 0x00, 0x00, 0xf2, + 0x05, 0xb7, 0x05, 0xd0, 0x05, 0x12, 0x00, 0x03, 0x04, 0x0b, 0x0c, 0x0d, + 0x18, 0x1a, 0xe9, 0x05, 0xc1, 0x05, 0xe9, 0x05, 0xc2, 0x05, 0x49, 0xfb, + 0xc1, 0x05, 0x49, 0xfb, 0xc2, 0x05, 0xd0, 0x05, 0xb7, 0x05, 0xd0, 0x05, + 0xb8, 0x05, 0xd0, 0x05, 0xbc, 0x05, 0xd8, 0x05, 0xbc, 0x05, 0xde, 0x05, + 0xbc, 0x05, 0xe0, 0x05, 0xbc, 0x05, 0xe3, 0x05, 0xbc, 0x05, 0xb9, 0x05, + 0x2d, 0x03, 0x2e, 0x03, 0x2f, 0x03, 0x30, 0x03, 0x31, 0x03, 0x1c, 0x00, + 0x18, 0x06, 0x22, 0x06, 0x2b, 0x06, 0xd0, 0x05, 0xdc, 0x05, 0x71, 0x06, + 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, 0x0f, 0x0f, + 0x0f, 0x0f, 0x09, 0x09, 0x09, 0x09, 0x0e, 0x0e, 0x0e, 0x0e, 0x08, 0x08, + 0x08, 0x08, 0x33, 0x33, 0x33, 0x33, 0x35, 0x35, 0x35, 0x35, 0x13, 0x13, + 0x13, 0x13, 0x12, 0x12, 0x12, 0x12, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, + 0x16, 0x16, 0x1c, 0x1c, 0x1b, 0x1b, 0x1d, 0x1d, 0x17, 0x17, 0x27, 0x27, + 0x20, 0x20, 0x38, 0x38, 0x38, 0x38, 0x3e, 0x3e, 0x3e, 0x3e, 0x42, 0x42, + 0x42, 0x42, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x4d, 0x4d, 0x4d, 0x4d, 0x61, 0x61, + 0x62, 0x62, 0x49, 0x06, 0x64, 0x64, 0x64, 0x64, 0x7e, 0x7e, 0x7d, 0x7d, + 0x7f, 0x7f, 0x2e, 0x82, 0x82, 0x7c, 0x7c, 0x80, 0x80, 0x87, 0x87, 0x87, + 0x87, 0x00, 0x00, 0x26, 0x06, 0x00, 0x01, 0x00, 0x01, 0x00, 0xaf, 0x00, + 0xaf, 0x00, 0x22, 0x00, 0x22, 0x00, 0xa1, 0x00, 0xa1, 0x00, 0xa0, 0x00, + 0xa0, 0x00, 0xa2, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, + 0x23, 0x00, 0x23, 0x00, 0x23, 0xcc, 0x06, 0x00, 0x00, 0x00, 0x00, 0x26, + 0x06, 0x00, 0x06, 0x00, 0x07, 0x00, 0x1f, 0x00, 0x23, 0x00, 0x24, 0x02, + 0x06, 0x02, 0x07, 0x02, 0x08, 0x02, 0x1f, 0x02, 0x23, 0x02, 0x24, 0x04, + 0x06, 0x04, 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x23, 0x04, 0x24, 0x05, + 0x06, 0x05, 0x1f, 0x05, 0x23, 0x05, 0x24, 0x06, 0x07, 0x06, 0x1f, 0x07, + 0x06, 0x07, 0x1f, 0x08, 0x06, 0x08, 0x07, 0x08, 0x1f, 0x0d, 0x06, 0x0d, + 0x07, 0x0d, 0x08, 0x0d, 0x1f, 0x0f, 0x07, 0x0f, 0x1f, 0x10, 0x06, 0x10, + 0x07, 0x10, 0x08, 0x10, 0x1f, 0x11, 0x07, 0x11, 0x1f, 0x12, 0x1f, 0x13, + 0x06, 0x13, 0x1f, 0x14, 0x06, 0x14, 0x1f, 0x1b, 0x06, 0x1b, 0x07, 0x1b, + 0x08, 0x1b, 0x1f, 0x1b, 0x23, 0x1b, 0x24, 0x1c, 0x07, 0x1c, 0x1f, 0x1c, + 0x23, 0x1c, 0x24, 0x1d, 0x01, 0x1d, 0x06, 0x1d, 0x07, 0x1d, 0x08, 0x1d, + 0x1e, 0x1d, 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x06, 0x1e, 0x07, 0x1e, + 0x08, 0x1e, 0x1f, 0x1e, 0x23, 0x1e, 0x24, 0x1f, 0x06, 0x1f, 0x07, 0x1f, + 0x08, 0x1f, 0x1f, 0x1f, 0x23, 0x1f, 0x24, 0x20, 0x06, 0x20, 0x07, 0x20, + 0x08, 0x20, 0x1f, 0x20, 0x23, 0x20, 0x24, 0x21, 0x06, 0x21, 0x1f, 0x21, + 0x23, 0x21, 0x24, 0x24, 0x06, 0x24, 0x07, 0x24, 0x08, 0x24, 0x1f, 0x24, + 0x23, 0x24, 0x24, 0x0a, 0x4a, 0x0b, 0x4a, 0x23, 0x4a, 0x20, 0x00, 0x4c, + 0x06, 0x51, 0x06, 0x51, 0x06, 0xff, 0x00, 0x1f, 0x26, 0x06, 0x00, 0x0b, + 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x20, 0x00, 0x23, 0x00, 0x24, 0x02, 0x0b, + 0x02, 0x0c, 0x02, 0x1f, 0x02, 0x20, 0x02, 0x23, 0x02, 0x24, 0x04, 0x0b, + 0x04, 0x0c, 0x04, 0x1f, 0x26, 0x06, 0x04, 0x20, 0x04, 0x23, 0x04, 0x24, + 0x05, 0x0b, 0x05, 0x0c, 0x05, 0x1f, 0x05, 0x20, 0x05, 0x23, 0x05, 0x24, + 0x1b, 0x23, 0x1b, 0x24, 0x1c, 0x23, 0x1c, 0x24, 0x1d, 0x01, 0x1d, 0x1e, + 0x1d, 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x1f, 0x1e, 0x23, 0x1e, 0x24, + 0x1f, 0x01, 0x1f, 0x1f, 0x20, 0x0b, 0x20, 0x0c, 0x20, 0x1f, 0x20, 0x20, + 0x20, 0x23, 0x20, 0x24, 0x23, 0x4a, 0x24, 0x0b, 0x24, 0x0c, 0x24, 0x1f, + 0x24, 0x20, 0x24, 0x23, 0x24, 0x24, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, + 0x00, 0x1f, 0x00, 0x21, 0x02, 0x06, 0x02, 0x07, 0x02, 0x08, 0x02, 0x1f, + 0x02, 0x21, 0x04, 0x06, 0x04, 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x21, + 0x05, 0x1f, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06, 0x07, 0x1f, 0x08, 0x06, + 0x08, 0x1f, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x08, 0x0d, 0x1f, 0x0f, 0x07, + 0x0f, 0x08, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07, 0x10, 0x08, 0x10, 0x1f, + 0x11, 0x07, 0x12, 0x1f, 0x13, 0x06, 0x13, 0x1f, 0x14, 0x06, 0x14, 0x1f, + 0x1b, 0x06, 0x1b, 0x07, 0x1b, 0x08, 0x1b, 0x1f, 0x1c, 0x07, 0x1c, 0x1f, + 0x1d, 0x06, 0x1d, 0x07, 0x1d, 0x08, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x06, + 0x1e, 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x21, 0x1f, 0x06, 0x1f, 0x07, + 0x1f, 0x08, 0x1f, 0x1f, 0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, + 0x20, 0x21, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x4a, 0x24, 0x06, 0x24, 0x07, + 0x24, 0x08, 0x24, 0x1f, 0x24, 0x21, 0x00, 0x1f, 0x00, 0x21, 0x02, 0x1f, + 0x02, 0x21, 0x04, 0x1f, 0x04, 0x21, 0x05, 0x1f, 0x05, 0x21, 0x0d, 0x1f, + 0x0d, 0x21, 0x0e, 0x1f, 0x0e, 0x21, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x1f, + 0x20, 0x1f, 0x20, 0x21, 0x24, 0x1f, 0x24, 0x21, 0x40, 0x06, 0x4e, 0x06, + 0x51, 0x06, 0x27, 0x06, 0x10, 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, + 0x13, 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, 0x0d, 0x22, 0x0d, 0x23, + 0x06, 0x22, 0x06, 0x23, 0x05, 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, + 0x0e, 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, 0x0d, 0x05, 0x0d, 0x06, + 0x0d, 0x07, 0x0d, 0x1e, 0x0d, 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, + 0x10, 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, 0x13, 0x22, 0x13, 0x23, + 0x0c, 0x22, 0x0c, 0x23, 0x0d, 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, + 0x05, 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, 0x0e, 0x22, 0x0e, 0x23, + 0x0f, 0x22, 0x0f, 0x23, 0x0d, 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, + 0x0d, 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x0d, 0x05, 0x0d, 0x06, + 0x0d, 0x07, 0x0d, 0x1e, 0x0c, 0x20, 0x0d, 0x20, 0x10, 0x1e, 0x0c, 0x05, + 0x0c, 0x06, 0x0c, 0x07, 0x0d, 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x10, 0x1e, + 0x11, 0x1e, 0x00, 0x24, 0x00, 0x24, 0x2a, 0x06, 0x00, 0x02, 0x1b, 0x00, + 0x03, 0x02, 0x00, 0x03, 0x02, 0x00, 0x03, 0x1b, 0x00, 0x04, 0x1b, 0x00, + 0x1b, 0x02, 0x00, 0x1b, 0x03, 0x00, 0x1b, 0x04, 0x02, 0x1b, 0x03, 0x02, + 0x1b, 0x03, 0x03, 0x1b, 0x20, 0x03, 0x1b, 0x1f, 0x09, 0x03, 0x02, 0x09, + 0x02, 0x03, 0x09, 0x02, 0x1f, 0x09, 0x1b, 0x03, 0x09, 0x1b, 0x03, 0x09, + 0x1b, 0x02, 0x09, 0x1b, 0x1b, 0x09, 0x1b, 0x1b, 0x0b, 0x03, 0x03, 0x0b, + 0x03, 0x03, 0x0b, 0x1b, 0x1b, 0x0a, 0x03, 0x1b, 0x0a, 0x03, 0x1b, 0x0a, + 0x02, 0x20, 0x0a, 0x1b, 0x04, 0x0a, 0x1b, 0x04, 0x0a, 0x1b, 0x1b, 0x0a, + 0x1b, 0x1b, 0x0c, 0x03, 0x1f, 0x0c, 0x04, 0x1b, 0x0c, 0x04, 0x1b, 0x0d, + 0x1b, 0x03, 0x0d, 0x1b, 0x03, 0x0d, 0x1b, 0x1b, 0x0d, 0x1b, 0x20, 0x0f, + 0x02, 0x1b, 0x0f, 0x1b, 0x1b, 0x0f, 0x1b, 0x1b, 0x0f, 0x1b, 0x1f, 0x10, + 0x1b, 0x1b, 0x10, 0x1b, 0x20, 0x10, 0x1b, 0x1f, 0x17, 0x04, 0x1b, 0x17, + 0x04, 0x1b, 0x18, 0x1b, 0x03, 0x18, 0x1b, 0x1b, 0x1a, 0x03, 0x1b, 0x1a, + 0x03, 0x20, 0x1a, 0x03, 0x1f, 0x1a, 0x02, 0x02, 0x1a, 0x02, 0x02, 0x1a, + 0x04, 0x1b, 0x1a, 0x04, 0x1b, 0x1a, 0x1b, 0x03, 0x1a, 0x1b, 0x03, 0x1b, + 0x03, 0x02, 0x1b, 0x03, 0x1b, 0x1b, 0x03, 0x20, 0x1b, 0x02, 0x03, 0x1b, + 0x02, 0x1b, 0x1b, 0x04, 0x02, 0x1b, 0x04, 0x1b, 0x28, 0x06, 0x1d, 0x04, + 0x06, 0x1f, 0x1d, 0x04, 0x1f, 0x1d, 0x1d, 0x1e, 0x05, 0x1d, 0x1e, 0x05, + 0x21, 0x1e, 0x04, 0x1d, 0x1e, 0x04, 0x1d, 0x1e, 0x04, 0x21, 0x1e, 0x1d, + 0x22, 0x1e, 0x1d, 0x21, 0x22, 0x1d, 0x1d, 0x22, 0x1d, 0x1d, 0x00, 0x06, + 0x22, 0x02, 0x04, 0x22, 0x02, 0x04, 0x21, 0x02, 0x06, 0x22, 0x02, 0x06, + 0x21, 0x02, 0x1d, 0x22, 0x02, 0x1d, 0x21, 0x04, 0x1d, 0x22, 0x04, 0x05, + 0x21, 0x04, 0x1d, 0x21, 0x0b, 0x06, 0x21, 0x0d, 0x05, 0x22, 0x0c, 0x05, + 0x22, 0x0e, 0x05, 0x22, 0x1c, 0x04, 0x22, 0x1c, 0x1d, 0x22, 0x22, 0x05, + 0x22, 0x22, 0x04, 0x22, 0x22, 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1a, 0x1d, + 0x22, 0x1e, 0x05, 0x22, 0x1a, 0x1d, 0x05, 0x1c, 0x05, 0x1d, 0x11, 0x1d, + 0x22, 0x1b, 0x1d, 0x22, 0x1e, 0x04, 0x05, 0x1d, 0x06, 0x22, 0x1c, 0x04, + 0x1d, 0x1b, 0x1d, 0x1d, 0x1c, 0x04, 0x1d, 0x1e, 0x04, 0x05, 0x04, 0x05, + 0x22, 0x05, 0x04, 0x22, 0x1d, 0x04, 0x22, 0x19, 0x1d, 0x22, 0x00, 0x05, + 0x22, 0x1b, 0x1d, 0x1d, 0x11, 0x04, 0x1d, 0x0d, 0x1d, 0x1d, 0x0b, 0x06, + 0x22, 0x1e, 0x04, 0x22, 0x35, 0x06, 0x00, 0x0f, 0x9d, 0x0d, 0x0f, 0x9d, + 0x27, 0x06, 0x00, 0x1d, 0x1d, 0x20, 0x00, 0x1c, 0x01, 0x0a, 0x1e, 0x06, + 0x1e, 0x08, 0x0e, 0x1d, 0x12, 0x1e, 0x0a, 0x0c, 0x21, 0x1d, 0x12, 0x1d, + 0x23, 0x20, 0x21, 0x0c, 0x1d, 0x1e, 0x35, 0x06, 0x00, 0x0f, 0x14, 0x27, + 0x06, 0x0e, 0x1d, 0x22, 0xff, 0x00, 0x1d, 0x1d, 0x20, 0xff, 0x12, 0x1d, + 0x23, 0x20, 0xff, 0x21, 0x0c, 0x1d, 0x1e, 0x27, 0x06, 0x05, 0x1d, 0xff, + 0x05, 0x1d, 0x00, 0x1d, 0x20, 0x27, 0x06, 0x0a, 0xa5, 0x00, 0x1d, 0x2c, + 0x00, 0x01, 0x30, 0x02, 0x30, 0x3a, 0x00, 0x3b, 0x00, 0x21, 0x00, 0x3f, + 0x00, 0x16, 0x30, 0x17, 0x30, 0x26, 0x20, 0x13, 0x20, 0x12, 0x01, 0x00, + 0x5f, 0x5f, 0x28, 0x29, 0x7b, 0x7d, 0x08, 0x30, 0x0c, 0x0d, 0x08, 0x09, + 0x02, 0x03, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07, 0x5b, 0x00, 0x5d, 0x00, + 0x3e, 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x5f, 0x00, 0x5f, 0x00, + 0x5f, 0x00, 0x2c, 0x00, 0x01, 0x30, 0x2e, 0x00, 0x00, 0x00, 0x3b, 0x00, + 0x3a, 0x00, 0x3f, 0x00, 0x21, 0x00, 0x14, 0x20, 0x28, 0x00, 0x29, 0x00, + 0x7b, 0x00, 0x7d, 0x00, 0x14, 0x30, 0x15, 0x30, 0x23, 0x26, 0x2a, 0x2b, + 0x2d, 0x3c, 0x3e, 0x3d, 0x00, 0x5c, 0x24, 0x25, 0x40, 0x40, 0x06, 0xff, + 0x0b, 0x00, 0x0b, 0xff, 0x0c, 0x20, 0x00, 0x4d, 0x06, 0x40, 0x06, 0xff, + 0x0e, 0x00, 0x0e, 0xff, 0x0f, 0x00, 0x0f, 0xff, 0x10, 0x00, 0x10, 0xff, + 0x11, 0x00, 0x11, 0xff, 0x12, 0x00, 0x12, 0x21, 0x06, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, + 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, + 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, + 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, + 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, + 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, + 0x28, 0x28, 0x29, 0x29, 0x29, 0x29, 0x22, 0x06, 0x22, 0x00, 0x22, 0x00, + 0x22, 0x01, 0x22, 0x01, 0x22, 0x03, 0x22, 0x03, 0x22, 0x05, 0x22, 0x05, + 0x21, 0x00, 0x85, 0x29, 0x01, 0x30, 0x01, 0x0b, 0x0c, 0x00, 0xfa, 0xf1, + 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xe2, 0xe4, 0xe6, 0xc2, 0xfb, 0xa1, 0xa3, + 0xa5, 0xa7, 0xa9, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, + 0xbc, 0xbe, 0xc0, 0xc3, 0xc5, 0xc7, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, + 0xd1, 0xd4, 0xd7, 0xda, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe3, 0xe5, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xee, 0xf2, 0x98, 0x99, 0x31, 0x31, 0x4f, + 0x31, 0x55, 0x31, 0x5b, 0x31, 0x61, 0x31, 0xa2, 0x00, 0xa3, 0x00, 0xac, + 0x00, 0xaf, 0x00, 0xa6, 0x00, 0xa5, 0x00, 0xa9, 0x20, 0x00, 0x00, 0x02, + 0x25, 0x90, 0x21, 0x91, 0x21, 0x92, 0x21, 0x93, 0x21, 0xa0, 0x25, 0xcb, + 0x25, 0x99, 0x10, 0xba, 0x10, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x10, 0xba, + 0x10, 0x05, 0x05, 0xa5, 0x10, 0xba, 0x10, 0x05, 0x31, 0x11, 0x27, 0x11, + 0x32, 0x11, 0x27, 0x11, 0x55, 0x47, 0x13, 0x3e, 0x13, 0x47, 0x13, 0x57, + 0x13, 0x55, 0xb9, 0x14, 0xba, 0x14, 0xb9, 0x14, 0xb0, 0x14, 0x00, 0x00, + 0x00, 0x00, 0xb9, 0x14, 0xbd, 0x14, 0x55, 0x50, 0xb8, 0x15, 0xaf, 0x15, + 0xb9, 0x15, 0xaf, 0x15, 0x55, 0x57, 0xd1, 0x65, 0xd1, 0x58, 0xd1, 0x65, + 0xd1, 0x5f, 0xd1, 0x6e, 0xd1, 0x5f, 0xd1, 0x6f, 0xd1, 0x5f, 0xd1, 0x70, + 0xd1, 0x5f, 0xd1, 0x71, 0xd1, 0x5f, 0xd1, 0x72, 0xd1, 0x55, 0x55, 0x55, + 0x05, 0xb9, 0xd1, 0x65, 0xd1, 0xba, 0xd1, 0x65, 0xd1, 0xbb, 0xd1, 0x6e, + 0xd1, 0xbc, 0xd1, 0x6e, 0xd1, 0xbb, 0xd1, 0x6f, 0xd1, 0xbc, 0xd1, 0x6f, + 0xd1, 0x55, 0x55, 0x55, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, + 0x69, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x43, 0x44, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x4a, 0x4b, 0x00, 0x00, 0x4e, 0x4f, 0x50, 0x51, 0x00, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, + 0x00, 0x66, 0x68, 0x00, 0x70, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x42, + 0x00, 0x44, 0x45, 0x46, 0x47, 0x4a, 0x00, 0x53, 0x00, 0x61, 0x00, 0x41, + 0x42, 0x00, 0x44, 0x45, 0x46, 0x47, 0x00, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, + 0x00, 0x4f, 0x53, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, + 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, + 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x31, 0x01, 0x37, 0x02, 0x91, 0x03, + 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, + 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, + 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, + 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, + 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, + 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x0b, 0x0c, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x27, 0x06, 0x00, 0x01, + 0x05, 0x08, 0x2a, 0x06, 0x1e, 0x08, 0x03, 0x0d, 0x20, 0x19, 0x1a, 0x1b, + 0x1c, 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, 0x06, + 0x0c, 0x0e, 0x10, 0x44, 0x90, 0x77, 0x45, 0x28, 0x06, 0x2c, 0x06, 0x00, + 0x00, 0x47, 0x06, 0x33, 0x06, 0x17, 0x10, 0x11, 0x12, 0x13, 0x00, 0x06, + 0x0e, 0x02, 0x0f, 0x34, 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, 0x00, + 0x00, 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, 0x2d, 0x06, 0x00, 0x00, 0x4a, + 0x06, 0x00, 0x00, 0x44, 0x06, 0x00, 0x00, 0x46, 0x06, 0x33, 0x06, 0x39, + 0x06, 0x00, 0x00, 0x35, 0x06, 0x42, 0x06, 0x00, 0x00, 0x34, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x2e, 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x3a, + 0x06, 0x00, 0x00, 0xba, 0x06, 0x00, 0x00, 0x6f, 0x06, 0x00, 0x00, 0x28, + 0x06, 0x2c, 0x06, 0x00, 0x00, 0x47, 0x06, 0x00, 0x00, 0x00, 0x00, 0x2d, + 0x06, 0x37, 0x06, 0x4a, 0x06, 0x43, 0x06, 0x00, 0x00, 0x45, 0x06, 0x46, + 0x06, 0x33, 0x06, 0x39, 0x06, 0x41, 0x06, 0x35, 0x06, 0x42, 0x06, 0x00, + 0x00, 0x34, 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, 0x00, 0x00, 0x36, + 0x06, 0x38, 0x06, 0x3a, 0x06, 0x6e, 0x06, 0x00, 0x00, 0xa1, 0x06, 0x27, + 0x06, 0x00, 0x01, 0x05, 0x08, 0x20, 0x21, 0x0b, 0x06, 0x10, 0x23, 0x2a, + 0x06, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, + 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x28, 0x06, 0x2c, 0x06, 0x2f, 0x06, + 0x00, 0x00, 0x48, 0x06, 0x32, 0x06, 0x2d, 0x06, 0x37, 0x06, 0x4a, 0x06, + 0x2a, 0x06, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, + 0x00, 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x30, 0x2e, 0x30, 0x00, 0x2c, + 0x00, 0x28, 0x00, 0x41, 0x00, 0x29, 0x00, 0x14, 0x30, 0x53, 0x00, 0x15, + 0x30, 0x43, 0x52, 0x43, 0x44, 0x57, 0x5a, 0x41, 0x00, 0x48, 0x56, 0x4d, + 0x56, 0x53, 0x44, 0x53, 0x53, 0x50, 0x50, 0x56, 0x57, 0x43, 0x4d, 0x43, + 0x4d, 0x44, 0x4d, 0x52, 0x44, 0x4a, 0x4b, 0x30, 0x30, 0x00, 0x68, 0x68, + 0x4b, 0x62, 0x57, 0x5b, 0xcc, 0x53, 0xc7, 0x30, 0x8c, 0x4e, 0x1a, 0x59, + 0xe3, 0x89, 0x29, 0x59, 0xa4, 0x4e, 0x20, 0x66, 0x21, 0x71, 0x99, 0x65, + 0x4d, 0x52, 0x8c, 0x5f, 0x8d, 0x51, 0xb0, 0x65, 0x1d, 0x52, 0x42, 0x7d, + 0x1f, 0x75, 0xa9, 0x8c, 0xf0, 0x58, 0x39, 0x54, 0x14, 0x6f, 0x95, 0x62, + 0x55, 0x63, 0x00, 0x4e, 0x09, 0x4e, 0x4a, 0x90, 0xe6, 0x5d, 0x2d, 0x4e, + 0xf3, 0x53, 0x07, 0x63, 0x70, 0x8d, 0x53, 0x62, 0x81, 0x79, 0x7a, 0x7a, + 0x08, 0x54, 0x80, 0x6e, 0x09, 0x67, 0x08, 0x67, 0x33, 0x75, 0x72, 0x52, + 0xb6, 0x55, 0x4d, 0x91, 0x14, 0x30, 0x15, 0x30, 0x2c, 0x67, 0x09, 0x4e, + 0x8c, 0x4e, 0x89, 0x5b, 0xb9, 0x70, 0x53, 0x62, 0xd7, 0x76, 0xdd, 0x52, + 0x57, 0x65, 0x97, 0x5f, 0xef, 0x53, 0x38, 0x4e, 0x05, 0x00, 0x09, 0x22, + 0x01, 0x60, 0x4f, 0xae, 0x4f, 0xbb, 0x4f, 0x02, 0x50, 0x7a, 0x50, 0x99, + 0x50, 0xe7, 0x50, 0xcf, 0x50, 0x9e, 0x34, 0x3a, 0x06, 0x4d, 0x51, 0x54, + 0x51, 0x64, 0x51, 0x77, 0x51, 0x1c, 0x05, 0xb9, 0x34, 0x67, 0x51, 0x8d, + 0x51, 0x4b, 0x05, 0x97, 0x51, 0xa4, 0x51, 0xcc, 0x4e, 0xac, 0x51, 0xb5, + 0x51, 0xdf, 0x91, 0xf5, 0x51, 0x03, 0x52, 0xdf, 0x34, 0x3b, 0x52, 0x46, + 0x52, 0x72, 0x52, 0x77, 0x52, 0x15, 0x35, 0x02, 0x00, 0x20, 0x80, 0x80, + 0x00, 0x08, 0x00, 0x00, 0xc7, 0x52, 0x00, 0x02, 0x1d, 0x33, 0x3e, 0x3f, + 0x50, 0x82, 0x8a, 0x93, 0xac, 0xb6, 0xb8, 0xb8, 0xb8, 0x2c, 0x0a, 0x70, + 0x70, 0xca, 0x53, 0xdf, 0x53, 0x63, 0x0b, 0xeb, 0x53, 0xf1, 0x53, 0x06, + 0x54, 0x9e, 0x54, 0x38, 0x54, 0x48, 0x54, 0x68, 0x54, 0xa2, 0x54, 0xf6, + 0x54, 0x10, 0x55, 0x53, 0x55, 0x63, 0x55, 0x84, 0x55, 0x84, 0x55, 0x99, + 0x55, 0xab, 0x55, 0xb3, 0x55, 0xc2, 0x55, 0x16, 0x57, 0x06, 0x56, 0x17, + 0x57, 0x51, 0x56, 0x74, 0x56, 0x07, 0x52, 0xee, 0x58, 0xce, 0x57, 0xf4, + 0x57, 0x0d, 0x58, 0x8b, 0x57, 0x32, 0x58, 0x31, 0x58, 0xac, 0x58, 0xe4, + 0x14, 0xf2, 0x58, 0xf7, 0x58, 0x06, 0x59, 0x1a, 0x59, 0x22, 0x59, 0x62, + 0x59, 0xa8, 0x16, 0xea, 0x16, 0xec, 0x59, 0x1b, 0x5a, 0x27, 0x5a, 0xd8, + 0x59, 0x66, 0x5a, 0xee, 0x36, 0xfc, 0x36, 0x08, 0x5b, 0x3e, 0x5b, 0x3e, + 0x5b, 0xc8, 0x19, 0xc3, 0x5b, 0xd8, 0x5b, 0xe7, 0x5b, 0xf3, 0x5b, 0x18, + 0x1b, 0xff, 0x5b, 0x06, 0x5c, 0x53, 0x5f, 0x22, 0x5c, 0x81, 0x37, 0x60, + 0x5c, 0x6e, 0x5c, 0xc0, 0x5c, 0x8d, 0x5c, 0xe4, 0x1d, 0x43, 0x5d, 0xe6, + 0x1d, 0x6e, 0x5d, 0x6b, 0x5d, 0x7c, 0x5d, 0xe1, 0x5d, 0xe2, 0x5d, 0x2f, + 0x38, 0xfd, 0x5d, 0x28, 0x5e, 0x3d, 0x5e, 0x69, 0x5e, 0x62, 0x38, 0x83, + 0x21, 0x7c, 0x38, 0xb0, 0x5e, 0xb3, 0x5e, 0xb6, 0x5e, 0xca, 0x5e, 0x92, + 0xa3, 0xfe, 0x5e, 0x31, 0x23, 0x31, 0x23, 0x01, 0x82, 0x22, 0x5f, 0x22, + 0x5f, 0xc7, 0x38, 0xb8, 0x32, 0xda, 0x61, 0x62, 0x5f, 0x6b, 0x5f, 0xe3, + 0x38, 0x9a, 0x5f, 0xcd, 0x5f, 0xd7, 0x5f, 0xf9, 0x5f, 0x81, 0x60, 0x3a, + 0x39, 0x1c, 0x39, 0x94, 0x60, 0xd4, 0x26, 0xc7, 0x60, 0x02, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x00, 0x02, + 0x08, 0x00, 0x80, 0x08, 0x00, 0x00, 0x08, 0x80, 0x28, 0x80, 0x02, 0x00, + 0x00, 0x02, 0x48, 0x61, 0x00, 0x04, 0x06, 0x04, 0x32, 0x46, 0x6a, 0x5c, + 0x67, 0x96, 0xaa, 0xae, 0xc8, 0xd3, 0x5d, 0x62, 0x00, 0x54, 0x77, 0xf3, + 0x0c, 0x2b, 0x3d, 0x63, 0xfc, 0x62, 0x68, 0x63, 0x83, 0x63, 0xe4, 0x63, + 0xf1, 0x2b, 0x22, 0x64, 0xc5, 0x63, 0xa9, 0x63, 0x2e, 0x3a, 0x69, 0x64, + 0x7e, 0x64, 0x9d, 0x64, 0x77, 0x64, 0x6c, 0x3a, 0x4f, 0x65, 0x6c, 0x65, + 0x0a, 0x30, 0xe3, 0x65, 0xf8, 0x66, 0x49, 0x66, 0x19, 0x3b, 0x91, 0x66, + 0x08, 0x3b, 0xe4, 0x3a, 0x92, 0x51, 0x95, 0x51, 0x00, 0x67, 0x9c, 0x66, + 0xad, 0x80, 0xd9, 0x43, 0x17, 0x67, 0x1b, 0x67, 0x21, 0x67, 0x5e, 0x67, + 0x53, 0x67, 0xc3, 0x33, 0x49, 0x3b, 0xfa, 0x67, 0x85, 0x67, 0x52, 0x68, + 0x85, 0x68, 0x6d, 0x34, 0x8e, 0x68, 0x1f, 0x68, 0x14, 0x69, 0x9d, 0x3b, + 0x42, 0x69, 0xa3, 0x69, 0xea, 0x69, 0xa8, 0x6a, 0xa3, 0x36, 0xdb, 0x6a, + 0x18, 0x3c, 0x21, 0x6b, 0xa7, 0x38, 0x54, 0x6b, 0x4e, 0x3c, 0x72, 0x6b, + 0x9f, 0x6b, 0xba, 0x6b, 0xbb, 0x6b, 0x8d, 0x3a, 0x0b, 0x1d, 0xfa, 0x3a, + 0x4e, 0x6c, 0xbc, 0x3c, 0xbf, 0x6c, 0xcd, 0x6c, 0x67, 0x6c, 0x16, 0x6d, + 0x3e, 0x6d, 0x77, 0x6d, 0x41, 0x6d, 0x69, 0x6d, 0x78, 0x6d, 0x85, 0x6d, + 0x1e, 0x3d, 0x34, 0x6d, 0x2f, 0x6e, 0x6e, 0x6e, 0x33, 0x3d, 0xcb, 0x6e, + 0xc7, 0x6e, 0xd1, 0x3e, 0xf9, 0x6d, 0x6e, 0x6f, 0x5e, 0x3f, 0x8e, 0x3f, + 0xc6, 0x6f, 0x39, 0x70, 0x1e, 0x70, 0x1b, 0x70, 0x96, 0x3d, 0x4a, 0x70, + 0x7d, 0x70, 0x77, 0x70, 0xad, 0x70, 0x25, 0x05, 0x45, 0x71, 0x63, 0x42, + 0x9c, 0x71, 0xab, 0x43, 0x28, 0x72, 0x35, 0x72, 0x50, 0x72, 0x08, 0x46, + 0x80, 0x72, 0x95, 0x72, 0x35, 0x47, 0x02, 0x20, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x02, 0x02, 0x80, 0x8a, 0x00, + 0x00, 0x20, 0x00, 0x08, 0x0a, 0x00, 0x80, 0x88, 0x80, 0x20, 0x14, 0x48, + 0x7a, 0x73, 0x8b, 0x73, 0xac, 0x3e, 0xa5, 0x73, 0xb8, 0x3e, 0xb8, 0x3e, + 0x47, 0x74, 0x5c, 0x74, 0x71, 0x74, 0x85, 0x74, 0xca, 0x74, 0x1b, 0x3f, + 0x24, 0x75, 0x36, 0x4c, 0x3e, 0x75, 0x92, 0x4c, 0x70, 0x75, 0x9f, 0x21, + 0x10, 0x76, 0xa1, 0x4f, 0xb8, 0x4f, 0x44, 0x50, 0xfc, 0x3f, 0x08, 0x40, + 0xf4, 0x76, 0xf3, 0x50, 0xf2, 0x50, 0x19, 0x51, 0x33, 0x51, 0x1e, 0x77, + 0x1f, 0x77, 0x1f, 0x77, 0x4a, 0x77, 0x39, 0x40, 0x8b, 0x77, 0x46, 0x40, + 0x96, 0x40, 0x1d, 0x54, 0x4e, 0x78, 0x8c, 0x78, 0xcc, 0x78, 0xe3, 0x40, + 0x26, 0x56, 0x56, 0x79, 0x9a, 0x56, 0xc5, 0x56, 0x8f, 0x79, 0xeb, 0x79, + 0x2f, 0x41, 0x40, 0x7a, 0x4a, 0x7a, 0x4f, 0x7a, 0x7c, 0x59, 0xa7, 0x5a, + 0xa7, 0x5a, 0xee, 0x7a, 0x02, 0x42, 0xab, 0x5b, 0xc6, 0x7b, 0xc9, 0x7b, + 0x27, 0x42, 0x80, 0x5c, 0xd2, 0x7c, 0xa0, 0x42, 0xe8, 0x7c, 0xe3, 0x7c, + 0x00, 0x7d, 0x86, 0x5f, 0x63, 0x7d, 0x01, 0x43, 0xc7, 0x7d, 0x02, 0x7e, + 0x45, 0x7e, 0x34, 0x43, 0x28, 0x62, 0x47, 0x62, 0x59, 0x43, 0xd9, 0x62, + 0x7a, 0x7f, 0x3e, 0x63, 0x95, 0x7f, 0xfa, 0x7f, 0x05, 0x80, 0xda, 0x64, + 0x23, 0x65, 0x60, 0x80, 0xa8, 0x65, 0x70, 0x80, 0x5f, 0x33, 0xd5, 0x43, + 0xb2, 0x80, 0x03, 0x81, 0x0b, 0x44, 0x3e, 0x81, 0xb5, 0x5a, 0xa7, 0x67, + 0xb5, 0x67, 0x93, 0x33, 0x9c, 0x33, 0x01, 0x82, 0x04, 0x82, 0x9e, 0x8f, + 0x6b, 0x44, 0x91, 0x82, 0x8b, 0x82, 0x9d, 0x82, 0xb3, 0x52, 0xb1, 0x82, + 0xb3, 0x82, 0xbd, 0x82, 0xe6, 0x82, 0x3c, 0x6b, 0xe5, 0x82, 0x1d, 0x83, + 0x63, 0x83, 0xad, 0x83, 0x23, 0x83, 0xbd, 0x83, 0xe7, 0x83, 0x57, 0x84, + 0x53, 0x83, 0xca, 0x83, 0xcc, 0x83, 0xdc, 0x83, 0x36, 0x6c, 0x6b, 0x6d, + 0x02, 0x00, 0x00, 0x20, 0x22, 0x2a, 0xa0, 0x0a, 0x00, 0x20, 0x80, 0x28, + 0x00, 0xa8, 0x20, 0x20, 0x00, 0x02, 0x80, 0x22, 0x02, 0x8a, 0x08, 0x00, + 0xaa, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x28, 0xd5, 0x6c, 0x2b, 0x45, + 0xf1, 0x84, 0xf3, 0x84, 0x16, 0x85, 0xca, 0x73, 0x64, 0x85, 0x2c, 0x6f, + 0x5d, 0x45, 0x61, 0x45, 0xb1, 0x6f, 0xd2, 0x70, 0x6b, 0x45, 0x50, 0x86, + 0x5c, 0x86, 0x67, 0x86, 0x69, 0x86, 0xa9, 0x86, 0x88, 0x86, 0x0e, 0x87, + 0xe2, 0x86, 0x79, 0x87, 0x28, 0x87, 0x6b, 0x87, 0x86, 0x87, 0xd7, 0x45, + 0xe1, 0x87, 0x01, 0x88, 0xf9, 0x45, 0x60, 0x88, 0x63, 0x88, 0x67, 0x76, + 0xd7, 0x88, 0xde, 0x88, 0x35, 0x46, 0xfa, 0x88, 0xbb, 0x34, 0xae, 0x78, + 0x66, 0x79, 0xbe, 0x46, 0xc7, 0x46, 0xa0, 0x8a, 0xed, 0x8a, 0x8a, 0x8b, + 0x55, 0x8c, 0xa8, 0x7c, 0xab, 0x8c, 0xc1, 0x8c, 0x1b, 0x8d, 0x77, 0x8d, + 0x2f, 0x7f, 0x04, 0x08, 0xcb, 0x8d, 0xbc, 0x8d, 0xf0, 0x8d, 0xde, 0x08, + 0xd4, 0x8e, 0x38, 0x8f, 0xd2, 0x85, 0xed, 0x85, 0x94, 0x90, 0xf1, 0x90, + 0x11, 0x91, 0x2e, 0x87, 0x1b, 0x91, 0x38, 0x92, 0xd7, 0x92, 0xd8, 0x92, + 0x7c, 0x92, 0xf9, 0x93, 0x15, 0x94, 0xfa, 0x8b, 0x8b, 0x95, 0x95, 0x49, + 0xb7, 0x95, 0x77, 0x8d, 0xe6, 0x49, 0xc3, 0x96, 0xb2, 0x5d, 0x23, 0x97, + 0x45, 0x91, 0x1a, 0x92, 0x6e, 0x4a, 0x76, 0x4a, 0xe0, 0x97, 0x0a, 0x94, + 0xb2, 0x4a, 0x96, 0x94, 0x0b, 0x98, 0x0b, 0x98, 0x29, 0x98, 0xb6, 0x95, + 0xe2, 0x98, 0x33, 0x4b, 0x29, 0x99, 0xa7, 0x99, 0xc2, 0x99, 0xfe, 0x99, + 0xce, 0x4b, 0x30, 0x9b, 0x12, 0x9b, 0x40, 0x9c, 0xfd, 0x9c, 0xce, 0x4c, + 0xed, 0x4c, 0x67, 0x9d, 0xce, 0xa0, 0xf8, 0x4c, 0x05, 0xa1, 0x0e, 0xa2, + 0x91, 0xa2, 0xbb, 0x9e, 0x56, 0x4d, 0xf9, 0x9e, 0xfe, 0x9e, 0x05, 0x9f, + 0x0f, 0x9f, 0x16, 0x9f, 0x3b, 0x9f, 0x00, 0xa6, 0x02, 0x88, 0xa0, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x28, 0x00, 0x08, 0xa0, 0x80, 0xa0, 0x80, + 0x00, 0x80, 0x80, 0x00, 0x0a, 0x88, 0x80, 0x00, 0x80, 0x00, 0x20, 0x2a, + 0x00, 0x80, +}; + +static const uint16_t unicode_comp_table[944] = { + 0x4a01, 0x49c0, 0x4a02, 0x0280, 0x0281, 0x0282, 0x0283, 0x02c0, 0x02c2, + 0x0a00, 0x0284, 0x2442, 0x0285, 0x07c0, 0x0980, 0x0982, 0x2440, 0x2280, + 0x02c4, 0x2282, 0x2284, 0x2286, 0x02c6, 0x02c8, 0x02ca, 0x02cc, 0x0287, + 0x228a, 0x02ce, 0x228c, 0x2290, 0x2292, 0x228e, 0x0288, 0x0289, 0x028a, + 0x2482, 0x0300, 0x0302, 0x0304, 0x028b, 0x2480, 0x0308, 0x0984, 0x0986, + 0x2458, 0x0a02, 0x0306, 0x2298, 0x229a, 0x229e, 0x0900, 0x030a, 0x22a0, + 0x030c, 0x030e, 0x0840, 0x0310, 0x0312, 0x22a2, 0x22a6, 0x09c0, 0x22a4, + 0x22a8, 0x22aa, 0x028c, 0x028d, 0x028e, 0x0340, 0x0342, 0x0344, 0x0380, + 0x028f, 0x248e, 0x07c2, 0x0988, 0x098a, 0x2490, 0x0346, 0x22ac, 0x0400, + 0x22b0, 0x0842, 0x22b2, 0x0402, 0x22b4, 0x0440, 0x0444, 0x22b6, 0x0442, + 0x22c2, 0x22c0, 0x22c4, 0x22c6, 0x22c8, 0x0940, 0x04c0, 0x0291, 0x22ca, + 0x04c4, 0x22cc, 0x04c2, 0x22d0, 0x22ce, 0x0292, 0x0293, 0x0294, 0x0295, + 0x0540, 0x0542, 0x0a08, 0x0296, 0x2494, 0x0544, 0x07c4, 0x098c, 0x098e, + 0x06c0, 0x2492, 0x0844, 0x2308, 0x230a, 0x0580, 0x230c, 0x0584, 0x0990, + 0x0992, 0x230e, 0x0582, 0x2312, 0x0586, 0x0588, 0x2314, 0x058c, 0x2316, + 0x0998, 0x058a, 0x231e, 0x0590, 0x2320, 0x099a, 0x058e, 0x2324, 0x2322, + 0x0299, 0x029a, 0x029b, 0x05c0, 0x05c2, 0x05c4, 0x029c, 0x24ac, 0x05c6, + 0x05c8, 0x07c6, 0x0994, 0x0996, 0x0700, 0x24aa, 0x2326, 0x05ca, 0x232a, + 0x2328, 0x2340, 0x2342, 0x2344, 0x2346, 0x05cc, 0x234a, 0x2348, 0x234c, + 0x234e, 0x2350, 0x24b8, 0x029d, 0x05ce, 0x24be, 0x0a0c, 0x2352, 0x0600, + 0x24bc, 0x24ba, 0x0640, 0x2354, 0x0642, 0x0644, 0x2356, 0x2358, 0x02a0, + 0x02a1, 0x02a2, 0x02a3, 0x02c1, 0x02c3, 0x0a01, 0x02a4, 0x2443, 0x02a5, + 0x07c1, 0x0981, 0x0983, 0x2441, 0x2281, 0x02c5, 0x2283, 0x2285, 0x2287, + 0x02c7, 0x02c9, 0x02cb, 0x02cd, 0x02a7, 0x228b, 0x02cf, 0x228d, 0x2291, + 0x2293, 0x228f, 0x02a8, 0x02a9, 0x02aa, 0x2483, 0x0301, 0x0303, 0x0305, + 0x02ab, 0x2481, 0x0309, 0x0985, 0x0987, 0x2459, 0x0a03, 0x0307, 0x2299, + 0x229b, 0x229f, 0x0901, 0x030b, 0x22a1, 0x030d, 0x030f, 0x0841, 0x0311, + 0x0313, 0x22a3, 0x22a7, 0x09c1, 0x22a5, 0x22a9, 0x22ab, 0x2380, 0x02ac, + 0x02ad, 0x02ae, 0x0341, 0x0343, 0x0345, 0x02af, 0x248f, 0x07c3, 0x0989, + 0x098b, 0x2491, 0x0347, 0x22ad, 0x0401, 0x0884, 0x22b1, 0x0843, 0x22b3, + 0x0403, 0x22b5, 0x0441, 0x0445, 0x22b7, 0x0443, 0x22c3, 0x22c1, 0x22c5, + 0x22c7, 0x22c9, 0x0941, 0x04c1, 0x02b1, 0x22cb, 0x04c5, 0x22cd, 0x04c3, + 0x22d1, 0x22cf, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x0541, 0x0543, 0x0a09, + 0x02b6, 0x2495, 0x0545, 0x07c5, 0x098d, 0x098f, 0x06c1, 0x2493, 0x0845, + 0x2309, 0x230b, 0x0581, 0x230d, 0x0585, 0x0991, 0x0993, 0x230f, 0x0583, + 0x2313, 0x0587, 0x0589, 0x2315, 0x058d, 0x2317, 0x0999, 0x058b, 0x231f, + 0x2381, 0x0591, 0x2321, 0x099b, 0x058f, 0x2325, 0x2323, 0x02b9, 0x02ba, + 0x02bb, 0x05c1, 0x05c3, 0x05c5, 0x02bc, 0x24ad, 0x05c7, 0x05c9, 0x07c7, + 0x0995, 0x0997, 0x0701, 0x24ab, 0x2327, 0x05cb, 0x232b, 0x2329, 0x2341, + 0x2343, 0x2345, 0x2347, 0x05cd, 0x234b, 0x2349, 0x2382, 0x234d, 0x234f, + 0x2351, 0x24b9, 0x02bd, 0x05cf, 0x24bf, 0x0a0d, 0x2353, 0x02bf, 0x24bd, + 0x2383, 0x24bb, 0x0641, 0x2355, 0x0643, 0x0645, 0x2357, 0x2359, 0x3101, + 0x0c80, 0x2e00, 0x2446, 0x2444, 0x244a, 0x2448, 0x0800, 0x0942, 0x0944, + 0x0804, 0x2288, 0x2486, 0x2484, 0x248a, 0x2488, 0x22ae, 0x2498, 0x2496, + 0x249c, 0x249a, 0x2300, 0x0a06, 0x2302, 0x0a04, 0x0946, 0x07ce, 0x07ca, + 0x07c8, 0x07cc, 0x2447, 0x2445, 0x244b, 0x2449, 0x0801, 0x0943, 0x0945, + 0x0805, 0x2289, 0x2487, 0x2485, 0x248b, 0x2489, 0x22af, 0x2499, 0x2497, + 0x249d, 0x249b, 0x2301, 0x0a07, 0x2303, 0x0a05, 0x0947, 0x07cf, 0x07cb, + 0x07c9, 0x07cd, 0x2450, 0x244e, 0x2454, 0x2452, 0x2451, 0x244f, 0x2455, + 0x2453, 0x2294, 0x2296, 0x2295, 0x2297, 0x2304, 0x2306, 0x2305, 0x2307, + 0x2318, 0x2319, 0x231a, 0x231b, 0x232c, 0x232d, 0x232e, 0x232f, 0x2400, + 0x24a2, 0x24a0, 0x24a6, 0x24a4, 0x24a8, 0x24a3, 0x24a1, 0x24a7, 0x24a5, + 0x24a9, 0x24b0, 0x24ae, 0x24b4, 0x24b2, 0x24b6, 0x24b1, 0x24af, 0x24b5, + 0x24b3, 0x24b7, 0x0882, 0x0880, 0x0881, 0x0802, 0x0803, 0x229c, 0x229d, + 0x0a0a, 0x0a0b, 0x0883, 0x0b40, 0x2c8a, 0x0c81, 0x2c89, 0x2c88, 0x2540, + 0x2541, 0x2d00, 0x2e07, 0x0d00, 0x2640, 0x2641, 0x2e80, 0x0d01, 0x26c8, + 0x26c9, 0x2f00, 0x2f84, 0x0d02, 0x2f83, 0x2f82, 0x0d40, 0x26d8, 0x26d9, + 0x3186, 0x0d04, 0x2740, 0x2741, 0x3100, 0x3086, 0x0d06, 0x3085, 0x3084, + 0x0d41, 0x2840, 0x3200, 0x0d07, 0x284f, 0x2850, 0x3280, 0x2c84, 0x2e03, + 0x2857, 0x0d42, 0x2c81, 0x2c80, 0x24c0, 0x24c1, 0x2c86, 0x2c83, 0x28c0, + 0x0d43, 0x25c0, 0x25c1, 0x2940, 0x0d44, 0x26c0, 0x26c1, 0x2e05, 0x2e02, + 0x29c0, 0x0d45, 0x2f05, 0x2f04, 0x0d80, 0x26d0, 0x26d1, 0x2f80, 0x2a40, + 0x0d82, 0x26e0, 0x26e1, 0x3080, 0x3081, 0x2ac0, 0x0d83, 0x3004, 0x3003, + 0x0d81, 0x27c0, 0x27c1, 0x3082, 0x2b40, 0x0d84, 0x2847, 0x2848, 0x3184, + 0x3181, 0x2f06, 0x0d08, 0x2f81, 0x3005, 0x0d46, 0x3083, 0x3182, 0x0e00, + 0x0e01, 0x0f40, 0x1180, 0x1182, 0x0f03, 0x0f00, 0x11c0, 0x0f01, 0x1140, + 0x1202, 0x1204, 0x0f81, 0x1240, 0x0fc0, 0x1242, 0x0f80, 0x1244, 0x1284, + 0x0f82, 0x1286, 0x1288, 0x128a, 0x12c0, 0x1282, 0x1181, 0x1183, 0x1043, + 0x1040, 0x11c1, 0x1041, 0x1141, 0x1203, 0x1205, 0x10c1, 0x1241, 0x1000, + 0x1243, 0x10c0, 0x1245, 0x1285, 0x10c2, 0x1287, 0x1289, 0x128b, 0x12c1, + 0x1283, 0x1080, 0x1100, 0x1101, 0x1200, 0x1201, 0x1280, 0x1281, 0x1340, + 0x1341, 0x1343, 0x1342, 0x1344, 0x13c2, 0x1400, 0x13c0, 0x1440, 0x1480, + 0x14c0, 0x1540, 0x1541, 0x1740, 0x1700, 0x1741, 0x17c0, 0x1800, 0x1802, + 0x1801, 0x1840, 0x1880, 0x1900, 0x18c0, 0x18c1, 0x1901, 0x1940, 0x1942, + 0x1941, 0x1980, 0x19c0, 0x19c2, 0x19c1, 0x1c80, 0x1cc0, 0x1dc0, 0x1f80, + 0x2000, 0x2002, 0x2004, 0x2006, 0x2008, 0x2040, 0x2080, 0x2082, 0x20c0, + 0x20c1, 0x2100, 0x22b8, 0x22b9, 0x2310, 0x2311, 0x231c, 0x231d, 0x244c, + 0x2456, 0x244d, 0x2457, 0x248c, 0x248d, 0x249e, 0x249f, 0x2500, 0x2502, + 0x2504, 0x2bc0, 0x2501, 0x2503, 0x2505, 0x2bc1, 0x2bc2, 0x2bc3, 0x2bc4, + 0x2bc5, 0x2bc6, 0x2bc7, 0x2580, 0x2582, 0x2584, 0x2bc8, 0x2581, 0x2583, + 0x2585, 0x2bc9, 0x2bca, 0x2bcb, 0x2bcc, 0x2bcd, 0x2bce, 0x2bcf, 0x2600, + 0x2602, 0x2601, 0x2603, 0x2680, 0x2682, 0x2681, 0x2683, 0x26c2, 0x26c4, + 0x26c6, 0x2c00, 0x26c3, 0x26c5, 0x26c7, 0x2c01, 0x2c02, 0x2c03, 0x2c04, + 0x2c05, 0x2c06, 0x2c07, 0x26ca, 0x26cc, 0x26ce, 0x2c08, 0x26cb, 0x26cd, + 0x26cf, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x26d2, + 0x26d4, 0x26d6, 0x26d3, 0x26d5, 0x26d7, 0x26da, 0x26dc, 0x26de, 0x26db, + 0x26dd, 0x26df, 0x2700, 0x2702, 0x2701, 0x2703, 0x2780, 0x2782, 0x2781, + 0x2783, 0x2800, 0x2802, 0x2804, 0x2801, 0x2803, 0x2805, 0x2842, 0x2844, + 0x2846, 0x2849, 0x284b, 0x284d, 0x2c40, 0x284a, 0x284c, 0x284e, 0x2c41, + 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2851, 0x2853, 0x2855, + 0x2c48, 0x2852, 0x2854, 0x2856, 0x2c49, 0x2c4a, 0x2c4b, 0x2c4c, 0x2c4d, + 0x2c4e, 0x2c4f, 0x2c82, 0x2e01, 0x3180, 0x2c87, 0x2f01, 0x2f02, 0x2f03, + 0x2e06, 0x3185, 0x3000, 0x3001, 0x3002, 0x4640, 0x4641, 0x4680, 0x46c0, + 0x46c2, 0x46c1, 0x4700, 0x4740, 0x4780, 0x47c0, 0x47c2, 0x4900, 0x4940, + 0x4980, 0x4982, 0x4a00, 0x49c2, 0x4a03, 0x4a04, 0x4a40, 0x4a41, 0x4a80, + 0x4a81, 0x4ac0, 0x4ac1, 0x4bc0, 0x4bc1, 0x4b00, 0x4b01, 0x4b40, 0x4b41, + 0x4bc2, 0x4bc3, 0x4b80, 0x4b81, 0x4b82, 0x4b83, 0x4c00, 0x4c01, 0x4c02, + 0x4c03, 0x5600, 0x5440, 0x5442, 0x5444, 0x5446, 0x5448, 0x544a, 0x544c, + 0x544e, 0x5450, 0x5452, 0x5454, 0x5456, 0x5480, 0x5482, 0x5484, 0x54c0, + 0x54c1, 0x5500, 0x5501, 0x5540, 0x5541, 0x5580, 0x5581, 0x55c0, 0x55c1, + 0x5680, 0x58c0, 0x5700, 0x5702, 0x5704, 0x5706, 0x5708, 0x570a, 0x570c, + 0x570e, 0x5710, 0x5712, 0x5714, 0x5716, 0x5740, 0x5742, 0x5744, 0x5780, + 0x5781, 0x57c0, 0x57c1, 0x5800, 0x5801, 0x5840, 0x5841, 0x5880, 0x5881, + 0x5900, 0x5901, 0x5902, 0x5903, 0x5940, 0x8e40, 0x8e42, 0x8e80, 0x8ec0, + 0x8ec1, 0x8f00, 0x8f01, 0x8f41, 0x8f40, 0x8f43, 0x8f80, 0x8f81, +}; + +typedef enum { + UNICODE_GC_Cn, + UNICODE_GC_Lu, + UNICODE_GC_Ll, + UNICODE_GC_Lt, + UNICODE_GC_Lm, + UNICODE_GC_Lo, + UNICODE_GC_Mn, + UNICODE_GC_Mc, + UNICODE_GC_Me, + UNICODE_GC_Nd, + UNICODE_GC_Nl, + UNICODE_GC_No, + UNICODE_GC_Sm, + UNICODE_GC_Sc, + UNICODE_GC_Sk, + UNICODE_GC_So, + UNICODE_GC_Pc, + UNICODE_GC_Pd, + UNICODE_GC_Ps, + UNICODE_GC_Pe, + UNICODE_GC_Pi, + UNICODE_GC_Pf, + UNICODE_GC_Po, + UNICODE_GC_Zs, + UNICODE_GC_Zl, + UNICODE_GC_Zp, + UNICODE_GC_Cc, + UNICODE_GC_Cf, + UNICODE_GC_Cs, + UNICODE_GC_Co, + UNICODE_GC_LC, + UNICODE_GC_L, + UNICODE_GC_M, + UNICODE_GC_N, + UNICODE_GC_S, + UNICODE_GC_P, + UNICODE_GC_Z, + UNICODE_GC_C, + UNICODE_GC_COUNT, +} UnicodeGCEnum; + +static const char unicode_gc_name_table[] = + "Cn,Unassigned" + "\0" + "Lu,Uppercase_Letter" + "\0" + "Ll,Lowercase_Letter" + "\0" + "Lt,Titlecase_Letter" + "\0" + "Lm,Modifier_Letter" + "\0" + "Lo,Other_Letter" + "\0" + "Mn,Nonspacing_Mark" + "\0" + "Mc,Spacing_Mark" + "\0" + "Me,Enclosing_Mark" + "\0" + "Nd,Decimal_Number,digit" + "\0" + "Nl,Letter_Number" + "\0" + "No,Other_Number" + "\0" + "Sm,Math_Symbol" + "\0" + "Sc,Currency_Symbol" + "\0" + "Sk,Modifier_Symbol" + "\0" + "So,Other_Symbol" + "\0" + "Pc,Connector_Punctuation" + "\0" + "Pd,Dash_Punctuation" + "\0" + "Ps,Open_Punctuation" + "\0" + "Pe,Close_Punctuation" + "\0" + "Pi,Initial_Punctuation" + "\0" + "Pf,Final_Punctuation" + "\0" + "Po,Other_Punctuation" + "\0" + "Zs,Space_Separator" + "\0" + "Zl,Line_Separator" + "\0" + "Zp,Paragraph_Separator" + "\0" + "Cc,Control,cntrl" + "\0" + "Cf,Format" + "\0" + "Cs,Surrogate" + "\0" + "Co,Private_Use" + "\0" + "LC,Cased_Letter" + "\0" + "L,Letter" + "\0" + "M,Mark,Combining_Mark" + "\0" + "N,Number" + "\0" + "S,Symbol" + "\0" + "P,Punctuation,punct" + "\0" + "Z,Separator" + "\0" + "C,Other" + "\0"; + +static const uint8_t unicode_gc_table[3719] = { + 0xfa, 0x18, 0x17, 0x56, 0x0d, 0x56, 0x12, 0x13, 0x16, 0x0c, 0x16, 0x11, + 0x36, 0xe9, 0x02, 0x36, 0x4c, 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e, + 0x10, 0x0e, 0xe2, 0x12, 0x12, 0x0c, 0x13, 0x0c, 0xfa, 0x19, 0x17, 0x16, + 0x6d, 0x0f, 0x16, 0x0e, 0x0f, 0x05, 0x14, 0x0c, 0x1b, 0x0f, 0x0e, 0x0f, + 0x0c, 0x2b, 0x0e, 0x02, 0x36, 0x0e, 0x0b, 0x05, 0x15, 0x4b, 0x16, 0xe1, + 0x0f, 0x0c, 0xc1, 0xe2, 0x10, 0x0c, 0xe2, 0x00, 0xff, 0x30, 0x02, 0xff, + 0x08, 0x02, 0xff, 0x27, 0xbf, 0x22, 0x21, 0x02, 0x5f, 0x5f, 0x21, 0x22, + 0x61, 0x02, 0x21, 0x02, 0x41, 0x42, 0x21, 0x02, 0x21, 0x02, 0x9f, 0x7f, + 0x02, 0x5f, 0x5f, 0x21, 0x02, 0x5f, 0x3f, 0x02, 0x05, 0x3f, 0x22, 0x65, + 0x01, 0x03, 0x02, 0x01, 0x03, 0x02, 0x01, 0x03, 0x02, 0xff, 0x08, 0x02, + 0xff, 0x0a, 0x02, 0x01, 0x03, 0x02, 0x5f, 0x21, 0x02, 0xff, 0x32, 0xa2, + 0x21, 0x02, 0x21, 0x22, 0x5f, 0x41, 0x02, 0xff, 0x00, 0xe2, 0x3c, 0x05, + 0xe2, 0x13, 0xe4, 0x0a, 0x6e, 0xe4, 0x04, 0xee, 0x06, 0x84, 0xce, 0x04, + 0x0e, 0x04, 0xee, 0x09, 0xe6, 0x68, 0x7f, 0x04, 0x0e, 0x3f, 0x20, 0x04, + 0x42, 0x16, 0x01, 0x60, 0x2e, 0x01, 0x16, 0x41, 0x00, 0x01, 0x00, 0x21, + 0x02, 0xe1, 0x09, 0x00, 0xe1, 0x01, 0xe2, 0x1b, 0x3f, 0x02, 0x41, 0x42, + 0xff, 0x10, 0x62, 0x3f, 0x0c, 0x5f, 0x3f, 0x02, 0xe1, 0x2b, 0xe2, 0x28, + 0xff, 0x1a, 0x0f, 0x86, 0x28, 0xff, 0x2f, 0xff, 0x06, 0x02, 0xff, 0x58, + 0x00, 0xe1, 0x1e, 0x20, 0x04, 0xb6, 0xe2, 0x21, 0x16, 0x11, 0x20, 0x2f, + 0x0d, 0x00, 0xe6, 0x25, 0x11, 0x06, 0x16, 0x26, 0x16, 0x26, 0x16, 0x06, + 0xe0, 0x00, 0xe5, 0x13, 0x60, 0x65, 0x36, 0xe0, 0x03, 0xbb, 0x4c, 0x36, + 0x0d, 0x36, 0x2f, 0xe6, 0x03, 0x16, 0x1b, 0x00, 0x36, 0xe5, 0x18, 0x04, + 0xe5, 0x02, 0xe6, 0x0d, 0xe9, 0x02, 0x76, 0x25, 0x06, 0xe5, 0x5b, 0x16, + 0x05, 0xc6, 0x1b, 0x0f, 0xa6, 0x24, 0x26, 0x0f, 0x66, 0x25, 0xe9, 0x02, + 0x45, 0x2f, 0x05, 0xf6, 0x06, 0x00, 0x1b, 0x05, 0x06, 0xe5, 0x16, 0xe6, + 0x13, 0x20, 0xe5, 0x51, 0xe6, 0x03, 0x05, 0xe0, 0x06, 0xe9, 0x02, 0xe5, + 0x19, 0xe6, 0x01, 0x24, 0x0f, 0x56, 0x04, 0x20, 0x06, 0x2d, 0xe5, 0x0e, + 0x66, 0x04, 0xe6, 0x01, 0x04, 0x46, 0x04, 0x86, 0x20, 0xf6, 0x07, 0x00, + 0xe5, 0x11, 0x46, 0x20, 0x16, 0x00, 0xe5, 0x03, 0xe0, 0x2d, 0xe5, 0x0d, + 0x00, 0xe5, 0x00, 0xe0, 0x0d, 0xe6, 0x07, 0x1b, 0xe6, 0x18, 0x07, 0xe5, + 0x2e, 0x06, 0x07, 0x06, 0x05, 0x47, 0xe6, 0x00, 0x67, 0x06, 0x27, 0x05, + 0xc6, 0xe5, 0x02, 0x26, 0x36, 0xe9, 0x02, 0x16, 0x04, 0xe5, 0x07, 0x06, + 0x27, 0x00, 0xe5, 0x00, 0x20, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, + 0x05, 0x40, 0x65, 0x20, 0x06, 0x05, 0x47, 0x66, 0x20, 0x27, 0x20, 0x27, + 0x06, 0x05, 0xe0, 0x00, 0x07, 0x60, 0x25, 0x00, 0x45, 0x26, 0x20, 0xe9, + 0x02, 0x25, 0x2d, 0xab, 0x0f, 0x0d, 0x05, 0x16, 0x06, 0x20, 0x26, 0x07, + 0x00, 0xa5, 0x60, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, + 0x25, 0x00, 0x25, 0x20, 0x06, 0x00, 0x47, 0x26, 0x60, 0x26, 0x20, 0x46, + 0x40, 0x06, 0xc0, 0x65, 0x00, 0x05, 0xc0, 0xe9, 0x02, 0x26, 0x45, 0x06, + 0x16, 0xe0, 0x02, 0x26, 0x07, 0x00, 0xe5, 0x01, 0x00, 0x45, 0x00, 0xe5, + 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, 0x85, 0x20, 0x06, 0x05, 0x47, 0x86, + 0x00, 0x26, 0x07, 0x00, 0x27, 0x06, 0x20, 0x05, 0xe0, 0x07, 0x25, 0x26, + 0x20, 0xe9, 0x02, 0x16, 0x0d, 0xc0, 0x05, 0xa6, 0x00, 0x06, 0x27, 0x00, + 0xe5, 0x00, 0x20, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, + 0x85, 0x20, 0x06, 0x05, 0x07, 0x06, 0x07, 0x66, 0x20, 0x27, 0x20, 0x27, + 0x06, 0xe0, 0x00, 0x06, 0x07, 0x60, 0x25, 0x00, 0x45, 0x26, 0x20, 0xe9, + 0x02, 0x0f, 0x05, 0xab, 0xe0, 0x02, 0x06, 0x05, 0x00, 0xa5, 0x40, 0x45, + 0x00, 0x65, 0x40, 0x25, 0x00, 0x05, 0x00, 0x25, 0x40, 0x25, 0x40, 0x45, + 0x40, 0xe5, 0x04, 0x60, 0x27, 0x06, 0x27, 0x40, 0x47, 0x00, 0x47, 0x06, + 0x20, 0x05, 0xa0, 0x07, 0xe0, 0x06, 0xe9, 0x02, 0x4b, 0xaf, 0x0d, 0x0f, + 0x80, 0x06, 0x47, 0x06, 0xe5, 0x00, 0x00, 0x45, 0x00, 0xe5, 0x0f, 0x00, + 0xe5, 0x08, 0x40, 0x05, 0x46, 0x67, 0x00, 0x46, 0x00, 0x66, 0xc0, 0x26, + 0x00, 0x45, 0x80, 0x25, 0x26, 0x20, 0xe9, 0x02, 0xc0, 0x16, 0xcb, 0x0f, + 0x05, 0x06, 0x27, 0x16, 0xe5, 0x00, 0x00, 0x45, 0x00, 0xe5, 0x0f, 0x00, + 0xe5, 0x02, 0x00, 0x85, 0x20, 0x06, 0x05, 0x07, 0x06, 0x87, 0x00, 0x06, + 0x27, 0x00, 0x27, 0x26, 0xc0, 0x27, 0xc0, 0x05, 0x00, 0x25, 0x26, 0x20, + 0xe9, 0x02, 0x00, 0x25, 0xe0, 0x05, 0x26, 0x27, 0x00, 0xe5, 0x00, 0x00, + 0x45, 0x00, 0xe5, 0x21, 0x26, 0x05, 0x47, 0x66, 0x00, 0x47, 0x00, 0x47, + 0x06, 0x05, 0x0f, 0x60, 0x45, 0x07, 0xcb, 0x45, 0x26, 0x20, 0xe9, 0x02, + 0xeb, 0x01, 0x0f, 0xa5, 0x20, 0x27, 0x00, 0xe5, 0x0a, 0x40, 0xe5, 0x10, + 0x00, 0xe5, 0x01, 0x00, 0x05, 0x20, 0xc5, 0x40, 0x06, 0x60, 0x47, 0x46, + 0x00, 0x06, 0x00, 0xe7, 0x00, 0xa0, 0xe9, 0x02, 0x20, 0x27, 0x16, 0xe0, + 0x04, 0xe5, 0x28, 0x06, 0x25, 0xc6, 0x60, 0x0d, 0xa5, 0x04, 0xe6, 0x00, + 0x16, 0xe9, 0x02, 0x36, 0xe0, 0x1d, 0x25, 0x00, 0x05, 0x00, 0x85, 0x00, + 0xe5, 0x10, 0x00, 0x05, 0x00, 0xe5, 0x02, 0x06, 0x25, 0xe6, 0x01, 0x05, + 0x20, 0x85, 0x00, 0x04, 0x00, 0xa6, 0x20, 0xe9, 0x02, 0x20, 0x65, 0xe0, + 0x18, 0x05, 0x4f, 0xf6, 0x07, 0x0f, 0x16, 0x4f, 0x26, 0xaf, 0xe9, 0x02, + 0xeb, 0x02, 0x0f, 0x06, 0x0f, 0x06, 0x0f, 0x06, 0x12, 0x13, 0x12, 0x13, + 0x27, 0xe5, 0x00, 0x00, 0xe5, 0x1c, 0x60, 0xe6, 0x06, 0x07, 0x86, 0x16, + 0x26, 0x85, 0xe6, 0x03, 0x00, 0xe6, 0x1c, 0x00, 0xef, 0x00, 0x06, 0xaf, + 0x00, 0x2f, 0x96, 0x6f, 0x36, 0xe0, 0x1d, 0xe5, 0x23, 0x27, 0x66, 0x07, + 0xa6, 0x07, 0x26, 0x27, 0x26, 0x05, 0xe9, 0x02, 0xb6, 0xa5, 0x27, 0x26, + 0x65, 0x46, 0x05, 0x47, 0x25, 0xc7, 0x45, 0x66, 0xe5, 0x05, 0x06, 0x27, + 0x26, 0xa7, 0x06, 0x05, 0x07, 0xe9, 0x02, 0x47, 0x06, 0x2f, 0xe1, 0x1e, + 0x00, 0x01, 0x80, 0x01, 0x20, 0xe2, 0x23, 0x16, 0x04, 0x42, 0xe5, 0x80, + 0xc1, 0x00, 0x65, 0x20, 0xc5, 0x00, 0x05, 0x00, 0x65, 0x20, 0xe5, 0x21, + 0x00, 0x65, 0x20, 0xe5, 0x19, 0x00, 0x65, 0x20, 0xc5, 0x00, 0x05, 0x00, + 0x65, 0x20, 0xe5, 0x07, 0x00, 0xe5, 0x31, 0x00, 0x65, 0x20, 0xe5, 0x3b, + 0x20, 0x46, 0xf6, 0x01, 0xeb, 0x0c, 0x40, 0xe5, 0x08, 0xef, 0x02, 0xa0, + 0xe1, 0x4e, 0x20, 0xa2, 0x20, 0x11, 0xe5, 0x81, 0xe4, 0x0f, 0x16, 0xe5, + 0x09, 0x17, 0xe5, 0x12, 0x12, 0x13, 0x40, 0xe5, 0x43, 0x56, 0x4a, 0xe5, + 0x00, 0xc0, 0xe5, 0x05, 0x00, 0x65, 0x46, 0xe0, 0x03, 0xe5, 0x0a, 0x46, + 0x36, 0xe0, 0x01, 0xe5, 0x0a, 0x26, 0xe0, 0x04, 0xe5, 0x05, 0x00, 0x45, + 0x00, 0x26, 0xe0, 0x04, 0xe5, 0x2c, 0x26, 0x07, 0xc6, 0xe7, 0x00, 0x06, + 0x27, 0xe6, 0x03, 0x56, 0x04, 0x56, 0x0d, 0x05, 0x06, 0x20, 0xe9, 0x02, + 0xa0, 0xeb, 0x02, 0xa0, 0xb6, 0x11, 0x76, 0x46, 0x1b, 0x00, 0xe9, 0x02, + 0xa0, 0xe5, 0x1b, 0x04, 0xe5, 0x2d, 0xc0, 0x85, 0x26, 0xe5, 0x1a, 0x06, + 0x05, 0x80, 0xe5, 0x3e, 0xe0, 0x02, 0xe5, 0x17, 0x00, 0x46, 0x67, 0x26, + 0x47, 0x60, 0x27, 0x06, 0xa7, 0x46, 0x60, 0x0f, 0x40, 0x36, 0xe9, 0x02, + 0xe5, 0x16, 0x20, 0x85, 0xe0, 0x03, 0xe5, 0x24, 0x60, 0xe5, 0x12, 0xa0, + 0xe9, 0x02, 0x0b, 0x40, 0xef, 0x1a, 0xe5, 0x0f, 0x26, 0x27, 0x06, 0x20, + 0x36, 0xe5, 0x2d, 0x07, 0x06, 0x07, 0xc6, 0x00, 0x06, 0x07, 0x06, 0x27, + 0xe6, 0x00, 0xa7, 0xe6, 0x02, 0x20, 0x06, 0xe9, 0x02, 0xa0, 0xe9, 0x02, + 0xa0, 0xd6, 0x04, 0xb6, 0x20, 0xe6, 0x06, 0x08, 0xe0, 0x39, 0x66, 0x07, + 0xe5, 0x27, 0x06, 0x07, 0x86, 0x07, 0x06, 0x87, 0x06, 0x27, 0xc5, 0x60, + 0xe9, 0x02, 0xd6, 0xef, 0x02, 0xe6, 0x01, 0xef, 0x01, 0x40, 0x26, 0x07, + 0xe5, 0x16, 0x07, 0x66, 0x27, 0x26, 0x07, 0x46, 0x25, 0xe9, 0x02, 0xe5, + 0x24, 0x06, 0x07, 0x26, 0x47, 0x06, 0x07, 0x46, 0x27, 0xe0, 0x00, 0x76, + 0xe5, 0x1c, 0xe7, 0x00, 0xe6, 0x00, 0x27, 0x26, 0x40, 0x96, 0xe9, 0x02, + 0x40, 0x45, 0xe9, 0x02, 0xe5, 0x16, 0xa4, 0x36, 0xe2, 0x01, 0xc0, 0xe1, + 0x23, 0x20, 0x41, 0xf6, 0x00, 0xe0, 0x00, 0x46, 0x16, 0xe6, 0x05, 0x07, + 0xc6, 0x65, 0x06, 0xa5, 0x06, 0x25, 0x07, 0x26, 0x05, 0x80, 0xe2, 0x24, + 0xe4, 0x37, 0xe2, 0x05, 0x04, 0xe2, 0x1a, 0xe4, 0x1d, 0xe6, 0x32, 0x00, + 0x86, 0xff, 0x80, 0x0e, 0xe2, 0x00, 0xff, 0x5a, 0xe2, 0x00, 0xe1, 0x00, + 0xa2, 0x20, 0xa1, 0x20, 0xe2, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe1, 0x00, + 0xa2, 0x20, 0xa1, 0x20, 0xe2, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x3f, 0xc2, 0xe1, 0x00, 0xe2, 0x06, 0x20, 0xe2, 0x00, 0xe3, 0x00, + 0xe2, 0x00, 0xe3, 0x00, 0xe2, 0x00, 0xe3, 0x00, 0x82, 0x00, 0x22, 0x61, + 0x03, 0x0e, 0x02, 0x4e, 0x42, 0x00, 0x22, 0x61, 0x03, 0x4e, 0x62, 0x20, + 0x22, 0x61, 0x00, 0x4e, 0xe2, 0x00, 0x81, 0x4e, 0x20, 0x42, 0x00, 0x22, + 0x61, 0x03, 0x2e, 0x00, 0xf7, 0x03, 0x9b, 0xb1, 0x36, 0x14, 0x15, 0x12, + 0x34, 0x15, 0x12, 0x14, 0xf6, 0x00, 0x18, 0x19, 0x9b, 0x17, 0xf6, 0x01, + 0x14, 0x15, 0x76, 0x30, 0x56, 0x0c, 0x12, 0x13, 0xf6, 0x03, 0x0c, 0x16, + 0x10, 0xf6, 0x02, 0x17, 0x9b, 0x00, 0xfb, 0x02, 0x0b, 0x04, 0x20, 0xab, + 0x4c, 0x12, 0x13, 0x04, 0xeb, 0x02, 0x4c, 0x12, 0x13, 0x00, 0xe4, 0x05, + 0x40, 0xed, 0x18, 0xe0, 0x08, 0xe6, 0x05, 0x68, 0x06, 0x48, 0xe6, 0x04, + 0xe0, 0x07, 0x2f, 0x01, 0x6f, 0x01, 0x2f, 0x02, 0x41, 0x22, 0x41, 0x02, + 0x0f, 0x01, 0x2f, 0x0c, 0x81, 0xaf, 0x01, 0x0f, 0x01, 0x0f, 0x01, 0x0f, + 0x61, 0x0f, 0x02, 0x61, 0x02, 0x65, 0x02, 0x2f, 0x22, 0x21, 0x8c, 0x3f, + 0x42, 0x0f, 0x0c, 0x2f, 0x02, 0x0f, 0xeb, 0x08, 0xea, 0x1b, 0x3f, 0x6a, + 0x0b, 0x2f, 0x60, 0x8c, 0x8f, 0x2c, 0x6f, 0x0c, 0x2f, 0x0c, 0x2f, 0x0c, + 0xcf, 0x0c, 0xef, 0x17, 0x2c, 0x2f, 0x0c, 0x0f, 0x0c, 0xef, 0x17, 0xec, + 0x80, 0x84, 0xef, 0x00, 0x12, 0x13, 0x12, 0x13, 0xef, 0x0c, 0x2c, 0xcf, + 0x12, 0x13, 0xef, 0x49, 0x0c, 0xef, 0x16, 0xec, 0x11, 0xef, 0x20, 0xac, + 0xef, 0x3d, 0xe0, 0x11, 0xef, 0x03, 0xe0, 0x0d, 0xeb, 0x34, 0xef, 0x46, + 0xeb, 0x0e, 0xef, 0x80, 0x2f, 0x0c, 0xef, 0x01, 0x0c, 0xef, 0x2e, 0xec, + 0x00, 0xef, 0x67, 0x0c, 0xef, 0x80, 0x70, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0xeb, 0x16, 0xef, + 0x24, 0x8c, 0x12, 0x13, 0xec, 0x17, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0xec, 0x08, 0xef, 0x80, 0x78, 0xec, 0x7b, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0xec, 0x37, 0x12, + 0x13, 0x12, 0x13, 0xec, 0x18, 0x12, 0x13, 0xec, 0x80, 0x7a, 0xef, 0x28, + 0xec, 0x0d, 0x2f, 0xac, 0xef, 0x1f, 0x20, 0xef, 0x18, 0x20, 0xef, 0x60, + 0xe1, 0x27, 0x00, 0xe2, 0x27, 0x00, 0x5f, 0x21, 0x22, 0xdf, 0x41, 0x02, + 0x3f, 0x02, 0x3f, 0x82, 0x24, 0x41, 0x02, 0xff, 0x5a, 0x02, 0xaf, 0x7f, + 0x46, 0x3f, 0x80, 0x76, 0x0b, 0x36, 0xe2, 0x1e, 0x00, 0x02, 0x80, 0x02, + 0x20, 0xe5, 0x30, 0xc0, 0x04, 0x16, 0xe0, 0x06, 0x06, 0xe5, 0x0f, 0xe0, + 0x01, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, + 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xe6, 0x18, 0x36, 0x14, 0x15, 0x14, 0x15, + 0x56, 0x14, 0x15, 0x16, 0x14, 0x15, 0xf6, 0x01, 0x11, 0x36, 0x11, 0x16, + 0x14, 0x15, 0x36, 0x14, 0x15, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x96, 0x04, 0xf6, 0x02, 0x31, 0x76, 0x11, 0x16, 0x12, 0xf6, 0x05, + 0xe0, 0x28, 0xef, 0x12, 0x00, 0xef, 0x51, 0xe0, 0x04, 0xef, 0x80, 0x4e, + 0xe0, 0x12, 0xef, 0x04, 0x60, 0x17, 0x56, 0x0f, 0x04, 0x05, 0x0a, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x2f, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x11, 0x12, 0x33, 0x0f, 0xea, 0x01, + 0x66, 0x27, 0x11, 0x84, 0x2f, 0x4a, 0x04, 0x05, 0x16, 0x2f, 0x00, 0xe5, + 0x4e, 0x20, 0x26, 0x2e, 0x24, 0x05, 0x11, 0xe5, 0x52, 0x16, 0x44, 0x05, + 0x80, 0xe5, 0x23, 0x00, 0xe5, 0x56, 0x00, 0x2f, 0x6b, 0xef, 0x02, 0xe5, + 0x13, 0x80, 0xef, 0x1c, 0xe0, 0x04, 0xe5, 0x08, 0xef, 0x17, 0x00, 0xeb, + 0x02, 0xef, 0x16, 0xeb, 0x00, 0x0f, 0xeb, 0x07, 0xef, 0x18, 0xeb, 0x02, + 0xef, 0x1f, 0xeb, 0x07, 0xef, 0x80, 0xb8, 0xe5, 0x99, 0x2e, 0xe0, 0x02, + 0xef, 0x38, 0xe5, 0xc0, 0x11, 0x68, 0xe0, 0x08, 0xe5, 0x0d, 0x04, 0xe5, + 0x83, 0xef, 0x40, 0xef, 0x2f, 0xe0, 0x01, 0xe5, 0x20, 0xa4, 0x36, 0xe5, + 0x80, 0x84, 0x04, 0x56, 0xe5, 0x08, 0xe9, 0x02, 0x25, 0xe0, 0x0c, 0xff, + 0x26, 0x05, 0x06, 0x48, 0x16, 0xe6, 0x02, 0x16, 0x04, 0xff, 0x14, 0x24, + 0x26, 0xe5, 0x3e, 0xea, 0x02, 0x26, 0xb6, 0xe0, 0x00, 0xee, 0x0f, 0xe4, + 0x01, 0x2e, 0xff, 0x06, 0x22, 0xff, 0x36, 0x04, 0xe2, 0x00, 0x9f, 0xff, + 0x02, 0x04, 0x2e, 0x7f, 0x05, 0x7f, 0x22, 0xff, 0x0d, 0x61, 0x02, 0x81, + 0x02, 0xff, 0x02, 0x20, 0x5f, 0x21, 0xe0, 0x28, 0x05, 0x24, 0x02, 0xc5, + 0x06, 0x45, 0x06, 0x65, 0x06, 0xe5, 0x0f, 0x27, 0x26, 0x07, 0x6f, 0x60, + 0xab, 0x2f, 0x0d, 0x0f, 0xa0, 0xe5, 0x2c, 0x76, 0xe0, 0x00, 0x27, 0xe5, + 0x2a, 0xe7, 0x08, 0x26, 0xe0, 0x00, 0x36, 0xe9, 0x02, 0xa0, 0xe6, 0x0a, + 0xa5, 0x56, 0x05, 0x16, 0x25, 0x06, 0xe9, 0x02, 0xe5, 0x14, 0xe6, 0x00, + 0x36, 0xe5, 0x0f, 0xe6, 0x03, 0x27, 0xe0, 0x03, 0x16, 0xe5, 0x15, 0x40, + 0x46, 0x07, 0xe5, 0x27, 0x06, 0x27, 0x66, 0x27, 0x26, 0x47, 0xf6, 0x05, + 0x00, 0x04, 0xe9, 0x02, 0x60, 0x36, 0x85, 0x06, 0x04, 0xe5, 0x01, 0xe9, + 0x02, 0x85, 0x00, 0xe5, 0x21, 0xa6, 0x27, 0x26, 0x27, 0x26, 0xe0, 0x01, + 0x45, 0x06, 0xe5, 0x00, 0x06, 0x07, 0x20, 0xe9, 0x02, 0x20, 0x76, 0xe5, + 0x08, 0x04, 0xa5, 0x4f, 0x05, 0x07, 0x06, 0x07, 0xe5, 0x2a, 0x06, 0x05, + 0x46, 0x25, 0x26, 0x85, 0x26, 0x05, 0x06, 0x05, 0xe0, 0x10, 0x25, 0x04, + 0x36, 0xe5, 0x03, 0x07, 0x26, 0x27, 0x36, 0x05, 0x24, 0x07, 0x06, 0xe0, + 0x02, 0xa5, 0x20, 0xa5, 0x20, 0xa5, 0xe0, 0x01, 0xc5, 0x00, 0xc5, 0x00, + 0xe2, 0x23, 0x0e, 0x64, 0xe2, 0x00, 0xe0, 0x00, 0xe2, 0x48, 0xe5, 0x1b, + 0x27, 0x06, 0x27, 0x06, 0x27, 0x16, 0x07, 0x06, 0x20, 0xe9, 0x02, 0xa0, + 0xe5, 0xab, 0x1c, 0xe0, 0x04, 0xe5, 0x0f, 0x60, 0xe5, 0x29, 0x60, 0xfc, + 0x87, 0x78, 0xfd, 0x98, 0x78, 0xe5, 0x80, 0xe6, 0x20, 0xe5, 0x62, 0xe0, + 0x1e, 0xc2, 0xe0, 0x04, 0x82, 0x80, 0x05, 0x06, 0xe5, 0x02, 0x0c, 0xe5, + 0x05, 0x00, 0x85, 0x00, 0x05, 0x00, 0x25, 0x00, 0x25, 0x00, 0xe5, 0x64, + 0xee, 0x08, 0xe0, 0x09, 0xe5, 0x80, 0xe3, 0x13, 0x12, 0xe0, 0x08, 0xe5, + 0x38, 0x20, 0xe5, 0x2e, 0xe0, 0x20, 0xe5, 0x04, 0x0d, 0x0f, 0x20, 0xe6, + 0x08, 0xd6, 0x12, 0x13, 0x16, 0xa0, 0xe6, 0x08, 0x16, 0x31, 0x30, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x36, 0x12, 0x13, 0x76, 0x50, 0x56, 0x00, 0x76, 0x11, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x56, 0x0c, 0x11, 0x4c, 0x00, 0x16, + 0x0d, 0x36, 0x60, 0x85, 0x00, 0xe5, 0x7f, 0x20, 0x1b, 0x00, 0x56, 0x0d, + 0x56, 0x12, 0x13, 0x16, 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, 0x36, 0x4c, + 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e, 0x10, 0x0e, 0xe2, 0x12, 0x12, + 0x0c, 0x13, 0x0c, 0x12, 0x13, 0x16, 0x12, 0x13, 0x36, 0xe5, 0x02, 0x04, + 0xe5, 0x25, 0x24, 0xe5, 0x17, 0x40, 0xa5, 0x20, 0xa5, 0x20, 0xa5, 0x20, + 0x45, 0x40, 0x2d, 0x0c, 0x0e, 0x0f, 0x2d, 0x00, 0x0f, 0x6c, 0x2f, 0xe0, + 0x02, 0x5b, 0x2f, 0x20, 0xe5, 0x04, 0x00, 0xe5, 0x12, 0x00, 0xe5, 0x0b, + 0x00, 0x25, 0x00, 0xe5, 0x07, 0x20, 0xe5, 0x06, 0xe0, 0x1a, 0xe5, 0x73, + 0x80, 0x56, 0x60, 0xeb, 0x25, 0x40, 0xef, 0x01, 0xea, 0x2d, 0x6b, 0xef, + 0x09, 0x2b, 0x4f, 0x00, 0xef, 0x04, 0x60, 0x0f, 0xe0, 0x27, 0xef, 0x25, + 0x06, 0xe0, 0x7a, 0xe5, 0x15, 0x40, 0xe5, 0x29, 0xe0, 0x07, 0x06, 0xeb, + 0x13, 0x60, 0xe5, 0x18, 0x6b, 0xe0, 0x01, 0xe5, 0x0c, 0x0a, 0xe5, 0x00, + 0x0a, 0x80, 0xe5, 0x1e, 0x86, 0x80, 0xe5, 0x16, 0x00, 0x16, 0xe5, 0x1c, + 0x60, 0xe5, 0x00, 0x16, 0x8a, 0xe0, 0x22, 0xe1, 0x20, 0xe2, 0x20, 0xe5, + 0x46, 0x20, 0xe9, 0x02, 0xa0, 0xe1, 0x1c, 0x60, 0xe2, 0x1c, 0x60, 0xe5, + 0x20, 0xe0, 0x00, 0xe5, 0x2c, 0xe0, 0x03, 0x16, 0xe0, 0x80, 0x08, 0xe5, + 0x80, 0xaf, 0xe0, 0x01, 0xe5, 0x0e, 0xe0, 0x02, 0xe5, 0x00, 0xe0, 0x80, + 0x10, 0xa5, 0x20, 0x05, 0x00, 0xe5, 0x24, 0x00, 0x25, 0x40, 0x05, 0x20, + 0xe5, 0x0f, 0x00, 0x16, 0xeb, 0x00, 0xe5, 0x0f, 0x2f, 0xcb, 0xe5, 0x17, + 0xe0, 0x00, 0xeb, 0x01, 0xe0, 0x28, 0xe5, 0x0b, 0x00, 0x25, 0x80, 0x8b, + 0xe5, 0x0e, 0xab, 0x40, 0x16, 0xe5, 0x12, 0x80, 0x16, 0xe0, 0x38, 0xe5, + 0x30, 0x60, 0x2b, 0x25, 0xeb, 0x08, 0x20, 0xeb, 0x26, 0x05, 0x46, 0x00, + 0x26, 0x80, 0x66, 0x65, 0x00, 0x45, 0x00, 0xe5, 0x15, 0x20, 0x46, 0x60, + 0x06, 0xeb, 0x01, 0xc0, 0xf6, 0x01, 0xc0, 0xe5, 0x15, 0x2b, 0x16, 0xe5, + 0x15, 0x4b, 0xe0, 0x18, 0xe5, 0x00, 0x0f, 0xe5, 0x14, 0x26, 0x60, 0x8b, + 0xd6, 0xe0, 0x01, 0xe5, 0x2e, 0x40, 0xd6, 0xe5, 0x0e, 0x20, 0xeb, 0x00, + 0xe5, 0x0b, 0x80, 0xeb, 0x00, 0xe5, 0x0a, 0xc0, 0x76, 0xe0, 0x04, 0xcb, + 0xe0, 0x48, 0xe5, 0x41, 0xe0, 0x2f, 0xe1, 0x2b, 0xe0, 0x05, 0xe2, 0x2b, + 0xc0, 0xab, 0xe5, 0x1c, 0x66, 0xe0, 0x00, 0xe9, 0x02, 0xe0, 0x80, 0x9e, + 0xeb, 0x17, 0xe0, 0x79, 0xe5, 0x15, 0xeb, 0x02, 0x05, 0xe0, 0x00, 0xe5, + 0x0e, 0xe6, 0x03, 0x6b, 0x96, 0xe0, 0x7e, 0xe5, 0x0f, 0xe0, 0x01, 0x07, + 0x06, 0x07, 0xe5, 0x2d, 0xe6, 0x07, 0xd6, 0x60, 0xeb, 0x0c, 0xe9, 0x02, + 0xe0, 0x07, 0x46, 0x07, 0xe5, 0x25, 0x47, 0x66, 0x27, 0x26, 0x36, 0x1b, + 0x76, 0xe0, 0x03, 0x1b, 0x20, 0xe5, 0x11, 0xc0, 0xe9, 0x02, 0xa0, 0x46, + 0xe5, 0x1c, 0x86, 0x07, 0xe6, 0x00, 0x00, 0xe9, 0x02, 0x76, 0x05, 0x27, + 0xe0, 0x01, 0xe5, 0x1b, 0x06, 0x36, 0x05, 0xe0, 0x01, 0x26, 0x07, 0xe5, + 0x28, 0x47, 0xe6, 0x01, 0x27, 0x65, 0x76, 0x66, 0x16, 0x20, 0xe9, 0x02, + 0x05, 0x16, 0x05, 0x56, 0x00, 0xeb, 0x0c, 0xe0, 0x03, 0xe5, 0x0a, 0x00, + 0xe5, 0x11, 0x47, 0x46, 0x27, 0x06, 0x07, 0x26, 0xb6, 0x06, 0xe0, 0x39, + 0xc5, 0x00, 0x05, 0x00, 0x65, 0x00, 0xe5, 0x07, 0x00, 0xe5, 0x02, 0x16, + 0xa0, 0xe5, 0x27, 0x06, 0x47, 0xe6, 0x00, 0x80, 0xe9, 0x02, 0xa0, 0x26, + 0x27, 0x00, 0xe5, 0x00, 0x20, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, + 0x25, 0x00, 0x85, 0x00, 0x26, 0x05, 0x27, 0x06, 0x67, 0x20, 0x27, 0x20, + 0x47, 0x20, 0x05, 0xa0, 0x07, 0x80, 0x85, 0x27, 0x20, 0xc6, 0x40, 0x86, + 0xe0, 0x80, 0x03, 0xe5, 0x2d, 0x47, 0xe6, 0x00, 0x27, 0x46, 0x07, 0x06, + 0x65, 0x96, 0xe9, 0x02, 0x00, 0x16, 0x00, 0x16, 0x06, 0x05, 0xe0, 0x18, + 0xe5, 0x28, 0x47, 0xa6, 0x07, 0x06, 0x67, 0x26, 0x07, 0x26, 0x25, 0x16, + 0x05, 0xe0, 0x00, 0xe9, 0x02, 0xe0, 0x80, 0x1e, 0xe5, 0x27, 0x47, 0x66, + 0x20, 0x67, 0x26, 0x07, 0x26, 0xf6, 0x0f, 0x65, 0x26, 0xe0, 0x1a, 0xe5, + 0x28, 0x47, 0xe6, 0x00, 0x27, 0x06, 0x07, 0x26, 0x56, 0x05, 0xe0, 0x03, + 0xe9, 0x02, 0xa0, 0xf6, 0x05, 0xe0, 0x0b, 0xe5, 0x23, 0x06, 0x07, 0x06, + 0x27, 0xa6, 0x07, 0x06, 0x05, 0xc0, 0xe9, 0x02, 0xe0, 0x2e, 0xe5, 0x13, + 0x20, 0x46, 0x27, 0x66, 0x07, 0x86, 0x60, 0xe9, 0x02, 0x2b, 0x56, 0x0f, + 0xe0, 0x80, 0x38, 0xe5, 0x24, 0x47, 0xe6, 0x01, 0x07, 0x26, 0x16, 0xe0, + 0x5c, 0xe1, 0x18, 0xe2, 0x18, 0xe9, 0x02, 0xeb, 0x01, 0xe0, 0x04, 0x05, + 0xe0, 0x80, 0x18, 0xe5, 0x00, 0x20, 0xe5, 0x1f, 0x47, 0x66, 0x20, 0x26, + 0x67, 0x06, 0x05, 0x16, 0x05, 0x07, 0xe0, 0x13, 0x05, 0xe6, 0x02, 0xe5, + 0x20, 0xa6, 0x07, 0x05, 0x66, 0xf6, 0x00, 0x06, 0xe0, 0x00, 0x05, 0xa6, + 0x27, 0x46, 0xe5, 0x26, 0xe6, 0x05, 0x07, 0x26, 0x56, 0x05, 0x96, 0xe0, + 0x15, 0xe5, 0x31, 0xe0, 0x80, 0x7f, 0xe5, 0x01, 0x00, 0xe5, 0x1d, 0x07, + 0xc6, 0x00, 0xa6, 0x07, 0x06, 0x05, 0x96, 0xe0, 0x02, 0xe9, 0x02, 0xeb, + 0x0b, 0x40, 0x36, 0xe5, 0x16, 0x20, 0xe6, 0x0e, 0x00, 0x07, 0xc6, 0x07, + 0x26, 0x07, 0x26, 0xe0, 0x41, 0xc5, 0x00, 0x25, 0x00, 0xe5, 0x1e, 0xa6, + 0x40, 0x06, 0x00, 0x26, 0x00, 0xc6, 0x05, 0x06, 0xe0, 0x00, 0xe9, 0x02, + 0xa0, 0xa5, 0x00, 0x25, 0x00, 0xe5, 0x18, 0x87, 0x00, 0x26, 0x00, 0x27, + 0x06, 0x07, 0x06, 0x05, 0xc0, 0xe9, 0x02, 0xe0, 0x80, 0xae, 0xe5, 0x0b, + 0x26, 0x27, 0x36, 0xe0, 0x80, 0x3f, 0xeb, 0x0d, 0xef, 0x00, 0x6d, 0xef, + 0x09, 0xe0, 0x05, 0x16, 0xe5, 0x83, 0x12, 0xe0, 0x5e, 0xea, 0x67, 0x00, + 0x96, 0xe0, 0x03, 0xe5, 0x80, 0x3c, 0xe0, 0x8a, 0x34, 0xe5, 0x83, 0xa7, + 0x00, 0xfb, 0x01, 0xe0, 0x8f, 0x3f, 0xe5, 0x81, 0xbf, 0xe0, 0xa1, 0x31, + 0xe5, 0x81, 0xb1, 0xc0, 0xe5, 0x17, 0x00, 0xe9, 0x02, 0x60, 0x36, 0xe0, + 0x58, 0xe5, 0x16, 0x20, 0x86, 0x16, 0xe0, 0x02, 0xe5, 0x28, 0xc6, 0x96, + 0x6f, 0x64, 0x16, 0x0f, 0xe0, 0x02, 0xe9, 0x02, 0x00, 0xcb, 0x00, 0xe5, + 0x0d, 0x80, 0xe5, 0x0b, 0xe0, 0x82, 0x28, 0xe1, 0x18, 0xe2, 0x18, 0xeb, + 0x0f, 0x76, 0xe0, 0x5d, 0xe5, 0x43, 0x60, 0x06, 0x05, 0xe7, 0x2f, 0xc0, + 0x66, 0xe4, 0x05, 0xe0, 0x38, 0x24, 0x16, 0x04, 0xe0, 0x14, 0xe5, 0x97, + 0x70, 0xe0, 0x00, 0xe5, 0x82, 0x6b, 0xe0, 0xa4, 0x85, 0xe5, 0x80, 0x97, + 0xe0, 0x29, 0x45, 0xe0, 0x09, 0x65, 0xe0, 0x00, 0xe5, 0x81, 0x04, 0xe0, + 0x88, 0x7c, 0xe5, 0x63, 0x80, 0xe5, 0x05, 0x40, 0xe5, 0x01, 0xc0, 0xe5, + 0x02, 0x20, 0x0f, 0x26, 0x16, 0x7b, 0xe0, 0x92, 0xd4, 0xef, 0x80, 0x6e, + 0xe0, 0x02, 0xef, 0x1f, 0x20, 0xef, 0x34, 0x27, 0x46, 0x4f, 0xa7, 0xfb, + 0x00, 0xe6, 0x00, 0x2f, 0xc6, 0xef, 0x16, 0x66, 0xef, 0x33, 0xe0, 0x0f, + 0xef, 0x3a, 0x46, 0x0f, 0xe0, 0x80, 0x12, 0xeb, 0x0c, 0xe0, 0x04, 0xef, + 0x4f, 0xe0, 0x01, 0xeb, 0x11, 0xe0, 0x7f, 0xe1, 0x12, 0xe2, 0x12, 0xe1, + 0x12, 0xc2, 0x00, 0xe2, 0x0a, 0xe1, 0x12, 0xe2, 0x12, 0x01, 0x00, 0x21, + 0x20, 0x01, 0x20, 0x21, 0x20, 0x61, 0x00, 0xe1, 0x00, 0x62, 0x00, 0x02, + 0x00, 0xc2, 0x00, 0xe2, 0x03, 0xe1, 0x12, 0xe2, 0x12, 0x21, 0x00, 0x61, + 0x20, 0xe1, 0x00, 0x00, 0xc1, 0x00, 0xe2, 0x12, 0x21, 0x00, 0x61, 0x00, + 0x81, 0x00, 0x01, 0x40, 0xc1, 0x00, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, + 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, + 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x14, 0x20, 0xe1, 0x11, 0x0c, + 0xe2, 0x11, 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0xe1, + 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, + 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0x3f, 0x20, 0xe9, 0x2a, + 0xef, 0x81, 0x78, 0xe6, 0x2f, 0x6f, 0xe6, 0x2a, 0xef, 0x00, 0x06, 0xef, + 0x06, 0x06, 0x2f, 0x96, 0xe0, 0x07, 0x86, 0x00, 0xe6, 0x07, 0xe0, 0x84, + 0xc8, 0xc6, 0x00, 0xe6, 0x09, 0x20, 0xc6, 0x00, 0x26, 0x00, 0x86, 0xe0, + 0x80, 0x4d, 0xe5, 0x25, 0x40, 0xc6, 0xc4, 0x20, 0xe9, 0x02, 0x60, 0x05, + 0x0f, 0xe0, 0x80, 0xe8, 0xe5, 0x24, 0x66, 0xe9, 0x02, 0x80, 0x0d, 0xe0, + 0x84, 0x78, 0xe5, 0x80, 0x3d, 0x20, 0xeb, 0x01, 0xc6, 0xe0, 0x21, 0xe1, + 0x1a, 0xe2, 0x1a, 0xc6, 0x04, 0x60, 0xe9, 0x02, 0x60, 0x36, 0xe0, 0x82, + 0x89, 0xeb, 0x33, 0x0f, 0x4b, 0x0d, 0x6b, 0xe0, 0x44, 0xeb, 0x25, 0x0f, + 0xeb, 0x07, 0xe0, 0x80, 0x3a, 0x65, 0x00, 0xe5, 0x13, 0x00, 0x25, 0x00, + 0x05, 0x20, 0x05, 0x00, 0xe5, 0x02, 0x00, 0x65, 0x00, 0x05, 0x00, 0x05, + 0xa0, 0x05, 0x60, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x45, 0x00, 0x25, + 0x00, 0x05, 0x20, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, + 0x00, 0x25, 0x00, 0x05, 0x20, 0x65, 0x00, 0xc5, 0x00, 0x65, 0x00, 0x65, + 0x00, 0x05, 0x00, 0xe5, 0x02, 0x00, 0xe5, 0x09, 0x80, 0x45, 0x00, 0x85, + 0x00, 0xe5, 0x09, 0xe0, 0x2c, 0x2c, 0xe0, 0x80, 0x86, 0xef, 0x24, 0x60, + 0xef, 0x5c, 0xe0, 0x04, 0xef, 0x07, 0x20, 0xef, 0x07, 0x00, 0xef, 0x07, + 0x00, 0xef, 0x1d, 0xe0, 0x02, 0xeb, 0x05, 0x40, 0xef, 0x55, 0x40, 0xef, + 0x35, 0xe0, 0x31, 0xef, 0x15, 0xe0, 0x05, 0xef, 0x24, 0x60, 0xef, 0x01, + 0xc0, 0x2f, 0xe0, 0x06, 0xaf, 0xe0, 0x80, 0x12, 0xef, 0x80, 0x73, 0x8e, + 0xef, 0x82, 0x4e, 0xe0, 0x02, 0xef, 0x05, 0x40, 0xef, 0x03, 0x80, 0xef, + 0x6c, 0xe0, 0x04, 0xef, 0x51, 0xc0, 0xef, 0x04, 0xe0, 0x0c, 0xef, 0x04, + 0x60, 0xef, 0x30, 0xe0, 0x00, 0xef, 0x02, 0xa0, 0xef, 0x20, 0xe0, 0x00, + 0xef, 0x16, 0xe0, 0x4a, 0xef, 0x04, 0x00, 0xef, 0x5d, 0x00, 0x6f, 0x40, + 0xef, 0x21, 0x20, 0xaf, 0x40, 0xef, 0x15, 0x20, 0xef, 0x7f, 0xe0, 0x04, + 0xef, 0x06, 0x20, 0x6f, 0x60, 0x4f, 0x80, 0x4f, 0xe0, 0x05, 0xaf, 0xe0, + 0x84, 0xe2, 0xe5, 0xc0, 0x66, 0x4f, 0xe0, 0x21, 0xe5, 0x8f, 0xad, 0xe0, + 0x03, 0xe5, 0x80, 0x56, 0x20, 0xe5, 0x95, 0xfa, 0xe0, 0x06, 0xe5, 0x9c, + 0xa9, 0xe0, 0x8b, 0x97, 0xe5, 0x81, 0x96, 0xe0, 0xca, 0xc5, 0x5b, 0x1b, + 0xe0, 0x16, 0xfb, 0x58, 0xe0, 0x78, 0xe6, 0x80, 0x68, 0xe0, 0xc0, 0xbd, + 0x88, 0xfd, 0xc0, 0xbf, 0x76, 0x20, 0xfd, 0xc0, 0xbf, 0x76, 0x20, +}; + +typedef enum { + UNICODE_SCRIPT_Unknown, + UNICODE_SCRIPT_Adlam, + UNICODE_SCRIPT_Ahom, + UNICODE_SCRIPT_Anatolian_Hieroglyphs, + UNICODE_SCRIPT_Arabic, + UNICODE_SCRIPT_Armenian, + UNICODE_SCRIPT_Avestan, + UNICODE_SCRIPT_Balinese, + UNICODE_SCRIPT_Bamum, + UNICODE_SCRIPT_Bassa_Vah, + UNICODE_SCRIPT_Batak, + UNICODE_SCRIPT_Bengali, + UNICODE_SCRIPT_Bhaiksuki, + UNICODE_SCRIPT_Bopomofo, + UNICODE_SCRIPT_Brahmi, + UNICODE_SCRIPT_Braille, + UNICODE_SCRIPT_Buginese, + UNICODE_SCRIPT_Buhid, + UNICODE_SCRIPT_Canadian_Aboriginal, + UNICODE_SCRIPT_Carian, + UNICODE_SCRIPT_Caucasian_Albanian, + UNICODE_SCRIPT_Chakma, + UNICODE_SCRIPT_Cham, + UNICODE_SCRIPT_Cherokee, + UNICODE_SCRIPT_Common, + UNICODE_SCRIPT_Coptic, + UNICODE_SCRIPT_Cuneiform, + UNICODE_SCRIPT_Cypriot, + UNICODE_SCRIPT_Cyrillic, + UNICODE_SCRIPT_Deseret, + UNICODE_SCRIPT_Devanagari, + UNICODE_SCRIPT_Dogra, + UNICODE_SCRIPT_Duployan, + UNICODE_SCRIPT_Egyptian_Hieroglyphs, + UNICODE_SCRIPT_Elbasan, + UNICODE_SCRIPT_Elymaic, + UNICODE_SCRIPT_Ethiopic, + UNICODE_SCRIPT_Georgian, + UNICODE_SCRIPT_Glagolitic, + UNICODE_SCRIPT_Gothic, + UNICODE_SCRIPT_Grantha, + UNICODE_SCRIPT_Greek, + UNICODE_SCRIPT_Gujarati, + UNICODE_SCRIPT_Gunjala_Gondi, + UNICODE_SCRIPT_Gurmukhi, + UNICODE_SCRIPT_Han, + UNICODE_SCRIPT_Hangul, + UNICODE_SCRIPT_Hanifi_Rohingya, + UNICODE_SCRIPT_Hanunoo, + UNICODE_SCRIPT_Hatran, + UNICODE_SCRIPT_Hebrew, + UNICODE_SCRIPT_Hiragana, + UNICODE_SCRIPT_Imperial_Aramaic, + UNICODE_SCRIPT_Inherited, + UNICODE_SCRIPT_Inscriptional_Pahlavi, + UNICODE_SCRIPT_Inscriptional_Parthian, + UNICODE_SCRIPT_Javanese, + UNICODE_SCRIPT_Kaithi, + UNICODE_SCRIPT_Kannada, + UNICODE_SCRIPT_Katakana, + UNICODE_SCRIPT_Kayah_Li, + UNICODE_SCRIPT_Kharoshthi, + UNICODE_SCRIPT_Khmer, + UNICODE_SCRIPT_Khojki, + UNICODE_SCRIPT_Khudawadi, + UNICODE_SCRIPT_Lao, + UNICODE_SCRIPT_Latin, + UNICODE_SCRIPT_Lepcha, + UNICODE_SCRIPT_Limbu, + UNICODE_SCRIPT_Linear_A, + UNICODE_SCRIPT_Linear_B, + UNICODE_SCRIPT_Lisu, + UNICODE_SCRIPT_Lycian, + UNICODE_SCRIPT_Lydian, + UNICODE_SCRIPT_Makasar, + UNICODE_SCRIPT_Mahajani, + UNICODE_SCRIPT_Malayalam, + UNICODE_SCRIPT_Mandaic, + UNICODE_SCRIPT_Manichaean, + UNICODE_SCRIPT_Marchen, + UNICODE_SCRIPT_Masaram_Gondi, + UNICODE_SCRIPT_Medefaidrin, + UNICODE_SCRIPT_Meetei_Mayek, + UNICODE_SCRIPT_Mende_Kikakui, + UNICODE_SCRIPT_Meroitic_Cursive, + UNICODE_SCRIPT_Meroitic_Hieroglyphs, + UNICODE_SCRIPT_Miao, + UNICODE_SCRIPT_Modi, + UNICODE_SCRIPT_Mongolian, + UNICODE_SCRIPT_Mro, + UNICODE_SCRIPT_Multani, + UNICODE_SCRIPT_Myanmar, + UNICODE_SCRIPT_Nabataean, + UNICODE_SCRIPT_Nandinagari, + UNICODE_SCRIPT_New_Tai_Lue, + UNICODE_SCRIPT_Newa, + UNICODE_SCRIPT_Nko, + UNICODE_SCRIPT_Nushu, + UNICODE_SCRIPT_Nyiakeng_Puachue_Hmong, + UNICODE_SCRIPT_Ogham, + UNICODE_SCRIPT_Ol_Chiki, + UNICODE_SCRIPT_Old_Hungarian, + UNICODE_SCRIPT_Old_Italic, + UNICODE_SCRIPT_Old_North_Arabian, + UNICODE_SCRIPT_Old_Permic, + UNICODE_SCRIPT_Old_Persian, + UNICODE_SCRIPT_Old_Sogdian, + UNICODE_SCRIPT_Old_South_Arabian, + UNICODE_SCRIPT_Old_Turkic, + UNICODE_SCRIPT_Oriya, + UNICODE_SCRIPT_Osage, + UNICODE_SCRIPT_Osmanya, + UNICODE_SCRIPT_Pahawh_Hmong, + UNICODE_SCRIPT_Palmyrene, + UNICODE_SCRIPT_Pau_Cin_Hau, + UNICODE_SCRIPT_Phags_Pa, + UNICODE_SCRIPT_Phoenician, + UNICODE_SCRIPT_Psalter_Pahlavi, + UNICODE_SCRIPT_Rejang, + UNICODE_SCRIPT_Runic, + UNICODE_SCRIPT_Samaritan, + UNICODE_SCRIPT_Saurashtra, + UNICODE_SCRIPT_Sharada, + UNICODE_SCRIPT_Shavian, + UNICODE_SCRIPT_Siddham, + UNICODE_SCRIPT_SignWriting, + UNICODE_SCRIPT_Sinhala, + UNICODE_SCRIPT_Sogdian, + UNICODE_SCRIPT_Sora_Sompeng, + UNICODE_SCRIPT_Soyombo, + UNICODE_SCRIPT_Sundanese, + UNICODE_SCRIPT_Syloti_Nagri, + UNICODE_SCRIPT_Syriac, + UNICODE_SCRIPT_Tagalog, + UNICODE_SCRIPT_Tagbanwa, + UNICODE_SCRIPT_Tai_Le, + UNICODE_SCRIPT_Tai_Tham, + UNICODE_SCRIPT_Tai_Viet, + UNICODE_SCRIPT_Takri, + UNICODE_SCRIPT_Tamil, + UNICODE_SCRIPT_Tangut, + UNICODE_SCRIPT_Telugu, + UNICODE_SCRIPT_Thaana, + UNICODE_SCRIPT_Thai, + UNICODE_SCRIPT_Tibetan, + UNICODE_SCRIPT_Tifinagh, + UNICODE_SCRIPT_Tirhuta, + UNICODE_SCRIPT_Ugaritic, + UNICODE_SCRIPT_Vai, + UNICODE_SCRIPT_Wancho, + UNICODE_SCRIPT_Warang_Citi, + UNICODE_SCRIPT_Yi, + UNICODE_SCRIPT_Zanabazar_Square, + UNICODE_SCRIPT_COUNT, +} UnicodeScriptEnum; + +static const char unicode_script_name_table[] = + "Adlam,Adlm" + "\0" + "Ahom,Ahom" + "\0" + "Anatolian_Hieroglyphs,Hluw" + "\0" + "Arabic,Arab" + "\0" + "Armenian,Armn" + "\0" + "Avestan,Avst" + "\0" + "Balinese,Bali" + "\0" + "Bamum,Bamu" + "\0" + "Bassa_Vah,Bass" + "\0" + "Batak,Batk" + "\0" + "Bengali,Beng" + "\0" + "Bhaiksuki,Bhks" + "\0" + "Bopomofo,Bopo" + "\0" + "Brahmi,Brah" + "\0" + "Braille,Brai" + "\0" + "Buginese,Bugi" + "\0" + "Buhid,Buhd" + "\0" + "Canadian_Aboriginal,Cans" + "\0" + "Carian,Cari" + "\0" + "Caucasian_Albanian,Aghb" + "\0" + "Chakma,Cakm" + "\0" + "Cham,Cham" + "\0" + "Cherokee,Cher" + "\0" + "Common,Zyyy" + "\0" + "Coptic,Copt,Qaac" + "\0" + "Cuneiform,Xsux" + "\0" + "Cypriot,Cprt" + "\0" + "Cyrillic,Cyrl" + "\0" + "Deseret,Dsrt" + "\0" + "Devanagari,Deva" + "\0" + "Dogra,Dogr" + "\0" + "Duployan,Dupl" + "\0" + "Egyptian_Hieroglyphs,Egyp" + "\0" + "Elbasan,Elba" + "\0" + "Elymaic,Elym" + "\0" + "Ethiopic,Ethi" + "\0" + "Georgian,Geor" + "\0" + "Glagolitic,Glag" + "\0" + "Gothic,Goth" + "\0" + "Grantha,Gran" + "\0" + "Greek,Grek" + "\0" + "Gujarati,Gujr" + "\0" + "Gunjala_Gondi,Gong" + "\0" + "Gurmukhi,Guru" + "\0" + "Han,Hani" + "\0" + "Hangul,Hang" + "\0" + "Hanifi_Rohingya,Rohg" + "\0" + "Hanunoo,Hano" + "\0" + "Hatran,Hatr" + "\0" + "Hebrew,Hebr" + "\0" + "Hiragana,Hira" + "\0" + "Imperial_Aramaic,Armi" + "\0" + "Inherited,Zinh,Qaai" + "\0" + "Inscriptional_Pahlavi,Phli" + "\0" + "Inscriptional_Parthian,Prti" + "\0" + "Javanese,Java" + "\0" + "Kaithi,Kthi" + "\0" + "Kannada,Knda" + "\0" + "Katakana,Kana" + "\0" + "Kayah_Li,Kali" + "\0" + "Kharoshthi,Khar" + "\0" + "Khmer,Khmr" + "\0" + "Khojki,Khoj" + "\0" + "Khudawadi,Sind" + "\0" + "Lao,Laoo" + "\0" + "Latin,Latn" + "\0" + "Lepcha,Lepc" + "\0" + "Limbu,Limb" + "\0" + "Linear_A,Lina" + "\0" + "Linear_B,Linb" + "\0" + "Lisu,Lisu" + "\0" + "Lycian,Lyci" + "\0" + "Lydian,Lydi" + "\0" + "Makasar,Maka" + "\0" + "Mahajani,Mahj" + "\0" + "Malayalam,Mlym" + "\0" + "Mandaic,Mand" + "\0" + "Manichaean,Mani" + "\0" + "Marchen,Marc" + "\0" + "Masaram_Gondi,Gonm" + "\0" + "Medefaidrin,Medf" + "\0" + "Meetei_Mayek,Mtei" + "\0" + "Mende_Kikakui,Mend" + "\0" + "Meroitic_Cursive,Merc" + "\0" + "Meroitic_Hieroglyphs,Mero" + "\0" + "Miao,Plrd" + "\0" + "Modi,Modi" + "\0" + "Mongolian,Mong" + "\0" + "Mro,Mroo" + "\0" + "Multani,Mult" + "\0" + "Myanmar,Mymr" + "\0" + "Nabataean,Nbat" + "\0" + "Nandinagari,Nand" + "\0" + "New_Tai_Lue,Talu" + "\0" + "Newa,Newa" + "\0" + "Nko,Nkoo" + "\0" + "Nushu,Nshu" + "\0" + "Nyiakeng_Puachue_Hmong,Hmnp" + "\0" + "Ogham,Ogam" + "\0" + "Ol_Chiki,Olck" + "\0" + "Old_Hungarian,Hung" + "\0" + "Old_Italic,Ital" + "\0" + "Old_North_Arabian,Narb" + "\0" + "Old_Permic,Perm" + "\0" + "Old_Persian,Xpeo" + "\0" + "Old_Sogdian,Sogo" + "\0" + "Old_South_Arabian,Sarb" + "\0" + "Old_Turkic,Orkh" + "\0" + "Oriya,Orya" + "\0" + "Osage,Osge" + "\0" + "Osmanya,Osma" + "\0" + "Pahawh_Hmong,Hmng" + "\0" + "Palmyrene,Palm" + "\0" + "Pau_Cin_Hau,Pauc" + "\0" + "Phags_Pa,Phag" + "\0" + "Phoenician,Phnx" + "\0" + "Psalter_Pahlavi,Phlp" + "\0" + "Rejang,Rjng" + "\0" + "Runic,Runr" + "\0" + "Samaritan,Samr" + "\0" + "Saurashtra,Saur" + "\0" + "Sharada,Shrd" + "\0" + "Shavian,Shaw" + "\0" + "Siddham,Sidd" + "\0" + "SignWriting,Sgnw" + "\0" + "Sinhala,Sinh" + "\0" + "Sogdian,Sogd" + "\0" + "Sora_Sompeng,Sora" + "\0" + "Soyombo,Soyo" + "\0" + "Sundanese,Sund" + "\0" + "Syloti_Nagri,Sylo" + "\0" + "Syriac,Syrc" + "\0" + "Tagalog,Tglg" + "\0" + "Tagbanwa,Tagb" + "\0" + "Tai_Le,Tale" + "\0" + "Tai_Tham,Lana" + "\0" + "Tai_Viet,Tavt" + "\0" + "Takri,Takr" + "\0" + "Tamil,Taml" + "\0" + "Tangut,Tang" + "\0" + "Telugu,Telu" + "\0" + "Thaana,Thaa" + "\0" + "Thai,Thai" + "\0" + "Tibetan,Tibt" + "\0" + "Tifinagh,Tfng" + "\0" + "Tirhuta,Tirh" + "\0" + "Ugaritic,Ugar" + "\0" + "Vai,Vaii" + "\0" + "Wancho,Wcho" + "\0" + "Warang_Citi,Wara" + "\0" + "Yi,Yiii" + "\0" + "Zanabazar_Square,Zanb" + "\0"; + +static const uint8_t unicode_script_table[2565] = { + 0xc0, 0x18, 0x99, 0x42, 0x85, 0x18, 0x99, 0x42, 0xae, 0x18, 0x80, 0x42, + 0x8e, 0x18, 0x80, 0x42, 0x84, 0x18, 0x96, 0x42, 0x80, 0x18, 0x9e, 0x42, + 0x80, 0x18, 0xe1, 0x60, 0x42, 0xa6, 0x18, 0x84, 0x42, 0x84, 0x18, 0x81, + 0x0d, 0x93, 0x18, 0xe0, 0x0f, 0x35, 0x83, 0x29, 0x80, 0x18, 0x82, 0x29, + 0x01, 0x83, 0x29, 0x80, 0x18, 0x80, 0x29, 0x03, 0x80, 0x29, 0x80, 0x18, + 0x80, 0x29, 0x80, 0x18, 0x82, 0x29, 0x00, 0x80, 0x29, 0x00, 0x93, 0x29, + 0x00, 0xbe, 0x29, 0x8d, 0x19, 0x8f, 0x29, 0xe0, 0x24, 0x1c, 0x81, 0x35, + 0xe0, 0x48, 0x1c, 0x00, 0xa5, 0x05, 0x01, 0xaf, 0x05, 0x80, 0x18, 0x80, + 0x05, 0x01, 0x82, 0x05, 0x00, 0xb6, 0x32, 0x07, 0x9a, 0x32, 0x03, 0x85, + 0x32, 0x0a, 0x84, 0x04, 0x80, 0x18, 0x85, 0x04, 0x80, 0x18, 0x8d, 0x04, + 0x80, 0x18, 0x80, 0x04, 0x00, 0x80, 0x04, 0x80, 0x18, 0x9f, 0x04, 0x80, + 0x18, 0x89, 0x04, 0x8a, 0x35, 0x99, 0x04, 0x80, 0x35, 0xe0, 0x0b, 0x04, + 0x80, 0x18, 0xa1, 0x04, 0x8d, 0x84, 0x00, 0xbb, 0x84, 0x01, 0x82, 0x84, + 0xaf, 0x04, 0xb1, 0x8e, 0x0d, 0xba, 0x60, 0x01, 0x82, 0x60, 0xad, 0x78, + 0x01, 0x8e, 0x78, 0x00, 0x9b, 0x4d, 0x01, 0x80, 0x4d, 0x00, 0x8a, 0x84, + 0x34, 0x94, 0x04, 0x00, 0x87, 0x04, 0x14, 0x8e, 0x04, 0x80, 0x18, 0x9c, + 0x04, 0xd0, 0x1e, 0x83, 0x35, 0x8e, 0x1e, 0x81, 0x18, 0x99, 0x1e, 0x83, + 0x0b, 0x00, 0x87, 0x0b, 0x01, 0x81, 0x0b, 0x01, 0x95, 0x0b, 0x00, 0x86, + 0x0b, 0x00, 0x80, 0x0b, 0x02, 0x83, 0x0b, 0x01, 0x88, 0x0b, 0x01, 0x81, + 0x0b, 0x01, 0x83, 0x0b, 0x07, 0x80, 0x0b, 0x03, 0x81, 0x0b, 0x00, 0x84, + 0x0b, 0x01, 0x98, 0x0b, 0x01, 0x82, 0x2c, 0x00, 0x85, 0x2c, 0x03, 0x81, + 0x2c, 0x01, 0x95, 0x2c, 0x00, 0x86, 0x2c, 0x00, 0x81, 0x2c, 0x00, 0x81, + 0x2c, 0x00, 0x81, 0x2c, 0x01, 0x80, 0x2c, 0x00, 0x84, 0x2c, 0x03, 0x81, + 0x2c, 0x01, 0x82, 0x2c, 0x02, 0x80, 0x2c, 0x06, 0x83, 0x2c, 0x00, 0x80, + 0x2c, 0x06, 0x90, 0x2c, 0x09, 0x82, 0x2a, 0x00, 0x88, 0x2a, 0x00, 0x82, + 0x2a, 0x00, 0x95, 0x2a, 0x00, 0x86, 0x2a, 0x00, 0x81, 0x2a, 0x00, 0x84, + 0x2a, 0x01, 0x89, 0x2a, 0x00, 0x82, 0x2a, 0x00, 0x82, 0x2a, 0x01, 0x80, + 0x2a, 0x0e, 0x83, 0x2a, 0x01, 0x8b, 0x2a, 0x06, 0x86, 0x2a, 0x00, 0x82, + 0x6d, 0x00, 0x87, 0x6d, 0x01, 0x81, 0x6d, 0x01, 0x95, 0x6d, 0x00, 0x86, + 0x6d, 0x00, 0x81, 0x6d, 0x00, 0x84, 0x6d, 0x01, 0x88, 0x6d, 0x01, 0x81, + 0x6d, 0x01, 0x82, 0x6d, 0x07, 0x81, 0x6d, 0x03, 0x81, 0x6d, 0x00, 0x84, + 0x6d, 0x01, 0x91, 0x6d, 0x09, 0x81, 0x8b, 0x00, 0x85, 0x8b, 0x02, 0x82, + 0x8b, 0x00, 0x83, 0x8b, 0x02, 0x81, 0x8b, 0x00, 0x80, 0x8b, 0x00, 0x81, + 0x8b, 0x02, 0x81, 0x8b, 0x02, 0x82, 0x8b, 0x02, 0x8b, 0x8b, 0x03, 0x84, + 0x8b, 0x02, 0x82, 0x8b, 0x00, 0x83, 0x8b, 0x01, 0x80, 0x8b, 0x05, 0x80, + 0x8b, 0x0d, 0x94, 0x8b, 0x04, 0x8c, 0x8d, 0x00, 0x82, 0x8d, 0x00, 0x96, + 0x8d, 0x00, 0x8f, 0x8d, 0x02, 0x87, 0x8d, 0x00, 0x82, 0x8d, 0x00, 0x83, + 0x8d, 0x06, 0x81, 0x8d, 0x00, 0x82, 0x8d, 0x04, 0x83, 0x8d, 0x01, 0x89, + 0x8d, 0x06, 0x88, 0x8d, 0x8c, 0x3a, 0x00, 0x82, 0x3a, 0x00, 0x96, 0x3a, + 0x00, 0x89, 0x3a, 0x00, 0x84, 0x3a, 0x01, 0x88, 0x3a, 0x00, 0x82, 0x3a, + 0x00, 0x83, 0x3a, 0x06, 0x81, 0x3a, 0x06, 0x80, 0x3a, 0x00, 0x83, 0x3a, + 0x01, 0x89, 0x3a, 0x00, 0x81, 0x3a, 0x0c, 0x83, 0x4c, 0x00, 0x87, 0x4c, + 0x00, 0x82, 0x4c, 0x00, 0xb2, 0x4c, 0x00, 0x82, 0x4c, 0x00, 0x85, 0x4c, + 0x03, 0x8f, 0x4c, 0x01, 0x99, 0x4c, 0x01, 0x81, 0x7e, 0x00, 0x91, 0x7e, + 0x02, 0x97, 0x7e, 0x00, 0x88, 0x7e, 0x00, 0x80, 0x7e, 0x01, 0x86, 0x7e, + 0x02, 0x80, 0x7e, 0x03, 0x85, 0x7e, 0x00, 0x80, 0x7e, 0x00, 0x87, 0x7e, + 0x05, 0x89, 0x7e, 0x01, 0x82, 0x7e, 0x0b, 0xb9, 0x8f, 0x03, 0x80, 0x18, + 0x9b, 0x8f, 0x24, 0x81, 0x41, 0x00, 0x80, 0x41, 0x00, 0x84, 0x41, 0x00, + 0x97, 0x41, 0x00, 0x80, 0x41, 0x00, 0x96, 0x41, 0x01, 0x84, 0x41, 0x00, + 0x80, 0x41, 0x00, 0x85, 0x41, 0x01, 0x89, 0x41, 0x01, 0x83, 0x41, 0x1f, + 0xc7, 0x90, 0x00, 0xa3, 0x90, 0x03, 0xa6, 0x90, 0x00, 0xa3, 0x90, 0x00, + 0x8e, 0x90, 0x00, 0x86, 0x90, 0x83, 0x18, 0x81, 0x90, 0x24, 0xe0, 0x3f, + 0x5b, 0xa5, 0x25, 0x00, 0x80, 0x25, 0x04, 0x80, 0x25, 0x01, 0xaa, 0x25, + 0x80, 0x18, 0x83, 0x25, 0xe0, 0x9f, 0x2e, 0xc8, 0x24, 0x00, 0x83, 0x24, + 0x01, 0x86, 0x24, 0x00, 0x80, 0x24, 0x00, 0x83, 0x24, 0x01, 0xa8, 0x24, + 0x00, 0x83, 0x24, 0x01, 0xa0, 0x24, 0x00, 0x83, 0x24, 0x01, 0x86, 0x24, + 0x00, 0x80, 0x24, 0x00, 0x83, 0x24, 0x01, 0x8e, 0x24, 0x00, 0xb8, 0x24, + 0x00, 0x83, 0x24, 0x01, 0xc2, 0x24, 0x01, 0x9f, 0x24, 0x02, 0x99, 0x24, + 0x05, 0xd5, 0x17, 0x01, 0x85, 0x17, 0x01, 0xe2, 0x1f, 0x12, 0x9c, 0x63, + 0x02, 0xca, 0x77, 0x82, 0x18, 0x8a, 0x77, 0x06, 0x8c, 0x85, 0x00, 0x86, + 0x85, 0x0a, 0x94, 0x30, 0x81, 0x18, 0x08, 0x93, 0x11, 0x0b, 0x8c, 0x86, + 0x00, 0x82, 0x86, 0x00, 0x81, 0x86, 0x0b, 0xdd, 0x3e, 0x01, 0x89, 0x3e, + 0x05, 0x89, 0x3e, 0x05, 0x81, 0x58, 0x81, 0x18, 0x80, 0x58, 0x80, 0x18, + 0x88, 0x58, 0x00, 0x89, 0x58, 0x05, 0xd8, 0x58, 0x06, 0xaa, 0x58, 0x04, + 0xc5, 0x12, 0x09, 0x9e, 0x44, 0x00, 0x8b, 0x44, 0x03, 0x8b, 0x44, 0x03, + 0x80, 0x44, 0x02, 0x8b, 0x44, 0x9d, 0x87, 0x01, 0x84, 0x87, 0x0a, 0xab, + 0x5e, 0x03, 0x99, 0x5e, 0x05, 0x8a, 0x5e, 0x02, 0x81, 0x5e, 0x9f, 0x3e, + 0x9b, 0x10, 0x01, 0x81, 0x10, 0xbe, 0x88, 0x00, 0x9c, 0x88, 0x01, 0x8a, + 0x88, 0x05, 0x89, 0x88, 0x05, 0x8d, 0x88, 0x01, 0x8e, 0x35, 0x40, 0xcb, + 0x07, 0x03, 0xac, 0x07, 0x02, 0xbf, 0x82, 0xb3, 0x0a, 0x07, 0x83, 0x0a, + 0xb7, 0x43, 0x02, 0x8e, 0x43, 0x02, 0x82, 0x43, 0xaf, 0x64, 0x88, 0x1c, + 0x06, 0xaa, 0x25, 0x01, 0x82, 0x25, 0x87, 0x82, 0x07, 0x82, 0x35, 0x80, + 0x18, 0x8c, 0x35, 0x80, 0x18, 0x86, 0x35, 0x83, 0x18, 0x80, 0x35, 0x85, + 0x18, 0x80, 0x35, 0x82, 0x18, 0x81, 0x35, 0x80, 0x18, 0x04, 0xa5, 0x42, + 0x84, 0x29, 0x80, 0x1c, 0xb0, 0x42, 0x84, 0x29, 0x83, 0x42, 0x84, 0x29, + 0x8c, 0x42, 0x80, 0x1c, 0xc5, 0x42, 0x80, 0x29, 0xb9, 0x35, 0x00, 0x84, + 0x35, 0xe0, 0x9f, 0x42, 0x95, 0x29, 0x01, 0x85, 0x29, 0x01, 0xa5, 0x29, + 0x01, 0x85, 0x29, 0x01, 0x87, 0x29, 0x00, 0x80, 0x29, 0x00, 0x80, 0x29, + 0x00, 0x80, 0x29, 0x00, 0x9e, 0x29, 0x01, 0xb4, 0x29, 0x00, 0x8e, 0x29, + 0x00, 0x8d, 0x29, 0x01, 0x85, 0x29, 0x00, 0x92, 0x29, 0x01, 0x82, 0x29, + 0x00, 0x88, 0x29, 0x00, 0x8b, 0x18, 0x81, 0x35, 0xd6, 0x18, 0x00, 0x8a, + 0x18, 0x80, 0x42, 0x01, 0x8a, 0x18, 0x80, 0x42, 0x8e, 0x18, 0x00, 0x8c, + 0x42, 0x02, 0x9f, 0x18, 0x0f, 0xa0, 0x35, 0x0e, 0xa5, 0x18, 0x80, 0x29, + 0x82, 0x18, 0x81, 0x42, 0x85, 0x18, 0x80, 0x42, 0x9a, 0x18, 0x80, 0x42, + 0x90, 0x18, 0xa8, 0x42, 0x82, 0x18, 0x03, 0xe2, 0x36, 0x18, 0x18, 0x8a, + 0x18, 0x14, 0xe3, 0x3f, 0x18, 0xe0, 0x9f, 0x0f, 0xe2, 0x13, 0x18, 0x01, + 0x9f, 0x18, 0x01, 0xe0, 0x07, 0x18, 0xae, 0x26, 0x00, 0xae, 0x26, 0x00, + 0x9f, 0x42, 0xe0, 0x13, 0x19, 0x04, 0x86, 0x19, 0xa5, 0x25, 0x00, 0x80, + 0x25, 0x04, 0x80, 0x25, 0x01, 0xb7, 0x91, 0x06, 0x81, 0x91, 0x0d, 0x80, + 0x91, 0x96, 0x24, 0x08, 0x86, 0x24, 0x00, 0x86, 0x24, 0x00, 0x86, 0x24, + 0x00, 0x86, 0x24, 0x00, 0x86, 0x24, 0x00, 0x86, 0x24, 0x00, 0x86, 0x24, + 0x00, 0x86, 0x24, 0x00, 0x9f, 0x1c, 0xcf, 0x18, 0x2f, 0x99, 0x2d, 0x00, + 0xd8, 0x2d, 0x0b, 0xe0, 0x75, 0x2d, 0x19, 0x8b, 0x18, 0x03, 0x84, 0x18, + 0x80, 0x2d, 0x80, 0x18, 0x80, 0x2d, 0x98, 0x18, 0x88, 0x2d, 0x83, 0x35, + 0x81, 0x2e, 0x87, 0x18, 0x83, 0x2d, 0x83, 0x18, 0x00, 0xd5, 0x33, 0x01, + 0x81, 0x35, 0x81, 0x18, 0x82, 0x33, 0x80, 0x18, 0xd9, 0x3b, 0x81, 0x18, + 0x82, 0x3b, 0x04, 0xaa, 0x0d, 0x00, 0xdd, 0x2e, 0x00, 0x8f, 0x18, 0x9a, + 0x0d, 0x04, 0xa3, 0x18, 0x0b, 0x8f, 0x3b, 0x9e, 0x2e, 0x00, 0xbf, 0x18, + 0x9e, 0x2e, 0xd0, 0x18, 0xae, 0x3b, 0x80, 0x18, 0xd7, 0x3b, 0xe0, 0x47, + 0x18, 0xf0, 0x09, 0x55, 0x2d, 0x09, 0xbf, 0x18, 0xf0, 0x41, 0x8f, 0x2d, + 0x0f, 0xe4, 0x2c, 0x97, 0x02, 0xb6, 0x97, 0x08, 0xaf, 0x47, 0xe0, 0xcb, + 0x94, 0x13, 0xdf, 0x1c, 0xd7, 0x08, 0x07, 0xa1, 0x18, 0xe0, 0x05, 0x42, + 0x82, 0x18, 0xb4, 0x42, 0x01, 0x84, 0x42, 0x2f, 0x88, 0x42, 0xab, 0x83, + 0x03, 0x89, 0x18, 0x05, 0xb7, 0x73, 0x07, 0xc5, 0x79, 0x07, 0x8b, 0x79, + 0x05, 0x9f, 0x1e, 0xad, 0x3c, 0x80, 0x18, 0x80, 0x3c, 0xa3, 0x76, 0x0a, + 0x80, 0x76, 0x9c, 0x2e, 0x02, 0xcd, 0x38, 0x00, 0x80, 0x18, 0x89, 0x38, + 0x03, 0x81, 0x38, 0x9e, 0x5b, 0x00, 0xb6, 0x16, 0x08, 0x8d, 0x16, 0x01, + 0x89, 0x16, 0x01, 0x83, 0x16, 0x9f, 0x5b, 0xc2, 0x89, 0x17, 0x84, 0x89, + 0x96, 0x52, 0x09, 0x85, 0x24, 0x01, 0x85, 0x24, 0x01, 0x85, 0x24, 0x08, + 0x86, 0x24, 0x00, 0x86, 0x24, 0x00, 0xaa, 0x42, 0x80, 0x18, 0x88, 0x42, + 0x80, 0x29, 0x81, 0x42, 0x07, 0xcf, 0x17, 0xad, 0x52, 0x01, 0x89, 0x52, + 0x05, 0xf0, 0x1b, 0x43, 0x2e, 0x0b, 0x96, 0x2e, 0x03, 0xb0, 0x2e, 0x70, + 0x10, 0xa3, 0xe1, 0x0d, 0x2d, 0x01, 0xe0, 0x09, 0x2d, 0x25, 0x86, 0x42, + 0x0b, 0x84, 0x05, 0x04, 0x99, 0x32, 0x00, 0x84, 0x32, 0x00, 0x80, 0x32, + 0x00, 0x81, 0x32, 0x00, 0x81, 0x32, 0x00, 0x89, 0x32, 0xe0, 0x11, 0x04, + 0x10, 0xe1, 0x0a, 0x04, 0x81, 0x18, 0x0f, 0xbf, 0x04, 0x01, 0xb5, 0x04, + 0x27, 0x8d, 0x04, 0x01, 0x8f, 0x35, 0x89, 0x18, 0x05, 0x8d, 0x35, 0x81, + 0x1c, 0xa2, 0x18, 0x00, 0x92, 0x18, 0x00, 0x83, 0x18, 0x03, 0x84, 0x04, + 0x00, 0xe0, 0x26, 0x04, 0x01, 0x80, 0x18, 0x00, 0x9f, 0x18, 0x99, 0x42, + 0x85, 0x18, 0x99, 0x42, 0x8a, 0x18, 0x89, 0x3b, 0x80, 0x18, 0xac, 0x3b, + 0x81, 0x18, 0x9e, 0x2e, 0x02, 0x85, 0x2e, 0x01, 0x85, 0x2e, 0x01, 0x85, + 0x2e, 0x01, 0x82, 0x2e, 0x02, 0x86, 0x18, 0x00, 0x86, 0x18, 0x09, 0x84, + 0x18, 0x01, 0x8b, 0x46, 0x00, 0x99, 0x46, 0x00, 0x92, 0x46, 0x00, 0x81, + 0x46, 0x00, 0x8e, 0x46, 0x01, 0x8d, 0x46, 0x21, 0xe0, 0x1a, 0x46, 0x04, + 0x82, 0x18, 0x03, 0xac, 0x18, 0x02, 0x88, 0x18, 0xce, 0x29, 0x00, 0x8b, + 0x18, 0x03, 0x80, 0x29, 0x2e, 0xac, 0x18, 0x80, 0x35, 0x60, 0x21, 0x9c, + 0x48, 0x02, 0xb0, 0x13, 0x0e, 0x80, 0x35, 0x9a, 0x18, 0x03, 0xa3, 0x66, + 0x08, 0x82, 0x66, 0x9a, 0x27, 0x04, 0xaa, 0x68, 0x04, 0x9d, 0x93, 0x00, + 0x80, 0x93, 0xa3, 0x69, 0x03, 0x8d, 0x69, 0x29, 0xcf, 0x1d, 0xaf, 0x7b, + 0x9d, 0x6f, 0x01, 0x89, 0x6f, 0x05, 0xa3, 0x6e, 0x03, 0xa3, 0x6e, 0x03, + 0xa7, 0x22, 0x07, 0xb3, 0x14, 0x0a, 0x80, 0x14, 0x60, 0x2f, 0xe0, 0xd6, + 0x45, 0x08, 0x95, 0x45, 0x09, 0x87, 0x45, 0x60, 0x37, 0x85, 0x1b, 0x01, + 0x80, 0x1b, 0x00, 0xab, 0x1b, 0x00, 0x81, 0x1b, 0x02, 0x80, 0x1b, 0x01, + 0x80, 0x1b, 0x95, 0x34, 0x00, 0x88, 0x34, 0x9f, 0x71, 0x9e, 0x5c, 0x07, + 0x88, 0x5c, 0x2f, 0x92, 0x31, 0x00, 0x81, 0x31, 0x04, 0x84, 0x31, 0x9b, + 0x74, 0x02, 0x80, 0x74, 0x99, 0x49, 0x04, 0x80, 0x49, 0x3f, 0x9f, 0x55, + 0x97, 0x54, 0x03, 0x93, 0x54, 0x01, 0xad, 0x54, 0x83, 0x3d, 0x00, 0x81, + 0x3d, 0x04, 0x87, 0x3d, 0x00, 0x82, 0x3d, 0x00, 0x9c, 0x3d, 0x01, 0x82, + 0x3d, 0x03, 0x89, 0x3d, 0x06, 0x88, 0x3d, 0x06, 0x9f, 0x6b, 0x9f, 0x67, + 0x1f, 0xa6, 0x4e, 0x03, 0x8b, 0x4e, 0x08, 0xb5, 0x06, 0x02, 0x86, 0x06, + 0x95, 0x37, 0x01, 0x87, 0x37, 0x92, 0x36, 0x04, 0x87, 0x36, 0x91, 0x75, + 0x06, 0x83, 0x75, 0x0b, 0x86, 0x75, 0x4f, 0xc8, 0x6c, 0x36, 0xb2, 0x65, + 0x0c, 0xb2, 0x65, 0x06, 0x85, 0x65, 0xa7, 0x2f, 0x07, 0x89, 0x2f, 0x60, + 0xc5, 0x9e, 0x04, 0x60, 0x20, 0xa7, 0x6a, 0x07, 0xa9, 0x7f, 0x60, 0x25, + 0x96, 0x23, 0x08, 0xcd, 0x0e, 0x03, 0x9d, 0x0e, 0x0e, 0x80, 0x0e, 0xc1, + 0x39, 0x0a, 0x80, 0x39, 0x01, 0x98, 0x80, 0x06, 0x89, 0x80, 0x05, 0xb4, + 0x15, 0x00, 0x90, 0x15, 0x08, 0xa6, 0x4b, 0x08, 0xcd, 0x7a, 0x01, 0x8f, + 0x7a, 0x00, 0x93, 0x7e, 0x0a, 0x91, 0x3f, 0x00, 0xab, 0x3f, 0x40, 0x86, + 0x5a, 0x00, 0x80, 0x5a, 0x00, 0x83, 0x5a, 0x00, 0x8e, 0x5a, 0x00, 0x8a, + 0x5a, 0x05, 0xba, 0x40, 0x04, 0x89, 0x40, 0x05, 0x83, 0x28, 0x00, 0x87, + 0x28, 0x01, 0x81, 0x28, 0x01, 0x95, 0x28, 0x00, 0x86, 0x28, 0x00, 0x81, + 0x28, 0x00, 0x84, 0x28, 0x00, 0x80, 0x35, 0x88, 0x28, 0x01, 0x81, 0x28, + 0x01, 0x82, 0x28, 0x01, 0x80, 0x28, 0x05, 0x80, 0x28, 0x04, 0x86, 0x28, + 0x01, 0x86, 0x28, 0x02, 0x84, 0x28, 0x60, 0x2a, 0xd9, 0x5f, 0x00, 0x80, + 0x5f, 0x00, 0x82, 0x5f, 0x1f, 0xc7, 0x92, 0x07, 0x89, 0x92, 0x60, 0x45, + 0xb5, 0x7c, 0x01, 0xa5, 0x7c, 0x21, 0xc4, 0x57, 0x0a, 0x89, 0x57, 0x05, + 0x8c, 0x58, 0x12, 0xb8, 0x8a, 0x06, 0x89, 0x8a, 0x35, 0x9a, 0x02, 0x01, + 0x8e, 0x02, 0x03, 0x8f, 0x02, 0x60, 0x5f, 0xbb, 0x1f, 0x60, 0x03, 0xd2, + 0x96, 0x0b, 0x80, 0x96, 0x60, 0x3f, 0x87, 0x5d, 0x01, 0xad, 0x5d, 0x01, + 0x8a, 0x5d, 0x1a, 0xc7, 0x98, 0x07, 0xd2, 0x81, 0x1c, 0xb8, 0x72, 0x60, + 0xa6, 0x88, 0x0c, 0x00, 0xac, 0x0c, 0x00, 0x8d, 0x0c, 0x09, 0x9c, 0x0c, + 0x02, 0x9f, 0x4f, 0x01, 0x95, 0x4f, 0x00, 0x8d, 0x4f, 0x48, 0x86, 0x50, + 0x00, 0x81, 0x50, 0x00, 0xab, 0x50, 0x02, 0x80, 0x50, 0x00, 0x81, 0x50, + 0x00, 0x88, 0x50, 0x07, 0x89, 0x50, 0x05, 0x85, 0x2b, 0x00, 0x81, 0x2b, + 0x00, 0xa4, 0x2b, 0x00, 0x81, 0x2b, 0x00, 0x85, 0x2b, 0x06, 0x89, 0x2b, + 0x60, 0xd5, 0x98, 0x4a, 0x60, 0x66, 0xb1, 0x8b, 0x0c, 0x80, 0x8b, 0xe3, + 0x39, 0x1a, 0x60, 0x05, 0xe0, 0x0e, 0x1a, 0x00, 0x84, 0x1a, 0x0a, 0xe0, + 0x63, 0x1a, 0x6a, 0x5b, 0xe3, 0xce, 0x21, 0x00, 0x88, 0x21, 0x6f, 0x66, + 0xe1, 0xe6, 0x03, 0x70, 0x11, 0x58, 0xe1, 0xd8, 0x08, 0x06, 0x9e, 0x59, + 0x00, 0x89, 0x59, 0x03, 0x81, 0x59, 0x5f, 0x9d, 0x09, 0x01, 0x85, 0x09, + 0x09, 0xc5, 0x70, 0x09, 0x89, 0x70, 0x00, 0x86, 0x70, 0x00, 0x94, 0x70, + 0x04, 0x92, 0x70, 0x62, 0x4f, 0xda, 0x51, 0x60, 0x04, 0xca, 0x56, 0x03, + 0xb8, 0x56, 0x06, 0x90, 0x56, 0x3f, 0x80, 0x8c, 0x80, 0x61, 0x81, 0x18, + 0x1b, 0xf0, 0x07, 0x97, 0x8c, 0x07, 0xe2, 0x92, 0x8c, 0x70, 0x14, 0xac, + 0x80, 0x3b, 0xe0, 0xbd, 0x33, 0x30, 0x82, 0x33, 0x10, 0x83, 0x3b, 0x07, + 0xe1, 0x2b, 0x61, 0x68, 0xa3, 0xe0, 0x0a, 0x20, 0x04, 0x8c, 0x20, 0x02, + 0x88, 0x20, 0x06, 0x89, 0x20, 0x01, 0x83, 0x20, 0x83, 0x18, 0x70, 0x02, + 0xfb, 0xe0, 0x95, 0x18, 0x09, 0xa6, 0x18, 0x01, 0xbd, 0x18, 0x82, 0x35, + 0x90, 0x18, 0x87, 0x35, 0x81, 0x18, 0x86, 0x35, 0x9d, 0x18, 0x83, 0x35, + 0xba, 0x18, 0x16, 0xc5, 0x29, 0x60, 0x39, 0x93, 0x18, 0x0b, 0xd6, 0x18, + 0x08, 0x98, 0x18, 0x60, 0x26, 0xd4, 0x18, 0x00, 0xc6, 0x18, 0x00, 0x81, + 0x18, 0x01, 0x80, 0x18, 0x01, 0x81, 0x18, 0x01, 0x83, 0x18, 0x00, 0x8b, + 0x18, 0x00, 0x80, 0x18, 0x00, 0x86, 0x18, 0x00, 0xc0, 0x18, 0x00, 0x83, + 0x18, 0x01, 0x87, 0x18, 0x00, 0x86, 0x18, 0x00, 0x9b, 0x18, 0x00, 0x83, + 0x18, 0x00, 0x84, 0x18, 0x00, 0x80, 0x18, 0x02, 0x86, 0x18, 0x00, 0xe0, + 0xf3, 0x18, 0x01, 0xe0, 0xc3, 0x18, 0x01, 0xb1, 0x18, 0xe2, 0x2b, 0x7d, + 0x0e, 0x84, 0x7d, 0x00, 0x8e, 0x7d, 0x64, 0xef, 0x86, 0x26, 0x00, 0x90, + 0x26, 0x01, 0x86, 0x26, 0x00, 0x81, 0x26, 0x00, 0x84, 0x26, 0x60, 0x74, + 0xac, 0x62, 0x02, 0x8d, 0x62, 0x01, 0x89, 0x62, 0x03, 0x81, 0x62, 0x61, + 0x0f, 0xb9, 0x95, 0x04, 0x80, 0x95, 0x64, 0x9f, 0xe0, 0x64, 0x53, 0x01, + 0x8f, 0x53, 0x28, 0xcb, 0x01, 0x03, 0x89, 0x01, 0x03, 0x81, 0x01, 0x62, + 0xb0, 0xc3, 0x18, 0x4b, 0xbc, 0x18, 0x60, 0x61, 0x83, 0x04, 0x00, 0x9a, + 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, 0x01, 0x80, 0x04, 0x00, 0x89, + 0x04, 0x00, 0x83, 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x05, 0x80, + 0x04, 0x03, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, 0x82, + 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, 0x01, 0x80, 0x04, 0x00, 0x80, + 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, 0x81, + 0x04, 0x00, 0x80, 0x04, 0x01, 0x83, 0x04, 0x00, 0x86, 0x04, 0x00, 0x83, + 0x04, 0x00, 0x83, 0x04, 0x00, 0x80, 0x04, 0x00, 0x89, 0x04, 0x00, 0x90, + 0x04, 0x04, 0x82, 0x04, 0x00, 0x84, 0x04, 0x00, 0x90, 0x04, 0x33, 0x81, + 0x04, 0x60, 0xad, 0xab, 0x18, 0x03, 0xe0, 0x03, 0x18, 0x0b, 0x8e, 0x18, + 0x01, 0x8e, 0x18, 0x00, 0x8e, 0x18, 0x00, 0xa4, 0x18, 0x09, 0x8c, 0x18, + 0x02, 0xdc, 0x18, 0x02, 0xbc, 0x18, 0x38, 0x99, 0x18, 0x80, 0x33, 0x81, + 0x18, 0x0c, 0xab, 0x18, 0x03, 0x88, 0x18, 0x06, 0x81, 0x18, 0x0d, 0x85, + 0x18, 0x60, 0x39, 0xe3, 0x75, 0x18, 0x09, 0x8c, 0x18, 0x02, 0x8a, 0x18, + 0x04, 0xe0, 0x13, 0x18, 0x0b, 0xd8, 0x18, 0x06, 0x8b, 0x18, 0x13, 0x8b, + 0x18, 0x03, 0xb7, 0x18, 0x07, 0x89, 0x18, 0x05, 0xa7, 0x18, 0x07, 0x9d, + 0x18, 0x51, 0x8b, 0x18, 0x00, 0xe0, 0x04, 0x18, 0x00, 0x83, 0x18, 0x02, + 0xa8, 0x18, 0x01, 0x85, 0x18, 0x02, 0x9c, 0x18, 0x01, 0xe0, 0x26, 0x18, + 0x0b, 0x8d, 0x18, 0x01, 0x83, 0x18, 0x03, 0x82, 0x18, 0x04, 0x82, 0x18, + 0x0c, 0x85, 0x18, 0x65, 0x09, 0xf0, 0x96, 0x76, 0x2d, 0x28, 0xef, 0xd4, + 0x2d, 0x0a, 0xe0, 0x7d, 0x2d, 0x01, 0xf0, 0x06, 0x21, 0x2d, 0x0d, 0xf0, + 0x0c, 0xd0, 0x2d, 0x6b, 0xbe, 0xe1, 0xbd, 0x2d, 0x7a, 0xf5, 0x82, 0x80, + 0x18, 0x1d, 0xdf, 0x18, 0x60, 0x1f, 0xe0, 0x8f, 0x35, +}; + +static const uint8_t unicode_script_ext_table[789] = { + 0x82, 0xc1, 0x00, 0x00, 0x01, 0x29, 0x01, 0x00, 0x00, 0x01, 0x29, 0x1c, + 0x00, 0x0c, 0x01, 0x42, 0x80, 0x92, 0x00, 0x00, 0x02, 0x1c, 0x68, 0x00, + 0x02, 0x1c, 0x26, 0x01, 0x02, 0x1c, 0x42, 0x00, 0x02, 0x1c, 0x26, 0x80, + 0x80, 0x00, 0x00, 0x02, 0x05, 0x25, 0x80, 0x01, 0x00, 0x00, 0x04, 0x04, + 0x2f, 0x84, 0x8e, 0x0d, 0x00, 0x00, 0x04, 0x04, 0x2f, 0x84, 0x8e, 0x00, + 0x03, 0x04, 0x84, 0x8e, 0x01, 0x00, 0x00, 0x04, 0x04, 0x2f, 0x84, 0x8e, + 0x1f, 0x00, 0x00, 0x08, 0x01, 0x04, 0x4d, 0x4e, 0x75, 0x2f, 0x7f, 0x84, + 0x09, 0x00, 0x0a, 0x02, 0x04, 0x84, 0x09, 0x00, 0x09, 0x02, 0x04, 0x8e, + 0x05, 0x00, 0x00, 0x02, 0x04, 0x84, 0x62, 0x00, 0x00, 0x02, 0x04, 0x2f, + 0x81, 0xfb, 0x00, 0x00, 0x0d, 0x0b, 0x1e, 0x28, 0x2a, 0x2c, 0x3a, 0x42, + 0x4c, 0x6d, 0x7a, 0x8b, 0x8d, 0x92, 0x00, 0x0c, 0x0b, 0x1e, 0x28, 0x2a, + 0x2c, 0x3a, 0x42, 0x4c, 0x6d, 0x8b, 0x8d, 0x92, 0x10, 0x00, 0x00, 0x14, + 0x0b, 0x1e, 0x1f, 0x2b, 0x50, 0x28, 0x2a, 0x2c, 0x3a, 0x4b, 0x4c, 0x5d, + 0x6d, 0x40, 0x7e, 0x83, 0x8a, 0x8b, 0x8d, 0x92, 0x00, 0x15, 0x0b, 0x1e, + 0x1f, 0x2b, 0x50, 0x28, 0x2a, 0x2c, 0x3a, 0x44, 0x4b, 0x4c, 0x5d, 0x6d, + 0x40, 0x7e, 0x83, 0x8a, 0x8b, 0x8d, 0x92, 0x09, 0x04, 0x1e, 0x1f, 0x39, + 0x4b, 0x75, 0x00, 0x09, 0x03, 0x0b, 0x15, 0x83, 0x75, 0x00, 0x09, 0x02, + 0x2c, 0x5a, 0x75, 0x00, 0x09, 0x02, 0x2a, 0x3f, 0x80, 0x75, 0x00, 0x0d, + 0x02, 0x28, 0x8b, 0x80, 0x71, 0x00, 0x09, 0x02, 0x3a, 0x5d, 0x82, 0xcf, + 0x00, 0x09, 0x03, 0x15, 0x5b, 0x87, 0x80, 0x30, 0x00, 0x00, 0x02, 0x25, + 0x42, 0x85, 0xb8, 0x00, 0x01, 0x04, 0x11, 0x30, 0x86, 0x85, 0x80, 0x4a, + 0x00, 0x01, 0x02, 0x58, 0x73, 0x00, 0x00, 0x00, 0x02, 0x58, 0x73, 0x84, + 0x49, 0x00, 0x00, 0x04, 0x0b, 0x1e, 0x28, 0x3a, 0x00, 0x01, 0x1e, 0x00, + 0x04, 0x0b, 0x1e, 0x28, 0x3a, 0x00, 0x02, 0x1e, 0x28, 0x00, 0x01, 0x1e, + 0x01, 0x02, 0x0b, 0x1e, 0x00, 0x02, 0x1e, 0x7a, 0x00, 0x02, 0x0b, 0x1e, + 0x00, 0x02, 0x1e, 0x7a, 0x00, 0x06, 0x1e, 0x3a, 0x4c, 0x6d, 0x8b, 0x8d, + 0x00, 0x01, 0x1e, 0x01, 0x02, 0x1e, 0x7a, 0x01, 0x01, 0x1e, 0x00, 0x02, + 0x1e, 0x7a, 0x00, 0x02, 0x0b, 0x1e, 0x06, 0x01, 0x1e, 0x00, 0x02, 0x1e, + 0x5d, 0x00, 0x02, 0x0b, 0x1e, 0x01, 0x01, 0x1e, 0x00, 0x02, 0x0b, 0x1e, + 0x03, 0x01, 0x1e, 0x00, 0x08, 0x0b, 0x1e, 0x28, 0x3a, 0x5d, 0x6d, 0x8d, + 0x92, 0x00, 0x02, 0x1e, 0x28, 0x00, 0x03, 0x1e, 0x28, 0x3a, 0x01, 0x02, + 0x0b, 0x1e, 0x00, 0x01, 0x0b, 0x01, 0x02, 0x1e, 0x28, 0x00, 0x01, 0x5d, + 0x80, 0x44, 0x00, 0x01, 0x01, 0x29, 0x81, 0xec, 0x00, 0x00, 0x02, 0x42, + 0x58, 0x80, 0x3f, 0x00, 0x00, 0x03, 0x1e, 0x28, 0x42, 0x8c, 0xd1, 0x00, + 0x00, 0x02, 0x1c, 0x26, 0x81, 0x3c, 0x00, 0x01, 0x06, 0x0d, 0x2e, 0x2d, + 0x33, 0x3b, 0x97, 0x00, 0x05, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x01, 0x00, + 0x00, 0x01, 0x2d, 0x00, 0x00, 0x09, 0x06, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, + 0x97, 0x00, 0x00, 0x00, 0x05, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x07, 0x06, + 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x97, 0x03, 0x05, 0x0d, 0x2e, 0x2d, 0x33, + 0x3b, 0x09, 0x00, 0x03, 0x02, 0x0d, 0x2d, 0x01, 0x00, 0x00, 0x05, 0x0d, + 0x2e, 0x2d, 0x33, 0x3b, 0x04, 0x02, 0x33, 0x3b, 0x00, 0x00, 0x00, 0x05, + 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x03, 0x00, 0x01, 0x03, 0x2d, 0x33, 0x3b, + 0x01, 0x01, 0x2d, 0x58, 0x00, 0x03, 0x02, 0x33, 0x3b, 0x02, 0x00, 0x00, + 0x02, 0x33, 0x3b, 0x59, 0x00, 0x00, 0x06, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, + 0x97, 0x00, 0x02, 0x33, 0x3b, 0x80, 0x12, 0x00, 0x0f, 0x01, 0x2d, 0x1f, + 0x00, 0x23, 0x01, 0x2d, 0x3b, 0x00, 0x27, 0x01, 0x2d, 0x37, 0x00, 0x30, + 0x01, 0x2d, 0x0e, 0x00, 0x0b, 0x01, 0x2d, 0x32, 0x00, 0x00, 0x01, 0x2d, + 0x57, 0x00, 0x18, 0x01, 0x2d, 0x09, 0x00, 0x04, 0x01, 0x2d, 0x5f, 0x00, + 0x1e, 0x01, 0x2d, 0xc0, 0x31, 0xef, 0x00, 0x00, 0x02, 0x1c, 0x26, 0x81, + 0x3f, 0x00, 0x02, 0x0e, 0x1e, 0x1f, 0x2a, 0x2c, 0x3f, 0x3a, 0x39, 0x4b, + 0x4c, 0x57, 0x5d, 0x40, 0x8a, 0x92, 0x02, 0x0d, 0x1e, 0x1f, 0x2a, 0x2c, + 0x3f, 0x3a, 0x39, 0x4b, 0x57, 0x5d, 0x40, 0x8a, 0x92, 0x03, 0x0b, 0x1e, + 0x1f, 0x2a, 0x2c, 0x3f, 0x39, 0x4b, 0x57, 0x40, 0x8a, 0x92, 0x80, 0x36, + 0x00, 0x00, 0x02, 0x0b, 0x1e, 0x00, 0x00, 0x00, 0x02, 0x1e, 0x8b, 0x39, + 0x00, 0x00, 0x03, 0x3c, 0x42, 0x5b, 0x80, 0x1f, 0x00, 0x00, 0x02, 0x10, + 0x38, 0xc0, 0x13, 0xa1, 0x00, 0x00, 0x02, 0x04, 0x8e, 0x09, 0x00, 0x00, + 0x02, 0x04, 0x8e, 0x46, 0x00, 0x01, 0x05, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, + 0x80, 0x99, 0x00, 0x04, 0x06, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x97, 0x09, + 0x00, 0x00, 0x02, 0x33, 0x3b, 0x2c, 0x00, 0x01, 0x02, 0x33, 0x3b, 0x80, + 0xdf, 0x00, 0x02, 0x02, 0x1b, 0x46, 0x03, 0x00, 0x2c, 0x03, 0x1b, 0x45, + 0x46, 0x02, 0x00, 0x08, 0x02, 0x1b, 0x46, 0x81, 0x1f, 0x00, 0x1b, 0x02, + 0x04, 0x19, 0x8f, 0x84, 0x00, 0x00, 0x02, 0x28, 0x8b, 0x00, 0x00, 0x00, + 0x02, 0x28, 0x8b, 0x36, 0x00, 0x01, 0x02, 0x28, 0x8b, 0x8c, 0x12, 0x00, + 0x01, 0x02, 0x28, 0x8b, 0x00, 0x00, 0x00, 0x02, 0x28, 0x8b, 0xc0, 0x5c, + 0x4b, 0x00, 0x03, 0x01, 0x20, 0x96, 0x3b, 0x00, 0x11, 0x01, 0x2d, 0x9e, + 0x5d, 0x00, 0x01, 0x01, 0x2d, 0xce, 0xcd, 0x2d, 0x00, +}; + +static const uint8_t unicode_prop_Hyphen_table[28] = { + 0xac, 0x80, 0xfe, 0x80, 0x44, 0xdb, 0x80, 0x52, 0x7a, 0x80, + 0x48, 0x08, 0x81, 0x4e, 0x04, 0x80, 0x42, 0xe2, 0x80, 0x60, + 0xcd, 0x66, 0x80, 0x40, 0xa8, 0x80, 0xd6, 0x80, +}; + +static const uint8_t unicode_prop_Other_Math_table[200] = { + 0xdd, 0x80, 0x43, 0x70, 0x11, 0x80, 0x99, 0x09, 0x81, 0x5c, 0x1f, 0x80, + 0x9a, 0x82, 0x8a, 0x80, 0x9f, 0x83, 0x97, 0x81, 0x8d, 0x81, 0xc0, 0x8c, + 0x18, 0x11, 0x1c, 0x91, 0x03, 0x01, 0x89, 0x00, 0x14, 0x28, 0x11, 0x09, + 0x02, 0x05, 0x13, 0x24, 0xca, 0x21, 0x18, 0x08, 0x08, 0x00, 0x21, 0x0b, + 0x0b, 0x91, 0x09, 0x00, 0x06, 0x00, 0x29, 0x41, 0x21, 0x83, 0x40, 0xa7, + 0x08, 0x80, 0x97, 0x80, 0x90, 0x80, 0x41, 0xbc, 0x81, 0x8b, 0x88, 0x24, + 0x21, 0x09, 0x14, 0x8d, 0x00, 0x01, 0x85, 0x97, 0x81, 0xb8, 0x00, 0x80, + 0x9c, 0x83, 0x88, 0x81, 0x41, 0x55, 0x81, 0x9e, 0x89, 0x41, 0x92, 0x95, + 0xbe, 0x83, 0x9f, 0x81, 0x60, 0xd4, 0x62, 0x00, 0x03, 0x80, 0x40, 0xd2, + 0x00, 0x80, 0x60, 0xd4, 0xc0, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, + 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, + 0x04, 0x00, 0x16, 0x80, 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x07, 0x81, 0xb1, 0x55, 0xff, 0x18, 0x9a, 0x01, 0x00, 0x08, + 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, 0x00, 0x02, 0x01, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, + 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, +}; + +static const uint8_t unicode_prop_Other_Alphabetic_table[396] = { + 0x43, 0x44, 0x80, 0x42, 0x69, 0x8d, 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, + 0xaf, 0x8c, 0x06, 0x8f, 0x80, 0xe4, 0x33, 0x19, 0x0b, 0x80, 0xa2, 0x80, + 0x9d, 0x8f, 0xe5, 0x8a, 0xe4, 0x0a, 0x88, 0x02, 0x03, 0x40, 0xa6, 0x8b, + 0x16, 0x85, 0x93, 0xb5, 0x09, 0x8e, 0x01, 0x22, 0x89, 0x81, 0x9c, 0x82, + 0xb9, 0x31, 0x09, 0x81, 0x89, 0x80, 0x89, 0x81, 0x9c, 0x82, 0xb9, 0x23, + 0x09, 0x0b, 0x80, 0x9d, 0x0a, 0x80, 0x8a, 0x82, 0xb9, 0x38, 0x10, 0x81, + 0x94, 0x81, 0x95, 0x13, 0x82, 0xb9, 0x31, 0x09, 0x81, 0x88, 0x81, 0x89, + 0x81, 0x9d, 0x80, 0xba, 0x22, 0x10, 0x82, 0x89, 0x80, 0xa7, 0x83, 0xb9, + 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9c, 0x82, 0xb9, 0x30, 0x10, 0x17, + 0x81, 0x8a, 0x81, 0x9b, 0x83, 0xb9, 0x30, 0x10, 0x82, 0x89, 0x80, 0x89, + 0x81, 0x9d, 0x81, 0xca, 0x28, 0x00, 0x87, 0x91, 0x81, 0xbc, 0x01, 0x86, + 0x91, 0x80, 0xe2, 0x01, 0x28, 0x81, 0x8f, 0x80, 0x40, 0xa2, 0x90, 0x8a, + 0x8a, 0x80, 0xa3, 0xed, 0x8b, 0x00, 0x0b, 0x96, 0x1b, 0x10, 0x11, 0x32, + 0x83, 0x8c, 0x8b, 0x00, 0x89, 0x83, 0x46, 0x73, 0x81, 0x9d, 0x81, 0x9d, + 0x81, 0x9d, 0x81, 0xc1, 0x92, 0x40, 0xbb, 0x81, 0xa1, 0x80, 0xf5, 0x8b, + 0x83, 0x88, 0x40, 0xdd, 0x84, 0xb8, 0x89, 0x81, 0x93, 0x40, 0x8a, 0x84, + 0xaf, 0x8e, 0xbb, 0x82, 0x9d, 0x88, 0x09, 0xb8, 0x8a, 0xb1, 0x92, 0x41, + 0xaf, 0x8d, 0x46, 0xc0, 0xb3, 0x48, 0xf5, 0x9f, 0x60, 0x78, 0x73, 0x87, + 0xa1, 0x81, 0x41, 0x61, 0x07, 0x80, 0x96, 0x84, 0xd7, 0x81, 0xb1, 0x8f, + 0x00, 0xb8, 0x80, 0xa5, 0x84, 0x9b, 0x8b, 0xac, 0x83, 0xaf, 0x8b, 0xa4, + 0x80, 0xc2, 0x8d, 0x8b, 0x07, 0x81, 0xac, 0x82, 0xb1, 0x00, 0x11, 0x0c, + 0x80, 0xab, 0x24, 0x80, 0x40, 0xec, 0x87, 0x60, 0x4f, 0x32, 0x80, 0x48, + 0x56, 0x84, 0x46, 0x85, 0x10, 0x0c, 0x83, 0x43, 0x13, 0x83, 0x42, 0xd7, + 0x82, 0xb4, 0x8d, 0xbb, 0x80, 0xac, 0x88, 0xc6, 0x82, 0xa3, 0x8b, 0x91, + 0x81, 0xb8, 0x82, 0xaf, 0x8c, 0xeb, 0x88, 0x08, 0x28, 0x40, 0x9f, 0x89, + 0x96, 0x83, 0xb9, 0x31, 0x09, 0x81, 0x89, 0x80, 0x89, 0x81, 0x40, 0xd0, + 0x8c, 0x02, 0xe9, 0x91, 0x40, 0xec, 0x31, 0x86, 0x9c, 0x81, 0xd1, 0x8e, + 0x00, 0xe9, 0x8a, 0xe6, 0x8d, 0x41, 0x00, 0x8c, 0x41, 0x97, 0x31, 0x2b, + 0x80, 0x9b, 0x89, 0xa9, 0x20, 0x83, 0x91, 0x8a, 0xad, 0x8d, 0x41, 0x96, + 0x38, 0x86, 0xd2, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, 0x10, 0x02, + 0x80, 0xc1, 0x20, 0x08, 0x83, 0x41, 0x5b, 0x83, 0x60, 0x50, 0x57, 0x00, + 0xb6, 0x33, 0x60, 0x4d, 0x0a, 0x80, 0x60, 0x23, 0x60, 0x30, 0x90, 0x0e, + 0x01, 0x04, 0x49, 0x1b, 0x80, 0x47, 0xe7, 0x99, 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Other_Lowercase_table[51] = { + 0x40, 0xa9, 0x80, 0x8e, 0x80, 0x41, 0xf4, 0x88, 0x31, 0x9d, 0x84, + 0xdf, 0x80, 0xb3, 0x80, 0x59, 0xb0, 0xbe, 0x8c, 0x80, 0xa1, 0xa4, + 0x42, 0xb0, 0x80, 0x8c, 0x80, 0x8f, 0x8c, 0x40, 0xd2, 0x8f, 0x43, + 0x4f, 0x99, 0x47, 0x91, 0x81, 0x60, 0x7a, 0x1d, 0x81, 0x40, 0xd1, + 0x80, 0x40, 0x86, 0x81, 0x43, 0x61, 0x83, +}; + +static const uint8_t unicode_prop_Other_Uppercase_table[15] = { + 0x60, 0x21, 0x5f, 0x8f, 0x43, 0x45, 0x99, 0x61, + 0xcc, 0x5f, 0x99, 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Other_Grapheme_Extend_table[62] = { + 0x49, 0xbd, 0x80, 0x97, 0x80, 0x41, 0x65, 0x80, 0x97, 0x80, 0xe5, + 0x80, 0x97, 0x80, 0x40, 0xe9, 0x80, 0x91, 0x81, 0xe6, 0x80, 0x97, + 0x80, 0xf6, 0x80, 0x8e, 0x80, 0x4d, 0x54, 0x80, 0x44, 0xd5, 0x80, + 0x50, 0x20, 0x81, 0x60, 0xcf, 0x6d, 0x81, 0x53, 0x9d, 0x80, 0x97, + 0x80, 0x41, 0x57, 0x80, 0x8b, 0x80, 0x40, 0xf0, 0x80, 0x60, 0xbb, + 0xb4, 0x07, 0x84, 0x6c, 0x2e, 0xac, 0xdf, +}; + +static const uint8_t unicode_prop_Other_Default_Ignorable_Code_Point_table[32] = + { + 0x43, 0x4e, 0x80, 0x4e, 0x0e, 0x81, 0x46, 0x52, 0x81, 0x48, 0xae, + 0x80, 0x50, 0xfd, 0x80, 0x60, 0xce, 0x3a, 0x80, 0xce, 0x88, 0x6d, + 0x00, 0x06, 0x00, 0x9d, 0xdf, 0xff, 0x40, 0xef, 0x4e, 0x0f, +}; + +static const uint8_t unicode_prop_Other_ID_Start_table[11] = { + 0x58, 0x84, 0x81, 0x48, 0x90, 0x80, 0x94, 0x80, 0x4f, 0x6b, 0x81, +}; + +static const uint8_t unicode_prop_Other_ID_Continue_table[12] = { + 0x40, 0xb6, 0x80, 0x42, 0xce, 0x80, 0x4f, 0xe0, 0x88, 0x46, 0x67, 0x80, +}; + +static const uint8_t unicode_prop_Prepended_Concatenation_Mark_table[17] = { + 0x45, 0xff, 0x85, 0x40, 0xd6, 0x80, 0xb0, 0x80, 0x41, + 0xd1, 0x80, 0x61, 0x07, 0xd9, 0x80, 0x8e, 0x80, +}; + +static const uint8_t unicode_prop_XID_Start1_table[31] = { + 0x43, 0x79, 0x80, 0x4a, 0xb7, 0x80, 0xfe, 0x80, 0x60, 0x21, 0xe6, + 0x81, 0x60, 0xcb, 0xc0, 0x85, 0x41, 0x95, 0x81, 0xf3, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x41, 0x1e, 0x81, +}; + +static const uint8_t unicode_prop_XID_Continue1_table[23] = { + 0x43, 0x79, 0x80, 0x60, 0x2d, 0x1f, 0x81, 0x60, 0xcb, 0xc0, 0x85, 0x41, + 0x95, 0x81, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_Titlecased1_table[22] = { + 0x41, 0xc3, 0x08, 0x08, 0x81, 0xa4, 0x81, 0x4e, 0xdc, 0xaa, 0x0a, + 0x4e, 0x87, 0x3f, 0x3f, 0x87, 0x8b, 0x80, 0x8e, 0x80, 0xae, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_Casefolded1_table[33] = { + 0x40, 0xde, 0x80, 0xcf, 0x80, 0x97, 0x80, 0x44, 0x3c, 0x80, 0x59, + 0x11, 0x80, 0x40, 0xe4, 0x3f, 0x3f, 0x87, 0x89, 0x11, 0x05, 0x02, + 0x11, 0x80, 0xa9, 0x11, 0x80, 0x60, 0xdb, 0x07, 0x86, 0x8b, 0x84, +}; + +static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[436] = { + 0x40, 0x9f, 0x06, 0x00, 0x01, 0x00, 0x01, 0x12, 0x10, 0x82, 0x9f, 0x80, + 0xcf, 0x01, 0x80, 0x8b, 0x07, 0x80, 0xfb, 0x01, 0x01, 0x80, 0xa5, 0x80, + 0x40, 0xbb, 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08, 0x81, 0x89, 0x80, 0xa3, + 0x04, 0x02, 0x04, 0x08, 0x80, 0xc9, 0x82, 0x9c, 0x80, 0x41, 0x93, 0x80, + 0x40, 0x93, 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87, 0xfb, 0x08, 0x80, 0xd2, + 0x01, 0x80, 0xa1, 0x11, 0x80, 0x40, 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe, + 0x80, 0xa7, 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88, 0x03, 0x03, 0x03, 0x80, + 0x8b, 0x80, 0x88, 0x00, 0x26, 0x80, 0x90, 0x80, 0x88, 0x03, 0x03, 0x03, + 0x80, 0x8b, 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81, 0x46, 0x52, 0x81, 0xd4, + 0x83, 0x45, 0x1c, 0x10, 0x8a, 0x80, 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1, + 0xa4, 0x40, 0xd9, 0x80, 0x40, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x3f, 0x3f, 0x87, 0x89, 0x11, 0x04, 0x00, 0x29, 0x04, 0x12, 0x80, + 0x88, 0x12, 0x80, 0x88, 0x11, 0x11, 0x04, 0x08, 0x8f, 0x00, 0x20, 0x8b, + 0x12, 0x2a, 0x08, 0x0b, 0x00, 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a, + 0x80, 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a, 0x01, 0x0c, 0x0a, 0x00, + 0x10, 0x11, 0x02, 0x06, 0x05, 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80, + 0x40, 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41, 0x34, 0xd5, 0x99, 0x9a, + 0x45, 0x20, 0x80, 0xe6, 0x82, 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0, + 0x80, 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40, 0xd5, 0xa9, 0x80, 0xb4, + 0x00, 0x82, 0xdf, 0x09, 0x80, 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf, + 0x9e, 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f, 0x60, 0x72, 0x9b, 0x81, + 0x40, 0xd1, 0x80, 0x40, 0x86, 0x81, 0x43, 0x61, 0x83, 0x60, 0x4d, 0x9f, + 0x41, 0x0d, 0x08, 0x00, 0x81, 0x89, 0x00, 0x00, 0x09, 0x82, 0xc3, 0x81, + 0xe9, 0xa5, 0x86, 0x8b, 0x24, 0x00, 0x97, 0x04, 0x00, 0x01, 0x01, 0x80, + 0xeb, 0xa0, 0x41, 0x6a, 0x91, 0xbf, 0x81, 0xb5, 0xa7, 0x8c, 0x82, 0x99, + 0x95, 0x94, 0x81, 0x8b, 0x80, 0x92, 0x03, 0x1a, 0x00, 0x80, 0x40, 0x86, + 0x08, 0x80, 0x9f, 0x99, 0x40, 0x83, 0x15, 0x0d, 0x0d, 0x0a, 0x16, 0x06, + 0x80, 0x88, 0x60, 0xbc, 0xa6, 0x83, 0x54, 0xb9, 0x86, 0x8d, 0x87, 0xbf, + 0x85, 0x42, 0x3e, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, 0x80, 0x8b, + 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, + 0x16, 0x80, 0x41, 0x53, 0x81, 0x41, 0x23, 0x81, 0xb1, 0x55, 0xff, 0x18, + 0x9a, 0x01, 0x00, 0x08, 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, + 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, + 0x06, 0x03, 0x03, 0x00, 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, + 0x42, 0x43, 0x8a, 0x84, 0x9e, 0x80, 0x9f, 0x99, 0x82, 0xa2, 0x80, 0xee, + 0x82, 0x8c, 0xab, 0x83, 0x88, 0x31, 0x61, 0x05, 0xad, 0x42, 0x1d, 0x6b, + 0x05, 0xe1, 0x4f, 0xff, +}; + +static const uint8_t unicode_prop_ASCII_Hex_Digit_table[5] = { + 0xaf, 0x89, 0x35, 0x99, 0x85, +}; + +static const uint8_t unicode_prop_Bidi_Control_table[10] = { + 0x46, 0x1b, 0x80, 0x59, 0xf0, 0x81, 0x99, 0x84, 0xb6, 0x83, +}; + +static const uint8_t unicode_prop_Dash_table[50] = { + 0xac, 0x80, 0x45, 0x5b, 0x80, 0xb2, 0x80, 0x4e, 0x40, 0x80, + 0x44, 0x04, 0x80, 0x48, 0x08, 0x85, 0xbc, 0x80, 0xa6, 0x80, + 0x8e, 0x80, 0x41, 0x85, 0x80, 0x4c, 0x03, 0x01, 0x80, 0x9e, + 0x0b, 0x80, 0x41, 0xda, 0x80, 0x92, 0x80, 0xee, 0x80, 0x60, + 0xcd, 0x8f, 0x81, 0xa4, 0x80, 0x89, 0x80, 0x40, 0xa8, 0x80, +}; + +static const uint8_t unicode_prop_Deprecated_table[23] = { + 0x41, 0x48, 0x80, 0x45, 0x28, 0x80, 0x49, 0x02, 0x00, 0x80, 0x48, 0x28, + 0x81, 0x48, 0xc4, 0x85, 0x42, 0xb8, 0x81, 0x6d, 0xdc, 0xd5, 0x80, +}; + +static const uint8_t unicode_prop_Diacritic_table[350] = { + 0xdd, 0x00, 0x80, 0xc6, 0x05, 0x03, 0x01, 0x81, 0x41, 0xf6, 0x40, 0x9e, + 0x07, 0x25, 0x90, 0x0b, 0x80, 0x88, 0x81, 0x40, 0xfc, 0x84, 0x40, 0xd0, + 0x80, 0xb6, 0x90, 0x80, 0x9a, 0x00, 0x01, 0x00, 0x40, 0x85, 0x3b, 0x81, + 0x40, 0x85, 0x0b, 0x0a, 0x82, 0xc2, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0xa1, + 0x81, 0x40, 0xc8, 0x9b, 0xbc, 0x80, 0x8f, 0x02, 0x83, 0x9b, 0x80, 0xc9, + 0x80, 0x8f, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xae, + 0x82, 0xbb, 0x80, 0x8f, 0x80, 0xfe, 0x80, 0xfe, 0x80, 0xed, 0x80, 0x8f, + 0x80, 0xec, 0x81, 0x8f, 0x80, 0xfb, 0x80, 0xfb, 0x28, 0x80, 0xea, 0x80, + 0x8c, 0x84, 0xca, 0x81, 0x9a, 0x00, 0x00, 0x03, 0x81, 0xc1, 0x10, 0x81, + 0xbd, 0x80, 0xef, 0x00, 0x81, 0xa7, 0x0b, 0x84, 0x98, 0x30, 0x80, 0x89, + 0x81, 0x42, 0xc0, 0x82, 0x44, 0x68, 0x8a, 0x88, 0x80, 0x41, 0x5a, 0x82, + 0x41, 0x38, 0x39, 0x80, 0xaf, 0x8d, 0xf5, 0x80, 0x8e, 0x80, 0xa5, 0x88, + 0xb5, 0x81, 0x40, 0x89, 0x81, 0xbf, 0x85, 0xd1, 0x98, 0x18, 0x28, 0x0a, + 0xb1, 0xbe, 0xd8, 0x8b, 0xa4, 0x22, 0x82, 0x41, 0xbc, 0x00, 0x82, 0x8a, + 0x82, 0x8c, 0x82, 0x8c, 0x82, 0x8c, 0x81, 0x4c, 0xef, 0x82, 0x41, 0x3c, + 0x80, 0x41, 0xf9, 0x85, 0xe8, 0x83, 0xde, 0x80, 0x60, 0x75, 0x71, 0x80, + 0x8b, 0x08, 0x80, 0x9b, 0x81, 0xd1, 0x81, 0x8d, 0xa1, 0xe5, 0x82, 0xec, + 0x81, 0x40, 0xc9, 0x80, 0x9a, 0x91, 0xb8, 0x83, 0xa3, 0x80, 0xde, 0x80, + 0x8b, 0x80, 0xa3, 0x80, 0x40, 0x94, 0x82, 0xc0, 0x83, 0xb2, 0x80, 0xe3, + 0x84, 0x40, 0x8b, 0x81, 0x60, 0x4f, 0x2f, 0x80, 0x43, 0x00, 0x8f, 0x41, + 0x0d, 0x00, 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2, 0x80, 0x42, 0xfb, 0x80, + 0x48, 0x03, 0x81, 0x42, 0x3a, 0x85, 0x42, 0x1d, 0x8a, 0x41, 0x67, 0x81, + 0xf7, 0x81, 0xbd, 0x80, 0xcb, 0x80, 0x88, 0x82, 0xe7, 0x81, 0x40, 0xb1, + 0x81, 0xd0, 0x80, 0x8f, 0x80, 0x97, 0x32, 0x84, 0x40, 0xcc, 0x02, 0x80, + 0xfa, 0x81, 0x40, 0xfa, 0x81, 0xfd, 0x80, 0xf5, 0x81, 0xf2, 0x80, 0x41, + 0x0c, 0x81, 0x41, 0xa4, 0x80, 0xd2, 0x80, 0x91, 0x80, 0xd0, 0x80, 0x41, + 0xa4, 0x80, 0x41, 0x01, 0x00, 0x81, 0xd0, 0x80, 0x60, 0x4d, 0x57, 0x84, + 0xba, 0x86, 0x44, 0x57, 0x90, 0x60, 0x61, 0xc6, 0x12, 0x2f, 0x39, 0x86, + 0x9d, 0x83, 0x4f, 0x81, 0x86, 0x41, 0xb4, 0x83, 0x45, 0xdf, 0x86, 0xec, + 0x10, 0x82, +}; + +static const uint8_t unicode_prop_Extender_table[86] = { + 0x40, 0xb6, 0x80, 0x42, 0x17, 0x81, 0x43, 0x6d, 0x80, 0x41, 0xb8, + 0x80, 0x46, 0x4a, 0x80, 0xfe, 0x80, 0x49, 0x42, 0x80, 0xb7, 0x80, + 0x42, 0x62, 0x80, 0x41, 0x8d, 0x80, 0xc3, 0x80, 0x53, 0x88, 0x80, + 0xaa, 0x84, 0xe6, 0x81, 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80, 0x45, + 0xf5, 0x80, 0x43, 0xc1, 0x80, 0x95, 0x80, 0x40, 0x88, 0x80, 0xeb, + 0x80, 0x94, 0x81, 0x60, 0x54, 0x7a, 0x80, 0x53, 0xeb, 0x80, 0x42, + 0x67, 0x82, 0x44, 0xce, 0x80, 0x60, 0x50, 0xa8, 0x81, 0x44, 0x9b, + 0x08, 0x80, 0x60, 0x71, 0x57, 0x81, 0x48, 0x05, 0x82, +}; + +static const uint8_t unicode_prop_Hex_Digit_table[12] = { + 0xaf, 0x89, 0x35, 0x99, 0x85, 0x60, 0xfe, 0xa8, 0x89, 0x35, 0x99, 0x85, +}; + +static const uint8_t unicode_prop_IDS_Binary_Operator_table[5] = { + 0x60, 0x2f, 0xef, 0x09, 0x87, +}; + +static const uint8_t unicode_prop_IDS_Trinary_Operator_table[4] = { + 0x60, + 0x2f, + 0xf1, + 0x81, +}; + +static const uint8_t unicode_prop_Ideographic_table[58] = { + 0x60, 0x30, 0x05, 0x81, 0x98, 0x88, 0x8d, 0x82, 0x43, 0xc4, 0x59, 0xb5, + 0xc9, 0x60, 0x51, 0xef, 0x60, 0x59, 0x0f, 0x41, 0x6d, 0x81, 0xe9, 0x60, + 0x75, 0x25, 0x57, 0xf7, 0x87, 0x42, 0xf2, 0x60, 0x26, 0x7c, 0x41, 0x8b, + 0x60, 0x4d, 0x03, 0x60, 0xa6, 0xd6, 0xa8, 0x50, 0x34, 0x8a, 0x40, 0xdd, + 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x4c, 0x1e, 0x42, 0x1d, +}; + +static const uint8_t unicode_prop_Join_Control_table[4] = { + 0x60, + 0x20, + 0x0b, + 0x81, +}; + +static const uint8_t unicode_prop_Logical_Order_Exception_table[15] = { + 0x4e, 0x3f, 0x84, 0xfa, 0x84, 0x4a, 0xef, 0x11, + 0x80, 0x60, 0x90, 0xf9, 0x09, 0x00, 0x81, +}; + +static const uint8_t unicode_prop_Noncharacter_Code_Point_table[71] = { + 0x60, 0xfd, 0xcf, 0x9f, 0x42, 0x0d, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, +}; + +static const uint8_t unicode_prop_Pattern_Syntax_table[58] = { + 0xa0, 0x8e, 0x89, 0x86, 0x99, 0x18, 0x80, 0x99, 0x83, 0xa1, 0x30, 0x00, + 0x08, 0x00, 0x0b, 0x03, 0x02, 0x80, 0x96, 0x80, 0x9e, 0x80, 0x5f, 0x17, + 0x97, 0x87, 0x8e, 0x81, 0x92, 0x80, 0x89, 0x41, 0x30, 0x42, 0xcf, 0x40, + 0x9f, 0x42, 0x75, 0x9d, 0x44, 0x6b, 0x41, 0xff, 0xff, 0x41, 0x80, 0x13, + 0x98, 0x8e, 0x80, 0x60, 0xcd, 0x0c, 0x81, 0x41, 0x04, 0x81, +}; + +static const uint8_t unicode_prop_Pattern_White_Space_table[11] = { + 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x5f, 0x87, 0x81, 0x97, 0x81, +}; + +static const uint8_t unicode_prop_Quotation_Mark_table[31] = { + 0xa1, 0x03, 0x80, 0x40, 0x82, 0x80, 0x8e, 0x80, 0x5f, 0x5b, 0x87, + 0x98, 0x81, 0x4e, 0x06, 0x80, 0x41, 0xc8, 0x83, 0x8c, 0x82, 0x60, + 0xce, 0x20, 0x83, 0x40, 0xbc, 0x03, 0x80, 0xd9, 0x81, +}; + +static const uint8_t unicode_prop_Radical_table[9] = { + 0x60, 0x2e, 0x7f, 0x99, 0x80, 0xd8, 0x8b, 0x40, 0xd5, +}; + +static const uint8_t unicode_prop_Regional_Indicator_table[4] = { + 0x61, + 0xf1, + 0xe5, + 0x99, +}; + +static const uint8_t unicode_prop_Sentence_Terminal_table[184] = { + 0xa0, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0x45, 0x48, 0x80, 0x40, 0x93, 0x81, + 0x40, 0xb3, 0x80, 0xaa, 0x82, 0x40, 0xf5, 0x80, 0xbc, 0x00, 0x02, 0x81, + 0x41, 0x24, 0x81, 0x46, 0xe3, 0x81, 0x43, 0x15, 0x03, 0x81, 0x43, 0x04, + 0x80, 0x40, 0xc5, 0x81, 0x40, 0xcb, 0x04, 0x80, 0x41, 0x39, 0x81, 0x41, + 0x61, 0x83, 0x40, 0xad, 0x09, 0x81, 0x40, 0xda, 0x81, 0xc0, 0x81, 0x43, + 0xbb, 0x81, 0x88, 0x82, 0x4d, 0xe3, 0x80, 0x8c, 0x80, 0x41, 0xc4, 0x80, + 0x60, 0x74, 0xfb, 0x80, 0x41, 0x0d, 0x81, 0x40, 0xe2, 0x02, 0x80, 0x41, + 0x7d, 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, 0x97, 0x81, 0x40, 0x92, 0x82, + 0x40, 0x8f, 0x81, 0x40, 0xf8, 0x80, 0x60, 0x52, 0x65, 0x02, 0x81, 0x40, + 0xa8, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0xc0, 0x80, 0x4a, 0xf3, 0x81, 0x44, + 0xfc, 0x84, 0x40, 0xec, 0x81, 0xf4, 0x83, 0xfe, 0x82, 0x40, 0x80, 0x0d, + 0x80, 0x8f, 0x81, 0xd7, 0x08, 0x81, 0xeb, 0x80, 0x41, 0xa0, 0x81, 0x41, + 0x74, 0x0c, 0x8e, 0xe8, 0x81, 0x40, 0xf8, 0x82, 0x43, 0x02, 0x81, 0xd6, + 0x81, 0x41, 0xa3, 0x81, 0x42, 0xb3, 0x81, 0x60, 0x4b, 0x74, 0x81, 0x40, + 0x84, 0x80, 0xc0, 0x81, 0x8a, 0x80, 0x43, 0x52, 0x80, 0x60, 0x4e, 0x05, + 0x80, 0x5d, 0xe7, 0x80, +}; + +static const uint8_t unicode_prop_Soft_Dotted_table[71] = { + 0xe8, 0x81, 0x40, 0xc3, 0x80, 0x41, 0x18, 0x80, 0x9d, 0x80, 0xb3, 0x80, + 0x93, 0x80, 0x41, 0x3f, 0x80, 0xe1, 0x00, 0x80, 0x59, 0x08, 0x80, 0xb2, + 0x80, 0x8c, 0x02, 0x80, 0x40, 0x83, 0x80, 0x40, 0x9c, 0x80, 0x41, 0xa4, + 0x80, 0x40, 0xd5, 0x81, 0x4b, 0x31, 0x80, 0x61, 0xa7, 0xa4, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, +}; + +static const uint8_t unicode_prop_Terminal_Punctuation_table[237] = { + 0xa0, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80, 0x43, 0x3d, 0x07, 0x80, + 0x42, 0x00, 0x80, 0xb8, 0x80, 0xc7, 0x80, 0x8d, 0x01, 0x81, 0x40, 0xb3, + 0x80, 0xaa, 0x8a, 0x00, 0x40, 0xea, 0x81, 0xb5, 0x8e, 0x9e, 0x80, 0x41, + 0x04, 0x81, 0x44, 0xf3, 0x81, 0x40, 0xab, 0x03, 0x85, 0x41, 0x36, 0x81, + 0x43, 0x14, 0x87, 0x43, 0x04, 0x80, 0xfb, 0x82, 0xc6, 0x81, 0x40, 0x9c, + 0x12, 0x80, 0xa6, 0x19, 0x81, 0x41, 0x39, 0x81, 0x41, 0x61, 0x83, 0x40, + 0xad, 0x08, 0x82, 0x40, 0xda, 0x84, 0xbd, 0x81, 0x43, 0xbb, 0x81, 0x88, + 0x82, 0x4d, 0xe3, 0x80, 0x8c, 0x03, 0x80, 0x89, 0x00, 0x81, 0x41, 0xb0, + 0x81, 0x60, 0x74, 0xfa, 0x81, 0x41, 0x0c, 0x82, 0x40, 0xe2, 0x84, 0x41, + 0x7d, 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, 0x96, 0x82, 0x40, 0x92, 0x82, + 0xfe, 0x80, 0x8f, 0x81, 0x40, 0xf8, 0x80, 0x60, 0x52, 0x63, 0x10, 0x83, + 0x40, 0xa8, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80, 0xc0, 0x01, 0x80, + 0x44, 0x39, 0x80, 0xaf, 0x80, 0x44, 0x85, 0x80, 0x40, 0xc6, 0x80, 0x41, + 0x35, 0x81, 0x40, 0x97, 0x85, 0xc3, 0x85, 0xd8, 0x83, 0x43, 0xb7, 0x84, + 0x40, 0xec, 0x86, 0xef, 0x83, 0xfe, 0x82, 0x40, 0x80, 0x0d, 0x80, 0x8f, + 0x81, 0xd7, 0x84, 0xeb, 0x80, 0x41, 0xa0, 0x82, 0x8c, 0x80, 0x41, 0x65, + 0x1a, 0x8e, 0xe8, 0x81, 0x40, 0xf8, 0x82, 0x43, 0x02, 0x81, 0xd6, 0x0b, + 0x81, 0x41, 0x9d, 0x82, 0xac, 0x80, 0x42, 0x84, 0x81, 0x45, 0x76, 0x84, + 0x60, 0x45, 0xf8, 0x81, 0x40, 0x84, 0x80, 0xc0, 0x82, 0x89, 0x80, 0x43, + 0x51, 0x81, 0x60, 0x4e, 0x05, 0x80, 0x5d, 0xe6, 0x83, +}; + +static const uint8_t unicode_prop_Unified_Ideograph_table[38] = { + 0x60, 0x33, 0xff, 0x59, 0xb5, 0xc9, 0x60, 0x51, 0xef, 0x60, + 0x5a, 0x1d, 0x08, 0x00, 0x81, 0x89, 0x00, 0x00, 0x09, 0x82, + 0x61, 0x05, 0xd5, 0x60, 0xa6, 0xd6, 0xa8, 0x50, 0x34, 0x8a, + 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, +}; + +static const uint8_t unicode_prop_Variation_Selector_table[12] = { + 0x58, 0x0a, 0x82, 0x60, 0xe5, 0xf1, 0x8f, 0x6d, 0x02, 0xef, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_White_Space_table[22] = { + 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x99, 0x80, 0x55, 0xde, 0x80, + 0x49, 0x7e, 0x8a, 0x9c, 0x0c, 0x80, 0xae, 0x80, 0x4f, 0x9f, 0x80, +}; + +static const uint8_t unicode_prop_Bidi_Mirrored_table[171] = { + 0xa7, 0x81, 0x91, 0x00, 0x80, 0x9b, 0x00, 0x80, 0x9c, 0x00, 0x80, 0xac, + 0x80, 0x8e, 0x80, 0x4e, 0x7d, 0x83, 0x47, 0x5c, 0x81, 0x49, 0x9b, 0x81, + 0x89, 0x81, 0xb5, 0x81, 0x8d, 0x81, 0x40, 0xb0, 0x80, 0x40, 0xbf, 0x1a, + 0x2a, 0x02, 0x0a, 0x18, 0x18, 0x00, 0x03, 0x88, 0x20, 0x80, 0x91, 0x23, + 0x88, 0x08, 0x00, 0x39, 0x9e, 0x0b, 0x20, 0x88, 0x09, 0x92, 0x21, 0x88, + 0x21, 0x0b, 0x97, 0x81, 0x8f, 0x3b, 0x93, 0x0e, 0x81, 0x44, 0x3c, 0x8d, + 0xc9, 0x01, 0x18, 0x08, 0x14, 0x1c, 0x12, 0x8d, 0x41, 0x92, 0x95, 0x0d, + 0x80, 0x8d, 0x38, 0x35, 0x10, 0x1c, 0x01, 0x0c, 0x18, 0x02, 0x09, 0x89, + 0x29, 0x81, 0x8b, 0x92, 0x03, 0x08, 0x00, 0x08, 0x03, 0x21, 0x2a, 0x97, + 0x81, 0x8a, 0x0b, 0x18, 0x09, 0x0b, 0xaa, 0x0f, 0x80, 0xa7, 0x20, 0x00, + 0x14, 0x22, 0x18, 0x14, 0x00, 0x40, 0xff, 0x80, 0x42, 0x02, 0x1a, 0x08, + 0x81, 0x8d, 0x09, 0x89, 0x41, 0xdd, 0x89, 0x0f, 0x60, 0xce, 0x3c, 0x2c, + 0x81, 0x40, 0xa1, 0x81, 0x91, 0x00, 0x80, 0x9b, 0x00, 0x80, 0x9c, 0x00, + 0x00, 0x08, 0x81, 0x60, 0xd7, 0x76, 0x80, 0xb8, 0x80, 0xb8, 0x80, 0xb8, + 0x80, 0xb8, 0x80, +}; + +static const uint8_t unicode_prop_Emoji_table[236] = { + 0xa2, 0x05, 0x04, 0x89, 0xee, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, 0x80, + 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, 0x8e, 0x81, 0x41, 0x6e, 0x81, + 0x8b, 0x80, 0x40, 0xa5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6, 0x80, 0x40, + 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80, 0xb9, 0x18, 0x84, 0x88, 0x01, 0x01, + 0x09, 0x03, 0x01, 0x00, 0x09, 0x02, 0x02, 0x0f, 0x14, 0x00, 0x04, 0x8b, + 0x8a, 0x09, 0x00, 0x08, 0x80, 0x91, 0x01, 0x81, 0x91, 0x28, 0x00, 0x0a, + 0x0f, 0x0b, 0x81, 0x8a, 0x0c, 0x09, 0x04, 0x08, 0x00, 0x81, 0x93, 0x0c, + 0x28, 0x19, 0x03, 0x01, 0x01, 0x28, 0x01, 0x00, 0x00, 0x05, 0x02, 0x05, + 0x80, 0x89, 0x81, 0x8e, 0x01, 0x03, 0x00, 0x03, 0x10, 0x80, 0x8a, 0x81, + 0xaf, 0x82, 0x88, 0x80, 0x8d, 0x80, 0x8d, 0x80, 0x41, 0x73, 0x81, 0x41, + 0xce, 0x82, 0x92, 0x81, 0xb2, 0x03, 0x80, 0x44, 0xd9, 0x80, 0x8b, 0x80, + 0x42, 0x58, 0x00, 0x80, 0x61, 0xbd, 0x69, 0x80, 0x40, 0xc9, 0x80, 0x40, + 0x9f, 0x81, 0x8b, 0x81, 0x8d, 0x01, 0x89, 0xca, 0x99, 0x01, 0x96, 0x80, + 0x93, 0x01, 0x88, 0x94, 0x81, 0x40, 0xad, 0xa1, 0x81, 0xef, 0x09, 0x02, + 0x81, 0xd2, 0x0a, 0x80, 0x41, 0x06, 0x80, 0xbe, 0x8a, 0x28, 0x97, 0x31, + 0x0f, 0x8b, 0x01, 0x19, 0x03, 0x81, 0x8c, 0x09, 0x07, 0x81, 0x88, 0x04, + 0x82, 0x8b, 0x17, 0x11, 0x00, 0x03, 0x05, 0x02, 0x05, 0xd5, 0xaf, 0xc5, + 0x27, 0x08, 0x89, 0x2a, 0x00, 0x0a, 0x01, 0x87, 0x40, 0xe4, 0x8b, 0x41, + 0x20, 0xad, 0x80, 0x89, 0x80, 0xaa, 0x03, 0x82, 0xa8, 0x0d, 0x82, 0x9c, + 0x81, 0xb2, 0xef, 0x1b, 0x14, 0x82, 0x8c, 0x85, +}; + +static const uint8_t unicode_prop_Emoji_Component_table[28] = { + 0xa2, 0x05, 0x04, 0x89, 0x5f, 0xd2, 0x80, 0x40, 0xd4, 0x80, + 0x60, 0xdd, 0x2a, 0x80, 0x60, 0xf3, 0xd5, 0x99, 0x41, 0xfa, + 0x84, 0x45, 0xaf, 0x83, 0x6c, 0x06, 0x6b, 0xdf, +}; + +static const uint8_t unicode_prop_Emoji_Modifier_table[4] = { + 0x61, + 0xf3, + 0xfa, + 0x84, +}; + +static const uint8_t unicode_prop_Emoji_Modifier_Base_table[63] = { + 0x60, 0x26, 0x1c, 0x80, 0x40, 0xda, 0x80, 0x8f, 0x83, 0x61, 0xcc, + 0x76, 0x80, 0xbb, 0x11, 0x01, 0x82, 0xf4, 0x09, 0x8a, 0x94, 0x92, + 0x10, 0x1a, 0x02, 0x30, 0x00, 0x97, 0x80, 0x40, 0xc8, 0x0b, 0x80, + 0x94, 0x03, 0x81, 0x40, 0xad, 0x12, 0x84, 0xd2, 0x80, 0x8f, 0x82, + 0x88, 0x80, 0x8a, 0x80, 0x42, 0x41, 0x07, 0x3d, 0x80, 0x88, 0x89, + 0x0a, 0xf5, 0x08, 0x08, 0x80, 0x90, 0x10, 0x8c, +}; + +static const uint8_t unicode_prop_Emoji_Presentation_table[143] = { + 0x60, 0x23, 0x19, 0x81, 0x40, 0xcc, 0x1a, 0x01, 0x80, 0x42, 0x08, 0x81, + 0x94, 0x81, 0xb1, 0x8b, 0xaa, 0x80, 0x92, 0x80, 0x8c, 0x07, 0x81, 0x90, + 0x0c, 0x0f, 0x04, 0x80, 0x94, 0x06, 0x08, 0x03, 0x01, 0x06, 0x03, 0x81, + 0x9b, 0x80, 0xa2, 0x00, 0x03, 0x10, 0x80, 0xbc, 0x82, 0x97, 0x80, 0x8d, + 0x80, 0x43, 0x5a, 0x81, 0xb2, 0x03, 0x80, 0x61, 0xc4, 0xad, 0x80, 0x40, + 0xc9, 0x80, 0x40, 0xbd, 0x01, 0x89, 0xca, 0x99, 0x00, 0x97, 0x80, 0x93, + 0x01, 0x20, 0x82, 0x94, 0x81, 0x40, 0xad, 0xa0, 0x8b, 0x88, 0x80, 0xc5, + 0x80, 0x95, 0x8b, 0xaa, 0x1c, 0x8b, 0x90, 0x10, 0x82, 0xc6, 0x00, 0x80, + 0x40, 0xba, 0x81, 0xbe, 0x8c, 0x18, 0x97, 0x91, 0x80, 0x99, 0x81, 0x8c, + 0x80, 0xd5, 0xd4, 0xaf, 0xc5, 0x28, 0x12, 0x08, 0x94, 0x0e, 0x86, 0x40, + 0xe4, 0x8b, 0x41, 0x20, 0xad, 0x80, 0x89, 0x80, 0xaa, 0x03, 0x82, 0xa8, + 0x0d, 0x82, 0x9c, 0x81, 0xb2, 0xef, 0x1b, 0x14, 0x82, 0x8c, 0x85, +}; + +static const uint8_t unicode_prop_Extended_Pictographic_table[152] = { + 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, 0x80, 0x40, 0xd7, 0x80, + 0x95, 0x80, 0xd9, 0x85, 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0xde, + 0x80, 0xc5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6, 0x80, 0x40, 0xe6, 0x81, + 0x89, 0x80, 0x88, 0x80, 0xb9, 0x18, 0x28, 0x8b, 0x80, 0xf1, 0x89, 0xf5, + 0x81, 0x8a, 0x00, 0x00, 0x28, 0x10, 0x28, 0x89, 0x81, 0x8e, 0x01, 0x03, + 0x00, 0x03, 0x10, 0x80, 0x8a, 0x84, 0xac, 0x82, 0x88, 0x80, 0x8d, 0x80, + 0x8d, 0x80, 0x41, 0x73, 0x81, 0x41, 0xce, 0x82, 0x92, 0x81, 0xb2, 0x03, + 0x80, 0x44, 0xd9, 0x80, 0x8b, 0x80, 0x42, 0x58, 0x00, 0x80, 0x61, 0xbd, + 0x65, 0x40, 0xff, 0x8c, 0x82, 0x9e, 0x80, 0xbb, 0x85, 0x8b, 0x81, 0x8d, + 0x01, 0x89, 0x91, 0xb8, 0x9a, 0x8e, 0x89, 0x80, 0x93, 0x01, 0x88, 0x03, + 0x88, 0x41, 0xb1, 0x84, 0x41, 0x3d, 0x87, 0x41, 0x09, 0xaf, 0xff, 0xf3, + 0x8b, 0xd4, 0xaa, 0x8b, 0x83, 0xb7, 0x87, 0x89, 0x85, 0xa7, 0x87, 0x9d, + 0xd1, 0x8b, 0xae, 0x80, 0x89, 0x80, 0x46, 0xb6, +}; + +static const uint8_t unicode_prop_Default_Ignorable_Code_Point_table[51] = { + 0x40, 0xac, 0x80, 0x42, 0xa0, 0x80, 0x42, 0xcb, 0x80, 0x4b, 0x41, + 0x81, 0x46, 0x52, 0x81, 0xd4, 0x83, 0x47, 0xfb, 0x84, 0x99, 0x84, + 0xb0, 0x8f, 0x50, 0xf3, 0x80, 0x60, 0xcc, 0x9a, 0x8f, 0x40, 0xee, + 0x80, 0x40, 0x9f, 0x80, 0xce, 0x88, 0x60, 0xbc, 0xa6, 0x83, 0x54, + 0xce, 0x87, 0x6c, 0x2e, 0x84, 0x4f, 0xff, +}; + +typedef enum { + UNICODE_PROP_Hyphen, + UNICODE_PROP_Other_Math, + UNICODE_PROP_Other_Alphabetic, + UNICODE_PROP_Other_Lowercase, + UNICODE_PROP_Other_Uppercase, + UNICODE_PROP_Other_Grapheme_Extend, + UNICODE_PROP_Other_Default_Ignorable_Code_Point, + UNICODE_PROP_Other_ID_Start, + UNICODE_PROP_Other_ID_Continue, + UNICODE_PROP_Prepended_Concatenation_Mark, + UNICODE_PROP_ID_Continue1, + UNICODE_PROP_XID_Start1, + UNICODE_PROP_XID_Continue1, + UNICODE_PROP_Changes_When_Titlecased1, + UNICODE_PROP_Changes_When_Casefolded1, + UNICODE_PROP_Changes_When_NFKC_Casefolded1, + UNICODE_PROP_ASCII_Hex_Digit, + UNICODE_PROP_Bidi_Control, + UNICODE_PROP_Dash, + UNICODE_PROP_Deprecated, + UNICODE_PROP_Diacritic, + UNICODE_PROP_Extender, + UNICODE_PROP_Hex_Digit, + UNICODE_PROP_IDS_Binary_Operator, + UNICODE_PROP_IDS_Trinary_Operator, + UNICODE_PROP_Ideographic, + UNICODE_PROP_Join_Control, + UNICODE_PROP_Logical_Order_Exception, + UNICODE_PROP_Noncharacter_Code_Point, + UNICODE_PROP_Pattern_Syntax, + UNICODE_PROP_Pattern_White_Space, + UNICODE_PROP_Quotation_Mark, + UNICODE_PROP_Radical, + UNICODE_PROP_Regional_Indicator, + UNICODE_PROP_Sentence_Terminal, + UNICODE_PROP_Soft_Dotted, + UNICODE_PROP_Terminal_Punctuation, + UNICODE_PROP_Unified_Ideograph, + UNICODE_PROP_Variation_Selector, + UNICODE_PROP_White_Space, + UNICODE_PROP_Bidi_Mirrored, + UNICODE_PROP_Emoji, + UNICODE_PROP_Emoji_Component, + UNICODE_PROP_Emoji_Modifier, + UNICODE_PROP_Emoji_Modifier_Base, + UNICODE_PROP_Emoji_Presentation, + UNICODE_PROP_Extended_Pictographic, + UNICODE_PROP_Default_Ignorable_Code_Point, + UNICODE_PROP_ID_Start, + UNICODE_PROP_Case_Ignorable, + UNICODE_PROP_ASCII, + UNICODE_PROP_Alphabetic, + UNICODE_PROP_Any, + UNICODE_PROP_Assigned, + UNICODE_PROP_Cased, + UNICODE_PROP_Changes_When_Casefolded, + UNICODE_PROP_Changes_When_Casemapped, + UNICODE_PROP_Changes_When_Lowercased, + UNICODE_PROP_Changes_When_NFKC_Casefolded, + UNICODE_PROP_Changes_When_Titlecased, + UNICODE_PROP_Changes_When_Uppercased, + UNICODE_PROP_Grapheme_Base, + UNICODE_PROP_Grapheme_Extend, + UNICODE_PROP_ID_Continue, + UNICODE_PROP_Lowercase, + UNICODE_PROP_Math, + UNICODE_PROP_Uppercase, + UNICODE_PROP_XID_Continue, + UNICODE_PROP_XID_Start, + UNICODE_PROP_Cased1, + UNICODE_PROP_COUNT, +} UnicodePropertyEnum; + +static const char unicode_prop_name_table[] = + "ASCII_Hex_Digit,AHex" + "\0" + "Bidi_Control,Bidi_C" + "\0" + "Dash" + "\0" + "Deprecated,Dep" + "\0" + "Diacritic,Dia" + "\0" + "Extender,Ext" + "\0" + "Hex_Digit,Hex" + "\0" + "IDS_Binary_Operator,IDSB" + "\0" + "IDS_Trinary_Operator,IDST" + "\0" + "Ideographic,Ideo" + "\0" + "Join_Control,Join_C" + "\0" + "Logical_Order_Exception,LOE" + "\0" + "Noncharacter_Code_Point,NChar" + "\0" + "Pattern_Syntax,Pat_Syn" + "\0" + "Pattern_White_Space,Pat_WS" + "\0" + "Quotation_Mark,QMark" + "\0" + "Radical" + "\0" + "Regional_Indicator,RI" + "\0" + "Sentence_Terminal,STerm" + "\0" + "Soft_Dotted,SD" + "\0" + "Terminal_Punctuation,Term" + "\0" + "Unified_Ideograph,UIdeo" + "\0" + "Variation_Selector,VS" + "\0" + "White_Space,space" + "\0" + "Bidi_Mirrored,Bidi_M" + "\0" + "Emoji" + "\0" + "Emoji_Component" + "\0" + "Emoji_Modifier" + "\0" + "Emoji_Modifier_Base" + "\0" + "Emoji_Presentation" + "\0" + "Extended_Pictographic" + "\0" + "Default_Ignorable_Code_Point,DI" + "\0" + "ID_Start,IDS" + "\0" + "Case_Ignorable,CI" + "\0" + "ASCII" + "\0" + "Alphabetic,Alpha" + "\0" + "Any" + "\0" + "Assigned" + "\0" + "Cased" + "\0" + "Changes_When_Casefolded,CWCF" + "\0" + "Changes_When_Casemapped,CWCM" + "\0" + "Changes_When_Lowercased,CWL" + "\0" + "Changes_When_NFKC_Casefolded,CWKCF" + "\0" + "Changes_When_Titlecased,CWT" + "\0" + "Changes_When_Uppercased,CWU" + "\0" + "Grapheme_Base,Gr_Base" + "\0" + "Grapheme_Extend,Gr_Ext" + "\0" + "ID_Continue,IDC" + "\0" + "Lowercase,Lower" + "\0" + "Math" + "\0" + "Uppercase,Upper" + "\0" + "XID_Continue,XIDC" + "\0" + "XID_Start,XIDS" + "\0"; + +static const uint8_t* const unicode_prop_table[] = { + unicode_prop_Hyphen_table, + unicode_prop_Other_Math_table, + unicode_prop_Other_Alphabetic_table, + unicode_prop_Other_Lowercase_table, + unicode_prop_Other_Uppercase_table, + unicode_prop_Other_Grapheme_Extend_table, + unicode_prop_Other_Default_Ignorable_Code_Point_table, + unicode_prop_Other_ID_Start_table, + unicode_prop_Other_ID_Continue_table, + unicode_prop_Prepended_Concatenation_Mark_table, + unicode_prop_ID_Continue1_table, + unicode_prop_XID_Start1_table, + unicode_prop_XID_Continue1_table, + unicode_prop_Changes_When_Titlecased1_table, + unicode_prop_Changes_When_Casefolded1_table, + unicode_prop_Changes_When_NFKC_Casefolded1_table, + unicode_prop_ASCII_Hex_Digit_table, + unicode_prop_Bidi_Control_table, + unicode_prop_Dash_table, + unicode_prop_Deprecated_table, + unicode_prop_Diacritic_table, + unicode_prop_Extender_table, + unicode_prop_Hex_Digit_table, + unicode_prop_IDS_Binary_Operator_table, + unicode_prop_IDS_Trinary_Operator_table, + unicode_prop_Ideographic_table, + unicode_prop_Join_Control_table, + unicode_prop_Logical_Order_Exception_table, + unicode_prop_Noncharacter_Code_Point_table, + unicode_prop_Pattern_Syntax_table, + unicode_prop_Pattern_White_Space_table, + unicode_prop_Quotation_Mark_table, + unicode_prop_Radical_table, + unicode_prop_Regional_Indicator_table, + unicode_prop_Sentence_Terminal_table, + unicode_prop_Soft_Dotted_table, + unicode_prop_Terminal_Punctuation_table, + unicode_prop_Unified_Ideograph_table, + unicode_prop_Variation_Selector_table, + unicode_prop_White_Space_table, + unicode_prop_Bidi_Mirrored_table, + unicode_prop_Emoji_table, + unicode_prop_Emoji_Component_table, + unicode_prop_Emoji_Modifier_table, + unicode_prop_Emoji_Modifier_Base_table, + unicode_prop_Emoji_Presentation_table, + unicode_prop_Extended_Pictographic_table, + unicode_prop_Default_Ignorable_Code_Point_table, + unicode_prop_ID_Start_table, + unicode_prop_Case_Ignorable_table, +}; + +static const uint16_t unicode_prop_len_table[] = { + countof(unicode_prop_Hyphen_table), + countof(unicode_prop_Other_Math_table), + countof(unicode_prop_Other_Alphabetic_table), + countof(unicode_prop_Other_Lowercase_table), + countof(unicode_prop_Other_Uppercase_table), + countof(unicode_prop_Other_Grapheme_Extend_table), + countof(unicode_prop_Other_Default_Ignorable_Code_Point_table), + countof(unicode_prop_Other_ID_Start_table), + countof(unicode_prop_Other_ID_Continue_table), + countof(unicode_prop_Prepended_Concatenation_Mark_table), + countof(unicode_prop_ID_Continue1_table), + countof(unicode_prop_XID_Start1_table), + countof(unicode_prop_XID_Continue1_table), + countof(unicode_prop_Changes_When_Titlecased1_table), + countof(unicode_prop_Changes_When_Casefolded1_table), + countof(unicode_prop_Changes_When_NFKC_Casefolded1_table), + countof(unicode_prop_ASCII_Hex_Digit_table), + countof(unicode_prop_Bidi_Control_table), + countof(unicode_prop_Dash_table), + countof(unicode_prop_Deprecated_table), + countof(unicode_prop_Diacritic_table), + countof(unicode_prop_Extender_table), + countof(unicode_prop_Hex_Digit_table), + countof(unicode_prop_IDS_Binary_Operator_table), + countof(unicode_prop_IDS_Trinary_Operator_table), + countof(unicode_prop_Ideographic_table), + countof(unicode_prop_Join_Control_table), + countof(unicode_prop_Logical_Order_Exception_table), + countof(unicode_prop_Noncharacter_Code_Point_table), + countof(unicode_prop_Pattern_Syntax_table), + countof(unicode_prop_Pattern_White_Space_table), + countof(unicode_prop_Quotation_Mark_table), + countof(unicode_prop_Radical_table), + countof(unicode_prop_Regional_Indicator_table), + countof(unicode_prop_Sentence_Terminal_table), + countof(unicode_prop_Soft_Dotted_table), + countof(unicode_prop_Terminal_Punctuation_table), + countof(unicode_prop_Unified_Ideograph_table), + countof(unicode_prop_Variation_Selector_table), + countof(unicode_prop_White_Space_table), + countof(unicode_prop_Bidi_Mirrored_table), + countof(unicode_prop_Emoji_table), + countof(unicode_prop_Emoji_Component_table), + countof(unicode_prop_Emoji_Modifier_table), + countof(unicode_prop_Emoji_Modifier_Base_table), + countof(unicode_prop_Emoji_Presentation_table), + countof(unicode_prop_Extended_Pictographic_table), + countof(unicode_prop_Default_Ignorable_Code_Point_table), + countof(unicode_prop_ID_Start_table), + countof(unicode_prop_Case_Ignorable_table), +}; + +#endif /* CONFIG_ALL_UNICODE */ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/libunicode.h b/NativeScript/napi/android/primjs/include/quickjs/include/libunicode.h new file mode 100644 index 000000000..6b76c75f7 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/libunicode.h @@ -0,0 +1,131 @@ +/* + * Unicode utilities + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_LIBUNICODE_H_ +#define SRC_INTERPRETER_QUICKJS_INCLUDE_LIBUNICODE_H_ + +#include +#include + +#include "base_export.h" + +#define LRE_BOOL int /* for documentation purposes */ + +/* define it to include all the unicode tables (40KB larger) */ +#if LYNX_SIMPLIFY +#define CONFIG_ALL_UNICODE +#endif + +#define LRE_CC_RES_LEN_MAX 3 + +typedef enum { + UNICODE_NFC, + UNICODE_NFD, + UNICODE_NFKC, + UNICODE_NFKD, +} UnicodeNormalizationEnum; + +QJS_HIDE int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); +QJS_HIDE LRE_BOOL lre_is_cased(uint32_t c); +QJS_HIDE LRE_BOOL lre_is_case_ignorable(uint32_t c); + +/* char ranges */ + +typedef struct { + int len; /* in points, always even */ + int size; + uint32_t *points; /* points sorted by increasing value */ + void *mem_opaque; + void *(*realloc_func)(void *opaque, void *ptr, size_t size, int alloc_tag); +} CharRange; + +typedef enum { + CR_OP_UNION, + CR_OP_INTER, + CR_OP_XOR, +} CharRangeOpEnum; + +QJS_HIDE void cr_init(CharRange *cr, void *mem_opaque, + void *(*realloc_func)(void *opaque, void *ptr, + size_t size, int alloc_tag)); +QJS_HIDE void cr_free(CharRange *cr); +QJS_HIDE int cr_realloc(CharRange *cr, int size, int alloc_tag = 0); +QJS_HIDE int cr_copy(CharRange *cr, const CharRange *cr1); + +static inline int cr_add_point(CharRange *cr, uint32_t v) { + if (cr->len >= cr->size) { + if (cr_realloc(cr, cr->len + 1, 1)) return -1; + } + cr->points[cr->len++] = v; + return 0; +} + +static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2) { + if ((cr->len + 2) > cr->size) { + if (cr_realloc(cr, cr->len + 2, 1)) return -1; + } + cr->points[cr->len++] = c1; + cr->points[cr->len++] = c2; + return 0; +} + +QJS_HIDE int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len); + +static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2) { + uint32_t b_pt[2]; + b_pt[0] = c1; + b_pt[1] = c2 + 1; + return cr_union1(cr, b_pt, 2); +} + +QJS_HIDE int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, + const uint32_t *b_pt, int b_len, int op); + +QJS_HIDE int cr_invert(CharRange *cr); + +#ifdef CONFIG_ALL_UNICODE + +LRE_BOOL lre_is_id_start(uint32_t c); +LRE_BOOL lre_is_id_continue(uint32_t c); + +int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, + UnicodeNormalizationEnum n_type, void *opaque, + void *mem_opaque, + void *(*realloc_func)(void *opaque, void *ptr, + size_t size, int alloc_tag)); + +/* Unicode character range functions */ + +int unicode_script(CharRange *cr, const char *script_name, LRE_BOOL is_ext); +int unicode_general_category(CharRange *cr, const char *gc_name); +int unicode_prop(CharRange *cr, const char *prop_name); + +#endif /* CONFIG_ALL_UNICODE */ + +#undef LRE_BOOL + +#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_LIBUNICODE_H_ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/list.h b/NativeScript/napi/android/primjs/include/quickjs/include/list.h new file mode 100644 index 000000000..0f24df4b1 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/list.h @@ -0,0 +1,97 @@ +/* + * Linux klist like system + * + * Copyright (c) 2016-2017 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_LIST_H_ +#define SRC_INTERPRETER_QUICKJS_INCLUDE_LIST_H_ + +#ifndef NULL +#include +#endif + +struct list_head { + struct list_head *prev; + struct list_head *next; +}; + +#define LIST_HEAD_INIT(el) \ + { &(el), &(el) } + +/* return the pointer of type 'type *' containing 'el' as field 'member' */ +#define list_entry(el, type, member) \ + ((type *)((uint8_t *)(el)-offsetof(type, member))) + +static inline void init_list_head(struct list_head *head) { + head->prev = head; + head->next = head; +} + +/* insert 'el' between 'prev' and 'next' */ +static inline void __list_add(struct list_head *el, struct list_head *prev, + struct list_head *next) { + prev->next = el; + el->prev = prev; + el->next = next; + next->prev = el; +} + +/* add 'el' at the head of the list 'head' (= after element head) */ +static inline void list_add(struct list_head *el, struct list_head *head) { + __list_add(el, head, head->next); +} + +/* add 'el' at the end of the list 'head' (= before element head) */ +static inline void list_add_tail(struct list_head *el, struct list_head *head) { + __list_add(el, head->prev, head); +} + +static inline void list_del(struct list_head *el) { + struct list_head *prev, *next; + prev = el->prev; + next = el->next; + prev->next = next; + next->prev = prev; + el->prev = NULL; /* fail safe */ + el->next = NULL; /* fail safe */ +} + +static inline int list_empty(struct list_head *el) { return el->next == el; } + +#define list_for_each(el, head) \ + for (el = (head)->next; el != (head); el = el->next) + +#define list_for_each_safe(el, el1, head) \ + for (el = (head)->next, el1 = el->next; el != (head); \ + el = el1, el1 = el->next) + +#define list_for_each_prev(el, head) \ + for (el = (head)->prev; el != (head); el = el->prev) + +#define list_for_each_prev_safe(el, el1, head) \ + for (el = (head)->prev, el1 = el->prev; el != (head); \ + el = el1, el1 = el->prev) + +#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_LIST_H_ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/primjs_monitor.h b/NativeScript/napi/android/primjs/include/quickjs/include/primjs_monitor.h new file mode 100644 index 000000000..d26cdc794 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/primjs_monitor.h @@ -0,0 +1,19 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_PRIMJS_MONITOR_H_ +#define SRC_INTERPRETER_QUICKJS_INCLUDE_PRIMJS_MONITOR_H_ + +#define MODULE_PRIMJS "primjs" +#define MODULE_QUICK "quickjs" + +#define MODULE_NAPI "napi" +#define DEFAULT_BIZ_NAME "unknown_biz_name" + +void MonitorEvent(const char* moduleName, const char* bizName, + const char* dataKey, const char* dataValue); +bool GetSettingsWithKey(const char* key); +int GetSettingsFlag(); + +#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_PRIMJS_MONITOR_H_ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-atom.h b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-atom.h new file mode 100644 index 000000000..c6c6367d2 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-atom.h @@ -0,0 +1,286 @@ +/* + * QuickJS atom definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifdef DEF + +/* Note: first atoms are considered as keywords in the parser */ +DEF(null, "null") /* must be first */ +DEF(false, "false") +DEF(true, "true") +DEF(if, "if") +DEF(else, "else") +DEF(return, "return") +DEF(var, "var") +DEF(this, "this") +DEF(delete, "delete") +DEF(void, "void") +DEF(typeof, "typeof") +DEF(new, "new") +DEF(in, "in") +DEF(instanceof, "instanceof") +DEF(do, "do") +DEF(while, "while") +DEF(for, "for") +DEF(break, "break") +DEF(continue, "continue") +DEF(switch, "switch") +DEF(case, "case") +DEF(default, "default") +DEF(throw, "throw") +DEF(try, "try") +DEF(catch, "catch") +DEF(finally, "finally") +DEF(function, "function") +DEF(debugger, "debugger") +DEF(with, "with") +/* FutureReservedWord */ +DEF(class, "class") +DEF(const, "const") +DEF(enum, "enum") +DEF(export, "export") +DEF(extends, "extends") +DEF(import, "import") +DEF(super, "super") +/* FutureReservedWords when parsing strict mode code */ +DEF(implements, "implements") +DEF(interface, "interface") +DEF(let, "let") +DEF(package, "package") +DEF(private, "private") +DEF(protected, "protected") +DEF(public, "public") +DEF(static, "static") +DEF(yield, "yield") +DEF(await, "await") + +/* empty string */ +DEF(empty_string, "") +/* identifiers */ +DEF(length, "length") +DEF(fileName, "fileName") +DEF(lineNumber, "lineNumber") +DEF(message, "message") +DEF(stack, "stack") +DEF(name, "name") +DEF(toString, "toString") +DEF(toLocaleString, "toLocaleString") +DEF(valueOf, "valueOf") +DEF(eval, "eval") +DEF(prototype, "prototype") +DEF(constructor, "constructor") +DEF(configurable, "configurable") +DEF(writable, "writable") +DEF(enumerable, "enumerable") +DEF(value, "value") +DEF(get, "get") +DEF(set, "set") +DEF(of, "of") +DEF(__proto__, "__proto__") +DEF(undefined, "undefined") +DEF(number, "number") +DEF(boolean, "boolean") +DEF(string, "string") +DEF(object, "object") +DEF(symbol, "symbol") +DEF(integer, "integer") +DEF(unknown, "unknown") +DEF(arguments, "arguments") +DEF(callee, "callee") +DEF(caller, "caller") +DEF(_eval_, "") +DEF(_ret_, "") +DEF(_var_, "") +DEF(_with_, "") +DEF(lastIndex, "lastIndex") +DEF(target, "target") +DEF(index, "index") +DEF(input, "input") +DEF(defineProperties, "defineProperties") +DEF(apply, "apply") +DEF(join, "join") +DEF(concat, "concat") +DEF(split, "split") +DEF(construct, "construct") +DEF(getPrototypeOf, "getPrototypeOf") +DEF(setPrototypeOf, "setPrototypeOf") +DEF(isExtensible, "isExtensible") +DEF(preventExtensions, "preventExtensions") +DEF(has, "has") +DEF(deleteProperty, "deleteProperty") +DEF(defineProperty, "defineProperty") +DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor") +DEF(ownKeys, "ownKeys") +DEF(add, "add") +DEF(done, "done") +DEF(next, "next") +DEF(values, "values") +DEF(source, "source") +DEF(flags, "flags") +DEF(global, "global") +DEF(unicode, "unicode") +DEF(raw, "raw") +DEF(new_target, "new.target") +DEF(this_active_func, "this.active_func") +DEF(home_object, "") +DEF(computed_field, "") +DEF(static_computed_field, + "") /* must come after computed_fields */ +DEF(class_fields_init, "") +DEF(brand, "") +DEF(hash_constructor, "#constructor") +DEF(as, "as") +DEF(from, "from") +DEF(_default_, "*default*") +DEF(_star_, "*") +DEF(Module, "Module") +DEF(then, "then") +DEF(resolve, "resolve") +DEF(reject, "reject") +DEF(promise, "promise") +DEF(proxy, "proxy") +DEF(revoke, "revoke") +DEF(async, "async") +DEF(exec, "exec") +DEF(groups, "groups") +DEF(status, "status") +DEF(reason, "reason") +#ifdef CONFIG_BIGNUM +DEF(bigint, "bigint") +DEF(bigfloat, "bigfloat") +#endif +#ifdef CONFIG_ATOMICS +DEF(not_equal, "not-equal") +DEF(timed_out, "timed-out") +DEF(ok, "ok") +#endif +DEF(toJSON, "toJSON") +/* class names */ +DEF(Object, "Object") +DEF(Array, "Array") +DEF(Error, "Error") +DEF(Number, "Number") +DEF(String, "String") +DEF(Boolean, "Boolean") +DEF(Symbol, "Symbol") +DEF(Arguments, "Arguments") +DEF(Math, "Math") +DEF(JSON, "JSON") +DEF(Date, "Date") +DEF(Function, "Function") +DEF(GeneratorFunction, "GeneratorFunction") +DEF(ForInIterator, "ForInIterator") +DEF(RegExp, "RegExp") +DEF(ArrayBuffer, "ArrayBuffer") +DEF(SharedArrayBuffer, "SharedArrayBuffer") +/* must keep same order as class IDs for typed arrays */ +DEF(Uint8ClampedArray, "Uint8ClampedArray") +DEF(Int8Array, "Int8Array") +DEF(Uint8Array, "Uint8Array") +DEF(Int16Array, "Int16Array") +DEF(Uint16Array, "Uint16Array") +DEF(Int32Array, "Int32Array") +DEF(Uint32Array, "Uint32Array") +#ifdef CONFIG_BIGNUM +DEF(BigInt64Array, "BigInt64Array") +DEF(BigUint64Array, "BigUint64Array") +#endif +DEF(Float32Array, "Float32Array") +DEF(Float64Array, "Float64Array") +DEF(DataView, "DataView") +#ifdef CONFIG_BIGNUM +DEF(BigInt, "BigInt") +DEF(BigFloat, "BigFloat") +DEF(BigFloatEnv, "BigFloatEnv") +#endif +DEF(Map, "Map") +DEF(Set, "Set") /* Map + 1 */ +DEF(WeakMap, "WeakMap") /* Map + 2 */ +DEF(WeakSet, "WeakSet") /* Map + 3 */ +DEF(Map_Iterator, "Map Iterator") +DEF(Set_Iterator, "Set Iterator") +DEF(Array_Iterator, "Array Iterator") +DEF(String_Iterator, "String Iterator") +DEF(RegExp_String_Iterator, "RegExp String Iterator") +DEF(Generator, "Generator") +DEF(Proxy, "Proxy") +DEF(Promise, "Promise") +DEF(PromiseResolveFunction, "PromiseResolveFunction") +DEF(PromiseRejectFunction, "PromiseRejectFunction") +DEF(AsyncFunction, "AsyncFunction") +DEF(AsyncFunctionResolve, "AsyncFunctionResolve") +DEF(AsyncFunctionReject, "AsyncFunctionReject") +DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction") +DEF(AsyncGenerator, "AsyncGenerator") +DEF(EvalError, "EvalError") +DEF(RangeError, "RangeError") +DEF(ReferenceError, "ReferenceError") +DEF(SyntaxError, "SyntaxError") +DEF(TypeError, "TypeError") +DEF(URIError, "URIError") +DEF(InternalError, "InternalError") +/* private symbols */ +DEF(Private_brand, "") +/* symbols */ +DEF(Symbol_toPrimitive, "Symbol.toPrimitive") +DEF(Symbol_iterator, "Symbol.iterator") +DEF(Symbol_match, "Symbol.match") +DEF(Symbol_matchAll, "Symbol.matchAll") +DEF(Symbol_replace, "Symbol.replace") +DEF(Symbol_search, "Symbol.search") +DEF(Symbol_split, "Symbol.split") +DEF(Symbol_toStringTag, "Symbol.toStringTag") +DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") +DEF(Symbol_hasInstance, "Symbol.hasInstance") +DEF(Symbol_species, "Symbol.species") +DEF(Symbol_unscopables, "Symbol.unscopables") +DEF(Symbol_asyncIterator, "Symbol.asyncIterator") +#ifdef CONFIG_BIGNUM +DEF(Symbol_operatorOrder, "Symbol.operatorOrder") +DEF(Symbol_operatorAdd, "Symbol.operatorAdd") +DEF(Symbol_operatorSub, "Symbol.operatorSub") +DEF(Symbol_operatorMul, "Symbol.operatorMul") +DEF(Symbol_operatorDiv, "Symbol.operatorDiv") +DEF(Symbol_operatorMod, "Symbol.operatorMod") +DEF(Symbol_operatorPow, "Symbol.operatorPow") +DEF(Symbol_operatorShl, "Symbol.operatorShl") +DEF(Symbol_operatorShr, "Symbol.operatorShr") +DEF(Symbol_operatorAnd, "Symbol.operatorAnd") +DEF(Symbol_operatorOr, "Symbol.operatorOr") +DEF(Symbol_operatorXor, "Symbol.operatorXor") +DEF(Symbol_operatorCmpLT, "Symbol.operatorCmpLT") +DEF(Symbol_operatorCmpLE, "Symbol.operatorCmpLE") +DEF(Symbol_operatorCmpEQ, "Symbol.operatorCmpEQ") +DEF(Symbol_operatorPlus, "Symbol.operatorPlus") +DEF(Symbol_operatorNeg, "Symbol.operatorNeg") +DEF(Symbol_operatorNot, "Symbol.operatorNot") +DEF(Symbol_operatorInc, "Symbol.operatorInc") +DEF(Symbol_operatorDec, "Symbol.operatorDec") +DEF(Symbol_operatorMathMod, "Symbol.operatorMathMod") +#endif + +#endif /* DEF */ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-inner.h b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-inner.h new file mode 100644 index 000000000..c3dd6e485 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-inner.h @@ -0,0 +1,3204 @@ +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2019 Fabrice Bellard + * Copyright (c) 2017-2019 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_INNER_H_ +#define SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_INNER_H_ + +#include "quickjs/include/base_export.h" +#ifdef __cplusplus +extern "C" { +#endif + +#include "quickjs/include/cutils.h" +#include "quickjs/include/list.h" +#include "quickjs/include/quickjs.h" + +#ifdef CONFIG_BIGNUM +#include "quickjs/include/libbf.h" +#endif + +#ifdef __cplusplus +} +#endif + +#include "gc/allocator.h" +#include "quickjs/include/primjs_monitor.h" +#include "quickjs/include/quickjs_queue.h" + +#ifndef _WIN32 +#include + +#include +#endif +#include + +#ifdef ENABLE_GC_DEBUG_TOOLS +#include +#ifndef DCHECK +#define DCHECK(condition) \ + if (!(condition)) abort(); +#endif +#else +#ifndef DCHECK +#define DCHECK(condition) ((void)0) +#endif +#endif + +typedef int BOOL; +#define SYSCALL_CHECK(condition) \ + if ((condition) == -1) { \ + /*abort()*/ \ + } + +#ifdef ENABLE_QUICKJS_DEBUGGER +#include "inspector/debugger_inner.h" +#endif + +#include "gc/qjsvaluevalue-space.h" + +#if defined(CONFIG_BIGNUM) and defined(ENABLE_LEPUSNG) +#error bignum and lepusng are now conflict! +#endif +#if defined(QJS_UNITTEST) || defined(__WASI_SDK__) +#define QJS_STATIC +#else +#define QJS_STATIC static +#endif + +#define OPTIMIZE 1 +#define SHORT_OPCODES 1 + +#if !defined(__aarch64__) +#if defined(ENABLE_PRIMJS_SNAPSHOT) +#undef ENABLE_PRIMJS_SNAPSHOT +#endif +#if defined(ENABLE_COMPATIBLE_MM) +#undef ENABLE_COMPATIBLE_MM +#endif +#endif + +#ifdef ENABLE_COMPATIBLE_MM +#undef DUMP_LEAKS +#undef DEBUG_MEMORY +#endif + +#define KB (1024) +#define MB (1024 * KB) +#define MS (1000) + +#define BUF_LEN (100) + +#ifdef ENABLE_GC_DEBUG_TOOLS +size_t get_cur_cnt(void *runtime, void *ptr); +size_t get_del_cnt(void *runtime, void *ptr); +#endif + +#define __exception __attribute__((warn_unused_result)) + +enum JS_CLASS_ID { + /* classid tag */ /* union usage | properties */ + JS_CLASS_OBJECT = 1, /* must be first */ + JS_CLASS_ARRAY, /* u.array | length */ + JS_CLASS_ERROR, + JS_CLASS_NUMBER, /* u.object_data */ + JS_CLASS_STRING, /* u.object_data */ + JS_CLASS_BOOLEAN, /* u.object_data */ + JS_CLASS_SYMBOL, /* u.object_data */ + JS_CLASS_ARGUMENTS, /* u.array | length */ + JS_CLASS_MAPPED_ARGUMENTS, /* | length */ + JS_CLASS_DATE, /* u.object_data */ + JS_CLASS_MODULE_NS, + JS_CLASS_C_FUNCTION, /* u.cfunc */ + JS_CLASS_BYTECODE_FUNCTION, /* u.func */ + JS_CLASS_BOUND_FUNCTION, /* u.bound_function */ + JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */ + JS_CLASS_GENERATOR_FUNCTION, /* u.func */ + JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */ + JS_CLASS_REGEXP, /* u.regexp */ + JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ +#ifdef CONFIG_BIGNUM + JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ +#endif + JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_DATAVIEW, /* u.typed_array */ +#ifdef CONFIG_BIGNUM + JS_CLASS_BIG_INT, /* u.object_data */ + JS_CLASS_BIG_FLOAT, /* u.object_data */ + JS_CLASS_FLOAT_ENV, /* u.float_env */ +#endif + JS_CLASS_MAP, /* u.map_state */ + JS_CLASS_SET, /* u.map_state */ + JS_CLASS_WEAKMAP, /* u.map_state */ + JS_CLASS_WEAKSET, /* u.map_state */ + JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ + JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */ + JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */ + JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */ + JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */ + JS_CLASS_GENERATOR, /* u.generator_data */ + JS_CLASS_PROXY, /* u.proxy_data */ + JS_CLASS_PROMISE, /* u.promise_data */ + JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */ + JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */ + JS_CLASS_ASYNC_FUNCTION, /* u.func */ + JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */ + JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */ + JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */ + JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */ + JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ + JS_CLASS_WeakRef, + JS_CLASS_FinalizationRegistry, + + JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ +}; + +typedef enum JSErrorEnum { + JS_EVAL_ERROR, + JS_RANGE_ERROR, + JS_REFERENCE_ERROR, + JS_SYNTAX_ERROR, + JS_TYPE_ERROR, + JS_URI_ERROR, + JS_INTERNAL_ERROR, + JS_AGGREGATE_ERROR, + + JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ +} JSErrorEnum; + +#define BUILD_ASYNC_STACK + +typedef struct JSShape JSShape; +typedef struct JSString JSString; +typedef struct JSString JSAtomStruct; + +typedef struct JSLepusType { + int32_t array_typeid_; + int32_t table_typeid_; + int32_t refcounted_typeid_; + LEPUSClassID refcounted_cid_; +} JSLepusType; + +typedef struct PrimjsCallbacks { + void (*print_by_alog)(char *msg); + int32_t (*js_has_property)(LEPUSContext *, LEPUSValue, JSAtom, int32_t); + int32_t (*js_delete_property)(LEPUSContext *, LEPUSValue, JSAtom, int32_t); + int32_t (*js_get_own_property_names)(LEPUSContext *, LEPUSValue, uint32_t *, + struct LEPUSPropertyEnum **, int32_t); + int32_t (*js_deep_equal_callback)(LEPUSContext *, LEPUSValue, LEPUSValue); + LEPUSValue (*jsarray_push)(LEPUSContext *, LEPUSValue, int32_t, + LEPUSValueConst *, int32_t); + LEPUSValue (*jsarray_pop)(LEPUSContext *, LEPUSValue, int32_t); + int64_t (*jsarray_find)(LEPUSContext *, LEPUSValue, LEPUSValue, int64_t, + int32_t); + LEPUSValue (*jsarray_reverse)(LEPUSContext *, LEPUSValue); + LEPUSValue (*jsarray_slice)(LEPUSContext *, LEPUSValue, size_t, size_t, + size_t, LEPUSValue *, int32_t); +} PrimjsCallbacks; + +typedef struct JSMallocState { + size_t malloc_count; + // + uint64_t malloc_size; + uint64_t malloc_limit; + struct malloc_state allocate_state; + // + void *opaque; /* user opaque */ +} JSMallocState; + +typedef struct LEPUSMallocFunctions { + void *(*lepus_malloc)(JSMallocState *s, size_t size, int alloc_tag); + void (*lepus_free)(JSMallocState *s, void *ptr); + void *(*lepus_realloc)(JSMallocState *s, void *ptr, size_t size, + int alloc_tag); + size_t (*lepus_malloc_usable_size)(const void *ptr); +} LEPUSMallocFunctions; + +#ifdef ENABLE_QUICKJS_DEBUGGER +typedef struct QJSDebuggerCallbacks2 { + // callbacks for quickjs debugger + void (*run_message_loop_on_pause)(LEPUSContext *ctx); + void (*quit_message_loop_on_pause)(LEPUSContext *ctx); + void (*get_messages)(LEPUSContext *ctx); + void (*send_response)(LEPUSContext *ctx, int32_t message_id, + const char *message); + void (*send_notification)(LEPUSContext *ctx, const char *message); + void (*free_messages)(LEPUSContext *ctx, char **messages, int32_t size); + + void (*inspector_check)(LEPUSContext *ctx); + void (*debugger_exception)(LEPUSContext *ctx); + void (*console_message)(LEPUSContext *ctx, int tag, LEPUSValueConst *argv, + int argc); + void (*script_parsed_ntfy)(LEPUSContext *ctx, LEPUSScriptSource *source); + void (*console_api_called_ntfy)(LEPUSContext *ctx, LEPUSValue *msg); + void (*script_fail_parse_ntfy)(LEPUSContext *ctx, LEPUSScriptSource *source); + void (*debugger_paused)(LEPUSContext *ctx, const uint8_t *cur_pc); + uint8_t (*is_devtool_on)(LEPUSRuntime *rt); + void (*send_response_with_view_id)(LEPUSContext *ctx, int32_t message_id, + const char *message, int32_t view_id); + void (*send_ntfy_with_view_id)(LEPUSContext *ctx, const char *message, + int32_t view_id); + void (*script_parsed_ntfy_with_view_id)(LEPUSContext *ctx, + LEPUSScriptSource *source, + int32_t view_id); + void (*script_fail_parse_ntfy_with_view_id)(LEPUSContext *ctx, + LEPUSScriptSource *source, + int32_t view_id); + void (*set_session_enable_state)(LEPUSContext *ctx, int32_t view_id, + int32_t protocol_type); + void (*get_session_state)(LEPUSContext *ctx, int32_t view_id, + bool *is_already_enabled, bool *is_paused); + void (*console_api_called_ntfy_with_rid)(LEPUSContext *ctx, LEPUSValue *msg); + void (*get_session_enable_state)(LEPUSContext *ctx, int32_t view_id, + int32_t protocol_type, bool *ret); + void (*get_console_stack_trace)(LEPUSContext *ctx, LEPUSValue *ret); + void (*on_console_message)(LEPUSContext *ctx, LEPUSValue console_message, + int32_t); +} QJSDebuggerCallbacks2; +#endif + +typedef struct SettingsOption { + /* + If this value is true, quickjs will not adjust stack size + when stack size is unconsistent. + */ + bool disable_adjust_stacksize; + bool disable_json_opt; + bool disable_deepclone_opt; + bool disable_separable_string; +} SettingsOption; +class GlobalHandles; +class GarbageCollector; +class PtrHandles; +class ByteThreadPool; +class napi_handle_scope__; + +struct LEPUSRuntime { + LEPUSMallocFunctions mf; + const char *rt_info; + + // for trace gc + + int atom_hash_size; /* power of two */ + int atom_count; + int atom_size; + int atom_count_resize; /* resize hash table at this count */ + uint32_t *atom_hash; + JSAtomStruct **atom_array; + int atom_free_index; /* 0 = none */ + + int class_count; /* size of class_array */ + LEPUSClass *class_array; + + struct list_head context_list; /* list of LEPUSContext.link */ + /* list of JSGCObjectHeader.link. List of allocated GC objects (used + by the garbage collector) */ + /* list of allocated objects (used by the garbage collector) */ + struct list_head obj_list; /* list of LEPUSObject.link */ + // + struct list_head gc_bytecode_list; + struct list_head gc_obj_list; + // + struct list_head tmp_obj_list; /* used during gc */ + struct list_head free_obj_list; /* used during gc */ + struct list_head *el_next; /* used during gc */ + BOOL in_gc_sweep : 8; + int c_stack_depth; + uint64_t malloc_gc_threshold; + /* stack limitation */ + const uint8_t *stack_top; + size_t stack_size; /* in bytes */ + + LEPUSValue current_exception; + /* true if a backtrace needs to be added to the current exception + (the backtrace generation cannot be done immediately in a bytecode + function) */ + BOOL exception_needs_backtrace; + /* true if inside an out of memory error, to avoid recursing */ + BOOL in_out_of_memory : 8; + + struct LEPUSStackFrame *current_stack_frame; + + LEPUSInterruptHandler *interrupt_handler; + void *interrupt_opaque; + + struct list_head job_list; /* list of JSJobEntry.link */ + + LEPUSModuleNormalizeFunc *module_normalize_func; + LEPUSModuleLoaderFunc *module_loader_func; + void *module_loader_opaque; + + BOOL can_block : 8; /* TRUE if Atomics.wait can block */ + + /* Shape hash table */ + int shape_hash_bits; + int shape_hash_size; + int shape_hash_count; /* number of hashed shapes */ + JSShape **shape_hash; + PrimjsCallbacks primjs_callbacks_; + + struct list_head + unhandled_rejections; // record the first unhandled rejection error + struct list_head async_func_sf; // record all async functions' stack frame. + +#ifdef BUILD_ASYNC_STACK + LEPUSValue *current_micro_task; +#endif + +#ifdef ENABLE_LEPUSNG + LEPUSLepusRefCallbacks js_callbacks_; + JSLepusType js_type_; +#endif + +#ifdef ENABLE_QUICKJS_DEBUGGER + QJSDebuggerCallbacks2 debugger_callbacks_; + int32_t next_script_id; // next script id that can be used +#endif +#ifdef CONFIG_BIGNUM + bf_context_t bf_ctx; +#endif + // +#ifdef ENABLE_PRIMJS_SNAPSHOT + bool use_primjs; +#endif +#ifdef DUMP_LEAKS + struct list_head string_list; /* list of JSString.link */ +#endif + // + void (*update_gc_info)(const char *, int); + char gc_info_start_[BUF_LEN]; + char gc_info_end_[BUF_LEN]; + int64_t init_time; + + ByteThreadPool *workerThreadPool; + GlobalHandles *global_handles_ = nullptr; + + QJSValueValueSpace *qjsvaluevalue_allocator = nullptr; + PtrHandles *ptr_handles; + GarbageCollector *gc; + size_t gc_cnt; + void *mem_for_oom; + bool gc_enable; + bool is_lepusng; + void *user_opaque; + // Primjs begin + SettingsOption settings_option; + // Primjs end + JSMallocState malloc_state; +#ifdef ENABLE_TRACING_GC + LEPUSObject *boilerplateArg0; + LEPUSObject *boilerplateArg1; + LEPUSObject *boilerplateArg2; + LEPUSObject *boilerplateArg3; +#endif +}; + +static const char *const native_error_name[JS_NATIVE_ERROR_COUNT] = { + "EvalError", "RangeError", "ReferenceError", "SyntaxError", + "TypeError", "URIError", "InternalError", "AggregateError"}; + +/* Set/Map/WeakSet/WeakMap */ + +typedef struct JSMapState { + BOOL is_weak; /* TRUE if WeakSet/WeakMap */ + struct list_head records; /* list of JSMapRecord.link */ + uint32_t record_count; + struct list_head *hash_table; + uint32_t hash_size; /* must be a power of two */ + uint32_t record_count_threshold; /* count at which a hash table + resize is needed */ +} JSMapState; + +typedef struct JSUnhandledRejectionEntry { + struct list_head link; + LEPUSValue error; + LEPUSValue promise; +} JSUnhandledRejectionEntry; + +struct LEPUSClass { + uint32_t class_id; /* 0 means free entry */ + JSAtom class_name; + LEPUSClassFinalizer *finalizer; + LEPUSClassGCMark *gc_mark; + LEPUSClassCall *call; + /* pointers for exotic behavior, can be NULL if none are present */ + const LEPUSClassExoticMethods *exotic; +}; + +#define JS_MODE_STRICT (1 << 0) +#define JS_MODE_STRIP (1 << 1) +#define JS_MODE_BIGINT (1 << 2) +#define JS_MODE_MATH (1 << 3) + +typedef struct LEPUSStackFrame { + struct LEPUSStackFrame *prev_frame; /* NULL if first stack frame */ + LEPUSValue + cur_func; /* current function, LEPUS_UNDEFINED if the frame is detached */ + LEPUSValue *arg_buf; /* arguments */ + LEPUSValue *var_buf; /* variables */ + struct list_head var_ref_list; /* list of JSVarRef.link */ + const uint8_t *cur_pc; /* only used in bytecode functions : PC of the + instruction after the call */ + int arg_count; + int js_mode; /* for C functions: 0 */ + /* only used in generators. Current stack pointer value. NULL if + the function is running. */ + LEPUSValue *cur_sp; + LEPUSValue *sp = nullptr; +#ifdef ENABLE_QUICKJS_DEBUGGER + // for debugger: this_obj of the stack frame + LEPUSValue pthis; +#endif + struct JSVarRef **var_refs = nullptr; + uint32_t ref_size = 0; +} LEPUSStackFrame; + +enum TOK { + TOK_NUMBER = -128, + TOK_STRING, + TOK_TEMPLATE, + TOK_IDENT, + TOK_REGEXP, + /* warning: order matters (see js_parse_assign_expr) */ + TOK_MUL_ASSIGN, + TOK_DIV_ASSIGN, + TOK_MOD_ASSIGN, + TOK_PLUS_ASSIGN, + TOK_MINUS_ASSIGN, + TOK_SHL_ASSIGN, + TOK_SAR_ASSIGN, + TOK_SHR_ASSIGN, + TOK_AND_ASSIGN, + TOK_XOR_ASSIGN, + TOK_OR_ASSIGN, +#ifdef CONFIG_BIGNUM + TOK_MATH_POW_ASSIGN, +#endif + TOK_POW_ASSIGN, + TOK_DOUBLE_QUESTION_MARK_ASSIGN, + TOK_DEC, + TOK_INC, + TOK_SHL, + TOK_SAR, + TOK_SHR, + TOK_LT, + TOK_LTE, + TOK_GT, + TOK_GTE, + TOK_EQ, + TOK_STRICT_EQ, + TOK_NEQ, + TOK_STRICT_NEQ, + TOK_LAND, + TOK_LOR, +#ifdef CONFIG_BIGNUM + TOK_MATH_POW, +#endif + TOK_POW, + TOK_ARROW, + TOK_ELLIPSIS, + TOK_DOUBLE_QUESTION_MARK, + TOK_QUESTION_MARK_DOT, + TOK_ERROR, + TOK_PRIVATE_NAME, + TOK_EOF, + /* keywords: WARNING: same order as atoms */ + TOK_NULL, /* must be first */ + TOK_FALSE, + TOK_TRUE, + TOK_IF, + TOK_ELSE, + TOK_RETURN, + TOK_VAR, + TOK_THIS, + TOK_DELETE, + TOK_VOID, + TOK_TYPEOF, + TOK_NEW, + TOK_IN, + TOK_INSTANCEOF, + TOK_DO, + TOK_WHILE, + TOK_FOR, + TOK_BREAK, + TOK_CONTINUE, + TOK_SWITCH, + TOK_CASE, + TOK_DEFAULT, + TOK_THROW, + TOK_TRY, + TOK_CATCH, + TOK_FINALLY, + TOK_FUNCTION, + TOK_DEBUGGER, + TOK_WITH, + /* FutureReservedWord */ + TOK_CLASS, + TOK_CONST, + TOK_ENUM, + TOK_EXPORT, + TOK_EXTENDS, + TOK_IMPORT, + TOK_SUPER, + /* FutureReservedWords when parsing strict mode code */ + TOK_IMPLEMENTS, + TOK_INTERFACE, + TOK_LET, + TOK_PACKAGE, + TOK_PRIVATE, + TOK_PROTECTED, + TOK_PUBLIC, + TOK_STATIC, + TOK_YIELD, + TOK_AWAIT, /* must be last */ + TOK_OF, /* only used for js_parse_skip_parens_token() */ +}; +#define TOK_FIRST_KEYWORD TOK_NULL +#define TOK_LAST_KEYWORD TOK_AWAIT +struct JSString { + LEPUSRefCountHeader header; /* must come first, 32-bit */ + uint32_t len : 31; + uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ + /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, + for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3 + XXX: could change encoding to have one more bit in hash */ + uint32_t hash : 30; + uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ + uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ +#ifdef DUMP_LEAKS + struct list_head link; /* string list */ +#endif + +#ifdef ENABLE_LEPUSNG + // Primjs add + void *cache_; // add to convert to jsString +#endif + union { + uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */ + uint16_t str16[0]; + } u; +}; + +typedef struct JSGCHeader { + uint8_t mark; +} JSGCHeader; + +typedef struct JSVarRef { + LEPUSRefCountHeader header; /* must come first, 32-bit */ + JSGCHeader gc_header; /* must come after LEPUSRefCountHeader, 8-bit */ + uint8_t is_arg : 1; + /* + * 0: the VarRef is on the stack. + * 1: the VarRef is detached, pvalue == &value; + */ + uint8_t is_detached : 1; + int var_idx; /* index of the corresponding function variable on + the stack */ + struct list_head link; + LEPUSValue *pvalue; /* pointer to the value, either on the stack or + to 'value' */ + LEPUSValue value; /* used when the variable is no longer on the stack */ +} JSVarRef; + +#ifdef CONFIG_BIGNUM +typedef struct JSFloatEnv { + limb_t prec; + bf_flags_t flags; + unsigned int status; +} JSFloatEnv; + +typedef struct JSBigFloat { + LEPUSRefCountHeader header; /* must come first, 32-bit */ + bf_t num; +} JSBigFloat; + +/* the same structure is used for big integers and big floats. Big + integers are never infinite or NaNs */ +#else +#ifdef ENABLE_LEPUSNG +// +typedef struct JSBigFloat { + LEPUSRefCountHeader header; /* must come first, 32-bit */ + uint64_t num; +} JSBigFloat; + +// +#endif +#endif + +/* must be large enough to have a negligible runtime cost and small + enough to call the interrupt callback often. */ +#define JS_INTERRUPT_COUNTER_INIT 10000 +// +#define DEFAULT_VIRTUAL_STACK_SIZE 1024 * 1024 * 4 +#define FALLBACK_VIRTUAL_STACK_SIZE 1024 * 1024 * 1 +#define MINIFY_VIRTUAL_STACK_SIZE 1024 * 1024 * 2 +// + +// +typedef unsigned char u_char; +typedef u_char *address; +// + +typedef enum OPCodeFormat { +#define FMT(f) OP_FMT_##f, +#define DEF(id, size, n_pop, n_push, f) +#include "quickjs/include/quickjs-opcode.h" +#undef DEF +#undef FMT +} OPCodeFormat; + +typedef enum OPCodeEnum { +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) OP_##id, +#define def(id, size, n_pop, n_push, f) +#include "quickjs/include/quickjs-opcode.h" +#undef def +#undef DEF +#undef FMT + OP_COUNT, /* excluding temporary opcodes */ + /* temporary opcodes : overlap with the short opcodes */ + OP_TEMP_START = OP_nop + 1, + OP___dummy = OP_TEMP_START - 1, +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) +#define def(id, size, n_pop, n_push, f) OP_##id, +#include "quickjs/include/quickjs-opcode.h" +#undef def +#undef DEF +#undef FMT + OP_TEMP_END, + +// The following opcode cannot be dumped into the bianry production. +#define COMPILE_TIME_OPCODE(id, size, n_pop, n_push, f) OP_##id, +#include "quickjs/include/quickjs-opcode.h" +#undef COMPILE_TIME_OPCODE + +} OPCodeEnum; + +struct FinalizationRegistryContext; + +struct LEPUSContext { +#ifdef ENABLE_PRIMJS_SNAPSHOT + address (*dispatch_table)[OP_COUNT]; +#endif +// +#ifndef ALLOCATE_WINDOWS + mstate allocate_state; +#endif + LEPUSRuntime *rt; + struct list_head link; + + uint16_t binary_object_count; + int binary_object_size; + + JSShape *array_shape; /* initial shape for Array objects */ + + LEPUSValue *class_proto; + LEPUSValue function_proto; + LEPUSValue function_ctor; + LEPUSValue regexp_ctor; + LEPUSValue promise_ctor; + LEPUSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; + LEPUSValue iterator_proto; + LEPUSValue async_iterator_proto; + LEPUSValue array_proto_values; + LEPUSValue throw_type_error; + LEPUSValue eval_obj; + + LEPUSValue global_obj; /* global object */ + LEPUSValue global_var_obj; /* contains the global let/const definitions */ + + uint64_t random_state; +#ifdef CONFIG_BIGNUM + bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ + JSFloatEnv fp_env; /* global FP environment */ +#endif + /* when the counter reaches zero, JSRutime.interrupt_handler is called */ + int interrupt_counter; + BOOL is_error_property_enabled; + + struct list_head loaded_modules; /* list of LEPUSModuleDef.link */ + + /* if NULL, RegExp compilation is not supported */ + LEPUSValue (*compile_regexp)(LEPUSContext *ctx, LEPUSValueConst pattern, + LEPUSValueConst flags); + /* if NULL, eval is not supported */ + LEPUSValue (*eval_internal)(LEPUSContext *ctx, LEPUSValueConst this_obj, + const char *input, size_t input_len, + const char *filename, int flags, int scope_idx, + bool debugger_eval, LEPUSStackFrame *sf); + + void *user_opaque; + // + int64_t napi_env; + BOOL no_lepus_strict_mode; +#if defined(__APPLE__) && !defined(GEN_ANDROID_EMBEDDED) + uint32_t stack_pos; + uint8_t *stack; +#endif +#ifdef ENABLE_QUICKJS_DEBUGGER + LEPUSDebuggerInfo *debugger_info; // structure for quickjs debugger +#endif + uint32_t next_function_id; // for lepusng debugger encode. + uint8_t + debuginfo_outside; // for lepusng debugger encode to avoid break change. + char *lynx_target_sdk_version; + BOOL debugger_mode; + BOOL debugger_parse_script; // for shared context debugger + BOOL debugger_need_polling; + BOOL console_inspect; + // + + PtrHandles *ptr_handles; + napi_handle_scope__ *napi_scope; + bool gc_enable; + bool is_lepusng; + uint64_t binary_version; + struct FinalizationRegistryContext *fg_ctx = nullptr; +}; + +typedef union JSFloat64Union { + double d; + uint64_t u64; + uint32_t u32[2]; +} JSFloat64Union; + +enum { + JS_ATOM_TYPE_STRING = 1, + JS_ATOM_TYPE_GLOBAL_SYMBOL, + JS_ATOM_TYPE_SYMBOL, + JS_ATOM_TYPE_PRIVATE, +}; + +enum { + JS_ATOM_HASH_SYMBOL, + JS_ATOM_HASH_PRIVATE, +}; + +typedef enum { + JS_ATOM_KIND_STRING, + JS_ATOM_KIND_SYMBOL, + JS_ATOM_KIND_PRIVATE, +} JSAtomKindEnum; + +#define JS_ATOM_HASH_MASK ((1 << 30) - 1) + +typedef struct LEPUSClosureVar { + uint8_t is_local : 1; + uint8_t is_arg : 1; + uint8_t is_const : 1; + uint8_t is_lexical : 1; + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* 9 bits available */ + uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the + parent function. otherwise: index to a closure + variable of the parent function */ + JSAtom var_name; +} LEPUSClosureVar; + +#define ARG_SCOPE_INDEX 1 +#define ARG_SCOPE_END (-2) +#define DEBUG_SCOPE_INDEX (-3) + +typedef struct JSVarScope { + int parent; /* index into fd->scopes of the enclosing scope */ + int first; /* index into fd->vars of the last variable in this scope */ +} JSVarScope; + +typedef enum { + /* XXX: add more variable kinds here instead of using bit fields */ + JS_VAR_NORMAL, + JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */ + JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator function + declaration */ + JS_VAR_CATCH, + JS_VAR_FUNCTION_NAME, + JS_VAR_PRIVATE_FIELD, + JS_VAR_PRIVATE_METHOD, + JS_VAR_PRIVATE_GETTER, + JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */ + JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER + */ +} JSVarKindEnum; + +typedef struct JSVarDef { + JSAtom var_name; + int scope_level; /* index into fd->scopes of this variable lexical scope */ + int scope_next; /* index into fd->vars of the next variable in the + * same or enclosing lexical scope */ + uint8_t is_const : 1; + uint8_t is_lexical : 1; + uint8_t is_captured : 1; + uint8_t var_kind : 4; /* see JSVarKindEnum */ + int func_pool_idx : 24; /* only used during compilation */ +} JSVarDef; + +/* for the encoding of the pc2line table */ +#define PC2LINE_BASE (-1) +#define PC2LINE_RANGE 5 +#define PC2LINE_OP_FIRST 1 +#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE) + +// +#define LINE_NUMBER_BITS_COUNT 24 +#define COLUMN_NUMBER_BITS_COUNT 40 +// for compatibility +#define OLD_LINE_NUMBER_BITS_COUNT 12 +// use 2 bits for type. +#define LINE_COLUMN_TYPE_SHIFT 62 +// + +typedef enum JSFunctionKindEnum { + JS_FUNC_NORMAL = 0, + JS_FUNC_GENERATOR = (1 << 0), + JS_FUNC_ASYNC = (1 << 1), + JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), +} JSFunctionKindEnum; + +// +enum class EntryMode { INTERPRETER, BASELINE }; + +#define JIT_THRESHOLD 6 + +// + +// settings key opt + +enum { + PRIMJS_SNAPSHOT_ENABLE = 0b000000001, + JSON_OPT_DISABLE = 0b000000010, + GC_INFO_ENABLE = 0b000000100, + DEEPCLONE_OPT_DISABLE = 0b000001000, + LEPUSNG_HEAP_20 = 0b000010000, + LEPUSNG_HEAP_24 = 0b000100000, + DISABLE_ADJUST_STACKSIZE = 0b001000000, + DISABLE_SEPARABLE_STRING = 0b010000000, + GC_ENABLE = 0b100000000, + EFFECT_ENABLE = 0b1000000000, + MINIFY_STACK_ENABLE = 0b10000000000, + ENABLE_LEPUSNG_STRAGETY = 0b100000000000, + LEPUSNG_HEAP_12 = 0b1000000000000, + LEPUSNG_GC_DISABLE = 0b10000000000000, +}; + +inline int settingsFlag = 0; +// settings key opt end + +typedef struct CallerStrSlot { + uint32_t pc; + uint32_t size : 31; + uint32_t is_str : 1; + union { + const char *str; + uint32_t off; + }; +} CallerStrSlot; + +typedef struct LEPUSFunctionBytecode { + LEPUSRefCountHeader header; /* must come first, 32-bit */ + JSGCHeader gc_header; /* must come after header, 8-bit */ + uint8_t js_mode; + uint8_t has_prototype : 1; /* true if a prototype field is necessary */ + uint8_t has_simple_parameter_list : 1; + uint8_t is_derived_class_constructor : 1; + /* true if home_object needs to be initialized */ + uint8_t need_home_object : 1; + uint8_t func_kind : 2; + uint8_t new_target_allowed : 1; + uint8_t super_call_allowed : 1; + uint8_t super_allowed : 1; + uint8_t arguments_allowed : 1; + uint8_t has_debug : 1; + uint8_t read_only_bytecode : 1; + /* XXX: 4 bits available */ + uint8_t *byte_code_buf; /* (self pointer) */ + int byte_code_len; + JSAtom func_name; + JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) + (self pointer) */ + LEPUSClosureVar + *closure_var; /* list of variables in the closure (self pointer) */ + uint16_t arg_count; + uint16_t var_count; + uint16_t defined_arg_count; /* for length function property */ + uint16_t stack_size; /* maximum stack size */ + LEPUSValue *cpool; /* constant pool (self pointer) */ + int cpool_count; + int closure_var_count; + +#ifdef ENABLE_QUICKJS_DEBUGGER + DebuggerFuncLevelState func_level_state; + struct list_head link; /*ctx->debugger_info->bytecode_list*/ + LEPUSScriptSource *script; + int32_t bp_num; +#endif + // + struct list_head gc_link; + uint32_t function_id; // for lepusNG debugger encode + // + struct { + /* debug info, move to separate structure to save memory? */ + JSAtom filename; + int line_num; + int source_len; + int pc2line_len; +#ifdef ENABLE_QUICKJS_DEBUGGER + int64_t column_num; +#endif + uint8_t *pc2line_buf; + char *source; + struct list_head link; + // for cpu profiler to use. + JSString *file_name; + JSString *func_name; + + CallerStrSlot *caller_slots; + size_t caller_size; + // end. + } debug; + // ATTENTION: NEW MEMBERS MUST BE ADDED IN FRONT OF DEBUG FIELD! +} LEPUSFunctionBytecode; + +typedef struct JSBoundFunction { + LEPUSValue func_obj; + LEPUSValue this_val; + int argc; + LEPUSValue argv[0]; +} JSBoundFunction; + +typedef enum JSIteratorKindEnum { + JS_ITERATOR_KIND_KEY, + JS_ITERATOR_KIND_VALUE, + JS_ITERATOR_KIND_KEY_AND_VALUE, +} JSIteratorKindEnum; + +typedef struct JSForInIterator { + LEPUSValue obj; + BOOL is_array; + uint32_t array_length; + uint32_t idx; +} JSForInIterator; + +typedef struct JSRegExp { + JSString *pattern; + JSString *bytecode; /* also contains the flags */ +} JSRegExp; + +typedef struct JSProxyData { + LEPUSValue target; + LEPUSValue handler; + LEPUSValue proto; + uint8_t is_func; + uint8_t is_revoked; +} JSProxyData; + +typedef struct JSArrayBuffer { + int byte_length; /* 0 if detached */ + uint8_t detached; + uint8_t shared; /* if shared, the array buffer cannot be detached */ + uint8_t *data; /* NULL if detached */ + struct list_head array_list; + void *opaque; + LEPUSFreeArrayBufferDataFunc *free_func; + BOOL from_js_heap; +} JSArrayBuffer; + +typedef struct JSTypedArray { + struct list_head link; /* link to arraybuffer */ + LEPUSObject *obj; /* back pointer to the TypedArray/DataView object */ + LEPUSObject *buffer; /* based array buffer */ + uint32_t offset; /* offset in the array buffer */ + uint32_t length; /* length in the array buffer */ +} JSTypedArray; + +typedef struct JSAsyncFunctionState { + LEPUSValue this_val; /* 'this' generator argument */ + int argc; /* number of function arguments */ + BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */ + LEPUSStackFrame frame; + list_head link; +#ifdef ENABLE_PRIMJS_SNAPSHOT + LEPUSValue *_arg_buf; +#endif +} JSAsyncFunctionState; + +/* XXX: could use an object instead to avoid the + LEPUS_TAG_ASYNC_FUNCTION tag for the GC */ +typedef struct JSAsyncFunctionData { + LEPUSRefCountHeader header; /* must come first, 32-bit */ + JSGCHeader gc_header; /* must come after LEPUSRefCountHeader, 8-bit */ + LEPUSValue resolving_funcs[2]; + BOOL is_active; /* true if the async function state is valid */ + JSAsyncFunctionState func_state; +} JSAsyncFunctionData; + +typedef struct JSReqModuleEntry { + JSAtom module_name; + LEPUSModuleDef *module; /* used using resolution */ +} JSReqModuleEntry; + +typedef enum JSExportTypeEnum { + JS_EXPORT_TYPE_LOCAL, + JS_EXPORT_TYPE_INDIRECT, +} JSExportTypeEnum; + +typedef struct JSExportEntry { + union { + struct { + int var_idx; /* closure variable index */ + JSVarRef *var_ref; /* if != NULL, reference to the variable */ + } local; /* for local export */ + int req_module_idx; /* module for indirect export */ + } u; + JSExportTypeEnum export_type; + JSAtom local_name; /* '*' if export ns from. not used for local + export after compilation */ + JSAtom export_name; /* exported variable name */ +} JSExportEntry; + +typedef struct JSStarExportEntry { + int req_module_idx; /* in req_module_entries */ +} JSStarExportEntry; + +typedef struct JSImportEntry { + int var_idx; /* closure variable index */ + JSAtom import_name; + int req_module_idx; /* in req_module_entries */ +} JSImportEntry; + +struct LEPUSModuleDef { + LEPUSRefCountHeader header; /* must come first, 32-bit */ + JSAtom module_name; + struct list_head link; + + JSReqModuleEntry *req_module_entries; + int req_module_entries_count; + int req_module_entries_size; + + JSExportEntry *export_entries; + int export_entries_count; + int export_entries_size; + + JSStarExportEntry *star_export_entries; + int star_export_entries_count; + int star_export_entries_size; + + JSImportEntry *import_entries; + int import_entries_count; + int import_entries_size; + + LEPUSValue module_ns; + LEPUSValue func_obj; /* only used for LEPUS modules */ + LEPUSModuleInitFunc *init_func; /* only used for C modules */ + BOOL resolved : 8; + BOOL instantiated : 8; + BOOL evaluated : 8; + BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */ + /* true if evaluation yielded an exception. It is saved in + eval_exception */ + BOOL eval_has_exception : 8; + LEPUSValue eval_exception; +}; + +typedef struct JSJobEntry { + struct list_head link; + LEPUSContext *ctx; + LEPUSJobFunc *job_func; + int argc; + LEPUSValue argv[0]; +} JSJobEntry; + +typedef enum { + WEAK_REF_KIND_WEAK_MAP, + WEAK_REF_KIND_WEAK_REF, + WEAK_REF_KIND_FINALIZATION_REGISTRY, +} WeakRefRecordKind; + +typedef struct FinalizationRegistryContext { + int32_t ref_count; + LEPUSContext *ctx; +} FinalizationRegistryContext; + +typedef struct FinalizationRegistryData { + struct FinalizationRegistryContext *fg_ctx; + list_head entries; + LEPUSValue cbs; +} FinalizationRegistryData; + +typedef struct FinalizationRegistryEntry { + struct list_head link; + FinalizationRegistryData *data; // FinalizationRegistryObj + LEPUSValue target; // registerd object + LEPUSValue held_value; + LEPUSValue token; +} FinalizationRegistryEntry; + +typedef struct WeakRefData { + LEPUSValue target; +} WeakRefData; + +typedef struct WeakRefRecord { + WeakRefRecordKind kind; + struct WeakRefRecord *next_weak_ref; + union { + JSMapRecord *map_record; + FinalizationRegistryEntry *fin_node; + WeakRefData *weak_ref; + void *ptr; + } u; +} WeakRefRecord; + +typedef enum JSGeneratorStateEnum { + JS_GENERATOR_STATE_SUSPENDED_START, + JS_GENERATOR_STATE_SUSPENDED_YIELD, + JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_GENERATOR_STATE_EXECUTING, + JS_GENERATOR_STATE_COMPLETED, +} JSGeneratorStateEnum; + +typedef struct JSGeneratorData { + JSGeneratorStateEnum state; + JSAsyncFunctionState func_state; +} JSGeneratorData; + +typedef struct JSProperty { + union { + LEPUSValue value; /* LEPUS_PROP_NORMAL */ + struct { /* LEPUS_PROP_GETSET */ + LEPUSObject *getter; /* NULL if undefined */ + LEPUSObject *setter; /* NULL if undefined */ + } getset; + JSVarRef *var_ref; /* LEPUS_PROP_VARREF */ + struct { /* LEPUS_PROP_AUTOINIT */ + LEPUSValue (*init_func)(LEPUSContext *ctx, LEPUSObject *obj, JSAtom prop, + void *opaque); + void *opaque; + } init; + } u; +} JSProperty; + +#define JS_PROP_INITIAL_SIZE 2 +#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ +#define JS_ARRAY_INITIAL_SIZE 2 + +typedef struct JSShapeProperty { + uint32_t hash_next : 26; /* 0 if last in list */ + uint32_t flags : 6; /* JS_PROP_XXX */ + JSAtom atom; /* JS_ATOM_NULL = free property entry */ +} JSShapeProperty; + +struct JSShape { + uint32_t prop_hash_end[0]; /* hash table of size hash_mask + 1 + before the start of the structure. */ + LEPUSRefCountHeader header; /* must come first, 32-bit */ + JSGCHeader gc_header; /* must come after LEPUSRefCountHeader, 8-bit */ + /* true if the shape is inserted in the shape hash table. If not, + JSShape.hash is not valid */ + uint8_t is_hashed; + /* If true, the shape may have small array index properties 'n' with 0 + <= n <= 2^31-1. If false, the shape is guaranteed not to have + small array index properties */ + uint8_t has_small_array_index; + uint32_t hash; /* current hash value */ + uint32_t prop_hash_mask; + int prop_size; /* allocated properties */ + int prop_count; + JSShape *shape_hash_next; /* in LEPUSRuntime.shape_hash[h] list */ + LEPUSObject *proto; + JSShapeProperty prop[0]; /* prop_size elements */ +}; +struct LEPUSObject { + LEPUSRefCountHeader header; /* must come first, 32-bit */ + JSGCHeader gc_header; /* must come after LEPUSRefCountHeader, 8-bit */ + uint8_t extensible : 1; + uint8_t free_mark : 1; /* only used when freeing objects with cycles */ + uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ + uint8_t fast_array : 1; /* TRUE if u.array is used for get/put */ + uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ + uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */ + uint8_t is_class : 1; /* TRUE if object is a class constructor */ + uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ + uint16_t class_id; /* see JS_CLASS_x */ + /* byte offsets: 8/8 */ + struct list_head link; /* object list */ + /* byte offsets: 16/24 */ + JSShape *shape; /* prototype and property names + flag */ + JSProperty *prop; /* array of properties */ + /* byte offsets: 24/40 */ + struct WeakRefRecord + *first_weak_ref; /* XXX: use a bit and an external hash table? */ + /* byte offsets: 28/48 */ + union { + void *opaque; + struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ + struct JSCFunctionDataRecord + *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ + struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ + struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, + JS_CLASS_SHARED_ARRAY_BUFFER */ + struct JSTypedArray + *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ +#ifdef CONFIG_BIGNUM + struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */ +#endif + struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ + struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, + JS_CLASS_SET_ITERATOR */ + struct JSArrayIteratorData + *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, + JS_CLASS_STRING_ITERATOR */ + struct JSRegExpStringIteratorData + *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ + struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */ + struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ + struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */ + struct JSPromiseFunctionData + *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, + JS_CLASS_PROMISE_REJECT_FUNCTION */ + struct JSAsyncFunctionData + *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, + JS_CLASS_ASYNC_FUNCTION_REJECT */ + struct JSAsyncFromSyncIteratorData + *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR + */ + struct JSAsyncGeneratorData + *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */ + struct FinalizationRegistryData + *fin_reg_data; // JS_CLASS_FinalizationRegistry + struct WeakRefData *weak_ref_data; + struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 + bytes */ + /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION + * and JS_CLASS_ASYNC_GENERATOR_FUNCTION */ + struct LEPUSFunctionBytecode *function_bytecode; + JSVarRef **var_refs; + LEPUSObject *home_object; /* for 'super' access */ + } func; + struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */ + LEPUSCFunctionType c_function; + uint8_t length; + uint8_t cproto; + int16_t magic; + } cfunc; + /* array part for fast arrays and typed arrays */ + struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, + JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + union { + uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + struct JSTypedArray + *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY + */ + } u1; + union { + LEPUSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */ + uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */ + int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */ + uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */ + int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */ + uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ + int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ + uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ + float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ + double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ + } u; + uint32_t count; /* <= 2^31-1. 0 for a detached typed array */ + } array; /* 12/20 bytes */ + JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ + LEPUSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ + } u; + /* byte sizes: 40/48/72 */ +}; + +constexpr const char *lepusjs_filename = "file://lepus.js"; +constexpr const char *lepusng_functionid_str = "__lepusNG_function_id__"; + +enum { + JS_ATOM_NULL, +#define DEF(name, str) JS_ATOM_##name, +#include "quickjs/include/quickjs-atom.h" +#undef DEF + JS_ATOM_END, +}; +#define JS_ATOM_LAST_KEYWORD JS_ATOM_super +#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield + +static const char js_atom_init[] = +#define DEF(name, str) str "\0" +#include "quickjs/include/quickjs-atom.h" +#undef DEF + ; + +typedef struct JSOpCode { +#if defined(ENABLE_PRIMJS_SNAPSHOT) || defined(DUMP_BYTECODE) + const char *name; +#endif + + uint8_t size; /* in bytes */ + /* the opcodes remove n_pop items from the top of the stack, then + pushes n_push items */ + uint8_t n_pop; + uint8_t n_push; + uint8_t fmt; +} JSOpCode; + +extern const JSOpCode opcode_info[]; + +#if SHORT_OPCODES +/* After the final compilation pass, short opcodes are used. Their + opcodes overlap with the temporary opcodes which cannot appear in + the final bytecode. Their description is after the temporary + opcodes in opcode_info[]. */ +#define short_opcode_info(op) \ + opcode_info[(op) >= OP_TEMP_START ? (op) + (OP_TEMP_END - OP_TEMP_START) \ + : (op)] +#else +#define short_opcode_info(op) opcode_info[op] +#endif + +// + +#if defined(ANDROID) || defined(__ANDROID__) +#include + +#define LOG_TAG "primjs" + +#ifndef LOGD +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#endif +#ifndef LOGE +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#endif +#ifndef LOGI +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#endif +#endif + +#define PRINT_LOG_TO_FILE 0 + +#ifdef ENABLE_PRIMJS_TRACE +#if defined(ANDROID) || defined(__ANDROID__) +#if PRINT_LOG_TO_FILE +extern FILE *log_f; +#define PRIM_LOG(...) \ + do { \ + fprintf(log_f, __VA_ARGS__); \ + fflush(log_f); \ + } while (0) +#else +#define PRIM_LOG(...) \ + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#endif // PRINT_LOG_TO_FILE +#else +#define PRIM_LOG printf +#endif // ANDROID +#else +#define PRIM_LOG(...) +#endif // ENABLE_PRIMJS_TRACE + +#define OP_DEFINE_METHOD_METHOD 0 +#define OP_DEFINE_METHOD_GETTER 1 +#define OP_DEFINE_METHOD_SETTER 2 +#define OP_DEFINE_METHOD_ENUMERABLE 4 + +#define JS_THROW_VAR_RO 0 +#define JS_THROW_VAR_REDECL 1 +#define JS_THROW_VAR_UNINITIALIZED 2 +#define JS_THROW_ERROR_DELETE_SUPER 3 + +#define LEPUS_CALL_FLAG_CONSTRUCTOR (1 << 0) +#define JS_CALL_FLAG_COPY_ARGV (1 << 1) +#define JS_CALL_FLAG_GENERATOR (1 << 2) + +#define __exception __attribute__((warn_unused_result)) + +/* JSAtom support */ +#define JS_ATOM_TAG_INT (1U << 31) +#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1) +#define JS_ATOM_MAX ((1U << 30) - 1) + +/* return the max count from the hash size */ +#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) + +/* argument of OP_special_object */ +typedef enum { + OP_SPECIAL_OBJECT_ARGUMENTS, + OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS, + OP_SPECIAL_OBJECT_THIS_FUNC, + OP_SPECIAL_OBJECT_NEW_TARGET, + OP_SPECIAL_OBJECT_HOME_OBJECT, + OP_SPECIAL_OBJECT_VAR_OBJECT, +} OPSpecialObjectEnum; + +#define FUNC_RET_AWAIT 0 +#define FUNC_RET_YIELD 1 +#define FUNC_RET_YIELD_STAR 2 + +#define HINT_STRING 0 +#define HINT_NUMBER 1 +#define HINT_NONE 2 +#ifdef CONFIG_BIGNUM +#define HINT_INTEGER 3 +#endif +/* don't try Symbol.toPrimitive */ +#define HINT_FORCE_ORDINARY (1 << 4) + +#define prim_abort() \ + { \ + printf("[%s:%d] Abort\n", __FILE__, __LINE__); \ + abort(); \ + } + +#ifdef __cplusplus +extern "C" { +#endif + +inline void *system_malloc(size_t size) { + void *ptr; + ptr = malloc(size); + if (!ptr) return NULL; + return ptr; +} +inline void *system_mallocz(size_t size) { + void *ptr; + ptr = system_malloc(size); + if (!ptr) return NULL; + return memset(ptr, 0, size); +} +inline __attribute__((unused)) void *system_realloc(void *ptr, size_t size) { + if (!ptr) { + if (size == 0) return NULL; + return system_malloc(size); + } + if (size == 0) { + free(ptr); + return NULL; + } + + ptr = realloc(ptr, size); + if (!ptr) return NULL; + return ptr; +} +inline void system_free(void *ptr) { + if (!ptr) return; + free(ptr); +} + +typedef struct JSSeparableString { + LEPUSRefCountHeader header; /* ref count for gc*/ + uint32_t len : 31; + uint8_t is_wide_char : 1; + uint32_t depth; + LEPUSValue left_op; + LEPUSValue right_op; + LEPUSValue flat_content; +} JSSeparableString; + +class CStack { + using element = JSSeparableString *; + element *base_ = nullptr; + element *top_ = nullptr; + size_t stack_size_ = 0; + LEPUSRuntime *runtime_ = nullptr; + static constexpr size_t STACK_INIT_SIZE = 64; + + public: + explicit CStack(LEPUSRuntime *rt, uint32_t depth = STACK_INIT_SIZE) { + runtime_ = rt; + base_ = static_cast(system_malloc(sizeof(element) * depth)); + if (!base_) return; + top_ = base_; + stack_size_ = depth; + } + + ~CStack() { system_free(base_); } + + void Push(element node) { + if (static_cast(top_ - base_) >= stack_size_) { + base_ = static_cast( + system_realloc(base_, 2 * stack_size_ * sizeof(element))); + if (!base_) { + stack_size_ = 0; + top_ = nullptr; + return; + } + top_ = base_ + stack_size_; + stack_size_ *= 2; + } + *top_++ = node; + } + + void Pop() { + if (!Empty()) { + --top_; + } + } + + element Top() { + if (!Empty()) { + return *(top_ - 1); + } + return nullptr; + } + + int32_t Empty() { return top_ == base_; } +}; + +QJS_STATIC inline int32_t JS_IsSeparableString(LEPUSValue val) { + return LEPUS_VALUE_IS_SEPARABLE_STRING(val); +} + +QJS_STATIC inline JSSeparableString *JS_GetSeparableString(LEPUSValue val) { + return reinterpret_cast(LEPUS_VALUE_GET_PTR(val)); +} + +LEPUSValue JS_GetSeparableStringContent(LEPUSContext *ctx, LEPUSValue val); +LEPUSValue JS_GetSeparableStringContentNotDup(LEPUSContext *ctx, + LEPUSValue val); + +QJS_HIDE LEPUSValue JS_GetPropertyInternalImpl(LEPUSContext *ctx, + LEPUSValueConst obj, JSAtom prop, + LEPUSValueConst this_obj, + BOOL throw_ref_error); +QJS_HIDE LEPUSValue JS_GetPropertyInternalImpl_GC(LEPUSContext *ctx, + LEPUSValueConst obj, + JSAtom prop, + LEPUSValueConst this_obj, + BOOL throw_ref_error); +QJS_HIDE void prim_js_print(const char *msg); +QJS_HIDE void prim_js_print_gc(const char *msg); + +QJS_HIDE void prim_js_print_register(uint64_t reg_val); + +QJS_HIDE void prim_js_print_trace(int bytecode, int tos); +QJS_HIDE void prim_js_print_trace_gc(int bytecode, int tos); + +QJS_HIDE void prim_js_print_func(LEPUSContext *ctx, LEPUSValue func_obj); +QJS_HIDE void prim_js_print_func_gc(LEPUSContext *ctx, LEPUSValue func_obj); + +QJS_HIDE void JS_FreeValueRef(LEPUSContext *ctx, LEPUSValue v); + +QJS_HIDE LEPUSValue js_closure(LEPUSContext *ctx, LEPUSValue bfunc, + JSVarRef **cur_var_refs, LEPUSStackFrame *sf); +LEPUSValue js_closure_gc(LEPUSContext *ctx, LEPUSValue bfunc, + JSVarRef **cur_var_refs, LEPUSStackFrame *sf); + +QJS_HIDE void DebuggerPause(LEPUSContext *ctx, LEPUSValue val, + const uint8_t *pc); + +QJS_HIDE void DebuggerCallEachOp(LEPUSContext *ctx, const uint8_t *pc, + LEPUSFunctionBytecode *b); + +QJS_HIDE void DebuggerCallEachFunc(LEPUSContext *ctx, const uint8_t *pc); + +QJS_HIDE LEPUSValue __JS_AtomToValue(LEPUSContext *ctx, JSAtom atom, + BOOL force_string); +QJS_HIDE LEPUSValue __JS_AtomToValue_GC(LEPUSContext *ctx, JSAtom atom, + BOOL force_string); +QJS_HIDE BOOL js_check_stack_overflow(LEPUSContext *ctx, size_t alloca_size); +QJS_HIDE LEPUSValue JS_ThrowStackOverflow(LEPUSContext *ctx); +QJS_HIDE LEPUSValue JS_ThrowStackOverflow_GC(LEPUSContext *ctx); +QJS_HIDE void build_backtrace( + LEPUSContext *ctx, LEPUSValueConst error_obj, const char *filename, + /* */ int64_t line_num, /* */ + const uint8_t *cur_pc, int backtrace_flags, uint8_t is_parse_error = 0); +QJS_HIDE LEPUSValue JS_NewSymbolFromAtom(LEPUSContext *ctx, JSAtom descr, + int atom_type); +QJS_HIDE LEPUSValue JS_NewSymbolFromAtom_GC(LEPUSContext *ctx, JSAtom descr, + int atom_type); +QJS_HIDE LEPUSValue JS_ToObject_GC(LEPUSContext *ctx, LEPUSValueConst val); +QJS_HIDE LEPUSValue PRIM_JS_NewObject(LEPUSContext *ctx); +QJS_HIDE LEPUSValue PRIM_JS_NewObject_GC(LEPUSContext *ctx); +QJS_HIDE LEPUSValue js_build_arguments(LEPUSContext *ctx, int argc, + LEPUSValueConst *argv); +QJS_HIDE LEPUSValue js_build_arguments_gc(LEPUSContext *ctx, int argc, + LEPUSValueConst *argv); +QJS_HIDE LEPUSValue js_build_mapped_arguments(LEPUSContext *ctx, int argc, + LEPUSValueConst *argv, + LEPUSStackFrame *sf, + int arg_count); +QJS_HIDE LEPUSValue js_build_mapped_arguments_gc(LEPUSContext *ctx, int argc, + LEPUSValueConst *argv, + LEPUSStackFrame *sf, + int arg_count); +QJS_HIDE void prim_close_var_refs(LEPUSContext *ctx, LEPUSStackFrame *sf); +QJS_HIDE void prim_close_var_refs_gc(LEPUSContext *ctx, LEPUSStackFrame *sf); +QJS_HIDE LEPUSValue js_build_rest(LEPUSContext *ctx, int first, int argc, + LEPUSValueConst *argv); +QJS_HIDE LEPUSValue js_build_rest_gc(LEPUSContext *ctx, int first, int argc, + LEPUSValueConst *argv); +QJS_HIDE LEPUSValue js_function_apply(LEPUSContext *ctx, + LEPUSValueConst this_val, int argc, + LEPUSValueConst *argv, int magic); +QJS_HIDE LEPUSValue js_function_apply_gc(LEPUSContext *ctx, + LEPUSValueConst this_val, int argc, + LEPUSValueConst *argv, int magic); +QJS_HIDE int JS_CheckBrand(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst func); +QJS_HIDE int JS_CheckBrand_GC(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst func); +QJS_HIDE int JS_AddBrand(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst home_obj); +QJS_HIDE int JS_AddBrand_GC(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst home_obj); +QJS_HIDE int JS_ThrowTypeErrorReadOnly(LEPUSContext *ctx, int flags, + JSAtom atom); +QJS_HIDE LEPUSValue JS_ThrowSyntaxErrorVarRedeclaration(LEPUSContext *ctx, + JSAtom prop); +QJS_HIDE LEPUSValue JS_ThrowSyntaxErrorVarRedeclaration_GC(LEPUSContext *ctx, + JSAtom prop); +QJS_HIDE LEPUSValue JS_ThrowReferenceErrorUninitialized(LEPUSContext *ctx, + JSAtom name); +QJS_HIDE LEPUSValue JS_ThrowReferenceErrorUninitialized_GC(LEPUSContext *ctx, + JSAtom name); +QJS_HIDE int JS_IteratorClose(LEPUSContext *ctx, LEPUSValueConst enum_obj, + BOOL is_exception_pending); + +QJS_HIDE LEPUSValue JS_CallConstructorInternal(LEPUSContext *ctx, + LEPUSValueConst func_obj, + LEPUSValueConst new_target, + int argc, LEPUSValue *argv, + int flags); +QJS_HIDE LEPUSValue JS_CallConstructorInternal_GC(LEPUSContext *ctx, + LEPUSValueConst func_obj, + LEPUSValueConst new_target, + int argc, LEPUSValue *argv, + int flags); +QJS_HIDE int JS_CheckGlobalVar(LEPUSContext *ctx, JSAtom prop); +QJS_HIDE int JS_CheckGlobalVar_GC(LEPUSContext *ctx, JSAtom prop); +QJS_HIDE LEPUSValue JS_GetPropertyValue(LEPUSContext *ctx, + LEPUSValueConst this_obj, + LEPUSValue prop); + +QJS_HIDE LEPUSValue JS_GetPropertyValue_GC(LEPUSContext *ctx, + LEPUSValueConst this_obj, + LEPUSValue prop); +QJS_HIDE int JS_DefineGlobalVar(LEPUSContext *ctx, JSAtom prop, int def_flags); +QJS_HIDE int JS_DefineGlobalVar_GC(LEPUSContext *ctx, JSAtom prop, + int def_flags); +QJS_HIDE LEPUSValue PRIM_JS_NewArray(LEPUSContext *ctx); +QJS_HIDE LEPUSValue PRIM_JS_NewArray_GC(LEPUSContext *ctx); + +QJS_HIDE LEPUSValue js_regexp_constructor_internal(LEPUSContext *ctx, + LEPUSValueConst ctor, + LEPUSValue pattern, + LEPUSValue bc); +QJS_HIDE LEPUSValue js_regexp_constructor_internal_gc(LEPUSContext *ctx, + LEPUSValueConst ctor, + LEPUSValue pattern, + LEPUSValue bc); +QJS_HIDE int JS_SetPropertyValue(LEPUSContext *ctx, LEPUSValueConst this_obj, + LEPUSValue prop, LEPUSValue val, int flags); +QJS_HIDE int JS_SetPropertyValue_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, + LEPUSValue prop, LEPUSValue val, int flags); +QJS_HIDE int JS_CheckDefineGlobalVar(LEPUSContext *ctx, JSAtom prop, int flags); +QJS_HIDE int JS_CheckDefineGlobalVar_GC(LEPUSContext *ctx, JSAtom prop, + int flags); +QJS_HIDE int JS_DefineGlobalFunction(LEPUSContext *ctx, JSAtom prop, + LEPUSValueConst func, int def_flags); +QJS_HIDE int JS_DefineGlobalFunction_GC(LEPUSContext *ctx, JSAtom prop, + LEPUSValueConst func, int def_flags); +QJS_HIDE LEPUSValue JS_GetPrivateField(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst name); +QJS_HIDE LEPUSValue JS_GetPrivateField_GC(LEPUSContext *ctx, + LEPUSValueConst obj, + LEPUSValueConst name); +QJS_HIDE int JS_DefinePrivateField(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst name, LEPUSValue val); +QJS_HIDE int JS_DefinePrivateField_GC(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst name, LEPUSValue val); +QJS_HIDE int JS_DefineObjectName(LEPUSContext *ctx, LEPUSValueConst obj, + JSAtom name, int flags); +QJS_HIDE int JS_DefineObjectNameComputed(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst str, int flags); +QJS_HIDE int JS_DefineObjectNameComputed_GC(LEPUSContext *ctx, + LEPUSValueConst obj, + LEPUSValueConst str, int flags); +QJS_HIDE int JS_SetPrototypeInternal(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst proto_val, + BOOL throw_flag); +QJS_HIDE int JS_SetPrototypeInternal_GC(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst proto_val, + BOOL throw_flag); +QJS_HIDE void js_method_set_home_object(LEPUSContext *ctx, + LEPUSValueConst func_obj, + LEPUSValueConst home_obj); +QJS_HIDE void js_method_set_home_object_gc(LEPUSContext *ctx, + LEPUSValueConst func_obj, + LEPUSValueConst home_obj); +QJS_HIDE int JS_DefinePropertyValueValue(LEPUSContext *ctx, + LEPUSValueConst this_obj, + LEPUSValue prop, LEPUSValue val, + int flags); +QJS_HIDE int JS_DefinePropertyValueValue_GC(LEPUSContext *ctx, + LEPUSValueConst this_obj, + LEPUSValue prop, LEPUSValue val, + int flags); +QJS_HIDE __exception int js_append_enumerate(LEPUSContext *ctx, LEPUSValue *sp); +QJS_HIDE __exception int js_append_enumerate_gc(LEPUSContext *ctx, + LEPUSValue *sp); +QJS_HIDE int js_method_set_properties(LEPUSContext *ctx, + LEPUSValueConst func_obj, JSAtom name, + int flags, LEPUSValueConst home_obj); +QJS_HIDE int js_method_set_properties_gc(LEPUSContext *ctx, + LEPUSValueConst func_obj, JSAtom name, + int flags, LEPUSValueConst home_obj); +QJS_HIDE int prim_js_copy_data_properties(LEPUSContext *ctx, LEPUSValue *sp, + int mask); +QJS_HIDE int prim_js_copy_data_properties_gc(LEPUSContext *ctx, LEPUSValue *sp, + int mask); +QJS_HIDE int JS_ToBoolFree(LEPUSContext *ctx, LEPUSValue val); +QJS_HIDE int JS_ToBoolFree_GC(LEPUSContext *ctx, LEPUSValue val); +QJS_HIDE int js_op_define_class(LEPUSContext *ctx, LEPUSValue *sp, + JSAtom class_name, int class_flags, + JSVarRef **cur_var_refs, LEPUSStackFrame *sf); +QJS_HIDE void close_lexical_var(LEPUSContext *ctx, LEPUSStackFrame *sf, + int idx); +QJS_HIDE int JS_SetPropertyGeneric(LEPUSContext *ctx, LEPUSObject *p, + JSAtom prop, LEPUSValue val, + LEPUSValueConst this_obj, int flags); +QJS_HIDE int JS_SetPropertyGeneric_GC(LEPUSContext *ctx, LEPUSObject *p, + JSAtom prop, LEPUSValue val, + LEPUSValueConst this_obj, int flags); +QJS_HIDE int JS_SetPrivateField(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst name, LEPUSValue val); +QJS_HIDE int JS_SetPrivateField_GC(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst name, LEPUSValue val); +QJS_HIDE LEPUSValue JS_ThrowTypeErrorNotAnObject(LEPUSContext *ctx); +QJS_HIDE int prim_js_with_op_gc(LEPUSContext *ctx, LEPUSValue *sp, JSAtom atom, + int is_with, int opcode); +QJS_HIDE JSVarRef *get_var_ref(LEPUSContext *ctx, LEPUSStackFrame *sf, + int var_idx, BOOL is_arg); +QJS_HIDE JSVarRef *get_var_ref_gc(LEPUSContext *ctx, LEPUSStackFrame *sf, + int var_idx, BOOL is_arg); +QJS_HIDE void free_var_ref(LEPUSRuntime *rt, JSVarRef *var_ref); + +QJS_HIDE JSProperty *add_property(LEPUSContext *ctx, LEPUSObject *p, + JSAtom prop, int prop_flags); +QJS_HIDE JSProperty *add_property_gc(LEPUSContext *ctx, LEPUSObject *p, + JSAtom prop, int prop_flags); +QJS_HIDE LEPUSValue prim_js_op_eval(LEPUSContext *ctx, int scope_idx, + LEPUSValue op1); +QJS_HIDE LEPUSValue prim_js_op_eval_gc(LEPUSContext *ctx, int scope_idx, + LEPUSValue op1); +QJS_HIDE int prim_js_with_op(LEPUSContext *ctx, LEPUSValue *sp, JSAtom atom, + int is_with, int opcode); + +QJS_HIDE LEPUSValue JS_GetGlobalVarImpl(LEPUSContext *ctx, JSAtom prop, + BOOL throw_ref_error); +QJS_HIDE LEPUSValue JS_GetGlobalVarImpl_GC(LEPUSContext *ctx, JSAtom prop, + BOOL throw_ref_error); +QJS_HIDE int JS_GetGlobalVarRef(LEPUSContext *ctx, JSAtom prop, LEPUSValue *sp); +QJS_HIDE int JS_GetGlobalVarRef_GC(LEPUSContext *ctx, JSAtom prop, + LEPUSValue *sp); +QJS_HIDE LEPUSValue prim_js_for_in_start(LEPUSContext *ctx, LEPUSValue op); +QJS_HIDE LEPUSValue prim_js_for_in_start_gc(LEPUSContext *ctx, LEPUSValue op); +QJS_HIDE int js_for_in_next(LEPUSContext *ctx, LEPUSValue *sp); +QJS_HIDE int js_for_in_next_gc(LEPUSContext *ctx, LEPUSValue *sp); +QJS_HIDE int js_for_await_of_next(LEPUSContext *ctx, LEPUSValue *sp); +QJS_HIDE int js_for_await_of_next_gc(LEPUSContext *ctx, LEPUSValue *sp); +QJS_HIDE int js_iterator_get_value_done(LEPUSContext *ctx, LEPUSValue *sp); +QJS_HIDE int js_iterator_get_value_done_gc(LEPUSContext *ctx, LEPUSValue *sp); +QJS_HIDE int js_for_of_start(LEPUSContext *ctx, LEPUSValue *sp, BOOL is_async); +QJS_HIDE int js_for_of_start_gc(LEPUSContext *ctx, LEPUSValue *sp, + BOOL is_async); +QJS_HIDE int js_for_of_next(LEPUSContext *ctx, LEPUSValue *sp, int offset); +QJS_HIDE int js_for_of_next_gc(LEPUSContext *ctx, LEPUSValue *sp, int offset); +QJS_HIDE LEPUSValue *prim_js_iterator_close_return(LEPUSContext *ctx, + LEPUSValue *sp, + LEPUSValue *stack_buf); +QJS_HIDE LEPUSValue *prim_js_iterator_close_return_gc(LEPUSContext *ctx, + LEPUSValue *sp); +QJS_HIDE int prim_js_async_iterator_close(LEPUSContext *ctx, LEPUSValue *sp); +QJS_HIDE int prim_js_async_iterator_close_gc(LEPUSContext *ctx, LEPUSValue *sp); +QJS_HIDE int prim_js_async_iterator_get(LEPUSContext *ctx, LEPUSValue *sp, + int flags); +QJS_HIDE int prim_js_async_iterator_get_gc(LEPUSContext *ctx, LEPUSValue *sp, + int flags); +QJS_HIDE LEPUSValue primjs_get_super_ctor(LEPUSContext *ctx, LEPUSValue op); +QJS_HIDE LEPUSValue primjs_get_super_ctor_gc(LEPUSContext *ctx, LEPUSValue op); +QJS_HIDE int prim_js_iterator_call(LEPUSContext *ctx, LEPUSValue *sp, + int flags); + +QJS_HIDE LEPUSValue JS_ToPrimitiveFree(LEPUSContext *ctx, LEPUSValue val, + int hint); +QJS_HIDE LEPUSValue JS_ToPrimitiveFree_GC(LEPUSContext *ctx, LEPUSValue val, + int hint); +QJS_HIDE LEPUSValue JS_ConcatString(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2); +QJS_HIDE LEPUSValue JS_ConcatString_GC(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2); +QJS_HIDE LEPUSValue prim_js_unary_arith_slow(LEPUSContext *ctx, LEPUSValue op1, + OPCodeEnum op); +QJS_HIDE LEPUSValue prim_js_unary_arith_slow_gc(LEPUSContext *ctx, + LEPUSValue op1, OPCodeEnum op); +QJS_HIDE LEPUSValue prim_js_add_slow(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2); +QJS_HIDE LEPUSValue prim_js_add_slow_gc(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2); +QJS_HIDE no_inline LEPUSValue prim_js_not_slow(LEPUSContext *ctx, + LEPUSValue op); +QJS_HIDE no_inline LEPUSValue prim_js_not_slow_gc(LEPUSContext *ctx, + LEPUSValue op); +QJS_HIDE LEPUSValue prim_js_binary_arith_slow(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2, OPCodeEnum op); +QJS_HIDE LEPUSValue prim_js_binary_arith_slow_gc(LEPUSContext *ctx, + LEPUSValue op1, LEPUSValue op2, + OPCodeEnum op); +QJS_HIDE double prim_js_fmod_double(double a, double b); +QJS_HIDE double prim_js_fmod_double_gc(double a, double b); +QJS_HIDE int js_post_inc_slow(LEPUSContext *ctx, LEPUSValue *sp, OPCodeEnum op); +QJS_HIDE int js_post_inc_slow_gc(LEPUSContext *ctx, LEPUSValue *sp, + OPCodeEnum op); +QJS_HIDE LEPUSValue prim_js_binary_logic_slow(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2, OPCodeEnum op); +QJS_HIDE LEPUSValue prim_js_binary_logic_slow_gc(LEPUSContext *ctx, + LEPUSValue op1, LEPUSValue op2, + OPCodeEnum op); +QJS_HIDE LEPUSValue prim_js_shr_slow(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2); +QJS_HIDE LEPUSValue prim_js_shr_slow_gc(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2); +QJS_HIDE LEPUSValue prim_js_relation_slow(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2, OPCodeEnum op); +QJS_HIDE LEPUSValue prim_js_relation_slow_gc(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2, OPCodeEnum op); +QJS_HIDE LEPUSValue prim_js_eq_slow(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2, int is_neq); +QJS_HIDE LEPUSValue prim_js_eq_slow_gc(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2, int is_neq); +QJS_HIDE LEPUSValue prim_js_strict_eq_slow(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2, BOOL is_neq); +QJS_HIDE LEPUSValue prim_js_strict_eq_slow_gc(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2, BOOL is_neq); +QJS_HIDE LEPUSValue prim_js_operator_instanceof(LEPUSContext *ctx, + LEPUSValue op1, LEPUSValue op2); +QJS_HIDE LEPUSValue prim_js_operator_instanceof_gc(LEPUSContext *ctx, + LEPUSValue op1, + LEPUSValue op2); +QJS_HIDE LEPUSValue prim_js_operator_in(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2); +QJS_HIDE LEPUSValue prim_js_operator_in_gc(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2); +QJS_HIDE __exception int js_operator_typeof(LEPUSContext *ctx, LEPUSValue op1); +QJS_HIDE __exception int js_operator_typeof_gc(LEPUSContext *ctx, + LEPUSValue op1); +QJS_HIDE LEPUSValue prim_js_operator_delete(LEPUSContext *ctx, LEPUSValue op1, + LEPUSValue op2); +QJS_HIDE LEPUSValue prim_js_operator_delete_gc(LEPUSContext *ctx, + LEPUSValue op1, LEPUSValue op2); +QJS_HIDE int JS_SetPropertyInternalImpl(LEPUSContext *ctx, + LEPUSValueConst this_obj, JSAtom prop, + LEPUSValue val, int flags); +QJS_HIDE int JS_SetPropertyInternalImpl_GC(LEPUSContext *ctx, + LEPUSValueConst this_obj, + JSAtom prop, LEPUSValue val, + int flags); + +QJS_HIDE int set_array_length(LEPUSContext *ctx, LEPUSObject *p, + JSProperty *prop, LEPUSValue val, int flags); + +QJS_HIDE BOOL JS_IsUncatchableError(LEPUSContext *ctx, LEPUSValueConst val); +QJS_HIDE BOOL JS_IsUncatchableError_GC(LEPUSContext *ctx, LEPUSValueConst val); +QJS_HIDE JSAtom js_value_to_atom(LEPUSContext *ctx, LEPUSValueConst val); +QJS_HIDE JSAtom js_value_to_atom_gc(LEPUSContext *ctx, LEPUSValueConst val); +QJS_HIDE LEPUSValue JS_ThrowReferenceErrorNotDefined(LEPUSContext *ctx, + JSAtom name); +QJS_HIDE LEPUSValue JS_ThrowReferenceErrorNotDefined_GC(LEPUSContext *ctx, + JSAtom name); +LEPUSValue JS_ThrowTypeErrorNotFunction(LEPUSContext *ctx); + +#ifdef ENABLE_PRIMJS_SNAPSHOT +typedef LEPUSValue (*QuickJsCallStub)(LEPUSValue this_arg, + LEPUSValue new_target, + LEPUSValue func_obj, address entry_point, + int argc, LEPUSValue *argv, int flags); + +extern QuickJsCallStub entry; +QJS_HIDE void compile_function(LEPUSContext *, LEPUSFunctionBytecode *bytecode); +#endif + +// + +typedef struct JSToken { + int val; + int line_num; /* line number of token start */ + const uint8_t *ptr; + union { + struct { + LEPUSValue str; + int sep; + } str; + struct { + LEPUSValue val; +#ifdef CONFIG_BIGNUM + slimb_t exponent; /* may be != 0 only if val is a float */ +#endif + } num; + struct { + JSAtom atom; + BOOL has_escape; + BOOL is_reserved; + } ident; + struct { + LEPUSValue body; + LEPUSValue flags; + } regexp; + } u; +} JSToken; + +#ifndef NO_QUICKJS_COMPILER +LEPUSValue js_dynamic_import(LEPUSContext *ctx, LEPUSValueConst specifier); +#endif + +#ifdef __cplusplus +} +#endif + +/* */ +static __attribute__((unused)) int JS_DefineProperty_RC( + LEPUSContext *ctx, LEPUSValueConst this_obj, JSAtom prop, + LEPUSValueConst val, LEPUSValueConst getter, LEPUSValueConst setter, + int flags); +static __attribute__((unused)) int JS_DefinePropertyValue_RC( + LEPUSContext *ctx, LEPUSValueConst this_obj, JSAtom prop, LEPUSValue val, + int flags); +static __attribute__((unused)) void JS_MarkValue_RC(LEPUSRuntime *rt, + LEPUSValueConst val, + LEPUS_MarkFunc *mark_func, + int local_idx = -1); +static __attribute__((unused)) int JS_ToBoolFree_RC(LEPUSContext *ctx, + LEPUSValue val); +static __attribute__((unused)) int JS_IsInstanceOf_RC(LEPUSContext *ctx, + LEPUSValueConst val, + LEPUSValueConst obj); +static __attribute__((unused)) LEPUSValue JS_ToString_RC(LEPUSContext *ctx, + LEPUSValueConst val); +/* */ + +/* */ +LEPUSValue JS_GetSeparableStringContent_GC(LEPUSContext *ctx, LEPUSValue val); +LEPUSValue JS_GetSeparableStringContentNotDup_GC(LEPUSContext *ctx, + LEPUSValue val); +QJS_HIDE int set_array_length_gc(LEPUSContext *ctx, LEPUSObject *p, + JSProperty *prop, LEPUSValue val, int flags); +bool gc_enabled(); +LEPUSRuntime *JS_NewRuntime_GC(); +LEPUSRuntime *JS_NewRuntime2_GC(const LEPUSMallocFunctions *mf, void *opaque); +void JS_FreeRuntime_GC(LEPUSRuntime *rt); +void JS_FreeRuntimeForEffect(LEPUSRuntime *rt); +LEPUSContext *JS_NewContext_GC(LEPUSRuntime *rt); +void JS_FreeContext_GC(LEPUSContext *ctx); +LEPUSContext *JS_NewContextRaw_GC(LEPUSRuntime *rt); + +void JS_SetMemoryLimit_GC(LEPUSRuntime *rt, size_t limit); +void JS_RunGC_GC(LEPUSRuntime *rt); + +void JS_AddIntrinsicBaseObjects_GC(LEPUSContext *ctx); +void JS_AddIntrinsicDate_GC(LEPUSContext *ctx); +void JS_AddIntrinsicEval_GC(LEPUSContext *ctx); +void JS_AddIntrinsicStringNormalize_GC(LEPUSContext *ctx); +void JS_AddIntrinsicRegExp_GC(LEPUSContext *ctx); +void JS_AddIntrinsicJSON_GC(LEPUSContext *ctx); +void JS_AddIntrinsicProxy_GC(LEPUSContext *ctx); +void JS_AddIntrinsicMapSet_GC(LEPUSContext *ctx); +void JS_AddIntrinsicTypedArrays_GC(LEPUSContext *ctx); +void JS_AddIntrinsicPromise_GC(LEPUSContext *ctx); + +void JS_SetClassProto_GC(LEPUSContext *ctx, LEPUSClassID class_id, + LEPUSValue obj); +LEPUSValue JS_GetClassProto_GC(LEPUSContext *ctx, LEPUSClassID class_id); +int JS_MoveUnhandledRejectionToException_GC(LEPUSContext *ctx); +void JS_AddIntrinsicRegExpCompiler_GC(LEPUSContext *ctx); +#ifdef QJS_UNITTEST +LEPUSValue js_string_codePointRange_GC(LEPUSContext *ctx, + LEPUSValueConst this_val, int argc, + LEPUSValueConst *argv); +#endif + +JSAtom LEPUS_NewAtom(LEPUSContext *ctx, const char *str); +JSAtom JS_NewAtomUInt32_GC(LEPUSContext *ctx, uint32_t n); +LEPUSValue JS_AtomToValue_GC(LEPUSContext *ctx, JSAtom atom); +LEPUSValue JS_AtomToString_GC(LEPUSContext *ctx, JSAtom atom); +typedef struct JSClassShortDef { + JSAtom class_name; + LEPUSClassFinalizer *finalizer; + LEPUSClassGCMark *gc_mark; +} JSClassShortDef; +QJS_HIDE int init_class_range(LEPUSRuntime *rt, JSClassShortDef const *tab, + int start, int count); +const char *JS_AtomToCString_GC(LEPUSContext *ctx, JSAtom atom); + +void JS_MarkValue_GC(LEPUSRuntime *rt, LEPUSValueConst val, + LEPUS_MarkFunc *mark_func, int local_idx = -1); + +LEPUSValue JS_NewInt64_GC(LEPUSContext *ctx, int64_t v); +LEPUSValue JS_NewError_GC(LEPUSContext *ctx); + +void JS_SetVirtualStackSize_GC(LEPUSContext *ctx, uint32_t stack_size); +int JS_ToBool_GC(LEPUSContext *ctx, LEPUSValueConst val); +int JS_ToInt32_GC(LEPUSContext *ctx, int32_t *pres, LEPUSValueConst val); +int JS_ToInt64_GC(LEPUSContext *ctx, int64_t *pres, LEPUSValueConst val); +int JS_ToIndex_GC(LEPUSContext *ctx, uint64_t *plen, LEPUSValueConst val); +int JS_ToFloat64_GC(LEPUSContext *ctx, double *pres, LEPUSValueConst val); +int JS_ToBigInt64_GC(LEPUSContext *ctx, int64_t *pres, LEPUSValueConst val); +LEPUSValue JS_NewStringLen_GC(LEPUSContext *ctx, const char *buf, + size_t buf_len); +LEPUSValue JS_NewString_GC(LEPUSContext *ctx, const char *str); +LEPUSValue JS_NewAtomString_GC(LEPUSContext *ctx, const char *str); +LEPUSValue JS_ToString_GC(LEPUSContext *ctx, LEPUSValueConst val); +LEPUSValue JS_ToPropertyKey_GC(LEPUSContext *ctx, LEPUSValueConst val); +const char *JS_ToCStringLen2_GC(LEPUSContext *ctx, size_t *plen, + LEPUSValueConst val1, BOOL cesu8); + +LEPUSValue __attribute__((always_inline)) +JS_NewObjectProtoClass_GC(LEPUSContext *ctx, LEPUSValueConst proto_val, + LEPUSClassID class_id); +LEPUSValue JS_NewObjectClass_GC(LEPUSContext *ctx, int class_id); +LEPUSValue JS_NewObjectProto_GC(LEPUSContext *ctx, LEPUSValueConst proto); +LEPUSValue JS_NewObject_GC(LEPUSContext *ctx); +LEPUSValue JS_NewArray_GC(LEPUSContext *ctx); +int JS_IsArray_GC(LEPUSContext *ctx, LEPUSValueConst val); + +LEPUSValue JS_GetPropertyInternal_GC(LEPUSContext *ctx, LEPUSValueConst obj, + JSAtom prop, LEPUSValueConst this_obj, + BOOL throw_ref_error); +LEPUSValue JS_GetPropertyStr_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, + const char *prop); +LEPUSValue JS_GetPropertyUint32_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, + uint32_t idx); +int JS_SetPropertyInternal_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, + JSAtom prop, LEPUSValue val, int flags); + +int JS_SetPropertyUint32_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, + uint32_t idx, LEPUSValue val); +int JS_SetPropertyInt64_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, + int64_t idx, LEPUSValue val); + +int JS_SetPropertyStr_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, + const char *prop, LEPUSValue val); +int JS_HasProperty_GC(LEPUSContext *ctx, LEPUSValueConst obj, JSAtom prop); +int JS_IsExtensible_GC(LEPUSContext *ctx, LEPUSValueConst obj); +int JS_PreventExtensions_GC(LEPUSContext *ctx, LEPUSValueConst obj); +int JS_DeleteProperty_GC(LEPUSContext *ctx, LEPUSValueConst obj, JSAtom prop, + int flags); +int JS_SetPrototype_GC(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst proto_val); +LEPUSValueConst JS_GetPrototype_GC(LEPUSContext *ctx, LEPUSValueConst val); +int JS_GetOwnPropertyNames_GC(LEPUSContext *ctx, LEPUSPropertyEnum **ptab, + uint32_t *plen, LEPUSValueConst obj, int flags); +int JS_GetOwnProperty_GC(LEPUSContext *ctx, LEPUSPropertyDescriptor *desc, + LEPUSValueConst obj, JSAtom prop); +LEPUSValue JS_Call_GC(LEPUSContext *ctx, LEPUSValueConst func_obj, + LEPUSValueConst this_obj, int argc, + LEPUSValueConst *argv); +LEPUSValue JS_CallInternalTI_GC(LEPUSContext *caller_ctx, LEPUSValue func_obj, + LEPUSValue this_obj, LEPUSValue new_target, + int argc, LEPUSValue *argv, int flags); + +LEPUSValue JS_Invoke_GC(LEPUSContext *ctx, LEPUSValueConst this_val, + JSAtom atom, int argc, LEPUSValueConst *argv); +LEPUSValue JS_CallConstructor_GC(LEPUSContext *ctx, LEPUSValueConst func_obj, + int argc, LEPUSValueConst *argv); +LEPUSValue JS_CallConstructor2_GC(LEPUSContext *ctx, LEPUSValueConst func_obj, + LEPUSValueConst new_target, int argc, + LEPUSValueConst *argv); +LEPUSValue JS_Eval_GC(LEPUSContext *ctx, const char *input, size_t input_len, + const char *filename, int eval_flags); +LEPUSValue JS_EvalBinary_GC(LEPUSContext *ctx, const uint8_t *buf, + size_t buf_len, int flags); +LEPUSValue JS_GetGlobalObject_GC(LEPUSContext *ctx); +LEPUSValue JS_GetGlobalVar_GC(LEPUSContext *ctx, JSAtom prop, + BOOL throw_ref_error); +void JS_SetStringCache_GC(LEPUSContext *ctx, LEPUSValue val, void *p); +int JS_SetGlobalVar_GC(LEPUSContext *ctx, JSAtom prop, LEPUSValue val, + int flag); +LEPUSValue JS_DeepEqual_GC(LEPUSContext *ctx, LEPUSValueConst obj1, + LEPUSValueConst obj2); +LEPUSValue JS_DeepCopy_GC(LEPUSContext *ctx, LEPUSValueConst obj); +void JS_IterateObject_GC(LEPUSContext *ctx, LEPUSValue obj, + IterateObject callback, void *pfunc, void *raw_data); +int JS_GetLength_GC(LEPUSContext *ctx, LEPUSValue val); + +int JS_IsInstanceOf_GC(LEPUSContext *ctx, LEPUSValueConst val, + LEPUSValueConst obj); + +int JS_DefineProperty_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, + JSAtom prop, LEPUSValueConst val, + LEPUSValueConst getter, LEPUSValueConst setter, + int flags); +int JS_DefinePropertyValue_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, + JSAtom prop, LEPUSValue val, int flags); +int JS_DefinePropertyValueUint32_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, + uint32_t idx, LEPUSValue val, int flags); +int JS_DefinePropertyValueStr_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, + const char *prop, LEPUSValue val, int flags); +int JS_DefinePropertyGetSet_GC(LEPUSContext *ctx, LEPUSValueConst this_obj, + JSAtom prop, LEPUSValue getter, + LEPUSValue setter, int flags); +void *JS_GetOpaque2_GC(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSClassID class_id); +LEPUSValue JS_NewArrayBuffer_GC(LEPUSContext *ctx, uint8_t *buf, size_t len, + LEPUSFreeArrayBufferDataFunc *free_func, + void *opaque, BOOL is_shared); +LEPUSValue JS_NewArrayBufferCopy_GC(LEPUSContext *ctx, const uint8_t *buf, + size_t len); +void JS_DetachArrayBuffer_GC(LEPUSContext *ctx, LEPUSValueConst obj); +uint8_t *JS_GetArrayBuffer_GC(LEPUSContext *ctx, size_t *psize, + LEPUSValueConst obj); +uint8_t *JS_MoveArrayBuffer_GC(LEPUSContext *ctx, size_t *psize, + LEPUSValueConst obj); +LEPUS_BOOL JS_StrictEq_GC(LEPUSContext *ctx, LEPUSValueConst op1, + LEPUSValueConst op2); +LEPUS_BOOL JS_SameValue_GC(LEPUSContext *ctx, LEPUSValueConst op1, + LEPUSValueConst op2); +LEPUSValue JS_NewPromiseCapability_GC(LEPUSContext *ctx, + LEPUSValue *resolving_funcs); +int JS_EnqueueJob_GC(LEPUSContext *ctx, LEPUSJobFunc *job_func, int argc, + LEPUSValueConst *argv); +int JS_ExecutePendingJob_GC(LEPUSRuntime *rt, LEPUSContext **pctx); + +uint8_t *JS_WriteObject_GC(LEPUSContext *ctx, size_t *psize, + LEPUSValueConst obj, int flags); +LEPUSValue JS_EvalFunction_GC(LEPUSContext *ctx, LEPUSValue fun_obj, + LEPUSValueConst this_obj); +LEPUSValue JS_NewWString_GC(LEPUSContext *ctx, const uint16_t *buf, + size_t length); +QJS_HIDE LEPUSValue js_json_stringify_opt_GC(LEPUSContext *, LEPUSValue, + int32_t, LEPUSValue *); +JSAtom JS_ValueToAtom_GC(LEPUSContext *ctx, LEPUSValueConst val); +const uint16_t *JS_GetStringChars_GC(LEPUSContext *ctx, LEPUSValueConst str); +QJS_HIDE LEPUSValue JS_NewArrayWithValue_GC(LEPUSContext *ctx, uint32_t length, + LEPUSValueConst *values); +QJS_HIDE LEPUSValue JS_NewTypedArray_GC(LEPUSContext *ctx, uint32_t length, + LEPUSClassID class_id); +QJS_HIDE LEPUSValue JS_NewTypedArrayWithBuffer_GC(LEPUSContext *ctx, + LEPUSValueConst buffer, + uint32_t byteOffset, + uint32_t length, + LEPUSClassID class_id); + +QJS_HIDE LEPUSValue JS_CallConstructorV_GC(LEPUSContext *ctx, + LEPUSValueConst func_obj, int argc, + LEPUSValue *argv); +QJS_HIDE LEPUSValue JS_NewCFunction2_GC(LEPUSContext *ctx, LEPUSCFunction *func, + const char *name, int length, + LEPUSCFunctionEnum cproto, int magic); +QJS_HIDE LEPUSValue JS_NewCFunctionData_GC(LEPUSContext *ctx, + LEPUSCFunctionData *func, int length, + int magic, int data_len, + LEPUSValueConst *data); +QJS_HIDE void JS_SetPropertyFunctionList_GC(LEPUSContext *ctx, + LEPUSValueConst obj, + const LEPUSCFunctionListEntry *tab, + int len); +QJS_HIDE LEPUSValue js_object_getOwnPropertyDescriptor_GC( + LEPUSContext *ctx, LEPUSValueConst this_val, int argc, + LEPUSValueConst *argv, int magic); +QJS_HIDE int js_get_length32_gc(LEPUSContext *ctx, uint32_t *pres, + LEPUSValueConst obj); + +QJS_HIDE LEPUSValue JS_NewObjectWithArgs_GC(LEPUSContext *ctx, int32_t size, + const char **keys, + LEPUSValue *values); +QJS_HIDE LEPUSValue JS_NewArrayWithArgs_GC(LEPUSContext *ctx, int32_t size, + LEPUSValue *values); + +QJS_HIDE __exception int js_append_enumerate_gc(LEPUSContext *ctx, + LEPUSValue *sp); +QJS_HIDE __exception int js_iterator_get_value_done_gc(LEPUSContext *ctx, + LEPUSValue *sp); + +QJS_HIDE int JS_ToBoolFree_GC(LEPUSContext *ctx, LEPUSValue val); +QJS_HIDE LEPUSValue JS_ToPrimitiveFree_GC(LEPUSContext *ctx, LEPUSValue val, + int hint); +QJS_HIDE LEPUSValue JS_NewBigUint64_GC(LEPUSContext *ctx, uint64_t v); + +QJS_HIDE void JS_VisitLEPUSValue_GC(LEPUSRuntime *rt, LEPUSValue *val, + int local_idx); +QJS_HIDE void DisposeGlobal_GC(LEPUSRuntime *runtime, + LEPUSValue *global_handle); +QJS_HIDE LEPUSValue *GlobalizeReference_GC(LEPUSRuntime *runtime, + LEPUSValue val, bool is_weak); +QJS_HIDE void FreeNapiScope_GC(LEPUSContext *ctx); +QJS_HIDE void ClearGlobalWeak_GC(LEPUSRuntime *runtime, + LEPUSValue *global_handle); +QJS_HIDE void SetGlobalWeak_GC(LEPUSRuntime *runtime, LEPUSValue *global_handle, + void *data, void (*cb)(void *)); +QJS_HIDE void *GetNapiScope_GC(LEPUSContext *ctx); +QJS_HIDE void InitNapiScope_GC(LEPUSContext *ctx); +QJS_HIDE void SetNapiScope_GC(LEPUSContext *ctx, void *scope); +QJS_HIDE void SetWeakState_GC(LEPUSRuntime *runtime, LEPUSValue *global_handle); + +QJS_HIDE void JS_SetGCPauseSuppressionMode_GC(LEPUSRuntime *rt, bool mode); +QJS_HIDE bool JS_GetGCPauseSuppressionMode_GC(LEPUSRuntime *rt); +QJS_HIDE __attribute__((unused)) bool CheckValidPtr_GC(void *runtime, + void *ptr); +QJS_HIDE JSString *js_alloc_string(LEPUSContext *ctx, int max_len, + int is_wide_char); +QJS_HIDE int JS_InitAtoms(LEPUSRuntime *rt); +QJS_HIDE int JS_isConcatSpreadable(LEPUSContext *ctx, LEPUSValueConst obj); +typedef struct StringBuffer { + LEPUSContext *ctx; + JSString *str; + int len; + int size; + int is_wide_char; + int error_status; +} StringBuffer; +QJS_HIDE int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len); +QJS_HIDE int string_buffer_concat(StringBuffer *s, const JSString *p, + uint32_t from, uint32_t to); +QJS_HIDE int string_buffer_putc8(StringBuffer *s, uint32_t c); +QJS_HIDE int string_buffer_putc16(StringBuffer *s, uint32_t c); +QJS_HIDE int string_getc(const JSString *p, int *pidx); +QJS_HIDE BOOL test_final_sigma(JSString *p, int sigma_pos); +QJS_HIDE int string_buffer_putc(StringBuffer *s, uint32_t c); +QJS_HIDE int string_buffer_fill(StringBuffer *s, int c, int count); +QJS_HIDE int seal_template_obj_GC(LEPUSContext *ctx, LEPUSValueConst obj); +struct BCReaderState; +QJS_HIDE LEPUSValue JS_ReadError_GC(BCReaderState *s); +QJS_HIDE LEPUSValue JS_ReadRegExp(BCReaderState *s); +QJS_HIDE LEPUSValue JS_ReadMap_GC(BCReaderState *s); +QJS_HIDE LEPUSValue js_typed_array_constructor_GC(LEPUSContext *ctx, + LEPUSValueConst new_target, + int argc, + LEPUSValueConst *argv, + int classid); +QJS_HIDE LEPUSValue JS_ReadObjectRec(BCReaderState *s); +QJS_HIDE LEPUSValue JS_ReadRegExp_GC(BCReaderState *s); +QJS_HIDE JSAtom __JS_NewAtom(LEPUSRuntime *rt, JSString *str, int atom_type); +QJS_HIDE int js_string_compare(LEPUSContext *ctx, const JSString *p1, + const JSString *p2); + +QJS_HIDE JSAtom JS_NewAtomStr(LEPUSContext *ctx, JSString *p); +QJS_HIDE BOOL JS_AtomIsArrayIndex(LEPUSContext *ctx, uint32_t *pval, + JSAtom atom); + +QJS_HIDE JSAtom js_get_atom_index(LEPUSRuntime *rt, JSAtomStruct *p); +QJS_HIDE LEPUSModuleDef *js_new_module_def(LEPUSContext *ctx, JSAtom name); +struct JSFunctionDef; +QJS_HIDE int resolve_labels(LEPUSContext *ctx, JSFunctionDef *s); +QJS_HIDE int resolve_variables(LEPUSContext *ctx, JSFunctionDef *s); +QJS_HIDE int new_label_fd(JSFunctionDef *fd, int label); +struct JSParseState; +QJS_HIDE int js_parse_unary_GC(JSParseState *s, int parse_flags); +QJS_HIDE int js_parse_array_literal(JSParseState *s); +QJS_HIDE int js_parse_cond_expr(JSParseState *s, int parse_flags); +QJS_HIDE int js_parse_assign_expr(JSParseState *s, int parse_flags); +QJS_HIDE int js_parse_expr(JSParseState *s); +QJS_HIDE int js_parse_expr2(JSParseState *s, int parse_flags); +QJS_HIDE __exception int next_token(JSParseState *s); +struct JSToken; +QJS_HIDE int js_parse_string(JSParseState *s, int sep, BOOL do_throw, + const uint8_t *p, JSToken *token, + const uint8_t **pp); + +QJS_HIDE int cpool_add(JSParseState *s, LEPUSValue val); +QJS_HIDE int emit_push_const(JSParseState *s, LEPUSValueConst val, + BOOL as_atom); +QJS_HIDE void emit_op(JSParseState *s, uint8_t val); +QJS_HIDE int emit_label(JSParseState *s, int label); +QJS_HIDE void emit_return(JSParseState *s, BOOL hasval); +QJS_HIDE int emit_goto(JSParseState *s, int opcode, int label); +QJS_HIDE int emit_break(JSParseState *s, JSAtom name, int is_cont); +QJS_HIDE void optional_chain_test(JSParseState *s, + int *poptional_chaining_label, + int drop_count); +QJS_HIDE int js_parse_template_part(JSParseState *s, const uint8_t *p); +QJS_HIDE int js_parse_template(JSParseState *s, int call, int *argc); +struct JSParsePos; +QJS_HIDE int js_parse_get_pos(JSParseState *s, JSParsePos *sp); +QJS_HIDE int js_parse_seek_token(JSParseState *s, const JSParsePos *sp); +QJS_HIDE int js_parse_regexp(JSParseState *s); +QJS_HIDE int js_parse_skip_parens_token(JSParseState *s, int *pbits, + BOOL no_line_terminator, + BOOL *has_ellipsis = nullptr); +QJS_HIDE int js_resize_array(LEPUSContext *ctx, void **parray, int elem_size, + int *psize, int *pcount, int new_count); +QJS_HIDE JSExportEntry *find_export_entry(LEPUSContext *ctx, LEPUSModuleDef *m, + JSAtom export_name); + +QJS_HIDE JSExportEntry *add_export_entry(JSParseState *s, LEPUSModuleDef *m, + JSAtom local_name, JSAtom export_name, + JSExportTypeEnum export_type); +QJS_HIDE void set_object_name_computed(JSParseState *s); +QJS_HIDE BOOL set_object_name(JSParseState *s, JSAtom name); +QJS_HIDE int add_scope_var(LEPUSContext *ctx, JSFunctionDef *fd, JSAtom name, + JSVarKindEnum var_kind); +QJS_HIDE int add_var(LEPUSContext *ctx, JSFunctionDef *fd, JSAtom name); +typedef struct JSHoistedDef { // called JSGlobalVar in latest version + int cpool_idx; /* -1 means variable global definition */ + uint8_t force_init : 1; /* initialize to undefined */ + uint8_t is_lexical : 1; /* global let/const definition */ + uint8_t is_const : 1; /* const definition */ + int var_idx; /* function object index if cpool_idx >= 0 */ + int scope_level; /* scope of definition */ + JSAtom var_name; /* variable name if cpool_idx < 0 */ +} JSHoistedDef; +QJS_HIDE JSHoistedDef *add_hoisted_def(LEPUSContext *ctx, JSFunctionDef *s, + int cpool_idx, JSAtom name, int var_idx, + BOOL is_lexical); +QJS_HIDE int peek_token(JSParseState *s, BOOL no_line_terminator); +QJS_HIDE int push_scope(JSParseState *s); +QJS_HIDE int find_private_class_field(LEPUSContext *ctx, JSFunctionDef *fd, + JSAtom name, int scope_level); +typedef enum JSParseFunctionEnum { + JS_PARSE_FUNC_STATEMENT, + JS_PARSE_FUNC_VAR, + JS_PARSE_FUNC_EXPR, + JS_PARSE_FUNC_ARROW, + JS_PARSE_FUNC_GETTER, + JS_PARSE_FUNC_SETTER, + JS_PARSE_FUNC_METHOD, + JS_PARSE_FUNC_CLASS_CONSTRUCTOR, + JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR, +} JSParseFunctionEnum; +typedef enum JSParseExportEnum { + JS_PARSE_EXPORT_NONE, + JS_PARSE_EXPORT_NAMED, + JS_PARSE_EXPORT_DEFAULT, +} JSParseExportEnum; +typedef enum { + JS_VAR_DEF_WITH, + JS_VAR_DEF_LET, + JS_VAR_DEF_CONST, + JS_VAR_DEF_FUNCTION_DECL, /* function declaration */ + JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */ + JS_VAR_DEF_CATCH, + JS_VAR_DEF_VAR, +} JSVarDefEnum; +QJS_HIDE int js_parse_function_decl2_GC( + JSParseState *s, JSParseFunctionEnum func_type, + JSFunctionKindEnum func_kind, JSAtom func_name, const uint8_t *ptr, + int function_line_num, JSParseExportEnum export_flag, JSFunctionDef **pfd); +QJS_HIDE int js_parse_class_default_ctor(JSParseState *s, BOOL has_super, + JSFunctionDef **pfd); + +QJS_HIDE int define_var_GC(JSParseState *s, JSFunctionDef *fd, JSAtom name, + JSVarDefEnum var_def_type); + +QJS_HIDE uint64_t compute_column(JSParseState *s, BOOL is_get_var); +QJS_HIDE BOOL js_is_live_code(JSParseState *s); +QJS_HIDE int js_parse_directives(JSParseState *s); +QJS_HIDE int add_closure_var(LEPUSContext *ctx, JSFunctionDef *s, BOOL is_local, + BOOL is_arg, int var_idx, JSAtom var_name, + BOOL is_const, BOOL is_lexical, + JSVarKindEnum var_kind); +QJS_HIDE BOOL is_var_in_arg_scope(LEPUSContext *ctx, const JSVarDef *vd); +QJS_HIDE LEPUSValue js_create_function(LEPUSContext *ctx, JSFunctionDef *fd); +QJS_HIDE void skip_shebang(JSParseState *s); +QJS_HIDE const char *JS_AtomGetStrRT(LEPUSRuntime *rt, char *buf, int buf_size, + JSAtom atom); +QJS_HIDE const char *JS_AtomGetStr(LEPUSContext *ctx, char *buf, int buf_size, + JSAtom atom); +QJS_HIDE int js_parse_error_reserved_identifier(JSParseState *s); +QJS_HIDE BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom); +QJS_HIDE BOOL token_is_ident(int tok); +QJS_HIDE int js_parse_expect(JSParseState *s, int tok); +QJS_HIDE int js_parse_object_literal_GC(JSParseState *s); +QJS_HIDE int js_parse_expr_paren(JSParseState *s); +QJS_HIDE __exception JSAtom js_parse_from_clause(JSParseState *s); +QJS_HIDE int add_req_module_entry(LEPUSContext *ctx, LEPUSModuleDef *m, + JSAtom module_name); +QJS_HIDE int js_parse_expect_semi(JSParseState *s); +QJS_HIDE __exception int js_parse_export(JSParseState *s); +QJS_HIDE __exception int js_parse_import(JSParseState *s); +QJS_HIDE LEPUSValue JS_CallFree_GC(LEPUSContext *ctx, LEPUSValue func_obj, + LEPUSValueConst this_obj, int argc, + LEPUSValueConst *argv); +QJS_HIDE int add_star_export_entry(LEPUSContext *ctx, LEPUSModuleDef *m, + int req_module_idx); +QJS_HIDE int js_define_var(JSParseState *s, JSAtom name, int tok); +QJS_HIDE int get_prev_opcode(JSFunctionDef *fd); +QJS_HIDE int update_label(JSFunctionDef *s, int label, int delta); +QJS_HIDE int get_lvalue(JSParseState *s, int *popcode, int *pscope, + JSAtom *pname, int *plabel, int *pdepth, BOOL keep, + int tok); +typedef enum { + PUT_LVALUE_NOKEEP, /* [depth] v -> */ + PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently + just disable optimizations) */ + PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */ + PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */ + PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */ +} PutLValueEnum; +QJS_HIDE void put_lvalue(JSParseState *s, int opcode, int scope, JSAtom name, + int label, PutLValueEnum special, BOOL is_let); +QJS_HIDE JSAtom js_parse_destructing_var(JSParseState *s, int tok, int is_arg); +QJS_HIDE int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name); +QJS_HIDE int js_parse_destructing_element_GC(JSParseState *s, int tok, + int is_arg, int hasval, + int has_ellipsis, + BOOL allow_initializer); +QJS_HIDE int js_parse_var(JSParseState *s, int parse_flags, int tok, + BOOL export_flag); +QJS_HIDE LEPUSValue js_evaluate_module(LEPUSContext *ctx, LEPUSModuleDef *m); +QJS_HIDE JSVarRef *js_create_module_var(LEPUSContext *ctx, BOOL is_lexical); +QJS_HIDE int js_create_module_function(LEPUSContext *ctx, LEPUSModuleDef *m); +QJS_HIDE void set_value_gc(LEPUSContext *ctx, LEPUSValue *pval, + LEPUSValue new_val); +QJS_HIDE int JS_DefineAutoInitProperty_GC( + LEPUSContext *ctx, LEPUSValueConst this_obj, JSAtom prop, + LEPUSValue (*init_func)(LEPUSContext *ctx, LEPUSObject *obj, JSAtom prop, + void *opaque), + void *opaque, int flags); +QJS_HIDE int js_link_module(LEPUSContext *ctx, LEPUSModuleDef *m); +QJS_HIDE int skip_spaces(const char *pc); +int JS_DefineObjectName_GC(LEPUSContext *ctx, LEPUSValueConst obj, JSAtom name, + int flags); +QJS_HIDE LEPUSValue js_atod(LEPUSContext *ctx, const char *str, const char **pp, + int radix, int flags); +QJS_HIDE int js_resolve_module(LEPUSContext *ctx, LEPUSModuleDef *m); +QJS_HIDE void close_scopes(JSParseState *s, int scope, int scope_stop); +QJS_HIDE JSFunctionDef *js_new_function_def_GC(LEPUSContext *ctx, + JSFunctionDef *parent, + BOOL is_eval, BOOL is_func_expr, + const char *filename, + int line_num); +QJS_HIDE void pop_scope(JSParseState *s); +QJS_HIDE JSHoistedDef *find_hoisted_def(JSFunctionDef *fd, JSAtom name); +QJS_HIDE BOOL is_child_scope(LEPUSContext *ctx, JSFunctionDef *fd, int scope, + int parent_scope); +QJS_HIDE int find_lexical_decl(LEPUSContext *ctx, JSFunctionDef *fd, + JSAtom name, int scope_idx, + BOOL check_catch_var); +QJS_HIDE int find_arg(LEPUSContext *ctx, JSFunctionDef *fd, JSAtom name); +QJS_HIDE int find_var(LEPUSContext *ctx, JSFunctionDef *fd, JSAtom name); +QJS_HIDE int js_parse_class(JSParseState *s, BOOL is_class_expr, + JSParseExportEnum export_flag); +QJS_HIDE int js_parse_left_hand_side_expr_GC(JSParseState *s); +QJS_HIDE int js_parse_property_name_GC(JSParseState *s, JSAtom *pname, + BOOL allow_method, BOOL allow_var, + BOOL allow_private); +QJS_HIDE int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, + JSAtom func_name); +QJS_HIDE int is_let(JSParseState *s, int decl_mask); +QJS_HIDE int __attribute__((format(printf, 2, 3))) QJS_HIDE js_parse_error( + JSParseState *s, const char *fmt, ...); +QJS_HIDE int find_var_in_child_scope(LEPUSContext *ctx, JSFunctionDef *fd, + JSAtom name, int scope_level); +QJS_HIDE int add_arg(LEPUSContext *ctx, JSFunctionDef *fd, JSAtom name); +#ifndef NO_QUICKJS_COMPILER +typedef struct JSParseState { + LEPUSContext *ctx; + int last_line_num; /* line number of last token */ + int line_num; /* line number of current offset */ + const char *filename; + JSToken token; + BOOL got_lf; /* true if got line feed before the current token */ + const uint8_t *last_ptr; + const uint8_t *buf_ptr; + const uint8_t *buf_end; + // + int debugger_last_line_num; + const uint8_t *line_begin_ptr; + const uint8_t *last_line_begin_ptr; + const uint8_t *last_emit_ptr; + const uint8_t *func_call_ptr; + const uint8_t *utf8_parse_front; + int utf8_adapte_size; + int func_call_adapte_size; + int last_utf8_adapte_size; + const uint8_t *last_last_ptr; + // + /* current function code */ + JSFunctionDef *cur_func; + BOOL is_module; /* parsing a module */ + BOOL allow_html_comments; +} JSParseState; +#endif +QJS_HIDE LEPUSValue js_finalizationRegistry_unregister(LEPUSContext *ctx, + LEPUSValueConst this_val, + int argc, + LEPUSValueConst *argv); +QJS_HIDE LEPUSValue js_finalizationRegistry_unregister_gc( + LEPUSContext *ctx, LEPUSValueConst this_val, int argc, + LEPUSValueConst *argv); + +QJS_HIDE LEPUSValue js_finalizationRegistry_register(LEPUSContext *ctx, + LEPUSValueConst this_val, + int argc, + LEPUSValueConst *argv); +QJS_HIDE LEPUSValue +js_finalizationRegistry_register_gc(LEPUSContext *ctx, LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv); + +QJS_HIDE void AddReferenceRecord(LEPUSContext *ctx, LEPUSObject *obj, + LEPUSValue val); +QJS_HIDE const char *generate_json_str(LEPUSContext *ctx, LEPUSValue obj, + size_t &str_len, const char ***str_arr, + size_t &ts, size_t &cs); +typedef union json_val_uni { + int64_t i64; + double f64; + const char *str; + size_t ofs; + LEPUSValue bigf; // for JSON.stringify bigfloat + LEPUSValue num; // for JSON.parse number +} json_val_uni; + +struct json_val { + uint64_t tag; /**< type, subtype and length */ + json_val_uni uni; /**< payload */ +}; +QJS_HIDE int make_json_val_incr(LEPUSContext *ctx, size_t &alc_len, + json_val **val_hdr, json_val **val, + json_val **ctn, json_val **val_end); +QJS_HIDE uint8_t *json_val_write_format(LEPUSContext *ctx, json_val *root, + const char *gap_str); +QJS_HIDE uint8_t *json_val_write(LEPUSContext *ctx, json_val *root); +QJS_HIDE void parse_json_space(JSParseState *s, const uint8_t **cur); +QJS_HIDE bool read_string(JSParseState *s, uint8_t **ptr, json_val *val); +QJS_HIDE json_val *json_parse_value(JSParseState *s, size_t dat_len); +QJS_HIDE void js_parse_init(LEPUSContext *ctx, JSParseState *s, + const char *input, size_t input_len, + const char *filename); +QJS_HIDE LEPUSValue JS_ParseJSONOPT(LEPUSContext *ctx, const char *buf, + size_t buf_len, const char *filename); +QJS_HIDE LEPUSObject *get_typed_array(LEPUSContext *ctx, + LEPUSValueConst this_val, + int is_dataview); +QJS_HIDE BOOL typed_array_is_detached(LEPUSContext *ctx, LEPUSObject *p); +QJS_HIDE LEPUSValue js_dataview_getValue(LEPUSContext *ctx, + LEPUSValueConst this_obj, int argc, + LEPUSValueConst *argv, int class_id); +QJS_HIDE LEPUSValue js_dataview_setValue(LEPUSContext *ctx, + LEPUSValueConst this_obj, int argc, + LEPUSValueConst *argv, int class_id); +QJS_HIDE JSArrayBuffer *js_get_array_buffer(LEPUSContext *ctx, + LEPUSValueConst obj); +QJS_HIDE LEPUSValue js_create_from_ctor_GC(LEPUSContext *ctx, + LEPUSValueConst ctor, int class_id); +QJS_HIDE LEPUSValue js_dataview_constructor(LEPUSContext *ctx, + LEPUSValueConst new_target, + int argc, LEPUSValueConst *argv); +QJS_HIDE LEPUSValue js_typed_array_get_buffer(LEPUSContext *ctx, + LEPUSValueConst this_val, + int is_dataview); +QJS_HIDE LEPUSValue js_typed_array_get_length(LEPUSContext *ctx, + LEPUSValueConst this_val); +QJS_HIDE LEPUSValue js_typed_array_get_byteLength(LEPUSContext *ctx, + LEPUSValueConst this_val, + int is_dataview); +QJS_HIDE LEPUSValue js_typed_array_get_byteOffset(LEPUSContext *ctx, + LEPUSValueConst this_val, + int is_dataview); +const LEPUSCFunctionListEntry js_dataview_proto_funcs[] = { + LEPUS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1), + LEPUS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, + 1), + LEPUS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, + 1), + LEPUS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, + JS_CLASS_INT8_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, + JS_CLASS_UINT8_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, + JS_CLASS_INT16_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, + JS_CLASS_UINT16_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, + JS_CLASS_INT32_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, + JS_CLASS_UINT32_ARRAY), +#ifdef CONFIG_BIGNUM + LEPUS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, + JS_CLASS_BIG_INT64_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, + JS_CLASS_BIG_UINT64_ARRAY), +#endif + LEPUS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, + JS_CLASS_FLOAT32_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, + JS_CLASS_FLOAT64_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, + JS_CLASS_INT8_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, + JS_CLASS_UINT8_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, + JS_CLASS_INT16_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, + JS_CLASS_UINT16_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, + JS_CLASS_INT32_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, + JS_CLASS_UINT32_ARRAY), +#ifdef CONFIG_BIGNUM + LEPUS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, + JS_CLASS_BIG_INT64_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, + JS_CLASS_BIG_UINT64_ARRAY), +#endif + LEPUS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, + JS_CLASS_FLOAT32_ARRAY), + LEPUS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, + JS_CLASS_FLOAT64_ARRAY), + LEPUS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", + LEPUS_PROP_CONFIGURABLE), +}; +QJS_HIDE void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags); +QJS_HIDE LEPUSValue js_closure2(LEPUSContext *ctx, LEPUSValue func_obj, + LEPUSFunctionBytecode *b, + JSVarRef **cur_var_refs, LEPUSStackFrame *sf); +QJS_HIDE void js_random_init(LEPUSContext *ctx); +QJS_HIDE LEPUSValue js_math_random(LEPUSContext *ctx, LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv); +QJS_HIDE int getTimezoneOffset(int64_t time); +QJS_HIDE int JS_SetPrototypeInternal_GC(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst proto_val, + BOOL throw_flag); +QJS_HIDE LEPUSValue js_reflect_setPrototypeOf(LEPUSContext *ctx, + LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv); +QJS_HIDE LEPUSValue js_reflect_has(LEPUSContext *ctx, LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv); +QJS_HIDE LEPUSValue js_reflect_set(LEPUSContext *ctx, LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv); +QJS_HIDE LEPUSValue js_reflect_get(LEPUSContext *ctx, LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv); +QJS_HIDE LEPUSValue js_reflect_deleteProperty(LEPUSContext *ctx, + LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv); +QJS_HIDE int JS_SetPropertyGeneric_GC(LEPUSContext *ctx, LEPUSObject *p, + JSAtom prop, LEPUSValue val, + LEPUSValueConst this_obj, int flags); +QJS_HIDE void js_shape_hash_unlink(LEPUSRuntime *rt, JSShape *sh); +QJS_HIDE void js_shape_hash_link(LEPUSRuntime *rt, JSShape *sh); +QJS_HIDE int resize_shape_hash(LEPUSRuntime *rt, int new_shape_hash_bits); +QJS_HIDE int resize_properties(LEPUSContext *ctx, JSShape **psh, LEPUSObject *p, + uint32_t count); +QJS_HIDE BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); +QJS_HIDE void build_backtrace_frame(LEPUSContext *ctx, LEPUSStackFrame *sf, + DynBuf *dbuf, const uint8_t *cur_pc, + BOOL is_async, BOOL is_debug_mode, + LEPUSValueConst error_obj); +QJS_HIDE void get_backtrace(LEPUSContext *ctx, DynBuf *dbuf, BOOL is_debug_mode, + LEPUSValueConst error_obj, const uint8_t *cur_pc, + int backtrace_flags); +QJS_HIDE LEPUSValue JS_GetIterator(LEPUSContext *ctx, LEPUSValueConst obj, + BOOL is_async); +QJS_HIDE LEPUSValue JS_GetIterator2(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst method); +QJS_HIDE LEPUSValue JS_IteratorNext2(LEPUSContext *ctx, + LEPUSValueConst enum_obj, + LEPUSValueConst method, int argc, + LEPUSValueConst *argv, int *pdone); +QJS_HIDE LEPUSValue JS_IteratorNext(LEPUSContext *ctx, LEPUSValueConst enum_obj, + LEPUSValueConst method, int argc, + LEPUSValueConst *argv, BOOL *pdone); +QJS_HIDE JSShapeProperty *find_own_property1(LEPUSObject *p, JSAtom atom); +QJS_HIDE LEPUSValue JS_ThrowError(LEPUSContext *ctx, JSErrorEnum error_num, + const char *fmt, va_list ap); +QJS_HIDE int __attribute__((format(printf, 3, 4))) +JS_ThrowTypeErrorOrFalse(LEPUSContext *ctx, int flags, const char *fmt, ...); +QJS_HIDE int __attribute__((format(printf, 2, 3))) QJS_HIDE js_throw_URIError( + LEPUSContext *ctx, const char *fmt, ...); +QJS_HIDE void *LEPUS_GetOpaque2(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSClassID class_id); +QJS_HIDE JSRegExp *js_get_regexp(LEPUSContext *ctx, LEPUSValueConst obj, + BOOL throw_error); +QJS_HIDE LEPUSValue __attribute__((format(printf, 3, 4))) +__JS_ThrowTypeErrorAtom(LEPUSContext *ctx, JSAtom atom, const char *fmt, ...); +QJS_HIDE LEPUSValue JS_ThrowTypeErrorPrivateNotFound(LEPUSContext *ctx, + JSAtom atom); +typedef struct BlockEnv { + struct BlockEnv *prev; + JSAtom label_name; /* JS_ATOM_NULL if none */ + int label_break; /* -1 if none */ + int label_cont; /* -1 if none */ + int drop_count; /* number of stack elements to drop */ + int label_finally; /* -1 if none */ + int scope_level; + int has_iterator; +} BlockEnv; + +typedef struct RelocEntry { + struct RelocEntry *next; + uint32_t addr; /* address to patch */ + int size; /* address size: 1, 2 or 4 bytes */ +} RelocEntry; + +typedef struct JumpSlot { + int op; + int size; + int pos; + int label; +} JumpSlot; + +typedef struct LabelSlot { + int ref_count; + int pos; /* phase 1 address, -1 means not resolved yet */ + int pos2; /* phase 2 address, -1 means not resolved yet */ + int addr; /* phase 3 address, -1 means not resolved yet */ + RelocEntry *first_reloc; +} LabelSlot; + +typedef struct LineNumberSlot { + uint32_t pc; + // + uint64_t line_num; + // +} LineNumberSlot; + +typedef struct JSFunctionDef { + LEPUSContext *ctx; + struct JSFunctionDef *parent; + int parent_cpool_idx; /* index in the constant pool of the parent + or -1 if none */ + int parent_scope_level; /* scope level in parent at point of definition */ + struct list_head child_list; /* list of JSFunctionDef.link */ + struct list_head link; + + BOOL is_eval; /* TRUE if eval code */ + int eval_type; /* only valid if is_eval = TRUE */ + BOOL is_global_var; /* TRUE if variables are not defined locally: + eval global, eval module or non strict eval */ + BOOL is_func_expr; /* TRUE if function expression */ + BOOL has_home_object; /* TRUE if the home object is available */ + BOOL has_prototype; /* true if a prototype field is necessary */ + BOOL has_simple_parameter_list; + BOOL has_parameter_expressions; /* if true, an argument scope is created */ + BOOL has_use_strict; /* to reject directive in special cases */ + BOOL has_eval_call; /* true if the function contains a call to eval() */ + BOOL has_arguments_binding; /* true if the 'arguments' binding is + available in the function */ + BOOL has_this_binding; /* true if the 'this' and new.target binding are + available in the function */ + BOOL new_target_allowed; /* true if the 'new.target' does not + throw a syntax error */ + BOOL super_call_allowed; /* true if super() is allowed */ + BOOL super_allowed; /* true if super. or super[] is allowed */ + BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */ + BOOL is_derived_class_constructor; + BOOL in_function_body; + JSFunctionKindEnum func_kind : 8; + JSParseFunctionEnum func_type : 8; + uint8_t js_mode; /* bitmap of JS_MODE_x */ + JSAtom func_name; /* JS_ATOM_NULL if no name */ + + JSVarDef *vars; + int var_size; /* allocated size for vars[] */ + int var_count; + JSVarDef *args; + int arg_size; /* allocated size for args[] */ + int arg_count; /* number of arguments */ + int defined_arg_count; + int var_object_idx; /* -1 if none */ + int arg_var_object_idx; /* -1 if none (var object for the argument scope) */ + int arguments_var_idx; /* -1 if none */ + int arguments_arg_idx; /* argument variable definition in argument scope, -1 + if none */ + int func_var_idx; /* variable containing the current function (-1 + if none, only used if is_func_expr is true) */ + int eval_ret_idx; /* variable containing the return value of the eval, -1 if + none */ + int this_var_idx; /* variable containg the 'this' value, -1 if none */ + int new_target_var_idx; /* variable containg the 'new.target' value, -1 if + none */ + int this_active_func_var_idx; /* variable containg the 'this.active_func' + value, -1 if none */ + int home_object_var_idx; + BOOL need_home_object; + + int scope_level; /* index into fd->scopes if the current lexical scope */ + int scope_first; /* index into vd->vars of first lexically scoped variable */ + int scope_size; /* allocated size of fd->scopes array */ + int scope_count; /* number of entries used in the fd->scopes array */ + JSVarScope *scopes; + JSVarScope def_scope_array[4]; + int body_scope; /* scope of the body of the function or eval */ + + int hoisted_def_count; + int hoisted_def_size; + JSHoistedDef *hoisted_def; + + DynBuf byte_code; + int last_opcode_pos; /* -1 if no last opcode */ + int last_opcode_line_num; + BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */ + + LabelSlot *label_slots; + int label_size; /* allocated size for label_slots[] */ + int label_count; + BlockEnv *top_break; /* break/continue label stack */ + + /* constant pool (strings, functions, numbers) */ + LEPUSValue *cpool; + uint32_t cpool_count; + uint32_t cpool_size; + + /* list of variables in the closure */ + int closure_var_count; + int closure_var_size; + LEPUSClosureVar *closure_var; + + JumpSlot *jump_slots; + int jump_size; + int jump_count; + + LineNumberSlot *line_number_slots; + int line_number_size; + int line_number_count; + // + int64_t line_number_last; + int64_t line_number_last_pc; + // + + /* pc2line table */ + JSAtom filename; + int line_num; +#ifdef ENABLE_QUICKJS_DEBUGGER + int64_t column_num; + LEPUSScriptSource *script; +#endif + DynBuf pc2line; + + CallerStrSlot *caller_slots; + int32_t caller_size; + int32_t caller_count; + int32_t resolve_caller_count; + bool should_add_slot; + + const char *src_start; + char *source; /* raw source, utf-8 encoded */ + int source_len; + + LEPUSModuleDef *module; /* != NULL when parsing a module */ +} JSFunctionDef; + +QJS_HIDE int add_closure_variables(LEPUSContext *ctx, JSFunctionDef *s, + LEPUSFunctionBytecode *b, int scope_idx); +QJS_HIDE int JS_DefinePropertyValueInt64_GC(LEPUSContext *ctx, + LEPUSValueConst this_obj, + int64_t idx, LEPUSValue val, + int flags); +QJS_HIDE LEPUSValue js_error_constructor(LEPUSContext *ctx, + LEPUSValueConst new_target, int argc, + LEPUSValueConst *argv, int magic); + +QJS_HIDE BOOL JS_AtomIsString(LEPUSContext *ctx, JSAtom v); + +QJS_HIDE LEPUSValue JS_NewObjectFromShape(LEPUSContext *, JSShape *, + LEPUSClassID); + +QJS_HIDE LEPUSValue JS_NewObjectFromShape_GC(LEPUSContext *, JSShape *, + LEPUSClassID); + +QJS_HIDE LEPUSFunctionBytecode *JS_GetFunctionBytecode(LEPUSValueConst); +QJS_HIDE JSAtom js_symbol_to_atom(LEPUSContext *, LEPUSValue); +QJS_HIDE LEPUSValueConst JS_GetActiveFunction(LEPUSContext *ctx); +QJS_HIDE LEPUSValue js_array_buffer_get_byteLength(LEPUSContext *, + LEPUSValueConst, int32_t); +#ifndef NO_QUICKJS_COMPILER +// the last two parameters are needed for qjs debugger, default value: false, +// NULL +QJS_HIDE LEPUSValue JS_EvalInternal(LEPUSContext *, LEPUSValue, const char *, + size_t, const char *, int, int, + bool = false, LEPUSStackFrame * = nullptr); +#endif + +LEPUSValue js_array_reduce_gc(LEPUSContext *ctx, LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv, int special); + +LEPUSValue js_array_concat_gc(LEPUSContext *ctx, LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv); +/* Shape support */ +QJS_STATIC inline JSShapeProperty *get_shape_prop(JSShape *sh) { + return sh->prop; +} + +QJS_STATIC inline size_t get_shape_size(size_t hash_size, size_t prop_size) { + return hash_size * sizeof(uint32_t) + sizeof(JSShape) + + prop_size * sizeof(JSShapeProperty); +} + +QJS_STATIC inline JSShape *get_shape_from_alloc(void *sh_alloc, + size_t hash_size) { + return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); +} + +QJS_STATIC inline void *get_alloc_from_shape(JSShape *sh) { + return sh->prop_hash_end - ((intptr_t)sh->prop_hash_mask + 1); +} + +QJS_STATIC inline BOOL atom_is_free(const JSAtomStruct *p) { + return (uintptr_t)p & 1; +} + +QJS_STATIC inline BOOL __JS_AtomIsTaggedInt(JSAtom v) { + return (v & JS_ATOM_TAG_INT) != 0; +} + +QJS_STATIC inline JSAtom __JS_AtomFromUInt32(uint32_t v) { + return v | JS_ATOM_TAG_INT; +} + +QJS_STATIC inline uint32_t __JS_AtomToUInt32(JSAtom atom) { + return atom & ~JS_ATOM_TAG_INT; +} + +QJS_HIDE JSAtomKindEnum JS_AtomGetKind(LEPUSContext *, JSAtom); +QJS_HIDE LEPUS_BOOL JS_LepusRefIsArray(LEPUSRuntime *rt, LEPUSValue v); +QJS_HIDE LEPUS_BOOL JS_LepusRefIsTable(LEPUSRuntime *rt, LEPUSValue v); +QJS_HIDE JSShape *js_dup_shape(JSShape *sh); + +typedef struct JSCFunctionDataRecord { + LEPUSCFunctionData *func; + uint8_t length; + uint8_t data_len; + uint16_t magic; + LEPUSValue data[0]; +} JSCFunctionDataRecord; + +typedef struct ValueBuffer { + LEPUSContext *ctx; + LEPUSValue *arr; + LEPUSValue def[4]; + int len; + int size; + int error_status; +} ValueBuffer; + +typedef enum JSPromiseStateEnum { + JS_PROMISE_PENDING, + JS_PROMISE_FULFILLED, + JS_PROMISE_REJECTED, +} JSPromiseStateEnum; + +typedef struct JSPromiseData { + JSPromiseStateEnum promise_state; + /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */ + struct list_head promise_reactions[2]; + BOOL is_handled; /* Note: only useful to debug */ + LEPUSValue promise_result; +} JSPromiseData; + +typedef struct JSPromiseFunctionDataResolved { + int ref_count; + BOOL already_resolved; +} JSPromiseFunctionDataResolved; + +typedef struct JSPromiseFunctionData { + LEPUSValue promise; + JSPromiseFunctionDataResolved *presolved; +} JSPromiseFunctionData; + +typedef struct JSPromiseReactionData { + struct list_head link; /* not used in promise_reaction_job */ + LEPUSValue resolving_funcs[2]; + LEPUSValue handler; +} JSPromiseReactionData; + +typedef struct ReferenceRecord { + int max_size; + LEPUSValue *references; + int length; +} ReferenceRecord; + +typedef struct RegistryRecord { + struct ReferenceRecord *registra; + struct ReferenceRecord *heldvalue; + struct ReferenceRecord *target; + struct ReferenceRecord *token; + struct JSFinalizationRegistryEntry *entry; + int *idx; +} RegistryRecord; + +typedef struct JSMapRecord { + int ref_count; /* used during enumeration to avoid freeing the record */ + BOOL empty; /* TRUE if the record is deleted */ + struct JSMapState *map; + struct list_head link; + struct list_head hash_link; + LEPUSValue key; + LEPUSValue value; +} JSMapRecord; + +typedef struct ValueSlot { + LEPUSValue val; + JSString *str; + int64_t pos; +} ValueSlot; + +struct array_sort_context { + LEPUSContext *ctx; + int exception; + int has_method; + LEPUSValueConst method; +}; + +typedef struct FinalizerOpaque { + LEPUSContext *ctx; +} FinalizerOpaque; + +/* */ +#define BC_NEW_PREFIX 0x8 +#define VERSION_PLACEHOLDER 0xCAB00000 +#define NEW_DEBUGINFO_FLAG 0x100000000 + +bool JS_IsNewVersion(LEPUSContext *ctx); +bool JS_CheckBytecodeVersion(uint64_t v64); + +QJS_HIDE bool primjs_snapshot_enabled(); + +QJS_HIDE void DeleteCurNode(LEPUSRuntime *rt, void *node, int type); +QJS_HIDE bool CheckValidNode(LEPUSRuntime *rt, void *node, int type); + +// #sec-tointgerorinfinity +inline double DoubleToInteger(double x) { + if (isnan(x) || x == 0.0) return 0; + if (!isfinite(x)) return x; + return ((x > 0) ? floor(x) : ceil(x)) + 0.0; +} + +inline void insert_weakref_record(LEPUSObject *p, + struct WeakRefRecord *record) { + record->next_weak_ref = p->first_weak_ref; + p->first_weak_ref = record; + return; +} + +char *js_strmalloc(const char *s, size_t n); +void AddLepusRefCount(LEPUSContext *ctx); + +class LynxTraceInstance { + public: + using BeginPtr = void *(*)(const char *); + using EndPtr = void (*)(void *); + static auto &GetInstance() { + static LynxTraceInstance instance; + return instance; + } + void InitBeginPtr(BeginPtr begin) { trace_start_ = begin; } + void InitEndPtr(EndPtr end) { trace_end_ = end; } + + auto GetBeginPtr() const { return trace_start_; } + auto GetEndPtr() const { return trace_end_; } + + private: + BeginPtr trace_start_{nullptr}; + EndPtr trace_end_{nullptr}; +}; + +class TraceManager { + public: + explicit TraceManager(const char *name) { + if (auto call_begin = LynxTraceInstance::GetInstance().GetBeginPtr()) { + ptr = call_begin(name); + } + } + + ~TraceManager() { + if (auto call_end = LynxTraceInstance::GetInstance().GetEndPtr()) { + call_end(ptr); + } + } + + private: + void *ptr{nullptr}; +}; + +#define JS_OBJECT_IS_OUTER(obj) (obj->class_id >= JS_CLASS_INIT_COUNT) + +#ifdef ENABLE_QUICKJS_DEBUGGER +#define TRACE_EVENT(name) auto tracer = TraceManager{name}; + +#ifdef ENABLE_COMPATIBLE_MM +#define DEBUGGER_COMPATIBLE_CALL_RET(ctx, name, args...) \ + (ctx->rt->gc_enable) ? (name##_GC(args)) : (name(args)) +#else +#define DEBUGGER_COMPATIBLE_CALL_RET(ctx, name, args...) (name(args)) +#endif /* ENABLE_COMPATIBLE_MM */ + +#else +#define TRACE_EVENT(name) +#define DEBUGGER_COMPATIBLE_CALL_RET +#endif /* ENABLE_QUICKJS_DEBUGGER */ + +int64_t date_now(); + +inline bool json_opt_disabled() { return JSON_OPT_DISABLE & settingsFlag; } +inline bool json_opt_disabled(LEPUSRuntime *rt) { + return rt->settings_option.disable_json_opt; +} +inline bool deepclone_opt_disabled() { + return DEEPCLONE_OPT_DISABLE & settingsFlag; +} + +inline bool deepclone_opt_disabled(LEPUSRuntime *rt) { + return rt->settings_option.disable_deepclone_opt; +} + +inline bool separable_string_disabled() { + return DISABLE_SEPARABLE_STRING & settingsFlag; +} + +inline bool minify_virtual_stack_size_enabled() { + return settingsFlag & MINIFY_STACK_ENABLE; +} + +inline bool separable_string_disabled(LEPUSRuntime *rt) { + return rt->settings_option.disable_separable_string; +} + +inline bool adjust_stacksize_disabled() { + return DISABLE_ADJUST_STACKSIZE & settingsFlag; +} + +inline void js_init_settings_options(LEPUSRuntime *rt) { + rt->settings_option.disable_adjust_stacksize = adjust_stacksize_disabled(); + rt->settings_option.disable_json_opt = json_opt_disabled(); + rt->settings_option.disable_deepclone_opt = deepclone_opt_disabled(); + rt->settings_option.disable_separable_string = separable_string_disabled(); + return; +} + +inline bool js_is_bytecode_function(LEPUSValue obj) { + return (LEPUS_VALUE_IS_OBJECT(obj)) && + (LEPUS_VALUE_GET_OBJ(obj)->class_id == JS_CLASS_BYTECODE_FUNCTION); +} + +bool emit_name_str(JSParseState *s, const uint8_t *start, const uint8_t *end); +void get_caller_string(JSFunctionDef *s); + +#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_INNER_H_ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-libc.h b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-libc.h new file mode 100644 index 000000000..f9844d866 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-libc.h @@ -0,0 +1,66 @@ +/* + * QuickJS C library + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_LIBC_H_ +#define SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_LIBC_H_ + +#include +#include + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include // portable: uint64_t MSVC: __int64 +#include +#endif + +#include "quickjs.h" + +QJS_HIDE LEPUSModuleDef *lepus_init_module_std(LEPUSContext *ctx, + const char *module_name); +QJS_HIDE LEPUSModuleDef *lepus_init_module_os(LEPUSContext *ctx, + const char *module_name); +QJS_HIDE void lepus_std_add_helpers(LEPUSContext *ctx, int argc, char **argv); +void lepus_std_loop(LEPUSContext *ctx); +QJS_HIDE void lepus_std_free_handlers(LEPUSRuntime *rt); +QJS_HIDE void lepus_std_dump_error(LEPUSContext *ctx); + +#if LYNX_SIMPLIFY +uint8_t *lepus_load_file(LEPUSContext *ctx, size_t *pbuf_len, + const char *filename); +LEPUSModuleDef *lepus_module_loader(LEPUSContext *ctx, const char *module_name, + void *opaque); +void lepus_std_eval_binary(LEPUSContext *ctx, const uint8_t *buf, + size_t buf_len, int flags); +#endif + +#if defined(_WIN32) +int gettimeofday(struct timeval *tp, void *tzp); +#endif + +#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_LIBC_H_ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-opcode.h b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-opcode.h new file mode 100644 index 000000000..871b32afd --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-opcode.h @@ -0,0 +1,385 @@ +/* + * QuickJS opcode definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +// +// clang-format off +#ifdef FMT +FMT(none) +FMT(none_int) +FMT(none_loc) +FMT(none_arg) +FMT(none_var_ref) +FMT(u8) +FMT(i8) +FMT(loc8) +FMT(const8) +FMT(label8) +FMT(u16) +FMT(i16) +FMT(label16) +FMT(npop) +FMT(npopx) +FMT(loc) +FMT(arg) +FMT(var_ref) +FMT(u32) +FMT(i32) +FMT(const) +FMT(label) +FMT(atom) +FMT(atom_u8) +FMT(atom_u16) +FMT(atom_label_u8) +FMT(atom_label_u16) +FMT(label_u16) +// +FMT(u64) +// +#undef FMT +#endif /* FMT */ + +#ifdef DEF + +#ifndef def +#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) +#endif + +DEF(invalid, 1, 0, 0, none) /* never emitted */ + +/* push values */ +DEF(push_i32, 5, 0, 1, i32) +DEF(push_const, 5, 0, 1, const) +DEF(fclosure, 5, 0, 1, const) /* must follow push_const */ +DEF(push_atom_value, 5, 0, 1, atom) +DEF(private_symbol, 5, 0, 1, atom) +DEF(undefined, 1, 0, 1, none) +DEF(null, 1, 0, 1, none) +DEF(push_this, 1, 0, 1, none) /* only used at the start of a function */ +DEF(push_false, 1, 0, 1, none) +DEF(push_true, 1, 0, 1, none) +DEF(object, 1, 0, 1, none) +DEF(special_object, 2, 0, 1, u8) /* only used at the start of a function */ +DEF(rest, 3, 0, 1, u16) /* only used at the start of a function */ + +DEF(drop, 1, 1, 0, none) /* a -> */ +DEF(nip, 1, 2, 1, none) /* a b -> b */ +DEF(nip1, 1, 3, 2, none) /* a b c -> b c */ +DEF(dup, 1, 1, 2, none) /* a -> a a */ +DEF(dup1, 1, 2, 3, none) /* a b -> a a b */ +DEF(dup2, 1, 2, 4, none) /* a b -> a b a b */ +DEF(dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ +DEF(insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ +DEF(insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ +DEF(insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ +DEF(perm3, 1, 3, 3, none) /* obj a b -> a obj b */ +DEF(perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ +DEF(perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ +DEF(swap, 1, 2, 2, none) /* a b -> b a */ +DEF(swap2, 1, 4, 4, none) /* a b c d -> c d a b */ +DEF(rot3l, 1, 3, 3, none) /* x a b -> a b x */ +DEF(rot3r, 1, 3, 3, none) /* a b x -> x a b */ +DEF(rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ +DEF(rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ + +DEF(call_constructor, 3, 2, 1, + npop) /* func new.target args -> ret. arguments are not counted in n_pop */ +DEF(call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ +DEF(tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ +DEF(call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ +DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ +DEF(array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ +DEF(apply, 3, 3, 1, u16) +DEF(return, 1, 1, 0, none) +DEF(return_undef, 1, 0, 0, none) +DEF(check_ctor_return, 1, 1, 2, none) +DEF(check_ctor, 1, 0, 0, none) +DEF(check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ +DEF(add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ +DEF(return_async, 1, 1, 0, none) +DEF(throw, 1, 1, 0, none) +DEF(throw_var, 6, 0, 0, atom_u8) +DEF(eval, 3, 1, 1, u16) +DEF(regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a + bytecode string */ +DEF(get_super_ctor, 1, 1, 1, none) +DEF(get_super, 1, 1, 1, none) +DEF(import, 1, 1, 1, none) /* dynamic module import */ + +DEF(check_var, 5, 0, 1, atom) /* check if a variable exists */ + +DEF(get_var_undef, 5, 0, 1, + atom) /* push undefined if the variable does not exist */ +DEF(get_var, 5, 0, 1, + atom) /* throw an exception if the variable does not exist */ + +DEF(put_var, 5, 1, 0, atom) /* must come after get_var */ +DEF(put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize + a global lexical variable */ +DEF(put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */ + +DEF(get_ref_value, 1, 2, 3, none) +DEF(put_ref_value, 1, 3, 0, none) + +DEF(define_var, 6, 0, 0, atom_u8) +DEF(check_define_var, 6, 0, 0, atom_u8) +DEF(define_func, 6, 1, 0, atom_u8) + +DEF(get_field, 5, 1, 1, atom) +DEF(get_field2, 5, 1, 2, atom) +DEF(put_field, 5, 2, 0, atom) + +DEF(get_private_field, 1, 2, 1, none) /* obj prop -> value */ +DEF(put_private_field, 1, 3, 0, none) /* obj value prop -> */ +DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ +DEF(get_array_el, 1, 2, 1, none) +DEF(get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ +DEF(put_array_el, 1, 3, 0, none) +DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ +DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ +DEF(define_field, 5, 2, 1, atom) +DEF(set_name, 5, 1, 1, atom) +DEF(set_name_computed, 1, 2, 2, none) +DEF(set_proto, 1, 2, 1, none) +DEF(set_home_object, 1, 2, 2, none) +DEF(define_array_el, 1, 3, 2, none) +DEF(append, 1, 3, 2, none) /* append enumerated object, update length */ +DEF(copy_data_properties, 2, 3, 3, u8) +DEF(define_method, 6, 2, 1, atom_u8) +DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ +DEF(define_class, 6, 2, 2, atom_u8) + +DEF(get_loc, 3, 0, 1, loc) +DEF(put_loc, 3, 1, 0, loc) /* must come after get_loc */ +DEF(set_loc, 3, 1, 1, loc) /* must come after put_loc */ +DEF(get_arg, 3, 0, 1, arg) +DEF(put_arg, 3, 1, 0, arg) /* must come after get_arg */ +DEF(set_arg, 3, 1, 1, arg) /* must come after put_arg */ +DEF(get_var_ref, 3, 0, 1, var_ref) +DEF(put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ +DEF(set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ +DEF(set_loc_uninitialized, 3, 0, 0, loc) +DEF(get_loc_check, 3, 0, 1, loc) +DEF(put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ +DEF(put_loc_check_init, 3, 1, 0, loc) +DEF(get_var_ref_check, 3, 0, 1, var_ref) +DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ +DEF(put_var_ref_check_init, 3, 1, 0, var_ref) +DEF(close_loc, 3, 0, 0, loc) +DEF(if_false, 5, 1, 0, label) +DEF(if_true, 5, 1, 0, label) /* must come after if_false */ +DEF(goto, 5, 0, 0, label) /* must come after if_true */ +DEF(catch, 5, 0, 1, label) +DEF(gosub, 5, 0, 0, label) /* used to execute the finally block */ +DEF(ret, 1, 1, 0, none) /* used to return from the finally block */ + +DEF(to_object, 1, 1, 1, none) +// DEF( to_string, 1, 1, 1, none) +DEF(to_propkey, 1, 1, 1, none) +DEF(to_propkey2, 1, 2, 2, none) + +DEF(with_get_var, 10, 1, 0, + atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_put_var, 10, 2, 1, + atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_delete_var, 10, 1, 0, + atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_make_ref, 10, 1, 0, + atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_get_ref, 10, 1, 0, + atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) + +DEF(make_loc_ref, 7, 0, 2, atom_u16) +DEF(make_arg_ref, 7, 0, 2, atom_u16) +DEF(make_var_ref_ref, 7, 0, 2, atom_u16) +DEF(make_var_ref, 5, 0, 2, atom) + +DEF(for_in_start, 1, 1, 1, none) +DEF(for_of_start, 1, 1, 3, none) +DEF(for_await_of_start, 1, 1, 3, none) +DEF(for_in_next, 1, 1, 3, none) +DEF(for_of_next, 2, 3, 5, u8) +DEF(for_await_of_next, 1, 3, 4, none) +DEF(iterator_get_value_done, 1, 1, 2, none) +DEF(iterator_close, 1, 3, 0, none) +DEF(iterator_close_return, 1, 4, 4, none) +DEF(async_iterator_close, 1, 3, 2, none) +DEF(async_iterator_next, 1, 4, 4, none) +DEF(async_iterator_get, 2, 4, 5, u8) +DEF(initial_yield, 1, 0, 0, none) +DEF(yield, 1, 1, 2, none) +DEF(yield_star, 1, 2, 2, none) +DEF(async_yield_star, 1, 1, 2, none) +DEF(await, 1, 1, 1, none) + +/* arithmetic/logic operations */ +DEF(neg, 1, 1, 1, none) +DEF(plus, 1, 1, 1, none) +DEF(dec, 1, 1, 1, none) +DEF(inc, 1, 1, 1, none) +DEF(post_dec, 1, 1, 2, none) +DEF(post_inc, 1, 1, 2, none) +DEF(dec_loc, 2, 0, 0, loc8) +DEF(inc_loc, 2, 0, 0, loc8) +DEF(add_loc, 2, 1, 0, loc8) +DEF(not, 1, 1, 1, none) +DEF(lnot, 1, 1, 1, none) +DEF(typeof, 1, 1, 1, none) +DEF(delete, 1, 2, 1, none) +DEF(delete_var, 5, 0, 1, atom) + +DEF(mul, 1, 2, 1, none) +DEF(div, 1, 2, 1, none) +DEF(mod, 1, 2, 1, none) +DEF(add, 1, 2, 1, none) +DEF(sub, 1, 2, 1, none) +DEF(pow, 1, 2, 1, none) +DEF(shl, 1, 2, 1, none) +DEF(sar, 1, 2, 1, none) +DEF(shr, 1, 2, 1, none) +DEF(lt, 1, 2, 1, none) +DEF(lte, 1, 2, 1, none) +DEF(gt, 1, 2, 1, none) +DEF(gte, 1, 2, 1, none) +DEF(instanceof, 1, 2, 1, none) +DEF(in, 1, 2, 1, none) +DEF(eq, 1, 2, 1, none) +DEF(neq, 1, 2, 1, none) +DEF(strict_eq, 1, 2, 1, none) +DEF(strict_neq, 1, 2, 1, none) +DEF(and, 1, 2, 1, none) +DEF(xor, 1, 2, 1, none) +DEF(or, 1, 2, 1, none) +#ifdef CONFIG_BIGNUM +DEF(mul_pow10, 1, 2, 1, none) +DEF(math_div, 1, 2, 1, none) +DEF(math_mod, 1, 2, 1, none) +DEF(math_pow, 1, 2, 1, none) +#endif +/* must be the last non short and non temporary opcode */ +DEF(nop, 1, 0, 0, none) +/* temporary opcodes: never emitted in the final bytecode */ + +def(set_arg_valid_upto, 3, 0, 0, arg) /* emitted in phase 1, removed in phase 2 */ + +def(close_var_object, 1, 0, 0, none) /* emitted in phase 1, removed in phase 2 */ +def(enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ +def(leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ +def(label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ +def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ +def(scope_put_private_field, 7, 1, 1, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ + +// +def(line_num, 9, 0, 0, u64) /* emitted in phase 1, removed in phase 3 */ + // + +#if SHORT_OPCODES +DEF(push_minus1, 1, 0, 1, none_int) +DEF(push_0, 1, 0, 1, none_int) +DEF(push_1, 1, 0, 1, none_int) +DEF(push_2, 1, 0, 1, none_int) +DEF(push_3, 1, 0, 1, none_int) +DEF(push_4, 1, 0, 1, none_int) +DEF(push_5, 1, 0, 1, none_int) +DEF(push_6, 1, 0, 1, none_int) +DEF(push_7, 1, 0, 1, none_int) +DEF(push_i8, 2, 0, 1, i8) +DEF(push_i16, 3, 0, 1, i16) +DEF(push_const8, 2, 0, 1, const8) +DEF(fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ +DEF(push_empty_string, 1, 0, 1, none) +DEF(get_loc8, 2, 0, 1, loc8) +DEF(put_loc8, 2, 1, 0,loc8) +DEF(set_loc8, 2, 1, 1, loc8) +DEF(get_loc0, 1, 0, 1, none_loc) +DEF(get_loc1, 1, 0, 1, none_loc) +DEF(get_loc2, 1, 0, 1, none_loc) +DEF(get_loc3, 1, 0, 1, none_loc) +DEF(put_loc0, 1, 1, 0, none_loc) +DEF(put_loc1, 1, 1, 0, none_loc) +DEF(put_loc2, 1, 1, 0, none_loc) +DEF(put_loc3, 1, 1, 0, none_loc) +DEF(set_loc0, 1, 1, 1, none_loc) +DEF(set_loc1, 1, 1, 1, none_loc) +DEF(set_loc2, 1, 1, 1, none_loc) +DEF(set_loc3, 1, 1, 1, none_loc) +DEF(get_arg0, 1, 0, 1, none_arg) +DEF(get_arg1, 1, 0, 1, none_arg) +DEF(get_arg2, 1, 0, 1, none_arg) +DEF(get_arg3, 1, 0, 1, none_arg) +DEF(put_arg0, 1, 1, 0, none_arg) +DEF(put_arg1, 1, 1, 0, none_arg) +DEF(put_arg2, 1, 1, 0, none_arg) +DEF(put_arg3, 1, 1, 0, none_arg) +DEF(set_arg0, 1, 1, 1, none_arg) +DEF(set_arg1, 1, 1, 1, none_arg) +DEF(set_arg2, 1, 1, 1, none_arg) +DEF(set_arg3, 1, 1, 1, none_arg) +DEF(get_var_ref0, 1, 0, 1, none_var_ref) +DEF(get_var_ref1, 1, 0, 1, none_var_ref) +DEF(get_var_ref2, 1, 0, 1, none_var_ref) +DEF(get_var_ref3, 1, 0, 1, none_var_ref) +DEF(put_var_ref0, 1, 1, 0, none_var_ref) +DEF(put_var_ref1, 1, 1, 0, none_var_ref) +DEF(put_var_ref2, 1, 1, 0, none_var_ref) +DEF(put_var_ref3, 1, 1, 0, none_var_ref) +DEF(set_var_ref0, 1, 1, 1, none_var_ref) +DEF(set_var_ref1, 1, 1, 1, none_var_ref) +DEF(set_var_ref2, 1, 1, 1, none_var_ref) +DEF(set_var_ref3, 1, 1, 1, none_var_ref) + +DEF(get_length, 1, 1, 1, none) + +DEF(if_false8, 2, 1, 0, label8) +DEF(if_true8, 2, 1, 0, label8) /* must come after if_false8 */ +DEF(goto8, 2, 0, 0, label8) /* must come after if_true8 */ +DEF(goto16, 3, 0, 0, label16) +DEF(call0, 1, 1, 1, npopx) +DEF(call1, 1, 1, 1, npopx) +DEF(call2, 1, 1, 1, npopx) +DEF(call3, 1, 1, 1, npopx) +DEF(is_undefined, 1, 1, 1, none) +DEF(is_null, 1, 1, 1, none) +DEF(is_function, 1, 1, 1, none) +#endif + +#undef DEF +#undef def +#endif /* DEF */ + +#ifdef COMPILE_TIME_OPCODE +COMPILE_TIME_OPCODE(caller_str, 9, 0, 0, none) +#endif diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-tag.h b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-tag.h new file mode 100644 index 000000000..960b17c38 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs-tag.h @@ -0,0 +1,64 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +// +// clang-format off +#ifdef DEFTAG + +#ifndef deftag +#define deftag(type, description) DEFTAG(type, description) +#endif +// LEPUSValue tag +DEFTAG(LEPUSObject, "LEPUSObject") +DEFTAG(LEPUSLepusRef, "LEPUSLepusRef") +DEFTAG(JSString, "JSString") +DEFTAG(JSShape, "JSShape") +DEFTAG(LEPUSFunctionBytecode, "LEPUSFunctionBytecode") +DEFTAG(JSTypedArray, "JSTypedArray") +DEFTAG(JSMapState, "JSMapState") +DEFTAG(JSMapIteratorData, "JSMapIteratorData") +DEFTAG(JSFunctionDef, "JSFunctionDef") +DEFTAG(JSArrayBuffer, "JSArrayBuffer") +DEFTAG(LEPUSScriptSource, "LEPUSScriptSource") +DEFTAG(LEPUSModuleDef, "LEPUSModuleDef") +DEFTAG(JSGeneratorData, "JSGeneratorData") +DEFTAG(JSAsyncFunctionData, "JSAsyncFunctionData") +DEFTAG(JSVarRef, "JSVarRef") +// LEPUSObject, class_id, finalizer +DEFTAG(JSBoundFunction, "JSBoundFunction") +DEFTAG(JSCFunctionDataRecord, "JSCFunctionDataRecord") +DEFTAG(JSForInIterator, "JSForInIterator") +DEFTAG(JSSeparableString, "JSSeparableString") +DEFTAG(JSArrayIteratorData, "JSArrayIteratorData") +DEFTAG(JSRegExpStringIteratorData, "JSRegExpStringIteratorData") +DEFTAG(JSProxyData, "JSProxyData") +DEFTAG(JSPromiseData, "JSPromiseData") +DEFTAG(JSPromiseReactionData, "JSPromiseReactionData") +DEFTAG(JSPromiseFunctionData, "JSPromiseFunctionData") +DEFTAG(JSAsyncFromSyncIteratorData, "JSAsyncFromSyncIteratorData") +DEFTAG(JSAsyncGeneratorData, "JSAsyncGeneratorData") +// other +DEFTAG(LEPUSPropertyEnum, "LEPUSPropertyEnum") +DEFTAG(JSMapRecord, "JSMapRecord") +DEFTAG(ValueSlot, "ValueSlot") +DEFTAG(LEPUSDebuggerInfo, "DebuggerInfo") + +DEFTAG(FinalizationRegistryData, "FinalizationRegistryData") +DEFTAG(WeakRefData, "WeakRefData") + +#ifdef CONFIG_BIGNUM +// big number +DEFTAG(JSBigFloat, "JSBigFloat") +#endif + +DEFTAG(JSOSRWHandler, "JSOSRWHandler") +DEFTAG(JSOSSignalHandler, "JSOSSignalHandler") +DEFTAG(JSOSTimer, "JSOSTimer") +DEFTAG(JSSTDFile, "JSSTDFile") + +deftag(JSSymbol, "JSSymbol") +deftag(JSValueArray, "JSValueArray") +deftag(JSConstString, "JSConstString") +deftag(JsonStrArray, "JsonStrArray") +#undef deftag +#endif diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/quickjs.h b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs.h new file mode 100644 index 000000000..01f961f77 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs.h @@ -0,0 +1,1611 @@ +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2019 Fabrice Bellard + * Copyright (c) 2017-2019 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_H_ +#define SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_H_ + +#include +#include +#include + +#include "base_export.h" +#include "list.h" +// #define DUMP_LEAKS 0 +// for Debug +// +// if want to dump atoms/shapes/strings, should open blew marco +// #define DUMP_QJS_VALUE + +#ifdef DEBUG_MEMORY +#ifdef __ANDROID__ +#include +// #define DUMP_LEAKS 0 +// log +#define printf(...) __android_log_print(ANDROID_LOG_ERROR, "LYNX", __VA_ARGS__); +#endif +#else +#define printf(...) +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define lepus_likely(x) __builtin_expect(!!(x), 1) +#define lepus_unlikely(x) __builtin_expect(!!(x), 0) +#define lepus_force_inline inline __attribute__((always_inline)) +#define __js_printf_like(f, a) __attribute__((format(printf, f, a))) +#else +#define lepus_likely(x) (x) +#define lepus_unlikely(x) (x) +#define lepus_force_inline inline +#define __js_printf_like(a, b) +#endif + +#define LEPUS_BOOL int + +typedef struct LEPUSRuntime LEPUSRuntime; +typedef struct LEPUSContext LEPUSContext; +typedef struct LEPUSObject LEPUSObject; +typedef struct LEPUSClass LEPUSClass; +typedef uint32_t LEPUSClassID; +typedef uint32_t JSAtom; +typedef uint32_t LEPUSAtom; + +struct LEPUSFunctionBytecode; +struct LEPUSStackFrame; +typedef struct LEPUSDebuggerInfo LEPUSDebuggerInfo; +struct qjs_queue; +struct DebuggerSuspendedState; +struct LEPUSClosureVar; +typedef struct LEPUSBreakpoint LEPUSBreakpoint; +typedef struct LEPUSScriptSource LEPUSScriptSource; + +#if defined(__x86_64__) || defined(__aarch64__) +#define LEPUS_PTR64 +#define LEPUS_PTR64_DEF(a) a +#else +#define LEPUS_PTR64_DEF(a) +#endif + +#if !defined(LEPUS_PTR64) +#define LEPUS_NAN_BOXING +#endif + +enum { + ALLOC_TAG_WITHOUT_PTR = 1, +#define DEFTAG(name, str) ALLOC_TAG_##name, +#include "quickjs-tag.h" +#undef DEFTAG + ALLOC_TAG_END, +}; + +// +typedef enum LEPUSTypedArrayType { + LEPUS_TYPED_UNKNOW, + LEPUS_TYPED_UINT8C_ARRAY, /* u.array (typed_array) */ + LEPUS_TYPED_INT8_ARRAY, /* u.array (typed_array) */ + LEPUS_TYPED_UINT8_ARRAY, /* u.array (typed_array) */ + LEPUS_TYPED_INT16_ARRAY, /* u.array (typed_array) */ + LEPUS_TYPED_UINT16_ARRAY, /* u.array (typed_array) */ + LEPUS_TYPED_INT32_ARRAY, /* u.array (typed_array) */ + LEPUS_TYPED_UINT32_ARRAY, /* u.array (typed_array) */ + LEPUS_TYPED_FLOAT32_ARRAY, /* u.array (typed_array) */ + LEPUS_TYPED_FLOAT64_ARRAY /* u.array (typed_array) */ +} LEPUSTypedArrayType; +// + +typedef struct LEPUSRefCountHeader { + int ref_count; +} LEPUSRefCountHeader; + +#if defined(__aarch64__) && !defined(OS_WIN) && !defined(CONFIG_BIGNUM) && \ + !DISABLE_NANBOX + +static const int64_t LEPUS_FLOAT64_NAN_BITS = 0x7ff8000000000000; + +static inline double pure_nan() { return (*(double *)&LEPUS_FLOAT64_NAN_BITS); } +#define LEPUS_FLOAT64_NAN (pure_nan()) + +static const int DOUBLE_ENCODE_OFFSET_BIT = 49; +static const int64_t DOUBLE_ENCODE_OFFSET = (1ll << DOUBLE_ENCODE_OFFSET_BIT); +static const int64_t NUMBER_TAG = 0xfffe000000000000; +static const int64_t LEPUS_TAG_Atom = 0xfffc000000000000; +static const int64_t NOT_NUMBER_MASK = 0xffff000000000000; +static const int64_t OTHER_TAG = 0x2; + +static const int64_t BOOL_TAG = 0x4; +static const int64_t BOOL_TRUE_TAG = (OTHER_TAG | BOOL_TAG | 0x1); +static const int64_t BOOL_FALSE_TAG = (OTHER_TAG | BOOL_TAG); +static const int64_t UNDEFINED_TAG = (OTHER_TAG | 0x10); +static const int64_t UNINITIALIZED_TAG = (OTHER_TAG | 0x20); +static const int64_t CATCH_OFFSET_TAG = (OTHER_TAG | 0x40); +static const int64_t EXCEPTION_TAG = (OTHER_TAG | 0x80); + +#define VALUE_FALSE ((LEPUSValue){.as_int64 = BOOL_FALSE_TAG}) +#define VALUE_TRUE ((LEPUSValue){.as_int64 = BOOL_TRUE_TAG}) +#define VALUE_UNDEFINED ((LEPUSValue){.as_int64 = UNDEFINED_TAG}) +#define VALUE_NULL ((LEPUSValue){.as_int64 = OTHER_TAG}) +#define VALUE_EXCEPTION ((LEPUSValue){.as_int64 = EXCEPTION_TAG}) +#define VALUE_UNINITIALIZED ((LEPUSValue){.as_int64 = UNINITIALIZED_TAG}) + +static const int64_t OTHER_PTR_TAG = 0x0001000000000000ll; +static const int64_t SYMBOL_TAG = (0x1 | OTHER_PTR_TAG); +static const int64_t STRING_TAG = (0x2 | OTHER_PTR_TAG); +static const int64_t MODULE_TAG = (0x3 | OTHER_PTR_TAG); +static const int64_t FUNCTION_BYTECODE_TAG = (0x0 | OTHER_PTR_TAG); +static const int64_t OTHER_PTR_MASK = 0x0000fffffffffffc; +static const int64_t NOT_OTHER_PTR_MASK = 0xffff000000000003; +static const int64_t NOT_CELL_MASK = (OTHER_TAG | NUMBER_TAG); +static const int64_t NOT_CELL_OTHER_PTR_MASK = + (OTHER_TAG | NUMBER_TAG | OTHER_PTR_TAG); +static const int64_t LEPUS_PTR_TAG = 0xffff000000000000ll; +static const int64_t LEPUS_REF_TAG = (0x0 | LEPUS_PTR_TAG); +static const int64_t LEPUS_CPOINTER_TAG = (0x1 | LEPUS_PTR_TAG); +static const int64_t LEPUS_BIG_INT_TAG = (0x2 | LEPUS_PTR_TAG); +static const int64_t SEPARABLE_STRING_TAG = (0x3 | LEPUS_PTR_TAG); +static const int64_t LEPUS_PTR_MASK = 0x0000fffffffffffc; +static const int64_t NOT_LEPUS_PTR_MASK = 0xffff000000000003; +static const int64_t INTERNAL_GC_TAG = 0xfffd000000000000ll; +// #define NOT_OTHER_MASK (NUMBER_TAG | OTHER_PTR_TAG) + +// +static const int64_t LEPUS_TAG_LEPUS_REF = + LEPUS_REF_TAG; /* Primjs add for lepus */ +static const int64_t LEPUS_TAG_LEPUS_CPOINTER = LEPUS_CPOINTER_TAG; +static const int64_t LEPUS_TAG_BIG_INT = LEPUS_BIG_INT_TAG; +static const int64_t LEPUS_TAG_BIG_FLOAT = -9; +static const int64_t LEPUS_TAG_SYMBOL = SYMBOL_TAG; +static const int64_t LEPUS_TAG_STRING = STRING_TAG; +static const int64_t LEPUS_TAG_MODULE = MODULE_TAG; +static const int64_t LEPUS_TAG_FUNCTION_BYTECODE = FUNCTION_BYTECODE_TAG; +static const int64_t LEPUS_TAG_OBJECT = 0; +static const int64_t LEPUS_TAG_INT = NUMBER_TAG; +static const int64_t LEPUS_TAG_BOOL = BOOL_TAG | OTHER_TAG; +static const int64_t LEPUS_TAG_NULL = OTHER_TAG; +static const int64_t LEPUS_TAG_UNDEFINED = UNDEFINED_TAG | OTHER_TAG; +static const int64_t LEPUS_TAG_UNINITIALIZED = UNINITIALIZED_TAG; +static const int64_t LEPUS_TAG_CATCH_OFFSET = CATCH_OFFSET_TAG; +static const int64_t LEPUS_TAG_EXCEPTION = EXCEPTION_TAG; +static const int64_t LEPUS_TAG_SHAPE = + (0x0 | INTERNAL_GC_TAG); /* used internally during GC */ +static const int64_t LEPUS_TAG_ASYNC_FUNCTION = + (0x1 | INTERNAL_GC_TAG); /* used internally during GC */ +static const int64_t LEPUS_TAG_VAR_REF = + (0x2 | INTERNAL_GC_TAG); /* used internally during GC */ +static const int64_t LEPUS_TAG_FLOAT64 = 1; +static const int64_t LEPUS_TAG_SEPARABLE_STRING = SEPARABLE_STRING_TAG; + +typedef union LEPUSValue { + int64_t as_int64; + void *ptr; + double as_double; +} LEPUSValue; + +#define LEPUSValueConst LEPUSValue + +inline int64_t LEPUS_VALUE_GET_TAG(LEPUSValue v) { + if ((v.as_int64 & NUMBER_TAG) == 0 && (v.as_int64 & OTHER_PTR_TAG)) { + return v.as_int64 & NOT_OTHER_PTR_MASK; + } else if ((v.as_int64 & NOT_CELL_MASK) == 0) { + return LEPUS_TAG_OBJECT; + } else if ((v.as_int64 & LEPUS_PTR_TAG) == LEPUS_PTR_TAG) { + return v.as_int64 & (~LEPUS_PTR_MASK); + } else if ((v.as_int64 & NUMBER_TAG) == 0) { + // true false undefined null + return v.as_int64 & 0xfe; + } else if ((v.as_int64 & NOT_NUMBER_MASK) == NUMBER_TAG) { + return LEPUS_TAG_INT; + } else if ((v.as_int64 & NOT_NUMBER_MASK) == LEPUS_TAG_Atom) { + return LEPUS_TAG_Atom; + } else if ((v.as_int64 & INTERNAL_GC_TAG) == INTERNAL_GC_TAG) { + return v.as_int64 & (~LEPUS_PTR_MASK); // shape, async_function, var_ref + } else { + return LEPUS_TAG_FLOAT64; + } +} + +#define LEPUS_VALUE_IS_STRING(v) \ + (((v).as_int64 & NOT_OTHER_PTR_MASK) == LEPUS_TAG_STRING) +#define LEPUS_VALUE_IS_SEPARABLE_STRING(v) \ + (((v).as_int64 & NOT_LEPUS_PTR_MASK) == LEPUS_TAG_SEPARABLE_STRING) +#define LEPUS_VALUE_IS_ATOM(v) \ + (((v).as_int64 & NOT_NUMBER_MASK) == LEPUS_TAG_Atom) +#define LEPUS_VALUE_IS_OBJECT(v) \ + (!((v).as_int64 & NOT_CELL_OTHER_PTR_MASK) && (v).as_int64) +#define LEPUS_VALUE_IS_NOT_OBJECT(v) \ + ((v).as_int64 & NOT_CELL_OTHER_PTR_MASK || (v).as_int64 == 0) +#define LEPUS_VALUE_IS_NULL(v) ((v).as_int64 == VALUE_NULL.as_int64) +#define LEPUS_VALUE_IS_UNDEFINED(v) ((v).as_int64 == VALUE_UNDEFINED.as_int64) +#define LEPUS_VALUE_IS_SYMBOL(v) \ + (((v).as_int64 & NOT_OTHER_PTR_MASK) == LEPUS_TAG_SYMBOL) +#define LEPUS_VALUE_IS_INT(v) (((v).as_int64 & NOT_NUMBER_MASK) == NUMBER_TAG) +// Note: This is faster than (((v).as_int64 & 0xffff0000000000f7) == +// LEPUS_TAG_CATCH_OFFSET) +#define LEPUS_VALUE_IS_CATCH_OFFSET(v) \ + (!((v).as_int64 & 0xffff000000000000) && \ + ((v).as_int64 & 0xf7) == LEPUS_TAG_CATCH_OFFSET) +// || ((v).as_int64 & 0xf7)!=LEPUS_TAG_CATCH_OFFSET) +#define LEPUS_VALUE_IS_FLOAT64(v) \ + (((v).as_int64 & NUMBER_TAG) && ((v).as_int64 & NUMBER_TAG) != NUMBER_TAG) +#define LEPUS_VALUE_IS_EXCEPTION(v) ((v).as_int64 == VALUE_EXCEPTION.as_int64) +#define LEPUS_VALUE_IS_BOOL(v) \ + ((v).as_int64 == VALUE_TRUE.as_int64 || (v).as_int64 == VALUE_FALSE.as_int64) +#define LEPUS_VALUE_IS_UNINITIALIZED(v) \ + ((v).as_int64 == VALUE_UNINITIALIZED.as_int64) +#define LEPUS_VALUE_IS_FUNCTION_BYTECODE(v) \ + (((v).as_int64 & NOT_OTHER_PTR_MASK) == LEPUS_TAG_FUNCTION_BYTECODE) +#define LEPUS_VALUE_IS_MODULE(v) \ + (((v).as_int64 & NOT_OTHER_PTR_MASK) == LEPUS_TAG_MODULE) + +#define LEPUS_VALUE_IS_LEPUS_REF(v) \ + (((v).as_int64 & NOT_LEPUS_PTR_MASK) == LEPUS_TAG_LEPUS_REF) + +#define LEPUS_VALUE_IS_LEPUS_CPOINTER(v) \ + (((v).as_int64 & NOT_LEPUS_PTR_MASK) == LEPUS_TAG_LEPUS_CPOINTER) + +#define LEPUS_VALUE_IS_BIG_INT(v) \ + (((v).as_int64 & NOT_LEPUS_PTR_MASK) == LEPUS_TAG_BIG_INT) + +#define LEPUS_VALUE_GET_NORM_TAG(v) (LEPUS_VALUE_GET_TAG(v)) + +#define LEPUS_VALUE_GET_INT(v) ((int)((v).as_int64)) + +#define LEPUS_VALUE_GET_CATCH_OFFSET(v) ((int)(v.as_int64 >> 16)) + +#define LEPUS_VALUE_GET_BOOL(v) (v.as_int64 == VALUE_TRUE.as_int64) + +// +#define LEPUS_VALUE_GET_INT64(v) (v.as_int64) + +// #define LEPUS_MKVAL(tag, val) \ +// ((LEPUSValue) { (((uint64_t)(tag) << 32) | (uintptr_t)(val)) } +static inline LEPUSValue LEPUS_MKVAL(int64_t tag, int32_t val) { + if (tag != LEPUS_TAG_CATCH_OFFSET) { + return (LEPUSValue){.as_int64 = + (int64_t)((tag) | (uint64_t)(uint32_t)(val))}; + } else { + return (LEPUSValue){.as_int64 = + (int64_t)((tag) | (uint64_t)(uint32_t)(val) << 16)}; + } +} + +#define LEPUS_MKPTR(tag, p) \ + ((LEPUSValue){.as_int64 = (int64_t)(((int64_t)(p) & LEPUS_PTR_MASK) | (tag))}) +// + +static inline double LEPUS_VALUE_GET_FLOAT64(LEPUSValue v) { + int64_t val = (v.as_int64 - DOUBLE_ENCODE_OFFSET); + return *(double *)&val; +} + +#if defined(ANDROID) || defined(__ANDROID__) +extern int64_t HEAP_TAG_OUTER; +extern int64_t HEAP_TAG_INNER; + +#define LEPUS_VALUE_GET_CPOINTER(v) \ + ((void *)(((int64_t)v.ptr & OTHER_PTR_MASK) | HEAP_TAG_OUTER)) + +#define LEPUS_VALUE_GET_PTR(v) \ + ((void *)(((int64_t)(v).ptr & OTHER_PTR_MASK) | HEAP_TAG_INNER)) +#else +#define LEPUS_VALUE_GET_PTR(v) ((void *)(((int64_t)(v).ptr & OTHER_PTR_MASK))) +#define LEPUS_VALUE_GET_CPOINTER(v) LEPUS_VALUE_GET_PTR(v) +#endif + +static inline LEPUSValue __JS_NewFloat64(LEPUSContext *ctx, double d) { + LEPUSValue v; + if (isnan(d)) { + d = pure_nan(); + } + v.as_double = d; + v.as_int64 = v.as_int64 + DOUBLE_ENCODE_OFFSET; + return v; +} + +#define LEPUS_NAN \ + ((LEPUSValue){.as_int64 = LEPUS_FLOAT64_NAN_BITS + DOUBLE_ENCODE_OFFSET}) + +// #undef isnan +// #define isnan(d) ((*(int64_t*)&d) == LEPUS_FLOAT64_NAN_BITS) + +inline LEPUS_BOOL LEPUS_VALUE_IS_NAN(LEPUSValue v) { + return v.as_int64 == LEPUS_NAN.as_int64; +} + +#define LEPUS_TAG_IS_FLOAT64(tag) ((tag) == LEPUS_TAG_FLOAT64) + +#define LEPUS_VALUE_HAS_REF_COUNT(v) \ + (((v.as_int64 & NUMBER_TAG) == 0 && (v.as_int64 & OTHER_PTR_TAG)) || \ + ((v.as_int64 & NOT_CELL_MASK) == 0 && v.as_int64 != 0) || \ + (((v.as_int64 & LEPUS_PTR_TAG) == LEPUS_PTR_TAG) && \ + ((v.as_int64 & 0x03) != 1)) || \ + ((v.as_int64 & LEPUS_PTR_TAG) == INTERNAL_GC_TAG)) + +#define LEPUS_VALUE_IS_BOTH_INT(v1, v2) \ + (LEPUS_VALUE_IS_INT((v1)) && LEPUS_VALUE_IS_INT(v2)) +#define LEPUS_VALUE_IS_BOTH_FLOAT(v1, v2) \ + (LEPUS_VALUE_IS_FLOAT64(v1) && LEPUS_VALUE_IS_FLOAT64(v2)) + +#define LEPUS_VALUE_GET_OBJ(v) ((LEPUSObject *)LEPUS_VALUE_GET_PTR(v)) +#define LEPUS_VALUE_GET_STRING(v) ((JSString *)LEPUS_VALUE_GET_PTR(v)) +// #define LEPUS_VALUE_HAS_REF_COUNT(v) ((unsigned)LEPUS_VALUE_GET_TAG(v) >= +// (unsigned)LEPUS_TAG_FIRST) + +/* special values */ +#define LEPUS_NULL VALUE_NULL +#define LEPUS_UNDEFINED VALUE_UNDEFINED +#define LEPUS_FALSE VALUE_FALSE +#define LEPUS_TRUE VALUE_TRUE +#define LEPUS_EXCEPTION VALUE_EXCEPTION +#define LEPUS_UNINITIALIZED VALUE_UNINITIALIZED + +#else /* !defined(__aarch64__) || defined(OS_WIN) || DISABLE_NANBOX */ + +enum { + /* all tags with a reference count are negative */ + LEPUS_TAG_FIRST = -12, /* first negative tag */ + LEPUS_TAG_SEPARABLE_STRING = -12, /* separable string tag */ + LEPUS_TAG_LEPUS_REF = -11, /* Primjs add for lepus */ + LEPUS_TAG_BIG_INT = -10, + LEPUS_TAG_BIG_FLOAT = -9, + LEPUS_TAG_SYMBOL = -8, + LEPUS_TAG_STRING = -7, + /* shape, async_function, var_ref is not need for PrimJs */ + LEPUS_TAG_SHAPE = -6, /* used internally during GC */ + LEPUS_TAG_ASYNC_FUNCTION = -5, /* used internally during GC */ + LEPUS_TAG_VAR_REF = -4, /* used internally during GC */ + LEPUS_TAG_MODULE = -3, /* used internally */ + LEPUS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ + LEPUS_TAG_OBJECT = -1, + + LEPUS_TAG_INT = 0, + LEPUS_TAG_BOOL = 1, + LEPUS_TAG_NULL = 2, + LEPUS_TAG_UNDEFINED = 3, + LEPUS_TAG_UNINITIALIZED = 4, + LEPUS_TAG_CATCH_OFFSET = 5, + LEPUS_TAG_EXCEPTION = 6, + + /* below is lepus type */ + LEPUS_TAG_LEPUS_CPOINTER = 7, // lepus CFunction + LEPUS_TAG_FLOAT64 = 12, + LEPUS_TAG_Atom = 13, + /* any larger tag is FLOAT64 if LEPUS_NAN_BOXING */ +}; + +#define LEPUS_FLOAT64_NAN NAN + +#ifdef CONFIG_CHECK_JSVALUE +/* LEPUSValue consistency : it is not possible to run the code in this + mode, but it is useful to detect simple reference counting + errors. It would be interesting to modify a static C analyzer to + handle specific annotations (clang has such annotations but only + for objective C) */ +typedef struct __JSValue *LEPUSValue; +typedef const struct __JSValue *LEPUSValueConst; + +#define LEPUS_VALUE_GET_TAG(v) (int)((uintptr_t)(v) & 0xf) +/* same as LEPUS_VALUE_GET_TAG, but return LEPUS_TAG_FLOAT64 with NaN boxing */ +#define LEPUS_VALUE_GET_NORM_TAG(v) LEPUS_VALUE_GET_TAG(v) +#define LEPUS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4) +#define LEPUS_VALUE_GET_BOOL(v) LEPUS_VALUE_GET_INT(v) +#define LEPUS_VALUE_GET_FLOAT64(v) (double)LEPUS_VALUE_GET_INT(v) +#define LEPUS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf) +#define LEPUS_VALUE_GET_CPOINTER(v) LEPUS_VALUE_GET_PTR(v) + +#define LEPUS_MKVAL(tag, val) (LEPUSValue)(intptr_t)(((val) << 4) | (tag)) +#define LEPUS_MKPTR(tag, p) (LEPUSValue)((intptr_t)(p) | (tag)) + +#define LEPUS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == LEPUS_TAG_FLOAT64) + +#define LEPUS_NAN LEPUS_MKVAL(LEPUS_TAG_FLOAT64, 1) + +static inline LEPUSValue __JS_NewFloat64(LEPUSContext *ctx, double d) { + return LEPUS_MKVAL(LEPUS_TAG_FLOAT64, (int)d); +} + +#elif defined(LEPUS_NAN_BOXING) +typedef uint64_t LEPUSValue; + +#define LEPUSValueConst LEPUSValue + +#define LEPUS_VALUE_GET_TAG(v) ((int)((v) >> 32)) +#define LEPUS_VALUE_GET_INT(v) ((int)(v)) +#define LEPUS_VALUE_GET_BOOL(v) ((int)(v)) +#define LEPUS_VALUE_GET_PTR(v) ((void *)(intptr_t)(v)) +#define LEPUS_VALUE_GET_CPOINTER(v) LEPUS_VALUE_GET_PTR(v) + +#define LEPUS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val)) +#define LEPUS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr)) + +// +#define LEPUS_VALUE_GET_INT64(v) ((uint64_t)v) +// + +#define LEPUS_FLOAT64_TAG_ADDEND \ + (0x7ff80000 - LEPUS_TAG_FIRST + 1) /* quiet NaN encoding */ + +static inline double LEPUS_VALUE_GET_FLOAT64(LEPUSValue v) { + union { + LEPUSValue v; + double d; + } u; + u.v = v; + u.v += (uint64_t)LEPUS_FLOAT64_TAG_ADDEND << 32; + return u.d; +} + +#define LEPUS_NAN \ + (0x7ff8000000000000 - ((uint64_t)LEPUS_FLOAT64_TAG_ADDEND << 32)) + +static inline LEPUSValue __JS_NewFloat64(LEPUSContext *ctx, double d) { + union { + double d; + uint64_t u64; + } u; + LEPUSValue v; + u.d = d; + /* normalize NaN */ + if (lepus_unlikely((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000)) + v = LEPUS_NAN; + else + v = u.u64 - ((uint64_t)LEPUS_FLOAT64_TAG_ADDEND << 32); + return v; +} + +#define LEPUS_TAG_IS_FLOAT64(tag) \ + ((unsigned)((tag)-LEPUS_TAG_FIRST) >= (LEPUS_TAG_FLOAT64 - LEPUS_TAG_FIRST)) + +/* same as LEPUS_VALUE_GET_TAG, but return LEPUS_TAG_FLOAT64 with NaN boxing */ +static inline int LEPUS_VALUE_GET_NORM_TAG(LEPUSValue v) { + uint32_t tag; + tag = LEPUS_VALUE_GET_TAG(v); + if (LEPUS_TAG_IS_FLOAT64(tag)) + return LEPUS_TAG_FLOAT64; + else + return tag; +} + +#else /* !LEPUS_NAN_BOXING */ + +typedef union LEPUSValueUnion { + int32_t int32; + double float64; + int64_t int64; + void *ptr; +} LEPUSValueUnion; + +typedef struct LEPUSValue { + LEPUSValueUnion u; + int64_t tag; +} LEPUSValue; + +#define LEPUSValueConst LEPUSValue + +#define LEPUS_VALUE_GET_TAG(v) ((int32_t)(v).tag) +/* same as LEPUS_VALUE_GET_TAG, but return LEPUS_TAG_FLOAT64 with NaN boxing */ +#define LEPUS_VALUE_GET_NORM_TAG(v) LEPUS_VALUE_GET_TAG(v) +#define LEPUS_VALUE_GET_INT(v) ((v).u.int32) +#define LEPUS_VALUE_GET_BOOL(v) ((v).u.int32) +#define LEPUS_VALUE_GET_FLOAT64(v) ((v).u.float64) +#define LEPUS_VALUE_GET_PTR(v) ((v).u.ptr) +#define LEPUS_VALUE_GET_CPOINTER(v) LEPUS_VALUE_GET_PTR(v) + +// +#define LEPUS_VALUE_GET_INT64(v) ((v).u.int64) +// + +#define LEPUS_MKVAL(tag, val) \ + (LEPUSValue) { .u.int32 = val, tag } +#define LEPUS_MKPTR(tag, p) \ + (LEPUSValue) { .u.ptr = p, tag } + +#define LEPUS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == LEPUS_TAG_FLOAT64) + +#define LEPUS_NAN \ + (LEPUSValue) { .u.float64 = LEPUS_FLOAT64_NAN, LEPUS_TAG_FLOAT64 } + +static inline LEPUSValue __JS_NewFloat64(LEPUSContext *ctx, double d) { + LEPUSValue v; + v.tag = LEPUS_TAG_FLOAT64; + v.u.float64 = d; + return v; +} + +#endif /* !LEPUS_NAN_BOXING */ + +#define LEPUS_VALUE_IS_BOTH_INT(v1, v2) \ + ((LEPUS_VALUE_GET_TAG(v1) | LEPUS_VALUE_GET_TAG(v2)) == 0) +#define LEPUS_VALUE_IS_BOTH_FLOAT(v1, v2) \ + (LEPUS_TAG_IS_FLOAT64(LEPUS_VALUE_GET_TAG(v1)) && \ + LEPUS_TAG_IS_FLOAT64(LEPUS_VALUE_GET_TAG(v2))) + +// add macro to avoid accessing LEPUS_TAG directly +#define LEPUS_VALUE_IS_STRING(v) (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_STRING) +#define LEPUS_VALUE_IS_SEPARABLE_STRING(v) \ + (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_SEPARABLE_STRING) +#define LEPUS_VALUE_IS_OBJECT(v) (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_OBJECT) +#define LEPUS_VALUE_IS_NOT_OBJECT(v) \ + (LEPUS_VALUE_GET_TAG(v) != LEPUS_TAG_OBJECT) +#define LEPUS_VALUE_IS_NULL(v) (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_NULL) +#define LEPUS_VALUE_IS_UNDEFINED(v) \ + (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_UNDEFINED) +#define LEPUS_VALUE_IS_SYMBOL(v) (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_SYMBOL) +#define LEPUS_VALUE_IS_INT(v) (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_INT) +#define LEPUS_VALUE_IS_CATCH_OFFSET(v) \ + (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_CATCH_OFFSET) +#define LEPUS_VALUE_IS_FLOAT64(v) (LEPUS_TAG_IS_FLOAT64(LEPUS_VALUE_GET_TAG(v))) +#define LEPUS_VALUE_IS_EXCEPTION(v) \ + (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_EXCEPTION) +#define LEPUS_VALUE_IS_BOOL(v) (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_BOOL) +#define LEPUS_VALUE_IS_UNINITIALIZED(v) \ + (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_UNINITIALIZED) +#define LEPUS_VALUE_IS_FUNCTION_BYTECODE(v) \ + (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_FUNCTION_BYTECODE) +#define LEPUS_VALUE_IS_MODULE(v) (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_MODULE) +#define LEPUS_VALUE_IS_LEPUS_REF(v) \ + (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_LEPUS_REF) +#define LEPUS_VALUE_IS_LEPUS_CPOINTER(v) \ + (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_LEPUS_CPOINTER) +#define LEPUS_VALUE_IS_BIG_INT(v) (LEPUS_VALUE_GET_TAG(v) == LEPUS_TAG_BIG_INT) + +#define LEPUS_VALUE_GET_OBJ(v) ((LEPUSObject *)LEPUS_VALUE_GET_PTR(v)) +#define LEPUS_VALUE_GET_STRING(v) ((JSString *)LEPUS_VALUE_GET_PTR(v)) +#define LEPUS_VALUE_GET_CATCH_OFFSET(v) LEPUS_VALUE_GET_INT((v)) +#define LEPUS_VALUE_HAS_REF_COUNT(v) \ + ((unsigned)LEPUS_VALUE_GET_TAG(v) >= (unsigned)LEPUS_TAG_FIRST) + +/* special values */ +#define LEPUS_NULL LEPUS_MKVAL(LEPUS_TAG_NULL, 0) +#define LEPUS_UNDEFINED LEPUS_MKVAL(LEPUS_TAG_UNDEFINED, 0) +#define LEPUS_FALSE LEPUS_MKVAL(LEPUS_TAG_BOOL, 0) +#define LEPUS_TRUE LEPUS_MKVAL(LEPUS_TAG_BOOL, 1) +#define LEPUS_EXCEPTION LEPUS_MKVAL(LEPUS_TAG_EXCEPTION, 0) +#define LEPUS_UNINITIALIZED LEPUS_MKVAL(LEPUS_TAG_UNINITIALIZED, 0) + +#endif /* !defined(__aarch64__) || defined(OS_WIN) || DISABLE_NANBOX */ + +/* flags for object properties */ +#define LEPUS_PROP_CONFIGURABLE (1 << 0) +#define LEPUS_PROP_WRITABLE (1 << 1) +#define LEPUS_PROP_ENUMERABLE (1 << 2) +#define LEPUS_PROP_C_W_E \ + (LEPUS_PROP_CONFIGURABLE | LEPUS_PROP_WRITABLE | LEPUS_PROP_ENUMERABLE) +#define LEPUS_PROP_LENGTH (1 << 3) /* used internally in Arrays */ +#define LEPUS_PROP_TMASK \ + (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */ +#define LEPUS_PROP_NORMAL (0 << 4) +#define LEPUS_PROP_GETSET (1 << 4) +#define LEPUS_PROP_VARREF (2 << 4) /* used internally */ +#define LEPUS_PROP_AUTOINIT (3 << 4) /* used internally */ + +/* flags for LEPUS_DefineProperty */ +#define LEPUS_PROP_HAS_SHIFT 8 +#define LEPUS_PROP_HAS_CONFIGURABLE (1 << 8) +#define LEPUS_PROP_HAS_WRITABLE (1 << 9) +#define LEPUS_PROP_HAS_ENUMERABLE (1 << 10) +#define LEPUS_PROP_HAS_GET (1 << 11) +#define LEPUS_PROP_HAS_SET (1 << 12) +#define LEPUS_PROP_HAS_VALUE (1 << 13) + +/* throw an exception if false would be returned + (LEPUS_DefineProperty/LEPUS_SetProperty) */ +#define LEPUS_PROP_THROW (1 << 14) +/* throw an exception if false would be returned in strict mode + (LEPUS_SetProperty) */ +#define LEPUS_PROP_THROW_STRICT (1 << 15) + +#define LEPUS_PROP_NO_ADD (1 << 16) /* internal use */ +#define LEPUS_PROP_NO_EXOTIC (1 << 17) /* internal use */ + +#define LEPUS_DEFAULT_STACK_SIZE (256 * 1024) + +/* LEPUS_Eval() flags */ +#define LEPUS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */ +#define LEPUS_EVAL_TYPE_MODULE (1 << 0) /* module code */ +#define LEPUS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */ +#define LEPUS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */ +#define LEPUS_EVAL_TYPE_MASK (3 << 0) + +#define LEPUS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */ +#define LEPUS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */ +#define LEPUS_EVAL_FLAG_COMPILE_ONLY (1 << 5) /* internal use */ +/* use for runtime.compileScript */ +#define LEPUS_DEBUGGER_NO_PERSIST_SCRIPT (1 << 6) + +typedef LEPUSValue LEPUSCFunction(LEPUSContext *ctx, LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv); +typedef LEPUSValue LEPUSCFunctionMagic(LEPUSContext *ctx, + LEPUSValueConst this_val, int argc, + LEPUSValueConst *argv, int magic); +typedef LEPUSValue LEPUSCFunctionData(LEPUSContext *ctx, + LEPUSValueConst this_val, int argc, + LEPUSValueConst *argv, int magic, + LEPUSValue *func_data); + +struct LEPUSMallocFunctions; +// +typedef struct LEPUSDebuggerCallbacks { + // callbacks for lepusNG debugger + void (*run_message_loop_on_pause)(LEPUSContext *ctx); + void (*quit_message_loop_on_pause)(LEPUSContext *ctx); + void (*get_messages)(LEPUSContext *ctx); + void (*send_response)(LEPUSContext *ctx, int32_t message_id, + const char *message); + void (*send_notification)(LEPUSContext *ctx, const char *message); + void (*free_messages)(LEPUSContext *ctx, char **messages, int32_t size); + + void (*inspector_check)(LEPUSContext *ctx); + void (*debugger_exception)(LEPUSContext *ctx); + uint8_t (*is_devtool_on)(LEPUSContext *ctx); +} LEPUSDebuggerCallbacks; + +typedef struct LEPUSLepusRefCallbacks { + LEPUSValue (*free_value)(LEPUSRuntime *rt, LEPUSValue val); + LEPUSValue (*get_property)(LEPUSContext *ctx, LEPUSValue thisObj, JSAtom prop, + int idx); + size_t (*get_length)(LEPUSContext *ctx, LEPUSValue val); + LEPUSValue (*convert_to_object)(LEPUSContext *ctx, LEPUSValue val); + LEPUSValue (*set_property)(LEPUSContext *ctx, LEPUSValue thisObj, + LEPUSValue prop, int idx, LEPUSValue val); + void (*free_str_cache)(void *old_ptr, void *new_ptr); + size_t (*lepus_ref_equal)(LEPUSValue val1, LEPUSValue val2); + LEPUSValue (*lepus_ref_tostring)(LEPUSContext *ctx, LEPUSValue val); + // void (*free_string_cache)(); +} LEPUSLepusRefCallbacks; + +typedef struct LEPUSLepusRef { + LEPUSRefCountHeader header; + int tag; // lepus value tag + void *p; // lepus value reference + LEPUSValue lepus_val; // convert to lepusvalue cache, default is undefined +} LEPUSLepusRef; + +void RegisterLepusType(LEPUSRuntime *rt, int32_t array_typeid, + int32_t table_typeid); + +void RegisterGCInfoCallback(LEPUSRuntime *rt, void (*func)(const char *, int)); + +void RegisterLepusRefCallbacks(LEPUSRuntime *rt, LEPUSLepusRefCallbacks *funcs); + +void RegisterPrimJSCallbacks(LEPUSRuntime *rt, void **funcs, + int32_t callback_size); +void RegisterQJSDebuggerCallbacks(LEPUSRuntime *rt, void **funcs, + int32_t callback_size); +void PrepareQJSDebuggerDefer(LEPUSContext *ctx, void **funcs, + int32_t callback_size); +// for shared context +void PrepareQJSDebuggerForSharedContext(LEPUSContext *ctx, void **funcs, + int32_t callback_size, + bool devtool_connect); + +// + +LEPUSRuntime *LEPUS_NewRuntime(void); +LEPUSRuntime *LEPUS_NewRuntimeWithMode(uint32_t mode); +/* info lifetime must exceed that of rt */ +void LEPUS_SetRuntimeInfo(LEPUSRuntime *rt, const char *info); +void LEPUS_SetMemoryLimit(LEPUSRuntime *rt, size_t limit); +void LEPUS_SetGCThreshold(LEPUSRuntime *rt, size_t gc_threshold); +LEPUSRuntime *LEPUS_NewRuntime2(const struct LEPUSMallocFunctions *mf, + void *opaque, uint32_t mode); +void LEPUS_FreeRuntime(LEPUSRuntime *rt); +typedef void LEPUS_MarkFunc(LEPUSRuntime *rt, LEPUSValueConst val, + int local_idx); +void LEPUS_MarkValue(LEPUSRuntime *rt, LEPUSValueConst val, + LEPUS_MarkFunc *mark_func, int local_idx); +void LEPUS_RunGC(LEPUSRuntime *rt); +LEPUS_BOOL LEPUS_IsInGCSweep(LEPUSRuntime *rt); + +LEPUSContext *LEPUS_NewContext(LEPUSRuntime *rt); +void LEPUS_FreeContext(LEPUSContext *s); +void *LEPUS_GetContextOpaque(LEPUSContext *ctx); +void LEPUS_SetContextOpaque(LEPUSContext *ctx, void *opaque); +LEPUSRuntime *LEPUS_GetRuntime(LEPUSContext *ctx); +void LEPUS_SetMaxStackSize(LEPUSContext *ctx, size_t stack_size); +void LEPUS_SetClassProto(LEPUSContext *ctx, LEPUSClassID class_id, + LEPUSValue obj); +LEPUSValue LEPUS_GetClassProto(LEPUSContext *ctx, LEPUSClassID class_id); +int LEPUS_MoveUnhandledRejectionToException(LEPUSContext *ctx); +size_t LEPUS_GetHeapSize(LEPUSRuntime *rt); +/* the following functions are used to select the intrinsic object to + save memory */ +LEPUSContext *LEPUS_NewContextRaw(LEPUSRuntime *rt); +QJS_HIDE void LEPUS_AddIntrinsicBaseObjects(LEPUSContext *ctx); +QJS_HIDE void LEPUS_AddIntrinsicDate(LEPUSContext *ctx); +QJS_HIDE void LEPUS_AddIntrinsicEval(LEPUSContext *ctx); +QJS_HIDE void LEPUS_AddIntrinsicStringNormalize(LEPUSContext *ctx); +QJS_HIDE void LEPUS_AddIntrinsicRegExpCompiler(LEPUSContext *ctx); +QJS_HIDE void LEPUS_AddIntrinsicRegExp(LEPUSContext *ctx); +QJS_HIDE void LEPUS_AddIntrinsicJSON(LEPUSContext *ctx); +QJS_HIDE void LEPUS_AddIntrinsicProxy(LEPUSContext *ctx); +QJS_HIDE void LEPUS_AddIntrinsicMapSet(LEPUSContext *ctx); +QJS_HIDE void LEPUS_AddIntrinsicTypedArrays(LEPUSContext *ctx); +QJS_HIDE void LEPUS_AddIntrinsicPromise(LEPUSContext *ctx); +#ifdef QJS_UNITTEST +QJS_HIDE LEPUSValue lepus_string_codePointRange(LEPUSContext *ctx, + LEPUSValueConst this_val, + int argc, + LEPUSValueConst *argv); + +QJS_HIDE LEPUSValue lepus_gc(LEPUSContext *ctx, LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv); +#endif +QJS_HIDE void *lepus_malloc_rt(LEPUSRuntime *rt, size_t size, int alloc_tag); +void lepus_free_rt(LEPUSRuntime *rt, void *ptr); +QJS_HIDE void *lepus_realloc_rt(LEPUSRuntime *rt, void *ptr, size_t size, + int alloc_tag); +QJS_HIDE size_t lepus_malloc_usable_size_rt(LEPUSRuntime *rt, const void *ptr); +QJS_HIDE void *lepus_mallocz_rt(LEPUSRuntime *rt, size_t size, int alloc_tag); + +void *lepus_malloc(LEPUSContext *ctx, size_t size, int alloc_tag); +void lepus_free(LEPUSContext *ctx, void *ptr); +void *lepus_mallocz(LEPUSContext *ctx, size_t size, int alloc_tag); +QJS_HIDE void *lepus_realloc(LEPUSContext *ctx, void *ptr, size_t size, + int alloc_tag); +QJS_HIDE size_t lepus_malloc_usable_size(LEPUSContext *ctx, const void *ptr); +QJS_HIDE void *lepus_realloc2(LEPUSContext *ctx, void *ptr, size_t size, + size_t *pslack, int alloc_tag); +QJS_HIDE char *lepus_strdup(LEPUSContext *ctx, const char *str, int alloc_tag); +QJS_HIDE char *lepus_strndup(LEPUSContext *ctx, const char *s, size_t n, + int alloc_tag); + +#if LYNX_SIMPLIFY +typedef struct LEPUSMemoryUsage { + int64_t malloc_size, malloc_limit, memory_used_size; + int64_t malloc_count; + int64_t memory_used_count; + int64_t atom_count, atom_size; + int64_t str_count, str_size; + int64_t obj_count, obj_size; + int64_t prop_count, prop_size; + int64_t shape_count, shape_size; + int64_t lepus_func_count, lepus_func_size, lepus_func_code_size; + int64_t lepus_func_pc2line_count, lepus_func_pc2line_size; + int64_t c_func_count, array_count; + int64_t fast_array_count, fast_array_elements; + int64_t binary_object_count, binary_object_size; +} LEPUSMemoryUsage; + +void LEPUS_ComputeMemoryUsage(LEPUSRuntime *rt, LEPUSMemoryUsage *s); +void LEPUS_DumpMemoryUsage(FILE *fp, const LEPUSMemoryUsage *s, + LEPUSRuntime *rt); + +#endif + +/* atom support */ +JSAtom LEPUS_NewAtomLen(LEPUSContext *ctx, const char *str, size_t len); +JSAtom LEPUS_NewAtom(LEPUSContext *ctx, const char *str); +JSAtom LEPUS_NewAtomUInt32(LEPUSContext *ctx, uint32_t n); +JSAtom LEPUS_DupAtom(LEPUSContext *ctx, JSAtom v); +void LEPUS_FreeAtom(LEPUSContext *ctx, JSAtom v); +void LEPUS_FreeAtomRT(LEPUSRuntime *rt, JSAtom v); +LEPUSValue LEPUS_AtomToValue(LEPUSContext *ctx, JSAtom atom); +LEPUSValue LEPUS_AtomToString(LEPUSContext *ctx, JSAtom atom); +const char *LEPUS_AtomToCString(LEPUSContext *ctx, JSAtom atom); + +/* object class support */ + +typedef struct LEPUSPropertyEnum { + LEPUS_BOOL is_enumerable; + JSAtom atom; +} LEPUSPropertyEnum; + +typedef struct LEPUSPropertyDescriptor { + int flags; + LEPUSValue value; + LEPUSValue getter; + LEPUSValue setter; +} LEPUSPropertyDescriptor; + +typedef struct LEPUSClassExoticMethods { + /* Return -1 if exception (can only happen in case of Proxy object), + FALSE if the property does not exists, TRUE if it exists. If 1 is + returned, the property descriptor 'desc' is filled if != NULL. */ + int (*get_own_property)(LEPUSContext *ctx, LEPUSPropertyDescriptor *desc, + LEPUSValueConst obj, JSAtom prop); + /* '*ptab' should hold the '*plen' property keys. Return 0 if OK, + -1 if exception. The 'is_enumerable' field is ignored. + */ + int (*get_own_property_names)(LEPUSContext *ctx, LEPUSPropertyEnum **ptab, + uint32_t *plen, LEPUSValueConst obj); + /* return < 0 if exception, or TRUE/FALSE */ + int (*delete_property)(LEPUSContext *ctx, LEPUSValueConst obj, JSAtom prop); + /* return < 0 if exception or TRUE/FALSE */ + int (*define_own_property)(LEPUSContext *ctx, LEPUSValueConst this_obj, + JSAtom prop, LEPUSValueConst val, + LEPUSValueConst getter, LEPUSValueConst setter, + int flags); + /* The following methods can be emulated with the previous ones, + so they are usually not needed */ + /* return < 0 if exception or TRUE/FALSE */ + int (*has_property)(LEPUSContext *ctx, LEPUSValueConst obj, JSAtom atom); + LEPUSValue (*get_property)(LEPUSContext *ctx, LEPUSValueConst obj, + JSAtom atom, LEPUSValueConst receiver); + /* return < 0 if exception or TRUE/FALSE */ + int (*set_property)(LEPUSContext *ctx, LEPUSValueConst obj, JSAtom atom, + LEPUSValueConst value, LEPUSValueConst receiver, + int flags); +} LEPUSClassExoticMethods; + +typedef void LEPUSClassFinalizer(LEPUSRuntime *rt, LEPUSValue val); +typedef void LEPUSClassGCMark(LEPUSRuntime *rt, LEPUSValueConst val, + LEPUS_MarkFunc *mark_func, int local_idx); +#define LEPUS_CALL_FLAG_CONSTRUCTOR (1 << 0) +typedef LEPUSValue LEPUSClassCall(LEPUSContext *ctx, LEPUSValueConst func_obj, + LEPUSValueConst this_val, int argc, + LEPUSValueConst *argv, int flags); +typedef struct LEPUSClassDef { + const char *class_name; + LEPUSClassFinalizer *finalizer; + LEPUSClassGCMark *gc_mark; + /* if call != NULL, the object is a function. If (flags & + JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a + constructor. In this case, 'this_val' is new.target. A + constructor call only happens if the object constructor bit is + set (see JS_SetConstructorBit()). */ + LEPUSClassCall *call; + /* XXX: suppress this indirection ? It is here only to save memory + because only a few classes need these methods */ + LEPUSClassExoticMethods *exotic; +} LEPUSClassDef; + +LEPUSClassID LEPUS_NewClassID(LEPUSClassID *pclass_id); +int LEPUS_NewClass(LEPUSRuntime *rt, LEPUSClassID class_id, + const LEPUSClassDef *class_def); +int LEPUS_IsRegisteredClass(LEPUSRuntime *rt, LEPUSClassID class_id); + +/* value handling */ + +static lepus_force_inline LEPUSValue LEPUS_NewBool(LEPUSContext *ctx, + LEPUS_BOOL val) { +#if defined(__aarch64__) && !defined(OS_WIN) && !defined(CONFIG_BIGNUM) && \ + !DISABLE_NANBOX + return LEPUS_MKVAL(LEPUS_TAG_BOOL, val != 0); +#else + return LEPUS_MKVAL(LEPUS_TAG_BOOL, val); +#endif +} + +static lepus_force_inline LEPUSValue LEPUS_NewInt32(LEPUSContext *ctx, + int32_t val) { + return LEPUS_MKVAL(LEPUS_TAG_INT, val); +} + +static lepus_force_inline LEPUSValue LEPUS_NewCatchOffset(LEPUSContext *ctx, + int32_t val) { + return LEPUS_MKVAL(LEPUS_TAG_CATCH_OFFSET, val); +} + +LEPUSValue LEPUS_NewInt64(LEPUSContext *ctx, int64_t v); +LEPUSValue LEPUS_NewBigInt64(LEPUSContext *ctx, int64_t v); +LEPUSValue LEPUS_NewBigUint64(LEPUSContext *ctx, uint64_t v); + +static lepus_force_inline LEPUSValue LEPUS_NewFloat64(LEPUSContext *ctx, + double d) { + LEPUSValue v; + int32_t val; + union { + double d; + uint64_t u; + } u, t; + u.d = d; + val = (int32_t)d; + t.d = val; + /* -0 cannot be represented as integer, so we compare the bit + representation */ + if (u.u == t.u) { + v = LEPUS_MKVAL(LEPUS_TAG_INT, val); + } else { + v = __JS_NewFloat64(ctx, d); + } + return v; +} + +LEPUS_BOOL LEPUS_IsNumber(LEPUSValueConst v); + +static inline LEPUS_BOOL LEPUS_IsInteger(LEPUSValueConst v) { + return LEPUS_VALUE_IS_INT(v) || LEPUS_VALUE_IS_BIG_INT(v); +} + +#ifdef CONFIG_BIGNUM +static inline LEPUS_BOOL LEPUS_IsBigFloat(LEPUSValueConst v) { + int tag = LEPUS_VALUE_GET_TAG(v); + return tag == LEPUS_TAG_BIG_FLOAT; +} +#endif + +static inline LEPUS_BOOL LEPUS_IsBool(LEPUSValueConst v) { + return LEPUS_VALUE_IS_BOOL(v); +} + +static inline LEPUS_BOOL LEPUS_IsNull(LEPUSValueConst v) { + return LEPUS_VALUE_IS_NULL(v); +} + +static inline LEPUS_BOOL LEPUS_IsUndefined(LEPUSValueConst v) { + return LEPUS_VALUE_IS_UNDEFINED(v); +} + +static inline LEPUS_BOOL LEPUS_IsException(LEPUSValueConst v) { + return (LEPUS_BOOL)(lepus_unlikely(LEPUS_VALUE_IS_EXCEPTION(v))); +} + +static inline LEPUS_BOOL LEPUS_IsUninitialized(LEPUSValueConst v) { + return (LEPUS_BOOL)(lepus_unlikely(LEPUS_VALUE_IS_UNINITIALIZED(v))); +} + +static inline LEPUS_BOOL LEPUS_IsString(LEPUSValueConst v) { + return LEPUS_VALUE_IS_STRING(v) || LEPUS_VALUE_IS_SEPARABLE_STRING(v); +} + +static inline LEPUS_BOOL LEPUS_IsSymbol(LEPUSValueConst v) { + return LEPUS_VALUE_IS_SYMBOL(v); +} + +static inline LEPUS_BOOL LEPUS_IsObject(LEPUSValueConst v) { + return LEPUS_VALUE_IS_OBJECT(v); +} + +LEPUSValue LEPUS_Throw(LEPUSContext *ctx, LEPUSValue obj); +LEPUSValue LEPUS_GetException(LEPUSContext *ctx); +LEPUS_BOOL LEPUS_IsError(LEPUSContext *ctx, LEPUSValueConst val); +#ifdef QJS_UNITTEST +void LEPUS_EnableIsErrorProperty(LEPUSContext *ctx, LEPUS_BOOL enable); +#endif +void LEPUS_ResetUncatchableError(LEPUSContext *ctx); +LEPUSValue LEPUS_NewError(LEPUSContext *ctx); +LEPUSValue __js_printf_like(2, 3) + LEPUS_ThrowSyntaxError(LEPUSContext *ctx, const char *fmt, ...); +LEPUSValue __js_printf_like(2, 3) + LEPUS_ThrowTypeError(LEPUSContext *ctx, const char *fmt, ...); +LEPUSValue __js_printf_like(2, 3) + LEPUS_ThrowReferenceError(LEPUSContext *ctx, const char *fmt, ...); +LEPUSValue __js_printf_like(2, 3) + LEPUS_ThrowRangeError(LEPUSContext *ctx, const char *fmt, ...); +LEPUSValue __js_printf_like(2, 3) + LEPUS_ThrowInternalError(LEPUSContext *ctx, const char *fmt, ...); +LEPUSValue LEPUS_ThrowOutOfMemory(LEPUSContext *ctx); + +void __JS_FreeValue(LEPUSContext *ctx, LEPUSValue v); +void LEPUS_FreeValue(LEPUSContext *ctx, LEPUSValue v); +QJS_HIDE void __JS_FreeValueRT(LEPUSRuntime *rt, LEPUSValue v); +void LEPUS_FreeValueRT(LEPUSRuntime *rt, LEPUSValue v); + +bool LEPUS_IsGCMode(LEPUSContext *ctx); +bool LEPUS_IsGCModeRT(LEPUSRuntime *rt); + +char *LEPUS_GetGCTimingInfo(LEPUSContext *ctx, bool is_start); + +void LEPUS_PushHandle(LEPUSContext *ctx, void *ptr, int type); +void LEPUS_ResetHandle(LEPUSContext *ctx, void *ptr, int type); + +static inline LEPUSValue LEPUS_DupValue(LEPUSContext *ctx, LEPUSValueConst v) { + if (LEPUS_VALUE_HAS_REF_COUNT(v)) { + LEPUSRefCountHeader *p = (LEPUSRefCountHeader *)LEPUS_VALUE_GET_PTR(v); + p->ref_count++; + } + return (LEPUSValue)v; +} + +// +// if no_lepus_strict_mode is set to true, these conditions will handle +// differently: if the object is null or undefined, read properties will return +// null if the object is null or undefined, write properties will not throw +// exception more info: +void LEPUS_SetNoStrictMode(LEPUSContext *ctx); +// + +// +void LEPUS_SetVirtualStackSize(LEPUSContext *ctx, uint32_t stack_size); +// + +static inline LEPUSValue LEPUS_DupValueRT(LEPUSRuntime *rt, LEPUSValueConst v) { + if (LEPUS_VALUE_HAS_REF_COUNT(v)) { + LEPUSRefCountHeader *p = (LEPUSRefCountHeader *)LEPUS_VALUE_GET_PTR(v); + p->ref_count++; + } + return (LEPUSValue)v; +} + +int LEPUS_ToBool(LEPUSContext *ctx, + LEPUSValueConst val); /* return -1 for LEPUS_EXCEPTION */ +int LEPUS_ToInt32(LEPUSContext *ctx, int32_t *pres, LEPUSValueConst val); +static int inline LEPUS_ToUint32(LEPUSContext *ctx, uint32_t *pres, + LEPUSValueConst val) { + return LEPUS_ToInt32(ctx, (int32_t *)pres, val); +} +int LEPUS_ToInt64(LEPUSContext *ctx, int64_t *pres, LEPUSValueConst val); +int LEPUS_ToIndex(LEPUSContext *ctx, uint64_t *plen, LEPUSValueConst val); +int LEPUS_ToFloat64(LEPUSContext *ctx, double *pres, LEPUSValueConst val); +int LEPUS_ToBigInt64(LEPUSContext *ctx, int64_t *pres, LEPUSValueConst val); + +LEPUSValue LEPUS_NewStringLen(LEPUSContext *ctx, const char *str1, size_t len1); +LEPUSValue LEPUS_NewString(LEPUSContext *ctx, const char *str); +LEPUSValue LEPUS_NewAtomString(LEPUSContext *ctx, const char *str); +LEPUSValue LEPUS_ToString(LEPUSContext *ctx, LEPUSValueConst val); +LEPUSValue LEPUS_ToPropertyKey(LEPUSContext *ctx, LEPUSValueConst val); +LEPUSValue LEPUS_ToObject(LEPUSContext *ctx, LEPUSValueConst val); +const char *LEPUS_ToCStringLen2(LEPUSContext *ctx, size_t *plen, + LEPUSValueConst val1, LEPUS_BOOL cesu8); +static inline const char *LEPUS_ToCStringLen(LEPUSContext *ctx, size_t *plen, + LEPUSValueConst val1) { + return LEPUS_ToCStringLen2(ctx, plen, val1, 0); +} +static inline const char *LEPUS_ToCString(LEPUSContext *ctx, + LEPUSValueConst val1) { + return LEPUS_ToCStringLen2(ctx, NULL, val1, 0); +} +void LEPUS_FreeCString(LEPUSContext *ctx, const char *ptr); + +LEPUSValue LEPUS_NewObjectProtoClass(LEPUSContext *ctx, LEPUSValueConst proto, + LEPUSClassID class_id); +LEPUSValue LEPUS_NewObjectClass(LEPUSContext *ctx, int class_id); +LEPUSValue LEPUS_NewObjectProto(LEPUSContext *ctx, LEPUSValueConst proto); +LEPUSValue LEPUS_NewObject(LEPUSContext *ctx); + +LEPUS_BOOL LEPUS_IsFunction(LEPUSContext *ctx, LEPUSValueConst val); +LEPUS_BOOL LEPUS_IsConstructor(LEPUSContext *ctx, LEPUSValueConst val); +LEPUS_BOOL LEPUS_SetConstructorBit(LEPUSContext *ctx, LEPUSValueConst func_obj, + LEPUS_BOOL val); + +LEPUSValue LEPUS_NewArray(LEPUSContext *ctx); +int LEPUS_IsArray(LEPUSContext *ctx, LEPUSValueConst val); + +LEPUSValue LEPUS_GetPropertyInternal(LEPUSContext *ctx, LEPUSValueConst obj, + JSAtom prop, LEPUSValueConst receiver, + LEPUS_BOOL throw_ref_error); +static lepus_force_inline LEPUSValue LEPUS_GetProperty(LEPUSContext *ctx, + LEPUSValueConst this_obj, + JSAtom prop) { + return LEPUS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0); +} +LEPUSValue LEPUS_GetPropertyStr(LEPUSContext *ctx, LEPUSValueConst this_obj, + const char *prop); +LEPUSValue LEPUS_GetPropertyUint32(LEPUSContext *ctx, LEPUSValueConst this_obj, + uint32_t idx); + +int LEPUS_SetPropertyInternal(LEPUSContext *ctx, LEPUSValueConst this_obj, + JSAtom prop, LEPUSValue val, int flags); +static inline int LEPUS_SetProperty(LEPUSContext *ctx, LEPUSValueConst this_obj, + JSAtom prop, LEPUSValue val) { + return LEPUS_SetPropertyInternal(ctx, this_obj, prop, val, LEPUS_PROP_THROW); +} +int LEPUS_SetPropertyUint32(LEPUSContext *ctx, LEPUSValueConst this_obj, + uint32_t idx, LEPUSValue val); +int LEPUS_SetPropertyInt64(LEPUSContext *ctx, LEPUSValueConst this_obj, + int64_t idx, LEPUSValue val); +int LEPUS_SetPropertyStr(LEPUSContext *ctx, LEPUSValueConst this_obj, + const char *prop, LEPUSValue val); +int LEPUS_HasProperty(LEPUSContext *ctx, LEPUSValueConst this_obj, JSAtom prop); +int LEPUS_IsExtensible(LEPUSContext *ctx, LEPUSValueConst obj); +int LEPUS_PreventExtensions(LEPUSContext *ctx, LEPUSValueConst obj); +int LEPUS_DeleteProperty(LEPUSContext *ctx, LEPUSValueConst obj, JSAtom prop, + int flags); +int LEPUS_SetPrototype(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSValueConst proto_val); +LEPUSValueConst LEPUS_GetPrototype(LEPUSContext *ctx, LEPUSValueConst val); + +#define LEPUS_GPN_STRING_MASK (1 << 0) +#define LEPUS_GPN_SYMBOL_MASK (1 << 1) +#define LEPUS_GPN_PRIVATE_MASK (1 << 2) +/* only include the enumerable properties */ +#define LEPUS_GPN_ENUM_ONLY (1 << 4) +/* set theJSPropertyEnum.is_enumerable field */ +#define LEPUS_GPN_SET_ENUM (1 << 5) + +int LEPUS_GetOwnPropertyNames(LEPUSContext *ctx, LEPUSPropertyEnum **ptab, + uint32_t *plen, LEPUSValueConst obj, int flags); +int LEPUS_GetOwnProperty(LEPUSContext *ctx, LEPUSPropertyDescriptor *desc, + LEPUSValueConst obj, JSAtom prop); + +LEPUSValue LEPUS_ParseJSON(LEPUSContext *ctx, const char *buf, size_t buf_len, + const char *filename); +LEPUSValue LEPUS_Call(LEPUSContext *ctx, LEPUSValueConst func_obj, + LEPUSValueConst this_obj, int argc, + LEPUSValueConst *argv); +LEPUSValue LEPUS_Invoke(LEPUSContext *ctx, LEPUSValueConst this_val, + JSAtom atom, int argc, LEPUSValueConst *argv); +LEPUSValue LEPUS_CallConstructor(LEPUSContext *ctx, LEPUSValueConst func_obj, + int argc, LEPUSValueConst *argv); +LEPUSValue LEPUS_CallConstructor2(LEPUSContext *ctx, LEPUSValueConst func_obj, + LEPUSValueConst new_target, int argc, + LEPUSValueConst *argv); +LEPUSValue LEPUS_Eval(LEPUSContext *ctx, const char *input, size_t input_len, + const char *filename, int eval_flags); +#define LEPUS_EVAL_BINARY_LOAD_ONLY (1 << 0) /* only load the module */ +LEPUSValue LEPUS_EvalBinary(LEPUSContext *ctx, const uint8_t *buf, + size_t buf_len, int flags); +LEPUSValue LEPUS_GetGlobalObject(LEPUSContext *ctx); + +/* trace gc begin */ +void LEPUS_SetGCPauseSuppressionMode(LEPUSRuntime *rt, bool mode); +bool LEPUS_GetGCPauseSuppressionMode(LEPUSRuntime *rt); + +void DisposeGlobal(LEPUSRuntime *runtime, LEPUSValue *global_handle); +LEPUSValue *GlobalizeReference(LEPUSRuntime *runtime, LEPUSValue val, + bool is_weak); + +void *AllocateQJSValueValue(LEPUSRuntime *runtime); +void FreeQJSValueValue(LEPUSRuntime *runtime, void *instance); + +void SetGlobalWeak(LEPUSRuntime *runtime, LEPUSValue *global_handle, void *data, + void (*cb)(void *)); +void ClearGlobalWeak(LEPUSRuntime *runtime, LEPUSValue *global_handle); +void SetWeakState(LEPUSRuntime *runtime, LEPUSValue *global_handle); + +void *GetNapiScope(LEPUSContext *ctx); +void SetNapiScope(LEPUSContext *ctx, void *scope); +void InitNapiScope(LEPUSContext *ctx); +void FreeNapiScope(LEPUSContext *ctx); + +void LEPUS_VisitLEPUSValue(LEPUSRuntime *rt, LEPUSValue *val, int local_idx); + +void AddCurNode(LEPUSRuntime *rt, void *node, int type); + +bool CheckValidPtr(void *runtime, void *ptr); + +void LEPUS_TrigGC(LEPUSRuntime *rt); +/* trace gc end*/ + +LEPUSValue LEPUS_GetGlobalVar(LEPUSContext *ctx, JSAtom prop, + LEPUS_BOOL throw_ref_error); +// +// #ifdef ENABLE_LEPUSNG +/* flag = 0: normal variable write + flag = 1: initialize lexical variable + flag = 2: normal variable write, strict check was done before +*/ +typedef void (*IterateObject)(LEPUSContext *ctx, LEPUSValue key, + LEPUSValue value, void *p, void *raw_data); +void LEPUS_SetStringCache(LEPUSContext *ctx, LEPUSValue val, void *p); +void *LEPUS_GetStringCache(LEPUSValue val); +void *LEPUS_GetStringCache_GC(LEPUSValue val); +int LEPUS_SetGlobalVar(LEPUSContext *ctx, JSAtom prop, LEPUSValue val, + int flag); +LEPUSValue LEPUS_DeepEqual(LEPUSContext *ctx, LEPUSValueConst obj1, + LEPUSValueConst obj2); +LEPUSValue LEPUS_DeepCopy(LEPUSContext *ctx, LEPUSValueConst val); +void LEPUS_IterateObject(LEPUSContext *ctx, LEPUSValue obj, + IterateObject callback, void *p, void *raw_data); +int LEPUS_GetLength(LEPUSContext *ctx, LEPUSValue val); + +LEPUSValue LEPUS_NewLepusWrap(LEPUSContext *ctx, void *p, int tag); +// check if has lepus refs count header +static inline LEPUS_BOOL LEPUS_IsLepusRef(LEPUSValue val) { + return LEPUS_VALUE_IS_LEPUS_REF(val); +} + +static inline int LEPUS_GetLepusRefTag(LEPUSValue val) { + if (!LEPUS_IsLepusRef(val)) return -1; + LEPUSLepusRef *pref = (LEPUSLepusRef *)(LEPUS_VALUE_GET_PTR(val)); + return pref->tag; +} + +static inline void *LEPUS_GetLepusRefPoint(LEPUSValue val) { + if (!LEPUS_IsLepusRef(val)) return NULL; + LEPUSLepusRef *pref = (LEPUSLepusRef *)(LEPUS_VALUE_GET_PTR(val)); + return pref->p; +} + +// #define CONFIG_BIGNUM +// #endif +// +int LEPUS_IsInstanceOf(LEPUSContext *ctx, LEPUSValueConst val, + LEPUSValueConst obj); +int LEPUS_DefineProperty(LEPUSContext *ctx, LEPUSValueConst this_obj, + JSAtom prop, LEPUSValueConst val, + LEPUSValueConst getter, LEPUSValueConst setter, + int flags); +// It is recommended to call LEPUS_DupValue to refer the original LEPUSValue val +// before calling the following define methods, otherwise it may crash +int LEPUS_DefinePropertyValue(LEPUSContext *ctx, LEPUSValueConst this_obj, + JSAtom prop, LEPUSValue val, int flags); +int LEPUS_DefinePropertyValueUint32(LEPUSContext *ctx, LEPUSValueConst this_obj, + uint32_t idx, LEPUSValue val, int flags); +int LEPUS_DefinePropertyValueStr(LEPUSContext *ctx, LEPUSValueConst this_obj, + const char *prop, LEPUSValue val, int flags); +int LEPUS_DefinePropertyGetSet(LEPUSContext *ctx, LEPUSValueConst this_obj, + JSAtom prop, LEPUSValue getter, + LEPUSValue setter, int flags); +void LEPUS_SetOpaque(LEPUSValue obj, void *opaque); +void *LEPUS_GetOpaque(LEPUSValueConst obj, LEPUSClassID class_id); +void *LEPUS_GetOpaque2(LEPUSContext *ctx, LEPUSValueConst obj, + LEPUSClassID class_id); + +typedef void LEPUSFreeArrayBufferDataFunc(LEPUSRuntime *rt, void *opaque, + void *ptr); +LEPUSValue LEPUS_NewArrayBuffer(LEPUSContext *ctx, uint8_t *buf, size_t len, + LEPUSFreeArrayBufferDataFunc *free_func, + void *opaque, LEPUS_BOOL is_shared); +LEPUSValue LEPUS_NewArrayBufferCopy(LEPUSContext *ctx, const uint8_t *buf, + size_t len); +void LEPUS_DetachArrayBuffer(LEPUSContext *ctx, LEPUSValueConst obj); +uint8_t *LEPUS_GetArrayBuffer(LEPUSContext *ctx, size_t *psize, + LEPUSValueConst obj); +LEPUSValue LEPUS_GetTypedArrayBuffer(LEPUSContext *ctx, LEPUSValueConst obj, + size_t *pbyte_offset, size_t *pbyte_length, + size_t *pbytes_per_element); + +// +LEPUS_BOOL LEPUS_IsArrayBuffer(LEPUSValueConst v); +LEPUSTypedArrayType LEPUS_GetTypedArrayType(LEPUSContext *ctx, + LEPUSValueConst val); +LEPUS_BOOL LEPUS_IsDataView(LEPUSContext *ctx, LEPUSValueConst val); +LEPUS_BOOL LEPUS_IsTypedArray(LEPUSContext *ctx, LEPUSValueConst val); + +LEPUSClassID LEPUS_GetTypedArrayClassID(LEPUSTypedArrayType type); + +// must be freed by lepus_free() / lepus_free_rt() +uint8_t *LEPUS_MoveArrayBuffer(LEPUSContext *, size_t *, LEPUSValue); +LEPUS_BOOL LEPUS_StrictEq(LEPUSContext *ctx, LEPUSValueConst op1, + LEPUSValueConst op2); +LEPUS_BOOL LEPUS_SameValue(LEPUSContext *ctx, LEPUSValueConst op1, + LEPUSValueConst op2); +// + +LEPUSValue LEPUS_NewPromiseCapability(LEPUSContext *ctx, + LEPUSValue *resolving_funcs); + +/* return != 0 if the LEPUS code needs to be interrupted */ +typedef int LEPUSInterruptHandler(LEPUSRuntime *rt, void *opaque); +void LEPUS_SetInterruptHandler(LEPUSRuntime *rt, LEPUSInterruptHandler *cb, + void *opaque); +/* if can_block is TRUE, Atomics.wait() can be used */ +void LEPUS_SetCanBlock(LEPUSRuntime *rt, LEPUS_BOOL can_block); + +typedef struct LEPUSModuleDef LEPUSModuleDef; + +/* return the module specifier (allocated with lepus_malloc()) or NULL if + exception */ +typedef char *LEPUSModuleNormalizeFunc(LEPUSContext *ctx, + const char *module_base_name, + const char *module_name, void *opaque); +typedef LEPUSModuleDef *LEPUSModuleLoaderFunc(LEPUSContext *ctx, + const char *module_name, + void *opaque); + +/* module_normalize = NULL is allowed and invokes the default module + filename normalizer */ +void LEPUS_SetModuleLoaderFunc(LEPUSRuntime *rt, + LEPUSModuleNormalizeFunc *module_normalize, + LEPUSModuleLoaderFunc *module_loader, + void *opaque); + +/* LEPUS Job support */ + +typedef LEPUSValue LEPUSJobFunc(LEPUSContext *ctx, int argc, + LEPUSValueConst *argv); +int LEPUS_EnqueueJob(LEPUSContext *ctx, LEPUSJobFunc *job_func, int argc, + LEPUSValueConst *argv); + +LEPUS_BOOL LEPUS_IsJobPending(LEPUSRuntime *rt); +int LEPUS_ExecutePendingJob(LEPUSRuntime *rt, LEPUSContext **pctx); + +uint64_t LEPUS_GetPrimjsVersion(); + +/* Object Writer/Reader (currently only used to handle precompiled code) */ +#define LEPUS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */ +#define LEPUS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */ +uint8_t *LEPUS_WriteObject(LEPUSContext *ctx, size_t *psize, + LEPUSValueConst obj, int flags); +#define LEPUS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */ +#define LEPUS_READ_OBJ_ROM_DATA (1 << 1) /* avoid duplicating 'buf' data */ +LEPUSValue LEPUS_ReadObject(LEPUSContext *ctx, const uint8_t *buf, + size_t buf_len, int flags); +LEPUSValue LEPUS_EvalFunction(LEPUSContext *ctx, LEPUSValue fun_obj, + LEPUSValueConst this_obj); + +LEPUSValue LEPUS_NewWString(LEPUSContext *, const uint16_t *, size_t length); +LEPUSValue LEPUS_FromJSON(LEPUSContext *, const char *); +LEPUSValue LEPUS_ToJSON(LEPUSContext *, LEPUSValueConst, int indent); +LEPUSValue LEPUS_ToWString(LEPUSContext *, LEPUSValueConst); +JSAtom LEPUS_ValueToAtom(LEPUSContext *ctx, LEPUSValueConst val); + +const uint16_t *LEPUS_GetStringChars(LEPUSContext *, LEPUSValueConst); +uint32_t LEPUS_GetStringLength(LEPUSContext *, LEPUSValueConst); +LEPUSClassID LEPUS_GetClassID(LEPUSContext *, LEPUSValueConst); +LEPUSValue LEPUS_NewArrayWithValue(LEPUSContext *, uint32_t length, + LEPUSValueConst *values); +LEPUSValue LEPUS_NewTypedArray(LEPUSContext *, uint32_t length, LEPUSClassID); +LEPUSValue LEPUS_NewTypedArrayWithBuffer(LEPUSContext *, LEPUSValueConst buffer, + uint32_t byteOffset, uint32_t length, + LEPUSClassID class_id); +LEPUSValue LEPUS_CallV(LEPUSContext *ctx, LEPUSValueConst func_obj, + LEPUSValueConst this_obj, int argc, LEPUSValue *argv); +LEPUSValue LEPUS_CallConstructorV(LEPUSContext *ctx, LEPUSValueConst func_obj, + int argc, LEPUSValue *argv); + +/* C function definition */ +typedef enum LEPUSCFunctionEnum { /* XXX: should rename for namespace isolation + */ + LEPUS_CFUNC_generic, + LEPUS_CFUNC_generic_magic, + LEPUS_CFUNC_constructor, + LEPUS_CFUNC_constructor_magic, + LEPUS_CFUNC_constructor_or_func, + LEPUS_CFUNC_constructor_or_func_magic, + LEPUS_CFUNC_f_f, + LEPUS_CFUNC_f_f_f, + LEPUS_CFUNC_getter, + LEPUS_CFUNC_setter, + LEPUS_CFUNC_getter_magic, + LEPUS_CFUNC_setter_magic, + LEPUS_CFUNC_iterator_next, +} LEPUSCFunctionEnum; + +typedef union LEPUSCFunctionType { + LEPUSCFunction *generic; + LEPUSValue (*generic_magic)(LEPUSContext *ctx, LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv, int magic); + LEPUSCFunction *constructor; + LEPUSValue (*constructor_magic)(LEPUSContext *ctx, LEPUSValueConst new_target, + int argc, LEPUSValueConst *argv, int magic); + LEPUSCFunction *constructor_or_func; + double (*f_f)(double); + double (*f_f_f)(double, double); + LEPUSValue (*getter)(LEPUSContext *ctx, LEPUSValueConst this_val); + LEPUSValue (*setter)(LEPUSContext *ctx, LEPUSValueConst this_val, + LEPUSValueConst val); + LEPUSValue (*getter_magic)(LEPUSContext *ctx, LEPUSValueConst this_val, + int magic); + LEPUSValue (*setter_magic)(LEPUSContext *ctx, LEPUSValueConst this_val, + LEPUSValueConst val, int magic); + LEPUSValue (*iterator_next)(LEPUSContext *ctx, LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv, int *pdone, + int magic); +} LEPUSCFunctionType; + +LEPUSValue LEPUS_NewCFunction2(LEPUSContext *ctx, LEPUSCFunction *func, + const char *name, int length, + LEPUSCFunctionEnum cproto, int magic); +LEPUSValue LEPUS_NewCFunctionData(LEPUSContext *ctx, LEPUSCFunctionData *func, + int length, int magic, int data_len, + LEPUSValueConst *data); + +static inline LEPUSValue LEPUS_NewCFunction(LEPUSContext *ctx, + LEPUSCFunction *func, + const char *name, int length) { + return LEPUS_NewCFunction2(ctx, func, name, length, LEPUS_CFUNC_generic, 0); +} + +static inline LEPUSValue LEPUS_NewCFunctionMagic(LEPUSContext *ctx, + LEPUSCFunctionMagic *func, + const char *name, int length, + LEPUSCFunctionEnum cproto, + int magic) { + return LEPUS_NewCFunction2(ctx, (LEPUSCFunction *)func, name, length, cproto, + magic); +} + +/* C property definition */ + +typedef struct LEPUSCFunctionListEntry { + const char *name; + uint8_t prop_flags; + uint8_t def_type; + int16_t magic; + union { + struct { + uint8_t length; /* XXX: should move outside union */ + uint8_t cproto; /* XXX: should move outside union */ + LEPUSCFunctionType cfunc; + } func; + struct { + LEPUSCFunctionType get; + LEPUSCFunctionType set; + } getset; + struct { + const char *name; + int base; + } alias; + struct { + const struct LEPUSCFunctionListEntry *tab; + int len; + } prop_list; + const char *str; + int32_t i32; + int64_t i64; + double f64; + } u; +} LEPUSCFunctionListEntry; + +#define LEPUS_DEF_CFUNC 0 +#define LEPUS_DEF_CGETSET 1 +#define LEPUS_DEF_CGETSET_MAGIC 2 +#define LEPUS_DEF_PROP_STRING 3 +#define LEPUS_DEF_PROP_INT32 4 +#define LEPUS_DEF_PROP_INT64 5 +#define LEPUS_DEF_PROP_DOUBLE 6 +#define LEPUS_DEF_PROP_UNDEFINED 7 +#define LEPUS_DEF_OBJECT 8 +#define LEPUS_DEF_ALIAS 9 + +#define LEPUS_CFUNC_DEF(name, length, func1) \ + { \ + name, LEPUS_PROP_WRITABLE | LEPUS_PROP_CONFIGURABLE, LEPUS_DEF_CFUNC, 0, \ + .u.func = { \ + length, \ + LEPUS_CFUNC_generic, \ + {.generic = func1} \ + } \ + } +#define LEPUS_CFUNC_MAGIC_DEF(name, length, func1, magic) \ + { \ + name, LEPUS_PROP_WRITABLE | LEPUS_PROP_CONFIGURABLE, LEPUS_DEF_CFUNC, \ + magic, .u.func = { \ + length, \ + LEPUS_CFUNC_generic_magic, \ + {.generic_magic = func1} \ + } \ + } +#define LEPUS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) \ + { \ + name, LEPUS_PROP_WRITABLE | LEPUS_PROP_CONFIGURABLE, LEPUS_DEF_CFUNC, 0, \ + .u.func = { \ + length, \ + LEPUS_CFUNC_##cproto, \ + {.cproto = func1} \ + } \ + } +#define LEPUS_ITERATOR_NEXT_DEF(name, length, func1, magic) \ + { \ + name, LEPUS_PROP_WRITABLE | LEPUS_PROP_CONFIGURABLE, LEPUS_DEF_CFUNC, \ + magic, .u.func = { \ + length, \ + LEPUS_CFUNC_iterator_next, \ + {.iterator_next = func1} \ + } \ + } +#define LEPUS_CGETSET_DEF(name, fgetter, fsetter) \ + { \ + name, LEPUS_PROP_CONFIGURABLE, LEPUS_DEF_CGETSET, 0, \ + .u.getset.get.getter = fgetter, .u.getset.set.setter = fsetter \ + } +#define LEPUS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) \ + { \ + name, LEPUS_PROP_CONFIGURABLE, LEPUS_DEF_CGETSET_MAGIC, magic, \ + .u.getset.get.getter_magic = fgetter, \ + .u.getset.set.setter_magic = fsetter \ + } +#define LEPUS_PROP_STRING_DEF(name, cstr, prop_flags) \ + { name, prop_flags, LEPUS_DEF_PROP_STRING, 0, .u.str = cstr } +#define LEPUS_PROP_INT32_DEF(name, val, prop_flags) \ + { name, prop_flags, LEPUS_DEF_PROP_INT32, 0, .u.i32 = val } +#define LEPUS_PROP_INT64_DEF(name, val, prop_flags) \ + { name, prop_flags, LEPUS_DEF_PROP_INT64, 0, .u.i64 = val } +#define LEPUS_PROP_DOUBLE_DEF(name, val, prop_flags) \ + { name, prop_flags, LEPUS_DEF_PROP_DOUBLE, 0, .u.f64 = val } +#define LEPUS_PROP_UNDEFINED_DEF(name, prop_flags) \ + { name, prop_flags, LEPUS_DEF_PROP_UNDEFINED, 0, .u.i32 = 0 } +#define LEPUS_OBJECT_DEF(name, tab, len, prop_flags) \ + { \ + name, prop_flags, LEPUS_DEF_OBJECT, 0, .u.prop_list = { tab, len } \ + } +#define LEPUS_ALIAS_DEF(name, from) \ + { \ + name, LEPUS_PROP_WRITABLE | LEPUS_PROP_CONFIGURABLE, LEPUS_DEF_ALIAS, 0, \ + .u.alias = { \ + from, \ + -1 \ + } \ + } +#define LEPUS_ALIAS_BASE_DEF(name, from, base) \ + { \ + name, LEPUS_PROP_WRITABLE | LEPUS_PROP_CONFIGURABLE, LEPUS_DEF_ALIAS, 0, \ + .u.alias = { \ + from, \ + base \ + } \ + } + +void LEPUS_SetPropertyFunctionList(LEPUSContext *ctx, LEPUSValueConst obj, + const LEPUSCFunctionListEntry *tab, int len); + +/* C module definition */ + +typedef int LEPUSModuleInitFunc(LEPUSContext *ctx, LEPUSModuleDef *m); + +LEPUSModuleDef *LEPUS_NewCModule(LEPUSContext *ctx, const char *name_str, + LEPUSModuleInitFunc *func); +/* can only be called before the module is instantiated */ +QJS_HIDE int LEPUS_AddModuleExport(LEPUSContext *ctx, LEPUSModuleDef *m, + const char *name_str); +QJS_HIDE int LEPUS_AddModuleExportList(LEPUSContext *ctx, LEPUSModuleDef *m, + const LEPUSCFunctionListEntry *tab, + int len); +/* can only be called after the module is instantiated */ +int LEPUS_SetModuleExport(LEPUSContext *ctx, LEPUSModuleDef *m, + const char *export_name, LEPUSValue val); +int LEPUS_SetModuleExportList(LEPUSContext *ctx, LEPUSModuleDef *m, + const LEPUSCFunctionListEntry *tab, int len); + +/* expose for napi use */ +LEPUSValue lepus_object_getOwnPropertyDescriptor(LEPUSContext *ctx, + LEPUSValueConst this_val, + int argc, + LEPUSValueConst *argv, + int magic); + +// +QJS_EXPORT_FOR_DEVTOOL int64_t find_line_num(LEPUSContext *ctx, + struct LEPUSFunctionBytecode *b, + uint32_t pc_value); + +QJS_EXPORT_FOR_DEVTOOL int lepus_class_has_bytecode(LEPUSClassID class_id); + +QJS_EXPORT_FOR_DEVTOOL const char *get_func_name(LEPUSContext *ctx, + LEPUSValueConst func); + +QJS_EXPORT_FOR_DEVTOOL int get_leb128_u64(uint64_t *pval, const uint8_t *buf, + const uint8_t *buf_end); + +QJS_EXPORT_FOR_DEVTOOL int get_sleb128_u64(int64_t *pval, const uint8_t *buf, + const uint8_t *buf_end); +QJS_EXPORT_FOR_DEVTOOL __attribute__((warn_unused_result)) int +lepus_get_length32(LEPUSContext *ctx, uint32_t *pres, LEPUSValueConst obj); +void SetLynxTargetSdkVersion(LEPUSContext *ctx, const char *version); + +/* + register lepus::Value::ValueType's value in qjs runtime for lepusng, + because these type value are used in qjs +*/ +void LEPUS_RegisterNgType(LEPUSRuntime *, int32_t *, uint32_t size); + +/* +when an object of type lepus::RefCounted is destructed, + call this function to free all MapRecords +*/ +void LEPUS_FreeRefCountedWeakRef(LEPUSRuntime *, struct JSMapRecord *); + +LEPUS_BOOL LEPUS_IsPrimjsEnabled(LEPUSRuntime *rt); + +/* + This function doesn't change the rc of value; +*/ +LEPUSValue LEPUS_NewObjectWithArgs(LEPUSContext *, int32_t size, const char **, + LEPUSValue *); +/* + This function doesn't change the rc of value; +*/ + +LEPUSValue LEPUS_NewArrayWithArgs(LEPUSContext *, int32_t, LEPUSValue *); + +/* + If input is an ASCII string, return pointer to string, otherwise + return nullptr; +*/ +const char *LEPUS_GetStringUtf8(LEPUSContext *, const struct JSString *); +void LEPUS_SetFuncFileName(LEPUSContext *, LEPUSValue, const char *); + +void InitLynxTraceEnv(void *(*)(const char *), void (*)(void *)); +// + +#undef lepus_unlikely +#undef lepus_force_inline + +#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_H_ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/quickjs_queue.h b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs_queue.h new file mode 100644 index 000000000..b19956fdd --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs_queue.h @@ -0,0 +1,54 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_QUEUE_H_ +#define SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_QUEUE_H_ + +#include + +#define DEFAULT_INIT_SIZE 2048 + +#ifdef ENABLE_GC_DEBUG_TOOLS +#include +#ifdef __cplusplus +extern "C" { +#endif +bool check_valid_ptr(void *runtime, void *ptr); +#ifdef __cplusplus +} +#endif +#endif + +// rear is emptry, capacity == size - 1 +class Queue { + public: + Queue(void *runtime, int initial_size); + explicit Queue(void *runtime); + ~Queue(); + + bool IsEmpty(); + bool IsFull(); + void EnQueue(void *ptr); + void *DeQueue(); + void *Front(); + void ResizeQueue(); + // void Display(); + int GetCount() { return count; } + int GetSize() { return size; } + const void *GetQueue() { return queue; } + void Split(int cnt, Queue *q) { + for (int i = 0; i < cnt; i++) { + q->EnQueue(DeQueue()); + } + } + + private: + void **queue; + void *rt_; + int front; + int rear; + int count; + int size; +}; + +#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_QUEUE_H_ diff --git a/NativeScript/napi/android/primjs/include/quickjs/include/quickjs_version.h b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs_version.h new file mode 100644 index 000000000..8c7a32924 --- /dev/null +++ b/NativeScript/napi/android/primjs/include/quickjs/include/quickjs_version.h @@ -0,0 +1,21 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_VERSION_H_ +#define SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_VERSION_H_ + +#include +#include +#define FEATURE_LEPUSNG_DEBUGINFO_OUTSIDE "2.5" +#define PRIMJS_ADD_VERSION_CODE "2.14" + +typedef struct Version { + int major, minor, revision, build; +} Version; + +void VersionInit(Version* v, const char* version); +uint8_t VersionLessOrEqual(Version v1, Version other); +uint8_t IsHigherOrEqual(const char* targetV, const char* baseV); + +#endif // SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_VERSION_H_ diff --git a/NativeScript/napi/android/primjs/jsr.cpp b/NativeScript/napi/android/primjs/jsr.cpp new file mode 100644 index 000000000..47a7cc104 --- /dev/null +++ b/NativeScript/napi/android/primjs/jsr.cpp @@ -0,0 +1,111 @@ +#include "napi_env_quickjs.h" +#include "jsr.h" + +JSR::JSR() = default; +tns::SimpleMap JSR::env_to_jsr_cache; + +struct napi_runtime__ { + LEPUSRuntime* runtime; + LEPUSContext* context; +}; + +napi_status js_create_runtime(napi_runtime *runtime) { + auto _runtime = new napi_runtime__(); + LEPUSRuntime* rt = LEPUS_NewRuntimeWithMode(0); + LEPUS_SetRuntimeInfo(rt, "Lynx_LepusNG"); + _runtime->context = LEPUS_NewContext(rt); + LEPUS_SetMaxStackSize(_runtime->context, 1024 * 1024 * 1024); + _runtime->runtime = rt; + *runtime = _runtime; + + LEPUS_Eval( _runtime->context, "var a;", strlen("var a;"), "a", LEPUS_EVAL_TYPE_GLOBAL); + + + return napi_ok; +} +napi_status js_create_napi_env(napi_env *env, napi_runtime runtime) { + + *env = napi_new_env(); + napi_attach_quickjs((*env), runtime->context); + + + JSR::env_to_jsr_cache.Insert((*env), new JSR()); + LEPUS_Eval( runtime->context, "var a;", strlen("var a;"), "a", LEPUS_EVAL_TYPE_GLOBAL); + + const char *requireFactoryScript = "var a;"; + + napi_value source; + napi_create_string_utf8((*env), requireFactoryScript, strlen(requireFactoryScript), &source); + +// napi_value global; +// napi_get_global(env, &global); + + napi_value result; + napi_status status = js_execute_script((*env), source, "a", &result); + + return napi_ok; +} + +napi_status js_set_runtime_flags(const char *flags) { + return napi_ok; +} + +napi_status js_lock_env(napi_env env) { + auto jsr = JSR::env_to_jsr_cache.Get(env); + if (jsr) jsr->lock(); + return napi_ok; +} + +napi_status js_unlock_env(napi_env env) { + auto jsr = JSR::env_to_jsr_cache.Get(env); + if (jsr) jsr->unlock(); + + return napi_ok; +} + +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); + napi_detach_quickjs(env); + return napi_ok; +} + +napi_status js_free_runtime(napi_runtime runtime) { + LEPUS_FreeContext(runtime->context); + LEPUS_FreeRuntime(runtime->runtime); + return napi_ok; +} + +napi_status js_execute_script(napi_env env, + napi_value script, + const char *file, + napi_value *result) { + + return napi_run_script_source(env, script, file, result); +} + +napi_status js_execute_pending_jobs(napi_env env) { + return primjs_execute_pending_jobs(env); +} + +napi_status +js_adjust_external_memory(napi_env env, int64_t changeInBytes, int64_t *externalMemory) { + napi_adjust_external_memory(env, changeInBytes, externalMemory); + return napi_ok; +} + +napi_status js_cache_script(napi_env env, const char *source, const char *file) { + return napi_ok; +} + +napi_status js_run_cached_script(napi_env env, const char *file, napi_value script, void *cache, + napi_value *result) { + return napi_ok; +} + + +napi_status js_get_runtime_version(napi_env env, napi_value *version) { + napi_create_string_utf8(env, "PrimJS", NAPI_AUTO_LENGTH, version); + return napi_ok; +} diff --git a/NativeScript/napi/android/primjs/jsr.h b/NativeScript/napi/android/primjs/jsr.h new file mode 100644 index 000000000..2850370db --- /dev/null +++ b/NativeScript/napi/android/primjs/jsr.h @@ -0,0 +1,55 @@ +// +// Created by Ammar Ahmed on 01/12/2024. +// + +#ifndef TEST_APP_JSR_H +#define TEST_APP_JSR_H +#include "jsr_common.h" +#include "napi_env_quickjs.h" +#include "mutex" +#include +#include "ConcurrentMap.h" + +class JSR { +public: + JSR(); + std::recursive_mutex js_mutex; + void lock() { + js_mutex.lock(); + } + void unlock() { + js_mutex.unlock(); + } + + static tns::SimpleMap env_to_jsr_cache; +}; + +class NapiScope { +public: + explicit NapiScope(napi_env env, bool open_handle = true) + : env_(env) + { + js_lock_env(env_); + // TODO: UPDATE STACK TOP HERE + if (open_handle) { + napi_open_handle_scope(env_, &napiHandleScope_); + } else { + napiHandleScope_ = nullptr; + } + } + + ~NapiScope() { + if (napiHandleScope_) { + napi_close_handle_scope(env_, napiHandleScope_); + } + js_unlock_env(env_); + } + +private: + napi_env env_; + napi_handle_scope napiHandleScope_; +}; + +#define JSEnterScope + +#endif //TEST_APP_JSR_H diff --git a/NativeScript/napi/android/primjs/napi_env.cc b/NativeScript/napi/android/primjs/napi_env.cc new file mode 100644 index 000000000..287de1ed3 --- /dev/null +++ b/NativeScript/napi/android/primjs/napi_env.cc @@ -0,0 +1,170 @@ +/** + * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. + * + * Use of this source code is governed by a MIT license that can be + * found in the LICENSE file in the root of the source tree. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#include "napi_env.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "napi_env_quickjs.h" +#include "primjs-api.h" + +struct napi_env_data__ { + void AddCleanupHook(void (*fun)(void* arg), void* arg) { + cleanup_hooks.insert(CleanupHook{fun, arg, cleanup_hooks.size()}); + } + + void RemoveCleanupHook(void (*fun)(void* arg), void* arg) { + cleanup_hooks.erase(CleanupHook{fun, arg, 0}); + } + + ~napi_env_data__() { RunCleanup(); } + + struct CleanupHook { + void (*fun)(void*); + void* arg; + uint64_t insertion_order_counter; + + struct Equal { + bool operator()(const CleanupHook& lhs, const CleanupHook& rhs) const { + return lhs.fun == rhs.fun && lhs.arg == rhs.arg; + } + }; + + struct Hash { + std::size_t operator()(CleanupHook const& s) const { + std::size_t h1 = + std::hash{}(reinterpret_cast(s.fun)); + std::size_t h2 = + std::hash{}(reinterpret_cast(s.arg)); + return h1 ^ (h2 << 1); + } + }; + }; + + void RunCleanup() { + while (!cleanup_hooks.empty()) { + // Copy into a vector, since we can't sort an unordered_set in-place. + std::vector callbacks(cleanup_hooks.begin(), + cleanup_hooks.end()); + // We can't erase the copied elements from `cleanup_hooks_` yet, because + // we need to be able to check whether they were un-scheduled by another + // hook. + + std::sort(callbacks.begin(), callbacks.end(), + [](const CleanupHook& a, const CleanupHook& b) { + // Sort in descending order so that the most recently inserted + // callbacks are run first. + return a.insertion_order_counter > b.insertion_order_counter; + }); + + for (const auto& cb : callbacks) { + if (cleanup_hooks.count(cb) == 0) { + // This hook was removed from the `cleanup_hooks_` set during another + // hook that was run earlier. Nothing to do here. + continue; + } + + cb.fun(cb.arg); + cleanup_hooks.erase(cb); + } + } + } + + std::unordered_set + cleanup_hooks; +}; + +napi_status napi_get_version(napi_env env, uint32_t* version) { + *version = NAPI_VERSION_EXPERIMENTAL; + return napi_clear_last_error(env); +} + +napi_status napi_add_env_cleanup_hook(napi_env env, void (*fun)(void* arg), + void* arg) { + env->state.env_data->AddCleanupHook(fun, arg); + return napi_clear_last_error(env); +} + +napi_status napi_remove_env_cleanup_hook(napi_env env, void (*fun)(void* arg), + void* arg) { + env->state.env_data->RemoveCleanupHook(fun, arg); + return napi_clear_last_error(env); +} + +// Warning: Keep in-sync with napi_status enum +static const char* error_messages[] = { + nullptr, + "Invalid argument", + "An object was expected", + "A string was expected", + "A string or symbol was expected", + "A function was expected", + "A number was expected", + "A boolean was expected", + "An array was expected", + "Unknown failure", + "An exception is pending", + "The async work item was cancelled", + "napi_escape_handle already called on scope", + "Invalid handle scope usage", + "Invalid callback scope usage", + "Thread-safe function queue is full", + "Thread-safe function handle is closing", + "A bigint was expected", + "A date was expected", + "An arraybuffer was expected", + "A detachable arraybuffer was expected", + "Napi deadlock", + "napi_no_external_buffers_allowed", + "napi_cannot_run_js", + "napi_handle_scope_empty", + "napi_memory_error", + "napi_promise_exception"}; + +#define NAPI_ARRAYSIZE(array) (sizeof(array) / sizeof(array[0])) + +napi_status napi_get_last_error_info(napi_env env, + const napi_extended_error_info** result) { + // you must update this assert to reference the last message + // in the napi_status enum each time a new error message is added. + // We don't have a napi_status_last as this would result in an ABI + // change each time a message was added. + const int last_status = napi_promise_exception; + + static_assert(NAPI_ARRAYSIZE(error_messages) == last_status + 1, + "Count of error messages must match count of error values"); + assert(env->state.last_error.error_code <= last_status); + + // Wait until someone requests the last error information to fetch the error + // message string + env->state.last_error.error_message = + error_messages[env->state.last_error.error_code]; + + *result = &(env->state.last_error); + return napi_ok; +} + +napi_env napi_new_env() { + napi_env env = new napi_env__{}; + env->state.env_data = new napi_env_data__{}; + return env; +} + +void napi_free_env(napi_env env) { + delete env->state.env_data; + delete env; +} \ No newline at end of file diff --git a/NativeScript/napi/android/primjs/napi_env.h b/NativeScript/napi/android/primjs/napi_env.h new file mode 100644 index 000000000..9a123453b --- /dev/null +++ b/NativeScript/napi/android/primjs/napi_env.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. + * + * Use of this source code is governed by a MIT license that can be + * found in the LICENSE file in the root of the source tree. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_NAPI_ENV_NAPI_ENV_H_ +#define SRC_NAPI_ENV_NAPI_ENV_H_ + +//#include "js_native_api.h" +// +//EXTERN_C_START +// +//NAPI_EXTERN napi_env napi_new_env(); +// +//NAPI_EXTERN void napi_free_env(napi_env); +// +//NAPI_EXTERN void napi_setup_loader(napi_env env, const char* name); +// +//NAPI_EXTERN napi_status primjs_execute_pending_jobs(napi_env env); +// +//EXTERN_C_END + + +#endif // SRC_NAPI_ENV_NAPI_ENV_H_ diff --git a/NativeScript/napi/android/primjs/napi_env_quickjs.h b/NativeScript/napi/android/primjs/napi_env_quickjs.h new file mode 100644 index 000000000..88c860b97 --- /dev/null +++ b/NativeScript/napi/android/primjs/napi_env_quickjs.h @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. + * + * Use of this source code is governed by a MIT license that can be + * found in the LICENSE file in the root of the source tree. + */ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +#ifndef SRC_NAPI_QUICKJS_NAPI_ENV_QUICKJS_H_ +#define SRC_NAPI_QUICKJS_NAPI_ENV_QUICKJS_H_ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus +#include "quickjs/include/quickjs.h" +#ifdef __cplusplus +} +#endif // __cplusplus + +#include "js_native_api.h" +EXTERN_C_START + +NAPI_EXTERN napi_env napi_new_env(); + +NAPI_EXTERN void napi_free_env(napi_env); + +NAPI_EXTERN void napi_setup_loader(napi_env env, const char* name); + + +NAPI_EXTERN void napi_attach_quickjs(napi_env env, LEPUSContext* ctx); + +NAPI_EXTERN void napi_detach_quickjs(napi_env env); + +NAPI_EXTERN LEPUSContext* napi_get_env_context_quickjs(napi_env env); + +// return value should be freed by caller +NAPI_EXTERN LEPUSValue napi_js_value_to_quickjs_value(napi_env env, + napi_value value); + +// input value is freed by napi +// return value is only valid in current handle scope +NAPI_EXTERN napi_value napi_quickjs_value_to_js_value(napi_env env, + LEPUSValue value); + +NAPI_EXTERN napi_status primjs_execute_pending_jobs(napi_env env); + +EXTERN_C_END + +#endif // SRC_NAPI_QUICKJS_NAPI_ENV_QUICKJS_H_ diff --git a/NativeScript/napi/android/primjs/napi_state.h b/NativeScript/napi/android/primjs/napi_state.h new file mode 100644 index 000000000..20b82448f --- /dev/null +++ b/NativeScript/napi/android/primjs/napi_state.h @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. + * + * Use of this source code is governed by a MIT license that can be + * found in the LICENSE file in the root of the source tree. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef SRC_NAPI_COMMON_NAPI_STATE_H_ +#define SRC_NAPI_COMMON_NAPI_STATE_H_ + +//#include "primjs-api.h" + +//typedef struct napi_env_data__* napi_env_data; +// +//struct napi_state__ { +// napi_extended_error_info last_error; +// napi_env_data env_data; +//}; +// +//inline napi_status napi_clear_last_error(napi_env env) { +// env->state->last_error.error_code = napi_ok; +// +// env->state->last_error.engine_error_code = 0; +// return napi_ok; +//} +// +//inline napi_status napi_set_last_error(napi_env env, napi_status error_code) { +// env->state->last_error.error_code = error_code; +// return error_code; +//} + +#endif // SRC_NAPI_COMMON_NAPI_STATE_H_ diff --git a/NativeScript/napi/android/primjs/primjs-api.cc b/NativeScript/napi/android/primjs/primjs-api.cc new file mode 100644 index 000000000..9dc08f372 --- /dev/null +++ b/NativeScript/napi/android/primjs/primjs-api.cc @@ -0,0 +1,2881 @@ +/** + * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. + * + * Use of this source code is governed by a MIT license that can be + * found in the LICENSE file in the root of the source tree. + */ + +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +#include "primjs-api.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/log/logging.h" +#include "napi_env_quickjs.h" +#include "quickjs/include/quickjs-inner.h" + + +std::unordered_map napi_context__::rt_to_env_cache; +struct napi_callback_info__ { + napi_value newTarget; + napi_value thisArg; + napi_value *argv; + void *data; + uint16_t argc; +}; + +static inline void js_enter(napi_env env) { + env->js_enter_state++; +} + +static inline void js_exit(napi_env env) { + if (--env->js_enter_state <= 0) { + primjs_execute_pending_jobs(env); + } +} + +namespace { + napi_status napi_set_exception(napi_env env, LEPUSValue exception) { + if (env->ctx->last_exception) { + JS_FreeValue_Comp(env->ctx->ctx, *env->ctx->last_exception); + } + env->ctx->last_exception.reset(new LEPUSValue(exception)); + + env->ctx->last_exception_pVal.Reset(nullptr, exception, nullptr, + env->ctx->ctx, true); + + return napi_set_last_error(env, napi_pending_exception); + } + + napi_status napi_set_error_msg_code(napi_env env, napi_value error, + napi_value code, napi_value msg, + const char *code_cstring) { + { + LEPUSValue msg_value = JS_DupValue_Comp(env->ctx->ctx, ToJSValue(msg)); + env->ctx->CreateHandle(msg_value, true); + + CHECK_QJS(env, LEPUS_SetProperty(env->ctx->ctx, ToJSValue(error), + env->ctx->PROP_MESSAGE, msg_value) != -1); + } + + if (code || code_cstring) { + LEPUSValue code_value; + if (code == nullptr) { + code_value = LEPUS_NewString(env->ctx->ctx, code_cstring); + env->ctx->CreateHandle(code_value, true); + } else { + code_value = ToJSValue(code); + RETURN_STATUS_IF_FALSE(env, + LEPUS_IsString(code_value) || LEPUS_IsUndefined(code_value), + napi_string_expected); + + code_value = JS_DupValue_Comp(env->ctx->ctx, code_value); + } + + if (!LEPUS_IsUndefined(code_value)) { + CHECK_QJS(env, LEPUS_SetProperty(env->ctx->ctx, ToJSValue(error), + env->ctx->PROP_CODE, code_value) != -1); + } + + } + + return napi_ok; + } + + template + class ArgsConverter { + public: + ArgsConverter(size_t argc, In *argv) { + Out *destination = inline_; + if (argc > kMaxStackArgs) { + outOfLine_ = std::make_unique(argc); + destination = outOfLine_.get(); + } + + for (size_t i = 0; i < argc; ++i) { + destination[i] = Convert(argv + i); + } + } + + operator Out *() { return outOfLine_ ? outOfLine_.get() : inline_; } + + private: + constexpr static unsigned kMaxStackArgs = 8; + Out inline_[kMaxStackArgs]; + std::unique_ptr outOfLine_; + }; +} // namespace + +namespace qjsimpl { + + enum NativeType { + External, Wrapper + }; + + class NativeInfo final { + public: + NativeInfo(napi_env env, NativeType type) + : _env(env), _type(type), _data(nullptr) {} + + std::list::const_iterator AddWeakRef(NAPIPersistent *ref) { + return _weakRefs.insert(_weakRefs.end(), ref); + } + + void RemoveWeakRef(std::list::const_iterator iter) { + _weakRefs.erase(iter); + } + + napi_env Env() const { return _env; } + + void Data(void *value) { _data = value; } + + void *Data() const { return _data; } + + NativeType Type() const { return _type; } + + static bool IsInstance(LEPUSClassID id) { return id == class_id; } + + static NativeInfo *Get(LEPUSValue val) { + return static_cast(LEPUS_GetOpaque(val, class_id)); + } + + static LEPUSClassID ClassId(napi_env env) { + static std::once_flag once_flag; + std::call_once(once_flag, [&] { LEPUS_NewClassID(&class_id); }); + auto rt = LEPUS_GetRuntime(env->ctx->ctx); + if (!LEPUS_IsRegisteredClass(rt, class_id)) { + static LEPUSClassDef def = {.class_name = "NAPIMagicNative", + .finalizer = NativeInfo::OnFinalize}; + if (LEPUS_NewClass(rt, class_id, &def) != 0) { + return 0; + } + } + + return class_id; + } + + private: + ~NativeInfo() { + // ref will remove itself when finalize, so copy is needed + for (NAPIPersistent *ref: std::vector( + std::begin(_weakRefs), std::end(_weakRefs))) { + NAPIPersistent::OnFinalize(ref); + } + } + + private: + const napi_env _env; + const NativeType _type; + + void *_data; + std::list _weakRefs; + + static LEPUSClassID class_id; + + static void OnFinalize(LEPUSRuntime *rt, LEPUSValue val) { + NativeInfo *info = static_cast(LEPUS_GetOpaque(val, class_id)); + LEPUS_SetOpaque(val, nullptr); + delete info; + } + }; + + LEPUSClassID NativeInfo::class_id = 0; + + class External { + public: + static LEPUSValue Create(napi_env env, NativeInfo **result) { + LEPUSClassID id = NativeInfo::ClassId(env); + if (!id) { + return LEPUS_ThrowInternalError(env->ctx->ctx, + "failed to create External Class"); + } + LEPUSValue object = LEPUS_NewObjectClass(env->ctx->ctx, id); + if (!LEPUS_IsException(object)) { + NativeInfo *info = new NativeInfo(env, NativeType::External); + LEPUS_SetOpaque(object, info); + *result = info; + } + return object; + } + }; + + class Wrapper { + public: + static LEPUSValue Create(napi_env env, LEPUSValue proto) { + LEPUSClassID id = NativeInfo::ClassId(env); + if (!id) { + return LEPUS_ThrowInternalError(env->ctx->ctx, + "failed to create Wrapper Class"); + } + LEPUSValue object = LEPUS_NewObjectProtoClass(env->ctx->ctx, proto, id); + if (!LEPUS_IsException(object)) { + LEPUS_SetOpaque(object, new NativeInfo(env, NativeType::Wrapper)); + } + return object; + } + }; + +// Wrapper around v8impl::Persistent that implements reference counting. + class RefBase : protected Finalizer, RefTracker { + protected: + RefBase(napi_env env, uint32_t initial_refcount, bool delete_self, + napi_finalize finalize_callback, void *finalize_data, + void *finalize_hint) + : Finalizer(env, finalize_callback, finalize_data, finalize_hint), + _refcount(initial_refcount), + _delete_self(delete_self), + _is_self_destroying(false) { + Link(finalize_callback == nullptr ? &env->ctx->reflist + : &env->ctx->finalizing_reflist); + } + + public: + static RefBase *New(napi_env env, uint32_t initial_refcount, bool delete_self, + napi_finalize finalize_callback, void *finalize_data, + void *finalize_hint) { + return new RefBase(env, initial_refcount, delete_self, finalize_callback, + finalize_data, finalize_hint); + } + + virtual ~RefBase() { Unlink(); } + + inline void *Data() { return _finalize_data; } + + // Delete is called in 2 ways. Either from the finalizer or + // from one of Unwrap or napi_delete_reference. + // + // When it is called from Unwrap or napi_delete_reference we only + // want to do the delete if the finalizer has already run or + // cannot have been queued to run (ie the reference count is > 0), + // otherwise we may crash when the finalizer does run. + // If the finalizer may have been queued and has not already run + // delay the delete until the finalizer runs by not doing the delete + // and setting _delete_self to true so that the finalizer will + // delete it when it runs. + // + // The second way this is called is from + // the finalizer and _delete_self is set. In this case we + // know we need to do the deletion so just do it. + static inline void Delete(RefBase *reference) { + if ((reference->RefCount() != 0) || (reference->_delete_self) || + (reference->_finalize_ran)) { + delete reference; + } else { + // defer until finalizer runs as + // it may alread be queued + reference->_delete_self = true; + } + } + + inline uint32_t Ref() { return ++_refcount; } + + inline uint32_t Unref() { + if (_refcount == 0) { + return 0; + } + return --_refcount; + } + + inline uint32_t RefCount() { return _refcount; } + + protected: + inline void Finalize(bool is_env_teardown = false) override { + if (is_env_teardown && RefCount() > 0) _refcount = 0; + + // There are cases where we want to avoid the reentrance of Finalize ( + // causing double-free): + // * When a wrapped object holds its own strong reference (either directly + // or indirectly) + // * (JSCore specific) when the destruction of a strong reference triggers + // garbage collection + // If we are sure this is getting deleted soon, there is no need for the + // finalizer to proceed. + if (_is_self_destroying && !is_env_teardown) { + return; + } + if (is_env_teardown) { + _is_self_destroying = true; + } + + if (_finalize_callback != nullptr) { + // This ensures that we never call the finalizer twice. + napi_finalize fini = _finalize_callback; + _finalize_callback = nullptr; + _env->ctx->CallFinalizer(fini, _finalize_data, _finalize_hint); + } + + // this is safe because if a request to delete the reference + // is made in the finalize_callback it will defer deletion + // to this block and set _delete_self to true + if (_delete_self || is_env_teardown) { + Delete(this); + } else { + _finalize_ran = true; + } + } + + private: + uint32_t _refcount; + bool _delete_self; + bool _is_self_destroying; + }; + + class Reference : public RefBase { + protected: + template + Reference(napi_env env, LEPUSValueConst value, NativeInfo *native_info, + Args &&... args) + : RefBase(env, std::forward(args)...), + _persistent(env, value, native_info, env->ctx->ctx) { + if (RefCount() == 0) { + _persistent.SetWeak(this, FinalizeCallback); + } + } + + public: + static inline Reference *New(napi_env env, LEPUSValueConst value, + NativeInfo *native_info, + uint32_t initial_refcount, bool delete_self, + napi_finalize finalize_callback = nullptr, + void *finalize_data = nullptr, + void *finalize_hint = nullptr) { + return new Reference(env, value, native_info, initial_refcount, delete_self, + finalize_callback, finalize_data, finalize_hint); + } + + void Finalize(bool is_env_teardown = false) override { + _persistent.Reset(true); + RefBase::Finalize(is_env_teardown); + } + + static inline void Delete(RefBase *reference) { + static_cast(reference)->_persistent.Reset(true); + RefBase::Delete(reference); + } + + inline uint32_t Ref() { + uint32_t refcount = RefBase::Ref(); + if (refcount == 1) { + _persistent.ClearWeak(); + } + return refcount; + } + + virtual ~Reference() { _persistent.Reset(true); } + + inline uint32_t Unref() { + uint32_t old_refcount = RefCount(); + uint32_t refcount = RefBase::Unref(); + if (old_refcount == 1 && refcount == 0) { + _persistent.SetWeak(this, FinalizeCallback); + } + return refcount; + } + + inline napi_value Get() { + return _persistent.IsEmpty() ? nullptr + : _env->ctx->CreateHandle(_persistent.Value()); + } + + private: + static void FinalizeCallback(void *data) { + Reference *r = static_cast(data); + r->_persistent.Reset(); + r->Finalize(); + } + + NAPIPersistent _persistent; + }; + + enum WrapType { + retrievable, anonymous + }; + + template + inline napi_status Wrap(napi_env env, napi_value js_object, void *native_object, + napi_finalize finalize_cb, void *finalize_hint, + napi_ref *result) { + LEPUSValueConst obj = ToJSValue(js_object); + + NativeInfo *info = NativeInfo::Get(obj); + + if (wrap_type == retrievable) { + RETURN_STATUS_IF_FALSE(env, + info != nullptr && + info->Type() == NativeType::Wrapper && + info->Data() == nullptr, + napi_invalid_arg); + } else { + // If no finalize callback is provided, we error out. + CHECK_ARG(env, finalize_cb); + } + + Reference *reference = nullptr; + if (result != nullptr) { + // The returned reference should be deleted via napi_delete_reference() + // ONLY in response to the finalize callback invocation. (If it is deleted + // before then, then the finalize callback will never be invoked.) + // Therefore a finalize callback is required when returning a reference. + CHECK_ARG(env, finalize_cb); + reference = Reference::New(env, obj, info, 0, false, finalize_cb, + native_object, finalize_hint); + *result = reinterpret_cast(reference); + } else { + // Create a self-deleting reference. + reference = + Reference::New(env, obj, info, 0, true, finalize_cb, native_object, + finalize_cb == nullptr ? nullptr : finalize_hint); + } + + if (wrap_type == retrievable) { + info->Data(reference); + } + + return napi_clear_last_error(env); + } + + enum UnwrapAction { + KeepWrap, RemoveWrap + }; + + inline static napi_status Unwrap(napi_env env, napi_value js_object, + void **result, UnwrapAction action) { + if (action == KeepWrap) { + CHECK_ARG(env, result); + } + + LEPUSValue obj = ToJSValue(js_object); + NativeInfo *info = NativeInfo::Get(obj); + + if (!info || info->Type() != qjsimpl::NativeType::Wrapper) { + if (result) { + *result = nullptr; + } + return napi_clear_last_error(env); + } + + Reference *reference = static_cast(info->Data()); + + if (result) { + *result = reference->Data(); + } + + if (action == RemoveWrap) { + info->Data(nullptr); + Reference::Delete(reference); + } + + return napi_clear_last_error(env); + } + + inline static Atom qjsAtomFromPropertyDescriptor( + napi_env env, const napi_property_descriptor &p) { + if (p.utf8name != nullptr) { + return Atom(env, env->ctx->ctx, p.utf8name); + } else { + return Atom(env, env->ctx->ctx, ToJSValue(p.name)); + } + } + + inline static uint8_t qjsFlagFromPropertyDescriptor( + napi_property_attributes attributes) { + uint8_t flags = 0; + if (attributes & napi_writable) { + flags |= LEPUS_PROP_WRITABLE; + } + if (attributes & napi_enumerable) { + flags |= LEPUS_PROP_ENUMERABLE; + } + if (attributes & napi_configurable) { + flags |= LEPUS_PROP_CONFIGURABLE; + } + return flags; + } + + inline NAPIPersistent::NAPIPersistent(napi_env env, LEPUSValueConst value, + NativeInfo *native_info, + LEPUSContext *ctx, bool is_weak) + : PersistentBase( + PersistentBase::New(LEPUS_GetRuntime(ctx), value, is_weak)), + _env(env), + _empty(false), + _value(JS_DupValue_Comp(env->ctx->ctx, value)), + _native_info(native_info), + _ctx(ctx) {} + + inline NAPIPersistent::NAPIPersistent(napi_env env, JSAtom atom, + NativeInfo *native_info, + LEPUSContext *ctx, bool is_weak) + : PersistentBase(PersistentBase::New(LEPUS_GetRuntime(ctx), + LEPUS_MKVAL(LEPUS_TAG_Atom, (int) atom), + is_weak)), + _env(env), + _empty(false), + _value(LEPUS_MKVAL(LEPUS_TAG_Atom, (int) atom)), + _native_info(native_info), + _ctx(ctx) {} + + void NAPIPersistent::Reset(bool for_gc) { + if (_empty) { + return; + } + if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { + PersistentBase::Reset(_ctx); + _env = nullptr; + _empty = true; + _native_info = nullptr; + } else if (!for_gc) { + if (_weak_info) { + ResetWeakInfo(); + } else { + JS_FreeValue_Comp(_env->ctx->ctx, _value); + } + _env = nullptr; + _empty = true; + _native_info = nullptr; + } + } + + void NAPIPersistent::Reset(napi_env env, LEPUSValueConst value, + NativeInfo *native_info, LEPUSContext *ctx, + bool for_gc) { + _ctx = ctx; + if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { // gc + PersistentBase::Reset(_ctx, value, false); + _empty = false; + _env = env; + _value = value; + _native_info = native_info; + } else if (!for_gc) { + Reset(); + _empty = false; + _env = env; + _value = JS_DupValue_Comp(env->ctx->ctx, value); + _native_info = native_info; + } + } + + void NAPIPersistent::Reset(napi_env env, LEPUSContext *ctx, JSAtom atom) { + _env = env; + _ctx = ctx; + if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { + PersistentBase::Reset(_ctx, LEPUS_MKVAL(LEPUS_TAG_Atom, (int) atom), false); + _empty = false; + } + } + + void NAPIPersistent::SetWeak(void *data, void (*cb)(void *)) { + if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { + SetGlobalWeak(LEPUS_GetRuntime(_ctx), this->val_, data, cb); + } else { + assert(!_empty); + if (_weak_info) { + _weak_info->cb_arg = data; + _weak_info->cb = cb; + } else { + _weak_info.reset( + new WeakInfo{_get_native_info()->AddWeakRef(this), cb, data}); + JS_FreeValue_Comp(_env->ctx->ctx, _value); + } + } + } + + void NAPIPersistent::ClearWeak() { + if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { + ClearGlobalWeak(LEPUS_GetRuntime(_ctx), this->val_); + } else { + JS_DupValue_Comp(_env->ctx->ctx, _value); + ResetWeakInfo(); + } + } + + LEPUSValue NAPIPersistent::Value() const { + if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { + return Get(); + } else { + return JS_DupValue_Comp(_env->ctx->ctx, _value); + } + } + + void NAPIPersistent::OnFinalize(NAPIPersistent *ref) { + auto cb = ref->_weak_info->cb; + auto cb_arg = ref->_weak_info->cb_arg; + ref->Reset(); + cb(cb_arg); + } + + void NAPIPersistent::ResetWeakInfo() { + assert(!_empty); + _get_native_info()->RemoveWeakRef(_weak_info->weak_iter); + _weak_info.reset(); + } + + NativeInfo *NAPIPersistent::_get_native_info() { + assert(!_empty); + if (!_native_info) { + LEPUSValue finalizer = + LEPUS_GetProperty(_env->ctx->ctx, _value, _env->ctx->PROP_FINALIZER); + assert(!LEPUS_IsException(finalizer)); + if (LEPUS_IsUndefined(finalizer)) { + NativeInfo *info; + finalizer = External::Create(_env, &info); + assert(!LEPUS_IsException(finalizer)); + int ret = LEPUS_DefinePropertyValue( + _env->ctx->ctx, _value, _env->ctx->PROP_FINALIZER, finalizer, 0); + (void) ret; + assert(ret != -1); + _native_info = info; + } else { + _native_info = qjsimpl::NativeInfo::Get(finalizer); + JS_FreeValue_Comp(_env->ctx->ctx, finalizer); + } + } + return _native_info; + } + +} // namespace qjsimpl + +namespace { + static inline LEPUSValue CallJSFunctionWithNAPI(napi_env env, napi_callback cb, + napi_callback_info cbinfo) { + napi_value result; + std::unique_ptr exception; + env->ctx->CallIntoModule([&](napi_env env) { result = cb(env, cbinfo); }, + [&](napi_env env, LEPUSValue exc) { + exception.reset(new LEPUSValue(exc)); + }); + + if (exception) { + env->ctx->CreateHandle(*exception, true); + return LEPUS_Throw(env->ctx->ctx, *exception); + } + + return result ? JS_DupValue_Comp(env->ctx->ctx, ToJSValue(result)) + : LEPUS_UNDEFINED; + } +} // namespace + +namespace qjsimpl { + typedef struct FunctionDataWrapper { + void *data; + } FunctionDataWrapper; +} + +napi_status napi_create_function(napi_env env, const char *utf8name, + size_t length, napi_callback cb, + void *callback_data, napi_value *result) { + LEPUSContext *ctx = env->ctx->ctx; + + auto dataWrapper = new qjsimpl::FunctionDataWrapper(); + dataWrapper->data = callback_data; + + LEPUSValue data[] = { + LEPUS_MKPTR(LEPUS_TAG_LEPUS_CPOINTER, env), + LEPUS_MKPTR(LEPUS_TAG_LEPUS_CPOINTER, reinterpret_cast(cb)), + LEPUS_MKPTR(LEPUS_TAG_LEPUS_CPOINTER, dataWrapper)}; + LEPUSValue fun = LEPUS_NewCFunctionData( + ctx, + [](LEPUSContext *ctx, LEPUSValueConst this_val, int argc, + LEPUSValueConst *argv, int magic, + LEPUSValue *func_data) -> LEPUSValue { + napi_env env = + reinterpret_cast(LEPUS_VALUE_GET_CPOINTER(func_data[0])); + napi_callback cb = reinterpret_cast( + LEPUS_VALUE_GET_CPOINTER(func_data[1])); + auto dataWrapper = reinterpret_cast LEPUS_VALUE_GET_CPOINTER( + func_data[2]); + + napi_clear_last_error(env); + + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + + ArgsConverter args(argc, argv); + + napi_callback_info__ cbinfo{}; + cbinfo.thisArg = ToNapi(&this_val); + cbinfo.newTarget = nullptr; + cbinfo.argc = argc; + cbinfo.argv = args; + cbinfo.data = dataWrapper->data; + + return CallJSFunctionWithNAPI(env, cb, &cbinfo); + }, + 0, 0, 3, data); + + napi_value external; + napi_create_external(env, dataWrapper, [](napi_env, void *data, void *hint) { + if (data) { + auto dataWrapper = reinterpret_cast(data); + delete dataWrapper; + } + }, dataWrapper, &external); + napi_set_named_property(env, ToNapi(&fun), "__qjs::finalizer__", external); + + CHECK_QJS(env, !LEPUS_IsException(fun)); + + *result = env->ctx->CreateHandle(fun); + + if (utf8name) { + // ignore error + LEPUSValue str = LEPUS_NewString(ctx, utf8name); + env->ctx->CreateHandle(str, true); + LEPUS_DefinePropertyValue(ctx, fun, env->ctx->PROP_NAME, str, + LEPUS_PROP_CONFIGURABLE); + } + + return napi_clear_last_error(env); +} + +static __attribute__((unused)) std::string GetExceptionMessage( + LEPUSContext *ctx, LEPUSValueConst exception_val) { + LEPUSValue val; + const char *stack; + const char *message = LEPUS_ToCString(ctx, exception_val); + std::string ret = "quickjs: "; + if (message) { + ret += message; + ret += "\n"; + JS_FreeCString_Comp(ctx, message); + } + + bool is_error = LEPUS_IsError(ctx, exception_val); + if (is_error) { + val = LEPUS_GetPropertyStr(ctx, exception_val, "stack"); + if (!LEPUS_IsUndefined(val)) { + stack = LEPUS_ToCString(ctx, val); + ret += stack; + JS_FreeCString_Comp(ctx, stack); + } + JS_FreeValue_Comp(ctx, val); + } + return ret; +} + +napi_status napi_define_class(napi_env env, const char *utf8name, size_t length, + napi_callback cb, void *data, + size_t property_count, + const napi_property_descriptor *properties, napi_value *result) { + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + + LEPUSContext *ctx = env->ctx->ctx; + + qjsimpl::Value proto(ctx, LEPUS_NewObject(ctx)); + + CHECK_QJS(env, !LEPUS_IsException(proto)); + + struct ClassData { + napi_callback cb; + void *data; + LEPUSValue proto; + qjsimpl::NAPIPersistent p_proto; + + ~ClassData() { p_proto.Reset(true); } + }; + + qjsimpl::NativeInfo *ctor_info; + qjsimpl::Value ctor_magic(ctx, qjsimpl::External::Create(env, &ctor_info)); + CHECK_QJS(env, !LEPUS_IsException(ctor_magic)); + + ClassData *ctor_magic_data = + new ClassData{.cb = cb, .data = data, .proto = proto.dup()}; + if (LEPUS_IsGCMode(env->ctx->ctx)) { + ctor_magic_data->p_proto.Reset(env, ctor_magic_data->proto, nullptr, + env->ctx->ctx, true); + } + ctor_info->Data(ctor_magic_data); + qjsimpl::Reference::New( + env, ctor_magic, ctor_info, 0, true, + [](napi_env env, void *data, void *hint) { + ClassData *ctor_magic_data = static_cast(data); + JS_FreeValue_Comp(env->ctx->ctx, ctor_magic_data->proto); + delete ctor_magic_data; + static_cast(hint)->Data(nullptr); + }, + ctor_magic_data, ctor_info); + LEPUSValue cfunction = LEPUS_NewCFunctionMagic( + ctx, + [](LEPUSContext *ctx, LEPUSValueConst new_target, int argc, + LEPUSValueConst *argv, int magic) -> LEPUSValue { + JSAtom prop_ctor_magic = LEPUS_NewAtom(ctx, "@#ctor@#"); + LEPUSValue ctor_magic = + LEPUS_GetProperty(ctx, new_target, prop_ctor_magic); + JS_FreeAtom_Comp(ctx, prop_ctor_magic); + if (LEPUS_IsException(ctor_magic) || LEPUS_IsUndefined(ctor_magic)) { + if (LEPUS_IsObject(new_target)) { + LOGI("new_target is an object"); + } + LOGI("new_target ptr is " + << LEPUS_VALUE_GET_PTR(new_target) << ", prop_ctor_magic is " + << prop_ctor_magic << ", function magic is " << magic + << ", exception message: " + << GetExceptionMessage(ctx, ctor_magic)); + return ctor_magic; + } + qjsimpl::NativeInfo *info = qjsimpl::NativeInfo::Get(ctor_magic); + JS_FreeValue_Comp(ctx, ctor_magic); + if (!(info != nullptr && + info->Type() == qjsimpl::NativeType::External && info->Data())) { + LOGI("ctor_magic native_info error return undefined, info is " + << info); + return LEPUS_UNDEFINED; + } + napi_env env = info->Env(); + ClassData *class_data = static_cast(info->Data()); + LEPUSValue this_val = qjsimpl::Wrapper::Create(env, class_data->proto); + + if (LEPUS_IsException(this_val)) { + LOGI("create Wrapper return exception"); + return this_val; + } + napi_clear_last_error(env); + + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + + ArgsConverter args(argc, argv); + + napi_callback_info__ cbinfo{}; + cbinfo.thisArg = env->ctx->CreateHandle(this_val); + cbinfo.newTarget = ToNapi(&new_target); + cbinfo.argc = argc; + cbinfo.argv = args; + cbinfo.data = class_data->data; + + auto result = CallJSFunctionWithNAPI(env, class_data->cb, &cbinfo); + if (LEPUS_IsUndefined(result)) { + LOGI("napi callback return undefined"); + } + return result; + }, + utf8name, 0, LEPUS_CFUNC_constructor_magic, env->ctx->PROP_CTOR_MAGIC); + + qjsimpl::Value constructor(ctx, cfunction); + + auto ctor_handle = env->ctx->CreateHandle(cfunction, true); + + if (LEPUS_IsException(constructor)) { + napi_status status = + napi_set_exception(env, LEPUS_GetException(env->ctx->ctx)); + LOGI(GetExceptionMessage(env->ctx->ctx, *env->ctx->last_exception)); + return status; + } + + if (LEPUS_DefinePropertyValue(ctx, constructor, env->ctx->PROP_CTOR_MAGIC, + ctor_magic.move(), 0) == -1) { + napi_status status = + napi_set_exception(env, LEPUS_GetException(env->ctx->ctx)); + LOGI(GetExceptionMessage(env->ctx->ctx, *env->ctx->last_exception)); + return status; + } + + + CHECK_QJS( + env, LEPUS_DefinePropertyValue(ctx, constructor, env->ctx->PROP_PROTOTYPE, + proto.dup(), 0) != -1); + CHECK_QJS(env, LEPUS_DefinePropertyValue( + ctx, proto, env->ctx->PROP_CONSTRUCTOR, constructor.dup(), + LEPUS_PROP_WRITABLE | LEPUS_PROP_CONFIGURABLE) != -1); + + int instancePropertyCount{0}; + int staticPropertyCount{0}; + for (size_t i = 0; i < property_count; i++) { + if ((properties[i].attributes & napi_static) != 0) { + staticPropertyCount++; + } else { + instancePropertyCount++; + } + } + + std::vector staticDescriptors{}; + std::vector instanceDescriptors{}; + staticDescriptors.reserve(staticPropertyCount); + instanceDescriptors.reserve(instancePropertyCount); + + for (size_t i = 0; i < property_count; i++) { + if ((properties[i].attributes & napi_static) != 0) { + staticDescriptors.push_back(properties[i]); + } else { + instanceDescriptors.push_back(properties[i]); + } + } + + if (staticPropertyCount > 0) { + LEPUSValue ctor_val = constructor; + + CHECK_NAPI(napi_define_properties(env, ToNapi(&ctor_val), + staticDescriptors.size(), + staticDescriptors.data())); + } + + if (instancePropertyCount > 0) { + LEPUSValue proto_val = proto; + + CHECK_NAPI(napi_define_properties(env, ToNapi(&proto_val), + instanceDescriptors.size(), + instanceDescriptors.data())); + } + + + *result = scope.Escape(ctor_handle); + + return napi_clear_last_error(env); +} + +napi_status napi_get_property_names_gc(napi_env env, napi_value object, + napi_value *result) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSPropertyEnum *props = nullptr; + HandleScope func_scope(ctx, &props, HANDLE_TYPE_HEAP_OBJ); + uint32_t props_length; + CHECK_QJS(env, LEPUS_GetOwnPropertyNames( + ctx, &props, &props_length, ToJSValue(object), + LEPUS_GPN_STRING_MASK | LEPUS_GPN_SYMBOL_MASK | + LEPUS_GPN_ENUM_ONLY | LEPUS_PROP_THROW) != -1); + + std::vector values; + values.reserve(props_length); + for (uint32_t i = 0; i < props_length; i++) { + values.emplace_back(LEPUS_AtomToValue(ctx, props[i].atom)); + func_scope.PushHandle(&values[i], HANDLE_TYPE_LEPUS_VALUE); + } + LEPUSValue arr = LEPUS_NewArrayWithValue(ctx, props_length, values.data()); + + CHECK_QJS(env, !LEPUS_IsException(arr)); + *result = env->ctx->CreateHandle(arr); + return napi_clear_last_error(env); +} + +napi_status napi_get_property_names(napi_env env, + napi_value object, + napi_value *result) { + LEPUSContext *ctx = env->ctx->ctx; + if (LEPUS_IsGCMode(ctx)) { + return napi_get_property_names_gc(env, object, result); + } + LEPUSPropertyEnum *props = nullptr; + uint32_t props_length; + CHECK_QJS(env, LEPUS_GetOwnPropertyNames( + ctx, &props, &props_length, ToJSValue(object), + LEPUS_GPN_STRING_MASK | LEPUS_GPN_SYMBOL_MASK | + LEPUS_GPN_ENUM_ONLY | LEPUS_PROP_THROW) != -1); + + std::vector values; + values.reserve(props_length); + for (uint32_t i = 0; i < props_length; i++) { + values.emplace_back(LEPUS_AtomToValue(ctx, props[i].atom)); + JS_FreeAtom_Comp(ctx, props[i].atom); + } + js_free_comp(ctx, props); + LEPUSValue arr = LEPUS_NewArrayWithValue(ctx, props_length, values.data()); + for (LEPUSValue v: values) { + JS_FreeValue_Comp(ctx, v); + } + + CHECK_QJS(env, !LEPUS_IsException(arr)); + *result = env->ctx->CreateHandle(arr); + return napi_clear_last_error(env); +} + +napi_status napi_get_all_property_names(napi_env env, + napi_value object, + napi_key_collection_mode key_mode, + napi_key_filter key_filter, + napi_key_conversion key_conversion, + napi_value *result) { + LEPUSContext *ctx = env->ctx->ctx; + if (LEPUS_IsGCMode(ctx)) { + return napi_get_property_names_gc(env, object, result); + } + LEPUSPropertyEnum *props = nullptr; + uint32_t props_length; + CHECK_QJS(env, LEPUS_GetOwnPropertyNames( + ctx, &props, &props_length, ToJSValue(object), + LEPUS_GPN_STRING_MASK | LEPUS_GPN_SYMBOL_MASK | + LEPUS_GPN_ENUM_ONLY | LEPUS_PROP_THROW) != -1); + + std::vector values; + values.reserve(props_length); + for (uint32_t i = 0; i < props_length; i++) { + values.emplace_back(LEPUS_AtomToValue(ctx, props[i].atom)); + JS_FreeAtom_Comp(ctx, props[i].atom); + } + js_free_comp(ctx, props); + LEPUSValue arr = LEPUS_NewArrayWithValue(ctx, props_length, values.data()); + for (LEPUSValue v: values) { + JS_FreeValue_Comp(ctx, v); + } + + CHECK_QJS(env, !LEPUS_IsException(arr)); + *result = env->ctx->CreateHandle(arr); + return napi_clear_last_error(env); +} + +napi_status napi_set_property(napi_env env, napi_value object, napi_value key, + napi_value value) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue obj = ToJSValue(object); + qjsimpl::Atom prop_atom(env, ctx, ToJSValue(key)); + CHECK_QJS(env, prop_atom.IsValid()); + int result = LEPUS_SetProperty(ctx, obj, prop_atom, + JS_DupValue_Comp(ctx, ToJSValue(value))); + CHECK_QJS(env, result != -1); + return napi_clear_last_error(env); +} + +napi_status napi_has_property(napi_env env, napi_value object, napi_value key, + bool *result) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue obj = ToJSValue(object); + qjsimpl::Atom prop_atom(env, ctx, ToJSValue(key)); + CHECK_QJS(env, prop_atom.IsValid()); + int result_has = LEPUS_HasProperty(ctx, obj, prop_atom); + CHECK_QJS(env, result_has != -1); + *result = result_has; + return napi_clear_last_error(env); +} + +napi_status napi_get_property(napi_env env, napi_value object, napi_value key, + napi_value *result) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue obj = ToJSValue(object); + qjsimpl::Atom prop_atom(env, ctx, ToJSValue(key)); + CHECK_QJS(env, prop_atom.IsValid()); + LEPUSValue val = LEPUS_GetProperty(ctx, obj, prop_atom); + CHECK_QJS(env, !LEPUS_IsException(val)); + *result = env->ctx->CreateHandle(val); + return napi_clear_last_error(env); +} + +napi_status napi_delete_property(napi_env env, napi_value object, + napi_value key, bool *result) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue obj = ToJSValue(object); + qjsimpl::Atom prop_atom(env, ctx, ToJSValue(key)); + CHECK_QJS(env, prop_atom.IsValid()); + int result_delete = + LEPUS_DeleteProperty(ctx, obj, prop_atom, LEPUS_PROP_THROW); + CHECK_QJS(env, result_delete != -1); + if (result) { + *result = result_delete; + } + return napi_clear_last_error(env); +} + +napi_status napi_has_own_property(napi_env env, napi_value object, + napi_value key, bool *result) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue obj = ToJSValue(object); + qjsimpl::Atom prop_atom(env, ctx, ToJSValue(key)); + CHECK_QJS(env, prop_atom.IsValid()); + int result_has = LEPUS_GetOwnProperty(ctx, nullptr, obj, prop_atom); + CHECK_QJS(env, result_has != -1); + *result = result_has; + return napi_clear_last_error(env); +} + +napi_status napi_set_named_property(napi_env env, napi_value object, + const char *utf8name, napi_value value) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue obj = ToJSValue(object); + qjsimpl::Atom prop_atom(env, ctx, LEPUS_NewAtom(ctx, utf8name)); + CHECK_QJS(env, prop_atom.IsValid()); + int result = LEPUS_SetProperty(ctx, obj, prop_atom, + JS_DupValue_Comp(ctx, ToJSValue(value))); + CHECK_QJS(env, result != -1); + return napi_clear_last_error(env); +} + +napi_status napi_has_named_property(napi_env env, napi_value object, + const char *utf8name, bool *result) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue obj = ToJSValue(object); + qjsimpl::Atom prop_atom(env, ctx, utf8name); + CHECK_QJS(env, prop_atom.IsValid()); + int result_has = LEPUS_HasProperty(ctx, obj, prop_atom); + CHECK_QJS(env, result_has != -1); + *result = result_has; + return napi_clear_last_error(env); +} + +napi_status napi_get_named_property(napi_env env, napi_value object, + const char *utf8name, napi_value *result) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue obj = ToJSValue(object); + qjsimpl::Atom prop_atom(env, ctx, utf8name); + CHECK_QJS(env, prop_atom.IsValid()); + LEPUSValue val = LEPUS_GetProperty(ctx, obj, prop_atom); + CHECK_QJS(env, !LEPUS_IsException(val)); + *result = env->ctx->CreateHandle(val); + return napi_clear_last_error(env); +} + +napi_status napi_set_element(napi_env env, napi_value object, uint32_t index, + napi_value value) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue obj = ToJSValue(object); + int result = LEPUS_SetPropertyUint32(ctx, obj, index, + JS_DupValue_Comp(ctx, ToJSValue(value))); + CHECK_QJS(env, result != -1); + return napi_clear_last_error(env); +} + +napi_status napi_has_element(napi_env env, napi_value object, uint32_t index, + bool *result) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue obj = ToJSValue(object); + LEPUSValue val = LEPUS_GetPropertyUint32(ctx, obj, index); + CHECK_QJS(env, !LEPUS_IsException(val)); + *result = !LEPUS_IsUndefined(val); + JS_FreeValue_Comp(ctx, val); + return napi_clear_last_error(env); +} + +napi_status napi_get_element(napi_env env, napi_value object, uint32_t index, + napi_value *result) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue obj = ToJSValue(object); + LEPUSValue val = LEPUS_GetPropertyUint32(ctx, obj, index); + + CHECK_QJS(env, !LEPUS_IsException(val)); + *result = env->ctx->CreateHandle(val); + return napi_clear_last_error(env); +} + +napi_status napi_delete_element(napi_env env, napi_value object, uint32_t index, + bool *result) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue obj = ToJSValue(object); + qjsimpl::Atom prop_atom(env, ctx, LEPUS_NewAtomUInt32(ctx, index)); + CHECK_QJS(env, prop_atom.IsValid()); + int result_delete = + LEPUS_DeleteProperty(ctx, obj, prop_atom, LEPUS_PROP_THROW); + CHECK_QJS(env, result_delete != -1); + *result = result_delete; + return napi_clear_last_error(env); +} + +napi_status napi_define_properties(napi_env env, napi_value object, + size_t property_count, + const napi_property_descriptor *properties) { + if (property_count > 0) { + CHECK_ARG(env, properties); + } + + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue obj = ToJSValue(object); + + for (size_t i = 0; i < property_count; i++) { + const napi_property_descriptor &p = properties[i]; + qjsimpl::Atom prop_atom = qjsimpl::qjsAtomFromPropertyDescriptor(env, p); + CHECK_QJS(env, prop_atom.IsValid()); + uint8_t flags = qjsimpl::qjsFlagFromPropertyDescriptor(p.attributes); + if (p.getter != nullptr || p.setter != nullptr) { + LEPUSValue getter = LEPUS_UNDEFINED; + char name_buf[128]; + memset(name_buf, 0, sizeof(name_buf)); + if (p.getter) { + napi_value napi_getter; + if (p.utf8name) { + snprintf(name_buf, sizeof(name_buf), "get %s", p.utf8name); + } + CHECK_NAPI(napi_create_function(env, name_buf, NAPI_AUTO_LENGTH, + p.getter, p.data, &napi_getter)); + getter = JS_DupValue_Comp(ctx, ToJSValue(napi_getter)); + } + LEPUSValue setter = LEPUS_UNDEFINED; + if (p.setter) { + napi_value napi_setter; + if (p.utf8name) { + snprintf(name_buf, sizeof(name_buf), "set %s", p.utf8name); + } + CHECK_NAPI(napi_create_function(env, name_buf, NAPI_AUTO_LENGTH, + p.setter, p.data, &napi_setter)); + setter = JS_DupValue_Comp(ctx, ToJSValue(napi_setter)); + } + CHECK_QJS(env, LEPUS_DefinePropertyGetSet(ctx, obj, prop_atom, getter, + setter, flags) != -1); + } else if (p.method != nullptr) { + napi_value method; + CHECK_NAPI(napi_create_function(env, p.utf8name, NAPI_AUTO_LENGTH, + p.method, p.data, &method)); + CHECK_QJS(env, + LEPUS_DefinePropertyValue( + ctx, obj, prop_atom, + JS_DupValue_Comp(ctx, ToJSValue(method)), flags) != -1); + } else { + LEPUSValue value = JS_DupValue_Comp(ctx, ToJSValue(p.value)); + CHECK_QJS(env, LEPUS_DefinePropertyValue(ctx, obj, prop_atom, value, + flags) != -1); + } + } + + return napi_clear_last_error(env); +} + +napi_status napi_is_array(napi_env env, napi_value value, bool *result) { + int result_is = LEPUS_IsArray(env->ctx->ctx, ToJSValue(value)); + CHECK_QJS(env, result_is != -1); + *result = result_is; + return napi_clear_last_error(env); +} + +napi_status napi_get_array_length(napi_env env, napi_value value, + uint32_t *result) { + LEPUSContext *ctx = env->ctx->ctx; + LEPUSValue v = + LEPUS_GetProperty(ctx, ToJSValue(value), env->ctx->PROP_LENGTH); + CHECK_QJS(env, !LEPUS_IsException(v)); + int result_toint = LEPUS_ToUint32(ctx, result, v); + JS_FreeValue_Comp(ctx, v); + CHECK_QJS(env, result_toint != -1); + return napi_clear_last_error(env); +} + +napi_status napi_equals(napi_env env, napi_value lhs, napi_value rhs, + bool *result) { + LEPUSValue a = ToJSValue(lhs); + LEPUSValue b = ToJSValue(rhs); + LEPUSContext *ctx = env->ctx->ctx; + *result = LEPUS_SameValue(ctx, a, b); + + return napi_clear_last_error(env); +} + +napi_status napi_strict_equals(napi_env env, napi_value lhs, napi_value rhs, + bool *result) { + LEPUSValue a = ToJSValue(lhs); + LEPUSValue b = ToJSValue(rhs); + LEPUSContext *ctx = env->ctx->ctx; + *result = + LEPUS_StrictEq(ctx, JS_DupValue_Comp(ctx, a), JS_DupValue_Comp(ctx, b)); + return napi_clear_last_error(env); +} + +napi_status napi_get_prototype(napi_env env, napi_value object, + napi_value *result) { + LEPUSValueConst prototype = + LEPUS_GetPrototype(env->ctx->ctx, ToJSValue(object)); + CHECK_QJS(env, !LEPUS_IsException(prototype)); + *result = env->ctx->CreateHandle(JS_DupValue_Comp(env->ctx->ctx, prototype)); + return napi_clear_last_error(env); +} + +napi_status napi_create_object(napi_env env, napi_value *result) { + LEPUSValue object = LEPUS_NewObject(env->ctx->ctx); + CHECK_QJS(env, !LEPUS_IsException(object)); + *result = env->ctx->CreateHandle(object); + return napi_clear_last_error(env); +} + +napi_status napi_create_array(napi_env env, napi_value *result) { + LEPUSValue array = LEPUS_NewArray(env->ctx->ctx); + CHECK_QJS(env, !LEPUS_IsException(array)); + *result = env->ctx->CreateHandle(array); + return napi_clear_last_error(env); +} + +napi_status napi_create_array_with_length(napi_env env, size_t length, + napi_value *result) { + LEPUSValue array = LEPUS_NewArray(env->ctx->ctx); + CHECK_QJS(env, !LEPUS_IsException(array)); + + *result = env->ctx->CreateHandle(array); + CHECK_QJS(env, + LEPUS_SetProperty(env->ctx->ctx, array, env->ctx->PROP_LENGTH, + LEPUS_NewInt64(env->ctx->ctx, length)) != -1); + + return napi_clear_last_error(env); +} + +napi_status napi_create_string_latin1(napi_env env, const char *str, + size_t length, napi_value *result) { + *result = env->ctx->CreateHandle( + length == NAPI_AUTO_LENGTH + ? LEPUS_NewString(env->ctx->ctx, str) + : LEPUS_NewStringLen(env->ctx->ctx, str, length)); + return napi_clear_last_error(env); +} + +napi_status napi_create_string_utf8(napi_env env, const char *str, + size_t length, napi_value *result) { + *result = env->ctx->CreateHandle( + length == NAPI_AUTO_LENGTH + ? LEPUS_NewString(env->ctx->ctx, str) + : LEPUS_NewStringLen(env->ctx->ctx, str, length)); + return napi_clear_last_error(env); +} + +napi_status napi_create_string_utf16(napi_env env, const char16_t *str, + size_t length, napi_value *result) { + *result = env->ctx->CreateHandle(LEPUS_NewWString( + env->ctx->ctx, reinterpret_cast(str), + length == NAPI_AUTO_LENGTH ? std::char_traits::length(str) + : length)); + return napi_clear_last_error(env); +} + +napi_status napi_create_double(napi_env env, double value, napi_value *result) { + *result = env->ctx->CreateHandle(LEPUS_NewFloat64(env->ctx->ctx, value)); + return napi_clear_last_error(env); +} + +napi_status napi_create_int32(napi_env env, int32_t value, napi_value *result) { + *result = env->ctx->CreateHandle(LEPUS_NewInt32(env->ctx->ctx, value)); + return napi_clear_last_error(env); +} + +napi_status napi_create_uint32(napi_env env, uint32_t value, + napi_value *result) { + *result = env->ctx->CreateHandle(LEPUS_NewInt64(env->ctx->ctx, value)); + return napi_clear_last_error(env); +} + +napi_status napi_create_int64(napi_env env, int64_t value, napi_value *result) { + *result = env->ctx->CreateHandle(LEPUS_NewInt64(env->ctx->ctx, value)); + return napi_clear_last_error(env); +} + +napi_status napi_get_boolean(napi_env env, bool value, napi_value *result) { + *result = env->ctx->CreateHandle(LEPUS_NewBool(env->ctx->ctx, value)); + return napi_clear_last_error(env); +} + +napi_status napi_create_symbol(napi_env env, napi_value description, + napi_value *result) { + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + napi_value global{}, symbol_func{}, symbol_value{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Symbol", &symbol_func)); + CHECK_NAPI(napi_call_function(env, global, symbol_func, 1, &description, + &symbol_value)); + *result = scope.Escape(symbol_value); + return napi_clear_last_error(env); +} + +napi_status napi_create_error(napi_env env, napi_value code, napi_value msg, + napi_value *result) { + LEPUSValue error = LEPUS_NewError(env->ctx->ctx); + *result = env->ctx->CreateHandle(error); + + CHECK_NAPI(napi_set_error_msg_code(env, ToNapi(&error), code, msg, nullptr)); + + return napi_clear_last_error(env); +} + +napi_status napi_create_type_error(napi_env env, napi_value code, + napi_value msg, napi_value *result) { + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + + napi_value global{}, error_ctor{}, error{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "TypeError", &error_ctor)); + CHECK_NAPI(napi_new_instance(env, error_ctor, 1, &msg, &error)); + CHECK_NAPI(napi_set_error_msg_code(env, error, code, msg, nullptr)); + + *result = scope.Escape(error); + return napi_clear_last_error(env); +} + +napi_status napi_create_range_error(napi_env env, napi_value code, + napi_value msg, napi_value *result) { + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + + napi_value global{}, error_ctor{}, error{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "RangeError", &error_ctor)); + CHECK_NAPI(napi_new_instance(env, error_ctor, 1, &msg, &error)); + CHECK_NAPI(napi_set_error_msg_code(env, error, code, msg, nullptr)); + + *result = scope.Escape(error); + return napi_clear_last_error(env); +} + +napi_status napi_typeof(napi_env env, napi_value value, + napi_valuetype *result) { + LEPUSValue v = ToJSValue(value); + int64_t tag = LEPUS_VALUE_GET_NORM_TAG(v); + + switch (tag) { + case LEPUS_TAG_INT: + case LEPUS_TAG_FLOAT64: + *result = napi_number; + break; + case LEPUS_TAG_BIG_INT: + *result = napi_bigint; + break; + case LEPUS_TAG_STRING: + *result = napi_string; + break; + case LEPUS_TAG_SEPARABLE_STRING: + *result = napi_string; + break; + case LEPUS_TAG_SYMBOL: + *result = napi_symbol; + break; + case LEPUS_TAG_NULL: + *result = napi_null; + break; + case LEPUS_TAG_UNDEFINED: + *result = napi_undefined; + break; + case LEPUS_TAG_BOOL: + *result = napi_boolean; + break; + case LEPUS_TAG_OBJECT: + if (LEPUS_IsFunction(env->ctx->ctx, v)) { + *result = napi_function; + } else { + qjsimpl::NativeInfo *info = qjsimpl::NativeInfo::Get(v); + if (info && info->Type() == qjsimpl::NativeType::External) { + *result = napi_external; + } else { + *result = napi_object; + } + } + break; + default: + // Should not get here unless QuickJS has added some new kind of value. + return napi_set_last_error(env, napi_invalid_arg); + } + + return napi_clear_last_error(env); +} + +napi_status napi_get_undefined(napi_env env, napi_value *result) { + *result = ToNapi(&(env->ctx->V_UNDEFINED)); + return napi_clear_last_error(env); +} + +napi_status napi_get_null(napi_env env, napi_value *result) { + *result = ToNapi(&(env->ctx->V_NULL)); + return napi_clear_last_error(env); +} + +napi_status napi_get_cb_info( + napi_env env, // [in] NAPI environment handle + napi_callback_info cbinfo, // [in] Opaque callback-info handle + size_t *argc, // [in-out] Specifies the size of the provided argv array + // and receives the actual count of args. + napi_value *argv, // [out] Array of values + napi_value *this_arg, // [out] Receives the JS 'this' arg for the call + void **data) { // [out] Receives the data pointer for the callback. + if (argv != nullptr) { + CHECK_ARG(env, argc); + + size_t i{0}; + size_t min{std::min(*argc, static_cast(cbinfo->argc))}; + + for (; i < min; i++) { + argv[i] = cbinfo->argv[i]; + } + + if (i < *argc) { + for (; i < *argc; i++) { + argv[i] = ToNapi(&(env->ctx->V_UNDEFINED)); + } + } + } + + if (argc != nullptr) { + *argc = cbinfo->argc; + } + + if (this_arg != nullptr) { + *this_arg = cbinfo->thisArg; + } + + if (data != nullptr) { + *data = cbinfo->data; + } + + return napi_clear_last_error(env); +} + +napi_status napi_get_new_target(napi_env env, napi_callback_info cbinfo, + napi_value *result) { + *result = cbinfo->newTarget; + return napi_clear_last_error(env); +} + +namespace { + inline LEPUSValueConst ToJSValue(napi_value *v) { return ToJSValue(*v); } +} // namespace + +napi_status napi_call_function(napi_env env, napi_value recv, napi_value func, + size_t argc, const napi_value *argv, + napi_value *result) { + if (argc > 0) { + CHECK_ARG(env, argv); + } + + LEPUSContext *ctx = env->ctx->ctx; + + js_enter(env); + + ArgsConverter args( + argc, const_cast(argv)); + + LEPUSValue call_result = + LEPUS_Call(ctx, ToJSValue(func), recv ? ToJSValue(recv) : LEPUS_UNDEFINED, + argc, args); + + + CHECK_QJS(env, !LEPUS_IsException(call_result)); + + if (result) { + *result = env->ctx->CreateHandle(call_result); + } else { + if (!LEPUS_IsGCMode(ctx)) { + LEPUS_FreeValue(ctx, call_result); + } + } + + js_exit(env); + auto rt = LEPUS_GetRuntime(ctx); + if (rt && !rt->current_stack_frame) { + LEPUSContext *pctx = NULL; + int result = 0; + while ((result = LEPUS_ExecutePendingJob(rt, &pctx))) { + if (result < 0) { + return napi_set_exception(env, LEPUS_GetException(pctx)); + } + } + } + + return napi_clear_last_error(env); +} + +napi_status napi_get_global(napi_env env, napi_value *result) { + *result = env->ctx->CreateHandle(LEPUS_GetGlobalObject(env->ctx->ctx)); + return napi_clear_last_error(env); +} + +napi_status napi_throw(napi_env env, napi_value error) { + if (env->ctx->last_exception) { + JS_FreeValue_Comp(env->ctx->ctx, *env->ctx->last_exception); + } + env->ctx->last_exception.reset( + new LEPUSValue(JS_DupValue_Comp(env->ctx->ctx, ToJSValue(error)))); + env->ctx->last_exception_pVal.Reset(nullptr, *env->ctx->last_exception, + nullptr, env->ctx->ctx, true); + return napi_clear_last_error(env); +} + +napi_status napi_throw_error(napi_env env, const char *code, const char *msg) { + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + + LEPUSValue code_val = LEPUS_UNDEFINED; + if (code != NULL) { + LEPUS_NewString(env->ctx->ctx, code); + env->ctx->CreateHandle(code_val, true); + } + + LEPUSValue msg_val = LEPUS_NewString(env->ctx->ctx, msg); + env->ctx->CreateHandle(msg_val, true); + + napi_value error{}; + napi_status ret = + napi_create_error(env, ToNapi(&code_val), ToNapi(&msg_val), &error); + JS_FreeValue_Comp(env->ctx->ctx, code_val); + JS_FreeValue_Comp(env->ctx->ctx, msg_val); + + CHECK_NAPI(ret); + + return napi_throw(env, error); +} + +napi_status napi_throw_type_error(napi_env env, const char *code, + const char *msg) { + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + + LEPUSValue code_val = LEPUS_UNDEFINED; + if (code != NULL) { + LEPUS_NewString(env->ctx->ctx, code); + env->ctx->CreateHandle(code_val, true); + } + + LEPUSValue msg_val = LEPUS_NewString(env->ctx->ctx, msg); + env->ctx->CreateHandle(msg_val, true); + + napi_value error{}; + napi_status ret = + napi_create_type_error(env, ToNapi(&code_val), ToNapi(&msg_val), &error); + JS_FreeValue_Comp(env->ctx->ctx, code_val); + JS_FreeValue_Comp(env->ctx->ctx, msg_val); + + CHECK_NAPI(ret); + + return napi_throw(env, error); +} + +napi_status napi_throw_range_error(napi_env env, const char *code, + const char *msg) { + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + LEPUSValue code_val = LEPUS_UNDEFINED; + if (code != NULL) { + LEPUS_NewString(env->ctx->ctx, code); + env->ctx->CreateHandle(code_val, true); + } + LEPUSValue msg_val = LEPUS_NewString(env->ctx->ctx, msg); + env->ctx->CreateHandle(msg_val, true); + + napi_value error{}; + napi_status ret = + napi_create_range_error(env, ToNapi(&code_val), ToNapi(&msg_val), &error); + + JS_FreeValue_Comp(env->ctx->ctx, code_val); + + JS_FreeValue_Comp(env->ctx->ctx, msg_val); + + CHECK_NAPI(ret); + + return napi_throw(env, error); +} + +napi_status napi_is_error(napi_env env, napi_value value, bool *result) { + *result = LEPUS_IsError(env->ctx->ctx, ToJSValue(value)); + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_double(napi_env env, napi_value value, + double *result) { + int ret = LEPUS_ToFloat64(env->ctx->ctx, result, ToJSValue(value)); + + CHECK_QJS(env, ret != -1); + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_int32(napi_env env, napi_value value, + int32_t *result) { + int ret = LEPUS_ToInt32(env->ctx->ctx, result, ToJSValue(value)); + + CHECK_QJS(env, ret != -1); + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_uint32(napi_env env, napi_value value, + uint32_t *result) { + int ret = LEPUS_ToUint32(env->ctx->ctx, result, ToJSValue(value)); + + CHECK_QJS(env, ret != -1); + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_int64(napi_env env, napi_value value, + int64_t *result) { + int ret = LEPUS_ToInt64(env->ctx->ctx, result, ToJSValue(value)); + + CHECK_QJS(env, ret != -1); + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_bool(napi_env env, napi_value value, bool *result) { + *result = LEPUS_ToBool(env->ctx->ctx, ToJSValue(value)); + + return napi_clear_last_error(env); +} + +// Copies a JavaScript string into a LATIN-1 string buffer. The result is the +// number of bytes (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in bytes) +// via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_latin1(napi_env env, napi_value value, + char *buf, size_t bufsize, + size_t *result) { + LEPUSValue wstring = LEPUS_ToWString(env->ctx->ctx, ToJSValue(value)); + env->ctx->CreateHandle(wstring, true); + + CHECK_QJS(env, !LEPUS_IsException(wstring)); + + size_t length = LEPUS_GetStringLength(env->ctx->ctx, wstring); + + if (buf == nullptr) { + *result = length; + } else { + const char16_t *chars = reinterpret_cast( + LEPUS_GetStringChars(env->ctx->ctx, wstring)); + size_t size{std::min(length, bufsize - 1)}; + for (size_t i = 0; i < size; ++i) { + const char16_t ch{chars[i]}; + buf[i] = (ch < 256) ? ch : '?'; + } + buf[size] = '\0'; + if (result != nullptr) { + *result = size; + } + } + + JS_FreeValue_Comp(env->ctx->ctx, wstring); + + return napi_clear_last_error(env); +} + +// Copies a JavaScript string into a UTF-8 string buffer. The result is the +// number of bytes (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in bytes) +// via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_utf8(napi_env env, napi_value value, + char *buf, size_t bufsize, + size_t *result) { + size_t length; + const char *str = + LEPUS_ToCStringLen(env->ctx->ctx, &length, ToJSValue(value)); + + CHECK_QJS(env, str); + + if (buf == nullptr) { + *result = length; + } else { + size_t size{std::min(length, bufsize - 1)}; + std::copy(str, str + size, buf); + buf[size] = '\0'; + if (result != nullptr) { + *result = size; + } + } + + JS_FreeCString_Comp(env->ctx->ctx, str); + + return napi_clear_last_error(env); +} + +// Copies a JavaScript string into a UTF-16 string buffer. The result is the +// number of 2-byte code units (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in 2-byte +// code units) via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_utf16(napi_env env, napi_value value, + char16_t *buf, size_t bufsize, + size_t *result) { + LEPUSValue wstring = LEPUS_ToWString(env->ctx->ctx, ToJSValue(value)); + env->ctx->CreateHandle(wstring, true); + + CHECK_QJS(env, !LEPUS_IsException(wstring)); + + size_t length = LEPUS_GetStringLength(env->ctx->ctx, wstring); + + if (buf == nullptr) { + *result = length; + } else { + const char16_t *chars = reinterpret_cast( + LEPUS_GetStringChars(env->ctx->ctx, wstring)); + size_t size{std::min(length, bufsize - 1)}; + std::copy(chars, chars + size, buf); + buf[size] = '\0'; + if (result != nullptr) { + *result = size; + } + } + + JS_FreeValue_Comp(env->ctx->ctx, wstring); + + return napi_clear_last_error(env); +} + +napi_status napi_coerce_to_bool(napi_env env, napi_value value, + napi_value *result) { + *result = env->ctx->CreateHandle(LEPUS_NewBool( + env->ctx->ctx, LEPUS_ToBool(env->ctx->ctx, ToJSValue(value)))); + return napi_clear_last_error(env); +} + +napi_status napi_coerce_to_number(napi_env env, napi_value value, + napi_value *result) { + double number; + int ret = LEPUS_ToFloat64(env->ctx->ctx, &number, ToJSValue(value)); + + CHECK_QJS(env, ret != -1); + + *result = env->ctx->CreateHandle(LEPUS_NewFloat64(env->ctx->ctx, number)); + return napi_clear_last_error(env); +} + +napi_status napi_coerce_to_object(napi_env env, napi_value value, + napi_value *result) { + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + napi_value global{}, object_func{}, object_value{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Object", &object_func)); + CHECK_NAPI( + napi_call_function(env, global, object_func, 1, &value, &object_value)); + *result = scope.Escape(object_value); + + return napi_clear_last_error(env); +} + +napi_status napi_coerce_to_string(napi_env env, napi_value value, + napi_value *result) { + LEPUSValue str = LEPUS_ToString(env->ctx->ctx, ToJSValue(value)); + CHECK_QJS(env, !LEPUS_IsException(str)); + *result = env->ctx->CreateHandle(str); + return napi_clear_last_error(env); +} + +napi_status napi_wrap(napi_env env, napi_value js_object, void *native_object, + napi_finalize finalize_cb, void *finalize_hint, + napi_ref *result) { + return qjsimpl::Wrap( + env, js_object, native_object, finalize_cb, finalize_hint, result); +} + +napi_status napi_unwrap(napi_env env, napi_value obj, void **result) { + return qjsimpl::Unwrap(env, obj, result, qjsimpl::KeepWrap); +} + +napi_status napi_remove_wrap(napi_env env, napi_value obj, void **result) { + return qjsimpl::Unwrap(env, obj, result, qjsimpl::RemoveWrap); +} + +napi_status napi_create_external(napi_env env, void *data, + napi_finalize finalize_cb, void *finalize_hint, + napi_value *result) { + qjsimpl::NativeInfo *info; + LEPUSValue value = qjsimpl::External::Create(env, &info); + + CHECK_QJS(env, !LEPUS_IsException(value)); + + info->Data(data); + + qjsimpl::Reference::New(env, value, info, 0, true, finalize_cb, data, + finalize_hint); + + *result = env->ctx->CreateHandle(value); + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_external(napi_env env, napi_value value, + void **result) { + qjsimpl::NativeInfo *info = qjsimpl::NativeInfo::Get(ToJSValue(value)); + *result = info != nullptr && info->Type() == qjsimpl::NativeType::External + ? info->Data() + : nullptr; + return napi_clear_last_error(env); +} + +// Set initial_refcount to 0 for a weak reference, >0 for a strong reference. +napi_status napi_create_reference(napi_env env, napi_value value, + uint32_t initial_refcount, napi_ref *result) { + LEPUSValueConst val = ToJSValue(value); + + if (LEPUS_VALUE_GET_NORM_TAG(val) != LEPUS_TAG_OBJECT) { + return napi_set_last_error(env, napi_object_expected); + } + + qjsimpl::Reference *reference = qjsimpl::Reference::New( + env, val, qjsimpl::NativeInfo::Get(val), initial_refcount, false); + + *result = reinterpret_cast(reference); + + return napi_clear_last_error(env); +} + +// Deletes a reference. The referenced value is released, and may be GC'd +// unless there are other references to it. +napi_status napi_delete_reference(napi_env env, napi_ref ref) { + qjsimpl::Reference::Delete(reinterpret_cast(ref)); + + return napi_clear_last_error(env); +} + +// Increments the reference count, optionally returning the resulting count. +// After this call the reference will be a strong reference because its refcount +// is >0, and the referenced object is effectively "pinned". Calling this when +// the refcount is 0 and the target is unavailable results in an error. +napi_status napi_reference_ref(napi_env env, napi_ref ref, uint32_t *result) { + qjsimpl::Reference *reference = reinterpret_cast(ref); + uint32_t count = reference->Ref(); + + if (result != nullptr) { + *result = count; + } + + return napi_clear_last_error(env); +} + +// Decrements the reference count, optionally returning the resulting count. +// If the result is 0 the reference is now weak and the object may be GC'd at +// any time if there are no other references. Calling this when the refcount +// is already 0 results in an error. +napi_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t *result) { + qjsimpl::Reference *reference = reinterpret_cast(ref); + + if (reference->RefCount() == 0) { + return napi_set_last_error(env, napi_generic_failure); + } + + uint32_t count = reference->Unref(); + + if (result != nullptr) { + *result = count; + } + + return napi_clear_last_error(env); +} + +// Attempts to get a referenced value. If the reference is weak, the value +// might no longer be available, in that case the call is still successful but +// the result is NULL. +napi_status napi_get_reference_value(napi_env env, napi_ref ref, + napi_value *result) { + qjsimpl::Reference *reference = reinterpret_cast(ref); + + *result = reference->Get(); + return napi_clear_last_error(env); +} + +//// Stub implementation of handle scope apis for QuickLEPUS. +//napi_status napi_open_context_scope(napi_env env, napi_context_scope* result) { +// *result = reinterpret_cast(1); +// return napi_clear_last_error(env); +//} +// +//// Stub implementation of handle scope apis for QuickJS. +//napi_status napi_close_context_scope(napi_env env, napi_context_scope scope) { +// return napi_clear_last_error(env); +//} + +napi_status napi_open_handle_scope(napi_env env, napi_handle_scope *result) { + *result = reinterpret_cast( + new napi_handle_scope__(env, env->ctx->ctx, reset_napi_env)); + env->ctx->open_handle_scopes++; + return napi_clear_last_error(env); +} + +napi_status napi_close_handle_scope(napi_env env, napi_handle_scope scope) { + if (env->ctx->open_handle_scopes == 0) { + return napi_handle_scope_mismatch; + } + env->ctx->open_handle_scopes--; + + delete reinterpret_cast(scope); + return napi_clear_last_error(env); +} + +napi_status napi_open_escapable_handle_scope( + napi_env env, napi_escapable_handle_scope *result) { + *result = reinterpret_cast( + new napi_handle_scope__(env, env->ctx->ctx, reset_napi_env)); + env->ctx->open_handle_scopes++; + return napi_clear_last_error(env); +} + +napi_status napi_close_escapable_handle_scope( + napi_env env, napi_escapable_handle_scope scope) { + if (env->ctx->open_handle_scopes == 0) { + return napi_handle_scope_mismatch; + } + env->ctx->open_handle_scopes--; + + delete reinterpret_cast(scope); + return napi_clear_last_error(env); +} + +napi_status napi_escape_handle(napi_env env, napi_escapable_handle_scope scope, + napi_value escapee, napi_value *result) { + *result = reinterpret_cast(scope)->Escape(escapee); + return napi_clear_last_error(env); +} + +napi_status napi_new_instance(napi_env env, napi_value constructor, size_t argc, + const napi_value *argv, napi_value *result) { + if (argc > 0) { + CHECK_ARG(env, argv); + } + + ArgsConverter args( + argc, const_cast(argv)); + + js_enter(env); + + LEPUSValue instance = + LEPUS_CallConstructor(env->ctx->ctx, ToJSValue(constructor), argc, args); + + js_exit(env); + + CHECK_QJS(env, !LEPUS_IsException(instance)); + + *result = env->ctx->CreateHandle(instance); + + return napi_clear_last_error(env); +} + +napi_status napi_instanceof(napi_env env, napi_value object, + napi_value constructor, bool *result) { + int ret = LEPUS_IsInstanceOf(env->ctx->ctx, ToJSValue(object), + ToJSValue(constructor)); + + CHECK_QJS(env, ret != -1); + + *result = ret; + + return napi_clear_last_error(env); +} + +napi_status napi_is_exception_pending(napi_env env, bool *result) { + *result = static_cast(env->ctx->last_exception); + return napi_clear_last_error(env); +} + +napi_status napi_get_and_clear_last_exception(napi_env env, + napi_value *result) { + if (!env->ctx->last_exception) { + return napi_get_undefined(env, result); + } else { + *result = env->ctx->CreateHandle(*env->ctx->last_exception); + env->ctx->last_exception.reset(); + env->ctx->last_exception_pVal.Reset(true); + } + + return napi_clear_last_error(env); +} + +std::string get_lepus_error_stack(LEPUSContext *ctx, LEPUSValue &value) { + std::string err; + if (LEPUS_IsError(ctx, value) || LEPUS_IsException(value)) { + LEPUSValue val = LEPUS_GetPropertyStr(ctx, value, "stack"); + if (!LEPUS_IsUndefined(val)) { + const char *stack = LEPUS_ToCString(ctx, val); + if (stack) { + err.append(stack); + } + JS_FreeCString_Comp(ctx, stack); + } + JS_FreeValue_Comp(ctx, val); + } + return err; +} + +napi_status napi_get_unhandled_rejection_exception(napi_env env, + napi_value *result) { + LEPUSContext *ctx = env->ctx->ctx; + std::string error_result; + while (LEPUS_MoveUnhandledRejectionToException(ctx)) { + LEPUSValue exception = LEPUS_GetException(ctx); + env->ctx->CreateHandle(exception, true); + const char *error_message = LEPUS_ToCString(ctx, exception); + if (error_message) { + error_result.append("message: "); + error_result.append(error_message); + } + JS_FreeCString_Comp(ctx, error_message); + + std::string error_stack = get_lepus_error_stack(ctx, exception); + error_result.append("\nstack: "); + error_result.append(error_stack); + error_result.append("\n"); + } + LEPUSValue result_lepus = LEPUS_NewString(ctx, error_result.c_str()); + *result = env->ctx->CreateHandle(result_lepus); + return napi_clear_last_error(env); +} + +napi_status napi_get_own_property_descriptor(napi_env env, napi_value obj, + napi_value prop, + napi_value *result) { + LEPUSValueConst args[2]; + args[0] = ToJSValue(obj); + args[1] = ToJSValue(prop); + LEPUSValue descriptor = lepus_object_getOwnPropertyDescriptor( + env->ctx->ctx, LEPUS_UNDEFINED, 2, args, 0); + CHECK_QJS(env, !LEPUS_IsException(descriptor)); + *result = env->ctx->CreateHandle(descriptor); + return napi_clear_last_error(env); +} + +napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool *result) { + LEPUSClassID id = LEPUS_GetClassID(env->ctx->ctx, ToJSValue(value)); + *result = id == JS_CLASS_ARRAY_BUFFER || id == JS_CLASS_SHARED_ARRAY_BUFFER; + return napi_clear_last_error(env); +} + +napi_status napi_create_arraybuffer(napi_env env, size_t byte_length, + void **data, napi_value *result) { + void *bytes = std::malloc(byte_length); + // v8 use zero initialized + std::memset(bytes, 0, byte_length); + LEPUSValue buffer = LEPUS_NewArrayBuffer( + env->ctx->ctx, static_cast(bytes), byte_length, + [](LEPUSRuntime *rt, void *opaque, void *ptr) { std::free(ptr); }, + nullptr, false); + + if (LEPUS_IsException(buffer)) { + std::free(bytes); + CHECK_QJS(env, false); + } + + *data = bytes; + *result = env->ctx->CreateHandle(buffer); + + return napi_clear_last_error(env); +} + +napi_status napi_create_external_arraybuffer(napi_env env, void *external_data, + size_t byte_length, + napi_finalize finalize_cb, + void *finalize_hint, + napi_value *result) { + LEPUSValue buffer = LEPUS_NewArrayBuffer( + env->ctx->ctx, static_cast(external_data), byte_length, + [](LEPUSRuntime *rt, void *opaque, void *ptr) {}, nullptr, false); + + CHECK_QJS(env, !LEPUS_IsException(buffer)); + + if (finalize_cb != nullptr) { + qjsimpl::Reference::New(env, buffer, nullptr, 0, true, finalize_cb, + external_data, finalize_hint); + } + + *result = env->ctx->CreateHandle(buffer); + + return napi_clear_last_error(env); +} + +napi_status napi_get_arraybuffer_info(napi_env env, napi_value arraybuffer, + void **data, size_t *byte_length) { + size_t size; + uint8_t *bytes = + LEPUS_GetArrayBuffer(env->ctx->ctx, &size, ToJSValue(arraybuffer)); + + CHECK_QJS(env, bytes); + + if (data) { + *data = static_cast(bytes); + } + if (byte_length) { + *byte_length = size; + } + return napi_clear_last_error(env); +} + +napi_status napi_is_typedarray(napi_env env, napi_value value, bool *result) { + LEPUSClassID class_id = LEPUS_GetClassID(env->ctx->ctx, ToJSValue(value)); + *result = + class_id >= JS_CLASS_UINT8C_ARRAY && class_id <= JS_CLASS_FLOAT64_ARRAY; + return napi_clear_last_error(env); +} + +#define FOR_EACH_TYPEDARRAY(V) \ + V(napi_uint8_clamped_array, JS_CLASS_UINT8C_ARRAY) \ + V(napi_uint8_array, JS_CLASS_UINT8_ARRAY) \ + V(napi_int8_array, JS_CLASS_INT8_ARRAY) \ + V(napi_int16_array, JS_CLASS_INT16_ARRAY) \ + V(napi_uint16_array, JS_CLASS_UINT16_ARRAY) \ + V(napi_int32_array, JS_CLASS_INT32_ARRAY) \ + V(napi_uint32_array, JS_CLASS_UINT32_ARRAY) \ + V(napi_float32_array, JS_CLASS_FLOAT32_ARRAY) \ + V(napi_float64_array, JS_CLASS_FLOAT64_ARRAY) + +napi_status napi_create_typedarray(napi_env env, napi_typedarray_type type, + size_t length, napi_value arraybuffer, + size_t byte_offset, napi_value *result) { + LEPUSClassID class_id; + + switch (type) { +#define CASE_TYPE(TYPE, CLASS_ID) \ + case TYPE: \ + class_id = CLASS_ID; \ + break; + + FOR_EACH_TYPEDARRAY(CASE_TYPE) + +#undef CASE_TYPE + case napi_bigint64_array: + case napi_biguint64_array: + return napi_set_last_error(env, napi_invalid_arg); + } + + LEPUSValue array = LEPUS_NewTypedArrayWithBuffer( + env->ctx->ctx, ToJSValue(arraybuffer), byte_offset, length, class_id); + + CHECK_QJS(env, !LEPUS_IsException(array)); + + *result = env->ctx->CreateHandle(array); + + return napi_clear_last_error(env); +} + +napi_status napi_is_typedarray_of(napi_env env, napi_value typedarray, + napi_typedarray_type type, bool *result) { + LEPUSClassID class_id = + LEPUS_GetClassID(env->ctx->ctx, ToJSValue(typedarray)); + + switch (type) { +#define CASE_TYPE(TYPE, CLASS_ID) \ + case TYPE: \ + *result = class_id == CLASS_ID; \ + break; + + FOR_EACH_TYPEDARRAY(CASE_TYPE) + +#undef CASE_TYPE + case napi_bigint64_array: + case napi_biguint64_array: + return napi_set_last_error(env, napi_invalid_arg); + } + + return napi_clear_last_error(env); +} + +napi_status napi_get_typedarray_info(napi_env env, napi_value typedarray, + napi_typedarray_type *type, size_t *length, + void **data, napi_value *arraybuffer, + size_t *byte_offset) { + LEPUSValueConst typedarray_val = ToJSValue(typedarray); + LEPUSClassID class_id = LEPUS_GetClassID(env->ctx->ctx, typedarray_val); + + switch (class_id) { +#define CASE_TYPE(TYPE, CLASS_ID) \ + case CLASS_ID: \ + if (type) *type = TYPE; \ + break; + + FOR_EACH_TYPEDARRAY(CASE_TYPE) + +#undef CASE_TYPE + case napi_bigint64_array: + case napi_biguint64_array: + return napi_set_last_error(env, napi_invalid_arg); + } + + uint32_t byte_offset_num; + + { + LEPUSValue val = LEPUS_GetProperty(env->ctx->ctx, typedarray_val, + env->ctx->PROP_BYTEOFFSET); + CHECK_QJS(env, !LEPUS_IsException(val)); + CHECK_QJS(env, LEPUS_ToUint32(env->ctx->ctx, &byte_offset_num, val) != -1); + if (byte_offset) { + *byte_offset = byte_offset_num; + } + } + + if (length) { + LEPUSValue val = + LEPUS_GetProperty(env->ctx->ctx, typedarray_val, env->ctx->PROP_LENGTH); + CHECK_QJS(env, !LEPUS_IsException(val)); + uint32_t length_num; + CHECK_QJS(env, LEPUS_ToUint32(env->ctx->ctx, &length_num, val) != -1); + *length = length_num; + } + + if (data || arraybuffer) { + LEPUSValue val = + LEPUS_GetProperty(env->ctx->ctx, typedarray_val, env->ctx->PROP_BUFFER); + CHECK_QJS(env, !LEPUS_IsException(val)); + if (arraybuffer) { + *arraybuffer = env->ctx->CreateHandle(val); + } + if (data) { + size_t unused; + uint8_t *buffer_start = LEPUS_GetArrayBuffer(env->ctx->ctx, &unused, val); + CHECK_QJS(env, buffer_start); + *data = buffer_start + byte_offset_num; + } + } + + return napi_clear_last_error(env); +} + +napi_status napi_create_dataview(napi_env env, size_t byte_length, + napi_value arraybuffer, size_t byte_offset, + napi_value *result) { + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + napi_value global{}, dataview_ctor{}, data_view{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "DataView", &dataview_ctor)); + + napi_value byte_offset_value{}, byte_length_value{}; + napi_create_double(env, static_cast(byte_offset), &byte_offset_value); + napi_create_double(env, static_cast(byte_length), &byte_length_value); + napi_value args[] = {arraybuffer, byte_offset_value, byte_length_value}; + CHECK_NAPI(napi_new_instance(env, dataview_ctor, 3, args, &data_view)); + + *result = scope.Escape(data_view); + + return napi_clear_last_error(env); +} + +napi_status napi_is_dataview(napi_env env, napi_value value, bool *result) { + LEPUSClassID class_id = LEPUS_GetClassID(env->ctx->ctx, ToJSValue(value)); + *result = class_id == JS_CLASS_DATAVIEW; + return napi_clear_last_error(env); +} + +napi_status napi_is_date(napi_env env, napi_value value, bool *result) { + LEPUSClassID class_id = LEPUS_GetClassID(env->ctx->ctx, ToJSValue(value)); + *result = class_id == JS_CLASS_DATE; + return napi_clear_last_error(env); +} + +napi_status napi_get_dataview_info(napi_env env, napi_value dataview, + size_t *byte_length, void **data, + napi_value *arraybuffer, + size_t *byte_offset) { + LEPUSValueConst dataview_val = ToJSValue(dataview); + LEPUSClassID class_id = LEPUS_GetClassID(env->ctx->ctx, dataview_val); + + if (class_id != JS_CLASS_DATAVIEW) { + return napi_set_last_error(env, napi_invalid_arg); + } + + uint32_t byte_offset_num; + + { + LEPUSValue val = LEPUS_GetProperty(env->ctx->ctx, dataview_val, + env->ctx->PROP_BYTEOFFSET); + CHECK_QJS(env, !LEPUS_IsException(val)); + CHECK_QJS(env, LEPUS_ToUint32(env->ctx->ctx, &byte_offset_num, val) != -1); + if (byte_offset) { + *byte_offset = byte_offset_num; + } + } + + if (byte_length) { + LEPUSValue val = LEPUS_GetProperty(env->ctx->ctx, dataview_val, + env->ctx->PROP_BYTELENGTH); + CHECK_QJS(env, !LEPUS_IsException(val)); + uint32_t byte_length_num; + CHECK_QJS(env, LEPUS_ToUint32(env->ctx->ctx, &byte_length_num, val) != -1); + *byte_length = byte_length_num; + } + + if (data || arraybuffer) { + LEPUSValue val = + LEPUS_GetProperty(env->ctx->ctx, dataview_val, env->ctx->PROP_BUFFER); + CHECK_QJS(env, !LEPUS_IsException(val)); + if (arraybuffer) { + *arraybuffer = env->ctx->CreateHandle(val); + } + if (data) { + size_t unused; + uint8_t *buffer_start = LEPUS_GetArrayBuffer(env->ctx->ctx, &unused, val); + CHECK_QJS(env, buffer_start); + *data = buffer_start + byte_offset_num; + } + } + + return napi_clear_last_error(env); +} + +struct napi_deferred__ { + qjsimpl::NAPIPersistent resolve; + qjsimpl::NAPIPersistent reject; + + bool has_init = false; + + static napi_value Callback(napi_env env, napi_callback_info cbinfo) { + napi_deferred__ *deferred = + static_cast(cbinfo->data); + deferred->has_init = true; + deferred->resolve.Reset(env, ToJSValue(cbinfo->argv[0]), nullptr, + env->ctx->ctx, false); + deferred->reject.Reset(env, ToJSValue(cbinfo->argv[1]), nullptr, + env->ctx->ctx, false); + return nullptr; + } + + ~napi_deferred__() { + resolve.Reset(true); + reject.Reset(true); + } +}; + +napi_status napi_create_promise(napi_env env, napi_deferred *deferred, + napi_value *promise) { + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + napi_value global{}, promise_ctor{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Promise", &promise_ctor)); + + std::unique_ptr deferred_val = + std::make_unique(); + napi_value executor{}, promise_val{}; + CHECK_NAPI(napi_create_function(env, "executor", NAPI_AUTO_LENGTH, + napi_deferred__::Callback, + deferred_val.get(), &executor)); + CHECK_NAPI(napi_new_instance(env, promise_ctor, 1, &executor, &promise_val)); + + if (!deferred_val->has_init) { + return napi_set_last_error(env, napi_generic_failure); + } + + *promise = scope.Escape(promise_val); + *deferred = deferred_val.release(); + + return napi_clear_last_error(env); +} + +napi_status napi_is_promise(napi_env env, napi_value promise, + bool *is_promise) { + napi_handle_scope__ scope(env, env->ctx->ctx, reset_napi_env); + + napi_value global{}, promise_ctor{}; + CHECK_NAPI(napi_get_global(env, &global)); + CHECK_NAPI(napi_get_named_property(env, global, "Promise", &promise_ctor)); + CHECK_NAPI(napi_instanceof(env, promise, promise_ctor, is_promise)); + + return napi_clear_last_error(env); +} + +napi_status napi_run_script(napi_env env, napi_value script, + napi_value *result) { + return napi_run_script_source(env, script, "ctx->ctx, &size, ToJSValue(script)); + js_enter(env); + result_val = LEPUS_Eval(env->ctx->ctx, src, size, + source_url, LEPUS_EVAL_TYPE_GLOBAL); + JS_FreeCString_Comp(env->ctx->ctx, src); + js_exit(env); + + CHECK_QJS(env, !LEPUS_IsException(result_val)); + + *result = env->ctx->CreateHandle(result_val); + return napi_clear_last_error(env); +} + +#ifdef ENABLE_CODECACHE + +napi_status napi_run_code_cache(napi_env env, const uint8_t* data, int length, + napi_value* result) { + LEPUSValue result_val = LEPUS_UNDEFINED; + LEPUSValue top_func = + LEPUS_EvalBinary(env->ctx->ctx, data, static_cast(length), + LEPUS_EVAL_BINARY_LOAD_ONLY); + if (!LEPUS_IsException(top_func) && !LEPUS_IsUndefined(top_func)) { + LEPUSValue global = LEPUS_GetGlobalObject(env->ctx->ctx); + env->ctx->CreateHandle(top_func, true); + result_val = LEPUS_EvalFunction(env->ctx->ctx, top_func, global); + } + CHECK_QJS(env, !LEPUS_IsException(result_val)); + + *result = env->ctx->CreateHandle(result_val); + return napi_clear_last_error(env); +} + +napi_status napi_run_script_cache(napi_env env, const char* script, + size_t length, const char* filename, + napi_value* result) { + if (length == NAPI_AUTO_LENGTH) { + length = std::strlen(script); + } + + LEPUSValue result_val = LEPUS_UNINITIALIZED; + { + int len = -1; + const uint8_t* data = nullptr; + env->napi_get_code_cache(env, filename, &data, &len); + if (data) { + // TODO(yang): check whether the script is obsolate + LOG_TIME_START(); + LEPUSValue top_func = + LEPUS_EvalBinary(env->ctx->ctx, data, static_cast(len), + LEPUS_EVAL_BINARY_LOAD_ONLY); + if (!LEPUS_IsException(top_func) && !LEPUS_IsUndefined(top_func)) { + LEPUSValue global = LEPUS_GetGlobalObject(env->ctx->ctx); + env->ctx->CreateHandle(top_func, true); + result_val = LEPUS_EvalFunction(env->ctx->ctx, top_func, global); + } + LOG_TIME_END("----- script eval with cache -----"); + } else if (len == 0) { + // if len is 0, we need to make cache. + // if len is -1, someone is modifying the cache, we do not make cache. + LOG_TIME_START(); + LEPUSValue top_func = + LEPUS_Eval(env->ctx->ctx, script, length, filename, + LEPUS_EVAL_FLAG_COMPILE_ONLY | LEPUS_EVAL_TYPE_GLOBAL); + CHECK_QJS(env, + !LEPUS_IsException(top_func) && !LEPUS_IsUndefined(top_func)); + LEPUSValue global = LEPUS_GetGlobalObject(env->ctx->ctx); + env->ctx->CreateHandle(top_func, true); + + size_t obj_len; + data = LEPUS_WriteObject(env->ctx->ctx, &obj_len, top_func, + LEPUS_WRITE_OBJ_BYTECODE); + env->napi_store_code_cache(env, filename, data, + static_cast(obj_len)); + js_free_comp(env->ctx->ctx, + reinterpret_cast(const_cast(data))); + result_val = LEPUS_EvalFunction(env->ctx->ctx, top_func, global); + LOG_TIME_END( + "---- evaluating %s and making code cache for it lengthed %d -----", + filename, (int)obj_len); + } + } + if (LEPUS_IsUninitialized(result_val)) { + LOG_TIME_START(); + result_val = LEPUS_Eval(env->ctx->ctx, script, length, + filename ? filename : "", LEPUS_EVAL_TYPE_GLOBAL); + LOG_TIME_END("----- script eval without cache -----"); + } + CHECK_QJS(env, !LEPUS_IsException(result_val)); + + *result = env->ctx->CreateHandle(result_val); + return napi_clear_last_error(env); +} + +// `data` memory need to be freed from outside +napi_status napi_gen_code_cache(napi_env env, const char* script, + size_t script_len, const uint8_t** data, + int* length) { + if (script_len == NAPI_AUTO_LENGTH) { + script_len = std::strlen(script); + } + LEPUSValue top_func = + LEPUS_Eval(env->ctx->ctx, script, script_len, "", + LEPUS_EVAL_FLAG_COMPILE_ONLY | LEPUS_EVAL_TYPE_GLOBAL); + CHECK_QJS(env, !LEPUS_IsException(top_func) && !LEPUS_IsUndefined(top_func)); + env->ctx->CreateHandle(top_func, true); + + size_t obj_len; + uint8_t* cache = LEPUS_WriteObject(env->ctx->ctx, &obj_len, top_func, + LEPUS_WRITE_OBJ_BYTECODE); + *data = static_cast(std::malloc(obj_len)); + *length = static_cast(obj_len); + memcpy(static_cast(const_cast(*data)), + static_cast(cache), obj_len); + js_free_comp(env->ctx->ctx, reinterpret_cast(cache)); + + return napi_clear_last_error(env); +} + +#endif // ENABLE_CODECACHE + +napi_status napi_add_finalizer(napi_env env, napi_value js_object, + void *native_object, napi_finalize finalize_cb, + void *finalize_hint, napi_ref *result) { + return qjsimpl::Wrap(env, js_object, native_object, + finalize_cb, finalize_hint, result); +} + +napi_status napi_adjust_external_memory(napi_env env, int64_t change_in_bytes, + int64_t *adjusted_value) { + // TODO: Determine if QJS needs or is able to do anything here + // For now, we can lie and say that we always adjusted more memory + *adjusted_value = change_in_bytes; + + return napi_clear_last_error(env); +} + +napi_status napi_set_instance_data(napi_env env, uint64_t key, void *data, + napi_finalize finalize_cb, + void *finalize_hint) { + auto it = env->ctx->instance_data_registry.find(key); + if (it != env->ctx->instance_data_registry.end()) { + return napi_invalid_arg; + } + + env->ctx->instance_data_registry[key] = + qjsimpl::RefBase::New(env, 0, true, finalize_cb, data, finalize_hint); + + return napi_clear_last_error(env); +} + +napi_status napi_get_instance_data(napi_env env, uint64_t key, void **data) { + auto it = env->ctx->instance_data_registry.find(key); + if (it == env->ctx->instance_data_registry.end()) { + *data = nullptr; + } else { + qjsimpl::RefBase *idata = static_cast(it->second); + + *data = idata->Data(); + } + + return napi_clear_last_error(env); +} + +void napi_attach_quickjs(napi_env env, LEPUSContext *context) { + env->ctx = new napi_context__(env, context); + + InitNapiScope(context); +} + +void napi_detach_quickjs(napi_env env) { + LEPUSContext *ctx = env->ctx->ctx; + delete env->ctx; + env->ctx = nullptr; + FreeNapiScope(ctx); +} + +LEPUSContext *napi_get_env_context_quickjs(napi_env env) { + return env->ctx->ctx; +} + +LEPUSValue napi_js_value_to_quickjs_value(napi_env env, napi_value value) { + return JS_DupValue_Comp(env->ctx->ctx, ToJSValue(value)); +} + +napi_value napi_quickjs_value_to_js_value(napi_env env, LEPUSValue value) { + return env->ctx->CreateHandle(value); +} + +inline napi_handle_scope__::napi_handle_scope__(napi_env env, LEPUSContext *ctx, + napi_func *func) + : env_(env), ctx_(ctx), handle_tail_(nullptr), reset_napi_env(func) { + is_gc = env_->ctx->gc_enable; + if (is_gc) { + prev_ = reinterpret_cast(GetNapiScope(ctx_)); + SetNapiScope(ctx_, this); + } else { + prev_ = env_->ctx->handle_scope; + env_->ctx->handle_scope = this; + } +} + +inline napi_value napi_handle_scope__::CreateHandle(LEPUSValue v) { + Handle *h = new Handle{.value = v, .prev = handle_tail_}; + handle_tail_ = h; + return ToNapi(&(h->value)); +} + +inline napi_value napi_handle_scope__::Escape(napi_value v) { + return prev_->CreateHandle(JS_DupValue_Comp(env_->ctx->ctx, ToJSValue(v))); +} + +napi_status primjs_execute_pending_jobs(napi_env env) { + int error; + do { + LEPUSContext *context; + error = LEPUS_ExecutePendingJob(LEPUS_GetRuntime(env->ctx->ctx), &context); + if (error == -1) { + return napi_set_last_error(env, napi_pending_exception); + } + } while (error != 0); + + return napi_clear_last_error(env); +} + + +typedef struct NapiHostObjectInfo { + void *data; + napi_ref ref; + napi_finalize finalize_cb; + bool is_array; + napi_ref getter; + napi_ref setter; +} NapiHostObjectInfo; + +void host_object_finalizer(LEPUSRuntime *rt, LEPUSValue value) { + napi_env env = (napi_env) napi_context__::GetEnv(rt); + NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(value, + env->ctx->napiHostObjectClassId); + if (info->finalize_cb) { + info->finalize_cb(env, info->data, NULL); + } + if (info->is_array) { + napi_delete_reference(env, info->getter); + napi_delete_reference(env, info->setter); + } + + napi_delete_reference(env, info->ref); + delete info; +} + +int host_object_set(LEPUSContext *ctx, LEPUSValue obj, JSAtom atom, + LEPUSValue value, LEPUSValue receiver, int flags) { + napi_env env = (napi_env) napi_context__::GetEnv(LEPUS_GetRuntime(ctx)); + NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(obj, + env->ctx->napiHostObjectClassId); + if (info != NULL) { + auto *target = reinterpret_cast(info->ref); + if (info->is_array) { + LEPUSValue atom_val = LEPUS_AtomToValue(ctx, atom); + + auto *setter = reinterpret_cast(info->setter); + + LEPUSValue argv[4] = { + ToJSValue(target->Get()), + atom_val, + value, + obj + }; + + LEPUSValue result = LEPUS_Call(ctx, ToJSValue(setter->Get()), LEPUS_UNDEFINED, 4, argv); + + JS_FreeValue_Comp(ctx, atom_val); + + if (LEPUS_IsException(result)) return -1; + + return true; + } + return LEPUS_SetProperty(ctx, ToJSValue(target->Get()), atom, JS_DupValue_Comp(ctx, value)); + } + return true; +} + +LEPUSValue host_object_get(LEPUSContext *ctx, LEPUSValue obj, JSAtom atom, LEPUSValue receiver) { + napi_env env = (napi_env) napi_context__::GetEnv(LEPUS_GetRuntime(ctx)); + NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(obj, + env->ctx->napiHostObjectClassId); + if (info != NULL) { + auto *target = reinterpret_cast(info->ref); + if (info->is_array) { + LEPUSValue atom_val = LEPUS_AtomToValue(ctx, atom); + auto *getter = reinterpret_cast(info->getter); + LEPUSValue argv[3] = { + ToJSValue(target->Get()), + atom_val, + obj + }; + LEPUSValue value = LEPUS_Call(ctx, ToJSValue(getter->Get()), LEPUS_UNDEFINED, 3, argv); + JS_FreeValue_Comp(ctx, atom_val); + return value; + } + return LEPUS_GetProperty(ctx, ToJSValue(target->Get()), atom); + } + return LEPUS_UNDEFINED; +} + +int host_object_has(LEPUSContext *ctx, LEPUSValue obj, JSAtom atom) { + napi_env env = (napi_env) napi_context__::GetEnv(LEPUS_GetRuntime(ctx)); + NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(obj, + env->ctx->napiHostObjectClassId); + if (info != NULL) { + auto *target = reinterpret_cast(info->ref); + return LEPUS_HasProperty(ctx, ToJSValue(target->Get()), atom); + } + return false; +} + +static int host_object_delete(LEPUSContext *ctx, LEPUSValue obj, JSAtom atom) { + napi_env env = (napi_env) napi_context__::GetEnv(LEPUS_GetRuntime(ctx)); + NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(obj, + env->ctx->napiHostObjectClassId); + if (info != NULL) { + auto *target = reinterpret_cast(info->ref); + return LEPUS_DeleteProperty(ctx, ToJSValue(target->Get()), atom, 0); + } + return true; +} + +LEPUSClassExoticMethods NapiHostObjectExoticMethods = { + .get_own_property = nullptr, + .get_own_property_names = nullptr, + .delete_property = host_object_delete, + .define_own_property = nullptr, + .has_property = host_object_has, + .get_property = host_object_get, + .set_property = host_object_set, +}; + +napi_status +napi_create_host_object(napi_env env, napi_value value, napi_finalize finalize, void *data, + bool is_array, napi_value getter, napi_value setter, napi_value *result) { + CHECK_ARG(env, result); + + if (env->ctx->napi_host_object_class_init == 0) { + LEPUSClassDef NapiHostObjectClassDef = {"NapiHostObject", host_object_finalizer, NULL, NULL, + &NapiHostObjectExoticMethods}; + LEPUS_NewClass(env->ctx->rt, env->ctx->napiHostObjectClassId, &NapiHostObjectClassDef); + env->ctx->napi_host_object_class_init = 1; + } + + napi_value constructor; + napi_get_named_property(env, value, "constructor", &constructor); + + napi_value prototype; + napi_get_named_property(env, constructor, "prototype", &prototype); + + LEPUSValue jsValue = LEPUS_NewObjectClass(env->ctx->ctx, env->ctx->napiHostObjectClassId); + LEPUS_SetPrototype(env->ctx->ctx, jsValue, ToJSValue(prototype)); + + NapiHostObjectInfo *info = new NapiHostObjectInfo; + info->data = data; + if (finalize) { + info->finalize_cb = finalize; + } else { + info->finalize_cb = NULL; + } + info->is_array = is_array; + + if (is_array) { + if (getter) napi_create_reference(env, getter, 1, &info->getter); + if (setter) napi_create_reference(env, setter, 1, &info->setter); + } + + napi_create_reference(env, value, 1, &info->ref); + + LEPUS_SetOpaque(jsValue, info); + + *result = env->ctx->CreateHandle(jsValue); + return napi_ok; +} + +napi_status napi_get_host_object_data(napi_env env, napi_value object, void **data) { + CHECK_ARG(env, object); + CHECK_ARG(env, data); + + LEPUSValue jsValue = ToJSValue(object); + + + if (!LEPUS_IsObject(jsValue)) { + return napi_set_last_error(env, napi_object_expected); + } + + NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(jsValue, + env->ctx->napiHostObjectClassId); + if (info) { + *data = info->data; + } else { + *data = NULL; + } + + return napi_clear_last_error(env); +} + +napi_status napi_is_host_object(napi_env env, napi_value object, bool *result) { + CHECK_ARG(env, object); + + LEPUSValue jsValue = ToJSValue(object); + + if (!LEPUS_IsObject(jsValue)) { + return napi_set_last_error(env, napi_object_expected); + } + + void *data = LEPUS_GetOpaque(jsValue, + env->ctx->napiHostObjectClassId); + if (data != NULL) { + *result = true; + } else { + *result = false; + } + + return napi_clear_last_error(env); +} \ No newline at end of file diff --git a/NativeScript/napi/android/primjs/primjs-api.h b/NativeScript/napi/android/primjs/primjs-api.h new file mode 100644 index 000000000..c7953eef9 --- /dev/null +++ b/NativeScript/napi/android/primjs/primjs-api.h @@ -0,0 +1,572 @@ +/** + * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. + * + * Use of this source code is governed by a MIT license that can be + * found in the LICENSE file in the root of the source tree. + */ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +#ifndef SRC_NAPI_QUICKJS_JS_NATIVE_API_QUICKJS_H_ +#define SRC_NAPI_QUICKJS_JS_NATIVE_API_QUICKJS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus +#include "quickjs/include/quickjs.h" +#ifdef __cplusplus +} +#endif // __cplusplus + +#include "gc/persistent-handle.h" +#include "gc/trace-gc.h" +#include "js_native_api.h" +#include "js_native_api_types.h" +#include "napi_env_quickjs.h" + + +typedef struct napi_env_data__ *napi_env_data; +typedef struct napi_state__ { + napi_extended_error_info last_error; + napi_env_data env_data; +} napi_state; + +typedef struct napi_context__ *napi_context; + +struct napi_env__ { + napi_state state; + napi_runtime rt; + napi_context ctx; + int js_enter_state; +}; + +inline napi_status napi_clear_last_error(napi_env env) { + env->state.last_error.error_code = napi_ok; + env->state.last_error.engine_error_code = 0; + return napi_ok; +} + +inline napi_status napi_set_last_error(napi_env env, napi_status error_code) { + env->state.last_error.error_code = error_code; + return error_code; +} + +inline napi_value ToNapi(LEPUSValueConst *v) { + return reinterpret_cast(v); +} + +inline LEPUSValueConst ToJSValue(napi_value v) { + return *reinterpret_cast(v); +} + +inline LEPUSValue JS_DupValue_Comp(LEPUSContext *ctx, LEPUSValueConst v) { + if (!LEPUS_IsGCMode(ctx)) { + return LEPUS_DupValue(ctx, v); + } + return v; +} + +inline void JS_FreeValue_Comp(LEPUSContext *ctx, LEPUSValue v) { + if (!LEPUS_IsGCMode(ctx)) { + LEPUS_FreeValue(ctx, v); + } +} + +inline void JS_FreeAtom_Comp(LEPUSContext *ctx, JSAtom v) { + if (!LEPUS_IsGCMode(ctx)) { + LEPUS_FreeAtom(ctx, v); + } +} + +inline void JS_FreeCString_Comp(LEPUSContext *ctx, const char *ptr) { + if (!LEPUS_IsGCMode(ctx)) { + LEPUS_FreeCString(ctx, ptr); + } +} + +inline void js_free_comp(LEPUSContext *ctx, void *ptr) { + if (!LEPUS_IsGCMode(ctx)) { + lepus_free(ctx, ptr); + } +} + +namespace qjsimpl { + + class NAPIPersistent; + + class NativeInfo; + + struct WeakInfo { + std::list::const_iterator weak_iter; + std::function cb; + void *cb_arg; + }; + + class NAPIPersistent : public PersistentBase { + public: + /** + * A Persistent with no storage cell. + */ + inline NAPIPersistent() + : PersistentBase(nullptr), + _env(nullptr), + _empty(true), + _native_info(nullptr), + _ctx(nullptr) {} + + /** + * Construct a NAPIPersistent from a Local. + * When the Local is non-empty, a new storage cell is created + * pointing to the same object, and no flags are set. + */ + inline NAPIPersistent(napi_env env, LEPUSValueConst value, + NativeInfo *native_info, LEPUSContext *ctx, + bool is_weak = false); + + inline NAPIPersistent(napi_env env, JSAtom atom, NativeInfo *native_info, + LEPUSContext *ctx, bool is_weak = false); + + NAPIPersistent(const NAPIPersistent &that) = delete; + + NAPIPersistent &operator=(const NAPIPersistent &that) = delete; + + void Reset(bool for_gc = false); + + void Reset(napi_env env, LEPUSValueConst value, NativeInfo *native_info, + LEPUSContext *ctx, bool for_gc = false); + + void Reset(napi_env env, LEPUSContext *ctx, JSAtom atom); + + void SetWeak(void *data, void (*cb)(void *)); + + void ClearWeak(); + + inline ~NAPIPersistent() { Reset(); } + + LEPUSValue Value() const; + + bool IsEmpty() { + if (_ctx != nullptr && LEPUS_IsGCMode(_ctx)) { + return val_ == nullptr; + } else { + return _empty; + } + } + + static void OnFinalize(NAPIPersistent *ref); + + private: + inline LEPUSValue *operator*() const { return this->val_; } + + void ResetWeakInfo(); + + NativeInfo *_get_native_info(); + + napi_env _env; + bool _empty; + LEPUSValue _value; + NativeInfo *_native_info; + std::unique_ptr _weak_info; + LEPUSContext *_ctx; + }; + + class Atom { + public: + Atom() : _env(nullptr), _ctx(nullptr), _atom(0) {} + + Atom(napi_env env, LEPUSContext *ctx, LEPUSValueConst value) + : _env(env), _ctx(ctx), _atom(LEPUS_ValueToAtom(ctx, value)) { + _atom_persist.Reset(_env, _ctx, _atom); + } + + Atom(napi_env env, LEPUSContext *ctx, JSAtom atom) + : _env(env), _ctx(ctx), _atom(atom) { + _atom_persist.Reset(_env, _ctx, _atom); + } + + Atom(napi_env env, LEPUSContext *ctx, const char *str, + size_t length = NAPI_AUTO_LENGTH) + : _env(env), + _ctx(ctx), + _atom(length == NAPI_AUTO_LENGTH ? LEPUS_NewAtom(ctx, str) + : LEPUS_NewAtomLen(ctx, str, length)) { + _atom_persist.Reset(_env, _ctx, _atom); + } + + Atom(Atom &other) + : _env(other._env), + _ctx(other._ctx), + _atom(LEPUS_DupAtom(_ctx, other._atom)) { + _atom_persist.Reset(_env, _ctx, _atom); + } + + Atom(Atom &&other) : _env(other._env), _ctx(other._ctx), _atom(other._atom) { + other._env = nullptr; + other._ctx = nullptr; + other._atom = 0; + other._atom_persist.Reset(true); + _atom_persist.Reset(_env, _ctx, _atom); + } + + ~Atom() { + if (_atom) { + JS_FreeAtom_Comp(_ctx, _atom); + } + _atom_persist.Reset(true); + } + + bool IsValid() { return _atom > 0; } + + operator JSAtom() const { return _atom; } + + private: + napi_env _env; + LEPUSContext *_ctx; + JSAtom _atom; + NAPIPersistent _atom_persist; + }; + + class Value { + public: + Value() : _ctx(nullptr), _val(), pVal(), is_gc(false) {} + + Value(LEPUSContext *ctx, LEPUSValue val) : _ctx(ctx), _val(val), pVal() { + if (ctx) { + is_gc = LEPUS_IsGCMode(ctx); + } + if (is_gc) { + pVal.Reset(nullptr, val, nullptr, ctx, true); + } + } + + ~Value() { + if (is_gc) { + pVal.Reset(true); + } else if (_ctx) { + JS_FreeValue_Comp(_ctx, _val); + } + } + + operator LEPUSValueConst() { + if (is_gc) { + return pVal.Value(); + } else { + return _val; + } + } + + LEPUSValue dup() { + if (is_gc) { + return pVal.Value(); + } else { + return JS_DupValue_Comp(_ctx, _val); + } + } + + LEPUSValue move() { + if (!is_gc) { + _ctx = nullptr; + return _val; + } else { + return pVal.Value(); + } + } + + private: + LEPUSContext *_ctx; + LEPUSValue _val; + NAPIPersistent pVal; + bool is_gc; + }; + + class RefTracker { + public: + RefTracker() {} + + virtual ~RefTracker() {} + + virtual void Finalize(bool isEnvTeardown) {} + + typedef RefTracker RefList; + + inline void Link(RefList *list) { + prev_ = list; + next_ = list->next_; + if (next_ != nullptr) { + next_->prev_ = this; + } + list->next_ = this; + } + + inline void Unlink() { + if (prev_ != nullptr) { + prev_->next_ = next_; + } + if (next_ != nullptr) { + next_->prev_ = prev_; + } + prev_ = nullptr; + next_ = nullptr; + } + + static void FinalizeAll(RefList *list) { + while (list->next_ != nullptr) { + list->next_->Finalize(true); + } + } + + private: + RefList *next_ = nullptr; + RefList *prev_ = nullptr; + }; + + class Reference; + +} // namespace qjsimpl + +inline void reset_napi_env(napi_env env, napi_handle_scope__ *scope); + + + +struct napi_context__ { + napi_env env; + LEPUSRuntime *rt{}; + LEPUSContext *ctx{}; + + LEPUSValue V_NULL{LEPUS_NULL}; + LEPUSValue V_UNDEFINED{LEPUS_UNDEFINED}; + + napi_context__(napi_env env, LEPUSContext *ctx) + : env(env), + rt{LEPUS_GetRuntime(ctx)}, + ctx{ctx}, + PROP_NAME(env, ctx, "name"), + PROP_LENGTH(env, ctx, "length"), + PROP_PROTOTYPE(env, ctx, "prototype"), + PROP_CONSTRUCTOR(env, ctx, "constructor"), + PROP_FINALIZER(env, ctx, "@#fin@#"), + PROP_MESSAGE(env, ctx, "message"), + PROP_CODE(env, ctx, "code"), + PROP_BUFFER(env, ctx, "buffer"), + PROP_BYTELENGTH(env, ctx, "byteLength"), + PROP_BYTEOFFSET(env, ctx, "byteOffset"), + PROP_CTOR_MAGIC(env, ctx, "@#ctor@#"), + gc_enable(LEPUS_IsGCModeRT(rt)) { + env->ctx = this; + napi_context__::rt_to_env_cache.emplace(rt, env); + handle_scope = new napi_handle_scope__(env, ctx, reset_napi_env); + + LEPUSValue gc = LEPUS_NewCFunction(ctx, [](LEPUSContext *ctx, LEPUSValueConst this_val, + int argc, LEPUSValueConst *argv) -> LEPUSValue { + LEPUS_RunGC(LEPUS_GetRuntime(ctx)); + return LEPUS_UNDEFINED; + }, "gc", 0); + auto globalValue = LEPUS_GetGlobalObject(ctx); + LEPUS_SetPropertyStr(ctx, globalValue, "gc", gc); + JS_FreeValue_Comp(ctx, globalValue); + LEPUS_NewClassID(&napiHostObjectClassId); + + // TODO primjs doesn't expose DupContext + } + + ~napi_context__() { + qjsimpl::RefTracker::FinalizeAll(&finalizing_reflist); + qjsimpl::RefTracker::FinalizeAll(&reflist); + + // root handle scope may be used during FinalizeAll + // must delete at last + delete handle_scope; + rt_to_env_cache.erase(rt); + } + + inline void Ref() { refs++; } + + inline void Unref() { + if (--refs == 0) delete this; + } + + template> + inline void CallIntoModule(T &&call, U &&handle_exception) { + int open_handle_scopes_before = open_handle_scopes; + (void) open_handle_scopes_before; + napi_clear_last_error(this->env); + call(this->env); + assert(open_handle_scopes == open_handle_scopes_before); + if (last_exception) { + handle_exception(this->env, *last_exception); + last_exception.reset(); + last_exception_pVal.Reset(true); + } + } + + void CallFinalizer(napi_finalize cb, void *data, void *hint) { + cb(this->env, data, hint); + } + + qjsimpl::RefTracker::RefList reflist; + qjsimpl::RefTracker::RefList finalizing_reflist; + + std::unique_ptr last_exception; + qjsimpl::NAPIPersistent last_exception_pVal; + + std::unordered_map instance_data_registry; + + int napi_host_object_class_init = 0; + LEPUSClassID napiHostObjectClassId = 0; + static std::unordered_map rt_to_env_cache; + + static napi_env GetEnv(LEPUSRuntime* rt) { + auto it = rt_to_env_cache.find(rt); + if (it == rt_to_env_cache.end()) return nullptr; + return it->second; + } + + int open_handle_scopes = 0; + + const qjsimpl::Atom PROP_NAME; + const qjsimpl::Atom PROP_LENGTH; + const qjsimpl::Atom PROP_PROTOTYPE; + const qjsimpl::Atom PROP_CONSTRUCTOR; + const qjsimpl::Atom PROP_FINALIZER; + const qjsimpl::Atom PROP_MESSAGE; + const qjsimpl::Atom PROP_CODE; + const qjsimpl::Atom PROP_BUFFER; + const qjsimpl::Atom PROP_BYTELENGTH; + const qjsimpl::Atom PROP_BYTEOFFSET; + const qjsimpl::Atom PROP_CTOR_MAGIC; + + napi_value CreateHandle(LEPUSValue v, bool only_gc = false) { + if (LEPUS_IsGCMode(ctx)) { + return static_cast(GetNapiScope(ctx))->CreateHandle(v); + } else if (!only_gc) { + return handle_scope->CreateHandle(v); + } + return {}; + } + + void set_handle_scope(napi_handle_scope__ *scope) { handle_scope = scope; } + +private: + int refs = 1; + napi_handle_scope__ *handle_scope{}; + bool gc_enable; + + friend class napi_handle_scope__; +}; + +inline void reset_napi_env(napi_env env, napi_handle_scope__ *scope) { + env->ctx->set_handle_scope(scope); +} + +struct napi_class__qjs { + napi_class__qjs(LEPUSContext *ctx, LEPUSValue proto, LEPUSValue constructor) + : ctx(ctx), proto(proto), constructor(constructor) { + if (LEPUS_IsGCMode(ctx)) { + proto_persist.Reset(nullptr, proto, nullptr, ctx, true); + constructor_persist.Reset(nullptr, constructor, nullptr, ctx, true); + } + } + + ~napi_class__qjs() { + if (LEPUS_IsGCMode(ctx)) { + proto_persist.Reset(true); + constructor_persist.Reset(true); + } else { + LEPUS_FreeValue(ctx, proto); + LEPUS_FreeValue(ctx, constructor); + } + } + + LEPUSValue GetFunction() { return JS_DupValue_Comp(ctx, constructor); } + + LEPUSContext *ctx; + + LEPUSValue proto; + qjsimpl::NAPIPersistent proto_persist; + LEPUSValue constructor; + qjsimpl::NAPIPersistent constructor_persist; +}; + +#define RETURN_STATUS_IF_FALSE(env, condition, status) \ + do { \ + if (!(condition)) { \ + return napi_set_last_error((env), (status)); \ + } \ + } while (0) + +#define CHECK_ARG(env, arg) \ + RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg) + +#define CHECK_QJS(env, condition) \ + do { \ + if (!(condition)) { \ + return napi_set_exception(env, LEPUS_GetException(env->ctx->ctx)); \ + } \ + } while (0) + +// This does not call napi_set_last_error because the expression +// is assumed to be a NAPI function call that already did. +#define CHECK_NAPI(expr) \ + do { \ + napi_status status = (expr); \ + if (status != napi_ok) return status; \ + } while (0) + +namespace qjsimpl { +// Adapter for napi_finalize callbacks. + class Finalizer { + public: + // Some Finalizers are run during shutdown when the napi_env is destroyed, + // and some need to keep an explicit reference to the napi_env because they + // are run independently. + enum EnvReferenceMode { + kNoEnvReference, kKeepEnvReference + }; + + protected: + Finalizer(napi_env env, napi_finalize finalize_callback, void *finalize_data, + void *finalize_hint, EnvReferenceMode refmode = kNoEnvReference) + : _env(env), + _finalize_callback(finalize_callback), + _finalize_data(finalize_data), + _finalize_hint(finalize_hint), + _has_env_reference(refmode == kKeepEnvReference) { + if (_has_env_reference) _env->ctx->Ref(); + } + + ~Finalizer() { + if (_has_env_reference) _env->ctx->Unref(); + } + + public: + static Finalizer *New(napi_env env, napi_finalize finalize_callback = nullptr, + void *finalize_data = nullptr, + void *finalize_hint = nullptr, + EnvReferenceMode refmode = kNoEnvReference) { + return new Finalizer(env, finalize_callback, finalize_data, finalize_hint, + refmode); + } + + static void Delete(Finalizer *finalizer) { delete finalizer; } + + protected: + napi_env _env; + napi_finalize _finalize_callback; + void *_finalize_data; + void *_finalize_hint; + bool _finalize_ran = false; + bool _has_env_reference = false; + }; + +} // namespace qjsimpl + +#endif // SRC_NAPI_QUICKJS_JS_NATIVE_API_QUICKJS_H_ diff --git a/NativeScript/napi/android/quickjs/jsr.cpp b/NativeScript/napi/android/quickjs/jsr.cpp new file mode 100644 index 000000000..b9ec43e77 --- /dev/null +++ b/NativeScript/napi/android/quickjs/jsr.cpp @@ -0,0 +1,75 @@ +#include "jsr.h" +#include "quicks-runtime.h" + +JSR::JSR() = default; +tns::SimpleMap JSR::env_to_jsr_cache; + +napi_status js_create_runtime(napi_runtime *runtime) { + return qjs_create_runtime(runtime); +} +napi_status js_create_napi_env(napi_env *env, napi_runtime runtime) { + napi_status status = qjs_create_napi_env(env, runtime); + JSR::env_to_jsr_cache.Insert((*env), new JSR()); + return status; +} + +napi_status js_set_runtime_flags(const char *flags) { + return napi_ok; +} + +napi_status js_lock_env(napi_env env) { + auto jsr = JSR::env_to_jsr_cache.Get(env); + if (jsr) jsr->lock(); + return napi_ok; +} + +napi_status js_unlock_env(napi_env env) { + auto jsr = JSR::env_to_jsr_cache.Get(env); + if (jsr) jsr->unlock(); + + return napi_ok; +} + +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); + return qjs_free_napi_env(env); +} + +napi_status js_free_runtime(napi_runtime runtime) { + return qjs_free_runtime(runtime); +} + +napi_status js_execute_script(napi_env env, + napi_value script, + const char *file, + napi_value *result) { + return qjs_execute_script(env, script, file, result); +} + +napi_status js_execute_pending_jobs(napi_env env) { + return qjs_execute_pending_jobs(env); +} + +napi_status +js_adjust_external_memory(napi_env env, int64_t changeInBytes, int64_t *externalMemory) { + napi_adjust_external_memory(env, changeInBytes, externalMemory); + return napi_ok; +} + +napi_status js_cache_script(napi_env env, const char *source, const char *file) { + return napi_ok; +} + +napi_status js_run_cached_script(napi_env env, const char *file, napi_value script, void *cache, + napi_value *result) { + return napi_ok; +} + + +napi_status js_get_runtime_version(napi_env env, napi_value *version) { + napi_create_string_utf8(env, "QuickJS", NAPI_AUTO_LENGTH, version); + + return napi_ok; +} diff --git a/NativeScript/napi/android/quickjs/jsr.h b/NativeScript/napi/android/quickjs/jsr.h new file mode 100644 index 000000000..e6d62c9f5 --- /dev/null +++ b/NativeScript/napi/android/quickjs/jsr.h @@ -0,0 +1,56 @@ +// +// Created by Ammar Ahmed on 01/12/2024. +// + +#ifndef TEST_APP_JSR_H +#define TEST_APP_JSR_H +#include "js_native_api.h" +#include "jsr_common.h" +#include "quicks-runtime.h" +#include "mutex" +#include +#include "ConcurrentMap.h" + +class JSR { +public: + JSR(); + std::recursive_mutex js_mutex; + void lock() { + js_mutex.lock(); + } + void unlock() { + js_mutex.unlock(); + } + + static tns::SimpleMap env_to_jsr_cache; +}; + +class NapiScope { +public: + explicit NapiScope(napi_env env, bool open_handle = true) + : env_(env) + { + js_lock_env(env_); + qjs_update_stack_top(env); + if (open_handle) { + napi_open_handle_scope(env_, &napiHandleScope_); + } else { + napiHandleScope_ = nullptr; + } + } + + ~NapiScope() { + if (napiHandleScope_) { + napi_close_handle_scope(env_, napiHandleScope_); + } + js_unlock_env(env_); + } + +private: + napi_env env_; + napi_handle_scope napiHandleScope_; +}; + +#define JSEnterScope + +#endif //TEST_APP_JSR_H diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/.gitattributes b/NativeScript/napi/android/quickjs/mimalloc-dev/.gitattributes new file mode 100644 index 000000000..0332e0315 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/.gitattributes @@ -0,0 +1,12 @@ +# default behavior is to always use unix style line endings +* text eol=lf +*.png binary +*.pdn binary +*.jpg binary +*.sln binary +*.suo binary +*.vcproj binary +*.patch binary +*.dll binary +*.lib binary +*.exe binary diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/.gitignore b/NativeScript/napi/android/quickjs/mimalloc-dev/.gitignore new file mode 100644 index 000000000..df1d58eb2 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/.gitignore @@ -0,0 +1,11 @@ +ide/vs20??/*.db +ide/vs20??/*.opendb +ide/vs20??/*.user +ide/vs20??/*.vcxproj.filters +ide/vs20??/.vs +ide/vs20??/VTune* +out/ +docs/ +*.zip +*.tar +*.gz diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/CMakeLists.txt b/NativeScript/napi/android/quickjs/mimalloc-dev/CMakeLists.txt new file mode 100644 index 000000000..bcfe91d86 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/CMakeLists.txt @@ -0,0 +1,596 @@ +cmake_minimum_required(VERSION 3.18) +project(libmimalloc C CXX) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +option(MI_SECURE "Use full security mitigations (like guard pages, allocation randomization, double-free mitigation, and free-list corruption detection)" OFF) +option(MI_DEBUG_FULL "Use full internal heap invariant checking in DEBUG mode (expensive)" OFF) +option(MI_PADDING "Enable padding to detect heap block overflow (always on in DEBUG or SECURE mode, or with Valgrind/ASAN)" OFF) +option(MI_OVERRIDE "Override the standard malloc interface (e.g. define entry points for malloc() etc)" ON) +option(MI_XMALLOC "Enable abort() call on memory allocation failure by default" OFF) +option(MI_SHOW_ERRORS "Show error and warning messages by default (only enabled by default in DEBUG mode)" OFF) +option(MI_TRACK_VALGRIND "Compile with Valgrind support (adds a small overhead)" OFF) +option(MI_TRACK_ASAN "Compile with address sanitizer support (adds a small overhead)" OFF) +option(MI_TRACK_ETW "Compile with Windows event tracing (ETW) support (adds a small overhead)" OFF) +option(MI_USE_CXX "Use the C++ compiler to compile the library (instead of the C compiler)" OFF) +option(MI_SEE_ASM "Generate assembly files" OFF) +option(MI_OSX_INTERPOSE "Use interpose to override standard malloc on macOS" ON) +option(MI_OSX_ZONE "Use malloc zone to override standard malloc on macOS" ON) +option(MI_WIN_REDIRECT "Use redirection module ('mimalloc-redirect') on Windows if compiling mimalloc as a DLL" ON) +option(MI_LOCAL_DYNAMIC_TLS "Use slightly slower, dlopen-compatible TLS mechanism (Unix)" OFF) +option(MI_LIBC_MUSL "Set this when linking with musl libc" OFF) +option(MI_BUILD_SHARED "Build shared library" ON) +option(MI_BUILD_STATIC "Build static library" ON) +option(MI_BUILD_OBJECT "Build object library" ON) +option(MI_BUILD_TESTS "Build test executables" ON) +option(MI_DEBUG_TSAN "Build with thread sanitizer (needs clang)" OFF) +option(MI_DEBUG_UBSAN "Build with undefined-behavior sanitizer (needs clang++)" OFF) +option(MI_SKIP_COLLECT_ON_EXIT "Skip collecting memory on program exit" OFF) +option(MI_NO_PADDING "Force no use of padding even in DEBUG mode etc." OFF) +option(MI_INSTALL_TOPLEVEL "Install directly into $CMAKE_INSTALL_PREFIX instead of PREFIX/lib/mimalloc-version" OFF) +option(MI_NO_THP "Disable transparent huge pages support on Linux/Android for the mimalloc process only" OFF) + +# deprecated options +option(MI_CHECK_FULL "Use full internal invariant checking in DEBUG mode (deprecated, use MI_DEBUG_FULL instead)" OFF) +option(MI_USE_LIBATOMIC "Explicitly link with -latomic (on older systems) (deprecated and detected automatically)" OFF) + +include(CheckLinkerFlag) # requires cmake 3.18 +include(CheckIncludeFiles) +include(GNUInstallDirs) +include("cmake/mimalloc-config-version.cmake") + +set(mi_sources + src/alloc.c + src/alloc-aligned.c + src/alloc-posix.c + src/arena.c + src/bitmap.c + src/heap.c + src/init.c + src/libc.c + src/options.c + src/os.c + src/page.c + src/random.c + src/segment.c + src/segment-map.c + src/stats.c + src/prim/prim.c) + +set(mi_cflags "") +set(mi_cflags_static "") # extra flags for a static library build +set(mi_cflags_dynamic "") # extra flags for a shared-object library build +set(mi_defines "") +set(mi_libraries "") + +# ----------------------------------------------------------------------------- +# Convenience: set default build type depending on the build directory +# ----------------------------------------------------------------------------- + +message(STATUS "") +if (NOT CMAKE_BUILD_TYPE) + if ("${CMAKE_BINARY_DIR}" MATCHES ".*(D|d)ebug$" OR MI_DEBUG_FULL) + message(STATUS "No build type selected, default to: Debug") + set(CMAKE_BUILD_TYPE "Debug") + else() + message(STATUS "No build type selected, default to: Release") + set(CMAKE_BUILD_TYPE "Release") + endif() +endif() + +if("${CMAKE_BINARY_DIR}" MATCHES ".*(S|s)ecure$") + message(STATUS "Default to secure build") + set(MI_SECURE "ON") +endif() + + +# ----------------------------------------------------------------------------- +# Process options +# ----------------------------------------------------------------------------- + +# put -Wall early so other warnings can be disabled selectively +if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang") + list(APPEND mi_cflags -Wall -Wextra -Wpedantic) +endif() +if(CMAKE_C_COMPILER_ID MATCHES "GNU") + list(APPEND mi_cflags -Wall -Wextra) +endif() +if(CMAKE_C_COMPILER_ID MATCHES "Intel") + list(APPEND mi_cflags -Wall) +endif() + +if(CMAKE_C_COMPILER_ID MATCHES "MSVC|Intel") + set(MI_USE_CXX "ON") +endif() + +if(MI_OVERRIDE) + message(STATUS "Override standard malloc (MI_OVERRIDE=ON)") + if(APPLE) + if(MI_OSX_ZONE) + # use zone's on macOS + message(STATUS " Use malloc zone to override malloc (MI_OSX_ZONE=ON)") + list(APPEND mi_sources src/prim/osx/alloc-override-zone.c) + list(APPEND mi_defines MI_OSX_ZONE=1) + if (NOT MI_OSX_INTERPOSE) + message(STATUS " WARNING: zone overriding usually also needs interpose (use -DMI_OSX_INTERPOSE=ON)") + endif() + endif() + if(MI_OSX_INTERPOSE) + # use interpose on macOS + message(STATUS " Use interpose to override malloc (MI_OSX_INTERPOSE=ON)") + list(APPEND mi_defines MI_OSX_INTERPOSE=1) + if (NOT MI_OSX_ZONE) + message(STATUS " WARNING: interpose usually also needs zone overriding (use -DMI_OSX_INTERPOSE=ON)") + endif() + endif() + if(MI_USE_CXX AND MI_OSX_INTERPOSE) + message(STATUS " WARNING: if dynamically overriding malloc/free, it is more reliable to build mimalloc as C code (use -DMI_USE_CXX=OFF)") + endif() + endif() +endif() + +if(WIN32) + if (MI_WIN_REDIRECT) + if (MSVC_C_ARCHITECTURE_ID MATCHES "ARM") + message(STATUS "Cannot use redirection on Windows ARM (MI_WIN_REDIRECT=OFF)") + set(MI_WIN_REDIRECT OFF) + endif() + endif() + if (NOT MI_WIN_REDIRECT) + # use a negative define for backward compatibility + list(APPEND mi_defines MI_WIN_NOREDIRECT=1) + endif() +endif() + +if(MI_SECURE) + message(STATUS "Set full secure build (MI_SECURE=ON)") + list(APPEND mi_defines MI_SECURE=4) +endif() + +if(MI_TRACK_VALGRIND) + CHECK_INCLUDE_FILES("valgrind/valgrind.h;valgrind/memcheck.h" MI_HAS_VALGRINDH) + if (NOT MI_HAS_VALGRINDH) + set(MI_TRACK_VALGRIND OFF) + message(WARNING "Cannot find the 'valgrind/valgrind.h' and 'valgrind/memcheck.h' -- install valgrind first") + message(STATUS "Compile **without** Valgrind support (MI_TRACK_VALGRIND=OFF)") + else() + message(STATUS "Compile with Valgrind support (MI_TRACK_VALGRIND=ON)") + list(APPEND mi_defines MI_TRACK_VALGRIND=1) + endif() +endif() + +if(MI_TRACK_ASAN) + if (APPLE AND MI_OVERRIDE) + set(MI_TRACK_ASAN OFF) + message(WARNING "Cannot enable address sanitizer support on macOS if MI_OVERRIDE is ON (MI_TRACK_ASAN=OFF)") + endif() + if (MI_TRACK_VALGRIND) + set(MI_TRACK_ASAN OFF) + message(WARNING "Cannot enable address sanitizer support with also Valgrind support enabled (MI_TRACK_ASAN=OFF)") + endif() + if(MI_TRACK_ASAN) + CHECK_INCLUDE_FILES("sanitizer/asan_interface.h" MI_HAS_ASANH) + if (NOT MI_HAS_ASANH) + set(MI_TRACK_ASAN OFF) + message(WARNING "Cannot find the 'sanitizer/asan_interface.h' -- install address sanitizer support first") + message(STATUS "Compile **without** address sanitizer support (MI_TRACK_ASAN=OFF)") + else() + message(STATUS "Compile with address sanitizer support (MI_TRACK_ASAN=ON)") + list(APPEND mi_defines MI_TRACK_ASAN=1) + list(APPEND mi_cflags -fsanitize=address) + list(APPEND mi_libraries -fsanitize=address) + endif() + endif() +endif() + +if(MI_TRACK_ETW) + if(NOT WIN32) + set(MI_TRACK_ETW OFF) + message(WARNING "Can only enable ETW support on Windows (MI_TRACK_ETW=OFF)") + endif() + if (MI_TRACK_VALGRIND OR MI_TRACK_ASAN) + set(MI_TRACK_ETW OFF) + message(WARNING "Cannot enable ETW support with also Valgrind or ASAN support enabled (MI_TRACK_ETW=OFF)") + endif() + if(MI_TRACK_ETW) + message(STATUS "Compile with Windows event tracing support (MI_TRACK_ETW=ON)") + list(APPEND mi_defines MI_TRACK_ETW=1) + endif() +endif() + +if(MI_SEE_ASM) + message(STATUS "Generate assembly listings (MI_SEE_ASM=ON)") + list(APPEND mi_cflags -save-temps) + if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang") + message(STATUS "No GNU Line marker") + list(APPEND mi_cflags -Wno-gnu-line-marker) + endif() +endif() + +if(MI_CHECK_FULL) + message(STATUS "The MI_CHECK_FULL option is deprecated, use MI_DEBUG_FULL instead") + set(MI_DEBUG_FULL "ON") +endif() + +if (MI_SKIP_COLLECT_ON_EXIT) + message(STATUS "Skip collecting memory on program exit (MI_SKIP_COLLECT_ON_EXIT=ON)") + list(APPEND mi_defines MI_SKIP_COLLECT_ON_EXIT=1) +endif() + +if(MI_DEBUG_FULL) + message(STATUS "Set debug level to full internal invariant checking (MI_DEBUG_FULL=ON)") + list(APPEND mi_defines MI_DEBUG=3) # full invariant checking +endif() + +if(MI_NO_PADDING) + message(STATUS "Suppress any padding of heap blocks (MI_NO_PADDING=ON)") + list(APPEND mi_defines MI_PADDING=0) +else() + if(MI_PADDING) + message(STATUS "Enable explicit padding of heap blocks (MI_PADDING=ON)") + list(APPEND mi_defines MI_PADDING=1) + endif() +endif() + +if(MI_XMALLOC) + message(STATUS "Enable abort() calls on memory allocation failure (MI_XMALLOC=ON)") + list(APPEND mi_defines MI_XMALLOC=1) +endif() + +if(MI_SHOW_ERRORS) + message(STATUS "Enable printing of error and warning messages by default (MI_SHOW_ERRORS=ON)") + list(APPEND mi_defines MI_SHOW_ERRORS=1) +endif() + +if(MI_DEBUG_TSAN) + if(CMAKE_C_COMPILER_ID MATCHES "Clang") + message(STATUS "Build with thread sanitizer (MI_DEBUG_TSAN=ON)") + list(APPEND mi_defines MI_TSAN=1) + list(APPEND mi_cflags -fsanitize=thread -g -O1) + list(APPEND mi_libraries -fsanitize=thread) + else() + message(WARNING "Can only use thread sanitizer with clang (MI_DEBUG_TSAN=ON but ignored)") + endif() +endif() + +if(MI_DEBUG_UBSAN) + if(CMAKE_BUILD_TYPE MATCHES "Debug") + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(STATUS "Build with undefined-behavior sanitizer (MI_DEBUG_UBSAN=ON)") + list(APPEND mi_cflags -fsanitize=undefined -g -fno-sanitize-recover=undefined) + list(APPEND mi_libraries -fsanitize=undefined) + if (NOT MI_USE_CXX) + message(STATUS "(switch to use C++ due to MI_DEBUG_UBSAN)") + set(MI_USE_CXX "ON") + endif() + else() + message(WARNING "Can only use undefined-behavior sanitizer with clang++ (MI_DEBUG_UBSAN=ON but ignored)") + endif() + else() + message(WARNING "Can only use undefined-behavior sanitizer with a debug build (CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})") + endif() +endif() + +if(MI_USE_CXX) + message(STATUS "Use the C++ compiler to compile (MI_USE_CXX=ON)") + set_source_files_properties(${mi_sources} PROPERTIES LANGUAGE CXX ) + set_source_files_properties(src/static.c test/test-api.c test/test-api-fill test/test-stress PROPERTIES LANGUAGE CXX ) + if(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang|Clang") + list(APPEND mi_cflags -Wno-deprecated) + endif() + if(CMAKE_CXX_COMPILER_ID MATCHES "Intel" AND NOT CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM") + list(APPEND mi_cflags -Kc++) + endif() +endif() + +if(CMAKE_SYSTEM_NAME MATCHES "Linux|Android") + if(MI_NO_THP) + message(STATUS "Disable transparent huge pages support (MI_NO_THP=ON)") + list(APPEND mi_defines MI_NO_THP=1) + endif() +endif() + +if(MI_LIBC_MUSL) + message(STATUS "Assume using musl libc (MI_LIBC_MUSL=ON)") + list(APPEND mi_defines MI_LIBC_MUSL=1) +endif() + +# On Haiku use `-DCMAKE_INSTALL_PREFIX` instead, issue #788 +# if(CMAKE_SYSTEM_NAME MATCHES "Haiku") +# SET(CMAKE_INSTALL_LIBDIR ~/config/non-packaged/lib) +# SET(CMAKE_INSTALL_INCLUDEDIR ~/config/non-packaged/headers) +# endif() + +# Compiler flags +if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU") + list(APPEND mi_cflags -Wno-unknown-pragmas -fvisibility=hidden) + if(NOT MI_USE_CXX) + list(APPEND mi_cflags -Wstrict-prototypes) + endif() + if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang") + list(APPEND mi_cflags -Wno-static-in-inline) + endif() +endif() + +if(CMAKE_C_COMPILER_ID MATCHES "Intel") + list(APPEND mi_cflags -fvisibility=hidden) +endif() + +if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel" AND NOT CMAKE_SYSTEM_NAME MATCHES "Haiku") + if(MI_LOCAL_DYNAMIC_TLS) + list(APPEND mi_cflags -ftls-model=local-dynamic) + else() + if(MI_LIBC_MUSL) + # with musl we use local-dynamic for the static build, see issue #644 + list(APPEND mi_cflags_static -ftls-model=local-dynamic) + list(APPEND mi_cflags_dynamic -ftls-model=initial-exec) + message(STATUS "Use local dynamic TLS for the static build (since MI_LIBC_MUSL=ON)") + else() + list(APPEND mi_cflags -ftls-model=initial-exec) + endif() + endif() + if(MI_OVERRIDE) + list(APPEND mi_cflags -fno-builtin-malloc) + endif() +endif() + +if (MSVC AND MSVC_VERSION GREATER_EQUAL 1914) + list(APPEND mi_cflags /Zc:__cplusplus) +endif() + +if(MINGW) + add_definitions(-D_WIN32_WINNT=0x600) +endif() + +# extra needed libraries + +# we prefer -l test over `find_library` as sometimes core libraries +# like `libatomic` are not on the system path (see issue #898) +function(find_link_library libname outlibname) + check_linker_flag(C "-l${libname}" mi_has_lib${libname}) + if (mi_has_lib${libname}) + message(VERBOSE "link library: -l${libname}") + set(${outlibname} ${libname} PARENT_SCOPE) + else() + find_library(MI_LIBPATH libname) + if (MI_LIBPATH) + message(VERBOSE "link library ${libname} at ${MI_LIBPATH}") + set(${outlibname} ${MI_LIBPATH} PARENT_SCOPE) + else() + message(VERBOSE "link library not found: ${libname}") + set(${outlibname} "" PARENT_SCOPE) + endif() + endif() +endfunction() + +if(WIN32) + list(APPEND mi_libraries psapi shell32 user32 advapi32 bcrypt) +else() + find_link_library("pthread" MI_LIB_PTHREAD) + if(MI_LIB_PTHREAD) + list(APPEND mi_libraries "${MI_LIB_PTHREAD}") + endif() + find_link_library("rt" MI_LIB_RT) + if(MI_LIB_RT) + list(APPEND mi_libraries "${MI_LIB_RT}") + endif() + find_link_library("atomic" MI_LIB_ATOMIC) + if(MI_LIB_ATOMIC) + list(APPEND mi_libraries "${MI_LIB_ATOMIC}") + endif() +endif() + +# ----------------------------------------------------------------------------- +# Install and output names +# ----------------------------------------------------------------------------- + +# dynamic/shared library and symlinks always go to /usr/local/lib equivalent +set(mi_install_libdir "${CMAKE_INSTALL_LIBDIR}") +set(mi_install_bindir "${CMAKE_INSTALL_BINDIR}") + +# static libraries and object files, includes, and cmake config files +# are either installed at top level, or use versioned directories for side-by-side installation (default) +if (MI_INSTALL_TOPLEVEL) + set(mi_install_objdir "${CMAKE_INSTALL_LIBDIR}") + set(mi_install_incdir "${CMAKE_INSTALL_INCLUDEDIR}") + set(mi_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/mimalloc") +else() + set(mi_install_objdir "${CMAKE_INSTALL_LIBDIR}/mimalloc-${mi_version}") # for static library and object files + set(mi_install_incdir "${CMAKE_INSTALL_INCLUDEDIR}/mimalloc-${mi_version}") # for includes + set(mi_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/mimalloc-${mi_version}") # for cmake package info +endif() + +set(mi_basename "mimalloc") +if(MI_SECURE) + set(mi_basename "${mi_basename}-secure") +endif() +if(MI_TRACK_VALGRIND) + set(mi_basename "${mi_basename}-valgrind") +endif() +if(MI_TRACK_ASAN) + set(mi_basename "${mi_basename}-asan") +endif() +string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LC) +if(NOT(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel|none)$")) + set(mi_basename "${mi_basename}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version +endif() + +if(MI_BUILD_SHARED) + list(APPEND mi_build_targets "shared") +endif() +if(MI_BUILD_STATIC) + list(APPEND mi_build_targets "static") +endif() +if(MI_BUILD_OBJECT) + list(APPEND mi_build_targets "object") +endif() +if(MI_BUILD_TESTS) + list(APPEND mi_build_targets "tests") +endif() + +message(STATUS "") +message(STATUS "Library base name: ${mi_basename}") +message(STATUS "Version : ${mi_version}") +message(STATUS "Build type : ${CMAKE_BUILD_TYPE_LC}") +if(MI_USE_CXX) + message(STATUS "C++ Compiler : ${CMAKE_CXX_COMPILER}") +else() + message(STATUS "C Compiler : ${CMAKE_C_COMPILER}") +endif() +message(STATUS "Compiler flags : ${mi_cflags}") +message(STATUS "Compiler defines : ${mi_defines}") +message(STATUS "Link libraries : ${mi_libraries}") +message(STATUS "Build targets : ${mi_build_targets}") +message(STATUS "") + +# ----------------------------------------------------------------------------- +# Main targets +# ----------------------------------------------------------------------------- + +# shared library +if(MI_BUILD_SHARED) + add_library(mimalloc SHARED ${mi_sources}) + set_target_properties(mimalloc PROPERTIES VERSION ${mi_version} SOVERSION ${mi_version_major} OUTPUT_NAME ${mi_basename} ) + target_compile_definitions(mimalloc PRIVATE ${mi_defines} MI_SHARED_LIB MI_SHARED_LIB_EXPORT) + target_compile_options(mimalloc PRIVATE ${mi_cflags} ${mi_cflags_dynamic}) + target_link_libraries(mimalloc PRIVATE ${mi_libraries}) + target_include_directories(mimalloc PUBLIC + $ + $ + ) + if(WIN32 AND MI_WIN_REDIRECT) + # On windows, link and copy the mimalloc redirection dll too. + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(MIMALLOC_REDIRECT_SUFFIX "32") + else() + set(MIMALLOC_REDIRECT_SUFFIX "") + endif() + + target_link_libraries(mimalloc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.lib) + add_custom_command(TARGET mimalloc POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll" $ + COMMENT "Copy mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll to output directory") + install(FILES "$/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll" DESTINATION ${mi_install_bindir}) + endif() + + install(TARGETS mimalloc EXPORT mimalloc ARCHIVE DESTINATION ${mi_install_libdir} RUNTIME DESTINATION ${mi_install_bindir} LIBRARY DESTINATION ${mi_install_libdir}) + install(EXPORT mimalloc DESTINATION ${mi_install_cmakedir}) +endif() + +# static library +if (MI_BUILD_STATIC) + add_library(mimalloc-static STATIC ${mi_sources}) + set_property(TARGET mimalloc-static PROPERTY POSITION_INDEPENDENT_CODE ON) + target_compile_definitions(mimalloc-static PRIVATE ${mi_defines} MI_STATIC_LIB) + target_compile_options(mimalloc-static PRIVATE ${mi_cflags} ${mi_cflags_static}) + target_link_libraries(mimalloc-static PRIVATE ${mi_libraries}) + target_include_directories(mimalloc-static PUBLIC + $ + $ + ) + if(WIN32) + # When building both static and shared libraries on Windows, a static library should use a + # different output name to avoid the conflict with the import library of a shared one. + string(REPLACE "mimalloc" "mimalloc-static" mi_output_name ${mi_basename}) + set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_output_name}) + else() + set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_basename}) + endif() + + install(TARGETS mimalloc-static EXPORT mimalloc DESTINATION ${mi_install_objdir} LIBRARY) + install(EXPORT mimalloc DESTINATION ${mi_install_cmakedir}) +endif() + +# install include files +install(FILES include/mimalloc.h DESTINATION ${mi_install_incdir}) +install(FILES include/mimalloc-override.h DESTINATION ${mi_install_incdir}) +install(FILES include/mimalloc-new-delete.h DESTINATION ${mi_install_incdir}) +install(FILES cmake/mimalloc-config.cmake DESTINATION ${mi_install_cmakedir}) +install(FILES cmake/mimalloc-config-version.cmake DESTINATION ${mi_install_cmakedir}) + + +# single object file for more predictable static overriding +if (MI_BUILD_OBJECT) + add_library(mimalloc-obj OBJECT src/static.c) + set_property(TARGET mimalloc-obj PROPERTY POSITION_INDEPENDENT_CODE ON) + target_compile_definitions(mimalloc-obj PRIVATE ${mi_defines}) + target_compile_options(mimalloc-obj PRIVATE ${mi_cflags} ${mi_cflags_static}) + target_include_directories(mimalloc-obj PUBLIC + $ + $ + ) + + # Copy the generated object file (`static.o`) to the output directory (as `mimalloc.o`) + if(NOT WIN32) + set(mimalloc-obj-static "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/mimalloc-obj.dir/src/static.c${CMAKE_C_OUTPUT_EXTENSION}") + set(mimalloc-obj-out "${CMAKE_CURRENT_BINARY_DIR}/${mi_basename}${CMAKE_C_OUTPUT_EXTENSION}") + add_custom_command(OUTPUT ${mimalloc-obj-out} DEPENDS mimalloc-obj COMMAND "${CMAKE_COMMAND}" -E copy "${mimalloc-obj-static}" "${mimalloc-obj-out}") + add_custom_target(mimalloc-obj-target ALL DEPENDS ${mimalloc-obj-out}) + endif() + + # the following seems to lead to cmake warnings/errors on some systems, disable for now :-( + # install(TARGETS mimalloc-obj EXPORT mimalloc DESTINATION ${mi_install_objdir}) + + # the FILES expression can also be: $ + # but that fails cmake versions less than 3.10 so we leave it as is for now + install(FILES ${mimalloc-obj-static} + DESTINATION ${mi_install_objdir} + RENAME ${mi_basename}${CMAKE_C_OUTPUT_EXTENSION} ) +endif() + +# pkg-config file support +set(pc_libraries "") +foreach(item IN LISTS mi_libraries) + if(item MATCHES " *[-].*") + set(pc_libraries "${pc_libraries} ${item}") + else() + set(pc_libraries "${pc_libraries} -l${item}") + endif() +endforeach() + +include("cmake/JoinPaths.cmake") +join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") +join_paths(libdir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_LIBDIR}") + +configure_file(mimalloc.pc.in mimalloc.pc @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/mimalloc.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig/") + + + +# ----------------------------------------------------------------------------- +# API surface testing +# ----------------------------------------------------------------------------- + +if (MI_BUILD_TESTS) + enable_testing() + + foreach(TEST_NAME api api-fill stress) + add_executable(mimalloc-test-${TEST_NAME} test/test-${TEST_NAME}.c) + target_compile_definitions(mimalloc-test-${TEST_NAME} PRIVATE ${mi_defines}) + target_compile_options(mimalloc-test-${TEST_NAME} PRIVATE ${mi_cflags}) + target_include_directories(mimalloc-test-${TEST_NAME} PRIVATE include) + target_link_libraries(mimalloc-test-${TEST_NAME} PRIVATE mimalloc ${mi_libraries}) + + add_test(NAME test-${TEST_NAME} COMMAND mimalloc-test-${TEST_NAME}) + endforeach() +endif() + +# ----------------------------------------------------------------------------- +# Set override properties +# ----------------------------------------------------------------------------- +if (MI_OVERRIDE) + if (MI_BUILD_SHARED) + target_compile_definitions(mimalloc PRIVATE MI_MALLOC_OVERRIDE) + endif() + if(NOT WIN32) + # It is only possible to override malloc on Windows when building as a DLL. + if (MI_BUILD_STATIC) + target_compile_definitions(mimalloc-static PRIVATE MI_MALLOC_OVERRIDE) + endif() + if (MI_BUILD_OBJECT) + target_compile_definitions(mimalloc-obj PRIVATE MI_MALLOC_OVERRIDE) + endif() + endif() +endif() diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/LICENSE b/NativeScript/napi/android/quickjs/mimalloc-dev/LICENSE new file mode 100644 index 000000000..670b668a0 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-2021 Microsoft Corporation, Daan Leijen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/SECURITY.md b/NativeScript/napi/android/quickjs/mimalloc-dev/SECURITY.md new file mode 100644 index 000000000..b3c89efc8 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). + + diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/azure-pipelines.yml b/NativeScript/napi/android/quickjs/mimalloc-dev/azure-pipelines.yml new file mode 100644 index 000000000..0247c76fd --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/azure-pipelines.yml @@ -0,0 +1,197 @@ +# Starter pipeline +# Start with a minimal pipeline that you can customize to build and deploy your code. +# Add steps that build, run tests, deploy, and more: +# https://aka.ms/yaml + +trigger: + branches: + include: + - master + - dev + - dev-slice + tags: + include: + - v* + +jobs: +- job: + displayName: Windows + pool: + vmImage: + windows-2022 + strategy: + matrix: + Debug: + BuildType: debug + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON + MSBuildConfiguration: Debug + Release: + BuildType: release + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release + MSBuildConfiguration: Release + Secure: + BuildType: secure + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=ON + MSBuildConfiguration: Release + steps: + - task: CMake@1 + inputs: + workingDirectory: $(BuildType) + cmakeArgs: .. $(cmakeExtraArgs) + - task: MSBuild@1 + inputs: + solution: $(BuildType)/libmimalloc.sln + configuration: '$(MSBuildConfiguration)' + msbuildArguments: -m + - script: ctest --verbose --timeout 120 -C $(MSBuildConfiguration) + workingDirectory: $(BuildType) + displayName: CTest + #- script: $(BuildType)\$(BuildType)\mimalloc-test-stress + # displayName: TestStress + #- upload: $(Build.SourcesDirectory)/$(BuildType) + # artifact: mimalloc-windows-$(BuildType) + +- job: + displayName: Linux + pool: + vmImage: + ubuntu-22.04 + strategy: + matrix: + Debug: + CC: gcc + CXX: g++ + BuildType: debug + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON + Release: + CC: gcc + CXX: g++ + BuildType: release + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release + Secure: + CC: gcc + CXX: g++ + BuildType: secure + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=ON + Debug++: + CC: gcc + CXX: g++ + BuildType: debug-cxx + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_USE_CXX=ON + Debug Clang: + CC: clang + CXX: clang++ + BuildType: debug-clang + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON + Release Clang: + CC: clang + CXX: clang++ + BuildType: release-clang + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release + Secure Clang: + CC: clang + CXX: clang++ + BuildType: secure-clang + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=ON + Debug++ Clang: + CC: clang + CXX: clang++ + BuildType: debug-clang-cxx + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_USE_CXX=ON + Debug ASAN Clang: + CC: clang + CXX: clang++ + BuildType: debug-asan-clang + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_TRACK_ASAN=ON + Debug UBSAN Clang: + CC: clang + CXX: clang++ + BuildType: debug-ubsan-clang + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_DEBUG_UBSAN=ON + Debug TSAN Clang++: + CC: clang + CXX: clang++ + BuildType: debug-tsan-clang-cxx + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_USE_CXX=ON -DMI_DEBUG_TSAN=ON + + steps: + - task: CMake@1 + inputs: + workingDirectory: $(BuildType) + cmakeArgs: .. $(cmakeExtraArgs) + - script: make -j$(nproc) -C $(BuildType) + displayName: Make + - script: ctest --verbose --timeout 180 + workingDirectory: $(BuildType) + displayName: CTest +# - upload: $(Build.SourcesDirectory)/$(BuildType) +# artifact: mimalloc-ubuntu-$(BuildType) + +- job: + displayName: macOS + pool: + vmImage: + macOS-latest + strategy: + matrix: + Debug: + BuildType: debug + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON + Release: + BuildType: release + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release + Secure: + BuildType: secure + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=ON + steps: + - task: CMake@1 + inputs: + workingDirectory: $(BuildType) + cmakeArgs: .. $(cmakeExtraArgs) + - script: make -j$(sysctl -n hw.ncpu) -C $(BuildType) + displayName: Make + # - script: MIMALLOC_VERBOSE=1 ./mimalloc-test-api + # workingDirectory: $(BuildType) + # displayName: TestAPI + # - script: MIMALLOC_VERBOSE=1 ./mimalloc-test-stress + # workingDirectory: $(BuildType) + # displayName: TestStress + - script: ctest --verbose --timeout 120 + workingDirectory: $(BuildType) + displayName: CTest + +# - upload: $(Build.SourcesDirectory)/$(BuildType) +# artifact: mimalloc-macos-$(BuildType) + +# - job: +# displayName: Windows-2017 +# pool: +# vmImage: +# vs2017-win2016 +# strategy: +# matrix: +# Debug: +# BuildType: debug +# cmakeExtraArgs: -A x64 -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON +# MSBuildConfiguration: Debug +# Release: +# BuildType: release +# cmakeExtraArgs: -A x64 -DCMAKE_BUILD_TYPE=Release +# MSBuildConfiguration: Release +# Secure: +# BuildType: secure +# cmakeExtraArgs: -A x64 -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=ON +# MSBuildConfiguration: Release +# steps: +# - task: CMake@1 +# inputs: +# workingDirectory: $(BuildType) +# cmakeArgs: .. $(cmakeExtraArgs) +# - task: MSBuild@1 +# inputs: +# solution: $(BuildType)/libmimalloc.sln +# configuration: '$(MSBuildConfiguration)' +# - script: | +# cd $(BuildType) +# ctest --verbose --timeout 120 +# displayName: CTest diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/bin/mimalloc-redirect.dll b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/mimalloc-redirect.dll new file mode 100644 index 000000000..a3a3591ff Binary files /dev/null and b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/mimalloc-redirect.dll differ diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/bin/mimalloc-redirect.lib b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/mimalloc-redirect.lib new file mode 100644 index 000000000..de128bb94 Binary files /dev/null and b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/mimalloc-redirect.lib differ diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/bin/mimalloc-redirect32.dll b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/mimalloc-redirect32.dll new file mode 100644 index 000000000..522723e50 Binary files /dev/null and b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/mimalloc-redirect32.dll differ diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/bin/mimalloc-redirect32.lib b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/mimalloc-redirect32.lib new file mode 100644 index 000000000..87f19b8ec Binary files /dev/null and b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/mimalloc-redirect32.lib differ diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/bin/minject.exe b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/minject.exe new file mode 100644 index 000000000..dba8f80fd Binary files /dev/null and b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/minject.exe differ diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/bin/minject32.exe b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/minject32.exe new file mode 100644 index 000000000..f837383b9 Binary files /dev/null and b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/minject32.exe differ diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/bin/readme.md b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/readme.md new file mode 100644 index 000000000..9b121bda5 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/bin/readme.md @@ -0,0 +1,71 @@ +# Windows Override + +Dynamically overriding on mimalloc on Windows +is robust and has the particular advantage to be able to redirect all malloc/free calls that go through +the (dynamic) C runtime allocator, including those from other DLL's or libraries. +As it intercepts all allocation calls on a low level, it can be used reliably +on large programs that include other 3rd party components. +There are four requirements to make the overriding work robustly: + +1. Use the C-runtime library as a DLL (using the `/MD` or `/MDd` switch). + +2. Link your program explicitly with `mimalloc-override.dll` library. + To ensure the `mimalloc-override.dll` is loaded at run-time it is easiest to insert some + call to the mimalloc API in the `main` function, like `mi_version()` + (or use the `/INCLUDE:mi_version` switch on the linker). See the `mimalloc-override-test` project + for an example on how to use this. + +3. The `mimalloc-redirect.dll` (or `mimalloc-redirect32.dll`) must be put + in the same folder as the main `mimalloc-override.dll` at runtime (as it is a dependency of that DLL). + The redirection DLL ensures that all calls to the C runtime malloc API get redirected to + mimalloc functions (which reside in `mimalloc-override.dll`). + +4. Ensure the `mimalloc-override.dll` comes as early as possible in the import + list of the final executable (so it can intercept all potential allocations). + +For best performance on Windows with C++, it +is also recommended to also override the `new`/`delete` operations (by including +[`mimalloc-new-delete.h`](../include/mimalloc-new-delete.h) +a single(!) source file in your project). + +The environment variable `MIMALLOC_DISABLE_REDIRECT=1` can be used to disable dynamic +overriding at run-time. Use `MIMALLOC_VERBOSE=1` to check if mimalloc was successfully redirected. + +## Minject + +We cannot always re-link an executable with `mimalloc-override.dll`, and similarly, we cannot always +ensure the the DLL comes first in the import table of the final executable. +In many cases though we can patch existing executables without any recompilation +if they are linked with the dynamic C runtime (`ucrtbase.dll`) -- just put the `mimalloc-override.dll` +into the import table (and put `mimalloc-redirect.dll` in the same folder) +Such patching can be done for example with [CFF Explorer](https://ntcore.com/?page_id=388). + +The `minject` program can also do this from the command line, use `minject --help` for options: + +``` +> minject --help + +minject: + Injects the mimalloc dll into the import table of a 64-bit executable, + and/or ensures that it comes first in het import table. + +usage: + > minject [options] + +options: + -h --help show this help + -v --verbose be verbose + -l --list only list imported modules + -i --inplace update the exe in-place (make sure there is a backup!) + -f --force always overwrite without prompting + --postfix=

use

as a postfix to the mimalloc dll (default is 'override') + e.g. use --postfix=override-debug to link with mimalloc-override-debug.dll + +notes: + Without '--inplace' an injected is generated with the same name ending in '-mi'. + Ensure 'mimalloc-redirect.dll' is in the same folder as the mimalloc dll. + +examples: + > minject --list myprogram.exe + > minject --force --inplace myprogram.exe +``` diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/cmake/JoinPaths.cmake b/NativeScript/napi/android/quickjs/mimalloc-dev/cmake/JoinPaths.cmake new file mode 100644 index 000000000..c68d91b84 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/cmake/JoinPaths.cmake @@ -0,0 +1,23 @@ +# This module provides function for joining paths +# known from most languages +# +# SPDX-License-Identifier: (MIT OR CC0-1.0) +# Copyright 2020 Jan Tojnar +# https://github.com/jtojnar/cmake-snips +# +# Modelled after Python’s os.path.join +# https://docs.python.org/3.7/library/os.path.html#os.path.join +# Windows not supported +function(join_paths joined_path first_path_segment) + set(temp_path "${first_path_segment}") + foreach(current_segment IN LISTS ARGN) + if(NOT ("${current_segment}" STREQUAL "")) + if(IS_ABSOLUTE "${current_segment}") + set(temp_path "${current_segment}") + else() + set(temp_path "${temp_path}/${current_segment}") + endif() + endif() + endforeach() + set(${joined_path} "${temp_path}" PARENT_SCOPE) +endfunction() diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/cmake/mimalloc-config-version.cmake b/NativeScript/napi/android/quickjs/mimalloc-dev/cmake/mimalloc-config-version.cmake new file mode 100644 index 000000000..81fd3c9da --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/cmake/mimalloc-config-version.cmake @@ -0,0 +1,19 @@ +set(mi_version_major 2) +set(mi_version_minor 1) +set(mi_version_patch 7) +set(mi_version ${mi_version_major}.${mi_version_minor}) + +set(PACKAGE_VERSION ${mi_version}) +if(PACKAGE_FIND_VERSION_MAJOR) + if("${PACKAGE_FIND_VERSION_MAJOR}" EQUAL "${mi_version_major}") + if ("${PACKAGE_FIND_VERSION_MINOR}" EQUAL "${mi_version_minor}") + set(PACKAGE_VERSION_EXACT TRUE) + elseif("${PACKAGE_FIND_VERSION_MINOR}" LESS "${mi_version_minor}") + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_UNSUITABLE TRUE) + endif() + else() + set(PACKAGE_VERSION_UNSUITABLE TRUE) + endif() +endif() diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/cmake/mimalloc-config.cmake b/NativeScript/napi/android/quickjs/mimalloc-dev/cmake/mimalloc-config.cmake new file mode 100644 index 000000000..a49b02a25 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/cmake/mimalloc-config.cmake @@ -0,0 +1,14 @@ +include(${CMAKE_CURRENT_LIST_DIR}/mimalloc.cmake) +get_filename_component(MIMALLOC_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}" PATH) # one up from the cmake dir, e.g. /usr/local/lib/cmake/mimalloc-2.0 +get_filename_component(MIMALLOC_VERSION_DIR "${CMAKE_CURRENT_LIST_DIR}" NAME) +string(REPLACE "/lib/cmake" "/lib" MIMALLOC_LIBRARY_DIR "${MIMALLOC_CMAKE_DIR}") +if("${MIMALLOC_VERSION_DIR}" EQUAL "mimalloc") + # top level install + string(REPLACE "/lib/cmake" "/include" MIMALLOC_INCLUDE_DIR "${MIMALLOC_CMAKE_DIR}") + set(MIMALLOC_OBJECT_DIR "${MIMALLOC_LIBRARY_DIR}") +else() + # versioned + string(REPLACE "/lib/cmake/" "/include/" MIMALLOC_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}") + string(REPLACE "/lib/cmake/" "/lib/" MIMALLOC_OBJECT_DIR "${CMAKE_CURRENT_LIST_DIR}") +endif() +set(MIMALLOC_TARGET_DIR "${MIMALLOC_LIBRARY_DIR}") # legacy diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-c5-18xlarge-2020-01-20-a.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-c5-18xlarge-2020-01-20-a.svg new file mode 100644 index 000000000..900509742 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-c5-18xlarge-2020-01-20-a.svg @@ -0,0 +1,887 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-c5-18xlarge-2020-01-20-b.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-c5-18xlarge-2020-01-20-b.svg new file mode 100644 index 000000000..2d853edcb --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-c5-18xlarge-2020-01-20-b.svg @@ -0,0 +1,1185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-c5-18xlarge-2020-01-20-rss-a.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-c5-18xlarge-2020-01-20-rss-a.svg new file mode 100644 index 000000000..393bfad97 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-c5-18xlarge-2020-01-20-rss-a.svg @@ -0,0 +1,757 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-c5-18xlarge-2020-01-20-rss-b.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-c5-18xlarge-2020-01-20-rss-b.svg new file mode 100644 index 000000000..419dc250f --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-c5-18xlarge-2020-01-20-rss-b.svg @@ -0,0 +1,1028 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-1.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-1.svg new file mode 100644 index 000000000..c296a0489 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-1.svg @@ -0,0 +1,769 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-12xlarge-2020-01-16-a.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-12xlarge-2020-01-16-a.svg new file mode 100644 index 000000000..b8a2f20e5 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-12xlarge-2020-01-16-a.svg @@ -0,0 +1,868 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-12xlarge-2020-01-16-b.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-12xlarge-2020-01-16-b.svg new file mode 100644 index 000000000..4a7e21e71 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-12xlarge-2020-01-16-b.svg @@ -0,0 +1,1157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-2.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-2.svg new file mode 100644 index 000000000..917ea5730 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-2.svg @@ -0,0 +1,983 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-rss-1.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-rss-1.svg new file mode 100644 index 000000000..375ebd204 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-rss-1.svg @@ -0,0 +1,683 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-rss-2.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-rss-2.svg new file mode 100644 index 000000000..cb2bbc89e --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-r5a-rss-2.svg @@ -0,0 +1,854 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-spec-rss.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-spec-rss.svg new file mode 100644 index 000000000..2c936166c --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-spec-rss.svg @@ -0,0 +1,713 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-spec.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-spec.svg new file mode 100644 index 000000000..af2b41ba9 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-spec.svg @@ -0,0 +1,713 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-z4-1.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-z4-1.svg new file mode 100644 index 000000000..dacd8ab94 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-z4-1.svg @@ -0,0 +1,890 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-z4-2.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-z4-2.svg new file mode 100644 index 000000000..9990cdcc3 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-z4-2.svg @@ -0,0 +1,1146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-z4-rss-1.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-z4-rss-1.svg new file mode 100644 index 000000000..891f7d68f --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-z4-rss-1.svg @@ -0,0 +1,796 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-z4-rss-2.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-z4-rss-2.svg new file mode 100644 index 000000000..f4265378a --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2020/bench-z4-rss-2.svg @@ -0,0 +1,974 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-amd5950x-2021-01-30-a.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-amd5950x-2021-01-30-a.svg new file mode 100644 index 000000000..86a97bfd2 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-amd5950x-2021-01-30-a.svg @@ -0,0 +1,952 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-amd5950x-2021-01-30-b.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-amd5950x-2021-01-30-b.svg new file mode 100644 index 000000000..c74887702 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-amd5950x-2021-01-30-b.svg @@ -0,0 +1,1255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-c5-18xlarge-2021-01-30-a.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-c5-18xlarge-2021-01-30-a.svg new file mode 100644 index 000000000..bc91c218c --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-c5-18xlarge-2021-01-30-a.svg @@ -0,0 +1,955 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-c5-18xlarge-2021-01-30-b.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-c5-18xlarge-2021-01-30-b.svg new file mode 100644 index 000000000..e8b04a0d9 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-c5-18xlarge-2021-01-30-b.svg @@ -0,0 +1,1269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-c5-18xlarge-2021-01-30-rss-a.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-c5-18xlarge-2021-01-30-rss-a.svg new file mode 100644 index 000000000..6cd36aaab --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-c5-18xlarge-2021-01-30-rss-a.svg @@ -0,0 +1,836 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-c5-18xlarge-2021-01-30-rss-b.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-c5-18xlarge-2021-01-30-rss-b.svg new file mode 100644 index 000000000..c81072e9b --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-c5-18xlarge-2021-01-30-rss-b.svg @@ -0,0 +1,1131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-macmini-2021-01-30.svg b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-macmini-2021-01-30.svg new file mode 100644 index 000000000..ece64185f --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/bench-2021/bench-macmini-2021-01-30.svg @@ -0,0 +1,766 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NativeScript/napi/android/quickjs/mimalloc-dev/doc/doxyfile b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/doxyfile new file mode 100644 index 000000000..d03a70f57 --- /dev/null +++ b/NativeScript/napi/android/quickjs/mimalloc-dev/doc/doxyfile @@ -0,0 +1,2659 @@ +# Doxyfile 1.9.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = mi-malloc + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = 1.8/2.1 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = mimalloc-logo.svg + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = .. + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = YES + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = YES + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 0 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = NO + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = mimalloc-doc.h + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, +# *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to +# YES then doxygen will add the directory of each input to the include path. +# The default value is: YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = docs + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = mimalloc-doxygen.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 189 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 12 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 240 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: +# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = YES + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 180 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /

+ // + // + // + // etc. + inplace_bswap16(&p[RE_HEADER_FLAGS]); + + n = get_u32(&p[RE_HEADER_BYTECODE_LEN]); + inplace_bswap32(&p[RE_HEADER_BYTECODE_LEN]); + if (is_byte_swapped) + n = bswap32(n); + if (n > len - RE_HEADER_LEN) + abort(); + + p = &buf[RE_HEADER_LEN]; + pe = &p[n]; + + while (p < pe) { + n = reopcode_info[*p].size; + switch (n) { + case 1: + case 2: + break; + case 3: + switch (*p) { + case REOP_save_reset: // has two 8 bit arguments + break; + case REOP_range32: // variable length + nw = get_u16(&p[1]); // number of pairs of uint32_t + if (is_byte_swapped) + n = bswap16(n); + for (r = 3 + 8 * nw; n < r; n += 4) + inplace_bswap32(&p[n]); + goto doswap16; + case REOP_range: // variable length + nw = get_u16(&p[1]); // number of pairs of uint16_t + if (is_byte_swapped) + n = bswap16(n); + for (r = 3 + 4 * nw; n < r; n += 2) + inplace_bswap16(&p[n]); + goto doswap16; + default: + doswap16: + inplace_bswap16(&p[1]); + break; + } + break; + case 5: + inplace_bswap32(&p[1]); + break; + case 17: + assert(*p == REOP_simple_greedy_quant); + inplace_bswap32(&p[1]); + inplace_bswap32(&p[5]); + inplace_bswap32(&p[9]); + inplace_bswap32(&p[13]); + break; + default: + abort(); + } + p = &p[n]; + } +} + +#ifdef TEST + +bool lre_check_stack_overflow(void *opaque, size_t alloca_size) +{ + return false; +} + +void *lre_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +int main(int argc, char **argv) +{ + int len, flags, ret, i; + uint8_t *bc; + char error_msg[64]; + uint8_t *capture[CAPTURE_COUNT_MAX * 2]; + const char *input; + int input_len, capture_count; + + if (argc < 4) { + printf("usage: %s regexp flags input\n", argv[0]); + exit(1); + } + flags = atoi(argv[2]); + bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1], + strlen(argv[1]), flags, NULL); + if (!bc) { + fprintf(stderr, "error: %s\n", error_msg); + exit(1); + } + + input = argv[3]; + input_len = strlen(input); + + ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL); + printf("ret=%d\n", ret); + if (ret == 1) { + capture_count = lre_get_capture_count(bc); + for(i = 0; i < 2 * capture_count; i++) { + uint8_t *ptr; + ptr = capture[i]; + printf("%d: ", i); + if (!ptr) + printf(""); + else + printf("%u", (int)(ptr - (uint8_t *)input)); + printf("\n"); + } + } + return 0; +} +#endif diff --git a/NativeScript/napi/android/quickjs/source_ng/libregexp.h b/NativeScript/napi/android/quickjs/source_ng/libregexp.h new file mode 100755 index 000000000..898e9a7a3 --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/libregexp.h @@ -0,0 +1,97 @@ +/* + * Regular Expression Engine + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBREGEXP_H +#define LIBREGEXP_H + +#include +#include + +#include "libunicode.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LRE_FLAG_GLOBAL (1 << 0) +#define LRE_FLAG_IGNORECASE (1 << 1) +#define LRE_FLAG_MULTILINE (1 << 2) +#define LRE_FLAG_DOTALL (1 << 3) +#define LRE_FLAG_UNICODE (1 << 4) +#define LRE_FLAG_STICKY (1 << 5) +#define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */ +#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */ +#define LRE_FLAG_UNICODE_SETS (1 << 8) + +#define LRE_RET_MEMORY_ERROR (-1) +#define LRE_RET_TIMEOUT (-2) + +uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, + const char *buf, size_t buf_len, int re_flags, + void *opaque); +int lre_get_capture_count(const uint8_t *bc_buf); +int lre_get_flags(const uint8_t *bc_buf); +const char *lre_get_groupnames(const uint8_t *bc_buf); +int lre_exec(uint8_t **capture, + const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, + int cbuf_type, void *opaque); + +int lre_parse_escape(const uint8_t **pp, int allow_utf16); +bool lre_is_space(int c); + +void lre_byte_swap(uint8_t *buf, size_t len, bool is_byte_swapped); + +/* must be provided by the user */ +bool lre_check_stack_overflow(void *opaque, size_t alloca_size); +/* must be provided by the user, return non zero if time out */ +int lre_check_timeout(void *opaque); +void *lre_realloc(void *opaque, void *ptr, size_t size); + +/* JS identifier test */ +extern uint32_t const lre_id_start_table_ascii[4]; +extern uint32_t const lre_id_continue_table_ascii[4]; + +static inline int lre_js_is_ident_first(int c) +{ + if ((uint32_t)c < 128) { + return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { + return lre_is_id_start(c); + } +} + +static inline int lre_js_is_ident_next(int c) +{ + if ((uint32_t)c < 128) { + return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { + /* ZWNJ and ZWJ are accepted in identifiers */ + return lre_is_id_continue(c) || c == 0x200C || c == 0x200D; + } +} + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIBREGEXP_H */ diff --git a/NativeScript/napi/android/quickjs/source_ng/libunicode-table.h b/NativeScript/napi/android/quickjs/source_ng/libunicode-table.h new file mode 100755 index 000000000..b48a3a746 --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/libunicode-table.h @@ -0,0 +1,4658 @@ +/* Compressed unicode tables */ +/* Automatically generated file - do not edit */ + +#include + +static const uint32_t case_conv_table1[378] = { + 0x00209a30, 0x00309a00, 0x005a8173, 0x00601730, + 0x006c0730, 0x006f81b3, 0x00701700, 0x007c0700, + 0x007f8100, 0x00803040, 0x009801c3, 0x00988190, + 0x00990640, 0x009c9040, 0x00a481b4, 0x00a52e40, + 0x00bc0130, 0x00bc8640, 0x00bf8170, 0x00c00100, + 0x00c08130, 0x00c10440, 0x00c30130, 0x00c38240, + 0x00c48230, 0x00c58240, 0x00c70130, 0x00c78130, + 0x00c80130, 0x00c88240, 0x00c98130, 0x00ca0130, + 0x00ca8100, 0x00cb0130, 0x00cb8130, 0x00cc0240, + 0x00cd0100, 0x00cd8101, 0x00ce0130, 0x00ce8130, + 0x00cf0100, 0x00cf8130, 0x00d00640, 0x00d30130, + 0x00d38240, 0x00d48130, 0x00d60240, 0x00d70130, + 0x00d78240, 0x00d88230, 0x00d98440, 0x00db8130, + 0x00dc0240, 0x00de0240, 0x00df8100, 0x00e20350, + 0x00e38350, 0x00e50350, 0x00e69040, 0x00ee8100, + 0x00ef1240, 0x00f801b4, 0x00f88350, 0x00fa0240, + 0x00fb0130, 0x00fb8130, 0x00fc2840, 0x01100130, + 0x01111240, 0x011d0131, 0x011d8240, 0x011e8130, + 0x011f0131, 0x011f8201, 0x01208240, 0x01218130, + 0x01220130, 0x01228130, 0x01230a40, 0x01280101, + 0x01288101, 0x01290101, 0x01298100, 0x012a0100, + 0x012b0200, 0x012c8100, 0x012d8100, 0x012e0101, + 0x01300100, 0x01308101, 0x01318100, 0x01320101, + 0x01328101, 0x01330101, 0x01340100, 0x01348100, + 0x01350101, 0x01358101, 0x01360101, 0x01378100, + 0x01388101, 0x01390100, 0x013a8100, 0x013e8101, + 0x01400100, 0x01410101, 0x01418100, 0x01438101, + 0x01440100, 0x01448100, 0x01450200, 0x01460100, + 0x01490100, 0x014e8101, 0x014f0101, 0x01a28173, + 0x01b80440, 0x01bb0240, 0x01bd8300, 0x01bf8130, + 0x01c30130, 0x01c40330, 0x01c60130, 0x01c70230, + 0x01c801d0, 0x01c89130, 0x01d18930, 0x01d60100, + 0x01d68300, 0x01d801d3, 0x01d89100, 0x01e10173, + 0x01e18900, 0x01e60100, 0x01e68200, 0x01e78130, + 0x01e80173, 0x01e88173, 0x01ea8173, 0x01eb0173, + 0x01eb8100, 0x01ec1840, 0x01f80173, 0x01f88173, + 0x01f90100, 0x01f98100, 0x01fa01a0, 0x01fa8173, + 0x01fb8240, 0x01fc8130, 0x01fd0240, 0x01fe8330, + 0x02001030, 0x02082030, 0x02182000, 0x02281000, + 0x02302240, 0x02453640, 0x02600130, 0x02608e40, + 0x02678100, 0x02686040, 0x0298a630, 0x02b0a600, + 0x02c381b5, 0x08502631, 0x08638131, 0x08668131, + 0x08682b00, 0x087e8300, 0x09d05011, 0x09f80610, + 0x09fc0620, 0x0e400174, 0x0e408174, 0x0e410174, + 0x0e418174, 0x0e420174, 0x0e428174, 0x0e430174, + 0x0e438180, 0x0e440180, 0x0e448240, 0x0e482b30, + 0x0e5e8330, 0x0ebc8101, 0x0ebe8101, 0x0ec70101, + 0x0f007e40, 0x0f3f1840, 0x0f4b01b5, 0x0f4b81b6, + 0x0f4c01b6, 0x0f4c81b6, 0x0f4d01b7, 0x0f4d8180, + 0x0f4f0130, 0x0f506040, 0x0f800800, 0x0f840830, + 0x0f880600, 0x0f8c0630, 0x0f900800, 0x0f940830, + 0x0f980800, 0x0f9c0830, 0x0fa00600, 0x0fa40630, + 0x0fa801b0, 0x0fa88100, 0x0fa901d3, 0x0fa98100, + 0x0faa01d3, 0x0faa8100, 0x0fab01d3, 0x0fab8100, + 0x0fac8130, 0x0fad8130, 0x0fae8130, 0x0faf8130, + 0x0fb00800, 0x0fb40830, 0x0fb80200, 0x0fb90400, + 0x0fbb0201, 0x0fbc0201, 0x0fbd0201, 0x0fbe0201, + 0x0fc008b7, 0x0fc40867, 0x0fc808b8, 0x0fcc0868, + 0x0fd008b8, 0x0fd40868, 0x0fd80200, 0x0fd901b9, + 0x0fd981b1, 0x0fda01b9, 0x0fdb01b1, 0x0fdb81d7, + 0x0fdc0230, 0x0fdd0230, 0x0fde0161, 0x0fdf0173, + 0x0fe101b9, 0x0fe181b2, 0x0fe201ba, 0x0fe301b2, + 0x0fe381d8, 0x0fe40430, 0x0fe60162, 0x0fe80201, + 0x0fe901d0, 0x0fe981d0, 0x0feb01b0, 0x0feb81d0, + 0x0fec0230, 0x0fed0230, 0x0ff00201, 0x0ff101d3, + 0x0ff181d3, 0x0ff201ba, 0x0ff28101, 0x0ff301b0, + 0x0ff381d3, 0x0ff40231, 0x0ff50230, 0x0ff60131, + 0x0ff901ba, 0x0ff981b2, 0x0ffa01bb, 0x0ffb01b2, + 0x0ffb81d9, 0x0ffc0230, 0x0ffd0230, 0x0ffe0162, + 0x109301a0, 0x109501a0, 0x109581a0, 0x10990131, + 0x10a70101, 0x10b01031, 0x10b81001, 0x10c18240, + 0x125b1a31, 0x12681a01, 0x16003031, 0x16183001, + 0x16300240, 0x16310130, 0x16318130, 0x16320130, + 0x16328100, 0x16330100, 0x16338640, 0x16368130, + 0x16370130, 0x16378130, 0x16380130, 0x16390240, + 0x163a8240, 0x163f0230, 0x16406440, 0x16758440, + 0x16790240, 0x16802600, 0x16938100, 0x16968100, + 0x53202e40, 0x53401c40, 0x53910e40, 0x53993e40, + 0x53bc8440, 0x53be8130, 0x53bf0a40, 0x53c58240, + 0x53c68130, 0x53c80440, 0x53ca0101, 0x53cb1440, + 0x53d50130, 0x53d58130, 0x53d60130, 0x53d68130, + 0x53d70130, 0x53d80130, 0x53d88130, 0x53d90130, + 0x53d98131, 0x53da1040, 0x53e20131, 0x53e28130, + 0x53e30130, 0x53e38440, 0x53e58130, 0x53e60240, + 0x53e80240, 0x53eb0640, 0x53ee0130, 0x53fa8240, + 0x55a98101, 0x55b85020, 0x7d8001b2, 0x7d8081b2, + 0x7d8101b2, 0x7d8181da, 0x7d8201da, 0x7d8281b3, + 0x7d8301b3, 0x7d8981bb, 0x7d8a01bb, 0x7d8a81bb, + 0x7d8b01bc, 0x7d8b81bb, 0x7f909a31, 0x7fa09a01, + 0x82002831, 0x82142801, 0x82582431, 0x826c2401, + 0x82b80b31, 0x82be0f31, 0x82c60731, 0x82ca0231, + 0x82cb8b01, 0x82d18f01, 0x82d98701, 0x82dd8201, + 0x86403331, 0x86603301, 0x86a81631, 0x86b81601, + 0x8c502031, 0x8c602001, 0xb7202031, 0xb7302001, + 0xf4802231, 0xf4912201, +}; + +static const uint8_t case_conv_table2[378] = { + 0x01, 0x00, 0x9c, 0x06, 0x07, 0x4d, 0x03, 0x04, + 0x10, 0x00, 0x8f, 0x0b, 0x00, 0x00, 0x11, 0x00, + 0x08, 0x00, 0x53, 0x4b, 0x52, 0x00, 0x53, 0x00, + 0x54, 0x00, 0x3b, 0x55, 0x56, 0x00, 0x58, 0x5a, + 0x40, 0x5f, 0x5e, 0x00, 0x47, 0x52, 0x63, 0x65, + 0x43, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, 0x6c, + 0x00, 0x6e, 0x00, 0x70, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x93, 0x00, 0x00, + 0x20, 0x36, 0x00, 0x28, 0x00, 0x24, 0x00, 0x24, + 0x25, 0x2d, 0x00, 0x13, 0x6d, 0x6f, 0x00, 0x29, + 0x27, 0x2a, 0x14, 0x16, 0x18, 0x1b, 0x1c, 0x41, + 0x1e, 0x42, 0x1f, 0x4e, 0x3c, 0x40, 0x22, 0x21, + 0x44, 0x21, 0x43, 0x26, 0x28, 0x27, 0x29, 0x23, + 0x2b, 0x4b, 0x2d, 0x46, 0x2f, 0x4c, 0x31, 0x4d, + 0x33, 0x47, 0x45, 0x99, 0x00, 0x00, 0x97, 0x91, + 0x7f, 0x80, 0x85, 0x86, 0x12, 0x82, 0x84, 0x78, + 0x79, 0x12, 0x7d, 0xa3, 0x7e, 0x7a, 0x7b, 0x8c, + 0x92, 0x98, 0xa6, 0xa0, 0x87, 0x00, 0x9a, 0xa1, + 0x95, 0x77, 0x33, 0x95, 0x00, 0x90, 0x00, 0x76, + 0x9b, 0x9a, 0x99, 0x98, 0x00, 0x00, 0xa0, 0x00, + 0x9e, 0x00, 0xa3, 0xa2, 0x15, 0x31, 0x32, 0x33, + 0xb7, 0xb8, 0x55, 0xac, 0xab, 0x12, 0x14, 0x1e, + 0x21, 0x22, 0x22, 0x2a, 0x34, 0x35, 0x00, 0xa8, + 0xa9, 0x39, 0x22, 0x4c, 0x00, 0x00, 0x97, 0x01, + 0x5a, 0xda, 0x1d, 0x36, 0x05, 0x00, 0xc7, 0xc6, + 0xc9, 0xc8, 0xcb, 0xca, 0xcd, 0xcc, 0xcf, 0xce, + 0xc4, 0xd8, 0x45, 0xd9, 0x42, 0xda, 0x46, 0xdb, + 0xd1, 0xd3, 0xd5, 0xd7, 0xdd, 0xdc, 0xf1, 0xf9, + 0x01, 0x11, 0x0a, 0x12, 0x80, 0x9f, 0x00, 0x21, + 0x80, 0xa3, 0xf0, 0x00, 0xc0, 0x40, 0xc6, 0x60, + 0xea, 0xde, 0xe6, 0x99, 0xc0, 0x00, 0x00, 0x06, + 0x60, 0xdf, 0x29, 0x00, 0x15, 0x12, 0x06, 0x16, + 0xfb, 0xe0, 0x09, 0x15, 0x12, 0x84, 0x0b, 0xc6, + 0x16, 0x02, 0xe2, 0x06, 0xc0, 0x40, 0x00, 0x46, + 0x60, 0xe1, 0xe3, 0x6d, 0x37, 0x38, 0x39, 0x18, + 0x17, 0x1a, 0x19, 0x00, 0x1d, 0x1c, 0x1f, 0x1e, + 0x00, 0x61, 0xba, 0x67, 0x45, 0x48, 0x00, 0x50, + 0x64, 0x4f, 0x51, 0x00, 0x00, 0x49, 0x00, 0x00, + 0x00, 0xa5, 0xa6, 0xa7, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb9, 0x00, 0x00, 0x5c, 0x00, 0x4a, 0x00, + 0x5d, 0x57, 0x59, 0x62, 0x60, 0x72, 0x6b, 0x71, + 0x54, 0x00, 0x3e, 0x69, 0xbb, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x25, 0x00, 0x48, 0xaa, 0x8a, 0x8b, + 0x8c, 0xab, 0xac, 0x58, 0x58, 0xaf, 0x94, 0xb0, + 0x6f, 0xb2, 0x63, 0x62, 0x65, 0x64, 0x67, 0x66, + 0x6c, 0x6d, 0x6e, 0x6f, 0x68, 0x69, 0x6a, 0x6b, + 0x71, 0x70, 0x73, 0x72, 0x75, 0x74, 0x77, 0x76, + 0x79, 0x78, +}; + +static const uint16_t case_conv_ext[58] = { + 0x0399, 0x0308, 0x0301, 0x03a5, 0x0313, 0x0300, 0x0342, 0x0391, + 0x0397, 0x03a9, 0x0046, 0x0049, 0x004c, 0x0053, 0x0069, 0x0307, + 0x02bc, 0x004e, 0x004a, 0x030c, 0x0535, 0x0552, 0x0048, 0x0331, + 0x0054, 0x0057, 0x030a, 0x0059, 0x0041, 0x02be, 0x1f08, 0x1f80, + 0x1f28, 0x1f90, 0x1f68, 0x1fa0, 0x1fba, 0x0386, 0x1fb3, 0x1fca, + 0x0389, 0x1fc3, 0x03a1, 0x1ffa, 0x038f, 0x1ff3, 0x0544, 0x0546, + 0x053b, 0x054e, 0x053d, 0x03b8, 0x0462, 0xa64a, 0x1e60, 0x03c9, + 0x006b, 0x00e5, +}; + +static const uint8_t unicode_prop_Cased1_table[193] = { + 0x40, 0xa9, 0x80, 0x8e, 0x80, 0xfc, 0x80, 0xd3, + 0x80, 0x9b, 0x81, 0x8d, 0x02, 0x80, 0xe1, 0x80, + 0x91, 0x85, 0x9a, 0x01, 0x00, 0x01, 0x11, 0x03, + 0x04, 0x08, 0x01, 0x08, 0x30, 0x08, 0x01, 0x15, + 0x20, 0x00, 0x39, 0x99, 0x31, 0x9d, 0x84, 0x40, + 0x94, 0x80, 0xd6, 0x82, 0xa6, 0x80, 0x41, 0x62, + 0x80, 0xa6, 0x80, 0x4b, 0x72, 0x80, 0x4c, 0x02, + 0xf8, 0x02, 0x80, 0x8f, 0x80, 0xb0, 0x40, 0xdb, + 0x08, 0x80, 0x41, 0xd0, 0x80, 0x8c, 0x80, 0x8f, + 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, 0x14, 0x28, + 0x10, 0x11, 0x02, 0x01, 0x18, 0x0b, 0x24, 0x4b, + 0x26, 0x01, 0x01, 0x86, 0xe5, 0x80, 0x60, 0x79, + 0xb6, 0x81, 0x40, 0x91, 0x81, 0xbd, 0x88, 0x94, + 0x05, 0x80, 0x98, 0x80, 0xa2, 0x00, 0x80, 0x9b, + 0x12, 0x82, 0x43, 0x34, 0xa2, 0x06, 0x80, 0x8d, + 0x60, 0x5c, 0x15, 0x01, 0x10, 0xa9, 0x80, 0x88, + 0x60, 0xcc, 0x44, 0xd4, 0x80, 0xc6, 0x01, 0x08, + 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, + 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, + 0x16, 0x80, 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x07, 0x47, 0x33, 0x89, 0x80, 0x93, 0x2d, 0x41, + 0x04, 0xbd, 0x50, 0xc1, 0x99, 0x85, 0x99, 0x85, + 0x99, +}; + +static const uint8_t unicode_prop_Cased1_index[18] = { + 0xb9, 0x02, 0x80, 0xa0, 0x1e, 0x40, 0x9e, 0xa6, + 0x40, 0xbb, 0x07, 0x01, 0xdb, 0xd6, 0x01, 0x8a, + 0xf1, 0x01, +}; + +static const uint8_t unicode_prop_Case_Ignorable_table[764] = { + 0xa6, 0x05, 0x80, 0x8a, 0x80, 0xa2, 0x00, 0x80, + 0xc6, 0x03, 0x00, 0x03, 0x01, 0x81, 0x41, 0xf6, + 0x40, 0xbf, 0x19, 0x18, 0x88, 0x08, 0x80, 0x40, + 0xfa, 0x86, 0x40, 0xce, 0x04, 0x80, 0xb0, 0xac, + 0x00, 0x01, 0x01, 0x00, 0xab, 0x80, 0x8a, 0x85, + 0x89, 0x8a, 0x00, 0xa2, 0x80, 0x89, 0x94, 0x8f, + 0x80, 0xe4, 0x38, 0x89, 0x03, 0xa0, 0x00, 0x80, + 0x9d, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0x18, 0x08, + 0x97, 0x97, 0xaa, 0x82, 0xab, 0x06, 0x0c, 0x88, + 0xa8, 0xb9, 0xb6, 0x00, 0x03, 0x3b, 0x02, 0x86, + 0x89, 0x81, 0x8c, 0x80, 0x8e, 0x80, 0xb9, 0x03, + 0x1f, 0x80, 0x93, 0x81, 0x99, 0x01, 0x81, 0xb8, + 0x03, 0x0b, 0x09, 0x12, 0x80, 0x9d, 0x0a, 0x80, + 0x8a, 0x81, 0xb8, 0x03, 0x20, 0x0b, 0x80, 0x93, + 0x81, 0x95, 0x28, 0x80, 0xb9, 0x01, 0x00, 0x1f, + 0x06, 0x81, 0x8a, 0x81, 0x9d, 0x80, 0xbc, 0x80, + 0x8b, 0x80, 0xb1, 0x02, 0x80, 0xb6, 0x00, 0x14, + 0x10, 0x1e, 0x81, 0x8a, 0x81, 0x9c, 0x80, 0xb9, + 0x01, 0x05, 0x04, 0x81, 0x93, 0x81, 0x9b, 0x81, + 0xb8, 0x0b, 0x1f, 0x80, 0x93, 0x81, 0x9c, 0x80, + 0xc7, 0x06, 0x10, 0x80, 0xd9, 0x01, 0x86, 0x8a, + 0x88, 0xe1, 0x01, 0x88, 0x88, 0x00, 0x86, 0xc8, + 0x81, 0x9a, 0x00, 0x00, 0x80, 0xb6, 0x8d, 0x04, + 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, 0x80, 0xe5, + 0x18, 0x28, 0x09, 0x81, 0x98, 0x0b, 0x82, 0x8f, + 0x83, 0x8c, 0x01, 0x0d, 0x80, 0x8e, 0x80, 0xdd, + 0x80, 0x42, 0x5f, 0x82, 0x43, 0xb1, 0x82, 0x9c, + 0x81, 0x9d, 0x81, 0x9d, 0x81, 0xbf, 0x08, 0x37, + 0x01, 0x8a, 0x10, 0x20, 0xac, 0x84, 0xb2, 0x80, + 0xc0, 0x81, 0xa1, 0x80, 0xf5, 0x13, 0x81, 0x88, + 0x05, 0x82, 0x40, 0xda, 0x09, 0x80, 0xb9, 0x00, + 0x30, 0x00, 0x01, 0x3d, 0x89, 0x08, 0xa6, 0x07, + 0x9e, 0xb0, 0x83, 0xaf, 0x00, 0x20, 0x04, 0x80, + 0xa7, 0x88, 0x8b, 0x81, 0x9f, 0x19, 0x08, 0x82, + 0xb7, 0x00, 0x0a, 0x00, 0x82, 0xb9, 0x39, 0x81, + 0xbf, 0x85, 0xd1, 0x10, 0x8c, 0x06, 0x18, 0x28, + 0x11, 0xb1, 0xbe, 0x8c, 0x80, 0xa1, 0xe4, 0x41, + 0xbc, 0x00, 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c, + 0x82, 0x8c, 0x81, 0x8b, 0x27, 0x81, 0x89, 0x01, + 0x01, 0x84, 0xb0, 0x20, 0x89, 0x00, 0x8c, 0x80, + 0x8f, 0x8c, 0xb2, 0xa0, 0x4b, 0x8a, 0x81, 0xf0, + 0x82, 0xfc, 0x80, 0x8e, 0x80, 0xdf, 0x9f, 0xae, + 0x80, 0x41, 0xd4, 0x80, 0xa3, 0x1a, 0x24, 0x80, + 0xdc, 0x85, 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80, + 0x44, 0xe1, 0x85, 0x41, 0x0d, 0x80, 0xe1, 0x18, + 0x89, 0x00, 0x9b, 0x83, 0xcf, 0x81, 0x8d, 0xa1, + 0xcd, 0x80, 0x96, 0x82, 0xe6, 0x12, 0x0f, 0x02, + 0x03, 0x80, 0x98, 0x0c, 0x80, 0x40, 0x96, 0x81, + 0x99, 0x91, 0x8c, 0x80, 0xa5, 0x87, 0x98, 0x8a, + 0xad, 0x82, 0xaf, 0x01, 0x19, 0x81, 0x90, 0x80, + 0x94, 0x81, 0xc1, 0x29, 0x09, 0x81, 0x8b, 0x07, + 0x80, 0xa2, 0x80, 0x8a, 0x80, 0xb2, 0x00, 0x11, + 0x0c, 0x08, 0x80, 0x9a, 0x80, 0x8d, 0x0c, 0x08, + 0x80, 0xe3, 0x84, 0x88, 0x82, 0xf8, 0x01, 0x03, + 0x80, 0x60, 0x4f, 0x2f, 0x80, 0x40, 0x92, 0x90, + 0x42, 0x3c, 0x8f, 0x10, 0x8b, 0x8f, 0xa1, 0x01, + 0x80, 0x40, 0xa8, 0x06, 0x05, 0x80, 0x8a, 0x80, + 0xa2, 0x00, 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2, + 0x80, 0x94, 0x82, 0x42, 0x00, 0x80, 0x40, 0xe1, + 0x80, 0x40, 0x94, 0x84, 0x44, 0x04, 0x28, 0xa9, + 0x80, 0x88, 0x42, 0x45, 0x10, 0x0c, 0x83, 0xa7, + 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x83, + 0xa5, 0x80, 0x99, 0x20, 0x80, 0x41, 0x3a, 0x81, + 0xce, 0x83, 0xc5, 0x8a, 0xb0, 0x83, 0xfa, 0x80, + 0xb5, 0x8e, 0xa8, 0x01, 0x81, 0x89, 0x82, 0xb0, + 0x19, 0x09, 0x03, 0x80, 0x89, 0x80, 0xb1, 0x82, + 0xa3, 0x20, 0x87, 0xbd, 0x80, 0x8b, 0x81, 0xb3, + 0x88, 0x89, 0x19, 0x80, 0xde, 0x11, 0x00, 0x0d, + 0x01, 0x80, 0x40, 0x9c, 0x02, 0x87, 0x94, 0x81, + 0xb8, 0x0a, 0x80, 0xa4, 0x32, 0x84, 0xc5, 0x85, + 0x8c, 0x00, 0x00, 0x80, 0x8d, 0x81, 0xd4, 0x39, + 0x10, 0x80, 0x96, 0x80, 0xd3, 0x28, 0x03, 0x08, + 0x81, 0x40, 0xed, 0x1d, 0x08, 0x81, 0x9a, 0x81, + 0xd4, 0x39, 0x00, 0x81, 0xe9, 0x00, 0x01, 0x28, + 0x80, 0xe4, 0x00, 0x01, 0x18, 0x84, 0x41, 0x02, + 0x88, 0x01, 0x40, 0xff, 0x08, 0x03, 0x80, 0x40, + 0x8f, 0x19, 0x0b, 0x80, 0x9f, 0x89, 0xa7, 0x29, + 0x1f, 0x80, 0x88, 0x29, 0x82, 0xad, 0x8c, 0x01, + 0x41, 0x95, 0x30, 0x28, 0x80, 0xd1, 0x95, 0x0e, + 0x01, 0x01, 0xf9, 0x2a, 0x00, 0x08, 0x30, 0x80, + 0xc7, 0x0a, 0x00, 0x80, 0x41, 0x5a, 0x81, 0x8a, + 0x81, 0xb3, 0x24, 0x00, 0x80, 0x96, 0x80, 0x54, + 0xd4, 0x90, 0x85, 0x8e, 0x60, 0x2c, 0xc7, 0x8b, + 0x12, 0x49, 0xbf, 0x84, 0xba, 0x86, 0x88, 0x83, + 0x41, 0xfb, 0x82, 0xa7, 0x81, 0x41, 0xe1, 0x80, + 0xbe, 0x90, 0xbf, 0x08, 0x81, 0x60, 0x40, 0x0a, + 0x18, 0x30, 0x81, 0x4c, 0x9d, 0x08, 0x83, 0x52, + 0x5b, 0xad, 0x81, 0x96, 0x42, 0x1f, 0x82, 0x88, + 0x8f, 0x0e, 0x9d, 0x83, 0x40, 0x93, 0x82, 0x47, + 0xba, 0xb6, 0x83, 0xb1, 0x38, 0x8d, 0x80, 0x95, + 0x20, 0x8e, 0x45, 0x4f, 0x30, 0x90, 0x0e, 0x01, + 0x04, 0x84, 0xbd, 0xa0, 0x80, 0x40, 0x9f, 0x8d, + 0x41, 0x6f, 0x80, 0xbc, 0x83, 0x41, 0xfa, 0x84, + 0x40, 0xfd, 0x81, 0x42, 0xdf, 0x86, 0xec, 0x87, + 0x4a, 0xae, 0x84, 0x6c, 0x0c, 0x00, 0x80, 0x9d, + 0xdf, 0xff, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_Case_Ignorable_index[72] = { + 0xbe, 0x05, 0x00, 0xfe, 0x07, 0x00, 0x52, 0x0a, + 0xa0, 0xc1, 0x0b, 0x00, 0x82, 0x0d, 0x00, 0x3f, + 0x10, 0x80, 0xd4, 0x17, 0x40, 0xcf, 0x1a, 0x20, + 0xf5, 0x1c, 0x00, 0x80, 0x20, 0x00, 0x16, 0xa0, + 0x00, 0xc6, 0xa8, 0x00, 0xc2, 0xaa, 0x60, 0x56, + 0xfe, 0x20, 0xb1, 0x07, 0x01, 0x02, 0x10, 0x01, + 0x42, 0x12, 0x41, 0xc4, 0x14, 0x21, 0xe1, 0x19, + 0x81, 0x48, 0x1d, 0x01, 0x44, 0x6b, 0x01, 0x83, + 0xd1, 0x21, 0x3e, 0xe1, 0x01, 0xf0, 0x01, 0x0e, +}; + +static const uint8_t unicode_prop_ID_Start_table[1133] = { + 0xc0, 0x99, 0x85, 0x99, 0xae, 0x80, 0x89, 0x03, + 0x04, 0x96, 0x80, 0x9e, 0x80, 0x41, 0xc9, 0x83, + 0x8b, 0x8d, 0x26, 0x00, 0x80, 0x40, 0x80, 0x20, + 0x09, 0x18, 0x05, 0x00, 0x10, 0x00, 0x93, 0x80, + 0xd2, 0x80, 0x40, 0x8a, 0x87, 0x40, 0xa5, 0x80, + 0xa5, 0x08, 0x85, 0xa8, 0xc6, 0x9a, 0x1b, 0xac, + 0xaa, 0xa2, 0x08, 0xe2, 0x00, 0x8e, 0x0e, 0x81, + 0x89, 0x11, 0x80, 0x8f, 0x00, 0x9d, 0x9c, 0xd8, + 0x8a, 0x80, 0x97, 0xa0, 0x88, 0x0b, 0x04, 0x95, + 0x18, 0x88, 0x02, 0x80, 0x96, 0x98, 0x86, 0x8a, + 0x84, 0x97, 0x05, 0x90, 0xa9, 0xb9, 0xb5, 0x10, + 0x91, 0x06, 0x89, 0x8e, 0x8f, 0x1f, 0x09, 0x81, + 0x95, 0x06, 0x00, 0x13, 0x10, 0x8f, 0x80, 0x8c, + 0x08, 0x82, 0x8d, 0x81, 0x89, 0x07, 0x2b, 0x09, + 0x95, 0x06, 0x01, 0x01, 0x01, 0x9e, 0x18, 0x80, + 0x92, 0x82, 0x8f, 0x88, 0x02, 0x80, 0x95, 0x06, + 0x01, 0x04, 0x10, 0x91, 0x80, 0x8e, 0x81, 0x96, + 0x80, 0x8a, 0x39, 0x09, 0x95, 0x06, 0x01, 0x04, + 0x10, 0x9d, 0x08, 0x82, 0x8e, 0x80, 0x90, 0x00, + 0x2a, 0x10, 0x1a, 0x08, 0x00, 0x0a, 0x0a, 0x12, + 0x8b, 0x95, 0x80, 0xb3, 0x38, 0x10, 0x96, 0x80, + 0x8f, 0x10, 0x99, 0x11, 0x01, 0x81, 0x9d, 0x03, + 0x38, 0x10, 0x96, 0x80, 0x89, 0x04, 0x10, 0x9e, + 0x08, 0x81, 0x8e, 0x81, 0x90, 0x88, 0x02, 0x80, + 0xa8, 0x08, 0x8f, 0x04, 0x17, 0x82, 0x97, 0x2c, + 0x91, 0x82, 0x97, 0x80, 0x88, 0x00, 0x0e, 0xb9, + 0xaf, 0x01, 0x8b, 0x86, 0xb9, 0x08, 0x00, 0x20, + 0x97, 0x00, 0x80, 0x89, 0x01, 0x88, 0x01, 0x20, + 0x80, 0x94, 0x83, 0x9f, 0x80, 0xbe, 0x38, 0xa3, + 0x9a, 0x84, 0xf2, 0xaa, 0x93, 0x80, 0x8f, 0x2b, + 0x1a, 0x02, 0x0e, 0x13, 0x8c, 0x8b, 0x80, 0x90, + 0xa5, 0x00, 0x20, 0x81, 0xaa, 0x80, 0x41, 0x4c, + 0x03, 0x0e, 0x00, 0x03, 0x81, 0xa8, 0x03, 0x81, + 0xa0, 0x03, 0x0e, 0x00, 0x03, 0x81, 0x8e, 0x80, + 0xb8, 0x03, 0x81, 0xc2, 0xa4, 0x8f, 0x8f, 0xd5, + 0x0d, 0x82, 0x42, 0x6b, 0x81, 0x90, 0x80, 0x99, + 0x84, 0xca, 0x82, 0x8a, 0x86, 0x91, 0x8c, 0x92, + 0x8d, 0x91, 0x8d, 0x8c, 0x02, 0x8e, 0xb3, 0xa2, + 0x03, 0x80, 0xc2, 0xd8, 0x86, 0xa8, 0x00, 0x84, + 0xc5, 0x89, 0x9e, 0xb0, 0x9d, 0x0c, 0x8a, 0xab, + 0x83, 0x99, 0xb5, 0x96, 0x88, 0xb4, 0xd1, 0x80, + 0xdc, 0xae, 0x90, 0x87, 0xb5, 0x9d, 0x8c, 0x81, + 0x89, 0xab, 0x99, 0xa3, 0xa8, 0x82, 0x89, 0xa3, + 0x81, 0x8a, 0x84, 0xaa, 0x0a, 0xa8, 0x18, 0x28, + 0x0a, 0x04, 0x40, 0xbf, 0xbf, 0x41, 0x15, 0x0d, + 0x81, 0xa5, 0x0d, 0x0f, 0x00, 0x00, 0x00, 0x80, + 0x9e, 0x81, 0xb4, 0x06, 0x00, 0x12, 0x06, 0x13, + 0x0d, 0x83, 0x8c, 0x22, 0x06, 0xf3, 0x80, 0x8c, + 0x80, 0x8f, 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, + 0x0d, 0x28, 0x00, 0x00, 0x80, 0x8f, 0x0b, 0x24, + 0x18, 0x90, 0xa8, 0x4a, 0x76, 0x40, 0xe4, 0x2b, + 0x11, 0x8b, 0xa5, 0x00, 0x20, 0x81, 0xb7, 0x30, + 0x8f, 0x96, 0x88, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x86, 0x42, 0x25, 0x82, 0x98, 0x88, + 0x34, 0x0c, 0x83, 0xd5, 0x1c, 0x80, 0xd9, 0x03, + 0x84, 0xaa, 0x80, 0xdd, 0x90, 0x9f, 0xaf, 0x8f, + 0x41, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x56, 0x8c, + 0xc2, 0xad, 0x81, 0x41, 0x0c, 0x82, 0x8f, 0x89, + 0x81, 0x93, 0xae, 0x8f, 0x9e, 0x81, 0xcf, 0xa6, + 0x88, 0x81, 0xe6, 0x81, 0xc2, 0x09, 0x00, 0x07, + 0x94, 0x8f, 0x02, 0x03, 0x80, 0x96, 0x9c, 0xb3, + 0x8d, 0xb1, 0xbd, 0x2a, 0x00, 0x81, 0x8a, 0x9b, + 0x89, 0x96, 0x98, 0x9c, 0x86, 0xae, 0x9b, 0x80, + 0x8f, 0x20, 0x89, 0x89, 0x20, 0xa8, 0x96, 0x10, + 0x87, 0x93, 0x96, 0x10, 0x82, 0xb1, 0x00, 0x11, + 0x0c, 0x08, 0x00, 0x97, 0x11, 0x8a, 0x32, 0x8b, + 0x29, 0x29, 0x85, 0x88, 0x30, 0x30, 0xaa, 0x80, + 0x8d, 0x85, 0xf2, 0x9c, 0x60, 0x2b, 0xa3, 0x8b, + 0x96, 0x83, 0xb0, 0x60, 0x21, 0x03, 0x41, 0x6d, + 0x81, 0xe9, 0xa5, 0x86, 0x8b, 0x24, 0x00, 0x89, + 0x80, 0x8c, 0x04, 0x00, 0x01, 0x01, 0x80, 0xeb, + 0xa0, 0x41, 0x6a, 0x91, 0xbf, 0x81, 0xb5, 0xa7, + 0x8b, 0xf3, 0x20, 0x40, 0x86, 0xa3, 0x99, 0x85, + 0x99, 0x8a, 0xd8, 0x15, 0x0d, 0x0d, 0x0a, 0xa2, + 0x8b, 0x80, 0x99, 0x80, 0x92, 0x01, 0x80, 0x8e, + 0x81, 0x8d, 0xa1, 0xfa, 0xc4, 0xb4, 0x41, 0x0a, + 0x9c, 0x82, 0xb0, 0xae, 0x9f, 0x8c, 0x9d, 0x84, + 0xa5, 0x89, 0x9d, 0x81, 0xa3, 0x1f, 0x04, 0xa9, + 0x40, 0x9d, 0x91, 0xa3, 0x83, 0xa3, 0x83, 0xa7, + 0x87, 0xb3, 0x8b, 0x8a, 0x80, 0x8e, 0x06, 0x01, + 0x80, 0x8a, 0x80, 0x8e, 0x06, 0x01, 0x82, 0xb3, + 0x8b, 0x41, 0x36, 0x88, 0x95, 0x89, 0x87, 0x97, + 0x28, 0xa9, 0x80, 0x88, 0xc4, 0x29, 0x00, 0xab, + 0x01, 0x10, 0x81, 0x96, 0x89, 0x96, 0x88, 0x9e, + 0xc0, 0x92, 0x01, 0x89, 0x95, 0x89, 0x99, 0xc5, + 0xb7, 0x29, 0xbf, 0x80, 0x8e, 0x18, 0x10, 0x9c, + 0xa9, 0x9c, 0x82, 0x9c, 0xa2, 0x38, 0x9b, 0x9a, + 0xb5, 0x89, 0x95, 0x89, 0x92, 0x8c, 0x91, 0xed, + 0xc8, 0xb6, 0xb2, 0x8c, 0xb2, 0x8c, 0xa3, 0xa5, + 0x9b, 0x88, 0x96, 0x40, 0xf9, 0xa9, 0x29, 0x8f, + 0x82, 0xba, 0x9c, 0x89, 0x07, 0x95, 0xa9, 0x91, + 0xad, 0x94, 0x9a, 0x96, 0x8b, 0xb4, 0xb8, 0x09, + 0x80, 0x8c, 0xac, 0x9f, 0x98, 0x99, 0xa3, 0x9c, + 0x01, 0x07, 0xa2, 0x10, 0x8b, 0xaf, 0x8d, 0x83, + 0x94, 0x00, 0x80, 0xa2, 0x91, 0x80, 0x98, 0x92, + 0x81, 0xbe, 0x30, 0x00, 0x18, 0x8e, 0x80, 0x89, + 0x86, 0xae, 0xa5, 0x39, 0x09, 0x95, 0x06, 0x01, + 0x04, 0x10, 0x91, 0x80, 0x8b, 0x84, 0x9d, 0x89, + 0x00, 0x08, 0x80, 0xa5, 0x00, 0x98, 0x00, 0x80, + 0xab, 0xb4, 0x91, 0x83, 0x93, 0x82, 0x9d, 0xaf, + 0x93, 0x08, 0x80, 0x40, 0xb7, 0xae, 0xa8, 0x83, + 0xa3, 0xaf, 0x93, 0x80, 0xba, 0xaa, 0x8c, 0x80, + 0xc6, 0x9a, 0xa4, 0x86, 0x40, 0xb8, 0xab, 0xf3, + 0xbf, 0x9e, 0x39, 0x01, 0x38, 0x08, 0x97, 0x8e, + 0x00, 0x80, 0xdd, 0x39, 0xa6, 0x8f, 0x00, 0x80, + 0x9b, 0x80, 0x89, 0xa7, 0x30, 0x94, 0x80, 0x8a, + 0xad, 0x92, 0x80, 0x91, 0xc8, 0x40, 0xc6, 0xa0, + 0x9e, 0x88, 0x80, 0xa4, 0x90, 0x80, 0xb0, 0x9d, + 0xef, 0x30, 0x08, 0xa5, 0x94, 0x80, 0x98, 0x28, + 0x08, 0x9f, 0x8d, 0x80, 0x41, 0x46, 0x92, 0x8e, + 0x00, 0x8c, 0x80, 0xa1, 0xfb, 0x80, 0xce, 0x43, + 0x99, 0xe5, 0xee, 0x90, 0x40, 0xc3, 0x4a, 0x4b, + 0xe0, 0x8e, 0x44, 0x2f, 0x90, 0x85, 0x98, 0x4f, + 0x9a, 0x84, 0x42, 0x46, 0x5a, 0xb8, 0x9d, 0x46, + 0xe1, 0x42, 0x38, 0x86, 0x9e, 0x90, 0xce, 0x90, + 0x9d, 0x91, 0xaf, 0x8f, 0x83, 0x9e, 0x94, 0x84, + 0x92, 0x41, 0xaf, 0xac, 0x40, 0xd2, 0xbf, 0xff, + 0xca, 0x20, 0xc1, 0x8c, 0xbf, 0x08, 0x80, 0x9b, + 0x57, 0xf7, 0x87, 0x44, 0xd5, 0xa8, 0x89, 0x60, + 0x22, 0xe6, 0x18, 0x30, 0x08, 0x41, 0x22, 0x8e, + 0x80, 0x9c, 0x11, 0x80, 0x8d, 0x1f, 0x41, 0x8b, + 0x49, 0x03, 0xea, 0x84, 0x8c, 0x82, 0x88, 0x86, + 0x89, 0x57, 0x65, 0xd4, 0x80, 0xc6, 0x01, 0x08, + 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, + 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, + 0x16, 0x80, 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x07, 0x47, 0x33, 0x9e, 0x2d, 0x41, 0x04, 0xbd, + 0x40, 0x91, 0xac, 0x89, 0x86, 0x8f, 0x80, 0x41, + 0x40, 0x9d, 0x91, 0xab, 0x41, 0xe3, 0x9b, 0x40, + 0xe3, 0x9d, 0x08, 0x41, 0xee, 0x30, 0x18, 0x08, + 0x8e, 0x80, 0x40, 0xc4, 0xba, 0xc3, 0x30, 0x44, + 0xb3, 0x18, 0x9a, 0x01, 0x00, 0x08, 0x80, 0x89, + 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, 0x00, 0x02, + 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, 0x80, 0x89, + 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, 0x51, 0x43, + 0x60, 0xa6, 0xdf, 0x9f, 0x50, 0x39, 0x85, 0x40, + 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x8e, + 0x42, 0x6d, 0x49, 0xa1, 0x42, 0x1d, 0x45, 0xe1, + 0x53, 0x4a, 0x84, 0x50, 0x5f, +}; + +static const uint8_t unicode_prop_ID_Start_index[108] = { + 0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xa9, 0x09, + 0x20, 0xb1, 0x0a, 0x00, 0xba, 0x0b, 0x20, 0x3b, + 0x0d, 0x20, 0xc7, 0x0e, 0x20, 0x49, 0x12, 0x00, + 0x9b, 0x16, 0x00, 0xac, 0x19, 0x00, 0xc0, 0x1d, + 0x80, 0x80, 0x20, 0x20, 0x70, 0x2d, 0x00, 0x00, + 0x32, 0x00, 0xdd, 0xa7, 0x00, 0x4c, 0xaa, 0x20, + 0xc7, 0xd7, 0x20, 0xfc, 0xfd, 0x20, 0x9d, 0x02, + 0x21, 0x96, 0x05, 0x01, 0x9f, 0x08, 0x01, 0x49, + 0x0c, 0x21, 0x76, 0x10, 0x21, 0xa9, 0x12, 0x01, + 0xb0, 0x14, 0x01, 0x42, 0x19, 0x41, 0x90, 0x1c, + 0x01, 0xf1, 0x2f, 0x21, 0x90, 0x6b, 0x21, 0x33, + 0xb1, 0x21, 0x06, 0xd5, 0x01, 0xc3, 0xd7, 0x01, + 0xff, 0xe7, 0x21, 0x63, 0xee, 0x01, 0x5e, 0xee, + 0x42, 0xb0, 0x23, 0x03, +}; + +static const uint8_t unicode_prop_ID_Continue1_table[695] = { + 0xaf, 0x89, 0xa4, 0x80, 0xd6, 0x80, 0x42, 0x47, + 0xef, 0x96, 0x80, 0x40, 0xfa, 0x84, 0x41, 0x08, + 0xac, 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, + 0x9e, 0x28, 0xe4, 0x31, 0x29, 0x08, 0x19, 0x89, + 0x96, 0x80, 0x9d, 0x9a, 0xda, 0x8a, 0x8e, 0x89, + 0xa0, 0x88, 0x88, 0x80, 0x97, 0x18, 0x88, 0x02, + 0x04, 0xaa, 0x82, 0xba, 0x88, 0xa9, 0x97, 0x80, + 0xa0, 0xb5, 0x10, 0x91, 0x06, 0x89, 0x09, 0x89, + 0x90, 0x82, 0xb7, 0x00, 0x31, 0x09, 0x82, 0x88, + 0x80, 0x89, 0x09, 0x89, 0x8d, 0x01, 0x82, 0xb7, + 0x00, 0x23, 0x09, 0x12, 0x80, 0x93, 0x8b, 0x10, + 0x8a, 0x82, 0xb7, 0x00, 0x38, 0x10, 0x82, 0x93, + 0x09, 0x89, 0x89, 0x28, 0x82, 0xb7, 0x00, 0x31, + 0x09, 0x16, 0x82, 0x89, 0x09, 0x89, 0x91, 0x80, + 0xba, 0x22, 0x10, 0x83, 0x88, 0x80, 0x8d, 0x89, + 0x8f, 0x84, 0xb6, 0x00, 0x30, 0x10, 0x1e, 0x81, + 0x8a, 0x09, 0x89, 0x90, 0x82, 0xb7, 0x00, 0x30, + 0x10, 0x1e, 0x81, 0x8a, 0x09, 0x89, 0x10, 0x8b, + 0x83, 0xb6, 0x08, 0x30, 0x10, 0x83, 0x88, 0x80, + 0x89, 0x09, 0x89, 0x90, 0x82, 0xc5, 0x03, 0x28, + 0x00, 0x3d, 0x89, 0x09, 0xbc, 0x01, 0x86, 0x8b, + 0x38, 0x89, 0xd6, 0x01, 0x88, 0x8a, 0x30, 0x89, + 0xbd, 0x0d, 0x89, 0x8a, 0x00, 0x00, 0x03, 0x81, + 0xb0, 0x93, 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, + 0x80, 0xe3, 0x93, 0x80, 0x89, 0x8b, 0x1b, 0x10, + 0x11, 0x32, 0x83, 0x8c, 0x8b, 0x80, 0x8e, 0x42, + 0xbe, 0x82, 0x88, 0x88, 0x43, 0x9f, 0x83, 0x9b, + 0x82, 0x9c, 0x81, 0x9d, 0x81, 0xbf, 0x9f, 0x88, + 0x01, 0x89, 0xa0, 0x10, 0x8a, 0x40, 0x8e, 0x80, + 0xf5, 0x8b, 0x83, 0x8b, 0x89, 0x89, 0xff, 0x8a, + 0xbb, 0x84, 0xb8, 0x89, 0x80, 0x9c, 0x81, 0x8a, + 0x85, 0x89, 0x95, 0x8d, 0x80, 0x8f, 0xb0, 0x84, + 0xae, 0x90, 0x8a, 0x89, 0x90, 0x88, 0x8b, 0x82, + 0x9d, 0x8c, 0x81, 0x89, 0xab, 0x8d, 0xaf, 0x93, + 0x87, 0x89, 0x85, 0x89, 0xf5, 0x10, 0x94, 0x18, + 0x28, 0x0a, 0x40, 0xc5, 0xbf, 0x42, 0x0b, 0x81, + 0xb0, 0x81, 0x92, 0x80, 0xfa, 0x8c, 0x18, 0x82, + 0x8b, 0x4b, 0xfd, 0x82, 0x40, 0x8c, 0x80, 0xdf, + 0x9f, 0x42, 0x29, 0x85, 0xe8, 0x81, 0xdf, 0x80, + 0x60, 0x75, 0x23, 0x89, 0xc4, 0x03, 0x89, 0x9f, + 0x81, 0xcf, 0x81, 0x41, 0x0f, 0x02, 0x03, 0x80, + 0x96, 0x23, 0x80, 0xd2, 0x81, 0xb1, 0x91, 0x89, + 0x89, 0x85, 0x91, 0x8c, 0x8a, 0x9b, 0x87, 0x98, + 0x8c, 0xab, 0x83, 0xae, 0x8d, 0x8e, 0x89, 0x8a, + 0x80, 0x89, 0x89, 0xae, 0x8d, 0x8b, 0x07, 0x09, + 0x89, 0xa0, 0x82, 0xb1, 0x00, 0x11, 0x0c, 0x08, + 0x80, 0xa8, 0x24, 0x81, 0x40, 0xeb, 0x38, 0x09, + 0x89, 0x60, 0x4f, 0x23, 0x80, 0x42, 0xe0, 0x8f, + 0x8f, 0x8f, 0x11, 0x97, 0x82, 0x40, 0xbf, 0x89, + 0xa4, 0x80, 0xa4, 0x80, 0x42, 0x96, 0x80, 0x40, + 0xe1, 0x80, 0x40, 0x94, 0x84, 0x41, 0x24, 0x89, + 0x45, 0x56, 0x10, 0x0c, 0x83, 0xa7, 0x13, 0x80, + 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x1f, 0x89, 0x85, + 0x89, 0x9e, 0x84, 0x41, 0x3c, 0x81, 0xce, 0x83, + 0xc5, 0x8a, 0xb0, 0x83, 0xf9, 0x82, 0xb4, 0x8e, + 0x9e, 0x8a, 0x09, 0x89, 0x83, 0xac, 0x8a, 0x30, + 0xac, 0x89, 0x2a, 0xa3, 0x8d, 0x80, 0x89, 0x21, + 0xab, 0x80, 0x8b, 0x82, 0xaf, 0x8d, 0x3b, 0x80, + 0x8b, 0xd1, 0x8b, 0x28, 0x08, 0x40, 0x9c, 0x8b, + 0x84, 0x89, 0x2b, 0xb6, 0x08, 0x31, 0x09, 0x82, + 0x88, 0x80, 0x89, 0x09, 0x32, 0x84, 0xc2, 0x88, + 0x00, 0x08, 0x03, 0x04, 0x00, 0x8d, 0x81, 0xd1, + 0x91, 0x88, 0x89, 0x18, 0xd0, 0x93, 0x8b, 0x89, + 0x40, 0xd4, 0x31, 0x88, 0x9a, 0x81, 0xd1, 0x90, + 0x8e, 0x89, 0xd0, 0x8c, 0x87, 0x89, 0x85, 0x93, + 0xb8, 0x8e, 0x83, 0x89, 0x40, 0xf1, 0x8e, 0x40, + 0xa4, 0x89, 0xc5, 0x28, 0x09, 0x18, 0x00, 0x81, + 0x8b, 0x89, 0xf6, 0x31, 0x32, 0x80, 0x9b, 0x89, + 0xa7, 0x30, 0x1f, 0x80, 0x88, 0x8a, 0xad, 0x8f, + 0x41, 0x55, 0x89, 0xb4, 0x38, 0x87, 0x8f, 0x89, + 0xb7, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, + 0x30, 0x07, 0x89, 0xaf, 0x20, 0x08, 0x27, 0x89, + 0x41, 0x48, 0x83, 0x88, 0x08, 0x80, 0xaf, 0x32, + 0x84, 0x8c, 0x8a, 0x54, 0xe4, 0x05, 0x8e, 0x60, + 0x2c, 0xc7, 0x9b, 0x49, 0x25, 0x89, 0xd5, 0x89, + 0xa5, 0x84, 0xba, 0x86, 0x98, 0x89, 0x42, 0x15, + 0x89, 0x41, 0xd4, 0x00, 0xb6, 0x33, 0xd0, 0x80, + 0x8a, 0x81, 0x60, 0x4c, 0xaa, 0x81, 0x50, 0x50, + 0x89, 0x42, 0x05, 0xad, 0x81, 0x96, 0x42, 0x1d, + 0x22, 0x2f, 0x39, 0x86, 0x9d, 0x83, 0x40, 0x93, + 0x82, 0x45, 0x88, 0xb1, 0x41, 0xff, 0xb6, 0x83, + 0xb1, 0x38, 0x8d, 0x80, 0x95, 0x20, 0x8e, 0x45, + 0x4f, 0x30, 0x90, 0x0e, 0x01, 0x04, 0xe3, 0x80, + 0x40, 0x9f, 0x86, 0x88, 0x89, 0x41, 0x63, 0x80, + 0xbc, 0x8d, 0x41, 0xf1, 0x8d, 0x40, 0xf3, 0x08, + 0x89, 0x42, 0xd4, 0x86, 0xec, 0x34, 0x89, 0x52, + 0x95, 0x89, 0x6c, 0x05, 0x05, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_ID_Continue1_index[66] = { + 0xfa, 0x06, 0x00, 0x70, 0x09, 0x00, 0xf0, 0x0a, + 0x40, 0x57, 0x0c, 0x00, 0xf0, 0x0d, 0x60, 0xc7, + 0x0f, 0x20, 0xea, 0x17, 0x40, 0x05, 0x1b, 0x00, + 0x0e, 0x20, 0x00, 0xa0, 0xa6, 0x20, 0xe6, 0xa9, + 0x20, 0x10, 0xfe, 0x00, 0x40, 0x0a, 0x01, 0xc3, + 0x10, 0x01, 0x4e, 0x13, 0x01, 0x41, 0x16, 0x01, + 0x0b, 0x1a, 0x01, 0xaa, 0x1d, 0x01, 0x7a, 0x6d, + 0x21, 0x45, 0xd2, 0x21, 0xaf, 0xe2, 0x01, 0xf0, + 0x01, 0x0e, +}; + +static const uint8_t unicode_prop_White_Space_table[22] = { + 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x99, 0x80, + 0x55, 0xde, 0x80, 0x49, 0x7e, 0x8a, 0x9c, 0x0c, + 0x80, 0xae, 0x80, 0x4f, 0x9f, 0x80, +}; + +static const uint8_t unicode_prop_White_Space_index[3] = { + 0x01, 0x30, 0x00, +}; + +static const uint8_t unicode_cc_table[916] = { + 0xb2, 0xcf, 0xd4, 0x00, 0xe8, 0x03, 0xdc, 0x00, + 0xe8, 0x00, 0xd8, 0x04, 0xdc, 0x01, 0xca, 0x03, + 0xdc, 0x01, 0xca, 0x0a, 0xdc, 0x04, 0x01, 0x03, + 0xdc, 0xc7, 0x00, 0xf0, 0xc0, 0x02, 0xdc, 0xc2, + 0x01, 0xdc, 0x80, 0xc2, 0x03, 0xdc, 0xc0, 0x00, + 0xe8, 0x01, 0xdc, 0xc0, 0x41, 0xe9, 0x00, 0xea, + 0x41, 0xe9, 0x00, 0xea, 0x00, 0xe9, 0xcc, 0xb0, + 0xe2, 0xc4, 0xb0, 0xd8, 0x00, 0xdc, 0xc3, 0x00, + 0xdc, 0xc2, 0x00, 0xde, 0x00, 0xdc, 0xc5, 0x05, + 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xde, 0x00, + 0xe4, 0xc0, 0x49, 0x0a, 0x43, 0x13, 0x80, 0x00, + 0x17, 0x80, 0x41, 0x18, 0x80, 0xc0, 0x00, 0xdc, + 0x80, 0x00, 0x12, 0xb0, 0x17, 0xc7, 0x42, 0x1e, + 0xaf, 0x47, 0x1b, 0xc1, 0x01, 0xdc, 0xc4, 0x00, + 0xdc, 0xc1, 0x00, 0xdc, 0x8f, 0x00, 0x23, 0xb0, + 0x34, 0xc6, 0x81, 0xc3, 0x00, 0xdc, 0xc0, 0x81, + 0xc1, 0x80, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xa2, + 0x00, 0x24, 0x9d, 0xc0, 0x00, 0xdc, 0xc1, 0x00, + 0xdc, 0xc1, 0x02, 0xdc, 0xc0, 0x01, 0xdc, 0xc0, + 0x00, 0xdc, 0xc2, 0x00, 0xdc, 0xc0, 0x00, 0xdc, + 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, + 0x6f, 0xc6, 0x00, 0xdc, 0xc0, 0x88, 0x00, 0xdc, + 0x97, 0xc3, 0x80, 0xc8, 0x80, 0xc2, 0x80, 0xc4, + 0xaa, 0x02, 0xdc, 0xb0, 0x0a, 0xc1, 0x02, 0xdc, + 0xc3, 0xa9, 0xc4, 0x04, 0xdc, 0xcd, 0x80, 0x00, + 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xc2, + 0x02, 0xdc, 0x42, 0x1b, 0xc2, 0x00, 0xdc, 0xc1, + 0x01, 0xdc, 0xc4, 0xb0, 0x0b, 0x00, 0x07, 0x8f, + 0x00, 0x09, 0x82, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, + 0x36, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xaf, 0xc0, + 0xb0, 0x0c, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, + 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3d, + 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x4e, 0x00, + 0x09, 0xb0, 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, + 0x86, 0x00, 0x54, 0x00, 0x5b, 0xb0, 0x34, 0x00, + 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3c, 0x01, 0x09, + 0x8f, 0x00, 0x09, 0xb0, 0x4b, 0x00, 0x09, 0xb0, + 0x3c, 0x01, 0x67, 0x00, 0x09, 0x8c, 0x03, 0x6b, + 0xb0, 0x3b, 0x01, 0x76, 0x00, 0x09, 0x8c, 0x03, + 0x7a, 0xb0, 0x1b, 0x01, 0xdc, 0x9a, 0x00, 0xdc, + 0x80, 0x00, 0xdc, 0x80, 0x00, 0xd8, 0xb0, 0x06, + 0x41, 0x81, 0x80, 0x00, 0x84, 0x84, 0x03, 0x82, + 0x81, 0x00, 0x82, 0x80, 0xc1, 0x00, 0x09, 0x80, + 0xc1, 0xb0, 0x0d, 0x00, 0xdc, 0xb0, 0x3f, 0x00, + 0x07, 0x80, 0x01, 0x09, 0xb0, 0x21, 0x00, 0xdc, + 0xb2, 0x9e, 0xc2, 0xb3, 0x83, 0x01, 0x09, 0x9d, + 0x00, 0x09, 0xb0, 0x6c, 0x00, 0x09, 0x89, 0xc0, + 0xb0, 0x9a, 0x00, 0xe4, 0xb0, 0x5e, 0x00, 0xde, + 0xc0, 0x00, 0xdc, 0xb0, 0xaa, 0xc0, 0x00, 0xdc, + 0xb0, 0x16, 0x00, 0x09, 0x93, 0xc7, 0x81, 0x00, + 0xdc, 0xaf, 0xc4, 0x05, 0xdc, 0xc1, 0x00, 0xdc, + 0x80, 0x01, 0xdc, 0xc1, 0x01, 0xdc, 0xc4, 0x00, + 0xdc, 0xc3, 0xb0, 0x34, 0x00, 0x07, 0x8e, 0x00, + 0x09, 0xa5, 0xc0, 0x00, 0xdc, 0xc6, 0xb0, 0x05, + 0x01, 0x09, 0xb0, 0x09, 0x00, 0x07, 0x8a, 0x01, + 0x09, 0xb0, 0x12, 0x00, 0x07, 0xb0, 0x67, 0xc2, + 0x41, 0x00, 0x04, 0xdc, 0xc1, 0x03, 0xdc, 0xc0, + 0x41, 0x00, 0x05, 0x01, 0x83, 0x00, 0xdc, 0x85, + 0xc0, 0x82, 0xc1, 0xb0, 0x95, 0xc1, 0x00, 0xdc, + 0xc6, 0x00, 0xdc, 0xc1, 0x00, 0xea, 0x00, 0xd6, + 0x00, 0xdc, 0x00, 0xca, 0xe4, 0x00, 0xe8, 0x01, + 0xe4, 0x00, 0xdc, 0x00, 0xda, 0xc0, 0x00, 0xe9, + 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xb2, 0x9f, 0xc1, + 0x01, 0x01, 0xc3, 0x02, 0x01, 0xc1, 0x83, 0xc0, + 0x82, 0x01, 0x01, 0xc0, 0x00, 0xdc, 0xc0, 0x01, + 0x01, 0x03, 0xdc, 0xc0, 0xb8, 0x03, 0xcd, 0xc2, + 0xb0, 0x5c, 0x00, 0x09, 0xb0, 0x2f, 0xdf, 0xb1, + 0xf9, 0x00, 0xda, 0x00, 0xe4, 0x00, 0xe8, 0x00, + 0xde, 0x01, 0xe0, 0xb0, 0x38, 0x01, 0x08, 0xb8, + 0x6d, 0xa3, 0xc0, 0x83, 0xc9, 0x9f, 0xc1, 0xb0, + 0x1f, 0xc1, 0xb0, 0xe3, 0x00, 0x09, 0xa4, 0x00, + 0x09, 0xb0, 0x66, 0x00, 0x09, 0x9a, 0xd1, 0xb0, + 0x08, 0x02, 0xdc, 0xa4, 0x00, 0x09, 0xb0, 0x2e, + 0x00, 0x07, 0x8b, 0x00, 0x09, 0xb0, 0xbe, 0xc0, + 0x80, 0xc1, 0x00, 0xdc, 0x81, 0xc1, 0x84, 0xc1, + 0x80, 0xc0, 0xb0, 0x03, 0x00, 0x09, 0xb0, 0xc5, + 0x00, 0x09, 0xb8, 0x46, 0xff, 0x00, 0x1a, 0xb2, + 0xd0, 0xc6, 0x06, 0xdc, 0xc1, 0xb3, 0x9c, 0x00, + 0xdc, 0xb0, 0xb1, 0x00, 0xdc, 0xb0, 0x64, 0xc4, + 0xb6, 0x61, 0x00, 0xdc, 0x80, 0xc0, 0xa7, 0xc0, + 0x00, 0x01, 0x00, 0xdc, 0x83, 0x00, 0x09, 0xb0, + 0x74, 0xc0, 0x00, 0xdc, 0xb2, 0x0c, 0xc3, 0xb0, + 0x10, 0xc4, 0xb1, 0x0c, 0xc1, 0xb0, 0x1f, 0x02, + 0xdc, 0xb0, 0x15, 0x01, 0xdc, 0xc2, 0x00, 0xdc, + 0xc0, 0x03, 0xdc, 0xb0, 0x00, 0xc0, 0x00, 0xdc, + 0xc0, 0x00, 0xdc, 0xb0, 0x8f, 0x00, 0x09, 0xa8, + 0x00, 0x09, 0x8d, 0x00, 0x09, 0xb0, 0x08, 0x00, + 0x09, 0x00, 0x07, 0xb0, 0x14, 0xc2, 0xaf, 0x01, + 0x09, 0xb0, 0x0d, 0x00, 0x07, 0xb0, 0x1b, 0x00, + 0x09, 0x88, 0x00, 0x07, 0xb0, 0x39, 0x00, 0x09, + 0x00, 0x07, 0xb0, 0x81, 0x00, 0x07, 0x00, 0x09, + 0xb0, 0x1f, 0x01, 0x07, 0x8f, 0x00, 0x09, 0x97, + 0xc6, 0x82, 0xc4, 0xb0, 0x28, 0x02, 0x09, 0xb0, + 0x40, 0x00, 0x09, 0x82, 0x00, 0x07, 0x96, 0xc0, + 0xb0, 0x32, 0x00, 0x09, 0x00, 0x07, 0xb0, 0xca, + 0x00, 0x09, 0x00, 0x07, 0xb0, 0x4d, 0x00, 0x09, + 0xb0, 0x45, 0x00, 0x09, 0x00, 0x07, 0xb0, 0x42, + 0x00, 0x09, 0xb0, 0xdc, 0x00, 0x09, 0x00, 0x07, + 0xb0, 0xd1, 0x01, 0x09, 0x83, 0x00, 0x07, 0xb0, + 0x6b, 0x00, 0x09, 0xb0, 0x22, 0x00, 0x09, 0x91, + 0x00, 0x09, 0xb0, 0x20, 0x00, 0x09, 0xb1, 0x74, + 0x00, 0x09, 0xb0, 0xd1, 0x00, 0x07, 0x80, 0x01, + 0x09, 0xb0, 0x20, 0x00, 0x09, 0xb1, 0x78, 0x01, + 0x09, 0xb8, 0x39, 0xbb, 0x00, 0x09, 0xb8, 0x01, + 0x8f, 0x04, 0x01, 0xb0, 0x0a, 0xc6, 0xb4, 0x88, + 0x01, 0x06, 0xb8, 0x44, 0x7b, 0x00, 0x01, 0xb8, + 0x0c, 0x95, 0x01, 0xd8, 0x02, 0x01, 0x82, 0x00, + 0xe2, 0x04, 0xd8, 0x87, 0x07, 0xdc, 0x81, 0xc4, + 0x01, 0xdc, 0x9d, 0xc3, 0xb0, 0x63, 0xc2, 0xb8, + 0x05, 0x8a, 0xc6, 0x80, 0xd0, 0x81, 0xc6, 0x80, + 0xc1, 0x80, 0xc4, 0xb0, 0x33, 0xc0, 0xb0, 0x6f, + 0xc6, 0xb1, 0x46, 0xc0, 0xb0, 0x0c, 0xc3, 0xb1, + 0xcb, 0x01, 0xe8, 0x00, 0xdc, 0xc0, 0xb0, 0xcd, + 0xc0, 0x00, 0xdc, 0xb2, 0xaf, 0x06, 0xdc, 0xb0, + 0x3c, 0xc5, 0x00, 0x07, +}; + +static const uint8_t unicode_cc_index[87] = { + 0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05, + 0x00, 0xe7, 0x06, 0x00, 0x45, 0x07, 0x00, 0x9c, + 0x08, 0x00, 0x4d, 0x09, 0x00, 0x3c, 0x0b, 0x00, + 0x3d, 0x0d, 0x00, 0x36, 0x0f, 0x00, 0x38, 0x10, + 0x20, 0x3a, 0x19, 0x00, 0xcb, 0x1a, 0x20, 0xd3, + 0x1c, 0x00, 0xcf, 0x1d, 0x00, 0xe2, 0x20, 0x00, + 0x2e, 0x30, 0x20, 0x2b, 0xa9, 0x20, 0xed, 0xab, + 0x00, 0x39, 0x0a, 0x01, 0x4c, 0x0f, 0x01, 0x35, + 0x11, 0x21, 0x66, 0x13, 0x01, 0x40, 0x16, 0x01, + 0x47, 0x1a, 0x01, 0xf0, 0x6a, 0x21, 0x8a, 0xd1, + 0x01, 0xec, 0xe4, 0x21, 0x4b, 0xe9, 0x01, +}; + +static const uint32_t unicode_decomp_table1[709] = { + 0x00280081, 0x002a0097, 0x002a8081, 0x002bc097, + 0x002c8115, 0x002d0097, 0x002d4081, 0x002e0097, + 0x002e4115, 0x002f0199, 0x00302016, 0x00400842, + 0x00448a42, 0x004a0442, 0x004c0096, 0x004c8117, + 0x004d0242, 0x004e4342, 0x004fc12f, 0x0050c342, + 0x005240bf, 0x00530342, 0x00550942, 0x005a0842, + 0x005e0096, 0x005e4342, 0x005fc081, 0x00680142, + 0x006bc142, 0x00710185, 0x0071c317, 0x00734844, + 0x00778344, 0x00798342, 0x007b02be, 0x007c4197, + 0x007d0142, 0x007e0444, 0x00800e42, 0x00878142, + 0x00898744, 0x00ac0483, 0x00b60317, 0x00b80283, + 0x00d00214, 0x00d10096, 0x00dd0080, 0x00de8097, + 0x00df8080, 0x00e10097, 0x00e1413e, 0x00e1c080, + 0x00e204be, 0x00ea83ae, 0x00f282ae, 0x00f401ad, + 0x00f4c12e, 0x00f54103, 0x00fc0303, 0x00fe4081, + 0x0100023e, 0x0101c0be, 0x010301be, 0x010640be, + 0x010e40be, 0x0114023e, 0x0115c0be, 0x011701be, + 0x011d8144, 0x01304144, 0x01340244, 0x01358144, + 0x01368344, 0x01388344, 0x013a8644, 0x013e0144, + 0x0161c085, 0x018882ae, 0x019d422f, 0x01b00184, + 0x01b4c084, 0x024a4084, 0x024c4084, 0x024d0084, + 0x0256042e, 0x0272c12e, 0x02770120, 0x0277c084, + 0x028cc084, 0x028d8084, 0x029641ae, 0x02978084, + 0x02d20084, 0x02d2c12e, 0x02d70120, 0x02e50084, + 0x02f281ae, 0x03120084, 0x03300084, 0x0331c122, + 0x0332812e, 0x035281ae, 0x03768084, 0x037701ae, + 0x038cc085, 0x03acc085, 0x03b7012f, 0x03c30081, + 0x03d0c084, 0x03d34084, 0x03d48084, 0x03d5c084, + 0x03d70084, 0x03da4084, 0x03dcc084, 0x03dd412e, + 0x03ddc085, 0x03de0084, 0x03de4085, 0x03e04084, + 0x03e4c084, 0x03e74084, 0x03e88084, 0x03e9c084, + 0x03eb0084, 0x03ee4084, 0x04098084, 0x043f0081, + 0x06c18484, 0x06c48084, 0x06cec184, 0x06d00120, + 0x06d0c084, 0x074b0383, 0x074cc41f, 0x074f1783, + 0x075e0081, 0x0766d283, 0x07801d44, 0x078e8942, + 0x07931844, 0x079f0d42, 0x07a58216, 0x07a68085, + 0x07a6c0be, 0x07a80d44, 0x07aea044, 0x07c00122, + 0x07c08344, 0x07c20122, 0x07c28344, 0x07c40122, + 0x07c48244, 0x07c60122, 0x07c68244, 0x07c8113e, + 0x07d08244, 0x07d20122, 0x07d28244, 0x07d40122, + 0x07d48344, 0x07d64c3e, 0x07dc4080, 0x07dc80be, + 0x07dcc080, 0x07dd00be, 0x07dd4080, 0x07dd80be, + 0x07ddc080, 0x07de00be, 0x07de4080, 0x07de80be, + 0x07dec080, 0x07df00be, 0x07df4080, 0x07e00820, + 0x07e40820, 0x07e80820, 0x07ec05be, 0x07eec080, + 0x07ef00be, 0x07ef4097, 0x07ef8080, 0x07efc117, + 0x07f0443e, 0x07f24080, 0x07f280be, 0x07f2c080, + 0x07f303be, 0x07f4c080, 0x07f582ae, 0x07f6c080, + 0x07f7433e, 0x07f8c080, 0x07f903ae, 0x07fac080, + 0x07fb013e, 0x07fb8102, 0x07fc83be, 0x07fe4080, + 0x07fe80be, 0x07fec080, 0x07ff00be, 0x07ff4080, + 0x07ff8097, 0x0800011e, 0x08008495, 0x08044081, + 0x0805c097, 0x08090081, 0x08094097, 0x08098099, + 0x080bc081, 0x080cc085, 0x080d00b1, 0x080d8085, + 0x080dc0b1, 0x080f0197, 0x0811c197, 0x0815c0b3, + 0x0817c081, 0x081c0595, 0x081ec081, 0x081f0215, + 0x0820051f, 0x08228583, 0x08254415, 0x082a0097, + 0x08400119, 0x08408081, 0x0840c0bf, 0x08414119, + 0x0841c081, 0x084240bf, 0x0842852d, 0x08454081, + 0x08458097, 0x08464295, 0x08480097, 0x08484099, + 0x08488097, 0x08490081, 0x08498080, 0x084a0081, + 0x084a8102, 0x084b0495, 0x084d421f, 0x084e4081, + 0x084ec099, 0x084f0283, 0x08514295, 0x08540119, + 0x0854809b, 0x0854c619, 0x0857c097, 0x08580081, + 0x08584097, 0x08588099, 0x0858c097, 0x08590081, + 0x08594097, 0x08598099, 0x0859c09b, 0x085a0097, + 0x085a4081, 0x085a8097, 0x085ac099, 0x085b0295, + 0x085c4097, 0x085c8099, 0x085cc097, 0x085d0081, + 0x085d4097, 0x085d8099, 0x085dc09b, 0x085e0097, + 0x085e4081, 0x085e8097, 0x085ec099, 0x085f0215, + 0x08624099, 0x0866813e, 0x086b80be, 0x087341be, + 0x088100be, 0x088240be, 0x088300be, 0x088901be, + 0x088b0085, 0x088b40b1, 0x088bc085, 0x088c00b1, + 0x089040be, 0x089100be, 0x0891c1be, 0x089801be, + 0x089b42be, 0x089d0144, 0x089e0144, 0x08a00144, + 0x08a10144, 0x08a20144, 0x08ab023e, 0x08b80244, + 0x08ba8220, 0x08ca411e, 0x0918049f, 0x091a4523, + 0x091cc097, 0x091d04a5, 0x091f452b, 0x0921c09b, + 0x092204a1, 0x09244525, 0x0926c099, 0x09270d25, + 0x092d8d1f, 0x09340d1f, 0x093a8081, 0x0a8300b3, + 0x0a9d0099, 0x0a9d4097, 0x0a9d8099, 0x0ab700be, + 0x0b1f0115, 0x0b5bc081, 0x0ba7c081, 0x0bbcc081, + 0x0bc004ad, 0x0bc244ad, 0x0bc484ad, 0x0bc6f383, + 0x0be0852d, 0x0be31d03, 0x0bf1882d, 0x0c000081, + 0x0c0d8283, 0x0c130b84, 0x0c194284, 0x0c1c0122, + 0x0c1cc122, 0x0c1d8122, 0x0c1e4122, 0x0c1f0122, + 0x0c250084, 0x0c26c123, 0x0c278084, 0x0c27c085, + 0x0c2b0b84, 0x0c314284, 0x0c340122, 0x0c34c122, + 0x0c358122, 0x0c364122, 0x0c370122, 0x0c3d0084, + 0x0c3dc220, 0x0c3f8084, 0x0c3fc085, 0x0c4c4a2d, + 0x0c51451f, 0x0c53ca9f, 0x0c5915ad, 0x0c648703, + 0x0c800741, 0x0c838089, 0x0c83c129, 0x0c8441a9, + 0x0c850089, 0x0c854129, 0x0c85c2a9, 0x0c870089, + 0x0c87408f, 0x0c87808d, 0x0c881241, 0x0c910203, + 0x0c940099, 0x0c9444a3, 0x0c968323, 0x0c98072d, + 0x0c9b84af, 0x0c9dc2a1, 0x0c9f00b5, 0x0c9f40b3, + 0x0c9f8085, 0x0ca01883, 0x0cac4223, 0x0cad4523, + 0x0cafc097, 0x0cb004a1, 0x0cb241a5, 0x0cb30097, + 0x0cb34099, 0x0cb38097, 0x0cb3c099, 0x0cb417ad, + 0x0cbfc085, 0x0cc001b3, 0x0cc0c0b1, 0x0cc100b3, + 0x0cc14131, 0x0cc1c0b5, 0x0cc200b3, 0x0cc241b1, + 0x0cc30133, 0x0cc38131, 0x0cc40085, 0x0cc440b1, + 0x0cc48133, 0x0cc50085, 0x0cc540b5, 0x0cc580b7, + 0x0cc5c0b5, 0x0cc600b1, 0x0cc64135, 0x0cc6c0b3, + 0x0cc701b1, 0x0cc7c0b3, 0x0cc800b5, 0x0cc840b3, + 0x0cc881b1, 0x0cc9422f, 0x0cca4131, 0x0ccac0b5, + 0x0ccb00b1, 0x0ccb40b3, 0x0ccb80b5, 0x0ccbc0b1, + 0x0ccc012f, 0x0ccc80b5, 0x0cccc0b3, 0x0ccd00b5, + 0x0ccd40b1, 0x0ccd80b5, 0x0ccdc085, 0x0cce02b1, + 0x0ccf40b3, 0x0ccf80b1, 0x0ccfc085, 0x0cd001b1, + 0x0cd0c0b3, 0x0cd101b1, 0x0cd1c0b5, 0x0cd200b3, + 0x0cd24085, 0x0cd280b5, 0x0cd2c085, 0x0cd30133, + 0x0cd381b1, 0x0cd440b3, 0x0cd48085, 0x0cd4c0b1, + 0x0cd500b3, 0x0cd54085, 0x0cd580b5, 0x0cd5c0b1, + 0x0cd60521, 0x0cd88525, 0x0cdb02a5, 0x0cdc4099, + 0x0cdc8117, 0x0cdd0099, 0x0cdd4197, 0x0cde0127, + 0x0cde8285, 0x0cdfc089, 0x0ce0043f, 0x0ce20099, + 0x0ce2409b, 0x0ce283bf, 0x0ce44219, 0x0ce54205, + 0x0ce6433f, 0x0ce7c131, 0x0ce84085, 0x0ce881b1, + 0x0ce94085, 0x0ce98107, 0x0cea0089, 0x0cea4097, + 0x0cea8219, 0x0ceb809d, 0x0cebc08d, 0x0cec083f, + 0x0cf00105, 0x0cf0809b, 0x0cf0c197, 0x0cf1809b, + 0x0cf1c099, 0x0cf20517, 0x0cf48099, 0x0cf4c117, + 0x0cf54119, 0x0cf5c097, 0x0cf6009b, 0x0cf64099, + 0x0cf68217, 0x0cf78119, 0x0cf804a1, 0x0cfa4525, + 0x0cfcc525, 0x0cff4125, 0x0cffc099, 0x29a70103, + 0x29dc0081, 0x29fc8195, 0x29fe0103, 0x2ad70203, + 0x2ada4081, 0x3e401482, 0x3e4a7f82, 0x3e6a3f82, + 0x3e8aa102, 0x3e9b0110, 0x3e9c2f82, 0x3eb3c590, + 0x3ec00197, 0x3ec0c119, 0x3ec1413f, 0x3ec4c2af, + 0x3ec74184, 0x3ec804ad, 0x3eca4081, 0x3eca8304, + 0x3ecc03a0, 0x3ece02a0, 0x3ecf8084, 0x3ed00120, + 0x3ed0c120, 0x3ed184ae, 0x3ed3c085, 0x3ed4312d, + 0x3ef4cbad, 0x3efa892f, 0x3eff022d, 0x3f002f2f, + 0x3f1782a5, 0x3f18c0b1, 0x3f1907af, 0x3f1cffaf, + 0x3f3c81a5, 0x3f3d64af, 0x3f542031, 0x3f649b31, + 0x3f7c0131, 0x3f7c83b3, 0x3f7e40b1, 0x3f7e80bd, + 0x3f7ec0bb, 0x3f7f00b3, 0x3f840503, 0x3f8c01ad, + 0x3f8cc315, 0x3f8e462d, 0x3f91cc03, 0x3f97c695, + 0x3f9c01af, 0x3f9d0085, 0x3f9d852f, 0x3fa03aad, + 0x3fbd442f, 0x3fc06f1f, 0x3fd7c11f, 0x3fd85fad, + 0x3fe80081, 0x3fe84f1f, 0x3ff0831f, 0x3ff2831f, + 0x3ff4831f, 0x3ff6819f, 0x3ff80783, 0x41724092, + 0x41790092, 0x41e04d83, 0x41e70f91, 0x44268192, + 0x442ac092, 0x444b8112, 0x44d2c112, 0x44e0c192, + 0x44e38092, 0x44e44092, 0x44f14212, 0x452ec212, + 0x456e8112, 0x464e0092, 0x58484412, 0x5b5a0192, + 0x73358d1f, 0x733c051f, 0x74578392, 0x746ec312, + 0x75000d1f, 0x75068d1f, 0x750d0d1f, 0x7513839f, + 0x7515891f, 0x751a0d1f, 0x75208d1f, 0x75271015, + 0x752f439f, 0x7531459f, 0x75340d1f, 0x753a8d1f, + 0x75410395, 0x7543441f, 0x7545839f, 0x75478d1f, + 0x754e0795, 0x7552839f, 0x75548d1f, 0x755b0d1f, + 0x75618d1f, 0x75680d1f, 0x756e8d1f, 0x75750d1f, + 0x757b8d1f, 0x75820d1f, 0x75888d1f, 0x758f0d1f, + 0x75958d1f, 0x759c0d1f, 0x75a28d1f, 0x75a90103, + 0x75aa089f, 0x75ae4081, 0x75ae839f, 0x75b04081, + 0x75b08c9f, 0x75b6c081, 0x75b7032d, 0x75b8889f, + 0x75bcc081, 0x75bd039f, 0x75bec081, 0x75bf0c9f, + 0x75c54081, 0x75c5832d, 0x75c7089f, 0x75cb4081, + 0x75cb839f, 0x75cd4081, 0x75cd8c9f, 0x75d3c081, + 0x75d4032d, 0x75d5889f, 0x75d9c081, 0x75da039f, + 0x75dbc081, 0x75dc0c9f, 0x75e24081, 0x75e2832d, + 0x75e4089f, 0x75e84081, 0x75e8839f, 0x75ea4081, + 0x75ea8c9f, 0x75f0c081, 0x75f1042d, 0x75f3851f, + 0x75f6051f, 0x75f8851f, 0x75fb051f, 0x75fd851f, + 0x780c049f, 0x780e419f, 0x780f059f, 0x7811c203, + 0x7812d0ad, 0x781b0103, 0x7b80022d, 0x7b814dad, + 0x7b884203, 0x7b89c081, 0x7b8a452d, 0x7b8d0403, + 0x7b908081, 0x7b91dc03, 0x7ba0052d, 0x7ba2c8ad, + 0x7ba84483, 0x7baac8ad, 0x7c400097, 0x7c404521, + 0x7c440d25, 0x7c4a8087, 0x7c4ac115, 0x7c4b4117, + 0x7c4c0d1f, 0x7c528217, 0x7c538099, 0x7c53c097, + 0x7c5a8197, 0x7c640097, 0x7c80012f, 0x7c808081, + 0x7c841603, 0x7c9004c1, 0x7c940103, 0x7efc051f, + 0xbe0001ac, 0xbe00d110, 0xbe0947ac, 0xbe0d3910, + 0xbe29872c, 0xbe2d022c, 0xbe2e3790, 0xbe49ff90, + 0xbe69bc10, +}; + +static const uint16_t unicode_decomp_table2[709] = { + 0x0020, 0x0000, 0x0061, 0x0002, 0x0004, 0x0006, 0x03bc, 0x0008, + 0x000a, 0x000c, 0x0015, 0x0095, 0x00a5, 0x00b9, 0x00c1, 0x00c3, + 0x00c7, 0x00cb, 0x00d1, 0x00d7, 0x00dd, 0x00e0, 0x00e6, 0x00f8, + 0x0108, 0x010a, 0x0073, 0x0110, 0x0112, 0x0114, 0x0120, 0x012c, + 0x0144, 0x014d, 0x0153, 0x0162, 0x0168, 0x016a, 0x0176, 0x0192, + 0x0194, 0x01a9, 0x01bb, 0x01c7, 0x01d1, 0x01d5, 0x02b9, 0x01d7, + 0x003b, 0x01d9, 0x01db, 0x00b7, 0x01e1, 0x01fc, 0x020c, 0x0218, + 0x021d, 0x0223, 0x0227, 0x03a3, 0x0233, 0x023f, 0x0242, 0x024b, + 0x024e, 0x0251, 0x025d, 0x0260, 0x0269, 0x026c, 0x026f, 0x0275, + 0x0278, 0x0281, 0x028a, 0x029c, 0x029f, 0x02a3, 0x02af, 0x02b9, + 0x02c5, 0x02c9, 0x02cd, 0x02d1, 0x02d5, 0x02e7, 0x02ed, 0x02f1, + 0x02f5, 0x02f9, 0x02fd, 0x0305, 0x0309, 0x030d, 0x0313, 0x0317, + 0x031b, 0x0323, 0x0327, 0x032b, 0x032f, 0x0335, 0x033d, 0x0341, + 0x0349, 0x034d, 0x0351, 0x0f0b, 0x0357, 0x035b, 0x035f, 0x0363, + 0x0367, 0x036b, 0x036f, 0x0373, 0x0379, 0x037d, 0x0381, 0x0385, + 0x0389, 0x038d, 0x0391, 0x0395, 0x0399, 0x039d, 0x03a1, 0x10dc, + 0x03a5, 0x03c9, 0x03cd, 0x03d9, 0x03dd, 0x03e1, 0x03ef, 0x03f1, + 0x043d, 0x044f, 0x0499, 0x04f0, 0x0502, 0x054a, 0x0564, 0x056c, + 0x0570, 0x0573, 0x059a, 0x05fa, 0x05fe, 0x0607, 0x060b, 0x0614, + 0x0618, 0x061e, 0x0622, 0x0628, 0x068e, 0x0694, 0x0698, 0x069e, + 0x06a2, 0x06ab, 0x03ac, 0x06f3, 0x03ad, 0x06f6, 0x03ae, 0x06f9, + 0x03af, 0x06fc, 0x03cc, 0x06ff, 0x03cd, 0x0702, 0x03ce, 0x0705, + 0x0709, 0x070d, 0x0711, 0x0386, 0x0732, 0x0735, 0x03b9, 0x0737, + 0x073b, 0x0388, 0x0753, 0x0389, 0x0756, 0x0390, 0x076b, 0x038a, + 0x0777, 0x03b0, 0x0789, 0x038e, 0x0799, 0x079f, 0x07a3, 0x038c, + 0x07b8, 0x038f, 0x07bb, 0x00b4, 0x07be, 0x07c0, 0x07c2, 0x2010, + 0x07cb, 0x002e, 0x07cd, 0x07cf, 0x0020, 0x07d2, 0x07d6, 0x07db, + 0x07df, 0x07e4, 0x07ea, 0x07f0, 0x0020, 0x07f6, 0x2212, 0x0801, + 0x0805, 0x0807, 0x081d, 0x0825, 0x0827, 0x0043, 0x082d, 0x0830, + 0x0190, 0x0836, 0x0839, 0x004e, 0x0845, 0x0847, 0x084c, 0x084e, + 0x0851, 0x005a, 0x03a9, 0x005a, 0x0853, 0x0857, 0x0860, 0x0069, + 0x0862, 0x0865, 0x086f, 0x0874, 0x087a, 0x087e, 0x08a2, 0x0049, + 0x08a4, 0x08a6, 0x08a9, 0x0056, 0x08ab, 0x08ad, 0x08b0, 0x08b4, + 0x0058, 0x08b6, 0x08b8, 0x08bb, 0x08c0, 0x08c2, 0x08c5, 0x0076, + 0x08c7, 0x08c9, 0x08cc, 0x08d0, 0x0078, 0x08d2, 0x08d4, 0x08d7, + 0x08db, 0x08de, 0x08e4, 0x08e7, 0x08f0, 0x08f3, 0x08f6, 0x08f9, + 0x0902, 0x0906, 0x090b, 0x090f, 0x0914, 0x0917, 0x091a, 0x0923, + 0x092c, 0x093b, 0x093e, 0x0941, 0x0944, 0x0947, 0x094a, 0x0956, + 0x095c, 0x0960, 0x0962, 0x0964, 0x0968, 0x096a, 0x0970, 0x0978, + 0x097c, 0x0980, 0x0986, 0x0989, 0x098f, 0x0991, 0x0030, 0x0993, + 0x0999, 0x099c, 0x099e, 0x09a1, 0x09a4, 0x2d61, 0x6bcd, 0x9f9f, + 0x09a6, 0x09b1, 0x09bc, 0x09c7, 0x0a95, 0x0aa1, 0x0b15, 0x0020, + 0x0b27, 0x0b31, 0x0b8d, 0x0ba1, 0x0ba5, 0x0ba9, 0x0bad, 0x0bb1, + 0x0bb5, 0x0bb9, 0x0bbd, 0x0bc1, 0x0bc5, 0x0c21, 0x0c35, 0x0c39, + 0x0c3d, 0x0c41, 0x0c45, 0x0c49, 0x0c4d, 0x0c51, 0x0c55, 0x0c59, + 0x0c6f, 0x0c71, 0x0c73, 0x0ca0, 0x0cbc, 0x0cdc, 0x0ce4, 0x0cec, + 0x0cf4, 0x0cfc, 0x0d04, 0x0d0c, 0x0d14, 0x0d22, 0x0d2e, 0x0d7a, + 0x0d82, 0x0d85, 0x0d89, 0x0d8d, 0x0d9d, 0x0db1, 0x0db5, 0x0dbc, + 0x0dc2, 0x0dc6, 0x0e28, 0x0e2c, 0x0e30, 0x0e32, 0x0e36, 0x0e3c, + 0x0e3e, 0x0e41, 0x0e43, 0x0e46, 0x0e77, 0x0e7b, 0x0e89, 0x0e8e, + 0x0e94, 0x0e9c, 0x0ea3, 0x0ea9, 0x0eb4, 0x0ebe, 0x0ec6, 0x0eca, + 0x0ecf, 0x0ed9, 0x0edd, 0x0ee4, 0x0eec, 0x0ef3, 0x0ef8, 0x0f04, + 0x0f0a, 0x0f15, 0x0f1b, 0x0f22, 0x0f28, 0x0f33, 0x0f3d, 0x0f45, + 0x0f4c, 0x0f51, 0x0f57, 0x0f5e, 0x0f63, 0x0f69, 0x0f70, 0x0f76, + 0x0f7d, 0x0f82, 0x0f89, 0x0f8d, 0x0f9e, 0x0fa4, 0x0fa9, 0x0fad, + 0x0fb8, 0x0fbe, 0x0fc9, 0x0fd0, 0x0fd6, 0x0fda, 0x0fe1, 0x0fe5, + 0x0fef, 0x0ffa, 0x1000, 0x1004, 0x1009, 0x100f, 0x1013, 0x101a, + 0x101f, 0x1023, 0x1029, 0x102f, 0x1032, 0x1036, 0x1039, 0x103f, + 0x1045, 0x1059, 0x1061, 0x1079, 0x107c, 0x1080, 0x1095, 0x10a1, + 0x10b1, 0x10c3, 0x10cb, 0x10cf, 0x10da, 0x10de, 0x10ea, 0x10f2, + 0x10f4, 0x1100, 0x1105, 0x1111, 0x1141, 0x1149, 0x114d, 0x1153, + 0x1157, 0x115a, 0x116e, 0x1171, 0x1175, 0x117b, 0x117d, 0x1181, + 0x1184, 0x118c, 0x1192, 0x1196, 0x119c, 0x11a2, 0x11a8, 0x11ab, + 0xa76f, 0x11af, 0x11b2, 0x11b6, 0x028d, 0x11be, 0x1210, 0x130e, + 0x140c, 0x1490, 0x1495, 0x1553, 0x156c, 0x1572, 0x1578, 0x157e, + 0x158a, 0x1596, 0x002b, 0x15a1, 0x15b9, 0x15bd, 0x15c1, 0x15c5, + 0x15c9, 0x15cd, 0x15e1, 0x15e5, 0x1649, 0x1662, 0x1688, 0x168e, + 0x174c, 0x1752, 0x1757, 0x1777, 0x1877, 0x187d, 0x1911, 0x19d3, + 0x1a77, 0x1a7f, 0x1a9d, 0x1aa2, 0x1ab6, 0x1ac0, 0x1ac6, 0x1ada, + 0x1adf, 0x1ae5, 0x1af3, 0x1b23, 0x1b30, 0x1b38, 0x1b3c, 0x1b52, + 0x1bc9, 0x1bdb, 0x1bdd, 0x1bdf, 0x3164, 0x1c20, 0x1c22, 0x1c24, + 0x1c26, 0x1c28, 0x1c2a, 0x1c48, 0x1c4d, 0x1c52, 0x1c88, 0x1cce, + 0x1cdc, 0x1ce1, 0x1cea, 0x1cf3, 0x1d01, 0x1d06, 0x1d0b, 0x1d1d, + 0x1d2f, 0x1d38, 0x1d3d, 0x1d61, 0x1d6f, 0x1d71, 0x1d73, 0x1d93, + 0x1dae, 0x1db0, 0x1db2, 0x1db4, 0x1db6, 0x1db8, 0x1dba, 0x1dbc, + 0x1ddc, 0x1dde, 0x1de0, 0x1de2, 0x1de4, 0x1deb, 0x1ded, 0x1def, + 0x1df1, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, + 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, + 0x1e20, 0x03f4, 0x1e22, 0x2207, 0x1e24, 0x2202, 0x1e26, 0x1e2e, + 0x03f4, 0x1e30, 0x2207, 0x1e32, 0x2202, 0x1e34, 0x1e3c, 0x03f4, + 0x1e3e, 0x2207, 0x1e40, 0x2202, 0x1e42, 0x1e4a, 0x03f4, 0x1e4c, + 0x2207, 0x1e4e, 0x2202, 0x1e50, 0x1e58, 0x03f4, 0x1e5a, 0x2207, + 0x1e5c, 0x2202, 0x1e5e, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, + 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e80, 0x1ea3, 0x1ea7, 0x1ead, + 0x1eca, 0x062d, 0x1ed2, 0x1ede, 0x062c, 0x1eee, 0x1f5e, 0x1f6a, + 0x1f7d, 0x1f8f, 0x1fa2, 0x1fa4, 0x1fa8, 0x1fae, 0x1fb4, 0x1fb6, + 0x1fba, 0x1fbc, 0x1fc4, 0x1fc7, 0x1fc9, 0x1fcf, 0x1fd1, 0x30b5, + 0x1fd7, 0x202f, 0x2045, 0x2049, 0x204b, 0x2050, 0x209d, 0x20ae, + 0x21af, 0x21bf, 0x21c5, 0x22bf, 0x23dd, +}; + +static const uint8_t unicode_decomp_data[9451] = { + 0x20, 0x88, 0x20, 0x84, 0x32, 0x33, 0x20, 0x81, + 0x20, 0xa7, 0x31, 0x6f, 0x31, 0xd0, 0x34, 0x31, + 0xd0, 0x32, 0x33, 0xd0, 0x34, 0x41, 0x80, 0x41, + 0x81, 0x41, 0x82, 0x41, 0x83, 0x41, 0x88, 0x41, + 0x8a, 0x00, 0x00, 0x43, 0xa7, 0x45, 0x80, 0x45, + 0x81, 0x45, 0x82, 0x45, 0x88, 0x49, 0x80, 0x49, + 0x81, 0x49, 0x82, 0x49, 0x88, 0x00, 0x00, 0x4e, + 0x83, 0x4f, 0x80, 0x4f, 0x81, 0x4f, 0x82, 0x4f, + 0x83, 0x4f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x55, + 0x80, 0x55, 0x81, 0x55, 0x82, 0x55, 0x88, 0x59, + 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x80, 0x61, + 0x81, 0x61, 0x82, 0x61, 0x83, 0x61, 0x88, 0x61, + 0x8a, 0x00, 0x00, 0x63, 0xa7, 0x65, 0x80, 0x65, + 0x81, 0x65, 0x82, 0x65, 0x88, 0x69, 0x80, 0x69, + 0x81, 0x69, 0x82, 0x69, 0x88, 0x00, 0x00, 0x6e, + 0x83, 0x6f, 0x80, 0x6f, 0x81, 0x6f, 0x82, 0x6f, + 0x83, 0x6f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x75, + 0x80, 0x75, 0x81, 0x75, 0x82, 0x75, 0x88, 0x79, + 0x81, 0x00, 0x00, 0x79, 0x88, 0x41, 0x84, 0x41, + 0x86, 0x41, 0xa8, 0x43, 0x81, 0x43, 0x82, 0x43, + 0x87, 0x43, 0x8c, 0x44, 0x8c, 0x45, 0x84, 0x45, + 0x86, 0x45, 0x87, 0x45, 0xa8, 0x45, 0x8c, 0x47, + 0x82, 0x47, 0x86, 0x47, 0x87, 0x47, 0xa7, 0x48, + 0x82, 0x49, 0x83, 0x49, 0x84, 0x49, 0x86, 0x49, + 0xa8, 0x49, 0x87, 0x49, 0x4a, 0x69, 0x6a, 0x4a, + 0x82, 0x4b, 0xa7, 0x4c, 0x81, 0x4c, 0xa7, 0x4c, + 0x8c, 0x4c, 0x00, 0x00, 0x6b, 0x20, 0x6b, 0x4e, + 0x81, 0x4e, 0xa7, 0x4e, 0x8c, 0xbc, 0x02, 0x6e, + 0x4f, 0x84, 0x4f, 0x86, 0x4f, 0x8b, 0x52, 0x81, + 0x52, 0xa7, 0x52, 0x8c, 0x53, 0x81, 0x53, 0x82, + 0x53, 0xa7, 0x53, 0x8c, 0x54, 0xa7, 0x54, 0x8c, + 0x55, 0x83, 0x55, 0x84, 0x55, 0x86, 0x55, 0x8a, + 0x55, 0x8b, 0x55, 0xa8, 0x57, 0x82, 0x59, 0x82, + 0x59, 0x88, 0x5a, 0x81, 0x5a, 0x87, 0x5a, 0x8c, + 0x4f, 0x9b, 0x55, 0x9b, 0x44, 0x00, 0x7d, 0x01, + 0x44, 0x00, 0x7e, 0x01, 0x64, 0x00, 0x7e, 0x01, + 0x4c, 0x4a, 0x4c, 0x6a, 0x6c, 0x6a, 0x4e, 0x4a, + 0x4e, 0x6a, 0x6e, 0x6a, 0x41, 0x00, 0x8c, 0x49, + 0x00, 0x8c, 0x4f, 0x00, 0x8c, 0x55, 0x00, 0x8c, + 0xdc, 0x00, 0x84, 0xdc, 0x00, 0x81, 0xdc, 0x00, + 0x8c, 0xdc, 0x00, 0x80, 0xc4, 0x00, 0x84, 0x26, + 0x02, 0x84, 0xc6, 0x00, 0x84, 0x47, 0x8c, 0x4b, + 0x8c, 0x4f, 0xa8, 0xea, 0x01, 0x84, 0xeb, 0x01, + 0x84, 0xb7, 0x01, 0x8c, 0x92, 0x02, 0x8c, 0x6a, + 0x00, 0x8c, 0x44, 0x5a, 0x44, 0x7a, 0x64, 0x7a, + 0x47, 0x81, 0x4e, 0x00, 0x80, 0xc5, 0x00, 0x81, + 0xc6, 0x00, 0x81, 0xd8, 0x00, 0x81, 0x41, 0x8f, + 0x41, 0x91, 0x45, 0x8f, 0x45, 0x91, 0x49, 0x8f, + 0x49, 0x91, 0x4f, 0x8f, 0x4f, 0x91, 0x52, 0x8f, + 0x52, 0x91, 0x55, 0x8f, 0x55, 0x91, 0x53, 0xa6, + 0x54, 0xa6, 0x48, 0x8c, 0x41, 0x00, 0x87, 0x45, + 0x00, 0xa7, 0xd6, 0x00, 0x84, 0xd5, 0x00, 0x84, + 0x4f, 0x00, 0x87, 0x2e, 0x02, 0x84, 0x59, 0x00, + 0x84, 0x68, 0x00, 0x66, 0x02, 0x6a, 0x00, 0x72, + 0x00, 0x79, 0x02, 0x7b, 0x02, 0x81, 0x02, 0x77, + 0x00, 0x79, 0x00, 0x20, 0x86, 0x20, 0x87, 0x20, + 0x8a, 0x20, 0xa8, 0x20, 0x83, 0x20, 0x8b, 0x63, + 0x02, 0x6c, 0x00, 0x73, 0x00, 0x78, 0x00, 0x95, + 0x02, 0x80, 0x81, 0x00, 0x93, 0x88, 0x81, 0x20, + 0xc5, 0x20, 0x81, 0xa8, 0x00, 0x81, 0x91, 0x03, + 0x81, 0x95, 0x03, 0x81, 0x97, 0x03, 0x81, 0x99, + 0x03, 0x81, 0x00, 0x00, 0x00, 0x9f, 0x03, 0x81, + 0x00, 0x00, 0x00, 0xa5, 0x03, 0x81, 0xa9, 0x03, + 0x81, 0xca, 0x03, 0x81, 0x01, 0x03, 0x98, 0x07, + 0xa4, 0x07, 0xb0, 0x00, 0xb4, 0x00, 0xb6, 0x00, + 0xb8, 0x00, 0xca, 0x00, 0x01, 0x03, 0xb8, 0x07, + 0xc4, 0x07, 0xbe, 0x00, 0xc4, 0x00, 0xc8, 0x00, + 0xa5, 0x03, 0x0d, 0x13, 0x00, 0x01, 0x03, 0xd1, + 0x00, 0xd1, 0x07, 0xc6, 0x03, 0xc0, 0x03, 0xba, + 0x03, 0xc1, 0x03, 0xc2, 0x03, 0x00, 0x00, 0x98, + 0x03, 0xb5, 0x03, 0x15, 0x04, 0x80, 0x15, 0x04, + 0x88, 0x00, 0x00, 0x00, 0x13, 0x04, 0x81, 0x06, + 0x04, 0x88, 0x1a, 0x04, 0x81, 0x18, 0x04, 0x80, + 0x23, 0x04, 0x86, 0x18, 0x04, 0x86, 0x38, 0x04, + 0x86, 0x35, 0x04, 0x80, 0x35, 0x04, 0x88, 0x00, + 0x00, 0x00, 0x33, 0x04, 0x81, 0x56, 0x04, 0x88, + 0x3a, 0x04, 0x81, 0x38, 0x04, 0x80, 0x43, 0x04, + 0x86, 0x74, 0x04, 0x8f, 0x16, 0x04, 0x86, 0x10, + 0x04, 0x86, 0x10, 0x04, 0x88, 0x15, 0x04, 0x86, + 0xd8, 0x04, 0x88, 0x16, 0x04, 0x88, 0x17, 0x04, + 0x88, 0x18, 0x04, 0x84, 0x18, 0x04, 0x88, 0x1e, + 0x04, 0x88, 0xe8, 0x04, 0x88, 0x2d, 0x04, 0x88, + 0x23, 0x04, 0x84, 0x23, 0x04, 0x88, 0x23, 0x04, + 0x8b, 0x27, 0x04, 0x88, 0x2b, 0x04, 0x88, 0x65, + 0x05, 0x82, 0x05, 0x27, 0x06, 0x00, 0x2c, 0x00, + 0x2d, 0x21, 0x2d, 0x00, 0x2e, 0x23, 0x2d, 0x27, + 0x06, 0x00, 0x4d, 0x21, 0x4d, 0xa0, 0x4d, 0x23, + 0x4d, 0xd5, 0x06, 0x54, 0x06, 0x00, 0x00, 0x00, + 0x00, 0xc1, 0x06, 0x54, 0x06, 0xd2, 0x06, 0x54, + 0x06, 0x28, 0x09, 0x3c, 0x09, 0x30, 0x09, 0x3c, + 0x09, 0x33, 0x09, 0x3c, 0x09, 0x15, 0x09, 0x00, + 0x27, 0x01, 0x27, 0x02, 0x27, 0x07, 0x27, 0x0c, + 0x27, 0x0d, 0x27, 0x16, 0x27, 0x1a, 0x27, 0xbe, + 0x09, 0x09, 0x00, 0x09, 0x19, 0xa1, 0x09, 0xbc, + 0x09, 0xaf, 0x09, 0xbc, 0x09, 0x32, 0x0a, 0x3c, + 0x0a, 0x38, 0x0a, 0x3c, 0x0a, 0x16, 0x0a, 0x00, + 0x26, 0x01, 0x26, 0x06, 0x26, 0x2b, 0x0a, 0x3c, + 0x0a, 0x47, 0x0b, 0x56, 0x0b, 0x3e, 0x0b, 0x09, + 0x00, 0x09, 0x19, 0x21, 0x0b, 0x3c, 0x0b, 0x92, + 0x0b, 0xd7, 0x0b, 0xbe, 0x0b, 0x08, 0x00, 0x09, + 0x00, 0x08, 0x19, 0x46, 0x0c, 0x56, 0x0c, 0xbf, + 0x0c, 0xd5, 0x0c, 0xc6, 0x0c, 0xd5, 0x0c, 0xc2, + 0x0c, 0x04, 0x00, 0x08, 0x13, 0x3e, 0x0d, 0x08, + 0x00, 0x09, 0x00, 0x08, 0x19, 0xd9, 0x0d, 0xca, + 0x0d, 0xca, 0x0d, 0x0f, 0x05, 0x12, 0x00, 0x0f, + 0x15, 0x4d, 0x0e, 0x32, 0x0e, 0xcd, 0x0e, 0xb2, + 0x0e, 0x99, 0x0e, 0x12, 0x00, 0x12, 0x08, 0x42, + 0x0f, 0xb7, 0x0f, 0x4c, 0x0f, 0xb7, 0x0f, 0x51, + 0x0f, 0xb7, 0x0f, 0x56, 0x0f, 0xb7, 0x0f, 0x5b, + 0x0f, 0xb7, 0x0f, 0x40, 0x0f, 0xb5, 0x0f, 0x71, + 0x0f, 0x72, 0x0f, 0x71, 0x0f, 0x00, 0x03, 0x41, + 0x0f, 0xb2, 0x0f, 0x81, 0x0f, 0xb3, 0x0f, 0x80, + 0x0f, 0xb3, 0x0f, 0x81, 0x0f, 0x71, 0x0f, 0x80, + 0x0f, 0x92, 0x0f, 0xb7, 0x0f, 0x9c, 0x0f, 0xb7, + 0x0f, 0xa1, 0x0f, 0xb7, 0x0f, 0xa6, 0x0f, 0xb7, + 0x0f, 0xab, 0x0f, 0xb7, 0x0f, 0x90, 0x0f, 0xb5, + 0x0f, 0x25, 0x10, 0x2e, 0x10, 0x05, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x09, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x1b, 0x35, + 0x1b, 0x11, 0x1b, 0x35, 0x1b, 0x3a, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x1b, 0x35, + 0x1b, 0x3e, 0x1b, 0x35, 0x1b, 0x42, 0x1b, 0x35, + 0x1b, 0x41, 0x00, 0xc6, 0x00, 0x42, 0x00, 0x00, + 0x00, 0x44, 0x00, 0x45, 0x00, 0x8e, 0x01, 0x47, + 0x00, 0x4f, 0x00, 0x22, 0x02, 0x50, 0x00, 0x52, + 0x00, 0x54, 0x00, 0x55, 0x00, 0x57, 0x00, 0x61, + 0x00, 0x50, 0x02, 0x51, 0x02, 0x02, 0x1d, 0x62, + 0x00, 0x64, 0x00, 0x65, 0x00, 0x59, 0x02, 0x5b, + 0x02, 0x5c, 0x02, 0x67, 0x00, 0x00, 0x00, 0x6b, + 0x00, 0x6d, 0x00, 0x4b, 0x01, 0x6f, 0x00, 0x54, + 0x02, 0x16, 0x1d, 0x17, 0x1d, 0x70, 0x00, 0x74, + 0x00, 0x75, 0x00, 0x1d, 0x1d, 0x6f, 0x02, 0x76, + 0x00, 0x25, 0x1d, 0xb2, 0x03, 0xb3, 0x03, 0xb4, + 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x69, 0x00, 0x72, + 0x00, 0x75, 0x00, 0x76, 0x00, 0xb2, 0x03, 0xb3, + 0x03, 0xc1, 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x52, + 0x02, 0x63, 0x00, 0x55, 0x02, 0xf0, 0x00, 0x5c, + 0x02, 0x66, 0x00, 0x5f, 0x02, 0x61, 0x02, 0x65, + 0x02, 0x68, 0x02, 0x69, 0x02, 0x6a, 0x02, 0x7b, + 0x1d, 0x9d, 0x02, 0x6d, 0x02, 0x85, 0x1d, 0x9f, + 0x02, 0x71, 0x02, 0x70, 0x02, 0x72, 0x02, 0x73, + 0x02, 0x74, 0x02, 0x75, 0x02, 0x78, 0x02, 0x82, + 0x02, 0x83, 0x02, 0xab, 0x01, 0x89, 0x02, 0x8a, + 0x02, 0x1c, 0x1d, 0x8b, 0x02, 0x8c, 0x02, 0x7a, + 0x00, 0x90, 0x02, 0x91, 0x02, 0x92, 0x02, 0xb8, + 0x03, 0x41, 0x00, 0xa5, 0x42, 0x00, 0x87, 0x42, + 0x00, 0xa3, 0x42, 0x00, 0xb1, 0xc7, 0x00, 0x81, + 0x44, 0x00, 0x87, 0x44, 0x00, 0xa3, 0x44, 0x00, + 0xb1, 0x44, 0x00, 0xa7, 0x44, 0x00, 0xad, 0x12, + 0x01, 0x80, 0x12, 0x01, 0x81, 0x45, 0x00, 0xad, + 0x45, 0x00, 0xb0, 0x28, 0x02, 0x86, 0x46, 0x00, + 0x87, 0x47, 0x00, 0x84, 0x48, 0x00, 0x87, 0x48, + 0x00, 0xa3, 0x48, 0x00, 0x88, 0x48, 0x00, 0xa7, + 0x48, 0x00, 0xae, 0x49, 0x00, 0xb0, 0xcf, 0x00, + 0x81, 0x4b, 0x00, 0x81, 0x4b, 0x00, 0xa3, 0x4b, + 0x00, 0xb1, 0x4c, 0x00, 0xa3, 0x36, 0x1e, 0x84, + 0x4c, 0xb1, 0x4c, 0xad, 0x4d, 0x81, 0x4d, 0x87, + 0x4d, 0xa3, 0x4e, 0x87, 0x4e, 0xa3, 0x4e, 0xb1, + 0x4e, 0xad, 0xd5, 0x00, 0x81, 0xd5, 0x00, 0x88, + 0x4c, 0x01, 0x80, 0x4c, 0x01, 0x81, 0x50, 0x00, + 0x81, 0x50, 0x00, 0x87, 0x52, 0x00, 0x87, 0x52, + 0x00, 0xa3, 0x5a, 0x1e, 0x84, 0x52, 0x00, 0xb1, + 0x53, 0x00, 0x87, 0x53, 0x00, 0xa3, 0x5a, 0x01, + 0x87, 0x60, 0x01, 0x87, 0x62, 0x1e, 0x87, 0x54, + 0x00, 0x87, 0x54, 0x00, 0xa3, 0x54, 0x00, 0xb1, + 0x54, 0x00, 0xad, 0x55, 0x00, 0xa4, 0x55, 0x00, + 0xb0, 0x55, 0x00, 0xad, 0x68, 0x01, 0x81, 0x6a, + 0x01, 0x88, 0x56, 0x83, 0x56, 0xa3, 0x57, 0x80, + 0x57, 0x81, 0x57, 0x88, 0x57, 0x87, 0x57, 0xa3, + 0x58, 0x87, 0x58, 0x88, 0x59, 0x87, 0x5a, 0x82, + 0x5a, 0xa3, 0x5a, 0xb1, 0x68, 0xb1, 0x74, 0x88, + 0x77, 0x8a, 0x79, 0x8a, 0x61, 0x00, 0xbe, 0x02, + 0x7f, 0x01, 0x87, 0x41, 0x00, 0xa3, 0x41, 0x00, + 0x89, 0xc2, 0x00, 0x81, 0xc2, 0x00, 0x80, 0xc2, + 0x00, 0x89, 0xc2, 0x00, 0x83, 0xa0, 0x1e, 0x82, + 0x02, 0x01, 0x81, 0x02, 0x01, 0x80, 0x02, 0x01, + 0x89, 0x02, 0x01, 0x83, 0xa0, 0x1e, 0x86, 0x45, + 0x00, 0xa3, 0x45, 0x00, 0x89, 0x45, 0x00, 0x83, + 0xca, 0x00, 0x81, 0xca, 0x00, 0x80, 0xca, 0x00, + 0x89, 0xca, 0x00, 0x83, 0xb8, 0x1e, 0x82, 0x49, + 0x00, 0x89, 0x49, 0x00, 0xa3, 0x4f, 0x00, 0xa3, + 0x4f, 0x00, 0x89, 0xd4, 0x00, 0x81, 0xd4, 0x00, + 0x80, 0xd4, 0x00, 0x89, 0xd4, 0x00, 0x83, 0xcc, + 0x1e, 0x82, 0xa0, 0x01, 0x81, 0xa0, 0x01, 0x80, + 0xa0, 0x01, 0x89, 0xa0, 0x01, 0x83, 0xa0, 0x01, + 0xa3, 0x55, 0x00, 0xa3, 0x55, 0x00, 0x89, 0xaf, + 0x01, 0x81, 0xaf, 0x01, 0x80, 0xaf, 0x01, 0x89, + 0xaf, 0x01, 0x83, 0xaf, 0x01, 0xa3, 0x59, 0x00, + 0x80, 0x59, 0x00, 0xa3, 0x59, 0x00, 0x89, 0x59, + 0x00, 0x83, 0xb1, 0x03, 0x13, 0x03, 0x00, 0x1f, + 0x80, 0x00, 0x1f, 0x81, 0x00, 0x1f, 0xc2, 0x91, + 0x03, 0x13, 0x03, 0x08, 0x1f, 0x80, 0x08, 0x1f, + 0x81, 0x08, 0x1f, 0xc2, 0xb5, 0x03, 0x13, 0x03, + 0x10, 0x1f, 0x80, 0x10, 0x1f, 0x81, 0x95, 0x03, + 0x13, 0x03, 0x18, 0x1f, 0x80, 0x18, 0x1f, 0x81, + 0xb7, 0x03, 0x93, 0xb7, 0x03, 0x94, 0x20, 0x1f, + 0x80, 0x21, 0x1f, 0x80, 0x20, 0x1f, 0x81, 0x21, + 0x1f, 0x81, 0x20, 0x1f, 0xc2, 0x21, 0x1f, 0xc2, + 0x97, 0x03, 0x93, 0x97, 0x03, 0x94, 0x28, 0x1f, + 0x80, 0x29, 0x1f, 0x80, 0x28, 0x1f, 0x81, 0x29, + 0x1f, 0x81, 0x28, 0x1f, 0xc2, 0x29, 0x1f, 0xc2, + 0xb9, 0x03, 0x93, 0xb9, 0x03, 0x94, 0x30, 0x1f, + 0x80, 0x31, 0x1f, 0x80, 0x30, 0x1f, 0x81, 0x31, + 0x1f, 0x81, 0x30, 0x1f, 0xc2, 0x31, 0x1f, 0xc2, + 0x99, 0x03, 0x93, 0x99, 0x03, 0x94, 0x38, 0x1f, + 0x80, 0x39, 0x1f, 0x80, 0x38, 0x1f, 0x81, 0x39, + 0x1f, 0x81, 0x38, 0x1f, 0xc2, 0x39, 0x1f, 0xc2, + 0xbf, 0x03, 0x93, 0xbf, 0x03, 0x94, 0x40, 0x1f, + 0x80, 0x40, 0x1f, 0x81, 0x9f, 0x03, 0x13, 0x03, + 0x48, 0x1f, 0x80, 0x48, 0x1f, 0x81, 0xc5, 0x03, + 0x13, 0x03, 0x50, 0x1f, 0x80, 0x50, 0x1f, 0x81, + 0x50, 0x1f, 0xc2, 0xa5, 0x03, 0x94, 0x00, 0x00, + 0x00, 0x59, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x59, + 0x1f, 0x81, 0x00, 0x00, 0x00, 0x59, 0x1f, 0xc2, + 0xc9, 0x03, 0x93, 0xc9, 0x03, 0x94, 0x60, 0x1f, + 0x80, 0x61, 0x1f, 0x80, 0x60, 0x1f, 0x81, 0x61, + 0x1f, 0x81, 0x60, 0x1f, 0xc2, 0x61, 0x1f, 0xc2, + 0xa9, 0x03, 0x93, 0xa9, 0x03, 0x94, 0x68, 0x1f, + 0x80, 0x69, 0x1f, 0x80, 0x68, 0x1f, 0x81, 0x69, + 0x1f, 0x81, 0x68, 0x1f, 0xc2, 0x69, 0x1f, 0xc2, + 0xb1, 0x03, 0x80, 0xb5, 0x03, 0x80, 0xb7, 0x03, + 0x80, 0xb9, 0x03, 0x80, 0xbf, 0x03, 0x80, 0xc5, + 0x03, 0x80, 0xc9, 0x03, 0x80, 0x00, 0x1f, 0x45, + 0x03, 0x20, 0x1f, 0x45, 0x03, 0x60, 0x1f, 0x45, + 0x03, 0xb1, 0x03, 0x86, 0xb1, 0x03, 0x84, 0x70, + 0x1f, 0xc5, 0xb1, 0x03, 0xc5, 0xac, 0x03, 0xc5, + 0x00, 0x00, 0x00, 0xb1, 0x03, 0xc2, 0xb6, 0x1f, + 0xc5, 0x91, 0x03, 0x86, 0x91, 0x03, 0x84, 0x91, + 0x03, 0x80, 0x91, 0x03, 0xc5, 0x20, 0x93, 0x20, + 0x93, 0x20, 0xc2, 0xa8, 0x00, 0xc2, 0x74, 0x1f, + 0xc5, 0xb7, 0x03, 0xc5, 0xae, 0x03, 0xc5, 0x00, + 0x00, 0x00, 0xb7, 0x03, 0xc2, 0xc6, 0x1f, 0xc5, + 0x95, 0x03, 0x80, 0x97, 0x03, 0x80, 0x97, 0x03, + 0xc5, 0xbf, 0x1f, 0x80, 0xbf, 0x1f, 0x81, 0xbf, + 0x1f, 0xc2, 0xb9, 0x03, 0x86, 0xb9, 0x03, 0x84, + 0xca, 0x03, 0x80, 0x00, 0x03, 0xb9, 0x42, 0xca, + 0x42, 0x99, 0x06, 0x99, 0x04, 0x99, 0x00, 0xfe, + 0x1f, 0x80, 0xfe, 0x1f, 0x81, 0xfe, 0x1f, 0xc2, + 0xc5, 0x03, 0x86, 0xc5, 0x03, 0x84, 0xcb, 0x03, + 0x80, 0x00, 0x03, 0xc1, 0x13, 0xc1, 0x14, 0xc5, + 0x42, 0xcb, 0x42, 0xa5, 0x06, 0xa5, 0x04, 0xa5, + 0x00, 0xa1, 0x03, 0x94, 0xa8, 0x00, 0x80, 0x85, + 0x03, 0x60, 0x00, 0x7c, 0x1f, 0xc5, 0xc9, 0x03, + 0xc5, 0xce, 0x03, 0xc5, 0x00, 0x00, 0x00, 0xc9, + 0x03, 0xc2, 0xf6, 0x1f, 0xc5, 0x9f, 0x03, 0x80, + 0xa9, 0x03, 0x80, 0xa9, 0x03, 0xc5, 0x20, 0x94, + 0x02, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xb3, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x32, 0x20, 0x32, 0x20, 0x32, 0x20, + 0x00, 0x00, 0x00, 0x35, 0x20, 0x35, 0x20, 0x35, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x21, 0x00, 0x00, + 0x20, 0x85, 0x3f, 0x3f, 0x3f, 0x21, 0x21, 0x3f, + 0x32, 0x20, 0x00, 0x00, 0x00, 0x00, 0x30, 0x69, + 0x00, 0x00, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x2b, 0x3d, 0x28, 0x29, 0x6e, 0x30, 0x00, 0x2b, + 0x00, 0x12, 0x22, 0x3d, 0x00, 0x28, 0x00, 0x29, + 0x00, 0x00, 0x00, 0x61, 0x00, 0x65, 0x00, 0x6f, + 0x00, 0x78, 0x00, 0x59, 0x02, 0x68, 0x6b, 0x6c, + 0x6d, 0x6e, 0x70, 0x73, 0x74, 0x52, 0x73, 0x61, + 0x2f, 0x63, 0x61, 0x2f, 0x73, 0xb0, 0x00, 0x43, + 0x63, 0x2f, 0x6f, 0x63, 0x2f, 0x75, 0xb0, 0x00, + 0x46, 0x48, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, + 0xdf, 0x01, 0x01, 0x04, 0x24, 0x4e, 0x6f, 0x50, + 0x51, 0x52, 0x52, 0x52, 0x53, 0x4d, 0x54, 0x45, + 0x4c, 0x54, 0x4d, 0x4b, 0x00, 0xc5, 0x00, 0x42, + 0x43, 0x00, 0x65, 0x45, 0x46, 0x00, 0x4d, 0x6f, + 0xd0, 0x05, 0x46, 0x41, 0x58, 0xc0, 0x03, 0xb3, + 0x03, 0x93, 0x03, 0xa0, 0x03, 0x11, 0x22, 0x44, + 0x64, 0x65, 0x69, 0x6a, 0x31, 0xd0, 0x37, 0x31, + 0xd0, 0x39, 0x31, 0xd0, 0x31, 0x30, 0x31, 0xd0, + 0x33, 0x32, 0xd0, 0x33, 0x31, 0xd0, 0x35, 0x32, + 0xd0, 0x35, 0x33, 0xd0, 0x35, 0x34, 0xd0, 0x35, + 0x31, 0xd0, 0x36, 0x35, 0xd0, 0x36, 0x31, 0xd0, + 0x38, 0x33, 0xd0, 0x38, 0x35, 0xd0, 0x38, 0x37, + 0xd0, 0x38, 0x31, 0xd0, 0x49, 0x49, 0x49, 0x49, + 0x49, 0x49, 0x56, 0x56, 0x49, 0x56, 0x49, 0x49, + 0x56, 0x49, 0x49, 0x49, 0x49, 0x58, 0x58, 0x49, + 0x58, 0x49, 0x49, 0x4c, 0x43, 0x44, 0x4d, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x76, 0x76, + 0x69, 0x76, 0x69, 0x69, 0x76, 0x69, 0x69, 0x69, + 0x69, 0x78, 0x78, 0x69, 0x78, 0x69, 0x69, 0x6c, + 0x63, 0x64, 0x6d, 0x30, 0xd0, 0x33, 0x90, 0x21, + 0xb8, 0x92, 0x21, 0xb8, 0x94, 0x21, 0xb8, 0xd0, + 0x21, 0xb8, 0xd4, 0x21, 0xb8, 0xd2, 0x21, 0xb8, + 0x03, 0x22, 0xb8, 0x08, 0x22, 0xb8, 0x0b, 0x22, + 0xb8, 0x23, 0x22, 0xb8, 0x00, 0x00, 0x00, 0x25, + 0x22, 0xb8, 0x2b, 0x22, 0x2b, 0x22, 0x2b, 0x22, + 0x00, 0x00, 0x00, 0x2e, 0x22, 0x2e, 0x22, 0x2e, + 0x22, 0x00, 0x00, 0x00, 0x3c, 0x22, 0xb8, 0x43, + 0x22, 0xb8, 0x45, 0x22, 0xb8, 0x00, 0x00, 0x00, + 0x48, 0x22, 0xb8, 0x3d, 0x00, 0xb8, 0x00, 0x00, + 0x00, 0x61, 0x22, 0xb8, 0x4d, 0x22, 0xb8, 0x3c, + 0x00, 0xb8, 0x3e, 0x00, 0xb8, 0x64, 0x22, 0xb8, + 0x65, 0x22, 0xb8, 0x72, 0x22, 0xb8, 0x76, 0x22, + 0xb8, 0x7a, 0x22, 0xb8, 0x82, 0x22, 0xb8, 0x86, + 0x22, 0xb8, 0xa2, 0x22, 0xb8, 0xa8, 0x22, 0xb8, + 0xa9, 0x22, 0xb8, 0xab, 0x22, 0xb8, 0x7c, 0x22, + 0xb8, 0x91, 0x22, 0xb8, 0xb2, 0x22, 0x38, 0x03, + 0x08, 0x30, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00, + 0x32, 0x30, 0x28, 0x00, 0x31, 0x00, 0x29, 0x00, + 0x28, 0x00, 0x31, 0x00, 0x30, 0x00, 0x29, 0x00, + 0x28, 0x32, 0x30, 0x29, 0x31, 0x00, 0x2e, 0x00, + 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x30, + 0x2e, 0x28, 0x00, 0x61, 0x00, 0x29, 0x00, 0x41, + 0x00, 0x61, 0x00, 0x2b, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x3a, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0xdd, 0x2a, 0xb8, 0x6a, 0x56, 0x00, 0x4e, + 0x00, 0x28, 0x36, 0x3f, 0x59, 0x85, 0x8c, 0xa0, + 0xba, 0x3f, 0x51, 0x00, 0x26, 0x2c, 0x43, 0x57, + 0x6c, 0xa1, 0xb6, 0xc1, 0x9b, 0x52, 0x00, 0x5e, + 0x7a, 0x7f, 0x9d, 0xa6, 0xc1, 0xce, 0xe7, 0xb6, + 0x53, 0xc8, 0x53, 0xe3, 0x53, 0xd7, 0x56, 0x1f, + 0x57, 0xeb, 0x58, 0x02, 0x59, 0x0a, 0x59, 0x15, + 0x59, 0x27, 0x59, 0x73, 0x59, 0x50, 0x5b, 0x80, + 0x5b, 0xf8, 0x5b, 0x0f, 0x5c, 0x22, 0x5c, 0x38, + 0x5c, 0x6e, 0x5c, 0x71, 0x5c, 0xdb, 0x5d, 0xe5, + 0x5d, 0xf1, 0x5d, 0xfe, 0x5d, 0x72, 0x5e, 0x7a, + 0x5e, 0x7f, 0x5e, 0xf4, 0x5e, 0xfe, 0x5e, 0x0b, + 0x5f, 0x13, 0x5f, 0x50, 0x5f, 0x61, 0x5f, 0x73, + 0x5f, 0xc3, 0x5f, 0x08, 0x62, 0x36, 0x62, 0x4b, + 0x62, 0x2f, 0x65, 0x34, 0x65, 0x87, 0x65, 0x97, + 0x65, 0xa4, 0x65, 0xb9, 0x65, 0xe0, 0x65, 0xe5, + 0x65, 0xf0, 0x66, 0x08, 0x67, 0x28, 0x67, 0x20, + 0x6b, 0x62, 0x6b, 0x79, 0x6b, 0xb3, 0x6b, 0xcb, + 0x6b, 0xd4, 0x6b, 0xdb, 0x6b, 0x0f, 0x6c, 0x14, + 0x6c, 0x34, 0x6c, 0x6b, 0x70, 0x2a, 0x72, 0x36, + 0x72, 0x3b, 0x72, 0x3f, 0x72, 0x47, 0x72, 0x59, + 0x72, 0x5b, 0x72, 0xac, 0x72, 0x84, 0x73, 0x89, + 0x73, 0xdc, 0x74, 0xe6, 0x74, 0x18, 0x75, 0x1f, + 0x75, 0x28, 0x75, 0x30, 0x75, 0x8b, 0x75, 0x92, + 0x75, 0x76, 0x76, 0x7d, 0x76, 0xae, 0x76, 0xbf, + 0x76, 0xee, 0x76, 0xdb, 0x77, 0xe2, 0x77, 0xf3, + 0x77, 0x3a, 0x79, 0xb8, 0x79, 0xbe, 0x79, 0x74, + 0x7a, 0xcb, 0x7a, 0xf9, 0x7a, 0x73, 0x7c, 0xf8, + 0x7c, 0x36, 0x7f, 0x51, 0x7f, 0x8a, 0x7f, 0xbd, + 0x7f, 0x01, 0x80, 0x0c, 0x80, 0x12, 0x80, 0x33, + 0x80, 0x7f, 0x80, 0x89, 0x80, 0xe3, 0x81, 0x00, + 0x07, 0x10, 0x19, 0x29, 0x38, 0x3c, 0x8b, 0x8f, + 0x95, 0x4d, 0x86, 0x6b, 0x86, 0x40, 0x88, 0x4c, + 0x88, 0x63, 0x88, 0x7e, 0x89, 0x8b, 0x89, 0xd2, + 0x89, 0x00, 0x8a, 0x37, 0x8c, 0x46, 0x8c, 0x55, + 0x8c, 0x78, 0x8c, 0x9d, 0x8c, 0x64, 0x8d, 0x70, + 0x8d, 0xb3, 0x8d, 0xab, 0x8e, 0xca, 0x8e, 0x9b, + 0x8f, 0xb0, 0x8f, 0xb5, 0x8f, 0x91, 0x90, 0x49, + 0x91, 0xc6, 0x91, 0xcc, 0x91, 0xd1, 0x91, 0x77, + 0x95, 0x80, 0x95, 0x1c, 0x96, 0xb6, 0x96, 0xb9, + 0x96, 0xe8, 0x96, 0x51, 0x97, 0x5e, 0x97, 0x62, + 0x97, 0x69, 0x97, 0xcb, 0x97, 0xed, 0x97, 0xf3, + 0x97, 0x01, 0x98, 0xa8, 0x98, 0xdb, 0x98, 0xdf, + 0x98, 0x96, 0x99, 0x99, 0x99, 0xac, 0x99, 0xa8, + 0x9a, 0xd8, 0x9a, 0xdf, 0x9a, 0x25, 0x9b, 0x2f, + 0x9b, 0x32, 0x9b, 0x3c, 0x9b, 0x5a, 0x9b, 0xe5, + 0x9c, 0x75, 0x9e, 0x7f, 0x9e, 0xa5, 0x9e, 0x00, + 0x16, 0x1e, 0x28, 0x2c, 0x54, 0x58, 0x69, 0x6e, + 0x7b, 0x96, 0xa5, 0xad, 0xe8, 0xf7, 0xfb, 0x12, + 0x30, 0x00, 0x00, 0x41, 0x53, 0x44, 0x53, 0x45, + 0x53, 0x4b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x4d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x51, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x53, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x55, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x57, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x59, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x61, 0x30, 0x99, 0x30, 0x64, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x66, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x68, 0x30, 0x99, + 0x30, 0x6f, 0x30, 0x99, 0x30, 0x72, 0x30, 0x99, + 0x30, 0x75, 0x30, 0x99, 0x30, 0x78, 0x30, 0x99, + 0x30, 0x7b, 0x30, 0x99, 0x30, 0x46, 0x30, 0x99, + 0x30, 0x20, 0x00, 0x99, 0x30, 0x9d, 0x30, 0x99, + 0x30, 0x88, 0x30, 0x8a, 0x30, 0xab, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xad, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x30, 0x99, + 0x30, 0xc4, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xc8, 0x30, 0x99, 0x30, 0xcf, 0x30, 0x99, + 0x30, 0xd2, 0x30, 0x99, 0x30, 0xd5, 0x30, 0x99, + 0x30, 0xd8, 0x30, 0x99, 0x30, 0xdb, 0x30, 0x99, + 0x30, 0xa6, 0x30, 0x99, 0x30, 0xef, 0x30, 0x99, + 0x30, 0xfd, 0x30, 0x99, 0x30, 0xb3, 0x30, 0xc8, + 0x30, 0x00, 0x11, 0x00, 0x01, 0xaa, 0x02, 0xac, + 0xad, 0x03, 0x04, 0x05, 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0x1a, 0x06, 0x07, 0x08, 0x21, 0x09, + 0x11, 0x61, 0x11, 0x14, 0x11, 0x4c, 0x00, 0x01, + 0xb3, 0xb4, 0xb8, 0xba, 0xbf, 0xc3, 0xc5, 0x08, + 0xc9, 0xcb, 0x09, 0x0a, 0x0c, 0x0e, 0x0f, 0x13, + 0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x22, + 0x2c, 0x33, 0x38, 0xdd, 0xde, 0x43, 0x44, 0x45, + 0x70, 0x71, 0x74, 0x7d, 0x7e, 0x80, 0x8a, 0x8d, + 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, + 0x0a, 0x4e, 0x2d, 0x4e, 0x0b, 0x4e, 0x32, 0x75, + 0x59, 0x4e, 0x19, 0x4e, 0x01, 0x4e, 0x29, 0x59, + 0x30, 0x57, 0xba, 0x4e, 0x28, 0x00, 0x29, 0x00, + 0x00, 0x11, 0x02, 0x11, 0x03, 0x11, 0x05, 0x11, + 0x06, 0x11, 0x07, 0x11, 0x09, 0x11, 0x0b, 0x11, + 0x0c, 0x11, 0x0e, 0x11, 0x0f, 0x11, 0x10, 0x11, + 0x11, 0x11, 0x12, 0x11, 0x28, 0x00, 0x00, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x02, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x05, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x09, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0e, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0c, 0x11, + 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, + 0x69, 0x11, 0x0c, 0x11, 0x65, 0x11, 0xab, 0x11, + 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, 0x69, 0x11, + 0x12, 0x11, 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, + 0x29, 0x00, 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, + 0xdb, 0x56, 0x94, 0x4e, 0x6d, 0x51, 0x03, 0x4e, + 0x6b, 0x51, 0x5d, 0x4e, 0x41, 0x53, 0x08, 0x67, + 0x6b, 0x70, 0x34, 0x6c, 0x28, 0x67, 0xd1, 0x91, + 0x1f, 0x57, 0xe5, 0x65, 0x2a, 0x68, 0x09, 0x67, + 0x3e, 0x79, 0x0d, 0x54, 0x79, 0x72, 0xa1, 0x8c, + 0x5d, 0x79, 0xb4, 0x52, 0xe3, 0x4e, 0x7c, 0x54, + 0x66, 0x5b, 0xe3, 0x76, 0x01, 0x4f, 0xc7, 0x8c, + 0x54, 0x53, 0x6d, 0x79, 0x11, 0x4f, 0xea, 0x81, + 0xf3, 0x81, 0x4f, 0x55, 0x7c, 0x5e, 0x87, 0x65, + 0x8f, 0x7b, 0x50, 0x54, 0x45, 0x32, 0x00, 0x31, + 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x11, 0x00, + 0x02, 0x03, 0x05, 0x06, 0x07, 0x09, 0x0b, 0x0c, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x00, 0x11, 0x00, + 0x61, 0x02, 0x61, 0x03, 0x61, 0x05, 0x61, 0x06, + 0x61, 0x07, 0x61, 0x09, 0x61, 0x0b, 0x61, 0x0c, + 0x61, 0x0e, 0x11, 0x61, 0x11, 0x00, 0x11, 0x0e, + 0x61, 0xb7, 0x00, 0x69, 0x0b, 0x11, 0x01, 0x63, + 0x00, 0x69, 0x0b, 0x11, 0x6e, 0x11, 0x00, 0x4e, + 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, 0x94, 0x4e, + 0x6d, 0x51, 0x03, 0x4e, 0x6b, 0x51, 0x5d, 0x4e, + 0x41, 0x53, 0x08, 0x67, 0x6b, 0x70, 0x34, 0x6c, + 0x28, 0x67, 0xd1, 0x91, 0x1f, 0x57, 0xe5, 0x65, + 0x2a, 0x68, 0x09, 0x67, 0x3e, 0x79, 0x0d, 0x54, + 0x79, 0x72, 0xa1, 0x8c, 0x5d, 0x79, 0xb4, 0x52, + 0xd8, 0x79, 0x37, 0x75, 0x73, 0x59, 0x69, 0x90, + 0x2a, 0x51, 0x70, 0x53, 0xe8, 0x6c, 0x05, 0x98, + 0x11, 0x4f, 0x99, 0x51, 0x63, 0x6b, 0x0a, 0x4e, + 0x2d, 0x4e, 0x0b, 0x4e, 0xe6, 0x5d, 0xf3, 0x53, + 0x3b, 0x53, 0x97, 0x5b, 0x66, 0x5b, 0xe3, 0x76, + 0x01, 0x4f, 0xc7, 0x8c, 0x54, 0x53, 0x1c, 0x59, + 0x33, 0x00, 0x36, 0x00, 0x34, 0x00, 0x30, 0x00, + 0x35, 0x30, 0x31, 0x00, 0x08, 0x67, 0x31, 0x00, + 0x30, 0x00, 0x08, 0x67, 0x48, 0x67, 0x65, 0x72, + 0x67, 0x65, 0x56, 0x4c, 0x54, 0x44, 0xa2, 0x30, + 0x00, 0x02, 0x04, 0x06, 0x08, 0x09, 0x0b, 0x0d, + 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, + 0x1f, 0x22, 0x24, 0x26, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x30, 0x33, 0x36, 0x39, 0x3c, 0x3d, + 0x3e, 0x3f, 0x40, 0x42, 0x44, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0xe4, + 0x4e, 0x8c, 0x54, 0xa1, 0x30, 0x01, 0x30, 0x5b, + 0x27, 0x01, 0x4a, 0x34, 0x00, 0x01, 0x52, 0x39, + 0x01, 0xa2, 0x30, 0x00, 0x5a, 0x49, 0xa4, 0x30, + 0x00, 0x27, 0x4f, 0x0c, 0xa4, 0x30, 0x00, 0x4f, + 0x1d, 0x02, 0x05, 0x4f, 0xa8, 0x30, 0x00, 0x11, + 0x07, 0x54, 0x21, 0xa8, 0x30, 0x00, 0x54, 0x03, + 0x54, 0xa4, 0x30, 0x06, 0x4f, 0x15, 0x06, 0x58, + 0x3c, 0x07, 0x00, 0x46, 0xab, 0x30, 0x00, 0x3e, + 0x18, 0x1d, 0x00, 0x42, 0x3f, 0x51, 0xac, 0x30, + 0x00, 0x41, 0x47, 0x00, 0x47, 0x32, 0xae, 0x30, + 0xac, 0x30, 0xae, 0x30, 0x00, 0x1d, 0x4e, 0xad, + 0x30, 0x00, 0x38, 0x3d, 0x4f, 0x01, 0x3e, 0x13, + 0x4f, 0xad, 0x30, 0xed, 0x30, 0xad, 0x30, 0x00, + 0x40, 0x03, 0x3c, 0x33, 0xad, 0x30, 0x00, 0x40, + 0x34, 0x4f, 0x1b, 0x3e, 0xad, 0x30, 0x00, 0x40, + 0x42, 0x16, 0x1b, 0xb0, 0x30, 0x00, 0x39, 0x30, + 0xa4, 0x30, 0x0c, 0x45, 0x3c, 0x24, 0x4f, 0x0b, + 0x47, 0x18, 0x00, 0x49, 0xaf, 0x30, 0x00, 0x3e, + 0x4d, 0x1e, 0xb1, 0x30, 0x00, 0x4b, 0x08, 0x02, + 0x3a, 0x19, 0x02, 0x4b, 0x2c, 0xa4, 0x30, 0x11, + 0x00, 0x0b, 0x47, 0xb5, 0x30, 0x00, 0x3e, 0x0c, + 0x47, 0x2b, 0xb0, 0x30, 0x07, 0x3a, 0x43, 0x00, + 0xb9, 0x30, 0x02, 0x3a, 0x08, 0x02, 0x3a, 0x0f, + 0x07, 0x43, 0x00, 0xb7, 0x30, 0x10, 0x00, 0x12, + 0x34, 0x11, 0x3c, 0x13, 0x17, 0xa4, 0x30, 0x2a, + 0x1f, 0x24, 0x2b, 0x00, 0x20, 0xbb, 0x30, 0x16, + 0x41, 0x00, 0x38, 0x0d, 0xc4, 0x30, 0x0d, 0x38, + 0x00, 0xd0, 0x30, 0x00, 0x2c, 0x1c, 0x1b, 0xa2, + 0x30, 0x32, 0x00, 0x17, 0x26, 0x49, 0xaf, 0x30, + 0x25, 0x00, 0x3c, 0xb3, 0x30, 0x21, 0x00, 0x20, + 0x38, 0xa1, 0x30, 0x34, 0x00, 0x48, 0x22, 0x28, + 0xa3, 0x30, 0x32, 0x00, 0x59, 0x25, 0xa7, 0x30, + 0x2f, 0x1c, 0x10, 0x00, 0x44, 0xd5, 0x30, 0x00, + 0x14, 0x1e, 0xaf, 0x30, 0x29, 0x00, 0x10, 0x4d, + 0x3c, 0xda, 0x30, 0xbd, 0x30, 0xb8, 0x30, 0x22, + 0x13, 0x1a, 0x20, 0x33, 0x0c, 0x22, 0x3b, 0x01, + 0x22, 0x44, 0x00, 0x21, 0x44, 0x07, 0xa4, 0x30, + 0x39, 0x00, 0x4f, 0x24, 0xc8, 0x30, 0x14, 0x23, + 0x00, 0xdb, 0x30, 0xf3, 0x30, 0xc9, 0x30, 0x14, + 0x2a, 0x00, 0x12, 0x33, 0x22, 0x12, 0x33, 0x2a, + 0xa4, 0x30, 0x3a, 0x00, 0x0b, 0x49, 0xa4, 0x30, + 0x3a, 0x00, 0x47, 0x3a, 0x1f, 0x2b, 0x3a, 0x47, + 0x0b, 0xb7, 0x30, 0x27, 0x3c, 0x00, 0x30, 0x3c, + 0xaf, 0x30, 0x30, 0x00, 0x3e, 0x44, 0xdf, 0x30, + 0xea, 0x30, 0xd0, 0x30, 0x0f, 0x1a, 0x00, 0x2c, + 0x1b, 0xe1, 0x30, 0xac, 0x30, 0xac, 0x30, 0x35, + 0x00, 0x1c, 0x47, 0x35, 0x50, 0x1c, 0x3f, 0xa2, + 0x30, 0x42, 0x5a, 0x27, 0x42, 0x5a, 0x49, 0x44, + 0x00, 0x51, 0xc3, 0x30, 0x27, 0x00, 0x05, 0x28, + 0xea, 0x30, 0xe9, 0x30, 0xd4, 0x30, 0x17, 0x00, + 0x28, 0xd6, 0x30, 0x15, 0x26, 0x00, 0x15, 0xec, + 0x30, 0xe0, 0x30, 0xb2, 0x30, 0x3a, 0x41, 0x16, + 0x00, 0x41, 0xc3, 0x30, 0x2c, 0x00, 0x05, 0x30, + 0x00, 0xb9, 0x70, 0x31, 0x00, 0x30, 0x00, 0xb9, + 0x70, 0x32, 0x00, 0x30, 0x00, 0xb9, 0x70, 0x68, + 0x50, 0x61, 0x64, 0x61, 0x41, 0x55, 0x62, 0x61, + 0x72, 0x6f, 0x56, 0x70, 0x63, 0x64, 0x6d, 0x64, + 0x00, 0x6d, 0x00, 0xb2, 0x00, 0x49, 0x00, 0x55, + 0x00, 0x73, 0x5e, 0x10, 0x62, 0x2d, 0x66, 0x8c, + 0x54, 0x27, 0x59, 0x63, 0x6b, 0x0e, 0x66, 0xbb, + 0x6c, 0x2a, 0x68, 0x0f, 0x5f, 0x1a, 0x4f, 0x3e, + 0x79, 0x70, 0x00, 0x41, 0x6e, 0x00, 0x41, 0xbc, + 0x03, 0x41, 0x6d, 0x00, 0x41, 0x6b, 0x00, 0x41, + 0x4b, 0x00, 0x42, 0x4d, 0x00, 0x42, 0x47, 0x00, + 0x42, 0x63, 0x61, 0x6c, 0x6b, 0x63, 0x61, 0x6c, + 0x70, 0x00, 0x46, 0x6e, 0x00, 0x46, 0xbc, 0x03, + 0x46, 0xbc, 0x03, 0x67, 0x6d, 0x00, 0x67, 0x6b, + 0x00, 0x67, 0x48, 0x00, 0x7a, 0x6b, 0x48, 0x7a, + 0x4d, 0x48, 0x7a, 0x47, 0x48, 0x7a, 0x54, 0x48, + 0x7a, 0xbc, 0x03, 0x13, 0x21, 0x6d, 0x00, 0x13, + 0x21, 0x64, 0x00, 0x13, 0x21, 0x6b, 0x00, 0x13, + 0x21, 0x66, 0x00, 0x6d, 0x6e, 0x00, 0x6d, 0xbc, + 0x03, 0x6d, 0x6d, 0x00, 0x6d, 0x63, 0x00, 0x6d, + 0x6b, 0x00, 0x6d, 0x63, 0x00, 0x0a, 0x0a, 0x4f, + 0x00, 0x0a, 0x4f, 0x6d, 0x00, 0xb2, 0x00, 0x63, + 0x00, 0x08, 0x0a, 0x4f, 0x0a, 0x0a, 0x50, 0x00, + 0x0a, 0x50, 0x6d, 0x00, 0xb3, 0x00, 0x6b, 0x00, + 0x6d, 0x00, 0xb3, 0x00, 0x6d, 0x00, 0x15, 0x22, + 0x73, 0x00, 0x6d, 0x00, 0x15, 0x22, 0x73, 0x00, + 0xb2, 0x00, 0x50, 0x61, 0x6b, 0x50, 0x61, 0x4d, + 0x50, 0x61, 0x47, 0x50, 0x61, 0x72, 0x61, 0x64, + 0x72, 0x61, 0x64, 0xd1, 0x73, 0x72, 0x00, 0x61, + 0x00, 0x64, 0x00, 0x15, 0x22, 0x73, 0x00, 0xb2, + 0x00, 0x70, 0x00, 0x73, 0x6e, 0x00, 0x73, 0xbc, + 0x03, 0x73, 0x6d, 0x00, 0x73, 0x70, 0x00, 0x56, + 0x6e, 0x00, 0x56, 0xbc, 0x03, 0x56, 0x6d, 0x00, + 0x56, 0x6b, 0x00, 0x56, 0x4d, 0x00, 0x56, 0x70, + 0x00, 0x57, 0x6e, 0x00, 0x57, 0xbc, 0x03, 0x57, + 0x6d, 0x00, 0x57, 0x6b, 0x00, 0x57, 0x4d, 0x00, + 0x57, 0x6b, 0x00, 0xa9, 0x03, 0x4d, 0x00, 0xa9, + 0x03, 0x61, 0x2e, 0x6d, 0x2e, 0x42, 0x71, 0x63, + 0x63, 0x63, 0x64, 0x43, 0xd1, 0x6b, 0x67, 0x43, + 0x6f, 0x2e, 0x64, 0x42, 0x47, 0x79, 0x68, 0x61, + 0x48, 0x50, 0x69, 0x6e, 0x4b, 0x4b, 0x4b, 0x4d, + 0x6b, 0x74, 0x6c, 0x6d, 0x6c, 0x6e, 0x6c, 0x6f, + 0x67, 0x6c, 0x78, 0x6d, 0x62, 0x6d, 0x69, 0x6c, + 0x6d, 0x6f, 0x6c, 0x50, 0x48, 0x70, 0x2e, 0x6d, + 0x2e, 0x50, 0x50, 0x4d, 0x50, 0x52, 0x73, 0x72, + 0x53, 0x76, 0x57, 0x62, 0x56, 0xd1, 0x6d, 0x41, + 0xd1, 0x6d, 0x31, 0x00, 0xe5, 0x65, 0x31, 0x00, + 0x30, 0x00, 0xe5, 0x65, 0x32, 0x00, 0x30, 0x00, + 0xe5, 0x65, 0x33, 0x00, 0x30, 0x00, 0xe5, 0x65, + 0x67, 0x61, 0x6c, 0x4a, 0x04, 0x4c, 0x04, 0x43, + 0x46, 0x51, 0x26, 0x01, 0x53, 0x01, 0x27, 0xa7, + 0x37, 0xab, 0x6b, 0x02, 0x52, 0xab, 0x48, 0x8c, + 0xf4, 0x66, 0xca, 0x8e, 0xc8, 0x8c, 0xd1, 0x6e, + 0x32, 0x4e, 0xe5, 0x53, 0x9c, 0x9f, 0x9c, 0x9f, + 0x51, 0x59, 0xd1, 0x91, 0x87, 0x55, 0x48, 0x59, + 0xf6, 0x61, 0x69, 0x76, 0x85, 0x7f, 0x3f, 0x86, + 0xba, 0x87, 0xf8, 0x88, 0x8f, 0x90, 0x02, 0x6a, + 0x1b, 0x6d, 0xd9, 0x70, 0xde, 0x73, 0x3d, 0x84, + 0x6a, 0x91, 0xf1, 0x99, 0x82, 0x4e, 0x75, 0x53, + 0x04, 0x6b, 0x1b, 0x72, 0x2d, 0x86, 0x1e, 0x9e, + 0x50, 0x5d, 0xeb, 0x6f, 0xcd, 0x85, 0x64, 0x89, + 0xc9, 0x62, 0xd8, 0x81, 0x1f, 0x88, 0xca, 0x5e, + 0x17, 0x67, 0x6a, 0x6d, 0xfc, 0x72, 0xce, 0x90, + 0x86, 0x4f, 0xb7, 0x51, 0xde, 0x52, 0xc4, 0x64, + 0xd3, 0x6a, 0x10, 0x72, 0xe7, 0x76, 0x01, 0x80, + 0x06, 0x86, 0x5c, 0x86, 0xef, 0x8d, 0x32, 0x97, + 0x6f, 0x9b, 0xfa, 0x9d, 0x8c, 0x78, 0x7f, 0x79, + 0xa0, 0x7d, 0xc9, 0x83, 0x04, 0x93, 0x7f, 0x9e, + 0xd6, 0x8a, 0xdf, 0x58, 0x04, 0x5f, 0x60, 0x7c, + 0x7e, 0x80, 0x62, 0x72, 0xca, 0x78, 0xc2, 0x8c, + 0xf7, 0x96, 0xd8, 0x58, 0x62, 0x5c, 0x13, 0x6a, + 0xda, 0x6d, 0x0f, 0x6f, 0x2f, 0x7d, 0x37, 0x7e, + 0x4b, 0x96, 0xd2, 0x52, 0x8b, 0x80, 0xdc, 0x51, + 0xcc, 0x51, 0x1c, 0x7a, 0xbe, 0x7d, 0xf1, 0x83, + 0x75, 0x96, 0x80, 0x8b, 0xcf, 0x62, 0x02, 0x6a, + 0xfe, 0x8a, 0x39, 0x4e, 0xe7, 0x5b, 0x12, 0x60, + 0x87, 0x73, 0x70, 0x75, 0x17, 0x53, 0xfb, 0x78, + 0xbf, 0x4f, 0xa9, 0x5f, 0x0d, 0x4e, 0xcc, 0x6c, + 0x78, 0x65, 0x22, 0x7d, 0xc3, 0x53, 0x5e, 0x58, + 0x01, 0x77, 0x49, 0x84, 0xaa, 0x8a, 0xba, 0x6b, + 0xb0, 0x8f, 0x88, 0x6c, 0xfe, 0x62, 0xe5, 0x82, + 0xa0, 0x63, 0x65, 0x75, 0xae, 0x4e, 0x69, 0x51, + 0xc9, 0x51, 0x81, 0x68, 0xe7, 0x7c, 0x6f, 0x82, + 0xd2, 0x8a, 0xcf, 0x91, 0xf5, 0x52, 0x42, 0x54, + 0x73, 0x59, 0xec, 0x5e, 0xc5, 0x65, 0xfe, 0x6f, + 0x2a, 0x79, 0xad, 0x95, 0x6a, 0x9a, 0x97, 0x9e, + 0xce, 0x9e, 0x9b, 0x52, 0xc6, 0x66, 0x77, 0x6b, + 0x62, 0x8f, 0x74, 0x5e, 0x90, 0x61, 0x00, 0x62, + 0x9a, 0x64, 0x23, 0x6f, 0x49, 0x71, 0x89, 0x74, + 0xca, 0x79, 0xf4, 0x7d, 0x6f, 0x80, 0x26, 0x8f, + 0xee, 0x84, 0x23, 0x90, 0x4a, 0x93, 0x17, 0x52, + 0xa3, 0x52, 0xbd, 0x54, 0xc8, 0x70, 0xc2, 0x88, + 0xaa, 0x8a, 0xc9, 0x5e, 0xf5, 0x5f, 0x7b, 0x63, + 0xae, 0x6b, 0x3e, 0x7c, 0x75, 0x73, 0xe4, 0x4e, + 0xf9, 0x56, 0xe7, 0x5b, 0xba, 0x5d, 0x1c, 0x60, + 0xb2, 0x73, 0x69, 0x74, 0x9a, 0x7f, 0x46, 0x80, + 0x34, 0x92, 0xf6, 0x96, 0x48, 0x97, 0x18, 0x98, + 0x8b, 0x4f, 0xae, 0x79, 0xb4, 0x91, 0xb8, 0x96, + 0xe1, 0x60, 0x86, 0x4e, 0xda, 0x50, 0xee, 0x5b, + 0x3f, 0x5c, 0x99, 0x65, 0x02, 0x6a, 0xce, 0x71, + 0x42, 0x76, 0xfc, 0x84, 0x7c, 0x90, 0x8d, 0x9f, + 0x88, 0x66, 0x2e, 0x96, 0x89, 0x52, 0x7b, 0x67, + 0xf3, 0x67, 0x41, 0x6d, 0x9c, 0x6e, 0x09, 0x74, + 0x59, 0x75, 0x6b, 0x78, 0x10, 0x7d, 0x5e, 0x98, + 0x6d, 0x51, 0x2e, 0x62, 0x78, 0x96, 0x2b, 0x50, + 0x19, 0x5d, 0xea, 0x6d, 0x2a, 0x8f, 0x8b, 0x5f, + 0x44, 0x61, 0x17, 0x68, 0x87, 0x73, 0x86, 0x96, + 0x29, 0x52, 0x0f, 0x54, 0x65, 0x5c, 0x13, 0x66, + 0x4e, 0x67, 0xa8, 0x68, 0xe5, 0x6c, 0x06, 0x74, + 0xe2, 0x75, 0x79, 0x7f, 0xcf, 0x88, 0xe1, 0x88, + 0xcc, 0x91, 0xe2, 0x96, 0x3f, 0x53, 0xba, 0x6e, + 0x1d, 0x54, 0xd0, 0x71, 0x98, 0x74, 0xfa, 0x85, + 0xa3, 0x96, 0x57, 0x9c, 0x9f, 0x9e, 0x97, 0x67, + 0xcb, 0x6d, 0xe8, 0x81, 0xcb, 0x7a, 0x20, 0x7b, + 0x92, 0x7c, 0xc0, 0x72, 0x99, 0x70, 0x58, 0x8b, + 0xc0, 0x4e, 0x36, 0x83, 0x3a, 0x52, 0x07, 0x52, + 0xa6, 0x5e, 0xd3, 0x62, 0xd6, 0x7c, 0x85, 0x5b, + 0x1e, 0x6d, 0xb4, 0x66, 0x3b, 0x8f, 0x4c, 0x88, + 0x4d, 0x96, 0x8b, 0x89, 0xd3, 0x5e, 0x40, 0x51, + 0xc0, 0x55, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x58, + 0x00, 0x00, 0x74, 0x66, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x51, 0x2a, 0x73, 0xca, 0x76, 0x3c, 0x79, + 0x5e, 0x79, 0x65, 0x79, 0x8f, 0x79, 0x56, 0x97, + 0xbe, 0x7c, 0xbd, 0x7f, 0x00, 0x00, 0x12, 0x86, + 0x00, 0x00, 0xf8, 0x8a, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x90, 0xfd, 0x90, 0xef, 0x98, 0xfc, 0x98, + 0x28, 0x99, 0xb4, 0x9d, 0xde, 0x90, 0xb7, 0x96, + 0xae, 0x4f, 0xe7, 0x50, 0x4d, 0x51, 0xc9, 0x52, + 0xe4, 0x52, 0x51, 0x53, 0x9d, 0x55, 0x06, 0x56, + 0x68, 0x56, 0x40, 0x58, 0xa8, 0x58, 0x64, 0x5c, + 0x6e, 0x5c, 0x94, 0x60, 0x68, 0x61, 0x8e, 0x61, + 0xf2, 0x61, 0x4f, 0x65, 0xe2, 0x65, 0x91, 0x66, + 0x85, 0x68, 0x77, 0x6d, 0x1a, 0x6e, 0x22, 0x6f, + 0x6e, 0x71, 0x2b, 0x72, 0x22, 0x74, 0x91, 0x78, + 0x3e, 0x79, 0x49, 0x79, 0x48, 0x79, 0x50, 0x79, + 0x56, 0x79, 0x5d, 0x79, 0x8d, 0x79, 0x8e, 0x79, + 0x40, 0x7a, 0x81, 0x7a, 0xc0, 0x7b, 0xf4, 0x7d, + 0x09, 0x7e, 0x41, 0x7e, 0x72, 0x7f, 0x05, 0x80, + 0xed, 0x81, 0x79, 0x82, 0x79, 0x82, 0x57, 0x84, + 0x10, 0x89, 0x96, 0x89, 0x01, 0x8b, 0x39, 0x8b, + 0xd3, 0x8c, 0x08, 0x8d, 0xb6, 0x8f, 0x38, 0x90, + 0xe3, 0x96, 0xff, 0x97, 0x3b, 0x98, 0x75, 0x60, + 0xee, 0x42, 0x18, 0x82, 0x02, 0x26, 0x4e, 0xb5, + 0x51, 0x68, 0x51, 0x80, 0x4f, 0x45, 0x51, 0x80, + 0x51, 0xc7, 0x52, 0xfa, 0x52, 0x9d, 0x55, 0x55, + 0x55, 0x99, 0x55, 0xe2, 0x55, 0x5a, 0x58, 0xb3, + 0x58, 0x44, 0x59, 0x54, 0x59, 0x62, 0x5a, 0x28, + 0x5b, 0xd2, 0x5e, 0xd9, 0x5e, 0x69, 0x5f, 0xad, + 0x5f, 0xd8, 0x60, 0x4e, 0x61, 0x08, 0x61, 0x8e, + 0x61, 0x60, 0x61, 0xf2, 0x61, 0x34, 0x62, 0xc4, + 0x63, 0x1c, 0x64, 0x52, 0x64, 0x56, 0x65, 0x74, + 0x66, 0x17, 0x67, 0x1b, 0x67, 0x56, 0x67, 0x79, + 0x6b, 0xba, 0x6b, 0x41, 0x6d, 0xdb, 0x6e, 0xcb, + 0x6e, 0x22, 0x6f, 0x1e, 0x70, 0x6e, 0x71, 0xa7, + 0x77, 0x35, 0x72, 0xaf, 0x72, 0x2a, 0x73, 0x71, + 0x74, 0x06, 0x75, 0x3b, 0x75, 0x1d, 0x76, 0x1f, + 0x76, 0xca, 0x76, 0xdb, 0x76, 0xf4, 0x76, 0x4a, + 0x77, 0x40, 0x77, 0xcc, 0x78, 0xb1, 0x7a, 0xc0, + 0x7b, 0x7b, 0x7c, 0x5b, 0x7d, 0xf4, 0x7d, 0x3e, + 0x7f, 0x05, 0x80, 0x52, 0x83, 0xef, 0x83, 0x79, + 0x87, 0x41, 0x89, 0x86, 0x89, 0x96, 0x89, 0xbf, + 0x8a, 0xf8, 0x8a, 0xcb, 0x8a, 0x01, 0x8b, 0xfe, + 0x8a, 0xed, 0x8a, 0x39, 0x8b, 0x8a, 0x8b, 0x08, + 0x8d, 0x38, 0x8f, 0x72, 0x90, 0x99, 0x91, 0x76, + 0x92, 0x7c, 0x96, 0xe3, 0x96, 0x56, 0x97, 0xdb, + 0x97, 0xff, 0x97, 0x0b, 0x98, 0x3b, 0x98, 0x12, + 0x9b, 0x9c, 0x9f, 0x4a, 0x28, 0x44, 0x28, 0xd5, + 0x33, 0x9d, 0x3b, 0x18, 0x40, 0x39, 0x40, 0x49, + 0x52, 0xd0, 0x5c, 0xd3, 0x7e, 0x43, 0x9f, 0x8e, + 0x9f, 0x2a, 0xa0, 0x02, 0x66, 0x66, 0x66, 0x69, + 0x66, 0x6c, 0x66, 0x66, 0x69, 0x66, 0x66, 0x6c, + 0x7f, 0x01, 0x74, 0x73, 0x00, 0x74, 0x65, 0x05, + 0x0f, 0x11, 0x0f, 0x00, 0x0f, 0x06, 0x19, 0x11, + 0x0f, 0x08, 0xd9, 0x05, 0xb4, 0x05, 0x00, 0x00, + 0x00, 0x00, 0xf2, 0x05, 0xb7, 0x05, 0xd0, 0x05, + 0x12, 0x00, 0x03, 0x04, 0x0b, 0x0c, 0x0d, 0x18, + 0x1a, 0xe9, 0x05, 0xc1, 0x05, 0xe9, 0x05, 0xc2, + 0x05, 0x49, 0xfb, 0xc1, 0x05, 0x49, 0xfb, 0xc2, + 0x05, 0xd0, 0x05, 0xb7, 0x05, 0xd0, 0x05, 0xb8, + 0x05, 0xd0, 0x05, 0xbc, 0x05, 0xd8, 0x05, 0xbc, + 0x05, 0xde, 0x05, 0xbc, 0x05, 0xe0, 0x05, 0xbc, + 0x05, 0xe3, 0x05, 0xbc, 0x05, 0xb9, 0x05, 0x2d, + 0x03, 0x2e, 0x03, 0x2f, 0x03, 0x30, 0x03, 0x31, + 0x03, 0x1c, 0x00, 0x18, 0x06, 0x22, 0x06, 0x2b, + 0x06, 0xd0, 0x05, 0xdc, 0x05, 0x71, 0x06, 0x00, + 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0f, 0x0f, 0x0f, 0x0f, 0x09, 0x09, 0x09, + 0x09, 0x0e, 0x0e, 0x0e, 0x0e, 0x08, 0x08, 0x08, + 0x08, 0x33, 0x33, 0x33, 0x33, 0x35, 0x35, 0x35, + 0x35, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, + 0x12, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, + 0x16, 0x1c, 0x1c, 0x1b, 0x1b, 0x1d, 0x1d, 0x17, + 0x17, 0x27, 0x27, 0x20, 0x20, 0x38, 0x38, 0x38, + 0x38, 0x3e, 0x3e, 0x3e, 0x3e, 0x42, 0x42, 0x42, + 0x42, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4f, 0x4f, 0x50, 0x50, 0x50, + 0x50, 0x4d, 0x4d, 0x4d, 0x4d, 0x61, 0x61, 0x62, + 0x62, 0x49, 0x06, 0x64, 0x64, 0x64, 0x64, 0x7e, + 0x7e, 0x7d, 0x7d, 0x7f, 0x7f, 0x2e, 0x82, 0x82, + 0x7c, 0x7c, 0x80, 0x80, 0x87, 0x87, 0x87, 0x87, + 0x00, 0x00, 0x26, 0x06, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xaf, 0x00, 0xaf, 0x00, 0x22, 0x00, 0x22, + 0x00, 0xa1, 0x00, 0xa1, 0x00, 0xa0, 0x00, 0xa0, + 0x00, 0xa2, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0xaa, + 0x00, 0xaa, 0x00, 0x23, 0x00, 0x23, 0x00, 0x23, + 0xcc, 0x06, 0x00, 0x00, 0x00, 0x00, 0x26, 0x06, + 0x00, 0x06, 0x00, 0x07, 0x00, 0x1f, 0x00, 0x23, + 0x00, 0x24, 0x02, 0x06, 0x02, 0x07, 0x02, 0x08, + 0x02, 0x1f, 0x02, 0x23, 0x02, 0x24, 0x04, 0x06, + 0x04, 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x23, + 0x04, 0x24, 0x05, 0x06, 0x05, 0x1f, 0x05, 0x23, + 0x05, 0x24, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06, + 0x07, 0x1f, 0x08, 0x06, 0x08, 0x07, 0x08, 0x1f, + 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x08, 0x0d, 0x1f, + 0x0f, 0x07, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07, + 0x10, 0x08, 0x10, 0x1f, 0x11, 0x07, 0x11, 0x1f, + 0x12, 0x1f, 0x13, 0x06, 0x13, 0x1f, 0x14, 0x06, + 0x14, 0x1f, 0x1b, 0x06, 0x1b, 0x07, 0x1b, 0x08, + 0x1b, 0x1f, 0x1b, 0x23, 0x1b, 0x24, 0x1c, 0x07, + 0x1c, 0x1f, 0x1c, 0x23, 0x1c, 0x24, 0x1d, 0x01, + 0x1d, 0x06, 0x1d, 0x07, 0x1d, 0x08, 0x1d, 0x1e, + 0x1d, 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x06, + 0x1e, 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x23, + 0x1e, 0x24, 0x1f, 0x06, 0x1f, 0x07, 0x1f, 0x08, + 0x1f, 0x1f, 0x1f, 0x23, 0x1f, 0x24, 0x20, 0x06, + 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, 0x20, 0x23, + 0x20, 0x24, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x23, + 0x21, 0x24, 0x24, 0x06, 0x24, 0x07, 0x24, 0x08, + 0x24, 0x1f, 0x24, 0x23, 0x24, 0x24, 0x0a, 0x4a, + 0x0b, 0x4a, 0x23, 0x4a, 0x20, 0x00, 0x4c, 0x06, + 0x51, 0x06, 0x51, 0x06, 0xff, 0x00, 0x1f, 0x26, + 0x06, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x1f, 0x00, + 0x20, 0x00, 0x23, 0x00, 0x24, 0x02, 0x0b, 0x02, + 0x0c, 0x02, 0x1f, 0x02, 0x20, 0x02, 0x23, 0x02, + 0x24, 0x04, 0x0b, 0x04, 0x0c, 0x04, 0x1f, 0x26, + 0x06, 0x04, 0x20, 0x04, 0x23, 0x04, 0x24, 0x05, + 0x0b, 0x05, 0x0c, 0x05, 0x1f, 0x05, 0x20, 0x05, + 0x23, 0x05, 0x24, 0x1b, 0x23, 0x1b, 0x24, 0x1c, + 0x23, 0x1c, 0x24, 0x1d, 0x01, 0x1d, 0x1e, 0x1d, + 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x1f, 0x1e, + 0x23, 0x1e, 0x24, 0x1f, 0x01, 0x1f, 0x1f, 0x20, + 0x0b, 0x20, 0x0c, 0x20, 0x1f, 0x20, 0x20, 0x20, + 0x23, 0x20, 0x24, 0x23, 0x4a, 0x24, 0x0b, 0x24, + 0x0c, 0x24, 0x1f, 0x24, 0x20, 0x24, 0x23, 0x24, + 0x24, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x1f, 0x00, 0x21, 0x02, 0x06, 0x02, 0x07, 0x02, + 0x08, 0x02, 0x1f, 0x02, 0x21, 0x04, 0x06, 0x04, + 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x21, 0x05, + 0x1f, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06, 0x07, + 0x1f, 0x08, 0x06, 0x08, 0x1f, 0x0d, 0x06, 0x0d, + 0x07, 0x0d, 0x08, 0x0d, 0x1f, 0x0f, 0x07, 0x0f, + 0x08, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07, 0x10, + 0x08, 0x10, 0x1f, 0x11, 0x07, 0x12, 0x1f, 0x13, + 0x06, 0x13, 0x1f, 0x14, 0x06, 0x14, 0x1f, 0x1b, + 0x06, 0x1b, 0x07, 0x1b, 0x08, 0x1b, 0x1f, 0x1c, + 0x07, 0x1c, 0x1f, 0x1d, 0x06, 0x1d, 0x07, 0x1d, + 0x08, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x06, 0x1e, + 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x21, 0x1f, + 0x06, 0x1f, 0x07, 0x1f, 0x08, 0x1f, 0x1f, 0x20, + 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, 0x20, + 0x21, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x4a, 0x24, + 0x06, 0x24, 0x07, 0x24, 0x08, 0x24, 0x1f, 0x24, + 0x21, 0x00, 0x1f, 0x00, 0x21, 0x02, 0x1f, 0x02, + 0x21, 0x04, 0x1f, 0x04, 0x21, 0x05, 0x1f, 0x05, + 0x21, 0x0d, 0x1f, 0x0d, 0x21, 0x0e, 0x1f, 0x0e, + 0x21, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x1f, 0x20, + 0x1f, 0x20, 0x21, 0x24, 0x1f, 0x24, 0x21, 0x40, + 0x06, 0x4e, 0x06, 0x51, 0x06, 0x27, 0x06, 0x10, + 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, 0x13, + 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, 0x0d, + 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, 0x05, + 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, 0x0e, + 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, 0x0d, + 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0d, + 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x10, + 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, 0x13, + 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, 0x0d, + 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, 0x05, + 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, 0x0e, + 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, 0x0d, + 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0d, + 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x0d, + 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0c, + 0x20, 0x0d, 0x20, 0x10, 0x1e, 0x0c, 0x05, 0x0c, + 0x06, 0x0c, 0x07, 0x0d, 0x05, 0x0d, 0x06, 0x0d, + 0x07, 0x10, 0x1e, 0x11, 0x1e, 0x00, 0x24, 0x00, + 0x24, 0x2a, 0x06, 0x00, 0x02, 0x1b, 0x00, 0x03, + 0x02, 0x00, 0x03, 0x02, 0x00, 0x03, 0x1b, 0x00, + 0x04, 0x1b, 0x00, 0x1b, 0x02, 0x00, 0x1b, 0x03, + 0x00, 0x1b, 0x04, 0x02, 0x1b, 0x03, 0x02, 0x1b, + 0x03, 0x03, 0x1b, 0x20, 0x03, 0x1b, 0x1f, 0x09, + 0x03, 0x02, 0x09, 0x02, 0x03, 0x09, 0x02, 0x1f, + 0x09, 0x1b, 0x03, 0x09, 0x1b, 0x03, 0x09, 0x1b, + 0x02, 0x09, 0x1b, 0x1b, 0x09, 0x1b, 0x1b, 0x0b, + 0x03, 0x03, 0x0b, 0x03, 0x03, 0x0b, 0x1b, 0x1b, + 0x0a, 0x03, 0x1b, 0x0a, 0x03, 0x1b, 0x0a, 0x02, + 0x20, 0x0a, 0x1b, 0x04, 0x0a, 0x1b, 0x04, 0x0a, + 0x1b, 0x1b, 0x0a, 0x1b, 0x1b, 0x0c, 0x03, 0x1f, + 0x0c, 0x04, 0x1b, 0x0c, 0x04, 0x1b, 0x0d, 0x1b, + 0x03, 0x0d, 0x1b, 0x03, 0x0d, 0x1b, 0x1b, 0x0d, + 0x1b, 0x20, 0x0f, 0x02, 0x1b, 0x0f, 0x1b, 0x1b, + 0x0f, 0x1b, 0x1b, 0x0f, 0x1b, 0x1f, 0x10, 0x1b, + 0x1b, 0x10, 0x1b, 0x20, 0x10, 0x1b, 0x1f, 0x17, + 0x04, 0x1b, 0x17, 0x04, 0x1b, 0x18, 0x1b, 0x03, + 0x18, 0x1b, 0x1b, 0x1a, 0x03, 0x1b, 0x1a, 0x03, + 0x20, 0x1a, 0x03, 0x1f, 0x1a, 0x02, 0x02, 0x1a, + 0x02, 0x02, 0x1a, 0x04, 0x1b, 0x1a, 0x04, 0x1b, + 0x1a, 0x1b, 0x03, 0x1a, 0x1b, 0x03, 0x1b, 0x03, + 0x02, 0x1b, 0x03, 0x1b, 0x1b, 0x03, 0x20, 0x1b, + 0x02, 0x03, 0x1b, 0x02, 0x1b, 0x1b, 0x04, 0x02, + 0x1b, 0x04, 0x1b, 0x28, 0x06, 0x1d, 0x04, 0x06, + 0x1f, 0x1d, 0x04, 0x1f, 0x1d, 0x1d, 0x1e, 0x05, + 0x1d, 0x1e, 0x05, 0x21, 0x1e, 0x04, 0x1d, 0x1e, + 0x04, 0x1d, 0x1e, 0x04, 0x21, 0x1e, 0x1d, 0x22, + 0x1e, 0x1d, 0x21, 0x22, 0x1d, 0x1d, 0x22, 0x1d, + 0x1d, 0x00, 0x06, 0x22, 0x02, 0x04, 0x22, 0x02, + 0x04, 0x21, 0x02, 0x06, 0x22, 0x02, 0x06, 0x21, + 0x02, 0x1d, 0x22, 0x02, 0x1d, 0x21, 0x04, 0x1d, + 0x22, 0x04, 0x05, 0x21, 0x04, 0x1d, 0x21, 0x0b, + 0x06, 0x21, 0x0d, 0x05, 0x22, 0x0c, 0x05, 0x22, + 0x0e, 0x05, 0x22, 0x1c, 0x04, 0x22, 0x1c, 0x1d, + 0x22, 0x22, 0x05, 0x22, 0x22, 0x04, 0x22, 0x22, + 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1a, 0x1d, 0x22, + 0x1e, 0x05, 0x22, 0x1a, 0x1d, 0x05, 0x1c, 0x05, + 0x1d, 0x11, 0x1d, 0x22, 0x1b, 0x1d, 0x22, 0x1e, + 0x04, 0x05, 0x1d, 0x06, 0x22, 0x1c, 0x04, 0x1d, + 0x1b, 0x1d, 0x1d, 0x1c, 0x04, 0x1d, 0x1e, 0x04, + 0x05, 0x04, 0x05, 0x22, 0x05, 0x04, 0x22, 0x1d, + 0x04, 0x22, 0x19, 0x1d, 0x22, 0x00, 0x05, 0x22, + 0x1b, 0x1d, 0x1d, 0x11, 0x04, 0x1d, 0x0d, 0x1d, + 0x1d, 0x0b, 0x06, 0x22, 0x1e, 0x04, 0x22, 0x35, + 0x06, 0x00, 0x0f, 0x9d, 0x0d, 0x0f, 0x9d, 0x27, + 0x06, 0x00, 0x1d, 0x1d, 0x20, 0x00, 0x1c, 0x01, + 0x0a, 0x1e, 0x06, 0x1e, 0x08, 0x0e, 0x1d, 0x12, + 0x1e, 0x0a, 0x0c, 0x21, 0x1d, 0x12, 0x1d, 0x23, + 0x20, 0x21, 0x0c, 0x1d, 0x1e, 0x35, 0x06, 0x00, + 0x0f, 0x14, 0x27, 0x06, 0x0e, 0x1d, 0x22, 0xff, + 0x00, 0x1d, 0x1d, 0x20, 0xff, 0x12, 0x1d, 0x23, + 0x20, 0xff, 0x21, 0x0c, 0x1d, 0x1e, 0x27, 0x06, + 0x05, 0x1d, 0xff, 0x05, 0x1d, 0x00, 0x1d, 0x20, + 0x27, 0x06, 0x0a, 0xa5, 0x00, 0x1d, 0x2c, 0x00, + 0x01, 0x30, 0x02, 0x30, 0x3a, 0x00, 0x3b, 0x00, + 0x21, 0x00, 0x3f, 0x00, 0x16, 0x30, 0x17, 0x30, + 0x26, 0x20, 0x13, 0x20, 0x12, 0x01, 0x00, 0x5f, + 0x5f, 0x28, 0x29, 0x7b, 0x7d, 0x08, 0x30, 0x0c, + 0x0d, 0x08, 0x09, 0x02, 0x03, 0x00, 0x01, 0x04, + 0x05, 0x06, 0x07, 0x5b, 0x00, 0x5d, 0x00, 0x3e, + 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x5f, + 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x2c, 0x00, 0x01, + 0x30, 0x2e, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x3a, + 0x00, 0x3f, 0x00, 0x21, 0x00, 0x14, 0x20, 0x28, + 0x00, 0x29, 0x00, 0x7b, 0x00, 0x7d, 0x00, 0x14, + 0x30, 0x15, 0x30, 0x23, 0x26, 0x2a, 0x2b, 0x2d, + 0x3c, 0x3e, 0x3d, 0x00, 0x5c, 0x24, 0x25, 0x40, + 0x40, 0x06, 0xff, 0x0b, 0x00, 0x0b, 0xff, 0x0c, + 0x20, 0x00, 0x4d, 0x06, 0x40, 0x06, 0xff, 0x0e, + 0x00, 0x0e, 0xff, 0x0f, 0x00, 0x0f, 0xff, 0x10, + 0x00, 0x10, 0xff, 0x11, 0x00, 0x11, 0xff, 0x12, + 0x00, 0x12, 0x21, 0x06, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, + 0x05, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, + 0x08, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, + 0x0f, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x12, + 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, + 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, + 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, + 0x18, 0x19, 0x19, 0x19, 0x19, 0x20, 0x20, 0x20, + 0x20, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, + 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, + 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, + 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x29, + 0x29, 0x22, 0x06, 0x22, 0x00, 0x22, 0x00, 0x22, + 0x01, 0x22, 0x01, 0x22, 0x03, 0x22, 0x03, 0x22, + 0x05, 0x22, 0x05, 0x21, 0x00, 0x85, 0x29, 0x01, + 0x30, 0x01, 0x0b, 0x0c, 0x00, 0xfa, 0xf1, 0xa0, + 0xa2, 0xa4, 0xa6, 0xa8, 0xe2, 0xe4, 0xe6, 0xc2, + 0xfb, 0xa1, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa, 0xac, + 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, + 0xbe, 0xc0, 0xc3, 0xc5, 0xc7, 0xc9, 0xca, 0xcb, + 0xcc, 0xcd, 0xce, 0xd1, 0xd4, 0xd7, 0xda, 0xdd, + 0xde, 0xdf, 0xe0, 0xe1, 0xe3, 0xe5, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, 0xee, 0xf2, 0x98, 0x99, + 0x31, 0x31, 0x4f, 0x31, 0x55, 0x31, 0x5b, 0x31, + 0x61, 0x31, 0xa2, 0x00, 0xa3, 0x00, 0xac, 0x00, + 0xaf, 0x00, 0xa6, 0x00, 0xa5, 0x00, 0xa9, 0x20, + 0x00, 0x00, 0x02, 0x25, 0x90, 0x21, 0x91, 0x21, + 0x92, 0x21, 0x93, 0x21, 0xa0, 0x25, 0xcb, 0x25, + 0xd2, 0x05, 0x07, 0x03, 0x01, 0xda, 0x05, 0x07, + 0x03, 0x01, 0xd0, 0x02, 0xd1, 0x02, 0xe6, 0x00, + 0x99, 0x02, 0x53, 0x02, 0x00, 0x00, 0xa3, 0x02, + 0x66, 0xab, 0xa5, 0x02, 0xa4, 0x02, 0x56, 0x02, + 0x57, 0x02, 0x91, 0x1d, 0x58, 0x02, 0x5e, 0x02, + 0xa9, 0x02, 0x64, 0x02, 0x62, 0x02, 0x60, 0x02, + 0x9b, 0x02, 0x27, 0x01, 0x9c, 0x02, 0x67, 0x02, + 0x84, 0x02, 0xaa, 0x02, 0xab, 0x02, 0x6c, 0x02, + 0x04, 0xdf, 0x8e, 0xa7, 0x6e, 0x02, 0x05, 0xdf, + 0x8e, 0x02, 0x06, 0xdf, 0xf8, 0x00, 0x76, 0x02, + 0x77, 0x02, 0x71, 0x00, 0x7a, 0x02, 0x08, 0xdf, + 0x7d, 0x02, 0x7e, 0x02, 0x80, 0x02, 0xa8, 0x02, + 0xa6, 0x02, 0x67, 0xab, 0xa7, 0x02, 0x88, 0x02, + 0x71, 0x2c, 0x00, 0x00, 0x8f, 0x02, 0xa1, 0x02, + 0xa2, 0x02, 0x98, 0x02, 0xc0, 0x01, 0xc1, 0x01, + 0xc2, 0x01, 0x0a, 0xdf, 0x1e, 0xdf, 0x41, 0x04, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x14, 0x99, 0x10, + 0xba, 0x10, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x10, + 0xba, 0x10, 0x05, 0x05, 0xa5, 0x10, 0xba, 0x10, + 0x05, 0x31, 0x11, 0x27, 0x11, 0x32, 0x11, 0x27, + 0x11, 0x55, 0x47, 0x13, 0x3e, 0x13, 0x47, 0x13, + 0x57, 0x13, 0x55, 0x82, 0x13, 0xc9, 0x13, 0x00, + 0x00, 0x00, 0x00, 0x84, 0x13, 0xbb, 0x13, 0x05, + 0x05, 0x8b, 0x13, 0xc2, 0x13, 0x05, 0x90, 0x13, + 0xc9, 0x13, 0x05, 0xc2, 0x13, 0xc2, 0x13, 0x00, + 0x00, 0x00, 0x00, 0xc2, 0x13, 0xb8, 0x13, 0xc2, + 0x13, 0xc9, 0x13, 0x05, 0x55, 0xb9, 0x14, 0xba, + 0x14, 0xb9, 0x14, 0xb0, 0x14, 0x00, 0x00, 0x00, + 0x00, 0xb9, 0x14, 0xbd, 0x14, 0x55, 0x50, 0xb8, + 0x15, 0xaf, 0x15, 0xb9, 0x15, 0xaf, 0x15, 0x55, + 0x35, 0x19, 0x30, 0x19, 0x05, 0x1e, 0x61, 0x1e, + 0x61, 0x1e, 0x61, 0x29, 0x61, 0x1e, 0x61, 0x1f, + 0x61, 0x29, 0x61, 0x1f, 0x61, 0x1e, 0x61, 0x20, + 0x61, 0x21, 0x61, 0x1f, 0x61, 0x22, 0x61, 0x1f, + 0x61, 0x21, 0x61, 0x20, 0x61, 0x55, 0x55, 0x55, + 0x55, 0x67, 0x6d, 0x67, 0x6d, 0x63, 0x6d, 0x67, + 0x6d, 0x69, 0x6d, 0x67, 0x6d, 0x55, 0x05, 0x41, + 0x00, 0x30, 0x00, 0x57, 0xd1, 0x65, 0xd1, 0x58, + 0xd1, 0x65, 0xd1, 0x5f, 0xd1, 0x6e, 0xd1, 0x5f, + 0xd1, 0x6f, 0xd1, 0x5f, 0xd1, 0x70, 0xd1, 0x5f, + 0xd1, 0x71, 0xd1, 0x5f, 0xd1, 0x72, 0xd1, 0x55, + 0x55, 0x55, 0x05, 0xb9, 0xd1, 0x65, 0xd1, 0xba, + 0xd1, 0x65, 0xd1, 0xbb, 0xd1, 0x6e, 0xd1, 0xbc, + 0xd1, 0x6e, 0xd1, 0xbb, 0xd1, 0x6f, 0xd1, 0xbc, + 0xd1, 0x6f, 0xd1, 0x55, 0x55, 0x55, 0x41, 0x00, + 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x69, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x43, 0x44, + 0x00, 0x00, 0x47, 0x00, 0x00, 0x4a, 0x4b, 0x00, + 0x00, 0x4e, 0x4f, 0x50, 0x51, 0x00, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, + 0x63, 0x64, 0x00, 0x66, 0x68, 0x00, 0x70, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x41, 0x42, 0x00, 0x44, + 0x45, 0x46, 0x47, 0x4a, 0x00, 0x53, 0x00, 0x61, + 0x00, 0x41, 0x42, 0x00, 0x44, 0x45, 0x46, 0x47, + 0x00, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x4f, + 0x53, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x31, 0x01, 0x37, 0x02, + 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, + 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, + 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, + 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, + 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, + 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, + 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, + 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, + 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x0b, 0x0c, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x04, 0x3a, 0x04, 0x3e, 0x04, + 0x4b, 0x04, 0x4d, 0x04, 0x4e, 0x04, 0x89, 0xa6, + 0x30, 0x04, 0xa9, 0x26, 0x28, 0xb9, 0x7f, 0x9f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x0a, 0x0b, 0x0e, 0x0f, 0x11, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x61, 0x26, + 0x25, 0x2f, 0x7b, 0x51, 0xa6, 0xb1, 0x04, 0x27, + 0x06, 0x00, 0x01, 0x05, 0x08, 0x2a, 0x06, 0x1e, + 0x08, 0x03, 0x0d, 0x20, 0x19, 0x1a, 0x1b, 0x1c, + 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, + 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x44, 0x90, + 0x77, 0x45, 0x28, 0x06, 0x2c, 0x06, 0x00, 0x00, + 0x47, 0x06, 0x33, 0x06, 0x17, 0x10, 0x11, 0x12, + 0x13, 0x00, 0x06, 0x0e, 0x02, 0x0f, 0x34, 0x06, + 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, 0x00, 0x00, + 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, 0x2d, 0x06, + 0x00, 0x00, 0x4a, 0x06, 0x00, 0x00, 0x44, 0x06, + 0x00, 0x00, 0x46, 0x06, 0x33, 0x06, 0x39, 0x06, + 0x00, 0x00, 0x35, 0x06, 0x42, 0x06, 0x00, 0x00, + 0x34, 0x06, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x06, + 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, + 0x00, 0x00, 0xba, 0x06, 0x00, 0x00, 0x6f, 0x06, + 0x00, 0x00, 0x28, 0x06, 0x2c, 0x06, 0x00, 0x00, + 0x47, 0x06, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x06, + 0x37, 0x06, 0x4a, 0x06, 0x43, 0x06, 0x00, 0x00, + 0x45, 0x06, 0x46, 0x06, 0x33, 0x06, 0x39, 0x06, + 0x41, 0x06, 0x35, 0x06, 0x42, 0x06, 0x00, 0x00, + 0x34, 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, + 0x00, 0x00, 0x36, 0x06, 0x38, 0x06, 0x3a, 0x06, + 0x6e, 0x06, 0x00, 0x00, 0xa1, 0x06, 0x27, 0x06, + 0x00, 0x01, 0x05, 0x08, 0x20, 0x21, 0x0b, 0x06, + 0x10, 0x23, 0x2a, 0x06, 0x1a, 0x1b, 0x1c, 0x09, + 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, + 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x28, 0x06, 0x2c, + 0x06, 0x2f, 0x06, 0x00, 0x00, 0x48, 0x06, 0x32, + 0x06, 0x2d, 0x06, 0x37, 0x06, 0x4a, 0x06, 0x2a, + 0x06, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, 0x17, 0x0b, + 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, 0x06, 0x0c, + 0x0e, 0x10, 0x30, 0x2e, 0x30, 0x00, 0x2c, 0x00, + 0x28, 0x00, 0x41, 0x00, 0x29, 0x00, 0x14, 0x30, + 0x53, 0x00, 0x15, 0x30, 0x43, 0x52, 0x43, 0x44, + 0x57, 0x5a, 0x41, 0x00, 0x48, 0x56, 0x4d, 0x56, + 0x53, 0x44, 0x53, 0x53, 0x50, 0x50, 0x56, 0x57, + 0x43, 0x4d, 0x43, 0x4d, 0x44, 0x4d, 0x52, 0x44, + 0x4a, 0x4b, 0x30, 0x30, 0x00, 0x68, 0x68, 0x4b, + 0x62, 0x57, 0x5b, 0xcc, 0x53, 0xc7, 0x30, 0x8c, + 0x4e, 0x1a, 0x59, 0xe3, 0x89, 0x29, 0x59, 0xa4, + 0x4e, 0x20, 0x66, 0x21, 0x71, 0x99, 0x65, 0x4d, + 0x52, 0x8c, 0x5f, 0x8d, 0x51, 0xb0, 0x65, 0x1d, + 0x52, 0x42, 0x7d, 0x1f, 0x75, 0xa9, 0x8c, 0xf0, + 0x58, 0x39, 0x54, 0x14, 0x6f, 0x95, 0x62, 0x55, + 0x63, 0x00, 0x4e, 0x09, 0x4e, 0x4a, 0x90, 0xe6, + 0x5d, 0x2d, 0x4e, 0xf3, 0x53, 0x07, 0x63, 0x70, + 0x8d, 0x53, 0x62, 0x81, 0x79, 0x7a, 0x7a, 0x08, + 0x54, 0x80, 0x6e, 0x09, 0x67, 0x08, 0x67, 0x33, + 0x75, 0x72, 0x52, 0xb6, 0x55, 0x4d, 0x91, 0x14, + 0x30, 0x15, 0x30, 0x2c, 0x67, 0x09, 0x4e, 0x8c, + 0x4e, 0x89, 0x5b, 0xb9, 0x70, 0x53, 0x62, 0xd7, + 0x76, 0xdd, 0x52, 0x57, 0x65, 0x97, 0x5f, 0xef, + 0x53, 0x30, 0x00, 0x38, 0x4e, 0x05, 0x00, 0x09, + 0x22, 0x01, 0x60, 0x4f, 0xae, 0x4f, 0xbb, 0x4f, + 0x02, 0x50, 0x7a, 0x50, 0x99, 0x50, 0xe7, 0x50, + 0xcf, 0x50, 0x9e, 0x34, 0x3a, 0x06, 0x4d, 0x51, + 0x54, 0x51, 0x64, 0x51, 0x77, 0x51, 0x1c, 0x05, + 0xb9, 0x34, 0x67, 0x51, 0x8d, 0x51, 0x4b, 0x05, + 0x97, 0x51, 0xa4, 0x51, 0xcc, 0x4e, 0xac, 0x51, + 0xb5, 0x51, 0xdf, 0x91, 0xf5, 0x51, 0x03, 0x52, + 0xdf, 0x34, 0x3b, 0x52, 0x46, 0x52, 0x72, 0x52, + 0x77, 0x52, 0x15, 0x35, 0x02, 0x00, 0x20, 0x80, + 0x80, 0x00, 0x08, 0x00, 0x00, 0xc7, 0x52, 0x00, + 0x02, 0x1d, 0x33, 0x3e, 0x3f, 0x50, 0x82, 0x8a, + 0x93, 0xac, 0xb6, 0xb8, 0xb8, 0xb8, 0x2c, 0x0a, + 0x70, 0x70, 0xca, 0x53, 0xdf, 0x53, 0x63, 0x0b, + 0xeb, 0x53, 0xf1, 0x53, 0x06, 0x54, 0x9e, 0x54, + 0x38, 0x54, 0x48, 0x54, 0x68, 0x54, 0xa2, 0x54, + 0xf6, 0x54, 0x10, 0x55, 0x53, 0x55, 0x63, 0x55, + 0x84, 0x55, 0x84, 0x55, 0x99, 0x55, 0xab, 0x55, + 0xb3, 0x55, 0xc2, 0x55, 0x16, 0x57, 0x06, 0x56, + 0x17, 0x57, 0x51, 0x56, 0x74, 0x56, 0x07, 0x52, + 0xee, 0x58, 0xce, 0x57, 0xf4, 0x57, 0x0d, 0x58, + 0x8b, 0x57, 0x32, 0x58, 0x31, 0x58, 0xac, 0x58, + 0xe4, 0x14, 0xf2, 0x58, 0xf7, 0x58, 0x06, 0x59, + 0x1a, 0x59, 0x22, 0x59, 0x62, 0x59, 0xa8, 0x16, + 0xea, 0x16, 0xec, 0x59, 0x1b, 0x5a, 0x27, 0x5a, + 0xd8, 0x59, 0x66, 0x5a, 0xee, 0x36, 0xfc, 0x36, + 0x08, 0x5b, 0x3e, 0x5b, 0x3e, 0x5b, 0xc8, 0x19, + 0xc3, 0x5b, 0xd8, 0x5b, 0xe7, 0x5b, 0xf3, 0x5b, + 0x18, 0x1b, 0xff, 0x5b, 0x06, 0x5c, 0x53, 0x5f, + 0x22, 0x5c, 0x81, 0x37, 0x60, 0x5c, 0x6e, 0x5c, + 0xc0, 0x5c, 0x8d, 0x5c, 0xe4, 0x1d, 0x43, 0x5d, + 0xe6, 0x1d, 0x6e, 0x5d, 0x6b, 0x5d, 0x7c, 0x5d, + 0xe1, 0x5d, 0xe2, 0x5d, 0x2f, 0x38, 0xfd, 0x5d, + 0x28, 0x5e, 0x3d, 0x5e, 0x69, 0x5e, 0x62, 0x38, + 0x83, 0x21, 0x7c, 0x38, 0xb0, 0x5e, 0xb3, 0x5e, + 0xb6, 0x5e, 0xca, 0x5e, 0x92, 0xa3, 0xfe, 0x5e, + 0x31, 0x23, 0x31, 0x23, 0x01, 0x82, 0x22, 0x5f, + 0x22, 0x5f, 0xc7, 0x38, 0xb8, 0x32, 0xda, 0x61, + 0x62, 0x5f, 0x6b, 0x5f, 0xe3, 0x38, 0x9a, 0x5f, + 0xcd, 0x5f, 0xd7, 0x5f, 0xf9, 0x5f, 0x81, 0x60, + 0x3a, 0x39, 0x1c, 0x39, 0x94, 0x60, 0xd4, 0x26, + 0xc7, 0x60, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x00, + 0x02, 0x08, 0x00, 0x80, 0x08, 0x00, 0x00, 0x08, + 0x80, 0x28, 0x80, 0x02, 0x00, 0x00, 0x02, 0x48, + 0x61, 0x00, 0x04, 0x06, 0x04, 0x32, 0x46, 0x6a, + 0x5c, 0x67, 0x96, 0xaa, 0xae, 0xc8, 0xd3, 0x5d, + 0x62, 0x00, 0x54, 0x77, 0xf3, 0x0c, 0x2b, 0x3d, + 0x63, 0xfc, 0x62, 0x68, 0x63, 0x83, 0x63, 0xe4, + 0x63, 0xf1, 0x2b, 0x22, 0x64, 0xc5, 0x63, 0xa9, + 0x63, 0x2e, 0x3a, 0x69, 0x64, 0x7e, 0x64, 0x9d, + 0x64, 0x77, 0x64, 0x6c, 0x3a, 0x4f, 0x65, 0x6c, + 0x65, 0x0a, 0x30, 0xe3, 0x65, 0xf8, 0x66, 0x49, + 0x66, 0x19, 0x3b, 0x91, 0x66, 0x08, 0x3b, 0xe4, + 0x3a, 0x92, 0x51, 0x95, 0x51, 0x00, 0x67, 0x9c, + 0x66, 0xad, 0x80, 0xd9, 0x43, 0x17, 0x67, 0x1b, + 0x67, 0x21, 0x67, 0x5e, 0x67, 0x53, 0x67, 0xc3, + 0x33, 0x49, 0x3b, 0xfa, 0x67, 0x85, 0x67, 0x52, + 0x68, 0x85, 0x68, 0x6d, 0x34, 0x8e, 0x68, 0x1f, + 0x68, 0x14, 0x69, 0x9d, 0x3b, 0x42, 0x69, 0xa3, + 0x69, 0xea, 0x69, 0xa8, 0x6a, 0xa3, 0x36, 0xdb, + 0x6a, 0x18, 0x3c, 0x21, 0x6b, 0xa7, 0x38, 0x54, + 0x6b, 0x4e, 0x3c, 0x72, 0x6b, 0x9f, 0x6b, 0xba, + 0x6b, 0xbb, 0x6b, 0x8d, 0x3a, 0x0b, 0x1d, 0xfa, + 0x3a, 0x4e, 0x6c, 0xbc, 0x3c, 0xbf, 0x6c, 0xcd, + 0x6c, 0x67, 0x6c, 0x16, 0x6d, 0x3e, 0x6d, 0x77, + 0x6d, 0x41, 0x6d, 0x69, 0x6d, 0x78, 0x6d, 0x85, + 0x6d, 0x1e, 0x3d, 0x34, 0x6d, 0x2f, 0x6e, 0x6e, + 0x6e, 0x33, 0x3d, 0xcb, 0x6e, 0xc7, 0x6e, 0xd1, + 0x3e, 0xf9, 0x6d, 0x6e, 0x6f, 0x5e, 0x3f, 0x8e, + 0x3f, 0xc6, 0x6f, 0x39, 0x70, 0x1e, 0x70, 0x1b, + 0x70, 0x96, 0x3d, 0x4a, 0x70, 0x7d, 0x70, 0x77, + 0x70, 0xad, 0x70, 0x25, 0x05, 0x45, 0x71, 0x63, + 0x42, 0x9c, 0x71, 0xab, 0x43, 0x28, 0x72, 0x35, + 0x72, 0x50, 0x72, 0x08, 0x46, 0x80, 0x72, 0x95, + 0x72, 0x35, 0x47, 0x02, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, + 0x02, 0x02, 0x80, 0x8a, 0x00, 0x00, 0x20, 0x00, + 0x08, 0x0a, 0x00, 0x80, 0x88, 0x80, 0x20, 0x14, + 0x48, 0x7a, 0x73, 0x8b, 0x73, 0xac, 0x3e, 0xa5, + 0x73, 0xb8, 0x3e, 0xb8, 0x3e, 0x47, 0x74, 0x5c, + 0x74, 0x71, 0x74, 0x85, 0x74, 0xca, 0x74, 0x1b, + 0x3f, 0x24, 0x75, 0x36, 0x4c, 0x3e, 0x75, 0x92, + 0x4c, 0x70, 0x75, 0x9f, 0x21, 0x10, 0x76, 0xa1, + 0x4f, 0xb8, 0x4f, 0x44, 0x50, 0xfc, 0x3f, 0x08, + 0x40, 0xf4, 0x76, 0xf3, 0x50, 0xf2, 0x50, 0x19, + 0x51, 0x33, 0x51, 0x1e, 0x77, 0x1f, 0x77, 0x1f, + 0x77, 0x4a, 0x77, 0x39, 0x40, 0x8b, 0x77, 0x46, + 0x40, 0x96, 0x40, 0x1d, 0x54, 0x4e, 0x78, 0x8c, + 0x78, 0xcc, 0x78, 0xe3, 0x40, 0x26, 0x56, 0x56, + 0x79, 0x9a, 0x56, 0xc5, 0x56, 0x8f, 0x79, 0xeb, + 0x79, 0x2f, 0x41, 0x40, 0x7a, 0x4a, 0x7a, 0x4f, + 0x7a, 0x7c, 0x59, 0xa7, 0x5a, 0xa7, 0x5a, 0xee, + 0x7a, 0x02, 0x42, 0xab, 0x5b, 0xc6, 0x7b, 0xc9, + 0x7b, 0x27, 0x42, 0x80, 0x5c, 0xd2, 0x7c, 0xa0, + 0x42, 0xe8, 0x7c, 0xe3, 0x7c, 0x00, 0x7d, 0x86, + 0x5f, 0x63, 0x7d, 0x01, 0x43, 0xc7, 0x7d, 0x02, + 0x7e, 0x45, 0x7e, 0x34, 0x43, 0x28, 0x62, 0x47, + 0x62, 0x59, 0x43, 0xd9, 0x62, 0x7a, 0x7f, 0x3e, + 0x63, 0x95, 0x7f, 0xfa, 0x7f, 0x05, 0x80, 0xda, + 0x64, 0x23, 0x65, 0x60, 0x80, 0xa8, 0x65, 0x70, + 0x80, 0x5f, 0x33, 0xd5, 0x43, 0xb2, 0x80, 0x03, + 0x81, 0x0b, 0x44, 0x3e, 0x81, 0xb5, 0x5a, 0xa7, + 0x67, 0xb5, 0x67, 0x93, 0x33, 0x9c, 0x33, 0x01, + 0x82, 0x04, 0x82, 0x9e, 0x8f, 0x6b, 0x44, 0x91, + 0x82, 0x8b, 0x82, 0x9d, 0x82, 0xb3, 0x52, 0xb1, + 0x82, 0xb3, 0x82, 0xbd, 0x82, 0xe6, 0x82, 0x3c, + 0x6b, 0xe5, 0x82, 0x1d, 0x83, 0x63, 0x83, 0xad, + 0x83, 0x23, 0x83, 0xbd, 0x83, 0xe7, 0x83, 0x57, + 0x84, 0x53, 0x83, 0xca, 0x83, 0xcc, 0x83, 0xdc, + 0x83, 0x36, 0x6c, 0x6b, 0x6d, 0x02, 0x00, 0x00, + 0x20, 0x22, 0x2a, 0xa0, 0x0a, 0x00, 0x20, 0x80, + 0x28, 0x00, 0xa8, 0x20, 0x20, 0x00, 0x02, 0x80, + 0x22, 0x02, 0x8a, 0x08, 0x00, 0xaa, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x28, 0xd5, 0x6c, 0x2b, + 0x45, 0xf1, 0x84, 0xf3, 0x84, 0x16, 0x85, 0xca, + 0x73, 0x64, 0x85, 0x2c, 0x6f, 0x5d, 0x45, 0x61, + 0x45, 0xb1, 0x6f, 0xd2, 0x70, 0x6b, 0x45, 0x50, + 0x86, 0x5c, 0x86, 0x67, 0x86, 0x69, 0x86, 0xa9, + 0x86, 0x88, 0x86, 0x0e, 0x87, 0xe2, 0x86, 0x79, + 0x87, 0x28, 0x87, 0x6b, 0x87, 0x86, 0x87, 0xd7, + 0x45, 0xe1, 0x87, 0x01, 0x88, 0xf9, 0x45, 0x60, + 0x88, 0x63, 0x88, 0x67, 0x76, 0xd7, 0x88, 0xde, + 0x88, 0x35, 0x46, 0xfa, 0x88, 0xbb, 0x34, 0xae, + 0x78, 0x66, 0x79, 0xbe, 0x46, 0xc7, 0x46, 0xa0, + 0x8a, 0xed, 0x8a, 0x8a, 0x8b, 0x55, 0x8c, 0xa8, + 0x7c, 0xab, 0x8c, 0xc1, 0x8c, 0x1b, 0x8d, 0x77, + 0x8d, 0x2f, 0x7f, 0x04, 0x08, 0xcb, 0x8d, 0xbc, + 0x8d, 0xf0, 0x8d, 0xde, 0x08, 0xd4, 0x8e, 0x38, + 0x8f, 0xd2, 0x85, 0xed, 0x85, 0x94, 0x90, 0xf1, + 0x90, 0x11, 0x91, 0x2e, 0x87, 0x1b, 0x91, 0x38, + 0x92, 0xd7, 0x92, 0xd8, 0x92, 0x7c, 0x92, 0xf9, + 0x93, 0x15, 0x94, 0xfa, 0x8b, 0x8b, 0x95, 0x95, + 0x49, 0xb7, 0x95, 0x77, 0x8d, 0xe6, 0x49, 0xc3, + 0x96, 0xb2, 0x5d, 0x23, 0x97, 0x45, 0x91, 0x1a, + 0x92, 0x6e, 0x4a, 0x76, 0x4a, 0xe0, 0x97, 0x0a, + 0x94, 0xb2, 0x4a, 0x96, 0x94, 0x0b, 0x98, 0x0b, + 0x98, 0x29, 0x98, 0xb6, 0x95, 0xe2, 0x98, 0x33, + 0x4b, 0x29, 0x99, 0xa7, 0x99, 0xc2, 0x99, 0xfe, + 0x99, 0xce, 0x4b, 0x30, 0x9b, 0x12, 0x9b, 0x40, + 0x9c, 0xfd, 0x9c, 0xce, 0x4c, 0xed, 0x4c, 0x67, + 0x9d, 0xce, 0xa0, 0xf8, 0x4c, 0x05, 0xa1, 0x0e, + 0xa2, 0x91, 0xa2, 0xbb, 0x9e, 0x56, 0x4d, 0xf9, + 0x9e, 0xfe, 0x9e, 0x05, 0x9f, 0x0f, 0x9f, 0x16, + 0x9f, 0x3b, 0x9f, 0x00, 0xa6, 0x02, 0x88, 0xa0, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x28, 0x00, + 0x08, 0xa0, 0x80, 0xa0, 0x80, 0x00, 0x80, 0x80, + 0x00, 0x0a, 0x88, 0x80, 0x00, 0x80, 0x00, 0x20, + 0x2a, 0x00, 0x80, +}; + +static const uint16_t unicode_comp_table[965] = { + 0x4a01, 0x49c0, 0x4a02, 0x0280, 0x0281, 0x0282, 0x0283, 0x02c0, + 0x02c2, 0x0a00, 0x0284, 0x2442, 0x0285, 0x07c0, 0x0980, 0x0982, + 0x2440, 0x2280, 0x02c4, 0x2282, 0x2284, 0x2286, 0x02c6, 0x02c8, + 0x02ca, 0x02cc, 0x0287, 0x228a, 0x02ce, 0x228c, 0x2290, 0x2292, + 0x228e, 0x0288, 0x0289, 0x028a, 0x2482, 0x0300, 0x0302, 0x0304, + 0x028b, 0x2480, 0x0308, 0x0984, 0x0986, 0x2458, 0x0a02, 0x0306, + 0x2298, 0x229a, 0x229e, 0x0900, 0x030a, 0x22a0, 0x030c, 0x030e, + 0x0840, 0x0310, 0x0312, 0x22a2, 0x22a6, 0x09c0, 0x22a4, 0x22a8, + 0x22aa, 0x028c, 0x028d, 0x028e, 0x0340, 0x0342, 0x0344, 0x0380, + 0x028f, 0x248e, 0x07c2, 0x0988, 0x098a, 0x2490, 0x0346, 0x22ac, + 0x0400, 0x22b0, 0x0842, 0x22b2, 0x0402, 0x22b4, 0x0440, 0x0444, + 0x22b6, 0x0442, 0x22c2, 0x22c0, 0x22c4, 0x22c6, 0x22c8, 0x0940, + 0x04c0, 0x0291, 0x22ca, 0x04c4, 0x22cc, 0x04c2, 0x22d0, 0x22ce, + 0x0292, 0x0293, 0x0294, 0x0295, 0x0540, 0x0542, 0x0a08, 0x0296, + 0x2494, 0x0544, 0x07c4, 0x098c, 0x098e, 0x06c0, 0x2492, 0x0844, + 0x2308, 0x230a, 0x0580, 0x230c, 0x0584, 0x0990, 0x0992, 0x230e, + 0x0582, 0x2312, 0x0586, 0x0588, 0x2314, 0x058c, 0x2316, 0x0998, + 0x058a, 0x231e, 0x0590, 0x2320, 0x099a, 0x058e, 0x2324, 0x2322, + 0x0299, 0x029a, 0x029b, 0x05c0, 0x05c2, 0x05c4, 0x029c, 0x24ac, + 0x05c6, 0x05c8, 0x07c6, 0x0994, 0x0996, 0x0700, 0x24aa, 0x2326, + 0x05ca, 0x232a, 0x2328, 0x2340, 0x2342, 0x2344, 0x2346, 0x05cc, + 0x234a, 0x2348, 0x234c, 0x234e, 0x2350, 0x24b8, 0x029d, 0x05ce, + 0x24be, 0x0a0c, 0x2352, 0x0600, 0x24bc, 0x24ba, 0x0640, 0x2354, + 0x0642, 0x0644, 0x2356, 0x2358, 0x02a0, 0x02a1, 0x02a2, 0x02a3, + 0x02c1, 0x02c3, 0x0a01, 0x02a4, 0x2443, 0x02a5, 0x07c1, 0x0981, + 0x0983, 0x2441, 0x2281, 0x02c5, 0x2283, 0x2285, 0x2287, 0x02c7, + 0x02c9, 0x02cb, 0x02cd, 0x02a7, 0x228b, 0x02cf, 0x228d, 0x2291, + 0x2293, 0x228f, 0x02a8, 0x02a9, 0x02aa, 0x2483, 0x0301, 0x0303, + 0x0305, 0x02ab, 0x2481, 0x0309, 0x0985, 0x0987, 0x2459, 0x0a03, + 0x0307, 0x2299, 0x229b, 0x229f, 0x0901, 0x030b, 0x22a1, 0x030d, + 0x030f, 0x0841, 0x0311, 0x0313, 0x22a3, 0x22a7, 0x09c1, 0x22a5, + 0x22a9, 0x22ab, 0x2380, 0x02ac, 0x02ad, 0x02ae, 0x0341, 0x0343, + 0x0345, 0x02af, 0x248f, 0x07c3, 0x0989, 0x098b, 0x2491, 0x0347, + 0x22ad, 0x0401, 0x0884, 0x22b1, 0x0843, 0x22b3, 0x0403, 0x22b5, + 0x0441, 0x0445, 0x22b7, 0x0443, 0x22c3, 0x22c1, 0x22c5, 0x22c7, + 0x22c9, 0x0941, 0x04c1, 0x02b1, 0x22cb, 0x04c5, 0x22cd, 0x04c3, + 0x22d1, 0x22cf, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x0541, 0x0543, + 0x0a09, 0x02b6, 0x2495, 0x0545, 0x07c5, 0x098d, 0x098f, 0x06c1, + 0x2493, 0x0845, 0x2309, 0x230b, 0x0581, 0x230d, 0x0585, 0x0991, + 0x0993, 0x230f, 0x0583, 0x2313, 0x0587, 0x0589, 0x2315, 0x058d, + 0x2317, 0x0999, 0x058b, 0x231f, 0x2381, 0x0591, 0x2321, 0x099b, + 0x058f, 0x2325, 0x2323, 0x02b9, 0x02ba, 0x02bb, 0x05c1, 0x05c3, + 0x05c5, 0x02bc, 0x24ad, 0x05c7, 0x05c9, 0x07c7, 0x0995, 0x0997, + 0x0701, 0x24ab, 0x2327, 0x05cb, 0x232b, 0x2329, 0x2341, 0x2343, + 0x2345, 0x2347, 0x05cd, 0x234b, 0x2349, 0x2382, 0x234d, 0x234f, + 0x2351, 0x24b9, 0x02bd, 0x05cf, 0x24bf, 0x0a0d, 0x2353, 0x02bf, + 0x24bd, 0x2383, 0x24bb, 0x0641, 0x2355, 0x0643, 0x0645, 0x2357, + 0x2359, 0x3101, 0x0c80, 0x2e00, 0x2446, 0x2444, 0x244a, 0x2448, + 0x0800, 0x0942, 0x0944, 0x0804, 0x2288, 0x2486, 0x2484, 0x248a, + 0x2488, 0x22ae, 0x2498, 0x2496, 0x249c, 0x249a, 0x2300, 0x0a06, + 0x2302, 0x0a04, 0x0946, 0x07ce, 0x07ca, 0x07c8, 0x07cc, 0x2447, + 0x2445, 0x244b, 0x2449, 0x0801, 0x0943, 0x0945, 0x0805, 0x2289, + 0x2487, 0x2485, 0x248b, 0x2489, 0x22af, 0x2499, 0x2497, 0x249d, + 0x249b, 0x2301, 0x0a07, 0x2303, 0x0a05, 0x0947, 0x07cf, 0x07cb, + 0x07c9, 0x07cd, 0x2450, 0x244e, 0x2454, 0x2452, 0x2451, 0x244f, + 0x2455, 0x2453, 0x2294, 0x2296, 0x2295, 0x2297, 0x2304, 0x2306, + 0x2305, 0x2307, 0x2318, 0x2319, 0x231a, 0x231b, 0x232c, 0x232d, + 0x232e, 0x232f, 0x2400, 0x24a2, 0x24a0, 0x24a6, 0x24a4, 0x24a8, + 0x24a3, 0x24a1, 0x24a7, 0x24a5, 0x24a9, 0x24b0, 0x24ae, 0x24b4, + 0x24b2, 0x24b6, 0x24b1, 0x24af, 0x24b5, 0x24b3, 0x24b7, 0x0882, + 0x0880, 0x0881, 0x0802, 0x0803, 0x229c, 0x229d, 0x0a0a, 0x0a0b, + 0x0883, 0x0b40, 0x2c8a, 0x0c81, 0x2c89, 0x2c88, 0x2540, 0x2541, + 0x2d00, 0x2e07, 0x0d00, 0x2640, 0x2641, 0x2e80, 0x0d01, 0x26c8, + 0x26c9, 0x2f00, 0x2f84, 0x0d02, 0x2f83, 0x2f82, 0x0d40, 0x26d8, + 0x26d9, 0x3186, 0x0d04, 0x2740, 0x2741, 0x3100, 0x3086, 0x0d06, + 0x3085, 0x3084, 0x0d41, 0x2840, 0x3200, 0x0d07, 0x284f, 0x2850, + 0x3280, 0x2c84, 0x2e03, 0x2857, 0x0d42, 0x2c81, 0x2c80, 0x24c0, + 0x24c1, 0x2c86, 0x2c83, 0x28c0, 0x0d43, 0x25c0, 0x25c1, 0x2940, + 0x0d44, 0x26c0, 0x26c1, 0x2e05, 0x2e02, 0x29c0, 0x0d45, 0x2f05, + 0x2f04, 0x0d80, 0x26d0, 0x26d1, 0x2f80, 0x2a40, 0x0d82, 0x26e0, + 0x26e1, 0x3080, 0x3081, 0x2ac0, 0x0d83, 0x3004, 0x3003, 0x0d81, + 0x27c0, 0x27c1, 0x3082, 0x2b40, 0x0d84, 0x2847, 0x2848, 0x3184, + 0x3181, 0x2f06, 0x0d08, 0x2f81, 0x3005, 0x0d46, 0x3083, 0x3182, + 0x0e00, 0x0e01, 0x0f40, 0x1180, 0x1182, 0x0f03, 0x0f00, 0x11c0, + 0x0f01, 0x1140, 0x1202, 0x1204, 0x0f81, 0x1240, 0x0fc0, 0x1242, + 0x0f80, 0x1244, 0x1284, 0x0f82, 0x1286, 0x1288, 0x128a, 0x12c0, + 0x1282, 0x1181, 0x1183, 0x1043, 0x1040, 0x11c1, 0x1041, 0x1141, + 0x1203, 0x1205, 0x10c1, 0x1241, 0x1000, 0x1243, 0x10c0, 0x1245, + 0x1285, 0x10c2, 0x1287, 0x1289, 0x128b, 0x12c1, 0x1283, 0x1080, + 0x1100, 0x1101, 0x1200, 0x1201, 0x1280, 0x1281, 0x1340, 0x1341, + 0x1343, 0x1342, 0x1344, 0x13c2, 0x1400, 0x13c0, 0x1440, 0x1480, + 0x14c0, 0x1540, 0x1541, 0x1740, 0x1700, 0x1741, 0x17c0, 0x1800, + 0x1802, 0x1801, 0x1840, 0x1880, 0x1900, 0x18c0, 0x18c1, 0x1901, + 0x1940, 0x1942, 0x1941, 0x1980, 0x19c0, 0x19c2, 0x19c1, 0x1c80, + 0x1cc0, 0x1dc0, 0x1f80, 0x2000, 0x2002, 0x2004, 0x2006, 0x2008, + 0x2040, 0x2080, 0x2082, 0x20c0, 0x20c1, 0x2100, 0x22b8, 0x22b9, + 0x2310, 0x2311, 0x231c, 0x231d, 0x244c, 0x2456, 0x244d, 0x2457, + 0x248c, 0x248d, 0x249e, 0x249f, 0x2500, 0x2502, 0x2504, 0x2bc0, + 0x2501, 0x2503, 0x2505, 0x2bc1, 0x2bc2, 0x2bc3, 0x2bc4, 0x2bc5, + 0x2bc6, 0x2bc7, 0x2580, 0x2582, 0x2584, 0x2bc8, 0x2581, 0x2583, + 0x2585, 0x2bc9, 0x2bca, 0x2bcb, 0x2bcc, 0x2bcd, 0x2bce, 0x2bcf, + 0x2600, 0x2602, 0x2601, 0x2603, 0x2680, 0x2682, 0x2681, 0x2683, + 0x26c2, 0x26c4, 0x26c6, 0x2c00, 0x26c3, 0x26c5, 0x26c7, 0x2c01, + 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x26ca, 0x26cc, + 0x26ce, 0x2c08, 0x26cb, 0x26cd, 0x26cf, 0x2c09, 0x2c0a, 0x2c0b, + 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x26d2, 0x26d4, 0x26d6, 0x26d3, + 0x26d5, 0x26d7, 0x26da, 0x26dc, 0x26de, 0x26db, 0x26dd, 0x26df, + 0x2700, 0x2702, 0x2701, 0x2703, 0x2780, 0x2782, 0x2781, 0x2783, + 0x2800, 0x2802, 0x2804, 0x2801, 0x2803, 0x2805, 0x2842, 0x2844, + 0x2846, 0x2849, 0x284b, 0x284d, 0x2c40, 0x284a, 0x284c, 0x284e, + 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2851, + 0x2853, 0x2855, 0x2c48, 0x2852, 0x2854, 0x2856, 0x2c49, 0x2c4a, + 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c82, 0x2e01, 0x3180, + 0x2c87, 0x2f01, 0x2f02, 0x2f03, 0x2e06, 0x3185, 0x3000, 0x3001, + 0x3002, 0x4640, 0x4641, 0x4680, 0x46c0, 0x46c2, 0x46c1, 0x4700, + 0x4740, 0x4780, 0x47c0, 0x47c2, 0x4900, 0x4940, 0x4980, 0x4982, + 0x4a00, 0x49c2, 0x4a03, 0x4a04, 0x4a40, 0x4a41, 0x4a80, 0x4a81, + 0x4ac0, 0x4ac1, 0x4bc0, 0x4bc1, 0x4b00, 0x4b01, 0x4b40, 0x4b41, + 0x4bc2, 0x4bc3, 0x4b80, 0x4b81, 0x4b82, 0x4b83, 0x4c00, 0x4c01, + 0x4c02, 0x4c03, 0x5600, 0x5440, 0x5442, 0x5444, 0x5446, 0x5448, + 0x544a, 0x544c, 0x544e, 0x5450, 0x5452, 0x5454, 0x5456, 0x5480, + 0x5482, 0x5484, 0x54c0, 0x54c1, 0x5500, 0x5501, 0x5540, 0x5541, + 0x5580, 0x5581, 0x55c0, 0x55c1, 0x5680, 0x58c0, 0x5700, 0x5702, + 0x5704, 0x5706, 0x5708, 0x570a, 0x570c, 0x570e, 0x5710, 0x5712, + 0x5714, 0x5716, 0x5740, 0x5742, 0x5744, 0x5780, 0x5781, 0x57c0, + 0x57c1, 0x5800, 0x5801, 0x5840, 0x5841, 0x5880, 0x5881, 0x5900, + 0x5901, 0x5902, 0x5903, 0x5940, 0x8ec0, 0x8f00, 0x8fc0, 0x8fc2, + 0x9000, 0x9040, 0x9041, 0x9080, 0x9081, 0x90c0, 0x90c2, 0x9100, + 0x9140, 0x9182, 0x9180, 0x9183, 0x91c1, 0x91c0, 0x91c3, 0x9200, + 0x9201, 0x9240, 0x9280, 0x9282, 0x9284, 0x9281, 0x9285, 0x9287, + 0x9286, 0x9283, 0x92c1, 0x92c0, 0x92c2, +}; + +typedef enum { + UNICODE_GC_Cn, + UNICODE_GC_Lu, + UNICODE_GC_Ll, + UNICODE_GC_Lt, + UNICODE_GC_Lm, + UNICODE_GC_Lo, + UNICODE_GC_Mn, + UNICODE_GC_Mc, + UNICODE_GC_Me, + UNICODE_GC_Nd, + UNICODE_GC_Nl, + UNICODE_GC_No, + UNICODE_GC_Sm, + UNICODE_GC_Sc, + UNICODE_GC_Sk, + UNICODE_GC_So, + UNICODE_GC_Pc, + UNICODE_GC_Pd, + UNICODE_GC_Ps, + UNICODE_GC_Pe, + UNICODE_GC_Pi, + UNICODE_GC_Pf, + UNICODE_GC_Po, + UNICODE_GC_Zs, + UNICODE_GC_Zl, + UNICODE_GC_Zp, + UNICODE_GC_Cc, + UNICODE_GC_Cf, + UNICODE_GC_Cs, + UNICODE_GC_Co, + UNICODE_GC_LC, + UNICODE_GC_L, + UNICODE_GC_M, + UNICODE_GC_N, + UNICODE_GC_S, + UNICODE_GC_P, + UNICODE_GC_Z, + UNICODE_GC_C, + UNICODE_GC_COUNT, +} UnicodeGCEnum; + +static const char unicode_gc_name_table[] = + "Cn,Unassigned" "\0" + "Lu,Uppercase_Letter" "\0" + "Ll,Lowercase_Letter" "\0" + "Lt,Titlecase_Letter" "\0" + "Lm,Modifier_Letter" "\0" + "Lo,Other_Letter" "\0" + "Mn,Nonspacing_Mark" "\0" + "Mc,Spacing_Mark" "\0" + "Me,Enclosing_Mark" "\0" + "Nd,Decimal_Number,digit" "\0" + "Nl,Letter_Number" "\0" + "No,Other_Number" "\0" + "Sm,Math_Symbol" "\0" + "Sc,Currency_Symbol" "\0" + "Sk,Modifier_Symbol" "\0" + "So,Other_Symbol" "\0" + "Pc,Connector_Punctuation" "\0" + "Pd,Dash_Punctuation" "\0" + "Ps,Open_Punctuation" "\0" + "Pe,Close_Punctuation" "\0" + "Pi,Initial_Punctuation" "\0" + "Pf,Final_Punctuation" "\0" + "Po,Other_Punctuation" "\0" + "Zs,Space_Separator" "\0" + "Zl,Line_Separator" "\0" + "Zp,Paragraph_Separator" "\0" + "Cc,Control,cntrl" "\0" + "Cf,Format" "\0" + "Cs,Surrogate" "\0" + "Co,Private_Use" "\0" + "LC,Cased_Letter" "\0" + "L,Letter" "\0" + "M,Mark,Combining_Mark" "\0" + "N,Number" "\0" + "S,Symbol" "\0" + "P,Punctuation,punct" "\0" + "Z,Separator" "\0" + "C,Other" "\0" +; + +static const uint8_t unicode_gc_table[4070] = { + 0xfa, 0x18, 0x17, 0x56, 0x0d, 0x56, 0x12, 0x13, + 0x16, 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, 0x36, + 0x4c, 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e, + 0x10, 0x0e, 0xe2, 0x12, 0x12, 0x0c, 0x13, 0x0c, + 0xfa, 0x19, 0x17, 0x16, 0x6d, 0x0f, 0x16, 0x0e, + 0x0f, 0x05, 0x14, 0x0c, 0x1b, 0x0f, 0x0e, 0x0f, + 0x0c, 0x2b, 0x0e, 0x02, 0x36, 0x0e, 0x0b, 0x05, + 0x15, 0x4b, 0x16, 0xe1, 0x0f, 0x0c, 0xc1, 0xe2, + 0x10, 0x0c, 0xe2, 0x00, 0xff, 0x30, 0x02, 0xff, + 0x08, 0x02, 0xff, 0x27, 0xbf, 0x22, 0x21, 0x02, + 0x5f, 0x5f, 0x21, 0x22, 0x61, 0x02, 0x21, 0x02, + 0x41, 0x42, 0x21, 0x02, 0x21, 0x02, 0x9f, 0x7f, + 0x02, 0x5f, 0x5f, 0x21, 0x02, 0x5f, 0x3f, 0x02, + 0x05, 0x3f, 0x22, 0x65, 0x01, 0x03, 0x02, 0x01, + 0x03, 0x02, 0x01, 0x03, 0x02, 0xff, 0x08, 0x02, + 0xff, 0x0a, 0x02, 0x01, 0x03, 0x02, 0x5f, 0x21, + 0x02, 0xff, 0x32, 0xa2, 0x21, 0x02, 0x21, 0x22, + 0x5f, 0x41, 0x02, 0xff, 0x00, 0xe2, 0x3c, 0x05, + 0xe2, 0x13, 0xe4, 0x0a, 0x6e, 0xe4, 0x04, 0xee, + 0x06, 0x84, 0xce, 0x04, 0x0e, 0x04, 0xee, 0x09, + 0xe6, 0x68, 0x7f, 0x04, 0x0e, 0x3f, 0x20, 0x04, + 0x42, 0x16, 0x01, 0x60, 0x2e, 0x01, 0x16, 0x41, + 0x00, 0x01, 0x00, 0x21, 0x02, 0xe1, 0x09, 0x00, + 0xe1, 0x01, 0xe2, 0x1b, 0x3f, 0x02, 0x41, 0x42, + 0xff, 0x10, 0x62, 0x3f, 0x0c, 0x5f, 0x3f, 0x02, + 0xe1, 0x2b, 0xe2, 0x28, 0xff, 0x1a, 0x0f, 0x86, + 0x28, 0xff, 0x2f, 0xff, 0x06, 0x02, 0xff, 0x58, + 0x00, 0xe1, 0x1e, 0x20, 0x04, 0xb6, 0xe2, 0x21, + 0x16, 0x11, 0x20, 0x2f, 0x0d, 0x00, 0xe6, 0x25, + 0x11, 0x06, 0x16, 0x26, 0x16, 0x26, 0x16, 0x06, + 0xe0, 0x00, 0xe5, 0x13, 0x60, 0x65, 0x36, 0xe0, + 0x03, 0xbb, 0x4c, 0x36, 0x0d, 0x36, 0x2f, 0xe6, + 0x03, 0x16, 0x1b, 0x56, 0xe5, 0x18, 0x04, 0xe5, + 0x02, 0xe6, 0x0d, 0xe9, 0x02, 0x76, 0x25, 0x06, + 0xe5, 0x5b, 0x16, 0x05, 0xc6, 0x1b, 0x0f, 0xa6, + 0x24, 0x26, 0x0f, 0x66, 0x25, 0xe9, 0x02, 0x45, + 0x2f, 0x05, 0xf6, 0x06, 0x00, 0x1b, 0x05, 0x06, + 0xe5, 0x16, 0xe6, 0x13, 0x20, 0xe5, 0x51, 0xe6, + 0x03, 0x05, 0xe0, 0x06, 0xe9, 0x02, 0xe5, 0x19, + 0xe6, 0x01, 0x24, 0x0f, 0x56, 0x04, 0x20, 0x06, + 0x2d, 0xe5, 0x0e, 0x66, 0x04, 0xe6, 0x01, 0x04, + 0x46, 0x04, 0x86, 0x20, 0xf6, 0x07, 0x00, 0xe5, + 0x11, 0x46, 0x20, 0x16, 0x00, 0xe5, 0x03, 0x80, + 0xe5, 0x10, 0x0e, 0xa5, 0x00, 0x3b, 0x80, 0xe6, + 0x01, 0xe5, 0x21, 0x04, 0xe6, 0x10, 0x1b, 0xe6, + 0x18, 0x07, 0xe5, 0x2e, 0x06, 0x07, 0x06, 0x05, + 0x47, 0xe6, 0x00, 0x67, 0x06, 0x27, 0x05, 0xc6, + 0xe5, 0x02, 0x26, 0x36, 0xe9, 0x02, 0x16, 0x04, + 0xe5, 0x07, 0x06, 0x27, 0x00, 0xe5, 0x00, 0x20, + 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x05, + 0x40, 0x65, 0x20, 0x06, 0x05, 0x47, 0x66, 0x20, + 0x27, 0x20, 0x27, 0x06, 0x05, 0xe0, 0x00, 0x07, + 0x60, 0x25, 0x00, 0x45, 0x26, 0x20, 0xe9, 0x02, + 0x25, 0x2d, 0xab, 0x0f, 0x0d, 0x05, 0x16, 0x06, + 0x20, 0x26, 0x07, 0x00, 0xa5, 0x60, 0x25, 0x20, + 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, 0x25, + 0x00, 0x25, 0x20, 0x06, 0x00, 0x47, 0x26, 0x60, + 0x26, 0x20, 0x46, 0x40, 0x06, 0xc0, 0x65, 0x00, + 0x05, 0xc0, 0xe9, 0x02, 0x26, 0x45, 0x06, 0x16, + 0xe0, 0x02, 0x26, 0x07, 0x00, 0xe5, 0x01, 0x00, + 0x45, 0x00, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, + 0x00, 0x85, 0x20, 0x06, 0x05, 0x47, 0x86, 0x00, + 0x26, 0x07, 0x00, 0x27, 0x06, 0x20, 0x05, 0xe0, + 0x07, 0x25, 0x26, 0x20, 0xe9, 0x02, 0x16, 0x0d, + 0xc0, 0x05, 0xa6, 0x00, 0x06, 0x27, 0x00, 0xe5, + 0x00, 0x20, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, + 0x00, 0x25, 0x00, 0x85, 0x20, 0x06, 0x05, 0x07, + 0x06, 0x07, 0x66, 0x20, 0x27, 0x20, 0x27, 0x06, + 0xc0, 0x26, 0x07, 0x60, 0x25, 0x00, 0x45, 0x26, + 0x20, 0xe9, 0x02, 0x0f, 0x05, 0xab, 0xe0, 0x02, + 0x06, 0x05, 0x00, 0xa5, 0x40, 0x45, 0x00, 0x65, + 0x40, 0x25, 0x00, 0x05, 0x00, 0x25, 0x40, 0x25, + 0x40, 0x45, 0x40, 0xe5, 0x04, 0x60, 0x27, 0x06, + 0x27, 0x40, 0x47, 0x00, 0x47, 0x06, 0x20, 0x05, + 0xa0, 0x07, 0xe0, 0x06, 0xe9, 0x02, 0x4b, 0xaf, + 0x0d, 0x0f, 0x80, 0x06, 0x47, 0x06, 0xe5, 0x00, + 0x00, 0x45, 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x08, + 0x20, 0x06, 0x05, 0x46, 0x67, 0x00, 0x46, 0x00, + 0x66, 0xc0, 0x26, 0x00, 0x45, 0x20, 0x05, 0x20, + 0x25, 0x26, 0x20, 0xe9, 0x02, 0xc0, 0x16, 0xcb, + 0x0f, 0x05, 0x06, 0x27, 0x16, 0xe5, 0x00, 0x00, + 0x45, 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x02, 0x00, + 0x85, 0x20, 0x06, 0x05, 0x07, 0x06, 0x87, 0x00, + 0x06, 0x27, 0x00, 0x27, 0x26, 0xc0, 0x27, 0xa0, + 0x25, 0x00, 0x25, 0x26, 0x20, 0xe9, 0x02, 0x00, + 0x25, 0x07, 0xe0, 0x04, 0x26, 0x27, 0xe5, 0x01, + 0x00, 0x45, 0x00, 0xe5, 0x21, 0x26, 0x05, 0x47, + 0x66, 0x00, 0x47, 0x00, 0x47, 0x06, 0x05, 0x0f, + 0x60, 0x45, 0x07, 0xcb, 0x45, 0x26, 0x20, 0xe9, + 0x02, 0xeb, 0x01, 0x0f, 0xa5, 0x00, 0x06, 0x27, + 0x00, 0xe5, 0x0a, 0x40, 0xe5, 0x10, 0x00, 0xe5, + 0x01, 0x00, 0x05, 0x20, 0xc5, 0x40, 0x06, 0x60, + 0x47, 0x46, 0x00, 0x06, 0x00, 0xe7, 0x00, 0xa0, + 0xe9, 0x02, 0x20, 0x27, 0x16, 0xe0, 0x04, 0xe5, + 0x28, 0x06, 0x25, 0xc6, 0x60, 0x0d, 0xa5, 0x04, + 0xe6, 0x00, 0x16, 0xe9, 0x02, 0x36, 0xe0, 0x1d, + 0x25, 0x00, 0x05, 0x00, 0x85, 0x00, 0xe5, 0x10, + 0x00, 0x05, 0x00, 0xe5, 0x02, 0x06, 0x25, 0xe6, + 0x01, 0x05, 0x20, 0x85, 0x00, 0x04, 0x00, 0xc6, + 0x00, 0xe9, 0x02, 0x20, 0x65, 0xe0, 0x18, 0x05, + 0x4f, 0xf6, 0x07, 0x0f, 0x16, 0x4f, 0x26, 0xaf, + 0xe9, 0x02, 0xeb, 0x02, 0x0f, 0x06, 0x0f, 0x06, + 0x0f, 0x06, 0x12, 0x13, 0x12, 0x13, 0x27, 0xe5, + 0x00, 0x00, 0xe5, 0x1c, 0x60, 0xe6, 0x06, 0x07, + 0x86, 0x16, 0x26, 0x85, 0xe6, 0x03, 0x00, 0xe6, + 0x1c, 0x00, 0xef, 0x00, 0x06, 0xaf, 0x00, 0x2f, + 0x96, 0x6f, 0x36, 0xe0, 0x1d, 0xe5, 0x23, 0x27, + 0x66, 0x07, 0xa6, 0x07, 0x26, 0x27, 0x26, 0x05, + 0xe9, 0x02, 0xb6, 0xa5, 0x27, 0x26, 0x65, 0x46, + 0x05, 0x47, 0x25, 0xc7, 0x45, 0x66, 0xe5, 0x05, + 0x06, 0x27, 0x26, 0xa7, 0x06, 0x05, 0x07, 0xe9, + 0x02, 0x47, 0x06, 0x2f, 0xe1, 0x1e, 0x00, 0x01, + 0x80, 0x01, 0x20, 0xe2, 0x23, 0x16, 0x04, 0x42, + 0xe5, 0x80, 0xc1, 0x00, 0x65, 0x20, 0xc5, 0x00, + 0x05, 0x00, 0x65, 0x20, 0xe5, 0x21, 0x00, 0x65, + 0x20, 0xe5, 0x19, 0x00, 0x65, 0x20, 0xc5, 0x00, + 0x05, 0x00, 0x65, 0x20, 0xe5, 0x07, 0x00, 0xe5, + 0x31, 0x00, 0x65, 0x20, 0xe5, 0x3b, 0x20, 0x46, + 0xf6, 0x01, 0xeb, 0x0c, 0x40, 0xe5, 0x08, 0xef, + 0x02, 0xa0, 0xe1, 0x4e, 0x20, 0xa2, 0x20, 0x11, + 0xe5, 0x81, 0xe4, 0x0f, 0x16, 0xe5, 0x09, 0x17, + 0xe5, 0x12, 0x12, 0x13, 0x40, 0xe5, 0x43, 0x56, + 0x4a, 0xe5, 0x00, 0xc0, 0xe5, 0x0a, 0x46, 0x07, + 0xe0, 0x01, 0xe5, 0x0b, 0x26, 0x07, 0x36, 0xe0, + 0x01, 0xe5, 0x0a, 0x26, 0xe0, 0x04, 0xe5, 0x05, + 0x00, 0x45, 0x00, 0x26, 0xe0, 0x04, 0xe5, 0x2c, + 0x26, 0x07, 0xc6, 0xe7, 0x00, 0x06, 0x27, 0xe6, + 0x03, 0x56, 0x04, 0x56, 0x0d, 0x05, 0x06, 0x20, + 0xe9, 0x02, 0xa0, 0xeb, 0x02, 0xa0, 0xb6, 0x11, + 0x76, 0x46, 0x1b, 0x06, 0xe9, 0x02, 0xa0, 0xe5, + 0x1b, 0x04, 0xe5, 0x2d, 0xc0, 0x85, 0x26, 0xe5, + 0x1a, 0x06, 0x05, 0x80, 0xe5, 0x3e, 0xe0, 0x02, + 0xe5, 0x17, 0x00, 0x46, 0x67, 0x26, 0x47, 0x60, + 0x27, 0x06, 0xa7, 0x46, 0x60, 0x0f, 0x40, 0x36, + 0xe9, 0x02, 0xe5, 0x16, 0x20, 0x85, 0xe0, 0x03, + 0xe5, 0x24, 0x60, 0xe5, 0x12, 0xa0, 0xe9, 0x02, + 0x0b, 0x40, 0xef, 0x1a, 0xe5, 0x0f, 0x26, 0x27, + 0x06, 0x20, 0x36, 0xe5, 0x2d, 0x07, 0x06, 0x07, + 0xc6, 0x00, 0x06, 0x07, 0x06, 0x27, 0xe6, 0x00, + 0xa7, 0xe6, 0x02, 0x20, 0x06, 0xe9, 0x02, 0xa0, + 0xe9, 0x02, 0xa0, 0xd6, 0x04, 0xb6, 0x20, 0xe6, + 0x06, 0x08, 0xe6, 0x08, 0xe0, 0x29, 0x66, 0x07, + 0xe5, 0x27, 0x06, 0x07, 0x86, 0x07, 0x06, 0x87, + 0x06, 0x27, 0xe5, 0x00, 0x00, 0x36, 0xe9, 0x02, + 0xd6, 0xef, 0x02, 0xe6, 0x01, 0xef, 0x01, 0x56, + 0x26, 0x07, 0xe5, 0x16, 0x07, 0x66, 0x27, 0x26, + 0x07, 0x46, 0x25, 0xe9, 0x02, 0xe5, 0x24, 0x06, + 0x07, 0x26, 0x47, 0x06, 0x07, 0x46, 0x27, 0xe0, + 0x00, 0x76, 0xe5, 0x1c, 0xe7, 0x00, 0xe6, 0x00, + 0x27, 0x26, 0x40, 0x96, 0xe9, 0x02, 0x40, 0x45, + 0xe9, 0x02, 0xe5, 0x16, 0xa4, 0x36, 0xe2, 0x01, + 0x3f, 0x80, 0xe1, 0x23, 0x20, 0x41, 0xf6, 0x00, + 0xe0, 0x00, 0x46, 0x16, 0xe6, 0x05, 0x07, 0xc6, + 0x65, 0x06, 0xa5, 0x06, 0x25, 0x07, 0x26, 0x05, + 0x80, 0xe2, 0x24, 0xe4, 0x37, 0xe2, 0x05, 0x04, + 0xe2, 0x1a, 0xe4, 0x1d, 0xe6, 0x38, 0xff, 0x80, + 0x0e, 0xe2, 0x00, 0xff, 0x5a, 0xe2, 0x00, 0xe1, + 0x00, 0xa2, 0x20, 0xa1, 0x20, 0xe2, 0x00, 0xe1, + 0x00, 0xe2, 0x00, 0xe1, 0x00, 0xa2, 0x20, 0xa1, + 0x20, 0xe2, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x3f, 0xc2, 0xe1, 0x00, 0xe2, 0x06, + 0x20, 0xe2, 0x00, 0xe3, 0x00, 0xe2, 0x00, 0xe3, + 0x00, 0xe2, 0x00, 0xe3, 0x00, 0x82, 0x00, 0x22, + 0x61, 0x03, 0x0e, 0x02, 0x4e, 0x42, 0x00, 0x22, + 0x61, 0x03, 0x4e, 0x62, 0x20, 0x22, 0x61, 0x00, + 0x4e, 0xe2, 0x00, 0x81, 0x4e, 0x20, 0x42, 0x00, + 0x22, 0x61, 0x03, 0x2e, 0x00, 0xf7, 0x03, 0x9b, + 0xb1, 0x36, 0x14, 0x15, 0x12, 0x34, 0x15, 0x12, + 0x14, 0xf6, 0x00, 0x18, 0x19, 0x9b, 0x17, 0xf6, + 0x01, 0x14, 0x15, 0x76, 0x30, 0x56, 0x0c, 0x12, + 0x13, 0xf6, 0x03, 0x0c, 0x16, 0x10, 0xf6, 0x02, + 0x17, 0x9b, 0x00, 0xfb, 0x02, 0x0b, 0x04, 0x20, + 0xab, 0x4c, 0x12, 0x13, 0x04, 0xeb, 0x02, 0x4c, + 0x12, 0x13, 0x00, 0xe4, 0x05, 0x40, 0xed, 0x19, + 0xe0, 0x07, 0xe6, 0x05, 0x68, 0x06, 0x48, 0xe6, + 0x04, 0xe0, 0x07, 0x2f, 0x01, 0x6f, 0x01, 0x2f, + 0x02, 0x41, 0x22, 0x41, 0x02, 0x0f, 0x01, 0x2f, + 0x0c, 0x81, 0xaf, 0x01, 0x0f, 0x01, 0x0f, 0x01, + 0x0f, 0x61, 0x0f, 0x02, 0x61, 0x02, 0x65, 0x02, + 0x2f, 0x22, 0x21, 0x8c, 0x3f, 0x42, 0x0f, 0x0c, + 0x2f, 0x02, 0x0f, 0xeb, 0x08, 0xea, 0x1b, 0x3f, + 0x6a, 0x0b, 0x2f, 0x60, 0x8c, 0x8f, 0x2c, 0x6f, + 0x0c, 0x2f, 0x0c, 0x2f, 0x0c, 0xcf, 0x0c, 0xef, + 0x17, 0x2c, 0x2f, 0x0c, 0x0f, 0x0c, 0xef, 0x17, + 0xec, 0x80, 0x84, 0xef, 0x00, 0x12, 0x13, 0x12, + 0x13, 0xef, 0x0c, 0x2c, 0xcf, 0x12, 0x13, 0xef, + 0x49, 0x0c, 0xef, 0x16, 0xec, 0x11, 0xef, 0x20, + 0xac, 0xef, 0x40, 0xe0, 0x0e, 0xef, 0x03, 0xe0, + 0x0d, 0xeb, 0x34, 0xef, 0x46, 0xeb, 0x0e, 0xef, + 0x80, 0x2f, 0x0c, 0xef, 0x01, 0x0c, 0xef, 0x2e, + 0xec, 0x00, 0xef, 0x67, 0x0c, 0xef, 0x80, 0x70, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0xeb, 0x16, + 0xef, 0x24, 0x8c, 0x12, 0x13, 0xec, 0x17, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0xec, 0x08, 0xef, 0x80, 0x78, 0xec, 0x7b, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0xec, 0x37, + 0x12, 0x13, 0x12, 0x13, 0xec, 0x18, 0x12, 0x13, + 0xec, 0x80, 0x7a, 0xef, 0x28, 0xec, 0x0d, 0x2f, + 0xac, 0xef, 0x1f, 0x20, 0xef, 0x18, 0x00, 0xef, + 0x61, 0xe1, 0x28, 0xe2, 0x28, 0x5f, 0x21, 0x22, + 0xdf, 0x41, 0x02, 0x3f, 0x02, 0x3f, 0x82, 0x24, + 0x41, 0x02, 0xff, 0x5a, 0x02, 0xaf, 0x7f, 0x46, + 0x3f, 0x80, 0x76, 0x0b, 0x36, 0xe2, 0x1e, 0x00, + 0x02, 0x80, 0x02, 0x20, 0xe5, 0x30, 0xc0, 0x04, + 0x16, 0xe0, 0x06, 0x06, 0xe5, 0x0f, 0xe0, 0x01, + 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, + 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, + 0xe6, 0x18, 0x36, 0x14, 0x15, 0x14, 0x15, 0x56, + 0x14, 0x15, 0x16, 0x14, 0x15, 0xf6, 0x01, 0x11, + 0x36, 0x11, 0x16, 0x14, 0x15, 0x36, 0x14, 0x15, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x96, 0x04, 0xf6, 0x02, 0x31, 0x76, 0x11, 0x16, + 0x12, 0xf6, 0x05, 0x2f, 0x56, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x11, 0xe0, 0x1a, + 0xef, 0x12, 0x00, 0xef, 0x51, 0xe0, 0x04, 0xef, + 0x80, 0x4e, 0xe0, 0x12, 0xef, 0x08, 0x17, 0x56, + 0x0f, 0x04, 0x05, 0x0a, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x2f, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x11, + 0x12, 0x33, 0x0f, 0xea, 0x01, 0x66, 0x27, 0x11, + 0x84, 0x2f, 0x4a, 0x04, 0x05, 0x16, 0x2f, 0x00, + 0xe5, 0x4e, 0x20, 0x26, 0x2e, 0x24, 0x05, 0x11, + 0xe5, 0x52, 0x16, 0x44, 0x05, 0x80, 0xe5, 0x23, + 0x00, 0xe5, 0x56, 0x00, 0x2f, 0x6b, 0xef, 0x02, + 0xe5, 0x18, 0xef, 0x1e, 0xe0, 0x01, 0x0f, 0xe5, + 0x08, 0xef, 0x17, 0x00, 0xeb, 0x02, 0xef, 0x16, + 0xeb, 0x00, 0x0f, 0xeb, 0x07, 0xef, 0x18, 0xeb, + 0x02, 0xef, 0x1f, 0xeb, 0x07, 0xef, 0x80, 0xb8, + 0xe5, 0x99, 0x38, 0xef, 0x38, 0xe5, 0xc0, 0x11, + 0x8d, 0x04, 0xe5, 0x83, 0xef, 0x40, 0xef, 0x2f, + 0xe0, 0x01, 0xe5, 0x20, 0xa4, 0x36, 0xe5, 0x80, + 0x84, 0x04, 0x56, 0xe5, 0x08, 0xe9, 0x02, 0x25, + 0xe0, 0x0c, 0xff, 0x26, 0x05, 0x06, 0x48, 0x16, + 0xe6, 0x02, 0x16, 0x04, 0xff, 0x14, 0x24, 0x26, + 0xe5, 0x3e, 0xea, 0x02, 0x26, 0xb6, 0xe0, 0x00, + 0xee, 0x0f, 0xe4, 0x01, 0x2e, 0xff, 0x06, 0x22, + 0xff, 0x36, 0x04, 0xe2, 0x00, 0x9f, 0xff, 0x02, + 0x04, 0x2e, 0x7f, 0x05, 0x7f, 0x22, 0xff, 0x0d, + 0x61, 0x02, 0x81, 0x02, 0xff, 0x07, 0x41, 0x02, + 0x5f, 0x3f, 0x20, 0x3f, 0x00, 0x02, 0x00, 0x02, + 0xdf, 0xe0, 0x0d, 0x44, 0x3f, 0x05, 0x24, 0x02, + 0xc5, 0x06, 0x45, 0x06, 0x65, 0x06, 0xe5, 0x0f, + 0x27, 0x26, 0x07, 0x6f, 0x06, 0x40, 0xab, 0x2f, + 0x0d, 0x0f, 0xa0, 0xe5, 0x2c, 0x76, 0xe0, 0x00, + 0x27, 0xe5, 0x2a, 0xe7, 0x08, 0x26, 0xe0, 0x00, + 0x36, 0xe9, 0x02, 0xa0, 0xe6, 0x0a, 0xa5, 0x56, + 0x05, 0x16, 0x25, 0x06, 0xe9, 0x02, 0xe5, 0x14, + 0xe6, 0x00, 0x36, 0xe5, 0x0f, 0xe6, 0x03, 0x27, + 0xe0, 0x03, 0x16, 0xe5, 0x15, 0x40, 0x46, 0x07, + 0xe5, 0x27, 0x06, 0x27, 0x66, 0x27, 0x26, 0x47, + 0xf6, 0x05, 0x00, 0x04, 0xe9, 0x02, 0x60, 0x36, + 0x85, 0x06, 0x04, 0xe5, 0x01, 0xe9, 0x02, 0x85, + 0x00, 0xe5, 0x21, 0xa6, 0x27, 0x26, 0x27, 0x26, + 0xe0, 0x01, 0x45, 0x06, 0xe5, 0x00, 0x06, 0x07, + 0x20, 0xe9, 0x02, 0x20, 0x76, 0xe5, 0x08, 0x04, + 0xa5, 0x4f, 0x05, 0x07, 0x06, 0x07, 0xe5, 0x2a, + 0x06, 0x05, 0x46, 0x25, 0x26, 0x85, 0x26, 0x05, + 0x06, 0x05, 0xe0, 0x10, 0x25, 0x04, 0x36, 0xe5, + 0x03, 0x07, 0x26, 0x27, 0x36, 0x05, 0x24, 0x07, + 0x06, 0xe0, 0x02, 0xa5, 0x20, 0xa5, 0x20, 0xa5, + 0xe0, 0x01, 0xc5, 0x00, 0xc5, 0x00, 0xe2, 0x23, + 0x0e, 0x64, 0xe2, 0x01, 0x04, 0x2e, 0x60, 0xe2, + 0x48, 0xe5, 0x1b, 0x27, 0x06, 0x27, 0x06, 0x27, + 0x16, 0x07, 0x06, 0x20, 0xe9, 0x02, 0xa0, 0xe5, + 0xab, 0x1c, 0xe0, 0x04, 0xe5, 0x0f, 0x60, 0xe5, + 0x29, 0x60, 0xfc, 0x87, 0x78, 0xfd, 0x98, 0x78, + 0xe5, 0x80, 0xe6, 0x20, 0xe5, 0x62, 0xe0, 0x1e, + 0xc2, 0xe0, 0x04, 0x82, 0x80, 0x05, 0x06, 0xe5, + 0x02, 0x0c, 0xe5, 0x05, 0x00, 0x85, 0x00, 0x05, + 0x00, 0x25, 0x00, 0x25, 0x00, 0xe5, 0x64, 0xee, + 0x09, 0xe0, 0x08, 0xe5, 0x80, 0xe3, 0x13, 0x12, + 0xef, 0x08, 0xe5, 0x38, 0x20, 0xe5, 0x2e, 0xc0, + 0x0f, 0xe0, 0x18, 0xe5, 0x04, 0x0d, 0x4f, 0xe6, + 0x08, 0xd6, 0x12, 0x13, 0x16, 0xa0, 0xe6, 0x08, + 0x16, 0x31, 0x30, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x36, 0x12, 0x13, 0x76, 0x50, + 0x56, 0x00, 0x76, 0x11, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x56, 0x0c, 0x11, 0x4c, 0x00, 0x16, + 0x0d, 0x36, 0x60, 0x85, 0x00, 0xe5, 0x7f, 0x20, + 0x1b, 0x00, 0x56, 0x0d, 0x56, 0x12, 0x13, 0x16, + 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, 0x36, 0x4c, + 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e, 0x10, + 0x0e, 0xe2, 0x12, 0x12, 0x0c, 0x13, 0x0c, 0x12, + 0x13, 0x16, 0x12, 0x13, 0x36, 0xe5, 0x02, 0x04, + 0xe5, 0x25, 0x24, 0xe5, 0x17, 0x40, 0xa5, 0x20, + 0xa5, 0x20, 0xa5, 0x20, 0x45, 0x40, 0x2d, 0x0c, + 0x0e, 0x0f, 0x2d, 0x00, 0x0f, 0x6c, 0x2f, 0xe0, + 0x02, 0x5b, 0x2f, 0x20, 0xe5, 0x04, 0x00, 0xe5, + 0x12, 0x00, 0xe5, 0x0b, 0x00, 0x25, 0x00, 0xe5, + 0x07, 0x20, 0xe5, 0x06, 0xe0, 0x1a, 0xe5, 0x73, + 0x80, 0x56, 0x60, 0xeb, 0x25, 0x40, 0xef, 0x01, + 0xea, 0x2d, 0x6b, 0xef, 0x09, 0x2b, 0x4f, 0x00, + 0xef, 0x05, 0x40, 0x0f, 0xe0, 0x27, 0xef, 0x25, + 0x06, 0xe0, 0x7a, 0xe5, 0x15, 0x40, 0xe5, 0x29, + 0xe0, 0x07, 0x06, 0xeb, 0x13, 0x60, 0xe5, 0x18, + 0x6b, 0xe0, 0x01, 0xe5, 0x0c, 0x0a, 0xe5, 0x00, + 0x0a, 0x80, 0xe5, 0x1e, 0x86, 0x80, 0xe5, 0x16, + 0x00, 0x16, 0xe5, 0x1c, 0x60, 0xe5, 0x00, 0x16, + 0x8a, 0xe0, 0x22, 0xe1, 0x20, 0xe2, 0x20, 0xe5, + 0x46, 0x20, 0xe9, 0x02, 0xa0, 0xe1, 0x1c, 0x60, + 0xe2, 0x1c, 0x60, 0xe5, 0x20, 0xe0, 0x00, 0xe5, + 0x2c, 0xe0, 0x03, 0x16, 0xe1, 0x03, 0x00, 0xe1, + 0x07, 0x00, 0xc1, 0x00, 0x21, 0x00, 0xe2, 0x03, + 0x00, 0xe2, 0x07, 0x00, 0xc2, 0x00, 0x22, 0x40, + 0xe5, 0x2c, 0xe0, 0x04, 0xe5, 0x80, 0xaf, 0xe0, + 0x01, 0xe5, 0x0e, 0xe0, 0x02, 0xe5, 0x00, 0xe0, + 0x10, 0xa4, 0x00, 0xe4, 0x22, 0x00, 0xe4, 0x01, + 0xe0, 0x3d, 0xa5, 0x20, 0x05, 0x00, 0xe5, 0x24, + 0x00, 0x25, 0x40, 0x05, 0x20, 0xe5, 0x0f, 0x00, + 0x16, 0xeb, 0x00, 0xe5, 0x0f, 0x2f, 0xcb, 0xe5, + 0x17, 0xe0, 0x00, 0xeb, 0x01, 0xe0, 0x28, 0xe5, + 0x0b, 0x00, 0x25, 0x80, 0x8b, 0xe5, 0x0e, 0xab, + 0x40, 0x16, 0xe5, 0x12, 0x80, 0x16, 0xe0, 0x38, + 0xe5, 0x30, 0x60, 0x2b, 0x25, 0xeb, 0x08, 0x20, + 0xeb, 0x26, 0x05, 0x46, 0x00, 0x26, 0x80, 0x66, + 0x65, 0x00, 0x45, 0x00, 0xe5, 0x15, 0x20, 0x46, + 0x60, 0x06, 0xeb, 0x01, 0xc0, 0xf6, 0x01, 0xc0, + 0xe5, 0x15, 0x2b, 0x16, 0xe5, 0x15, 0x4b, 0xe0, + 0x18, 0xe5, 0x00, 0x0f, 0xe5, 0x14, 0x26, 0x60, + 0x8b, 0xd6, 0xe0, 0x01, 0xe5, 0x2e, 0x40, 0xd6, + 0xe5, 0x0e, 0x20, 0xeb, 0x00, 0xe5, 0x0b, 0x80, + 0xeb, 0x00, 0xe5, 0x0a, 0xc0, 0x76, 0xe0, 0x04, + 0xcb, 0xe0, 0x48, 0xe5, 0x41, 0xe0, 0x2f, 0xe1, + 0x2b, 0xe0, 0x05, 0xe2, 0x2b, 0xc0, 0xab, 0xe5, + 0x1c, 0x66, 0xe0, 0x00, 0xe9, 0x02, 0xa0, 0xe9, + 0x02, 0x65, 0x04, 0x05, 0xe1, 0x0e, 0x40, 0x86, + 0x11, 0x04, 0xe2, 0x0e, 0xe0, 0x00, 0x2c, 0xe0, + 0x80, 0x48, 0xeb, 0x17, 0x00, 0xe5, 0x22, 0x00, + 0x26, 0x11, 0x20, 0x25, 0xe0, 0x08, 0x45, 0xe0, + 0x2f, 0x66, 0xe5, 0x15, 0xeb, 0x02, 0x05, 0xe0, + 0x00, 0xe5, 0x0e, 0xe6, 0x03, 0x6b, 0x96, 0xe0, + 0x0e, 0xe5, 0x0a, 0x66, 0x76, 0xe0, 0x1e, 0xe5, + 0x0d, 0xcb, 0xe0, 0x0c, 0xe5, 0x0f, 0xe0, 0x01, + 0x07, 0x06, 0x07, 0xe5, 0x2d, 0xe6, 0x07, 0xd6, + 0x60, 0xeb, 0x0c, 0xe9, 0x02, 0x06, 0x25, 0x26, + 0x05, 0xe0, 0x01, 0x46, 0x07, 0xe5, 0x25, 0x47, + 0x66, 0x27, 0x26, 0x36, 0x1b, 0x76, 0x06, 0xe0, + 0x02, 0x1b, 0x20, 0xe5, 0x11, 0xc0, 0xe9, 0x02, + 0xa0, 0x46, 0xe5, 0x1c, 0x86, 0x07, 0xe6, 0x00, + 0x00, 0xe9, 0x02, 0x76, 0x05, 0x27, 0x05, 0xe0, + 0x00, 0xe5, 0x1b, 0x06, 0x36, 0x05, 0xe0, 0x01, + 0x26, 0x07, 0xe5, 0x28, 0x47, 0xe6, 0x01, 0x27, + 0x65, 0x76, 0x66, 0x16, 0x07, 0x06, 0xe9, 0x02, + 0x05, 0x16, 0x05, 0x56, 0x00, 0xeb, 0x0c, 0xe0, + 0x03, 0xe5, 0x0a, 0x00, 0xe5, 0x11, 0x47, 0x46, + 0x27, 0x06, 0x07, 0x26, 0xb6, 0x06, 0x25, 0x06, + 0xe0, 0x36, 0xc5, 0x00, 0x05, 0x00, 0x65, 0x00, + 0xe5, 0x07, 0x00, 0xe5, 0x02, 0x16, 0xa0, 0xe5, + 0x27, 0x06, 0x47, 0xe6, 0x00, 0x80, 0xe9, 0x02, + 0xa0, 0x26, 0x27, 0x00, 0xe5, 0x00, 0x20, 0x25, + 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, + 0x85, 0x00, 0x26, 0x05, 0x27, 0x06, 0x67, 0x20, + 0x27, 0x20, 0x47, 0x20, 0x05, 0xa0, 0x07, 0x80, + 0x85, 0x27, 0x20, 0xc6, 0x40, 0x86, 0xe0, 0x03, + 0xe5, 0x02, 0x00, 0x05, 0x20, 0x05, 0x00, 0xe5, + 0x1e, 0x00, 0x05, 0x47, 0xa6, 0x00, 0x07, 0x20, + 0x07, 0x00, 0x67, 0x00, 0x27, 0x06, 0x07, 0x06, + 0x05, 0x06, 0x05, 0x36, 0x00, 0x36, 0xe0, 0x00, + 0x26, 0xe0, 0x15, 0xe5, 0x2d, 0x47, 0xe6, 0x00, + 0x27, 0x46, 0x07, 0x06, 0x65, 0x96, 0xe9, 0x02, + 0x36, 0x00, 0x16, 0x06, 0x45, 0xe0, 0x16, 0xe5, + 0x28, 0x47, 0xa6, 0x07, 0x06, 0x67, 0x26, 0x07, + 0x26, 0x25, 0x16, 0x05, 0xe0, 0x00, 0xe9, 0x02, + 0xe0, 0x80, 0x1e, 0xe5, 0x27, 0x47, 0x66, 0x20, + 0x67, 0x26, 0x07, 0x26, 0xf6, 0x0f, 0x65, 0x26, + 0xe0, 0x1a, 0xe5, 0x28, 0x47, 0xe6, 0x00, 0x27, + 0x06, 0x07, 0x26, 0x56, 0x05, 0xe0, 0x03, 0xe9, + 0x02, 0xa0, 0xf6, 0x05, 0xe0, 0x0b, 0xe5, 0x23, + 0x06, 0x07, 0x06, 0x27, 0xa6, 0x07, 0x06, 0x05, + 0x16, 0xa0, 0xe9, 0x02, 0xa0, 0xe9, 0x0c, 0xe0, + 0x14, 0xe5, 0x13, 0x20, 0x06, 0x07, 0x06, 0x27, + 0x66, 0x07, 0x86, 0x60, 0xe9, 0x02, 0x2b, 0x56, + 0x0f, 0xc5, 0xe0, 0x80, 0x31, 0xe5, 0x24, 0x47, + 0xe6, 0x01, 0x07, 0x26, 0x16, 0xe0, 0x5c, 0xe1, + 0x18, 0xe2, 0x18, 0xe9, 0x02, 0xeb, 0x01, 0xe0, + 0x04, 0xe5, 0x00, 0x20, 0x05, 0x20, 0xe5, 0x00, + 0x00, 0x25, 0x00, 0xe5, 0x10, 0xa7, 0x00, 0x27, + 0x20, 0x26, 0x07, 0x06, 0x05, 0x07, 0x05, 0x07, + 0x06, 0x56, 0xe0, 0x01, 0xe9, 0x02, 0xe0, 0x3e, + 0xe5, 0x00, 0x20, 0xe5, 0x1f, 0x47, 0x66, 0x20, + 0x26, 0x67, 0x06, 0x05, 0x16, 0x05, 0x07, 0xe0, + 0x13, 0x05, 0xe6, 0x02, 0xe5, 0x20, 0xa6, 0x07, + 0x05, 0x66, 0xf6, 0x00, 0x06, 0xe0, 0x00, 0x05, + 0xa6, 0x27, 0x46, 0xe5, 0x26, 0xe6, 0x05, 0x07, + 0x26, 0x56, 0x05, 0x96, 0xe0, 0x05, 0xe5, 0x41, + 0xc0, 0xf6, 0x02, 0xe0, 0x80, 0x2e, 0xe5, 0x19, + 0x16, 0xe0, 0x06, 0xe9, 0x02, 0xa0, 0xe5, 0x01, + 0x00, 0xe5, 0x1d, 0x07, 0xc6, 0x00, 0xa6, 0x07, + 0x06, 0x05, 0x96, 0xe0, 0x02, 0xe9, 0x02, 0xeb, + 0x0b, 0x40, 0x36, 0xe5, 0x16, 0x20, 0xe6, 0x0e, + 0x00, 0x07, 0xc6, 0x07, 0x26, 0x07, 0x26, 0xe0, + 0x41, 0xc5, 0x00, 0x25, 0x00, 0xe5, 0x1e, 0xa6, + 0x40, 0x06, 0x00, 0x26, 0x00, 0xc6, 0x05, 0x06, + 0xe0, 0x00, 0xe9, 0x02, 0xa0, 0xa5, 0x00, 0x25, + 0x00, 0xe5, 0x18, 0x87, 0x00, 0x26, 0x00, 0x27, + 0x06, 0x07, 0x06, 0x05, 0xc0, 0xe9, 0x02, 0xe0, + 0x80, 0xae, 0xe5, 0x0b, 0x26, 0x27, 0x36, 0xc0, + 0x26, 0x05, 0x07, 0xe5, 0x05, 0x00, 0xe5, 0x1a, + 0x27, 0x86, 0x40, 0x27, 0x06, 0x07, 0x06, 0xf6, + 0x05, 0xe9, 0x02, 0x06, 0xe0, 0x4d, 0x05, 0xe0, + 0x07, 0xeb, 0x0d, 0xef, 0x00, 0x6d, 0xef, 0x09, + 0xe0, 0x05, 0x16, 0xe5, 0x83, 0x12, 0xe0, 0x5e, + 0xea, 0x67, 0x00, 0x96, 0xe0, 0x03, 0xe5, 0x80, + 0x3c, 0xe0, 0x89, 0xc4, 0xe5, 0x59, 0x36, 0xe0, + 0x05, 0xe5, 0x83, 0xa8, 0xfb, 0x08, 0x06, 0xa5, + 0xe6, 0x07, 0xe0, 0x02, 0xe5, 0x8f, 0x13, 0x80, + 0xe5, 0x81, 0xbf, 0xe0, 0x9a, 0x31, 0xe5, 0x16, + 0xe6, 0x04, 0x47, 0x46, 0xe9, 0x02, 0xe0, 0x86, + 0x3e, 0xe5, 0x81, 0xb1, 0xc0, 0xe5, 0x17, 0x00, + 0xe9, 0x02, 0x60, 0x36, 0xe5, 0x47, 0x00, 0xe9, + 0x02, 0xa0, 0xe5, 0x16, 0x20, 0x86, 0x16, 0xe0, + 0x02, 0xe5, 0x28, 0xc6, 0x96, 0x6f, 0x64, 0x16, + 0x0f, 0xe0, 0x02, 0xe9, 0x02, 0x00, 0xcb, 0x00, + 0xe5, 0x0d, 0x80, 0xe5, 0x0b, 0xe0, 0x81, 0x28, + 0x44, 0xe5, 0x20, 0x24, 0x56, 0xe9, 0x02, 0xe0, + 0x80, 0x3e, 0xe1, 0x18, 0xe2, 0x18, 0xeb, 0x0f, + 0x76, 0xe0, 0x5d, 0xe5, 0x43, 0x60, 0x06, 0x05, + 0xe7, 0x2f, 0xc0, 0x66, 0xe4, 0x05, 0xe0, 0x38, + 0x24, 0x16, 0x04, 0x06, 0xe0, 0x03, 0x27, 0xe0, + 0x06, 0xe5, 0x97, 0x70, 0xe0, 0x00, 0xe5, 0x84, + 0x4e, 0xe0, 0x21, 0xe5, 0x02, 0xe0, 0xa2, 0x5f, + 0x64, 0x00, 0xc4, 0x00, 0x24, 0x00, 0xe5, 0x80, + 0x9b, 0xe0, 0x07, 0x05, 0xe0, 0x15, 0x45, 0x20, + 0x05, 0xe0, 0x06, 0x65, 0xe0, 0x00, 0xe5, 0x81, + 0x04, 0xe0, 0x88, 0x7c, 0xe5, 0x63, 0x80, 0xe5, + 0x05, 0x40, 0xe5, 0x01, 0xc0, 0xe5, 0x02, 0x20, + 0x0f, 0x26, 0x16, 0x7b, 0xe0, 0x8e, 0xd4, 0xef, + 0x80, 0x68, 0xe9, 0x02, 0xa0, 0xef, 0x81, 0x2c, + 0xe0, 0x44, 0xe6, 0x26, 0x20, 0xe6, 0x0f, 0xe0, + 0x01, 0xef, 0x6c, 0xe0, 0x34, 0xef, 0x80, 0x6e, + 0xe0, 0x02, 0xef, 0x1f, 0x20, 0xef, 0x34, 0x27, + 0x46, 0x4f, 0xa7, 0xfb, 0x00, 0xe6, 0x00, 0x2f, + 0xc6, 0xef, 0x16, 0x66, 0xef, 0x35, 0xe0, 0x0d, + 0xef, 0x3a, 0x46, 0x0f, 0xe0, 0x72, 0xeb, 0x0c, + 0xe0, 0x04, 0xeb, 0x0c, 0xe0, 0x04, 0xef, 0x4f, + 0xe0, 0x01, 0xeb, 0x11, 0xe0, 0x7f, 0xe1, 0x12, + 0xe2, 0x12, 0xe1, 0x12, 0xc2, 0x00, 0xe2, 0x0a, + 0xe1, 0x12, 0xe2, 0x12, 0x01, 0x00, 0x21, 0x20, + 0x01, 0x20, 0x21, 0x20, 0x61, 0x00, 0xe1, 0x00, + 0x62, 0x00, 0x02, 0x00, 0xc2, 0x00, 0xe2, 0x03, + 0xe1, 0x12, 0xe2, 0x12, 0x21, 0x00, 0x61, 0x20, + 0xe1, 0x00, 0x00, 0xc1, 0x00, 0xe2, 0x12, 0x21, + 0x00, 0x61, 0x00, 0x81, 0x00, 0x01, 0x40, 0xc1, + 0x00, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, + 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, + 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, + 0x12, 0xe2, 0x14, 0x20, 0xe1, 0x11, 0x0c, 0xe2, + 0x11, 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, + 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, + 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, + 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0x3f, + 0x20, 0xe9, 0x2a, 0xef, 0x81, 0x78, 0xe6, 0x2f, + 0x6f, 0xe6, 0x2a, 0xef, 0x00, 0x06, 0xef, 0x06, + 0x06, 0x2f, 0x96, 0xe0, 0x07, 0x86, 0x00, 0xe6, + 0x07, 0xe0, 0x83, 0xc8, 0xe2, 0x02, 0x05, 0xe2, + 0x0c, 0xa0, 0xa2, 0xe0, 0x80, 0x4d, 0xc6, 0x00, + 0xe6, 0x09, 0x20, 0xc6, 0x00, 0x26, 0x00, 0x86, + 0x80, 0xe4, 0x36, 0xe0, 0x19, 0x06, 0xe0, 0x68, + 0xe5, 0x25, 0x40, 0xc6, 0xc4, 0x20, 0xe9, 0x02, + 0x60, 0x05, 0x0f, 0xe0, 0x80, 0xb8, 0xe5, 0x16, + 0x06, 0xe0, 0x09, 0xe5, 0x24, 0x66, 0xe9, 0x02, + 0x80, 0x0d, 0xe0, 0x81, 0x48, 0xe5, 0x13, 0x04, + 0x66, 0xe9, 0x02, 0xe0, 0x80, 0x4e, 0xe5, 0x16, + 0x26, 0x05, 0xe9, 0x02, 0x60, 0x16, 0xe0, 0x81, + 0x58, 0xc5, 0x00, 0x65, 0x00, 0x25, 0x00, 0xe5, + 0x07, 0x00, 0xe5, 0x80, 0x3d, 0x20, 0xeb, 0x01, + 0xc6, 0xe0, 0x21, 0xe1, 0x1a, 0xe2, 0x1a, 0xc6, + 0x04, 0x60, 0xe9, 0x02, 0x60, 0x36, 0xe0, 0x82, + 0x89, 0xeb, 0x33, 0x0f, 0x4b, 0x0d, 0x6b, 0xe0, + 0x44, 0xeb, 0x25, 0x0f, 0xeb, 0x07, 0xe0, 0x80, + 0x3a, 0x65, 0x00, 0xe5, 0x13, 0x00, 0x25, 0x00, + 0x05, 0x20, 0x05, 0x00, 0xe5, 0x02, 0x00, 0x65, + 0x00, 0x05, 0x00, 0x05, 0xa0, 0x05, 0x60, 0x05, + 0x00, 0x05, 0x00, 0x05, 0x00, 0x45, 0x00, 0x25, + 0x00, 0x05, 0x20, 0x05, 0x00, 0x05, 0x00, 0x05, + 0x00, 0x05, 0x00, 0x05, 0x00, 0x25, 0x00, 0x05, + 0x20, 0x65, 0x00, 0xc5, 0x00, 0x65, 0x00, 0x65, + 0x00, 0x05, 0x00, 0xe5, 0x02, 0x00, 0xe5, 0x09, + 0x80, 0x45, 0x00, 0x85, 0x00, 0xe5, 0x09, 0xe0, + 0x2c, 0x2c, 0xe0, 0x80, 0x86, 0xef, 0x24, 0x60, + 0xef, 0x5c, 0xe0, 0x04, 0xef, 0x07, 0x20, 0xef, + 0x07, 0x00, 0xef, 0x07, 0x00, 0xef, 0x1d, 0xe0, + 0x02, 0xeb, 0x05, 0xef, 0x80, 0x19, 0xe0, 0x30, + 0xef, 0x15, 0xe0, 0x05, 0xef, 0x24, 0x60, 0xef, + 0x01, 0xc0, 0x2f, 0xe0, 0x06, 0xaf, 0xe0, 0x80, + 0x12, 0xef, 0x80, 0x73, 0x8e, 0xef, 0x82, 0x50, + 0x60, 0xef, 0x09, 0x40, 0xef, 0x05, 0x40, 0xef, + 0x6f, 0x60, 0xef, 0x57, 0xa0, 0xef, 0x04, 0x60, + 0x0f, 0xe0, 0x07, 0xef, 0x04, 0x60, 0xef, 0x30, + 0xe0, 0x00, 0xef, 0x02, 0xa0, 0xef, 0x20, 0xe0, + 0x00, 0xef, 0x16, 0x20, 0xef, 0x04, 0x60, 0x2f, + 0xe0, 0x36, 0xef, 0x80, 0xcc, 0xe0, 0x04, 0xef, + 0x06, 0x20, 0xef, 0x05, 0x40, 0xef, 0x02, 0x80, + 0xef, 0x30, 0xc0, 0xef, 0x07, 0x20, 0xef, 0x03, + 0xa0, 0xef, 0x01, 0xc0, 0xef, 0x80, 0x0b, 0x00, + 0xef, 0x54, 0xe9, 0x02, 0xe0, 0x83, 0x7e, 0xe5, + 0xc0, 0x66, 0x58, 0xe0, 0x18, 0xe5, 0x8f, 0xb2, + 0xa0, 0xe5, 0x80, 0x56, 0x20, 0xe5, 0x95, 0xfa, + 0xe0, 0x06, 0xe5, 0x9c, 0xa9, 0xe0, 0x07, 0xe5, + 0x81, 0xe6, 0xe0, 0x89, 0x1a, 0xe5, 0x81, 0x96, + 0xe0, 0x85, 0x5a, 0xe5, 0x92, 0xc3, 0x80, 0xe5, + 0x8f, 0xd8, 0xe0, 0xca, 0x9b, 0xc9, 0x1b, 0xe0, + 0x16, 0xfb, 0x58, 0xe0, 0x78, 0xe6, 0x80, 0x68, + 0xe0, 0xc0, 0xbd, 0x88, 0xfd, 0xc0, 0xbf, 0x76, + 0x20, 0xfd, 0xc0, 0xbf, 0x76, 0x20, +}; + +typedef enum { + UNICODE_SCRIPT_Unknown, + UNICODE_SCRIPT_Adlam, + UNICODE_SCRIPT_Ahom, + UNICODE_SCRIPT_Anatolian_Hieroglyphs, + UNICODE_SCRIPT_Arabic, + UNICODE_SCRIPT_Armenian, + UNICODE_SCRIPT_Avestan, + UNICODE_SCRIPT_Balinese, + UNICODE_SCRIPT_Bamum, + UNICODE_SCRIPT_Bassa_Vah, + UNICODE_SCRIPT_Batak, + UNICODE_SCRIPT_Bengali, + UNICODE_SCRIPT_Bhaiksuki, + UNICODE_SCRIPT_Bopomofo, + UNICODE_SCRIPT_Brahmi, + UNICODE_SCRIPT_Braille, + UNICODE_SCRIPT_Buginese, + UNICODE_SCRIPT_Buhid, + UNICODE_SCRIPT_Canadian_Aboriginal, + UNICODE_SCRIPT_Carian, + UNICODE_SCRIPT_Caucasian_Albanian, + UNICODE_SCRIPT_Chakma, + UNICODE_SCRIPT_Cham, + UNICODE_SCRIPT_Cherokee, + UNICODE_SCRIPT_Chorasmian, + UNICODE_SCRIPT_Common, + UNICODE_SCRIPT_Coptic, + UNICODE_SCRIPT_Cuneiform, + UNICODE_SCRIPT_Cypriot, + UNICODE_SCRIPT_Cyrillic, + UNICODE_SCRIPT_Cypro_Minoan, + UNICODE_SCRIPT_Deseret, + UNICODE_SCRIPT_Devanagari, + UNICODE_SCRIPT_Dives_Akuru, + UNICODE_SCRIPT_Dogra, + UNICODE_SCRIPT_Duployan, + UNICODE_SCRIPT_Egyptian_Hieroglyphs, + UNICODE_SCRIPT_Elbasan, + UNICODE_SCRIPT_Elymaic, + UNICODE_SCRIPT_Ethiopic, + UNICODE_SCRIPT_Georgian, + UNICODE_SCRIPT_Glagolitic, + UNICODE_SCRIPT_Gothic, + UNICODE_SCRIPT_Garay, + UNICODE_SCRIPT_Grantha, + UNICODE_SCRIPT_Greek, + UNICODE_SCRIPT_Gujarati, + UNICODE_SCRIPT_Gunjala_Gondi, + UNICODE_SCRIPT_Gurmukhi, + UNICODE_SCRIPT_Gurung_Khema, + UNICODE_SCRIPT_Han, + UNICODE_SCRIPT_Hangul, + UNICODE_SCRIPT_Hanifi_Rohingya, + UNICODE_SCRIPT_Hanunoo, + UNICODE_SCRIPT_Hatran, + UNICODE_SCRIPT_Hebrew, + UNICODE_SCRIPT_Hiragana, + UNICODE_SCRIPT_Imperial_Aramaic, + UNICODE_SCRIPT_Inherited, + UNICODE_SCRIPT_Inscriptional_Pahlavi, + UNICODE_SCRIPT_Inscriptional_Parthian, + UNICODE_SCRIPT_Javanese, + UNICODE_SCRIPT_Kaithi, + UNICODE_SCRIPT_Kannada, + UNICODE_SCRIPT_Katakana, + UNICODE_SCRIPT_Kawi, + UNICODE_SCRIPT_Kayah_Li, + UNICODE_SCRIPT_Kharoshthi, + UNICODE_SCRIPT_Khmer, + UNICODE_SCRIPT_Khojki, + UNICODE_SCRIPT_Khitan_Small_Script, + UNICODE_SCRIPT_Khudawadi, + UNICODE_SCRIPT_Kirat_Rai, + UNICODE_SCRIPT_Lao, + UNICODE_SCRIPT_Latin, + UNICODE_SCRIPT_Lepcha, + UNICODE_SCRIPT_Limbu, + UNICODE_SCRIPT_Linear_A, + UNICODE_SCRIPT_Linear_B, + UNICODE_SCRIPT_Lisu, + UNICODE_SCRIPT_Lycian, + UNICODE_SCRIPT_Lydian, + UNICODE_SCRIPT_Makasar, + UNICODE_SCRIPT_Mahajani, + UNICODE_SCRIPT_Malayalam, + UNICODE_SCRIPT_Mandaic, + UNICODE_SCRIPT_Manichaean, + UNICODE_SCRIPT_Marchen, + UNICODE_SCRIPT_Masaram_Gondi, + UNICODE_SCRIPT_Medefaidrin, + UNICODE_SCRIPT_Meetei_Mayek, + UNICODE_SCRIPT_Mende_Kikakui, + UNICODE_SCRIPT_Meroitic_Cursive, + UNICODE_SCRIPT_Meroitic_Hieroglyphs, + UNICODE_SCRIPT_Miao, + UNICODE_SCRIPT_Modi, + UNICODE_SCRIPT_Mongolian, + UNICODE_SCRIPT_Mro, + UNICODE_SCRIPT_Multani, + UNICODE_SCRIPT_Myanmar, + UNICODE_SCRIPT_Nabataean, + UNICODE_SCRIPT_Nag_Mundari, + UNICODE_SCRIPT_Nandinagari, + UNICODE_SCRIPT_New_Tai_Lue, + UNICODE_SCRIPT_Newa, + UNICODE_SCRIPT_Nko, + UNICODE_SCRIPT_Nushu, + UNICODE_SCRIPT_Nyiakeng_Puachue_Hmong, + UNICODE_SCRIPT_Ogham, + UNICODE_SCRIPT_Ol_Chiki, + UNICODE_SCRIPT_Ol_Onal, + UNICODE_SCRIPT_Old_Hungarian, + UNICODE_SCRIPT_Old_Italic, + UNICODE_SCRIPT_Old_North_Arabian, + UNICODE_SCRIPT_Old_Permic, + UNICODE_SCRIPT_Old_Persian, + UNICODE_SCRIPT_Old_Sogdian, + UNICODE_SCRIPT_Old_South_Arabian, + UNICODE_SCRIPT_Old_Turkic, + UNICODE_SCRIPT_Old_Uyghur, + UNICODE_SCRIPT_Oriya, + UNICODE_SCRIPT_Osage, + UNICODE_SCRIPT_Osmanya, + UNICODE_SCRIPT_Pahawh_Hmong, + UNICODE_SCRIPT_Palmyrene, + UNICODE_SCRIPT_Pau_Cin_Hau, + UNICODE_SCRIPT_Phags_Pa, + UNICODE_SCRIPT_Phoenician, + UNICODE_SCRIPT_Psalter_Pahlavi, + UNICODE_SCRIPT_Rejang, + UNICODE_SCRIPT_Runic, + UNICODE_SCRIPT_Samaritan, + UNICODE_SCRIPT_Saurashtra, + UNICODE_SCRIPT_Sharada, + UNICODE_SCRIPT_Shavian, + UNICODE_SCRIPT_Siddham, + UNICODE_SCRIPT_SignWriting, + UNICODE_SCRIPT_Sinhala, + UNICODE_SCRIPT_Sogdian, + UNICODE_SCRIPT_Sora_Sompeng, + UNICODE_SCRIPT_Soyombo, + UNICODE_SCRIPT_Sundanese, + UNICODE_SCRIPT_Sunuwar, + UNICODE_SCRIPT_Syloti_Nagri, + UNICODE_SCRIPT_Syriac, + UNICODE_SCRIPT_Tagalog, + UNICODE_SCRIPT_Tagbanwa, + UNICODE_SCRIPT_Tai_Le, + UNICODE_SCRIPT_Tai_Tham, + UNICODE_SCRIPT_Tai_Viet, + UNICODE_SCRIPT_Takri, + UNICODE_SCRIPT_Tamil, + UNICODE_SCRIPT_Tangut, + UNICODE_SCRIPT_Telugu, + UNICODE_SCRIPT_Thaana, + UNICODE_SCRIPT_Thai, + UNICODE_SCRIPT_Tibetan, + UNICODE_SCRIPT_Tifinagh, + UNICODE_SCRIPT_Tirhuta, + UNICODE_SCRIPT_Tangsa, + UNICODE_SCRIPT_Todhri, + UNICODE_SCRIPT_Toto, + UNICODE_SCRIPT_Tulu_Tigalari, + UNICODE_SCRIPT_Ugaritic, + UNICODE_SCRIPT_Vai, + UNICODE_SCRIPT_Vithkuqi, + UNICODE_SCRIPT_Wancho, + UNICODE_SCRIPT_Warang_Citi, + UNICODE_SCRIPT_Yezidi, + UNICODE_SCRIPT_Yi, + UNICODE_SCRIPT_Zanabazar_Square, + UNICODE_SCRIPT_COUNT, +} UnicodeScriptEnum; + +static const char unicode_script_name_table[] = + "Adlam,Adlm" "\0" + "Ahom,Ahom" "\0" + "Anatolian_Hieroglyphs,Hluw" "\0" + "Arabic,Arab" "\0" + "Armenian,Armn" "\0" + "Avestan,Avst" "\0" + "Balinese,Bali" "\0" + "Bamum,Bamu" "\0" + "Bassa_Vah,Bass" "\0" + "Batak,Batk" "\0" + "Bengali,Beng" "\0" + "Bhaiksuki,Bhks" "\0" + "Bopomofo,Bopo" "\0" + "Brahmi,Brah" "\0" + "Braille,Brai" "\0" + "Buginese,Bugi" "\0" + "Buhid,Buhd" "\0" + "Canadian_Aboriginal,Cans" "\0" + "Carian,Cari" "\0" + "Caucasian_Albanian,Aghb" "\0" + "Chakma,Cakm" "\0" + "Cham,Cham" "\0" + "Cherokee,Cher" "\0" + "Chorasmian,Chrs" "\0" + "Common,Zyyy" "\0" + "Coptic,Copt,Qaac" "\0" + "Cuneiform,Xsux" "\0" + "Cypriot,Cprt" "\0" + "Cyrillic,Cyrl" "\0" + "Cypro_Minoan,Cpmn" "\0" + "Deseret,Dsrt" "\0" + "Devanagari,Deva" "\0" + "Dives_Akuru,Diak" "\0" + "Dogra,Dogr" "\0" + "Duployan,Dupl" "\0" + "Egyptian_Hieroglyphs,Egyp" "\0" + "Elbasan,Elba" "\0" + "Elymaic,Elym" "\0" + "Ethiopic,Ethi" "\0" + "Georgian,Geor" "\0" + "Glagolitic,Glag" "\0" + "Gothic,Goth" "\0" + "Garay,Gara" "\0" + "Grantha,Gran" "\0" + "Greek,Grek" "\0" + "Gujarati,Gujr" "\0" + "Gunjala_Gondi,Gong" "\0" + "Gurmukhi,Guru" "\0" + "Gurung_Khema,Gukh" "\0" + "Han,Hani" "\0" + "Hangul,Hang" "\0" + "Hanifi_Rohingya,Rohg" "\0" + "Hanunoo,Hano" "\0" + "Hatran,Hatr" "\0" + "Hebrew,Hebr" "\0" + "Hiragana,Hira" "\0" + "Imperial_Aramaic,Armi" "\0" + "Inherited,Zinh,Qaai" "\0" + "Inscriptional_Pahlavi,Phli" "\0" + "Inscriptional_Parthian,Prti" "\0" + "Javanese,Java" "\0" + "Kaithi,Kthi" "\0" + "Kannada,Knda" "\0" + "Katakana,Kana" "\0" + "Kawi,Kawi" "\0" + "Kayah_Li,Kali" "\0" + "Kharoshthi,Khar" "\0" + "Khmer,Khmr" "\0" + "Khojki,Khoj" "\0" + "Khitan_Small_Script,Kits" "\0" + "Khudawadi,Sind" "\0" + "Kirat_Rai,Krai" "\0" + "Lao,Laoo" "\0" + "Latin,Latn" "\0" + "Lepcha,Lepc" "\0" + "Limbu,Limb" "\0" + "Linear_A,Lina" "\0" + "Linear_B,Linb" "\0" + "Lisu,Lisu" "\0" + "Lycian,Lyci" "\0" + "Lydian,Lydi" "\0" + "Makasar,Maka" "\0" + "Mahajani,Mahj" "\0" + "Malayalam,Mlym" "\0" + "Mandaic,Mand" "\0" + "Manichaean,Mani" "\0" + "Marchen,Marc" "\0" + "Masaram_Gondi,Gonm" "\0" + "Medefaidrin,Medf" "\0" + "Meetei_Mayek,Mtei" "\0" + "Mende_Kikakui,Mend" "\0" + "Meroitic_Cursive,Merc" "\0" + "Meroitic_Hieroglyphs,Mero" "\0" + "Miao,Plrd" "\0" + "Modi,Modi" "\0" + "Mongolian,Mong" "\0" + "Mro,Mroo" "\0" + "Multani,Mult" "\0" + "Myanmar,Mymr" "\0" + "Nabataean,Nbat" "\0" + "Nag_Mundari,Nagm" "\0" + "Nandinagari,Nand" "\0" + "New_Tai_Lue,Talu" "\0" + "Newa,Newa" "\0" + "Nko,Nkoo" "\0" + "Nushu,Nshu" "\0" + "Nyiakeng_Puachue_Hmong,Hmnp" "\0" + "Ogham,Ogam" "\0" + "Ol_Chiki,Olck" "\0" + "Ol_Onal,Onao" "\0" + "Old_Hungarian,Hung" "\0" + "Old_Italic,Ital" "\0" + "Old_North_Arabian,Narb" "\0" + "Old_Permic,Perm" "\0" + "Old_Persian,Xpeo" "\0" + "Old_Sogdian,Sogo" "\0" + "Old_South_Arabian,Sarb" "\0" + "Old_Turkic,Orkh" "\0" + "Old_Uyghur,Ougr" "\0" + "Oriya,Orya" "\0" + "Osage,Osge" "\0" + "Osmanya,Osma" "\0" + "Pahawh_Hmong,Hmng" "\0" + "Palmyrene,Palm" "\0" + "Pau_Cin_Hau,Pauc" "\0" + "Phags_Pa,Phag" "\0" + "Phoenician,Phnx" "\0" + "Psalter_Pahlavi,Phlp" "\0" + "Rejang,Rjng" "\0" + "Runic,Runr" "\0" + "Samaritan,Samr" "\0" + "Saurashtra,Saur" "\0" + "Sharada,Shrd" "\0" + "Shavian,Shaw" "\0" + "Siddham,Sidd" "\0" + "SignWriting,Sgnw" "\0" + "Sinhala,Sinh" "\0" + "Sogdian,Sogd" "\0" + "Sora_Sompeng,Sora" "\0" + "Soyombo,Soyo" "\0" + "Sundanese,Sund" "\0" + "Sunuwar,Sunu" "\0" + "Syloti_Nagri,Sylo" "\0" + "Syriac,Syrc" "\0" + "Tagalog,Tglg" "\0" + "Tagbanwa,Tagb" "\0" + "Tai_Le,Tale" "\0" + "Tai_Tham,Lana" "\0" + "Tai_Viet,Tavt" "\0" + "Takri,Takr" "\0" + "Tamil,Taml" "\0" + "Tangut,Tang" "\0" + "Telugu,Telu" "\0" + "Thaana,Thaa" "\0" + "Thai,Thai" "\0" + "Tibetan,Tibt" "\0" + "Tifinagh,Tfng" "\0" + "Tirhuta,Tirh" "\0" + "Tangsa,Tnsa" "\0" + "Todhri,Todr" "\0" + "Toto,Toto" "\0" + "Tulu_Tigalari,Tutg" "\0" + "Ugaritic,Ugar" "\0" + "Vai,Vaii" "\0" + "Vithkuqi,Vith" "\0" + "Wancho,Wcho" "\0" + "Warang_Citi,Wara" "\0" + "Yezidi,Yezi" "\0" + "Yi,Yiii" "\0" + "Zanabazar_Square,Zanb" "\0" +; + +static const uint8_t unicode_script_table[2803] = { + 0xc0, 0x19, 0x99, 0x4a, 0x85, 0x19, 0x99, 0x4a, + 0xae, 0x19, 0x80, 0x4a, 0x8e, 0x19, 0x80, 0x4a, + 0x84, 0x19, 0x96, 0x4a, 0x80, 0x19, 0x9e, 0x4a, + 0x80, 0x19, 0xe1, 0x60, 0x4a, 0xa6, 0x19, 0x84, + 0x4a, 0x84, 0x19, 0x81, 0x0d, 0x93, 0x19, 0xe0, + 0x0f, 0x3a, 0x83, 0x2d, 0x80, 0x19, 0x82, 0x2d, + 0x01, 0x83, 0x2d, 0x80, 0x19, 0x80, 0x2d, 0x03, + 0x80, 0x2d, 0x80, 0x19, 0x80, 0x2d, 0x80, 0x19, + 0x82, 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x93, 0x2d, + 0x00, 0xbe, 0x2d, 0x8d, 0x1a, 0x8f, 0x2d, 0xe0, + 0x24, 0x1d, 0x81, 0x3a, 0xe0, 0x48, 0x1d, 0x00, + 0xa5, 0x05, 0x01, 0xb1, 0x05, 0x01, 0x82, 0x05, + 0x00, 0xb6, 0x37, 0x07, 0x9a, 0x37, 0x03, 0x85, + 0x37, 0x0a, 0x84, 0x04, 0x80, 0x19, 0x85, 0x04, + 0x80, 0x19, 0x8d, 0x04, 0x80, 0x19, 0x82, 0x04, + 0x80, 0x19, 0x9f, 0x04, 0x80, 0x19, 0x89, 0x04, + 0x8a, 0x3a, 0x99, 0x04, 0x80, 0x3a, 0xe0, 0x0b, + 0x04, 0x80, 0x19, 0xa1, 0x04, 0x8d, 0x90, 0x00, + 0xbb, 0x90, 0x01, 0x82, 0x90, 0xaf, 0x04, 0xb1, + 0x9a, 0x0d, 0xba, 0x69, 0x01, 0x82, 0x69, 0xad, + 0x83, 0x01, 0x8e, 0x83, 0x00, 0x9b, 0x55, 0x01, + 0x80, 0x55, 0x00, 0x8a, 0x90, 0x04, 0x9e, 0x04, + 0x00, 0x81, 0x04, 0x04, 0xca, 0x04, 0x80, 0x19, + 0x9c, 0x04, 0xd0, 0x20, 0x83, 0x3a, 0x8e, 0x20, + 0x81, 0x19, 0x99, 0x20, 0x83, 0x0b, 0x00, 0x87, + 0x0b, 0x01, 0x81, 0x0b, 0x01, 0x95, 0x0b, 0x00, + 0x86, 0x0b, 0x00, 0x80, 0x0b, 0x02, 0x83, 0x0b, + 0x01, 0x88, 0x0b, 0x01, 0x81, 0x0b, 0x01, 0x83, + 0x0b, 0x07, 0x80, 0x0b, 0x03, 0x81, 0x0b, 0x00, + 0x84, 0x0b, 0x01, 0x98, 0x0b, 0x01, 0x82, 0x30, + 0x00, 0x85, 0x30, 0x03, 0x81, 0x30, 0x01, 0x95, + 0x30, 0x00, 0x86, 0x30, 0x00, 0x81, 0x30, 0x00, + 0x81, 0x30, 0x00, 0x81, 0x30, 0x01, 0x80, 0x30, + 0x00, 0x84, 0x30, 0x03, 0x81, 0x30, 0x01, 0x82, + 0x30, 0x02, 0x80, 0x30, 0x06, 0x83, 0x30, 0x00, + 0x80, 0x30, 0x06, 0x90, 0x30, 0x09, 0x82, 0x2e, + 0x00, 0x88, 0x2e, 0x00, 0x82, 0x2e, 0x00, 0x95, + 0x2e, 0x00, 0x86, 0x2e, 0x00, 0x81, 0x2e, 0x00, + 0x84, 0x2e, 0x01, 0x89, 0x2e, 0x00, 0x82, 0x2e, + 0x00, 0x82, 0x2e, 0x01, 0x80, 0x2e, 0x0e, 0x83, + 0x2e, 0x01, 0x8b, 0x2e, 0x06, 0x86, 0x2e, 0x00, + 0x82, 0x78, 0x00, 0x87, 0x78, 0x01, 0x81, 0x78, + 0x01, 0x95, 0x78, 0x00, 0x86, 0x78, 0x00, 0x81, + 0x78, 0x00, 0x84, 0x78, 0x01, 0x88, 0x78, 0x01, + 0x81, 0x78, 0x01, 0x82, 0x78, 0x06, 0x82, 0x78, + 0x03, 0x81, 0x78, 0x00, 0x84, 0x78, 0x01, 0x91, + 0x78, 0x09, 0x81, 0x97, 0x00, 0x85, 0x97, 0x02, + 0x82, 0x97, 0x00, 0x83, 0x97, 0x02, 0x81, 0x97, + 0x00, 0x80, 0x97, 0x00, 0x81, 0x97, 0x02, 0x81, + 0x97, 0x02, 0x82, 0x97, 0x02, 0x8b, 0x97, 0x03, + 0x84, 0x97, 0x02, 0x82, 0x97, 0x00, 0x83, 0x97, + 0x01, 0x80, 0x97, 0x05, 0x80, 0x97, 0x0d, 0x94, + 0x97, 0x04, 0x8c, 0x99, 0x00, 0x82, 0x99, 0x00, + 0x96, 0x99, 0x00, 0x8f, 0x99, 0x01, 0x88, 0x99, + 0x00, 0x82, 0x99, 0x00, 0x83, 0x99, 0x06, 0x81, + 0x99, 0x00, 0x82, 0x99, 0x01, 0x80, 0x99, 0x01, + 0x83, 0x99, 0x01, 0x89, 0x99, 0x06, 0x88, 0x99, + 0x8c, 0x3f, 0x00, 0x82, 0x3f, 0x00, 0x96, 0x3f, + 0x00, 0x89, 0x3f, 0x00, 0x84, 0x3f, 0x01, 0x88, + 0x3f, 0x00, 0x82, 0x3f, 0x00, 0x83, 0x3f, 0x06, + 0x81, 0x3f, 0x05, 0x81, 0x3f, 0x00, 0x83, 0x3f, + 0x01, 0x89, 0x3f, 0x00, 0x82, 0x3f, 0x0b, 0x8c, + 0x54, 0x00, 0x82, 0x54, 0x00, 0xb2, 0x54, 0x00, + 0x82, 0x54, 0x00, 0x85, 0x54, 0x03, 0x8f, 0x54, + 0x01, 0x99, 0x54, 0x00, 0x82, 0x89, 0x00, 0x91, + 0x89, 0x02, 0x97, 0x89, 0x00, 0x88, 0x89, 0x00, + 0x80, 0x89, 0x01, 0x86, 0x89, 0x02, 0x80, 0x89, + 0x03, 0x85, 0x89, 0x00, 0x80, 0x89, 0x00, 0x87, + 0x89, 0x05, 0x89, 0x89, 0x01, 0x82, 0x89, 0x0b, + 0xb9, 0x9b, 0x03, 0x80, 0x19, 0x9b, 0x9b, 0x24, + 0x81, 0x49, 0x00, 0x80, 0x49, 0x00, 0x84, 0x49, + 0x00, 0x97, 0x49, 0x00, 0x80, 0x49, 0x00, 0x96, + 0x49, 0x01, 0x84, 0x49, 0x00, 0x80, 0x49, 0x00, + 0x86, 0x49, 0x00, 0x89, 0x49, 0x01, 0x83, 0x49, + 0x1f, 0xc7, 0x9c, 0x00, 0xa3, 0x9c, 0x03, 0xa6, + 0x9c, 0x00, 0xa3, 0x9c, 0x00, 0x8e, 0x9c, 0x00, + 0x86, 0x9c, 0x83, 0x19, 0x81, 0x9c, 0x24, 0xe0, + 0x3f, 0x63, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04, + 0x80, 0x28, 0x01, 0xaa, 0x28, 0x80, 0x19, 0x83, + 0x28, 0xe0, 0x9f, 0x33, 0xc8, 0x27, 0x00, 0x83, + 0x27, 0x01, 0x86, 0x27, 0x00, 0x80, 0x27, 0x00, + 0x83, 0x27, 0x01, 0xa8, 0x27, 0x00, 0x83, 0x27, + 0x01, 0xa0, 0x27, 0x00, 0x83, 0x27, 0x01, 0x86, + 0x27, 0x00, 0x80, 0x27, 0x00, 0x83, 0x27, 0x01, + 0x8e, 0x27, 0x00, 0xb8, 0x27, 0x00, 0x83, 0x27, + 0x01, 0xc2, 0x27, 0x01, 0x9f, 0x27, 0x02, 0x99, + 0x27, 0x05, 0xd5, 0x17, 0x01, 0x85, 0x17, 0x01, + 0xe2, 0x1f, 0x12, 0x9c, 0x6c, 0x02, 0xca, 0x82, + 0x82, 0x19, 0x8a, 0x82, 0x06, 0x95, 0x91, 0x08, + 0x80, 0x91, 0x94, 0x35, 0x81, 0x19, 0x08, 0x93, + 0x11, 0x0b, 0x8c, 0x92, 0x00, 0x82, 0x92, 0x00, + 0x81, 0x92, 0x0b, 0xdd, 0x44, 0x01, 0x89, 0x44, + 0x05, 0x89, 0x44, 0x05, 0x81, 0x60, 0x81, 0x19, + 0x80, 0x60, 0x80, 0x19, 0x93, 0x60, 0x05, 0xd8, + 0x60, 0x06, 0xaa, 0x60, 0x04, 0xc5, 0x12, 0x09, + 0x9e, 0x4c, 0x00, 0x8b, 0x4c, 0x03, 0x8b, 0x4c, + 0x03, 0x80, 0x4c, 0x02, 0x8b, 0x4c, 0x9d, 0x93, + 0x01, 0x84, 0x93, 0x0a, 0xab, 0x67, 0x03, 0x99, + 0x67, 0x05, 0x8a, 0x67, 0x02, 0x81, 0x67, 0x9f, + 0x44, 0x9b, 0x10, 0x01, 0x81, 0x10, 0xbe, 0x94, + 0x00, 0x9c, 0x94, 0x01, 0x8a, 0x94, 0x05, 0x89, + 0x94, 0x05, 0x8d, 0x94, 0x01, 0x9e, 0x3a, 0x30, + 0xcc, 0x07, 0x00, 0xb1, 0x07, 0xbf, 0x8d, 0xb3, + 0x0a, 0x07, 0x83, 0x0a, 0xb7, 0x4b, 0x02, 0x8e, + 0x4b, 0x02, 0x82, 0x4b, 0xaf, 0x6d, 0x8a, 0x1d, + 0x04, 0xaa, 0x28, 0x01, 0x82, 0x28, 0x87, 0x8d, + 0x07, 0x82, 0x3a, 0x80, 0x19, 0x8c, 0x3a, 0x80, + 0x19, 0x86, 0x3a, 0x83, 0x19, 0x80, 0x3a, 0x85, + 0x19, 0x80, 0x3a, 0x82, 0x19, 0x81, 0x3a, 0x80, + 0x19, 0x04, 0xa5, 0x4a, 0x84, 0x2d, 0x80, 0x1d, + 0xb0, 0x4a, 0x84, 0x2d, 0x83, 0x4a, 0x84, 0x2d, + 0x8c, 0x4a, 0x80, 0x1d, 0xc5, 0x4a, 0x80, 0x2d, + 0xbf, 0x3a, 0xe0, 0x9f, 0x4a, 0x95, 0x2d, 0x01, + 0x85, 0x2d, 0x01, 0xa5, 0x2d, 0x01, 0x85, 0x2d, + 0x01, 0x87, 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x80, + 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x9e, 0x2d, 0x01, + 0xb4, 0x2d, 0x00, 0x8e, 0x2d, 0x00, 0x8d, 0x2d, + 0x01, 0x85, 0x2d, 0x00, 0x92, 0x2d, 0x01, 0x82, + 0x2d, 0x00, 0x88, 0x2d, 0x00, 0x8b, 0x19, 0x81, + 0x3a, 0xd6, 0x19, 0x00, 0x8a, 0x19, 0x80, 0x4a, + 0x01, 0x8a, 0x19, 0x80, 0x4a, 0x8e, 0x19, 0x00, + 0x8c, 0x4a, 0x02, 0xa0, 0x19, 0x0e, 0xa0, 0x3a, + 0x0e, 0xa5, 0x19, 0x80, 0x2d, 0x82, 0x19, 0x81, + 0x4a, 0x85, 0x19, 0x80, 0x4a, 0x9a, 0x19, 0x80, + 0x4a, 0x90, 0x19, 0xa8, 0x4a, 0x82, 0x19, 0x03, + 0xe2, 0x39, 0x19, 0x15, 0x8a, 0x19, 0x14, 0xe3, + 0x3f, 0x19, 0xe0, 0x9f, 0x0f, 0xe2, 0x13, 0x19, + 0x01, 0x9f, 0x19, 0x00, 0xe0, 0x08, 0x19, 0xdf, + 0x29, 0x9f, 0x4a, 0xe0, 0x13, 0x1a, 0x04, 0x86, + 0x1a, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04, 0x80, + 0x28, 0x01, 0xb7, 0x9d, 0x06, 0x81, 0x9d, 0x0d, + 0x80, 0x9d, 0x96, 0x27, 0x08, 0x86, 0x27, 0x00, + 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, + 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, + 0x27, 0x00, 0x86, 0x27, 0x00, 0x9f, 0x1d, 0xdd, + 0x19, 0x21, 0x99, 0x32, 0x00, 0xd8, 0x32, 0x0b, + 0xe0, 0x75, 0x32, 0x19, 0x94, 0x19, 0x80, 0x32, + 0x80, 0x19, 0x80, 0x32, 0x98, 0x19, 0x88, 0x32, + 0x83, 0x3a, 0x81, 0x33, 0x87, 0x19, 0x83, 0x32, + 0x83, 0x19, 0x00, 0xd5, 0x38, 0x01, 0x81, 0x3a, + 0x81, 0x19, 0x82, 0x38, 0x80, 0x19, 0xd9, 0x40, + 0x81, 0x19, 0x82, 0x40, 0x04, 0xaa, 0x0d, 0x00, + 0xdd, 0x33, 0x00, 0x8f, 0x19, 0x9f, 0x0d, 0xa5, + 0x19, 0x08, 0x80, 0x19, 0x8f, 0x40, 0x9e, 0x33, + 0x00, 0xbf, 0x19, 0x9e, 0x33, 0xd0, 0x19, 0xae, + 0x40, 0x80, 0x19, 0xd7, 0x40, 0xe0, 0x47, 0x19, + 0xf0, 0x09, 0x5f, 0x32, 0xbf, 0x19, 0xf0, 0x41, + 0x9f, 0x32, 0xe4, 0x2c, 0xa9, 0x02, 0xb6, 0xa9, + 0x08, 0xaf, 0x4f, 0xe0, 0xcb, 0xa4, 0x13, 0xdf, + 0x1d, 0xd7, 0x08, 0x07, 0xa1, 0x19, 0xe0, 0x05, + 0x4a, 0x82, 0x19, 0xc2, 0x4a, 0x01, 0x81, 0x4a, + 0x00, 0x80, 0x4a, 0x00, 0x87, 0x4a, 0x14, 0x8d, + 0x4a, 0xac, 0x8f, 0x02, 0x89, 0x19, 0x05, 0xb7, + 0x7e, 0x07, 0xc5, 0x84, 0x07, 0x8b, 0x84, 0x05, + 0x9f, 0x20, 0xad, 0x42, 0x80, 0x19, 0x80, 0x42, + 0xa3, 0x81, 0x0a, 0x80, 0x81, 0x9c, 0x33, 0x02, + 0xcd, 0x3d, 0x00, 0x80, 0x19, 0x89, 0x3d, 0x03, + 0x81, 0x3d, 0x9e, 0x63, 0x00, 0xb6, 0x16, 0x08, + 0x8d, 0x16, 0x01, 0x89, 0x16, 0x01, 0x83, 0x16, + 0x9f, 0x63, 0xc2, 0x95, 0x17, 0x84, 0x95, 0x96, + 0x5a, 0x09, 0x85, 0x27, 0x01, 0x85, 0x27, 0x01, + 0x85, 0x27, 0x08, 0x86, 0x27, 0x00, 0x86, 0x27, + 0x00, 0xaa, 0x4a, 0x80, 0x19, 0x88, 0x4a, 0x80, + 0x2d, 0x83, 0x4a, 0x81, 0x19, 0x03, 0xcf, 0x17, + 0xad, 0x5a, 0x01, 0x89, 0x5a, 0x05, 0xf0, 0x1b, + 0x43, 0x33, 0x0b, 0x96, 0x33, 0x03, 0xb0, 0x33, + 0x70, 0x10, 0xa3, 0xe1, 0x0d, 0x32, 0x01, 0xe0, + 0x09, 0x32, 0x25, 0x86, 0x4a, 0x0b, 0x84, 0x05, + 0x04, 0x99, 0x37, 0x00, 0x84, 0x37, 0x00, 0x80, + 0x37, 0x00, 0x81, 0x37, 0x00, 0x81, 0x37, 0x00, + 0x89, 0x37, 0xe0, 0x12, 0x04, 0x0f, 0xe1, 0x0a, + 0x04, 0x81, 0x19, 0xcf, 0x04, 0x01, 0xb5, 0x04, + 0x06, 0x80, 0x04, 0x1f, 0x8f, 0x04, 0x8f, 0x3a, + 0x89, 0x19, 0x05, 0x8d, 0x3a, 0x81, 0x1d, 0xa2, + 0x19, 0x00, 0x92, 0x19, 0x00, 0x83, 0x19, 0x03, + 0x84, 0x04, 0x00, 0xe0, 0x26, 0x04, 0x01, 0x80, + 0x19, 0x00, 0x9f, 0x19, 0x99, 0x4a, 0x85, 0x19, + 0x99, 0x4a, 0x8a, 0x19, 0x89, 0x40, 0x80, 0x19, + 0xac, 0x40, 0x81, 0x19, 0x9e, 0x33, 0x02, 0x85, + 0x33, 0x01, 0x85, 0x33, 0x01, 0x85, 0x33, 0x01, + 0x82, 0x33, 0x02, 0x86, 0x19, 0x00, 0x86, 0x19, + 0x09, 0x84, 0x19, 0x01, 0x8b, 0x4e, 0x00, 0x99, + 0x4e, 0x00, 0x92, 0x4e, 0x00, 0x81, 0x4e, 0x00, + 0x8e, 0x4e, 0x01, 0x8d, 0x4e, 0x21, 0xe0, 0x1a, + 0x4e, 0x04, 0x82, 0x19, 0x03, 0xac, 0x19, 0x02, + 0x88, 0x19, 0xce, 0x2d, 0x00, 0x8c, 0x19, 0x02, + 0x80, 0x2d, 0x2e, 0xac, 0x19, 0x80, 0x3a, 0x60, + 0x21, 0x9c, 0x50, 0x02, 0xb0, 0x13, 0x0e, 0x80, + 0x3a, 0x9a, 0x19, 0x03, 0xa3, 0x70, 0x08, 0x82, + 0x70, 0x9a, 0x2a, 0x04, 0xaa, 0x72, 0x04, 0x9d, + 0xa3, 0x00, 0x80, 0xa3, 0xa3, 0x73, 0x03, 0x8d, + 0x73, 0x29, 0xcf, 0x1f, 0xaf, 0x86, 0x9d, 0x7a, + 0x01, 0x89, 0x7a, 0x05, 0xa3, 0x79, 0x03, 0xa3, + 0x79, 0x03, 0xa7, 0x25, 0x07, 0xb3, 0x14, 0x0a, + 0x80, 0x14, 0x8a, 0xa5, 0x00, 0x8e, 0xa5, 0x00, + 0x86, 0xa5, 0x00, 0x81, 0xa5, 0x00, 0x8a, 0xa5, + 0x00, 0x8e, 0xa5, 0x00, 0x86, 0xa5, 0x00, 0x81, + 0xa5, 0x02, 0xb3, 0xa0, 0x0b, 0xe0, 0xd6, 0x4d, + 0x08, 0x95, 0x4d, 0x09, 0x87, 0x4d, 0x17, 0x85, + 0x4a, 0x00, 0xa9, 0x4a, 0x00, 0x88, 0x4a, 0x44, + 0x85, 0x1c, 0x01, 0x80, 0x1c, 0x00, 0xab, 0x1c, + 0x00, 0x81, 0x1c, 0x02, 0x80, 0x1c, 0x01, 0x80, + 0x1c, 0x95, 0x39, 0x00, 0x88, 0x39, 0x9f, 0x7c, + 0x9e, 0x64, 0x07, 0x88, 0x64, 0x2f, 0x92, 0x36, + 0x00, 0x81, 0x36, 0x04, 0x84, 0x36, 0x9b, 0x7f, + 0x02, 0x80, 0x7f, 0x99, 0x51, 0x04, 0x80, 0x51, + 0x3f, 0x9f, 0x5d, 0x97, 0x5c, 0x03, 0x93, 0x5c, + 0x01, 0xad, 0x5c, 0x83, 0x43, 0x00, 0x81, 0x43, + 0x04, 0x87, 0x43, 0x00, 0x82, 0x43, 0x00, 0x9c, + 0x43, 0x01, 0x82, 0x43, 0x03, 0x89, 0x43, 0x06, + 0x88, 0x43, 0x06, 0x9f, 0x75, 0x9f, 0x71, 0x1f, + 0xa6, 0x56, 0x03, 0x8b, 0x56, 0x08, 0xb5, 0x06, + 0x02, 0x86, 0x06, 0x95, 0x3c, 0x01, 0x87, 0x3c, + 0x92, 0x3b, 0x04, 0x87, 0x3b, 0x91, 0x80, 0x06, + 0x83, 0x80, 0x0b, 0x86, 0x80, 0x4f, 0xc8, 0x76, + 0x36, 0xb2, 0x6f, 0x0c, 0xb2, 0x6f, 0x06, 0x85, + 0x6f, 0xa7, 0x34, 0x07, 0x89, 0x34, 0x05, 0xa5, + 0x2b, 0x02, 0x9c, 0x2b, 0x07, 0x81, 0x2b, 0x60, + 0x6f, 0x9e, 0x04, 0x00, 0xa9, 0xa8, 0x00, 0x82, + 0xa8, 0x01, 0x81, 0xa8, 0x0f, 0x82, 0x04, 0x36, + 0x83, 0x04, 0xa7, 0x74, 0x07, 0xa9, 0x8a, 0x15, + 0x99, 0x77, 0x25, 0x9b, 0x18, 0x13, 0x96, 0x26, + 0x08, 0xcd, 0x0e, 0x03, 0xa3, 0x0e, 0x08, 0x80, + 0x0e, 0xc2, 0x3e, 0x09, 0x80, 0x3e, 0x01, 0x98, + 0x8b, 0x06, 0x89, 0x8b, 0x05, 0xb4, 0x15, 0x00, + 0x91, 0x15, 0x07, 0xa6, 0x53, 0x08, 0xdf, 0x85, + 0x00, 0x93, 0x89, 0x0a, 0x91, 0x45, 0x00, 0xae, + 0x45, 0x3d, 0x86, 0x62, 0x00, 0x80, 0x62, 0x00, + 0x83, 0x62, 0x00, 0x8e, 0x62, 0x00, 0x8a, 0x62, + 0x05, 0xba, 0x47, 0x04, 0x89, 0x47, 0x05, 0x83, + 0x2c, 0x00, 0x87, 0x2c, 0x01, 0x81, 0x2c, 0x01, + 0x95, 0x2c, 0x00, 0x86, 0x2c, 0x00, 0x81, 0x2c, + 0x00, 0x84, 0x2c, 0x00, 0x80, 0x3a, 0x88, 0x2c, + 0x01, 0x81, 0x2c, 0x01, 0x82, 0x2c, 0x01, 0x80, + 0x2c, 0x05, 0x80, 0x2c, 0x04, 0x86, 0x2c, 0x01, + 0x86, 0x2c, 0x02, 0x84, 0x2c, 0x0a, 0x89, 0xa2, + 0x00, 0x80, 0xa2, 0x01, 0x80, 0xa2, 0x00, 0xa5, + 0xa2, 0x00, 0x89, 0xa2, 0x00, 0x80, 0xa2, 0x01, + 0x80, 0xa2, 0x00, 0x83, 0xa2, 0x00, 0x89, 0xa2, + 0x00, 0x81, 0xa2, 0x07, 0x81, 0xa2, 0x1c, 0xdb, + 0x68, 0x00, 0x84, 0x68, 0x1d, 0xc7, 0x9e, 0x07, + 0x89, 0x9e, 0x60, 0x45, 0xb5, 0x87, 0x01, 0xa5, + 0x87, 0x21, 0xc4, 0x5f, 0x0a, 0x89, 0x5f, 0x05, + 0x8c, 0x60, 0x12, 0xb9, 0x96, 0x05, 0x89, 0x96, + 0x05, 0x93, 0x63, 0x1b, 0x9a, 0x02, 0x01, 0x8e, + 0x02, 0x03, 0x96, 0x02, 0x60, 0x58, 0xbb, 0x22, + 0x60, 0x03, 0xd2, 0xa7, 0x0b, 0x80, 0xa7, 0x86, + 0x21, 0x01, 0x80, 0x21, 0x01, 0x87, 0x21, 0x00, + 0x81, 0x21, 0x00, 0x9d, 0x21, 0x00, 0x81, 0x21, + 0x01, 0x8b, 0x21, 0x08, 0x89, 0x21, 0x45, 0x87, + 0x66, 0x01, 0xad, 0x66, 0x01, 0x8a, 0x66, 0x1a, + 0xc7, 0xaa, 0x07, 0xd2, 0x8c, 0x0c, 0x8f, 0x12, + 0xb8, 0x7d, 0x06, 0x89, 0x20, 0x60, 0x55, 0xa1, + 0x8e, 0x0d, 0x89, 0x8e, 0x05, 0x88, 0x0c, 0x00, + 0xac, 0x0c, 0x00, 0x8d, 0x0c, 0x09, 0x9c, 0x0c, + 0x02, 0x9f, 0x57, 0x01, 0x95, 0x57, 0x00, 0x8d, + 0x57, 0x48, 0x86, 0x58, 0x00, 0x81, 0x58, 0x00, + 0xab, 0x58, 0x02, 0x80, 0x58, 0x00, 0x81, 0x58, + 0x00, 0x88, 0x58, 0x07, 0x89, 0x58, 0x05, 0x85, + 0x2f, 0x00, 0x81, 0x2f, 0x00, 0xa4, 0x2f, 0x00, + 0x81, 0x2f, 0x00, 0x85, 0x2f, 0x06, 0x89, 0x2f, + 0x60, 0xd5, 0x98, 0x52, 0x06, 0x90, 0x41, 0x00, + 0xa8, 0x41, 0x02, 0x9c, 0x41, 0x54, 0x80, 0x4f, + 0x0e, 0xb1, 0x97, 0x0c, 0x80, 0x97, 0xe3, 0x39, + 0x1b, 0x60, 0x05, 0xe0, 0x0e, 0x1b, 0x00, 0x84, + 0x1b, 0x0a, 0xe0, 0x63, 0x1b, 0x69, 0xeb, 0xe0, + 0x02, 0x1e, 0x0c, 0xe3, 0xf5, 0x24, 0x09, 0xef, + 0x3a, 0x24, 0x04, 0xe1, 0xe6, 0x03, 0x70, 0x0a, + 0x58, 0xb9, 0x31, 0x66, 0x65, 0xe1, 0xd8, 0x08, + 0x06, 0x9e, 0x61, 0x00, 0x89, 0x61, 0x03, 0x81, + 0x61, 0xce, 0x9f, 0x00, 0x89, 0x9f, 0x05, 0x9d, + 0x09, 0x01, 0x85, 0x09, 0x09, 0xc5, 0x7b, 0x09, + 0x89, 0x7b, 0x00, 0x86, 0x7b, 0x00, 0x94, 0x7b, + 0x04, 0x92, 0x7b, 0x61, 0x4f, 0xb9, 0x48, 0x60, + 0x65, 0xda, 0x59, 0x60, 0x04, 0xca, 0x5e, 0x03, + 0xb8, 0x5e, 0x06, 0x90, 0x5e, 0x3f, 0x80, 0x98, + 0x80, 0x6a, 0x81, 0x32, 0x80, 0x46, 0x0a, 0x81, + 0x32, 0x0d, 0xf0, 0x07, 0x97, 0x98, 0x07, 0xe2, + 0x9f, 0x98, 0xe1, 0x75, 0x46, 0x28, 0x80, 0x46, + 0x88, 0x98, 0x70, 0x12, 0x86, 0x83, 0x40, 0x00, + 0x86, 0x40, 0x00, 0x81, 0x40, 0x00, 0x80, 0x40, + 0xe0, 0xbe, 0x38, 0x82, 0x40, 0x0e, 0x80, 0x38, + 0x1c, 0x82, 0x38, 0x01, 0x80, 0x40, 0x0d, 0x83, + 0x40, 0x07, 0xe1, 0x2b, 0x6a, 0x68, 0xa3, 0xe0, + 0x0a, 0x23, 0x04, 0x8c, 0x23, 0x02, 0x88, 0x23, + 0x06, 0x89, 0x23, 0x01, 0x83, 0x23, 0x83, 0x19, + 0x6e, 0xfb, 0xe0, 0x99, 0x19, 0x05, 0xe1, 0x53, + 0x19, 0x4b, 0xad, 0x3a, 0x01, 0x96, 0x3a, 0x08, + 0xe0, 0x13, 0x19, 0x3b, 0xe0, 0x95, 0x19, 0x09, + 0xa6, 0x19, 0x01, 0xbd, 0x19, 0x82, 0x3a, 0x90, + 0x19, 0x87, 0x3a, 0x81, 0x19, 0x86, 0x3a, 0x9d, + 0x19, 0x83, 0x3a, 0xbc, 0x19, 0x14, 0xc5, 0x2d, + 0x60, 0x19, 0x93, 0x19, 0x0b, 0x93, 0x19, 0x0b, + 0xd6, 0x19, 0x08, 0x98, 0x19, 0x60, 0x26, 0xd4, + 0x19, 0x00, 0xc6, 0x19, 0x00, 0x81, 0x19, 0x01, + 0x80, 0x19, 0x01, 0x81, 0x19, 0x01, 0x83, 0x19, + 0x00, 0x8b, 0x19, 0x00, 0x80, 0x19, 0x00, 0x86, + 0x19, 0x00, 0xc0, 0x19, 0x00, 0x83, 0x19, 0x01, + 0x87, 0x19, 0x00, 0x86, 0x19, 0x00, 0x9b, 0x19, + 0x00, 0x83, 0x19, 0x00, 0x84, 0x19, 0x00, 0x80, + 0x19, 0x02, 0x86, 0x19, 0x00, 0xe0, 0xf3, 0x19, + 0x01, 0xe0, 0xc3, 0x19, 0x01, 0xb1, 0x19, 0xe2, + 0x2b, 0x88, 0x0e, 0x84, 0x88, 0x00, 0x8e, 0x88, + 0x63, 0xef, 0x9e, 0x4a, 0x05, 0x85, 0x4a, 0x60, + 0x74, 0x86, 0x29, 0x00, 0x90, 0x29, 0x01, 0x86, + 0x29, 0x00, 0x81, 0x29, 0x00, 0x84, 0x29, 0x04, + 0xbd, 0x1d, 0x20, 0x80, 0x1d, 0x60, 0x0f, 0xac, + 0x6b, 0x02, 0x8d, 0x6b, 0x01, 0x89, 0x6b, 0x03, + 0x81, 0x6b, 0x60, 0xdf, 0x9e, 0xa1, 0x10, 0xb9, + 0xa6, 0x04, 0x80, 0xa6, 0x61, 0x6f, 0xa9, 0x65, + 0x60, 0x75, 0xaa, 0x6e, 0x03, 0x80, 0x6e, 0x61, + 0x7f, 0x86, 0x27, 0x00, 0x83, 0x27, 0x00, 0x81, + 0x27, 0x00, 0x8e, 0x27, 0x00, 0xe0, 0x64, 0x5b, + 0x01, 0x8f, 0x5b, 0x28, 0xcb, 0x01, 0x03, 0x89, + 0x01, 0x03, 0x81, 0x01, 0x62, 0xb0, 0xc3, 0x19, + 0x4b, 0xbc, 0x19, 0x60, 0x61, 0x83, 0x04, 0x00, + 0x9a, 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, + 0x01, 0x80, 0x04, 0x00, 0x89, 0x04, 0x00, 0x83, + 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x05, + 0x80, 0x04, 0x03, 0x80, 0x04, 0x00, 0x80, 0x04, + 0x00, 0x80, 0x04, 0x00, 0x82, 0x04, 0x00, 0x81, + 0x04, 0x00, 0x80, 0x04, 0x01, 0x80, 0x04, 0x00, + 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, + 0x00, 0x80, 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, + 0x04, 0x01, 0x83, 0x04, 0x00, 0x86, 0x04, 0x00, + 0x83, 0x04, 0x00, 0x83, 0x04, 0x00, 0x80, 0x04, + 0x00, 0x89, 0x04, 0x00, 0x90, 0x04, 0x04, 0x82, + 0x04, 0x00, 0x84, 0x04, 0x00, 0x90, 0x04, 0x33, + 0x81, 0x04, 0x60, 0xad, 0xab, 0x19, 0x03, 0xe0, + 0x03, 0x19, 0x0b, 0x8e, 0x19, 0x01, 0x8e, 0x19, + 0x00, 0x8e, 0x19, 0x00, 0xa4, 0x19, 0x09, 0xe0, + 0x4d, 0x19, 0x37, 0x99, 0x19, 0x80, 0x38, 0x81, + 0x19, 0x0c, 0xab, 0x19, 0x03, 0x88, 0x19, 0x06, + 0x81, 0x19, 0x0d, 0x85, 0x19, 0x60, 0x39, 0xe3, + 0x77, 0x19, 0x03, 0x90, 0x19, 0x02, 0x8c, 0x19, + 0x02, 0xe0, 0x16, 0x19, 0x03, 0xde, 0x19, 0x05, + 0x8b, 0x19, 0x03, 0x80, 0x19, 0x0e, 0x8b, 0x19, + 0x03, 0xb7, 0x19, 0x07, 0x89, 0x19, 0x05, 0xa7, + 0x19, 0x07, 0x9d, 0x19, 0x01, 0x8b, 0x19, 0x03, + 0x81, 0x19, 0x3d, 0xe0, 0xf3, 0x19, 0x0b, 0x8d, + 0x19, 0x01, 0x8c, 0x19, 0x02, 0x89, 0x19, 0x04, + 0xb7, 0x19, 0x06, 0x8e, 0x19, 0x01, 0x8a, 0x19, + 0x05, 0x88, 0x19, 0x06, 0xe0, 0x32, 0x19, 0x00, + 0xe0, 0x05, 0x19, 0x63, 0xa5, 0xf0, 0x96, 0x7f, + 0x32, 0x1f, 0xef, 0xd9, 0x32, 0x05, 0xe0, 0x7d, + 0x32, 0x01, 0xf0, 0x06, 0x21, 0x32, 0x0d, 0xf0, + 0x0c, 0xd0, 0x32, 0x0e, 0xe2, 0x0d, 0x32, 0x69, + 0x41, 0xe1, 0xbd, 0x32, 0x65, 0x81, 0xf0, 0x02, + 0xea, 0x32, 0x04, 0xef, 0xff, 0x32, 0x7a, 0xcb, + 0xf0, 0x80, 0x19, 0x1d, 0xdf, 0x19, 0x60, 0x1f, + 0xe0, 0x8f, 0x3a, +}; + +static const uint8_t unicode_script_ext_table[1253] = { + 0x80, 0x36, 0x00, 0x00, 0x10, 0x06, 0x13, 0x1a, + 0x23, 0x25, 0x28, 0x29, 0x2f, 0x2a, 0x2d, 0x32, + 0x4a, 0x51, 0x53, 0x72, 0x86, 0x81, 0x83, 0x00, + 0x00, 0x07, 0x0b, 0x1d, 0x20, 0x4a, 0x4f, 0x9b, + 0xa1, 0x09, 0x00, 0x00, 0x02, 0x0d, 0x4a, 0x00, + 0x00, 0x02, 0x02, 0x0d, 0x4a, 0x00, 0x00, 0x00, + 0x02, 0x4a, 0x4f, 0x08, 0x00, 0x00, 0x02, 0x4a, + 0x9b, 0x00, 0x00, 0x00, 0x02, 0x0d, 0x4a, 0x25, + 0x00, 0x00, 0x08, 0x17, 0x1a, 0x1d, 0x2d, 0x4a, + 0x72, 0x8e, 0x93, 0x00, 0x08, 0x17, 0x1d, 0x2d, + 0x4a, 0x79, 0x8e, 0x93, 0xa0, 0x00, 0x04, 0x17, + 0x1d, 0x4a, 0x9d, 0x00, 0x05, 0x29, 0x4a, 0x8e, + 0x90, 0x9b, 0x00, 0x0b, 0x14, 0x17, 0x1a, 0x1d, + 0x2a, 0x2d, 0x4a, 0x79, 0x90, 0x9d, 0xa0, 0x00, + 0x06, 0x1a, 0x25, 0x29, 0x2a, 0x40, 0x4a, 0x00, + 0x04, 0x1d, 0x2d, 0x4a, 0x72, 0x00, 0x09, 0x1a, + 0x23, 0x37, 0x4a, 0x72, 0x90, 0x93, 0x9d, 0xa0, + 0x00, 0x0a, 0x05, 0x1d, 0x23, 0x2a, 0x2d, 0x37, + 0x4a, 0x72, 0x90, 0x93, 0x00, 0x02, 0x4a, 0x9d, + 0x00, 0x03, 0x23, 0x4a, 0x90, 0x00, 0x04, 0x17, + 0x1d, 0x4a, 0x79, 0x00, 0x03, 0x17, 0x4a, 0x93, + 0x00, 0x02, 0x4a, 0x8e, 0x00, 0x02, 0x27, 0x4a, + 0x00, 0x00, 0x00, 0x02, 0x4a, 0x8e, 0x00, 0x03, + 0x1d, 0x4a, 0xa0, 0x00, 0x00, 0x00, 0x04, 0x2d, + 0x4a, 0x72, 0xa0, 0x0b, 0x00, 0x00, 0x02, 0x4a, + 0x90, 0x01, 0x00, 0x00, 0x05, 0x17, 0x23, 0x40, + 0x4a, 0x90, 0x00, 0x04, 0x17, 0x23, 0x4a, 0x90, + 0x00, 0x02, 0x4a, 0x90, 0x06, 0x00, 0x00, 0x03, + 0x4a, 0x8e, 0x90, 0x00, 0x02, 0x4a, 0x90, 0x00, + 0x00, 0x00, 0x03, 0x17, 0x4a, 0x90, 0x00, 0x06, + 0x14, 0x17, 0x2a, 0x4a, 0x8e, 0x9b, 0x0f, 0x00, + 0x00, 0x01, 0x2d, 0x01, 0x00, 0x00, 0x01, 0x2d, + 0x11, 0x00, 0x00, 0x02, 0x4a, 0x79, 0x04, 0x00, + 0x00, 0x03, 0x14, 0x4a, 0xa0, 0x03, 0x00, 0x0c, + 0x01, 0x4a, 0x03, 0x00, 0x01, 0x02, 0x1a, 0x2d, + 0x80, 0x8c, 0x00, 0x00, 0x02, 0x1d, 0x72, 0x00, + 0x02, 0x1d, 0x29, 0x01, 0x02, 0x1d, 0x4a, 0x00, + 0x02, 0x1d, 0x29, 0x80, 0x80, 0x00, 0x00, 0x03, + 0x05, 0x28, 0x29, 0x80, 0x01, 0x00, 0x00, 0x07, + 0x04, 0x2b, 0x69, 0x34, 0x90, 0x9a, 0xa8, 0x0d, + 0x00, 0x00, 0x07, 0x04, 0x2b, 0x69, 0x34, 0x90, + 0x9a, 0xa8, 0x00, 0x03, 0x04, 0x90, 0x9a, 0x01, + 0x00, 0x00, 0x08, 0x01, 0x04, 0x2b, 0x69, 0x34, + 0x90, 0x9a, 0xa8, 0x1f, 0x00, 0x00, 0x09, 0x01, + 0x04, 0x55, 0x56, 0x77, 0x80, 0x34, 0x8a, 0x90, + 0x09, 0x00, 0x0a, 0x02, 0x04, 0x90, 0x09, 0x00, + 0x09, 0x03, 0x04, 0x9a, 0xa8, 0x05, 0x00, 0x00, + 0x02, 0x04, 0x90, 0x62, 0x00, 0x00, 0x02, 0x04, + 0x34, 0x81, 0xfb, 0x00, 0x00, 0x0d, 0x0b, 0x20, + 0x2c, 0x2e, 0x30, 0x3f, 0x4a, 0x54, 0x78, 0x85, + 0x97, 0x99, 0x9e, 0x00, 0x0c, 0x0b, 0x20, 0x2c, + 0x2e, 0x30, 0x3f, 0x4a, 0x54, 0x78, 0x97, 0x99, + 0x9e, 0x10, 0x00, 0x00, 0x15, 0x0b, 0x20, 0x22, + 0x2f, 0x58, 0x2c, 0x2e, 0x30, 0x3f, 0x53, 0x54, + 0x66, 0x6e, 0x78, 0x47, 0x89, 0x8f, 0x96, 0x97, + 0x99, 0x9e, 0x00, 0x17, 0x0b, 0x20, 0x22, 0x2f, + 0x58, 0x2c, 0x2e, 0x31, 0x30, 0x3f, 0x4c, 0x53, + 0x54, 0x66, 0x6e, 0x78, 0x47, 0x89, 0x8f, 0x96, + 0x97, 0x99, 0x9e, 0x09, 0x04, 0x20, 0x22, 0x3e, + 0x53, 0x75, 0x00, 0x09, 0x03, 0x0b, 0x15, 0x8f, + 0x75, 0x00, 0x09, 0x02, 0x30, 0x62, 0x75, 0x00, + 0x09, 0x02, 0x2e, 0x45, 0x80, 0x75, 0x00, 0x0d, + 0x02, 0x2c, 0x97, 0x80, 0x71, 0x00, 0x09, 0x03, + 0x3f, 0x66, 0xa2, 0x82, 0xcf, 0x00, 0x09, 0x03, + 0x15, 0x63, 0x93, 0x80, 0x30, 0x00, 0x00, 0x03, + 0x28, 0x29, 0x4a, 0x85, 0x6e, 0x00, 0x02, 0x01, + 0x82, 0x46, 0x00, 0x01, 0x04, 0x11, 0x35, 0x92, + 0x91, 0x80, 0x4a, 0x00, 0x01, 0x02, 0x60, 0x7e, + 0x00, 0x00, 0x00, 0x02, 0x60, 0x7e, 0x84, 0x49, + 0x00, 0x00, 0x04, 0x0b, 0x20, 0x2c, 0x3f, 0x00, + 0x01, 0x20, 0x00, 0x04, 0x0b, 0x20, 0x2c, 0x3f, + 0x00, 0x03, 0x20, 0x2c, 0x3f, 0x00, 0x01, 0x20, + 0x01, 0x02, 0x0b, 0x20, 0x00, 0x02, 0x20, 0x85, + 0x00, 0x02, 0x0b, 0x20, 0x00, 0x02, 0x20, 0x85, + 0x00, 0x06, 0x20, 0x3f, 0x54, 0x78, 0x97, 0x99, + 0x00, 0x01, 0x20, 0x01, 0x02, 0x20, 0x85, 0x01, + 0x01, 0x20, 0x00, 0x02, 0x20, 0x85, 0x00, 0x02, + 0x0b, 0x20, 0x06, 0x01, 0x20, 0x00, 0x02, 0x20, + 0x66, 0x00, 0x02, 0x0b, 0x20, 0x01, 0x01, 0x20, + 0x00, 0x02, 0x0b, 0x20, 0x03, 0x01, 0x20, 0x00, + 0x0b, 0x0b, 0x20, 0x2c, 0x3f, 0x54, 0x66, 0x78, + 0x89, 0x99, 0x9e, 0xa2, 0x00, 0x02, 0x20, 0x2c, + 0x00, 0x04, 0x20, 0x2c, 0x3f, 0xa2, 0x01, 0x02, + 0x0b, 0x20, 0x00, 0x01, 0x0b, 0x01, 0x02, 0x20, + 0x2c, 0x00, 0x01, 0x66, 0x80, 0x44, 0x00, 0x01, + 0x01, 0x2d, 0x35, 0x00, 0x00, 0x03, 0x1d, 0x4a, + 0x90, 0x00, 0x00, 0x00, 0x01, 0x90, 0x81, 0xb3, + 0x00, 0x00, 0x03, 0x4a, 0x60, 0x7e, 0x1e, 0x00, + 0x00, 0x02, 0x01, 0x04, 0x09, 0x00, 0x00, 0x06, + 0x13, 0x28, 0x29, 0x6f, 0x50, 0x76, 0x01, 0x00, + 0x00, 0x04, 0x13, 0x2d, 0x6f, 0x5d, 0x80, 0x11, + 0x00, 0x00, 0x03, 0x20, 0x2c, 0x4a, 0x8c, 0xa5, + 0x00, 0x00, 0x02, 0x1a, 0x4a, 0x17, 0x00, 0x00, + 0x02, 0x06, 0x76, 0x00, 0x07, 0x06, 0x13, 0x28, + 0x6f, 0x3e, 0x51, 0x83, 0x09, 0x00, 0x00, 0x01, + 0x23, 0x03, 0x00, 0x00, 0x03, 0x01, 0x04, 0x6f, + 0x00, 0x00, 0x00, 0x02, 0x1d, 0x29, 0x81, 0x2b, + 0x00, 0x0f, 0x02, 0x32, 0x98, 0x00, 0x00, 0x00, + 0x07, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, 0xa9, + 0x00, 0x08, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, + 0x7e, 0xa9, 0x00, 0x05, 0x0d, 0x33, 0x32, 0x38, + 0x40, 0x01, 0x00, 0x00, 0x01, 0x32, 0x00, 0x00, + 0x01, 0x08, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, + 0x9c, 0xa9, 0x01, 0x09, 0x0d, 0x33, 0x32, 0x38, + 0x40, 0x4f, 0x60, 0x9c, 0xa9, 0x05, 0x06, 0x0d, + 0x33, 0x32, 0x38, 0x40, 0xa9, 0x00, 0x00, 0x00, + 0x05, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x07, 0x06, + 0x0d, 0x33, 0x32, 0x38, 0x40, 0xa9, 0x03, 0x05, + 0x0d, 0x33, 0x32, 0x38, 0x40, 0x09, 0x00, 0x03, + 0x02, 0x0d, 0x32, 0x01, 0x00, 0x00, 0x05, 0x0d, + 0x33, 0x32, 0x38, 0x40, 0x04, 0x02, 0x38, 0x40, + 0x00, 0x00, 0x00, 0x05, 0x0d, 0x33, 0x32, 0x38, + 0x40, 0x03, 0x00, 0x01, 0x03, 0x32, 0x38, 0x40, + 0x01, 0x01, 0x32, 0x58, 0x00, 0x03, 0x02, 0x38, + 0x40, 0x02, 0x00, 0x00, 0x02, 0x38, 0x40, 0x59, + 0x00, 0x00, 0x06, 0x0d, 0x33, 0x32, 0x38, 0x40, + 0xa9, 0x00, 0x02, 0x38, 0x40, 0x80, 0x12, 0x00, + 0x0f, 0x01, 0x32, 0x1f, 0x00, 0x25, 0x01, 0x32, + 0x08, 0x00, 0x00, 0x02, 0x32, 0x98, 0x2f, 0x00, + 0x27, 0x01, 0x32, 0x37, 0x00, 0x30, 0x01, 0x32, + 0x0e, 0x00, 0x0b, 0x01, 0x32, 0x32, 0x00, 0x00, + 0x01, 0x32, 0x57, 0x00, 0x18, 0x01, 0x32, 0x09, + 0x00, 0x04, 0x01, 0x32, 0x5f, 0x00, 0x1e, 0x01, + 0x32, 0xc0, 0x31, 0xef, 0x00, 0x00, 0x02, 0x1d, + 0x29, 0x80, 0x0f, 0x00, 0x07, 0x02, 0x32, 0x4a, + 0x80, 0xa7, 0x00, 0x02, 0x10, 0x20, 0x22, 0x2e, + 0x30, 0x45, 0x3f, 0x3e, 0x53, 0x54, 0x5f, 0x66, + 0x85, 0x47, 0x96, 0x9e, 0xa2, 0x02, 0x0f, 0x20, + 0x22, 0x2e, 0x30, 0x45, 0x3f, 0x3e, 0x53, 0x5f, + 0x66, 0x85, 0x47, 0x96, 0x9e, 0xa2, 0x01, 0x0b, + 0x20, 0x22, 0x2e, 0x30, 0x45, 0x3e, 0x53, 0x5f, + 0x47, 0x96, 0x9e, 0x00, 0x0c, 0x20, 0x22, 0x2e, + 0x30, 0x45, 0x3e, 0x53, 0x5f, 0x85, 0x47, 0x96, + 0x9e, 0x00, 0x0b, 0x20, 0x22, 0x2e, 0x30, 0x45, + 0x3e, 0x53, 0x5f, 0x47, 0x96, 0x9e, 0x80, 0x36, + 0x00, 0x00, 0x03, 0x0b, 0x20, 0xa2, 0x00, 0x00, + 0x00, 0x02, 0x20, 0x97, 0x39, 0x00, 0x00, 0x03, + 0x42, 0x4a, 0x63, 0x80, 0x1f, 0x00, 0x00, 0x02, + 0x10, 0x3d, 0xc0, 0x12, 0xed, 0x00, 0x01, 0x02, + 0x04, 0x69, 0x80, 0x31, 0x00, 0x00, 0x02, 0x04, + 0x9a, 0x09, 0x00, 0x00, 0x02, 0x04, 0x9a, 0x46, + 0x00, 0x01, 0x05, 0x0d, 0x33, 0x32, 0x38, 0x40, + 0x80, 0x99, 0x00, 0x04, 0x06, 0x0d, 0x33, 0x32, + 0x38, 0x40, 0xa9, 0x09, 0x00, 0x00, 0x02, 0x38, + 0x40, 0x2c, 0x00, 0x01, 0x02, 0x38, 0x40, 0x80, + 0xdf, 0x00, 0x01, 0x03, 0x1e, 0x1c, 0x4e, 0x00, + 0x02, 0x1c, 0x4e, 0x03, 0x00, 0x2c, 0x03, 0x1c, + 0x4d, 0x4e, 0x02, 0x00, 0x08, 0x02, 0x1c, 0x4e, + 0x81, 0x1f, 0x00, 0x1b, 0x02, 0x04, 0x1a, 0x87, + 0x75, 0x00, 0x00, 0x02, 0x56, 0x77, 0x87, 0x8d, + 0x00, 0x00, 0x02, 0x2c, 0x97, 0x00, 0x00, 0x00, + 0x02, 0x2c, 0x97, 0x36, 0x00, 0x01, 0x02, 0x2c, + 0x97, 0x8c, 0x12, 0x00, 0x01, 0x02, 0x2c, 0x97, + 0x00, 0x00, 0x00, 0x02, 0x2c, 0x97, 0xc0, 0x5c, + 0x4b, 0x00, 0x03, 0x01, 0x23, 0x96, 0x3b, 0x00, + 0x11, 0x01, 0x32, 0x9e, 0x5d, 0x00, 0x01, 0x01, + 0x32, 0xce, 0xcd, 0x2d, 0x00, +}; + +static const uint8_t unicode_prop_Hyphen_table[28] = { + 0xac, 0x80, 0xfe, 0x80, 0x44, 0xdb, 0x80, 0x52, + 0x7a, 0x80, 0x48, 0x08, 0x81, 0x4e, 0x04, 0x80, + 0x42, 0xe2, 0x80, 0x60, 0xcd, 0x66, 0x80, 0x40, + 0xa8, 0x80, 0xd6, 0x80, +}; + +static const uint8_t unicode_prop_Other_Math_table[200] = { + 0xdd, 0x80, 0x43, 0x70, 0x11, 0x80, 0x99, 0x09, + 0x81, 0x5c, 0x1f, 0x80, 0x9a, 0x82, 0x8a, 0x80, + 0x9f, 0x83, 0x97, 0x81, 0x8d, 0x81, 0xc0, 0x8c, + 0x18, 0x11, 0x1c, 0x91, 0x03, 0x01, 0x89, 0x00, + 0x14, 0x28, 0x11, 0x09, 0x02, 0x05, 0x13, 0x24, + 0xca, 0x21, 0x18, 0x08, 0x08, 0x00, 0x21, 0x0b, + 0x0b, 0x91, 0x09, 0x00, 0x06, 0x00, 0x29, 0x41, + 0x21, 0x83, 0x40, 0xa7, 0x08, 0x80, 0x97, 0x80, + 0x90, 0x80, 0x41, 0xbc, 0x81, 0x8b, 0x88, 0x24, + 0x21, 0x09, 0x14, 0x8d, 0x00, 0x01, 0x85, 0x97, + 0x81, 0xb8, 0x00, 0x80, 0x9c, 0x83, 0x88, 0x81, + 0x41, 0x55, 0x81, 0x9e, 0x89, 0x41, 0x92, 0x95, + 0xbe, 0x83, 0x9f, 0x81, 0x60, 0xd4, 0x62, 0x00, + 0x03, 0x80, 0x40, 0xd2, 0x00, 0x80, 0x60, 0xd4, + 0xc0, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, + 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f, + 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80, + 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x81, + 0xb1, 0x55, 0xff, 0x18, 0x9a, 0x01, 0x00, 0x08, + 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, + 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, + 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, +}; + +static const uint8_t unicode_prop_Other_Alphabetic_table[443] = { + 0x43, 0x44, 0x80, 0x9c, 0x8c, 0x42, 0x3f, 0x8d, + 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, 0x8c, + 0x06, 0x8f, 0x80, 0xe4, 0x33, 0x19, 0x0b, 0x80, + 0xa2, 0x80, 0x9d, 0x8f, 0xe5, 0x8a, 0xe4, 0x0a, + 0x88, 0x02, 0x03, 0xe9, 0x80, 0xbb, 0x8b, 0x16, + 0x85, 0x93, 0xb5, 0x09, 0x8e, 0x01, 0x22, 0x89, + 0x81, 0x9c, 0x82, 0xb9, 0x31, 0x09, 0x81, 0x89, + 0x80, 0x89, 0x81, 0x9c, 0x82, 0xb9, 0x23, 0x09, + 0x0b, 0x80, 0x9d, 0x0a, 0x80, 0x8a, 0x82, 0xb9, + 0x38, 0x10, 0x81, 0x94, 0x81, 0x95, 0x13, 0x82, + 0xb9, 0x31, 0x09, 0x81, 0x88, 0x81, 0x89, 0x81, + 0x9d, 0x80, 0xba, 0x22, 0x10, 0x82, 0x89, 0x80, + 0xa7, 0x84, 0xb8, 0x30, 0x10, 0x17, 0x81, 0x8a, + 0x81, 0x9c, 0x82, 0xb9, 0x30, 0x10, 0x17, 0x81, + 0x8a, 0x81, 0x8e, 0x80, 0x8b, 0x83, 0xb9, 0x30, + 0x10, 0x82, 0x89, 0x80, 0x89, 0x81, 0x9c, 0x82, + 0xca, 0x28, 0x00, 0x87, 0x91, 0x81, 0xbc, 0x01, + 0x86, 0x91, 0x80, 0xe2, 0x01, 0x28, 0x81, 0x8f, + 0x80, 0x40, 0xa2, 0x92, 0x88, 0x8a, 0x80, 0xa3, + 0xed, 0x8b, 0x00, 0x0b, 0x96, 0x1b, 0x10, 0x11, + 0x32, 0x83, 0x8c, 0x8b, 0x00, 0x89, 0x83, 0x46, + 0x73, 0x81, 0x9d, 0x81, 0x9d, 0x81, 0x9d, 0x81, + 0xc1, 0x92, 0x40, 0xbb, 0x81, 0xa1, 0x80, 0xf5, + 0x8b, 0x83, 0x88, 0x40, 0xdd, 0x84, 0xb8, 0x89, + 0x81, 0x93, 0xc9, 0x81, 0x8a, 0x82, 0xb0, 0x84, + 0xaf, 0x8e, 0xbb, 0x82, 0x9d, 0x88, 0x09, 0xb8, + 0x8a, 0xb1, 0x92, 0x41, 0x9b, 0xa1, 0x46, 0xc0, + 0xb3, 0x48, 0xf5, 0x9f, 0x60, 0x78, 0x73, 0x87, + 0xa1, 0x81, 0x41, 0x61, 0x07, 0x80, 0x96, 0x84, + 0xd7, 0x81, 0xb1, 0x8f, 0x00, 0xb8, 0x80, 0xa5, + 0x84, 0x9b, 0x8b, 0xac, 0x83, 0xaf, 0x8b, 0xa4, + 0x80, 0xc2, 0x8d, 0x8b, 0x07, 0x81, 0xac, 0x82, + 0xb1, 0x00, 0x11, 0x0c, 0x80, 0xab, 0x24, 0x80, + 0x40, 0xec, 0x87, 0x60, 0x4f, 0x32, 0x80, 0x48, + 0x56, 0x84, 0x46, 0x85, 0x10, 0x0c, 0x83, 0x43, + 0x13, 0x83, 0xc0, 0x80, 0x41, 0x40, 0x81, 0xce, + 0x80, 0x41, 0x02, 0x82, 0xb4, 0x8d, 0xac, 0x81, + 0x8a, 0x82, 0xac, 0x88, 0x88, 0x80, 0xbc, 0x82, + 0xa3, 0x8b, 0x91, 0x81, 0xb8, 0x82, 0xaf, 0x8c, + 0x8d, 0x81, 0xdb, 0x88, 0x08, 0x28, 0x08, 0x40, + 0x9c, 0x89, 0x96, 0x83, 0xb9, 0x31, 0x09, 0x81, + 0x89, 0x80, 0x89, 0x81, 0xd3, 0x88, 0x00, 0x08, + 0x03, 0x01, 0xe6, 0x8c, 0x02, 0xe9, 0x91, 0x40, + 0xec, 0x31, 0x86, 0x9c, 0x81, 0xd1, 0x8e, 0x00, + 0xe9, 0x8a, 0xe6, 0x8d, 0x41, 0x00, 0x8c, 0x40, + 0xf6, 0x28, 0x09, 0x0a, 0x00, 0x80, 0x40, 0x8d, + 0x31, 0x2b, 0x80, 0x9b, 0x89, 0xa9, 0x20, 0x83, + 0x91, 0x8a, 0xad, 0x8d, 0x41, 0x96, 0x38, 0x86, + 0xd2, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, + 0x10, 0x02, 0x80, 0xc1, 0x20, 0x08, 0x83, 0x41, + 0x5b, 0x83, 0x88, 0x08, 0x80, 0xaf, 0x32, 0x82, + 0x60, 0x41, 0xdc, 0x90, 0x4e, 0x1f, 0x00, 0xb6, + 0x33, 0xdc, 0x81, 0x60, 0x4c, 0xab, 0x80, 0x60, + 0x23, 0x60, 0x30, 0x90, 0x0e, 0x01, 0x04, 0xe3, + 0x80, 0x48, 0xb6, 0x80, 0x47, 0xe7, 0x99, 0x85, + 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Other_Lowercase_table[69] = { + 0x40, 0xa9, 0x80, 0x8e, 0x80, 0x41, 0xf4, 0x88, + 0x31, 0x9d, 0x84, 0xdf, 0x80, 0xb3, 0x80, 0x4d, + 0x80, 0x80, 0x4c, 0x2e, 0xbe, 0x8c, 0x80, 0xa1, + 0xa4, 0x42, 0xb0, 0x80, 0x8c, 0x80, 0x8f, 0x8c, + 0x40, 0xd2, 0x8f, 0x43, 0x4f, 0x99, 0x47, 0x91, + 0x81, 0x60, 0x7a, 0x1d, 0x81, 0x40, 0xd1, 0x80, + 0x40, 0x80, 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, + 0x80, 0x60, 0x5c, 0x15, 0x01, 0x10, 0xa9, 0x80, + 0x88, 0x60, 0xd8, 0x74, 0xbd, +}; + +static const uint8_t unicode_prop_Other_Uppercase_table[15] = { + 0x60, 0x21, 0x5f, 0x8f, 0x43, 0x45, 0x99, 0x61, + 0xcc, 0x5f, 0x99, 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Other_Grapheme_Extend_table[112] = { + 0x49, 0xbd, 0x80, 0x97, 0x80, 0x41, 0x65, 0x80, + 0x97, 0x80, 0xe5, 0x80, 0x97, 0x80, 0x40, 0xe7, + 0x00, 0x03, 0x08, 0x81, 0x88, 0x81, 0xe6, 0x80, + 0x97, 0x80, 0xf6, 0x80, 0x8e, 0x80, 0x49, 0x34, + 0x80, 0x9d, 0x80, 0x43, 0xff, 0x04, 0x00, 0x04, + 0x81, 0xe4, 0x80, 0xc6, 0x81, 0x44, 0x17, 0x80, + 0x50, 0x20, 0x81, 0x60, 0x79, 0x22, 0x80, 0xeb, + 0x80, 0x60, 0x55, 0xdc, 0x81, 0x52, 0x1f, 0x80, + 0xf3, 0x80, 0x41, 0x07, 0x80, 0x8d, 0x80, 0x88, + 0x80, 0xdf, 0x80, 0x88, 0x01, 0x00, 0x14, 0x80, + 0x40, 0xdf, 0x80, 0x8b, 0x80, 0x40, 0xf0, 0x80, + 0x41, 0x05, 0x80, 0x42, 0x78, 0x80, 0x8b, 0x80, + 0x46, 0x02, 0x80, 0x60, 0x50, 0xad, 0x81, 0x60, + 0x61, 0x72, 0x0d, 0x85, 0x6c, 0x2e, 0xac, 0xdf, +}; + +static const uint8_t unicode_prop_Other_Default_Ignorable_Code_Point_table[32] = { + 0x43, 0x4e, 0x80, 0x4e, 0x0e, 0x81, 0x46, 0x52, + 0x81, 0x48, 0xae, 0x80, 0x50, 0xfd, 0x80, 0x60, + 0xce, 0x3a, 0x80, 0xce, 0x88, 0x6d, 0x00, 0x06, + 0x00, 0x9d, 0xdf, 0xff, 0x40, 0xef, 0x4e, 0x0f, +}; + +static const uint8_t unicode_prop_Other_ID_Start_table[11] = { + 0x58, 0x84, 0x81, 0x48, 0x90, 0x80, 0x94, 0x80, + 0x4f, 0x6b, 0x81, +}; + +static const uint8_t unicode_prop_Other_ID_Continue_table[22] = { + 0x40, 0xb6, 0x80, 0x42, 0xce, 0x80, 0x4f, 0xe0, + 0x88, 0x46, 0x67, 0x80, 0x46, 0x30, 0x81, 0x50, + 0xec, 0x80, 0x60, 0xce, 0x68, 0x80, +}; + +static const uint8_t unicode_prop_Prepended_Concatenation_Mark_table[19] = { + 0x45, 0xff, 0x85, 0x40, 0xd6, 0x80, 0xb0, 0x80, + 0x41, 0x7f, 0x81, 0xcf, 0x80, 0x61, 0x07, 0xd9, + 0x80, 0x8e, 0x80, +}; + +static const uint8_t unicode_prop_XID_Start1_table[31] = { + 0x43, 0x79, 0x80, 0x4a, 0xb7, 0x80, 0xfe, 0x80, + 0x60, 0x21, 0xe6, 0x81, 0x60, 0xcb, 0xc0, 0x85, + 0x41, 0x95, 0x81, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x41, 0x1e, 0x81, +}; + +static const uint8_t unicode_prop_XID_Continue1_table[23] = { + 0x43, 0x79, 0x80, 0x60, 0x2d, 0x1f, 0x81, 0x60, + 0xcb, 0xc0, 0x85, 0x41, 0x95, 0x81, 0xf3, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_Titlecased1_table[22] = { + 0x41, 0xc3, 0x08, 0x08, 0x81, 0xa4, 0x81, 0x4e, + 0xdc, 0xaa, 0x0a, 0x4e, 0x87, 0x3f, 0x3f, 0x87, + 0x8b, 0x80, 0x8e, 0x80, 0xae, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_Casefolded1_table[29] = { + 0x41, 0xef, 0x80, 0x41, 0x9e, 0x80, 0x9e, 0x80, + 0x5a, 0xe4, 0x83, 0x40, 0xb5, 0x00, 0x00, 0x00, + 0x80, 0xde, 0x06, 0x06, 0x80, 0x8a, 0x09, 0x81, + 0x89, 0x10, 0x81, 0x8d, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[450] = { + 0x40, 0x9f, 0x06, 0x00, 0x01, 0x00, 0x01, 0x12, + 0x10, 0x82, 0xf3, 0x80, 0x8b, 0x80, 0x40, 0x84, + 0x01, 0x01, 0x80, 0xa2, 0x01, 0x80, 0x40, 0xbb, + 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08, 0x81, 0x89, + 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08, 0x07, 0x80, + 0x9e, 0x80, 0xa0, 0x82, 0x9c, 0x80, 0x42, 0x28, + 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87, 0xfb, 0x08, + 0x80, 0xd2, 0x01, 0x80, 0xa1, 0x11, 0x80, 0x40, + 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe, 0x80, 0xa7, + 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88, 0x03, 0x03, + 0x03, 0x80, 0x8b, 0x80, 0x88, 0x00, 0x26, 0x80, + 0x90, 0x80, 0x88, 0x03, 0x03, 0x03, 0x80, 0x8b, + 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81, 0x46, 0x52, + 0x81, 0xd4, 0x84, 0x45, 0x1b, 0x10, 0x8a, 0x80, + 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1, 0xa4, 0x40, + 0xd5, 0x83, 0x40, 0xb5, 0x00, 0x00, 0x00, 0x80, + 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xb7, 0x05, 0x00, 0x13, 0x05, 0x11, 0x02, 0x0c, + 0x11, 0x00, 0x00, 0x0c, 0x15, 0x05, 0x08, 0x8f, + 0x00, 0x20, 0x8b, 0x12, 0x2a, 0x08, 0x0b, 0x00, + 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a, 0x80, + 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a, 0x01, + 0x0c, 0x0a, 0x00, 0x10, 0x11, 0x02, 0x06, 0x05, + 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80, 0x40, + 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41, 0x34, + 0xd5, 0x99, 0x9a, 0x45, 0x20, 0x80, 0xe6, 0x82, + 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0, 0x80, + 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40, 0xd5, + 0xa9, 0x80, 0xb4, 0x00, 0x82, 0xdf, 0x09, 0x80, + 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf, 0x9e, + 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f, 0x60, + 0x72, 0x9b, 0x81, 0x40, 0xd1, 0x80, 0x40, 0x80, + 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, 0x80, 0x60, + 0x4d, 0x95, 0x41, 0x0d, 0x08, 0x00, 0x81, 0x89, + 0x00, 0x00, 0x09, 0x82, 0xc3, 0x81, 0xe9, 0xc2, + 0x00, 0x97, 0x04, 0x00, 0x01, 0x01, 0x80, 0xeb, + 0xa0, 0x41, 0x6a, 0x91, 0xbf, 0x81, 0xb5, 0xa7, + 0x8c, 0x82, 0x99, 0x95, 0x94, 0x81, 0x8b, 0x80, + 0x92, 0x03, 0x1a, 0x00, 0x80, 0x40, 0x86, 0x08, + 0x80, 0x9f, 0x99, 0x40, 0x83, 0x15, 0x0d, 0x0d, + 0x0a, 0x16, 0x06, 0x80, 0x88, 0x47, 0x87, 0x20, + 0xa9, 0x80, 0x88, 0x60, 0xb4, 0xe4, 0x83, 0x50, + 0x31, 0xa3, 0x44, 0x63, 0x86, 0x8d, 0x87, 0xbf, + 0x85, 0x42, 0x3e, 0xd4, 0x80, 0xc6, 0x01, 0x08, + 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, + 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, + 0x16, 0x80, 0x41, 0x53, 0x81, 0x41, 0x23, 0x81, + 0xb1, 0x48, 0x2f, 0xbd, 0x4d, 0x91, 0x18, 0x9a, + 0x01, 0x00, 0x08, 0x80, 0x89, 0x03, 0x00, 0x00, + 0x28, 0x18, 0x00, 0x00, 0x02, 0x01, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x06, + 0x03, 0x03, 0x00, 0x80, 0x89, 0x80, 0x90, 0x22, + 0x04, 0x80, 0x90, 0x42, 0x43, 0x8a, 0x84, 0x9e, + 0x80, 0x9f, 0x99, 0x82, 0xa2, 0x80, 0xee, 0x82, + 0x8c, 0xab, 0x83, 0x88, 0x31, 0x49, 0x9d, 0x89, + 0x60, 0xfc, 0x05, 0x42, 0x1d, 0x6b, 0x05, 0xe1, + 0x4f, 0xff, +}; + +static const uint8_t unicode_prop_ASCII_Hex_Digit_table[5] = { + 0xaf, 0x89, 0x35, 0x99, 0x85, +}; + +static const uint8_t unicode_prop_Bidi_Control_table[10] = { + 0x46, 0x1b, 0x80, 0x59, 0xf0, 0x81, 0x99, 0x84, + 0xb6, 0x83, +}; + +static const uint8_t unicode_prop_Dash_table[58] = { + 0xac, 0x80, 0x45, 0x5b, 0x80, 0xb2, 0x80, 0x4e, + 0x40, 0x80, 0x44, 0x04, 0x80, 0x48, 0x08, 0x85, + 0xbc, 0x80, 0xa6, 0x80, 0x8e, 0x80, 0x41, 0x85, + 0x80, 0x4c, 0x03, 0x01, 0x80, 0x9e, 0x0b, 0x80, + 0x9b, 0x80, 0x41, 0xbd, 0x80, 0x92, 0x80, 0xee, + 0x80, 0x60, 0xcd, 0x8f, 0x81, 0xa4, 0x80, 0x89, + 0x80, 0x40, 0xa8, 0x80, 0x4e, 0x5f, 0x80, 0x41, + 0x3d, 0x80, +}; + +static const uint8_t unicode_prop_Deprecated_table[23] = { + 0x41, 0x48, 0x80, 0x45, 0x28, 0x80, 0x49, 0x02, + 0x00, 0x80, 0x48, 0x28, 0x81, 0x48, 0xc4, 0x85, + 0x42, 0xb8, 0x81, 0x6d, 0xdc, 0xd5, 0x80, +}; + +static const uint8_t unicode_prop_Diacritic_table[438] = { + 0xdd, 0x00, 0x80, 0xc6, 0x05, 0x03, 0x01, 0x81, + 0x41, 0xf6, 0x40, 0x9e, 0x07, 0x25, 0x90, 0x0b, + 0x80, 0x88, 0x81, 0x40, 0xfc, 0x84, 0x40, 0xd0, + 0x80, 0xb6, 0x90, 0x80, 0x9a, 0x00, 0x01, 0x00, + 0x40, 0x85, 0x3b, 0x81, 0x40, 0x85, 0x0b, 0x0a, + 0x82, 0xc2, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0xa1, + 0x81, 0xfd, 0x87, 0xa8, 0x89, 0x8f, 0x9b, 0xbc, + 0x80, 0x8f, 0x02, 0x83, 0x9b, 0x80, 0xc9, 0x80, + 0x8f, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed, 0x80, + 0x8f, 0x80, 0xae, 0x82, 0xbb, 0x80, 0x8f, 0x06, + 0x80, 0xf6, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed, + 0x80, 0x8f, 0x80, 0xec, 0x81, 0x8f, 0x80, 0xfb, + 0x80, 0xee, 0x80, 0x8b, 0x28, 0x80, 0xea, 0x80, + 0x8c, 0x84, 0xca, 0x81, 0x9a, 0x00, 0x00, 0x03, + 0x81, 0xc1, 0x10, 0x81, 0xbd, 0x80, 0xef, 0x00, + 0x81, 0xa7, 0x0b, 0x84, 0x98, 0x30, 0x80, 0x89, + 0x81, 0x42, 0xc0, 0x82, 0x43, 0xb3, 0x81, 0x9d, + 0x80, 0x40, 0x93, 0x8a, 0x88, 0x80, 0x41, 0x5a, + 0x82, 0x41, 0x23, 0x80, 0x93, 0x39, 0x80, 0xaf, + 0x8e, 0x81, 0x8a, 0xe7, 0x80, 0x8e, 0x80, 0xa5, + 0x88, 0xb5, 0x81, 0xb9, 0x80, 0x8a, 0x81, 0xc1, + 0x81, 0xbf, 0x85, 0xd1, 0x98, 0x18, 0x28, 0x0a, + 0xb1, 0xbe, 0xd8, 0x8b, 0xa4, 0x8a, 0x41, 0xbc, + 0x00, 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c, 0x82, + 0x8c, 0x81, 0x4c, 0xef, 0x82, 0x41, 0x3c, 0x80, + 0x41, 0xf9, 0x85, 0xe8, 0x83, 0xde, 0x80, 0x60, + 0x75, 0x71, 0x80, 0x8b, 0x08, 0x80, 0x9b, 0x81, + 0xd1, 0x81, 0x8d, 0xa1, 0xe5, 0x82, 0xec, 0x81, + 0x8b, 0x80, 0xa4, 0x80, 0x40, 0x96, 0x80, 0x9a, + 0x91, 0xb8, 0x83, 0xa3, 0x80, 0xde, 0x80, 0x8b, + 0x80, 0xa3, 0x80, 0x40, 0x94, 0x82, 0xc0, 0x83, + 0xb2, 0x80, 0xe3, 0x84, 0x88, 0x82, 0xff, 0x81, + 0x60, 0x4f, 0x2f, 0x80, 0x43, 0x00, 0x8f, 0x41, + 0x0d, 0x00, 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2, + 0x80, 0x42, 0xfb, 0x80, 0x44, 0x9e, 0x28, 0xa9, + 0x80, 0x88, 0x42, 0x7c, 0x13, 0x80, 0x40, 0xa4, + 0x81, 0x42, 0x3a, 0x85, 0xa5, 0x80, 0x99, 0x84, + 0x41, 0x8e, 0x82, 0xc5, 0x8a, 0xb0, 0x83, 0x40, + 0xbf, 0x80, 0xa8, 0x80, 0xc7, 0x81, 0xf7, 0x81, + 0xbd, 0x80, 0xcb, 0x80, 0x88, 0x82, 0xe7, 0x81, + 0x40, 0xb1, 0x81, 0xcf, 0x81, 0x8f, 0x80, 0x97, + 0x32, 0x84, 0xd8, 0x10, 0x81, 0x8c, 0x81, 0xde, + 0x02, 0x80, 0xfa, 0x81, 0x40, 0xfa, 0x81, 0xfd, + 0x80, 0xf5, 0x81, 0xf2, 0x80, 0x41, 0x0c, 0x81, + 0x41, 0x01, 0x0b, 0x80, 0x40, 0x9b, 0x80, 0xd2, + 0x80, 0x91, 0x80, 0xd0, 0x80, 0x41, 0xa4, 0x80, + 0x41, 0x01, 0x00, 0x81, 0xd0, 0x80, 0x41, 0xa8, + 0x81, 0x96, 0x80, 0x54, 0xeb, 0x8e, 0x60, 0x2c, + 0xd8, 0x80, 0x49, 0xbf, 0x84, 0xba, 0x86, 0x42, + 0x33, 0x81, 0x42, 0x21, 0x90, 0xcf, 0x81, 0x60, + 0x3f, 0xfd, 0x18, 0x30, 0x81, 0x5f, 0x00, 0xad, + 0x81, 0x96, 0x42, 0x1f, 0x12, 0x2f, 0x39, 0x86, + 0x9d, 0x83, 0x4e, 0x81, 0xbd, 0x40, 0xc1, 0x86, + 0x41, 0x76, 0x80, 0xbc, 0x83, 0x42, 0xfd, 0x81, + 0x42, 0xdf, 0x86, 0xec, 0x10, 0x82, +}; + +static const uint8_t unicode_prop_Extender_table[111] = { + 0x40, 0xb6, 0x80, 0x42, 0x17, 0x81, 0x43, 0x6d, + 0x80, 0x41, 0xb8, 0x80, 0x42, 0x75, 0x80, 0x40, + 0x88, 0x80, 0xd8, 0x80, 0x42, 0xef, 0x80, 0xfe, + 0x80, 0x49, 0x42, 0x80, 0xb7, 0x80, 0x42, 0x62, + 0x80, 0x41, 0x8d, 0x80, 0xc3, 0x80, 0x53, 0x88, + 0x80, 0xaa, 0x84, 0xe6, 0x81, 0xdc, 0x82, 0x60, + 0x6f, 0x15, 0x80, 0x45, 0xf5, 0x80, 0x43, 0xc1, + 0x80, 0x95, 0x80, 0x40, 0x88, 0x80, 0xeb, 0x80, + 0x94, 0x81, 0x60, 0x54, 0x7a, 0x80, 0x48, 0x0f, + 0x81, 0x45, 0xca, 0x80, 0x9a, 0x03, 0x80, 0x44, + 0xc6, 0x80, 0x41, 0x24, 0x80, 0xf3, 0x81, 0x41, + 0xf1, 0x82, 0x44, 0xce, 0x80, 0x60, 0x50, 0xa8, + 0x81, 0x44, 0x9b, 0x08, 0x80, 0x60, 0x71, 0x57, + 0x81, 0x44, 0xb0, 0x80, 0x43, 0x53, 0x82, +}; + +static const uint8_t unicode_prop_Hex_Digit_table[12] = { + 0xaf, 0x89, 0x35, 0x99, 0x85, 0x60, 0xfe, 0xa8, + 0x89, 0x35, 0x99, 0x85, +}; + +static const uint8_t unicode_prop_IDS_Unary_Operator_table[4] = { + 0x60, 0x2f, 0xfd, 0x81, +}; + +static const uint8_t unicode_prop_IDS_Binary_Operator_table[8] = { + 0x60, 0x2f, 0xef, 0x09, 0x89, 0x41, 0xf0, 0x80, +}; + +static const uint8_t unicode_prop_IDS_Trinary_Operator_table[4] = { + 0x60, 0x2f, 0xf1, 0x81, +}; + +static const uint8_t unicode_prop_Ideographic_table[72] = { + 0x60, 0x30, 0x05, 0x81, 0x98, 0x88, 0x8d, 0x82, + 0x43, 0xc4, 0x59, 0xbf, 0xbf, 0x60, 0x51, 0xff, + 0x60, 0x58, 0xff, 0x41, 0x6d, 0x81, 0xe9, 0x60, + 0x75, 0x09, 0x80, 0x9a, 0x57, 0xf7, 0x87, 0x44, + 0xd5, 0xa8, 0x89, 0x60, 0x24, 0x66, 0x41, 0x8b, + 0x60, 0x4d, 0x03, 0x60, 0xa6, 0xdf, 0x9f, 0x50, + 0x39, 0x85, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, + 0x5d, 0x30, 0x8e, 0x42, 0x6d, 0x49, 0xa1, 0x42, + 0x1d, 0x45, 0xe1, 0x53, 0x4a, 0x84, 0x50, 0x5f, +}; + +static const uint8_t unicode_prop_Join_Control_table[4] = { + 0x60, 0x20, 0x0b, 0x81, +}; + +static const uint8_t unicode_prop_Logical_Order_Exception_table[15] = { + 0x4e, 0x3f, 0x84, 0xfa, 0x84, 0x4a, 0xef, 0x11, + 0x80, 0x60, 0x90, 0xf9, 0x09, 0x00, 0x81, +}; + +static const uint8_t unicode_prop_Modifier_Combining_Mark_table[16] = { + 0x46, 0x53, 0x09, 0x80, 0x40, 0x82, 0x05, 0x02, + 0x81, 0x41, 0xe0, 0x08, 0x12, 0x80, 0x9e, 0x80, +}; + +static const uint8_t unicode_prop_Noncharacter_Code_Point_table[71] = { + 0x60, 0xfd, 0xcf, 0x9f, 0x42, 0x0d, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, +}; + +static const uint8_t unicode_prop_Pattern_Syntax_table[58] = { + 0xa0, 0x8e, 0x89, 0x86, 0x99, 0x18, 0x80, 0x99, + 0x83, 0xa1, 0x30, 0x00, 0x08, 0x00, 0x0b, 0x03, + 0x02, 0x80, 0x96, 0x80, 0x9e, 0x80, 0x5f, 0x17, + 0x97, 0x87, 0x8e, 0x81, 0x92, 0x80, 0x89, 0x41, + 0x30, 0x42, 0xcf, 0x40, 0x9f, 0x42, 0x75, 0x9d, + 0x44, 0x6b, 0x41, 0xff, 0xff, 0x41, 0x80, 0x13, + 0x98, 0x8e, 0x80, 0x60, 0xcd, 0x0c, 0x81, 0x41, + 0x04, 0x81, +}; + +static const uint8_t unicode_prop_Pattern_White_Space_table[11] = { + 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x5f, 0x87, + 0x81, 0x97, 0x81, +}; + +static const uint8_t unicode_prop_Quotation_Mark_table[31] = { + 0xa1, 0x03, 0x80, 0x40, 0x82, 0x80, 0x8e, 0x80, + 0x5f, 0x5b, 0x87, 0x98, 0x81, 0x4e, 0x06, 0x80, + 0x41, 0xc8, 0x83, 0x8c, 0x82, 0x60, 0xce, 0x20, + 0x83, 0x40, 0xbc, 0x03, 0x80, 0xd9, 0x81, +}; + +static const uint8_t unicode_prop_Radical_table[9] = { + 0x60, 0x2e, 0x7f, 0x99, 0x80, 0xd8, 0x8b, 0x40, + 0xd5, +}; + +static const uint8_t unicode_prop_Regional_Indicator_table[4] = { + 0x61, 0xf1, 0xe5, 0x99, +}; + +static const uint8_t unicode_prop_Sentence_Terminal_table[213] = { + 0xa0, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0x45, 0x48, + 0x80, 0x40, 0x92, 0x82, 0x40, 0xb3, 0x80, 0xaa, + 0x82, 0x40, 0xf5, 0x80, 0xbc, 0x00, 0x02, 0x81, + 0x41, 0x24, 0x81, 0x46, 0xe3, 0x81, 0x43, 0x15, + 0x03, 0x81, 0x43, 0x04, 0x80, 0x40, 0xc5, 0x81, + 0x40, 0x9c, 0x81, 0xac, 0x04, 0x80, 0x41, 0x39, + 0x81, 0x41, 0x61, 0x83, 0x40, 0xa1, 0x81, 0x89, + 0x09, 0x81, 0x9c, 0x82, 0x40, 0xba, 0x81, 0xc0, + 0x81, 0x43, 0xa3, 0x80, 0x96, 0x81, 0x88, 0x82, + 0x4c, 0xae, 0x82, 0x41, 0x31, 0x80, 0x8c, 0x80, + 0x95, 0x81, 0x41, 0xac, 0x80, 0x60, 0x74, 0xfb, + 0x80, 0x41, 0x0d, 0x81, 0x40, 0xe2, 0x02, 0x80, + 0x41, 0x7d, 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, + 0x97, 0x81, 0x40, 0x92, 0x82, 0x40, 0x8f, 0x81, + 0x40, 0xf8, 0x80, 0x60, 0x52, 0x25, 0x01, 0x81, + 0xba, 0x02, 0x81, 0x40, 0xa8, 0x80, 0x8b, 0x80, + 0x8f, 0x80, 0xc0, 0x80, 0x4a, 0xf3, 0x81, 0x44, + 0xfc, 0x84, 0xab, 0x83, 0x40, 0xbc, 0x81, 0xf4, + 0x83, 0xfe, 0x82, 0x40, 0x80, 0x0d, 0x80, 0x8f, + 0x81, 0xd7, 0x08, 0x81, 0xeb, 0x80, 0x41, 0x29, + 0x81, 0xf4, 0x81, 0x41, 0x74, 0x0c, 0x8e, 0xe8, + 0x81, 0x40, 0xf8, 0x82, 0x42, 0x04, 0x00, 0x80, + 0x40, 0xfa, 0x81, 0xd6, 0x81, 0x41, 0xa3, 0x81, + 0x42, 0xb3, 0x81, 0xc9, 0x81, 0x60, 0x4b, 0x28, + 0x81, 0x40, 0x84, 0x80, 0xc0, 0x81, 0x8a, 0x80, + 0x42, 0x28, 0x81, 0x41, 0x27, 0x80, 0x60, 0x4e, + 0x05, 0x80, 0x5d, 0xe7, 0x80, +}; + +static const uint8_t unicode_prop_Soft_Dotted_table[79] = { + 0xe8, 0x81, 0x40, 0xc3, 0x80, 0x41, 0x18, 0x80, + 0x9d, 0x80, 0xb3, 0x80, 0x93, 0x80, 0x41, 0x3f, + 0x80, 0xe1, 0x00, 0x80, 0x59, 0x08, 0x80, 0xb2, + 0x80, 0x8c, 0x02, 0x80, 0x40, 0x83, 0x80, 0x40, + 0x9c, 0x80, 0x41, 0xa4, 0x80, 0x40, 0xd5, 0x81, + 0x4b, 0x31, 0x80, 0x61, 0xa7, 0xa4, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0x48, + 0x85, 0x80, 0x41, 0x30, 0x81, 0x99, 0x80, +}; + +static const uint8_t unicode_prop_Terminal_Punctuation_table[264] = { + 0xa0, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80, + 0x43, 0x3d, 0x07, 0x80, 0x42, 0x00, 0x80, 0xb8, + 0x80, 0xc7, 0x80, 0x8d, 0x00, 0x82, 0x40, 0xb3, + 0x80, 0xaa, 0x8a, 0x00, 0x40, 0xea, 0x81, 0xb5, + 0x28, 0x87, 0x9e, 0x80, 0x41, 0x04, 0x81, 0x44, + 0xf3, 0x81, 0x40, 0xab, 0x03, 0x85, 0x41, 0x36, + 0x81, 0x43, 0x14, 0x87, 0x43, 0x04, 0x80, 0xfb, + 0x82, 0xc6, 0x81, 0x40, 0x9c, 0x12, 0x80, 0xa6, + 0x19, 0x81, 0x41, 0x39, 0x81, 0x41, 0x61, 0x83, + 0x40, 0xa1, 0x81, 0x89, 0x08, 0x82, 0x9c, 0x82, + 0x40, 0xba, 0x84, 0xbd, 0x81, 0x43, 0xa3, 0x80, + 0x96, 0x81, 0x88, 0x82, 0x4c, 0xae, 0x82, 0x41, + 0x31, 0x80, 0x8c, 0x03, 0x80, 0x89, 0x00, 0x0a, + 0x81, 0x41, 0xab, 0x81, 0x60, 0x74, 0xfa, 0x81, + 0x41, 0x0c, 0x82, 0x40, 0xe2, 0x84, 0x41, 0x7d, + 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, 0x96, 0x82, + 0x40, 0x92, 0x82, 0xfe, 0x80, 0x8f, 0x81, 0x40, + 0xf8, 0x80, 0x60, 0x52, 0x25, 0x01, 0x81, 0xb8, + 0x10, 0x83, 0x40, 0xa8, 0x80, 0x89, 0x00, 0x80, + 0x8a, 0x0a, 0x80, 0xc0, 0x01, 0x80, 0x44, 0x39, + 0x80, 0xaf, 0x80, 0x44, 0x85, 0x80, 0x40, 0xc6, + 0x80, 0x41, 0x35, 0x81, 0x40, 0x97, 0x85, 0xc3, + 0x85, 0xd8, 0x83, 0x43, 0xb7, 0x84, 0xab, 0x83, + 0x40, 0xbc, 0x86, 0xef, 0x83, 0xfe, 0x82, 0x40, + 0x80, 0x0d, 0x80, 0x8f, 0x81, 0xd7, 0x84, 0xeb, + 0x80, 0x41, 0x29, 0x81, 0xf4, 0x82, 0x8b, 0x81, + 0x41, 0x65, 0x1a, 0x8e, 0xe8, 0x81, 0x40, 0xf8, + 0x82, 0x42, 0x04, 0x00, 0x80, 0x40, 0xfa, 0x81, + 0xd6, 0x0b, 0x81, 0x41, 0x9d, 0x82, 0xac, 0x80, + 0x42, 0x84, 0x81, 0xc9, 0x81, 0x45, 0x2a, 0x84, + 0x60, 0x45, 0xf8, 0x81, 0x40, 0x84, 0x80, 0xc0, + 0x82, 0x89, 0x80, 0x42, 0x28, 0x81, 0x41, 0x26, + 0x81, 0x60, 0x4e, 0x05, 0x80, 0x5d, 0xe6, 0x83, +}; + +static const uint8_t unicode_prop_Unified_Ideograph_table[48] = { + 0x60, 0x33, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x51, + 0xff, 0x60, 0x5a, 0x0d, 0x08, 0x00, 0x81, 0x89, + 0x00, 0x00, 0x09, 0x82, 0x61, 0x05, 0xd5, 0x60, + 0xa6, 0xdf, 0x9f, 0x50, 0x39, 0x85, 0x40, 0xdd, + 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x8e, 0x42, + 0x6d, 0x51, 0xa1, 0x53, 0x4a, 0x84, 0x50, 0x5f, +}; + +static const uint8_t unicode_prop_Variation_Selector_table[13] = { + 0x58, 0x0a, 0x10, 0x80, 0x60, 0xe5, 0xef, 0x8f, + 0x6d, 0x02, 0xef, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_Bidi_Mirrored_table[173] = { + 0xa7, 0x81, 0x91, 0x00, 0x80, 0x9b, 0x00, 0x80, + 0x9c, 0x00, 0x80, 0xac, 0x80, 0x8e, 0x80, 0x4e, + 0x7d, 0x83, 0x47, 0x5c, 0x81, 0x49, 0x9b, 0x81, + 0x89, 0x81, 0xb5, 0x81, 0x8d, 0x81, 0x40, 0xb0, + 0x80, 0x40, 0xbf, 0x1a, 0x2a, 0x02, 0x0a, 0x18, + 0x18, 0x00, 0x03, 0x88, 0x20, 0x80, 0x91, 0x23, + 0x88, 0x08, 0x00, 0x38, 0x9f, 0x0b, 0x20, 0x88, + 0x09, 0x92, 0x21, 0x88, 0x21, 0x0b, 0x97, 0x81, + 0x8f, 0x3b, 0x93, 0x0e, 0x81, 0x44, 0x3c, 0x8d, + 0xc9, 0x01, 0x18, 0x08, 0x14, 0x1c, 0x12, 0x8d, + 0x41, 0x92, 0x95, 0x0d, 0x80, 0x8d, 0x38, 0x35, + 0x10, 0x1c, 0x01, 0x0c, 0x18, 0x02, 0x09, 0x89, + 0x29, 0x81, 0x8b, 0x92, 0x03, 0x08, 0x00, 0x08, + 0x03, 0x21, 0x2a, 0x97, 0x81, 0x8a, 0x0b, 0x18, + 0x09, 0x0b, 0xaa, 0x0f, 0x80, 0xa7, 0x20, 0x00, + 0x14, 0x22, 0x18, 0x14, 0x00, 0x40, 0xff, 0x80, + 0x42, 0x02, 0x1a, 0x08, 0x81, 0x8d, 0x09, 0x89, + 0xaa, 0x87, 0x41, 0xaa, 0x89, 0x0f, 0x60, 0xce, + 0x3c, 0x2c, 0x81, 0x40, 0xa1, 0x81, 0x91, 0x00, + 0x80, 0x9b, 0x00, 0x80, 0x9c, 0x00, 0x00, 0x08, + 0x81, 0x60, 0xd7, 0x76, 0x80, 0xb8, 0x80, 0xb8, + 0x80, 0xb8, 0x80, 0xb8, 0x80, +}; + +static const uint8_t unicode_prop_Emoji_table[238] = { + 0xa2, 0x05, 0x04, 0x89, 0xee, 0x03, 0x80, 0x5f, + 0x8c, 0x80, 0x8b, 0x80, 0x40, 0xd7, 0x80, 0x95, + 0x80, 0xd9, 0x85, 0x8e, 0x81, 0x41, 0x6e, 0x81, + 0x8b, 0x80, 0x40, 0xa5, 0x80, 0x98, 0x8a, 0x1a, + 0x40, 0xc6, 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, + 0x88, 0x80, 0xb9, 0x18, 0x84, 0x88, 0x01, 0x01, + 0x09, 0x03, 0x01, 0x00, 0x09, 0x02, 0x02, 0x0f, + 0x14, 0x00, 0x04, 0x8b, 0x8a, 0x09, 0x00, 0x08, + 0x80, 0x91, 0x01, 0x81, 0x91, 0x28, 0x00, 0x0a, + 0x0c, 0x01, 0x0b, 0x81, 0x8a, 0x0c, 0x09, 0x04, + 0x08, 0x00, 0x81, 0x93, 0x0c, 0x28, 0x19, 0x03, + 0x01, 0x01, 0x28, 0x01, 0x00, 0x00, 0x05, 0x02, + 0x05, 0x80, 0x89, 0x81, 0x8e, 0x01, 0x03, 0x00, + 0x03, 0x10, 0x80, 0x8a, 0x81, 0xaf, 0x82, 0x88, + 0x80, 0x8d, 0x80, 0x8d, 0x80, 0x41, 0x73, 0x81, + 0x41, 0xce, 0x82, 0x92, 0x81, 0xb2, 0x03, 0x80, + 0x44, 0xd9, 0x80, 0x8b, 0x80, 0x42, 0x58, 0x00, + 0x80, 0x61, 0xbd, 0x69, 0x80, 0x40, 0xc9, 0x80, + 0x40, 0x9f, 0x81, 0x8b, 0x81, 0x8d, 0x01, 0x89, + 0xca, 0x99, 0x01, 0x96, 0x80, 0x93, 0x01, 0x88, + 0x94, 0x81, 0x40, 0xad, 0xa1, 0x81, 0xef, 0x09, + 0x02, 0x81, 0xd2, 0x0a, 0x80, 0x41, 0x06, 0x80, + 0xbe, 0x8a, 0x28, 0x97, 0x31, 0x0f, 0x8b, 0x01, + 0x19, 0x03, 0x81, 0x8c, 0x09, 0x07, 0x81, 0x88, + 0x04, 0x82, 0x8b, 0x17, 0x11, 0x00, 0x03, 0x05, + 0x02, 0x05, 0xd5, 0xaf, 0xc5, 0x27, 0x0a, 0x83, + 0x89, 0x10, 0x01, 0x10, 0x81, 0x89, 0x40, 0xe2, + 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, 0x89, 0x80, + 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x89, 0x84, 0xb7, + 0x86, 0x8e, 0x81, 0x8a, 0x85, 0x88, +}; + +static const uint8_t unicode_prop_Emoji_Component_table[28] = { + 0xa2, 0x05, 0x04, 0x89, 0x5f, 0xd2, 0x80, 0x40, + 0xd4, 0x80, 0x60, 0xdd, 0x2a, 0x80, 0x60, 0xf3, + 0xd5, 0x99, 0x41, 0xfa, 0x84, 0x45, 0xaf, 0x83, + 0x6c, 0x06, 0x6b, 0xdf, +}; + +static const uint8_t unicode_prop_Emoji_Modifier_table[4] = { + 0x61, 0xf3, 0xfa, 0x84, +}; + +static const uint8_t unicode_prop_Emoji_Modifier_Base_table[71] = { + 0x60, 0x26, 0x1c, 0x80, 0x40, 0xda, 0x80, 0x8f, + 0x83, 0x61, 0xcc, 0x76, 0x80, 0xbb, 0x11, 0x01, + 0x82, 0xf4, 0x09, 0x8a, 0x94, 0x92, 0x10, 0x1a, + 0x02, 0x30, 0x00, 0x97, 0x80, 0x40, 0xc8, 0x0b, + 0x80, 0x94, 0x03, 0x81, 0x40, 0xad, 0x12, 0x84, + 0xd2, 0x80, 0x8f, 0x82, 0x88, 0x80, 0x8a, 0x80, + 0x42, 0x3e, 0x01, 0x07, 0x3d, 0x80, 0x88, 0x89, + 0x0a, 0xb7, 0x80, 0xbc, 0x08, 0x08, 0x80, 0x90, + 0x10, 0x8c, 0x40, 0xe4, 0x82, 0xa9, 0x88, +}; + +static const uint8_t unicode_prop_Emoji_Presentation_table[144] = { + 0x60, 0x23, 0x19, 0x81, 0x40, 0xcc, 0x1a, 0x01, + 0x80, 0x42, 0x08, 0x81, 0x94, 0x81, 0xb1, 0x8b, + 0xaa, 0x80, 0x92, 0x80, 0x8c, 0x07, 0x81, 0x90, + 0x0c, 0x0f, 0x04, 0x80, 0x94, 0x06, 0x08, 0x03, + 0x01, 0x06, 0x03, 0x81, 0x9b, 0x80, 0xa2, 0x00, + 0x03, 0x10, 0x80, 0xbc, 0x82, 0x97, 0x80, 0x8d, + 0x80, 0x43, 0x5a, 0x81, 0xb2, 0x03, 0x80, 0x61, + 0xc4, 0xad, 0x80, 0x40, 0xc9, 0x80, 0x40, 0xbd, + 0x01, 0x89, 0xca, 0x99, 0x00, 0x97, 0x80, 0x93, + 0x01, 0x20, 0x82, 0x94, 0x81, 0x40, 0xad, 0xa0, + 0x8b, 0x88, 0x80, 0xc5, 0x80, 0x95, 0x8b, 0xaa, + 0x1c, 0x8b, 0x90, 0x10, 0x82, 0xc6, 0x00, 0x80, + 0x40, 0xba, 0x81, 0xbe, 0x8c, 0x18, 0x97, 0x91, + 0x80, 0x99, 0x81, 0x8c, 0x80, 0xd5, 0xd4, 0xaf, + 0xc5, 0x28, 0x12, 0x0a, 0x1b, 0x8a, 0x0e, 0x88, + 0x40, 0xe2, 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, + 0x89, 0x80, 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x89, + 0x84, 0xb7, 0x86, 0x8e, 0x81, 0x8a, 0x85, 0x88, +}; + +static const uint8_t unicode_prop_Extended_Pictographic_table[156] = { + 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, + 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, + 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0xde, + 0x80, 0xc5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6, + 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80, + 0xb9, 0x18, 0x28, 0x8b, 0x80, 0xf1, 0x89, 0xf5, + 0x81, 0x8a, 0x00, 0x00, 0x28, 0x10, 0x28, 0x89, + 0x81, 0x8e, 0x01, 0x03, 0x00, 0x03, 0x10, 0x80, + 0x8a, 0x84, 0xac, 0x82, 0x88, 0x80, 0x8d, 0x80, + 0x8d, 0x80, 0x41, 0x73, 0x81, 0x41, 0xce, 0x82, + 0x92, 0x81, 0xb2, 0x03, 0x80, 0x44, 0xd9, 0x80, + 0x8b, 0x80, 0x42, 0x58, 0x00, 0x80, 0x61, 0xbd, + 0x65, 0x40, 0xff, 0x8c, 0x82, 0x9e, 0x80, 0xbb, + 0x85, 0x8b, 0x81, 0x8d, 0x01, 0x89, 0x91, 0xb8, + 0x9a, 0x8e, 0x89, 0x80, 0x93, 0x01, 0x88, 0x03, + 0x88, 0x41, 0xb1, 0x84, 0x41, 0x3d, 0x87, 0x41, + 0x09, 0xaf, 0xff, 0xf3, 0x8b, 0xd4, 0xaa, 0x8b, + 0x83, 0xb7, 0x87, 0x89, 0x85, 0xa7, 0x87, 0x9d, + 0xd1, 0x8b, 0xae, 0x80, 0x89, 0x80, 0x41, 0xb8, + 0x40, 0xff, 0x43, 0xfd, +}; + +static const uint8_t unicode_prop_Default_Ignorable_Code_Point_table[51] = { + 0x40, 0xac, 0x80, 0x42, 0xa0, 0x80, 0x42, 0xcb, + 0x80, 0x4b, 0x41, 0x81, 0x46, 0x52, 0x81, 0xd4, + 0x84, 0x47, 0xfa, 0x84, 0x99, 0x84, 0xb0, 0x8f, + 0x50, 0xf3, 0x80, 0x60, 0xcc, 0x9a, 0x8f, 0x40, + 0xee, 0x80, 0x40, 0x9f, 0x80, 0xce, 0x88, 0x60, + 0xbc, 0xa6, 0x83, 0x54, 0xce, 0x87, 0x6c, 0x2e, + 0x84, 0x4f, 0xff, +}; + +typedef enum { + UNICODE_PROP_Hyphen, + UNICODE_PROP_Other_Math, + UNICODE_PROP_Other_Alphabetic, + UNICODE_PROP_Other_Lowercase, + UNICODE_PROP_Other_Uppercase, + UNICODE_PROP_Other_Grapheme_Extend, + UNICODE_PROP_Other_Default_Ignorable_Code_Point, + UNICODE_PROP_Other_ID_Start, + UNICODE_PROP_Other_ID_Continue, + UNICODE_PROP_Prepended_Concatenation_Mark, + UNICODE_PROP_ID_Continue1, + UNICODE_PROP_XID_Start1, + UNICODE_PROP_XID_Continue1, + UNICODE_PROP_Changes_When_Titlecased1, + UNICODE_PROP_Changes_When_Casefolded1, + UNICODE_PROP_Changes_When_NFKC_Casefolded1, + UNICODE_PROP_ASCII_Hex_Digit, + UNICODE_PROP_Bidi_Control, + UNICODE_PROP_Dash, + UNICODE_PROP_Deprecated, + UNICODE_PROP_Diacritic, + UNICODE_PROP_Extender, + UNICODE_PROP_Hex_Digit, + UNICODE_PROP_IDS_Unary_Operator, + UNICODE_PROP_IDS_Binary_Operator, + UNICODE_PROP_IDS_Trinary_Operator, + UNICODE_PROP_Ideographic, + UNICODE_PROP_Join_Control, + UNICODE_PROP_Logical_Order_Exception, + UNICODE_PROP_Modifier_Combining_Mark, + UNICODE_PROP_Noncharacter_Code_Point, + UNICODE_PROP_Pattern_Syntax, + UNICODE_PROP_Pattern_White_Space, + UNICODE_PROP_Quotation_Mark, + UNICODE_PROP_Radical, + UNICODE_PROP_Regional_Indicator, + UNICODE_PROP_Sentence_Terminal, + UNICODE_PROP_Soft_Dotted, + UNICODE_PROP_Terminal_Punctuation, + UNICODE_PROP_Unified_Ideograph, + UNICODE_PROP_Variation_Selector, + UNICODE_PROP_White_Space, + UNICODE_PROP_Bidi_Mirrored, + UNICODE_PROP_Emoji, + UNICODE_PROP_Emoji_Component, + UNICODE_PROP_Emoji_Modifier, + UNICODE_PROP_Emoji_Modifier_Base, + UNICODE_PROP_Emoji_Presentation, + UNICODE_PROP_Extended_Pictographic, + UNICODE_PROP_Default_Ignorable_Code_Point, + UNICODE_PROP_ID_Start, + UNICODE_PROP_Case_Ignorable, + UNICODE_PROP_ASCII, + UNICODE_PROP_Alphabetic, + UNICODE_PROP_Any, + UNICODE_PROP_Assigned, + UNICODE_PROP_Cased, + UNICODE_PROP_Changes_When_Casefolded, + UNICODE_PROP_Changes_When_Casemapped, + UNICODE_PROP_Changes_When_Lowercased, + UNICODE_PROP_Changes_When_NFKC_Casefolded, + UNICODE_PROP_Changes_When_Titlecased, + UNICODE_PROP_Changes_When_Uppercased, + UNICODE_PROP_Grapheme_Base, + UNICODE_PROP_Grapheme_Extend, + UNICODE_PROP_ID_Continue, + UNICODE_PROP_ID_Compat_Math_Start, + UNICODE_PROP_ID_Compat_Math_Continue, + UNICODE_PROP_Lowercase, + UNICODE_PROP_Math, + UNICODE_PROP_Uppercase, + UNICODE_PROP_XID_Continue, + UNICODE_PROP_XID_Start, + UNICODE_PROP_Cased1, + UNICODE_PROP_InCB, + UNICODE_PROP_COUNT, +} UnicodePropertyEnum; + +static const char unicode_prop_name_table[] = + "ASCII_Hex_Digit,AHex" "\0" + "Bidi_Control,Bidi_C" "\0" + "Dash" "\0" + "Deprecated,Dep" "\0" + "Diacritic,Dia" "\0" + "Extender,Ext" "\0" + "Hex_Digit,Hex" "\0" + "IDS_Unary_Operator,IDSU" "\0" + "IDS_Binary_Operator,IDSB" "\0" + "IDS_Trinary_Operator,IDST" "\0" + "Ideographic,Ideo" "\0" + "Join_Control,Join_C" "\0" + "Logical_Order_Exception,LOE" "\0" + "Modifier_Combining_Mark,MCM" "\0" + "Noncharacter_Code_Point,NChar" "\0" + "Pattern_Syntax,Pat_Syn" "\0" + "Pattern_White_Space,Pat_WS" "\0" + "Quotation_Mark,QMark" "\0" + "Radical" "\0" + "Regional_Indicator,RI" "\0" + "Sentence_Terminal,STerm" "\0" + "Soft_Dotted,SD" "\0" + "Terminal_Punctuation,Term" "\0" + "Unified_Ideograph,UIdeo" "\0" + "Variation_Selector,VS" "\0" + "White_Space,space" "\0" + "Bidi_Mirrored,Bidi_M" "\0" + "Emoji" "\0" + "Emoji_Component,EComp" "\0" + "Emoji_Modifier,EMod" "\0" + "Emoji_Modifier_Base,EBase" "\0" + "Emoji_Presentation,EPres" "\0" + "Extended_Pictographic,ExtPict" "\0" + "Default_Ignorable_Code_Point,DI" "\0" + "ID_Start,IDS" "\0" + "Case_Ignorable,CI" "\0" + "ASCII" "\0" + "Alphabetic,Alpha" "\0" + "Any" "\0" + "Assigned" "\0" + "Cased" "\0" + "Changes_When_Casefolded,CWCF" "\0" + "Changes_When_Casemapped,CWCM" "\0" + "Changes_When_Lowercased,CWL" "\0" + "Changes_When_NFKC_Casefolded,CWKCF" "\0" + "Changes_When_Titlecased,CWT" "\0" + "Changes_When_Uppercased,CWU" "\0" + "Grapheme_Base,Gr_Base" "\0" + "Grapheme_Extend,Gr_Ext" "\0" + "ID_Continue,IDC" "\0" + "ID_Compat_Math_Start" "\0" + "ID_Compat_Math_Continue" "\0" + "Lowercase,Lower" "\0" + "Math" "\0" + "Uppercase,Upper" "\0" + "XID_Continue,XIDC" "\0" + "XID_Start,XIDS" "\0" +; + +static const uint8_t * const unicode_prop_table[] = { + unicode_prop_Hyphen_table, + unicode_prop_Other_Math_table, + unicode_prop_Other_Alphabetic_table, + unicode_prop_Other_Lowercase_table, + unicode_prop_Other_Uppercase_table, + unicode_prop_Other_Grapheme_Extend_table, + unicode_prop_Other_Default_Ignorable_Code_Point_table, + unicode_prop_Other_ID_Start_table, + unicode_prop_Other_ID_Continue_table, + unicode_prop_Prepended_Concatenation_Mark_table, + unicode_prop_ID_Continue1_table, + unicode_prop_XID_Start1_table, + unicode_prop_XID_Continue1_table, + unicode_prop_Changes_When_Titlecased1_table, + unicode_prop_Changes_When_Casefolded1_table, + unicode_prop_Changes_When_NFKC_Casefolded1_table, + unicode_prop_ASCII_Hex_Digit_table, + unicode_prop_Bidi_Control_table, + unicode_prop_Dash_table, + unicode_prop_Deprecated_table, + unicode_prop_Diacritic_table, + unicode_prop_Extender_table, + unicode_prop_Hex_Digit_table, + unicode_prop_IDS_Unary_Operator_table, + unicode_prop_IDS_Binary_Operator_table, + unicode_prop_IDS_Trinary_Operator_table, + unicode_prop_Ideographic_table, + unicode_prop_Join_Control_table, + unicode_prop_Logical_Order_Exception_table, + unicode_prop_Modifier_Combining_Mark_table, + unicode_prop_Noncharacter_Code_Point_table, + unicode_prop_Pattern_Syntax_table, + unicode_prop_Pattern_White_Space_table, + unicode_prop_Quotation_Mark_table, + unicode_prop_Radical_table, + unicode_prop_Regional_Indicator_table, + unicode_prop_Sentence_Terminal_table, + unicode_prop_Soft_Dotted_table, + unicode_prop_Terminal_Punctuation_table, + unicode_prop_Unified_Ideograph_table, + unicode_prop_Variation_Selector_table, + unicode_prop_White_Space_table, + unicode_prop_Bidi_Mirrored_table, + unicode_prop_Emoji_table, + unicode_prop_Emoji_Component_table, + unicode_prop_Emoji_Modifier_table, + unicode_prop_Emoji_Modifier_Base_table, + unicode_prop_Emoji_Presentation_table, + unicode_prop_Extended_Pictographic_table, + unicode_prop_Default_Ignorable_Code_Point_table, + unicode_prop_ID_Start_table, + unicode_prop_Case_Ignorable_table, +}; + +static const uint16_t unicode_prop_len_table[] = { + countof(unicode_prop_Hyphen_table), + countof(unicode_prop_Other_Math_table), + countof(unicode_prop_Other_Alphabetic_table), + countof(unicode_prop_Other_Lowercase_table), + countof(unicode_prop_Other_Uppercase_table), + countof(unicode_prop_Other_Grapheme_Extend_table), + countof(unicode_prop_Other_Default_Ignorable_Code_Point_table), + countof(unicode_prop_Other_ID_Start_table), + countof(unicode_prop_Other_ID_Continue_table), + countof(unicode_prop_Prepended_Concatenation_Mark_table), + countof(unicode_prop_ID_Continue1_table), + countof(unicode_prop_XID_Start1_table), + countof(unicode_prop_XID_Continue1_table), + countof(unicode_prop_Changes_When_Titlecased1_table), + countof(unicode_prop_Changes_When_Casefolded1_table), + countof(unicode_prop_Changes_When_NFKC_Casefolded1_table), + countof(unicode_prop_ASCII_Hex_Digit_table), + countof(unicode_prop_Bidi_Control_table), + countof(unicode_prop_Dash_table), + countof(unicode_prop_Deprecated_table), + countof(unicode_prop_Diacritic_table), + countof(unicode_prop_Extender_table), + countof(unicode_prop_Hex_Digit_table), + countof(unicode_prop_IDS_Unary_Operator_table), + countof(unicode_prop_IDS_Binary_Operator_table), + countof(unicode_prop_IDS_Trinary_Operator_table), + countof(unicode_prop_Ideographic_table), + countof(unicode_prop_Join_Control_table), + countof(unicode_prop_Logical_Order_Exception_table), + countof(unicode_prop_Modifier_Combining_Mark_table), + countof(unicode_prop_Noncharacter_Code_Point_table), + countof(unicode_prop_Pattern_Syntax_table), + countof(unicode_prop_Pattern_White_Space_table), + countof(unicode_prop_Quotation_Mark_table), + countof(unicode_prop_Radical_table), + countof(unicode_prop_Regional_Indicator_table), + countof(unicode_prop_Sentence_Terminal_table), + countof(unicode_prop_Soft_Dotted_table), + countof(unicode_prop_Terminal_Punctuation_table), + countof(unicode_prop_Unified_Ideograph_table), + countof(unicode_prop_Variation_Selector_table), + countof(unicode_prop_White_Space_table), + countof(unicode_prop_Bidi_Mirrored_table), + countof(unicode_prop_Emoji_table), + countof(unicode_prop_Emoji_Component_table), + countof(unicode_prop_Emoji_Modifier_table), + countof(unicode_prop_Emoji_Modifier_Base_table), + countof(unicode_prop_Emoji_Presentation_table), + countof(unicode_prop_Extended_Pictographic_table), + countof(unicode_prop_Default_Ignorable_Code_Point_table), + countof(unicode_prop_ID_Start_table), + countof(unicode_prop_Case_Ignorable_table), +}; + diff --git a/NativeScript/napi/android/quickjs/source_ng/libunicode.c b/NativeScript/napi/android/quickjs/source_ng/libunicode.c new file mode 100755 index 000000000..a621c5235 --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/libunicode.c @@ -0,0 +1,1746 @@ +/* + * Unicode utilities + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "libunicode.h" +#include "libunicode-table.h" + +// note: stored as 4 bit tag, not much room left +enum { + RUN_TYPE_U, + RUN_TYPE_L, + RUN_TYPE_UF, + RUN_TYPE_LF, + RUN_TYPE_UL, + RUN_TYPE_LSU, + RUN_TYPE_U2L_399_EXT2, + RUN_TYPE_UF_D20, + RUN_TYPE_UF_D1_EXT, + RUN_TYPE_U_EXT, + RUN_TYPE_LF_EXT, + RUN_TYPE_UF_EXT2, + RUN_TYPE_LF_EXT2, + RUN_TYPE_UF_EXT3, +}; + +static int lre_case_conv1(uint32_t c, int conv_type) +{ + uint32_t res[LRE_CC_RES_LEN_MAX]; + lre_case_conv(res, c, conv_type); + return res[0]; +} + +/* case conversion using the table entry 'idx' with value 'v' */ +static int lre_case_conv_entry(uint32_t *res, uint32_t c, int conv_type, uint32_t idx, uint32_t v) +{ + uint32_t code, data, type, a, is_lower; + is_lower = (conv_type != 0); + type = (v >> (32 - 17 - 7 - 4)) & 0xf; + data = ((v & 0xf) << 8) | case_conv_table2[idx]; + code = v >> (32 - 17); + switch(type) { + case RUN_TYPE_U: + case RUN_TYPE_L: + case RUN_TYPE_UF: + case RUN_TYPE_LF: + if (conv_type == (type & 1) || + (type >= RUN_TYPE_UF && conv_type == 2)) { + c = c - code + (case_conv_table1[data] >> (32 - 17)); + } + break; + case RUN_TYPE_UL: + a = c - code; + if ((a & 1) != (1 - is_lower)) + break; + c = (a ^ 1) + code; + break; + case RUN_TYPE_LSU: + a = c - code; + if (a == 1) { + c += 2 * is_lower - 1; + } else if (a == (1 - is_lower) * 2) { + c += (2 * is_lower - 1) * 2; + } + break; + case RUN_TYPE_U2L_399_EXT2: + if (!is_lower) { + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = 0x399; + return 2; + } else { + c = c - code + case_conv_ext[data & 0x3f]; + } + break; + case RUN_TYPE_UF_D20: + if (conv_type == 1) + break; + c = data + (conv_type == 2) * 0x20; + break; + case RUN_TYPE_UF_D1_EXT: + if (conv_type == 1) + break; + c = case_conv_ext[data] + (conv_type == 2); + break; + case RUN_TYPE_U_EXT: + case RUN_TYPE_LF_EXT: + if (is_lower != (type - RUN_TYPE_U_EXT)) + break; + c = case_conv_ext[data]; + break; + case RUN_TYPE_LF_EXT2: + if (!is_lower) + break; + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = case_conv_ext[data & 0x3f]; + return 2; + case RUN_TYPE_UF_EXT2: + if (conv_type == 1) + break; + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = case_conv_ext[data & 0x3f]; + if (conv_type == 2) { + /* convert to lower */ + res[0] = lre_case_conv1(res[0], 1); + res[1] = lre_case_conv1(res[1], 1); + } + return 2; + default: + case RUN_TYPE_UF_EXT3: + if (conv_type == 1) + break; + res[0] = case_conv_ext[data >> 8]; + res[1] = case_conv_ext[(data >> 4) & 0xf]; + res[2] = case_conv_ext[data & 0xf]; + if (conv_type == 2) { + /* convert to lower */ + res[0] = lre_case_conv1(res[0], 1); + res[1] = lre_case_conv1(res[1], 1); + res[2] = lre_case_conv1(res[2], 1); + } + return 3; + } + res[0] = c; + return 1; +} + +/* conv_type: + 0 = to upper + 1 = to lower + 2 = case folding (= to lower with modifications) +*/ +int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) +{ + if (c < 128) { + if (conv_type) { + if (c >= 'A' && c <= 'Z') { + c = c - 'A' + 'a'; + } + } else { + if (c >= 'a' && c <= 'z') { + c = c - 'a' + 'A'; + } + } + } else { + uint32_t v, code, len; + int idx, idx_min, idx_max; + + idx_min = 0; + idx_max = countof(case_conv_table1) - 1; + while (idx_min <= idx_max) { + idx = (unsigned)(idx_max + idx_min) / 2; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + return lre_case_conv_entry(res, c, conv_type, idx, v); + } + } + } + res[0] = c; + return 1; +} + +static int lre_case_folding_entry(uint32_t c, uint32_t idx, uint32_t v, bool is_unicode) +{ + uint32_t res[LRE_CC_RES_LEN_MAX]; + int len; + + if (is_unicode) { + len = lre_case_conv_entry(res, c, 2, idx, v); + if (len == 1) { + c = res[0]; + } else { + /* handle the few specific multi-character cases (see + unicode_gen.c:dump_case_folding_special_cases()) */ + if (c == 0xfb06) { + c = 0xfb05; + } else if (c == 0x01fd3) { + c = 0x390; + } else if (c == 0x01fe3) { + c = 0x3b0; + } + } + } else { + if (likely(c < 128)) { + if (c >= 'a' && c <= 'z') + c = c - 'a' + 'A'; + } else { + /* legacy regexp: to upper case if single char >= 128 */ + len = lre_case_conv_entry(res, c, false, idx, v); + if (len == 1 && res[0] >= 128) + c = res[0]; + } + } + return c; +} + +/* JS regexp specific rules for case folding */ +int lre_canonicalize(uint32_t c, bool is_unicode) +{ + if (c < 128) { + /* fast case */ + if (is_unicode) { + if (c >= 'A' && c <= 'Z') { + c = c - 'A' + 'a'; + } + } else { + if (c >= 'a' && c <= 'z') { + c = c - 'a' + 'A'; + } + } + } else { + uint32_t v, code, len; + int idx, idx_min, idx_max; + + idx_min = 0; + idx_max = countof(case_conv_table1) - 1; + while (idx_min <= idx_max) { + idx = (unsigned)(idx_max + idx_min) / 2; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + return lre_case_folding_entry(c, idx, v, is_unicode); + } + } + } + return c; +} + +static uint32_t get_le24(const uint8_t *ptr) +{ + return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16); +} + +#define UNICODE_INDEX_BLOCK_LEN 32 + +/* return -1 if not in table, otherwise the offset in the block */ +static int get_index_pos(uint32_t *pcode, uint32_t c, + const uint8_t *index_table, int index_table_len) +{ + uint32_t code, v; + int idx_min, idx_max, idx; + + idx_min = 0; + v = get_le24(index_table); + code = v & ((1 << 21) - 1); + if (c < code) { + *pcode = 0; + return 0; + } + idx_max = index_table_len - 1; + code = get_le24(index_table + idx_max * 3); + if (c >= code) + return -1; + /* invariant: tab[idx_min] <= c < tab2[idx_max] */ + while ((idx_max - idx_min) > 1) { + idx = (idx_max + idx_min) / 2; + v = get_le24(index_table + idx * 3); + code = v & ((1 << 21) - 1); + if (c < code) { + idx_max = idx; + } else { + idx_min = idx; + } + } + v = get_le24(index_table + idx_min * 3); + *pcode = v & ((1 << 21) - 1); + return (idx_min + 1) * UNICODE_INDEX_BLOCK_LEN + (v >> 21); +} + +static bool lre_is_in_table(uint32_t c, const uint8_t *table, + const uint8_t *index_table, int index_table_len) +{ + uint32_t code, b, bit; + int pos; + const uint8_t *p; + + pos = get_index_pos(&code, c, index_table, index_table_len); + if (pos < 0) + return false; /* outside the table */ + p = table + pos; + bit = 0; + for(;;) { + b = *p++; + if (b < 64) { + code += (b >> 3) + 1; + if (c < code) + return bit; + bit ^= 1; + code += (b & 7) + 1; + } else if (b >= 0x80) { + code += b - 0x80 + 1; + } else if (b < 0x60) { + code += (((b - 0x40) << 8) | p[0]) + 1; + p++; + } else { + code += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; + p += 2; + } + if (c < code) + return bit; + bit ^= 1; + } +} + +bool lre_is_cased(uint32_t c) +{ + uint32_t v, code, len; + int idx, idx_min, idx_max; + + idx_min = 0; + idx_max = countof(case_conv_table1) - 1; + while (idx_min <= idx_max) { + idx = (unsigned)(idx_max + idx_min) / 2; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + return true; + } + } + return lre_is_in_table(c, unicode_prop_Cased1_table, + unicode_prop_Cased1_index, + sizeof(unicode_prop_Cased1_index) / 3); +} + +bool lre_is_case_ignorable(uint32_t c) +{ + return lre_is_in_table(c, unicode_prop_Case_Ignorable_table, + unicode_prop_Case_Ignorable_index, + sizeof(unicode_prop_Case_Ignorable_index) / 3); +} + +/* character range */ + +static __maybe_unused void cr_dump(CharRange *cr) +{ + int i; + for(i = 0; i < cr->len; i++) + printf("%d: 0x%04x\n", i, cr->points[i]); +} + +static void *cr_default_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +void cr_init(CharRange *cr, void *mem_opaque, DynBufReallocFunc *realloc_func) +{ + cr->len = cr->size = 0; + cr->points = NULL; + cr->mem_opaque = mem_opaque; + cr->realloc_func = realloc_func ? realloc_func : cr_default_realloc; +} + +void cr_free(CharRange *cr) +{ + cr->realloc_func(cr->mem_opaque, cr->points, 0); +} + +int cr_realloc(CharRange *cr, int size) +{ + int new_size; + uint32_t *new_buf; + + if (size > cr->size) { + new_size = max_int(size, cr->size * 3 / 2); + new_buf = cr->realloc_func(cr->mem_opaque, cr->points, + new_size * sizeof(cr->points[0])); + if (!new_buf) + return -1; + cr->points = new_buf; + cr->size = new_size; + } + return 0; +} + +int cr_copy(CharRange *cr, const CharRange *cr1) +{ + if (cr_realloc(cr, cr1->len)) + return -1; + memcpy(cr->points, cr1->points, sizeof(cr->points[0]) * cr1->len); + cr->len = cr1->len; + return 0; +} + +/* merge consecutive intervals and remove empty intervals */ +static void cr_compress(CharRange *cr) +{ + int i, j, k, len; + uint32_t *pt; + + pt = cr->points; + len = cr->len; + i = 0; + j = 0; + k = 0; + while ((i + 1) < len) { + if (pt[i] == pt[i + 1]) { + /* empty interval */ + i += 2; + } else { + j = i; + while ((j + 3) < len && pt[j + 1] == pt[j + 2]) + j += 2; + /* just copy */ + pt[k] = pt[i]; + pt[k + 1] = pt[j + 1]; + k += 2; + i = j + 2; + } + } + cr->len = k; +} + +/* union or intersection */ +int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, + const uint32_t *b_pt, int b_len, int op) +{ + int a_idx, b_idx, is_in; + uint32_t v; + + a_idx = 0; + b_idx = 0; + for(;;) { + /* get one more point from a or b in increasing order */ + if (a_idx < a_len && b_idx < b_len) { + if (a_pt[a_idx] < b_pt[b_idx]) { + goto a_add; + } else if (a_pt[a_idx] == b_pt[b_idx]) { + v = a_pt[a_idx]; + a_idx++; + b_idx++; + } else { + goto b_add; + } + } else if (a_idx < a_len) { + a_add: + v = a_pt[a_idx++]; + } else if (b_idx < b_len) { + b_add: + v = b_pt[b_idx++]; + } else { + break; + } + /* add the point if the in/out status changes */ + switch(op) { + case CR_OP_UNION: + is_in = (a_idx & 1) | (b_idx & 1); + break; + case CR_OP_INTER: + is_in = (a_idx & 1) & (b_idx & 1); + break; + case CR_OP_XOR: + is_in = (a_idx & 1) ^ (b_idx & 1); + break; + default: + abort(); + } + if (is_in != (cr->len & 1)) { + if (cr_add_point(cr, v)) + return -1; + } + } + cr_compress(cr); + return 0; +} + +int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len) +{ + CharRange a = *cr; + int ret; + cr->len = 0; + cr->size = 0; + cr->points = NULL; + ret = cr_op(cr, a.points, a.len, b_pt, b_len, CR_OP_UNION); + cr_free(&a); + return ret; +} + +int cr_invert(CharRange *cr) +{ + int len; + len = cr->len; + if (cr_realloc(cr, len + 2)) + return -1; + memmove(cr->points + 1, cr->points, len * sizeof(cr->points[0])); + cr->points[0] = 0; + cr->points[len + 1] = UINT32_MAX; + cr->len = len + 2; + cr_compress(cr); + return 0; +} + +bool lre_is_id_start(uint32_t c) +{ + return lre_is_in_table(c, unicode_prop_ID_Start_table, + unicode_prop_ID_Start_index, + sizeof(unicode_prop_ID_Start_index) / 3); +} + +bool lre_is_id_continue(uint32_t c) +{ + return lre_is_id_start(c) || + lre_is_in_table(c, unicode_prop_ID_Continue1_table, + unicode_prop_ID_Continue1_index, + sizeof(unicode_prop_ID_Continue1_index) / 3); +} + +bool lre_is_white_space(uint32_t c) +{ + return lre_is_in_table(c, unicode_prop_White_Space_table, + unicode_prop_White_Space_index, + sizeof(unicode_prop_White_Space_index) / 3); +} + +#define UNICODE_DECOMP_LEN_MAX 18 + +typedef enum { + DECOMP_TYPE_C1, /* 16 bit char */ + DECOMP_TYPE_L1, /* 16 bit char table */ + DECOMP_TYPE_L2, + DECOMP_TYPE_L3, + DECOMP_TYPE_L4, + DECOMP_TYPE_L5, /* XXX: not used */ + DECOMP_TYPE_L6, /* XXX: could remove */ + DECOMP_TYPE_L7, /* XXX: could remove */ + DECOMP_TYPE_LL1, /* 18 bit char table */ + DECOMP_TYPE_LL2, + DECOMP_TYPE_S1, /* 8 bit char table */ + DECOMP_TYPE_S2, + DECOMP_TYPE_S3, + DECOMP_TYPE_S4, + DECOMP_TYPE_S5, + DECOMP_TYPE_I1, /* increment 16 bit char value */ + DECOMP_TYPE_I2_0, + DECOMP_TYPE_I2_1, + DECOMP_TYPE_I3_1, + DECOMP_TYPE_I3_2, + DECOMP_TYPE_I4_1, + DECOMP_TYPE_I4_2, + DECOMP_TYPE_B1, /* 16 bit base + 8 bit offset */ + DECOMP_TYPE_B2, + DECOMP_TYPE_B3, + DECOMP_TYPE_B4, + DECOMP_TYPE_B5, + DECOMP_TYPE_B6, + DECOMP_TYPE_B7, + DECOMP_TYPE_B8, + DECOMP_TYPE_B18, + DECOMP_TYPE_LS2, + DECOMP_TYPE_PAT3, + DECOMP_TYPE_S2_UL, + DECOMP_TYPE_LS2_UL, +} DecompTypeEnum; + +static uint32_t unicode_get_short_code(uint32_t c) +{ + static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 }; + + if (c < 0x80) + return c; + else if (c < 0x80 + 0x50) + return c - 0x80 + 0x300; + else + return unicode_short_table[c - 0x80 - 0x50]; +} + +static uint32_t unicode_get_lower_simple(uint32_t c) +{ + if (c < 0x100 || (c >= 0x410 && c <= 0x42f)) + c += 0x20; + else + c++; + return c; +} + +static uint16_t unicode_get16(const uint8_t *p) +{ + return p[0] | (p[1] << 8); +} + +static int unicode_decomp_entry(uint32_t *res, uint32_t c, + int idx, uint32_t code, uint32_t len, + uint32_t type) +{ + uint32_t c1; + int l, i, p; + const uint8_t *d; + + if (type == DECOMP_TYPE_C1) { + res[0] = unicode_decomp_table2[idx]; + return 1; + } else { + d = unicode_decomp_data + unicode_decomp_table2[idx]; + switch(type) { + case DECOMP_TYPE_L1: + case DECOMP_TYPE_L2: + case DECOMP_TYPE_L3: + case DECOMP_TYPE_L4: + case DECOMP_TYPE_L5: + case DECOMP_TYPE_L6: + case DECOMP_TYPE_L7: + l = type - DECOMP_TYPE_L1 + 1; + d += (c - code) * l * 2; + for(i = 0; i < l; i++) { + if ((res[i] = unicode_get16(d + 2 * i)) == 0) + return 0; + } + return l; + case DECOMP_TYPE_LL1: + case DECOMP_TYPE_LL2: + { + uint32_t k, p; + l = type - DECOMP_TYPE_LL1 + 1; + k = (c - code) * l; + p = len * l * 2; + for(i = 0; i < l; i++) { + c1 = unicode_get16(d + 2 * k) | + (((d[p + (k / 4)] >> ((k % 4) * 2)) & 3) << 16); + if (!c1) + return 0; + res[i] = c1; + k++; + } + } + return l; + case DECOMP_TYPE_S1: + case DECOMP_TYPE_S2: + case DECOMP_TYPE_S3: + case DECOMP_TYPE_S4: + case DECOMP_TYPE_S5: + l = type - DECOMP_TYPE_S1 + 1; + d += (c - code) * l; + for(i = 0; i < l; i++) { + if ((res[i] = unicode_get_short_code(d[i])) == 0) + return 0; + } + return l; + case DECOMP_TYPE_I1: + l = 1; + p = 0; + goto decomp_type_i; + case DECOMP_TYPE_I2_0: + case DECOMP_TYPE_I2_1: + case DECOMP_TYPE_I3_1: + case DECOMP_TYPE_I3_2: + case DECOMP_TYPE_I4_1: + case DECOMP_TYPE_I4_2: + l = 2 + ((type - DECOMP_TYPE_I2_0) >> 1); + p = ((type - DECOMP_TYPE_I2_0) & 1) + (l > 2); + decomp_type_i: + for(i = 0; i < l; i++) { + c1 = unicode_get16(d + 2 * i); + if (i == p) + c1 += c - code; + res[i] = c1; + } + return l; + case DECOMP_TYPE_B18: + l = 18; + goto decomp_type_b; + case DECOMP_TYPE_B1: + case DECOMP_TYPE_B2: + case DECOMP_TYPE_B3: + case DECOMP_TYPE_B4: + case DECOMP_TYPE_B5: + case DECOMP_TYPE_B6: + case DECOMP_TYPE_B7: + case DECOMP_TYPE_B8: + l = type - DECOMP_TYPE_B1 + 1; + decomp_type_b: + { + uint32_t c_min; + c_min = unicode_get16(d); + d += 2 + (c - code) * l; + for(i = 0; i < l; i++) { + c1 = d[i]; + if (c1 == 0xff) + c1 = 0x20; + else + c1 += c_min; + res[i] = c1; + } + } + return l; + case DECOMP_TYPE_LS2: + d += (c - code) * 3; + if (!(res[0] = unicode_get16(d))) + return 0; + res[1] = unicode_get_short_code(d[2]); + return 2; + case DECOMP_TYPE_PAT3: + res[0] = unicode_get16(d); + res[2] = unicode_get16(d + 2); + d += 4 + (c - code) * 2; + res[1] = unicode_get16(d); + return 3; + case DECOMP_TYPE_S2_UL: + case DECOMP_TYPE_LS2_UL: + c1 = c - code; + if (type == DECOMP_TYPE_S2_UL) { + d += c1 & ~1; + c = unicode_get_short_code(*d); + d++; + } else { + d += (c1 >> 1) * 3; + c = unicode_get16(d); + d += 2; + } + if (c1 & 1) + c = unicode_get_lower_simple(c); + res[0] = c; + res[1] = unicode_get_short_code(*d); + return 2; + } + } + return 0; +} + + +/* return the length of the decomposition (length <= + UNICODE_DECOMP_LEN_MAX) or 0 if no decomposition */ +static int unicode_decomp_char(uint32_t *res, uint32_t c, bool is_compat1) +{ + uint32_t v, type, is_compat, code, len; + int idx_min, idx_max, idx; + + idx_min = 0; + idx_max = countof(unicode_decomp_table1) - 1; + while (idx_min <= idx_max) { + idx = (idx_max + idx_min) / 2; + v = unicode_decomp_table1[idx]; + code = v >> (32 - 18); + len = (v >> (32 - 18 - 7)) & 0x7f; + // printf("idx=%d code=%05x len=%d\n", idx, code, len); + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + is_compat = v & 1; + if (is_compat1 < is_compat) + break; + type = (v >> (32 - 18 - 7 - 6)) & 0x3f; + return unicode_decomp_entry(res, c, idx, code, len, type); + } + } + return 0; +} + +/* return 0 if no pair found */ +static int unicode_compose_pair(uint32_t c0, uint32_t c1) +{ + uint32_t code, len, type, v, idx1, d_idx, d_offset, ch; + int idx_min, idx_max, idx, d; + uint32_t pair[2]; + + idx_min = 0; + idx_max = countof(unicode_comp_table) - 1; + while (idx_min <= idx_max) { + idx = (idx_max + idx_min) / 2; + idx1 = unicode_comp_table[idx]; + + /* idx1 represent an entry of the decomposition table */ + d_idx = idx1 >> 6; + d_offset = idx1 & 0x3f; + v = unicode_decomp_table1[d_idx]; + code = v >> (32 - 18); + len = (v >> (32 - 18 - 7)) & 0x7f; + type = (v >> (32 - 18 - 7 - 6)) & 0x3f; + ch = code + d_offset; + unicode_decomp_entry(pair, ch, d_idx, code, len, type); + d = c0 - pair[0]; + if (d == 0) + d = c1 - pair[1]; + if (d < 0) { + idx_max = idx - 1; + } else if (d > 0) { + idx_min = idx + 1; + } else { + return ch; + } + } + return 0; +} + +/* return the combining class of character c (between 0 and 255) */ +static int unicode_get_cc(uint32_t c) +{ + uint32_t code, n, type, cc, c1, b; + int pos; + const uint8_t *p; + + pos = get_index_pos(&code, c, + unicode_cc_index, sizeof(unicode_cc_index) / 3); + if (pos < 0) + return 0; + p = unicode_cc_table + pos; + for(;;) { + b = *p++; + type = b >> 6; + n = b & 0x3f; + if (n < 48) { + } else if (n < 56) { + n = (n - 48) << 8; + n |= *p++; + n += 48; + } else { + n = (n - 56) << 8; + n |= *p++ << 8; + n |= *p++; + n += 48 + (1 << 11); + } + if (type <= 1) + p++; + c1 = code + n + 1; + if (c < c1) { + switch(type) { + case 0: + cc = p[-1]; + break; + case 1: + cc = p[-1] + c - code; + break; + case 2: + cc = 0; + break; + default: + case 3: + cc = 230; + break; + } + return cc; + } + code = c1; + } +} + +static void sort_cc(int *buf, int len) +{ + int i, j, k, cc, cc1, start, ch1; + + for(i = 0; i < len; i++) { + cc = unicode_get_cc(buf[i]); + if (cc != 0) { + start = i; + j = i + 1; + while (j < len) { + ch1 = buf[j]; + cc1 = unicode_get_cc(ch1); + if (cc1 == 0) + break; + k = j - 1; + while (k >= start) { + if (unicode_get_cc(buf[k]) <= cc1) + break; + buf[k + 1] = buf[k]; + k--; + } + buf[k + 1] = ch1; + j++; + } + i = j; + } + } +} + +static void to_nfd_rec(DynBuf *dbuf, + const int *src, int src_len, int is_compat) +{ + uint32_t c, v; + int i, l; + uint32_t res[UNICODE_DECOMP_LEN_MAX]; + + for(i = 0; i < src_len; i++) { + c = src[i]; + if (c >= 0xac00 && c < 0xd7a4) { + /* Hangul decomposition */ + c -= 0xac00; + dbuf_put_u32(dbuf, 0x1100 + c / 588); + dbuf_put_u32(dbuf, 0x1161 + (c % 588) / 28); + v = c % 28; + if (v != 0) + dbuf_put_u32(dbuf, 0x11a7 + v); + } else { + l = unicode_decomp_char(res, c, is_compat); + if (l) { + to_nfd_rec(dbuf, (int *)res, l, is_compat); + } else { + dbuf_put_u32(dbuf, c); + } + } + } +} + +/* return 0 if not found */ +static int compose_pair(uint32_t c0, uint32_t c1) +{ + /* Hangul composition */ + if (c0 >= 0x1100 && c0 < 0x1100 + 19 && + c1 >= 0x1161 && c1 < 0x1161 + 21) { + return 0xac00 + (c0 - 0x1100) * 588 + (c1 - 0x1161) * 28; + } else if (c0 >= 0xac00 && c0 < 0xac00 + 11172 && + (c0 - 0xac00) % 28 == 0 && + c1 >= 0x11a7 && c1 < 0x11a7 + 28) { + return c0 + c1 - 0x11a7; + } else { + return unicode_compose_pair(c0, c1); + } +} + +int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, + UnicodeNormalizationEnum n_type, + void *opaque, DynBufReallocFunc *realloc_func) +{ + int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len; + bool is_compat; + DynBuf dbuf_s, *dbuf = &dbuf_s; + + is_compat = n_type >> 1; + + dbuf_init2(dbuf, opaque, realloc_func); + if (dbuf_realloc(dbuf, sizeof(int) * src_len)) + goto fail; + + /* common case: latin1 is unaffected by NFC */ + if (n_type == UNICODE_NFC) { + for(i = 0; i < src_len; i++) { + if (src[i] >= 0x100) + goto not_latin1; + } + buf = (int *)dbuf->buf; + memcpy(buf, src, src_len * sizeof(int)); + *pdst = (uint32_t *)buf; + return src_len; + not_latin1: ; + } + + to_nfd_rec(dbuf, (const int *)src, src_len, is_compat); + if (dbuf_error(dbuf)) { + fail: + *pdst = NULL; + return -1; + } + buf = (int *)dbuf->buf; + buf_len = dbuf->size / sizeof(int); + + sort_cc(buf, buf_len); + + if (buf_len <= 1 || (n_type & 1) != 0) { + /* NFD / NFKD */ + *pdst = (uint32_t *)buf; + return buf_len; + } + + i = 1; + out_len = 1; + while (i < buf_len) { + /* find the starter character and test if it is blocked from + the character at 'i' */ + last_cc = unicode_get_cc(buf[i]); + starter_pos = out_len - 1; + while (starter_pos >= 0) { + cc = unicode_get_cc(buf[starter_pos]); + if (cc == 0) + break; + if (cc >= last_cc) + goto next; + last_cc = 256; + starter_pos--; + } + if (starter_pos >= 0 && + (p = compose_pair(buf[starter_pos], buf[i])) != 0) { + buf[starter_pos] = p; + i++; + } else { + next: + buf[out_len++] = buf[i++]; + } + } + *pdst = (uint32_t *)buf; + return out_len; +} + +/* char ranges for various unicode properties */ + +static int unicode_find_name(const char *name_table, const char *name) +{ + const char *p, *r; + int pos; + size_t name_len, len; + + p = name_table; + pos = 0; + name_len = strlen(name); + while (*p) { + for(;;) { + r = strchr(p, ','); + if (!r) + len = strlen(p); + else + len = r - p; + if (len == name_len && !memcmp(p, name, name_len)) + return pos; + p += len + 1; + if (!r) + break; + } + pos++; + } + return -1; +} + +/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 + if not found */ +int unicode_script(CharRange *cr, + const char *script_name, bool is_ext) +{ + int script_idx; + const uint8_t *p, *p_end; + uint32_t c, c1, b, n, v, v_len, i, type; + CharRange cr1_s = { 0 }, *cr1 = NULL; + CharRange cr2_s = { 0 }, *cr2 = &cr2_s; + bool is_common; + + script_idx = unicode_find_name(unicode_script_name_table, script_name); + if (script_idx < 0) + return -2; + /* Note: we remove the "Unknown" Script */ + script_idx += UNICODE_SCRIPT_Unknown + 1; + + is_common = (script_idx == UNICODE_SCRIPT_Common || + script_idx == UNICODE_SCRIPT_Inherited); + if (is_ext) { + cr1 = &cr1_s; + cr_init(cr1, cr->mem_opaque, cr->realloc_func); + cr_init(cr2, cr->mem_opaque, cr->realloc_func); + } else { + cr1 = cr; + } + + p = unicode_script_table; + p_end = unicode_script_table + countof(unicode_script_table); + c = 0; + while (p < p_end) { + b = *p++; + type = b >> 7; + n = b & 0x7f; + if (n < 96) { + } else if (n < 112) { + n = (n - 96) << 8; + n |= *p++; + n += 96; + } else { + n = (n - 112) << 16; + n |= *p++ << 8; + n |= *p++; + n += 96 + (1 << 12); + } + if (type == 0) + v = 0; + else + v = *p++; + c1 = c + n + 1; + if (v == script_idx) { + if (cr_add_interval(cr1, c, c1)) + goto fail; + } + c = c1; + } + + if (is_ext) { + /* add the script extensions */ + p = unicode_script_ext_table; + p_end = unicode_script_ext_table + countof(unicode_script_ext_table); + c = 0; + while (p < p_end) { + b = *p++; + if (b < 128) { + n = b; + } else if (b < 128 + 64) { + n = (b - 128) << 8; + n |= *p++; + n += 128; + } else { + n = (b - 128 - 64) << 16; + n |= *p++ << 8; + n |= *p++; + n += 128 + (1 << 14); + } + c1 = c + n + 1; + v_len = *p++; + if (is_common) { + if (v_len != 0) { + if (cr_add_interval(cr2, c, c1)) + goto fail; + } + } else { + for(i = 0; i < v_len; i++) { + if (p[i] == script_idx) { + if (cr_add_interval(cr2, c, c1)) + goto fail; + break; + } + } + } + p += v_len; + c = c1; + } + if (is_common) { + /* remove all the characters with script extensions */ + if (cr_invert(cr2)) + goto fail; + if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, + CR_OP_INTER)) + goto fail; + } else { + if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, + CR_OP_UNION)) + goto fail; + } + cr_free(cr1); + cr_free(cr2); + } + return 0; + fail: + if (is_ext) { + cr_free(cr1); + cr_free(cr2); + } + goto fail; +} + +#define M(id) (1U << UNICODE_GC_ ## id) + +static int unicode_general_category1(CharRange *cr, uint32_t gc_mask) +{ + const uint8_t *p, *p_end; + uint32_t c, c0, b, n, v; + + p = unicode_gc_table; + p_end = unicode_gc_table + countof(unicode_gc_table); + c = 0; + while (p < p_end) { + b = *p++; + n = b >> 5; + v = b & 0x1f; + if (n == 7) { + n = *p++; + if (n < 128) { + n += 7; + } else if (n < 128 + 64) { + n = (n - 128) << 8; + n |= *p++; + n += 7 + 128; + } else { + n = (n - 128 - 64) << 16; + n |= *p++ << 8; + n |= *p++; + n += 7 + 128 + (1 << 14); + } + } + c0 = c; + c += n + 1; + if (v == 31) { + /* run of Lu / Ll */ + b = gc_mask & (M(Lu) | M(Ll)); + if (b != 0) { + if (b == (M(Lu) | M(Ll))) { + goto add_range; + } else { + c0 += ((gc_mask & M(Ll)) != 0); + for(; c0 < c; c0 += 2) { + if (cr_add_interval(cr, c0, c0 + 1)) + return -1; + } + } + } + } else if ((gc_mask >> v) & 1) { + add_range: + if (cr_add_interval(cr, c0, c)) + return -1; + } + } + return 0; +} + +static int unicode_prop1(CharRange *cr, int prop_idx) +{ + const uint8_t *p, *p_end; + uint32_t c, c0, b, bit; + + p = unicode_prop_table[prop_idx]; + p_end = p + unicode_prop_len_table[prop_idx]; + c = 0; + bit = 0; + while (p < p_end) { + c0 = c; + b = *p++; + if (b < 64) { + c += (b >> 3) + 1; + if (bit) { + if (cr_add_interval(cr, c0, c)) + return -1; + } + bit ^= 1; + c0 = c; + c += (b & 7) + 1; + } else if (b >= 0x80) { + c += b - 0x80 + 1; + } else if (b < 0x60) { + c += (((b - 0x40) << 8) | p[0]) + 1; + p++; + } else { + c += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; + p += 2; + } + if (bit) { + if (cr_add_interval(cr, c0, c)) + return -1; + } + bit ^= 1; + } + return 0; +} + +#define CASE_U (1 << 0) +#define CASE_L (1 << 1) +#define CASE_F (1 << 2) + +/* use the case conversion table to generate range of characters. + CASE_U: set char if modified by uppercasing, + CASE_L: set char if modified by lowercasing, + CASE_F: set char if modified by case folding, + */ +static int unicode_case1(CharRange *cr, int case_mask) +{ +#define MR(x) (1 << RUN_TYPE_ ## x) + const uint32_t tab_run_mask[3] = { + MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) | + MR(UF_D1_EXT) | MR(U_EXT) | MR(UF_EXT2) | MR(UF_EXT3), + + MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2), + + MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT) | MR(UF_EXT2) | MR(UF_EXT3), + }; +#undef MR + uint32_t mask, v, code, type, len, i, idx; + + if (case_mask == 0) + return 0; + mask = 0; + for(i = 0; i < 3; i++) { + if ((case_mask >> i) & 1) + mask |= tab_run_mask[i]; + } + for(idx = 0; idx < countof(case_conv_table1); idx++) { + v = case_conv_table1[idx]; + type = (v >> (32 - 17 - 7 - 4)) & 0xf; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if ((mask >> type) & 1) { + // printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1); + switch(type) { + case RUN_TYPE_UL: + if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) + goto def_case; + code += ((case_mask & CASE_U) != 0); + for(i = 0; i < len; i += 2) { + if (cr_add_interval(cr, code + i, code + i + 1)) + return -1; + } + break; + case RUN_TYPE_LSU: + if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) + goto def_case; + if (!(case_mask & CASE_U)) { + if (cr_add_interval(cr, code, code + 1)) + return -1; + } + if (cr_add_interval(cr, code + 1, code + 2)) + return -1; + if (case_mask & CASE_U) { + if (cr_add_interval(cr, code + 2, code + 3)) + return -1; + } + break; + default: + def_case: + if (cr_add_interval(cr, code, code + len)) + return -1; + break; + } + } + } + return 0; +} + +static int point_cmp(const void *p1, const void *p2, void *arg) +{ + uint32_t v1 = *(uint32_t *)p1; + uint32_t v2 = *(uint32_t *)p2; + return (v1 > v2) - (v1 < v2); +} + +static void cr_sort_and_remove_overlap(CharRange *cr) +{ + uint32_t start, end, start1, end1, i, j; + + /* the resulting ranges are not necessarily sorted and may overlap */ + rqsort(cr->points, cr->len / 2, sizeof(cr->points[0]) * 2, point_cmp, NULL); + j = 0; + for(i = 0; i < cr->len; ) { + start = cr->points[i]; + end = cr->points[i + 1]; + i += 2; + while (i < cr->len) { + start1 = cr->points[i]; + end1 = cr->points[i + 1]; + if (start1 > end) { + /* |------| + * |-------| */ + break; + } else if (end1 <= end) { + /* |------| + * |--| */ + i += 2; + } else { + /* |------| + * |-------| */ + end = end1; + i += 2; + } + } + cr->points[j] = start; + cr->points[j + 1] = end; + j += 2; + } + cr->len = j; +} + +/* canonicalize a character set using the JS regex case folding rules + (see lre_canonicalize()) */ +int cr_regexp_canonicalize(CharRange *cr, bool is_unicode) +{ + CharRange cr_inter, cr_mask, cr_result, cr_sub; + uint32_t v, code, len, i, idx, start, end, c, d_start, d_end, d; + + cr_init(&cr_mask, cr->mem_opaque, cr->realloc_func); + cr_init(&cr_inter, cr->mem_opaque, cr->realloc_func); + cr_init(&cr_result, cr->mem_opaque, cr->realloc_func); + cr_init(&cr_sub, cr->mem_opaque, cr->realloc_func); + + if (unicode_case1(&cr_mask, is_unicode ? CASE_F : CASE_U)) + goto fail; + if (cr_op(&cr_inter, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER)) + goto fail; + + if (cr_invert(&cr_mask)) + goto fail; + if (cr_op(&cr_sub, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER)) + goto fail; + + /* cr_inter = cr & cr_mask */ + /* cr_sub = cr & ~cr_mask */ + + /* use the case conversion table to compute the result */ + d_start = -1; + d_end = -1; + idx = 0; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + for(i = 0; i < cr_inter.len; i += 2) { + start = cr_inter.points[i]; + end = cr_inter.points[i + 1]; + + for(c = start; c < end; c++) { + for(;;) { + if (c >= code && c < code + len) + break; + idx++; + assert(idx < countof(case_conv_table1)); + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + } + d = lre_case_folding_entry(c, idx, v, is_unicode); + /* try to merge with the current interval */ + if (d_start == -1) { + d_start = d; + d_end = d + 1; + } else if (d_end == d) { + d_end++; + } else { + cr_add_interval(&cr_result, d_start, d_end); + d_start = d; + d_end = d + 1; + } + } + } + if (d_start != -1) { + if (cr_add_interval(&cr_result, d_start, d_end)) + goto fail; + } + + /* the resulting ranges are not necessarily sorted and may overlap */ + cr_sort_and_remove_overlap(&cr_result); + + /* or with the character not affected by the case folding */ + cr->len = 0; + if (cr_op(cr, cr_result.points, cr_result.len, cr_sub.points, cr_sub.len, CR_OP_UNION)) + goto fail; + + cr_free(&cr_inter); + cr_free(&cr_mask); + cr_free(&cr_result); + cr_free(&cr_sub); + return 0; + fail: + cr_free(&cr_inter); + cr_free(&cr_mask); + cr_free(&cr_result); + cr_free(&cr_sub); + return -1; +} + +typedef enum { + POP_GC, + POP_PROP, + POP_CASE, + POP_UNION, + POP_INTER, + POP_XOR, + POP_INVERT, + POP_END, +} PropOPEnum; + +#define POP_STACK_LEN_MAX 4 + +static int unicode_prop_ops(CharRange *cr, ...) +{ + va_list ap; + CharRange stack[POP_STACK_LEN_MAX]; + int stack_len, op, ret, i; + uint32_t a; + + va_start(ap, cr); + stack_len = 0; + for(;;) { + op = va_arg(ap, int); + switch(op) { + case POP_GC: + assert(stack_len < POP_STACK_LEN_MAX); + a = va_arg(ap, int); + cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); + if (unicode_general_category1(&stack[stack_len - 1], a)) + goto fail; + break; + case POP_PROP: + assert(stack_len < POP_STACK_LEN_MAX); + a = va_arg(ap, int); + cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); + if (unicode_prop1(&stack[stack_len - 1], a)) + goto fail; + break; + case POP_CASE: + assert(stack_len < POP_STACK_LEN_MAX); + a = va_arg(ap, int); + cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); + if (unicode_case1(&stack[stack_len - 1], a)) + goto fail; + break; + case POP_UNION: + case POP_INTER: + case POP_XOR: + { + CharRange *cr1, *cr2, *cr3; + assert(stack_len >= 2); + assert(stack_len < POP_STACK_LEN_MAX); + cr1 = &stack[stack_len - 2]; + cr2 = &stack[stack_len - 1]; + cr3 = &stack[stack_len++]; + cr_init(cr3, cr->mem_opaque, cr->realloc_func); + if (cr_op(cr3, cr1->points, cr1->len, + cr2->points, cr2->len, op - POP_UNION + CR_OP_UNION)) + goto fail; + cr_free(cr1); + cr_free(cr2); + *cr1 = *cr3; + stack_len -= 2; + } + break; + case POP_INVERT: + assert(stack_len >= 1); + if (cr_invert(&stack[stack_len - 1])) + goto fail; + break; + case POP_END: + goto done; + default: + abort(); + } + } + done: + va_end(ap); + assert(stack_len == 1); + ret = cr_copy(cr, &stack[0]); + cr_free(&stack[0]); + return ret; + fail: + va_end(ap); + for(i = 0; i < stack_len; i++) + cr_free(&stack[i]); + return -1; +} + +static const uint32_t unicode_gc_mask_table[] = { + M(Lu) | M(Ll) | M(Lt), /* LC */ + M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo), /* L */ + M(Mn) | M(Mc) | M(Me), /* M */ + M(Nd) | M(Nl) | M(No), /* N */ + M(Sm) | M(Sc) | M(Sk) | M(So), /* S */ + M(Pc) | M(Pd) | M(Ps) | M(Pe) | M(Pi) | M(Pf) | M(Po), /* P */ + M(Zs) | M(Zl) | M(Zp), /* Z */ + M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn), /* C */ +}; + +/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 + if not found */ +int unicode_general_category(CharRange *cr, const char *gc_name) +{ + int gc_idx; + uint32_t gc_mask; + + gc_idx = unicode_find_name(unicode_gc_name_table, gc_name); + if (gc_idx < 0) + return -2; + if (gc_idx <= UNICODE_GC_Co) { + gc_mask = (uint64_t)1 << gc_idx; + } else { + gc_mask = unicode_gc_mask_table[gc_idx - UNICODE_GC_LC]; + } + return unicode_general_category1(cr, gc_mask); +} + + +/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 + if not found */ +int unicode_prop(CharRange *cr, const char *prop_name) +{ + int prop_idx, ret; + + prop_idx = unicode_find_name(unicode_prop_name_table, prop_name); + if (prop_idx < 0) + return -2; + prop_idx += UNICODE_PROP_ASCII_Hex_Digit; + + ret = 0; + switch(prop_idx) { + case UNICODE_PROP_ASCII: + if (cr_add_interval(cr, 0x00, 0x7f + 1)) + return -1; + break; + case UNICODE_PROP_Any: + if (cr_add_interval(cr, 0x00000, 0x10ffff + 1)) + return -1; + break; + case UNICODE_PROP_Assigned: + ret = unicode_prop_ops(cr, + POP_GC, M(Cn), + POP_INVERT, + POP_END); + break; + case UNICODE_PROP_Math: + ret = unicode_prop_ops(cr, + POP_GC, M(Sm), + POP_PROP, UNICODE_PROP_Other_Math, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Lowercase: + ret = unicode_prop_ops(cr, + POP_GC, M(Ll), + POP_PROP, UNICODE_PROP_Other_Lowercase, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Uppercase: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu), + POP_PROP, UNICODE_PROP_Other_Uppercase, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Cased: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt), + POP_PROP, UNICODE_PROP_Other_Uppercase, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_Lowercase, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Alphabetic: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), + POP_PROP, UNICODE_PROP_Other_Uppercase, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_Lowercase, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_Alphabetic, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Grapheme_Base: + ret = unicode_prop_ops(cr, + POP_GC, M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn) | M(Zl) | M(Zp) | M(Me) | M(Mn), + POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, + POP_UNION, + POP_INVERT, + POP_END); + break; + case UNICODE_PROP_Grapheme_Extend: + ret = unicode_prop_ops(cr, + POP_GC, M(Me) | M(Mn), + POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_XID_Start: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), + POP_PROP, UNICODE_PROP_Other_ID_Start, + POP_UNION, + POP_PROP, UNICODE_PROP_Pattern_Syntax, + POP_PROP, UNICODE_PROP_Pattern_White_Space, + POP_UNION, + POP_PROP, UNICODE_PROP_XID_Start1, + POP_UNION, + POP_INVERT, + POP_INTER, + POP_END); + break; + case UNICODE_PROP_XID_Continue: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) | + M(Mn) | M(Mc) | M(Nd) | M(Pc), + POP_PROP, UNICODE_PROP_Other_ID_Start, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_ID_Continue, + POP_UNION, + POP_PROP, UNICODE_PROP_Pattern_Syntax, + POP_PROP, UNICODE_PROP_Pattern_White_Space, + POP_UNION, + POP_PROP, UNICODE_PROP_XID_Continue1, + POP_UNION, + POP_INVERT, + POP_INTER, + POP_END); + break; + case UNICODE_PROP_Changes_When_Uppercased: + ret = unicode_case1(cr, CASE_U); + break; + case UNICODE_PROP_Changes_When_Lowercased: + ret = unicode_case1(cr, CASE_L); + break; + case UNICODE_PROP_Changes_When_Casemapped: + ret = unicode_case1(cr, CASE_U | CASE_L | CASE_F); + break; + case UNICODE_PROP_Changes_When_Titlecased: + ret = unicode_prop_ops(cr, + POP_CASE, CASE_U, + POP_PROP, UNICODE_PROP_Changes_When_Titlecased1, + POP_XOR, + POP_END); + break; + case UNICODE_PROP_Changes_When_Casefolded: + ret = unicode_prop_ops(cr, + POP_CASE, CASE_F, + POP_PROP, UNICODE_PROP_Changes_When_Casefolded1, + POP_XOR, + POP_END); + break; + case UNICODE_PROP_Changes_When_NFKC_Casefolded: + ret = unicode_prop_ops(cr, + POP_CASE, CASE_F, + POP_PROP, UNICODE_PROP_Changes_When_NFKC_Casefolded1, + POP_XOR, + POP_END); + break; + /* we use the existing tables */ + case UNICODE_PROP_ID_Continue: + ret = unicode_prop_ops(cr, + POP_PROP, UNICODE_PROP_ID_Start, + POP_PROP, UNICODE_PROP_ID_Continue1, + POP_XOR, + POP_END); + break; + default: + if (prop_idx >= countof(unicode_prop_table)) + return -2; + ret = unicode_prop1(cr, prop_idx); + break; + } + return ret; +} diff --git a/NativeScript/napi/android/quickjs/source_ng/libunicode.h b/NativeScript/napi/android/quickjs/source_ng/libunicode.h new file mode 100755 index 000000000..8e6f2a01d --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/libunicode.h @@ -0,0 +1,126 @@ +/* + * Unicode utilities + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBUNICODE_H +#define LIBUNICODE_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define LRE_CC_RES_LEN_MAX 3 + +typedef enum { + UNICODE_NFC, + UNICODE_NFD, + UNICODE_NFKC, + UNICODE_NFKD, +} UnicodeNormalizationEnum; + +int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); +int lre_canonicalize(uint32_t c, bool is_unicode); +bool lre_is_cased(uint32_t c); +bool lre_is_case_ignorable(uint32_t c); + +/* char ranges */ + +typedef struct { + int len; /* in points, always even */ + int size; + uint32_t *points; /* points sorted by increasing value */ + void *mem_opaque; + void *(*realloc_func)(void *opaque, void *ptr, size_t size); +} CharRange; + +typedef enum { + CR_OP_UNION, + CR_OP_INTER, + CR_OP_XOR, +} CharRangeOpEnum; + +void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); +void cr_free(CharRange *cr); +int cr_realloc(CharRange *cr, int size); +int cr_copy(CharRange *cr, const CharRange *cr1); + +static inline int cr_add_point(CharRange *cr, uint32_t v) +{ + if (cr->len >= cr->size) { + if (cr_realloc(cr, cr->len + 1)) + return -1; + } + cr->points[cr->len++] = v; + return 0; +} + +static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2) +{ + if ((cr->len + 2) > cr->size) { + if (cr_realloc(cr, cr->len + 2)) + return -1; + } + cr->points[cr->len++] = c1; + cr->points[cr->len++] = c2; + return 0; +} + +int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len); + +static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2) +{ + uint32_t b_pt[2]; + b_pt[0] = c1; + b_pt[1] = c2 + 1; + return cr_union1(cr, b_pt, 2); +} + +int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, + const uint32_t *b_pt, int b_len, int op); + +int cr_invert(CharRange *cr); +int cr_regexp_canonicalize(CharRange *cr, bool is_unicode); + +bool lre_is_id_start(uint32_t c); +bool lre_is_id_continue(uint32_t c); +bool lre_is_white_space(uint32_t c); + +int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, + UnicodeNormalizationEnum n_type, + void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); + +/* Unicode character range functions */ + +int unicode_script(CharRange *cr, + const char *script_name, bool is_ext); +int unicode_general_category(CharRange *cr, const char *gc_name); +int unicode_prop(CharRange *cr, const char *prop_name); + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIBUNICODE_H */ diff --git a/NativeScript/napi/android/quickjs/source_ng/list.h b/NativeScript/napi/android/quickjs/source_ng/list.h new file mode 100755 index 000000000..b8dd71681 --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/list.h @@ -0,0 +1,107 @@ +/* + * Linux klist like system + * + * Copyright (c) 2016-2017 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIST_H +#define LIST_H + +#ifndef NULL +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct list_head { + struct list_head *prev; + struct list_head *next; +}; + +#define LIST_HEAD_INIT(el) { &(el), &(el) } + +/* return the pointer of type 'type *' containing 'el' as field 'member' */ +#define list_entry(el, type, member) container_of(el, type, member) + +static inline void init_list_head(struct list_head *head) +{ + head->prev = head; + head->next = head; +} + +/* insert 'el' between 'prev' and 'next' */ +static inline void __list_add(struct list_head *el, + struct list_head *prev, struct list_head *next) +{ + prev->next = el; + el->prev = prev; + el->next = next; + next->prev = el; +} + +/* add 'el' at the head of the list 'head' (= after element head) */ +static inline void list_add(struct list_head *el, struct list_head *head) +{ + __list_add(el, head, head->next); +} + +/* add 'el' at the end of the list 'head' (= before element head) */ +static inline void list_add_tail(struct list_head *el, struct list_head *head) +{ + __list_add(el, head->prev, head); +} + +static inline void list_del(struct list_head *el) +{ + struct list_head *prev, *next; + prev = el->prev; + next = el->next; + prev->next = next; + next->prev = prev; + el->prev = NULL; /* fail safe */ + el->next = NULL; /* fail safe */ +} + +static inline int list_empty(struct list_head *el) +{ + return el->next == el; +} + +#define list_for_each(el, head) \ + for(el = (head)->next; el != (head); el = el->next) + +#define list_for_each_safe(el, el1, head) \ + for(el = (head)->next, el1 = el->next; el != (head); \ + el = el1, el1 = el->next) + +#define list_for_each_prev(el, head) \ + for(el = (head)->prev; el != (head); el = el->prev) + +#define list_for_each_prev_safe(el, el1, head) \ + for(el = (head)->prev, el1 = el->prev; el != (head); \ + el = el1, el1 = el->prev) + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIST_H */ diff --git a/NativeScript/napi/android/quickjs/source_ng/meson.build b/NativeScript/napi/android/quickjs/source_ng/meson.build new file mode 100755 index 000000000..1c178513c --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/meson.build @@ -0,0 +1,635 @@ +project( + 'quickjs-ng', + 'c', + version: '0.11.0', + default_options: [ + 'c_std=gnu11,c11', + 'warning_level=3', + 'default_library=static', + ], + license: 'MIT', + license_files: 'LICENSE', + meson_version: '>=1.3.0', +) + +host_system = host_machine.system() +cc = meson.get_compiler('c') + +qjs_gcc_warning_args = [ + '-Wno-unsafe-buffer-usage', + '-Wno-sign-conversion', + '-Wno-nonportable-system-include-path', + '-Wno-implicit-int-conversion', + '-Wno-shorten-64-to-32', + '-Wno-reserved-macro-identifier', + '-Wno-reserved-identifier', + '-Wdeprecated-declarations', + + '-Wno-implicit-fallthrough', + '-Wno-sign-compare', + '-Wno-missing-field-initializers', + '-Wno-unused-parameter', + '-Wno-unused-but-set-variable', + '-Wno-array-bounds', + '-Wno-format-truncation', +] +qjs_gcc_args = [ + '-funsigned-char', +] + +if host_system == 'darwin' + # https://github.com/quickjs-ng/quickjs/issues/453 + qjs_gcc_warning_args += '-Wno-maybe-uninitialized' +endif + +# https://github.com/microsoft/cpp-docs/tree/main/docs/error-messages/compiler-warnings +qjs_msvc_warning_args = [ + '/wd4018', # -Wno-sign-conversion + '/wd4061', # -Wno-implicit-fallthrough + '/wd4100', # -Wno-unused-parameter + '/wd4200', # -Wno-zero-length-array + '/wd4242', # -Wno-shorten-64-to-32 + '/wd4244', # -Wno-shorten-64-to-32 + '/wd4245', # -Wno-sign-compare + '/wd4267', # -Wno-shorten-64-to-32 + '/wd4388', # -Wno-sign-compare + '/wd4389', # -Wno-sign-compare + '/wd4710', # Function not inlined + '/wd4711', # Function was inlined + '/wd4820', # Padding added after construct + '/wd4996', # -Wdeprecated-declarations + '/wd5045', # Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified +] +qjs_msvc_args = [ + '/experimental:c11atomics', + '/J', # -funsigned-char +] + +if cc.get_argument_syntax() == 'msvc' + add_project_arguments( + cc.get_supported_arguments(qjs_msvc_warning_args), + cc.get_id().contains('clang') ? cc.get_supported_arguments(qjs_gcc_warning_args) : [], + qjs_msvc_args, + language: 'c', + ) +else + add_project_arguments( + cc.get_supported_arguments(qjs_gcc_warning_args), + qjs_gcc_args, + language: 'c', + ) +endif + +if host_system == 'windows' + # Set a 8MB default stack size on Windows. + # It defaults to 1MB on MSVC, which is the same as our current JS stack size, + # so it will overflow and crash otherwise. + # On MinGW it defaults to 2MB. + stack_size = 8 * 1024 * 1024 + if cc.get_argument_syntax() == 'msvc' + add_project_link_arguments(f'/STACK:@stack_size@', language: 'c') + else + add_project_link_arguments(f'-Wl,--stack,@stack_size@', language: 'c') + endif +endif + +if meson.is_cross_build() + native_cc = meson.get_compiler('c', native: true) + + if native_cc.get_argument_syntax() == 'msvc' + # https://github.com/microsoft/cpp-docs/tree/main/docs/error-messages/compiler-warnings + add_project_arguments( + native_cc.get_supported_arguments(qjs_msvc_warning_args), + native_cc.get_id().contains('clang') ? native_cc.get_supported_arguments(qjs_gcc_warning_args) : [], + qjs_msvc_args, + + language: 'c', + native: true, + ) + else + add_project_arguments( + native_cc.get_supported_arguments(qjs_gcc_warning_args), + qjs_gcc_args, + + language: 'c', + native: true, + ) + endif +endif +if get_option('debug') + add_project_arguments( + cc.get_supported_arguments('-fno-omit-frame-pointer'), + language: 'c', + ) +endif + +qjs_sys_deps = [] + +m_dep = cc.find_library('m', required: false) +qjs_sys_deps += m_dep +qjs_sys_deps += dependency('threads', required: false) +qjs_sys_deps += dependency('dl', required: false) + +qjs_srcs = files( + 'cutils.c', + 'dtoa.c', + 'libregexp.c', + 'libunicode.c', + 'quickjs.c', +) +qjs_hdrs = files( + 'quickjs.h', +) + +qjs_libc = get_option('libc') +qjs_libc_srcs = files('quickjs-libc.c') +qjs_libc_hdrs = files('quickjs-libc.h') + +if qjs_libc + qjs_hdrs += qjs_libc_hdrs +endif + +qjs_parser = get_option('parser') + +qjs_c_args = ['-D_GNU_SOURCE'] + +if host_system == 'windows' + qjs_c_args += ['-DWIN32_LEAN_AND_MEAN', '-D_WIN32_WINNT=0x0601'] +endif + +if not qjs_parser + qjs_c_args += ['-DQJS_DISABLE_PARSER'] +endif + +qjs_libc_lib = static_library( + 'quickjs-libc', + qjs_libc_srcs, + + dependencies: qjs_sys_deps, + c_args: qjs_c_args, + gnu_symbol_visibility: 'hidden', +) + +qjs_lib = library( + 'qjs', + qjs_srcs, + + # export public headers + generator( + find_program('cp', 'xcopy'), + output: ['@PLAINNAME@'], + arguments: ['@INPUT@', '@OUTPUT@'], + ).process(qjs_hdrs), + + dependencies: qjs_sys_deps, + link_whole: qjs_libc ? qjs_libc_lib : [], + c_args: qjs_c_args, + gnu_symbol_visibility: 'hidden', + + install: true, + version: meson.project_version(), +) + +qjs_export_variables = [ + f'have_parser=@qjs_parser@' +] + +qjs_dep = declare_dependency( + link_with: qjs_lib, + dependencies: qjs_sys_deps, + include_directories: qjs_lib.private_dir_include(), + variables: qjs_export_variables, +) + +if host_system == 'emscripten' + qjs_wasm_export_name = 'getQuickJs' + executable( + 'qjs_wasm', + qjs_srcs, + link_args: cc.get_supported_link_arguments( + # in emscripten 3.x, this will be set to 16k which is too small for quickjs. + '-sSTACK_SIZE=@0@'.format(2 * 1024 * 1024), # let it be 2m = 2 * 1024 * 1024, otherwise, stack overflow may be occured at bootstrap + '-sNO_INVOKE_RUN', + '-sNO_EXIT_RUNTIME', + '-sMODULARIZE', # do not mess the global + '-sEXPORT_ES6', # export js file to morden es module + '-sEXPORT_NAME=@0@'.format(qjs_wasm_export_name), # give a name + '-sTEXTDECODER=1', # it will be 2 if we use -Oz, and that will cause js -> c string convertion fail + '-sNO_DEFAULT_TO_CXX', # this project is pure c project, no need for c plus plus handle + '-sEXPORTED_RUNTIME_METHODS=ccall,cwrap', + ), + dependencies: m_dep, + c_args: qjs_c_args, + ) +endif + +install_headers(qjs_hdrs, subdir: 'quickjs-ng') + +if not meson.is_subproject() + docdir = get_option('docdir') + datadir = get_option('datadir') + + if docdir == '' + docdir = datadir / 'doc' / meson.project_name() + endif + install_data( + 'LICENSE', + install_dir: docdir, + + install_tag: 'doc', + ) + install_subdir( + 'examples', + install_dir: docdir, + + strip_directory: false, + install_tag: 'doc', + ) +endif + +meson.override_dependency('quickjs-ng', qjs_dep) + +import('pkgconfig').generate( + qjs_lib, + subdirs: 'quickjs-ng', + name: 'quickjs-ng', + description: 'QuickJS, the Next Generation: a mighty JavaScript engine', + url: 'https://github.com/quickjs-ng/quickjs', + version: meson.project_version(), + variables: qjs_export_variables, +) + +if not qjs_parser + subdir_done() +endif + +# QuickJS bytecode compiler +qjsc_srcs = files( + 'qjsc.c', +) +qjsc_exe = executable( + 'qjsc', + qjsc_srcs, + + c_args: qjs_c_args, + link_with: qjs_libc ? [] : qjs_libc_lib, + dependencies: qjs_dep, + + install: true, +) + +mimalloc_dep = [] +mimalloc_sys_dep = dependency('mimalloc', required: get_option('cli_mimalloc')) +if mimalloc_sys_dep.found() + mimalloc_dep = declare_dependency( + dependencies: mimalloc_sys_dep, + compile_args: '-DQJS_USE_MIMALLOC', + ) +endif + +# QuickJS CLI +qjs_exe_srcs = files( + 'gen/repl.c', + 'gen/standalone.c', + 'qjs.c', +) +qjs_exe = executable( + 'qjs', + qjs_exe_srcs, + + c_args: qjs_c_args, + link_with: qjs_libc ? [] : qjs_libc_lib, + dependencies: [qjs_dep, mimalloc_dep], + export_dynamic: true, + + install: true, +) + +if meson.is_cross_build() + mimalloc_native_dep = [] + mimalloc_sys_native_dep = dependency('mimalloc', required: get_option('cli_mimalloc'), native: true) + if mimalloc_sys_dep.found() + mimalloc_native_dep = declare_dependency( + dependencies: mimalloc_sys_native_dep, + compile_args: '-DQJS_USE_MIMALLOC', + ) + endif + + qjs_sys_native_deps = [ + native_cc.find_library('m', required: false), + dependency('threads', required: false, native: true), + dependency('dl', required: false, native: true), + ] + qjs_native_lib = static_library( + 'qjs_native', + qjs_srcs, + qjs_libc_srcs, + + dependencies: qjs_sys_native_deps, + c_args: qjs_c_args, + gnu_symbol_visibility: 'hidden', + + build_by_default: false, + native: true, + install: false, + ) + + meson.override_find_program( + 'qjsc', + executable( + 'qjsc_native', + qjsc_srcs, + + c_args: qjs_c_args, + link_with: qjs_native_lib, + dependencies: qjs_sys_native_deps, + + build_by_default: false, + native: true, + install: false, + ), + ) + meson.override_find_program( + 'qjs', + executable( + 'qjs_native', + qjs_exe_srcs, + + c_args: qjs_c_args, + link_with: qjs_native_lib, + dependencies: [qjs_sys_native_deps, mimalloc_native_dep], + export_dynamic: true, + + build_by_default: false, + native: true, + install: false, + ), + ) +else + meson.override_find_program('qjsc', qjsc_exe) + meson.override_find_program('qjs', qjs_exe) +endif + +tests = get_option('tests').disable_auto_if(meson.is_subproject()) +examples = get_option('examples').disable_auto_if(meson.is_subproject()) + +if tests.allowed() + if host_system != 'emscripten' + # Test262 runner + run262_exe = executable( + 'run-test262', + 'run-test262.c', + qjs_libc_srcs, + + c_args: qjs_c_args, + dependencies: qjs_dep, + ) + + test( + 'test', + run262_exe, + args: ['-c', files('tests.conf')], + workdir: meson.current_source_dir(), + ) + + foreach bench : [ + 'empty_loop', + + 'date_now', + + 'prop_read', + 'prop_write', + 'prop_create', + 'prop_delete', + + 'array_read', + 'array_write', + 'array_prop_create', + 'array_length_decr', + 'array_hole_length_decr', + 'array_push', + 'array_pop', + + 'typed_array_read', + 'typed_array_write', + + 'global_read', + 'global_write', + 'global_write_strict', + + 'local_destruct', + 'global_destruct', + 'global_destruct_strict', + + 'func_call', + 'closure_var', + + 'int_arith', + 'float_arith', + + 'map_set', + 'map_delete', + 'weak_map_set', + 'weak_map_delete', + + 'array_for', + 'array_for_in', + 'array_for_of', + + 'math_min', + + 'object_null', + + 'regexp_ascii', + 'regexp_utf16', + + 'string_build1', + 'string_build2', + + 'string_slice1', + 'string_slice2', + 'string_slice3', + + 'sort_bench', + + 'int_to_string', + 'int_toString', + + 'float_to_string', + 'float_toString', + 'float_toFixed', + 'float_toPrecision', + 'float_toExponential', + + 'string_to_int', + 'string_to_float', + + 'bigint64_arith', + 'bigint256_arith', + ] + benchmark( + bench, + qjs_exe, + args: [files('tests/microbench.js'), bench], + ) + endforeach + endif + + # API test + test( + 'api', + executable( + 'api-test', + 'api-test.c', + + c_args: qjs_c_args, + dependencies: qjs_dep, + build_by_default: false, + ), + ) + + # Function.toString() test + test( + 'function_source', + executable( + 'function_source', + 'gen/function_source.c', + qjs_libc_srcs, + + c_args: qjs_c_args, + dependencies: qjs_dep, + build_by_default: false, + ), + ) +endif + +# Unicode generator +unicode_gen = executable( + 'unicode_gen', + 'cutils.c', + 'unicode_gen.c', + + c_args: qjs_c_args, + build_by_default: false, +) + +run_target( + 'libunicode-table.h', + command: [ + unicode_gen, + meson.current_source_dir() / 'unicode', + files('libunicode-table.h'), + ], +) + +# bytecode to c source code for builtin and examples +alias_target('codegen', + run_target( + 'codegen_repl.c', + command: [ + qjsc_exe, + '-ss', + '-o', files('gen/repl.c'), + '-m', + '-n', 'repl.js', + files('repl.js'), + ], + ), + run_target( + 'codegen_standalone.c', + command: [ + qjsc_exe, + '-ss', + '-o', files('gen/standalone.c'), + '-m', + '-n', 'standalone.js', + files('standalone.js'), + ], + ), + run_target( + 'codegen_function_source.c', + command: [ + qjsc_exe, + '-e', + '-o', files('gen/function_source.c'), + '-n', 'tests/function_source.js', + files('tests/function_source.js'), + ], + ), + run_target( + 'codegen_hello.c', + command: [ + qjsc_exe, + '-e', + '-o', files('gen/hello.c'), + '-n', 'examples/hello.js', + files('examples/hello.js'), + ], + ), + run_target( + 'codegen_hello_module.c', + command: [ + qjsc_exe, + '-e', + '-o', files('gen/hello_module.c'), + '-m', + '-n', 'examples/hello_module.js', + files('examples/hello_module.js'), + ], + ), + run_target( + 'codegen_test_fib.c', + command: [ + qjsc_exe, + '-e', + '-o', files('gen/test_fib.c'), + '-m', + '-n', 'examples/test_fib.js', + files('examples/test_fib.js'), + ], + ), + run_target( + 'codegen_builtin-array-fromasync.h', + command: [ + qjsc_exe, + '-C', + '-ss', + '-o', files('builtin-array-fromasync.h'), + '-n', 'builtin-array-fromasync.js', + files('builtin-array-fromasync.js'), + ], + ), +) + +if examples.allowed() + executable( + 'hello', + 'gen/hello.c', + qjs_libc_srcs, + + c_args: qjs_c_args, + dependencies: qjs_dep, + ) + + executable( + 'hello_module', + 'gen/hello_module.c', + qjs_libc_srcs, + + c_args: qjs_c_args, + dependencies: qjs_dep, + ) + + subdir('examples') + + executable( + 'test_fib', + 'examples/fib.c', + 'gen/test_fib.c', + qjs_libc_srcs, + + c_args: qjs_c_args, + dependencies: qjs_dep, + export_dynamic: true, + ) +endif diff --git a/NativeScript/napi/android/quickjs/source_ng/meson_options.txt b/NativeScript/napi/android/quickjs/source_ng/meson_options.txt new file mode 100755 index 000000000..20e661e1a --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/meson_options.txt @@ -0,0 +1,6 @@ +option('tests', type: 'feature', description: 'build tests') +option('examples', type: 'feature', description: 'build examples') +option('libc', type: 'boolean', value: false, description: 'build qjs standard library modules as part of the library') +option('cli_mimalloc', type: 'feature', value: 'disabled', description: 'build qjs cli with mimalloc') +option('docdir', type: 'string', description: 'documentation directory') +option('parser', type: 'boolean', value: true, description: 'Enable JS source code parser') diff --git a/NativeScript/napi/android/quickjs/source_ng/qjs.c b/NativeScript/napi/android/quickjs/source_ng/qjs.c new file mode 100755 index 000000000..4aed6837e --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/qjs.c @@ -0,0 +1,748 @@ +/* + * QuickJS stand alone interpreter + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "quickjs.h" +#include "quickjs-libc.h" + +#ifdef QJS_USE_MIMALLOC +#include +#endif + +extern const uint8_t qjsc_repl[]; +extern const uint32_t qjsc_repl_size; +extern const uint8_t qjsc_standalone[]; +extern const uint32_t qjsc_standalone_size; + +// Must match standalone.js +#define TRAILER_SIZE 12 +static const char trailer_magic[] = "quickjs2"; +static const int trailer_magic_size = sizeof(trailer_magic) - 1; +static const int trailer_size = TRAILER_SIZE; + +static int qjs__argc; +static char **qjs__argv; + + +static bool is_standalone(const char *exe) +{ + FILE *exe_f = fopen(exe, "rb"); + if (!exe_f) + return false; + if (fseek(exe_f, -trailer_size, SEEK_END) < 0) + goto fail; + uint8_t buf[TRAILER_SIZE]; + if (fread(buf, 1, trailer_size, exe_f) != trailer_size) + goto fail; + fclose(exe_f); + return !memcmp(buf, trailer_magic, trailer_magic_size); +fail: + fclose(exe_f); + return false; +} + +static JSValue load_standalone_module(JSContext *ctx) +{ + JSModuleDef *m; + JSValue obj, val; + obj = JS_ReadObject(ctx, qjsc_standalone, qjsc_standalone_size, JS_READ_OBJ_BYTECODE); + if (JS_IsException(obj)) + goto exception; + assert(JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE); + if (JS_ResolveModule(ctx, obj) < 0) { + JS_FreeValue(ctx, obj); + goto exception; + } + if (js_module_set_import_meta(ctx, obj, false, true) < 0) { + JS_FreeValue(ctx, obj); + goto exception; + } + val = JS_EvalFunction(ctx, JS_DupValue(ctx, obj)); + val = js_std_await(ctx, val); + + if (JS_IsException(val)) { + JS_FreeValue(ctx, obj); + exception: + js_std_dump_error(ctx); + exit(1); + } + JS_FreeValue(ctx, val); + + m = JS_VALUE_GET_PTR(obj); + JS_FreeValue(ctx, obj); + return JS_GetModuleNamespace(ctx, m); +} + +static int eval_buf(JSContext *ctx, const void *buf, int buf_len, + const char *filename, int eval_flags) +{ + bool use_realpath; + JSValue val; + int ret; + + if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) { + /* for the modules, we compile then run to be able to set + import.meta */ + val = JS_Eval(ctx, buf, buf_len, filename, + eval_flags | JS_EVAL_FLAG_COMPILE_ONLY); + if (!JS_IsException(val)) { + // ex. "" pr "/dev/stdin" + use_realpath = + !(*filename == '<' || !strncmp(filename, "/dev/", 5)); + if (js_module_set_import_meta(ctx, val, use_realpath, true) < 0) { + js_std_dump_error(ctx); + ret = -1; + goto end; + } + val = JS_EvalFunction(ctx, val); + } + val = js_std_await(ctx, val); + } else { + val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); + } + if (JS_IsException(val)) { + js_std_dump_error(ctx); + ret = -1; + } else { + ret = 0; + } +end: + JS_FreeValue(ctx, val); + return ret; +} + +static int eval_file(JSContext *ctx, const char *filename, int module) +{ + uint8_t *buf; + int ret, eval_flags; + size_t buf_len; + + buf = js_load_file(ctx, &buf_len, filename); + if (!buf) { + perror(filename); + exit(1); + } + + if (module < 0) { + module = (js__has_suffix(filename, ".mjs") || + JS_DetectModule((const char *)buf, buf_len)); + } + if (module) + eval_flags = JS_EVAL_TYPE_MODULE; + else + eval_flags = JS_EVAL_TYPE_GLOBAL; + ret = eval_buf(ctx, buf, buf_len, filename, eval_flags); + js_free(ctx, buf); + return ret; +} + +static int64_t parse_limit(const char *arg) { + char *p; + unsigned long unit = 1024; /* default to traditional KB */ + double d = strtod(arg, &p); + + if (p == arg) { + fprintf(stderr, "Invalid limit: %s\n", arg); + return -1; + } + + if (*p) { + switch (*p++) { + case 'b': case 'B': unit = 1UL << 0; break; + case 'k': case 'K': unit = 1UL << 10; break; /* IEC kibibytes */ + case 'm': case 'M': unit = 1UL << 20; break; /* IEC mebibytes */ + case 'g': case 'G': unit = 1UL << 30; break; /* IEC gigibytes */ + default: + fprintf(stderr, "Invalid limit: %s, unrecognized suffix, only k,m,g are allowed\n", arg); + return -1; + } + if (*p) { + fprintf(stderr, "Invalid limit: %s, only one suffix allowed\n", arg); + return -1; + } + } + + return (int64_t)(d * unit); +} + +static JSValue js_gc(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JS_RunGC(JS_GetRuntime(ctx)); + return JS_UNDEFINED; +} + +static JSValue js_navigator_get_userAgent(JSContext *ctx, JSValueConst this_val) +{ + char version[32]; + snprintf(version, sizeof(version), "quickjs-ng/%s", JS_GetVersion()); + return JS_NewString(ctx, version); +} + +static const JSCFunctionListEntry navigator_proto_funcs[] = { + JS_CGETSET_DEF2("userAgent", js_navigator_get_userAgent, NULL, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Navigator", JS_PROP_CONFIGURABLE), +}; + +static const JSCFunctionListEntry global_obj[] = { + JS_CFUNC_DEF("gc", 0, js_gc), +}; + +/* also used to initialize the worker context */ +static JSContext *JS_NewCustomContext(JSRuntime *rt) +{ + JSContext *ctx; + ctx = JS_NewContext(rt); + if (!ctx) + return NULL; + /* system modules */ + js_init_module_std(ctx, "qjs:std"); + js_init_module_os(ctx, "qjs:os"); + js_init_module_bjson(ctx, "qjs:bjson"); + + JSValue global = JS_GetGlobalObject(ctx); + JS_SetPropertyFunctionList(ctx, global, global_obj, countof(global_obj)); + JSValue args = JS_NewArray(ctx); + int i; + for(i = 0; i < qjs__argc; i++) { + JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, qjs__argv[i])); + } + JS_SetPropertyStr(ctx, global, "execArgv", args); + JS_SetPropertyStr(ctx, global, "argv0", JS_NewString(ctx, qjs__argv[0])); + JSValue navigator_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, navigator_proto, navigator_proto_funcs, countof(navigator_proto_funcs)); + JSValue navigator = JS_NewObjectProto(ctx, navigator_proto); + JS_DefinePropertyValueStr(ctx, global, "navigator", navigator, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE); + JS_FreeValue(ctx, global); + JS_FreeValue(ctx, navigator_proto); + + return ctx; +} + +struct trace_malloc_data { + uint8_t *base; +}; + +static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr, + struct trace_malloc_data *dp) +{ + return ptr - dp->base; +} + +static void JS_PRINTF_FORMAT_ATTR(2, 3) js_trace_malloc_printf(void *opaque, JS_PRINTF_FORMAT const char *fmt, ...) +{ + va_list ap; + int c; + + va_start(ap, fmt); + while ((c = *fmt++) != '\0') { + if (c == '%') { + /* only handle %p and %zd */ + if (*fmt == 'p') { + uint8_t *ptr = va_arg(ap, void *); + if (ptr == NULL) { + printf("NULL"); + } else { + printf("H%+06lld.%zd", + js_trace_malloc_ptr_offset(ptr, opaque), + js__malloc_usable_size(ptr)); + } + fmt++; + continue; + } + if (fmt[0] == 'z' && fmt[1] == 'd') { + size_t sz = va_arg(ap, size_t); + printf("%zd", sz); + fmt += 2; + continue; + } + } + putc(c, stdout); + } + va_end(ap); +} + +static void js_trace_malloc_init(struct trace_malloc_data *s) +{ + free(s->base = malloc(8)); +} + +static void *js_trace_calloc(void *opaque, size_t count, size_t size) +{ + void *ptr; + ptr = calloc(count, size); + js_trace_malloc_printf(opaque, "C %zd %zd -> %p\n", count, size, ptr); + return ptr; +} + +static void *js_trace_malloc(void *opaque, size_t size) +{ + void *ptr; + ptr = malloc(size); + js_trace_malloc_printf(opaque, "A %zd -> %p\n", size, ptr); + return ptr; +} + +static void js_trace_free(void *opaque, void *ptr) +{ + if (!ptr) + return; + js_trace_malloc_printf(opaque, "F %p\n", ptr); + free(ptr); +} + +static void *js_trace_realloc(void *opaque, void *ptr, size_t size) +{ + js_trace_malloc_printf(opaque, "R %zd %p", size, ptr); + ptr = realloc(ptr, size); + js_trace_malloc_printf(opaque, " -> %p\n", ptr); + return ptr; +} + +static const JSMallocFunctions trace_mf = { + js_trace_calloc, + js_trace_malloc, + js_trace_free, + js_trace_realloc, + js__malloc_usable_size +}; + +#ifdef QJS_USE_MIMALLOC +static void *js_mi_calloc(void *opaque, size_t count, size_t size) +{ + return mi_calloc(count, size); +} + +static void *js_mi_malloc(void *opaque, size_t size) +{ + return mi_malloc(size); +} + +static void js_mi_free(void *opaque, void *ptr) +{ + if (!ptr) + return; + mi_free(ptr); +} + +static void *js_mi_realloc(void *opaque, void *ptr, size_t size) +{ + return mi_realloc(ptr, size); +} + +static const JSMallocFunctions mi_mf = { + js_mi_calloc, + js_mi_malloc, + js_mi_free, + js_mi_realloc, + mi_malloc_usable_size +}; +#endif + +#define PROG_NAME "qjs" + +void help(void) +{ + printf("QuickJS-ng version %s\n" + "usage: " PROG_NAME " [options] [file [args]]\n" + "-h --help list options\n" + "-e --eval EXPR evaluate EXPR\n" + "-i --interactive go to interactive mode\n" + "-C --script load as JS classic script (default=autodetect)\n" + "-m --module load as ES module (default=autodetect)\n" + "-I --include file include an additional file\n" + " --std make 'std', 'os' and 'bjson' available to script\n" + "-T --trace trace memory allocation\n" + "-d --dump dump the memory usage stats\n" + "-D --dump-flags flags for dumping debug data (see DUMP_* defines)\n" + "-c --compile FILE compile the given JS file as a standalone executable\n" + "-o --out FILE output file for standalone executables\n" + " --exe select the executable to use as the base, defaults to the current one\n" + " --memory-limit n limit the memory usage to 'n' Kbytes\n" + " --stack-size n limit the stack size to 'n' Kbytes\n" + "-q --quit just instantiate the interpreter and quit\n", JS_GetVersion()); + exit(1); +} + +int main(int argc, char **argv) +{ + JSRuntime *rt; + JSContext *ctx; + JSValue ret = JS_UNDEFINED; + struct trace_malloc_data trace_data = { NULL }; + int r = 0; + int optind = 1; + char exebuf[JS__PATH_MAX]; + size_t exebuf_size = sizeof(exebuf); + char *compile_file = NULL; + char *exe = NULL; + char *expr = NULL; + char *dump_flags_str = NULL; + char *out = NULL; + int standalone = 0; + int interactive = 0; + int dump_memory = 0; + int dump_flags = 0; + int trace_memory = 0; + int empty_run = 0; + int module = -1; + int load_std = 0; + char *include_list[32]; + int i, include_count = 0; + int64_t memory_limit = -1; + int64_t stack_size = -1; + + /* save for later */ + qjs__argc = argc; + qjs__argv = argv; + + /* check if this is a standalone executable */ + + if (!js_exepath(exebuf, &exebuf_size) && is_standalone(exebuf)) { + standalone = 1; + goto start; + } + + dump_flags_str = getenv("QJS_DUMP_FLAGS"); + dump_flags = dump_flags_str ? strtol(dump_flags_str, NULL, 16) : 0; + + /* cannot use getopt because we want to pass the command line to + the script */ + while (optind < argc && *argv[optind] == '-') { + char *arg = argv[optind] + 1; + const char *longopt = ""; + char *optarg = NULL; + /* a single - is not an option, it also stops argument scanning */ + if (!*arg) + break; + optind++; + if (*arg == '-') { + longopt = arg + 1; + optarg = strchr(longopt, '='); + if (optarg) + *optarg++ = '\0'; + arg += strlen(arg); + /* -- stops argument scanning */ + if (!*longopt) + break; + } + for (; *arg || *longopt; longopt = "") { + char opt = *arg; + if (opt) { + arg++; + if (!optarg && *arg) + optarg = arg; + } + if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { + help(); + continue; + } + if (opt == 'e' || !strcmp(longopt, "eval")) { + if (!optarg) { + if (optind >= argc) { + fprintf(stderr, "qjs: missing expression for -e\n"); + exit(1); + } + optarg = argv[optind++]; + } + expr = optarg; + break; + } + if (opt == 'I' || !strcmp(longopt, "include")) { + if (optind >= argc) { + fprintf(stderr, "expecting filename"); + exit(1); + } + if (include_count >= countof(include_list)) { + fprintf(stderr, "too many included files"); + exit(1); + } + include_list[include_count++] = argv[optind++]; + continue; + } + if (opt == 'i' || !strcmp(longopt, "interactive")) { + interactive++; + continue; + } + if (opt == 'm' || !strcmp(longopt, "module")) { + module = 1; + continue; + } + if (opt == 'C' || !strcmp(longopt, "script")) { + module = 0; + continue; + } + if (opt == 'd' || !strcmp(longopt, "dump")) { + dump_memory++; + continue; + } + if (opt == 'D' || !strcmp(longopt, "dump-flags")) { + dump_flags = optarg ? strtol(optarg, NULL, 16) : 0; + break; + } + if (opt == 'T' || !strcmp(longopt, "trace")) { + trace_memory++; + continue; + } + if (!strcmp(longopt, "std")) { + load_std = 1; + continue; + } + if (opt == 'q' || !strcmp(longopt, "quit")) { + empty_run++; + continue; + } + if (!strcmp(longopt, "memory-limit")) { + if (!optarg) { + if (optind >= argc) { + fprintf(stderr, "expecting memory limit"); + exit(1); + } + optarg = argv[optind++]; + } + memory_limit = parse_limit(optarg); + break; + } + if (!strcmp(longopt, "stack-size")) { + if (!optarg) { + if (optind >= argc) { + fprintf(stderr, "expecting stack size"); + exit(1); + } + optarg = argv[optind++]; + } + stack_size = parse_limit(optarg); + break; + } + if (opt == 'c' || !strcmp(longopt, "compile")) { + if (!optarg) { + if (optind >= argc) { + fprintf(stderr, "qjs: missing file for -c\n"); + exit(1); + } + optarg = argv[optind++]; + } + compile_file = optarg; + break; + } + if (opt == 'o' || !strcmp(longopt, "out")) { + if (!optarg) { + if (optind >= argc) { + fprintf(stderr, "qjs: missing file for -o\n"); + exit(1); + } + optarg = argv[optind++]; + } + out = optarg; + break; + } + if (!strcmp(longopt, "exe")) { + if (!optarg) { + if (optind >= argc) { + fprintf(stderr, "qjs: missing file for --exe\n"); + exit(1); + } + optarg = argv[optind++]; + } + exe = optarg; + break; + } + if (opt) { + fprintf(stderr, "qjs: unknown option '-%c'\n", opt); + } else { + fprintf(stderr, "qjs: unknown option '--%s'\n", longopt); + } + help(); + } + } + + if (compile_file && !out) + help(); + +start: + + if (trace_memory) { + js_trace_malloc_init(&trace_data); + rt = JS_NewRuntime2(&trace_mf, &trace_data); + } else { +#ifdef QJS_USE_MIMALLOC + rt = JS_NewRuntime2(&mi_mf, NULL); +#else + rt = JS_NewRuntime(); +#endif + } + if (!rt) { + fprintf(stderr, "qjs: cannot allocate JS runtime\n"); + exit(2); + } + if (memory_limit >= 0) + JS_SetMemoryLimit(rt, (size_t)memory_limit); + if (stack_size >= 0) + JS_SetMaxStackSize(rt, (size_t)stack_size); + if (dump_flags != 0) + JS_SetDumpFlags(rt, dump_flags); + js_std_set_worker_new_context_func(JS_NewCustomContext); + js_std_init_handlers(rt); + ctx = JS_NewCustomContext(rt); + if (!ctx) { + fprintf(stderr, "qjs: cannot allocate JS context\n"); + exit(2); + } + + /* loader for ES6 modules */ + JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); + + /* exit on unhandled promise rejections */ + JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, NULL); + + if (!empty_run) { + js_std_add_helpers(ctx, argc - optind, argv + optind); + + /* make 'std' and 'os' visible to non module code */ + if (load_std) { + const char *str = + "import * as bjson from 'qjs:bjson';\n" + "import * as std from 'qjs:std';\n" + "import * as os from 'qjs:os';\n" + "globalThis.bjson = bjson;\n" + "globalThis.std = std;\n" + "globalThis.os = os;\n"; + eval_buf(ctx, str, strlen(str), "", JS_EVAL_TYPE_MODULE); + } + + for(i = 0; i < include_count; i++) { + if (eval_file(ctx, include_list[i], 0)) + goto fail; + } + + if (standalone) { + JSValue ns = load_standalone_module(ctx); + if (JS_IsException(ns)) + goto fail; + JSValue func = JS_GetPropertyStr(ctx, ns, "runStandalone"); + JS_FreeValue(ctx, ns); + if (JS_IsException(func)) + goto fail; + ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL); + JS_FreeValue(ctx, func); + } else if (compile_file) { + JSValue ns = load_standalone_module(ctx); + if (JS_IsException(ns)) + goto fail; + JSValue func = JS_GetPropertyStr(ctx, ns, "compileStandalone"); + JS_FreeValue(ctx, ns); + if (JS_IsException(func)) + goto fail; + JSValue args[3]; + args[0] = JS_NewString(ctx, compile_file); + args[1] = JS_NewString(ctx, out); + args[2] = exe != NULL ? JS_NewString(ctx, exe) : JS_UNDEFINED; + ret = JS_Call(ctx, func, JS_UNDEFINED, 3, (JSValueConst *)args); + JS_FreeValue(ctx, func); + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + JS_FreeValue(ctx, args[2]); + } else if (expr) { + int flags = module ? JS_EVAL_TYPE_MODULE : 0; + if (eval_buf(ctx, expr, strlen(expr), "", flags)) + goto fail; + } else if (optind >= argc) { + /* interactive mode */ + interactive = 1; + } else { + const char *filename; + filename = argv[optind]; + if (eval_file(ctx, filename, module)) + goto fail; + } + if (interactive) { + JS_SetHostPromiseRejectionTracker(rt, NULL, NULL); + js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0); + } + if (standalone || compile_file) { + if (JS_IsException(ret)) { + r = 1; + } else { + JS_FreeValue(ctx, ret); + r = js_std_loop(ctx); + } + } else { + r = js_std_loop(ctx); + } + if (r) { + js_std_dump_error(ctx); + goto fail; + } + } + + if (dump_memory) { + JSMemoryUsage stats; + JS_ComputeMemoryUsage(rt, &stats); + JS_DumpMemoryUsage(stdout, &stats, rt); + } + js_std_free_handlers(rt); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + + if (empty_run && dump_memory) { + clock_t t[5]; + double best[5] = {0}; + int i, j; + for (i = 0; i < 100; i++) { + t[0] = clock(); + rt = JS_NewRuntime(); + t[1] = clock(); + ctx = JS_NewContext(rt); + t[2] = clock(); + JS_FreeContext(ctx); + t[3] = clock(); + JS_FreeRuntime(rt); + t[4] = clock(); + for (j = 4; j > 0; j--) { + double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC; + if (i == 0 || best[j] > ms) + best[j] = ms; + } + } + printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n", + best[1] + best[2] + best[3] + best[4], + best[1], best[2], best[3], best[4]); + } + return 0; + fail: + js_std_free_handlers(rt); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return 1; +} diff --git a/NativeScript/napi/android/quickjs/source_ng/qjsc.c b/NativeScript/napi/android/quickjs/source_ng/qjsc.c new file mode 100755 index 000000000..395bcb34d --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/qjsc.c @@ -0,0 +1,673 @@ +/* + * QuickJS command line compiler + * + * Copyright (c) 2018-2024 Fabrice Bellard + * Copyright (c) 2023-2025 Ben Noordhuis + * Copyright (c) 2023-2025 Saúl Ibarra Corretgé + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "quickjs-libc.h" + +typedef enum { + OUTPUT_C, + OUTPUT_C_MAIN, + OUTPUT_RAW, +} OutputTypeEnum; + +typedef struct { + char *name; + char *short_name; + int flags; +} namelist_entry_t; + +typedef struct namelist_t { + namelist_entry_t *array; + int count; + int size; +} namelist_t; + +static namelist_t cname_list; +static namelist_t cmodule_list; +static namelist_t init_module_list; +static OutputTypeEnum output_type; +static FILE *outfile; +static const char *c_ident_prefix = "qjsc_"; +static int strip; + +void namelist_add(namelist_t *lp, const char *name, const char *short_name, + int flags) +{ + namelist_entry_t *e; + if (lp->count == lp->size) { + size_t newsize = lp->size + (lp->size >> 1) + 4; + namelist_entry_t *a = + realloc(lp->array, sizeof(lp->array[0]) * newsize); + /* XXX: check for realloc failure */ + lp->array = a; + lp->size = newsize; + } + e = &lp->array[lp->count++]; + e->name = strdup(name); + if (short_name) + e->short_name = strdup(short_name); + else + e->short_name = NULL; + e->flags = flags; +} + +void namelist_free(namelist_t *lp) +{ + while (lp->count > 0) { + namelist_entry_t *e = &lp->array[--lp->count]; + free(e->name); + free(e->short_name); + } + free(lp->array); + lp->array = NULL; + lp->size = 0; +} + +namelist_entry_t *namelist_find(namelist_t *lp, const char *name) +{ + int i; + for(i = 0; i < lp->count; i++) { + namelist_entry_t *e = &lp->array[i]; + if (!strcmp(e->name, name)) + return e; + } + return NULL; +} + +static void get_c_name(char *buf, size_t buf_size, const char *file) +{ + const char *p, *r; + size_t len, i; + int c; + char *q; + + p = strrchr(file, '/'); + if (!p) + p = file; + else + p++; + r = strrchr(p, '.'); + if (!r) + len = strlen(p); + else + len = r - p; + js__pstrcpy(buf, buf_size, c_ident_prefix); + q = buf + strlen(buf); + for(i = 0; i < len; i++) { + c = p[i]; + if (!((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z'))) { + c = '_'; + } + if ((q - buf) < buf_size - 1) + *q++ = c; + } + *q = '\0'; +} + +static void dump_hex(FILE *f, const uint8_t *buf, size_t len) +{ + size_t i, col; + col = 0; + for(i = 0; i < len; i++) { + fprintf(f, " 0x%02x,", buf[i]); + if (++col == 8) { + fprintf(f, "\n"); + col = 0; + } + } + if (col != 0) + fprintf(f, "\n"); +} + +static void output_object_code(JSContext *ctx, + FILE *fo, JSValue obj, const char *c_name, + bool load_only) +{ + uint8_t *out_buf; + size_t out_buf_len; + int flags = JS_WRITE_OBJ_BYTECODE; + + if (strip) { + flags |= JS_WRITE_OBJ_STRIP_SOURCE; + if (strip > 1) + flags |= JS_WRITE_OBJ_STRIP_DEBUG; + } + + out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags); + if (!out_buf) { + js_std_dump_error(ctx); + exit(1); + } + + namelist_add(&cname_list, c_name, NULL, load_only); + + if (output_type == OUTPUT_RAW) { + fwrite(out_buf, 1, out_buf_len, fo); + } else { + fprintf(fo, "const uint32_t %s_size = %u;\n\n", + c_name, (unsigned int)out_buf_len); + fprintf(fo, "const uint8_t %s[%u] = {\n", + c_name, (unsigned int)out_buf_len); + dump_hex(fo, out_buf, out_buf_len); + fprintf(fo, "};\n\n"); + } + + js_free(ctx, out_buf); +} + +static int js_module_dummy_init(JSContext *ctx, JSModuleDef *m) +{ + /* should never be called when compiling JS code */ + abort(); + return -1; // pacify compiler +} + +static void find_unique_cname(char *cname, size_t cname_size) +{ + char cname1[1024]; + int suffix_num; + size_t len, max_len; + assert(cname_size >= 32); + /* find a C name not matching an existing module C name by + adding a numeric suffix */ + len = strlen(cname); + max_len = cname_size - 16; + if (len > max_len) + cname[max_len] = '\0'; + suffix_num = 1; + for(;;) { + snprintf(cname1, sizeof(cname1), "%s_%d", cname, suffix_num); + if (!namelist_find(&cname_list, cname1)) + break; + suffix_num++; + } + js__pstrcpy(cname, cname_size, cname1); +} + +JSModuleDef *jsc_module_loader(JSContext *ctx, + const char *module_name, void *opaque) +{ + JSModuleDef *m; + namelist_entry_t *e; + + /* check if it is a declared C or system module */ + e = namelist_find(&cmodule_list, module_name); + if (e) { + /* add in the static init module list */ + namelist_add(&init_module_list, e->name, e->short_name, 0); + /* create a dummy module */ + m = JS_NewCModule(ctx, module_name, js_module_dummy_init); + } else if (js__has_suffix(module_name, ".so")) { + JS_ThrowReferenceError(ctx, "%s: dynamically linking to shared libraries not supported", + module_name); + return NULL; + } else { + size_t buf_len; + uint8_t *buf; + JSValue func_val; + char cname[1000]; + + buf = js_load_file(ctx, &buf_len, module_name); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s'", + module_name); + return NULL; + } + + /* compile the module */ + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + if (JS_IsException(func_val)) + return NULL; + get_c_name(cname, sizeof(cname), module_name); + if (namelist_find(&cname_list, cname)) { + find_unique_cname(cname, sizeof(cname)); + } + output_object_code(ctx, outfile, func_val, cname, true); + + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + } + return m; +} + +static void compile_file(JSContext *ctx, FILE *fo, + const char *filename, + const char *script_name, + const char *c_name1, + int module) +{ + uint8_t *buf; + char c_name[1024]; + int eval_flags; + JSValue obj; + size_t buf_len; + + buf = js_load_file(ctx, &buf_len, filename); + if (!buf) { + fprintf(stderr, "Could not load '%s'\n", filename); + exit(1); + } + eval_flags = JS_EVAL_FLAG_COMPILE_ONLY; + if (module < 0) { + module = (js__has_suffix(filename, ".mjs") || + JS_DetectModule((const char *)buf, buf_len)); + } + if (module) + eval_flags |= JS_EVAL_TYPE_MODULE; + else + eval_flags |= JS_EVAL_TYPE_GLOBAL; + obj = JS_Eval(ctx, (const char *)buf, buf_len, script_name ? script_name : filename, eval_flags); + if (JS_IsException(obj)) { + js_std_dump_error(ctx); + exit(1); + } + js_free(ctx, buf); + if (c_name1) { + js__pstrcpy(c_name, sizeof(c_name), c_name1); + } else { + get_c_name(c_name, sizeof(c_name), filename); + } + output_object_code(ctx, fo, obj, c_name, false); + JS_FreeValue(ctx, obj); +} + +static const char main_c_template1[] = + "int main(int argc, char **argv)\n" + "{\n" + " int r;\n" + " JSRuntime *rt;\n" + " JSContext *ctx;\n" + " r = 0;\n" + " rt = JS_NewRuntime();\n" + " js_std_set_worker_new_context_func(JS_NewCustomContext);\n" + " js_std_init_handlers(rt);\n" + ; + +static const char main_c_template2[] = + " r = js_std_loop(ctx);\n" + " if (r) {\n" + " js_std_dump_error(ctx);\n" + " }\n" + " js_std_free_handlers(rt);\n" + " JS_FreeContext(ctx);\n" + " JS_FreeRuntime(rt);\n" + " return r;\n" + "}\n"; + +#define PROG_NAME "qjsc" + +void help(void) +{ + printf("QuickJS-ng Compiler version %s\n" + "usage: " PROG_NAME " [options] [files]\n" + "\n" + "options are:\n" + "-b output raw bytecode instead of C code\n" + "-e output main() and bytecode in a C file\n" + "-o output set the output filename\n" + "-n script_name set the script name (as used in stack traces)\n" + "-N cname set the C name of the generated data\n" + "-C compile as JS classic script (default=autodetect)\n" + "-m compile as ES module (default=autodetect)\n" + "-D module_name compile a dynamically loaded module or worker\n" + "-M module_name[,cname] add initialization code for an external C module\n" + "-p prefix set the prefix of the generated C names\n" + "-P do not add default system modules\n" + "-s strip the source code, specify twice to also strip debug info\n" + "-S n set the maximum stack size to 'n' bytes (default=%d)\n", + JS_GetVersion(), + JS_DEFAULT_STACK_SIZE); + exit(1); +} + +// TODO(bnoordhuis) share with qjs.c maybe +static int64_t parse_limit(const char *arg) { + char *p; + unsigned long unit = 1; // bytes for backcompat; qjs defaults to kilobytes + double d = strtod(arg, &p); + + if (p == arg) { + fprintf(stderr, "qjsc: invalid limit: %s\n", arg); + return -1; + } + + if (*p) { + switch (*p++) { + case 'b': case 'B': unit = 1UL << 0; break; + case 'k': case 'K': unit = 1UL << 10; break; /* IEC kibibytes */ + case 'm': case 'M': unit = 1UL << 20; break; /* IEC mebibytes */ + case 'g': case 'G': unit = 1UL << 30; break; /* IEC gigibytes */ + default: + fprintf(stderr, "qjsc: invalid limit: %s, unrecognized suffix, only k,m,g are allowed\n", arg); + return -1; + } + if (*p) { + fprintf(stderr, "qjsc: invalid limit: %s, only one suffix allowed\n", arg); + return -1; + } + } + + return (int64_t)(d * unit); +} + +static void check_hasarg(int optind, int argc, int opt) +{ + if (optind >= argc) { + fprintf(stderr, "qjsc: missing file for -%c\n", opt); + exit(1); + } +} + +int main(int argc, char **argv) +{ + int optind = 1; + int i; + const char *out_filename, *cname, *script_name; + char cfilename[1024]; + FILE *fo; + JSRuntime *rt; + JSContext *ctx; + int module; + size_t stack_size; + namelist_t dynamic_module_list; + bool load_system_modules = true; + + out_filename = NULL; + script_name = NULL; + output_type = OUTPUT_C; + cname = NULL; + module = -1; + strip = 0; + stack_size = 0; + memset(&dynamic_module_list, 0, sizeof(dynamic_module_list)); + + + while (optind < argc && *argv[optind] == '-') { + char *arg = argv[optind] + 1; + const char *longopt = ""; + char *optarg = NULL; + /* a single - is not an option, it also stops argument scanning */ + if (!*arg) + break; + optind++; + if (*arg == '-') { + longopt = arg + 1; + optarg = strchr(longopt, '='); + if (optarg) + *optarg++ = '\0'; + arg += strlen(arg); + /* -- stops argument scanning */ + if (!*longopt) + break; + } + for (; *arg || *longopt; longopt = "") { + char opt = *arg; + if (opt) { + arg++; + if (!optarg && *arg) + optarg = arg; + } + if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { + help(); + continue; + } + if (opt == 'b') { + output_type = OUTPUT_RAW; + continue; + } + if (opt == 'o') { + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } + out_filename = optarg; + continue; + } + if (opt == 'e') { + output_type = OUTPUT_C_MAIN; + continue; + } + if (opt == 'n') { + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } + script_name = optarg; + continue; + } + if (opt == 'N') { + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } + cname = optarg; + continue; + } + if (opt == 'C') { + module = 0; + continue; + } + if (opt == 'm') { + module = 1; + continue; + } + if (opt == 'M') { + char *p; + char path[1024]; + char cname[1024]; + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } + js__pstrcpy(path, sizeof(path), optarg); + p = strchr(path, ','); + if (p) { + *p = '\0'; + js__pstrcpy(cname, sizeof(cname), p + 1); + } else { + get_c_name(cname, sizeof(cname), path); + } + namelist_add(&cmodule_list, path, cname, 0); + continue; + } + if (opt == 'D') { + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } + namelist_add(&dynamic_module_list, optarg, NULL, 0); + continue; + } + if (opt == 'P') { + load_system_modules = false; + continue; + } + if (opt == 's') { + strip++; + continue; + } + if (opt == 'p') { + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } + c_ident_prefix = optarg; + continue; + } + if (opt == 'S') { + if (!optarg) { + check_hasarg(optind, argc, opt); + optarg = argv[optind++]; + } + stack_size = parse_limit(optarg); + continue; + } + help(); + } + } + + if (load_system_modules) { + /* add system modules */ + namelist_add(&cmodule_list, "qjs:std", "std", 0); + namelist_add(&cmodule_list, "qjs:os", "os", 0); + namelist_add(&cmodule_list, "qjs:bjson", "bjson", 0); + namelist_add(&cmodule_list, "std", "std", 0); + namelist_add(&cmodule_list, "os", "os", 0); + namelist_add(&cmodule_list, "bjson", "bjson", 0); + } + + if (optind >= argc) + help(); + + if (!out_filename) + out_filename = "out.c"; + + js__pstrcpy(cfilename, sizeof(cfilename), out_filename); + + if (output_type == OUTPUT_RAW) + fo = fopen(cfilename, "wb"); + else + fo = fopen(cfilename, "w"); + + if (!fo) { + perror(cfilename); + exit(1); + } + outfile = fo; + + rt = JS_NewRuntime(); + ctx = JS_NewContext(rt); + + /* loader for ES6 modules */ + JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL); + + if (output_type != OUTPUT_RAW) { + fprintf(fo, "/* File generated automatically by the QuickJS-ng compiler. */\n" + "\n" + ); + } + + if (output_type == OUTPUT_C_MAIN) { + fprintf(fo, "#include \"quickjs-libc.h\"\n" + "\n" + ); + } else if (output_type == OUTPUT_C) { + fprintf(fo, "#include \n" + "\n" + ); + } + + for(i = optind; i < argc; i++) { + const char *filename = argv[i]; + compile_file(ctx, fo, filename, script_name, cname, module); + cname = NULL; + } + + for(i = 0; i < dynamic_module_list.count; i++) { + if (!jsc_module_loader(ctx, dynamic_module_list.array[i].name, NULL)) { + fprintf(stderr, "Could not load dynamic module '%s'\n", + dynamic_module_list.array[i].name); + exit(1); + } + } + + if (output_type == OUTPUT_C_MAIN) { + fprintf(fo, + "static JSContext *JS_NewCustomContext(JSRuntime *rt)\n" + "{\n" + " JSContext *ctx = JS_NewContext(rt);\n" + " if (!ctx)\n" + " return NULL;\n"); + /* add the precompiled modules (XXX: could modify the module + loader instead) */ + for(i = 0; i < init_module_list.count; i++) { + namelist_entry_t *e = &init_module_list.array[i]; + /* initialize the static C modules */ + + fprintf(fo, + " {\n" + " extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n" + " js_init_module_%s(ctx, \"%s\");\n" + " }\n", + e->short_name, e->short_name, e->name); + } + for(i = 0; i < cname_list.count; i++) { + namelist_entry_t *e = &cname_list.array[i]; + if (e->flags) { + fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 1);\n", + e->name, e->name); + } + } + fprintf(fo, + " return ctx;\n" + "}\n\n"); + + fputs(main_c_template1, fo); + + if (stack_size != 0) { + fprintf(fo, " JS_SetMaxStackSize(rt, %u);\n", + (unsigned int)stack_size); + } + + /* add the module loader */ + fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n"); + + fprintf(fo, + " ctx = JS_NewCustomContext(rt);\n" + " js_std_add_helpers(ctx, argc, argv);\n"); + + for(i = 0; i < cname_list.count; i++) { + namelist_entry_t *e = &cname_list.array[i]; + if (!e->flags) { + fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 0);\n", + e->name, e->name); + } + } + fputs(main_c_template2, fo); + } + + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + + fclose(fo); + + namelist_free(&cname_list); + namelist_free(&cmodule_list); + namelist_free(&init_module_list); + return 0; +} diff --git a/NativeScript/napi/android/quickjs/source_ng/quickjs-atom.h b/NativeScript/napi/android/quickjs/source_ng/quickjs-atom.h new file mode 100755 index 000000000..2a1232843 --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/quickjs-atom.h @@ -0,0 +1,265 @@ +/* + * QuickJS atom definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef DEF + +/* Note: first atoms are considered as keywords in the parser */ +DEF(null, "null") /* must be first */ +DEF(false, "false") +DEF(true, "true") +DEF(if, "if") +DEF(else, "else") +DEF(return, "return") +DEF(var, "var") +DEF(this, "this") +DEF(delete, "delete") +DEF(void, "void") +DEF(typeof, "typeof") +DEF(new, "new") +DEF(in, "in") +DEF(instanceof, "instanceof") +DEF(do, "do") +DEF(while, "while") +DEF(for, "for") +DEF(break, "break") +DEF(continue, "continue") +DEF(switch, "switch") +DEF(case, "case") +DEF(default, "default") +DEF(throw, "throw") +DEF(try, "try") +DEF(catch, "catch") +DEF(finally, "finally") +DEF(function, "function") +DEF(debugger, "debugger") +DEF(with, "with") +/* FutureReservedWord */ +DEF(class, "class") +DEF(const, "const") +DEF(enum, "enum") +DEF(export, "export") +DEF(extends, "extends") +DEF(import, "import") +DEF(super, "super") +/* FutureReservedWords when parsing strict mode code */ +DEF(implements, "implements") +DEF(interface, "interface") +DEF(let, "let") +DEF(package, "package") +DEF(private, "private") +DEF(protected, "protected") +DEF(public, "public") +DEF(static, "static") +DEF(yield, "yield") +DEF(await, "await") + +/* empty string */ +DEF(empty_string, "") +/* identifiers */ +DEF(keys, "keys") +DEF(size, "size") +DEF(length, "length") +DEF(message, "message") +DEF(cause, "cause") +DEF(errors, "errors") +DEF(stack, "stack") +DEF(name, "name") +DEF(toString, "toString") +DEF(toLocaleString, "toLocaleString") +DEF(valueOf, "valueOf") +DEF(eval, "eval") +DEF(prototype, "prototype") +DEF(constructor, "constructor") +DEF(configurable, "configurable") +DEF(writable, "writable") +DEF(enumerable, "enumerable") +DEF(value, "value") +DEF(get, "get") +DEF(set, "set") +DEF(of, "of") +DEF(__proto__, "__proto__") +DEF(undefined, "undefined") +DEF(number, "number") +DEF(boolean, "boolean") +DEF(string, "string") +DEF(object, "object") +DEF(symbol, "symbol") +DEF(integer, "integer") +DEF(unknown, "unknown") +DEF(arguments, "arguments") +DEF(callee, "callee") +DEF(caller, "caller") +DEF(_eval_, "") +DEF(_ret_, "") +DEF(_var_, "") +DEF(_arg_var_, "") +DEF(_with_, "") +DEF(lastIndex, "lastIndex") +DEF(target, "target") +DEF(index, "index") +DEF(input, "input") +DEF(defineProperties, "defineProperties") +DEF(apply, "apply") +DEF(join, "join") +DEF(concat, "concat") +DEF(split, "split") +DEF(construct, "construct") +DEF(getPrototypeOf, "getPrototypeOf") +DEF(setPrototypeOf, "setPrototypeOf") +DEF(isExtensible, "isExtensible") +DEF(preventExtensions, "preventExtensions") +DEF(has, "has") +DEF(deleteProperty, "deleteProperty") +DEF(defineProperty, "defineProperty") +DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor") +DEF(ownKeys, "ownKeys") +DEF(add, "add") +DEF(done, "done") +DEF(next, "next") +DEF(values, "values") +DEF(source, "source") +DEF(flags, "flags") +DEF(global, "global") +DEF(unicode, "unicode") +DEF(raw, "raw") +DEF(new_target, "new.target") +DEF(this_active_func, "this.active_func") +DEF(home_object, "") +DEF(computed_field, "") +DEF(static_computed_field, "") /* must come after computed_fields */ +DEF(class_fields_init, "") +DEF(brand, "") +DEF(hash_constructor, "#constructor") +DEF(as, "as") +DEF(from, "from") +DEF(fromAsync, "fromAsync") +DEF(meta, "meta") +DEF(_default_, "*default*") +DEF(_star_, "*") +DEF(Module, "Module") +DEF(then, "then") +DEF(resolve, "resolve") +DEF(reject, "reject") +DEF(promise, "promise") +DEF(proxy, "proxy") +DEF(revoke, "revoke") +DEF(async, "async") +DEF(exec, "exec") +DEF(groups, "groups") +DEF(indices, "indices") +DEF(status, "status") +DEF(reason, "reason") +DEF(globalThis, "globalThis") +DEF(bigint, "bigint") +DEF(not_equal, "not-equal") +DEF(timed_out, "timed-out") +DEF(ok, "ok") +DEF(toJSON, "toJSON") +DEF(maxByteLength, "maxByteLength") +/* class names */ +DEF(Object, "Object") +DEF(Array, "Array") +DEF(Error, "Error") +DEF(Number, "Number") +DEF(String, "String") +DEF(Boolean, "Boolean") +DEF(Symbol, "Symbol") +DEF(Arguments, "Arguments") +DEF(Math, "Math") +DEF(JSON, "JSON") +DEF(Date, "Date") +DEF(Function, "Function") +DEF(GeneratorFunction, "GeneratorFunction") +DEF(ForInIterator, "ForInIterator") +DEF(RegExp, "RegExp") +DEF(ArrayBuffer, "ArrayBuffer") +DEF(SharedArrayBuffer, "SharedArrayBuffer") +/* must keep same order as class IDs for typed arrays */ +DEF(Uint8ClampedArray, "Uint8ClampedArray") +DEF(Int8Array, "Int8Array") +DEF(Uint8Array, "Uint8Array") +DEF(Int16Array, "Int16Array") +DEF(Uint16Array, "Uint16Array") +DEF(Int32Array, "Int32Array") +DEF(Uint32Array, "Uint32Array") +DEF(BigInt64Array, "BigInt64Array") +DEF(BigUint64Array, "BigUint64Array") +DEF(Float16Array, "Float16Array") +DEF(Float32Array, "Float32Array") +DEF(Float64Array, "Float64Array") +DEF(DataView, "DataView") +DEF(BigInt, "BigInt") +DEF(WeakRef, "WeakRef") +DEF(FinalizationRegistry, "FinalizationRegistry") +DEF(Map, "Map") +DEF(Set, "Set") /* Map + 1 */ +DEF(WeakMap, "WeakMap") /* Map + 2 */ +DEF(WeakSet, "WeakSet") /* Map + 3 */ +DEF(Iterator, "Iterator") +DEF(IteratorConcat, "Iterator Concat") +DEF(IteratorHelper, "Iterator Helper") +DEF(IteratorWrap, "Iterator Wrap") +DEF(Map_Iterator, "Map Iterator") +DEF(Set_Iterator, "Set Iterator") +DEF(Array_Iterator, "Array Iterator") +DEF(String_Iterator, "String Iterator") +DEF(RegExp_String_Iterator, "RegExp String Iterator") +DEF(Generator, "Generator") +DEF(Proxy, "Proxy") +DEF(Promise, "Promise") +DEF(PromiseResolveFunction, "PromiseResolveFunction") +DEF(PromiseRejectFunction, "PromiseRejectFunction") +DEF(AsyncFunction, "AsyncFunction") +DEF(AsyncFunctionResolve, "AsyncFunctionResolve") +DEF(AsyncFunctionReject, "AsyncFunctionReject") +DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction") +DEF(AsyncGenerator, "AsyncGenerator") +DEF(EvalError, "EvalError") +DEF(RangeError, "RangeError") +DEF(ReferenceError, "ReferenceError") +DEF(SyntaxError, "SyntaxError") +DEF(TypeError, "TypeError") +DEF(URIError, "URIError") +DEF(InternalError, "InternalError") +DEF(DOMException, "DOMException") +DEF(CallSite, "CallSite") +/* private symbols */ +DEF(Private_brand, "") +/* symbols */ +DEF(Symbol_toPrimitive, "Symbol.toPrimitive") +DEF(Symbol_iterator, "Symbol.iterator") +DEF(Symbol_match, "Symbol.match") +DEF(Symbol_matchAll, "Symbol.matchAll") +DEF(Symbol_replace, "Symbol.replace") +DEF(Symbol_search, "Symbol.search") +DEF(Symbol_split, "Symbol.split") +DEF(Symbol_toStringTag, "Symbol.toStringTag") +DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") +DEF(Symbol_hasInstance, "Symbol.hasInstance") +DEF(Symbol_species, "Symbol.species") +DEF(Symbol_unscopables, "Symbol.unscopables") +DEF(Symbol_asyncIterator, "Symbol.asyncIterator") + +#endif /* DEF */ diff --git a/NativeScript/napi/android/quickjs/source_ng/quickjs-c-atomics.h b/NativeScript/napi/android/quickjs/source_ng/quickjs-c-atomics.h new file mode 100755 index 000000000..8fc6b7203 --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/quickjs-c-atomics.h @@ -0,0 +1,54 @@ +/* + * QuickJS C atomics definitions + * + * Copyright (c) 2023 Marcin Kolny + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if (defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) + // Use GCC builtins for version < 4.9 +# if((__GNUC__ << 16) + __GNUC_MINOR__ < ((4) << 16) + 9) +# define GCC_BUILTIN_ATOMICS +# endif +#endif + +#ifdef GCC_BUILTIN_ATOMICS +#define atomic_fetch_add(obj, arg) \ + __atomic_fetch_add(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_compare_exchange_strong(obj, expected, desired) \ + __atomic_compare_exchange_n(obj, expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#define atomic_exchange(obj, desired) \ + __atomic_exchange_n (obj, desired, __ATOMIC_SEQ_CST) +#define atomic_load(obj) \ + __atomic_load_n(obj, __ATOMIC_SEQ_CST) +#define atomic_store(obj, desired) \ + __atomic_store_n(obj, desired, __ATOMIC_SEQ_CST) +#define atomic_fetch_or(obj, arg) \ + __atomic_fetch_or(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_fetch_xor(obj, arg) \ + __atomic_fetch_xor(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_fetch_and(obj, arg) \ + __atomic_fetch_and(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_fetch_sub(obj, arg) \ + __atomic_fetch_sub(obj, arg, __ATOMIC_SEQ_CST) +#define _Atomic +#else +#include +#endif diff --git a/NativeScript/napi/android/quickjs/source_ng/quickjs-libc.c b/NativeScript/napi/android/quickjs/source_ng/quickjs-libc.c new file mode 100755 index 000000000..2c7ac7704 --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/quickjs-libc.c @@ -0,0 +1,4685 @@ +/* + * QuickJS C library + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "quickjs.h" +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#include +#endif +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#if defined(_WIN32) +#include +#include +#include +#include +#include +#include +#include +#define popen _popen +#define pclose _pclose +#define rmdir _rmdir +#define getcwd _getcwd +#define chdir _chdir +#else +#include +#include +#if !defined(__wasi__) +#include +#include +#include +#include +#include +#endif + +#if defined(__APPLE__) +typedef sig_t sighandler_t; +#include +#include +#define environ (*_NSGetEnviron()) +#endif + +#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) +typedef sig_t sighandler_t; +extern char **environ; +#endif + +#endif /* _WIN32 */ + +#include "cutils.h" +#include "list.h" +#include "quickjs-libc.h" + +#if JS_HAVE_THREADS +#include "quickjs-c-atomics.h" +#define USE_WORKER // enable os.Worker +#endif + +#ifndef S_IFBLK +#define S_IFBLK 0 +#endif + +#ifndef S_IFIFO +#define S_IFIFO 0 +#endif + +#ifndef MAX_SAFE_INTEGER // already defined in amalgamation builds +#define MAX_SAFE_INTEGER (((int64_t) 1 << 53) - 1) +#endif + +#ifndef QJS_NATIVE_MODULE_SUFFIX +#ifdef _WIN32 +#define QJS_NATIVE_MODULE_SUFFIX ".dll" +#else +#define QJS_NATIVE_MODULE_SUFFIX ".so" +#endif +#endif + +/* TODO: + - add socket calls +*/ + +typedef struct { + struct list_head link; + int fd; + JSValue rw_func[2]; +} JSOSRWHandler; + +typedef struct { + struct list_head link; + int sig_num; + JSValue func; +} JSOSSignalHandler; + +typedef struct { + struct list_head link; + int64_t timer_id; + uint8_t repeats:1; + int64_t timeout; + int64_t delay; + JSValue func; +} JSOSTimer; + +typedef struct { + struct list_head link; + JSValue promise; + JSValue reason; +} JSRejectedPromiseEntry; + +#ifdef USE_WORKER + +typedef struct { + struct list_head link; + uint8_t *data; + size_t data_len; + /* list of SharedArrayBuffers, necessary to free the message */ + uint8_t **sab_tab; + size_t sab_tab_len; +} JSWorkerMessage; + +typedef struct JSWaker { +#ifdef _WIN32 + HANDLE handle; +#else + int read_fd; + int write_fd; +#endif +} JSWaker; + +typedef struct { + int ref_count; + js_mutex_t mutex; + struct list_head msg_queue; /* list of JSWorkerMessage.link */ + JSWaker waker; +} JSWorkerMessagePipe; + +typedef struct { + struct list_head link; + JSWorkerMessagePipe *recv_pipe; + JSValue on_message_func; +} JSWorkerMessageHandler; + +#endif // USE_WORKER + +typedef struct JSThreadState { + struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */ + struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */ + struct list_head os_timers; /* list of JSOSTimer.link */ + struct list_head port_list; /* list of JSWorkerMessageHandler.link */ + struct list_head rejected_promise_list; /* list of JSRejectedPromiseEntry.link */ + int eval_script_recurse; /* only used in the main thread */ + int64_t next_timer_id; /* for setTimeout / setInterval */ + bool can_js_os_poll; + /* not used in the main thread */ +#ifdef USE_WORKER + JSWorkerMessagePipe *recv_pipe, *send_pipe; +#else + void *recv_pipe; +#endif // USE_WORKER + JSClassID std_file_class_id; + JSClassID worker_class_id; +} JSThreadState; + +static uint64_t os_pending_signals; + +static void *js_std_dbuf_realloc(void *opaque, void *ptr, size_t size) +{ + JSRuntime *rt = opaque; + return js_realloc_rt(rt, ptr, size); +} + +static void js_std_dbuf_init(JSContext *ctx, DynBuf *s) +{ + dbuf_init2(s, JS_GetRuntime(ctx), js_std_dbuf_realloc); +} + +static bool my_isdigit(int c) +{ + return (c >= '0' && c <= '9'); +} + +static JSThreadState *js_get_thread_state(JSRuntime *rt) +{ + return (JSThreadState *)js_std_cmd(/*GetOpaque*/0, rt); +} + +static void js_set_thread_state(JSRuntime *rt, JSThreadState *ts) +{ + js_std_cmd(/*SetOpaque*/1, rt, ts); +} + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif // __GNUC__ +static JSValue js_printf_internal(JSContext *ctx, + int argc, JSValueConst *argv, FILE *fp) +{ + char fmtbuf[32]; + uint8_t cbuf[UTF8_CHAR_LEN_MAX+1]; + JSValue res; + DynBuf dbuf; + const char *fmt_str = NULL; + const uint8_t *fmt, *fmt_end; + const uint8_t *p; + char *q; + int i, c, len, mod; + size_t fmt_len; + int32_t int32_arg; + int64_t int64_arg; + double double_arg; + const char *string_arg; + + js_std_dbuf_init(ctx, &dbuf); + + if (argc > 0) { + fmt_str = JS_ToCStringLen(ctx, &fmt_len, argv[0]); + if (!fmt_str) + goto fail; + + i = 1; + fmt = (const uint8_t *)fmt_str; + fmt_end = fmt + fmt_len; + while (fmt < fmt_end) { + for (p = fmt; fmt < fmt_end && *fmt != '%'; fmt++) + continue; + dbuf_put(&dbuf, p, fmt - p); + if (fmt >= fmt_end) + break; + q = fmtbuf; + *q++ = *fmt++; /* copy '%' */ + + /* flags */ + for(;;) { + c = *fmt; + if (c == '0' || c == '#' || c == '+' || c == '-' || c == ' ' || + c == '\'') { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = c; + fmt++; + } else { + break; + } + } + /* width */ + if (*fmt == '*') { + if (i >= argc) + goto missing; + if (JS_ToInt32(ctx, &int32_arg, argv[i++])) + goto fail; + q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg); + fmt++; + } else { + while (my_isdigit(*fmt)) { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = *fmt++; + } + } + if (*fmt == '.') { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = *fmt++; + if (*fmt == '*') { + if (i >= argc) + goto missing; + if (JS_ToInt32(ctx, &int32_arg, argv[i++])) + goto fail; + q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg); + fmt++; + } else { + while (my_isdigit(*fmt)) { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = *fmt++; + } + } + } + + /* we only support the "l" modifier for 64 bit numbers */ + mod = ' '; + if (*fmt == 'l') { + mod = *fmt++; + } + + /* type */ + c = *fmt++; + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = c; + *q = '\0'; + + switch (c) { + case 'c': + if (i >= argc) + goto missing; + if (JS_IsString(argv[i])) { + // TODO(chqrlie) need an API to wrap charCodeAt and codePointAt */ + string_arg = JS_ToCString(ctx, argv[i++]); + if (!string_arg) + goto fail; + int32_arg = utf8_decode((const uint8_t *)string_arg, &p); + JS_FreeCString(ctx, string_arg); + } else { + if (JS_ToInt32(ctx, &int32_arg, argv[i++])) + goto fail; + } + // XXX: throw an exception? + if ((unsigned)int32_arg > 0x10FFFF) + int32_arg = 0xFFFD; + /* ignore conversion flags, width and precision */ + len = utf8_encode(cbuf, int32_arg); + dbuf_put(&dbuf, cbuf, len); + break; + + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + if (i >= argc) + goto missing; + if (JS_ToInt64Ext(ctx, &int64_arg, argv[i++])) + goto fail; + if (mod == 'l') { + /* 64 bit number */ +#if defined(_WIN32) + if (q >= fmtbuf + sizeof(fmtbuf) - 3) + goto invalid; + q[2] = q[-1]; + q[-1] = 'I'; + q[0] = '6'; + q[1] = '4'; + q[3] = '\0'; + dbuf_printf(&dbuf, fmtbuf, (int64_t)int64_arg); +#else + if (q >= fmtbuf + sizeof(fmtbuf) - 2) + goto invalid; + q[1] = q[-1]; + q[-1] = q[0] = 'l'; + q[2] = '\0'; + dbuf_printf(&dbuf, fmtbuf, (long long)int64_arg); +#endif + } else { + dbuf_printf(&dbuf, fmtbuf, (int)int64_arg); + } + break; + + case 's': + if (i >= argc) + goto missing; + /* XXX: handle strings containing null characters */ + string_arg = JS_ToCString(ctx, argv[i++]); + if (!string_arg) + goto fail; + dbuf_printf(&dbuf, fmtbuf, string_arg); + JS_FreeCString(ctx, string_arg); + break; + + case 'e': + case 'f': + case 'g': + case 'a': + case 'E': + case 'F': + case 'G': + case 'A': + if (i >= argc) + goto missing; + if (JS_ToFloat64(ctx, &double_arg, argv[i++])) + goto fail; + dbuf_printf(&dbuf, fmtbuf, double_arg); + break; + + case '%': + dbuf_putc(&dbuf, '%'); + break; + + default: + /* XXX: should support an extension mechanism */ + invalid: + JS_ThrowTypeError(ctx, "invalid conversion specifier in format string"); + goto fail; + missing: + JS_ThrowReferenceError(ctx, "missing argument for conversion specifier"); + goto fail; + } + } + JS_FreeCString(ctx, fmt_str); + } + if (dbuf.error) { + res = JS_ThrowOutOfMemory(ctx); + } else { + if (fp) { + len = fwrite(dbuf.buf, 1, dbuf.size, fp); + res = JS_NewInt32(ctx, len); + } else { + res = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size); + } + } + dbuf_free(&dbuf); + return res; + +fail: + JS_FreeCString(ctx, fmt_str); + dbuf_free(&dbuf); + return JS_EXCEPTION; +} +#ifdef __GNUC__ +#pragma GCC diagnostic pop // ignored "-Wformat-nonliteral" +#endif // __GNUC__ + +uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename) +{ + FILE *f; + size_t n, len; + uint8_t *p, *buf, tmp[8192]; + + f = fopen(filename, "rb"); + if (!f) + return NULL; + buf = NULL; + len = 0; + do { + n = fread(tmp, 1, sizeof(tmp), f); + if (ctx) { + p = js_realloc(ctx, buf, len + n + 1); + } else { + p = realloc(buf, len + n + 1); + } + if (!p) { + if (ctx) { + js_free(ctx, buf); + } else { + free(buf); + } + fclose(f); + return NULL; + } + memcpy(&p[len], tmp, n); + buf = p; + len += n; + buf[len] = '\0'; + } while (n == sizeof(tmp)); + fclose(f); + *pbuf_len = len; + return buf; +} + +/* load and evaluate a file */ +static JSValue js_loadScript(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + uint8_t *buf; + const char *filename; + JSValue ret; + size_t buf_len; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + buf = js_load_file(ctx, &buf_len, filename); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load '%s'", filename); + JS_FreeCString(ctx, filename); + return JS_EXCEPTION; + } + ret = JS_Eval(ctx, (char *)buf, buf_len, filename, + JS_EVAL_TYPE_GLOBAL); + js_free(ctx, buf); + JS_FreeCString(ctx, filename); + return ret; +} + +static int get_bool_option(JSContext *ctx, bool *pbool, + JSValueConst obj, + const char *option) +{ + JSValue val; + val = JS_GetPropertyStr(ctx, obj, option); + if (JS_IsException(val)) + return -1; + if (!JS_IsUndefined(val)) { + *pbool = JS_ToBool(ctx, val); + } + JS_FreeValue(ctx, val); + return 0; +} + +static void free_buf(JSRuntime *rt, void *opaque, void *ptr) { + js_free_rt(rt, ptr); +} + +/* load a file as a UTF-8 encoded string or Uint8Array */ +static JSValue js_std_loadFile(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + uint8_t *buf; + const char *filename; + JSValueConst options_obj; + JSValue ret; + size_t buf_len; + bool binary = false; + + if (argc >= 2) { + options_obj = argv[1]; + if (get_bool_option(ctx, &binary, options_obj, + "binary")) + return JS_EXCEPTION; + } + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + buf = js_load_file(ctx, &buf_len, filename); + JS_FreeCString(ctx, filename); + if (!buf) + return JS_NULL; + if (binary) { + ret = JS_NewUint8Array(ctx, buf, buf_len, free_buf, NULL, false); + } else { + ret = JS_NewStringLen(ctx, (char *)buf, buf_len); + js_free(ctx, buf); + } + + return ret; +} + +static JSValue js_std_writeFile(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename; + const char *mode; + const void *buf; + size_t len, n; + JSValueConst data; + JSValue val, ret, unref; + bool release; + FILE *fp; + + ret = JS_EXCEPTION; + len = 0; + buf = ""; + mode = "w"; + data = argv[1]; + unref = JS_UNDEFINED; + release = false; + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + if (JS_IsObject(data)) { + val = JS_GetPropertyStr(ctx, data, "buffer"); + if (JS_IsException(val)) + goto exception; + if (JS_IsArrayBuffer(val)) { + data = unref = val; + } else { + JS_FreeValue(ctx, val); + } + } + if (JS_IsArrayBuffer(data)) { + buf = JS_GetArrayBuffer(ctx, &len, data); + mode = "wb"; + } else if (!JS_IsUndefined(data)) { + buf = JS_ToCStringLen(ctx, &len, data); + release = true; + } + if (!buf) + goto exception; + fp = fopen(filename, mode); + if (!fp) { + JS_ThrowPlainError(ctx, "error opening %s for writing", filename); + goto exception; + } + n = fwrite(buf, len, 1, fp); + fclose(fp); + if (n != 1) { + JS_ThrowPlainError(ctx, "error writing to %s", filename); + goto exception; + } + ret = JS_UNDEFINED; +exception: + JS_FreeCString(ctx, filename); + if (release) + JS_FreeCString(ctx, buf); + JS_FreeValue(ctx, unref); + return ret; +} + +typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx, + const char *module_name); + + +#if defined(_WIN32) +static JSModuleDef *js_module_loader_so(JSContext *ctx, + const char *module_name) +{ + JSModuleDef *m; + HINSTANCE hd; + JSInitModuleFunc *init; + char *filename = NULL; + size_t len = strlen(module_name); + bool is_absolute = len > 2 && ((module_name[0] >= 'A' && module_name[0] <= 'Z') || + (module_name[0] >= 'a' && module_name[0] <= 'z')) && module_name[1] == ':'; + bool is_relative = len > 2 && module_name[0] == '.' && (module_name[1] == '/' || module_name[1] == '\\'); + if (is_absolute || is_relative) { + filename = (char *)module_name; + } else { + filename = js_malloc(ctx, len + 2 + 1); + if (!filename) + return NULL; + strcpy(filename, "./"); + strcpy(filename + 2, module_name); + } + hd = LoadLibraryA(filename); + if (filename != module_name) + js_free(ctx, filename); + if (hd == NULL) { + JS_ThrowReferenceError(ctx, "js_load_module '%s' error: %lu", + module_name, GetLastError()); + goto fail; + } + init = (JSInitModuleFunc *)(uintptr_t)GetProcAddress(hd, "js_init_module"); + if (!init) { + JS_ThrowReferenceError(ctx, "js_init_module '%s' not found: %lu", + module_name, GetLastError()); + goto fail; + } + m = init(ctx, module_name); + if (!m) { + JS_ThrowReferenceError(ctx, "js_call_module '%s' initialization error", + module_name); + fail: + if (hd != NULL) + FreeLibrary(hd); + return NULL; + } + return m; +} +#elif defined(__wasi__) +static JSModuleDef *js_module_loader_so(JSContext *ctx, + const char *module_name) +{ + JS_ThrowReferenceError(ctx, "shared library modules are not supported yet"); + return NULL; +} +#else +static JSModuleDef *js_module_loader_so(JSContext *ctx, + const char *module_name) +{ + JSModuleDef *m; + void *hd; + JSInitModuleFunc *init; + char *filename; + + if (!strchr(module_name, '/')) { + /* must add a '/' so that the DLL is not searched in the + system library paths */ + filename = js_malloc(ctx, strlen(module_name) + 2 + 1); + if (!filename) + return NULL; + strcpy(filename, "./"); + strcpy(filename + 2, module_name); + } else { + filename = (char *)module_name; + } + + /* C module */ + hd = dlopen(filename, RTLD_NOW | RTLD_LOCAL); + if (filename != module_name) + js_free(ctx, filename); + if (!hd) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library: %s", + module_name, dlerror()); + goto fail; + } + + *(void **) (&init) = dlsym(hd, "js_init_module"); + if (!init) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found", + module_name); + goto fail; + } + + m = init(ctx, module_name); + if (!m) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error", + module_name); + fail: + if (hd) + dlclose(hd); + return NULL; + } + return m; +} +#endif /* !_WIN32 */ + +int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, + bool use_realpath, bool is_main) +{ + JSModuleDef *m; + char buf[JS__PATH_MAX + 16]; + JSValue meta_obj; + JSAtom module_name_atom; + const char *module_name; + + assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE); + m = JS_VALUE_GET_PTR(func_val); + + module_name_atom = JS_GetModuleName(ctx, m); + module_name = JS_AtomToCString(ctx, module_name_atom); + JS_FreeAtom(ctx, module_name_atom); + if (!module_name) + return -1; + if (!strchr(module_name, ':')) { + strcpy(buf, "file://"); +#if !defined(_WIN32) && !defined(__wasi__) + /* realpath() cannot be used with modules compiled with qjsc + because the corresponding module source code is not + necessarily present */ + if (use_realpath) { + char *res = realpath(module_name, buf + strlen(buf)); + if (!res) { + JS_ThrowTypeError(ctx, "realpath failure"); + JS_FreeCString(ctx, module_name); + return -1; + } + } else +#endif + { + js__pstrcat(buf, sizeof(buf), module_name); + } + } else { + js__pstrcpy(buf, sizeof(buf), module_name); + } + JS_FreeCString(ctx, module_name); + + meta_obj = JS_GetImportMeta(ctx, m); + if (JS_IsException(meta_obj)) + return -1; + JS_DefinePropertyValueStr(ctx, meta_obj, "url", + JS_NewString(ctx, buf), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, meta_obj, "main", + JS_NewBool(ctx, is_main), + JS_PROP_C_W_E); + JS_FreeValue(ctx, meta_obj); + return 0; +} + +JSModuleDef *js_module_loader(JSContext *ctx, + const char *module_name, void *opaque) +{ + JSModuleDef *m; + + if (js__has_suffix(module_name, QJS_NATIVE_MODULE_SUFFIX)) { + m = js_module_loader_so(ctx, module_name); + } else { + size_t buf_len; + uint8_t *buf; + JSValue func_val; + + buf = js_load_file(ctx, &buf_len, module_name); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s'", + module_name); + return NULL; + } + + /* compile the module */ + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + if (JS_IsException(func_val)) + return NULL; + if (js_module_set_import_meta(ctx, func_val, true, false) < 0) { + JS_FreeValue(ctx, func_val); + return NULL; + } + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + } + return m; +} + +static JSValue js_std_exit(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int status; + if (JS_ToInt32(ctx, &status, argv[0])) + status = -1; + exit(status); + return JS_UNDEFINED; +} + +static JSValue js_std_getenv(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *name, *str; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + str = getenv(name); + JS_FreeCString(ctx, name); + if (!str) + return JS_UNDEFINED; + else + return JS_NewString(ctx, str); +} + +#if defined(_WIN32) +static void setenv(const char *name, const char *value, int overwrite) +{ + char *str; + size_t name_len, value_len; + name_len = strlen(name); + value_len = strlen(value); + str = malloc(name_len + 1 + value_len + 1); + memcpy(str, name, name_len); + str[name_len] = '='; + memcpy(str + name_len + 1, value, value_len); + str[name_len + 1 + value_len] = '\0'; + _putenv(str); + free(str); +} + +static void unsetenv(const char *name) +{ + setenv(name, "", true); +} +#endif /* _WIN32 */ + +static JSValue js_std_setenv(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *name, *value; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + value = JS_ToCString(ctx, argv[1]); + if (!value) { + JS_FreeCString(ctx, name); + return JS_EXCEPTION; + } + setenv(name, value, true); + JS_FreeCString(ctx, name); + JS_FreeCString(ctx, value); + return JS_UNDEFINED; +} + +static JSValue js_std_unsetenv(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *name; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + unsetenv(name); + JS_FreeCString(ctx, name); + return JS_UNDEFINED; +} + +/* return an object containing the list of the available environment + variables. */ +static JSValue js_std_getenviron(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + char **envp; + const char *name, *p, *value; + JSValue obj; + uint32_t idx; + size_t name_len; + JSAtom atom; + int ret; + + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + envp = environ; + for(idx = 0; envp[idx] != NULL; idx++) { + name = envp[idx]; + p = strchr(name, '='); + name_len = p - name; + if (!p) + continue; + value = p + 1; + atom = JS_NewAtomLen(ctx, name, name_len); + if (atom == JS_ATOM_NULL) + goto fail; + ret = JS_DefinePropertyValue(ctx, obj, atom, JS_NewString(ctx, value), + JS_PROP_C_W_E); + JS_FreeAtom(ctx, atom); + if (ret < 0) + goto fail; + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_std_gc(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JS_RunGC(JS_GetRuntime(ctx)); + return JS_UNDEFINED; +} + +static int interrupt_handler(JSRuntime *rt, void *opaque) +{ + return (os_pending_signals >> SIGINT) & 1; +} + +static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + const char *str = NULL; + size_t len; + JSValue ret, obj; + JSValueConst options_obj, arg; + bool backtrace_barrier = false; + bool eval_function = false; + bool eval_module = false; + bool compile_only = false; + bool compile_module = false; + bool is_async = false; + int flags; + + if (argc >= 2) { + options_obj = argv[1]; + if (get_bool_option(ctx, &backtrace_barrier, options_obj, + "backtrace_barrier")) + return JS_EXCEPTION; + if (get_bool_option(ctx, &eval_function, options_obj, + "eval_function")) + return JS_EXCEPTION; + if (get_bool_option(ctx, &eval_module, options_obj, + "eval_module")) + return JS_EXCEPTION; + if (get_bool_option(ctx, &compile_only, options_obj, + "compile_only")) + return JS_EXCEPTION; + if (get_bool_option(ctx, &compile_module, options_obj, + "compile_module")) + return JS_EXCEPTION; + if (get_bool_option(ctx, &is_async, options_obj, + "async")) + return JS_EXCEPTION; + } + + if (eval_module) { + arg = argv[0]; + if (JS_VALUE_GET_TAG(arg) != JS_TAG_MODULE) + return JS_ThrowTypeError(ctx, "not a module"); + + if (JS_ResolveModule(ctx, arg) < 0) + return JS_EXCEPTION; + + if (js_module_set_import_meta(ctx, arg, false, false) < 0) + return JS_EXCEPTION; + + return JS_EvalFunction(ctx, JS_DupValue(ctx, arg)); + } + + if (!eval_function) { + str = JS_ToCStringLen(ctx, &len, argv[0]); + if (!str) + return JS_EXCEPTION; + } + if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) { + /* install the interrupt handler */ + JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); + } + flags = compile_module ? JS_EVAL_TYPE_MODULE : JS_EVAL_TYPE_GLOBAL; + if (backtrace_barrier) + flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER; + if (compile_only) + flags |= JS_EVAL_FLAG_COMPILE_ONLY; + if (is_async) + flags |= JS_EVAL_FLAG_ASYNC; + if (eval_function) { + obj = JS_DupValue(ctx, argv[0]); + ret = JS_EvalFunction(ctx, obj); // takes ownership of |obj| + } else { + ret = JS_Eval(ctx, str, len, "", flags); + } + JS_FreeCString(ctx, str); + if (!ts->recv_pipe && --ts->eval_script_recurse == 0) { + /* remove the interrupt handler */ + JS_SetInterruptHandler(JS_GetRuntime(ctx), NULL, NULL); + os_pending_signals &= ~((uint64_t)1 << SIGINT); + /* convert the uncatchable "interrupted" error into a normal error + so that it can be caught by the REPL */ + if (JS_IsException(ret)) + JS_ResetUncatchableError(ctx); + } + return ret; +} + +typedef struct { + FILE *f; + bool is_popen; +} JSSTDFile; + +static bool is_stdio(FILE *f) +{ + return f == stdin || f == stdout || f == stderr; +} + +static void js_std_file_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSThreadState *ts = js_get_thread_state(rt); + JSSTDFile *s = JS_GetOpaque(val, ts->std_file_class_id); + if (s) { + if (s->f && !is_stdio(s->f)) { +#if !defined(__wasi__) + if (s->is_popen) + pclose(s->f); + else +#endif + fclose(s->f); + } + js_free_rt(rt, s); + } +} + +static ssize_t js_get_errno(ssize_t ret) +{ + if (ret == -1) + ret = -errno; + return ret; +} + +static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int err; + if (JS_ToInt32(ctx, &err, argv[0])) + return JS_EXCEPTION; + return JS_NewString(ctx, strerror(err)); +} + +static JSValue js_new_std_file(JSContext *ctx, FILE *f, bool is_popen) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSSTDFile *s; + JSValue obj; + obj = JS_NewObjectClass(ctx, ts->std_file_class_id); + if (JS_IsException(obj)) + return obj; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + s->is_popen = is_popen; + s->f = f; + JS_SetOpaque(obj, s); + return obj; +} + +static void js_set_error_object(JSContext *ctx, JSValueConst obj, int err) +{ + if (!JS_IsUndefined(obj)) { + JS_SetPropertyStr(ctx, obj, "errno", JS_NewInt32(ctx, err)); + } +} + +static JSValue js_std_open(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename, *mode = NULL; + FILE *f; + int err; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rwa+bx")] != '\0') { + JS_ThrowTypeError(ctx, "invalid file mode"); + goto fail; + } + + f = fopen(filename, mode); + if (!f) + err = errno; + else + err = 0; + if (argc >= 3) + js_set_error_object(ctx, argv[2], err); + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + if (!f) + return JS_NULL; + return js_new_std_file(ctx, f, false); + fail: + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} + +#if !defined(__wasi__) +static JSValue js_std_popen(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename, *mode = NULL; + FILE *f; + int err; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (strcmp(mode, "r") && strcmp(mode, "w")) { + JS_ThrowTypeError(ctx, "invalid file mode"); + goto fail; + } + + f = popen(filename, mode); + if (!f) + err = errno; + else + err = 0; + if (argc >= 3) + js_set_error_object(ctx, argv[2], err); + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + if (!f) + return JS_NULL; + return js_new_std_file(ctx, f, true); + fail: + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} +#endif // !defined(__wasi__) + +static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *mode; + FILE *f; + int fd, err; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rwa+")] != '\0') { + JS_ThrowTypeError(ctx, "invalid file mode"); + goto fail; + } + + f = fdopen(fd, mode); + if (!f) + err = errno; + else + err = 0; + if (argc >= 3) + js_set_error_object(ctx, argv[2], err); + JS_FreeCString(ctx, mode); + if (!f) + return JS_NULL; + return js_new_std_file(ctx, f, false); + fail: + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} + +#if !defined(__wasi__) +static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f; + f = tmpfile(); + if (argc >= 1) + js_set_error_object(ctx, argv[0], f ? 0 : errno); + if (!f) + return JS_NULL; + return js_new_std_file(ctx, f, false); +} +#endif + +static JSValue js_std_sprintf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_printf_internal(ctx, argc, argv, NULL); +} + +static JSValue js_std_printf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_printf_internal(ctx, argc, argv, stdout); +} + +static FILE *js_std_file_get(JSContext *ctx, JSValueConst obj) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSSTDFile *s = JS_GetOpaque2(ctx, obj, ts->std_file_class_id); + if (!s) + return NULL; + if (!s->f) { + JS_ThrowTypeError(ctx, "invalid file handle"); + return NULL; + } + return s->f; +} + +static JSValue js_std_file_puts(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + FILE *f; + int i; + const char *str; + size_t len; + + if (magic == 0) { + f = stdout; + } else { + f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + } + + for(i = 0; i < argc; i++) { + str = JS_ToCStringLen(ctx, &len, argv[i]); + if (!str) + return JS_EXCEPTION; + fwrite(str, 1, len, f); + JS_FreeCString(ctx, str); + } + return JS_UNDEFINED; +} + +static JSValue js_std_file_close(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSSTDFile *s = JS_GetOpaque2(ctx, this_val, ts->std_file_class_id); + int err; + if (!s) + return JS_EXCEPTION; + if (!s->f) + return JS_ThrowTypeError(ctx, "invalid file handle"); + if (is_stdio(s->f)) + return JS_ThrowTypeError(ctx, "cannot close stdio"); +#if !defined(__wasi__) + if (s->is_popen) + err = js_get_errno(pclose(s->f)); + else +#endif + err = js_get_errno(fclose(s->f)); + s->f = NULL; + return JS_NewInt32(ctx, err); +} + +static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return js_printf_internal(ctx, argc, argv, f); +} + +static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + fflush(f); + return JS_UNDEFINED; +} + +static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int is_bigint) +{ + FILE *f = js_std_file_get(ctx, this_val); + int64_t pos; + if (!f) + return JS_EXCEPTION; +#if defined(__linux__) || defined(__GLIBC__) + pos = ftello(f); +#else + pos = ftell(f); +#endif + if (is_bigint) + return JS_NewBigInt64(ctx, pos); + else + return JS_NewInt64(ctx, pos); +} + +static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int64_t pos; + int whence, ret; + if (!f) + return JS_EXCEPTION; + if (JS_ToInt64Ext(ctx, &pos, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &whence, argv[1])) + return JS_EXCEPTION; +#if defined(__linux__) || defined(__GLIBC__) + ret = fseeko(f, pos, whence); +#else + ret = fseek(f, pos, whence); +#endif + if (ret < 0) + ret = -errno; + return JS_NewInt32(ctx, ret); +} + +static JSValue js_std_file_eof(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewBool(ctx, feof(f)); +} + +static JSValue js_std_file_error(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewBool(ctx, ferror(f)); +} + +static JSValue js_std_file_clearerr(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + clearerr(f); + return JS_UNDEFINED; +} + +static JSValue js_std_file_fileno(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewInt32(ctx, fileno(f)); +} + +static JSValue js_std_file_read_write(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + FILE *f = js_std_file_get(ctx, this_val); + uint64_t pos, len; + size_t size, ret; + uint8_t *buf; + + if (!f) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &pos, argv[1])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &len, argv[2])) + return JS_EXCEPTION; + buf = JS_GetArrayBuffer(ctx, &size, argv[0]); + if (!buf) + return JS_EXCEPTION; + if (pos + len > size) + return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); + if (magic) + ret = fwrite(buf + pos, 1, len, f); + else + ret = fread(buf + pos, 1, len, f); + return JS_NewInt64(ctx, ret); +} + +/* XXX: could use less memory and go faster */ +static JSValue js_std_file_getline(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + DynBuf dbuf; + JSValue obj; + + if (!f) + return JS_EXCEPTION; + + js_std_dbuf_init(ctx, &dbuf); + for(;;) { + c = fgetc(f); + if (c == EOF) { + if (dbuf.size == 0) { + /* EOF */ + dbuf_free(&dbuf); + return JS_NULL; + } else { + break; + } + } + if (c == '\n') + break; + if (dbuf_putc(&dbuf, c)) { + dbuf_free(&dbuf); + return JS_ThrowOutOfMemory(ctx); + } + } + obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); + dbuf_free(&dbuf); + return obj; +} + +/* XXX: could use less memory and go faster */ +static JSValue js_std_file_readAs(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + DynBuf dbuf; + JSValue obj; + uint64_t max_size64; + size_t max_size; + JSValueConst max_size_val; + + if (!f) + return JS_EXCEPTION; + + if (argc >= 1) + max_size_val = argv[0]; + else + max_size_val = JS_UNDEFINED; + max_size = (size_t)-1; + if (!JS_IsUndefined(max_size_val)) { + if (JS_ToIndex(ctx, &max_size64, max_size_val)) + return JS_EXCEPTION; + if (max_size64 < max_size) + max_size = max_size64; + } + + js_std_dbuf_init(ctx, &dbuf); + while (max_size != 0) { + c = fgetc(f); + if (c == EOF) + break; + if (dbuf_putc(&dbuf, c)) { + dbuf_free(&dbuf); + return JS_EXCEPTION; + } + max_size--; + } + if (magic) { + obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); + } else { + obj = JS_NewArrayBufferCopy(ctx, dbuf.buf, dbuf.size); + } + dbuf_free(&dbuf); + return obj; +} + +static JSValue js_std_file_getByte(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewInt32(ctx, fgetc(f)); +} + +static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + if (!f) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &c, argv[0])) + return JS_EXCEPTION; + c = fputc(c, f); + return JS_NewInt32(ctx, c); +} + +/* urlGet */ +#if !defined(__wasi__) + +#define URL_GET_PROGRAM "curl -s -i --" +#define URL_GET_BUF_SIZE 4096 + +static int http_get_header_line(FILE *f, char *buf, size_t buf_size, + DynBuf *dbuf) +{ + int c; + char *p; + + p = buf; + for(;;) { + c = fgetc(f); + if (c < 0) + return -1; + if ((p - buf) < buf_size - 1) + *p++ = c; + if (dbuf) + dbuf_putc(dbuf, c); + if (c == '\n') + break; + } + *p = '\0'; + return 0; +} + +static int http_get_status(const char *buf) +{ + const char *p = buf; + while (*p != ' ' && *p != '\0') + p++; + if (*p != ' ') + return 0; + while (*p == ' ') + p++; + return atoi(p); +} + +static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *url; + DynBuf cmd_buf; + DynBuf data_buf_s, *data_buf = &data_buf_s; + DynBuf header_buf_s, *header_buf = &header_buf_s; + char *buf; + size_t i, len; + int status; + JSValue response = JS_UNDEFINED, ret_obj; + JSValueConst options_obj; + FILE *f; + bool binary_flag, full_flag; + + url = JS_ToCString(ctx, argv[0]); + if (!url) + return JS_EXCEPTION; + + binary_flag = false; + full_flag = false; + + if (argc >= 2) { + options_obj = argv[1]; + + if (get_bool_option(ctx, &binary_flag, options_obj, "binary")) + goto fail_obj; + + if (get_bool_option(ctx, &full_flag, options_obj, "full")) { + fail_obj: + JS_FreeCString(ctx, url); + return JS_EXCEPTION; + } + } + + js_std_dbuf_init(ctx, &cmd_buf); + dbuf_printf(&cmd_buf, "%s '", URL_GET_PROGRAM); + for(i = 0; url[i] != '\0'; i++) { + unsigned char c = url[i]; + switch (c) { + case '\'': + /* shell single quoted string does not support \' */ + dbuf_putstr(&cmd_buf, "'\\''"); + break; + case '[': case ']': case '{': case '}': case '\\': + /* prevent interpretation by curl as range or set specification */ + dbuf_putc(&cmd_buf, '\\'); + /* FALLTHROUGH */ + default: + dbuf_putc(&cmd_buf, c); + break; + } + } + JS_FreeCString(ctx, url); + dbuf_putstr(&cmd_buf, "'"); + dbuf_putc(&cmd_buf, '\0'); + if (dbuf_error(&cmd_buf)) { + dbuf_free(&cmd_buf); + return JS_EXCEPTION; + } + // printf("%s\n", (char *)cmd_buf.buf); + f = popen((char *)cmd_buf.buf, "r"); + dbuf_free(&cmd_buf); + if (!f) { + return JS_ThrowTypeError(ctx, "could not start curl"); + } + + js_std_dbuf_init(ctx, data_buf); + js_std_dbuf_init(ctx, header_buf); + + buf = js_malloc(ctx, URL_GET_BUF_SIZE); + if (!buf) + goto fail; + + /* get the HTTP status */ + if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, NULL) < 0) { + status = 0; + goto bad_header; + } + status = http_get_status(buf); + if (!full_flag && !(status >= 200 && status <= 299)) { + goto bad_header; + } + + /* wait until there is an empty line */ + for(;;) { + if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, header_buf) < 0) { + bad_header: + response = JS_NULL; + goto done; + } + if (!strcmp(buf, "\r\n")) + break; + } + if (dbuf_error(header_buf)) + goto fail; + header_buf->size -= 2; /* remove the trailing CRLF */ + + /* download the data */ + for(;;) { + len = fread(buf, 1, URL_GET_BUF_SIZE, f); + if (len == 0) + break; + dbuf_put(data_buf, (uint8_t *)buf, len); + } + if (dbuf_error(data_buf)) + goto fail; + if (binary_flag) { + response = JS_NewArrayBufferCopy(ctx, + data_buf->buf, data_buf->size); + } else { + response = JS_NewStringLen(ctx, (char *)data_buf->buf, data_buf->size); + } + if (JS_IsException(response)) + goto fail; + done: + js_free(ctx, buf); + buf = NULL; + pclose(f); + f = NULL; + dbuf_free(data_buf); + data_buf = NULL; + + if (full_flag) { + ret_obj = JS_NewObject(ctx); + if (JS_IsException(ret_obj)) + goto fail; + JS_DefinePropertyValueStr(ctx, ret_obj, "response", + response, + JS_PROP_C_W_E); + if (!JS_IsNull(response)) { + JS_DefinePropertyValueStr(ctx, ret_obj, "responseHeaders", + JS_NewStringLen(ctx, (char *)header_buf->buf, + header_buf->size), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, ret_obj, "status", + JS_NewInt32(ctx, status), + JS_PROP_C_W_E); + } + } else { + ret_obj = response; + } + dbuf_free(header_buf); + return ret_obj; + fail: + if (f) + pclose(f); + js_free(ctx, buf); + if (data_buf) + dbuf_free(data_buf); + if (header_buf) + dbuf_free(header_buf); + JS_FreeValue(ctx, response); + return JS_EXCEPTION; +} +#endif // !defined(__wasi__) + +static JSClassDef js_std_file_class = { + "FILE", + .finalizer = js_std_file_finalizer, +}; + +static const JSCFunctionListEntry js_std_error_props[] = { + /* various errno values */ +#define DEF(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) + DEF(EINVAL), + DEF(EIO), + DEF(EACCES), + DEF(EEXIST), + DEF(ENOSPC), + DEF(ENOSYS), + DEF(EBUSY), + DEF(ENOENT), + DEF(EPERM), + DEF(EPIPE), + DEF(EBADF), +#undef DEF +}; + +static const JSCFunctionListEntry js_std_funcs[] = { + JS_CFUNC_DEF("exit", 1, js_std_exit ), + JS_CFUNC_DEF("gc", 0, js_std_gc ), + JS_CFUNC_DEF("evalScript", 1, js_evalScript ), + JS_CFUNC_DEF("loadScript", 1, js_loadScript ), + JS_CFUNC_DEF("getenv", 1, js_std_getenv ), + JS_CFUNC_DEF("setenv", 1, js_std_setenv ), + JS_CFUNC_DEF("unsetenv", 1, js_std_unsetenv ), + JS_CFUNC_DEF("getenviron", 1, js_std_getenviron ), +#if !defined(__wasi__) + JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ), +#endif + JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ), + JS_CFUNC_DEF("writeFile", 2, js_std_writeFile ), + JS_CFUNC_DEF("strerror", 1, js_std_strerror ), + + /* FILE I/O */ + JS_CFUNC_DEF("open", 2, js_std_open ), +#if !defined(__wasi__) + JS_CFUNC_DEF("popen", 2, js_std_popen ), + JS_CFUNC_DEF("tmpfile", 0, js_std_tmpfile ), +#endif + JS_CFUNC_DEF("fdopen", 2, js_std_fdopen ), + JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 0 ), + JS_CFUNC_DEF("printf", 1, js_std_printf ), + JS_CFUNC_DEF("sprintf", 1, js_std_sprintf ), + JS_PROP_INT32_DEF("SEEK_SET", SEEK_SET, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ), + JS_OBJECT_DEF("Error", js_std_error_props, countof(js_std_error_props), JS_PROP_CONFIGURABLE), +}; + +static const JSCFunctionListEntry js_std_file_proto_funcs[] = { + JS_CFUNC_DEF("close", 0, js_std_file_close ), + JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 1 ), + JS_CFUNC_DEF("printf", 1, js_std_file_printf ), + JS_CFUNC_DEF("flush", 0, js_std_file_flush ), + JS_CFUNC_MAGIC_DEF("tell", 0, js_std_file_tell, 0 ), + JS_CFUNC_MAGIC_DEF("tello", 0, js_std_file_tell, 1 ), + JS_CFUNC_DEF("seek", 2, js_std_file_seek ), + JS_CFUNC_DEF("eof", 0, js_std_file_eof ), + JS_CFUNC_DEF("fileno", 0, js_std_file_fileno ), + JS_CFUNC_DEF("error", 0, js_std_file_error ), + JS_CFUNC_DEF("clearerr", 0, js_std_file_clearerr ), + JS_CFUNC_MAGIC_DEF("read", 3, js_std_file_read_write, 0 ), + JS_CFUNC_MAGIC_DEF("write", 3, js_std_file_read_write, 1 ), + JS_CFUNC_DEF("getline", 0, js_std_file_getline ), + JS_CFUNC_MAGIC_DEF("readAsArrayBuffer", 0, js_std_file_readAs, 0 ), + JS_CFUNC_MAGIC_DEF("readAsString", 0, js_std_file_readAs, 1 ), + JS_CFUNC_DEF("getByte", 0, js_std_file_getByte ), + JS_CFUNC_DEF("putByte", 1, js_std_file_putByte ), + /* setvbuf, ... */ +}; + +static int js_std_init(JSContext *ctx, JSModuleDef *m) +{ + JSValue proto; + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + + /* FILE class */ + /* the class ID is created once */ + JS_NewClassID(rt, &ts->std_file_class_id); + /* the class is created once per runtime */ + JS_NewClass(rt, ts->std_file_class_id, &js_std_file_class); + proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_std_file_proto_funcs, + countof(js_std_file_proto_funcs)); + JS_SetClassProto(ctx, ts->std_file_class_id, proto); + + JS_SetModuleExportList(ctx, m, js_std_funcs, + countof(js_std_funcs)); + JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, false)); + JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, false)); + JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, false)); + return 0; +} + +JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_std_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_std_funcs, countof(js_std_funcs)); + JS_AddModuleExport(ctx, m, "in"); + JS_AddModuleExport(ctx, m, "out"); + JS_AddModuleExport(ctx, m, "err"); + return m; +} + +/**********************************************************/ +/* 'os' object */ + +static JSValue js_os_open(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename; + int flags, mode, ret; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &flags, argv[1])) + goto fail; + if (argc >= 3 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt32(ctx, &mode, argv[2])) { + fail: + JS_FreeCString(ctx, filename); + return JS_EXCEPTION; + } + } else { + mode = 0666; + } +#if defined(_WIN32) + /* force binary mode by default */ + if (!(flags & O_TEXT)) + flags |= O_BINARY; +#endif + ret = js_get_errno(open(filename, flags, mode)); + JS_FreeCString(ctx, filename); + return JS_NewInt32(ctx, ret); +} + +static JSValue js_os_close(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd, ret; + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + ret = js_get_errno(close(fd)); + return JS_NewInt32(ctx, ret); +} + +static JSValue js_os_seek(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd, whence; + int64_t pos, ret; + bool is_bigint; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + is_bigint = JS_IsBigInt(argv[1]); + if (JS_ToInt64Ext(ctx, &pos, argv[1])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &whence, argv[2])) + return JS_EXCEPTION; + ret = lseek(fd, pos, whence); + if (ret == -1) + ret = -errno; + if (is_bigint) + return JS_NewBigInt64(ctx, ret); + else + return JS_NewInt64(ctx, ret); +} + +static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + int fd; + uint64_t pos, len; + size_t size; + ssize_t ret; + uint8_t *buf; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &pos, argv[2])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &len, argv[3])) + return JS_EXCEPTION; + buf = JS_GetArrayBuffer(ctx, &size, argv[1]); + if (!buf) + return JS_EXCEPTION; + if (pos + len > size) + return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); + if (magic) + ret = js_get_errno(write(fd, buf + pos, len)); + else + ret = js_get_errno(read(fd, buf + pos, len)); + return JS_NewInt64(ctx, ret); +} + +static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd; + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + return JS_NewBool(ctx, (isatty(fd) != 0)); +} + +#if defined(_WIN32) +static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd; + HANDLE handle; + CONSOLE_SCREEN_BUFFER_INFO info; + JSValue obj; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + handle = (HANDLE)_get_osfhandle(fd); + + if (!GetConsoleScreenBufferInfo(handle, &info)) + return JS_NULL; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, info.dwSize.X), JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, info.dwSize.Y), JS_PROP_C_W_E); + return obj; +} + +/* Windows 10 built-in VT100 emulation */ +#define __ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#define __ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 + +static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd; + HANDLE handle; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + handle = (HANDLE)_get_osfhandle(fd); + SetConsoleMode(handle, ENABLE_WINDOW_INPUT | __ENABLE_VIRTUAL_TERMINAL_INPUT); + _setmode(fd, _O_BINARY); + if (fd == 0) { + handle = (HANDLE)_get_osfhandle(1); /* corresponding output */ + SetConsoleMode(handle, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | __ENABLE_VIRTUAL_TERMINAL_PROCESSING); + } + return JS_UNDEFINED; +} +#elif !defined(__wasi__) +static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd; + struct winsize ws; + JSValue obj; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (ioctl(fd, TIOCGWINSZ, &ws) == 0 && + ws.ws_col >= 4 && ws.ws_row >= 4) { + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ws.ws_col), JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, ws.ws_row), JS_PROP_C_W_E); + return obj; + } else { + return JS_NULL; + } +} + +static struct termios oldtty; + +static void term_exit(void) +{ + tcsetattr(0, TCSANOW, &oldtty); +} + +/* XXX: should add a way to go back to normal mode */ +static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + struct termios tty; + int fd; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + + memset(&tty, 0, sizeof(tty)); + tcgetattr(fd, &tty); + oldtty = tty; + + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + tty.c_oflag |= OPOST; + tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); + tty.c_cflag &= ~(CSIZE|PARENB); + tty.c_cflag |= CS8; + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + + tcsetattr(fd, TCSANOW, &tty); + + atexit(term_exit); + return JS_UNDEFINED; +} + +#endif /* !_WIN32 && !__wasi__ */ + +static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename; + int ret; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; +#if defined(_WIN32) + { + struct stat st; + if (stat(filename, &st) == 0 && (st.st_mode & _S_IFDIR)) { + ret = rmdir(filename); + } else { + ret = unlink(filename); + } + } +#else + ret = remove(filename); +#endif + ret = js_get_errno(ret); + JS_FreeCString(ctx, filename); + return JS_NewInt32(ctx, ret); +} + +static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *oldpath, *newpath; + int ret; + + oldpath = JS_ToCString(ctx, argv[0]); + if (!oldpath) + return JS_EXCEPTION; + newpath = JS_ToCString(ctx, argv[1]); + if (!newpath) { + JS_FreeCString(ctx, oldpath); + return JS_EXCEPTION; + } + ret = js_get_errno(rename(oldpath, newpath)); + JS_FreeCString(ctx, oldpath); + JS_FreeCString(ctx, newpath); + return JS_NewInt32(ctx, ret); +} + +static bool is_main_thread(JSRuntime *rt) +{ + JSThreadState *ts = js_get_thread_state(rt); + return !ts->recv_pipe; +} + +static JSOSRWHandler *find_rh(JSThreadState *ts, int fd) +{ + JSOSRWHandler *rh; + struct list_head *el; + + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == fd) + return rh; + } + return NULL; +} + +static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh) +{ + int i; + list_del(&rh->link); + for(i = 0; i < 2; i++) { + JS_FreeValueRT(rt, rh->rw_func[i]); + } + js_free_rt(rt, rh); +} + +static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSOSRWHandler *rh; + int fd; + JSValueConst func; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + func = argv[1]; + if (JS_IsNull(func)) { + rh = find_rh(ts, fd); + if (rh) { + JS_FreeValue(ctx, rh->rw_func[magic]); + rh->rw_func[magic] = JS_NULL; + if (JS_IsNull(rh->rw_func[0]) && + JS_IsNull(rh->rw_func[1])) { + /* remove the entry */ + free_rw_handler(JS_GetRuntime(ctx), rh); + } + } + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + rh = find_rh(ts, fd); + if (!rh) { + rh = js_mallocz(ctx, sizeof(*rh)); + if (!rh) + return JS_EXCEPTION; + rh->fd = fd; + rh->rw_func[0] = JS_NULL; + rh->rw_func[1] = JS_NULL; + list_add_tail(&rh->link, &ts->os_rw_handlers); + } + JS_FreeValue(ctx, rh->rw_func[magic]); + rh->rw_func[magic] = JS_DupValue(ctx, func); + } + return JS_UNDEFINED; +} + +static JSOSSignalHandler *find_sh(JSThreadState *ts, int sig_num) +{ + JSOSSignalHandler *sh; + struct list_head *el; + list_for_each(el, &ts->os_signal_handlers) { + sh = list_entry(el, JSOSSignalHandler, link); + if (sh->sig_num == sig_num) + return sh; + } + return NULL; +} + +static void free_sh(JSRuntime *rt, JSOSSignalHandler *sh) +{ + list_del(&sh->link); + JS_FreeValueRT(rt, sh->func); + js_free_rt(rt, sh); +} + +static void os_signal_handler(int sig_num) +{ + os_pending_signals |= ((uint64_t)1 << sig_num); +} + +#if defined(_WIN32) +typedef void (*sighandler_t)(int sig_num); +#endif + +static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSOSSignalHandler *sh; + uint32_t sig_num; + JSValueConst func; + sighandler_t handler; + + if (!is_main_thread(rt)) + return JS_ThrowTypeError(ctx, "signal handler can only be set in the main thread"); + + if (JS_ToUint32(ctx, &sig_num, argv[0])) + return JS_EXCEPTION; + if (sig_num >= 64) + return JS_ThrowRangeError(ctx, "invalid signal number"); + func = argv[1]; + /* func = null: SIG_DFL, func = undefined, SIG_IGN */ + if (JS_IsNull(func) || JS_IsUndefined(func)) { + sh = find_sh(ts, sig_num); + if (sh) { + free_sh(JS_GetRuntime(ctx), sh); + } + if (JS_IsNull(func)) + handler = SIG_DFL; + else + handler = SIG_IGN; + signal(sig_num, handler); + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + sh = find_sh(ts, sig_num); + if (!sh) { + sh = js_mallocz(ctx, sizeof(*sh)); + if (!sh) + return JS_EXCEPTION; + sh->sig_num = sig_num; + list_add_tail(&sh->link, &ts->os_signal_handlers); + } + JS_FreeValue(ctx, sh->func); + sh->func = JS_DupValue(ctx, func); + signal(sig_num, os_signal_handler); + } + return JS_UNDEFINED; +} + +#if !defined(_WIN32) && !defined(__wasi__) +static JSValue js_os_cputime(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + struct rusage ru; + int64_t cputime; + + cputime = 0; + if (!getrusage(RUSAGE_SELF, &ru)) + cputime = (int64_t)ru.ru_utime.tv_sec * 1000000 + ru.ru_utime.tv_usec; + + return JS_NewInt64(ctx, cputime); +} +#endif + +static JSValue js_os_exepath(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + char buf[JS__PATH_MAX]; + size_t len = sizeof(buf); + if (js_exepath(buf, &len)) + return JS_UNDEFINED; + return JS_NewStringLen(ctx, buf, len); +} + +static JSValue js_os_now(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_NewInt64(ctx, js__hrtime_ns() / 1000); +} + +static uint64_t js__hrtime_ms(void) +{ + return js__hrtime_ns() / (1000 * 1000); +} + +static void free_timer(JSRuntime *rt, JSOSTimer *th) +{ + list_del(&th->link); + JS_FreeValueRT(rt, th->func); + js_free_rt(rt, th); +} + +// TODO(bnoordhuis) accept string as first arg and eval at timer expiry +// TODO(bnoordhuis) retain argv[2..] as args for callback if argc > 2 +static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + int64_t delay; + JSValueConst func; + JSOSTimer *th; + + func = argv[0]; + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + if (JS_ToInt64(ctx, &delay, argv[1])) + return JS_EXCEPTION; + if (delay < 1) + delay = 1; + th = js_mallocz(ctx, sizeof(*th)); + if (!th) + return JS_EXCEPTION; + th->timer_id = ts->next_timer_id++; + if (ts->next_timer_id > MAX_SAFE_INTEGER) + ts->next_timer_id = 1; + th->repeats = (magic > 0); + th->timeout = js__hrtime_ms() + delay; + th->delay = delay; + th->func = JS_DupValue(ctx, func); + list_add_tail(&th->link, &ts->os_timers); + return JS_NewInt64(ctx, th->timer_id); +} + +static JSOSTimer *find_timer_by_id(JSThreadState *ts, int timer_id) +{ + struct list_head *el; + if (timer_id <= 0) + return NULL; + list_for_each(el, &ts->os_timers) { + JSOSTimer *th = list_entry(el, JSOSTimer, link); + if (th->timer_id == timer_id) + return th; + } + return NULL; +} + +static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSOSTimer *th; + int64_t timer_id; + + if (JS_ToInt64(ctx, &timer_id, argv[0])) + return JS_EXCEPTION; + th = find_timer_by_id(ts, timer_id); + if (!th) + return JS_UNDEFINED; + free_timer(rt, th); + return JS_UNDEFINED; +} + +/* return a promise */ +static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + int64_t delay; + JSOSTimer *th; + JSValue promise, resolving_funcs[2]; + + if (JS_ToInt64(ctx, &delay, argv[0])) + return JS_EXCEPTION; + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + + th = js_mallocz(ctx, sizeof(*th)); + if (!th) { + JS_FreeValue(ctx, promise); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return JS_EXCEPTION; + } + th->timer_id = -1; + th->timeout = js__hrtime_ms() + delay; + th->func = JS_DupValue(ctx, resolving_funcs[0]); + list_add_tail(&th->link, &ts->os_timers); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; +} + +static int call_handler(JSContext *ctx, JSValue func) +{ + int r; + JSValue ret, func1; + /* 'func' might be destroyed when calling itself (if it frees the + handler), so must take extra care */ + func1 = JS_DupValue(ctx, func); + ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL); + JS_FreeValue(ctx, func1); + r = 0; + if (JS_IsException(ret)) + r = -1; + JS_FreeValue(ctx, ret); + return r; +} + +static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts, int *min_delay) +{ + JSValue func; + JSOSTimer *th; + int64_t cur_time, delay; + struct list_head *el; + int r; + + if (list_empty(&ts->os_timers)) { + *min_delay = -1; + return 0; + } + + cur_time = js__hrtime_ms(); + *min_delay = INT32_MAX; + + list_for_each(el, &ts->os_timers) { + th = list_entry(el, JSOSTimer, link); + delay = th->timeout - cur_time; + if (delay > 0) { + *min_delay = min_int(*min_delay, delay); + } else { + *min_delay = 0; + func = JS_DupValueRT(rt, th->func); + if (th->repeats) + th->timeout = cur_time + th->delay; + else + free_timer(rt, th); + r = call_handler(ctx, func); + JS_FreeValueRT(rt, func); + return r; + } + } + + return 0; +} + +#ifdef USE_WORKER + +#ifdef _WIN32 + +static int js_waker_init(JSWaker *w) +{ + w->handle = CreateEvent(NULL, TRUE, FALSE, NULL); + return w->handle ? 0 : -1; +} + +static void js_waker_signal(JSWaker *w) +{ + SetEvent(w->handle); +} + +static void js_waker_clear(JSWaker *w) +{ + ResetEvent(w->handle); +} + +static void js_waker_close(JSWaker *w) +{ + CloseHandle(w->handle); + w->handle = INVALID_HANDLE_VALUE; +} + +#else // !_WIN32 + +static int js_waker_init(JSWaker *w) +{ + int fds[2]; + + if (pipe(fds) < 0) + return -1; + w->read_fd = fds[0]; + w->write_fd = fds[1]; + return 0; +} + +static void js_waker_signal(JSWaker *w) +{ + int ret; + + for(;;) { + ret = write(w->write_fd, "", 1); + if (ret == 1) + break; + if (ret < 0 && (errno != EAGAIN || errno != EINTR)) + break; + } +} + +static void js_waker_clear(JSWaker *w) +{ + uint8_t buf[16]; + int ret; + + for(;;) { + ret = read(w->read_fd, buf, sizeof(buf)); + if (ret >= 0) + break; + if (errno != EAGAIN && errno != EINTR) + break; + } +} + +static void js_waker_close(JSWaker *w) +{ + close(w->read_fd); + close(w->write_fd); + w->read_fd = -1; + w->write_fd = -1; +} + +#endif // _WIN32 + +static void js_free_message(JSWorkerMessage *msg); + +/* return 1 if a message was handled, 0 if no message */ +static int handle_posted_message(JSRuntime *rt, JSContext *ctx, + JSWorkerMessageHandler *port) +{ + JSWorkerMessagePipe *ps = port->recv_pipe; + int ret; + struct list_head *el; + JSWorkerMessage *msg; + JSValue obj, data_obj, func, retval; + + js_mutex_lock(&ps->mutex); + if (!list_empty(&ps->msg_queue)) { + el = ps->msg_queue.next; + msg = list_entry(el, JSWorkerMessage, link); + + /* remove the message from the queue */ + list_del(&msg->link); + + // drain read end of pipe + if (list_empty(&ps->msg_queue)) + js_waker_clear(&ps->waker); + + js_mutex_unlock(&ps->mutex); + + data_obj = JS_ReadObject(ctx, msg->data, msg->data_len, + JS_READ_OBJ_SAB | JS_READ_OBJ_REFERENCE); + + js_free_message(msg); + + if (JS_IsException(data_obj)) + goto fail; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, data_obj); + goto fail; + } + JS_DefinePropertyValueStr(ctx, obj, "data", data_obj, JS_PROP_C_W_E); + + /* 'func' might be destroyed when calling itself (if it frees the + handler), so must take extra care */ + func = JS_DupValue(ctx, port->on_message_func); + retval = JS_Call(ctx, func, JS_UNDEFINED, 1, (JSValueConst *)&obj); + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, func); + if (JS_IsException(retval)) { + fail: + js_std_dump_error(ctx); + } else { + JS_FreeValue(ctx, retval); + } + ret = 1; + } else { + js_mutex_unlock(&ps->mutex); + ret = 0; + } + return ret; +} + +#endif // USE_WORKER + +#if defined(_WIN32) +static int js_os_poll(JSContext *ctx) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + int min_delay, count; + JSOSRWHandler *rh; + struct list_head *el; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; // 64 + + /* XXX: handle signals if useful */ + + if (js_os_run_timers(rt, ctx, ts, &min_delay)) + return -1; + if (min_delay == 0) + return 0; // expired timer + if (min_delay < 0) + if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->port_list)) + return -1; /* no more events */ + + count = 0; + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) + handles[count++] = (HANDLE)_get_osfhandle(rh->fd); // stdin + if (count == (int)countof(handles)) + break; + } + + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (JS_IsNull(port->on_message_func)) + continue; + handles[count++] = port->recv_pipe->waker.handle; + if (count == (int)countof(handles)) + break; + } + + if (count > 0) { + DWORD ret, timeout = INFINITE; + if (min_delay != -1) + timeout = min_delay; + ret = WaitForMultipleObjects(count, handles, FALSE, timeout); + if (ret < count) { + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { + return call_handler(ctx, rh->rw_func[0]); + /* must stop because the list may have been modified */ + } + } + + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (!JS_IsNull(port->on_message_func)) { + JSWorkerMessagePipe *ps = port->recv_pipe; + if (ps->waker.handle == handles[ret]) { + if (handle_posted_message(rt, ctx, port)) + goto done; + } + } + } + } + } else { + Sleep(min_delay); + } +done: + return 0; +} +#else // !defined(_WIN32) +static int js_os_poll(JSContext *ctx) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + int r, w, ret, nfds, min_delay; + JSOSRWHandler *rh; + struct list_head *el; + struct pollfd *pfd, *pfds, pfds_local[64]; + + /* only check signals in the main thread */ + if (!ts->recv_pipe && + unlikely(os_pending_signals != 0)) { + JSOSSignalHandler *sh; + uint64_t mask; + + list_for_each(el, &ts->os_signal_handlers) { + sh = list_entry(el, JSOSSignalHandler, link); + mask = (uint64_t)1 << sh->sig_num; + if (os_pending_signals & mask) { + os_pending_signals &= ~mask; + return call_handler(ctx, sh->func); + } + } + } + + if (js_os_run_timers(rt, ctx, ts, &min_delay)) + return -1; + if (min_delay == 0) + return 0; // expired timer + if (min_delay < 0) + if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->port_list)) + return -1; /* no more events */ + + nfds = 0; + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + nfds += (!JS_IsNull(rh->rw_func[0]) || !JS_IsNull(rh->rw_func[1])); + } + +#ifdef USE_WORKER + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + nfds += !JS_IsNull(port->on_message_func); + } +#endif // USE_WORKER + + pfd = pfds = pfds_local; + if (nfds > (int)countof(pfds_local)) { + pfd = pfds = js_malloc(ctx, nfds * sizeof(*pfd)); + if (!pfd) + return -1; + } + + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + r = POLLIN * !JS_IsNull(rh->rw_func[0]); + w = POLLOUT * !JS_IsNull(rh->rw_func[1]); + if (r || w) + *pfd++ = (struct pollfd){rh->fd, r|w, 0}; + } + +#ifdef USE_WORKER + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (!JS_IsNull(port->on_message_func)) { + JSWorkerMessagePipe *ps = port->recv_pipe; + *pfd++ = (struct pollfd){ps->waker.read_fd, POLLIN, 0}; + } + } +#endif // USE_WORKER + + // FIXME(bnoordhuis) the loop below is quadratic in theory but + // linear-ish in practice because we bail out on the first hit, + // i.e., it's probably good enough for now + ret = 0; + nfds = poll(pfds, nfds, min_delay); + for (pfd = pfds; nfds-- > 0; pfd++) { + rh = find_rh(ts, pfd->fd); + if (rh) { + r = (POLLERR|POLLHUP|POLLNVAL|POLLIN) * !JS_IsNull(rh->rw_func[0]); + w = (POLLERR|POLLHUP|POLLNVAL|POLLOUT) * !JS_IsNull(rh->rw_func[1]); + if (r & pfd->revents) { + ret = call_handler(ctx, rh->rw_func[0]); + goto done; + /* must stop because the list may have been modified */ + } + if (w & pfd->revents) { + ret = call_handler(ctx, rh->rw_func[1]); + goto done; + /* must stop because the list may have been modified */ + } + } else { +#ifdef USE_WORKER + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (!JS_IsNull(port->on_message_func)) { + JSWorkerMessagePipe *ps = port->recv_pipe; + if (pfd->fd == ps->waker.read_fd) { + if (handle_posted_message(rt, ctx, port)) + goto done; + } + } + } +#endif // USE_WORKER + } + } +done: + if (pfds != pfds_local) + js_free(ctx, pfds); + return ret; +} +#endif // defined(_WIN32) + + +static JSValue make_obj_error(JSContext *ctx, + JSValue obj, + int err) +{ + JSValue arr; + if (JS_IsException(obj)) + return obj; + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + return JS_EXCEPTION; + JS_DefinePropertyValueUint32(ctx, arr, 0, obj, + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, arr, 1, JS_NewInt32(ctx, err), + JS_PROP_C_W_E); + return arr; +} + +static JSValue make_string_error(JSContext *ctx, + const char *buf, + int err) +{ + return make_obj_error(ctx, JS_NewString(ctx, buf), err); +} + +/* return [cwd, errorcode] */ +static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + char buf[JS__PATH_MAX]; + int err; + + if (!getcwd(buf, sizeof(buf))) { + buf[0] = '\0'; + err = errno; + } else { + err = 0; + } + return make_string_error(ctx, buf, err); +} + +static JSValue js_os_chdir(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *target; + int err; + + target = JS_ToCString(ctx, argv[0]); + if (!target) + return JS_EXCEPTION; + err = js_get_errno(chdir(target)); + JS_FreeCString(ctx, target); + return JS_NewInt32(ctx, err); +} + +static JSValue js_os_mkdir(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int mode, ret; + const char *path; + + if (argc >= 2) { + if (JS_ToInt32(ctx, &mode, argv[1])) + return JS_EXCEPTION; + } else { + mode = 0777; + } + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; +#if defined(_WIN32) + (void)mode; + ret = js_get_errno(mkdir(path)); +#else + ret = js_get_errno(mkdir(path, mode)); +#endif + JS_FreeCString(ctx, path); + return JS_NewInt32(ctx, ret); +} + +/* return [array, errorcode] */ +static JSValue js_os_readdir(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ +#ifdef _WIN32 + const char *path; + JSValue obj; + int err; + uint32_t len; + HANDLE h; + WIN32_FIND_DATAA d; + char s[1024]; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) { + JS_FreeCString(ctx, path); + return JS_EXCEPTION; + } + snprintf(s, sizeof(s), "%s/*", path); + JS_FreeCString(ctx, path); + err = 0; + h = FindFirstFileA(s, &d); + if (h == INVALID_HANDLE_VALUE) + err = GetLastError(); + if (err) + goto done; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewString(ctx, "."), + JS_PROP_C_W_E); + for (len = 1; FindNextFileA(h, &d); len++) { + JS_DefinePropertyValueUint32(ctx, obj, len, + JS_NewString(ctx, d.cFileName), + JS_PROP_C_W_E); + } + FindClose(h); +done: + return make_obj_error(ctx, obj, err); +#else + const char *path; + DIR *f; + struct dirent *d; + JSValue obj; + int err; + uint32_t len; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) { + JS_FreeCString(ctx, path); + return JS_EXCEPTION; + } + f = opendir(path); + if (!f) + err = errno; + else + err = 0; + JS_FreeCString(ctx, path); + if (!f) + goto done; + len = 0; + for(;;) { + errno = 0; + d = readdir(f); + if (!d) { + err = errno; + break; + } + JS_DefinePropertyValueUint32(ctx, obj, len++, + JS_NewString(ctx, d->d_name), + JS_PROP_C_W_E); + } + closedir(f); + done: + return make_obj_error(ctx, obj, err); +#endif +} + +#if !defined(_WIN32) +static int64_t timespec_to_ms(const struct timespec *tv) +{ + return (int64_t)tv->tv_sec * 1000 + (tv->tv_nsec / 1000000); +} +#endif + +/* return [obj, errcode] */ +static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int is_lstat) +{ + const char *path; + int err, res; + struct stat st; + JSValue obj; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; +#if defined(_WIN32) + res = stat(path, &st); +#else + if (is_lstat) + res = lstat(path, &st); + else + res = stat(path, &st); +#endif + err = (res < 0) ? errno : 0; + JS_FreeCString(ctx, path); + if (res < 0) { + obj = JS_NULL; + } else { + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + JS_DefinePropertyValueStr(ctx, obj, "dev", + JS_NewInt64(ctx, st.st_dev), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ino", + JS_NewInt64(ctx, st.st_ino), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mode", + JS_NewInt32(ctx, st.st_mode), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "nlink", + JS_NewInt64(ctx, st.st_nlink), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "uid", + JS_NewInt64(ctx, st.st_uid), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "gid", + JS_NewInt64(ctx, st.st_gid), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "rdev", + JS_NewInt64(ctx, st.st_rdev), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "size", + JS_NewInt64(ctx, st.st_size), + JS_PROP_C_W_E); +#if !defined(_WIN32) + JS_DefinePropertyValueStr(ctx, obj, "blocks", + JS_NewInt64(ctx, st.st_blocks), + JS_PROP_C_W_E); +#endif +#if defined(_WIN32) + JS_DefinePropertyValueStr(ctx, obj, "atime", + JS_NewInt64(ctx, (int64_t)st.st_atime * 1000), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, (int64_t)st.st_mtime * 1000), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, (int64_t)st.st_ctime * 1000), + JS_PROP_C_W_E); +#elif defined(__APPLE__) + JS_DefinePropertyValueStr(ctx, obj, "atime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_atimespec)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_mtimespec)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_ctimespec)), + JS_PROP_C_W_E); +#else + JS_DefinePropertyValueStr(ctx, obj, "atime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_atim)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_mtim)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_ctim)), + JS_PROP_C_W_E); +#endif + } + return make_obj_error(ctx, obj, err); +} + +#if !defined(_WIN32) +static void ms_to_timeval(struct timeval *tv, uint64_t v) +{ + tv->tv_sec = v / 1000; + tv->tv_usec = (v % 1000) * 1000; +} +#endif + +static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *path; + int64_t atime, mtime; + int ret; + + if (JS_ToInt64(ctx, &atime, argv[1])) + return JS_EXCEPTION; + if (JS_ToInt64(ctx, &mtime, argv[2])) + return JS_EXCEPTION; + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; +#if defined(_WIN32) + { + struct _utimbuf times; + times.actime = atime / 1000; + times.modtime = mtime / 1000; + ret = js_get_errno(_utime(path, ×)); + } +#else + { + struct timeval times[2]; + ms_to_timeval(×[0], atime); + ms_to_timeval(×[1], mtime); + ret = js_get_errno(utimes(path, times)); + } +#endif + JS_FreeCString(ctx, path); + return JS_NewInt32(ctx, ret); +} + +/* sleep(delay_ms) */ +static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int64_t delay; + int ret; + + if (JS_ToInt64(ctx, &delay, argv[0])) + return JS_EXCEPTION; + if (delay < 0) + delay = 0; +#if defined(_WIN32) + { + if (delay > INT32_MAX) + delay = INT32_MAX; + Sleep(delay); + ret = 0; + } +#else + { + struct timespec ts; + + ts.tv_sec = delay / 1000; + ts.tv_nsec = (delay % 1000) * 1000000; + ret = js_get_errno(nanosleep(&ts, NULL)); + } +#endif + return JS_NewInt32(ctx, ret); +} + +#if defined(_WIN32) +static char *realpath(const char *path, char *buf) +{ + if (!_fullpath(buf, path, JS__PATH_MAX)) { + errno = ENOENT; + return NULL; + } else { + return buf; + } +} +#endif + +#if !defined(__wasi__) +/* return [path, errorcode] */ +static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *path; + char buf[JS__PATH_MAX], *res; + int err; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + res = realpath(path, buf); + JS_FreeCString(ctx, path); + if (!res) { + buf[0] = '\0'; + err = errno; + } else { + err = 0; + } + return make_string_error(ctx, buf, err); +} +#endif + +#if !defined(_WIN32) && !defined(__wasi__) && !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) +static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *target, *linkpath; + int err; + + target = JS_ToCString(ctx, argv[0]); + if (!target) + return JS_EXCEPTION; + linkpath = JS_ToCString(ctx, argv[1]); + if (!linkpath) { + JS_FreeCString(ctx, target); + return JS_EXCEPTION; + } + err = js_get_errno(symlink(target, linkpath)); + JS_FreeCString(ctx, target); + JS_FreeCString(ctx, linkpath); + return JS_NewInt32(ctx, err); +} + +/* return [path, errorcode] */ +static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *path; + char buf[JS__PATH_MAX]; + int err; + ssize_t res; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + res = readlink(path, buf, sizeof(buf) - 1); + if (res < 0) { + buf[0] = '\0'; + err = errno; + } else { + buf[res] = '\0'; + err = 0; + } + JS_FreeCString(ctx, path); + return make_string_error(ctx, buf, err); +} + +static char **build_envp(JSContext *ctx, JSValue obj) +{ + uint32_t len, i; + JSPropertyEnum *tab; + char **envp, *pair; + const char *key, *str; + JSValue val; + size_t key_len, str_len; + + if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) + return NULL; + envp = js_mallocz(ctx, sizeof(envp[0]) * ((size_t)len + 1)); + if (!envp) + goto fail; + for(i = 0; i < len; i++) { + val = JS_GetProperty(ctx, obj, tab[i].atom); + if (JS_IsException(val)) + goto fail; + str = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!str) + goto fail; + key = JS_AtomToCString(ctx, tab[i].atom); + if (!key) { + JS_FreeCString(ctx, str); + goto fail; + } + key_len = strlen(key); + str_len = strlen(str); + pair = js_malloc(ctx, key_len + str_len + 2); + if (!pair) { + JS_FreeCString(ctx, key); + JS_FreeCString(ctx, str); + goto fail; + } + memcpy(pair, key, key_len); + pair[key_len] = '='; + memcpy(pair + key_len + 1, str, str_len); + pair[key_len + 1 + str_len] = '\0'; + envp[i] = pair; + JS_FreeCString(ctx, key); + JS_FreeCString(ctx, str); + } + done: + for(i = 0; i < len; i++) + JS_FreeAtom(ctx, tab[i].atom); + js_free(ctx, tab); + return envp; + fail: + if (envp) { + for(i = 0; i < len; i++) + js_free(ctx, envp[i]); + js_free(ctx, envp); + envp = NULL; + } + goto done; +} + +/* execvpe is not available on non GNU systems */ +static int my_execvpe(const char *filename, char **argv, char **envp) +{ + char *path, *p, *p_next, *p1; + char buf[JS__PATH_MAX]; + size_t filename_len, path_len; + bool eacces_error; + + filename_len = strlen(filename); + if (filename_len == 0) { + errno = ENOENT; + return -1; + } + if (strchr(filename, '/')) + return execve(filename, argv, envp); + + path = getenv("PATH"); + if (!path) + path = (char *)"/bin:/usr/bin"; + eacces_error = false; + p = path; + for(p = path; p != NULL; p = p_next) { + p1 = strchr(p, ':'); + if (!p1) { + p_next = NULL; + path_len = strlen(p); + } else { + p_next = p1 + 1; + path_len = p1 - p; + } + /* path too long */ + if ((path_len + 1 + filename_len + 1) > JS__PATH_MAX) + continue; + memcpy(buf, p, path_len); + buf[path_len] = '/'; + memcpy(buf + path_len + 1, filename, filename_len); + buf[path_len + 1 + filename_len] = '\0'; + + execve(buf, argv, envp); + + switch(errno) { + case EACCES: + eacces_error = true; + break; + case ENOENT: + case ENOTDIR: + break; + default: + return -1; + } + } + if (eacces_error) + errno = EACCES; + return -1; +} + +static void (*js_os_exec_closefrom)(int); + +#if !defined(EMSCRIPTEN) && !defined(__wasi__) + +static js_once_t js_os_exec_once = JS_ONCE_INIT; + +static void js_os_exec_once_init(void) +{ + *(void **) (&js_os_exec_closefrom) = dlsym(RTLD_DEFAULT, "closefrom"); +} + +#endif + +/* exec(args[, options]) -> exitcode */ +static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst options, args = argv[0]; + JSValue val, ret_val; + const char **exec_argv, *file = NULL, *str, *cwd = NULL; + char **envp = environ; + uint32_t exec_argc, i; + int ret, pid, status; + bool block_flag = true, use_path = true; + static const char *std_name[3] = { "stdin", "stdout", "stderr" }; + int std_fds[3]; + uint32_t uid = -1, gid = -1; + int ngroups = -1; + gid_t groups[64]; + + val = JS_GetPropertyStr(ctx, args, "length"); + if (JS_IsException(val)) + return JS_EXCEPTION; + ret = JS_ToUint32(ctx, &exec_argc, val); + JS_FreeValue(ctx, val); + if (ret) + return JS_EXCEPTION; + /* arbitrary limit to avoid overflow */ + if (exec_argc < 1 || exec_argc > 65535) { + return JS_ThrowTypeError(ctx, "invalid number of arguments"); + } + exec_argv = js_mallocz(ctx, sizeof(exec_argv[0]) * (exec_argc + 1)); + if (!exec_argv) + return JS_EXCEPTION; + for(i = 0; i < exec_argc; i++) { + val = JS_GetPropertyUint32(ctx, args, i); + if (JS_IsException(val)) + goto exception; + str = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!str) + goto exception; + exec_argv[i] = str; + } + exec_argv[exec_argc] = NULL; + + for(i = 0; i < 3; i++) + std_fds[i] = i; + + /* get the options, if any */ + if (argc >= 2) { + options = argv[1]; + + if (get_bool_option(ctx, &block_flag, options, "block")) + goto exception; + if (get_bool_option(ctx, &use_path, options, "usePath")) + goto exception; + + val = JS_GetPropertyStr(ctx, options, "file"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + file = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!file) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "cwd"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + cwd = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!cwd) + goto exception; + } + + /* stdin/stdout/stderr handles */ + for(i = 0; i < 3; i++) { + val = JS_GetPropertyStr(ctx, options, std_name[i]); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + int fd; + ret = JS_ToInt32(ctx, &fd, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + std_fds[i] = fd; + } + } + + val = JS_GetPropertyStr(ctx, options, "env"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + envp = build_envp(ctx, val); + JS_FreeValue(ctx, val); + if (!envp) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "uid"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + ret = JS_ToUint32(ctx, &uid, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "gid"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + ret = JS_ToUint32(ctx, &gid, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "groups"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + int64_t idx, len; + JSValue prop; + uint32_t id; + ngroups = 0; + if (JS_GetLength(ctx, val, &len)) { + JS_FreeValue(ctx, val); + goto exception; + } + for (idx = 0; idx < len; idx++) { + prop = JS_GetPropertyInt64(ctx, val, idx); + if (JS_IsException(prop)) + break; + if (JS_IsUndefined(prop)) + continue; + ret = JS_ToUint32(ctx, &id, prop); + JS_FreeValue(ctx, prop); + if (ret) + break; + if (ngroups == countof(groups)) { + JS_ThrowRangeError(ctx, "too many groups"); + break; + } + groups[ngroups++] = id; + } + JS_FreeValue(ctx, val); + if (idx < len) + goto exception; + } + + } + +#if !defined(EMSCRIPTEN) && !defined(__wasi__) + // should happen pre-fork because it calls dlsym() + // and that's not an async-signal-safe function + js_once(&js_os_exec_once, js_os_exec_once_init); +#endif + + pid = fork(); + if (pid < 0) { + JS_ThrowTypeError(ctx, "fork error"); + goto exception; + } + if (pid == 0) { + /* child */ + /* remap the stdin/stdout/stderr handles if necessary */ + for(i = 0; i < 3; i++) { + if (std_fds[i] != i) { + if (dup2(std_fds[i], i) < 0) + _exit(127); + } + } + + if (js_os_exec_closefrom) { + js_os_exec_closefrom(3); + } else { + int fd_max = sysconf(_SC_OPEN_MAX); + for(i = 3; i < fd_max; i++) + close(i); + } + + if (cwd) { + if (chdir(cwd) < 0) + _exit(127); + } + if (ngroups != -1) { + if (setgroups(ngroups, groups) < 0) + _exit(127); + } + if (uid != -1) { + if (setuid(uid) < 0) + _exit(127); + } + if (gid != -1) { + if (setgid(gid) < 0) + _exit(127); + } + + if (!file) + file = exec_argv[0]; + if (use_path) + ret = my_execvpe(file, (char **)exec_argv, envp); + else + ret = execve(file, (char **)exec_argv, envp); + _exit(127); + } + /* parent */ + if (block_flag) { + for(;;) { + ret = waitpid(pid, &status, 0); + if (ret == pid) { + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + break; + } else if (WIFSIGNALED(status)) { + ret = -WTERMSIG(status); + break; + } + } + } + } else { + ret = pid; + } + ret_val = JS_NewInt32(ctx, ret); + done: + JS_FreeCString(ctx, file); + JS_FreeCString(ctx, cwd); + for(i = 0; i < exec_argc; i++) + JS_FreeCString(ctx, exec_argv[i]); + js_free(ctx, exec_argv); + if (envp != environ) { + char **p; + p = envp; + while (*p != NULL) { + js_free(ctx, *p); + p++; + } + js_free(ctx, envp); + } + return ret_val; + exception: + ret_val = JS_EXCEPTION; + goto done; +} + +/* getpid() -> pid */ +static JSValue js_os_getpid(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_NewInt32(ctx, getpid()); +} + +/* waitpid(pid, block) -> [pid, status] */ +static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pid, status, options, ret; + JSValue obj; + + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &options, argv[1])) + return JS_EXCEPTION; + + ret = waitpid(pid, &status, options); + if (ret < 0) { + ret = -errno; + status = 0; + } + + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ret), + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, status), + JS_PROP_C_W_E); + return obj; +} + +/* pipe() -> [read_fd, write_fd] or null if error */ +static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pipe_fds[2], ret; + JSValue obj; + + ret = pipe(pipe_fds); + if (ret < 0) + return JS_NULL; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]), + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]), + JS_PROP_C_W_E); + return obj; +} + +/* kill(pid, sig) */ +static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pid, sig, ret; + + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &sig, argv[1])) + return JS_EXCEPTION; + ret = js_get_errno(kill(pid, sig)); + return JS_NewInt32(ctx, ret); +} + +/* dup(fd) */ +static JSValue js_os_dup(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd, ret; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + ret = js_get_errno(dup(fd)); + return JS_NewInt32(ctx, ret); +} + +/* dup2(fd) */ +static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd, fd2, ret; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &fd2, argv[1])) + return JS_EXCEPTION; + ret = js_get_errno(dup2(fd, fd2)); + return JS_NewInt32(ctx, ret); +} + +#endif /* !_WIN32 */ + +#ifdef USE_WORKER + +/* Worker */ + +typedef struct { + JSWorkerMessagePipe *recv_pipe; + JSWorkerMessagePipe *send_pipe; + JSWorkerMessageHandler *msg_handler; +} JSWorkerData; + +typedef struct { + char *filename; /* module filename */ + char *basename; /* module base name */ + JSWorkerMessagePipe *recv_pipe, *send_pipe; +} WorkerFuncArgs; + +typedef struct { + int ref_count; + uint64_t buf[]; +} JSSABHeader; + +static JSContext *(*js_worker_new_context_func)(JSRuntime *rt); + +static int atomic_add_int(int *ptr, int v) +{ + return atomic_fetch_add((_Atomic uint32_t*)ptr, v) + v; +} + +/* shared array buffer allocator */ +static void *js_sab_alloc(void *opaque, size_t size) +{ + JSSABHeader *sab; + sab = malloc(sizeof(JSSABHeader) + size); + if (!sab) + return NULL; + sab->ref_count = 1; + return sab->buf; +} + +static void js_sab_free(void *opaque, void *ptr) +{ + JSSABHeader *sab; + int ref_count; + sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader)); + ref_count = atomic_add_int(&sab->ref_count, -1); + assert(ref_count >= 0); + if (ref_count == 0) { + free(sab); + } +} + +static void js_sab_dup(void *opaque, void *ptr) +{ + JSSABHeader *sab; + sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader)); + atomic_add_int(&sab->ref_count, 1); +} + +static JSWorkerMessagePipe *js_new_message_pipe(void) +{ + JSWorkerMessagePipe *ps; + + ps = malloc(sizeof(*ps)); + if (!ps) + return NULL; + if (js_waker_init(&ps->waker)) { + free(ps); + return NULL; + } + ps->ref_count = 1; + init_list_head(&ps->msg_queue); + js_mutex_init(&ps->mutex); + return ps; +} + +static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps) +{ + atomic_add_int(&ps->ref_count, 1); + return ps; +} + +static void js_free_message(JSWorkerMessage *msg) +{ + size_t i; + /* free the SAB */ + for(i = 0; i < msg->sab_tab_len; i++) { + js_sab_free(NULL, msg->sab_tab[i]); + } + free(msg->sab_tab); + free(msg->data); + free(msg); +} + +static void js_free_message_pipe(JSWorkerMessagePipe *ps) +{ + struct list_head *el, *el1; + JSWorkerMessage *msg; + int ref_count; + + if (!ps) + return; + + ref_count = atomic_add_int(&ps->ref_count, -1); + assert(ref_count >= 0); + if (ref_count == 0) { + list_for_each_safe(el, el1, &ps->msg_queue) { + msg = list_entry(el, JSWorkerMessage, link); + js_free_message(msg); + } + js_mutex_destroy(&ps->mutex); + js_waker_close(&ps->waker); + free(ps); + } +} + +static void js_free_port(JSRuntime *rt, JSWorkerMessageHandler *port) +{ + if (port) { + js_free_message_pipe(port->recv_pipe); + JS_FreeValueRT(rt, port->on_message_func); + list_del(&port->link); + js_free_rt(rt, port); + } +} + +static void js_worker_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSThreadState *ts = js_get_thread_state(rt); + JSWorkerData *worker = JS_GetOpaque(val, ts->worker_class_id); + if (worker) { + js_free_message_pipe(worker->recv_pipe); + js_free_message_pipe(worker->send_pipe); + js_free_port(rt, worker->msg_handler); + js_free_rt(rt, worker); + } +} + +static JSClassDef js_worker_class = { + "Worker", + .finalizer = js_worker_finalizer, +}; + +static void worker_func(void *opaque) +{ + WorkerFuncArgs *args = opaque; + JSRuntime *rt; + JSThreadState *ts; + JSContext *ctx; + JSValue val; + + rt = JS_NewRuntime(); + if (rt == NULL) { + fprintf(stderr, "JS_NewRuntime failure"); + exit(1); + } + js_std_init_handlers(rt); + + JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); + + /* set the pipe to communicate with the parent */ + ts = js_get_thread_state(rt); + ts->recv_pipe = args->recv_pipe; + ts->send_pipe = args->send_pipe; + + /* function pointer to avoid linking the whole JS_NewContext() if + not needed */ + ctx = js_worker_new_context_func(rt); + if (ctx == NULL) { + fprintf(stderr, "JS_NewContext failure"); + } + + JS_SetCanBlock(rt, true); + + js_std_add_helpers(ctx, -1, NULL); + + val = JS_LoadModule(ctx, args->basename, args->filename); + free(args->filename); + free(args->basename); + free(args); + val = js_std_await(ctx, val); + if (JS_IsException(val)) + js_std_dump_error(ctx); + JS_FreeValue(ctx, val); + + js_std_loop(ctx); + + js_std_free_handlers(rt); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + +static JSValue js_worker_ctor_internal(JSContext *ctx, JSValueConst new_target, + JSWorkerMessagePipe *recv_pipe, + JSWorkerMessagePipe *send_pipe) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSValue obj = JS_UNDEFINED, proto; + JSWorkerData *s; + + /* create the object */ + if (JS_IsUndefined(new_target)) { + proto = JS_GetClassProto(ctx, ts->worker_class_id); + } else { + proto = JS_GetPropertyStr(ctx, new_target, "prototype"); + if (JS_IsException(proto)) + goto fail; + } + obj = JS_NewObjectProtoClass(ctx, proto, ts->worker_class_id); + JS_FreeValue(ctx, proto); + if (JS_IsException(obj)) + goto fail; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + goto fail; + s->recv_pipe = js_dup_message_pipe(recv_pipe); + s->send_pipe = js_dup_message_pipe(send_pipe); + + JS_SetOpaque(obj, s); + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + WorkerFuncArgs *args = NULL; + js_thread_t thr; + JSValue obj = JS_UNDEFINED; + int ret; + const char *filename = NULL, *basename; + JSAtom basename_atom; + + /* XXX: in order to avoid problems with resource liberation, we + don't support creating workers inside workers */ + if (!is_main_thread(rt)) + return JS_ThrowTypeError(ctx, "cannot create a worker inside a worker"); + + /* base name, assuming the calling function is a normal JS + function */ + basename_atom = JS_GetScriptOrModuleName(ctx, 1); + if (basename_atom == JS_ATOM_NULL) { + return JS_ThrowTypeError(ctx, "could not determine calling script or module name"); + } + basename = JS_AtomToCString(ctx, basename_atom); + JS_FreeAtom(ctx, basename_atom); + if (!basename) + goto fail; + + /* module name */ + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + + args = malloc(sizeof(*args)); + if (!args) + goto oom_fail; + memset(args, 0, sizeof(*args)); + args->filename = strdup(filename); + args->basename = strdup(basename); + + /* ports */ + args->recv_pipe = js_new_message_pipe(); + if (!args->recv_pipe) + goto oom_fail; + args->send_pipe = js_new_message_pipe(); + if (!args->send_pipe) + goto oom_fail; + + obj = js_worker_ctor_internal(ctx, new_target, + args->send_pipe, args->recv_pipe); + if (JS_IsException(obj)) + goto fail; + + ret = js_thread_create(&thr, worker_func, args, JS_THREAD_CREATE_DETACHED); + if (ret != 0) { + JS_ThrowTypeError(ctx, "could not create worker"); + goto fail; + } + JS_FreeCString(ctx, basename); + JS_FreeCString(ctx, filename); + return obj; + oom_fail: + JS_ThrowOutOfMemory(ctx); + fail: + JS_FreeCString(ctx, basename); + JS_FreeCString(ctx, filename); + if (args) { + free(args->filename); + free(args->basename); + js_free_message_pipe(args->recv_pipe); + js_free_message_pipe(args->send_pipe); + free(args); + } + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, ts->worker_class_id); + JSWorkerMessagePipe *ps; + size_t data_len, i; + uint8_t *data; + JSWorkerMessage *msg; + JSSABTab sab_tab; + + if (!worker) + return JS_EXCEPTION; + + data = JS_WriteObject2(ctx, &data_len, argv[0], + JS_WRITE_OBJ_SAB | JS_WRITE_OBJ_REFERENCE, + &sab_tab); + if (!data) + return JS_EXCEPTION; + + msg = malloc(sizeof(*msg)); + if (!msg) + goto fail; + msg->data = NULL; + msg->sab_tab = NULL; + + /* must reallocate because the allocator may be different */ + msg->data = malloc(data_len); + if (!msg->data) + goto fail; + memcpy(msg->data, data, data_len); + msg->data_len = data_len; + + if (sab_tab.len > 0) { + msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab.len); + if (!msg->sab_tab) + goto fail; + memcpy(msg->sab_tab, sab_tab.tab, sizeof(msg->sab_tab[0]) * sab_tab.len); + } + msg->sab_tab_len = sab_tab.len; + + js_free(ctx, data); + js_free(ctx, sab_tab.tab); + + /* increment the SAB reference counts */ + for(i = 0; i < msg->sab_tab_len; i++) { + js_sab_dup(NULL, msg->sab_tab[i]); + } + + ps = worker->send_pipe; + js_mutex_lock(&ps->mutex); + /* indicate that data is present */ + if (list_empty(&ps->msg_queue)) + js_waker_signal(&ps->waker); + list_add_tail(&msg->link, &ps->msg_queue); + js_mutex_unlock(&ps->mutex); + return JS_UNDEFINED; + fail: + if (msg) { + free(msg->data); + free(msg->sab_tab); + free(msg); + } + js_free(ctx, data); + js_free(ctx, sab_tab.tab); + return JS_EXCEPTION; + +} + +static JSValue js_worker_set_onmessage(JSContext *ctx, JSValueConst this_val, + JSValueConst func) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, ts->worker_class_id); + JSWorkerMessageHandler *port; + + if (!worker) + return JS_EXCEPTION; + + port = worker->msg_handler; + if (JS_IsNull(func)) { + if (port) { + js_free_port(rt, port); + worker->msg_handler = NULL; + } + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + if (!port) { + port = js_mallocz(ctx, sizeof(*port)); + if (!port) + return JS_EXCEPTION; + port->recv_pipe = js_dup_message_pipe(worker->recv_pipe); + port->on_message_func = JS_NULL; + list_add_tail(&port->link, &ts->port_list); + worker->msg_handler = port; + } + JS_FreeValue(ctx, port->on_message_func); + port->on_message_func = JS_DupValue(ctx, func); + } + return JS_UNDEFINED; +} + +static JSValue js_worker_get_onmessage(JSContext *ctx, JSValueConst this_val) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, ts->worker_class_id); + JSWorkerMessageHandler *port; + if (!worker) + return JS_EXCEPTION; + port = worker->msg_handler; + if (port) { + return JS_DupValue(ctx, port->on_message_func); + } else { + return JS_NULL; + } +} + +static const JSCFunctionListEntry js_worker_proto_funcs[] = { + JS_CFUNC_DEF("postMessage", 1, js_worker_postMessage ), + JS_CGETSET_DEF("onmessage", js_worker_get_onmessage, js_worker_set_onmessage ), +}; + +#endif /* USE_WORKER */ + +void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)) +{ +#ifdef USE_WORKER + js_worker_new_context_func = func; +#endif +} + +#if defined(_WIN32) +#define OS_PLATFORM "win32" +#elif defined(__APPLE__) +#define OS_PLATFORM "darwin" +#elif defined(EMSCRIPTEN) +#define OS_PLATFORM "js" +#elif defined(__CYGWIN__) +#define OS_PLATFORM "cygwin" +#elif defined(__linux__) +#define OS_PLATFORM "linux" +#elif defined(__OpenBSD__) +#define OS_PLATFORM "openbsd" +#elif defined(__NetBSD__) +#define OS_PLATFORM "netbsd" +#elif defined(__FreeBSD__) +#define OS_PLATFORM "freebsd" +#elif defined(__wasi__) +#define OS_PLATFORM "wasi" +#elif defined(__GNU__) +#define OS_PLATFORM "hurd" +#else +#define OS_PLATFORM "unknown" +#endif + +#define OS_FLAG(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) + +static const JSCFunctionListEntry js_os_funcs[] = { + JS_CFUNC_DEF("open", 2, js_os_open ), + OS_FLAG(O_RDONLY), + OS_FLAG(O_WRONLY), + OS_FLAG(O_RDWR), + OS_FLAG(O_APPEND), + OS_FLAG(O_CREAT), + OS_FLAG(O_EXCL), + OS_FLAG(O_TRUNC), +#if defined(_WIN32) + OS_FLAG(O_BINARY), + OS_FLAG(O_TEXT), +#endif + JS_CFUNC_DEF("close", 1, js_os_close ), + JS_CFUNC_DEF("seek", 3, js_os_seek ), + JS_CFUNC_MAGIC_DEF("read", 4, js_os_read_write, 0 ), + JS_CFUNC_MAGIC_DEF("write", 4, js_os_read_write, 1 ), + JS_CFUNC_DEF("isatty", 1, js_os_isatty ), +#if !defined(__wasi__) + JS_CFUNC_DEF("ttyGetWinSize", 1, js_os_ttyGetWinSize ), + JS_CFUNC_DEF("ttySetRaw", 1, js_os_ttySetRaw ), +#endif + JS_CFUNC_DEF("remove", 1, js_os_remove ), + JS_CFUNC_DEF("rename", 2, js_os_rename ), + JS_CFUNC_MAGIC_DEF("setReadHandler", 2, js_os_setReadHandler, 0 ), + JS_CFUNC_MAGIC_DEF("setWriteHandler", 2, js_os_setReadHandler, 1 ), + JS_CFUNC_DEF("signal", 2, js_os_signal ), + OS_FLAG(SIGINT), + OS_FLAG(SIGABRT), + OS_FLAG(SIGFPE), + OS_FLAG(SIGILL), + OS_FLAG(SIGSEGV), + OS_FLAG(SIGTERM), +#if !defined(_WIN32) && !defined(__wasi__) + OS_FLAG(SIGQUIT), + OS_FLAG(SIGPIPE), + OS_FLAG(SIGALRM), + OS_FLAG(SIGUSR1), + OS_FLAG(SIGUSR2), + OS_FLAG(SIGCHLD), + OS_FLAG(SIGCONT), + OS_FLAG(SIGSTOP), + OS_FLAG(SIGTSTP), + OS_FLAG(SIGTTIN), + OS_FLAG(SIGTTOU), + JS_CFUNC_DEF("cputime", 0, js_os_cputime ), +#endif + JS_CFUNC_DEF("exePath", 0, js_os_exepath ), + JS_CFUNC_DEF("now", 0, js_os_now ), + JS_CFUNC_MAGIC_DEF("setTimeout", 2, js_os_setTimeout, 0 ), + JS_CFUNC_MAGIC_DEF("setInterval", 2, js_os_setTimeout, 1 ), + // per spec: both functions can cancel timeouts and intervals + JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ), + JS_CFUNC_DEF("clearInterval", 1, js_os_clearTimeout ), + JS_CFUNC_DEF("sleepAsync", 1, js_os_sleepAsync ), + JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ), + JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ), + JS_CFUNC_DEF("chdir", 0, js_os_chdir ), + JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ), + JS_CFUNC_DEF("readdir", 1, js_os_readdir ), + /* st_mode constants */ + OS_FLAG(S_IFMT), + OS_FLAG(S_IFIFO), + OS_FLAG(S_IFCHR), + OS_FLAG(S_IFDIR), + OS_FLAG(S_IFBLK), + OS_FLAG(S_IFREG), +#if !defined(_WIN32) + OS_FLAG(S_IFSOCK), + OS_FLAG(S_IFLNK), + OS_FLAG(S_ISGID), + OS_FLAG(S_ISUID), +#endif + JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ), + JS_CFUNC_DEF("utimes", 3, js_os_utimes ), + JS_CFUNC_DEF("sleep", 1, js_os_sleep ), +#if !defined(__wasi__) + JS_CFUNC_DEF("realpath", 1, js_os_realpath ), +#endif +#if !defined(_WIN32) && !defined(__wasi__) && !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) + JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), + JS_CFUNC_DEF("symlink", 2, js_os_symlink ), + JS_CFUNC_DEF("readlink", 1, js_os_readlink ), + JS_CFUNC_DEF("exec", 1, js_os_exec ), + JS_CFUNC_DEF("getpid", 0, js_os_getpid ), + JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ), + OS_FLAG(WNOHANG), + JS_CFUNC_DEF("pipe", 0, js_os_pipe ), + JS_CFUNC_DEF("kill", 2, js_os_kill ), + JS_CFUNC_DEF("dup", 1, js_os_dup ), + JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), +#endif +}; + +static int js_os_init(JSContext *ctx, JSModuleDef *m) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + + ts->can_js_os_poll = true; + +#ifdef USE_WORKER + { + JSValue proto, obj; + /* Worker class */ + JS_NewClassID(rt, &ts->worker_class_id); + JS_NewClass(rt, ts->worker_class_id, &js_worker_class); + proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_worker_proto_funcs, countof(js_worker_proto_funcs)); + + obj = JS_NewCFunction2(ctx, js_worker_ctor, "Worker", 1, + JS_CFUNC_constructor, 0); + JS_SetConstructor(ctx, obj, proto); + + JS_SetClassProto(ctx, ts->worker_class_id, proto); + + /* set 'Worker.parent' if necessary */ + if (ts->recv_pipe && ts->send_pipe) { + JS_DefinePropertyValueStr(ctx, obj, "parent", + js_worker_ctor_internal(ctx, JS_UNDEFINED, ts->recv_pipe, ts->send_pipe), + JS_PROP_C_W_E); + } + + JS_SetModuleExport(ctx, m, "Worker", obj); + } +#endif /* USE_WORKER */ + + return JS_SetModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); +} + +JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_os_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); +#ifdef USE_WORKER + JS_AddModuleExport(ctx, m, "Worker"); +#endif + return m; +} + +/**********************************************************/ + +static JSValue js_print(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ +#ifdef _WIN32 + HANDLE handle; + DWORD mode; +#endif + const char *s; + JSValueConst v; + DynBuf b; + int i; + + dbuf_init(&b); + for(i = 0; i < argc; i++) { + v = argv[i]; + s = JS_ToCString(ctx, v); + if (!s && JS_IsObject(v)) { + JS_FreeValue(ctx, JS_GetException(ctx)); + JSValue t = JS_ToObjectString(ctx, v); + s = JS_ToCString(ctx, t); + JS_FreeValue(ctx, t); + } + if (s) { + dbuf_printf(&b, "%s%s", &" "[!i], s); + JS_FreeCString(ctx, s); + } else { + dbuf_printf(&b, "%s", &" "[!i]); + JS_FreeValue(ctx, JS_GetException(ctx)); + } + } + dbuf_putc(&b, '\n'); +#ifdef _WIN32 + // use WriteConsoleA with CP_UTF8 for better Unicode handling vis-a-vis + // the mangling that happens when going through msvcrt's stdio layer, + // *except* when stdout is redirected to something that is not a console + handle = (HANDLE)_get_osfhandle(/*STDOUT_FILENO*/1); // don't CloseHandle + if (GetFileType(handle) != FILE_TYPE_CHAR) + goto fallback; + if (!GetConsoleMode(handle, &mode)) + goto fallback; + handle = GetStdHandle(STD_OUTPUT_HANDLE); + if (handle == INVALID_HANDLE_VALUE) + goto fallback; + mode = GetConsoleOutputCP(); + SetConsoleOutputCP(CP_UTF8); + WriteConsoleA(handle, b.buf, b.size, NULL, NULL); + SetConsoleOutputCP(mode); + FlushFileBuffers(handle); + goto done; +fallback: +#endif + fwrite(b.buf, 1, b.size, stdout); + fflush(stdout); + goto done; // avoid unused label warning +done: + dbuf_free(&b); + return JS_UNDEFINED; +} + +void js_std_add_helpers(JSContext *ctx, int argc, char **argv) +{ + JSValue global_obj, console, args; + int i; + + /* XXX: should these global definitions be enumerable? */ + global_obj = JS_GetGlobalObject(ctx); + + console = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, console, "log", + JS_NewCFunction(ctx, js_print, "log", 1)); + JS_SetPropertyStr(ctx, global_obj, "console", console); + + /* same methods as the mozilla JS shell */ + if (argc >= 0) { + args = JS_NewArray(ctx); + for(i = 0; i < argc; i++) { + JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, argv[i])); + } + JS_SetPropertyStr(ctx, global_obj, "scriptArgs", args); + } + + JS_SetPropertyStr(ctx, global_obj, "print", + JS_NewCFunction(ctx, js_print, "print", 1)); + + JS_FreeValue(ctx, global_obj); +} + +static void js_std_finalize(JSRuntime *rt, void *arg) +{ + JSThreadState *ts = arg; + js_set_thread_state(rt, NULL); + js_free_rt(rt, ts); +} + +void js_std_init_handlers(JSRuntime *rt) +{ + JSThreadState *ts; + + ts = js_mallocz_rt(rt, sizeof(*ts)); + if (!ts) { + fprintf(stderr, "Could not allocate memory for the worker"); + exit(1); + } + init_list_head(&ts->os_rw_handlers); + init_list_head(&ts->os_signal_handlers); + init_list_head(&ts->os_timers); + init_list_head(&ts->port_list); + init_list_head(&ts->rejected_promise_list); + + ts->next_timer_id = 1; + + js_set_thread_state(rt, ts); + JS_AddRuntimeFinalizer(rt, js_std_finalize, ts); + +#ifdef USE_WORKER + /* set the SharedArrayBuffer memory handlers */ + { + JSSharedArrayBufferFunctions sf; + memset(&sf, 0, sizeof(sf)); + sf.sab_alloc = js_sab_alloc; + sf.sab_free = js_sab_free; + sf.sab_dup = js_sab_dup; + JS_SetSharedArrayBufferFunctions(rt, &sf); + } +#endif +} + +static void free_rp(JSRuntime *rt, JSRejectedPromiseEntry *rp) +{ + list_del(&rp->link); + JS_FreeValueRT(rt, rp->promise); + JS_FreeValueRT(rt, rp->reason); + js_free_rt(rt, rp); +} + +void js_std_free_handlers(JSRuntime *rt) +{ + JSThreadState *ts = js_get_thread_state(rt); + struct list_head *el, *el1; + + list_for_each_safe(el, el1, &ts->os_rw_handlers) { + JSOSRWHandler *rh = list_entry(el, JSOSRWHandler, link); + free_rw_handler(rt, rh); + } + + list_for_each_safe(el, el1, &ts->os_signal_handlers) { + JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link); + free_sh(rt, sh); + } + + list_for_each_safe(el, el1, &ts->os_timers) { + JSOSTimer *th = list_entry(el, JSOSTimer, link); + free_timer(rt, th); + } + + list_for_each_safe(el, el1, &ts->rejected_promise_list) { + JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); + free_rp(rt, rp); + } + +#ifdef USE_WORKER + /* XXX: free port_list ? */ + js_free_message_pipe(ts->recv_pipe); + js_free_message_pipe(ts->send_pipe); +#endif +} + +static void js_dump_obj(JSContext *ctx, FILE *f, JSValueConst val) +{ + const char *str; + + str = JS_ToCString(ctx, val); + if (str) { + fprintf(f, "%s\n", str); + JS_FreeCString(ctx, str); + } else { + fprintf(f, "[exception]\n"); + } +} + +static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val) +{ + JSValue val; + bool is_error; + + is_error = JS_IsError(exception_val); + js_dump_obj(ctx, stderr, exception_val); + if (is_error) { + val = JS_GetPropertyStr(ctx, exception_val, "stack"); + } else { + js_std_cmd(/*ErrorBackTrace*/2, ctx, &val); + } + if (!JS_IsUndefined(val)) { + js_dump_obj(ctx, stderr, val); + JS_FreeValue(ctx, val); + } +} + +void js_std_dump_error(JSContext *ctx) +{ + JSValue exception_val; + + exception_val = JS_GetException(ctx); + js_std_dump_error1(ctx, exception_val); + JS_FreeValue(ctx, exception_val); +} + +static JSRejectedPromiseEntry *find_rejected_promise(JSContext *ctx, JSThreadState *ts, + JSValueConst promise) +{ + struct list_head *el; + + list_for_each(el, &ts->rejected_promise_list) { + JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); + if (JS_IsSameValue(ctx, rp->promise, promise)) + return rp; + } + return NULL; +} + +void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, + JSValueConst reason, + bool is_handled, void *opaque) +{ + + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSRejectedPromiseEntry *rp = find_rejected_promise(ctx, ts, promise); + + if (is_handled) { + /* the rejection is handled, so the entry can be removed if present */ + if (rp) + free_rp(rt, rp); + } else { + /* add a new entry if needed */ + if (!rp) { + rp = js_malloc_rt(rt, sizeof(*rp)); + if (rp) { + rp->promise = JS_DupValue(ctx, promise); + rp->reason = JS_DupValue(ctx, reason); + list_add_tail(&rp->link, &ts->rejected_promise_list); + } + } + } +} + +/* check if there are pending promise rejections. It must be done + asynchrously in case a rejected promise is handled later. Currently + we do it once the application is about to sleep. It could be done + more often if needed. */ +static void js_std_promise_rejection_check(JSContext *ctx) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + struct list_head *el; + + if (unlikely(!list_empty(&ts->rejected_promise_list))) { + list_for_each(el, &ts->rejected_promise_list) { + JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); + fprintf(stderr, "Possibly unhandled promise rejection: "); + js_std_dump_error1(ctx, rp->reason); + fflush(stderr); + } + exit(1); + } +} + +/* main loop which calls the user JS callbacks */ +int js_std_loop(JSContext *ctx) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSContext *ctx1; + int err; + + for(;;) { + /* execute the pending jobs */ + for(;;) { + err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (err <= 0) { + if (err < 0) + goto done; + break; + } + } + + js_std_promise_rejection_check(ctx); + + if (!ts->can_js_os_poll || js_os_poll(ctx)) + break; + } +done: + return JS_HasException(ctx); +} + +/* Wait for a promise and execute pending jobs while waiting for + it. Return the promise result or JS_EXCEPTION in case of promise + rejection. */ +JSValue js_std_await(JSContext *ctx, JSValue obj) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSValue ret; + int state; + + for(;;) { + state = JS_PromiseState(ctx, obj); + if (state == JS_PROMISE_FULFILLED) { + ret = JS_PromiseResult(ctx, obj); + JS_FreeValue(ctx, obj); + break; + } else if (state == JS_PROMISE_REJECTED) { + ret = JS_Throw(ctx, JS_PromiseResult(ctx, obj)); + JS_FreeValue(ctx, obj); + break; + } else if (state == JS_PROMISE_PENDING) { + JSContext *ctx1; + int err; + err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (err < 0) { + js_std_dump_error(ctx1); + } + if (err == 0) + js_std_promise_rejection_check(ctx); + if (ts->can_js_os_poll) + js_os_poll(ctx); + } else { + /* not a promise */ + ret = obj; + break; + } + } + return ret; +} + +void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int load_only) +{ + JSValue obj, val; + obj = JS_ReadObject(ctx, buf, buf_len, JS_READ_OBJ_BYTECODE); + if (JS_IsException(obj)) + goto exception; + if (load_only) { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { + if (js_module_set_import_meta(ctx, obj, false, false) < 0) + goto exception; + } + JS_FreeValue(ctx, obj); + } else { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { + if (JS_ResolveModule(ctx, obj) < 0) { + JS_FreeValue(ctx, obj); + goto exception; + } + if (js_module_set_import_meta(ctx, obj, false, true) < 0) + goto exception; + val = JS_EvalFunction(ctx, obj); + val = js_std_await(ctx, val); + } else { + val = JS_EvalFunction(ctx, obj); + } + if (JS_IsException(val)) { + exception: + js_std_dump_error(ctx); + exit(1); + } + JS_FreeValue(ctx, val); + } +} + +static JSValue js_bjson_read(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + uint8_t *buf; + uint64_t pos, len; + JSValue obj; + size_t size; + int flags; + + if (JS_ToIndex(ctx, &pos, argv[1])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &len, argv[2])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &flags, argv[3])) + return JS_EXCEPTION; + buf = JS_GetArrayBuffer(ctx, &size, argv[0]); + if (!buf) + return JS_EXCEPTION; + if (pos + len > size) + return JS_ThrowRangeError(ctx, "array buffer overflow"); + obj = JS_ReadObject(ctx, buf + pos, len, flags); + return obj; +} + +static JSValue js_bjson_write(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + size_t len; + uint8_t *buf; + JSValue array; + int flags; + + if (JS_ToInt32(ctx, &flags, argv[1])) + return JS_EXCEPTION; + buf = JS_WriteObject(ctx, &len, argv[0], flags); + if (!buf) + return JS_EXCEPTION; + array = JS_NewArrayBufferCopy(ctx, buf, len); + js_free(ctx, buf); + return array; +} + + +static const JSCFunctionListEntry js_bjson_funcs[] = { + JS_CFUNC_DEF("read", 4, js_bjson_read ), + JS_CFUNC_DEF("write", 2, js_bjson_write ), +#define DEF(x) JS_PROP_INT32_DEF(#x, JS_##x, JS_PROP_CONFIGURABLE) + DEF(READ_OBJ_BYTECODE), + DEF(READ_OBJ_REFERENCE), + DEF(READ_OBJ_SAB), + DEF(WRITE_OBJ_BYTECODE), + DEF(WRITE_OBJ_REFERENCE), + DEF(WRITE_OBJ_SAB), + DEF(WRITE_OBJ_STRIP_DEBUG), + DEF(WRITE_OBJ_STRIP_SOURCE), +#undef DEF +}; + +static int js_bjson_init(JSContext *ctx, JSModuleDef *m) +{ + return JS_SetModuleExportList(ctx, m, js_bjson_funcs, + countof(js_bjson_funcs)); +} + +JSModuleDef *js_init_module_bjson(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_bjson_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_bjson_funcs, countof(js_bjson_funcs)); + return m; +} diff --git a/NativeScript/napi/android/quickjs/source_ng/quickjs-libc.h b/NativeScript/napi/android/quickjs/source_ng/quickjs-libc.h new file mode 100755 index 000000000..6af5a639d --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/quickjs-libc.h @@ -0,0 +1,76 @@ +/* + * QuickJS C library + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QUICKJS_LIBC_H +#define QUICKJS_LIBC_H + +#include +#include +#include + +#include "quickjs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define JS_EXTERN __attribute__((visibility("default"))) +#else +#define JS_EXTERN /* nothing */ +#endif + +JS_EXTERN JSModuleDef *js_init_module_std(JSContext *ctx, + const char *module_name); +JS_EXTERN JSModuleDef *js_init_module_os(JSContext *ctx, + const char *module_name); +JS_EXTERN JSModuleDef *js_init_module_bjson(JSContext *ctx, + const char *module_name); +JS_EXTERN void js_std_add_helpers(JSContext *ctx, int argc, char **argv); +JS_EXTERN int js_std_loop(JSContext *ctx); +JS_EXTERN JSValue js_std_await(JSContext *ctx, JSValue obj); +JS_EXTERN void js_std_init_handlers(JSRuntime *rt); +JS_EXTERN void js_std_free_handlers(JSRuntime *rt); +JS_EXTERN void js_std_dump_error(JSContext *ctx); +JS_EXTERN uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, + const char *filename); +JS_EXTERN int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, + bool use_realpath, bool is_main); +JS_EXTERN JSModuleDef *js_module_loader(JSContext *ctx, + const char *module_name, void *opaque); +JS_EXTERN void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, + size_t buf_len, int flags); +JS_EXTERN void js_std_promise_rejection_tracker(JSContext *ctx, + JSValueConst promise, + JSValueConst reason, + bool is_handled, + void *opaque); +JS_EXTERN void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); + +#undef JS_EXTERN + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* QUICKJS_LIBC_H */ diff --git a/NativeScript/napi/android/quickjs/source_ng/quickjs-opcode.h b/NativeScript/napi/android/quickjs/source_ng/quickjs-opcode.h new file mode 100755 index 000000000..bd5be754e --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/quickjs-opcode.h @@ -0,0 +1,371 @@ +/* + * QuickJS opcode definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef FMT +FMT(none) +FMT(none_int) +FMT(none_loc) +FMT(none_arg) +FMT(none_var_ref) +FMT(u8) +FMT(i8) +FMT(loc8) +FMT(const8) +FMT(label8) +FMT(u16) +FMT(i16) +FMT(label16) +FMT(npop) +FMT(npopx) +FMT(npop_u16) +FMT(loc) +FMT(arg) +FMT(var_ref) +FMT(u32) +FMT(u32x2) +FMT(i32) +FMT(const) +FMT(label) +FMT(atom) +FMT(atom_u8) +FMT(atom_u16) +FMT(atom_label_u8) +FMT(atom_label_u16) +FMT(label_u16) +#undef FMT +#endif /* FMT */ + +#ifdef DEF + +#ifndef def +#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) +#endif + +DEF(invalid, 1, 0, 0, none) /* never emitted */ + +/* push values */ +DEF( push_i32, 5, 0, 1, i32) +DEF( push_const, 5, 0, 1, const) +DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ +DEF(push_atom_value, 5, 0, 1, atom) +DEF( private_symbol, 5, 0, 1, atom) +DEF( undefined, 1, 0, 1, none) +DEF( null, 1, 0, 1, none) +DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ +DEF( push_false, 1, 0, 1, none) +DEF( push_true, 1, 0, 1, none) +DEF( object, 1, 0, 1, none) +DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */ +DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */ + +DEF( drop, 1, 1, 0, none) /* a -> */ +DEF( nip, 1, 2, 1, none) /* a b -> b */ +DEF( nip1, 1, 3, 2, none) /* a b c -> b c */ +DEF( dup, 1, 1, 2, none) /* a -> a a */ +DEF( dup1, 1, 2, 3, none) /* a b -> a a b */ +DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */ +DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ +DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ +DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ +DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ +DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */ +DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ +DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ +DEF( swap, 1, 2, 2, none) /* a b -> b a */ +DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */ +DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */ +DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */ +DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ +DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ + +DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */ +DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ +DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ +DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ +DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ +DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ +DEF( apply, 3, 3, 1, u16) +DEF( return, 1, 1, 0, none) +DEF( return_undef, 1, 0, 0, none) +DEF(check_ctor_return, 1, 1, 2, none) +DEF( check_ctor, 1, 0, 0, none) +DEF( init_ctor, 1, 0, 1, none) +DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ +DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ +DEF( return_async, 1, 1, 0, none) +DEF( throw, 1, 1, 0, none) +DEF( throw_error, 6, 0, 0, atom_u8) +DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ +DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ +DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a + bytecode string */ +DEF( get_super, 1, 1, 1, none) +DEF( import, 1, 1, 1, none) /* dynamic module import */ + +DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */ +DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */ +DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */ +DEF( put_var, 5, 1, 0, atom) /* must come after get_var */ +DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */ +DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */ + +DEF( get_ref_value, 1, 2, 3, none) +DEF( put_ref_value, 1, 3, 0, none) + +DEF( define_var, 6, 0, 0, atom_u8) +DEF(check_define_var, 6, 0, 0, atom_u8) +DEF( define_func, 6, 1, 0, atom_u8) + +// order matters, see IC counterparts +DEF( get_field, 5, 1, 1, atom) +DEF( get_field2, 5, 1, 2, atom) +DEF( put_field, 5, 2, 0, atom) + +DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */ +DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */ +DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ +DEF( get_array_el, 1, 2, 1, none) +DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ +DEF( put_array_el, 1, 3, 0, none) +DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ +DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ +DEF( define_field, 5, 2, 1, atom) +DEF( set_name, 5, 1, 1, atom) +DEF(set_name_computed, 1, 2, 2, none) +DEF( set_proto, 1, 2, 1, none) +DEF(set_home_object, 1, 2, 2, none) +DEF(define_array_el, 1, 3, 2, none) +DEF( append, 1, 3, 2, none) /* append enumerated object, update length */ +DEF(copy_data_properties, 2, 3, 3, u8) +DEF( define_method, 6, 2, 1, atom_u8) +DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ +DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */ +DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */ + +DEF( get_loc, 3, 0, 1, loc) +DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ +DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ +DEF( get_arg, 3, 0, 1, arg) +DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ +DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ +DEF( get_var_ref, 3, 0, 1, var_ref) +DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ +DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ +DEF(set_loc_uninitialized, 3, 0, 0, loc) +DEF( get_loc_check, 3, 0, 1, loc) +DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ +DEF( put_loc_check_init, 3, 1, 0, loc) +DEF(get_var_ref_check, 3, 0, 1, var_ref) +DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ +DEF(put_var_ref_check_init, 3, 1, 0, var_ref) +DEF( close_loc, 3, 0, 0, loc) +DEF( if_false, 5, 1, 0, label) +DEF( if_true, 5, 1, 0, label) /* must come after if_false */ +DEF( goto, 5, 0, 0, label) /* must come after if_true */ +DEF( catch, 5, 0, 1, label) +DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */ +DEF( ret, 1, 1, 0, none) /* used to return from the finally block */ +DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */ + +DEF( to_object, 1, 1, 1, none) +//DEF( to_string, 1, 1, 1, none) +DEF( to_propkey, 1, 1, 1, none) +DEF( to_propkey2, 1, 2, 2, none) + +DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) + +DEF( make_loc_ref, 7, 0, 2, atom_u16) +DEF( make_arg_ref, 7, 0, 2, atom_u16) +DEF(make_var_ref_ref, 7, 0, 2, atom_u16) +DEF( make_var_ref, 5, 0, 2, atom) + +DEF( for_in_start, 1, 1, 1, none) +DEF( for_of_start, 1, 1, 3, none) +DEF(for_await_of_start, 1, 1, 3, none) +DEF( for_in_next, 1, 1, 3, none) +DEF( for_of_next, 2, 3, 5, u8) +DEF(iterator_check_object, 1, 1, 1, none) +DEF(iterator_get_value_done, 1, 1, 2, none) +DEF( iterator_close, 1, 3, 0, none) +DEF( iterator_next, 1, 4, 4, none) +DEF( iterator_call, 2, 4, 5, u8) +DEF( initial_yield, 1, 0, 0, none) +DEF( yield, 1, 1, 2, none) +DEF( yield_star, 1, 1, 2, none) +DEF(async_yield_star, 1, 1, 2, none) +DEF( await, 1, 1, 1, none) + +/* arithmetic/logic operations */ +DEF( neg, 1, 1, 1, none) +DEF( plus, 1, 1, 1, none) +DEF( dec, 1, 1, 1, none) +DEF( inc, 1, 1, 1, none) +DEF( post_dec, 1, 1, 2, none) +DEF( post_inc, 1, 1, 2, none) +DEF( dec_loc, 2, 0, 0, loc8) +DEF( inc_loc, 2, 0, 0, loc8) +DEF( add_loc, 2, 1, 0, loc8) +DEF( not, 1, 1, 1, none) +DEF( lnot, 1, 1, 1, none) +DEF( typeof, 1, 1, 1, none) +DEF( delete, 1, 2, 1, none) +DEF( delete_var, 5, 0, 1, atom) + +/* warning: order matters (see js_parse_assign_expr) */ +DEF( mul, 1, 2, 1, none) +DEF( div, 1, 2, 1, none) +DEF( mod, 1, 2, 1, none) +DEF( add, 1, 2, 1, none) +DEF( sub, 1, 2, 1, none) +DEF( shl, 1, 2, 1, none) +DEF( sar, 1, 2, 1, none) +DEF( shr, 1, 2, 1, none) +DEF( and, 1, 2, 1, none) +DEF( xor, 1, 2, 1, none) +DEF( or, 1, 2, 1, none) +DEF( pow, 1, 2, 1, none) + +DEF( lt, 1, 2, 1, none) +DEF( lte, 1, 2, 1, none) +DEF( gt, 1, 2, 1, none) +DEF( gte, 1, 2, 1, none) +DEF( instanceof, 1, 2, 1, none) +DEF( in, 1, 2, 1, none) +DEF( eq, 1, 2, 1, none) +DEF( neq, 1, 2, 1, none) +DEF( strict_eq, 1, 2, 1, none) +DEF( strict_neq, 1, 2, 1, none) +DEF(is_undefined_or_null, 1, 1, 1, none) +DEF( private_in, 1, 2, 1, none) +DEF(push_bigint_i32, 5, 0, 1, i32) +/* must be the last non short and non temporary opcode */ +DEF( nop, 1, 0, 0, none) + +/* temporary opcodes: never emitted in the final bytecode */ + +def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ +def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ + +def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ + +def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ +def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ +def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */ +def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */ +def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */ +def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ + +def( source_loc, 9, 0, 0, u32x2) /* emitted in phase 1, removed in phase 3 */ + +DEF( push_minus1, 1, 0, 1, none_int) +DEF( push_0, 1, 0, 1, none_int) +DEF( push_1, 1, 0, 1, none_int) +DEF( push_2, 1, 0, 1, none_int) +DEF( push_3, 1, 0, 1, none_int) +DEF( push_4, 1, 0, 1, none_int) +DEF( push_5, 1, 0, 1, none_int) +DEF( push_6, 1, 0, 1, none_int) +DEF( push_7, 1, 0, 1, none_int) +DEF( push_i8, 2, 0, 1, i8) +DEF( push_i16, 3, 0, 1, i16) +DEF( push_const8, 2, 0, 1, const8) +DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ +DEF(push_empty_string, 1, 0, 1, none) + +DEF( get_loc8, 2, 0, 1, loc8) +DEF( put_loc8, 2, 1, 0, loc8) +DEF( set_loc8, 2, 1, 1, loc8) + +DEF( get_loc0_loc1, 1, 0, 2, none_loc) +DEF( get_loc0, 1, 0, 1, none_loc) +DEF( get_loc1, 1, 0, 1, none_loc) +DEF( get_loc2, 1, 0, 1, none_loc) +DEF( get_loc3, 1, 0, 1, none_loc) +DEF( put_loc0, 1, 1, 0, none_loc) +DEF( put_loc1, 1, 1, 0, none_loc) +DEF( put_loc2, 1, 1, 0, none_loc) +DEF( put_loc3, 1, 1, 0, none_loc) +DEF( set_loc0, 1, 1, 1, none_loc) +DEF( set_loc1, 1, 1, 1, none_loc) +DEF( set_loc2, 1, 1, 1, none_loc) +DEF( set_loc3, 1, 1, 1, none_loc) +DEF( get_arg0, 1, 0, 1, none_arg) +DEF( get_arg1, 1, 0, 1, none_arg) +DEF( get_arg2, 1, 0, 1, none_arg) +DEF( get_arg3, 1, 0, 1, none_arg) +DEF( put_arg0, 1, 1, 0, none_arg) +DEF( put_arg1, 1, 1, 0, none_arg) +DEF( put_arg2, 1, 1, 0, none_arg) +DEF( put_arg3, 1, 1, 0, none_arg) +DEF( set_arg0, 1, 1, 1, none_arg) +DEF( set_arg1, 1, 1, 1, none_arg) +DEF( set_arg2, 1, 1, 1, none_arg) +DEF( set_arg3, 1, 1, 1, none_arg) +DEF( get_var_ref0, 1, 0, 1, none_var_ref) +DEF( get_var_ref1, 1, 0, 1, none_var_ref) +DEF( get_var_ref2, 1, 0, 1, none_var_ref) +DEF( get_var_ref3, 1, 0, 1, none_var_ref) +DEF( put_var_ref0, 1, 1, 0, none_var_ref) +DEF( put_var_ref1, 1, 1, 0, none_var_ref) +DEF( put_var_ref2, 1, 1, 0, none_var_ref) +DEF( put_var_ref3, 1, 1, 0, none_var_ref) +DEF( set_var_ref0, 1, 1, 1, none_var_ref) +DEF( set_var_ref1, 1, 1, 1, none_var_ref) +DEF( set_var_ref2, 1, 1, 1, none_var_ref) +DEF( set_var_ref3, 1, 1, 1, none_var_ref) + +DEF( get_length, 1, 1, 1, none) + +DEF( if_false8, 2, 1, 0, label8) +DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */ +DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */ +DEF( goto16, 3, 0, 0, label16) + +DEF( call0, 1, 1, 1, npopx) +DEF( call1, 1, 1, 1, npopx) +DEF( call2, 1, 1, 1, npopx) +DEF( call3, 1, 1, 1, npopx) + +DEF( is_undefined, 1, 1, 1, none) +DEF( is_null, 1, 1, 1, none) +DEF(typeof_is_undefined, 1, 1, 1, none) +DEF( typeof_is_function, 1, 1, 1, none) + +#undef DEF +#undef def +#endif /* DEF */ diff --git a/NativeScript/napi/android/quickjs/source_ng/quickjs.c b/NativeScript/napi/android/quickjs/source_ng/quickjs.c new file mode 100755 index 000000000..1a3174e8f --- /dev/null +++ b/NativeScript/napi/android/quickjs/source_ng/quickjs.c @@ -0,0 +1,58355 @@ +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon + * Copyright (c) 2023-2025 Ben Noordhuis + * Copyright (c) 2023-2025 Saúl Ibarra Corretgé + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#if defined(_WIN32) +#include +#endif +#endif +#if defined(_WIN32) +#include +#endif +#include +#include +#include + +#include "cutils.h" +#include "list.h" +#include "quickjs.h" +#include "libregexp.h" +#include "dtoa.h" + +#if defined(EMSCRIPTEN) || defined(_MSC_VER) +#define DIRECT_DISPATCH 0 +#else +#define DIRECT_DISPATCH 1 +#endif + +#if defined(__APPLE__) +#define MALLOC_OVERHEAD 0 +#else +#define MALLOC_OVERHEAD 8 +#endif + +#if defined(__NEWLIB__) +#define NO_TM_GMTOFF +#endif + +// atomic_store etc. are completely busted in recent versions of tcc; +// somehow the compiler forgets to load |ptr| into %rdi when calling +// the __atomic_*() helpers in its lib/stdatomic.c and lib/atomic.S +#if !defined(__TINYC__) && !defined(EMSCRIPTEN) && !defined(__wasi__) && !__STDC_NO_ATOMICS__ +#include "quickjs-c-atomics.h" +#define CONFIG_ATOMICS +#endif + +#ifndef __GNUC__ +#define __extension__ +#endif + +#ifndef NDEBUG +#define ENABLE_DUMPS +#endif + +//#define FORCE_GC_AT_MALLOC /* test the GC by forcing it before each object allocation */ + +#define check_dump_flag(rt, flag) ((rt->dump_flags & (flag +0)) == (flag +0)) + +#define STRINGIFY_(x) #x +#define STRINGIFY(x) STRINGIFY_(x) + +#define QJS_VERSION_STRING \ + STRINGIFY(QJS_VERSION_MAJOR) "." STRINGIFY(QJS_VERSION_MINOR) "." STRINGIFY(QJS_VERSION_PATCH) QJS_VERSION_SUFFIX + +const char* JS_GetVersion(void) { + return QJS_VERSION_STRING; +} + +#undef STRINFIGY_ +#undef STRINGIFY + +static inline JSValueConst *vc(JSValue *vals) +{ + return (JSValueConst *)vals; +} + +static inline JSValue unsafe_unconst(JSValueConst v) +{ +#ifdef JS_CHECK_JSVALUE + return (JSValue)v; +#else + return v; +#endif +} + +static inline JSValueConst safe_const(JSValue v) +{ +#ifdef JS_CHECK_JSVALUE + return (JSValueConst)v; +#else + return v; +#endif +} + +enum { + /* classid tag */ /* union usage | properties */ + JS_CLASS_OBJECT = 1, /* must be first */ + JS_CLASS_ARRAY, /* u.array | length */ + JS_CLASS_ERROR, + JS_CLASS_NUMBER, /* u.object_data */ + JS_CLASS_STRING, /* u.object_data */ + JS_CLASS_BOOLEAN, /* u.object_data */ + JS_CLASS_SYMBOL, /* u.object_data */ + JS_CLASS_ARGUMENTS, /* u.array | length */ + JS_CLASS_MAPPED_ARGUMENTS, /* | length */ + JS_CLASS_DATE, /* u.object_data */ + JS_CLASS_MODULE_NS, + JS_CLASS_C_FUNCTION, /* u.cfunc */ + JS_CLASS_BYTECODE_FUNCTION, /* u.func */ + JS_CLASS_BOUND_FUNCTION, /* u.bound_function */ + JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */ + JS_CLASS_GENERATOR_FUNCTION, /* u.func */ + JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */ + JS_CLASS_REGEXP, /* u.regexp */ + JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_DATAVIEW, /* u.typed_array */ + JS_CLASS_BIG_INT, /* u.object_data */ + JS_CLASS_MAP, /* u.map_state */ + JS_CLASS_SET, /* u.map_state */ + JS_CLASS_WEAKMAP, /* u.map_state */ + JS_CLASS_WEAKSET, /* u.map_state */ + JS_CLASS_ITERATOR, + JS_CLASS_ITERATOR_CONCAT, /* u.iterator_concat_data */ + JS_CLASS_ITERATOR_HELPER, /* u.iterator_helper_data */ + JS_CLASS_ITERATOR_WRAP, /* u.iterator_wrap_data */ + JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ + JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */ + JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */ + JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */ + JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */ + JS_CLASS_GENERATOR, /* u.generator_data */ + JS_CLASS_PROXY, /* u.proxy_data */ + JS_CLASS_PROMISE, /* u.promise_data */ + JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */ + JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */ + JS_CLASS_ASYNC_FUNCTION, /* u.func */ + JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */ + JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */ + JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */ + JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */ + JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ + JS_CLASS_WEAK_REF, + JS_CLASS_FINALIZATION_REGISTRY, + JS_CLASS_DOM_EXCEPTION, + JS_CLASS_CALL_SITE, + + JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ +}; + +/* number of typed array types */ +#define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1) +static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT]; +#define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY]) + +typedef enum JSErrorEnum { + JS_EVAL_ERROR, + JS_RANGE_ERROR, + JS_REFERENCE_ERROR, + JS_SYNTAX_ERROR, + JS_TYPE_ERROR, + JS_URI_ERROR, + JS_INTERNAL_ERROR, + JS_AGGREGATE_ERROR, + + JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ + JS_PLAIN_ERROR = JS_NATIVE_ERROR_COUNT +} JSErrorEnum; + +#define JS_MAX_LOCAL_VARS 65535 +#define JS_STACK_SIZE_MAX 65534 +#define JS_STRING_LEN_MAX ((1 << 30) - 1) +// 1,024 bytes is about the cutoff point where it starts getting +// more profitable to ref slice than to copy +#define JS_STRING_SLICE_LEN_MAX 1024 // in bytes + +#define __exception __attribute__((warn_unused_result)) + +typedef struct JSShape JSShape; +typedef struct JSString JSString; +typedef struct JSString JSAtomStruct; + +#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v)) + +typedef enum { + JS_GC_PHASE_NONE, + JS_GC_PHASE_DECREF, + JS_GC_PHASE_REMOVE_CYCLES, +} JSGCPhaseEnum; + +typedef struct JSMallocState { + size_t malloc_count; + size_t malloc_size; + size_t malloc_limit; + void *opaque; /* user opaque */ +} JSMallocState; + +typedef struct JSRuntimeFinalizerState { + struct JSRuntimeFinalizerState *next; + JSRuntimeFinalizer *finalizer; + void *arg; +} JSRuntimeFinalizerState; + +typedef struct JSValueLink { + struct JSValueLink *next; + JSValueConst value; +} JSValueLink; + +struct JSRuntime { + JSMallocFunctions mf; + JSMallocState malloc_state; + const char *rt_info; + + int atom_hash_size; /* power of two */ + int atom_count; + int atom_size; + int atom_count_resize; /* resize hash table at this count */ + uint32_t *atom_hash; + JSAtomStruct **atom_array; + int atom_free_index; /* 0 = none */ + + JSClassID js_class_id_alloc; /* counter for user defined classes */ + int class_count; /* size of class_array */ + JSClass *class_array; + + struct list_head context_list; /* list of JSContext.link */ + /* list of JSGCObjectHeader.link. List of allocated GC objects (used + by the garbage collector) */ + struct list_head gc_obj_list; + /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */ + struct list_head gc_zero_ref_count_list; + struct list_head tmp_obj_list; /* used during GC */ + JSGCPhaseEnum gc_phase : 8; + size_t malloc_gc_threshold; +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + struct list_head string_list; /* list of JSString.link */ +#endif + /* stack limitation */ + uintptr_t stack_size; /* in bytes, 0 if no limit */ + uintptr_t stack_top; + uintptr_t stack_limit; /* lower stack limit */ + + JSValue current_exception; + /* true if inside an out of memory error, to avoid recursing */ + bool in_out_of_memory; + /* true if inside build_backtrace, to avoid recursing */ + bool in_build_stack_trace; + /* true if inside JS_FreeRuntime */ + bool in_free; + + struct JSStackFrame *current_stack_frame; + + JSInterruptHandler *interrupt_handler; + void *interrupt_opaque; + + JSPromiseHook *promise_hook; + void *promise_hook_opaque; + // for smuggling the parent promise from js_promise_then + // to js_promise_constructor + JSValueLink *parent_promise; + + JSHostPromiseRejectionTracker *host_promise_rejection_tracker; + void *host_promise_rejection_tracker_opaque; + + struct list_head job_list; /* list of JSJobEntry.link */ + struct list_head weakref_keepalive_list; /* strong refs kept until the current host job completes */ + + JSModuleNormalizeFunc *module_normalize_func; + JSModuleLoaderFunc *module_loader_func; + void *module_loader_opaque; + /* timestamp for internal use in module evaluation */ + int64_t module_async_evaluation_next_timestamp; + + /* used to allocate, free and clone SharedArrayBuffers */ + JSSharedArrayBufferFunctions sab_funcs; + + bool can_block; /* true if Atomics.wait can block */ + uint32_t dump_flags : 24; + + /* Shape hash table */ + int shape_hash_bits; + int shape_hash_size; + int shape_hash_count; /* number of hashed shapes */ + JSShape **shape_hash; + void *user_opaque; + void *libc_opaque; + JSRuntimeFinalizerState *finalizers; +}; + +struct JSClass { + uint32_t class_id; /* 0 means free entry */ + JSAtom class_name; + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; + JSClassCall *call; + /* pointers for exotic behavior, can be NULL if none are present */ + const JSClassExoticMethods *exotic; +}; + +typedef struct JSStackFrame { + struct JSStackFrame *prev_frame; /* NULL if first stack frame */ + JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */ + JSValue *arg_buf; /* arguments */ + JSValue *var_buf; /* variables */ + struct list_head var_ref_list; /* list of JSVarRef.link */ + uint8_t *cur_pc; /* only used in bytecode functions : PC of the + instruction after the call */ + uint32_t arg_count : 31; + uint32_t is_strict_mode : 1; + /* only used in generators. Current stack pointer value. NULL if + the function is running. */ + JSValue *cur_sp; +} JSStackFrame; + +typedef struct JSWeakRefKeepAliveEntry { + struct list_head link; + JSValue value; +} JSWeakRefKeepAliveEntry; + +typedef enum { + JS_GC_OBJ_TYPE_JS_OBJECT, + JS_GC_OBJ_TYPE_FUNCTION_BYTECODE, + JS_GC_OBJ_TYPE_SHAPE, + JS_GC_OBJ_TYPE_VAR_REF, + JS_GC_OBJ_TYPE_ASYNC_FUNCTION, + JS_GC_OBJ_TYPE_JS_CONTEXT, +} JSGCObjectTypeEnum; + +/* header for GC objects. GC objects are C data structures with a + reference count that can reference other GC objects. JS Objects are + a particular type of GC object. */ +struct JSGCObjectHeader { + int ref_count; /* must come first, 32-bit */ + JSGCObjectTypeEnum gc_obj_type : 4; + uint8_t mark : 4; /* used by the GC */ + uint8_t dummy1; /* not used by the GC */ + uint16_t dummy2; /* not used by the GC */ + struct list_head link; +}; + +typedef struct JSVarRef { + union { + JSGCObjectHeader header; /* must come first */ + struct { + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ + uint8_t is_detached; + }; + }; + JSValue *pvalue; /* pointer to the value, either on the stack or + to 'value' */ + JSValue value; /* used when the variable is no longer on the stack */ +} JSVarRef; + +typedef struct JSRefCountHeader { + int ref_count; +} JSRefCountHeader; + +/* bigint */ +typedef int32_t js_slimb_t; +typedef uint32_t js_limb_t; +typedef int64_t js_sdlimb_t; +typedef uint64_t js_dlimb_t; + +#define JS_LIMB_DIGITS 9 + +/* Must match the size of short_big_int in JSValueUnion */ +#define JS_LIMB_BITS 32 +#define JS_SHORT_BIG_INT_BITS JS_LIMB_BITS +#define JS_BIGINT_MAX_SIZE ((1024 * 1024) / JS_LIMB_BITS) /* in limbs */ +#define JS_SHORT_BIG_INT_MIN INT32_MIN +#define JS_SHORT_BIG_INT_MAX INT32_MAX + + +typedef struct JSBigInt { + JSRefCountHeader header; /* must come first, 32-bit */ + uint32_t len; /* number of limbs, >= 1 */ + js_limb_t tab[]; /* two's complement representation, always + normalized so that 'len' is the minimum + possible length >= 1 */ +} JSBigInt; + +/* this bigint structure can hold a 64 bit integer */ +typedef struct { + js_limb_t big_int_buf[sizeof(JSBigInt) / sizeof(js_limb_t)]; /* for JSBigInt */ + /* must come just after */ + js_limb_t tab[(64 + JS_LIMB_BITS - 1) / JS_LIMB_BITS]; +} JSBigIntBuf; + +typedef enum { + JS_AUTOINIT_ID_PROTOTYPE, + JS_AUTOINIT_ID_MODULE_NS, + JS_AUTOINIT_ID_PROP, + JS_AUTOINIT_ID_BYTECODE, +} JSAutoInitIDEnum; + +enum { + JS_BUILTIN_ARRAY_FROMASYNC = 1, +}; + +/* must be large enough to have a negligible runtime cost and small + enough to call the interrupt callback often. */ +#define JS_INTERRUPT_COUNTER_INIT 10000 + +struct JSContext { + JSGCObjectHeader header; /* must come first */ + JSRuntime *rt; + struct list_head link; + + uint16_t binary_object_count; + int binary_object_size; + + JSShape *array_shape; /* initial shape for Array objects */ + + JSValue *class_proto; + JSValue function_proto; + JSValue function_ctor; + JSValue array_ctor; + JSValue regexp_ctor; + JSValue promise_ctor; + JSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; + JSValue error_ctor; + JSValue error_back_trace; + JSValue error_prepare_stack; + JSValue error_stack_trace_limit; + JSValue iterator_ctor; + JSValue iterator_ctor_getset; + JSValue iterator_proto; + JSValue async_iterator_proto; + JSValue array_proto_values; + JSValue throw_type_error; + JSValue eval_obj; + + JSValue global_obj; /* global object */ + JSValue global_var_obj; /* contains the global let/const definitions */ + + double time_origin; + + uint64_t random_state; + + /* when the counter reaches zero, JSRutime.interrupt_handler is called */ + int interrupt_counter; + + struct list_head loaded_modules; /* list of JSModuleDef.link */ + + /* if NULL, RegExp compilation is not supported */ + JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern, + JSValueConst flags); + /* if NULL, eval is not supported */ + JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + const char *filename, int line, int flags, int scope_idx); + void *user_opaque; +}; + +typedef union JSFloat64Union { + double d; + uint64_t u64; + uint32_t u32[2]; +} JSFloat64Union; + +typedef enum { + JS_WEAK_REF_KIND_MAP, + JS_WEAK_REF_KIND_WEAK_REF, + JS_WEAK_REF_KIND_FINALIZATION_REGISTRY_ENTRY, +} JSWeakRefKindEnum; + +typedef struct JSWeakRefRecord { + JSWeakRefKindEnum kind; + struct JSWeakRefRecord *next_weak_ref; + union { + struct JSMapRecord *map_record; + struct JSWeakRefData *weak_ref_data; + struct JSFinRecEntry *fin_rec_entry; + } u; +} JSWeakRefRecord; + +typedef struct JSMapRecord { + int ref_count; /* used during enumeration to avoid freeing the record */ + bool empty; /* true if the record is deleted */ + struct JSMapState *map; + struct list_head link; + struct list_head hash_link; + JSValue key; + JSValue value; +} JSMapRecord; + +typedef struct JSMapState { + bool is_weak; /* true if WeakSet/WeakMap */ + struct list_head records; /* list of JSMapRecord.link */ + uint32_t record_count; + struct list_head *hash_table; + uint32_t hash_size; /* must be a power of two */ + uint32_t record_count_threshold; /* count at which a hash table + resize is needed */ +} JSMapState; + +enum +{ + JS_TO_STRING_IS_PROPERTY_KEY = 1 << 0, + JS_TO_STRING_NO_SIDE_EFFECTS = 1 << 1, +}; + +enum { + JS_ATOM_TYPE_STRING = 1, + JS_ATOM_TYPE_GLOBAL_SYMBOL, + JS_ATOM_TYPE_SYMBOL, + JS_ATOM_TYPE_PRIVATE, +}; + +enum { + JS_ATOM_HASH_SYMBOL, + JS_ATOM_HASH_PRIVATE, +}; + +typedef enum { + JS_ATOM_KIND_STRING, + JS_ATOM_KIND_SYMBOL, + JS_ATOM_KIND_PRIVATE, +} JSAtomKindEnum; + +typedef enum { + JS_STRING_KIND_NORMAL, + JS_STRING_KIND_SLICE, +} JSStringKind; + +#define JS_ATOM_HASH_MASK ((1 << 29) - 1) + +struct JSString { + JSRefCountHeader header; /* must come first, 32-bit */ + uint32_t len : 31; + uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ + /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, + for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3 + XXX: could change encoding to have one more bit in hash */ + uint32_t hash : 29; + uint8_t kind : 1; + uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ + uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ + JSWeakRefRecord *first_weak_ref; +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + struct list_head link; /* string list */ +#endif +}; + +typedef struct JSStringSlice { + JSString *parent; + uint32_t start; // in bytes, not characters +} JSStringSlice; + +static inline void *strv(JSString *p) +{ + JSStringSlice *slice; + + switch (p->kind) { + case JS_STRING_KIND_NORMAL: + return (void *)&p[1]; + case JS_STRING_KIND_SLICE: + slice = (void *)&p[1]; + return (char *)&slice->parent[1] + slice->start; + } + abort(); + return NULL; +} + +static inline uint8_t *str8(JSString *p) +{ + return strv(p); +} + +static inline uint16_t *str16(JSString *p) +{ + return strv(p); +} + +typedef struct JSClosureVar { + uint8_t is_local : 1; + uint8_t is_arg : 1; + uint8_t is_const : 1; + uint8_t is_lexical : 1; + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* 8 bits available */ + uint16_t var_idx; /* is_local = true: index to a normal variable of the + parent function. otherwise: index to a closure + variable of the parent function */ + JSAtom var_name; +} JSClosureVar; + +#define ARG_SCOPE_INDEX 1 +#define ARG_SCOPE_END (-2) + +typedef struct JSVarScope { + int parent; /* index into fd->scopes of the enclosing scope */ + int first; /* index into fd->vars of the last variable in this scope */ +} JSVarScope; + +typedef enum { + /* XXX: add more variable kinds here instead of using bit fields */ + JS_VAR_NORMAL, + JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */ + JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator + function declaration */ + JS_VAR_CATCH, + JS_VAR_FUNCTION_NAME, /* function expression name */ + JS_VAR_PRIVATE_FIELD, + JS_VAR_PRIVATE_METHOD, + JS_VAR_PRIVATE_GETTER, + JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */ + JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */ +} JSVarKindEnum; + +/* XXX: could use a different structure in bytecode functions to save + memory */ +typedef struct JSVarDef { + JSAtom var_name; + /* index into fd->scopes of this variable lexical scope */ + int scope_level; + /* during compilation: + - if scope_level = 0: scope in which the variable is defined + - if scope_level != 0: index into fd->vars of the next + variable in the same or enclosing lexical scope + in a bytecode function: + index into fd->vars of the next + variable in the same or enclosing lexical scope + */ + int scope_next; + uint8_t is_const : 1; + uint8_t is_lexical : 1; + uint8_t is_captured : 1; + uint8_t is_static_private : 1; /* only used during private class field parsing */ + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* only used during compilation: function pool index for lexical + variables with var_kind = + JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of + the definition of the 'var' variables (they have scope_level = + 0) */ + int func_pool_idx : 24; /* only used during compilation : index in + the constant pool for hoisted function + definition */ +} JSVarDef; + +/* for the encoding of the pc2line table */ +#define PC2LINE_BASE (-1) +#define PC2LINE_RANGE 5 +#define PC2LINE_OP_FIRST 1 +#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE) + +typedef enum JSFunctionKindEnum { + JS_FUNC_NORMAL = 0, + JS_FUNC_GENERATOR = (1 << 0), + JS_FUNC_ASYNC = (1 << 1), + JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), +} JSFunctionKindEnum; + +typedef struct JSFunctionBytecode { + JSGCObjectHeader header; /* must come first */ + uint8_t is_strict_mode : 1; + uint8_t has_prototype : 1; /* true if a prototype field is necessary */ + uint8_t has_simple_parameter_list : 1; + uint8_t is_derived_class_constructor : 1; + /* true if home_object needs to be initialized */ + uint8_t need_home_object : 1; + uint8_t func_kind : 2; + uint8_t new_target_allowed : 1; + uint8_t super_call_allowed : 1; + uint8_t super_allowed : 1; + uint8_t arguments_allowed : 1; + uint8_t backtrace_barrier : 1; /* stop backtrace on this function */ + /* XXX: 5 bits available */ + uint8_t *byte_code_buf; /* (self pointer) */ + int byte_code_len; + JSAtom func_name; + JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */ + JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */ + uint16_t arg_count; + uint16_t var_count; + uint16_t defined_arg_count; /* for length function property */ + uint16_t stack_size; /* maximum stack size */ + JSContext *realm; /* function realm */ + JSValue *cpool; /* constant pool (self pointer) */ + int cpool_count; + int closure_var_count; + JSAtom filename; + int line_num; + int col_num; + int source_len; + int pc2line_len; + uint8_t *pc2line_buf; + char *source; +} JSFunctionBytecode; + +typedef struct JSBoundFunction { + JSValue func_obj; + JSValue this_val; + int argc; + JSValue argv[]; +} JSBoundFunction; + +typedef enum JSIteratorKindEnum { + JS_ITERATOR_KIND_KEY, + JS_ITERATOR_KIND_VALUE, + JS_ITERATOR_KIND_KEY_AND_VALUE, +} JSIteratorKindEnum; + +typedef enum JSIteratorHelperKindEnum { + JS_ITERATOR_HELPER_KIND_DROP, + JS_ITERATOR_HELPER_KIND_EVERY, + JS_ITERATOR_HELPER_KIND_FILTER, + JS_ITERATOR_HELPER_KIND_FIND, + JS_ITERATOR_HELPER_KIND_FLAT_MAP, + JS_ITERATOR_HELPER_KIND_FOR_EACH, + JS_ITERATOR_HELPER_KIND_MAP, + JS_ITERATOR_HELPER_KIND_SOME, + JS_ITERATOR_HELPER_KIND_TAKE, +} JSIteratorHelperKindEnum; + +typedef struct JSForInIterator { + JSValue obj; + bool is_array; + uint32_t array_length; + uint32_t idx; +} JSForInIterator; + +typedef struct JSRegExp { + JSString *pattern; + JSString *bytecode; /* also contains the flags */ +} JSRegExp; + +typedef struct JSProxyData { + JSValue target; + JSValue handler; + uint8_t is_func; + uint8_t is_revoked; +} JSProxyData; + +typedef struct JSArrayBuffer { + int byte_length; /* 0 if detached */ + int max_byte_length; /* -1 if not resizable; >= byte_length otherwise */ + uint8_t detached; + uint8_t shared; /* if shared, the array buffer cannot be detached */ + uint8_t *data; /* NULL if detached */ + struct list_head array_list; + void *opaque; + JSFreeArrayBufferDataFunc *free_func; +} JSArrayBuffer; + +typedef struct JSTypedArray { + struct list_head link; /* link to arraybuffer */ + JSObject *obj; /* back pointer to the TypedArray/DataView object */ + JSObject *buffer; /* based array buffer */ + uint32_t offset; /* byte offset in the array buffer */ + uint32_t length; /* byte length in the array buffer */ + bool track_rab; /* auto-track length of backing array buffer */ +} JSTypedArray; + +typedef struct JSAsyncFunctionState { + JSValue this_val; /* 'this' generator argument */ + int argc; /* number of function arguments */ + bool throw_flag; /* used to throw an exception in JS_CallInternal() */ + JSStackFrame frame; +} JSAsyncFunctionState; + +/* XXX: could use an object instead to avoid the + JS_TAG_ASYNC_FUNCTION tag for the GC */ +typedef struct JSAsyncFunctionData { + JSGCObjectHeader header; /* must come first */ + JSValue resolving_funcs[2]; + bool is_active; /* true if the async function state is valid */ + JSAsyncFunctionState func_state; +} JSAsyncFunctionData; + +typedef struct JSReqModuleEntry { + JSAtom module_name; + JSModuleDef *module; /* used using resolution */ +} JSReqModuleEntry; + +typedef enum JSExportTypeEnum { + JS_EXPORT_TYPE_LOCAL, + JS_EXPORT_TYPE_INDIRECT, +} JSExportTypeEnum; + +typedef struct JSExportEntry { + union { + struct { + int var_idx; /* closure variable index */ + JSVarRef *var_ref; /* if != NULL, reference to the variable */ + } local; /* for local export */ + int req_module_idx; /* module for indirect export */ + } u; + JSExportTypeEnum export_type; + JSAtom local_name; /* '*' if export ns from. not used for local + export after compilation */ + JSAtom export_name; /* exported variable name */ +} JSExportEntry; + +typedef struct JSStarExportEntry { + int req_module_idx; /* in req_module_entries */ +} JSStarExportEntry; + +typedef struct JSImportEntry { + int var_idx; /* closure variable index */ + JSAtom import_name; + int req_module_idx; /* in req_module_entries */ +} JSImportEntry; + +typedef enum { + JS_MODULE_STATUS_UNLINKED, + JS_MODULE_STATUS_LINKING, + JS_MODULE_STATUS_LINKED, + JS_MODULE_STATUS_EVALUATING, + JS_MODULE_STATUS_EVALUATING_ASYNC, + JS_MODULE_STATUS_EVALUATED, +} JSModuleStatus; + +struct JSModuleDef { + JSRefCountHeader header; /* must come first, 32-bit */ + JSAtom module_name; + struct list_head link; + + JSReqModuleEntry *req_module_entries; + int req_module_entries_count; + int req_module_entries_size; + + JSExportEntry *export_entries; + int export_entries_count; + int export_entries_size; + + JSStarExportEntry *star_export_entries; + int star_export_entries_count; + int star_export_entries_size; + + JSImportEntry *import_entries; + int import_entries_count; + int import_entries_size; + + JSValue module_ns; + JSValue func_obj; /* only used for JS modules */ + JSModuleInitFunc *init_func; /* only used for C modules */ + bool has_tla; /* true if func_obj contains await */ + bool resolved; + bool func_created; + JSModuleStatus status : 8; + /* temp use during js_module_link() & js_module_evaluate() */ + int dfs_index, dfs_ancestor_index; + JSModuleDef *stack_prev; + /* temp use during js_module_evaluate() */ + JSModuleDef **async_parent_modules; + int async_parent_modules_count; + int async_parent_modules_size; + int pending_async_dependencies; + bool async_evaluation; + int64_t async_evaluation_timestamp; + JSModuleDef *cycle_root; + JSValue promise; /* corresponds to spec field: capability */ + JSValue resolving_funcs[2]; /* corresponds to spec field: capability */ + /* true if evaluation yielded an exception. It is saved in + eval_exception */ + bool eval_has_exception; + JSValue eval_exception; + JSValue meta_obj; /* for import.meta */ +}; + +typedef struct JSJobEntry { + struct list_head link; + JSContext *ctx; + JSJobFunc *job_func; + int argc; + JSValue argv[]; +} JSJobEntry; + +typedef struct JSProperty { + union { + JSValue value; /* JS_PROP_NORMAL */ + struct { /* JS_PROP_GETSET */ + JSObject *getter; /* NULL if undefined */ + JSObject *setter; /* NULL if undefined */ + } getset; + JSVarRef *var_ref; /* JS_PROP_VARREF */ + struct { /* JS_PROP_AUTOINIT */ + /* in order to use only 2 pointers, we compress the realm + and the init function pointer */ + uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x) + in the 2 low bits */ + void *opaque; + } init; + } u; +} JSProperty; + +#define JS_PROP_INITIAL_SIZE 2 +#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ +#define JS_ARRAY_INITIAL_SIZE 2 + +typedef struct JSShapeProperty { + uint32_t hash_next : 26; /* 0 if last in list */ + uint32_t flags : 6; /* JS_PROP_XXX */ + JSAtom atom; /* JS_ATOM_NULL = free property entry */ +} JSShapeProperty; + +struct JSShape { + /* hash table of size hash_mask + 1 before the start of the + structure (see prop_hash_end()). */ + JSGCObjectHeader header; + /* true if the shape is inserted in the shape hash table. If not, + JSShape.hash is not valid */ + uint8_t is_hashed; + /* If true, the shape may have small array index properties 'n' with 0 + <= n <= 2^31-1. If false, the shape is guaranteed not to have + small array index properties */ + uint8_t has_small_array_index; + uint32_t hash; /* current hash value */ + uint32_t prop_hash_mask; + int prop_size; /* allocated properties */ + int prop_count; /* include deleted properties */ + int deleted_prop_count; + JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ + JSObject *proto; + JSShapeProperty prop[]; /* prop_size elements */ +}; + +struct JSObject { + union { + JSGCObjectHeader header; + struct { + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ + + uint8_t extensible : 1; + uint8_t free_mark : 1; /* only used when freeing objects with cycles */ + uint8_t is_exotic : 1; /* true if object has exotic property handlers */ + uint8_t fast_array : 1; /* true if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */ + uint8_t is_constructor : 1; /* true if object is a constructor function */ + uint8_t is_uncatchable_error : 1; /* if true, error is not catchable */ + uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ + uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ + uint16_t class_id; /* see JS_CLASS_x */ + }; + }; + /* byte offsets: 16/24 */ + JSShape *shape; /* prototype and property names + flag */ + JSProperty *prop; /* array of properties */ + /* byte offsets: 24/40 */ + JSWeakRefRecord *first_weak_ref; + /* byte offsets: 28/48 */ + union { + void *opaque; + struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ + struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ + struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ + struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */ + struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ + struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ + struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ + struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ + struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ + struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */ + struct JSIteratorConcatData *iterator_concat_data; /* JS_CLASS_ITERATOR_CONCAT */ + struct JSIteratorHelperData *iterator_helper_data; /* JS_CLASS_ITERATOR_HELPER */ + struct JSIteratorWrapData *iterator_wrap_data; /* JS_CLASS_ITERATOR_WRAP */ + struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ + struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */ + struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */ + struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */ + struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ + struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */ + struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */ + /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */ + struct JSFunctionBytecode *function_bytecode; + JSVarRef **var_refs; + JSObject *home_object; /* for 'super' access */ + } func; + struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */ + JSContext *realm; + JSCFunctionType c_function; + uint8_t length; + uint8_t cproto; + int16_t magic; + } cfunc; + /* array part for fast arrays and typed arrays */ + struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + union { + uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + } u1; + union { + JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */ + uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */ + int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */ + uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */ + int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */ + uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ + int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ + uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ + uint16_t *fp16_ptr; /* JS_CLASS_FLOAT16_ARRAY */ + float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ + double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ + } u; + uint32_t count; /* <= 2^31-1. 0 for a detached typed array */ + } array; /* 12/20 bytes */ + JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ + JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ + } u; + /* byte sizes: 40/48/72 */ +}; + +typedef struct JSCallSiteData { + JSValue filename; + JSValue func; + JSValue func_name; + bool native; + int line_num; + int col_num; +} JSCallSiteData; + +enum { + __JS_ATOM_NULL = JS_ATOM_NULL, +#define DEF(name, str) JS_ATOM_ ## name, +#include "quickjs-atom.h" +#undef DEF + JS_ATOM_END, +}; +#define JS_ATOM_LAST_KEYWORD JS_ATOM_super +#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield + +static const char js_atom_init[] = +#define DEF(name, str) str "\0" +#include "quickjs-atom.h" +#undef DEF +; + +typedef enum OPCodeFormat { +#define FMT(f) OP_FMT_ ## f, +#define DEF(id, size, n_pop, n_push, f) +#include "quickjs-opcode.h" +#undef DEF +#undef FMT +} OPCodeFormat; + +typedef enum OPCodeEnum { +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) OP_ ## id, +#define def(id, size, n_pop, n_push, f) +#include "quickjs-opcode.h" +#undef def +#undef DEF +#undef FMT + OP_COUNT, /* excluding temporary opcodes */ + /* temporary opcodes : overlap with the short opcodes */ + OP_TEMP_START = OP_nop + 1, + OP___dummy = OP_TEMP_START - 1, +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) +#define def(id, size, n_pop, n_push, f) OP_ ## id, +#include "quickjs-opcode.h" +#undef def +#undef DEF +#undef FMT + OP_TEMP_END, +} OPCodeEnum; + +static int JS_InitAtoms(JSRuntime *rt); +static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, + int atom_type); +static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p); +static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); +static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags); +static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags); +static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, JSValueConst new_target, + int argc, JSValueConst *argv, int flags); +static JSValue JS_CallConstructorInternal(JSContext *ctx, + JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv, int flags); +static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv); +static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValueConst *argv); +static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, + JSValue val, bool is_array_ctor); +static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, + JSValueConst val, int flags, int scope_idx); +static __maybe_unused void JS_DumpString(JSRuntime *rt, JSString *p); +static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); +static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); +static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); +static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValueConst val); +static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); +static __maybe_unused void JS_DumpShapes(JSRuntime *rt); + +static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); +static void js_array_finalizer(JSRuntime *rt, JSValueConst val); +static void js_array_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_object_data_finalizer(JSRuntime *rt, JSValueConst val); +static void js_object_data_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_c_function_finalizer(JSRuntime *rt, JSValueConst val); +static void js_c_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_bytecode_function_finalizer(JSRuntime *rt, JSValueConst val); +static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_bound_function_finalizer(JSRuntime *rt, JSValueConst val); +static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValueConst val); +static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_regexp_finalizer(JSRuntime *rt, JSValueConst val); +static void js_array_buffer_finalizer(JSRuntime *rt, JSValueConst val); +static void js_typed_array_finalizer(JSRuntime *rt, JSValueConst val); +static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_proxy_finalizer(JSRuntime *rt, JSValueConst val); +static void js_proxy_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_map_finalizer(JSRuntime *rt, JSValueConst val); +static void js_map_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_map_iterator_finalizer(JSRuntime *rt, JSValueConst val); +static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_array_iterator_finalizer(JSRuntime *rt, JSValueConst val); +static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_iterator_concat_finalizer(JSRuntime *rt, JSValueConst val); +static void js_iterator_concat_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_iterator_helper_finalizer(JSRuntime *rt, JSValueConst val); +static void js_iterator_helper_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValueConst val); +static void js_iterator_wrap_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_regexp_string_iterator_finalizer(JSRuntime *rt, + JSValueConst val); +static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_generator_finalizer(JSRuntime *rt, JSValueConst val); +static void js_generator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_promise_finalizer(JSRuntime *rt, JSValueConst val); +static void js_promise_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValueConst val); +static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); + +#define HINT_STRING 0 +#define HINT_NUMBER 1 +#define HINT_NONE 2 +#define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive +static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint); +static JSValue JS_ToStringFree(JSContext *ctx, JSValue val); +static int JS_ToBoolFree(JSContext *ctx, JSValue val); +static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val); +static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val); +static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val); +static JSValue JS_ToPropertyKeyInternal(JSContext *ctx, JSValueConst val, + int flags); +static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len); +static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, + JSValueConst flags); +static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, + JSValue pattern, JSValue bc); +static void gc_decref(JSRuntime *rt); +static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, + const JSClassDef *class_def, JSAtom name); +static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int unshift); +static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv); +static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv, int magic); +static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); + +typedef enum JSStrictEqModeEnum { + JS_EQ_STRICT, + JS_EQ_SAME_VALUE, + JS_EQ_SAME_VALUE_ZERO, +} JSStrictEqModeEnum; + +static bool js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, + JSStrictEqModeEnum eq_mode); +static bool js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2); +static bool js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2); +static bool js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2); +static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val); +static JSProperty *add_property(JSContext *ctx, + JSObject *p, JSAtom prop, int prop_flags); +static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); +static JSValue JS_ThrowStackOverflow(JSContext *ctx); +static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); +static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); +static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, + JSValueConst proto_val, bool throw_flag); +static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); +static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj); +static int js_proxy_isArray(JSContext *ctx, JSValueConst obj); +static int JS_CreateProperty(JSContext *ctx, JSObject *p, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags); +static int js_string_memcmp(JSString *p1, JSString *p2, int len); +static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref); +static bool is_valid_weakref_target(JSValueConst val); +static void insert_weakref_record(JSValueConst target, + struct JSWeakRefRecord *wr); +static JSValue js_array_buffer_constructor3(JSContext *ctx, + JSValueConst new_target, + uint64_t len, uint64_t *max_len, + JSClassID class_id, + uint8_t *buf, + JSFreeArrayBufferDataFunc *free_func, + void *opaque, bool alloc_flag); +static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr); +static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj); +static bool array_buffer_is_resizable(const JSArrayBuffer *abuf); +static JSValue js_typed_array_constructor(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int classid); +static JSValue js_typed_array_constructor_ta(JSContext *ctx, + JSValueConst new_target, + JSValueConst src_obj, + int classid, uint32_t len); +static bool is_typed_array(JSClassID class_id); +static bool typed_array_is_oob(JSObject *p); +static uint32_t typed_array_length(JSObject *p); +static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); +static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx); +static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, + bool is_arg); +static JSValue js_call_generator_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags); +static void js_async_function_resolve_finalizer(JSRuntime *rt, + JSValueConst val); +static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + const char *filename, int line, int flags, int scope_idx); +static void js_free_module_def(JSContext *ctx, JSModuleDef *m); +static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, + JS_MarkFunc *mark_func); +static JSValue js_import_meta(JSContext *ctx); +static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier); +static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); +static JSValue js_new_promise_capability(JSContext *ctx, + JSValue *resolving_funcs, + JSValueConst ctor); +static __exception int perform_promise_then(JSContext *ctx, + JSValueConst promise, + JSValueConst *resolve_reject, + JSValueConst *cap_resolving_funcs); +static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); +static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue js_promise_resolve_thenable_job(JSContext *ctx, + int argc, JSValueConst *argv); +static bool js_string_eq(JSString *p1, JSString *p2); +static int js_string_compare(JSString *p1, JSString *p2); +static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, + JSValue prop, JSValue val, int flags); +static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val); +static bool JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val); +static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val); +static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, + JSObject *p, JSAtom prop); +static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc); +static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, + JS_MarkFunc *mark_func); +static void JS_AddIntrinsicBasicObjects(JSContext *ctx); +static void js_free_shape(JSRuntime *rt, JSShape *sh); +static void js_free_shape_null(JSRuntime *rt, JSShape *sh); +static int js_shape_prepare_update(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs); +static int init_shape_hash(JSRuntime *rt); +static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, + JSValueConst obj); +static __exception int js_get_length64(JSContext *ctx, int64_t *pres, + JSValueConst obj); +static __exception int js_set_length64(JSContext *ctx, JSValueConst obj, + int64_t len); +static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len); +static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, + JSValueConst array_arg); +static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab); +static bool js_get_fast_array(JSContext *ctx, JSValue obj, + JSValue **arrpp, uint32_t *countp); +static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len); +static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, + JSValue sync_iter); +static void js_c_function_data_finalizer(JSRuntime *rt, JSValueConst val); +static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static JSValue js_call_c_function_data(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_val, + int argc, JSValueConst *argv, int flags); +static JSAtom js_symbol_to_atom(JSContext *ctx, JSValueConst val); +static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, + JSGCObjectTypeEnum type); +static void remove_gc_object(JSGCObjectHeader *h); +static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s); +static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); +static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, + void *opaque); +static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, + JSAtom atom, void *opaque); + +static void js_set_uncatchable_error(JSContext *ctx, JSValueConst val, + bool flag); + +static JSValue js_new_callsite(JSContext *ctx, JSCallSiteData *csd); +static void js_new_callsite_data(JSContext *ctx, JSCallSiteData *csd, JSStackFrame *sf); +static void js_new_callsite_data2(JSContext *ctx, JSCallSiteData *csd, const char *filename, int line_num, int col_num); +static void _JS_AddIntrinsicCallSite(JSContext *ctx); + +static void JS_SetOpaqueInternal(JSValueConst obj, void *opaque); + +static const JSClassExoticMethods js_arguments_exotic_methods; +static const JSClassExoticMethods js_string_exotic_methods; +static const JSClassExoticMethods js_proxy_exotic_methods; +static const JSClassExoticMethods js_module_ns_exotic_methods; + +static inline bool double_is_int32(double d) +{ + uint64_t u, e; + JSFloat64Union t; + + t.d = d; + u = t.u64; + + e = ((u >> 52) & 0x7FF) - 1023; + if (e > 30) { + // accept 0, INT32_MIN, reject too large, too small, nan, inf, -0 + return !u || (u == 0xc1e0000000000000); + } else { + // shift out sign, exponent and whole part bits + // value is fractional if remaining low bits are non-zero + return !(u << 12 << e); + } +} + +static JSValue js_float64(double d) +{ + return __JS_NewFloat64(d); +} + +static int compare_u32(uint32_t a, uint32_t b) +{ + return -(a < b) + (b < a); // -1, 0 or 1 +} + +static JSValue js_int32(int32_t v) +{ + return JS_MKVAL(JS_TAG_INT, v); +} + +static JSValue js_uint32(uint32_t v) +{ + if (v <= INT32_MAX) + return js_int32(v); + else + return js_float64(v); +} + +static JSValue js_int64(int64_t v) +{ + if (v >= INT32_MIN && v <= INT32_MAX) + return js_int32(v); + else + return js_float64(v); +} + +static JSValue js_number(double d) +{ + if (double_is_int32(d)) + return js_int32((int32_t)d); + else + return js_float64(d); +} + +JSValue JS_NewNumber(JSContext *ctx, double d) +{ + return js_number(d); +} + +static JSValue js_bool(bool v) +{ + return JS_MKVAL(JS_TAG_BOOL, (v != 0)); +} + +static JSValue js_dup(JSValueConst v) +{ + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); + p->ref_count++; + } + return unsafe_unconst(v); +} + +JSValue JS_DupValue(JSContext *ctx, JSValueConst v) +{ + return js_dup(v); +} + +JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) +{ + return js_dup(v); +} + +static void js_trigger_gc(JSRuntime *rt, size_t size) +{ + bool force_gc; +#ifdef FORCE_GC_AT_MALLOC + force_gc = true; +#else + force_gc = ((rt->malloc_state.malloc_size + size) > + rt->malloc_gc_threshold); +#endif + if (force_gc) { +#ifdef ENABLE_DUMPS // JS_DUMP_GC + if (check_dump_flag(rt, JS_DUMP_GC)) { + printf("GC: size=%zd\n", rt->malloc_state.malloc_size); + } +#endif + JS_RunGC(rt); + rt->malloc_gc_threshold = rt->malloc_state.malloc_size + + (rt->malloc_state.malloc_size >> 1); + } +} + +static size_t js_malloc_usable_size_unknown(const void *ptr) +{ + return 0; +} + +void *js_calloc_rt(JSRuntime *rt, size_t count, size_t size) +{ + void *ptr; + JSMallocState *s; + + /* Do not allocate zero bytes: behavior is platform dependent */ + assert(count != 0 && size != 0); + + if (size > 0) + if (unlikely(count != (count * size) / size)) + return NULL; + + s = &rt->malloc_state; + /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ + if (unlikely(s->malloc_size + (count * size) > s->malloc_limit - 1)) + return NULL; + + ptr = rt->mf.js_calloc(s->opaque, count, size); + if (!ptr) + return NULL; + + s->malloc_count++; + s->malloc_size += rt->mf.js_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + return ptr; +} + +void *js_malloc_rt(JSRuntime *rt, size_t size) +{ + void *ptr; + JSMallocState *s; + + /* Do not allocate zero bytes: behavior is platform dependent */ + assert(size != 0); + + s = &rt->malloc_state; + /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ + if (unlikely(s->malloc_size + size > s->malloc_limit - 1)) + return NULL; + + ptr = rt->mf.js_malloc(s->opaque, size); + if (!ptr) + return NULL; + + s->malloc_count++; + s->malloc_size += rt->mf.js_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + return ptr; +} + +void js_free_rt(JSRuntime *rt, void *ptr) +{ + JSMallocState *s; + + if (!ptr) + return; + + s = &rt->malloc_state; + s->malloc_count--; + s->malloc_size -= rt->mf.js_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + rt->mf.js_free(s->opaque, ptr); +} + +void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size) +{ + size_t old_size; + JSMallocState *s; + + if (!ptr) { + if (size == 0) + return NULL; + return js_malloc_rt(rt, size); + } + if (unlikely(size == 0)) { + js_free_rt(rt, ptr); + return NULL; + } + old_size = rt->mf.js_malloc_usable_size(ptr); + s = &rt->malloc_state; + /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ + if (s->malloc_size + size - old_size > s->malloc_limit - 1) + return NULL; + + ptr = rt->mf.js_realloc(s->opaque, ptr, size); + if (!ptr) + return NULL; + + s->malloc_size += rt->mf.js_malloc_usable_size(ptr) - old_size; + return ptr; +} + +size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr) +{ + return rt->mf.js_malloc_usable_size(ptr); +} + +/** + * This used to be implemented as malloc + memset, but using calloc + * yields better performance in initial, bursty allocations, something useful + * for QuickJS. + * + * More information: https://github.com/quickjs-ng/quickjs/pull/519 + */ +void *js_mallocz_rt(JSRuntime *rt, size_t size) +{ + return js_calloc_rt(rt, 1, size); +} + +/* Throw out of memory in case of error */ +void *js_calloc(JSContext *ctx, size_t count, size_t size) +{ + void *ptr; + ptr = js_calloc_rt(ctx->rt, count, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +/* Throw out of memory in case of error */ +void *js_malloc(JSContext *ctx, size_t size) +{ + void *ptr; + ptr = js_malloc_rt(ctx->rt, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +/* Throw out of memory in case of error */ +void *js_mallocz(JSContext *ctx, size_t size) +{ + void *ptr; + ptr = js_mallocz_rt(ctx->rt, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +void js_free(JSContext *ctx, void *ptr) +{ + js_free_rt(ctx->rt, ptr); +} + +/* Throw out of memory in case of error */ +void *js_realloc(JSContext *ctx, void *ptr, size_t size) +{ + void *ret; + ret = js_realloc_rt(ctx->rt, ptr, size); + if (unlikely(!ret && size != 0)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ret; +} + +/* store extra allocated size in *pslack if successful */ +void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack) +{ + void *ret; + ret = js_realloc_rt(ctx->rt, ptr, size); + if (unlikely(!ret && size != 0)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + if (pslack) { + size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret); + *pslack = (new_size > size) ? new_size - size : 0; + } + return ret; +} + +size_t js_malloc_usable_size(JSContext *ctx, const void *ptr) +{ + return js_malloc_usable_size_rt(ctx->rt, ptr); +} + +/* Throw out of memory exception in case of error */ +char *js_strndup(JSContext *ctx, const char *s, size_t n) +{ + char *ptr; + ptr = js_malloc(ctx, n + 1); + if (ptr) { + memcpy(ptr, s, n); + ptr[n] = '\0'; + } + return ptr; +} + +char *js_strdup(JSContext *ctx, const char *str) +{ + return js_strndup(ctx, str, strlen(str)); +} + +static no_inline int js_realloc_array(JSContext *ctx, void **parray, + int elem_size, int *psize, int req_size) +{ + int new_size; + size_t slack; + void *new_array; + /* XXX: potential arithmetic overflow */ + new_size = max_int(req_size, *psize * 3 / 2); + new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack); + if (!new_array) + return -1; + new_size += slack / elem_size; + *psize = new_size; + *parray = new_array; + return 0; +} + +/* resize the array and update its size if req_size > *psize */ +static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size, + int *psize, int req_size) +{ + if (unlikely(req_size > *psize)) + return js_realloc_array(ctx, parray, elem_size, psize, req_size); + else + return 0; +} + +static void *js_dbuf_realloc(void *ctx, void *ptr, size_t size) +{ + return js_realloc(ctx, ptr, size); +} + +static inline void js_dbuf_init(JSContext *ctx, DynBuf *s) +{ + dbuf_init2(s, ctx, js_dbuf_realloc); +} + +static inline int is_digit(int c) { + return c >= '0' && c <= '9'; +} + +static inline int string_get(JSString *p, int idx) { + return p->is_wide_char ? str16(p)[idx] : str8(p)[idx]; +} + +typedef struct JSClassShortDef { + JSAtom class_name; + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; +} JSClassShortDef; + +static JSClassShortDef const js_std_class_def[] = { + { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */ + { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */ + { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */ + { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */ + { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */ + { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */ + { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */ + { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */ + { JS_ATOM_Arguments, NULL, NULL }, /* JS_CLASS_MAPPED_ARGUMENTS */ + { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */ + { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */ + { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */ + { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */ + { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */ + { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */ + { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */ + { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */ + { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */ + { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */ + { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */ + { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */ + { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */ + { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */ + { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */ + { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */ + { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */ + { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */ + { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */ + { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */ + { JS_ATOM_Float16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT16_ARRAY */ + { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */ + { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */ + { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */ + { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */ + { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ + { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ + { JS_ATOM_WeakMap, js_map_finalizer, NULL }, /* JS_CLASS_WEAKMAP */ + { JS_ATOM_WeakSet, js_map_finalizer, NULL }, /* JS_CLASS_WEAKSET */ + { JS_ATOM_Iterator, NULL, NULL }, /* JS_CLASS_ITERATOR */ + { JS_ATOM_IteratorConcat, js_iterator_concat_finalizer, js_iterator_concat_mark }, /* JS_CLASS_ITERATOR_CONCAT */ + { JS_ATOM_IteratorHelper, js_iterator_helper_finalizer, js_iterator_helper_mark }, /* JS_CLASS_ITERATOR_HELPER */ + { JS_ATOM_IteratorWrap, js_iterator_wrap_finalizer, js_iterator_wrap_mark }, /* JS_CLASS_ITERATOR_WRAP */ + { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */ + { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */ + { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */ + { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */ + { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */ + { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */ +}; + +static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, + int start, int count) +{ + JSClassDef cm_s, *cm = &cm_s; + int i, class_id; + + for(i = 0; i < count; i++) { + class_id = i + start; + memset(cm, 0, sizeof(*cm)); + cm->finalizer = tab[i].finalizer; + cm->gc_mark = tab[i].gc_mark; + if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0) + return -1; + } + return 0; +} + +/* Uses code from LLVM project. */ +static inline uintptr_t js_get_stack_pointer(void) +{ +#if defined(__clang__) || defined(__GNUC__) + return (uintptr_t)__builtin_frame_address(0); +#elif defined(_MSC_VER) + return (uintptr_t)_AddressOfReturnAddress(); +#else + char CharOnStack = 0; + // The volatile store here is intended to escape the local variable, to + // prevent the compiler from optimizing CharOnStack into anything other + // than a char on the stack. + // + // Tested on: MSVC 2015 - 2019, GCC 4.9 - 9, Clang 3.2 - 9, ICC 13 - 19. + char *volatile Ptr = &CharOnStack; + return (uintptr_t) Ptr; +#endif +} + +static inline bool js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) +{ + uintptr_t sp; + sp = js_get_stack_pointer() - alloca_size; + return unlikely(sp < rt->stack_limit); +} + +JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) +{ + JSRuntime *rt; + JSMallocState ms; + + memset(&ms, 0, sizeof(ms)); + ms.opaque = opaque; + ms.malloc_limit = 0; + + rt = mf->js_calloc(opaque, 1, sizeof(JSRuntime)); + if (!rt) + return NULL; + rt->mf = *mf; + if (!rt->mf.js_malloc_usable_size) { + /* use dummy function if none provided */ + rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown; + } + /* Inline what js_malloc_rt does since we cannot use it here. */ + ms.malloc_count++; + ms.malloc_size += rt->mf.js_malloc_usable_size(rt) + MALLOC_OVERHEAD; + rt->malloc_state = ms; + rt->malloc_gc_threshold = 256 * 1024; + + init_list_head(&rt->context_list); + init_list_head(&rt->gc_obj_list); + init_list_head(&rt->gc_zero_ref_count_list); + rt->gc_phase = JS_GC_PHASE_NONE; + +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + init_list_head(&rt->string_list); +#endif + init_list_head(&rt->job_list); + init_list_head(&rt->weakref_keepalive_list); + + if (JS_InitAtoms(rt)) + goto fail; + + /* create the object, array and function classes */ + if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT, + countof(js_std_class_def)) < 0) + goto fail; + rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods; + rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods; + rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods; + + rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function; + rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_call_c_function_data; + rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function; + rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_call_generator_function; + if (init_shape_hash(rt)) + goto fail; + + rt->js_class_id_alloc = JS_CLASS_INIT_COUNT; + + rt->stack_size = JS_DEFAULT_STACK_SIZE; +#ifdef __wasi__ + rt->stack_size = 0; +#endif + + JS_UpdateStackTop(rt); + + rt->current_exception = JS_UNINITIALIZED; + + return rt; + fail: + JS_FreeRuntime(rt); + return NULL; +} + +void *JS_GetRuntimeOpaque(JSRuntime *rt) +{ + return rt->user_opaque; +} + +void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque) +{ + rt->user_opaque = opaque; +} + +int JS_AddRuntimeFinalizer(JSRuntime *rt, JSRuntimeFinalizer *finalizer, + void *arg) +{ + JSRuntimeFinalizerState *fs = js_malloc_rt(rt, sizeof(*fs)); + if (!fs) + return -1; + fs->next = rt->finalizers; + fs->finalizer = finalizer; + fs->arg = arg; + rt->finalizers = fs; + return 0; +} + +static void *js_def_calloc(void *opaque, size_t count, size_t size) +{ + return calloc(count, size); +} + +static void *js_def_malloc(void *opaque, size_t size) +{ + return malloc(size); +} + +static void js_def_free(void *opaque, void *ptr) +{ + free(ptr); +} + +static void *js_def_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +static const JSMallocFunctions def_malloc_funcs = { + js_def_calloc, + js_def_malloc, + js_def_free, + js_def_realloc, + js__malloc_usable_size +}; + +JSRuntime *JS_NewRuntime(void) +{ + return JS_NewRuntime2(&def_malloc_funcs, NULL); +} + +void JS_SetMemoryLimit(JSRuntime *rt, size_t limit) +{ + rt->malloc_state.malloc_limit = limit; +} + +void JS_SetDumpFlags(JSRuntime *rt, uint64_t flags) +{ +#ifdef ENABLE_DUMPS + rt->dump_flags = flags; +#endif +} + +uint64_t JS_GetDumpFlags(JSRuntime *rt) +{ +#ifdef ENABLE_DUMPS + return rt->dump_flags; +#else + return 0; +#endif +} + +size_t JS_GetGCThreshold(JSRuntime *rt) { + return rt->malloc_gc_threshold; +} + +/* use -1 to disable automatic GC */ +void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold) +{ + rt->malloc_gc_threshold = gc_threshold; +} + +#define malloc(s) malloc_is_forbidden(s) +#define free(p) free_is_forbidden(p) +#define realloc(p,s) realloc_is_forbidden(p,s) + +void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque) +{ + rt->interrupt_handler = cb; + rt->interrupt_opaque = opaque; +} + +void JS_SetCanBlock(JSRuntime *rt, bool can_block) +{ + rt->can_block = can_block; +} + +void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, + const JSSharedArrayBufferFunctions *sf) +{ + rt->sab_funcs = *sf; +} + +/* return 0 if OK, < 0 if exception */ +int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = ctx->rt; + JSJobEntry *e; + int i; + + assert(!rt->in_free); + + e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue)); + if (!e) + return -1; + e->ctx = ctx; + e->job_func = job_func; + e->argc = argc; + for(i = 0; i < argc; i++) { + e->argv[i] = js_dup(argv[i]); + } + list_add_tail(&e->link, &rt->job_list); + return 0; +} + +bool JS_IsJobPending(JSRuntime *rt) +{ + return !list_empty(&rt->job_list); +} + +/* return < 0 if exception, 0 if no job pending, 1 if a job was + executed successfully. the context of the job is stored in '*pctx' */ +int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx) +{ + JSContext *ctx; + JSJobEntry *e; + JSValue res; + int i, ret; + + if (list_empty(&rt->job_list)) { + *pctx = NULL; + return 0; + } + + /* get the first pending job and execute it */ + e = list_entry(rt->job_list.next, JSJobEntry, link); + list_del(&e->link); + ctx = e->ctx; + res = e->job_func(e->ctx, e->argc, vc(e->argv)); + for(i = 0; i < e->argc; i++) + JS_FreeValue(ctx, e->argv[i]); + if (JS_IsException(res)) + ret = -1; + else + ret = 1; + JS_FreeValue(ctx, res); + js_free(ctx, e); + *pctx = ctx; + return ret; +} + +static inline uint32_t atom_get_free(const JSAtomStruct *p) +{ + return (uintptr_t)p >> 1; +} + +static inline bool atom_is_free(const JSAtomStruct *p) +{ + return (uintptr_t)p & 1; +} + +static inline JSAtomStruct *atom_set_free(uint32_t v) +{ + return (JSAtomStruct *)(((uintptr_t)v << 1) | 1); +} + +/* Note: the string contents are uninitialized */ +static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char) +{ + JSString *str; + str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char); + if (unlikely(!str)) + return NULL; + str->header.ref_count = 1; + str->is_wide_char = is_wide_char; + str->len = max_len; + str->kind = JS_STRING_KIND_NORMAL; + str->atom_type = 0; + str->hash = 0; /* optional but costless */ + str->hash_next = 0; /* optional */ +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_add_tail(&str->link, &rt->string_list); +#endif + return str; +} + +static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char) +{ + JSString *p; + p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char); + if (unlikely(!p)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return p; +} + +static inline void js_free_string0(JSRuntime *rt, JSString *str); + +/* same as JS_FreeValueRT() but faster */ +static inline void js_free_string(JSRuntime *rt, JSString *str) +{ + if (--str->header.ref_count <= 0) + js_free_string0(rt, str); +} + +static inline void js_free_string0(JSRuntime *rt, JSString *str) +{ + if (str->atom_type) { + JS_FreeAtomStruct(rt, str); + } else { +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_del(&str->link); +#endif + if (str->kind == JS_STRING_KIND_SLICE) { + JSStringSlice *slice = (void *)&str[1]; + js_free_string(rt, slice->parent); // safe, recurses only 1 level + } + js_free_rt(rt, str); + } +} + +void JS_SetRuntimeInfo(JSRuntime *rt, const char *s) +{ + if (rt) + rt->rt_info = s; +} + +void JS_FreeRuntime(JSRuntime *rt) +{ + struct list_head *el, *el1; + int i; + + rt->in_free = true; + JS_FreeValueRT(rt, rt->current_exception); + + list_for_each_safe(el, el1, &rt->job_list) { + JSJobEntry *e = list_entry(el, JSJobEntry, link); + for(i = 0; i < e->argc; i++) + JS_FreeValueRT(rt, e->argv[i]); + js_free_rt(rt, e); + } + init_list_head(&rt->job_list); + JS_ClearWeakRefKeepAlives(rt); + + JS_RunGC(rt); + +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + /* leaking objects */ + if (check_dump_flag(rt, JS_DUMP_LEAKS)) { + bool header_done; + JSGCObjectHeader *p; + int count; + + /* remove the internal refcounts to display only the object + referenced externally */ + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + p->mark = 0; + } + gc_decref(rt); + + header_done = false; + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + if (p->ref_count != 0) { + if (!header_done) { + printf("Object leaks:\n"); + JS_DumpObjectHeader(rt); + header_done = true; + } + JS_DumpGCObject(rt, p); + } + } + + count = 0; + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + if (p->ref_count == 0) { + count++; + } + } + if (count != 0) + printf("Secondary object leaks: %d\n", count); + } +#endif + + assert(list_empty(&rt->gc_obj_list)); + + /* free the classes */ + for(i = 0; i < rt->class_count; i++) { + JSClass *cl = &rt->class_array[i]; + if (cl->class_id != 0) { + JS_FreeAtomRT(rt, cl->class_name); + } + } + js_free_rt(rt, rt->class_array); + +#ifdef ENABLE_DUMPS // JS_DUMP_ATOM_LEAKS + /* only the atoms defined in JS_InitAtoms() should be left */ + if (check_dump_flag(rt, JS_DUMP_ATOM_LEAKS)) { + bool header_done = false; + + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p) /* && p->str*/) { + if (i >= JS_ATOM_END || p->header.ref_count != 1) { + if (!header_done) { + header_done = true; + if (rt->rt_info) { + printf("%s:1: atom leakage:", rt->rt_info); + } else { + printf("Atom leaks:\n" + " %6s %6s %s\n", + "ID", "REFCNT", "NAME"); + } + } + if (rt->rt_info) { + printf(" "); + } else { + printf(" %6u %6u ", i, p->header.ref_count); + } + switch (p->atom_type) { + case JS_ATOM_TYPE_STRING: + JS_DumpString(rt, p); + break; + case JS_ATOM_TYPE_GLOBAL_SYMBOL: + printf("Symbol.for("); + JS_DumpString(rt, p); + printf(")"); + break; + case JS_ATOM_TYPE_SYMBOL: + if (p->hash == JS_ATOM_HASH_SYMBOL) { + printf("Symbol("); + JS_DumpString(rt, p); + printf(")"); + } else { + printf("Private("); + JS_DumpString(rt, p); + printf(")"); + } + break; + } + if (rt->rt_info) { + printf(":%u", p->header.ref_count); + } else { + printf("\n"); + } + } + } + } + if (rt->rt_info && header_done) + printf("\n"); + } +#endif + + /* free the atoms */ + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p)) { +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + } + } + js_free_rt(rt, rt->atom_array); + js_free_rt(rt, rt->atom_hash); + js_free_rt(rt, rt->shape_hash); +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + if (check_dump_flag(rt, JS_DUMP_LEAKS) && !list_empty(&rt->string_list)) { + if (rt->rt_info) { + printf("%s:1: string leakage:", rt->rt_info); + } else { + printf("String leaks:\n" + " %6s %s\n", + "REFCNT", "VALUE"); + } + list_for_each_safe(el, el1, &rt->string_list) { + JSString *str = list_entry(el, JSString, link); + if (rt->rt_info) { + printf(" "); + } else { + printf(" %6u ", str->header.ref_count); + } + JS_DumpString(rt, str); + if (rt->rt_info) { + printf(":%u", str->header.ref_count); + } else { + printf("\n"); + } + list_del(&str->link); + js_free_rt(rt, str); + } + if (rt->rt_info) + printf("\n"); + } +#endif + + while (rt->finalizers) { + JSRuntimeFinalizerState *fs = rt->finalizers; + rt->finalizers = fs->next; + fs->finalizer(rt, fs->arg); + js_free_rt(rt, fs); + } + +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + if (check_dump_flag(rt, JS_DUMP_LEAKS)) { + JSMallocState *s = &rt->malloc_state; + if (s->malloc_count > 1) { + if (rt->rt_info) + printf("%s:1: ", rt->rt_info); + printf("Memory leak: %zd bytes lost in %zd block%s\n", + s->malloc_size - sizeof(JSRuntime), + s->malloc_count - 1, &"s"[s->malloc_count == 2]); + } + } +#endif + + { + JSMallocState *ms = &rt->malloc_state; + rt->mf.js_free(ms->opaque, rt); + } +} + +JSContext *JS_NewContextRaw(JSRuntime *rt) +{ + JSContext *ctx; + int i; + + ctx = js_mallocz_rt(rt, sizeof(JSContext)); + if (!ctx) + return NULL; + ctx->header.ref_count = 1; + add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT); + + ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) * + rt->class_count); + if (!ctx->class_proto) { + js_free_rt(rt, ctx); + return NULL; + } + ctx->rt = rt; + list_add_tail(&ctx->link, &rt->context_list); + for(i = 0; i < rt->class_count; i++) + ctx->class_proto[i] = JS_NULL; + ctx->array_ctor = JS_NULL; + ctx->iterator_ctor = JS_NULL; + ctx->iterator_ctor_getset = JS_NULL; + ctx->regexp_ctor = JS_NULL; + ctx->promise_ctor = JS_NULL; + ctx->error_ctor = JS_NULL; + ctx->error_back_trace = JS_UNDEFINED; + ctx->error_prepare_stack = JS_UNDEFINED; + ctx->error_stack_trace_limit = js_int32(10); + init_list_head(&ctx->loaded_modules); + + JS_AddIntrinsicBasicObjects(ctx); + return ctx; +} + +JSContext *JS_NewContext(JSRuntime *rt) +{ + JSContext *ctx; + + ctx = JS_NewContextRaw(rt); + if (!ctx) + return NULL; + + JS_AddIntrinsicBaseObjects(ctx); + JS_AddIntrinsicDate(ctx); + JS_AddIntrinsicEval(ctx); + JS_AddIntrinsicRegExp(ctx); + JS_AddIntrinsicJSON(ctx); + JS_AddIntrinsicProxy(ctx); + JS_AddIntrinsicMapSet(ctx); + JS_AddIntrinsicTypedArrays(ctx); + JS_AddIntrinsicPromise(ctx); + JS_AddIntrinsicBigInt(ctx); + JS_AddIntrinsicWeakRef(ctx); + JS_AddIntrinsicDOMException(ctx); + + JS_AddPerformance(ctx); + + return ctx; +} + +void *JS_GetContextOpaque(JSContext *ctx) +{ + return ctx->user_opaque; +} + +void JS_SetContextOpaque(JSContext *ctx, void *opaque) +{ + ctx->user_opaque = opaque; +} + +/* set the new value and free the old value after (freeing the value + can reallocate the object data) */ +static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val) +{ + JSValue old_val; + old_val = *pval; + *pval = new_val; + JS_FreeValue(ctx, old_val); +} + +void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj) +{ + assert(class_id < ctx->rt->class_count); + set_value(ctx, &ctx->class_proto[class_id], obj); +} + +JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id) +{ + assert(class_id < ctx->rt->class_count); + return js_dup(ctx->class_proto[class_id]); +} + +JSValue JS_GetFunctionProto(JSContext *ctx) +{ + return js_dup(ctx->function_proto); +} + +typedef enum JSFreeModuleEnum { + JS_FREE_MODULE_ALL, + JS_FREE_MODULE_NOT_RESOLVED, +} JSFreeModuleEnum; + +/* XXX: would be more efficient with separate module lists */ +static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag) +{ + struct list_head *el, *el1; + list_for_each_safe(el, el1, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el, JSModuleDef, link); + if (flag == JS_FREE_MODULE_ALL || + (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) { + js_free_module_def(ctx, m); + } + } +} + +JSContext *JS_DupContext(JSContext *ctx) +{ + ctx->header.ref_count++; + return ctx; +} + +/* used by the GC */ +static void JS_MarkContext(JSRuntime *rt, JSContext *ctx, + JS_MarkFunc *mark_func) +{ + int i; + struct list_head *el; + + /* modules are not seen by the GC, so we directly mark the objects + referenced by each module */ + list_for_each(el, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el, JSModuleDef, link); + js_mark_module_def(rt, m, mark_func); + } + + JS_MarkValue(rt, ctx->global_obj, mark_func); + JS_MarkValue(rt, ctx->global_var_obj, mark_func); + + JS_MarkValue(rt, ctx->throw_type_error, mark_func); + JS_MarkValue(rt, ctx->eval_obj, mark_func); + + JS_MarkValue(rt, ctx->array_proto_values, mark_func); + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { + JS_MarkValue(rt, ctx->native_error_proto[i], mark_func); + } + JS_MarkValue(rt, ctx->error_ctor, mark_func); + JS_MarkValue(rt, ctx->error_back_trace, mark_func); + JS_MarkValue(rt, ctx->error_prepare_stack, mark_func); + JS_MarkValue(rt, ctx->error_stack_trace_limit, mark_func); + for(i = 0; i < rt->class_count; i++) { + JS_MarkValue(rt, ctx->class_proto[i], mark_func); + } + JS_MarkValue(rt, ctx->iterator_ctor, mark_func); + JS_MarkValue(rt, ctx->iterator_ctor_getset, mark_func); + JS_MarkValue(rt, ctx->async_iterator_proto, mark_func); + JS_MarkValue(rt, ctx->promise_ctor, mark_func); + JS_MarkValue(rt, ctx->array_ctor, mark_func); + JS_MarkValue(rt, ctx->regexp_ctor, mark_func); + JS_MarkValue(rt, ctx->function_ctor, mark_func); + JS_MarkValue(rt, ctx->function_proto, mark_func); + + if (ctx->array_shape) + mark_func(rt, &ctx->array_shape->header); +} + +void JS_FreeContext(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + int i; + + if (--ctx->header.ref_count > 0) + return; + assert(ctx->header.ref_count == 0); + +#ifdef ENABLE_DUMPS // JS_DUMP_ATOMS + if (check_dump_flag(rt, JS_DUMP_ATOMS)) + JS_DumpAtoms(ctx->rt); +#endif +#ifdef ENABLE_DUMPS // JS_DUMP_SHAPES + if (check_dump_flag(rt, JS_DUMP_SHAPES)) + JS_DumpShapes(ctx->rt); +#endif +#ifdef ENABLE_DUMPS // JS_DUMP_OBJECTS + if (check_dump_flag(rt, JS_DUMP_OBJECTS)) { + struct list_head *el; + JSGCObjectHeader *p; + printf("JSObjects: {\n"); + JS_DumpObjectHeader(ctx->rt); + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + JS_DumpGCObject(rt, p); + } + printf("}\n"); + } +#endif +#ifdef ENABLE_DUMPS // JS_DUMP_MEM + if (check_dump_flag(rt, JS_DUMP_MEM)) { + JSMemoryUsage stats; + JS_ComputeMemoryUsage(rt, &stats); + JS_DumpMemoryUsage(stdout, &stats, rt); + } +#endif + + js_free_modules(ctx, JS_FREE_MODULE_ALL); + + JS_FreeValue(ctx, ctx->global_obj); + JS_FreeValue(ctx, ctx->global_var_obj); + + JS_FreeValue(ctx, ctx->throw_type_error); + JS_FreeValue(ctx, ctx->eval_obj); + + JS_FreeValue(ctx, ctx->array_proto_values); + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { + JS_FreeValue(ctx, ctx->native_error_proto[i]); + } + JS_FreeValue(ctx, ctx->error_ctor); + JS_FreeValue(ctx, ctx->error_back_trace); + JS_FreeValue(ctx, ctx->error_prepare_stack); + JS_FreeValue(ctx, ctx->error_stack_trace_limit); + for(i = 0; i < rt->class_count; i++) { + JS_FreeValue(ctx, ctx->class_proto[i]); + } + js_free_rt(rt, ctx->class_proto); + JS_FreeValue(ctx, ctx->iterator_ctor); + JS_FreeValue(ctx, ctx->iterator_ctor_getset); + JS_FreeValue(ctx, ctx->async_iterator_proto); + JS_FreeValue(ctx, ctx->promise_ctor); + JS_FreeValue(ctx, ctx->array_ctor); + JS_FreeValue(ctx, ctx->regexp_ctor); + JS_FreeValue(ctx, ctx->function_ctor); + JS_FreeValue(ctx, ctx->function_proto); + + js_free_shape_null(ctx->rt, ctx->array_shape); + + list_del(&ctx->link); + remove_gc_object(&ctx->header); + js_free_rt(ctx->rt, ctx); +} + +JSRuntime *JS_GetRuntime(JSContext *ctx) +{ + return ctx->rt; +} + +static void update_stack_limit(JSRuntime *rt) +{ +#if defined(__wasi__) + rt->stack_limit = 0; /* no limit */ +#else + if (rt->stack_size == 0) { + rt->stack_limit = 0; /* no limit */ + } else { + rt->stack_limit = rt->stack_top - rt->stack_size; + } +#endif +} + +void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size) +{ + rt->stack_size = stack_size; + update_stack_limit(rt); +} + +void JS_UpdateStackTop(JSRuntime *rt) +{ + rt->stack_top = js_get_stack_pointer(); + update_stack_limit(rt); +} + +static inline bool is_strict_mode(JSContext *ctx) +{ + JSStackFrame *sf = ctx->rt->current_stack_frame; + return sf && sf->is_strict_mode; +} + +/* JSAtom support */ + +#define JS_ATOM_TAG_INT (1U << 31) +#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1) +#define JS_ATOM_MAX ((1U << 30) - 1) + +/* return the max count from the hash size */ +#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) + +static inline bool __JS_AtomIsConst(JSAtom v) +{ + return (int32_t)v < JS_ATOM_END; +} + +static inline bool __JS_AtomIsTaggedInt(JSAtom v) +{ + return (v & JS_ATOM_TAG_INT) != 0; +} + +static inline JSAtom __JS_AtomFromUInt32(uint32_t v) +{ + return v | JS_ATOM_TAG_INT; +} + +static inline uint32_t __JS_AtomToUInt32(JSAtom atom) +{ + return atom & ~JS_ATOM_TAG_INT; +} + +static inline int is_num(int c) +{ + return c >= '0' && c <= '9'; +} + +/* return true if the string is a number n with 0 <= n <= 2^32-1 */ +static inline bool is_num_string(uint32_t *pval, JSString *p) +{ + uint32_t n; + uint64_t n64; + int c, i, len; + + len = p->len; + if (len == 0 || len > 10) + return false; + c = string_get(p, 0); + if (is_num(c)) { + if (c == '0') { + if (len != 1) + return false; + n = 0; + } else { + n = c - '0'; + for(i = 1; i < len; i++) { + c = string_get(p, i); + if (!is_num(c)) + return false; + n64 = (uint64_t)n * 10 + (c - '0'); + if ((n64 >> 32) != 0) + return false; + n = n64; + } + } + *pval = n; + return true; + } else { + return false; + } +} + +/* XXX: could use faster version ? */ +static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h) +{ + size_t i; + + for(i = 0; i < len; i++) + h = h * 263 + str[i]; + return h; +} + +static inline uint32_t hash_string16(const uint16_t *str, + size_t len, uint32_t h) +{ + size_t i; + + for(i = 0; i < len; i++) + h = h * 263 + str[i]; + return h; +} + +static uint32_t hash_string(JSString *str, uint32_t h) +{ + if (str->is_wide_char) + h = hash_string16(str16(str), str->len, h); + else + h = hash_string8(str8(str), str->len, h); + return h; +} + +static __maybe_unused void JS_DumpString(JSRuntime *rt, JSString *p) +{ + int i, c, sep; + + if (p == NULL) { + printf(""); + return; + } + if (p->header.ref_count != 1) + printf("%d", p->header.ref_count); + if (p->is_wide_char) + putchar('L'); + sep = '\"'; + putchar(sep); + for(i = 0; i < p->len; i++) { + c = string_get(p, i); + if (c == sep || c == '\\') { + putchar('\\'); + putchar(c); + } else if (c >= ' ' && c <= 126) { + putchar(c); + } else if (c == '\n') { + putchar('\\'); + putchar('n'); + } else { + printf("\\u%04x", c); + } + } + putchar(sep); +} + +static __maybe_unused void JS_DumpAtoms(JSRuntime *rt) +{ + JSAtomStruct *p; + int h, i; + /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */ + printf("JSAtom count=%d size=%d hash_size=%d:\n", + rt->atom_count, rt->atom_size, rt->atom_hash_size); + printf("JSAtom hash table: {\n"); + for(i = 0; i < rt->atom_hash_size; i++) { + h = rt->atom_hash[i]; + if (h) { + printf(" %d:", i); + while (h) { + p = rt->atom_array[h]; + printf(" "); + JS_DumpString(rt, p); + h = p->hash_next; + } + printf("\n"); + } + } + printf("}\n"); + printf("JSAtom table: {\n"); + for(i = 0; i < rt->atom_size; i++) { + p = rt->atom_array[i]; + if (!atom_is_free(p)) { + printf(" %d: { %d %08x ", i, p->atom_type, p->hash); + if (!(p->len == 0 && p->is_wide_char != 0)) + JS_DumpString(rt, p); + printf(" %d }\n", p->hash_next); + } + } + printf("}\n"); +} + +static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size) +{ + JSAtomStruct *p; + uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash; + + assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */ + new_hash_mask = new_hash_size - 1; + new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size); + if (!new_hash) + return -1; + for(i = 0; i < rt->atom_hash_size; i++) { + h = rt->atom_hash[i]; + while (h != 0) { + p = rt->atom_array[h]; + hash_next1 = p->hash_next; + /* add in new hash table */ + j = p->hash & new_hash_mask; + p->hash_next = new_hash[j]; + new_hash[j] = h; + h = hash_next1; + } + } + js_free_rt(rt, rt->atom_hash); + rt->atom_hash = new_hash; + rt->atom_hash_size = new_hash_size; + rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size); + // JS_DumpAtoms(rt); + return 0; +} + +static int JS_InitAtoms(JSRuntime *rt) +{ + int i, len, atom_type; + const char *p; + + rt->atom_hash_size = 0; + rt->atom_hash = NULL; + rt->atom_count = 0; + rt->atom_size = 0; + rt->atom_free_index = 0; + if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */ + return -1; + + p = js_atom_init; + for(i = 1; i < JS_ATOM_END; i++) { + if (i == JS_ATOM_Private_brand) + atom_type = JS_ATOM_TYPE_PRIVATE; + else if (i >= JS_ATOM_Symbol_toPrimitive) + atom_type = JS_ATOM_TYPE_SYMBOL; + else + atom_type = JS_ATOM_TYPE_STRING; + len = strlen(p); + if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL) + return -1; + p = p + len + 1; + } + return 0; +} + +static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v) +{ + JSAtomStruct *p; + + if (!__JS_AtomIsConst(v)) { + p = rt->atom_array[v]; + p->header.ref_count++; + } + return v; +} + +JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + if (!__JS_AtomIsConst(v)) { + rt = ctx->rt; + p = rt->atom_array[v]; + p->header.ref_count++; + } + return v; +} + +static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + rt = ctx->rt; + if (__JS_AtomIsTaggedInt(v)) + return JS_ATOM_KIND_STRING; + p = rt->atom_array[v]; + switch(p->atom_type) { + case JS_ATOM_TYPE_STRING: + return JS_ATOM_KIND_STRING; + case JS_ATOM_TYPE_GLOBAL_SYMBOL: + return JS_ATOM_KIND_SYMBOL; + case JS_ATOM_TYPE_SYMBOL: + switch(p->hash) { + case JS_ATOM_HASH_SYMBOL: + return JS_ATOM_KIND_SYMBOL; + case JS_ATOM_HASH_PRIVATE: + return JS_ATOM_KIND_PRIVATE; + default: + abort(); + } + default: + abort(); + } + return (JSAtomKindEnum){-1}; // pacify compiler +} + +static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p) +{ + uint32_t i = p->hash_next; /* atom_index */ + if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { + JSAtomStruct *p1; + + i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)]; + p1 = rt->atom_array[i]; + while (p1 != p) { + assert(i != 0); + i = p1->hash_next; + p1 = rt->atom_array[i]; + } + } + return i; +} + +/* string case (internal). Return JS_ATOM_NULL if error. 'str' is + freed. */ +static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) +{ + uint32_t h, h1, i; + JSAtomStruct *p; + int len; + + if (atom_type < JS_ATOM_TYPE_SYMBOL) { + /* str is not NULL */ + if (str->atom_type == atom_type) { + /* str is the atom, return its index */ + i = js_get_atom_index(rt, str); + /* reduce string refcount and increase atom's unless constant */ + if (__JS_AtomIsConst(i)) + str->header.ref_count--; + return i; + } + /* try and locate an already registered atom */ + len = str->len; + h = hash_string(str, atom_type); + h &= JS_ATOM_HASH_MASK; + h1 = h & (rt->atom_hash_size - 1); + i = rt->atom_hash[h1]; + while (i != 0) { + p = rt->atom_array[i]; + if (p->hash == h && + p->atom_type == atom_type && + p->len == len && + js_string_memcmp(p, str, len) == 0) { + if (!__JS_AtomIsConst(i)) + p->header.ref_count++; + goto done; + } + i = p->hash_next; + } + } else { + h1 = 0; /* avoid warning */ + if (atom_type == JS_ATOM_TYPE_SYMBOL) { + h = JS_ATOM_HASH_SYMBOL; + } else { + h = JS_ATOM_HASH_PRIVATE; + atom_type = JS_ATOM_TYPE_SYMBOL; + } + } + + if (rt->atom_free_index == 0) { + /* allow new atom entries */ + uint32_t new_size, start; + JSAtomStruct **new_array; + + /* alloc new with size progression 3/2: + 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092 + preallocating space for predefined atoms (at least 195). + */ + new_size = max_int(211, rt->atom_size * 3 / 2); + if (new_size > JS_ATOM_MAX) + goto fail; + /* XXX: should use realloc2 to use slack space */ + new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size); + if (!new_array) + goto fail; + /* Note: the atom 0 is not used */ + start = rt->atom_size; + if (start == 0) { + /* JS_ATOM_NULL entry */ + p = js_mallocz_rt(rt, sizeof(JSAtomStruct)); + if (!p) { + js_free_rt(rt, new_array); + goto fail; + } + p->header.ref_count = 1; /* not refcounted */ + p->atom_type = JS_ATOM_TYPE_SYMBOL; +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + new_array[0] = p; + rt->atom_count++; + start = 1; + } + rt->atom_size = new_size; + rt->atom_array = new_array; + rt->atom_free_index = start; + for(i = start; i < new_size; i++) { + uint32_t next; + if (i == (new_size - 1)) + next = 0; + else + next = i + 1; + rt->atom_array[i] = atom_set_free(next); + } + } + + if (str) { + if (str->atom_type == 0) { + p = str; + p->atom_type = atom_type; + } else { + p = js_malloc_rt(rt, sizeof(JSString) + + (str->len << str->is_wide_char) + + 1 - str->is_wide_char); + if (unlikely(!p)) + goto fail; + p->header.ref_count = 1; + p->is_wide_char = str->is_wide_char; + p->len = str->len; + p->kind = JS_STRING_KIND_NORMAL; +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + memcpy(str8(p), str8(str), (str->len << str->is_wide_char) + + 1 - str->is_wide_char); + js_free_string(rt, str); + } + } else { + p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */ + if (!p) + return JS_ATOM_NULL; + p->header.ref_count = 1; + p->is_wide_char = 1; /* Hack to represent NULL as a JSString */ + p->len = 0; + p->kind = JS_STRING_KIND_NORMAL; +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + } + + /* use an already free entry */ + i = rt->atom_free_index; + rt->atom_free_index = atom_get_free(rt->atom_array[i]); + rt->atom_array[i] = p; + + p->hash = h; + p->hash_next = i; /* atom_index */ + p->atom_type = atom_type; + p->first_weak_ref = NULL; + + rt->atom_count++; + + if (atom_type != JS_ATOM_TYPE_SYMBOL) { + p->hash_next = rt->atom_hash[h1]; + rt->atom_hash[h1] = i; + if (unlikely(rt->atom_count >= rt->atom_count_resize)) + JS_ResizeAtomHash(rt, rt->atom_hash_size * 2); + } + + // JS_DumpAtoms(rt); + return i; + + fail: + i = JS_ATOM_NULL; + done: + if (str) + js_free_string(rt, str); + return i; +} + +// XXX: `str` must be pure ASCII. No UTF-8 encoded strings +// XXX: `str` must not be the string representation of a small integer +static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, + int atom_type) +{ + JSString *p; + p = js_alloc_string_rt(rt, len, 0); + if (!p) + return JS_ATOM_NULL; + memcpy(str8(p), str, len); + str8(p)[len] = '\0'; + return __JS_NewAtom(rt, p, atom_type); +} + +// XXX: `str` must be raw 8-bit contents. No UTF-8 encoded strings +static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, + int atom_type) +{ + uint32_t h, h1, i; + JSAtomStruct *p; + + h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING); + h &= JS_ATOM_HASH_MASK; + h1 = h & (rt->atom_hash_size - 1); + i = rt->atom_hash[h1]; + while (i != 0) { + p = rt->atom_array[i]; + if (p->hash == h && + p->atom_type == JS_ATOM_TYPE_STRING && + p->len == len && + p->is_wide_char == 0 && + memcmp(str8(p), str, len) == 0) { + if (!__JS_AtomIsConst(i)) + p->header.ref_count++; + return i; + } + i = p->hash_next; + } + return JS_ATOM_NULL; +} + +static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p) +{ + uint32_t i = p->hash_next; /* atom_index */ + if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { + JSAtomStruct *p0, *p1; + uint32_t h0; + + h0 = p->hash & (rt->atom_hash_size - 1); + i = rt->atom_hash[h0]; + p1 = rt->atom_array[i]; + if (p1 == p) { + rt->atom_hash[h0] = p1->hash_next; + } else { + for(;;) { + assert(i != 0); + p0 = p1; + i = p1->hash_next; + p1 = rt->atom_array[i]; + if (p1 == p) { + p0->hash_next = p1->hash_next; + break; + } + } + } + } + /* insert in free atom list */ + rt->atom_array[i] = atom_set_free(rt->atom_free_index); + rt->atom_free_index = i; + if (unlikely(p->first_weak_ref)) { + reset_weak_ref(rt, &p->first_weak_ref); + } + /* free the string structure */ +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + rt->atom_count--; + assert(rt->atom_count >= 0); +} + +static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) +{ + JSAtomStruct *p; + + p = rt->atom_array[i]; + if (--p->header.ref_count > 0) + return; + JS_FreeAtomStruct(rt, p); +} + +/* Warning: 'p' is freed */ +static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) +{ + JSRuntime *rt = ctx->rt; + uint32_t n; + if (is_num_string(&n, p)) { + if (n <= JS_ATOM_MAX_INT) { + js_free_string(rt, p); + return __JS_AtomFromUInt32(n); + } + } + /* XXX: should generate an exception */ + return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); +} + +/* `str` may be pure ASCII or UTF-8 encoded */ +JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) +{ + JSValue val; + + if (len == 0 || !is_digit(*str)) { + // TODO(chqrlie): this does not work if `str` has UTF-8 encoded contents + // bug example: `({ "\u00c3\u00a9": 1 }).\u00e9` evaluates to `1`. + JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); + if (atom) + return atom; + } + val = JS_NewStringLen(ctx, str, len); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val)); +} + +/* `str` may be pure ASCII or UTF-8 encoded */ +JSAtom JS_NewAtom(JSContext *ctx, const char *str) +{ + return JS_NewAtomLen(ctx, str, strlen(str)); +} + +JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) +{ + if (n <= JS_ATOM_MAX_INT) { + return __JS_AtomFromUInt32(n); + } else { + char buf[16]; + size_t len = u32toa(buf, n); + JSValue val = js_new_string8_len(ctx, buf, len); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), + JS_ATOM_TYPE_STRING); + } +} + +static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) +{ + if ((uint64_t)n <= JS_ATOM_MAX_INT) { + return __JS_AtomFromUInt32((uint32_t)n); + } else { + char buf[24]; + size_t len = i64toa(buf, n); + JSValue val = js_new_string8_len(ctx, buf, len); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), + JS_ATOM_TYPE_STRING); + } +} + +/* 'p' is freed */ +static JSValue JS_NewSymbolInternal(JSContext *ctx, JSString *p, int atom_type) +{ + JSRuntime *rt = ctx->rt; + JSAtom atom; + atom = __JS_NewAtom(rt, p, atom_type); + if (atom == JS_ATOM_NULL) + return JS_ThrowOutOfMemory(ctx); + return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]); +} + +/* descr must be a non-numeric string atom */ +static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, + int atom_type) +{ + JSRuntime *rt = ctx->rt; + JSString *p; + + assert(!__JS_AtomIsTaggedInt(descr)); + assert(descr < rt->atom_size); + p = rt->atom_array[descr]; + js_dup(JS_MKPTR(JS_TAG_STRING, p)); + return JS_NewSymbolInternal(ctx, p, atom_type); +} + +/* `description` may be pure ASCII or UTF-8 encoded */ +JSValue JS_NewSymbol(JSContext *ctx, const char *description, bool is_global) +{ + JSAtom atom = JS_NewAtom(ctx, description); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; + return JS_NewSymbolFromAtom(ctx, atom, is_global ? JS_ATOM_TYPE_GLOBAL_SYMBOL : JS_ATOM_TYPE_SYMBOL); +} + +#define ATOM_GET_STR_BUF_SIZE 64 + +static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, + JSAtom atom) +{ + if (__JS_AtomIsTaggedInt(atom)) { + snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom)); + } else if (atom == JS_ATOM_NULL) { + snprintf(buf, buf_size, ""); + } else if (atom >= rt->atom_size) { + assert(atom < rt->atom_size); + snprintf(buf, buf_size, "", atom); + } else { + JSAtomStruct *p = rt->atom_array[atom]; + *buf = '\0'; + if (atom_is_free(p)) { + snprintf(buf, buf_size, "", atom); + } else if (p != NULL) { + JSString *str = p; + if (str->is_wide_char) { + /* encode surrogates correctly */ + utf8_encode_buf16(buf, buf_size, str16(str), str->len); + } else { + utf8_encode_buf8(buf, buf_size, str8(str), str->len); + } + } + } + return buf; +} + +static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom) +{ + return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom); +} + +static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, bool force_string) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + + if (__JS_AtomIsTaggedInt(atom)) { + size_t len = u32toa(buf, __JS_AtomToUInt32(atom)); + return js_new_string8_len(ctx, buf, len); + } else { + JSRuntime *rt = ctx->rt; + JSAtomStruct *p; + assert(atom < rt->atom_size); + p = rt->atom_array[atom]; + if (p->atom_type == JS_ATOM_TYPE_STRING) { + goto ret_string; + } else if (force_string) { + if (p->len == 0 && p->is_wide_char != 0) { + /* no description string */ + p = rt->atom_array[JS_ATOM_empty_string]; + } + ret_string: + return js_dup(JS_MKPTR(JS_TAG_STRING, p)); + } else { + return js_dup(JS_MKPTR(JS_TAG_SYMBOL, p)); + } + } +} + +JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom) +{ + return __JS_AtomToValue(ctx, atom, false); +} + +JSValue JS_AtomToString(JSContext *ctx, JSAtom atom) +{ + return __JS_AtomToValue(ctx, atom, true); +} + +/* return true if the atom is an array index (i.e. 0 <= index <= + 2^32-2 and return its value */ +static bool JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom) +{ + if (__JS_AtomIsTaggedInt(atom)) { + *pval = __JS_AtomToUInt32(atom); + return true; + } else { + JSRuntime *rt = ctx->rt; + JSAtomStruct *p; + uint32_t val; + + assert(atom < rt->atom_size); + p = rt->atom_array[atom]; + if (p->atom_type == JS_ATOM_TYPE_STRING && + is_num_string(&val, p) && val != -1) { + *pval = val; + return true; + } else { + *pval = 0; + return false; + } + } +} + +/* This test must be fast if atom is not a numeric index (e.g. a + method name). Return JS_UNDEFINED if not a numeric + index. JS_EXCEPTION can also be returned. */ +static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) +{ + JSRuntime *rt = ctx->rt; + JSAtomStruct *p1; + JSString *p; + int c, len, ret; + JSValue num, str; + + if (__JS_AtomIsTaggedInt(atom)) + return js_int32(__JS_AtomToUInt32(atom)); + assert(atom < rt->atom_size); + p1 = rt->atom_array[atom]; + if (p1->atom_type != JS_ATOM_TYPE_STRING) + return JS_UNDEFINED; + p = p1; + len = p->len; + if (p->is_wide_char) { + const uint16_t *r = str16(p), *r_end = str16(p) + len; + if (r >= r_end) + return JS_UNDEFINED; + c = *r; + if (c == '-') { + if (r >= r_end) + return JS_UNDEFINED; + r++; + c = *r; + /* -0 case is specific */ + if (c == '0' && len == 2) + goto minus_zero; + } + /* XXX: should test NaN, but the tests do not check it */ + if (!is_num(c)) { + /* XXX: String should be normalized, therefore 8-bit only */ + const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' }; + if (!(c =='I' && (r_end - r) == 8 && + !memcmp(r + 1, nfinity16, sizeof(nfinity16)))) + return JS_UNDEFINED; + } + } else { + const uint8_t *r = str8(p), *r_end = str8(p) + len; + if (r >= r_end) + return JS_UNDEFINED; + c = *r; + if (c == '-') { + if (r >= r_end) + return JS_UNDEFINED; + r++; + c = *r; + /* -0 case is specific */ + if (c == '0' && len == 2) { + minus_zero: + return js_float64(-0.0); + } + } + if (!is_num(c)) { + if (!(c =='I' && (r_end - r) == 8 && + !memcmp(r + 1, "nfinity", 7))) + return JS_UNDEFINED; + } + } + /* this is ECMA CanonicalNumericIndexString primitive */ + num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p)); + if (JS_IsException(num)) + return num; + str = JS_ToString(ctx, num); + if (JS_IsException(str)) { + JS_FreeValue(ctx, num); + return str; + } + ret = js_string_eq(p, JS_VALUE_GET_STRING(str)); + JS_FreeValue(ctx, str); + if (ret) { + return num; + } else { + JS_FreeValue(ctx, num); + return JS_UNDEFINED; + } +} + +/* return -1 if exception or true/false */ +static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom) +{ + JSValue num; + num = JS_AtomIsNumericIndex1(ctx, atom); + if (likely(JS_IsUndefined(num))) + return false; + if (JS_IsException(num)) + return -1; + JS_FreeValue(ctx, num); + return true; +} + +void JS_FreeAtom(JSContext *ctx, JSAtom v) +{ + if (!__JS_AtomIsConst(v)) + __JS_FreeAtom(ctx->rt, v); +} + +void JS_FreeAtomRT(JSRuntime *rt, JSAtom v) +{ + if (!__JS_AtomIsConst(v)) + __JS_FreeAtom(rt, v); +} + +/* return true if 'v' is a symbol with a string description */ +static bool JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + rt = ctx->rt; + if (__JS_AtomIsTaggedInt(v)) + return false; + p = rt->atom_array[v]; + return (((p->atom_type == JS_ATOM_TYPE_SYMBOL && + p->hash == JS_ATOM_HASH_SYMBOL) || + p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) && + !(p->len == 0 && p->is_wide_char != 0)); +} + +static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + const char *p; + int i; + + /* XXX: should handle embedded null characters */ + /* XXX: should move encoding code to JS_AtomGetStr */ + p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom); + for (i = 0; p[i]; i++) { + int c = (unsigned char)p[i]; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0))) + break; + } + if (i > 0 && p[i] == '\0') { + printf("%s", p); + } else { + putchar('"'); + printf("%.*s", i, p); + for (; p[i]; i++) { + int c = (unsigned char)p[i]; + if (c == '\"' || c == '\\') { + putchar('\\'); + putchar(c); + } else if (c >= ' ' && c <= 126) { + putchar(c); + } else if (c == '\n') { + putchar('\\'); + putchar('n'); + } else { + printf("\\u%04x", c); + } + } + putchar('\"'); + } +} + +/* free with JS_FreeCString() */ +const char *JS_AtomToCStringLen(JSContext *ctx, size_t *plen, JSAtom atom) +{ + JSValue str; + const char *cstr; + + str = JS_AtomToString(ctx, atom); + if (JS_IsException(str)) { + if (plen) + *plen = 0; + return NULL; + } + cstr = JS_ToCStringLen(ctx, plen, str); + JS_FreeValue(ctx, str); + return cstr; +} + +#ifndef QJS_DISABLE_PARSER + +/* return a string atom containing name concatenated with str1 */ +/* `str1` may be pure ASCII or UTF-8 encoded */ +// TODO(chqrlie): use string concatenation instead of UTF-8 conversion +static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1) +{ + JSValue str; + JSAtom atom; + const char *cstr; + char *cstr2; + size_t len, len1; + + str = JS_AtomToString(ctx, name); + if (JS_IsException(str)) + return JS_ATOM_NULL; + cstr = JS_ToCStringLen(ctx, &len, str); + if (!cstr) + goto fail; + len1 = strlen(str1); + cstr2 = js_malloc(ctx, len + len1 + 1); + if (!cstr2) + goto fail; + memcpy(cstr2, cstr, len); + memcpy(cstr2 + len, str1, len1); + cstr2[len + len1] = '\0'; + atom = JS_NewAtomLen(ctx, cstr2, len + len1); + js_free(ctx, cstr2); + JS_FreeCString(ctx, cstr); + JS_FreeValue(ctx, str); + return atom; + fail: + JS_FreeCString(ctx, cstr); + JS_FreeValue(ctx, str); + return JS_ATOM_NULL; +} + +static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) +{ + char buf[16]; + size_t len; + + len = u32toa(buf, n); + buf[len] = '\0'; + return js_atom_concat_str(ctx, name, buf); +} + +#endif // QJS_DISABLE_PARSER + +static inline bool JS_IsEmptyString(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0; +} + +/* JSClass support */ + +/* a new class ID is allocated if *pclass_id == 0, otherwise *pclass_id is left unchanged */ +JSClassID JS_NewClassID(JSRuntime *rt, JSClassID *pclass_id) +{ + JSClassID class_id = *pclass_id; + if (class_id == 0) { + class_id = rt->js_class_id_alloc++; + *pclass_id = class_id; + } + return class_id; +} + +JSClassID JS_GetClassID(JSValueConst v) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) + return JS_INVALID_CLASS_ID; + p = JS_VALUE_GET_OBJ(v); + return p->class_id; +} + +bool JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id) +{ + return (class_id < rt->class_count && + rt->class_array[class_id].class_id != 0); +} + +JSAtom JS_GetClassName(JSRuntime *rt, JSClassID class_id) +{ + if (JS_IsRegisteredClass(rt, class_id)) { + return JS_DupAtomRT(rt, rt->class_array[class_id].class_id); + } else { + return JS_ATOM_NULL; + } +} + +/* create a new object internal class. Return -1 if error, 0 if + OK. The finalizer can be NULL if none is needed. */ +static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, + const JSClassDef *class_def, JSAtom name) +{ + int new_size, i; + JSClass *cl, *new_class_array; + struct list_head *el; + + if (class_id >= (1 << 16)) + return -1; + if (class_id < rt->class_count && + rt->class_array[class_id].class_id != 0) + return -1; + + if (class_id >= rt->class_count) { + new_size = max_int(JS_CLASS_INIT_COUNT, + max_int(class_id + 1, rt->class_count * 3 / 2)); + + /* reallocate the context class prototype array, if any */ + list_for_each(el, &rt->context_list) { + JSContext *ctx = list_entry(el, JSContext, link); + JSValue *new_tab; + new_tab = js_realloc_rt(rt, ctx->class_proto, + sizeof(ctx->class_proto[0]) * new_size); + if (!new_tab) + return -1; + for(i = rt->class_count; i < new_size; i++) + new_tab[i] = JS_NULL; + ctx->class_proto = new_tab; + } + /* reallocate the class array */ + new_class_array = js_realloc_rt(rt, rt->class_array, + sizeof(JSClass) * new_size); + if (!new_class_array) + return -1; + memset(new_class_array + rt->class_count, 0, + (new_size - rt->class_count) * sizeof(JSClass)); + rt->class_array = new_class_array; + rt->class_count = new_size; + } + cl = &rt->class_array[class_id]; + cl->class_id = class_id; + cl->class_name = JS_DupAtomRT(rt, name); + cl->finalizer = class_def->finalizer; + cl->gc_mark = class_def->gc_mark; + cl->call = class_def->call; + cl->exotic = class_def->exotic; + return 0; +} + +int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) +{ + int ret, len; + JSAtom name; + + // XXX: class_def->class_name must be raw 8-bit contents. No UTF-8 encoded strings + len = strlen(class_def->class_name); + name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); + if (name == JS_ATOM_NULL) { + name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); + if (name == JS_ATOM_NULL) + return -1; + } + ret = JS_NewClass1(rt, class_id, class_def, name); + JS_FreeAtomRT(rt, name); + return ret; +} + +static inline JSValue js_empty_string(JSRuntime *rt) +{ + JSAtomStruct *p = rt->atom_array[JS_ATOM_empty_string]; + return js_dup(JS_MKPTR(JS_TAG_STRING, p)); +} + +// XXX: `buf` contains raw 8-bit data, no UTF-8 decoding is performed +// XXX: no special case for len == 0 +static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len) +{ + JSString *str; + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + memcpy(str8(str), buf, len); + str8(str)[len] = '\0'; + return JS_MKPTR(JS_TAG_STRING, str); +} + +// XXX: `buf` contains raw 8-bit data, no UTF-8 decoding is performed +// XXX: no special case for the empty string +static inline JSValue js_new_string8(JSContext *ctx, const char *str) +{ + return js_new_string8_len(ctx, str, strlen(str)); +} + +static JSValue js_new_string16_len(JSContext *ctx, const uint16_t *buf, int len) +{ + JSString *str; + str = js_alloc_string(ctx, len, 1); + if (!str) + return JS_EXCEPTION; + memcpy(str16(str), buf, len * 2); + return JS_MKPTR(JS_TAG_STRING, str); +} + +static JSValue js_new_string_char(JSContext *ctx, uint16_t c) +{ + if (c < 0x100) { + char ch8 = c; + return js_new_string8_len(ctx, &ch8, 1); + } else { + uint16_t ch16 = c; + return js_new_string16_len(ctx, &ch16, 1); + } +} + +static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) +{ + JSStringSlice *slice; + JSString *q; + int len; + + len = end - start; + if (start == 0 && end == p->len) { + return js_dup(JS_MKPTR(JS_TAG_STRING, p)); + } + if (len <= 0) { + return js_empty_string(ctx->rt); + } + if (len > (JS_STRING_SLICE_LEN_MAX >> p->is_wide_char)) { + if (p->kind == JS_STRING_KIND_SLICE) { + slice = (void *)&p[1]; + p = slice->parent; + start += slice->start >> p->is_wide_char; // bytes -> chars + } + // allocate as 16 bit wide string to avoid wastage; + // js_alloc_string allocates 1 byte extra for 8 bit strings; + q = js_alloc_string(ctx, sizeof(*slice)/2, /*is_wide_char*/true); + if (!q) + return JS_EXCEPTION; + q->is_wide_char = p->is_wide_char; + q->kind = JS_STRING_KIND_SLICE; + q->len = len; + slice = (void *)&q[1]; + slice->parent = p; + slice->start = start << p->is_wide_char; // chars -> bytes + p->header.ref_count++; + return JS_MKPTR(JS_TAG_STRING, q); + } + if (p->is_wide_char) { + JSString *str; + int i; + uint16_t c = 0; + for (i = start; i < end; i++) { + c |= str16(p)[i]; + } + if (c > 0xFF) + return js_new_string16_len(ctx, str16(p) + start, len); + + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + for (i = 0; i < len; i++) { + str8(str)[i] = str16(p)[start + i]; + } + str8(str)[len] = '\0'; + return JS_MKPTR(JS_TAG_STRING, str); + } else { + return js_new_string8_len(ctx, (const char *)(str8(p) + start), len); + } +} + +typedef struct StringBuffer { + JSContext *ctx; + JSString *str; + int len; + int size; + int is_wide_char; + int error_status; +} StringBuffer; + +/* It is valid to call string_buffer_end() and all string_buffer functions even + if string_buffer_init() or another string_buffer function returns an error. + If the error_status is set, string_buffer_end() returns JS_EXCEPTION. + */ +static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size, + int is_wide) +{ + s->ctx = ctx; + s->size = size; + s->len = 0; + s->is_wide_char = is_wide; + s->error_status = 0; + s->str = js_alloc_string(ctx, size, is_wide); + if (unlikely(!s->str)) { + s->size = 0; + return s->error_status = -1; + } +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + /* the StringBuffer may reallocate the JSString, only link it at the end */ + list_del(&s->str->link); +#endif + return 0; +} + +static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size) +{ + return string_buffer_init2(ctx, s, size, 0); +} + +static void string_buffer_free(StringBuffer *s) +{ + js_free(s->ctx, s->str); + s->str = NULL; +} + +static int string_buffer_set_error(StringBuffer *s) +{ + js_free(s->ctx, s->str); + s->str = NULL; + s->size = 0; + s->len = 0; + return s->error_status = -1; +} + +static no_inline int string_buffer_widen(StringBuffer *s, int size) +{ + JSString *str; + size_t slack; + int i; + + if (s->error_status) + return -1; + + str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack); + if (!str) + return string_buffer_set_error(s); + size += slack >> 1; + for(i = s->len; i-- > 0;) { + str16(str)[i] = str8(str)[i]; + } + s->is_wide_char = 1; + s->size = size; + s->str = str; + return 0; +} + +static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c) +{ + JSString *new_str; + int new_size; + size_t new_size_bytes, slack; + + if (s->error_status) + return -1; + + if (new_len > JS_STRING_LEN_MAX) { + JS_ThrowRangeError(s->ctx, "invalid string length"); + return string_buffer_set_error(s); + } + new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX); + if (!s->is_wide_char && c >= 0x100) { + return string_buffer_widen(s, new_size); + } + new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char; + new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack); + if (!new_str) + return string_buffer_set_error(s); + new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX); + s->size = new_size; + s->str = new_str; + return 0; +} + +static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c) +{ + if (unlikely(s->len >= s->size)) { + if (string_buffer_realloc(s, s->len + 1, c)) + return -1; + } + if (s->is_wide_char) { + str16(s->str)[s->len++] = c; + } else if (c < 0x100) { + str8(s->str)[s->len++] = c; + } else { + if (string_buffer_widen(s, s->size)) + return -1; + str16(s->str)[s->len++] = c; + } + return 0; +} + +/* 0 <= c <= 0xff */ +static int string_buffer_putc8(StringBuffer *s, uint32_t c) +{ + if (unlikely(s->len >= s->size)) { + if (string_buffer_realloc(s, s->len + 1, c)) + return -1; + } + if (s->is_wide_char) { + str16(s->str)[s->len++] = c; + } else { + str8(s->str)[s->len++] = c; + } + return 0; +} + +/* 0 <= c <= 0xffff */ +static int string_buffer_putc16(StringBuffer *s, uint32_t c) +{ + if (likely(s->len < s->size)) { + if (s->is_wide_char) { + str16(s->str)[s->len++] = c; + return 0; + } else if (c < 0x100) { + str8(s->str)[s->len++] = c; + return 0; + } + } + return string_buffer_putc_slow(s, c); +} + +/* 0 <= c <= 0x10ffff */ +static int string_buffer_putc(StringBuffer *s, uint32_t c) +{ + if (unlikely(c >= 0x10000)) { + /* surrogate pair */ + if (string_buffer_putc16(s, get_hi_surrogate(c))) + return -1; + c = get_lo_surrogate(c); + } + return string_buffer_putc16(s, c); +} + +static int string_getc(JSString *p, int *pidx) +{ + int idx, c, c1; + idx = *pidx; + if (p->is_wide_char) { + c = str16(p)[idx++]; + if (is_hi_surrogate(c) && idx < p->len) { + c1 = str16(p)[idx]; + if (is_lo_surrogate(c1)) { + c = from_surrogate(c, c1); + idx++; + } + } + } else { + c = str8(p)[idx++]; + } + *pidx = idx; + return c; +} + +static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len) +{ + int i; + + if (s->len + len > s->size) { + if (string_buffer_realloc(s, s->len + len, 0)) + return -1; + } + if (s->is_wide_char) { + for (i = 0; i < len; i++) { + str16(s->str)[s->len + i] = p[i]; + } + s->len += len; + } else { + memcpy(&str8(s->str)[s->len], p, len); + s->len += len; + } + return 0; +} + +static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len) +{ + int c = 0, i; + + for (i = 0; i < len; i++) { + c |= p[i]; + } + if (s->len + len > s->size) { + if (string_buffer_realloc(s, s->len + len, c)) + return -1; + } else if (!s->is_wide_char && c >= 0x100) { + if (string_buffer_widen(s, s->size)) + return -1; + } + if (s->is_wide_char) { + memcpy(&str16(s->str)[s->len], p, len << 1); + s->len += len; + } else { + for (i = 0; i < len; i++) { + str8(s->str)[s->len + i] = p[i]; + } + s->len += len; + } + return 0; +} + +/* appending an ASCII string */ +static int string_buffer_puts8(StringBuffer *s, const char *str) +{ + return string_buffer_write8(s, (const uint8_t *)str, strlen(str)); +} + +static int string_buffer_concat(StringBuffer *s, JSString *p, + uint32_t from, uint32_t to) +{ + if (to <= from) + return 0; + if (p->is_wide_char) + return string_buffer_write16(s, str16(p) + from, to - from); + else + return string_buffer_write8(s, str8(p) + from, to - from); +} + +static int string_buffer_concat_value(StringBuffer *s, JSValueConst v) +{ + JSString *p; + JSValue v1; + int res; + + if (s->error_status) { + /* prevent exception overload */ + return -1; + } + if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { + v1 = JS_ToString(s->ctx, v); + if (JS_IsException(v1)) + return string_buffer_set_error(s); + p = JS_VALUE_GET_STRING(v1); + res = string_buffer_concat(s, p, 0, p->len); + JS_FreeValue(s->ctx, v1); + return res; + } + p = JS_VALUE_GET_STRING(v); + return string_buffer_concat(s, p, 0, p->len); +} + +static int string_buffer_concat_value_free(StringBuffer *s, JSValue v) +{ + JSString *p; + int res; + + if (s->error_status) { + /* prevent exception overload */ + JS_FreeValue(s->ctx, v); + return -1; + } + if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { + v = JS_ToStringFree(s->ctx, v); + if (JS_IsException(v)) + return string_buffer_set_error(s); + } + p = JS_VALUE_GET_STRING(v); + res = string_buffer_concat(s, p, 0, p->len); + JS_FreeValue(s->ctx, v); + return res; +} + +static int string_buffer_fill(StringBuffer *s, int c, int count) +{ + /* XXX: optimize */ + if (s->len + count > s->size) { + if (string_buffer_realloc(s, s->len + count, c)) + return -1; + } + while (count-- > 0) { + if (string_buffer_putc16(s, c)) + return -1; + } + return 0; +} + +static JSValue string_buffer_end(StringBuffer *s) +{ + JSString *str; + str = s->str; + if (s->error_status) + return JS_EXCEPTION; + if (s->len == 0) { + js_free(s->ctx, str); + s->str = NULL; + return js_empty_string(s->ctx->rt); + } + if (s->len < s->size) { + /* smaller size so js_realloc should not fail, but OK if it does */ + /* XXX: should add some slack to avoid unnecessary calls */ + /* XXX: might need to use malloc+free to ensure smaller size */ + str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) + + (s->len << s->is_wide_char) + 1 - s->is_wide_char); + if (str == NULL) + str = s->str; + s->str = str; + } + if (!s->is_wide_char) + str8(str)[s->len] = 0; +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_add_tail(&str->link, &s->ctx->rt->string_list); +#endif + str->is_wide_char = s->is_wide_char; + str->len = s->len; + s->str = NULL; + return JS_MKPTR(JS_TAG_STRING, str); +} + +/* create a string from a UTF-8 buffer */ +JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) +{ + JSString *str; + size_t len; + int kind; + + if (buf_len <= 0) { + return js_empty_string(ctx->rt); + } + /* Compute string kind and length: 7-bit, 8-bit, 16-bit, 16-bit UTF-16 */ + kind = utf8_scan(buf, buf_len, &len); + if (len > JS_STRING_LEN_MAX) + return JS_ThrowRangeError(ctx, "invalid string length"); + + switch (kind) { + case UTF8_PLAIN_ASCII: + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + memcpy(str8(str), buf, len); + str8(str)[len] = '\0'; + break; + case UTF8_NON_ASCII: + /* buf contains non-ASCII code-points, but limited to 8-bit values */ + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + utf8_decode_buf8(str8(str), len + 1, buf, buf_len); + break; + default: + // This causes a potential problem in JS_ThrowError if message is invalid + //if (kind & UTF8_HAS_ERRORS) + // return JS_ThrowRangeError(ctx, "invalid UTF-8 sequence"); + str = js_alloc_string(ctx, len, 1); + if (!str) + return JS_EXCEPTION; + utf8_decode_buf16(str16(str), len, buf, buf_len); + break; + } + return JS_MKPTR(JS_TAG_STRING, str); +} + +JSValue JS_NewTwoByteString(JSContext *ctx, const uint16_t *buf, size_t len) +{ + JSString *str; + + if (!len) + return js_empty_string(ctx->rt); + str = js_alloc_string(ctx, len, 1); + if (!str) + return JS_EXCEPTION; + memcpy(str16(str), buf, len * sizeof(*buf)); + return JS_MKPTR(JS_TAG_STRING, str); +} + +static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, + JSValue str2, const char *str3) +{ + StringBuffer b_s, *b = &b_s; + int len1, len3; + JSString *p; + + if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) { + str2 = JS_ToStringFree(ctx, str2); + if (JS_IsException(str2)) + goto fail; + } + p = JS_VALUE_GET_STRING(str2); + len1 = strlen(str1); + len3 = strlen(str3); + + if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char)) + goto fail; + + string_buffer_write8(b, (const uint8_t *)str1, len1); + string_buffer_concat(b, p, 0, p->len); + string_buffer_write8(b, (const uint8_t *)str3, len3); + + JS_FreeValue(ctx, str2); + return string_buffer_end(b); + + fail: + JS_FreeValue(ctx, str2); + return JS_EXCEPTION; +} + +/* `str` may be pure ASCII or UTF-8 encoded */ +JSValue JS_NewAtomString(JSContext *ctx, const char *str) +{ + JSAtom atom = JS_NewAtom(ctx, str); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; + JSValue val = JS_AtomToString(ctx, atom); + JS_FreeAtom(ctx, atom); + return val; +} + +/* return (NULL, 0) if exception. */ +/* return pointer into a JSString with a live ref_count */ +/* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */ +const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, + bool cesu8) +{ + JSValue val; + JSString *str, *str_new; + int pos, len, c, c1; + JSObject *p; + uint8_t *q; + + if (JS_VALUE_GET_TAG(val1) == JS_TAG_STRING) { + val = js_dup(val1); + goto go; + } + + val = JS_ToString(ctx, val1); + if (!JS_IsException(val)) + goto go; + + // Stringification can fail when there is an exception pending, + // e.g. a stack overflow InternalError. Special-case exception + // objects to make debugging easier, look up the .message property + // and stringify that. + if (JS_VALUE_GET_TAG(val1) != JS_TAG_OBJECT) + goto fail; + + p = JS_VALUE_GET_OBJ(val1); + if (p->class_id != JS_CLASS_ERROR) + goto fail; + + val = JS_GetProperty(ctx, val1, JS_ATOM_message); + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) { + JS_FreeValue(ctx, val); + goto fail; + } + +go: + str = JS_VALUE_GET_STRING(val); + len = str->len; + if (!str->is_wide_char) { + const uint8_t *src = str8(str); + int count; + + /* count the number of non-ASCII characters */ + /* Scanning the whole string is required for ASCII strings, + and computing the number of non-ASCII bytes is less expensive + than testing each byte, hence this method is faster for ASCII + strings, which is the most common case. + */ + count = 0; + for (pos = 0; pos < len; pos++) { + count += src[pos] >> 7; + } + if (count == 0 && str->kind == JS_STRING_KIND_NORMAL) { + if (plen) + *plen = len; + return (const char *)src; + } + str_new = js_alloc_string(ctx, len + count, 0); + if (!str_new) + goto fail; + q = str8(str_new); + for (pos = 0; pos < len; pos++) { + c = src[pos]; + if (c < 0x80) { + *q++ = c; + } else { + *q++ = (c >> 6) | 0xc0; + *q++ = (c & 0x3f) | 0x80; + } + } + } else { + const uint16_t *src = str16(str); + /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may + produce 4 bytes but use 2 code points. + */ + str_new = js_alloc_string(ctx, len * 3, 0); + if (!str_new) + goto fail; + q = str8(str_new); + pos = 0; + while (pos < len) { + c = src[pos++]; + if (c < 0x80) { + *q++ = c; + } else { + if (is_hi_surrogate(c)) { + if (pos < len && !cesu8) { + c1 = src[pos]; + if (is_lo_surrogate(c1)) { + pos++; + c = from_surrogate(c, c1); + } else { + /* Keep unmatched surrogate code points */ + /* c = 0xfffd; */ /* error */ + } + } else { + /* Keep unmatched surrogate code points */ + /* c = 0xfffd; */ /* error */ + } + } + q += utf8_encode(q, c); + } + } + } + + *q = '\0'; + str_new->len = q - str8(str_new); + JS_FreeValue(ctx, val); + if (plen) + *plen = str_new->len; + return (const char *)str8(str_new); + fail: + if (plen) + *plen = 0; + return NULL; +} + +void JS_FreeCString(JSContext *ctx, const char *ptr) +{ + if (!ptr) + return; + /* purposely removing constness */ + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, (JSString *)ptr - 1)); +} + +static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len) +{ + int c, i; + for(i = 0; i < len; i++) { + c = src1[i] - src2[i]; + if (c != 0) + return c; + } + return 0; +} + +static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len) +{ + int c, i; + for(i = 0; i < len; i++) { + c = src1[i] - src2[i]; + if (c != 0) + return c; + } + return 0; +} + +static int js_string_memcmp(JSString *p1, JSString *p2, int len) +{ + int res; + + if (likely(!p1->is_wide_char)) { + if (likely(!p2->is_wide_char)) + res = memcmp(str8(p1), str8(p2), len); + else + res = -memcmp16_8(str16(p2), str8(p1), len); + } else { + if (!p2->is_wide_char) + res = memcmp16_8(str16(p1), str8(p2), len); + else + res = memcmp16(str16(p1), str16(p2), len); + } + return res; +} + +static bool js_string_eq(JSString *p1, JSString *p2) { + if (p1->len != p2->len) + return false; + return js_string_memcmp(p1, p2, p1->len) == 0; +} + +/* return < 0, 0 or > 0 */ +static int js_string_compare(JSString *p1, JSString *p2) +{ + int res, len; + len = min_int(p1->len, p2->len); + res = js_string_memcmp(p1, p2, len); + if (res == 0) + res = compare_u32(p1->len, p2->len); + return res; +} + +static void copy_str16(uint16_t *dst, JSString *p, int offset, int len) +{ + if (p->is_wide_char) { + memcpy(dst, str16(p) + offset, len * 2); + } else { + const uint8_t *src1 = str8(p) + offset; + int i; + + for(i = 0; i < len; i++) + dst[i] = src1[i]; + } +} + +static JSValue JS_ConcatString1(JSContext *ctx, JSString *p1, JSString *p2) +{ + JSString *p; + uint32_t len; + int is_wide_char; + + len = p1->len + p2->len; + if (len > JS_STRING_LEN_MAX) + return JS_ThrowRangeError(ctx, "invalid string length"); + is_wide_char = p1->is_wide_char | p2->is_wide_char; + p = js_alloc_string(ctx, len, is_wide_char); + if (!p) + return JS_EXCEPTION; + if (!is_wide_char) { + memcpy(str8(p), str8(p1), p1->len); + memcpy(str8(p) + p1->len, str8(p2), p2->len); + str8(p)[len] = '\0'; + } else { + copy_str16(str16(p), p1, 0, p1->len); + copy_str16(str16(p) + p1->len, p2, 0, p2->len); + } + return JS_MKPTR(JS_TAG_STRING, p); +} + +/* op1 and op2 are converted to strings. For convience, op1 or op2 = + JS_EXCEPTION are accepted and return JS_EXCEPTION. */ +static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) +{ + JSValue ret; + JSString *p1, *p2; + + if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) { + op1 = JS_ToStringFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + } + if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) { + op2 = JS_ToStringFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } + } + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + + /* XXX: could also check if p1 is empty */ + if (p2->len == 0) { + goto ret_op1; + } + if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char + && js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) { + /* Concatenate in place in available space at the end of p1 */ + if (p1->is_wide_char) { + memcpy(str16(p1) + p1->len, str16(p2), p2->len << 1); + p1->len += p2->len; + } else { + memcpy(str8(p1) + p1->len, str8(p2), p2->len); + p1->len += p2->len; + str8(p1)[p1->len] = '\0'; + } + ret_op1: + JS_FreeValue(ctx, op2); + return op1; + } + ret = JS_ConcatString1(ctx, p1, p2); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return ret; +} + +/* Shape support */ + +static inline size_t get_shape_size(size_t hash_size, size_t prop_size) +{ + return hash_size * sizeof(uint32_t) + sizeof(JSShape) + + prop_size * sizeof(JSShapeProperty); +} + +static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size) +{ + return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); +} + +static inline uint32_t *prop_hash_end(JSShape *sh) +{ + return (uint32_t *)sh; +} + +static inline void *get_alloc_from_shape(JSShape *sh) +{ + return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1); +} + +static inline JSShapeProperty *get_shape_prop(JSShape *sh) +{ + return sh->prop; +} + +static int init_shape_hash(JSRuntime *rt) +{ + rt->shape_hash_bits = 6; /* 64 shapes */ + rt->shape_hash_size = 1 << rt->shape_hash_bits; + rt->shape_hash_count = 0; + rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * + rt->shape_hash_size); + if (!rt->shape_hash) + return -1; + return 0; +} + +/* same magic hash multiplier as the Linux kernel */ +static uint32_t shape_hash(uint32_t h, uint32_t val) +{ + return (h + val) * 0x9e370001; +} + +/* truncate the shape hash to 'hash_bits' bits */ +static uint32_t get_shape_hash(uint32_t h, int hash_bits) +{ + return h >> (32 - hash_bits); +} + +static uint32_t shape_initial_hash(JSObject *proto) +{ + uint32_t h; + h = shape_hash(1, (uintptr_t)proto); + if (sizeof(proto) > 4) + h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32); + return h; +} + +static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits) +{ + int new_shape_hash_size, i; + uint32_t h; + JSShape **new_shape_hash, *sh, *sh_next; + + new_shape_hash_size = 1 << new_shape_hash_bits; + new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * + new_shape_hash_size); + if (!new_shape_hash) + return -1; + for(i = 0; i < rt->shape_hash_size; i++) { + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) { + sh_next = sh->shape_hash_next; + h = get_shape_hash(sh->hash, new_shape_hash_bits); + sh->shape_hash_next = new_shape_hash[h]; + new_shape_hash[h] = sh; + } + } + js_free_rt(rt, rt->shape_hash); + rt->shape_hash_bits = new_shape_hash_bits; + rt->shape_hash_size = new_shape_hash_size; + rt->shape_hash = new_shape_hash; + return 0; +} + +static void js_shape_hash_link(JSRuntime *rt, JSShape *sh) +{ + uint32_t h; + h = get_shape_hash(sh->hash, rt->shape_hash_bits); + sh->shape_hash_next = rt->shape_hash[h]; + rt->shape_hash[h] = sh; + rt->shape_hash_count++; +} + +static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh) +{ + uint32_t h; + JSShape **psh; + + h = get_shape_hash(sh->hash, rt->shape_hash_bits); + psh = &rt->shape_hash[h]; + while (*psh != sh) + psh = &(*psh)->shape_hash_next; + *psh = sh->shape_hash_next; + rt->shape_hash_count--; +} + +/* create a new empty shape with prototype 'proto' */ +static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, + int hash_size, int prop_size) +{ + JSRuntime *rt = ctx->rt; + void *sh_alloc; + JSShape *sh; + + /* resize the shape hash table if necessary */ + if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) { + resize_shape_hash(rt, rt->shape_hash_bits + 1); + } + + sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size)); + if (!sh_alloc) + return NULL; + sh = get_shape_from_alloc(sh_alloc, hash_size); + sh->header.ref_count = 1; + add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); + if (proto) + js_dup(JS_MKPTR(JS_TAG_OBJECT, proto)); + sh->proto = proto; + memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) * + hash_size); + sh->prop_hash_mask = hash_size - 1; + sh->prop_size = prop_size; + sh->prop_count = 0; + sh->deleted_prop_count = 0; + + /* insert in the hash table */ + sh->hash = shape_initial_hash(proto); + sh->is_hashed = true; + sh->has_small_array_index = false; + js_shape_hash_link(ctx->rt, sh); + return sh; +} + +static JSShape *js_new_shape(JSContext *ctx, JSObject *proto) +{ + return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE, + JS_PROP_INITIAL_SIZE); +} + +/* The shape is cloned. The new shape is not inserted in the shape + hash table */ +static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1) +{ + JSShape *sh; + void *sh_alloc, *sh_alloc1; + size_t size; + JSShapeProperty *pr; + uint32_t i, hash_size; + + hash_size = sh1->prop_hash_mask + 1; + size = get_shape_size(hash_size, sh1->prop_size); + sh_alloc = js_malloc(ctx, size); + if (!sh_alloc) + return NULL; + sh_alloc1 = get_alloc_from_shape(sh1); + memcpy(sh_alloc, sh_alloc1, size); + sh = get_shape_from_alloc(sh_alloc, hash_size); + sh->header.ref_count = 1; + add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); + sh->is_hashed = false; + if (sh->proto) { + js_dup(JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + } + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { + JS_DupAtom(ctx, pr->atom); + } + return sh; +} + +static JSShape *js_dup_shape(JSShape *sh) +{ + sh->header.ref_count++; + return sh; +} + +static void js_free_shape0(JSRuntime *rt, JSShape *sh) +{ + uint32_t i; + JSShapeProperty *pr; + + assert(sh->header.ref_count == 0); + if (sh->is_hashed) + js_shape_hash_unlink(rt, sh); + if (sh->proto != NULL) { + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + } + pr = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JS_FreeAtomRT(rt, pr->atom); + pr++; + } + remove_gc_object(&sh->header); + js_free_rt(rt, get_alloc_from_shape(sh)); +} + +static void js_free_shape(JSRuntime *rt, JSShape *sh) +{ + if (unlikely(--sh->header.ref_count <= 0)) { + js_free_shape0(rt, sh); + } +} + +static void js_free_shape_null(JSRuntime *rt, JSShape *sh) +{ + if (sh) + js_free_shape(rt, sh); +} + +/* make space to hold at least 'count' properties */ +static no_inline int resize_properties(JSContext *ctx, JSShape **psh, + JSObject *p, uint32_t count) +{ + JSShape *sh; + uint32_t new_size, new_hash_size, new_hash_mask, i; + JSShapeProperty *pr; + void *sh_alloc; + intptr_t h; + + sh = *psh; + new_size = max_int(count, sh->prop_size * 3 / 2); + /* Reallocate prop array first to avoid crash or size inconsistency + in case of memory allocation failure */ + if (p) { + JSProperty *new_prop; + new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); + if (unlikely(!new_prop)) + return -1; + p->prop = new_prop; + } + new_hash_size = sh->prop_hash_mask + 1; + while (new_hash_size < new_size) + new_hash_size = 2 * new_hash_size; + if (new_hash_size != (sh->prop_hash_mask + 1)) { + JSShape *old_sh; + /* resize the hash table and the properties */ + old_sh = sh; + sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); + if (!sh_alloc) + return -1; + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_del(&old_sh->header.link); + /* copy all the fields and the properties */ + memcpy(sh, old_sh, + sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + new_hash_mask = new_hash_size - 1; + sh->prop_hash_mask = new_hash_mask; + memset(prop_hash_end(sh) - new_hash_size, 0, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); + for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { + if (pr->atom != JS_ATOM_NULL) { + h = ((uintptr_t)pr->atom & new_hash_mask); + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = i + 1; + } + } + js_free(ctx, get_alloc_from_shape(old_sh)); + } else { + /* only resize the properties */ + list_del(&sh->header.link); + sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh), + get_shape_size(new_hash_size, new_size)); + if (unlikely(!sh_alloc)) { + /* insert again in the GC list */ + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + return -1; + } + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + } + *psh = sh; + sh->prop_size = new_size; + return 0; +} + +/* remove the deleted properties. */ +static int compact_properties(JSContext *ctx, JSObject *p) +{ + JSShape *sh, *old_sh; + void *sh_alloc; + intptr_t h; + uint32_t new_hash_size, i, j, new_hash_mask, new_size; + JSShapeProperty *old_pr, *pr; + JSProperty *prop, *new_prop; + + sh = p->shape; + assert(!sh->is_hashed); + + new_size = max_int(JS_PROP_INITIAL_SIZE, + sh->prop_count - sh->deleted_prop_count); + assert(new_size <= sh->prop_size); + + new_hash_size = sh->prop_hash_mask + 1; + while ((new_hash_size / 2) >= new_size) + new_hash_size = new_hash_size / 2; + new_hash_mask = new_hash_size - 1; + + /* resize the hash table and the properties */ + old_sh = sh; + sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); + if (!sh_alloc) + return -1; + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_del(&old_sh->header.link); + memcpy(sh, old_sh, sizeof(JSShape)); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + + memset(prop_hash_end(sh) - new_hash_size, 0, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); + + j = 0; + old_pr = old_sh->prop; + pr = sh->prop; + prop = p->prop; + for(i = 0; i < sh->prop_count; i++) { + if (old_pr->atom != JS_ATOM_NULL) { + pr->atom = old_pr->atom; + pr->flags = old_pr->flags; + h = ((uintptr_t)old_pr->atom & new_hash_mask); + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = j + 1; + prop[j] = prop[i]; + j++; + pr++; + } + old_pr++; + } + assert(j == (sh->prop_count - sh->deleted_prop_count)); + sh->prop_hash_mask = new_hash_mask; + sh->prop_size = new_size; + sh->deleted_prop_count = 0; + sh->prop_count = j; + + p->shape = sh; + js_free(ctx, get_alloc_from_shape(old_sh)); + + /* reduce the size of the object properties */ + new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); + if (new_prop) + p->prop = new_prop; + return 0; +} + +static int add_shape_property(JSContext *ctx, JSShape **psh, + JSObject *p, JSAtom atom, int prop_flags) +{ + JSRuntime *rt = ctx->rt; + JSShape *sh = *psh; + JSShapeProperty *pr, *prop; + uint32_t hash_mask, new_shape_hash = 0; + intptr_t h; + + /* update the shape hash */ + if (sh->is_hashed) { + js_shape_hash_unlink(rt, sh); + new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags); + } + + if (unlikely(sh->prop_count >= sh->prop_size)) { + if (resize_properties(ctx, psh, p, sh->prop_count + 1)) { + /* in case of error, reinsert in the hash table. + sh is still valid if resize_properties() failed */ + if (sh->is_hashed) + js_shape_hash_link(rt, sh); + return -1; + } + sh = *psh; + } + if (sh->is_hashed) { + sh->hash = new_shape_hash; + js_shape_hash_link(rt, sh); + } + /* Initialize the new shape property. + The object property at p->prop[sh->prop_count] is uninitialized */ + prop = get_shape_prop(sh); + pr = &prop[sh->prop_count++]; + pr->atom = JS_DupAtom(ctx, atom); + pr->flags = prop_flags; + sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); + /* add in hash table */ + hash_mask = sh->prop_hash_mask; + h = atom & hash_mask; + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = sh->prop_count; + return 0; +} + +/* find a hashed empty shape matching the prototype. Return NULL if + not found */ +static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto) +{ + JSShape *sh1; + uint32_t h, h1; + + h = shape_initial_hash(proto); + h1 = get_shape_hash(h, rt->shape_hash_bits); + for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { + if (sh1->hash == h && + sh1->proto == proto && + sh1->prop_count == 0) { + return sh1; + } + } + return NULL; +} + +/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if + not found */ +static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh, + JSAtom atom, int prop_flags) +{ + JSShape *sh1; + uint32_t h, h1, i, n; + + h = sh->hash; + h = shape_hash(h, atom); + h = shape_hash(h, prop_flags); + h1 = get_shape_hash(h, rt->shape_hash_bits); + for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { + /* we test the hash first so that the rest is done only if the + shapes really match */ + if (sh1->hash == h && + sh1->proto == sh->proto && + sh1->prop_count == ((n = sh->prop_count) + 1)) { + for(i = 0; i < n; i++) { + if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) || + unlikely(sh1->prop[i].flags != sh->prop[i].flags)) + goto next; + } + if (unlikely(sh1->prop[n].atom != atom) || + unlikely(sh1->prop[n].flags != prop_flags)) + goto next; + return sh1; + } + next: ; + } + return NULL; +} + +static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) +{ + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + int j; + + /* XXX: should output readable class prototype */ + printf("%5d %3d%c %14p %5d %5d", i, + sh->header.ref_count, " *"[sh->is_hashed], + (void *)sh->proto, sh->prop_size, sh->prop_count); + for(j = 0; j < sh->prop_count; j++) { + printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), + sh->prop[j].atom)); + } + printf("\n"); +} + +static __maybe_unused void JS_DumpShapes(JSRuntime *rt) +{ + int i; + JSShape *sh; + struct list_head *el; + JSObject *p; + JSGCObjectHeader *gp; + + printf("JSShapes: {\n"); + printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS"); + for(i = 0; i < rt->shape_hash_size; i++) { + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { + JS_DumpShape(rt, i, sh); + assert(sh->is_hashed); + } + } + /* dump non-hashed shapes */ + list_for_each(el, &rt->gc_obj_list) { + gp = list_entry(el, JSGCObjectHeader, link); + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + p = (JSObject *)gp; + if (!p->shape->is_hashed) { + JS_DumpShape(rt, -1, p->shape); + } + } + } + printf("}\n"); +} + +static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id) +{ + JSObject *p; + + js_trigger_gc(ctx->rt, sizeof(JSObject)); + p = js_malloc(ctx, sizeof(JSObject)); + if (unlikely(!p)) + goto fail; + p->class_id = class_id; + p->extensible = true; + p->free_mark = 0; + p->is_exotic = 0; + p->fast_array = 0; + p->is_constructor = 0; + p->is_uncatchable_error = 0; + p->tmp_mark = 0; + p->is_HTMLDDA = 0; + p->first_weak_ref = NULL; + p->u.opaque = NULL; + p->shape = sh; + p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size); + if (unlikely(!p->prop)) { + js_free(ctx, p); + fail: + js_free_shape(ctx->rt, sh); + return JS_EXCEPTION; + } + + switch(class_id) { + case JS_CLASS_OBJECT: + break; + case JS_CLASS_ARRAY: + { + JSProperty *pr; + p->is_exotic = 1; + p->fast_array = 1; + p->u.array.u.values = NULL; + p->u.array.count = 0; + p->u.array.u1.size = 0; + /* the length property is always the first one */ + if (likely(sh == ctx->array_shape)) { + pr = &p->prop[0]; + } else { + /* only used for the first array */ + /* cannot fail */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_LENGTH); + } + pr->u.value = js_int32(0); + } + break; + case JS_CLASS_C_FUNCTION: + p->prop[0].u.value = JS_UNDEFINED; + break; + case JS_CLASS_ARGUMENTS: + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + case JS_CLASS_FLOAT16_ARRAY: + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: + p->is_exotic = 1; + p->fast_array = 1; + p->u.array.u.ptr = NULL; + p->u.array.count = 0; + break; + case JS_CLASS_DATAVIEW: + p->u.array.u.ptr = NULL; + p->u.array.count = 0; + break; + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_SYMBOL: + case JS_CLASS_DATE: + case JS_CLASS_BIG_INT: + p->u.object_data = JS_UNDEFINED; + goto set_exotic; + case JS_CLASS_REGEXP: + p->u.regexp.pattern = NULL; + p->u.regexp.bytecode = NULL; + goto set_exotic; + default: + set_exotic: + if (ctx->rt->class_array[class_id].exotic) { + p->is_exotic = 1; + } + break; + } + p->header.ref_count = 1; + add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT); + return JS_MKPTR(JS_TAG_OBJECT, p); +} + +static JSObject *get_proto_obj(JSValueConst proto_val) +{ + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) + return NULL; + else + return JS_VALUE_GET_OBJ(proto_val); +} + +/* WARNING: proto must be an object or JS_NULL */ +JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val, + JSClassID class_id) +{ + JSShape *sh; + JSObject *proto; + + proto = get_proto_obj(proto_val); + sh = find_hashed_shape_proto(ctx->rt, proto); + if (likely(sh)) { + sh = js_dup_shape(sh); + } else { + sh = js_new_shape(ctx, proto); + if (!sh) + return JS_EXCEPTION; + } + return JS_NewObjectFromShape(ctx, sh, class_id); +} + +static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val) +{ + JSObject *p; + + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + switch(p->class_id) { + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_SYMBOL: + case JS_CLASS_DATE: + case JS_CLASS_BIG_INT: + JS_FreeValue(ctx, p->u.object_data); + p->u.object_data = val; + return 0; + } + } + JS_FreeValue(ctx, val); + if (!JS_IsException(obj)) + JS_ThrowTypeError(ctx, "invalid object type"); + return -1; +} + +JSValue JS_NewObjectClass(JSContext *ctx, JSClassID class_id) +{ + return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id); +} + +JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto) +{ + return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); +} + +JSValue JS_NewObjectFrom(JSContext *ctx, int count, const JSAtom *props, + const JSValue *values) +{ + JSShapeProperty *pr; + uint32_t *hash; + JSRuntime *rt; + JSObject *p; + JSShape *sh; + JSValue obj; + JSAtom atom; + intptr_t h; + int i; + + rt = ctx->rt; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + if (count > 0) { + p = JS_VALUE_GET_OBJ(obj); + sh = p->shape; + assert(sh->is_hashed); + assert(sh->header.ref_count == 1); + js_shape_hash_unlink(rt, sh); + if (resize_properties(ctx, &sh, p, count)) { + js_shape_hash_link(rt, sh); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + p->shape = sh; + for (i = 0; i < count; i++) { + atom = props[i]; + pr = &sh->prop[i]; + sh->hash = shape_hash(shape_hash(sh->hash, atom), JS_PROP_C_W_E); + sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); + h = atom & sh->prop_hash_mask; + hash = &prop_hash_end(sh)[-h - 1]; + pr->hash_next = *hash; + *hash = i + 1; + pr->atom = JS_DupAtom(ctx, atom); + pr->flags = JS_PROP_C_W_E; + p->prop[i].u.value = values[i]; + } + js_shape_hash_link(rt, sh); + sh->prop_count = count; + } + return obj; +} + +JSValue JS_NewObjectFromStr(JSContext *ctx, int count, const char **props, + const JSValue *values) +{ + JSAtom atoms_s[16], *atoms = atoms_s; + JSValue ret; + int i; + + i = 0; + ret = JS_EXCEPTION; + if (count < 1) + goto out; + if (count > (int)countof(atoms_s)) { + atoms = js_malloc(ctx, count * sizeof(*atoms)); + if (!atoms) + return JS_EXCEPTION; + } + for (i = 0; i < count; i++) { + atoms[i] = JS_NewAtom(ctx, props[i]); + if (atoms[i] == JS_ATOM_NULL) + goto out; + } + ret = JS_NewObjectFrom(ctx, count, atoms, values); +out: + while (i-- > 0) + JS_FreeAtom(ctx, atoms[i]); + if (atoms != atoms_s) + js_free(ctx, atoms); + return ret; +} + +JSValue JS_NewArray(JSContext *ctx) +{ + return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape), + JS_CLASS_ARRAY); +} + +// note: takes ownership of |values|, unlike js_create_array +JSValue JS_NewArrayFrom(JSContext *ctx, int count, const JSValue *values) +{ + JSObject *p; + JSValue obj; + int i; + + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + goto exception; + if (count > 0) { + p = JS_VALUE_GET_OBJ(obj); + if (expand_fast_array(ctx, p, count)) { + JS_FreeValue(ctx, obj); + goto exception; + } + p->u.array.count = count; + p->prop[0].u.value = js_int32(count); + memcpy(p->u.array.u.values, values, count * sizeof(*values)); + } + return obj; +exception: + for (i = 0; i < count; i++) + JS_FreeValue(ctx, values[i]); + return JS_EXCEPTION; +} + +JSValue JS_NewObject(JSContext *ctx) +{ + /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */ + return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT); +} + +static void js_function_set_properties(JSContext *ctx, JSValue func_obj, + JSAtom name, int len) +{ + /* ES6 feature non compatible with ES5.1: length is configurable */ + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, js_int32(len), + JS_PROP_CONFIGURABLE); + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, + JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE); +} + +static bool js_class_has_bytecode(JSClassID class_id) +{ + return (class_id == JS_CLASS_BYTECODE_FUNCTION || + class_id == JS_CLASS_GENERATOR_FUNCTION || + class_id == JS_CLASS_ASYNC_FUNCTION || + class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION); +} + +/* return NULL without exception if not a function or no bytecode */ +static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return NULL; + p = JS_VALUE_GET_OBJ(val); + if (!js_class_has_bytecode(p->class_id)) + return NULL; + return p->u.func.function_bytecode; +} + +static void js_method_set_home_object(JSContext *ctx, JSValue func_obj, + JSValue home_obj) +{ + JSObject *p, *p1; + JSFunctionBytecode *b; + + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(func_obj); + if (!js_class_has_bytecode(p->class_id)) + return; + b = p->u.func.function_bytecode; + if (b->need_home_object) { + p1 = p->u.func.home_object; + if (p1) { + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT) + p1 = JS_VALUE_GET_OBJ(js_dup(home_obj)); + else + p1 = NULL; + p->u.func.home_object = p1; + } +} + +static JSValue js_get_function_name(JSContext *ctx, JSAtom name) +{ + JSValue name_str; + + name_str = JS_AtomToString(ctx, name); + if (JS_AtomSymbolHasDescription(ctx, name)) { + name_str = JS_ConcatString3(ctx, "[", name_str, "]"); + } + return name_str; +} + +/* Modify the name of a method according to the atom and + 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and + JS_PROP_HAS_SET. Also set the home object of the method. + Return < 0 if exception. */ +static int js_method_set_properties(JSContext *ctx, JSValue func_obj, + JSAtom name, int flags, JSValue home_obj) +{ + JSValue name_str; + + name_str = js_get_function_name(ctx, name); + if (flags & JS_PROP_HAS_GET) { + name_str = JS_ConcatString3(ctx, "get ", name_str, ""); + } else if (flags & JS_PROP_HAS_SET) { + name_str = JS_ConcatString3(ctx, "set ", name_str, ""); + } + if (JS_IsException(name_str)) + return -1; + if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str, + JS_PROP_CONFIGURABLE) < 0) + return -1; + js_method_set_home_object(ctx, func_obj, home_obj); + return 0; +} + +/* Note: at least 'length' arguments will be readable in 'argv' */ +/* `name` may be NULL, pure ASCII or UTF-8 encoded */ +JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic, + JSValueConst proto_val) +{ + JSValue func_obj; + JSObject *p; + JSAtom name_atom; + + func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); + if (JS_IsException(func_obj)) + return func_obj; + p = JS_VALUE_GET_OBJ(func_obj); + p->u.cfunc.realm = JS_DupContext(ctx); + p->u.cfunc.c_function.generic = func; + p->u.cfunc.length = length; + p->u.cfunc.cproto = cproto; + p->u.cfunc.magic = magic; + p->is_constructor = (cproto == JS_CFUNC_constructor || + cproto == JS_CFUNC_constructor_magic || + cproto == JS_CFUNC_constructor_or_func || + cproto == JS_CFUNC_constructor_or_func_magic); + name_atom = JS_ATOM_empty_string; + if (name && *name) { + name_atom = JS_NewAtom(ctx, name); + if (name_atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; + } + } + js_function_set_properties(ctx, func_obj, name_atom, length); + JS_FreeAtom(ctx, name_atom); + return func_obj; +} + +/* Note: at least 'length' arguments will be readable in 'argv' */ +JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic) +{ + return JS_NewCFunction3(ctx, func, name, length, cproto, magic, + ctx->function_proto); +} + +typedef struct JSCFunctionDataRecord { + JSCFunctionData *func; + uint8_t length; + uint8_t data_len; + uint16_t magic; + JSValue data[]; +} JSCFunctionDataRecord; + +static void js_c_function_data_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); + int i; + + if (s) { + for(i = 0; i < s->data_len; i++) { + JS_FreeValueRT(rt, s->data[i]); + } + js_free_rt(rt, s); + } +} + +static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); + int i; + + if (s) { + for(i = 0; i < s->data_len; i++) { + JS_MarkValue(rt, s->data[i], mark_func); + } + } +} + +static JSValue js_call_c_function_data(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_val, + int argc, JSValueConst *argv, int flags) +{ + JSRuntime *rt = ctx->rt; + JSStackFrame sf_s, *sf = &sf_s, *prev_sf; + JSCFunctionDataRecord *s; + JSValueConst *arg_buf; + JSValue ret; + size_t stack_size; + int arg_count; + int i; + + s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); + if (!s) + return JS_EXCEPTION; // can't really happen + arg_buf = argv; + arg_count = s->length; + if (unlikely(argc < arg_count)) { + stack_size = arg_count * sizeof(arg_buf[0]); + if (js_check_stack_overflow(rt, stack_size)) + return JS_ThrowStackOverflow(ctx); + arg_buf = alloca(stack_size); + for(i = 0; i < argc; i++) + arg_buf[i] = argv[i]; + for(i = argc; i < arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + } + prev_sf = rt->current_stack_frame; + sf->prev_frame = prev_sf; + rt->current_stack_frame = sf; + // TODO(bnoordhuis) switch realms like js_call_c_function does + sf->is_strict_mode = false; + sf->cur_func = unsafe_unconst(func_obj); + sf->arg_count = argc; + ret = s->func(ctx, this_val, argc, arg_buf, s->magic, vc(s->data)); + rt->current_stack_frame = sf->prev_frame; + return ret; +} + +JSValue JS_NewCFunctionData2(JSContext *ctx, JSCFunctionData *func, + const char *name, + int length, int magic, int data_len, + JSValueConst *data) +{ + JSCFunctionDataRecord *s; + JSAtom name_atom; + JSValue func_obj; + int i; + + func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_C_FUNCTION_DATA); + if (JS_IsException(func_obj)) + return func_obj; + s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue)); + if (!s) { + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; + } + s->func = func; + s->length = length; + s->data_len = data_len; + s->magic = magic; + for(i = 0; i < data_len; i++) + s->data[i] = js_dup(data[i]); + JS_SetOpaqueInternal(func_obj, s); + name_atom = JS_ATOM_empty_string; + if (name && *name) { + name_atom = JS_NewAtom(ctx, name); + if (name_atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; + } + } + js_function_set_properties(ctx, func_obj, name_atom, length); + JS_FreeAtom(ctx, name_atom); + return func_obj; +} + +JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, + int length, int magic, int data_len, + JSValueConst *data) +{ + return JS_NewCFunctionData2(ctx, func, NULL, length, magic, data_len, data); +} + +static JSContext *js_autoinit_get_realm(JSProperty *pr) +{ + return (JSContext *)(pr->u.init.realm_and_id & ~3); +} + +static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr) +{ + return pr->u.init.realm_and_id & 3; +} + +static void js_autoinit_free(JSRuntime *rt, JSProperty *pr) +{ + JS_FreeContext(js_autoinit_get_realm(pr)); +} + +static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr, + JS_MarkFunc *mark_func) +{ + mark_func(rt, &js_autoinit_get_realm(pr)->header); +} + +static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) +{ + if (unlikely(prop_flags & JS_PROP_TMASK)) { + if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (pr->u.getset.getter) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + free_var_ref(rt, pr->u.var_ref); + } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + js_autoinit_free(rt, pr); + } + } else { + JS_FreeValueRT(rt, pr->u.value); + } +} + +static force_inline JSShapeProperty *find_own_property1(JSObject *p, + JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + return pr; + } + h = pr->hash_next; + } + return NULL; +} + +static force_inline JSShapeProperty *find_own_property(JSProperty **ppr, + JSObject *p, + JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + *ppr = &p->prop[h - 1]; + /* the compiler should be able to assume that pr != NULL here */ + return pr; + } + h = pr->hash_next; + } + *ppr = NULL; + return NULL; +} + +static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) +{ + if (var_ref) { + assert(var_ref->header.ref_count > 0); + if (--var_ref->header.ref_count == 0) { + if (var_ref->is_detached) { + JS_FreeValueRT(rt, var_ref->value); + remove_gc_object(&var_ref->header); + } else { + list_del(&var_ref->header.link); /* still on the stack */ + } + js_free_rt(rt, var_ref); + } + } +} + +static void js_array_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + int i; + + for(i = 0; i < p->u.array.count; i++) { + JS_FreeValueRT(rt, p->u.array.u.values[i]); + } + js_free_rt(rt, p->u.array.u.values); +} + +static void js_array_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + int i; + + for(i = 0; i < p->u.array.count; i++) { + JS_MarkValue(rt, p->u.array.u.values[i], mark_func); + } +} + +static void js_object_data_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JS_FreeValueRT(rt, p->u.object_data); + p->u.object_data = JS_UNDEFINED; +} + +static void js_object_data_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JS_MarkValue(rt, p->u.object_data, mark_func); +} + +static void js_c_function_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + + if (p->u.cfunc.realm) + JS_FreeContext(p->u.cfunc.realm); +} + +static void js_c_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + + if (p->u.cfunc.realm) + mark_func(rt, &p->u.cfunc.realm->header); +} + +static void js_bytecode_function_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p1, *p = JS_VALUE_GET_OBJ(val); + JSFunctionBytecode *b; + JSVarRef **var_refs; + int i; + + p1 = p->u.func.home_object; + if (p1) { + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + b = p->u.func.function_bytecode; + if (b) { + var_refs = p->u.func.var_refs; + if (var_refs) { + for(i = 0; i < b->closure_var_count; i++) + free_var_ref(rt, var_refs[i]); + js_free_rt(rt, var_refs); + } + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b)); + } +} + +static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSVarRef **var_refs = p->u.func.var_refs; + JSFunctionBytecode *b = p->u.func.function_bytecode; + int i; + + if (p->u.func.home_object) { + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object), + mark_func); + } + if (b) { + if (var_refs) { + for(i = 0; i < b->closure_var_count; i++) { + JSVarRef *var_ref = var_refs[i]; + if (var_ref && var_ref->is_detached) { + mark_func(rt, &var_ref->header); + } + } + } + /* must mark the function bytecode because template objects may be + part of a cycle */ + JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func); + } +} + +static void js_bound_function_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSBoundFunction *bf = p->u.bound_function; + int i; + + JS_FreeValueRT(rt, bf->func_obj); + JS_FreeValueRT(rt, bf->this_val); + for(i = 0; i < bf->argc; i++) { + JS_FreeValueRT(rt, bf->argv[i]); + } + js_free_rt(rt, bf); +} + +static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSBoundFunction *bf = p->u.bound_function; + int i; + + JS_MarkValue(rt, bf->func_obj, mark_func); + JS_MarkValue(rt, bf->this_val, mark_func); + for(i = 0; i < bf->argc; i++) + JS_MarkValue(rt, bf->argv[i], mark_func); +} + +static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSForInIterator *it = p->u.for_in_iterator; + JS_FreeValueRT(rt, it->obj); + js_free_rt(rt, it); +} + +static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSForInIterator *it = p->u.for_in_iterator; + JS_MarkValue(rt, it->obj, mark_func); +} + +static void free_object(JSRuntime *rt, JSObject *p) +{ + int i; + JSClassFinalizer *finalizer; + JSShape *sh; + JSShapeProperty *pr; + + p->free_mark = 1; /* used to tell the object is invalid when + freeing cycles */ + /* free all the fields */ + sh = p->shape; + pr = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + free_property(rt, &p->prop[i], pr->flags); + pr++; + } + js_free_rt(rt, p->prop); + /* as an optimization we destroy the shape immediately without + putting it in gc_zero_ref_count_list */ + js_free_shape(rt, sh); + + /* fail safe */ + p->shape = NULL; + p->prop = NULL; + + if (unlikely(p->first_weak_ref)) { + reset_weak_ref(rt, &p->first_weak_ref); + } + + finalizer = rt->class_array[p->class_id].finalizer; + if (finalizer) + (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p)); + + /* fail safe */ + p->class_id = 0; + p->u.opaque = NULL; + p->u.func.var_refs = NULL; + p->u.func.home_object = NULL; + + remove_gc_object(&p->header); + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) { + list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); + } else { + js_free_rt(rt, p); + } +} + +static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) +{ + switch(gp->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + free_object(rt, (JSObject *)gp); + break; + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + free_function_bytecode(rt, (JSFunctionBytecode *)gp); + break; + default: + abort(); + } +} + +static void free_zero_refcount(JSRuntime *rt) +{ + struct list_head *el; + JSGCObjectHeader *p; + + rt->gc_phase = JS_GC_PHASE_DECREF; + for(;;) { + el = rt->gc_zero_ref_count_list.next; + if (el == &rt->gc_zero_ref_count_list) + break; + p = list_entry(el, JSGCObjectHeader, link); + assert(p->ref_count == 0); + free_gc_object(rt, p); + } + rt->gc_phase = JS_GC_PHASE_NONE; +} + +/* called with the ref_count of 'v' reaches zero. */ +static void js_free_value_rt(JSRuntime *rt, JSValue v) +{ + uint32_t tag = JS_VALUE_GET_TAG(v); + +#ifdef ENABLE_DUMPS // JS_DUMP_FREE + if (check_dump_flag(rt, JS_DUMP_FREE)) { + /* Prevent invalid object access during GC */ + if ((rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) + || (tag != JS_TAG_OBJECT && tag != JS_TAG_FUNCTION_BYTECODE)) { + printf("Freeing "); + if (tag == JS_TAG_OBJECT) { + JS_DumpObject(rt, JS_VALUE_GET_OBJ(v)); + } else { + JS_DumpValue(rt, v); + printf("\n"); + } + } + } +#endif + + switch(tag) { + case JS_TAG_STRING: + js_free_string0(rt, JS_VALUE_GET_STRING(v)); + break; + case JS_TAG_OBJECT: + case JS_TAG_FUNCTION_BYTECODE: + { + JSGCObjectHeader *p = JS_VALUE_GET_PTR(v); + if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { + list_del(&p->link); + list_add(&p->link, &rt->gc_zero_ref_count_list); + if (rt->gc_phase == JS_GC_PHASE_NONE) { + free_zero_refcount(rt); + } + } + } + break; + case JS_TAG_MODULE: + abort(); /* never freed here */ + break; + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(v); + js_free_rt(rt, p); + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p = JS_VALUE_GET_PTR(v); + JS_FreeAtomStruct(rt, p); + } + break; + default: + printf("js_free_value_rt: unknown tag=%d\n", tag); + abort(); + } +} + +void JS_FreeValueRT(JSRuntime *rt, JSValue v) +{ + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); + if (--p->ref_count <= 0) { + js_free_value_rt(rt, v); + } + } +} + +void JS_FreeValue(JSContext *ctx, JSValue v) +{ + JS_FreeValueRT(ctx->rt, v); +} + +/* garbage collection */ + +static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, + JSGCObjectTypeEnum type) +{ + h->mark = 0; + h->gc_obj_type = type; + list_add_tail(&h->link, &rt->gc_obj_list); +} + +static void remove_gc_object(JSGCObjectHeader *h) +{ + list_del(&h->link); +} + +void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) +{ + if (JS_VALUE_HAS_REF_COUNT(val)) { + switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_OBJECT: + case JS_TAG_FUNCTION_BYTECODE: + mark_func(rt, JS_VALUE_GET_PTR(val)); + break; + default: + break; + } + } +} + +static void mark_weak_map_value(JSRuntime *rt, JSWeakRefRecord *first_weak_ref, JS_MarkFunc *mark_func) { + JSWeakRefRecord *wr; + JSMapRecord *mr; + JSMapState *s; + + for (wr = first_weak_ref; wr != NULL; wr = wr->next_weak_ref) { + if (wr->kind == JS_WEAK_REF_KIND_MAP) { + mr = wr->u.map_record; + s = mr->map; + assert(s->is_weak); + assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ + JS_MarkValue(rt, mr->value, mark_func); + } + } +} + +static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, + JS_MarkFunc *mark_func) +{ + switch(gp->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + { + JSObject *p = (JSObject *)gp; + JSShapeProperty *prs; + JSShape *sh; + int i; + sh = p->shape; + mark_func(rt, &sh->header); + /* mark all the fields */ + prs = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JSProperty *pr = &p->prop[i]; + if (prs->atom != JS_ATOM_NULL) { + if (prs->flags & JS_PROP_TMASK) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (pr->u.getset.getter) + mark_func(rt, &pr->u.getset.getter->header); + if (pr->u.getset.setter) + mark_func(rt, &pr->u.getset.setter->header); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if (pr->u.var_ref->is_detached) { + /* Note: the tag does not matter + provided it is a GC object */ + mark_func(rt, &pr->u.var_ref->header); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + js_autoinit_mark(rt, pr, mark_func); + } + } else { + JS_MarkValue(rt, pr->u.value, mark_func); + } + } + prs++; + } + + if (unlikely(p->first_weak_ref)) { + mark_weak_map_value(rt, p->first_weak_ref, mark_func); + } + + if (p->class_id != JS_CLASS_OBJECT) { + JSClassGCMark *gc_mark; + gc_mark = rt->class_array[p->class_id].gc_mark; + if (gc_mark) + gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func); + } + } + break; + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + /* the template objects can be part of a cycle */ + { + JSFunctionBytecode *b = (JSFunctionBytecode *)gp; + int i; + for(i = 0; i < b->cpool_count; i++) { + JS_MarkValue(rt, b->cpool[i], mark_func); + } + if (b->realm) + mark_func(rt, &b->realm->header); + } + break; + case JS_GC_OBJ_TYPE_VAR_REF: + { + JSVarRef *var_ref = (JSVarRef *)gp; + /* only detached variable referenced are taken into account */ + assert(var_ref->is_detached); + JS_MarkValue(rt, *var_ref->pvalue, mark_func); + } + break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + { + JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp; + if (s->is_active) + async_func_mark(rt, &s->func_state, mark_func); + JS_MarkValue(rt, s->resolving_funcs[0], mark_func); + JS_MarkValue(rt, s->resolving_funcs[1], mark_func); + } + break; + case JS_GC_OBJ_TYPE_SHAPE: + { + JSShape *sh = (JSShape *)gp; + if (sh->proto != NULL) { + mark_func(rt, &sh->proto->header); + } + } + break; + case JS_GC_OBJ_TYPE_JS_CONTEXT: + { + JSContext *ctx = (JSContext *)gp; + JS_MarkContext(rt, ctx, mark_func); + } + break; + default: + abort(); + } +} + +static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p) +{ + assert(p->ref_count > 0); + p->ref_count--; + if (p->ref_count == 0 && p->mark == 1) { + list_del(&p->link); + list_add_tail(&p->link, &rt->tmp_obj_list); + } +} + +static void gc_decref(JSRuntime *rt) +{ + struct list_head *el, *el1; + JSGCObjectHeader *p; + + init_list_head(&rt->tmp_obj_list); + + /* decrement the refcount of all the children of all the GC + objects and move the GC objects with zero refcount to + tmp_obj_list */ + list_for_each_safe(el, el1, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->mark == 0); + mark_children(rt, p, gc_decref_child); + p->mark = 1; + if (p->ref_count == 0) { + list_del(&p->link); + list_add_tail(&p->link, &rt->tmp_obj_list); + } + } +} + +static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p) +{ + p->ref_count++; + if (p->ref_count == 1) { + /* ref_count was 0: remove from tmp_obj_list and add at the + end of gc_obj_list */ + list_del(&p->link); + list_add_tail(&p->link, &rt->gc_obj_list); + p->mark = 0; /* reset the mark for the next GC call */ + } +} + +static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p) +{ + p->ref_count++; +} + +static void gc_scan(JSRuntime *rt) +{ + struct list_head *el; + JSGCObjectHeader *p; + + /* keep the objects with a refcount > 0 and their children. */ + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->ref_count > 0); + p->mark = 0; /* reset the mark for the next GC call */ + mark_children(rt, p, gc_scan_incref_child); + } + + /* restore the refcount of the objects to be deleted. */ + list_for_each(el, &rt->tmp_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + mark_children(rt, p, gc_scan_incref_child2); + } +} + +static void gc_free_cycles(JSRuntime *rt) +{ + struct list_head *el, *el1; + JSGCObjectHeader *p; +#ifdef ENABLE_DUMPS // JS_DUMP_GC_FREE + bool header_done = false; +#endif + + rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES; + + for(;;) { + el = rt->tmp_obj_list.next; + if (el == &rt->tmp_obj_list) + break; + p = list_entry(el, JSGCObjectHeader, link); + /* Only need to free the GC object associated with JS + values. The rest will be automatically removed because they + must be referenced by them. */ + switch(p->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: +#ifdef ENABLE_DUMPS // JS_DUMP_GC_FREE + if (check_dump_flag(rt, JS_DUMP_GC_FREE)) { + if (!header_done) { + printf("Freeing cycles:\n"); + JS_DumpObjectHeader(rt); + header_done = true; + } + JS_DumpGCObject(rt, p); + } +#endif + free_gc_object(rt, p); + break; + default: + list_del(&p->link); + list_add_tail(&p->link, &rt->gc_zero_ref_count_list); + break; + } + } + rt->gc_phase = JS_GC_PHASE_NONE; + + list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || + p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + js_free_rt(rt, p); + } + + init_list_head(&rt->gc_zero_ref_count_list); +} + +void JS_RunGC(JSRuntime *rt) +{ + /* decrement the reference of the children of each object. mark = + 1 after this pass. */ + gc_decref(rt); + + /* keep the GC objects with a non zero refcount and their childs */ + gc_scan(rt); + + /* free the GC objects in a cycle */ + gc_free_cycles(rt); +} + +/* Return false if not an object or if the object has already been + freed (zombie objects are visible in finalizers when freeing + cycles). */ +bool JS_IsLiveObject(JSRuntime *rt, JSValueConst obj) +{ + JSObject *p; + if (!JS_IsObject(obj)) + return false; + p = JS_VALUE_GET_OBJ(obj); + return !p->free_mark; +} + +/* Compute memory used by various object types */ +/* XXX: poor man's approach to handling multiply referenced objects */ +typedef struct JSMemoryUsage_helper { + double memory_used_count; + double str_count; + double str_size; + int64_t js_func_count; + double js_func_size; + int64_t js_func_code_size; + int64_t js_func_pc2line_count; + int64_t js_func_pc2line_size; +} JSMemoryUsage_helper; + +static void compute_value_size(JSValue val, JSMemoryUsage_helper *hp); + +static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp) +{ + if (!str->atom_type) { /* atoms are handled separately */ + double s_ref_count = str->header.ref_count; + hp->str_count += 1 / s_ref_count; + hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) + + 1 - str->is_wide_char) / s_ref_count); + } +} + +static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp) +{ + int memory_used_count, js_func_size, i; + + memory_used_count = 0; + js_func_size = sizeof(*b); + if (b->vardefs) { + js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs); + } + if (b->cpool) { + js_func_size += b->cpool_count * sizeof(*b->cpool); + for (i = 0; i < b->cpool_count; i++) { + JSValue val = b->cpool[i]; + compute_value_size(val, hp); + } + } + if (b->closure_var) { + js_func_size += b->closure_var_count * sizeof(*b->closure_var); + } + if (b->byte_code_buf) { + hp->js_func_code_size += b->byte_code_len; + } + memory_used_count++; + js_func_size += b->source_len + 1; + if (b->pc2line_len) { + memory_used_count++; + hp->js_func_pc2line_count += 1; + hp->js_func_pc2line_size += b->pc2line_len; + } + hp->js_func_size += js_func_size; + hp->js_func_count += 1; + hp->memory_used_count += memory_used_count; +} + +static void compute_value_size(JSValue val, JSMemoryUsage_helper *hp) +{ + switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_STRING: + compute_jsstring_size(JS_VALUE_GET_STRING(val), hp); + break; + case JS_TAG_BIG_INT: + /* should track JSBigInt usage */ + break; + } +} + +void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) +{ + struct list_head *el, *el1; + int i; + JSMemoryUsage_helper mem = { 0 }, *hp = &mem; + + memset(s, 0, sizeof(*s)); + s->malloc_count = rt->malloc_state.malloc_count; + s->malloc_size = rt->malloc_state.malloc_size; + s->malloc_limit = rt->malloc_state.malloc_limit; + + s->memory_used_count = 2; /* rt + rt->class_array */ + s->memory_used_size = sizeof(JSRuntime) + sizeof(JSClass) * rt->class_count; + + list_for_each(el, &rt->context_list) { + JSContext *ctx = list_entry(el, JSContext, link); + JSShape *sh = ctx->array_shape; + s->memory_used_count += 2; /* ctx + ctx->class_proto */ + s->memory_used_size += sizeof(JSContext) + + sizeof(JSValue) * rt->class_count; + s->binary_object_count += ctx->binary_object_count; + s->binary_object_size += ctx->binary_object_size; + + /* the hashed shapes are counted separately */ + if (sh && !sh->is_hashed) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + list_for_each(el1, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el1, JSModuleDef, link); + s->memory_used_count += 1; + s->memory_used_size += sizeof(*m); + if (m->req_module_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries); + } + if (m->export_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries); + for (i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) { + /* potential multiple count */ + s->memory_used_count += 1; + compute_value_size(me->u.local.var_ref->value, hp); + } + } + } + if (m->star_export_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries); + } + if (m->import_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries); + } + compute_value_size(m->module_ns, hp); + compute_value_size(m->func_obj, hp); + } + } + + list_for_each(el, &rt->gc_obj_list) { + JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); + JSObject *p; + JSShape *sh; + JSShapeProperty *prs; + + /* XXX: could count the other GC object types too */ + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) { + compute_bytecode_size((JSFunctionBytecode *)gp, hp); + continue; + } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) { + continue; + } + p = (JSObject *)gp; + sh = p->shape; + s->obj_count++; + if (p->prop) { + s->memory_used_count++; + s->prop_size += sh->prop_size * sizeof(*p->prop); + s->prop_count += sh->prop_count; + prs = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JSProperty *pr = &p->prop[i]; + if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) { + compute_value_size(pr->u.value, hp); + } + prs++; + } + } + /* the hashed shapes are counted separately */ + if (!sh->is_hashed) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + + switch(p->class_id) { + case JS_CLASS_ARRAY: /* u.array | length */ + case JS_CLASS_ARGUMENTS: /* u.array | length */ + s->array_count++; + if (p->fast_array) { + s->fast_array_count++; + if (p->u.array.u.values) { + s->memory_used_count++; + s->memory_used_size += p->u.array.count * + sizeof(*p->u.array.u.values); + s->fast_array_elements += p->u.array.count; + for (i = 0; i < p->u.array.count; i++) { + compute_value_size(p->u.array.u.values[i], hp); + } + } + } + break; + case JS_CLASS_NUMBER: /* u.object_data */ + case JS_CLASS_STRING: /* u.object_data */ + case JS_CLASS_BOOLEAN: /* u.object_data */ + case JS_CLASS_SYMBOL: /* u.object_data */ + case JS_CLASS_DATE: /* u.object_data */ + case JS_CLASS_BIG_INT: /* u.object_data */ + compute_value_size(p->u.object_data, hp); + break; + case JS_CLASS_C_FUNCTION: /* u.cfunc */ + s->c_func_count++; + break; + case JS_CLASS_BYTECODE_FUNCTION: /* u.func */ + { + JSFunctionBytecode *b = p->u.func.function_bytecode; + JSVarRef **var_refs = p->u.func.var_refs; + /* home_object: object will be accounted for in list scan */ + if (var_refs) { + s->memory_used_count++; + s->js_func_size += b->closure_var_count * sizeof(*var_refs); + for (i = 0; i < b->closure_var_count; i++) { + if (var_refs[i]) { + double ref_count = var_refs[i]->header.ref_count; + s->memory_used_count += 1 / ref_count; + s->js_func_size += sizeof(*var_refs[i]) / ref_count; + /* handle non object closed values */ + if (var_refs[i]->pvalue == &var_refs[i]->value) { + /* potential multiple count */ + compute_value_size(var_refs[i]->value, hp); + } + } + } + } + } + break; + case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */ + { + JSBoundFunction *bf = p->u.bound_function; + /* func_obj and this_val are objects */ + for (i = 0; i < bf->argc; i++) { + compute_value_size(bf->argv[i], hp); + } + s->memory_used_count += 1; + s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv); + } + break; + case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */ + { + JSCFunctionDataRecord *fd = p->u.c_function_data_record; + if (fd) { + for (i = 0; i < fd->data_len; i++) { + compute_value_size(fd->data[i], hp); + } + s->memory_used_count += 1; + s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data); + } + } + break; + case JS_CLASS_REGEXP: /* u.regexp */ + compute_jsstring_size(p->u.regexp.pattern, hp); + compute_jsstring_size(p->u.regexp.bytecode, hp); + break; + + case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */ + { + JSForInIterator *it = p->u.for_in_iterator; + if (it) { + compute_value_size(it->obj, hp); + s->memory_used_count += 1; + s->memory_used_size += sizeof(*it); + } + } + break; + case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */ + case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */ + { + JSArrayBuffer *abuf = p->u.array_buffer; + if (abuf) { + s->memory_used_count += 1; + s->memory_used_size += sizeof(*abuf); + if (abuf->data) { + s->memory_used_count += 1; + s->memory_used_size += abuf->byte_length; + } + } + } + break; + case JS_CLASS_GENERATOR: /* u.generator_data */ + case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_DATAVIEW: /* u.typed_array */ + case JS_CLASS_MAP: /* u.map_state */ + case JS_CLASS_SET: /* u.map_state */ + case JS_CLASS_WEAKMAP: /* u.map_state */ + case JS_CLASS_WEAKSET: /* u.map_state */ + case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */ + case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */ + case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */ + case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */ + case JS_CLASS_PROXY: /* u.proxy_data */ + case JS_CLASS_PROMISE: /* u.promise_data */ + case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */ + case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */ + case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */ + case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */ + case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */ + case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */ + /* TODO */ + default: + /* XXX: class definition should have an opaque block size */ + if (p->u.opaque) { + s->memory_used_count += 1; + } + break; + } + } + s->obj_size += s->obj_count * sizeof(JSObject); + + /* hashed shapes */ + s->memory_used_count++; /* rt->shape_hash */ + s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size; + for(i = 0; i < rt->shape_hash_size; i++) { + JSShape *sh; + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + } + + /* atoms */ + s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */ + s->atom_count = rt->atom_count; + s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size + + sizeof(rt->atom_hash[0]) * rt->atom_hash_size; + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p)) { + s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) + + 1 - p->is_wide_char); + } + } + s->str_count = round(mem.str_count); + s->str_size = round(mem.str_size); + s->js_func_count = mem.js_func_count; + s->js_func_size = round(mem.js_func_size); + s->js_func_code_size = mem.js_func_code_size; + s->js_func_pc2line_count = mem.js_func_pc2line_count; + s->js_func_pc2line_size = mem.js_func_pc2line_size; + s->memory_used_count += round(mem.memory_used_count) + + s->atom_count + s->str_count + + s->obj_count + s->shape_count + + s->js_func_count + s->js_func_pc2line_count; + s->memory_used_size += s->atom_size + s->str_size + + s->obj_size + s->prop_size + s->shape_size + + s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size; +} + +void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) +{ + fprintf(fp, "QuickJS-ng memory usage -- %s version, %d-bit, %s Endian, malloc limit: %"PRId64"\n\n", + JS_GetVersion(), (int)sizeof(void *) * 8, is_be() ? "Big" : "Little", s->malloc_limit); + if (rt) { + static const struct { + const char *name; + size_t size; + } object_types[] = { + { "JSRuntime", sizeof(JSRuntime) }, + { "JSContext", sizeof(JSContext) }, + { "JSObject", sizeof(JSObject) }, + { "JSString", sizeof(JSString) }, + { "JSFunctionBytecode", sizeof(JSFunctionBytecode) }, + }; + int i, usage_size_ok = 0; + for(i = 0; i < countof(object_types); i++) { + unsigned int size = object_types[i].size; + void *p = js_malloc_rt(rt, size); + if (p) { + unsigned int size1 = js_malloc_usable_size_rt(rt, p); + if (size1 >= size) { + usage_size_ok = 1; + fprintf(fp, " %3u + %-2u %s\n", + size, size1 - size, object_types[i].name); + } + js_free_rt(rt, p); + } + } + if (!usage_size_ok) { + fprintf(fp, " malloc_usable_size unavailable\n"); + } + { + int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 }; + int class_id; + struct list_head *el; + list_for_each(el, &rt->gc_obj_list) { + JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); + JSObject *p; + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + p = (JSObject *)gp; + obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++; + } + } + fprintf(fp, "\n" "JSObject classes\n"); + if (obj_classes[0]) + fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none"); + for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) { + if (obj_classes[class_id] && class_id < rt->class_count) { + char buf[ATOM_GET_STR_BUF_SIZE]; + fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id, + JS_AtomGetStrRT(rt, buf, sizeof(buf), rt->class_array[class_id].class_name)); + } + } + if (obj_classes[JS_CLASS_INIT_COUNT]) + fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other"); + } + fprintf(fp, "\n"); + } + fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE"); + + if (s->malloc_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n", + "memory allocated", s->malloc_count, s->malloc_size, + (double)s->malloc_size / s->malloc_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n", + "memory used", s->memory_used_count, s->memory_used_size, + MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) / + s->memory_used_count)); + } + if (s->atom_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n", + "atoms", s->atom_count, s->atom_size, + (double)s->atom_size / s->atom_count); + } + if (s->str_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n", + "strings", s->str_count, s->str_size, + (double)s->str_size / s->str_count); + } + if (s->obj_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", + "objects", s->obj_count, s->obj_size, + (double)s->obj_size / s->obj_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", + " properties", s->prop_count, s->prop_size, + (double)s->prop_count / s->obj_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n", + " shapes", s->shape_count, s->shape_size, + (double)s->shape_size / s->shape_count); + } + if (s->js_func_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", + "bytecode functions", s->js_func_count, s->js_func_size); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", + " bytecode", s->js_func_count, s->js_func_code_size, + (double)s->js_func_code_size / s->js_func_count); + if (s->js_func_pc2line_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", + " pc2line", s->js_func_pc2line_count, + s->js_func_pc2line_size, + (double)s->js_func_pc2line_size / s->js_func_pc2line_count); + } + } + if (s->c_func_count) { + fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count); + } + if (s->array_count) { + fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count); + if (s->fast_array_count) { + fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n", + " elements", s->fast_array_elements, + s->fast_array_elements * (int)sizeof(JSValue), + (double)s->fast_array_elements / s->fast_array_count); + } + } + if (s->binary_object_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", + "binary objects", s->binary_object_count, s->binary_object_size); + } +} + +JSValue JS_GetGlobalObject(JSContext *ctx) +{ + return js_dup(ctx->global_obj); +} + +/* WARNING: obj is freed */ +JSValue JS_Throw(JSContext *ctx, JSValue obj) +{ + JSRuntime *rt = ctx->rt; + JS_FreeValue(ctx, rt->current_exception); + rt->current_exception = obj; + return JS_EXCEPTION; +} + +/* return the pending exception (cannot be called twice). */ +JSValue JS_GetException(JSContext *ctx) +{ + JSValue val; + JSRuntime *rt = ctx->rt; + val = rt->current_exception; + rt->current_exception = JS_UNINITIALIZED; + return val; +} + +bool JS_HasException(JSContext *ctx) +{ + return !JS_IsUninitialized(ctx->rt->current_exception); +} + +static void dbuf_put_leb128(DynBuf *s, uint32_t v) +{ + uint32_t a; + for(;;) { + a = v & 0x7f; + v >>= 7; + if (v != 0) { + dbuf_putc(s, a | 0x80); + } else { + dbuf_putc(s, a); + break; + } + } +} + +static void dbuf_put_sleb128(DynBuf *s, int32_t v1) +{ + uint32_t v = v1; + dbuf_put_leb128(s, (2 * v) ^ -(v >> 31)); +} + +static int get_leb128(uint32_t *pval, const uint8_t *buf, + const uint8_t *buf_end) +{ + const uint8_t *ptr = buf; + uint32_t v, a, i; + v = 0; + for(i = 0; i < 5; i++) { + if (unlikely(ptr >= buf_end)) + break; + a = *ptr++; + v |= (a & 0x7f) << (i * 7); + if (!(a & 0x80)) { + *pval = v; + return ptr - buf; + } + } + *pval = 0; + return -1; +} + +static int get_sleb128(int32_t *pval, const uint8_t *buf, + const uint8_t *buf_end) +{ + int ret; + uint32_t val; + ret = get_leb128(&val, buf, buf_end); + if (ret < 0) { + *pval = 0; + return -1; + } + *pval = (val >> 1) ^ -(val & 1); + return ret; +} + +static int find_line_num(JSContext *ctx, JSFunctionBytecode *b, + uint32_t pc_value, int *col) +{ + const uint8_t *p_end, *p; + int new_line_num, new_col_num, line_num, col_num, pc, v, ret; + unsigned int op; + + *col = 1; + p = b->pc2line_buf; + if (!p) + goto fail; + p_end = p + b->pc2line_len; + pc = 0; + line_num = b->line_num; + col_num = b->col_num; + while (p < p_end) { + op = *p++; + if (op == 0) { + uint32_t val; + ret = get_leb128(&val, p, p_end); + if (ret < 0) + goto fail; + pc += val; + p += ret; + ret = get_sleb128(&v, p, p_end); + if (ret < 0) + goto fail; + p += ret; + new_line_num = line_num + v; + } else { + op -= PC2LINE_OP_FIRST; + pc += (op / PC2LINE_RANGE); + new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE; + } + ret = get_sleb128(&v, p, p_end); + if (ret < 0) + goto fail; + p += ret; + new_col_num = col_num + v; + if (pc_value < pc) + break; + line_num = new_line_num; + col_num = new_col_num; + } + *col = col_num; + return line_num; +fail: + /* should never happen */ + return b->line_num; +} + +/* in order to avoid executing arbitrary code during the stack trace + generation, we only look at simple 'name' properties containing a + string. */ +static const char *get_func_name(JSContext *ctx, JSValueConst func) +{ + JSProperty *pr; + JSShapeProperty *prs; + JSValue val; + + if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT) + return NULL; + prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name); + if (!prs) + return NULL; + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) + return NULL; + val = pr->u.value; + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) + return NULL; + return JS_ToCString(ctx, val); +} + +/* Note: it is important that no exception is returned by this function */ +static bool can_add_backtrace(JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ERROR && p->class_id != JS_CLASS_DOM_EXCEPTION) + return false; + if (find_own_property1(p, JS_ATOM_stack)) + return false; + return true; +} + +#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0) +/* only taken into account if filename is provided */ +#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1) +#define JS_BACKTRACE_FLAG_FILTER_FUNC (1 << 2) + +/* if filename != NULL, an additional level is added with the filename + and line number information (used for parse error). */ +static void build_backtrace(JSContext *ctx, JSValueConst error_val, + JSValueConst filter_func, const char *filename, + int line_num, int col_num, int backtrace_flags) +{ + JSStackFrame *sf, *sf_start; + JSValue stack, prepare, saved_exception; + DynBuf dbuf; + const char *func_name_str; + const char *str1; + JSObject *p; + JSFunctionBytecode *b; + bool backtrace_barrier, has_prepare, has_filter_func; + JSRuntime *rt; + JSCallSiteData csd[64]; + uint32_t i; + double d; + int stack_trace_limit; + + rt = ctx->rt; + if (rt->in_build_stack_trace) + return; + rt->in_build_stack_trace = true; + + // Save exception because conversion to double may fail. + saved_exception = JS_GetException(ctx); + + // Extract stack trace limit. + // Ignore error since it sets d to NAN anyway. + // coverity[check_return] + JS_ToFloat64(ctx, &d, ctx->error_stack_trace_limit); + if (isnan(d) || d < 0.0) + stack_trace_limit = 0; + else if (d > INT32_MAX) + stack_trace_limit = INT32_MAX; + else + stack_trace_limit = fabs(d); + + // Restore current exception. + JS_Throw(ctx, saved_exception); + saved_exception = JS_UNINITIALIZED; + + stack_trace_limit = min_int(stack_trace_limit, countof(csd)); + stack_trace_limit = max_int(stack_trace_limit, 0); + has_prepare = false; + has_filter_func = backtrace_flags & JS_BACKTRACE_FLAG_FILTER_FUNC; + i = 0; + + if (!JS_IsNull(ctx->error_ctor)) { + prepare = js_dup(ctx->error_prepare_stack); + has_prepare = JS_IsFunction(ctx, prepare); + } + + if (has_prepare) { + saved_exception = JS_GetException(ctx); + if (stack_trace_limit == 0) + goto done; + if (filename) + js_new_callsite_data2(ctx, &csd[i++], filename, line_num, col_num); + } else { + js_dbuf_init(ctx, &dbuf); + if (stack_trace_limit == 0) + goto done; + if (filename) { + i++; + dbuf_printf(&dbuf, " at %s", filename); + if (line_num != -1) + dbuf_printf(&dbuf, ":%d:%d", line_num, col_num); + dbuf_putc(&dbuf, '\n'); + } + } + + if (filename && (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)) + goto done; + + sf_start = rt->current_stack_frame; + + /* Find the frame we want to start from. Note that when a filter is used the filter + function will be the first, but we also specify we want to skip the first one. */ + if (has_filter_func) { + for (sf = sf_start; sf != NULL && i < stack_trace_limit; sf = sf->prev_frame) { + if (js_same_value(ctx, sf->cur_func, filter_func)) { + sf_start = sf; + break; + } + } + } + + for (sf = sf_start; sf != NULL && i < stack_trace_limit; sf = sf->prev_frame) { + if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) { + backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL; + continue; + } + + p = JS_VALUE_GET_OBJ(sf->cur_func); + b = NULL; + backtrace_barrier = false; + + if (js_class_has_bytecode(p->class_id)) { + b = p->u.func.function_bytecode; + backtrace_barrier = b->backtrace_barrier; + } + + if (has_prepare) { + js_new_callsite_data(ctx, &csd[i], sf); + } else { + /* func_name_str is UTF-8 encoded if needed */ + func_name_str = get_func_name(ctx, sf->cur_func); + if (!func_name_str || func_name_str[0] == '\0') + str1 = ""; + else + str1 = func_name_str; + dbuf_printf(&dbuf, " at %s", str1); + JS_FreeCString(ctx, func_name_str); + + if (b && sf->cur_pc) { + const char *atom_str; + int line_num1, col_num1; + uint32_t pc; + + pc = sf->cur_pc - b->byte_code_buf - 1; + line_num1 = find_line_num(ctx, b, pc, &col_num1); + atom_str = b->filename ? JS_AtomToCString(ctx, b->filename) : NULL; + dbuf_printf(&dbuf, " (%s", atom_str ? atom_str : ""); + JS_FreeCString(ctx, atom_str); + if (line_num1 != -1) + dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1); + dbuf_putc(&dbuf, ')'); + } else if (b) { + // FIXME(bnoordhuis) Missing `sf->cur_pc = pc` in bytecode + // handler in JS_CallInternal. Almost never user observable + // except with intercepting JS proxies that throw exceptions. + dbuf_printf(&dbuf, " (missing)"); + } else { + dbuf_printf(&dbuf, " (native)"); + } + dbuf_putc(&dbuf, '\n'); + } + i++; + + /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */ + if (backtrace_barrier) + break; + } + done: + if (has_prepare) { + int j = 0, k; + stack = JS_NewArray(ctx); + if (JS_IsException(stack)) { + stack = JS_NULL; + } else { + for (; j < i; j++) { + JSValue v = js_new_callsite(ctx, &csd[j]); + if (JS_IsException(v)) + break; + if (JS_DefinePropertyValueUint32(ctx, stack, j, v, JS_PROP_C_W_E) < 0) { + JS_FreeValue(ctx, v); + break; + } + } + } + // Clear the csd's we didn't use in case of error. + for (k = j; k < i; k++) { + JS_FreeValue(ctx, csd[k].filename); + JS_FreeValue(ctx, csd[k].func); + JS_FreeValue(ctx, csd[k].func_name); + } + JSValueConst args[] = { + error_val, + stack, + }; + JSValue stack2 = JS_Call(ctx, prepare, ctx->error_ctor, countof(args), args); + JS_FreeValue(ctx, stack); + if (JS_IsException(stack2)) + stack = JS_NULL; + else + stack = stack2; + JS_FreeValue(ctx, prepare); + JS_Throw(ctx, saved_exception); + } else { + if (dbuf_error(&dbuf)) + stack = JS_NULL; + else + stack = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size); + dbuf_free(&dbuf); + } + + if (JS_IsUndefined(ctx->error_back_trace)) + ctx->error_back_trace = js_dup(stack); + if (has_filter_func || can_add_backtrace(error_val)) { + JS_DefinePropertyValue(ctx, error_val, JS_ATOM_stack, stack, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } else { + JS_FreeValue(ctx, stack); + } + + rt->in_build_stack_trace = false; +} + +JSValue JS_NewError(JSContext *ctx) +{ + JSValue obj = JS_NewObjectClass(ctx, JS_CLASS_ERROR); + if (JS_IsException(obj)) + return JS_EXCEPTION; + build_backtrace(ctx, obj, JS_UNDEFINED, NULL, 0, 0, 0); + return obj; +} + +static JSValue JS_MakeError2(JSContext *ctx, JSErrorEnum error_num, + bool add_backtrace, const char *message) +{ + JSValue obj, msg; + + if (error_num == JS_PLAIN_ERROR) { + obj = JS_NewObjectClass(ctx, JS_CLASS_ERROR); + } else { + obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num], + JS_CLASS_ERROR); + } + if (JS_IsException(obj)) + return JS_EXCEPTION; + msg = JS_NewString(ctx, message); + if (JS_IsException(msg)) + msg = JS_NewString(ctx, "Invalid error message"); + if (!JS_IsException(msg)) { + JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } + if (add_backtrace) + build_backtrace(ctx, obj, JS_UNDEFINED, NULL, 0, 0, 0); + return obj; +} + +static JSValue JS_PRINTF_FORMAT_ATTR(4, 0) +JS_MakeError(JSContext *ctx, JSErrorEnum error_num, bool add_backtrace, + JS_PRINTF_FORMAT const char *fmt, va_list ap) +{ + char buf[256]; + + vsnprintf(buf, sizeof(buf), fmt, ap); + return JS_MakeError2(ctx, error_num, add_backtrace, buf); +} + +/* fmt and arguments may be pure ASCII or UTF-8 encoded contents */ +static JSValue JS_PRINTF_FORMAT_ATTR(4, 0) +JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, bool add_backtrace, + JS_PRINTF_FORMAT const char *fmt, va_list ap) +{ + JSValue obj; + + obj = JS_MakeError(ctx, error_num, add_backtrace, fmt, ap); + if (unlikely(JS_IsException(obj))) { + /* out of memory: throw JS_NULL to avoid recursing */ + obj = JS_NULL; + } + return JS_Throw(ctx, obj); +} + +static JSValue JS_PRINTF_FORMAT_ATTR(3, 0) +JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, + JS_PRINTF_FORMAT const char *fmt, va_list ap) +{ + JSRuntime *rt = ctx->rt; + JSStackFrame *sf; + bool add_backtrace; + + /* the backtrace is added later if called from a bytecode function */ + sf = rt->current_stack_frame; + add_backtrace = !rt->in_out_of_memory && + (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL)); + return JS_ThrowError2(ctx, error_num, add_backtrace, fmt, ap); +} + +#define JS_ERROR_MAP(X) \ + X(Internal, INTERNAL) \ + X(Plain, PLAIN) \ + X(Range, RANGE) \ + X(Reference, REFERENCE) \ + X(Syntax, SYNTAX) \ + X(Type, TYPE) \ + +#define X(lc, uc) \ + JSValue JS_PRINTF_FORMAT_ATTR(2, 3) \ + JS_New##lc##Error(JSContext *ctx, \ + JS_PRINTF_FORMAT const char *fmt, ...) \ + { \ + JSValue val; \ + va_list ap; \ + \ + va_start(ap, fmt); \ + val = JS_MakeError(ctx, JS_##uc##_ERROR, \ + /*add_backtrace*/true, fmt, ap); \ + va_end(ap); \ + return val; \ + } \ + JSValue JS_PRINTF_FORMAT_ATTR(2, 3) \ + JS_Throw##lc##Error(JSContext *ctx, \ + JS_PRINTF_FORMAT const char *fmt, ...) \ + { \ + JSValue val; \ + va_list ap; \ + \ + va_start(ap, fmt); \ + val = JS_ThrowError(ctx, JS_##uc##_ERROR, fmt, ap); \ + va_end(ap); \ + return val; \ + } \ + +JS_ERROR_MAP(X) + +#undef X +#undef JS_ERROR_MAP + +static int JS_PRINTF_FORMAT_ATTR(3, 4) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, JS_PRINTF_FORMAT const char *fmt, ...) +{ + va_list ap; + + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + va_start(ap, fmt); + JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); + va_end(ap); + return -1; + } else { + return false; + } +} + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif // __GNUC__ +static JSValue JS_ThrowTypeErrorAtom(JSContext *ctx, const char *fmt, JSAtom atom) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + JS_AtomGetStr(ctx, buf, sizeof(buf), atom); + return JS_ThrowTypeError(ctx, fmt, buf); +} + +static JSValue JS_ThrowSyntaxErrorAtom(JSContext *ctx, const char *fmt, JSAtom atom) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + JS_AtomGetStr(ctx, buf, sizeof(buf), atom); + return JS_ThrowSyntaxError(ctx, fmt, buf); +} +#ifdef __GNUC__ +#pragma GCC diagnostic pop // ignored "-Wformat-nonliteral" +#endif // __GNUC__ + +static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) +{ + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom); + return -1; + } else { + return false; + } +} + +JSValue JS_ThrowOutOfMemory(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + if (!rt->in_out_of_memory) { + rt->in_out_of_memory = true; + JS_ThrowInternalError(ctx, "out of memory"); + rt->in_out_of_memory = false; + } + return JS_EXCEPTION; +} + +static JSValue JS_ThrowStackOverflow(JSContext *ctx) +{ + return JS_ThrowRangeError(ctx, "Maximum call stack size exceeded"); +} + +static JSValue JS_ThrowTypeErrorNotAConstructor(JSContext *ctx, + JSValueConst func_obj) +{ + JSObject *p; + JSAtom name; + + if (JS_TAG_OBJECT != JS_VALUE_GET_TAG(func_obj)) + goto fini; + p = JS_VALUE_GET_OBJ(func_obj); + if (!js_class_has_bytecode(p->class_id)) + goto fini; + name = p->u.func.function_bytecode->func_name; + if (name == JS_ATOM_NULL) + goto fini; + return JS_ThrowTypeErrorAtom(ctx, "%s is not a constructor", name); +fini: + return JS_ThrowTypeError(ctx, "not a constructor"); +} + +static JSValue JS_ThrowTypeErrorNotAFunction(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not a function"); +} + +static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not an object"); +} + +static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not a symbol"); +} + +static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowReferenceError(ctx, "%s is not defined", + JS_AtomGetStr(ctx, buf, sizeof(buf), name)); +} + +static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowReferenceError(ctx, "%s is not initialized", + name == JS_ATOM_NULL ? "lexical variable" : + JS_AtomGetStr(ctx, buf, sizeof(buf), name)); +} + +static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx, + JSFunctionBytecode *b, + int idx, bool is_ref) +{ + JSAtom atom = JS_ATOM_NULL; + if (is_ref) { + atom = b->closure_var[idx].var_name; + } else { + /* not present if the function is stripped and contains no eval() */ + if (b->vardefs) + atom = b->vardefs[b->arg_count + idx].var_name; + } + return JS_ThrowReferenceErrorUninitialized(ctx, atom); +} + +static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) +{ + JSRuntime *rt = ctx->rt; + JSAtom name; + name = rt->class_array[class_id].class_name; + return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); +} + +static void JS_ThrowInterrupted(JSContext *ctx) +{ + JS_ThrowInternalError(ctx, "interrupted"); + JS_SetUncatchableError(ctx, ctx->rt->current_exception); +} + +static no_inline __exception int __js_poll_interrupts(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; + if (rt->interrupt_handler) { + if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { + JS_ThrowInterrupted(ctx); + return -1; + } + } + return 0; +} + +static inline __exception int js_poll_interrupts(JSContext *ctx) +{ + if (unlikely(--ctx->interrupt_counter <= 0)) { + return __js_poll_interrupts(ctx); + } else { + return 0; + } +} + +/* return -1 (exception) or true/false */ +static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj, + JSValueConst proto_val, bool throw_flag) +{ + JSObject *proto, *p, *p1; + JSShape *sh; + + if (throw_flag) { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL || + JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED) + goto not_obj; + } else { + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + goto not_obj; + } + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) { + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) { + not_obj: + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + proto = NULL; + } else { + proto = JS_VALUE_GET_OBJ(proto_val); + } + + if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return true; + + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag); + sh = p->shape; + if (sh->proto == proto) + return true; + if (p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_OBJECT])) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "'Immutable prototype object \'Object.prototype\' cannot have their prototype set'"); + return -1; + } + return false; + } + if (!p->extensible) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "object is not extensible"); + return -1; + } else { + return false; + } + } + if (proto) { + /* check if there is a cycle */ + p1 = proto; + do { + if (p1 == p) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "circular prototype chain"); + return -1; + } else { + return false; + } + } + /* Note: for Proxy objects, proto is NULL */ + p1 = p1->shape->proto; + } while (p1 != NULL); + js_dup(proto_val); + } + + if (js_shape_prepare_update(ctx, p, NULL)) + return -1; + sh = p->shape; + if (sh->proto) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + sh->proto = proto; + return true; +} + +/* return -1 (exception) or true/false */ +int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val) +{ + return JS_SetPrototypeInternal(ctx, obj, proto_val, true); +} + +/* Only works for primitive types, otherwise return JS_NULL. */ +static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val) +{ + JSValue ret; + switch(JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + ret = ctx->class_proto[JS_CLASS_BIG_INT]; + break; + case JS_TAG_INT: + case JS_TAG_FLOAT64: + ret = ctx->class_proto[JS_CLASS_NUMBER]; + break; + case JS_TAG_BOOL: + ret = ctx->class_proto[JS_CLASS_BOOLEAN]; + break; + case JS_TAG_STRING: + ret = ctx->class_proto[JS_CLASS_STRING]; + break; + case JS_TAG_SYMBOL: + ret = ctx->class_proto[JS_CLASS_SYMBOL]; + break; + case JS_TAG_OBJECT: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + default: + ret = JS_NULL; + break; + } + return ret; +} + +/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */ +JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj) +{ + JSValue val; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) { + val = js_proxy_getPrototypeOf(ctx, obj); + } else { + p = p->shape->proto; + if (!p) + val = JS_NULL; + else + val = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + } + } else { + val = js_dup(JS_GetPrototypePrimitive(ctx, obj)); + } + return val; +} + +static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj) +{ + JSValue obj1; + obj1 = JS_GetPrototype(ctx, obj); + JS_FreeValue(ctx, obj); + return obj1; +} + +int JS_GetLength(JSContext *ctx, JSValueConst obj, int64_t *pres) { + return js_get_length64(ctx, pres, obj); +} + +int JS_SetLength(JSContext *ctx, JSValueConst obj, int64_t len) { + return js_set_length64(ctx, obj, len); +} + +/* return true, false or (-1) in case of exception */ +static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, + JSValueConst obj) +{ + JSValue obj_proto; + JSObject *proto; + const JSObject *p, *proto1; + int ret; + + if (!JS_IsFunction(ctx, obj)) + return false; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_BOUND_FUNCTION) { + JSBoundFunction *s = p->u.bound_function; + return JS_IsInstanceOf(ctx, val, s->func_obj); + } + + /* Only explicitly boxed values are instances of constructors */ + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return false; + obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype); + if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) { + if (!JS_IsException(obj_proto)) + JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object"); + ret = -1; + goto done; + } + proto = JS_VALUE_GET_OBJ(obj_proto); + p = JS_VALUE_GET_OBJ(val); + for(;;) { + proto1 = p->shape->proto; + if (!proto1) { + /* slow case if proxy in the prototype chain */ + if (unlikely(p->class_id == JS_CLASS_PROXY)) { + JSValue obj1; + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p)); + for(;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsException(obj1)) { + ret = -1; + break; + } + if (JS_IsNull(obj1)) { + ret = false; + break; + } + if (proto == JS_VALUE_GET_OBJ(obj1)) { + JS_FreeValue(ctx, obj1); + ret = true; + break; + } + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + ret = -1; + break; + } + } + } else { + ret = false; + } + break; + } + p = proto1; + if (proto == p) { + ret = true; + break; + } + } +done: + JS_FreeValue(ctx, obj_proto); + return ret; +} + +/* return true, false or (-1) in case of exception */ +int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) +{ + JSValue method; + + if (!JS_IsObject(obj)) + goto fail; + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance); + if (JS_IsException(method)) + return -1; + if (!JS_IsNull(method) && !JS_IsUndefined(method)) { + JSValue ret; + ret = JS_CallFree(ctx, method, obj, 1, &val); + return JS_ToBoolFree(ctx, ret); + } + + /* legacy case */ + if (!JS_IsFunction(ctx, obj)) { + fail: + JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand"); + return -1; + } + return JS_OrdinaryIsInstanceOf(ctx, val, obj); +} + +#include "builtin-array-fromasync.h" + +static JSValue js_bytecode_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, + void *opaque) +{ + switch ((uintptr_t)opaque) { + default: + abort(); + case JS_BUILTIN_ARRAY_FROMASYNC: + { + JSValue obj = JS_ReadObject(ctx, qjsc_builtin_array_fromasync, + sizeof(qjsc_builtin_array_fromasync), + JS_READ_OBJ_BYTECODE); + if (JS_IsException(obj)) + return JS_EXCEPTION; + JSValue fun = JS_EvalFunction(ctx, obj); + if (JS_IsException(fun)) + return JS_EXCEPTION; + assert(JS_IsFunction(ctx, fun)); + JSValue args[] = { + JS_NewCFunction(ctx, js_array_constructor, "Array", 0), + JS_NewCFunctionMagic(ctx, js_error_constructor, "TypeError", 1, + JS_CFUNC_constructor_or_func_magic, + JS_TYPE_ERROR), + JS_AtomToValue(ctx, JS_ATOM_Symbol_asyncIterator), + JS_NewCFunctionMagic(ctx, js_object_defineProperty, + "Object.defineProperty", 3, + JS_CFUNC_generic_magic, 0), + JS_AtomToValue(ctx, JS_ATOM_Symbol_iterator), + }; + JSValue result = JS_Call(ctx, fun, JS_UNDEFINED, + countof(args), vc(args)); + for (size_t i = 0; i < countof(args); i++) + JS_FreeValue(ctx, args[i]); + JS_FreeValue(ctx, fun); + if (JS_SetPrototypeInternal(ctx, result, ctx->function_proto, + /*throw_flag*/true) < 0) { + JS_FreeValue(ctx, result); + return JS_EXCEPTION; + } + return result; + } + } + return JS_UNDEFINED; +} + +/* return the value associated to the autoinit property or an exception */ +typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); + +static JSAutoInitFunc *const js_autoinit_func_table[] = { + js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */ + js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */ + JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ + js_bytecode_autoinit, /* JS_AUTOINIT_ID_BYTECODE */ +}; + +/* warning: 'prs' is reallocated after it */ +static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, + JSProperty *pr, JSShapeProperty *prs) +{ + JSValue val; + JSContext *realm; + JSAutoInitFunc *func; + + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + + realm = js_autoinit_get_realm(pr); + func = js_autoinit_func_table[js_autoinit_get_id(pr)]; + /* 'func' shall not modify the object properties 'pr' */ + val = func(realm, p, prop, pr->u.init.opaque); + js_autoinit_free(ctx->rt, pr); + prs->flags &= ~JS_PROP_TMASK; + pr->u.value = JS_UNDEFINED; + if (JS_IsException(val)) + return -1; + pr->u.value = val; + return 0; +} + +static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValueConst this_obj, + bool throw_ref_error) +{ + JSObject *p; + JSProperty *pr; + JSShapeProperty *prs; + uint32_t tag; + + tag = JS_VALUE_GET_TAG(obj); + if (unlikely(tag != JS_TAG_OBJECT)) { + switch(tag) { + case JS_TAG_NULL: + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop); + case JS_TAG_UNDEFINED: + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop); + case JS_TAG_EXCEPTION: + return JS_EXCEPTION; + case JS_TAG_STRING: + { + JSString *p1 = JS_VALUE_GET_STRING(obj); + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx, ch; + idx = __JS_AtomToUInt32(prop); + if (idx < p1->len) { + ch = string_get(p1, idx); + return js_new_string_char(ctx, ch); + } + } else if (prop == JS_ATOM_length) { + return js_int32(p1->len); + } + } + break; + default: + break; + } + /* cannot raise an exception */ + p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); + if (!p) + return JS_UNDEFINED; + } else { + p = JS_VALUE_GET_OBJ(obj); + } + + for(;;) { + prs = find_own_property(&pr, p, prop); + if (prs) { + /* found */ + if (unlikely(prs->flags & JS_PROP_TMASK)) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (unlikely(!pr->u.getset.getter)) { + return JS_UNDEFINED; + } else { + JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter); + /* Note: the field could be removed in the getter */ + func = js_dup(func); + return JS_CallFree(ctx, func, this_obj, 0, NULL); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + JSValue val = *pr->u.var_ref->pvalue; + if (unlikely(JS_IsUninitialized(val))) + return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return js_dup(val); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return JS_EXCEPTION; + continue; + } + } else { + return js_dup(pr->u.value); + } + } + if (unlikely(p->is_exotic)) { + /* exotic behaviors */ + if (p->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + /* we avoid duplicating the code */ + return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); + } else if (is_typed_array(p->class_id)) { + return JS_UNDEFINED; + } + } else if (is_typed_array(p->class_id)) { + int ret; + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return JS_EXCEPTION; + return JS_UNDEFINED; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em) { + if (em->get_property) { + JSValue obj1, retval; + /* XXX: should pass throw_ref_error */ + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + retval = em->get_property(ctx, obj1, prop, this_obj); + JS_FreeValue(ctx, obj1); + return retval; + } + if (em->get_own_property) { + JSPropertyDescriptor desc; + int ret; + JSValue obj1; + + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->get_own_property(ctx, &desc, obj1, prop); + JS_FreeValue(ctx, obj1); + if (ret < 0) + return JS_EXCEPTION; + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JS_FreeValue(ctx, desc.setter); + return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL); + } else { + return desc.value; + } + } + } + } + } + } + p = p->shape->proto; + if (!p) + break; + } + if (unlikely(throw_ref_error)) { + return JS_ThrowReferenceErrorNotDefined(ctx, prop); + } else { + return JS_UNDEFINED; + } +} + +JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop) +{ + return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, false); +} + +static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) +{ + return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist", + atom); +} + +/* Private fields can be added even on non extensible objects or + Proxies */ +static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, + JSValue name, JSValue val) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { + JS_ThrowTypeErrorNotASymbol(ctx); + goto fail; + } + prop = js_symbol_to_atom(ctx, name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists", + prop); + goto fail; + } + pr = add_property(ctx, p, prop, JS_PROP_C_W_E); + if (unlikely(!pr)) { + fail: + JS_FreeValue(ctx, val); + return -1; + } + pr->u.value = val; + return 0; +} + +static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return JS_ThrowTypeErrorNotAnObject(ctx); + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) + return JS_ThrowTypeErrorNotASymbol(ctx); + prop = js_symbol_to_atom(ctx, name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + return JS_EXCEPTION; + } + return js_dup(pr->u.value); +} + +static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name, JSValue val) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { + JS_ThrowTypeErrorNotASymbol(ctx); + goto fail; + } + prop = js_symbol_to_atom(ctx, name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + fail: + JS_FreeValue(ctx, val); + return -1; + } + set_value(ctx, &pr->u.value, val); + return 0; +} + +/* add a private brand field to 'home_obj' if not already present and + if obj is != null add a private brand to it */ +static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) +{ + JSObject *p, *p1; + JSShapeProperty *prs; + JSProperty *pr; + JSValue brand; + JSAtom brand_atom; + + if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(home_obj); + prs = find_own_property(&pr, p, JS_ATOM_Private_brand); + if (!prs) { + /* if the brand is not present, add it */ + brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(brand)) + return -1; + pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E); + if (!pr) { + JS_FreeValue(ctx, brand); + return -1; + } + pr->u.value = js_dup(brand); + } else { + brand = js_dup(pr->u.value); + } + brand_atom = js_symbol_to_atom(ctx, brand); + + if (JS_IsObject(obj)) { + p1 = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p1, brand_atom); + if (unlikely(prs)) { + JS_FreeAtom(ctx, brand_atom); + JS_ThrowTypeError(ctx, "private method is already present"); + return -1; + } + pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E); + JS_FreeAtom(ctx, brand_atom); + if (!pr) + return -1; + pr->u.value = JS_UNDEFINED; + } else { + JS_FreeAtom(ctx, brand_atom); + } + + return 0; +} + +/* return a boolean telling if the brand of the home object of 'func' + is present on 'obj' or -1 in case of exception */ +static int JS_CheckBrand(JSContext *ctx, JSValue obj, JSValue func) +{ + JSObject *p, *p1, *home_obj; + JSShapeProperty *prs; + JSProperty *pr; + JSValue brand; + + /* get the home object of 'func' */ + if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) + goto not_obj; + p1 = JS_VALUE_GET_OBJ(func); + if (!js_class_has_bytecode(p1->class_id)) + goto not_obj; + home_obj = p1->u.func.home_object; + if (!home_obj) + goto not_obj; + prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand); + if (!prs) { + JS_ThrowTypeError(ctx, "expecting private field"); + return -1; + } + brand = pr->u.value; + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL)) + goto not_obj; + + /* get the brand array of 'obj' */ + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + not_obj: + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, brand)); + return (prs != NULL); +} + +static uint32_t js_string_obj_get_length(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + JSString *p1; + uint32_t len = 0; + + /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { + p1 = JS_VALUE_GET_STRING(p->u.object_data); + len = p1->len; + } + return len; +} + +static int num_keys_cmp(const void *p1, const void *p2, void *opaque) +{ + JSContext *ctx = opaque; + JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom; + JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom; + uint32_t v1, v2; + bool atom1_is_integer, atom2_is_integer; + + atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1); + atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2); + assert(atom1_is_integer && atom2_is_integer); + if (v1 < v2) + return -1; + else if (v1 == v2) + return 0; + else + return 1; +} + +static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) +{ + uint32_t i; + if (tab) { + for(i = 0; i < len; i++) + JS_FreeAtom(ctx, tab[i].atom); + js_free(ctx, tab); + } +} + +/* return < 0 in case if exception, 0 if OK. ptab and its atoms must + be freed by the user. */ +static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, + JSPropertyEnum **ptab, + uint32_t *plen, + JSObject *p, int flags) +{ + int i, j; + JSShape *sh; + JSShapeProperty *prs; + JSPropertyEnum *tab_atom, *tab_exotic; + JSAtom atom; + uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count; + uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count; + bool is_enumerable, num_sorted; + uint32_t num_key; + JSAtomKindEnum kind; + + /* clear pointer for consistency in case of failure */ + *ptab = NULL; + *plen = 0; + + /* compute the number of returned properties */ + num_keys_count = 0; + str_keys_count = 0; + sym_keys_count = 0; + exotic_keys_count = 0; + exotic_count = 0; + tab_exotic = NULL; + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + atom = prs->atom; + if (atom != JS_ATOM_NULL) { + is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + /* need to raise an exception in case of the module + name space (implicit GetOwnProperty) */ + if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) && + (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) { + JSVarRef *var_ref = p->prop[i].u.var_ref; + if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + } + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + num_keys_count++; + } else if (kind == JS_ATOM_KIND_STRING) { + str_keys_count++; + } else { + sym_keys_count++; + } + } + } + } + + if (p->is_exotic) { + if (p->fast_array) { + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += p->u.array.count; + } + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->get_own_property_names) { + if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count, + JS_MKPTR(JS_TAG_OBJECT, p))) + return -1; + for(i = 0; i < exotic_count; i++) { + atom = tab_exotic[i].atom; + kind = JS_AtomGetKind(ctx, atom); + if (((flags >> kind) & 1) != 0) { + is_enumerable = false; + if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) { + JSPropertyDescriptor desc; + int res; + /* set the "is_enumerable" field if necessary */ + res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); + if (res < 0) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + return -1; + } + if (res) { + is_enumerable = + ((desc.flags & JS_PROP_ENUMERABLE) != 0); + js_free_desc(ctx, &desc); + } + tab_exotic[i].is_enumerable = is_enumerable; + } + if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) { + exotic_keys_count++; + } + } + } + } + } + } + + /* fill them */ + + atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count; + /* avoid allocating 0 bytes */ + tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); + if (!tab_atom) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + return -1; + } + + num_index = 0; + str_index = num_keys_count; + sym_index = str_index + str_keys_count; + + num_sorted = true; + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + atom = prs->atom; + if (atom != JS_ATOM_NULL) { + is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + j = num_index++; + num_sorted = false; + } else if (kind == JS_ATOM_KIND_STRING) { + j = str_index++; + } else { + j = sym_index++; + } + tab_atom[j].atom = JS_DupAtom(ctx, atom); + tab_atom[j].is_enumerable = is_enumerable; + } + } + } + + if (p->is_exotic) { + int len; + if (p->fast_array) { + if (flags & JS_GPN_STRING_MASK) { + len = p->u.array.count; + goto add_array_keys; + } + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + add_array_keys: + for(i = 0; i < len; i++) { + tab_atom[num_index].atom = __JS_AtomFromUInt32(i); + if (tab_atom[num_index].atom == JS_ATOM_NULL) { + js_free_prop_enum(ctx, tab_atom, num_index); + return -1; + } + tab_atom[num_index].is_enumerable = true; + num_index++; + } + } + } else { + /* Note: exotic keys are not reordered and comes after the object own properties. */ + for(i = 0; i < exotic_count; i++) { + atom = tab_exotic[i].atom; + is_enumerable = tab_exotic[i].is_enumerable; + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + tab_atom[sym_index].atom = atom; + tab_atom[sym_index].is_enumerable = is_enumerable; + sym_index++; + } else { + JS_FreeAtom(ctx, atom); + } + } + js_free(ctx, tab_exotic); + } + } + + assert(num_index == num_keys_count); + assert(str_index == num_keys_count + str_keys_count); + assert(sym_index == atom_count); + + if (num_keys_count != 0 && !num_sorted) { + rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp, + ctx); + } + *ptab = tab_atom; + *plen = atom_count; + return 0; +} + +int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, + uint32_t *plen, JSValueConst obj, int flags) +{ + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, + JS_VALUE_GET_OBJ(obj), flags); +} + +/* Return -1 if exception, + false if the property does not exist, true if it exists. If true is + returned, the property descriptor 'desc' is filled present. */ +static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, + JSObject *p, JSAtom prop) +{ + JSShapeProperty *prs; + JSProperty *pr; + +retry: + prs = find_own_property(&pr, p, prop); + if (prs) { + if (desc) { + desc->flags = prs->flags & JS_PROP_C_W_E; + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + desc->value = JS_UNDEFINED; + if (unlikely(prs->flags & JS_PROP_TMASK)) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + desc->flags |= JS_PROP_GETSET; + if (pr->u.getset.getter) + desc->getter = js_dup(JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + desc->setter = js_dup(JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + JSValue val = *pr->u.var_ref->pvalue; + if (unlikely(JS_IsUninitialized(val))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + desc->value = js_dup(val); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return -1; + goto retry; + } + } else { + desc->value = js_dup(pr->u.value); + } + } else { + /* for consistency, send the exception even if desc is NULL */ + if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) { + if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* nothing to do: delay instantiation until actual value and/or attributes are read */ + } + } + return true; + } + if (p->is_exotic) { + if (p->fast_array) { + /* specific case for fast arrays */ + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx; + idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + if (desc) { + desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | + JS_PROP_CONFIGURABLE; + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); + } + return true; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->get_own_property) { + return em->get_own_property(ctx, desc, + JS_MKPTR(JS_TAG_OBJECT, p), prop); + } + } + } + return false; +} + +int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, + JSValueConst obj, JSAtom prop) +{ + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop); +} + +void JS_FreePropertyEnum(JSContext *ctx, JSPropertyEnum *tab, + uint32_t len) +{ + js_free_prop_enum(ctx, tab, len); +} + +/* return -1 if exception (Proxy object only) or true/false */ +int JS_IsExtensible(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return false; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_isExtensible(ctx, obj); + else + return p->extensible; +} + +/* return -1 if exception (Proxy object only) or true/false */ +int JS_PreventExtensions(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return false; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_preventExtensions(ctx, obj); + p->extensible = false; + return true; +} + +/* return -1 if exception otherwise true or false */ +int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) +{ + JSObject *p; + int ret; + JSValue obj1; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return false; + p = JS_VALUE_GET_OBJ(obj); + for(;;) { + if (p->is_exotic) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->has_property) { + /* has_property can free the prototype */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->has_property(ctx, obj1, prop); + JS_FreeValue(ctx, obj1); + return ret; + } + } + /* JS_GetOwnPropertyInternal can free the prototype */ + js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ret != 0) + return ret; + if (is_typed_array(p->class_id)) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return -1; + return false; + } + } + p = p->shape->proto; + if (!p) + break; + } + return false; +} + +/* val must be a symbol */ +static JSAtom js_symbol_to_atom(JSContext *ctx, JSValueConst val) +{ + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + return js_get_atom_index(ctx->rt, p); +} + +/* return JS_ATOM_NULL in case of exception */ +static JSAtom JS_ValueToAtomInternal(JSContext *ctx, JSValueConst val, + int flags) +{ + JSAtom atom; + uint32_t tag; + tag = JS_VALUE_GET_TAG(val); + if (tag == JS_TAG_INT && + (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) { + /* fast path for integer values */ + atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val)); + } else if (tag == JS_TAG_SYMBOL) { + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p)); + } else { + JSValue str; + str = JS_ToPropertyKeyInternal(ctx, val, flags); + if (JS_IsException(str)) + return JS_ATOM_NULL; + if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) { + atom = js_symbol_to_atom(ctx, str); + } else { + atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str)); + } + } + return atom; +} + +JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val) +{ + return JS_ValueToAtomInternal(ctx, val, /*flags*/0); +} + +static bool js_get_fast_array_element(JSContext *ctx, JSObject *p, + uint32_t idx, JSValue *pval) +{ + switch(p->class_id) { + case JS_CLASS_ARRAY: + case JS_CLASS_ARGUMENTS: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_dup(p->u.array.u.values[idx]); + return true; + case JS_CLASS_INT8_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_int32(p->u.array.u.int8_ptr[idx]); + return true; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_int32(p->u.array.u.uint8_ptr[idx]); + return true; + case JS_CLASS_INT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_int32(p->u.array.u.int16_ptr[idx]); + return true; + case JS_CLASS_UINT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_int32(p->u.array.u.uint16_ptr[idx]); + return true; + case JS_CLASS_INT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_int32(p->u.array.u.int32_ptr[idx]); + return true; + case JS_CLASS_UINT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_uint32(p->u.array.u.uint32_ptr[idx]); + return true; + case JS_CLASS_BIG_INT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); + return true; + case JS_CLASS_BIG_UINT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); + return true; + case JS_CLASS_FLOAT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_float64(fromfp16(p->u.array.u.fp16_ptr[idx])); + return true; + case JS_CLASS_FLOAT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_float64(p->u.array.u.float_ptr[idx]); + return true; + case JS_CLASS_FLOAT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_float64(p->u.array.u.double_ptr[idx]); + return true; + default: + return false; + } +} + +static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, + JSValue prop) +{ + JSAtom atom; + JSValue ret; + uint32_t tag; + + tag = JS_VALUE_GET_TAG(this_obj); + if (likely(tag == JS_TAG_OBJECT)) { + if (JS_VALUE_GET_TAG(prop) == JS_TAG_INT) { + JSObject *p = JS_VALUE_GET_OBJ(this_obj); + uint32_t idx = JS_VALUE_GET_INT(prop); + JSValue val; + /* fast path for array and typed array access */ + if (js_get_fast_array_element(ctx, p, idx, &val)) + return val; + } + } else if (unlikely(tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)) { + // per spec: not allowed to call ToPropertyKey before ToObject + // so we must ensure to not invoke JS anything that's observable + // from JS code + atom = JS_ValueToAtomInternal(ctx, prop, JS_TO_STRING_NO_SIDE_EFFECTS); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + if (tag == JS_TAG_NULL) { + JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", atom); + } else { + JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", atom); + } + JS_FreeAtom(ctx, atom); + return JS_EXCEPTION; + } + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_GetProperty(ctx, this_obj, atom); + JS_FreeAtom(ctx, atom); + return ret; +} + +JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx) +{ + return JS_GetPropertyInt64(ctx, this_obj, idx); +} + +/* Check if an object has a generalized numeric property. Return value: + -1 for exception, *pval set to JS_EXCEPTION + true if property exists, stored into *pval, + false if property does not exist. *pval set to JS_UNDEFINED. + */ +static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval) +{ + JSValue val; + JSAtom prop; + int present; + + if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT && + (uint64_t)idx <= INT32_MAX)) { + /* fast path for array and typed array access */ + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (js_get_fast_array_element(ctx, p, idx, pval)) + return true; + } + val = JS_EXCEPTION; + present = -1; + prop = JS_NewAtomInt64(ctx, idx); + if (likely(prop != JS_ATOM_NULL)) { + present = JS_HasProperty(ctx, obj, prop); + if (present > 0) { + val = JS_GetProperty(ctx, obj, prop); + if (unlikely(JS_IsException(val))) + present = -1; + } else if (present == false) { + val = JS_UNDEFINED; + } + JS_FreeAtom(ctx, prop); + } + *pval = val; + return present; +} + +JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx) +{ + JSAtom prop; + JSValue val; + + if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT && + (uint64_t)idx <= INT32_MAX)) { + /* fast path for array and typed array access */ + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (js_get_fast_array_element(ctx, p, idx, &val)) + return val; + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) + return JS_EXCEPTION; + + val = JS_GetProperty(ctx, obj, prop); + JS_FreeAtom(ctx, prop); + return val; +} + +/* `prop` may be pure ASCII or UTF-8 encoded */ +JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, + const char *prop) +{ + JSAtom atom; + JSValue ret; + atom = JS_NewAtom(ctx, prop); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; + ret = JS_GetProperty(ctx, this_obj, atom); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* Note: the property value is not initialized. Return NULL if memory + error. */ +static JSProperty *add_property(JSContext *ctx, + JSObject *p, JSAtom prop, int prop_flags) +{ + JSShape *sh, *new_sh; + + sh = p->shape; + if (sh->is_hashed) { + /* try to find an existing shape */ + new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags); + if (new_sh) { + /* matching shape found: use it */ + /* the property array may need to be resized */ + if (new_sh->prop_size != sh->prop_size) { + JSProperty *new_prop; + new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) * + new_sh->prop_size); + if (!new_prop) + return NULL; + p->prop = new_prop; + } + p->shape = js_dup_shape(new_sh); + js_free_shape(ctx->rt, sh); + return &p->prop[new_sh->prop_count - 1]; + } else if (sh->header.ref_count != 1) { + /* if the shape is shared, clone it */ + new_sh = js_clone_shape(ctx, sh); + if (!new_sh) + return NULL; + /* hash the cloned shape */ + new_sh->is_hashed = true; + js_shape_hash_link(ctx->rt, new_sh); + js_free_shape(ctx->rt, p->shape); + p->shape = new_sh; + } + } + assert(p->shape->header.ref_count == 1); + if (add_shape_property(ctx, &p->shape, p, prop, prop_flags)) + return NULL; + return &p->prop[p->shape->prop_count - 1]; +} + +/* can be called on Array or Arguments objects. return < 0 if + memory alloc error. */ +static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, + JSObject *p) +{ + JSProperty *pr; + JSShape *sh; + JSValue *tab; + uint32_t i, len, new_count; + + if (js_shape_prepare_update(ctx, p, NULL)) + return -1; + len = p->u.array.count; + /* resize the properties once to simplify the error handling */ + sh = p->shape; + new_count = sh->prop_count + len; + if (new_count > sh->prop_size) { + if (resize_properties(ctx, &p->shape, p, new_count)) + return -1; + } + + tab = p->u.array.u.values; + for(i = 0; i < len; i++) { + /* add_property cannot fail here but + __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E); + pr->u.value = *tab++; + } + js_free(ctx, p->u.array.u.values); + p->u.array.count = 0; + p->u.array.u.values = NULL; /* fail safe */ + p->u.array.u1.size = 0; + p->fast_array = 0; + return 0; +} + +static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *lpr, *prop; + JSProperty *pr1; + uint32_t lpr_idx; + intptr_t h, h1; + + redo: + sh = p->shape; + h1 = atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h1 - 1]; + prop = get_shape_prop(sh); + lpr = NULL; + lpr_idx = 0; /* prevent warning */ + while (h != 0) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + /* found ! */ + if (!(pr->flags & JS_PROP_CONFIGURABLE)) + return false; + /* realloc the shape if needed */ + if (lpr) + lpr_idx = lpr - get_shape_prop(sh); + if (js_shape_prepare_update(ctx, p, &pr)) + return -1; + sh = p->shape; + /* remove property */ + if (lpr) { + lpr = get_shape_prop(sh) + lpr_idx; + lpr->hash_next = pr->hash_next; + } else { + prop_hash_end(sh)[-h1 - 1] = pr->hash_next; + } + sh->deleted_prop_count++; + /* free the entry */ + pr1 = &p->prop[h - 1]; + free_property(ctx->rt, pr1, pr->flags); + JS_FreeAtom(ctx, pr->atom); + /* put default values */ + pr->flags = 0; + pr->atom = JS_ATOM_NULL; + pr1->u.value = JS_UNDEFINED; + + /* compact the properties if too many deleted properties */ + if (sh->deleted_prop_count >= 8 && + sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { + compact_properties(ctx, p); + } + return true; + } + lpr = pr; + h = pr->hash_next; + } + + if (p->is_exotic) { + if (p->fast_array) { + uint32_t idx; + if (JS_AtomIsArrayIndex(ctx, &idx, atom) && + idx < p->u.array.count) { + if (p->class_id == JS_CLASS_ARRAY || + p->class_id == JS_CLASS_ARGUMENTS) { + /* Special case deleting the last element of a fast Array */ + if (idx == p->u.array.count - 1) { + JS_FreeValue(ctx, p->u.array.u.values[idx]); + p->u.array.count = idx; + return true; + } + if (convert_fast_array_to_array(ctx, p)) + return -1; + goto redo; + } else { + return false; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->delete_property) { + return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom); + } + } + } + /* not found */ + return true; +} + +static int call_setter(JSContext *ctx, JSObject *setter, + JSValueConst this_obj, JSValue val, int flags) +{ + JSValue ret, func; + if (likely(setter)) { + func = JS_MKPTR(JS_TAG_OBJECT, setter); + /* Note: the field could be removed in the setter */ + func = js_dup(func); + ret = JS_CallFree(ctx, func, this_obj, 1, vc(&val)); + JS_FreeValue(ctx, val); + if (JS_IsException(ret)) + return -1; + JS_FreeValue(ctx, ret); + return true; + } else { + JS_FreeValue(ctx, val); + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeError(ctx, "no setter for property"); + return -1; + } + return false; + } +} + +/* set the array length and remove the array elements if necessary. */ +static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, + int flags) +{ + uint32_t len, idx, cur_len; + int i, ret; + + /* Note: this call can reallocate the properties of 'p' */ + ret = JS_ToArrayLengthFree(ctx, &len, val, false); + if (ret) + return -1; + /* JS_ToArrayLengthFree() must be done before the read-only test */ + if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE))) + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + + if (likely(p->fast_array)) { + uint32_t old_len = p->u.array.count; + if (len < old_len) { + for(i = len; i < old_len; i++) { + JS_FreeValue(ctx, p->u.array.u.values[i]); + } + p->u.array.count = len; + } + p->prop[0].u.value = js_uint32(len); + } else { + /* Note: length is always a uint32 because the object is an + array */ + JS_ToUint32(ctx, &cur_len, p->prop[0].u.value); + if (len < cur_len) { + uint32_t d; + JSShape *sh; + JSShapeProperty *pr; + + d = cur_len - len; + sh = p->shape; + if (d <= sh->prop_count) { + JSAtom atom; + + /* faster to iterate */ + while (cur_len > len) { + atom = JS_NewAtomUInt32(ctx, cur_len - 1); + ret = delete_property(ctx, p, atom); + JS_FreeAtom(ctx, atom); + if (unlikely(!ret)) { + /* unlikely case: property is not + configurable */ + break; + } + cur_len--; + } + } else { + /* faster to iterate thru all the properties. Need two + passes in case one of the property is not + configurable */ + cur_len = len; + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; + i++, pr++) { + if (pr->atom != JS_ATOM_NULL && + JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { + if (idx >= cur_len && + !(pr->flags & JS_PROP_CONFIGURABLE)) { + cur_len = idx + 1; + } + } + } + + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; + i++, pr++) { + if (pr->atom != JS_ATOM_NULL && + JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { + if (idx >= cur_len) { + /* remove the property */ + delete_property(ctx, p, pr->atom); + /* WARNING: the shape may have been modified */ + sh = p->shape; + pr = get_shape_prop(sh) + i; + } + } + } + } + } else { + cur_len = len; + } + set_value(ctx, &p->prop[0].u.value, js_uint32(cur_len)); + if (unlikely(cur_len > len)) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable"); + } + } + return true; +} + +/* return -1 if exception */ +static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len) +{ + uint32_t new_size; + size_t slack; + JSValue *new_array_prop; + /* XXX: potential arithmetic overflow */ + new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); + new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); + if (!new_array_prop) + return -1; + new_size += slack / sizeof(*new_array_prop); + p->u.array.u.values = new_array_prop; + p->u.array.u1.size = new_size; + return 0; +} + +/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array = + true and p->extensible = true */ +static int add_fast_array_element(JSContext *ctx, JSObject *p, + JSValue val, int flags) +{ + uint32_t new_len, array_len; + /* extend the array by one */ + /* XXX: convert to slow array if new_len > 2^31-1 elements */ + new_len = p->u.array.count + 1; + /* update the length if necessary. We assume that if the length is + not an integer, then if it >= 2^31. */ + if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { + array_len = JS_VALUE_GET_INT(p->prop[0].u.value); + if (new_len > array_len) { + if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + } + p->prop[0].u.value = js_int32(new_len); + } + } + if (unlikely(new_len > p->u.array.u1.size)) { + if (expand_fast_array(ctx, p, new_len)) { + JS_FreeValue(ctx, val); + return -1; + } + } + p->u.array.u.values[new_len - 1] = val; + p->u.array.count = new_len; + return true; +} + +static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) +{ + JS_FreeValue(ctx, desc->getter); + JS_FreeValue(ctx, desc->setter); + JS_FreeValue(ctx, desc->value); +} + +/* return -1 in case of exception or true or false. Warning: 'val' is + freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD, + JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set, + the new property is not added and an error is raised. + 'obj' must be an object when obj != this_obj. + */ +static int JS_SetPropertyInternal2(JSContext *ctx, JSValueConst obj, JSAtom prop, + JSValue val, JSValueConst this_obj, int flags) +{ + JSObject *p, *p1; + JSShapeProperty *prs; + JSProperty *pr; + JSPropertyDescriptor desc; + int ret; + + switch(JS_VALUE_GET_TAG(this_obj)) { + case JS_TAG_NULL: + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop); + goto fail; + case JS_TAG_UNDEFINED: + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop); + goto fail; + case JS_TAG_OBJECT: + p = JS_VALUE_GET_OBJ(this_obj); + p1 = JS_VALUE_GET_OBJ(obj); + if (p == p1) + break; + goto retry2; + default: + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + obj = JS_GetPrototypePrimitive(ctx, obj); + p = NULL; + p1 = JS_VALUE_GET_OBJ(obj); + goto prototype_lookup; + } + +retry: + prs = find_own_property(&pr, p1, prop); + if (prs) { + if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | + JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { + /* fast case */ + set_value(ctx, &pr->u.value, val); + return true; + } else if (prs->flags & JS_PROP_LENGTH) { + assert(p->class_id == JS_CLASS_ARRAY); + assert(prop == JS_ATOM_length); + return set_array_length(ctx, p, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + /* JS_PROP_WRITABLE is always true for variable + references, but they are write protected in module name + spaces. */ + if (p->class_id == JS_CLASS_MODULE_NS) + goto read_only_prop; + set_value(ctx, pr->u.var_ref->pvalue, val); + return true; + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry (potentially useless) */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + goto fail; + goto retry; + } else { + goto read_only_prop; + } + } + + for(;;) { + if (p1->is_exotic) { + if (p1->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx < p1->u.array.count) { + if (unlikely(p == p1)) + return JS_SetPropertyValue(ctx, this_obj, js_int32(idx), val, flags); + else + break; + } else if (is_typed_array(p1->class_id)) { + goto typed_array_oob; + } + } else if (is_typed_array(p1->class_id)) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + goto fail; + typed_array_oob: + // per spec: evaluate value for side effects + if (p1->class_id == JS_CLASS_BIG_INT64_ARRAY || + p1->class_id == JS_CLASS_BIG_UINT64_ARRAY) { + int64_t v; + if (JS_ToBigInt64Free(ctx, &v, val)) + return -1; + } else { + val = JS_ToNumberFree(ctx, val); + JS_FreeValue(ctx, val); + if (JS_IsException(val)) + return -1; + } + return true; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic; + if (em) { + JSValue obj1; + if (em->set_property) { + /* set_property can free the prototype */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p1)); + ret = em->set_property(ctx, obj1, prop, + val, this_obj, flags); + JS_FreeValue(ctx, obj1); + JS_FreeValue(ctx, val); + return ret; + } + if (em->get_own_property) { + /* get_own_property can free the prototype */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p1)); + ret = em->get_own_property(ctx, &desc, + obj1, prop); + JS_FreeValue(ctx, obj1); + if (ret < 0) + goto fail; + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JSObject *setter; + if (JS_IsUndefined(desc.setter)) + setter = NULL; + else + setter = JS_VALUE_GET_OBJ(desc.setter); + ret = call_setter(ctx, setter, this_obj, val, flags); + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + return ret; + } else { + JS_FreeValue(ctx, desc.value); + if (!(desc.flags & JS_PROP_WRITABLE)) + goto read_only_prop; + if (likely(p == p1)) { + ret = JS_DefineProperty(ctx, this_obj, prop, val, + JS_UNDEFINED, JS_UNDEFINED, + JS_PROP_HAS_VALUE); + JS_FreeValue(ctx, val); + return ret; + } else { + break; + } + } + } + } + } + } + } + p1 = p1->shape->proto; + prototype_lookup: + if (!p1) + break; + + retry2: + prs = find_own_property(&pr, p1, prop); + if (prs) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry (potentially useless) */ + if (JS_AutoInitProperty(ctx, p1, prop, pr, prs)) + return -1; + goto retry2; + } else if (!(prs->flags & JS_PROP_WRITABLE)) { + goto read_only_prop; + } else { + break; + } + } + } + + if (unlikely(flags & JS_PROP_NO_ADD)) { + JS_ThrowReferenceErrorNotDefined(ctx, prop); + goto fail; + } + + if (unlikely(!p)) { + ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object"); + goto done; + } + + if (unlikely(!p->extensible)) { + ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); + goto done; + } + + if (p == JS_VALUE_GET_OBJ(obj)) { + if (p->is_exotic) { + if (p->class_id == JS_CLASS_ARRAY && p->fast_array && + __JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx == p->u.array.count) { + /* fast case */ + return add_fast_array_element(ctx, p, val, flags); + } + } + goto generic_create_prop; + } else { + pr = add_property(ctx, p, prop, JS_PROP_C_W_E); + if (!pr) + goto fail; + pr->u.value = val; + return true; + } + } + + // TODO(bnoordhuis) return JSProperty slot and update in place + // when plain property (not is_exotic/setter/etc.) to avoid + // calling find_own_property() thrice? + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (ret < 0) + goto fail; + + if (ret) { + JS_FreeValue(ctx, desc.value); + if (desc.flags & JS_PROP_GETSET) { + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden"); + goto done; + } else if (!(desc.flags & JS_PROP_WRITABLE) || + p->class_id == JS_CLASS_MODULE_NS) { + read_only_prop: + ret = JS_ThrowTypeErrorReadOnly(ctx, flags, prop); + goto done; + } + ret = JS_DefineProperty(ctx, this_obj, prop, val, + JS_UNDEFINED, JS_UNDEFINED, + JS_PROP_HAS_VALUE); + } else { + generic_create_prop: + ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, + flags | + JS_PROP_HAS_VALUE | + JS_PROP_HAS_ENUMERABLE | + JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_CONFIGURABLE | + JS_PROP_C_W_E); + } + +done: + JS_FreeValue(ctx, val); + return ret; +fail: + JS_FreeValue(ctx, val); + return -1; +} + +static int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, JSAtom prop, + JSValue val, int flags) +{ + return JS_SetPropertyInternal2(ctx, obj, prop, val, obj, flags); +} + +int JS_SetProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue val) +{ + return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW); +} + +/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ +static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, + JSValue prop, JSValue val, int flags) +{ + if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && + JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { + JSObject *p; + uint32_t idx; + double d; + int32_t v; + + /* fast path for array access */ + p = JS_VALUE_GET_OBJ(this_obj); + idx = JS_VALUE_GET_INT(prop); + switch(p->class_id) { + case JS_CLASS_ARRAY: + if (unlikely(idx >= (uint32_t)p->u.array.count)) { + JSObject *p1; + JSShape *sh1; + + /* fast path to add an element to the array */ + if (idx != (uint32_t)p->u.array.count || + !p->fast_array || !p->extensible) + goto slow_path; + /* check if prototype chain has a numeric property */ + p1 = p->shape->proto; + while (p1 != NULL) { + sh1 = p1->shape; + if (p1->class_id == JS_CLASS_ARRAY) { + if (unlikely(!p1->fast_array)) + goto slow_path; + } else if (p1->class_id == JS_CLASS_OBJECT) { + if (unlikely(sh1->has_small_array_index)) + goto slow_path; + } else { + goto slow_path; + } + p1 = sh1->proto; + } + /* add element */ + return add_fast_array_element(ctx, p, val, flags); + } + set_value(ctx, &p->u.array.u.values[idx], val); + break; + case JS_CLASS_ARGUMENTS: + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto slow_path; + set_value(ctx, &p->u.array.u.values[idx], val); + break; + case JS_CLASS_UINT8C_ARRAY: + if (JS_ToUint8ClampFree(ctx, &v, val)) + goto ta_cvt_fail; + /* Note: the conversion can detach the typed array, so the + array bound check must be done after */ + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint8_ptr[idx] = v; + break; + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint8_ptr[idx] = v; + break; + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint16_ptr[idx] = v; + break; + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint32_ptr[idx] = v; + break; + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + /* XXX: need specific conversion function */ + { + int64_t v; + if (JS_ToBigInt64Free(ctx, &v, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint64_ptr[idx] = v; + } + break; + case JS_CLASS_FLOAT16_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.fp16_ptr[idx] = tofp16(d); + break; + case JS_CLASS_FLOAT32_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.float_ptr[idx] = d; + break; + case JS_CLASS_FLOAT64_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) { + ta_cvt_fail: + if (flags & JS_PROP_REFLECT_DEFINE_PROPERTY) { + JS_FreeValue(ctx, JS_GetException(ctx)); + return false; + } + return -1; + } + if (unlikely(idx >= (uint32_t)p->u.array.count)) { + ta_out_of_bound: + if (typed_array_is_oob(p)) + if (flags & JS_PROP_DEFINE_PROPERTY) + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); + return true; // per spec: no OOB exception + } + p->u.array.u.double_ptr[idx] = d; + break; + default: + goto slow_path; + } + return true; + } else { + JSAtom atom; + int ret; + slow_path: + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; + } +} + +int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx, JSValue val) +{ + return JS_SetPropertyValue(ctx, this_obj, js_uint32(idx), val, + JS_PROP_THROW); +} + +int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, + int64_t idx, JSValue val) +{ + JSAtom prop; + int res; + + if ((uint64_t)idx <= INT32_MAX) { + /* fast path for fast arrays */ + return JS_SetPropertyValue(ctx, this_obj, js_int32(idx), val, + JS_PROP_THROW); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } + res = JS_SetProperty(ctx, this_obj, prop, val); + JS_FreeAtom(ctx, prop); + return res; +} + +/* `prop` may be pure ASCII or UTF-8 encoded */ +int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, + const char *prop, JSValue val) +{ + JSAtom atom; + int ret; + atom = JS_NewAtom(ctx, prop); + if (atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* compute the property flags. For each flag: (JS_PROP_HAS_x forces + it, otherwise def_flags is used) + Note: makes assumption about the bit pattern of the flags +*/ +static int get_prop_flags(int flags, int def_flags) +{ + int mask; + mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E; + return (flags & mask) | (def_flags & ~mask); +} + +static int JS_CreateProperty(JSContext *ctx, JSObject *p, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags) +{ + JSProperty *pr; + int ret, prop_flags; + + /* add a new property or modify an existing exotic one */ + if (p->is_exotic) { + if (p->class_id == JS_CLASS_ARRAY) { + uint32_t idx, len; + + if (p->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx == p->u.array.count) { + if (!p->extensible) + goto not_extensible; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) + goto convert_to_array; + prop_flags = get_prop_flags(flags, 0); + if (prop_flags != JS_PROP_C_W_E) + goto convert_to_array; + return add_fast_array_element(ctx, p, + js_dup(val), flags); + } else { + goto convert_to_array; + } + } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { + /* convert the fast array to normal array */ + convert_to_array: + if (convert_fast_array_to_array(ctx, p)) + return -1; + goto generic_array; + } + } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { + JSProperty *plen; + JSShapeProperty *pslen; + generic_array: + /* update the length field */ + plen = &p->prop[0]; + JS_ToUint32(ctx, &len, plen->u.value); + if ((idx + 1) > len) { + pslen = get_shape_prop(p->shape); + if (unlikely(!(pslen->flags & JS_PROP_WRITABLE))) + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + /* XXX: should update the length after defining + the property */ + len = idx + 1; + set_value(ctx, &plen->u.value, js_uint32(len)); + } + } + } else if (is_typed_array(p->class_id)) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return -1; + return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array"); + } + } else if (!(flags & JS_PROP_NO_EXOTIC)) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em) { + if (em->define_own_property) { + return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), + prop, val, getter, setter, flags); + } + ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ret < 0) + return -1; + if (!ret) + goto not_extensible; + } + } + } + + if (!p->extensible) { + not_extensible: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); + } + + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | + JS_PROP_GETSET; + } else { + prop_flags = flags & JS_PROP_C_W_E; + } + pr = add_property(ctx, p, prop, prop_flags); + if (unlikely(!pr)) + return -1; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + pr->u.getset.getter = NULL; + if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) { + pr->u.getset.getter = + JS_VALUE_GET_OBJ(js_dup(getter)); + } + pr->u.getset.setter = NULL; + if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) { + pr->u.getset.setter = + JS_VALUE_GET_OBJ(js_dup(setter)); + } + } else { + if (flags & JS_PROP_HAS_VALUE) { + pr->u.value = js_dup(val); + } else { + pr->u.value = JS_UNDEFINED; + } + } + return true; +} + +/* return false if not OK */ +static bool check_define_prop_flags(int prop_flags, int flags) +{ + bool has_accessor, is_getset; + + if (!(prop_flags & JS_PROP_CONFIGURABLE)) { + if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) == + (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) { + return false; + } + if ((flags & JS_PROP_HAS_ENUMERABLE) && + (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE)) + return false; + } + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if (!(prop_flags & JS_PROP_CONFIGURABLE)) { + has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0); + is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET); + if (has_accessor != is_getset) + return false; + if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) { + /* not writable: cannot set the writable bit */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == + (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) + return false; + } + } + } + return true; +} + +/* ensure that the shape can be safely modified */ +static int js_shape_prepare_update(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs) +{ + JSShape *sh; + uint32_t idx = 0; /* prevent warning */ + + sh = p->shape; + if (sh->is_hashed) { + if (sh->header.ref_count != 1) { + if (pprs) + idx = *pprs - get_shape_prop(sh); + /* clone the shape (the resulting one is no longer hashed) */ + sh = js_clone_shape(ctx, sh); + if (!sh) + return -1; + js_free_shape(ctx->rt, p->shape); + p->shape = sh; + if (pprs) + *pprs = get_shape_prop(sh) + idx; + } else { + js_shape_hash_unlink(ctx->rt, sh); + sh->is_hashed = false; + } + } + return 0; +} + +static int js_update_property_flags(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs, int flags) +{ + if (flags != (*pprs)->flags) { + if (js_shape_prepare_update(ctx, p, pprs)) + return -1; + (*pprs)->flags = flags; + } + return 0; +} + +/* allowed flags: + JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE + JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE, + JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE, + JS_PROP_THROW, JS_PROP_NO_EXOTIC. + If JS_PROP_THROW is set, return an exception instead of false. + if JS_PROP_NO_EXOTIC is set, do not call the exotic + define_own_property callback. + return -1 (exception), false or true. +*/ +int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, int flags) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int mask, res; + + if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(this_obj); + + redo_prop_update: + prs = find_own_property(&pr, p, prop); + if (prs) { + /* the range of the Array length property is always tested before */ + if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) { + uint32_t array_length; + if (JS_ToArrayLengthFree(ctx, &array_length, + js_dup(val), false)) { + return -1; + } + /* this code relies on the fact that Uint32 are never allocated */ + val = js_uint32(array_length); + /* prs may have been modified */ + prs = find_own_property(&pr, p, prop); + assert(prs != NULL); + } + /* property already exists */ + if (!check_define_prop_flags(prs->flags, flags)) { + not_configurable: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); + } + + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return -1; + goto redo_prop_update; + } + + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + JSObject *new_getter, *new_setter; + + if (JS_IsFunction(ctx, getter)) { + new_getter = JS_VALUE_GET_OBJ(getter); + } else { + new_getter = NULL; + } + if (JS_IsFunction(ctx, setter)) { + new_setter = JS_VALUE_GET_OBJ(setter); + } else { + new_setter = NULL; + } + + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) { + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + /* convert to getset */ + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + free_var_ref(ctx->rt, pr->u.var_ref); + } else { + JS_FreeValue(ctx, pr->u.value); + } + prs->flags = (prs->flags & + (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | + JS_PROP_GETSET; + pr->u.getset.getter = NULL; + pr->u.getset.setter = NULL; + } else { + if (!(prs->flags & JS_PROP_CONFIGURABLE)) { + if ((flags & JS_PROP_HAS_GET) && + new_getter != pr->u.getset.getter) { + goto not_configurable; + } + if ((flags & JS_PROP_HAS_SET) && + new_setter != pr->u.getset.setter) { + goto not_configurable; + } + } + } + if (flags & JS_PROP_HAS_GET) { + if (pr->u.getset.getter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (new_getter) + js_dup(getter); + pr->u.getset.getter = new_getter; + } + if (flags & JS_PROP_HAS_SET) { + if (pr->u.getset.setter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + if (new_setter) + js_dup(setter); + pr->u.getset.setter = new_setter; + } + } else { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + /* convert to data descriptor */ + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + if (pr->u.getset.getter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); + pr->u.value = JS_UNDEFINED; + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + /* Note: JS_PROP_VARREF is always writable */ + } else { + if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && + (flags & JS_PROP_HAS_VALUE)) { + if (!js_same_value(ctx, val, pr->u.value)) { + goto not_configurable; + } else { + return true; + } + } + } + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if (flags & JS_PROP_HAS_VALUE) { + if (p->class_id == JS_CLASS_MODULE_NS) { + /* JS_PROP_WRITABLE is always true for variable + references, but they are write protected in module name + spaces. */ + if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue)) + goto not_configurable; + } + /* update the reference */ + set_value(ctx, pr->u.var_ref->pvalue, js_dup(val)); + } + /* if writable is set to false, no longer a + reference (for mapped arguments) */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { + JSValue val1; + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + val1 = js_dup(*pr->u.var_ref->pvalue); + free_var_ref(ctx->rt, pr->u.var_ref); + pr->u.value = val1; + prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); + } + } else if (prs->flags & JS_PROP_LENGTH) { + if (flags & JS_PROP_HAS_VALUE) { + /* Note: no JS code is executable because + 'val' is guaranted to be a Uint32 */ + res = set_array_length(ctx, p, js_dup(val), flags); + } else { + res = true; + } + /* still need to reset the writable flag if + needed. The JS_PROP_LENGTH is kept because the + Uint32 test is still done if the length + property is read-only. */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == + JS_PROP_HAS_WRITABLE) { + prs = get_shape_prop(p->shape); + if (js_update_property_flags(ctx, p, &prs, + prs->flags & ~JS_PROP_WRITABLE)) + return -1; + } + return res; + } else { + if (flags & JS_PROP_HAS_VALUE) { + JS_FreeValue(ctx, pr->u.value); + pr->u.value = js_dup(val); + } + if (flags & JS_PROP_HAS_WRITABLE) { + if (js_update_property_flags(ctx, p, &prs, + (prs->flags & ~JS_PROP_WRITABLE) | + (flags & JS_PROP_WRITABLE))) + return -1; + } + } + } + } + mask = 0; + if (flags & JS_PROP_HAS_CONFIGURABLE) + mask |= JS_PROP_CONFIGURABLE; + if (flags & JS_PROP_HAS_ENUMERABLE) + mask |= JS_PROP_ENUMERABLE; + if (js_update_property_flags(ctx, p, &prs, + (prs->flags & ~mask) | (flags & mask))) + return -1; + return true; + } + + /* handle modification of fast array elements */ + if (p->fast_array) { + uint32_t idx; + uint32_t prop_flags; + if (p->class_id == JS_CLASS_ARRAY) { + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + prop_flags = get_prop_flags(flags, JS_PROP_C_W_E); + if (prop_flags != JS_PROP_C_W_E) + goto convert_to_slow_array; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + convert_to_slow_array: + if (convert_fast_array_to_array(ctx, p)) + return -1; + else + goto redo_prop_update; + } + if (flags & JS_PROP_HAS_VALUE) { + set_value(ctx, &p->u.array.u.values[idx], js_dup(val)); + } + return true; + } + } + } else if (is_typed_array(p->class_id)) { + JSValue num; + int ret; + + if (!__JS_AtomIsTaggedInt(prop)) { + /* slow path with to handle all numeric indexes */ + num = JS_AtomIsNumericIndex1(ctx, prop); + if (JS_IsUndefined(num)) + goto typed_array_done; + if (JS_IsException(num)) + return -1; + ret = JS_NumberIsInteger(ctx, num); + if (ret < 0) { + JS_FreeValue(ctx, num); + return -1; + } + if (!ret) { + JS_FreeValue(ctx, num); + return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array"); + } + ret = JS_NumberIsNegativeOrMinusZero(ctx, num); + JS_FreeValue(ctx, num); + if (ret) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array"); + } + if (!__JS_AtomIsTaggedInt(prop)) + goto typed_array_oob; + } + idx = __JS_AtomToUInt32(prop); + /* if the typed array is detached, p->u.array.count = 0 */ + if (idx >= p->u.array.count) { + typed_array_oob: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array"); + } + prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) || + prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags"); + } + if (flags & JS_PROP_HAS_VALUE) { + return JS_SetPropertyValue(ctx, this_obj, js_int32(idx), js_dup(val), flags); + } + return true; + typed_array_done: ; + } + } + + return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags); +} + +static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSAutoInitIDEnum id, + void *opaque, int flags) +{ + JSObject *p; + JSProperty *pr; + + if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) + return false; + + p = JS_VALUE_GET_OBJ(this_obj); + + if (find_own_property(&pr, p, prop)) { + /* property already exists */ + abort(); + return false; + } + + /* Specialized CreateProperty */ + pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT); + if (unlikely(!pr)) + return -1; + pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx); + assert((pr->u.init.realm_and_id & 3) == 0); + assert(id <= 3); + pr->u.init.realm_and_id |= id; + pr->u.init.opaque = opaque; + return true; +} + +/* shortcut to add or redefine a new property value */ +int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValue val, int flags) +{ + int ret; + ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, + flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE); + JS_FreeValue(ctx, val); + return ret; +} + +int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj, + JSValue prop, JSValue val, int flags) +{ + JSAtom atom; + int ret; + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; +} + +int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx, JSValue val, int flags) +{ + return JS_DefinePropertyValueValue(ctx, this_obj, js_uint32(idx), + val, flags); +} + +int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj, + int64_t idx, JSValue val, int flags) +{ + return JS_DefinePropertyValueValue(ctx, this_obj, js_int64(idx), + val, flags); +} + +/* `prop` may be pure ASCII or UTF-8 encoded */ +int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, + const char *prop, JSValue val, int flags) +{ + JSAtom atom; + int ret; + atom = JS_NewAtom(ctx, prop); + if (atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* shortcut to add getter & setter */ +int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValue getter, JSValue setter, + int flags) +{ + int ret; + ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter, + flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE); + JS_FreeValue(ctx, getter); + JS_FreeValue(ctx, setter); + return ret; +} + +static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj, + int64_t idx, JSValue val, int flags) +{ + return JS_DefinePropertyValueValue(ctx, this_obj, js_int64(idx), + val, flags | JS_PROP_CONFIGURABLE | + JS_PROP_ENUMERABLE | JS_PROP_WRITABLE); +} + + +/* return true if 'obj' has a non empty 'name' string */ +static bool js_object_has_name(JSContext *ctx, JSValue obj) +{ + JSProperty *pr; + JSShapeProperty *prs; + JSValue val; + JSString *p; + + prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name); + if (!prs) + return false; + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) + return true; + val = pr->u.value; + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) + return true; + p = JS_VALUE_GET_STRING(val); + return (p->len != 0); +} + +static int JS_DefineObjectName(JSContext *ctx, JSValue obj, + JSAtom name, int flags) +{ + if (name != JS_ATOM_NULL + && JS_IsObject(obj) + && !js_object_has_name(ctx, obj) + && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) { + return -1; + } + return 0; +} + +static int JS_DefineObjectNameComputed(JSContext *ctx, JSValue obj, + JSValue str, int flags) +{ + if (JS_IsObject(obj) && + !js_object_has_name(ctx, obj)) { + JSAtom prop; + JSValue name_str; + prop = JS_ValueToAtom(ctx, str); + if (prop == JS_ATOM_NULL) + return -1; + name_str = js_get_function_name(ctx, prop); + JS_FreeAtom(ctx, prop); + if (JS_IsException(name_str)) + return -1; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0) + return -1; + } + return 0; +} + +#define DEFINE_GLOBAL_LEX_VAR (1 << 7) +#define DEFINE_GLOBAL_FUNC_VAR (1 << 6) + +static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop) +{ + return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop); +} + +/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ +/* XXX: could support exotic global object. */ +static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) +{ + JSObject *p; + JSShapeProperty *prs; + + p = JS_VALUE_GET_OBJ(ctx->global_obj); + prs = find_own_property1(p, prop); + /* XXX: should handle JS_PROP_AUTOINIT */ + if (flags & DEFINE_GLOBAL_LEX_VAR) { + if (prs && !(prs->flags & JS_PROP_CONFIGURABLE)) + goto fail_redeclaration; + } else { + if (!prs && !p->extensible) + goto define_error; + if (flags & DEFINE_GLOBAL_FUNC_VAR) { + if (prs) { + if (!(prs->flags & JS_PROP_CONFIGURABLE) && + ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET || + ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) != + (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) { + define_error: + JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'", + prop); + return -1; + } + } + } + } + /* check if there already is a lexical declaration */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property1(p, prop); + if (prs) { + fail_redeclaration: + JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop); + return -1; + } + return 0; +} + +/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) | + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */ +/* XXX: could support exotic global object. */ +static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSValue val; + int flags; + + if (def_flags & DEFINE_GLOBAL_LEX_VAR) { + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) | + JS_PROP_CONFIGURABLE; + val = JS_UNINITIALIZED; + } else { + p = JS_VALUE_GET_OBJ(ctx->global_obj); + flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | + (def_flags & JS_PROP_CONFIGURABLE); + val = JS_UNDEFINED; + } + prs = find_own_property1(p, prop); + if (prs) + return 0; + if (!p->extensible) + return 0; + pr = add_property(ctx, p, prop, flags); + if (unlikely(!pr)) + return -1; + pr->u.value = val; + return 0; +} + +/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */ +/* XXX: could support exotic global object. */ +static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop, + JSValue func, int def_flags) +{ + + JSObject *p; + JSShapeProperty *prs; + int flags; + + p = JS_VALUE_GET_OBJ(ctx->global_obj); + prs = find_own_property1(p, prop); + flags = JS_PROP_HAS_VALUE | JS_PROP_THROW; + if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) { + flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE; + } + if (JS_DefineProperty(ctx, ctx->global_obj, prop, func, + JS_UNDEFINED, JS_UNDEFINED, flags) < 0) + return -1; + return 0; +} + +static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop, + bool throw_ref_error) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_TMASK properties */ + if (unlikely(JS_IsUninitialized(pr->u.value))) + return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return js_dup(pr->u.value); + } + return JS_GetPropertyInternal(ctx, ctx->global_obj, prop, + ctx->global_obj, throw_ref_error); +} + +/* construct a reference to a global variable */ +static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_AUTOINIT properties? */ + /* XXX: conformance: do these tests in + OP_put_var_ref/OP_get_var_ref ? */ + if (unlikely(JS_IsUninitialized(pr->u.value))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { + return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); + } + sp[0] = js_dup(ctx->global_var_obj); + } else { + int ret; + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + if (ret) { + sp[0] = js_dup(ctx->global_obj); + } else { + sp[0] = JS_UNDEFINED; + } + } + sp[1] = JS_AtomToValue(ctx, prop); + return 0; +} + +/* use for strict variable access: test if the variable exists */ +static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop) +{ + JSObject *p; + JSShapeProperty *prs; + int ret; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property1(p, prop); + if (prs) { + ret = true; + } else { + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + } + return ret; +} + +/* flag = 0: normal variable write + flag = 1: initialize lexical variable + flag = 2: normal variable write, strict check was done before +*/ +static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, + int flag) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int flags; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_AUTOINIT properties? */ + if (flag != 1) { + if (unlikely(JS_IsUninitialized(pr->u.value))) { + JS_FreeValue(ctx, val); + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); + } + } + set_value(ctx, &pr->u.value, val); + return 0; + } + flags = JS_PROP_THROW_STRICT; + if (is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD; + return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags); +} + +/* return -1, false or true */ +static int JS_DeleteGlobalVar(JSContext *ctx, JSAtom prop) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int ret; + + /* 9.1.1.4.7 DeleteBinding ( N ) */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) + return false; /* lexical variables cannot be deleted */ + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + if (ret) { + return JS_DeleteProperty(ctx, ctx->global_obj, prop, 0); + } else { + return true; + } +} + +/* return -1, false or true. return false if not configurable or + invalid object. return -1 in case of exception. + flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */ +int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags) +{ + JSValue obj1; + JSObject *p; + int res; + + obj1 = JS_ToObject(ctx, obj); + if (JS_IsException(obj1)) + return -1; + p = JS_VALUE_GET_OBJ(obj1); + res = delete_property(ctx, p, prop); + JS_FreeValue(ctx, obj1); + if (res != false) + return res; + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeError(ctx, "could not delete property"); + return -1; + } + return false; +} + +int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags) +{ + JSAtom prop; + int res; + + if ((uint64_t)idx <= JS_ATOM_MAX_INT) { + /* fast path for fast arrays */ + return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) + return -1; + res = JS_DeleteProperty(ctx, obj, prop, flags); + JS_FreeAtom(ctx, prop); + return res; +} + +bool JS_IsFunction(JSContext *ctx, JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(val); + switch(p->class_id) { + case JS_CLASS_BYTECODE_FUNCTION: + return true; + case JS_CLASS_PROXY: + return p->u.proxy_data->is_func; + default: + return (ctx->rt->class_array[p->class_id].call != NULL); + } +} + +static bool JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, + int magic) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(val); + if (p->class_id == JS_CLASS_C_FUNCTION) + return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic); + else + return false; +} + +bool JS_IsConstructor(JSContext *ctx, JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(val); + return p->is_constructor; +} + +bool JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, bool val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(func_obj); + p->is_constructor = val; + return true; +} + +bool JS_IsRegExp(JSValueConst val) +{ + return JS_CLASS_REGEXP == JS_GetClassID(val); +} + +bool JS_IsMap(JSValueConst val) +{ + return JS_CLASS_MAP == JS_GetClassID(val); +} + +bool JS_IsSet(JSValueConst val) +{ + return JS_CLASS_SET == JS_GetClassID(val); +} + +bool JS_IsWeakRef(JSValueConst val) +{ + return JS_CLASS_WEAK_REF == JS_GetClassID(val); +} + +bool JS_IsWeakSet(JSValueConst val) +{ + return JS_CLASS_WEAKSET == JS_GetClassID(val); +} + +bool JS_IsWeakMap(JSValueConst val) +{ + return JS_CLASS_WEAKMAP == JS_GetClassID(val); +} + +bool JS_IsDataView(JSValueConst val) +{ + return JS_CLASS_DATAVIEW == JS_GetClassID(val); +} + +bool JS_IsError(JSValueConst val) +{ + return JS_CLASS_ERROR == JS_GetClassID(val); +} + +/* used to avoid catching interrupt exceptions */ +bool JS_IsUncatchableError(JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error; +} + +static void js_set_uncatchable_error(JSContext *ctx, JSValueConst val, bool flag) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(val); + if (p->class_id == JS_CLASS_ERROR) + p->is_uncatchable_error = flag; +} + +void JS_SetUncatchableError(JSContext *ctx, JSValueConst val) +{ + js_set_uncatchable_error(ctx, val, true); +} + +void JS_ClearUncatchableError(JSContext *ctx, JSValueConst val) +{ + js_set_uncatchable_error(ctx, val, false); +} + +void JS_ResetUncatchableError(JSContext *ctx) +{ + js_set_uncatchable_error(ctx, ctx->rt->current_exception, false); +} + +int JS_SetOpaque(JSValueConst obj, void *opaque) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + // User code can't set the opaque of internal objects. + if (p->class_id >= JS_CLASS_INIT_COUNT) { + p->u.opaque = opaque; + return 0; + } + } + + return -1; +} + +/* |obj| must be a JSObject of an internal class. */ +static void JS_SetOpaqueInternal(JSValueConst obj, void *opaque) +{ + JSObject *p; + assert(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT); + p = JS_VALUE_GET_OBJ(obj); + assert(p->class_id < JS_CLASS_INIT_COUNT); + p->u.opaque = opaque; +} + +/* return NULL if not an object of class class_id */ +void *JS_GetOpaque(JSValueConst obj, JSClassID class_id) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return NULL; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != class_id) + return NULL; + return p->u.opaque; +} + +void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id) +{ + void *p = JS_GetOpaque(obj, class_id); + if (unlikely(!p)) { + JS_ThrowTypeErrorInvalidClass(ctx, class_id); + } + return p; +} + +void *JS_GetAnyOpaque(JSValueConst obj, JSClassID *class_id) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + *class_id = 0; + return NULL; + } + p = JS_VALUE_GET_OBJ(obj); + *class_id = p->class_id; + return p->u.opaque; +} + +static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) +{ + int i; + bool force_ordinary; + + JSAtom method_name; + JSValue method, ret; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return val; + force_ordinary = hint & HINT_FORCE_ORDINARY; + hint &= ~HINT_FORCE_ORDINARY; + if (!force_ordinary) { + method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive); + if (JS_IsException(method)) + goto exception; + /* ECMA says *If exoticToPrim is not undefined* but tests in + test262 use null as a non callable converter */ + if (!JS_IsUndefined(method) && !JS_IsNull(method)) { + JSAtom atom; + JSValue arg; + switch(hint) { + case HINT_STRING: + atom = JS_ATOM_string; + break; + case HINT_NUMBER: + atom = JS_ATOM_number; + break; + default: + case HINT_NONE: + atom = JS_ATOM_default; + break; + } + arg = JS_AtomToString(ctx, atom); + ret = JS_CallFree(ctx, method, val, 1, vc(&arg)); + JS_FreeValue(ctx, arg); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, val); + if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) + return ret; + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "toPrimitive"); + } + } + if (hint != HINT_STRING) + hint = HINT_NUMBER; + for(i = 0; i < 2; i++) { + if ((i ^ hint) == 0) { + method_name = JS_ATOM_toString; + } else { + method_name = JS_ATOM_valueOf; + } + method = JS_GetProperty(ctx, val, method_name); + if (JS_IsException(method)) + goto exception; + if (JS_IsFunction(ctx, method)) { + ret = JS_CallFree(ctx, method, val, 0, NULL); + if (JS_IsException(ret)) + goto exception; + if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { + JS_FreeValue(ctx, val); + return ret; + } + JS_FreeValue(ctx, ret); + } else { + JS_FreeValue(ctx, method); + } + } + JS_ThrowTypeError(ctx, "toPrimitive"); +exception: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint) +{ + return JS_ToPrimitiveFree(ctx, js_dup(val), hint); +} + +void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(obj); + p->is_HTMLDDA = true; +} + +static inline bool JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(obj); + return p->is_HTMLDDA; +} + +static int JS_ToBoolFree(JSContext *ctx, JSValue val) +{ + uint32_t tag = JS_VALUE_GET_TAG(val); + switch(tag) { + case JS_TAG_INT: + return JS_VALUE_GET_INT(val) != 0; + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + return JS_VALUE_GET_INT(val); + case JS_TAG_EXCEPTION: + return -1; + case JS_TAG_STRING: + { + bool ret = JS_VALUE_GET_STRING(val)->len != 0; + JS_FreeValue(ctx, val); + return ret; + } + case JS_TAG_SHORT_BIG_INT: + return JS_VALUE_GET_SHORT_BIG_INT(val) != 0; + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + bool ret; + int i; + + /* fail safe: we assume it is not necessarily + normalized. Beginning from the MSB ensures that the + test is fast. */ + ret = false; + for(i = p->len - 1; i >= 0; i--) { + if (p->tab[i] != 0) { + ret = true; + break; + } + } + JS_FreeValue(ctx, val); + return ret; + } + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(val); + bool ret = !p->is_HTMLDDA; + JS_FreeValue(ctx, val); + return ret; + } + break; + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d = JS_VALUE_GET_FLOAT64(val); + return !isnan(d) && d != 0; + } else { + JS_FreeValue(ctx, val); + return true; + } + } +} + +int JS_ToBool(JSContext *ctx, JSValueConst val) +{ + return JS_ToBoolFree(ctx, js_dup(val)); +} + +/* pc points to pure ASCII or UTF-8, null terminated contents */ +static int skip_spaces(const char *pc) +{ + const uint8_t *p, *p_next, *p_start; + uint32_t c; + + p = p_start = (const uint8_t *)pc; + for (;;) { + c = *p++; + if (c < 0x80) { + if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20))) + break; + } else { + c = utf8_decode(p - 1, &p_next); + /* no need to test for invalid UTF-8, 0xFFFD is not a space */ + if (!lre_is_space(c)) + break; + p = p_next; + } + } + return p - 1 - p_start; +} + +static inline int js_to_digit(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'Z') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + return c - 'a' + 10; + else + return 36; +} + +/* bigint support */ + +#define ADDC(res, carry_out, op1, op2, carry_in) \ +do { \ + js_limb_t __v, __a, __k, __k1; \ + __v = (op1); \ + __a = __v + (op2); \ + __k1 = __a < __v; \ + __k = (carry_in); \ + __a = __a + __k; \ + carry_out = (__a < __k) | __k1; \ + res = __a; \ +} while (0) + +/* a != 0 */ +static inline js_limb_t js_limb_clz(js_limb_t a) +{ + if (!a) + return JS_LIMB_BITS; + return clz32(a); +} + +static js_limb_t js_mp_add(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2, + js_limb_t n, js_limb_t carry) +{ + int i; + for(i = 0;i < n; i++) { + ADDC(res[i], carry, op1[i], op2[i], carry); + } + return carry; +} + +static js_limb_t js_mp_sub(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2, + int n, js_limb_t carry) +{ + int i; + js_limb_t k, a, v, k1; + + k = carry; + for(i=0;i v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} + +/* compute 0 - op2. carry = 0 or 1. */ +static js_limb_t js_mp_neg(js_limb_t *res, const js_limb_t *op2, int n) +{ + int i; + js_limb_t v, carry; + + carry = 1; + for(i=0;i> JS_LIMB_BITS; + } + return l; +} + +static js_limb_t js_mp_div1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b, js_limb_t r) +{ + js_slimb_t i; + js_dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; + } + return r; +} + +/* tabr[] += taba[] * b, return the high word. */ +static js_limb_t js_mp_add_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b) +{ + js_limb_t i, l; + js_dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l + tabr[i]; + tabr[i] = t; + l = t >> JS_LIMB_BITS; + } + return l; +} + +/* size of the result : op1_size + op2_size. */ +static void js_mp_mul_basecase(js_limb_t *result, + const js_limb_t *op1, js_limb_t op1_size, + const js_limb_t *op2, js_limb_t op2_size) +{ + int i; + js_limb_t r; + + result[op1_size] = js_mp_mul1(result, op1, op1_size, op2[0], 0); + for(i=1;i> JS_LIMB_BITS); + } + return l; +} + +/* WARNING: d must be >= 2^(JS_LIMB_BITS-1) */ +static inline js_limb_t js_udiv1norm_init(js_limb_t d) +{ + js_limb_t a0, a1; + a1 = -d - 1; + a0 = -1; + return (((js_dlimb_t)a1 << JS_LIMB_BITS) | a0) / d; +} + +/* return the quotient and the remainder in '*pr'of 'a1*2^JS_LIMB_BITS+a0 + / d' with 0 <= a1 < d. */ +static inline js_limb_t js_udiv1norm(js_limb_t *pr, js_limb_t a1, js_limb_t a0, + js_limb_t d, js_limb_t d_inv) +{ + js_limb_t n1m, n_adj, q, r, ah; + js_dlimb_t a; + n1m = ((js_slimb_t)a0 >> (JS_LIMB_BITS - 1)); + n_adj = a0 + (n1m & d); + a = (js_dlimb_t)d_inv * (a1 - n1m) + n_adj; + q = (a >> JS_LIMB_BITS) + a1; + /* compute a - q * r and update q so that the remainder is\ + between 0 and d - 1 */ + a = ((js_dlimb_t)a1 << JS_LIMB_BITS) | a0; + a = a - (js_dlimb_t)q * d - d; + ah = a >> JS_LIMB_BITS; + q += 1 + ah; + r = (js_limb_t)a + (ah & d); + *pr = r; + return q; +} + +#define UDIV1NORM_THRESHOLD 3 + +/* b must be >= 1 << (JS_LIMB_BITS - 1) */ +static js_limb_t js_mp_div1norm(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b, js_limb_t r) +{ + js_slimb_t i; + + if (n >= UDIV1NORM_THRESHOLD) { + js_limb_t b_inv; + b_inv = js_udiv1norm_init(b); + for(i = n - 1; i >= 0; i--) { + tabr[i] = js_udiv1norm(&r, r, taba[i], b, b_inv); + } + } else { + js_dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; + } + } + return r; +} + +/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb + - 1] must be >= 1 << (JS_LIMB_BITS - 1). na - nb must be >= 0. 'taba' + is modified and contains the remainder (nb limbs). tabq[0..na-nb] + contains the quotient with tabq[na - nb] <= 1. */ +static void js_mp_divnorm(js_limb_t *tabq, js_limb_t *taba, js_limb_t na, + const js_limb_t *tabb, js_limb_t nb) +{ + js_limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r; + int i, j; + + b1 = tabb[nb - 1]; + if (nb == 1) { + taba[0] = js_mp_div1norm(tabq, taba, na, b1, 0); + return; + } + n = na - nb; + + if (n >= UDIV1NORM_THRESHOLD) + b1_inv = js_udiv1norm_init(b1); + else + b1_inv = 0; + + /* first iteration: the quotient is only 0 or 1 */ + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[n + j] != tabb[j]) { + if (taba[n + j] < tabb[j]) + q = 0; + break; + } + } + tabq[n] = q; + if (q) { + js_mp_sub(taba + n, taba + n, tabb, nb, 0); + } + + for(i = n - 1; i >= 0; i--) { + if (unlikely(taba[i + nb] >= b1)) { + q = -1; + } else if (b1_inv) { + q = js_udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv); + } else { + js_dlimb_t al; + al = ((js_dlimb_t)taba[i + nb] << JS_LIMB_BITS) | taba[i + nb - 1]; + q = al / b1; + r = al % b1; + } + r = js_mp_sub_mul1(taba + i, tabb, nb, q); + + v = taba[i + nb]; + a = v - r; + c = (a > v); + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = js_mp_add(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == 0) { + break; + } + } + } + } + tabq[i] = q; + } +} + +/* 1 <= shift <= JS_LIMB_BITS - 1 */ +static js_limb_t js_mp_shl(js_limb_t *tabr, const js_limb_t *taba, int n, + int shift) +{ + int i; + js_limb_t l, v; + l = 0; + for(i = 0; i < n; i++) { + v = taba[i]; + tabr[i] = (v << shift) | l; + l = v >> (JS_LIMB_BITS - shift); + } + return l; +} + +/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). + 1 <= shift <= LIMB_BITS - 1 */ +static js_limb_t js_mp_shr(js_limb_t *tab_r, const js_limb_t *tab, int n, + int shift, js_limb_t high) +{ + int i; + js_limb_t l, a; + + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + tab_r[i] = (a >> shift) | (l << (JS_LIMB_BITS - shift)); + l = a; + } + return l & (((js_limb_t)1 << shift) - 1); +} + +static JSBigInt *js_bigint_new(JSContext *ctx, int len) +{ + JSBigInt *r; + if (len > JS_BIGINT_MAX_SIZE) { + JS_ThrowRangeError(ctx, "BigInt is too large to allocate"); + return NULL; + } + r = js_malloc(ctx, sizeof(JSBigInt) + len * sizeof(js_limb_t)); + if (!r) + return NULL; + r->header.ref_count = 1; + r->len = len; + return r; +} + +static JSBigInt *js_bigint_set_si(JSBigIntBuf *buf, js_slimb_t a) +{ + JSBigInt *r = (JSBigInt *)buf->big_int_buf; + r->header.ref_count = 0; /* fail safe */ + r->len = 1; + r->tab[0] = a; + return r; +} + +static JSBigInt *js_bigint_set_si64(JSBigIntBuf *buf, int64_t a) +{ + JSBigInt *r = (JSBigInt *)buf->big_int_buf; + r->header.ref_count = 0; /* fail safe */ + if (a >= INT32_MIN && a <= INT32_MAX) { + r->len = 1; + r->tab[0] = a; + } else { + r->len = 2; + r->tab[0] = a; + r->tab[1] = a >> JS_LIMB_BITS; + } + return r; +} + +/* val must be a short big int */ +static JSBigInt *js_bigint_set_short(JSBigIntBuf *buf, JSValueConst val) +{ + return js_bigint_set_si(buf, JS_VALUE_GET_SHORT_BIG_INT(val)); +} + +static __maybe_unused void js_bigint_dump1(JSContext *ctx, const char *str, + const js_limb_t *tab, int len) +{ + int i; + printf("%s: ", str); + for(i = len - 1; i >= 0; i--) { + printf(" %08x", tab[i]); + } + printf("\n"); +} + +static __maybe_unused void js_bigint_dump(JSContext *ctx, const char *str, + const JSBigInt *p) +{ + js_bigint_dump1(ctx, str, p->tab, p->len); +} + +static JSBigInt *js_bigint_new_si(JSContext *ctx, js_slimb_t a) +{ + JSBigInt *r; + r = js_bigint_new(ctx, 1); + if (!r) + return NULL; + r->tab[0] = a; + return r; +} + +static JSBigInt *js_bigint_new_si64(JSContext *ctx, int64_t a) +{ + if (a >= INT32_MIN && a <= INT32_MAX) { + return js_bigint_new_si(ctx, a); + } else { + JSBigInt *r; + r = js_bigint_new(ctx, 2); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> 32; + return r; + } +} + +static JSBigInt *js_bigint_new_ui64(JSContext *ctx, uint64_t a) +{ + if (a <= INT64_MAX) { + return js_bigint_new_si64(ctx, a); + } else { + JSBigInt *r; + r = js_bigint_new(ctx, (65 + JS_LIMB_BITS - 1) / JS_LIMB_BITS); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> 32; + r->tab[2] = 0; + return r; + } +} + +static JSBigInt *js_bigint_new_di(JSContext *ctx, js_sdlimb_t a) +{ + JSBigInt *r; + if (a == (js_slimb_t)a) { + r = js_bigint_new(ctx, 1); + if (!r) + return NULL; + r->tab[0] = a; + } else { + r = js_bigint_new(ctx, 2); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> JS_LIMB_BITS; + } + return r; +} + +/* Remove redundant high order limbs. Warning: 'a' may be + reallocated. Can never fail. +*/ +static JSBigInt *js_bigint_normalize1(JSContext *ctx, JSBigInt *a, int l) +{ + js_limb_t v; + + assert(a->header.ref_count == 1); + while (l > 1) { + v = a->tab[l - 1]; + if ((v != 0 && v != -1) || + (v & 1) != (a->tab[l - 2] >> (JS_LIMB_BITS - 1))) { + break; + } + l--; + } + if (l != a->len) { + JSBigInt *a1; + /* realloc to reduce the size */ + a->len = l; + a1 = js_realloc(ctx, a, sizeof(JSBigInt) + l * sizeof(js_limb_t)); + if (a1) + a = a1; + } + return a; +} + +static JSBigInt *js_bigint_normalize(JSContext *ctx, JSBigInt *a) +{ + return js_bigint_normalize1(ctx, a, a->len); +} + +/* return 0 or 1 depending on the sign */ +static inline int js_bigint_sign(const JSBigInt *a) +{ + return a->tab[a->len - 1] >> (JS_LIMB_BITS - 1); +} + +static js_slimb_t js_bigint_get_si_sat(const JSBigInt *a) +{ + if (a->len == 1) { + return a->tab[0]; + } else { + if (js_bigint_sign(a)) + return INT32_MIN; + else + return INT32_MAX; + } +} + +/* add the op1 limb */ +static JSBigInt *js_bigint_extend(JSContext *ctx, JSBigInt *r, + js_limb_t op1) +{ + int n2 = r->len; + if ((op1 != 0 && op1 != -1) || + (op1 & 1) != r->tab[n2 - 1] >> (JS_LIMB_BITS - 1)) { + JSBigInt *r1; + r1 = js_realloc(ctx, r, + sizeof(JSBigInt) + (n2 + 1) * sizeof(js_limb_t)); + if (!r1) { + js_free(ctx, r); + return NULL; + } + r = r1; + r->len = n2 + 1; + r->tab[n2] = op1; + } else { + /* otherwise still need to normalize the result */ + r = js_bigint_normalize(ctx, r); + } + return r; +} + +/* return NULL in case of error. Compute a + b (b_neg = 0) or a - b + (b_neg = 1) */ +/* XXX: optimize */ +static JSBigInt *js_bigint_add(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, int b_neg) +{ + JSBigInt *r; + int n1, n2, i; + js_limb_t carry, op1, op2, a_sign, b_sign; + + n2 = max_int(a->len, b->len); + n1 = min_int(a->len, b->len); + r = js_bigint_new(ctx, n2); + if (!r) + return NULL; + /* XXX: optimize */ + /* common part */ + carry = b_neg; + for(i = 0; i < n1; i++) { + op1 = a->tab[i]; + op2 = b->tab[i] ^ (-b_neg); + ADDC(r->tab[i], carry, op1, op2, carry); + } + a_sign = -js_bigint_sign(a); + b_sign = (-js_bigint_sign(b)) ^ (-b_neg); + /* part with sign extension of one operand */ + if (a->len > b->len) { + for(i = n1; i < n2; i++) { + op1 = a->tab[i]; + ADDC(r->tab[i], carry, op1, b_sign, carry); + } + } else if (a->len < b->len) { + for(i = n1; i < n2; i++) { + op2 = b->tab[i] ^ (-b_neg); + ADDC(r->tab[i], carry, a_sign, op2, carry); + } + } + + /* part with sign extension for both operands. Extend the result + if necessary */ + return js_bigint_extend(ctx, r, a_sign + b_sign + carry); +} + +/* XXX: optimize */ +static JSBigInt *js_bigint_neg(JSContext *ctx, const JSBigInt *a) +{ + JSBigIntBuf buf; + JSBigInt *b; + b = js_bigint_set_si(&buf, 0); + return js_bigint_add(ctx, b, a, 1); +} + +static JSBigInt *js_bigint_mul(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b) +{ + JSBigInt *r; + + r = js_bigint_new(ctx, a->len + b->len); + if (!r) + return NULL; + js_mp_mul_basecase(r->tab, a->tab, a->len, b->tab, b->len); + /* correct the result if negative operands (no overflow is + possible) */ + if (js_bigint_sign(a)) + js_mp_sub(r->tab + a->len, r->tab + a->len, b->tab, b->len, 0); + if (js_bigint_sign(b)) + js_mp_sub(r->tab + b->len, r->tab + b->len, a->tab, a->len, 0); + return js_bigint_normalize(ctx, r); +} + +/* return the division or the remainder. 'b' must be != 0. return NULL + in case of exception (division by zero or memory error) */ +static JSBigInt *js_bigint_divrem(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, bool is_rem) +{ + JSBigInt *r, *q; + js_limb_t *tabb, h; + int na, nb, a_sign, b_sign, shift; + + if (b->len == 1 && b->tab[0] == 0) { + JS_ThrowRangeError(ctx, "BigInt division by zero"); + return NULL; + } + + a_sign = js_bigint_sign(a); + b_sign = js_bigint_sign(b); + na = a->len; + nb = b->len; + + r = js_bigint_new(ctx, na + 2); + if (!r) + return NULL; + if (a_sign) { + js_mp_neg(r->tab, a->tab, na); + } else { + memcpy(r->tab, a->tab, na * sizeof(a->tab[0])); + } + /* normalize */ + while (na > 1 && r->tab[na - 1] == 0) + na--; + + tabb = js_malloc(ctx, nb * sizeof(tabb[0])); + if (!tabb) { + js_free(ctx, r); + return NULL; + } + if (b_sign) { + js_mp_neg(tabb, b->tab, nb); + } else { + memcpy(tabb, b->tab, nb * sizeof(tabb[0])); + } + /* normalize */ + while (nb > 1 && tabb[nb - 1] == 0) + nb--; + + /* trivial case if 'a' is small */ + if (na < nb) { + js_free(ctx, r); + js_free(ctx, tabb); + if (is_rem) { + /* r = a */ + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0])); + return r; + } else { + /* q = 0 */ + return js_bigint_new_si(ctx, 0); + } + } + + /* normalize 'b' */ + shift = js_limb_clz(tabb[nb - 1]); + if (shift != 0) { + js_mp_shl(tabb, tabb, nb, shift); + h = js_mp_shl(r->tab, r->tab, na, shift); + if (h != 0) + r->tab[na++] = h; + } + + q = js_bigint_new(ctx, na - nb + 2); /* one more limb for the sign */ + if (!q) { + js_free(ctx, r); + js_free(ctx, tabb); + return NULL; + } + + // js_bigint_dump1(ctx, "a", r->tab, na); + // js_bigint_dump1(ctx, "b", tabb, nb); + js_mp_divnorm(q->tab, r->tab, na, tabb, nb); + js_free(ctx, tabb); + + if (is_rem) { + js_free(ctx, q); + if (shift != 0) + js_mp_shr(r->tab, r->tab, nb, shift, 0); + r->tab[nb++] = 0; + if (a_sign) + js_mp_neg(r->tab, r->tab, nb); + r = js_bigint_normalize1(ctx, r, nb); + return r; + } else { + js_free(ctx, r); + q->tab[na - nb + 1] = 0; + if (a_sign ^ b_sign) { + js_mp_neg(q->tab, q->tab, q->len); + } + q = js_bigint_normalize(ctx, q); + return q; + } +} + +/* and, or, xor */ +static JSBigInt *js_bigint_logic(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, OPCodeEnum op) +{ + JSBigInt *r; + js_limb_t b_sign; + int a_len, b_len, i; + + if (a->len < b->len) { + const JSBigInt *tmp; + tmp = a; + a = b; + b = tmp; + } + /* a_len >= b_len */ + a_len = a->len; + b_len = b->len; + b_sign = -js_bigint_sign(b); + + r = js_bigint_new(ctx, a_len); + if (!r) + return NULL; + switch(op) { + case OP_or: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] | b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] | b_sign; + } + break; + case OP_and: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] & b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] & b_sign; + } + break; + case OP_xor: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] ^ b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] ^ b_sign; + } + break; + default: + abort(); + } + return js_bigint_normalize(ctx, r); +} + +static JSBigInt *js_bigint_not(JSContext *ctx, const JSBigInt *a) +{ + JSBigInt *r; + int i; + + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + for(i = 0; i < a->len; i++) { + r->tab[i] = ~a->tab[i]; + } + /* no normalization is needed */ + return r; +} + +static JSBigInt *js_bigint_shl(JSContext *ctx, const JSBigInt *a, + unsigned int shift1) +{ + int d, i, shift; + JSBigInt *r; + js_limb_t l; + + if (a->len == 1 && a->tab[0] == 0) + return js_bigint_new_si(ctx, 0); /* zero case */ + d = shift1 / JS_LIMB_BITS; + shift = shift1 % JS_LIMB_BITS; + r = js_bigint_new(ctx, a->len + d); + if (!r) + return NULL; + for(i = 0; i < d; i++) + r->tab[i] = 0; + if (shift == 0) { + for(i = 0; i < a->len; i++) { + r->tab[i + d] = a->tab[i]; + } + } else { + l = js_mp_shl(r->tab + d, a->tab, a->len, shift); + if (js_bigint_sign(a)) + l |= (js_limb_t)(-1) << shift; + r = js_bigint_extend(ctx, r, l); + } + return r; +} + +static JSBigInt *js_bigint_shr(JSContext *ctx, const JSBigInt *a, + unsigned int shift1) +{ + int d, i, shift, a_sign, n1; + JSBigInt *r; + + d = shift1 / JS_LIMB_BITS; + shift = shift1 % JS_LIMB_BITS; + a_sign = js_bigint_sign(a); + if (d >= a->len) + return js_bigint_new_si(ctx, -a_sign); + n1 = a->len - d; + r = js_bigint_new(ctx, n1); + if (!r) + return NULL; + if (shift == 0) { + for(i = 0; i < n1; i++) { + r->tab[i] = a->tab[i + d]; + } + /* no normalization is needed */ + } else { + js_mp_shr(r->tab, a->tab + d, n1, shift, -a_sign); + r = js_bigint_normalize(ctx, r); + } + return r; +} + +static JSBigInt *js_bigint_pow(JSContext *ctx, const JSBigInt *a, JSBigInt *b) +{ + uint32_t e; + int n_bits, i; + JSBigInt *r, *r1; + + /* b must be >= 0 */ + if (js_bigint_sign(b)) { + JS_ThrowRangeError(ctx, "BigInt negative exponent"); + return NULL; + } + if (b->len == 1 && b->tab[0] == 0) { + /* a^0 = 1 */ + return js_bigint_new_si(ctx, 1); + } else if (a->len == 1) { + js_limb_t v; + bool is_neg; + + v = a->tab[0]; + if (v <= 1) + return js_bigint_new_si(ctx, v); + else if (v == -1) + return js_bigint_new_si(ctx, 1 - 2 * (b->tab[0] & 1)); + is_neg = (js_slimb_t)v < 0; + if (is_neg) + v = -v; + if ((v & (v - 1)) == 0) { + uint64_t e1; + int n; + /* v = 2^n */ + n = JS_LIMB_BITS - 1 - js_limb_clz(v); + if (b->len > 1) + goto overflow; + if (b->tab[0] > INT32_MAX) + goto overflow; + e = b->tab[0]; + e1 = (uint64_t)e * n; + if (e1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS) + goto overflow; + e = e1; + if (is_neg) + is_neg = b->tab[0] & 1; + r = js_bigint_new(ctx, + (e + JS_LIMB_BITS + 1 - is_neg) / JS_LIMB_BITS); + if (!r) + return NULL; + memset(r->tab, 0, sizeof(r->tab[0]) * r->len); + r->tab[e / JS_LIMB_BITS] = + (js_limb_t)(1 - 2 * is_neg) << (e % JS_LIMB_BITS); + return r; + } + } + if (b->len > 1) + goto overflow; + if (b->tab[0] > INT32_MAX) + goto overflow; + e = b->tab[0]; + n_bits = 32 - clz32(e); + + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0])); + for(i = n_bits - 2; i >= 0; i--) { + r1 = js_bigint_mul(ctx, r, r); + if (!r1) + return NULL; + js_free(ctx, r); + r = r1; + if ((e >> i) & 1) { + r1 = js_bigint_mul(ctx, r, a); + if (!r1) + return NULL; + js_free(ctx, r); + r = r1; + } + } + return r; + overflow: + JS_ThrowRangeError(ctx, "BigInt is too large"); + return NULL; +} + +/* return (mant, exp) so that abs(a) ~ mant*2^(exp - (limb_bits - + 1). a must be != 0. */ +static uint64_t js_bigint_get_mant_exp(JSContext *ctx, + int *pexp, const JSBigInt *a) +{ + js_limb_t t[4 - JS_LIMB_BITS / 32], carry, v, low_bits; + int n1, n2, sgn, shift, i, j, e; + uint64_t a1, a0; + + n2 = 4 - JS_LIMB_BITS / 32; + n1 = a->len - n2; + sgn = js_bigint_sign(a); + + /* low_bits != 0 if there are a non zero low bit in abs(a) */ + low_bits = 0; + carry = sgn; + for(i = 0; i < n1; i++) { + v = (a->tab[i] ^ (-sgn)) + carry; + carry = v < carry; + low_bits |= v; + } + /* get the n2 high limbs of abs(a) */ + for(j = 0; j < n2; j++) { + i = j + n1; + if (i < 0) { + v = 0; + } else { + v = (a->tab[i] ^ (-sgn)) + carry; + carry = v < carry; + } + t[j] = v; + } + + a1 = ((uint64_t)t[2] << 32) | t[1]; + a0 = (uint64_t)t[0] << 32; + a0 |= (low_bits != 0); + /* normalize */ + { + shift = clz64(a1); + if (shift != 0) { + a1 = (a1 << shift) | (a0 >> (64 - shift)); + a0 <<= shift; + } + } + a1 |= (a0 != 0); /* keep the bits for the final rounding */ + /* compute the exponent */ + e = a->len * JS_LIMB_BITS - shift - 1; + *pexp = e; + return a1; +} + +/* shift left with round to nearest, ties to even. n >= 1 */ +static uint64_t shr_rndn(uint64_t a, int n) +{ + uint64_t addend = ((a >> n) & 1) + ((1 << (n - 1)) - 1); + return (a + addend) >> n; +} + +/* convert to float64 with round to nearest, ties to even. Return + +/-infinity if too large. */ +static double js_bigint_to_float64(JSContext *ctx, const JSBigInt *a) +{ + int sgn, e; + uint64_t mant; + + if (a->len == 1) { + /* fast case, including zero */ + return (double)(js_slimb_t)a->tab[0]; + } + + sgn = js_bigint_sign(a); + mant = js_bigint_get_mant_exp(ctx, &e, a); + if (e > 1023) { + /* overflow: return infinity */ + mant = 0; + e = 1024; + } else { + mant = (mant >> 1) | (mant & 1); /* avoid overflow in rounding */ + mant = shr_rndn(mant, 10); + /* rounding can cause an overflow */ + if (mant >= ((uint64_t)1 << 53)) { + mant >>= 1; + e++; + } + mant &= (((uint64_t)1 << 52) - 1); + } + return uint64_as_float64(((uint64_t)sgn << 63) | + ((uint64_t)(e + 1023) << 52) | + mant); +} + +/* return (1, NULL) if not an integer, (2, NULL) if NaN or Infinity, + (0, n) if an integer, (0, NULL) in case of memory error */ +static JSBigInt *js_bigint_from_float64(JSContext *ctx, int *pres, double a1) +{ + uint64_t a = float64_as_uint64(a1); + int sgn, e, shift; + uint64_t mant; + JSBigIntBuf buf; + JSBigInt *r; + + sgn = a >> 63; + e = (a >> 52) & ((1 << 11) - 1); + mant = a & (((uint64_t)1 << 52) - 1); + if (e == 2047) { + /* NaN, Infinity */ + *pres = 2; + return NULL; + } + if (e == 0 && mant == 0) { + /* zero */ + *pres = 0; + return js_bigint_new_si(ctx, 0); + } + e -= 1023; + /* 0 < a < 1 : not an integer */ + if (e < 0) + goto not_an_integer; + mant |= (uint64_t)1 << 52; + if (e < 52) { + shift = 52 - e; + /* check that there is no fractional part */ + if (mant & (((uint64_t)1 << shift) - 1)) { + not_an_integer: + *pres = 1; + return NULL; + } + mant >>= shift; + e = 0; + } else { + e -= 52; + } + if (sgn) + mant = -mant; + /* the integer is mant*2^e */ + r = js_bigint_set_si64(&buf, (int64_t)mant); + *pres = 0; + return js_bigint_shl(ctx, r, e); +} + +/* return -1, 0, 1 or (2) (unordered) */ +static int js_bigint_float64_cmp(JSContext *ctx, const JSBigInt *a, + double b) +{ + int b_sign, a_sign, e, f; + uint64_t mant, b1, a_mant; + + b1 = float64_as_uint64(b); + b_sign = b1 >> 63; + e = (b1 >> 52) & ((1 << 11) - 1); + mant = b1 & (((uint64_t)1 << 52) - 1); + a_sign = js_bigint_sign(a); + if (e == 2047) { + if (mant != 0) { + /* NaN */ + return 2; + } else { + /* +/- infinity */ + return 2 * b_sign - 1; + } + } else if (e == 0 && mant == 0) { + /* b = +/-0 */ + if (a->len == 1 && a->tab[0] == 0) + return 0; + else + return 1 - 2 * a_sign; + } else if (a->len == 1 && a->tab[0] == 0) { + /* a = 0, b != 0 */ + return 2 * b_sign - 1; + } else if (a_sign != b_sign) { + return 1 - 2 * a_sign; + } else { + e -= 1023; + /* Note: handling denormals is not necessary because we + compare to integers hence f >= 0 */ + /* compute f so that 2^f <= abs(a) < 2^(f+1) */ + a_mant = js_bigint_get_mant_exp(ctx, &f, a); + if (f != e) { + if (f < e) + return -1; + else + return 1; + } else { + mant = (mant | ((uint64_t)1 << 52)) << 11; /* align to a_mant */ + if (a_mant < mant) + return 2 * a_sign - 1; + else if (a_mant > mant) + return 1 - 2 * a_sign; + else + return 0; + } + } +} + +/* return -1, 0 or 1 */ +static int js_bigint_cmp(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b) +{ + int a_sign, b_sign, res, i; + a_sign = js_bigint_sign(a); + b_sign = js_bigint_sign(b); + if (a_sign != b_sign) { + res = 1 - 2 * a_sign; + } else { + /* we assume the numbers are normalized */ + if (a->len != b->len) { + if (a->len < b->len) + res = 2 * a_sign - 1; + else + res = 1 - 2 * a_sign; + } else { + res = 0; + for(i = a->len -1; i >= 0; i--) { + if (a->tab[i] != b->tab[i]) { + if (a->tab[i] < b->tab[i]) + res = -1; + else + res = 1; + break; + } + } + } + } + return res; +} + +/* contains 10^i */ +static const js_limb_t js_pow_dec[JS_LIMB_DIGITS + 1] = { + 1U, + 10U, + 100U, + 1000U, + 10000U, + 100000U, + 1000000U, + 10000000U, + 100000000U, + 1000000000U, +}; + +/* syntax: [-]digits in base radix. Return NULL if memory error. radix + = 10, 2, 8 or 16. */ +static JSBigInt *js_bigint_from_string(JSContext *ctx, + const char *str, int radix) +{ + const char *p = str; + size_t n_digits1; + int is_neg, n_digits, n_limbs, len, log2_radix, n_bits, i; + JSBigInt *r; + js_limb_t v, c, h; + + is_neg = 0; + if (*p == '-') { + is_neg = 1; + p++; + } + while (*p == '0') + p++; + n_digits1 = strlen(p); + /* the real check for overflox is done js_bigint_new(). Here + we just avoid integer overflow */ + if (n_digits1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS) { + JS_ThrowRangeError(ctx, "BigInt is too large to allocate"); + return NULL; + } + n_digits = n_digits1; + log2_radix = 32 - clz32(radix - 1); /* ceil(log2(radix)) */ + /* compute the maximum number of limbs */ + if (radix == 10) { + n_bits = (n_digits * 27 + 7) / 8; /* >= ceil(n_digits * log2(10)) */ + } else { + n_bits = n_digits * log2_radix; + } + /* we add one extra bit for the sign */ + n_limbs = max_int(1, n_bits / JS_LIMB_BITS + 1); + r = js_bigint_new(ctx, n_limbs); + if (!r) + return NULL; + if (radix == 10) { + int digits_per_limb = JS_LIMB_DIGITS; + len = 1; + r->tab[0] = 0; + for(;;) { + /* XXX: slow */ + v = 0; + for(i = 0; i < digits_per_limb; i++) { + c = js_to_digit(*p); + if (c >= radix) + break; + p++; + v = v * 10 + c; + } + if (i == 0) + break; + if (len == 1 && r->tab[0] == 0) { + r->tab[0] = v; + } else { + h = js_mp_mul1(r->tab, r->tab, len, js_pow_dec[i], v); + if (h != 0) { + r->tab[len++] = h; + } + } + } + /* add one extra limb to have the correct sign*/ + if ((r->tab[len - 1] >> (JS_LIMB_BITS - 1)) != 0) + r->tab[len++] = 0; + r->len = len; + } else { + unsigned int bit_pos, shift, pos; + + /* power of two base: no multiplication is needed */ + r->len = n_limbs; + memset(r->tab, 0, sizeof(r->tab[0]) * n_limbs); + for(i = 0; i < n_digits; i++) { + c = js_to_digit(p[n_digits - 1 - i]); + assert(c < radix); + bit_pos = i * log2_radix; + shift = bit_pos & (JS_LIMB_BITS - 1); + pos = bit_pos / JS_LIMB_BITS; + r->tab[pos] |= c << shift; + /* if log2_radix does not divide JS_LIMB_BITS, needed an + additional op */ + if (shift + log2_radix > JS_LIMB_BITS) { + r->tab[pos + 1] |= c >> (JS_LIMB_BITS - shift); + } + } + } + r = js_bigint_normalize(ctx, r); + /* XXX: could do it in place */ + if (is_neg) { + JSBigInt *r1; + r1 = js_bigint_neg(ctx, r); + js_free(ctx, r); + r = r1; + } + return r; +} + +/* 2 <= base <= 36 */ +static char const digits[36] = { + '0','1','2','3','4','5','6','7','8','9', + 'a','b','c','d','e','f','g','h','i','j', + 'k','l','m','n','o','p','q','r','s','t', + 'u','v','w','x','y','z' +}; + +/* special version going backwards */ +/* XXX: use dtoa.c */ +static char *js_u64toa(char *q, int64_t n, unsigned int base) +{ + int digit; + if (base == 10) { + /* division by known base uses multiplication */ + do { + digit = (uint64_t)n % 10; + n = (uint64_t)n / 10; + *--q = '0' + digit; + } while (n != 0); + } else { + do { + digit = (uint64_t)n % base; + n = (uint64_t)n / base; + *--q = digits[digit]; + } while (n != 0); + } + return q; +} + +/* len >= 1. 2 <= radix <= 36 */ +static char *js_limb_to_a(char *q, js_limb_t n, unsigned int radix, int len) +{ + int digit, i; + + if (radix == 10) { + /* specific case with constant divisor */ + /* XXX: optimize */ + for(i = 0; i < len; i++) { + digit = (js_limb_t)n % 10; + n = (js_limb_t)n / 10; + *--q = digit + '0'; + } + } else { + for(i = 0; i < len; i++) { + digit = (js_limb_t)n % radix; + n = (js_limb_t)n / radix; + *--q = digits[digit]; + } + } + return q; +} + +#define JS_RADIX_MAX 36 + +static const uint8_t js_digits_per_limb_table[JS_RADIX_MAX - 1] = { +32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +}; + +static const js_limb_t js_radix_base_table[JS_RADIX_MAX - 1] = { + 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395, + 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91, + 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021, + 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571, + 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d, + 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51, + 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899, + 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1, + 0x5c13d840, 0x6d91b519, 0x81bf1000, +}; + +static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { + char buf[66]; + int len; + len = i64toa_radix(buf, JS_VALUE_GET_SHORT_BIG_INT(val), radix); + return js_new_string8_len(ctx, buf, len); + } else { + JSBigInt *r, *tmp = NULL; + char *buf, *q, *buf_end; + int is_neg, n_bits, log2_radix, n_digits; + bool is_binary_radix; + JSValue res; + + assert(JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT); + r = JS_VALUE_GET_PTR(val); + if (r->len == 1 && r->tab[0] == 0) { + /* '0' case */ + return js_new_string8_len(ctx, "0", 1); + } + is_binary_radix = ((radix & (radix - 1)) == 0); + is_neg = js_bigint_sign(r); + if (is_neg) { + tmp = js_bigint_neg(ctx, r); + if (!tmp) + return JS_EXCEPTION; + r = tmp; + } else if (!is_binary_radix) { + /* need to modify 'r' */ + tmp = js_bigint_new(ctx, r->len); + if (!tmp) + return JS_EXCEPTION; + memcpy(tmp->tab, r->tab, r->len * sizeof(r->tab[0])); + r = tmp; + } + log2_radix = 31 - clz32(radix); /* floor(log2(radix)) */ + n_bits = r->len * JS_LIMB_BITS - js_limb_clz(r->tab[r->len - 1]); + /* n_digits is exact only if radix is a power of + two. Otherwise it is >= the exact number of digits */ + n_digits = (n_bits + log2_radix - 1) / log2_radix; + /* XXX: could directly build the JSString */ + buf = js_malloc(ctx, n_digits + is_neg + 1); + if (!buf) { + js_free(ctx, tmp); + return JS_EXCEPTION; + } + q = buf + n_digits + is_neg + 1; + *--q = '\0'; + buf_end = q; + if (!is_binary_radix) { + int len; + js_limb_t radix_base, v; + radix_base = js_radix_base_table[radix - 2]; + len = r->len; + for(;;) { + /* remove leading zero limbs */ + while (len > 1 && r->tab[len - 1] == 0) + len--; + if (len == 1 && r->tab[0] < radix_base) { + v = r->tab[0]; + if (v != 0) { + q = js_u64toa(q, v, radix); + } + break; + } else { + v = js_mp_div1(r->tab, r->tab, len, radix_base, 0); + q = js_limb_to_a(q, v, radix, js_digits_per_limb_table[radix - 2]); + } + } + } else { + int i, shift; + unsigned int bit_pos, pos, c; + + /* radix is a power of two */ + for(i = 0; i < n_digits; i++) { + bit_pos = i * log2_radix; + pos = bit_pos / JS_LIMB_BITS; + shift = bit_pos % JS_LIMB_BITS; + c = r->tab[pos] >> shift; + if ((shift + log2_radix) > JS_LIMB_BITS && + (pos + 1) < r->len) { + c |= r->tab[pos + 1] << (JS_LIMB_BITS - shift); + } + c &= (radix - 1); + *--q = digits[c]; + } + } + if (is_neg) + *--q = '-'; + js_free(ctx, tmp); + res = js_new_string8_len(ctx, q, buf_end - q); + js_free(ctx, buf); + return res; + } +} + +/* if possible transform a BigInt to short big and free it, otherwise + return a normal bigint */ +static JSValue JS_CompactBigInt(JSContext *ctx, JSBigInt *p) +{ + JSValue res; + if (p->len == 1) { + res = __JS_NewShortBigInt(ctx, (js_slimb_t)p->tab[0]); + js_free(ctx, p); + return res; + } else { + return JS_MKPTR(JS_TAG_BIG_INT, p); + } +} + +#define ATOD_INT_ONLY (1 << 0) +/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ +#define ATOD_ACCEPT_BIN_OCT (1 << 2) +/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ +#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4) +/* accept _ between digits as a digit separator */ +#define ATOD_ACCEPT_UNDERSCORES (1 << 5) +/* allow a suffix to override the type */ +#define ATOD_ACCEPT_SUFFIX (1 << 6) +/* default type */ +#define ATOD_TYPE_MASK (3 << 7) +#define ATOD_TYPE_FLOAT64 (0 << 7) +#define ATOD_TYPE_BIG_INT (1 << 7) +/* accept -0x1 */ +#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10) + +/* return an exception in case of memory error. Return JS_NAN if + invalid syntax */ +/* XXX: directly use js_atod() */ +static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, + int radix, int flags) +{ + const char *p, *p_start; + int sep, is_neg; + bool is_float, has_legacy_octal; + int atod_type = flags & ATOD_TYPE_MASK; + char buf1[64], *buf; + int i, j, len; + bool buf_allocated = false; + JSValue val; + JSATODTempMem atod_mem; + + /* optional separator between digits */ + sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; + has_legacy_octal = false; + + p = str; + p_start = p; + is_neg = 0; + if (p[0] == '+') { + p++; + p_start++; + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) + goto no_radix_prefix; + } else if (p[0] == '-') { + p++; + p_start++; + is_neg = 1; + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) + goto no_radix_prefix; + } + if (p[0] == '0') { + if ((p[1] == 'x' || p[1] == 'X') && + (radix == 0 || radix == 16)) { + p += 2; + radix = 16; + } else if ((p[1] == 'o' || p[1] == 'O') && + radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 8; + } else if ((p[1] == 'b' || p[1] == 'B') && + radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 2; + } else if ((p[1] >= '0' && p[1] <= '9') && + radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) { + int i; + has_legacy_octal = true; + sep = 256; + for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++) + continue; + if (p[i] == '8' || p[i] == '9') + goto no_prefix; + p += 1; + radix = 8; + } else { + goto no_prefix; + } + /* there must be a digit after the prefix */ + if (js_to_digit((uint8_t)*p) >= radix) + goto fail; + no_prefix: ; + } else { + no_radix_prefix: + if (!(flags & ATOD_INT_ONLY) && + (atod_type == ATOD_TYPE_FLOAT64) && + js__strstart(p, "Infinity", &p)) { + double d = INF; + if (is_neg) + d = -d; + val = js_float64(d); + goto done; + } + } + if (radix == 0) + radix = 10; + is_float = false; + p_start = p; + while (js_to_digit((uint8_t)*p) < radix + || (*p == sep && (radix != 10 || + p != p_start + 1 || p[-1] != '0') && + js_to_digit((uint8_t)p[1]) < radix)) { + p++; + } + if (!(flags & ATOD_INT_ONLY) && radix == 10) { + if (*p == '.' && (p > p_start || js_to_digit((uint8_t)p[1]) < radix)) { + is_float = true; + p++; + if (*p == sep) + goto fail; + while (js_to_digit((uint8_t)*p) < radix || + (*p == sep && js_to_digit((uint8_t)p[1]) < radix)) + p++; + } + if (p > p_start && (*p == 'e' || *p == 'E')) { + const char *p1 = p + 1; + is_float = true; + if (*p1 == '+') { + p1++; + } else if (*p1 == '-') { + p1++; + } + if (is_digit((uint8_t)*p1)) { + p = p1 + 1; + while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1]))) + p++; + } + } + } + if (p == p_start) + goto fail; + + buf = buf1; + buf_allocated = false; + len = p - p_start; + if (unlikely((len + 2) > sizeof(buf1))) { + buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */ + if (!buf) + goto mem_error; + buf_allocated = true; + } + /* remove the separators and the radix prefixes */ + j = 0; + if (is_neg) + buf[j++] = '-'; + for (i = 0; i < len; i++) { + if (p_start[i] != '_') + buf[j++] = p_start[i]; + } + buf[j] = '\0'; + + if (flags & ATOD_ACCEPT_SUFFIX) { + if (*p == 'n') { + p++; + atod_type = ATOD_TYPE_BIG_INT; + } + } + + switch(atod_type) { + case ATOD_TYPE_FLOAT64: + { + double d; + d = js_atod(buf, NULL, radix, is_float ? 0 : JS_ATOD_INT_ONLY, + &atod_mem); + /* return int or float64 */ + val = js_number(d); + } + break; + case ATOD_TYPE_BIG_INT: + { + JSBigInt *r; + if (has_legacy_octal || is_float) + goto fail; + r = js_bigint_from_string(ctx, buf, radix); + if (!r) { + val = JS_EXCEPTION; + goto done; + } + val = JS_CompactBigInt(ctx, r); + } + break; + default: + abort(); + } + +done: + if (buf_allocated) + js_free_rt(ctx->rt, buf); + if (pp) + *pp = p; + return val; + fail: + val = JS_NAN; + goto done; + mem_error: + val = JS_ThrowOutOfMemory(ctx); + goto done; +} + +typedef enum JSToNumberHintEnum { + TON_FLAG_NUMBER, + TON_FLAG_NUMERIC, +} JSToNumberHintEnum; + +static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, + JSToNumberHintEnum flag) +{ + uint32_t tag; + JSValue ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_BIG_INT: + case JS_TAG_SHORT_BIG_INT: + if (flag != TON_FLAG_NUMERIC) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert BigInt to number"); + } + ret = val; + break; + case JS_TAG_FLOAT64: + case JS_TAG_INT: + case JS_TAG_EXCEPTION: + ret = val; + break; + case JS_TAG_BOOL: + case JS_TAG_NULL: + ret = js_int32(JS_VALUE_GET_INT(val)); + break; + case JS_TAG_UNDEFINED: + ret = JS_NAN; + break; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + return JS_EXCEPTION; + goto redo; + case JS_TAG_STRING: + { + const char *str; + const char *p; + size_t len; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + p = str; + p += skip_spaces(p); + if ((p - str) == len) { + ret = JS_NewInt32(ctx, 0); + } else { + int flags = ATOD_ACCEPT_BIN_OCT; + ret = js_atof(ctx, p, &p, 0, flags); + if (!JS_IsException(ret)) { + p += skip_spaces(p); + if ((p - str) != len) { + JS_FreeValue(ctx, ret); + ret = JS_NAN; + } + } + } + JS_FreeCString(ctx, str); + } + break; + case JS_TAG_SYMBOL: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert symbol to number"); + default: + JS_FreeValue(ctx, val); + ret = JS_NAN; + break; + } + return ret; +} + +static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val) +{ + return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); +} + +static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) +{ + return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); +} + +static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val) +{ + return JS_ToNumericFree(ctx, js_dup(val)); +} + +static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, + JSValue val) +{ + double d; + uint32_t tag; + + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + goto fail; + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + d = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + d = JS_VALUE_GET_FLOAT64(val); + break; + default: + abort(); + } + *pres = d; + return 0; +fail: + *pres = NAN; + return -1; +} + +static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) +{ + uint32_t tag; + + tag = JS_VALUE_GET_TAG(val); + if (tag <= JS_TAG_NULL) { + *pres = JS_VALUE_GET_INT(val); + return 0; + } else if (JS_TAG_IS_FLOAT64(tag)) { + *pres = JS_VALUE_GET_FLOAT64(val); + return 0; + } else { + return __JS_ToFloat64Free(ctx, pres, val); + } +} + +int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val) +{ + return JS_ToFloat64Free(ctx, pres, js_dup(val)); +} + +JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) +{ + return JS_ToNumberFree(ctx, js_dup(val)); +} + +/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ +static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) +{ + uint32_t tag; + JSValue ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = js_int32(JS_VALUE_GET_INT(val)); + break; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + ret = js_int32(0); + } else { + /* convert -0 to +0 */ + d = trunc(d) + 0.0; + ret = js_number(d); + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return val; + goto redo; + } + return ret; +} + +/* Note: the integer value is satured to 32 bits */ +static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) +{ + uint32_t tag; + int ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_EXCEPTION: + *pres = 0; + return -1; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + ret = 0; + } else { + if (d < INT32_MIN) + ret = INT32_MIN; + else if (d > INT32_MAX) + ret = INT32_MAX; + else + ret = (int)d; + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +static int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val) +{ + return JS_ToInt32SatFree(ctx, pres, js_dup(val)); +} + +static int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val, + int min, int max, int min_offset) +{ + int res = JS_ToInt32SatFree(ctx, pres, js_dup(val)); + if (res == 0) { + if (*pres < min) { + *pres += min_offset; + if (*pres < min) + *pres = min; + } else { + if (*pres > max) + *pres = max; + } + } + return res; +} + +static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) +{ + uint32_t tag; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + *pres = JS_VALUE_GET_INT(val); + return 0; + case JS_TAG_EXCEPTION: + *pres = 0; + return -1; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + *pres = 0; + } else { + if (d < INT64_MIN) + *pres = INT64_MIN; + else if (d >= 0x1p63) + *pres = INT64_MAX; + else + *pres = (int64_t)d; + } + } + return 0; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } +} + +int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + return JS_ToInt64SatFree(ctx, pres, js_dup(val)); +} + +int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val, + int64_t min, int64_t max, int64_t neg_offset) +{ + int res = JS_ToInt64SatFree(ctx, pres, js_dup(val)); + if (res == 0) { + if (*pres < 0) + *pres += neg_offset; + if (*pres < min) + *pres = min; + else if (*pres > max) + *pres = max; + } + return res; +} + +/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0) + in case of exception */ +static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) +{ + uint32_t tag; + int64_t ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + double d; + int e; + d = JS_VALUE_GET_FLOAT64(val); + u.d = d; + /* we avoid doing fmod(x, 2^64) */ + e = (u.u64 >> 52) & 0x7ff; + if (likely(e <= (1023 + 62))) { + /* fast case */ + ret = (int64_t)d; + } else if (e <= (1023 + 62 + 53)) { + uint64_t v; + /* remainder modulo 2^64 */ + v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + ret = v << ((e - 1023) - 52); + /* take the sign into account */ + if (u.u64 >> 63) + if (ret != INT64_MIN) + ret = -ret; + } else { + ret = 0; /* also handles NaN and +inf */ + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + return JS_ToInt64Free(ctx, pres, js_dup(val)); +} + +int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + if (JS_IsBigInt(val)) + return JS_ToBigInt64(ctx, pres, val); + else + return JS_ToInt64(ctx, pres, val); +} + +/* return (<0, 0) in case of exception */ +static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) +{ + uint32_t tag; + int32_t ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + double d; + int e; + d = JS_VALUE_GET_FLOAT64(val); + u.d = d; + /* we avoid doing fmod(x, 2^32) */ + e = (u.u64 >> 52) & 0x7ff; + if (likely(e <= (1023 + 30))) { + /* fast case */ + ret = (int32_t)d; + } else if (e <= (1023 + 30 + 53)) { + uint64_t v; + /* remainder modulo 2^32 */ + v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + v = v << ((e - 1023) - 52 + 32); + ret = v >> 32; + /* take the sign into account */ + if (u.u64 >> 63) + if (ret != INT32_MIN) + ret = -ret; + } else { + ret = 0; /* also handles NaN and +inf */ + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val) +{ + return JS_ToInt32Free(ctx, pres, js_dup(val)); +} + +static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val) +{ + return JS_ToInt32Free(ctx, (int32_t *)pres, val); +} + +static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) +{ + uint32_t tag; + int res; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + res = JS_VALUE_GET_INT(val); + res = max_int(0, min_int(255, res)); + break; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + res = 0; + } else { + if (d < 0) + res = 0; + else if (d > 255) + res = 255; + else + res = lrint(d); + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = res; + return 0; +} + +static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, + JSValue val, bool is_array_ctor) +{ + uint32_t tag, len; + + tag = JS_VALUE_GET_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + { + int v; + v = JS_VALUE_GET_INT(val); + if (v < 0) + goto fail; + len = v; + } + break; + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d; + d = JS_VALUE_GET_FLOAT64(val); + if (!(d >= 0 && d <= UINT32_MAX)) + goto fail; + len = (uint32_t)d; + if (len != d) + goto fail; + } else { + uint32_t len1; + + if (is_array_ctor) { + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len, val, true)) + return -1; + } else { + /* legacy behavior: must do the conversion twice and compare */ + if (JS_ToUint32(ctx, &len, val)) { + JS_FreeValue(ctx, val); + return -1; + } + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len1, val, false)) + return -1; + if (len1 != len) { + fail: + JS_ThrowRangeError(ctx, "invalid array length"); + return -1; + } + } + } + break; + } + *plen = len; + return 0; +} + +#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) + +static bool is_safe_integer(double d) +{ + return isfinite(d) && floor(d) == d && + fabs(d) <= (double)MAX_SAFE_INTEGER; +} + +int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) +{ + int64_t v; + if (JS_ToInt64Sat(ctx, &v, val)) + return -1; + if (v < 0 || v > MAX_SAFE_INTEGER) { + JS_ThrowRangeError(ctx, "invalid array index"); + *plen = 0; + return -1; + } + *plen = v; + return 0; +} + +/* convert a value to a length between 0 and MAX_SAFE_INTEGER. + return -1 for exception */ +static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, + JSValue val) +{ + int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0); + JS_FreeValue(ctx, val); + return res; +} + +/* Note: can return an exception */ +static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val) +{ + double d; + if (!JS_IsNumber(val)) + return false; + if (unlikely(JS_ToFloat64(ctx, &d, val))) + return -1; + return isfinite(d) && floor(d) == d; +} + +static bool JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) +{ + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + { + int v; + v = JS_VALUE_GET_INT(val); + return (v < 0); + } + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + u.d = JS_VALUE_GET_FLOAT64(val); + return (u.u64 >> 63); + } + case JS_TAG_SHORT_BIG_INT: + return (JS_VALUE_GET_SHORT_BIG_INT(val) < 0); + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + return js_bigint_sign(p); + } + default: + return false; + } +} + +static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val) +{ + return js_bigint_to_string1(ctx, val, 10); +} + +/*---- floating point number to string conversions ----*/ + +static JSValue js_dtoa2(JSContext *ctx, + double d, int radix, int n_digits, int flags) +{ + char static_buf[128], *buf, *tmp_buf; + int len, len_max; + JSValue res; + JSDTOATempMem dtoa_mem; + len_max = js_dtoa_max_len(d, radix, n_digits, flags); + + /* longer buffer may be used if radix != 10 */ + if (len_max > sizeof(static_buf) - 1) { + tmp_buf = js_malloc(ctx, len_max + 1); + if (!tmp_buf) + return JS_EXCEPTION; + buf = tmp_buf; + } else { + tmp_buf = NULL; + buf = static_buf; + } + len = js_dtoa(buf, d, radix, n_digits, flags, &dtoa_mem); + res = js_new_string8_len(ctx, buf, len); + js_free(ctx, tmp_buf); + return res; +} + +static JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, + int flags) +{ + uint32_t tag; + char buf[32]; + size_t len; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_STRING: + return js_dup(val); + case JS_TAG_INT: + len = i32toa(buf, JS_VALUE_GET_INT(val)); + return js_new_string8_len(ctx, buf, len); + case JS_TAG_BOOL: + return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? + JS_ATOM_true : JS_ATOM_false); + case JS_TAG_NULL: + return JS_AtomToString(ctx, JS_ATOM_null); + case JS_TAG_UNDEFINED: + return JS_AtomToString(ctx, JS_ATOM_undefined); + case JS_TAG_EXCEPTION: + return JS_EXCEPTION; + case JS_TAG_OBJECT: + if (flags & JS_TO_STRING_NO_SIDE_EFFECTS) { + return js_new_string8(ctx, "{}"); + } else { + JSValue val1, ret; + val1 = JS_ToPrimitive(ctx, val, HINT_STRING); + if (JS_IsException(val1)) + return val1; + ret = JS_ToStringInternal(ctx, val1, flags); + JS_FreeValue(ctx, val1); + return ret; + } + break; + case JS_TAG_FUNCTION_BYTECODE: + return js_new_string8(ctx, "[function bytecode]"); + case JS_TAG_SYMBOL: + if (flags & JS_TO_STRING_IS_PROPERTY_KEY) { + return js_dup(val); + } else { + return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); + } + case JS_TAG_FLOAT64: + return js_dtoa2(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0, + JS_DTOA_FORMAT_FREE); + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + return js_bigint_to_string(ctx, val); + case JS_TAG_UNINITIALIZED: + return js_new_string8(ctx, "[uninitialized]"); + default: + return js_new_string8(ctx, "[unsupported type]"); + } +} + +JSValue JS_ToString(JSContext *ctx, JSValueConst val) +{ + return JS_ToStringInternal(ctx, val, /*flags*/0); +} + +static JSValue JS_ToStringFree(JSContext *ctx, JSValue val) +{ + JSValue ret; + ret = JS_ToString(ctx, val); + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val) +{ + if (JS_IsUndefined(val) || JS_IsNull(val)) + return JS_ToStringFree(ctx, val); + return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL); +} + +static JSValue JS_ToPropertyKeyInternal(JSContext *ctx, JSValueConst val, + int flags) +{ + return JS_ToStringInternal(ctx, val, flags | JS_TO_STRING_IS_PROPERTY_KEY); +} + +JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val) +{ + return JS_ToPropertyKeyInternal(ctx, val, /*flags*/0); +} + +static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val) +{ + uint32_t tag = JS_VALUE_GET_TAG(val); + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) + return JS_ThrowTypeError(ctx, "null or undefined are forbidden"); + return JS_ToString(ctx, val); +} + +static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1) +{ + JSValue val; + JSString *p; + int i; + uint32_t c; + StringBuffer b_s, *b = &b_s; + char buf[16]; + + val = JS_ToStringCheckObject(ctx, val1); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + + if (string_buffer_init(ctx, b, p->len + 2)) + goto fail; + + if (string_buffer_putc8(b, '\"')) + goto fail; + for(i = 0; i < p->len; ) { + c = string_getc(p, &i); + switch(c) { + case '\t': + c = 't'; + goto quote; + case '\r': + c = 'r'; + goto quote; + case '\n': + c = 'n'; + goto quote; + case '\b': + c = 'b'; + goto quote; + case '\f': + c = 'f'; + goto quote; + case '\"': + case '\\': + quote: + if (string_buffer_putc8(b, '\\')) + goto fail; + if (string_buffer_putc8(b, c)) + goto fail; + break; + default: + if (c < 32 || is_surrogate(c)) { + snprintf(buf, sizeof(buf), "\\u%04x", c); + if (string_buffer_write8(b, (uint8_t*)buf, 6)) + goto fail; + } else { + if (string_buffer_putc(b, c)) + goto fail; + } + break; + } + } + if (string_buffer_putc8(b, '\"')) + goto fail; + JS_FreeValue(ctx, val); + return string_buffer_end(b); + fail: + JS_FreeValue(ctx, val); + string_buffer_free(b); + return JS_EXCEPTION; +} + +static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) +{ + printf("%14s %4s %4s %14s %10s %s\n", + "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS"); +} + +/* for debug only: dump an object without side effect */ +static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) +{ + uint32_t i; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + JSShape *sh; + JSShapeProperty *prs; + JSProperty *pr; + bool is_first = true; + + /* XXX: should encode atoms with special characters */ + sh = p->shape; /* the shape can be NULL while freeing an object */ + printf("%14p %4d ", + (void *)p, + p->header.ref_count); + if (sh) { + printf("%3d%c %14p ", + sh->header.ref_count, + " *"[sh->is_hashed], + (void *)sh->proto); + } else { + printf("%3s %14s ", "-", "-"); + } + printf("%10s ", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name)); + if (p->is_exotic && p->fast_array) { + printf("[ "); + for(i = 0; i < p->u.array.count; i++) { + if (i != 0) + printf(", "); + switch (p->class_id) { + case JS_CLASS_ARRAY: + case JS_CLASS_ARGUMENTS: + JS_DumpValue(rt, p->u.array.u.values[i]); + break; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + case JS_CLASS_FLOAT16_ARRAY: + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: + { + int size = 1 << typed_array_size_log2(p->class_id); + const uint8_t *b = p->u.array.u.uint8_ptr + i * size; + while (size-- > 0) + printf("%02X", *b++); + } + break; + } + } + printf(" ] "); + } + + if (sh) { + printf("{ "); + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + if (prs->atom != JS_ATOM_NULL) { + pr = &p->prop[i]; + if (!is_first) + printf(", "); + printf("%s: ", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom)); + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + printf("[getset %p %p]", (void *)pr->u.getset.getter, + (void *)pr->u.getset.setter); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + printf("[varref %p]", (void *)pr->u.var_ref); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + printf("[autoinit %p %d %p]", + (void *)js_autoinit_get_realm(pr), + js_autoinit_get_id(pr), + (void *)pr->u.init.opaque); + } else { + JS_DumpValue(rt, pr->u.value); + } + is_first = false; + } + } + printf(" }"); + } + + if (js_class_has_bytecode(p->class_id)) { + JSFunctionBytecode *b = p->u.func.function_bytecode; + JSVarRef **var_refs; + if (b->closure_var_count) { + var_refs = p->u.func.var_refs; + printf(" Closure:"); + for(i = 0; i < b->closure_var_count; i++) { + printf(" "); + JS_DumpValue(rt, var_refs[i]->value); + } + if (p->u.func.home_object) { + printf(" HomeObject: "); + JS_DumpValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object)); + } + } + } + printf("\n"); +} + +static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) +{ + if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + JS_DumpObject(rt, (JSObject *)p); + } else { + printf("%14p %4d ", + (void *)p, + p->ref_count); + switch(p->gc_obj_type) { + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + printf("[function bytecode]"); + break; + case JS_GC_OBJ_TYPE_SHAPE: + printf("[shape]"); + break; + case JS_GC_OBJ_TYPE_VAR_REF: + printf("[var_ref]"); + break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + printf("[async_function]"); + break; + case JS_GC_OBJ_TYPE_JS_CONTEXT: + printf("[js_context]"); + break; + default: + printf("[unknown %d]", p->gc_obj_type); + break; + } + printf("\n"); + } +} + +static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValueConst val) +{ + uint32_t tag = JS_VALUE_GET_NORM_TAG(val); + const char *str; + + switch(tag) { + case JS_TAG_INT: + printf("%d", JS_VALUE_GET_INT(val)); + break; + case JS_TAG_BOOL: + if (JS_VALUE_GET_BOOL(val)) + str = "true"; + else + str = "false"; + goto print_str; + case JS_TAG_NULL: + str = "null"; + goto print_str; + case JS_TAG_EXCEPTION: + str = "exception"; + goto print_str; + case JS_TAG_UNINITIALIZED: + str = "uninitialized"; + goto print_str; + case JS_TAG_UNDEFINED: + str = "undefined"; + print_str: + printf("%s", str); + break; + case JS_TAG_FLOAT64: + printf("%.14g", JS_VALUE_GET_FLOAT64(val)); + break; + case JS_TAG_SHORT_BIG_INT: + printf("%" PRId64 "n", (int64_t)JS_VALUE_GET_SHORT_BIG_INT(val)); + break; + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + int sgn, i; + /* In order to avoid allocations we just dump the limbs */ + sgn = js_bigint_sign(p); + if (sgn) + printf("BigInt.asIntN(%d,", p->len * JS_LIMB_BITS); + printf("0x"); + for(i = p->len - 1; i >= 0; i--) { + if (i != p->len - 1) + printf("_"); + printf("%08x", p->tab[i]); + } + printf("n"); + if (sgn) + printf(")"); + } + break; + case JS_TAG_STRING: + { + JSString *p; + p = JS_VALUE_GET_STRING(val); + JS_DumpString(rt, p); + } + break; + case JS_TAG_FUNCTION_BYTECODE: + { + JSFunctionBytecode *b = JS_VALUE_GET_PTR(val); + char buf[ATOM_GET_STR_BUF_SIZE]; + if (b->func_name) { + printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); + } else { + printf("[bytecode (anonymous)]"); + } + } + break; + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAtom atom = rt->class_array[p->class_id].class_name; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + printf("[%s %p]", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p); + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + printf("Symbol(%s)", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p))); + } + break; + case JS_TAG_MODULE: + printf("[module]"); + break; + default: + printf("[unknown tag %d]", tag); + break; + } +} + +bool JS_IsArray(JSValueConst val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_ARRAY; + } + return false; +} + +/* return -1 if exception (proxy case) or true/false */ +static int js_is_array(JSContext *ctx, JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(val); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_isArray(ctx, val); + else + return p->class_id == JS_CLASS_ARRAY; + } else { + return false; + } +} + +static double js_math_pow(double a, double b) +{ + double d; + + if (unlikely(!isfinite(b)) && fabs(a) == 1) { + /* not compatible with IEEE 754 */ + d = NAN; + } else { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + d = pow(a, b); + JS_X87_FPCW_RESTORE(fpcw); + } + return d; +} + +JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) +{ + if (v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX) { + return __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *p; + p = js_bigint_new_si64(ctx, v); + if (!p) + return JS_EXCEPTION; + return JS_MKPTR(JS_TAG_BIG_INT, p); + } +} + +JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) +{ + if (v <= JS_SHORT_BIG_INT_MAX) { + return __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *p; + p = js_bigint_new_ui64(ctx, v); + if (!p) + return JS_EXCEPTION; + return JS_MKPTR(JS_TAG_BIG_INT, p); + } +} + +/* return NaN if bad bigint literal */ +static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) +{ + const char *str, *p; + size_t len; + int flags; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + p = str; + p += skip_spaces(p); + if ((p - str) == len) { + val = JS_NewBigInt64(ctx, 0); + } else { + flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; + val = js_atof(ctx, p, &p, 0, flags); + p += skip_spaces(p); + if (!JS_IsException(val)) { + if ((p - str) != len) { + JS_FreeValue(ctx, val); + val = JS_NAN; + } + } + } + JS_FreeCString(ctx, str); + return val; +} + +static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) +{ + val = JS_StringToBigInt(ctx, val); + if (JS_VALUE_IS_NAN(val)) + return JS_ThrowSyntaxError(ctx, "invalid BigInt literal"); + return val; +} + +/* JS Numbers are not allowed */ +static JSValue JS_ToBigIntFree(JSContext *ctx, JSValue val) +{ + uint32_t tag; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + break; + case JS_TAG_INT: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + case JS_TAG_FLOAT64: + goto fail; + case JS_TAG_BOOL: + val = __JS_NewShortBigInt(ctx, JS_VALUE_GET_INT(val)); + break; + case JS_TAG_STRING: + val = JS_StringToBigIntErr(ctx, val); + if (JS_IsException(val)) + return val; + goto redo; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + return val; + goto redo; + default: + fail: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert to bigint"); + } + return val; +} + +static JSValue JS_ToBigInt(JSContext *ctx, JSValueConst val) +{ + return JS_ToBigIntFree(ctx, js_dup(val)); +} + +/* XXX: merge with JS_ToInt64Free with a specific flag */ +static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) +{ + uint64_t res; + + val = JS_ToBigIntFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { + res = JS_VALUE_GET_SHORT_BIG_INT(val); + } else { + JSBigInt *p = JS_VALUE_GET_PTR(val); + /* return the value mod 2^64 */ + res = p->tab[0]; + if (p->len >= 2) + res |= (uint64_t)p->tab[1] << 32; + JS_FreeValue(ctx, val); + } + *pres = res; + return 0; +} + +int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + return JS_ToBigInt64Free(ctx, pres, js_dup(val)); +} + +int JS_ToBigUint64(JSContext *ctx, uint64_t *pres, JSValueConst val) +{ + return JS_ToBigInt64Free(ctx, (int64_t *)pres, js_dup(val)); +} + +static no_inline __exception int js_unary_arith_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1; + int v; + uint32_t tag; + JSBigIntBuf buf1; + JSBigInt *p1; + + op1 = sp[-1]; + /* fast path for float64 */ + if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) + goto handle_float64; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + tag = JS_VALUE_GET_TAG(op1); + switch(tag) { + case JS_TAG_INT: + { + int64_t v64; + v64 = JS_VALUE_GET_INT(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + v64 += v; + break; + case OP_plus: + break; + case OP_neg: + if (v64 == 0) { + sp[-1] = js_float64(-0.0); + return 0; + } else { + v64 = -v64; + } + break; + default: + abort(); + } + sp[-1] = js_int64(v64); + } + break; + case JS_TAG_SHORT_BIG_INT: + { + int64_t v; + v = JS_VALUE_GET_SHORT_BIG_INT(op1); + switch(op) { + case OP_plus: + JS_ThrowTypeError(ctx, "bigint argument with unary +"); + goto exception; + case OP_inc: + if (v == JS_SHORT_BIG_INT_MAX) + goto bigint_slow_case; + sp[-1] = __JS_NewShortBigInt(ctx, v + 1); + break; + case OP_dec: + if (v == JS_SHORT_BIG_INT_MIN) + goto bigint_slow_case; + sp[-1] = __JS_NewShortBigInt(ctx, v - 1); + break; + case OP_neg: + v = JS_VALUE_GET_SHORT_BIG_INT(op1); + if (v == JS_SHORT_BIG_INT_MIN) { + bigint_slow_case: + p1 = js_bigint_set_short(&buf1, op1); + goto bigint_slow_case1; + } + sp[-1] = __JS_NewShortBigInt(ctx, -v); + break; + default: + abort(); + } + } + break; + case JS_TAG_BIG_INT: + { + JSBigInt *r; + p1 = JS_VALUE_GET_PTR(op1); + bigint_slow_case1: + switch(op) { + case OP_plus: + JS_ThrowTypeError(ctx, "bigint argument with unary +"); + JS_FreeValue(ctx, op1); + goto exception; + case OP_inc: + case OP_dec: + { + JSBigIntBuf buf2; + JSBigInt *p2; + p2 = js_bigint_set_si(&buf2, 2 * (op - OP_dec) - 1); + r = js_bigint_add(ctx, p1, p2, 0); + } + break; + case OP_neg: + r = js_bigint_neg(ctx, p1); + break; + case OP_not: + r = js_bigint_not(ctx, p1); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + if (!r) + goto exception; + sp[-1] = JS_CompactBigInt(ctx, r); + } + break; + default: + handle_float64: + { + double d; + d = JS_VALUE_GET_FLOAT64(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + d += v; + break; + case OP_plus: + break; + case OP_neg: + d = -d; + break; + default: + abort(); + } + sp[-1] = js_float64(d); + } + break; + } + return 0; + exception: + sp[-1] = JS_UNDEFINED; + return -1; +} + +static __exception int js_post_inc_slow(JSContext *ctx, + JSValue *sp, OPCodeEnum op) +{ + JSValue op1; + + /* XXX: allow custom operators */ + op1 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + sp[-1] = JS_UNDEFINED; + return -1; + } + sp[-1] = op1; + sp[0] = js_dup(op1); + return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec); +} + +static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1; + + op1 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) { + sp[-1] = __JS_NewShortBigInt(ctx, ~JS_VALUE_GET_SHORT_BIG_INT(op1)); + } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { + JSBigInt *r; + r = js_bigint_not(ctx, JS_VALUE_GET_PTR(op1)); + JS_FreeValue(ctx, op1); + if (!r) + goto exception; + sp[-1] = JS_CompactBigInt(ctx, r); + } else { + int32_t v1; + if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) + goto exception; + sp[-1] = js_int32(~v1); + } + return 0; + exception: + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + double d1, d2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* fast path for float operations */ + if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + d2 = JS_VALUE_GET_FLOAT64(op2); + goto handle_float64; + } + /* fast path for short big int operations */ + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2; + js_sdlimb_t v; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + switch(op) { + case OP_sub: + v = (js_sdlimb_t)v1 - (js_sdlimb_t)v2; + break; + case OP_mul: + v = (js_sdlimb_t)v1 * (js_sdlimb_t)v2; + break; + case OP_div: + if (v2 == 0 || + ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) && + v2 == -1)) { + goto slow_big_int; + } + sp[-2] = __JS_NewShortBigInt(ctx, v1 / v2); + return 0; + case OP_mod: + if (v2 == 0 || + ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) && + v2 == -1)) { + goto slow_big_int; + } + sp[-2] = __JS_NewShortBigInt(ctx, v1 % v2); + return 0; + case OP_pow: + goto slow_big_int; + default: + abort(); + } + if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) { + sp[-2] = __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *r = js_bigint_new_di(ctx, v); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); + } + return 0; + } + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + int32_t v1, v2; + int64_t v; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + switch(op) { + case OP_sub: + v = (int64_t)v1 - (int64_t)v2; + break; + case OP_mul: + v = (int64_t)v1 * (int64_t)v2; + if (v == 0 && (v1 | v2) < 0) { + sp[-2] = js_float64(-0.0); + return 0; + } + break; + case OP_div: + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + sp[-2] = js_number((double)v1 / (double)v2); + JS_X87_FPCW_RESTORE(fpcw); + return 0; + case OP_mod: + if (v1 < 0 || v2 <= 0) { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + sp[-2] = js_number(fmod(v1, v2)); + JS_X87_FPCW_RESTORE(fpcw); + return 0; + } else { + v = (int64_t)v1 % (int64_t)v2; + } + break; + case OP_pow: + sp[-2] = js_number(js_math_pow(v1, v2)); + return 0; + default: + abort(); + } + sp[-2] = js_int64(v); + } else if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_BIG_INT) && + (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + slow_big_int: + /* bigint result */ + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + switch(op) { + case OP_add: + r = js_bigint_add(ctx, p1, p2, 0); + break; + case OP_sub: + r = js_bigint_add(ctx, p1, p2, 1); + break; + case OP_mul: + r = js_bigint_mul(ctx, p1, p2); + break; + case OP_div: + r = js_bigint_divrem(ctx, p1, p2, false); + break; + case OP_mod: + r = js_bigint_divrem(ctx, p1, p2, true); + break; + case OP_pow: + r = js_bigint_pow(ctx, p1, p2); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) + goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); + } else { + double dr; + /* float64 result */ + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + handle_float64: + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + switch(op) { + case OP_sub: + dr = d1 - d2; + break; + case OP_mul: + dr = d1 * d2; + break; + case OP_div: + dr = d1 / d2; + break; + case OP_mod: + dr = fmod(d1, d2); + break; + case OP_pow: + dr = js_math_pow(d1, d2); + break; + default: + abort(); + } + JS_X87_FPCW_RESTORE(fpcw); + sp[-2] = js_float64(dr); + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* fast path for float64 */ + if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { + double d1, d2; + d1 = JS_VALUE_GET_FLOAT64(op1); + d2 = JS_VALUE_GET_FLOAT64(op2); + sp[-2] = js_float64(d1 + d2); + return 0; + } + /* fast path for short bigint */ + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2; + js_sdlimb_t v; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + v = (js_sdlimb_t)v1 + (js_sdlimb_t)v2; + if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) { + sp[-2] = __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *r = js_bigint_new_di(ctx, v); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); + } + return 0; + } + + if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + } + + if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { + sp[-2] = JS_ConcatString(ctx, op1, op2); + if (JS_IsException(sp[-2])) + goto exception; + return 0; + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + int32_t v1, v2; + int64_t v; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + v = (int64_t)v1 + (int64_t)v2; + sp[-2] = js_int64(v); + } else if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + /* bigint result */ + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + r = js_bigint_add(ctx, p1, p2, 0); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) + goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); + } else { + double d1, d2; + /* float64 result */ + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + sp[-2] = js_float64(d1 + d2); + JS_X87_FPCW_RESTORE(fpcw); + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_binary_logic_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2, v; + js_sdlimb_t vd; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + /* bigint fast path */ + switch(op) { + case OP_and: + v = v1 & v2; + break; + case OP_or: + v = v1 | v2; + break; + case OP_xor: + v = v1 ^ v2; + break; + case OP_sar: + if (v2 > (JS_LIMB_BITS - 1)) { + goto slow_big_int; + } else if (v2 < 0) { + if (v2 < -(JS_LIMB_BITS - 1)) + goto slow_big_int; + v2 = -v2; + goto bigint_shl; + } + bigint_sar: + v = v1 >> v2; + break; + case OP_shl: + if (v2 > (JS_LIMB_BITS - 1)) { + goto slow_big_int; + } else if (v2 < 0) { + if (v2 < -(JS_LIMB_BITS - 1)) + goto slow_big_int; + v2 = -v2; + goto bigint_sar; + } + bigint_shl: + vd = (js_dlimb_t)v1 << v2; + if (likely(vd >= JS_SHORT_BIG_INT_MIN && + vd <= JS_SHORT_BIG_INT_MAX)) { + v = vd; + } else { + JSBigInt *r = js_bigint_new_di(ctx, vd); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); + return 0; + } + break; + default: + abort(); + } + sp[-2] = __JS_NewShortBigInt(ctx, v); + return 0; + } + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + + tag1 = JS_VALUE_GET_TAG(op1); + tag2 = JS_VALUE_GET_TAG(op2); + if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + slow_big_int: + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + switch(op) { + case OP_and: + case OP_or: + case OP_xor: + r = js_bigint_logic(ctx, p1, p2, op); + break; + case OP_shl: + case OP_sar: + { + js_slimb_t shift; + shift = js_bigint_get_si_sat(p2); + if (shift > INT32_MAX) + shift = INT32_MAX; + else if (shift < -INT32_MAX) + shift = -INT32_MAX; + if (op == OP_sar) + shift = -shift; + if (shift >= 0) + r = js_bigint_shl(ctx, p1, shift); + else + r = js_bigint_shr(ctx, p1, -shift); + } + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) + goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); + } else { + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) + goto exception; + switch(op) { + case OP_shl: + r = v1 << (v2 & 0x1f); + break; + case OP_sar: + r = (int)v1 >> (v2 & 0x1f); + break; + case OP_and: + r = v1 & v2; + break; + case OP_or: + r = v1 | v2; + break; + case OP_xor: + r = v1 ^ v2; + break; + default: + abort(); + } + sp[-2] = js_int32(r); + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +/* op1 must be a bigint or int. */ +static JSBigInt *JS_ToBigIntBuf(JSContext *ctx, JSBigIntBuf *buf1, + JSValue op1) +{ + JSBigInt *p1; + + switch(JS_VALUE_GET_TAG(op1)) { + case JS_TAG_INT: + p1 = js_bigint_set_si(buf1, JS_VALUE_GET_INT(op1)); + break; + case JS_TAG_SHORT_BIG_INT: + p1 = js_bigint_set_short(buf1, op1); + break; + case JS_TAG_BIG_INT: + p1 = JS_VALUE_GET_PTR(op1); + break; + default: + abort(); + } + return p1; +} + +/* op1 and op2 must be numeric types and at least one must be a + bigint. No exception is generated. */ +static int js_compare_bigint(JSContext *ctx, OPCodeEnum op, + JSValue op1, JSValue op2) +{ + int res, val, tag1, tag2; + JSBigIntBuf buf1, buf2; + JSBigInt *p1, *p2; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_INT) && + (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_INT)) { + /* fast path */ + js_slimb_t v1, v2; + if (tag1 == JS_TAG_INT) + v1 = JS_VALUE_GET_INT(op1); + else + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + if (tag2 == JS_TAG_INT) + v2 = JS_VALUE_GET_INT(op2); + else + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + val = (v1 > v2) - (v1 < v2); + } else { + if (tag1 == JS_TAG_FLOAT64) { + p2 = JS_ToBigIntBuf(ctx, &buf2, op2); + val = js_bigint_float64_cmp(ctx, p2, JS_VALUE_GET_FLOAT64(op1)); + if (val == 2) + goto unordered; + val = -val; + } else if (tag2 == JS_TAG_FLOAT64) { + p1 = JS_ToBigIntBuf(ctx, &buf1, op1); + val = js_bigint_float64_cmp(ctx, p1, JS_VALUE_GET_FLOAT64(op2)); + if (val == 2) { + unordered: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return false; + } + } else { + p1 = JS_ToBigIntBuf(ctx, &buf1, op1); + p2 = JS_ToBigIntBuf(ctx, &buf2, op2); + val = js_bigint_cmp(ctx, p1, p2); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + } + + switch(op) { + case OP_lt: + res = val < 0; + break; + case OP_lte: + res = val <= 0; + break; + case OP_gt: + res = val > 0; + break; + case OP_gte: + res = val >= 0; + break; + case OP_eq: + res = val == 0; + break; + default: + abort(); + } + return res; +} + +static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + int res; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { + JSString *p1, *p2; + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + res = js_string_compare(p1, p2); + switch(op) { + case OP_lt: + res = (res < 0); + break; + case OP_lte: + res = (res <= 0); + break; + case OP_gt: + res = (res > 0); + break; + default: + case OP_gte: + res = (res >= 0); + break; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) && + (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) { + /* fast path for float64/int */ + goto float64_compare; + } else { + if ((((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + tag2 == JS_TAG_STRING) || + ((tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) && + tag1 == JS_TAG_STRING))) { + if (tag1 == JS_TAG_STRING) { + op1 = JS_StringToBigInt(ctx, op1); + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT) + goto invalid_bigint_string; + } + if (tag2 == JS_TAG_STRING) { + op2 = JS_StringToBigInt(ctx, op2); + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT) { + invalid_bigint_string: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = false; + goto done; + } + } + } else { + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + } + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT || + tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) { + res = js_compare_bigint(ctx, op, op1, op2); + } else { + double d1, d2; + + float64_compare: + /* can use floating point comparison */ + if (tag1 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + } else { + d1 = JS_VALUE_GET_INT(op1); + } + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else { + d2 = JS_VALUE_GET_INT(op2); + } + switch(op) { + case OP_lt: + res = (d1 < d2); /* if NaN return false */ + break; + case OP_lte: + res = (d1 <= d2); /* if NaN return false */ + break; + case OP_gt: + res = (d1 > d2); /* if NaN return false */ + break; + default: + case OP_gte: + res = (d1 >= d2); /* if NaN return false */ + break; + } + } + } + done: + sp[-2] = js_bool(res); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static bool tag_is_number(uint32_t tag) +{ + return (tag == JS_TAG_INT || + tag == JS_TAG_FLOAT64 || + tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT); +} + +static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, + bool is_neq) +{ + JSValue op1, op2; + int res; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + redo: + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + if (tag_is_number(tag1) && tag_is_number(tag2)) { + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); + } else if ((tag1 == JS_TAG_FLOAT64 && + (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) || + (tag2 == JS_TAG_FLOAT64 && + (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) { + double d1, d2; + if (tag1 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + } else { + d1 = JS_VALUE_GET_INT(op1); + } + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else { + d2 = JS_VALUE_GET_INT(op2); + } + res = (d1 == d2); + } else { + res = js_compare_bigint(ctx, OP_eq, op1, op2); + if (res < 0) + goto exception; + } + } else if (tag1 == tag2) { + res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); + } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) || + (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) { + res = true; + } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) || + (tag2 == JS_TAG_STRING && tag_is_number(tag1))) { + + if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT || + tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) { + if (tag1 == JS_TAG_STRING) { + op1 = JS_StringToBigInt(ctx, op1); + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT) + goto invalid_bigint_string; + } + if (tag2 == JS_TAG_STRING) { + op2 = JS_StringToBigInt(ctx, op2); + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT ) { + invalid_bigint_string: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = false; + goto done; + } + } + } else { + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + } + res = js_strict_eq(ctx, op1, op2); + } else if (tag1 == JS_TAG_BOOL) { + op1 = js_int32(JS_VALUE_GET_INT(op1)); + goto redo; + } else if (tag2 == JS_TAG_BOOL) { + op2 = js_int32(JS_VALUE_GET_INT(op2)); + goto redo; + } else if ((tag1 == JS_TAG_OBJECT && + (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) || + (tag2 == JS_TAG_OBJECT && + (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + goto redo; + } else { + /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */ + if ((JS_IsHTMLDDA(ctx, op1) && + (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) || + (JS_IsHTMLDDA(ctx, op2) && + (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) { + res = true; + } else { + res = false; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + } + done: + sp[-2] = js_bool(res ^ is_neq); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + + if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) { + JS_ThrowTypeError(ctx, "BigInt operands are forbidden for >>>"); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + goto exception; + } + /* cannot give an exception */ + JS_ToUint32Free(ctx, &v1, op1); + JS_ToUint32Free(ctx, &v2, op2); + r = v1 >> (v2 & 0x1f); + sp[-2] = js_uint32(r); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static bool js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, + JSStrictEqModeEnum eq_mode) +{ + bool res; + int tag1, tag2; + double d1, d2; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + switch(tag1) { + case JS_TAG_BOOL: + if (tag1 != tag2) { + res = false; + } else { + res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); + goto done_no_free; + } + break; + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + res = (tag1 == tag2); + break; + case JS_TAG_STRING: + { + JSString *p1, *p2; + if (tag1 != tag2) { + res = false; + } else { + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + res = js_string_eq(p1, p2); + } + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p1, *p2; + if (tag1 != tag2) { + res = false; + } else { + p1 = JS_VALUE_GET_PTR(op1); + p2 = JS_VALUE_GET_PTR(op2); + res = (p1 == p2); + } + } + break; + case JS_TAG_OBJECT: + if (tag1 != tag2) + res = false; + else + res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2); + break; + case JS_TAG_INT: + d1 = JS_VALUE_GET_INT(op1); + if (tag2 == JS_TAG_INT) { + d2 = JS_VALUE_GET_INT(op2); + goto number_test; + } else if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + goto number_test; + } else { + res = false; + } + break; + case JS_TAG_FLOAT64: + d1 = JS_VALUE_GET_FLOAT64(op1); + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else if (tag2 == JS_TAG_INT) { + d2 = JS_VALUE_GET_INT(op2); + } else { + res = false; + break; + } + number_test: + if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { + JSFloat64Union u1, u2; + /* NaN is not always normalized, so this test is necessary */ + if (isnan(d1) || isnan(d2)) { + res = isnan(d1) == isnan(d2); + } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) { + res = (d1 == d2); /* +0 == -0 */ + } else { + u1.d = d1; + u2.d = d2; + res = (u1.u64 == u2.u64); /* +0 != -0 */ + } + } else { + res = (d1 == d2); /* if NaN return false and +0 == -0 */ + } + goto done_no_free; + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + { + JSBigIntBuf buf1, buf2; + JSBigInt *p1, *p2; + + if (tag2 != JS_TAG_SHORT_BIG_INT && + tag2 != JS_TAG_BIG_INT) { + res = false; + break; + } + + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + res = (js_bigint_cmp(ctx, p1, p2) == 0); + } + break; + default: + res = false; + break; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + done_no_free: + return res; +} + +static bool js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2) +{ + return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); +} + +static bool js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2) +{ + return js_strict_eq2(ctx, js_dup(op1), js_dup(op2), JS_EQ_SAME_VALUE); +} + +static bool js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2) +{ + return js_strict_eq2(ctx, js_dup(op1), js_dup(op2), JS_EQ_SAME_VALUE_ZERO); +} + +static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp, + bool is_neq) +{ + bool res; + res = js_strict_eq(ctx, sp[-2], sp[-1]); + sp[-2] = js_bool(res ^ is_neq); + return 0; +} + +static __exception int js_operator_in(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + JSAtom atom; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + + if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "invalid 'in' operand"); + return -1; + } + atom = JS_ValueToAtom(ctx, op1); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + ret = JS_HasProperty(ctx, op2, atom); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return -1; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static __exception int js_operator_private_in(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + int ret; + op1 = sp[-2]; /* object */ + op2 = sp[-1]; /* field name or method function */ + if (JS_VALUE_GET_TAG(op1) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "invalid 'in' operand"); + return -1; + } + if (JS_IsObject(op2)) { + /* method: use the brand */ + ret = JS_CheckBrand(ctx, op1, op2); + if (ret < 0) + return -1; + } else { + JSAtom atom; + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + /* field */ + atom = JS_ValueToAtom(ctx, op2); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + p = JS_VALUE_GET_OBJ(op1); + prs = find_own_property(&pr, p, atom); + JS_FreeAtom(ctx, atom); + ret = (prs != NULL); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static __exception int js_has_unscopable(JSContext *ctx, JSValue obj, + JSAtom atom) +{ + JSValue arr, val; + int ret; + + arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables); + if (JS_IsException(arr)) + return -1; + ret = 0; + if (JS_IsObject(arr)) { + val = JS_GetProperty(ctx, arr, atom); + ret = JS_ToBoolFree(ctx, val); + } + JS_FreeValue(ctx, arr); + return ret; +} + +static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + ret = JS_IsInstanceOf(ctx, op1, op2); + if (ret < 0) + return ret; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) +{ + JSAtom atom; + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(op1); + switch(tag) { + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + atom = JS_ATOM_bigint; + break; + case JS_TAG_INT: + case JS_TAG_FLOAT64: + atom = JS_ATOM_number; + break; + case JS_TAG_UNDEFINED: + atom = JS_ATOM_undefined; + break; + case JS_TAG_BOOL: + atom = JS_ATOM_boolean; + break; + case JS_TAG_STRING: + atom = JS_ATOM_string; + break; + case JS_TAG_OBJECT: + { + JSObject *p; + p = JS_VALUE_GET_OBJ(op1); + if (unlikely(p->is_HTMLDDA)) + atom = JS_ATOM_undefined; + else if (JS_IsFunction(ctx, op1)) + atom = JS_ATOM_function; + else + goto obj_type; + } + break; + case JS_TAG_NULL: + obj_type: + atom = JS_ATOM_object; + break; + case JS_TAG_SYMBOL: + atom = JS_ATOM_symbol; + break; + default: + atom = JS_ATOM_unknown; + break; + } + return atom; +} + +static __exception int js_operator_delete(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + JSAtom atom; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + atom = JS_ValueToAtom(ctx, op2); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT); + JS_FreeAtom(ctx, atom); + if (unlikely(ret < 0)) + return -1; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (!b || b->is_strict_mode || !b->has_prototype) { + return JS_ThrowTypeError(ctx, "invalid property access"); + } + return JS_UNDEFINED; +} + +static JSValue js_function_proto_fileName(JSContext *ctx, + JSValueConst this_val) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (b) { + return JS_AtomToString(ctx, b->filename); + } + return JS_UNDEFINED; +} + +static JSValue js_function_proto_int32(JSContext *ctx, + JSValueConst this_val, + int magic) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (b) { + int *field = (int *) ((char *)b + magic); + return js_int32(*field); + } + return JS_UNDEFINED; +} + +static int js_arguments_define_own_property(JSContext *ctx, + JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, + JSValueConst setter, int flags) +{ + JSObject *p; + uint32_t idx; + p = JS_VALUE_GET_OBJ(this_obj); + /* convert to normal array when redefining an existing numeric field */ + if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) && + idx < p->u.array.count) { + if (convert_fast_array_to_array(ctx, p)) + return -1; + } + /* run the default define own property */ + return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, + flags | JS_PROP_NO_EXOTIC); +} + +static const JSClassExoticMethods js_arguments_exotic_methods = { + .define_own_property = js_arguments_define_own_property, +}; + +static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv) +{ + JSValue val, *tab; + JSProperty *pr; + JSObject *p; + int i; + + val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_ARGUMENTS); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + + /* add the length field (cannot fail) */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (!pr) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + pr->u.value = js_int32(argc); + + /* initialize the fast array part */ + tab = NULL; + if (argc > 0) { + tab = js_malloc(ctx, sizeof(tab[0]) * argc); + if (!tab) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + for(i = 0; i < argc; i++) { + tab[i] = js_dup(argv[i]); + } + } + p->u.array.u.values = tab; + p->u.array.count = argc; + + JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, + js_dup(ctx->array_proto_values), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + /* add callee property to throw a TypeError in strict mode */ + JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED, + ctx->throw_type_error, ctx->throw_type_error, + JS_PROP_HAS_GET | JS_PROP_HAS_SET); + return val; +} + +#define GLOBAL_VAR_OFFSET 0x40000000 +#define ARGUMENT_VAR_OFFSET 0x20000000 + +/* legacy arguments object: add references to the function arguments */ +static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, + JSValueConst *argv, + JSStackFrame *sf, int arg_count) +{ + JSValue val; + JSProperty *pr; + JSObject *p; + int i; + + val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_MAPPED_ARGUMENTS); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + + /* add the length field (cannot fail) */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (!pr) + goto fail; + pr->u.value = js_int32(argc); + + for(i = 0; i < arg_count; i++) { + JSVarRef *var_ref; + var_ref = get_var_ref(ctx, sf, i, true); + if (!var_ref) + goto fail; + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); + if (!pr) { + free_var_ref(ctx->rt, var_ref); + goto fail; + } + pr->u.var_ref = var_ref; + } + + /* the arguments not mapped to the arguments of the function can + be normal properties */ + for(i = arg_count; i < argc; i++) { + if (JS_DefinePropertyValueUint32(ctx, val, i, + js_dup(argv[i]), + JS_PROP_C_W_E) < 0) + goto fail; + } + + JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, + js_dup(ctx->array_proto_values), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + /* callee returns this function in non strict mode */ + JS_DefinePropertyValue(ctx, val, JS_ATOM_callee, + js_dup(ctx->rt->current_stack_frame->cur_func), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) +{ + JSObject *p; + JSPropertyEnum *tab_atom; + int i; + JSValue enum_obj, obj1; + JSForInIterator *it; + uint32_t tag, tab_atom_count; + + tag = JS_VALUE_GET_TAG(obj); + if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) { + obj = JS_ToObjectFree(ctx, obj); + } + + it = js_malloc(ctx, sizeof(*it)); + if (!it) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR); + if (JS_IsException(enum_obj)) { + js_free(ctx, it); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + it->is_array = false; + it->obj = obj; + it->idx = 0; + p = JS_VALUE_GET_OBJ(enum_obj); + p->u.for_in_iterator = it; + + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) + return enum_obj; + + /* fast path: assume no enumerable properties in the prototype chain */ + obj1 = js_dup(obj); + for(;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + if (tab_atom_count != 0) { + JS_FreeValue(ctx, obj1); + goto slow_path; + } + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + } + + p = JS_VALUE_GET_OBJ(obj); + + if (p->fast_array) { + JSShape *sh; + JSShapeProperty *prs; + /* check that there are no enumerable normal fields */ + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + if (prs->flags & JS_PROP_ENUMERABLE) + goto normal_case; + } + /* for fast arrays, we only store the number of elements */ + it->is_array = true; + it->array_length = p->u.array.count; + } else { + normal_case: + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) + goto fail; + for(i = 0; i < tab_atom_count; i++) { + JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + } + return enum_obj; + + slow_path: + /* non enumerable properties hide the enumerables ones in the + prototype chain */ + obj1 = js_dup(obj); + for(;;) { + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + for(i = 0; i < tab_atom_count; i++) { + JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL, + (tab_atom[i].is_enumerable ? + JS_PROP_ENUMERABLE : 0)); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + } + return enum_obj; + + fail: + JS_FreeValue(ctx, enum_obj); + return JS_EXCEPTION; +} + +/* obj -> enum_obj */ +static __exception int js_for_in_start(JSContext *ctx, JSValue *sp) +{ + sp[-1] = build_for_in_iterator(ctx, sp[-1]); + if (JS_IsException(sp[-1])) + return -1; + return 0; +} + +/* enum_obj -> enum_obj value done */ +static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) +{ + JSValue enum_obj; + JSObject *p; + JSAtom prop; + JSForInIterator *it; + int ret; + + enum_obj = sp[-1]; + /* fail safe */ + if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT) + goto done; + p = JS_VALUE_GET_OBJ(enum_obj); + if (p->class_id != JS_CLASS_FOR_IN_ITERATOR) + goto done; + it = p->u.for_in_iterator; + + for(;;) { + if (it->is_array) { + if (it->idx >= it->array_length) + goto done; + prop = __JS_AtomFromUInt32(it->idx); + it->idx++; + } else { + JSShape *sh = p->shape; + JSShapeProperty *prs; + if (it->idx >= sh->prop_count) + goto done; + prs = get_shape_prop(sh) + it->idx; + prop = prs->atom; + it->idx++; + if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE)) + continue; + } + // check if the property was deleted unless we're dealing with a proxy + JSValue obj = it->obj; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_PROXY) + break; + } + ret = JS_HasProperty(ctx, obj, prop); + if (ret < 0) + return ret; + if (ret) + break; + } + /* return the property */ + sp[0] = JS_AtomToValue(ctx, prop); + sp[1] = JS_FALSE; + return 0; + done: + /* return the end */ + sp[0] = JS_UNDEFINED; + sp[1] = JS_TRUE; + return 0; +} + +static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj, + JSValueConst method) +{ + JSValue enum_obj; + + enum_obj = JS_Call(ctx, method, obj, 0, NULL); + if (JS_IsException(enum_obj)) + return enum_obj; + if (!JS_IsObject(enum_obj)) { + JS_FreeValue(ctx, enum_obj); + return JS_ThrowTypeErrorNotAnObject(ctx); + } + return enum_obj; +} + +static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, bool is_async) +{ + JSValue method, ret, sync_iter; + + if (is_async) { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator); + if (JS_IsException(method)) + return method; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return method; + sync_iter = JS_GetIterator2(ctx, obj, method); + JS_FreeValue(ctx, method); + if (JS_IsException(sync_iter)) + return sync_iter; + ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter); + JS_FreeValue(ctx, sync_iter); + return ret; + } + } else { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return method; + } + if (!JS_IsFunction(ctx, method)) { + JS_FreeValue(ctx, method); + return JS_ThrowTypeError(ctx, "value is not iterable"); + } + ret = JS_GetIterator2(ctx, obj, method); + JS_FreeValue(ctx, method); + return ret; +} + +/* return *pdone = 2 if the iterator object is not parsed */ +static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj, + JSValueConst method, + int argc, JSValueConst *argv, int *pdone) +{ + JSValue obj; + + /* fast path for the built-in iterators (avoid creating the + intermediate result object) */ + if (JS_IsObject(method)) { + JSObject *p = JS_VALUE_GET_OBJ(method); + if (p->class_id == JS_CLASS_C_FUNCTION && + p->u.cfunc.cproto == JS_CFUNC_iterator_next) { + JSCFunctionType func; + JSValueConst args[1]; + + /* in case the function expects one argument */ + if (argc == 0) { + args[0] = JS_UNDEFINED; + argv = args; + } + func = p->u.cfunc.c_function; + return func.iterator_next(ctx, enum_obj, argc, argv, + pdone, p->u.cfunc.magic); + } + } + obj = JS_Call(ctx, method, enum_obj, argc, argv); + if (JS_IsException(obj)) + goto fail; + if (!JS_IsObject(obj)) { + JS_FreeValue(ctx, obj); + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto fail; + } + *pdone = 2; + return obj; + fail: + *pdone = false; + return JS_EXCEPTION; +} + +static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, + JSValueConst method, + int argc, JSValueConst *argv, int *pdone) +{ + JSValue obj, value, done_val; + int done; + + obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); + if (JS_IsException(obj)) + goto fail; + if (likely(done == 0)) { + *pdone = false; + return obj; + } else if (done != 2) { + JS_FreeValue(ctx, obj); + *pdone = true; + return JS_UNDEFINED; + } else { + done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); + if (JS_IsException(done_val)) + goto fail; + *pdone = JS_ToBoolFree(ctx, done_val); + value = JS_UNDEFINED; + if (!*pdone) { + value = JS_GetProperty(ctx, obj, JS_ATOM_value); + } + JS_FreeValue(ctx, obj); + return value; + } + fail: + JS_FreeValue(ctx, obj); + *pdone = false; + return JS_EXCEPTION; +} + +/* return < 0 in case of exception */ +static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, + bool is_exception_pending) +{ + JSValue method, ret, ex_obj; + int res; + + if (is_exception_pending) { + ex_obj = ctx->rt->current_exception; + ctx->rt->current_exception = JS_UNINITIALIZED; + res = -1; + } else { + ex_obj = JS_UNDEFINED; + res = 0; + } + method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return); + if (JS_IsException(method)) { + res = -1; + goto done; + } + if (JS_IsUndefined(method) || JS_IsNull(method)) { + goto done; + } + ret = JS_CallFree(ctx, method, enum_obj, 0, NULL); + if (!is_exception_pending) { + if (JS_IsException(ret)) { + res = -1; + } else if (!JS_IsObject(ret)) { + JS_ThrowTypeErrorNotAnObject(ctx); + res = -1; + } + } + JS_FreeValue(ctx, ret); + done: + if (is_exception_pending) { + JS_Throw(ctx, ex_obj); + } + return res; +} + +/* obj -> enum_rec (3 slots) */ +static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, + bool is_async) +{ + JSValue op1, obj, method; + op1 = sp[-1]; + obj = JS_GetIterator(ctx, op1, is_async); + if (JS_IsException(obj)) + return -1; + JS_FreeValue(ctx, op1); + sp[-1] = obj; + method = JS_GetProperty(ctx, obj, JS_ATOM_next); + if (JS_IsException(method)) + return -1; + sp[0] = method; + return 0; +} + +/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset' + objs. If 'done' is true or in case of exception, 'enum_rec' is set + to undefined. If 'done' is true, 'value' is always set to + undefined. */ +static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) +{ + JSValue value = JS_UNDEFINED; + int done = 1; + + if (likely(!JS_IsUndefined(sp[offset]))) { + value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done); + if (JS_IsException(value)) + done = -1; + if (done) { + /* value is JS_UNDEFINED or JS_EXCEPTION */ + /* replace the iteration object with undefined */ + JS_FreeValue(ctx, sp[offset]); + sp[offset] = JS_UNDEFINED; + if (done < 0) { + return -1; + } else { + JS_FreeValue(ctx, value); + value = JS_UNDEFINED; + } + } + } + sp[0] = value; + sp[1] = js_bool(done); + return 0; +} + +static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValue obj, + int *pdone) +{ + JSValue done_val, value; + int done; + done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); + if (JS_IsException(done_val)) + goto fail; + done = JS_ToBoolFree(ctx, done_val); + value = JS_GetProperty(ctx, obj, JS_ATOM_value); + if (JS_IsException(value)) + goto fail; + *pdone = done; + return value; + fail: + *pdone = false; + return JS_EXCEPTION; +} + +static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) +{ + JSValue obj, value; + int done; + obj = sp[-1]; + if (!JS_IsObject(obj)) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + return -1; + } + value = JS_IteratorGetCompleteValue(ctx, obj, &done); + if (JS_IsException(value)) + return -1; + JS_FreeValue(ctx, obj); + sp[-1] = value; + sp[0] = js_bool(done); + return 0; +} + +static JSValue js_create_iterator_result(JSContext *ctx, + JSValue val, + bool done) +{ + JSValue obj; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, val); + return obj; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value, + val, JS_PROP_C_W_E) < 0) { + goto fail; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done, + js_bool(done), JS_PROP_C_W_E) < 0) { + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int *pdone, int magic); + +static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); + +static bool js_is_fast_array(JSContext *ctx, JSValue obj) +{ + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + return true; + } + } + return false; +} + +/* Access an Array's internal JSValue array if available */ +static bool js_get_fast_array(JSContext *ctx, JSValue obj, + JSValue **arrpp, uint32_t *countp) +{ + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + *countp = p->u.array.count; + *arrpp = p->u.array.u.values; + return true; + } + } + return false; +} + +static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) +{ + JSValue iterator, enumobj, method, value; + int is_array_iterator; + JSValue *arrp; + uint32_t i, count32, pos; + + if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { + JS_ThrowInternalError(ctx, "invalid index for append"); + return -1; + } + + pos = JS_VALUE_GET_INT(sp[-2]); + + /* XXX: further optimisations: + - use ctx->array_proto_values? + - check if array_iterator_prototype next method is built-in and + avoid constructing actual iterator object? + - build this into js_for_of_start and use in all `for (x of o)` loops + */ + iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator); + if (JS_IsException(iterator)) + return -1; + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft = { .generic_magic = js_create_array_iterator }; + is_array_iterator = JS_IsCFunction(ctx, iterator, + ft.generic, + JS_ITERATOR_KIND_VALUE); + JS_FreeValue(ctx, iterator); + + enumobj = JS_GetIterator(ctx, sp[-1], false); + if (JS_IsException(enumobj)) + return -1; + method = JS_GetProperty(ctx, enumobj, JS_ATOM_next); + if (JS_IsException(method)) { + JS_FreeValue(ctx, enumobj); + return -1; + } + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft2 = { .iterator_next = js_array_iterator_next }; + if (is_array_iterator + && JS_IsCFunction(ctx, method, ft2.generic, 0) + && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { + uint32_t len; + if (js_get_length32(ctx, &len, sp[-1])) + goto exception; + /* if len > count32, the elements >= count32 might be read in + the prototypes and might have side effects */ + if (len != count32) + goto general_case; + /* Handle fast arrays explicitly */ + for (i = 0; i < count32; i++) { + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, + js_dup(arrp[i]), JS_PROP_C_W_E) < 0) + goto exception; + } + } else { + general_case: + for (;;) { + int done; + value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); + if (JS_IsException(value)) + goto exception; + if (done) { + /* value is JS_UNDEFINED */ + break; + } + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0) + goto exception; + } + } + /* Note: could raise an error if too many elements */ + sp[-2] = js_int32(pos); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return 0; + +exception: + JS_IteratorClose(ctx, enumobj, true); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return -1; +} + +static __exception int JS_CopyDataProperties(JSContext *ctx, + JSValue target, + JSValue source, + JSValue excluded, + bool setprop) +{ + JSPropertyEnum *tab_atom; + JSValue val; + uint32_t i, tab_atom_count; + JSObject *p; + JSObject *pexcl = NULL; + int ret, gpn_flags; + JSPropertyDescriptor desc; + bool is_enumerable; + + if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) + return 0; + + if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT) + pexcl = JS_VALUE_GET_OBJ(excluded); + + p = JS_VALUE_GET_OBJ(source); + + gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY; + if (p->is_exotic) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it + introduces a visible change */ + if (em && em->get_own_property_names) { + gpn_flags &= ~JS_GPN_ENUM_ONLY; + } + } + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, + gpn_flags)) + return -1; + + for (i = 0; i < tab_atom_count; i++) { + if (pexcl) { + ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom); + if (ret) { + if (ret < 0) + goto exception; + continue; + } + } + if (!(gpn_flags & JS_GPN_ENUM_ONLY)) { + /* test if the property is enumerable */ + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom); + if (ret < 0) + goto exception; + if (!ret) + continue; + is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0; + js_free_desc(ctx, &desc); + if (!is_enumerable) + continue; + } + val = JS_GetProperty(ctx, source, tab_atom[i].atom); + if (JS_IsException(val)) + goto exception; + if (setprop) + ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val); + else + ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, + JS_PROP_C_W_E); + if (ret < 0) + goto exception; + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + return 0; + exception: + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + return -1; +} + +/* only valid inside C functions */ +static JSValueConst JS_GetActiveFunction(JSContext *ctx) +{ + return ctx->rt->current_stack_frame->cur_func; +} + +static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, + int var_idx, bool is_arg) +{ + JSVarRef *var_ref; + struct list_head *el; + JSValue *pvalue; + + if (is_arg) + pvalue = &sf->arg_buf[var_idx]; + else + pvalue = &sf->var_buf[var_idx]; + + list_for_each(el, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + if (var_ref->pvalue == pvalue) { + var_ref->header.ref_count++; + return var_ref; + } + } + /* create a new one */ + var_ref = js_malloc(ctx, sizeof(JSVarRef)); + if (!var_ref) + return NULL; + var_ref->header.ref_count = 1; + var_ref->is_detached = false; + list_add_tail(&var_ref->header.link, &sf->var_ref_list); + var_ref->pvalue = pvalue; + var_ref->value = JS_UNDEFINED; + return var_ref; +} + +static JSValue js_closure2(JSContext *ctx, JSValue func_obj, + JSFunctionBytecode *b, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSObject *p; + JSVarRef **var_refs; + int i; + + p = JS_VALUE_GET_OBJ(func_obj); + p->u.func.function_bytecode = b; + p->u.func.home_object = NULL; + p->u.func.var_refs = NULL; + if (b->closure_var_count) { + var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); + if (!var_refs) + goto fail; + p->u.func.var_refs = var_refs; + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + JSVarRef *var_ref; + if (cv->is_local) { + /* reuse the existing variable reference if it already exists */ + var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg); + if (!var_ref) + goto fail; + } else { + var_ref = cur_var_refs[cv->var_idx]; + var_ref->header.ref_count++; + } + var_refs[i] = var_ref; + } + } + return func_obj; + fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) +{ + JSValue obj, this_val; + int ret; + + this_val = JS_MKPTR(JS_TAG_OBJECT, p); + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, + js_dup(this_val), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (ret < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static const uint16_t func_kind_to_class_id[] = { + [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION, + [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION, + [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION, + [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION, +}; + +static JSValue js_closure(JSContext *ctx, JSValue bfunc, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSFunctionBytecode *b; + JSValue func_obj; + JSAtom name_atom; + + b = JS_VALUE_GET_PTR(bfunc); + func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]); + if (JS_IsException(func_obj)) { + JS_FreeValue(ctx, bfunc); + return JS_EXCEPTION; + } + func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf); + if (JS_IsException(func_obj)) { + /* bfunc has been freed */ + goto fail; + } + name_atom = b->func_name; + if (name_atom == JS_ATOM_NULL) + name_atom = JS_ATOM_empty_string; + js_function_set_properties(ctx, func_obj, name_atom, + b->defined_arg_count); + + if (b->func_kind & JS_FUNC_GENERATOR) { + JSValue proto; + int proto_class_id; + /* generators have a prototype field which is used as + prototype for the generator object */ + if (b->func_kind == JS_FUNC_ASYNC_GENERATOR) + proto_class_id = JS_CLASS_ASYNC_GENERATOR; + else + proto_class_id = JS_CLASS_GENERATOR; + proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]); + if (JS_IsException(proto)) + goto fail; + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto, + JS_PROP_WRITABLE); + } else if (b->has_prototype) { + /* add the 'prototype' property: delay instantiation to avoid + creating cycles for every javascript function. The prototype + object is created on the fly when first accessed */ + JS_SetConstructorBit(ctx, func_obj, true); + JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype, + JS_AUTOINIT_ID_PROTOTYPE, NULL, + JS_PROP_WRITABLE); + } + return func_obj; + fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0) + +static int js_op_define_class(JSContext *ctx, JSValue *sp, + JSAtom class_name, int class_flags, + JSVarRef **cur_var_refs, + JSStackFrame *sf, bool is_computed_name) +{ + JSValue bfunc, parent_class, proto = JS_UNDEFINED; + JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED; + JSFunctionBytecode *b; + + parent_class = sp[-2]; + bfunc = sp[-1]; + + if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) { + if (JS_IsNull(parent_class)) { + parent_proto = JS_NULL; + parent_class = js_dup(ctx->function_proto); + } else { + if (!JS_IsConstructor(ctx, parent_class)) { + JS_ThrowTypeError(ctx, "parent class must be constructor"); + goto fail; + } + parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype); + if (JS_IsException(parent_proto)) + goto fail; + if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) { + JS_ThrowTypeError(ctx, "parent prototype must be an object or null"); + goto fail; + } + } + } else { + /* parent_class is JS_UNDEFINED in this case */ + parent_proto = js_dup(ctx->class_proto[JS_CLASS_OBJECT]); + parent_class = js_dup(ctx->function_proto); + } + proto = JS_NewObjectProto(ctx, parent_proto); + if (JS_IsException(proto)) + goto fail; + + b = JS_VALUE_GET_PTR(bfunc); + assert(b->func_kind == JS_FUNC_NORMAL); + ctor = JS_NewObjectProtoClass(ctx, parent_class, + JS_CLASS_BYTECODE_FUNCTION); + if (JS_IsException(ctor)) + goto fail; + ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf); + bfunc = JS_UNDEFINED; + if (JS_IsException(ctor)) + goto fail; + js_method_set_home_object(ctx, ctor, proto); + JS_SetConstructorBit(ctx, ctor, true); + + JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length, + js_int32(b->defined_arg_count), + JS_PROP_CONFIGURABLE); + + if (is_computed_name) { + if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3], + JS_PROP_CONFIGURABLE) < 0) + goto fail; + } else { + if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0) + goto fail; + } + + /* the constructor property must be first. It can be overriden by + computed property names */ + if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, + js_dup(ctor), + JS_PROP_CONFIGURABLE | + JS_PROP_WRITABLE | JS_PROP_THROW) < 0) + goto fail; + /* set the prototype property */ + if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype, + js_dup(proto), JS_PROP_THROW) < 0) + goto fail; + + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, parent_class); + + sp[-2] = ctor; + sp[-1] = proto; + return 0; + fail: + JS_FreeValue(ctx, parent_class); + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, bfunc); + JS_FreeValue(ctx, proto); + JS_FreeValue(ctx, ctor); + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) +{ + struct list_head *el, *el1; + JSVarRef *var_ref; + + list_for_each_safe(el, el1, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + var_ref->value = js_dup(*var_ref->pvalue); + var_ref->pvalue = &var_ref->value; + /* the reference is no longer to a local variable */ + var_ref->is_detached = true; + add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + } +} + +static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int var_idx) +{ + JSValue *pvalue; + struct list_head *el, *el1; + JSVarRef *var_ref; + + pvalue = &sf->var_buf[var_idx]; + list_for_each_safe(el, el1, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + if (var_ref->pvalue == pvalue) { + var_ref->value = js_dup(*var_ref->pvalue); + var_ref->pvalue = &var_ref->value; + list_del(&var_ref->header.link); + /* the reference is no longer to a local variable */ + var_ref->is_detached = true; + add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + } + } +} + +#define JS_CALL_FLAG_COPY_ARGV (1 << 1) +#define JS_CALL_FLAG_GENERATOR (1 << 2) + +static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) +{ + JSRuntime *rt = ctx->rt; + JSCFunctionType func; + JSObject *p; + JSStackFrame sf_s, *sf = &sf_s, *prev_sf; + JSValue ret_val; + JSValueConst *arg_buf; + int arg_count, i; + JSCFunctionEnum cproto; + + p = JS_VALUE_GET_OBJ(func_obj); + cproto = p->u.cfunc.cproto; + arg_count = p->u.cfunc.length; + + /* better to always check stack overflow */ + if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count)) + return JS_ThrowStackOverflow(ctx); + + prev_sf = rt->current_stack_frame; + sf->prev_frame = prev_sf; + rt->current_stack_frame = sf; + ctx = p->u.cfunc.realm; /* change the current realm */ + + sf->is_strict_mode = false; + sf->cur_func = unsafe_unconst(func_obj); + sf->arg_count = argc; + arg_buf = argv; + + if (unlikely(argc < arg_count)) { + /* ensure that at least argc_count arguments are readable */ + arg_buf = alloca(sizeof(arg_buf[0]) * arg_count); + for(i = 0; i < argc; i++) + arg_buf[i] = argv[i]; + for(i = argc; i < arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + sf->arg_count = arg_count; + } + sf->arg_buf = (JSValue *)arg_buf; + + func = p->u.cfunc.c_function; + switch(cproto) { + case JS_CFUNC_constructor: + case JS_CFUNC_constructor_or_func: + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { + if (cproto == JS_CFUNC_constructor) { + not_a_constructor: + ret_val = JS_ThrowTypeError(ctx, "must be called with new"); + break; + } else { + this_obj = JS_UNDEFINED; + } + } + /* here this_obj is new_target */ + /* fall thru */ + case JS_CFUNC_generic: + ret_val = func.generic(ctx, this_obj, argc, arg_buf); + break; + case JS_CFUNC_constructor_magic: + case JS_CFUNC_constructor_or_func_magic: + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { + if (cproto == JS_CFUNC_constructor_magic) { + goto not_a_constructor; + } else { + this_obj = JS_UNDEFINED; + } + } + /* fall thru */ + case JS_CFUNC_generic_magic: + ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf, + p->u.cfunc.magic); + break; + case JS_CFUNC_getter: + ret_val = func.getter(ctx, this_obj); + break; + case JS_CFUNC_setter: + ret_val = func.setter(ctx, this_obj, arg_buf[0]); + break; + case JS_CFUNC_getter_magic: + ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic); + break; + case JS_CFUNC_setter_magic: + ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic); + break; + case JS_CFUNC_f_f: + { + double d1; + + if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { + ret_val = JS_EXCEPTION; + break; + } + ret_val = js_number(func.f_f(d1)); + } + break; + case JS_CFUNC_f_f_f: + { + double d1, d2; + + if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { + ret_val = JS_EXCEPTION; + break; + } + if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) { + ret_val = JS_EXCEPTION; + break; + } + ret_val = js_number(func.f_f_f(d1, d2)); + } + break; + case JS_CFUNC_iterator_next: + { + int done; + ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf, + &done, p->u.cfunc.magic); + if (!JS_IsException(ret_val) && done != 2) { + ret_val = js_create_iterator_result(ctx, ret_val, done); + } + } + break; + default: + abort(); + } + + rt->current_stack_frame = sf->prev_frame; + return ret_val; +} + +static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) +{ + JSObject *p; + JSBoundFunction *bf; + JSValueConst *arg_buf, new_target; + int arg_count, i; + + p = JS_VALUE_GET_OBJ(func_obj); + bf = p->u.bound_function; + arg_count = bf->argc + argc; + if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count)) + return JS_ThrowStackOverflow(ctx); + arg_buf = alloca(sizeof(JSValue) * arg_count); + for(i = 0; i < bf->argc; i++) { + arg_buf[i] = bf->argv[i]; + } + for(i = 0; i < argc; i++) { + arg_buf[bf->argc + i] = argv[i]; + } + if (flags & JS_CALL_FLAG_CONSTRUCTOR) { + new_target = this_obj; + if (js_same_value(ctx, func_obj, new_target)) + new_target = bf->func_obj; + return JS_CallConstructor2(ctx, bf->func_obj, new_target, + arg_count, arg_buf); + } else { + return JS_Call(ctx, bf->func_obj, bf->this_val, + arg_count, arg_buf); + } +} + +/* argument of OP_special_object */ +typedef enum { + OP_SPECIAL_OBJECT_ARGUMENTS, + OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS, + OP_SPECIAL_OBJECT_THIS_FUNC, + OP_SPECIAL_OBJECT_NEW_TARGET, + OP_SPECIAL_OBJECT_HOME_OBJECT, + OP_SPECIAL_OBJECT_VAR_OBJECT, + OP_SPECIAL_OBJECT_IMPORT_META, + OP_SPECIAL_OBJECT_NULL_PROTO, +} OPSpecialObjectEnum; + +#define FUNC_RET_AWAIT 0 +#define FUNC_RET_YIELD 1 +#define FUNC_RET_YIELD_STAR 2 + +#ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_* +static void dump_single_byte_code(JSContext *ctx, const uint8_t *pc, + JSFunctionBytecode *b, int start_pos); +static void print_func_name(JSFunctionBytecode *b); +#endif + +static bool needs_backtrace(JSValue exc) +{ + JSObject *p; + + if (JS_VALUE_GET_TAG(exc) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(exc); + if (p->class_id != JS_CLASS_ERROR) + return false; + return !find_own_property1(p, JS_ATOM_stack); +} + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, + JSValueConst this_obj, JSValueConst new_target, + int argc, JSValueConst *argv, int flags) +{ + JSRuntime *rt = caller_ctx->rt; + JSContext *ctx; + JSObject *p; + JSFunctionBytecode *b; + JSStackFrame sf_s, *sf = &sf_s; + uint8_t *pc; + int opcode, arg_allocated_size, i; + JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval; + JSVarRef **var_refs; + size_t alloca_size; + +#ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_STEP +#define DUMP_BYTECODE_OR_DONT(pc) \ + if (check_dump_flag(ctx->rt, JS_DUMP_BYTECODE_STEP)) dump_single_byte_code(ctx, pc, b, 0); +#else +#define DUMP_BYTECODE_OR_DONT(pc) +#endif + +#if !DIRECT_DISPATCH +#define SWITCH(pc) DUMP_BYTECODE_OR_DONT(pc) switch (opcode = *pc++) +#define CASE(op) case op +#define DEFAULT default +#define BREAK break +#else + __extension__ static const void * const dispatch_table[256] = { +#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id, +#define def(id, size, n_pop, n_push, f) +#include "quickjs-opcode.h" + [ OP_COUNT ... 255 ] = &&case_default + }; +#define SWITCH(pc) DUMP_BYTECODE_OR_DONT(pc) __extension__ ({ goto *dispatch_table[opcode = *pc++]; }); +#define CASE(op) case_ ## op +#define DEFAULT case_default +#define BREAK SWITCH(pc) +#endif + + if (js_poll_interrupts(caller_ctx)) + return JS_EXCEPTION; + if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { + if (flags & JS_CALL_FLAG_GENERATOR) { + JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj); + /* func_obj get contains a pointer to JSFuncAsyncState */ + /* the stack frame is already allocated */ + sf = &s->frame; + p = JS_VALUE_GET_OBJ(sf->cur_func); + b = p->u.func.function_bytecode; + ctx = b->realm; + var_refs = p->u.func.var_refs; + local_buf = arg_buf = sf->arg_buf; + var_buf = sf->var_buf; + stack_buf = sf->var_buf + b->var_count; + sp = sf->cur_sp; + sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */ + pc = sf->cur_pc; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; + if (s->throw_flag) + goto exception; + else + goto restart; + } else { + goto not_a_function; + } + } + p = JS_VALUE_GET_OBJ(func_obj); + if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { + JSClassCall *call_func; + call_func = rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeErrorNotAFunction(caller_ctx); + } + return call_func(caller_ctx, func_obj, this_obj, argc, + argv, flags); + } + b = p->u.func.function_bytecode; + + if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) { + arg_allocated_size = b->arg_count; + } else { + arg_allocated_size = 0; + } + + alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count + + b->stack_size); + if (js_check_stack_overflow(rt, alloca_size)) + return JS_ThrowStackOverflow(caller_ctx); + + sf->is_strict_mode = b->is_strict_mode; + arg_buf = (JSValue *)argv; + sf->arg_count = argc; + sf->cur_func = unsafe_unconst(func_obj); + init_list_head(&sf->var_ref_list); + var_refs = p->u.func.var_refs; + + local_buf = alloca(alloca_size); + if (unlikely(arg_allocated_size)) { + int n = min_int(argc, b->arg_count); + arg_buf = local_buf; + for(i = 0; i < n; i++) + arg_buf[i] = js_dup(argv[i]); + for(; i < b->arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + sf->arg_count = b->arg_count; + } + var_buf = local_buf + arg_allocated_size; + sf->var_buf = var_buf; + sf->arg_buf = arg_buf; + + for(i = 0; i < b->var_count; i++) + var_buf[i] = JS_UNDEFINED; + + stack_buf = var_buf + b->var_count; + sp = stack_buf; + pc = b->byte_code_buf; + /* sf->cur_pc must we set to pc before any recursive calls to JS_CallInternal. */ + sf->cur_pc = NULL; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; + ctx = b->realm; /* set the current realm */ + +#ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_STEP + if (check_dump_flag(ctx->rt, JS_DUMP_BYTECODE_STEP)) + print_func_name(b); +#endif + + restart: + for(;;) { + int call_argc; + JSValue *call_argv; + + SWITCH(pc) { + CASE(OP_push_i32): + *sp++ = js_int32(get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_push_bigint_i32): + *sp++ = __JS_NewShortBigInt(ctx, (int)get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_push_const): + *sp++ = js_dup(b->cpool[get_u32(pc)]); + pc += 4; + BREAK; + CASE(OP_push_minus1): + CASE(OP_push_0): + CASE(OP_push_1): + CASE(OP_push_2): + CASE(OP_push_3): + CASE(OP_push_4): + CASE(OP_push_5): + CASE(OP_push_6): + CASE(OP_push_7): + *sp++ = js_int32(opcode - OP_push_0); + BREAK; + CASE(OP_push_i8): + *sp++ = js_int32(get_i8(pc)); + pc += 1; + BREAK; + CASE(OP_push_i16): + *sp++ = js_int32(get_i16(pc)); + pc += 2; + BREAK; + CASE(OP_push_const8): + *sp++ = js_dup(b->cpool[*pc++]); + BREAK; + CASE(OP_fclosure8): + *sp++ = js_closure(ctx, js_dup(b->cpool[*pc++]), var_refs, sf); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + BREAK; + CASE(OP_push_empty_string): + *sp++ = js_empty_string(rt); + BREAK; + CASE(OP_get_length): + { + JSValue val; + + sf->cur_pc = pc; + val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + CASE(OP_push_atom_value): + *sp++ = JS_AtomToValue(ctx, get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_undefined): + *sp++ = JS_UNDEFINED; + BREAK; + CASE(OP_null): + *sp++ = JS_NULL; + BREAK; + CASE(OP_push_this): + /* OP_push_this is only called at the start of a function */ + { + JSValue val; + if (!b->is_strict_mode) { + uint32_t tag = JS_VALUE_GET_TAG(this_obj); + if (likely(tag == JS_TAG_OBJECT)) + goto normal_this; + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) { + val = js_dup(ctx->global_obj); + } else { + val = JS_ToObject(ctx, this_obj); + if (JS_IsException(val)) + goto exception; + } + } else { + normal_this: + val = js_dup(this_obj); + } + *sp++ = val; + } + BREAK; + CASE(OP_push_false): + *sp++ = JS_FALSE; + BREAK; + CASE(OP_push_true): + *sp++ = JS_TRUE; + BREAK; + CASE(OP_object): + *sp++ = JS_NewObject(ctx); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + BREAK; + CASE(OP_special_object): + { + int arg = *pc++; + switch(arg) { + case OP_SPECIAL_OBJECT_ARGUMENTS: + *sp++ = js_build_arguments(ctx, argc, argv); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: + *sp++ = js_build_mapped_arguments(ctx, argc, argv, + sf, min_int(argc, b->arg_count)); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_THIS_FUNC: + *sp++ = js_dup(sf->cur_func); + break; + case OP_SPECIAL_OBJECT_NEW_TARGET: + *sp++ = js_dup(new_target); + break; + case OP_SPECIAL_OBJECT_HOME_OBJECT: + { + JSObject *p1; + p1 = p->u.func.home_object; + if (unlikely(!p1)) + *sp++ = JS_UNDEFINED; + else + *sp++ = js_dup(JS_MKPTR(JS_TAG_OBJECT, p1)); + } + break; + case OP_SPECIAL_OBJECT_VAR_OBJECT: + *sp++ = JS_NewObjectProto(ctx, JS_NULL); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_IMPORT_META: + *sp++ = js_import_meta(ctx); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_NULL_PROTO: + *sp++ = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OBJECT); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + default: + abort(); + } + } + BREAK; + CASE(OP_rest): + { + int i, n, first = get_u16(pc); + pc += 2; + i = min_int(first, argc); + n = argc - i; + *sp++ = js_create_array(ctx, n, &argv[i]); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + } + BREAK; + + CASE(OP_drop): + JS_FreeValue(ctx, sp[-1]); + sp--; + BREAK; + CASE(OP_nip): + JS_FreeValue(ctx, sp[-2]); + sp[-2] = sp[-1]; + sp--; + BREAK; + CASE(OP_nip1): /* a b c -> b c */ + JS_FreeValue(ctx, sp[-3]); + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp--; + BREAK; + CASE(OP_dup): + sp[0] = js_dup(sp[-1]); + sp++; + BREAK; + CASE(OP_dup2): /* a b -> a b a b */ + sp[0] = js_dup(sp[-2]); + sp[1] = js_dup(sp[-1]); + sp += 2; + BREAK; + CASE(OP_dup3): /* a b c -> a b c a b c */ + sp[0] = js_dup(sp[-3]); + sp[1] = js_dup(sp[-2]); + sp[2] = js_dup(sp[-1]); + sp += 3; + BREAK; + CASE(OP_dup1): /* a b -> a a b */ + sp[0] = sp[-1]; + sp[-1] = js_dup(sp[-2]); + sp++; + BREAK; + CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = js_dup(sp[0]); + sp++; + BREAK; + CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = js_dup(sp[0]); + sp++; + BREAK; + CASE(OP_insert4): /* this obj prop a -> a this obj prop a */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = js_dup(sp[0]); + sp++; + BREAK; + CASE(OP_perm3): /* obj a b -> a obj b (213) */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = tmp; + } + BREAK; + CASE(OP_rot3l): /* x a b -> a b x (231) */ + { + JSValue tmp; + tmp = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot4l): /* x a b c -> a b c x */ + { + JSValue tmp; + tmp = sp[-4]; + sp[-4] = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot5l): /* x a b c d -> a b c d x */ + { + JSValue tmp; + tmp = sp[-5]; + sp[-5] = sp[-4]; + sp[-4] = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot3r): /* a b x -> x a b (312) */ + { + JSValue tmp; + tmp = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = tmp; + } + BREAK; + CASE(OP_perm4): /* obj prop a b -> a obj prop b */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = tmp; + } + BREAK; + CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = sp[-5]; + sp[-5] = tmp; + } + BREAK; + CASE(OP_swap): /* a b -> b a */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_swap2): /* a b c d -> c d a b */ + { + JSValue tmp1, tmp2; + tmp1 = sp[-4]; + tmp2 = sp[-3]; + sp[-4] = sp[-2]; + sp[-3] = sp[-1]; + sp[-2] = tmp1; + sp[-1] = tmp2; + } + BREAK; + + CASE(OP_fclosure): + { + JSValue bfunc = js_dup(b->cpool[get_u32(pc)]); + pc += 4; + *sp++ = js_closure(ctx, bfunc, var_refs, sf); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + } + BREAK; + CASE(OP_call0): + CASE(OP_call1): + CASE(OP_call2): + CASE(OP_call3): + call_argc = opcode - OP_call0; + goto has_call_argc; + CASE(OP_call): + CASE(OP_tail_call): + { + call_argc = get_u16(pc); + pc += 2; + goto has_call_argc; + has_call_argc: + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, + JS_UNDEFINED, call_argc, + vc(call_argv), 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + if (opcode == OP_tail_call) + goto done; + for(i = -1; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 1; + *sp++ = ret_val; + } + BREAK; + CASE(OP_call_constructor): + { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallConstructorInternal(ctx, call_argv[-2], + call_argv[-1], call_argc, + vc(call_argv), 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + for(i = -2; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 2; + *sp++ = ret_val; + } + BREAK; + CASE(OP_call_method): + CASE(OP_tail_call_method): + { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2], + JS_UNDEFINED, call_argc, + vc(call_argv), 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + if (opcode == OP_tail_call_method) + goto done; + for(i = -2; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 2; + *sp++ = ret_val; + } + BREAK; + CASE(OP_array_from): + { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + ret_val = JS_NewArrayFrom(ctx, call_argc, call_argv); + sp -= call_argc; + if (unlikely(JS_IsException(ret_val))) + goto exception; + *sp++ = ret_val; + } + BREAK; + + CASE(OP_apply): + { + int magic; + magic = get_u16(pc); + pc += 2; + sf->cur_pc = pc; + + ret_val = js_function_apply(ctx, sp[-3], 2, vc(&sp[-2]), magic); + if (unlikely(JS_IsException(ret_val))) + goto exception; + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 3; + *sp++ = ret_val; + } + BREAK; + CASE(OP_return): + ret_val = *--sp; + goto done; + CASE(OP_return_undef): + ret_val = JS_UNDEFINED; + goto done; + + CASE(OP_check_ctor_return): + /* return true if 'this' should be returned */ + if (!JS_IsObject(sp[-1])) { + if (!JS_IsUndefined(sp[-1])) { + JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined"); + goto exception; + } + sp[0] = JS_TRUE; + } else { + sp[0] = JS_FALSE; + } + sp++; + BREAK; + CASE(OP_check_ctor): + if (JS_IsUndefined(new_target)) { + non_ctor_call: + JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); + goto exception; + } + BREAK; + CASE(OP_init_ctor): + { + JSValue super, ret; + sf->cur_pc = pc; + if (JS_IsUndefined(new_target)) + goto non_ctor_call; + super = JS_GetPrototype(ctx, func_obj); + if (JS_IsException(super)) + goto exception; + ret = JS_CallConstructor2(ctx, super, new_target, argc, argv); + JS_FreeValue(ctx, super); + if (JS_IsException(ret)) + goto exception; + *sp++ = ret; + } + BREAK; + CASE(OP_check_brand): + { + int ret = JS_CheckBrand(ctx, sp[-2], sp[-1]); + if (ret < 0) + goto exception; + if (!ret) { + JS_ThrowTypeError(ctx, "invalid brand on object"); + goto exception; + } + } + BREAK; + CASE(OP_add_brand): + if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + BREAK; + + CASE(OP_throw): + JS_Throw(ctx, *--sp); + goto exception; + + CASE(OP_throw_error): +#define JS_THROW_VAR_RO 0 +#define JS_THROW_VAR_REDECL 1 +#define JS_THROW_VAR_UNINITIALIZED 2 +#define JS_THROW_ERROR_DELETE_SUPER 3 +#define JS_THROW_ERROR_ITERATOR_THROW 4 + { + JSAtom atom; + int type; + atom = get_u32(pc); + type = pc[4]; + pc += 5; + if (type == JS_THROW_VAR_RO) + JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom); + else + if (type == JS_THROW_VAR_REDECL) + JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom); + else + if (type == JS_THROW_VAR_UNINITIALIZED) + JS_ThrowReferenceErrorUninitialized(ctx, atom); + else + if (type == JS_THROW_ERROR_DELETE_SUPER) + JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); + else + if (type == JS_THROW_ERROR_ITERATOR_THROW) + JS_ThrowTypeError(ctx, "iterator does not have a throw method"); + else + JS_ThrowInternalError(ctx, "invalid throw var type %d", type); + } + goto exception; + + CASE(OP_eval): + { + JSValue obj; + int scope_idx; + call_argc = get_u16(pc); + scope_idx = get_u16(pc + 2) - 1; + pc += 4; + call_argv = sp - call_argc; + sf->cur_pc = pc; + if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) { + if (call_argc >= 1) + obj = call_argv[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, + JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, + JS_UNDEFINED, call_argc, + vc(call_argv), 0); + } + if (unlikely(JS_IsException(ret_val))) + goto exception; + for(i = -1; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 1; + *sp++ = ret_val; + } + BREAK; + /* could merge with OP_apply */ + CASE(OP_apply_eval): + { + int scope_idx; + uint32_t len; + JSValue *tab; + JSValue obj; + + scope_idx = get_u16(pc) - 1; + pc += 2; + sf->cur_pc = pc; + tab = build_arg_list(ctx, &len, sp[-1]); + if (!tab) + goto exception; + if (js_same_value(ctx, sp[-2], ctx->eval_obj)) { + if (len >= 1) + obj = tab[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, + JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, vc(tab)); + } + free_arg_list(ctx, tab, len); + if (unlikely(JS_IsException(ret_val))) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + *sp++ = ret_val; + } + BREAK; + + CASE(OP_regexp): + { + sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED, + sp[-2], sp[-1]); + sp--; + } + BREAK; + + CASE(OP_get_super): + { + JSValue proto; + proto = JS_GetPrototype(ctx, sp[-1]); + if (JS_IsException(proto)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = proto; + } + BREAK; + + CASE(OP_import): + { + JSValue val; + sf->cur_pc = pc; + val = js_dynamic_import(ctx, sp[-1]); + if (JS_IsException(val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + + CASE(OP_check_var): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_CheckGlobalVar(ctx, atom); + if (ret < 0) + goto exception; + *sp++ = js_bool(ret); + } + BREAK; + + CASE(OP_get_var_undef): + CASE(OP_get_var): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + + val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_put_var): + CASE(OP_put_var_init): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + + ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var); + sp--; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_var_strict): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + + /* sp[-2] is JS_TRUE or JS_FALSE */ + if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + goto exception; + } + ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_check_define_var): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_CheckDefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_var): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_func): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp--; + } + BREAK; + + CASE(OP_get_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = js_dup(var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], js_dup(sp[-1])); + } + BREAK; + CASE(OP_get_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = js_dup(arg_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], js_dup(sp[-1])); + } + BREAK; + + CASE(OP_get_loc8): *sp++ = js_dup(var_buf[*pc++]); BREAK; + CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK; + CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], js_dup(sp[-1])); BREAK; + + // Observation: get_loc0 and get_loc1 are individually very + // frequent opcodes _and_ they are very often paired together, + // making them ideal candidates for opcode fusion. + CASE(OP_get_loc0_loc1): + *sp++ = js_dup(var_buf[0]); + *sp++ = js_dup(var_buf[1]); + BREAK; + + CASE(OP_get_loc0): *sp++ = js_dup(var_buf[0]); BREAK; + CASE(OP_get_loc1): *sp++ = js_dup(var_buf[1]); BREAK; + CASE(OP_get_loc2): *sp++ = js_dup(var_buf[2]); BREAK; + CASE(OP_get_loc3): *sp++ = js_dup(var_buf[3]); BREAK; + CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK; + CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK; + CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK; + CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK; + CASE(OP_set_loc0): set_value(ctx, &var_buf[0], js_dup(sp[-1])); BREAK; + CASE(OP_set_loc1): set_value(ctx, &var_buf[1], js_dup(sp[-1])); BREAK; + CASE(OP_set_loc2): set_value(ctx, &var_buf[2], js_dup(sp[-1])); BREAK; + CASE(OP_set_loc3): set_value(ctx, &var_buf[3], js_dup(sp[-1])); BREAK; + CASE(OP_get_arg0): *sp++ = js_dup(arg_buf[0]); BREAK; + CASE(OP_get_arg1): *sp++ = js_dup(arg_buf[1]); BREAK; + CASE(OP_get_arg2): *sp++ = js_dup(arg_buf[2]); BREAK; + CASE(OP_get_arg3): *sp++ = js_dup(arg_buf[3]); BREAK; + CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK; + CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK; + CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK; + CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK; + CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], js_dup(sp[-1])); BREAK; + CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], js_dup(sp[-1])); BREAK; + CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], js_dup(sp[-1])); BREAK; + CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], js_dup(sp[-1])); BREAK; + CASE(OP_get_var_ref0): *sp++ = js_dup(*var_refs[0]->pvalue); BREAK; + CASE(OP_get_var_ref1): *sp++ = js_dup(*var_refs[1]->pvalue); BREAK; + CASE(OP_get_var_ref2): *sp++ = js_dup(*var_refs[2]->pvalue); BREAK; + CASE(OP_get_var_ref3): *sp++ = js_dup(*var_refs[3]->pvalue); BREAK; + CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK; + CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, js_dup(sp[-1])); BREAK; + CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, js_dup(sp[-1])); BREAK; + CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, js_dup(sp[-1])); BREAK; + CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, js_dup(sp[-1])); BREAK; + + CASE(OP_get_var_ref): + { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + sp[0] = js_dup(val); + sp++; + } + BREAK; + CASE(OP_put_var_ref): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_var_ref): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, js_dup(sp[-1])); + } + BREAK; + CASE(OP_get_var_ref_check): + { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + if (unlikely(JS_IsUninitialized(val))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, true); + goto exception; + } + sp[0] = js_dup(val); + sp++; + } + BREAK; + CASE(OP_put_var_ref_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, true); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_var_ref_check_init): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, true); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc_uninitialized): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], JS_UNINITIALIZED); + } + BREAK; + CASE(OP_get_loc_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, + false); + goto exception; + } + sp[0] = js_dup(var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, + false); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_loc_check_init): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(!JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceError(caller_ctx, + "'this' can be initialized only once"); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_close_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + close_lexical_var(ctx, sf, idx); + } + BREAK; + + CASE(OP_make_loc_ref): + CASE(OP_make_arg_ref): + CASE(OP_make_var_ref_ref): + { + JSVarRef *var_ref; + JSProperty *pr; + JSAtom atom; + int idx; + atom = get_u32(pc); + idx = get_u16(pc + 4); + pc += 6; + *sp++ = JS_NewObjectProto(ctx, JS_NULL); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + if (opcode == OP_make_var_ref_ref) { + var_ref = var_refs[idx]; + var_ref->header.ref_count++; + } else { + var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref); + if (!var_ref) + goto exception; + } + pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom, + JS_PROP_WRITABLE | JS_PROP_VARREF); + if (!pr) { + free_var_ref(rt, var_ref); + goto exception; + } + pr->u.var_ref = var_ref; + *sp++ = JS_AtomToValue(ctx, atom); + } + BREAK; + CASE(OP_make_var_ref): + { + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + if (JS_GetGlobalVarRef(ctx, atom, sp)) + goto exception; + sp += 2; + } + BREAK; + + CASE(OP_goto): + pc += (int32_t)get_u32(pc); + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_goto16): + pc += (int16_t)get_u16(pc); + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_goto8): + pc += (int8_t)pc[0]; + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_if_true): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_true8): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int8_t)pc[-1] - 1; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false8): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int8_t)pc[-1] - 1; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_catch): + { + int32_t diff; + diff = get_u32(pc); + sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf); + sp++; + pc += 4; + } + BREAK; + CASE(OP_gosub): + { + int32_t diff; + diff = get_u32(pc); + /* XXX: should have a different tag to avoid security flaw */ + sp[0] = js_int32(pc + 4 - b->byte_code_buf); + sp++; + pc += diff; + } + BREAK; + CASE(OP_ret): + { + JSValue op1; + uint32_t pos; + op1 = sp[-1]; + if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT)) + goto ret_fail; + pos = JS_VALUE_GET_INT(op1); + if (unlikely(pos >= b->byte_code_len)) { + ret_fail: + JS_ThrowInternalError(ctx, "invalid ret value"); + goto exception; + } + sp--; + pc = b->byte_code_buf + pos; + } + BREAK; + + CASE(OP_for_in_start): + sf->cur_pc = pc; + if (js_for_in_start(ctx, sp)) + goto exception; + BREAK; + CASE(OP_for_in_next): + sf->cur_pc = pc; + if (js_for_in_next(ctx, sp)) + goto exception; + sp += 2; + BREAK; + CASE(OP_for_of_start): + sf->cur_pc = pc; + if (js_for_of_start(ctx, sp, false)) + goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_for_of_next): + { + int offset = -3 - pc[0]; + pc += 1; + sf->cur_pc = pc; + if (js_for_of_next(ctx, sp, offset)) + goto exception; + sp += 2; + } + BREAK; + CASE(OP_for_await_of_start): + sf->cur_pc = pc; + if (js_for_of_start(ctx, sp, true)) + goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_iterator_get_value_done): + sf->cur_pc = pc; + if (js_iterator_get_value_done(ctx, sp)) + goto exception; + sp += 1; + BREAK; + CASE(OP_iterator_check_object): + if (unlikely(!JS_IsObject(sp[-1]))) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto exception; + } + BREAK; + + CASE(OP_iterator_close): + /* iter_obj next catch_offset -> */ + sp--; /* drop the catch offset to avoid getting caught by exception */ + JS_FreeValue(ctx, sp[-1]); /* drop the next method */ + sp--; + if (!JS_IsUndefined(sp[-1])) { + sf->cur_pc = pc; + if (JS_IteratorClose(ctx, sp[-1], false)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + } + sp--; + BREAK; + CASE(OP_nip_catch): + { + JSValue ret_val; + /* catch_offset ... ret_val -> ret_eval */ + ret_val = *--sp; + while (sp > stack_buf && + JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) { + JS_FreeValue(ctx, *--sp); + } + if (unlikely(sp == stack_buf)) { + JS_ThrowInternalError(ctx, "nip_catch"); + JS_FreeValue(ctx, ret_val); + goto exception; + } + sp[-1] = ret_val; + } + BREAK; + + CASE(OP_iterator_next): + /* stack: iter_obj next catch_offset val */ + { + JSValue ret; + sf->cur_pc = pc; + ret = JS_Call(ctx, sp[-3], sp[-4], 1, vc(sp - 1)); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + } + BREAK; + + CASE(OP_iterator_call): + /* stack: iter_obj next catch_offset val */ + { + JSValue method, ret; + bool ret_flag; + int flags; + flags = *pc++; + sf->cur_pc = pc; + method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? + JS_ATOM_throw : JS_ATOM_return); + if (JS_IsException(method)) + goto exception; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + ret_flag = true; + } else { + if (flags & 2) { + /* no argument */ + ret = JS_CallFree(ctx, method, sp[-4], + 0, NULL); + } else { + ret = JS_CallFree(ctx, method, sp[-4], + 1, vc(sp - 1)); + } + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + ret_flag = false; + } + sp[0] = js_bool(ret_flag); + sp += 1; + } + BREAK; + + CASE(OP_lnot): + { + int res; + JSValue op1; + + op1 = sp[-1]; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1) != 0; + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp[-1] = js_bool(!res); + } + BREAK; + + CASE(OP_get_field): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + val = JS_GetPropertyInternal(ctx, sp[-1], atom, sp[-1], false); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + + CASE(OP_get_field2): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + val = JS_GetPropertyInternal(ctx, sp[-1], atom, sp[-1], false); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_put_field): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + ret = JS_SetPropertyInternal2(ctx, + sp[-2], atom, + sp[-1], sp[-2], + JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_private_symbol): + { + JSAtom atom; + JSValue val; + + atom = get_u32(pc); + pc += 4; + val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(val)) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_get_private_field): + { + JSValue val; + sf->cur_pc = pc; + val = JS_GetPrivateField(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_put_private_field): + { + int ret; + sf->cur_pc = pc; + ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-1]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_private_field): + { + int ret; + ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_field): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1], + JS_PROP_C_W_E | JS_PROP_THROW); + sp--; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_set_name): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE); + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_name_computed): + { + int ret; + ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE); + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_proto): + { + JSValue proto; + proto = sp[-1]; + if (JS_IsObject(proto) || JS_IsNull(proto)) { + if (JS_SetPrototypeInternal(ctx, sp[-2], proto, true) < 0) + goto exception; + } + JS_FreeValue(ctx, proto); + sp--; + } + BREAK; + CASE(OP_set_home_object): + js_method_set_home_object(ctx, sp[-1], sp[-2]); + BREAK; + CASE(OP_define_method): + CASE(OP_define_method_computed): + { + JSValue getter, setter, value; + JSValue obj; + JSAtom atom; + int flags, ret, op_flags; + bool is_computed; +#define OP_DEFINE_METHOD_METHOD 0 +#define OP_DEFINE_METHOD_GETTER 1 +#define OP_DEFINE_METHOD_SETTER 2 +#define OP_DEFINE_METHOD_ENUMERABLE 4 + + is_computed = (opcode == OP_define_method_computed); + if (is_computed) { + atom = JS_ValueToAtom(ctx, sp[-2]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + opcode += OP_define_method - OP_define_method_computed; + } else { + atom = get_u32(pc); + pc += 4; + } + op_flags = *pc++; + + obj = sp[-2 - is_computed]; + flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE | + JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW; + if (op_flags & OP_DEFINE_METHOD_ENUMERABLE) + flags |= JS_PROP_ENUMERABLE; + op_flags &= 3; + value = JS_UNDEFINED; + getter = JS_UNDEFINED; + setter = JS_UNDEFINED; + if (op_flags == OP_DEFINE_METHOD_METHOD) { + value = sp[-1]; + flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE; + } else if (op_flags == OP_DEFINE_METHOD_GETTER) { + getter = sp[-1]; + flags |= JS_PROP_HAS_GET; + } else { + setter = sp[-1]; + flags |= JS_PROP_HAS_SET; + } + ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj); + if (ret >= 0) { + ret = JS_DefineProperty(ctx, obj, atom, value, + getter, setter, flags); + } + JS_FreeValue(ctx, sp[-1]); + if (is_computed) { + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-2]); + } + sp -= 1 + is_computed; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_class): + CASE(OP_define_class_computed): + { + int class_flags; + JSAtom atom; + + atom = get_u32(pc); + class_flags = pc[4]; + pc += 5; + if (js_op_define_class(ctx, sp, atom, class_flags, + var_refs, sf, + (opcode == OP_define_class_computed)) < 0) + goto exception; + } + BREAK; + + CASE(OP_get_array_el): + { + JSValue val; + + sf->cur_pc = pc; + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_get_array_el2): + { + JSValue val; + + sf->cur_pc = pc; + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + sp[-1] = val; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_get_ref_value): + { + JSValue val; + sf->cur_pc = pc; + if (unlikely(JS_IsUndefined(sp[-2]))) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-1]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } + val = JS_GetPropertyValue(ctx, sp[-2], + js_dup(sp[-1])); + if (unlikely(JS_IsException(val))) + goto exception; + sp[0] = val; + sp++; + } + BREAK; + + CASE(OP_get_super_value): + { + JSValue val; + JSAtom atom; + sf->cur_pc = pc; + atom = JS_ValueToAtom(ctx, sp[-1]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], false); + JS_FreeAtom(ctx, atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-3]); + sp[-3] = val; + sp -= 2; + } + BREAK; + + CASE(OP_put_array_el): + { + int ret; + sf->cur_pc = pc; + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_ref_value): + { + int ret, flags; + sf->cur_pc = pc; + flags = JS_PROP_THROW_STRICT; + if (unlikely(JS_IsUndefined(sp[-3]))) { + if (is_strict_mode(ctx)) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } else { + sp[-3] = js_dup(ctx->global_obj); + } + } else { + if (is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD; + } + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_super_value): + { + int ret; + JSAtom atom; + sf->cur_pc = pc; + if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto exception; + } + atom = JS_ValueToAtom(ctx, sp[-2]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + ret = JS_SetPropertyInternal2(ctx, + sp[-3], atom, + sp[-1], sp[-4], + JS_PROP_THROW_STRICT); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-4]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + sp -= 4; + if (ret < 0) + goto exception; + } + BREAK; + + CASE(OP_define_array_el): + { + int ret; + ret = JS_DefinePropertyValueValue(ctx, sp[-3], js_dup(sp[-2]), sp[-1], + JS_PROP_C_W_E | JS_PROP_THROW); + sp -= 1; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_append): /* array pos enumobj -- array pos */ + { + sf->cur_pc = pc; + if (js_append_enumerate(ctx, sp)) + goto exception; + JS_FreeValue(ctx, *--sp); + } + BREAK; + + CASE(OP_copy_data_properties): /* target source excludeList */ + { + /* stack offsets (-1 based): + 2 bits for target, + 3 bits for source, + 2 bits for exclusionList */ + int mask; + + mask = *pc++; + sf->cur_pc = pc; + if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)], + sp[-1 - ((mask >> 2) & 7)], + sp[-1 - ((mask >> 5) & 7)], 0)) + goto exception; + } + BREAK; + + CASE(OP_add): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2); + if (unlikely((int)r != r)) + goto add_slow; + sp[-2] = js_int32(r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) + + JS_VALUE_GET_FLOAT64(op2)); + JS_X87_FPCW_RESTORE(fpcw); + sp--; + } else { + add_slow: + sf->cur_pc = pc; + if (js_add_slow(ctx, sp)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_add_loc): + { + JSValue *pv; + int idx; + idx = *pc; + pc += 1; + + pv = &var_buf[idx]; + if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(*pv) + + JS_VALUE_GET_INT(sp[-1]); + if (unlikely((int)r != r)) + goto add_loc_slow; + *pv = js_int32(r); + sp--; + } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { + JSValue op1; + op1 = sp[-1]; + sp--; + sf->cur_pc = pc; + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) + goto exception; + op1 = JS_ConcatString(ctx, js_dup(*pv), op1); + if (JS_IsException(op1)) + goto exception; + set_value(ctx, pv, op1); + } else { + JSValue ops[2]; + add_loc_slow: + /* In case of exception, js_add_slow frees ops[0] + and ops[1], so we must duplicate *pv */ + sf->cur_pc = pc; + ops[0] = js_dup(*pv); + ops[1] = sp[-1]; + sp--; + if (js_add_slow(ctx, ops + 2)) + goto exception; + set_value(ctx, pv, ops[0]); + } + } + BREAK; + CASE(OP_sub): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2); + if (unlikely((int)r != r)) + goto binary_arith_slow; + sp[-2] = js_int32(r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) - + JS_VALUE_GET_FLOAT64(op2)); + JS_X87_FPCW_RESTORE(fpcw); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mul): + { + JSValue op1, op2; + double d; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int32_t v1, v2; + int64_t r; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + r = (int64_t)v1 * v2; + if (unlikely((int)r != r)) { + d = (double)r; + goto mul_fp_res; + } + /* need to test zero case for -0 result */ + if (unlikely(r == 0 && (v1 | v2) < 0)) { + d = -0.0; + goto mul_fp_res; + } + sp[-2] = js_int32(r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); + JS_X87_FPCW_RESTORE(fpcw); + mul_fp_res: + sp[-2] = js_float64(d); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_div): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + sp[-2] = js_number((double)v1 / (double)v2); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mod): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2, r; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + /* We must avoid v2 = 0, v1 = INT32_MIN and v2 = + -1 and the cases where the result is -0. */ + if (unlikely(v1 < 0 || v2 <= 0)) + goto binary_arith_slow; + r = v1 % v2; + sp[-2] = js_int32(r); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_pow): + binary_arith_slow: + sf->cur_pc = pc; + if (js_binary_arith_slow(ctx, sp, opcode)) + goto exception; + sp--; + BREAK; + + CASE(OP_plus): + { + JSValue op1; + uint32_t tag; + op1 = sp[-1]; + tag = JS_VALUE_GET_TAG(op1); + if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) { + } else { + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_neg): + { + JSValue op1; + uint32_t tag; + int val; + double d; + op1 = sp[-1]; + tag = JS_VALUE_GET_TAG(op1); + if (tag == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + /* Note: -0 cannot be expressed as integer */ + if (unlikely(val == 0)) { + d = -0.0; + goto neg_fp_res; + } + if (unlikely(val == INT32_MIN)) { + d = -(double)val; + goto neg_fp_res; + } + sp[-1] = js_int32(-val); + } else if (JS_TAG_IS_FLOAT64(tag)) { + d = -JS_VALUE_GET_FLOAT64(op1); + neg_fp_res: + sp[-1] = js_float64(d); + } else { + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_inc): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MAX)) + goto inc_slow; + sp[-1] = js_int32(val + 1); + } else { + inc_slow: + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_dec): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MIN)) + goto dec_slow; + sp[-1] = js_int32(val - 1); + } else { + dec_slow: + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_post_inc): + CASE(OP_post_dec): + sf->cur_pc = pc; + if (js_post_inc_slow(ctx, sp, opcode)) + goto exception; + sp++; + BREAK; + CASE(OP_inc_loc): + { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MAX)) + goto inc_loc_slow; + var_buf[idx] = js_int32(val + 1); + } else { + inc_loc_slow: + sf->cur_pc = pc; + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = js_dup(op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc)) + goto exception; + set_value(ctx, &var_buf[idx], op1); + } + } + BREAK; + CASE(OP_dec_loc): + { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MIN)) + goto dec_loc_slow; + var_buf[idx] = js_int32(val - 1); + } else { + dec_loc_slow: + sf->cur_pc = pc; + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = js_dup(op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec)) + goto exception; + set_value(ctx, &var_buf[idx], op1); + } + } + BREAK; + CASE(OP_not): + { + JSValue op1; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + sp[-1] = js_int32(~JS_VALUE_GET_INT(op1)); + } else { + sf->cur_pc = pc; + if (js_not_slow(ctx, sp)) + goto exception; + } + } + BREAK; + + CASE(OP_shl): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v1, v2; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2) & 0x1f; + sp[-2] = js_int32(v1 << v2); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_shr): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v2; + v2 = JS_VALUE_GET_INT(op2); + v2 &= 0x1f; + sp[-2] = js_uint32((uint32_t)JS_VALUE_GET_INT(op1) >> v2); + sp--; + } else { + sf->cur_pc = pc; + if (js_shr_slow(ctx, sp)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_sar): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v2; + v2 = JS_VALUE_GET_INT(op2); + if (unlikely(v2 > 0x1f)) { + v2 &= 0x1f; + } + sp[-2] = js_int32((int)JS_VALUE_GET_INT(op1) >> v2); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_and): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = js_int32(JS_VALUE_GET_INT(op1) & JS_VALUE_GET_INT(op2)); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_or): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = js_int32(JS_VALUE_GET_INT(op1) | JS_VALUE_GET_INT(op2)); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_xor): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = js_int32(JS_VALUE_GET_INT(op1) ^ JS_VALUE_GET_INT(op2)); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + + +#define OP_CMP(opcode, binary_op, slow_call) \ + CASE(opcode): \ + { \ + JSValue op1, op2; \ + op1 = sp[-2]; \ + op2 = sp[-1]; \ + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \ + sp[-2] = js_bool(JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \ + sp--; \ + } else { \ + sf->cur_pc = pc; \ + if (slow_call) \ + goto exception; \ + sp--; \ + } \ + } \ + BREAK + + OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0)); + OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1)); + OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0)); + OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1)); + + CASE(OP_in): + sf->cur_pc = pc; + if (js_operator_in(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_private_in): + if (js_operator_private_in(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_instanceof): + sf->cur_pc = pc; + if (js_operator_instanceof(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_typeof): + { + JSValue op1; + JSAtom atom; + + op1 = sp[-1]; + atom = js_operator_typeof(ctx, op1); + JS_FreeValue(ctx, op1); + sp[-1] = JS_AtomToString(ctx, atom); + } + BREAK; + CASE(OP_delete): + sf->cur_pc = pc; + if (js_operator_delete(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_delete_var): + { + JSAtom atom; + int ret; + + atom = get_u32(pc); + pc += 4; + + sf->cur_pc = pc; + ret = JS_DeleteGlobalVar(ctx, atom); + if (unlikely(ret < 0)) + goto exception; + *sp++ = js_bool(ret); + } + BREAK; + + CASE(OP_to_object): + if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) { + sf->cur_pc = pc; + ret_val = JS_ToObject(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + } + BREAK; + + CASE(OP_to_propkey): + switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + sf->cur_pc = pc; + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; + + CASE(OP_to_propkey2): + /* must be tested first */ + if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { + JS_ThrowTypeError(ctx, "value has no property"); + goto exception; + } + switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + sf->cur_pc = pc; + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; + CASE(OP_with_get_var): + CASE(OP_with_put_var): + CASE(OP_with_delete_var): + CASE(OP_with_make_ref): + CASE(OP_with_get_ref): + CASE(OP_with_get_ref_undef): + { + JSAtom atom; + int32_t diff; + JSValue obj, val; + int ret, is_with; + atom = get_u32(pc); + diff = get_u32(pc + 4); + is_with = pc[8]; + pc += 9; + sf->cur_pc = pc; + + obj = sp[-1]; + ret = JS_HasProperty(ctx, obj, atom); + if (unlikely(ret < 0)) + goto exception; + if (ret) { + if (is_with) { + ret = js_has_unscopable(ctx, obj, atom); + if (unlikely(ret < 0)) + goto exception; + if (ret) + goto no_with; + } + switch (opcode) { + case OP_with_get_var: + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + set_value(ctx, &sp[-1], val); + break; + case OP_with_put_var: + /* XXX: check if strict mode */ + ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], + JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + break; + case OP_with_delete_var: + ret = JS_DeleteProperty(ctx, obj, atom, 0); + if (unlikely(ret < 0)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = js_bool(ret); + break; + case OP_with_make_ref: + /* produce a pair object/propname on the stack */ + *sp++ = JS_AtomToValue(ctx, atom); + break; + case OP_with_get_ref: + /* produce a pair object/method on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + break; + case OP_with_get_ref_undef: + /* produce a pair undefined/function on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_UNDEFINED; + *sp++ = val; + break; + } + pc += diff - 5; + } else { + no_with: + /* if not jumping, drop the object argument */ + JS_FreeValue(ctx, sp[-1]); + sp--; + } + } + BREAK; + + CASE(OP_await): + ret_val = js_int32(FUNC_RET_AWAIT); + goto done_generator; + CASE(OP_yield): + ret_val = js_int32(FUNC_RET_YIELD); + goto done_generator; + CASE(OP_yield_star): + CASE(OP_async_yield_star): + ret_val = js_int32(FUNC_RET_YIELD_STAR); + goto done_generator; + CASE(OP_return_async): + CASE(OP_initial_yield): + ret_val = JS_UNDEFINED; + goto done_generator; + + CASE(OP_nop): + BREAK; + CASE(OP_is_undefined_or_null): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED || + JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } else { + goto free_and_set_false; + } + CASE(OP_is_undefined): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) { + goto set_true; + } else { + goto free_and_set_false; + } + CASE(OP_is_null): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } else { + goto free_and_set_false; + } + /* XXX: could merge to a single opcode */ + CASE(OP_typeof_is_undefined): + /* different from OP_is_undefined because of isHTMLDDA */ + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) { + goto free_and_set_true; + } else { + goto free_and_set_false; + } + CASE(OP_typeof_is_function): + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { + goto free_and_set_true; + } else { + goto free_and_set_false; + } + free_and_set_true: + JS_FreeValue(ctx, sp[-1]); + set_true: + sp[-1] = JS_TRUE; + BREAK; + free_and_set_false: + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_FALSE; + BREAK; + CASE(OP_invalid): + DEFAULT: + JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", + (int)(pc - b->byte_code_buf - 1), opcode); + goto exception; + } + } + exception: + if (needs_backtrace(rt->current_exception) + || JS_IsUndefined(ctx->error_back_trace)) { + sf->cur_pc = pc; + build_backtrace(ctx, rt->current_exception, JS_UNDEFINED, + NULL, 0, 0, 0); + } + if (!JS_IsUncatchableError(rt->current_exception)) { + while (sp > stack_buf) { + JSValue val = *--sp; + JS_FreeValue(ctx, val); + if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) { + int pos = JS_VALUE_GET_INT(val); + if (pos == 0) { + /* enumerator: close it with a throw */ + JS_FreeValue(ctx, sp[-1]); /* drop the next method */ + sp--; + JS_IteratorClose(ctx, sp[-1], true); + } else { + *sp++ = rt->current_exception; + rt->current_exception = JS_UNINITIALIZED; + JS_FreeValueRT(rt, ctx->error_back_trace); + ctx->error_back_trace = JS_UNDEFINED; + pc = b->byte_code_buf + pos; + goto restart; + } + } + } + } + ret_val = JS_EXCEPTION; + /* the local variables are freed by the caller in the generator + case. Hence the label 'done' should never be reached in a + generator function. */ + if (b->func_kind != JS_FUNC_NORMAL) { + done_generator: + sf->cur_pc = pc; + sf->cur_sp = sp; + } else { + done: + if (unlikely(!list_empty(&sf->var_ref_list))) { + /* variable references reference the stack: must close them */ + close_var_refs(rt, sf); + } + /* free the local variables and stack */ + for(pval = local_buf; pval < sp; pval++) { + JS_FreeValue(ctx, *pval); + } + } + rt->current_stack_frame = sf->prev_frame; + return ret_val; +} + +JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, + argc, argv, JS_CALL_FLAG_COPY_ARGV); +} + +static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, + argc, argv, JS_CALL_FLAG_COPY_ARGV); + JS_FreeValue(ctx, func_obj); + return res; +} + +/* warning: the refcount of the context is not incremented. Return + NULL in case of exception (case of revoked proxy only) */ +static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj) +{ + JSObject *p; + JSContext *realm; + + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return ctx; + p = JS_VALUE_GET_OBJ(func_obj); + switch(p->class_id) { + case JS_CLASS_C_FUNCTION: + realm = p->u.cfunc.realm; + break; + case JS_CLASS_BYTECODE_FUNCTION: + case JS_CLASS_GENERATOR_FUNCTION: + case JS_CLASS_ASYNC_FUNCTION: + case JS_CLASS_ASYNC_GENERATOR_FUNCTION: + { + JSFunctionBytecode *b; + b = p->u.func.function_bytecode; + realm = b->realm; + } + break; + case JS_CLASS_PROXY: + { + JSProxyData *s = p->u.opaque; + if (!s) + return ctx; + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + return NULL; + } else { + realm = JS_GetFunctionRealm(ctx, s->target); + } + } + break; + case JS_CLASS_BOUND_FUNCTION: + { + JSBoundFunction *bf = p->u.bound_function; + realm = JS_GetFunctionRealm(ctx, bf->func_obj); + } + break; + default: + realm = ctx; + break; + } + return realm; +} + +static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor, + int class_id) +{ + JSValue proto, obj; + JSContext *realm; + + if (JS_IsUndefined(ctor)) { + proto = js_dup(ctx->class_proto[class_id]); + } else { + proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); + if (JS_IsException(proto)) + return proto; + if (!JS_IsObject(proto)) { + JS_FreeValue(ctx, proto); + realm = JS_GetFunctionRealm(ctx, ctor); + if (!realm) + return JS_EXCEPTION; + proto = js_dup(realm->class_proto[class_id]); + } + } + obj = JS_NewObjectProtoClass(ctx, proto, class_id); + JS_FreeValue(ctx, proto); + return obj; +} + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +static JSValue JS_CallConstructorInternal(JSContext *ctx, + JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv, + int flags) +{ + JSObject *p; + JSFunctionBytecode *b; + + if (js_poll_interrupts(ctx)) + return JS_EXCEPTION; + flags |= JS_CALL_FLAG_CONSTRUCTOR; + if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) + goto not_a_function; + p = JS_VALUE_GET_OBJ(func_obj); + if (unlikely(!p->is_constructor)) + return JS_ThrowTypeErrorNotAConstructor(ctx, func_obj); + if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { + JSClassCall *call_func; + call_func = ctx->rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeErrorNotAFunction(ctx); + } + return call_func(ctx, func_obj, new_target, argc, + argv, flags); + } + + b = p->u.func.function_bytecode; + if (b->is_derived_class_constructor) { + return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags); + } else { + JSValue obj, ret; + /* legacy constructor behavior */ + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); + if (JS_IsException(obj)) + return JS_EXCEPTION; + ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags); + if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT || + JS_IsException(ret)) { + JS_FreeValue(ctx, obj); + return ret; + } else { + JS_FreeValue(ctx, ret); + return obj; + } + } +} + +JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + return JS_CallConstructorInternal(ctx, func_obj, new_target, + argc, argv, + JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, + int argc, JSValueConst *argv) +{ + return JS_CallConstructorInternal(ctx, func_obj, func_obj, + argc, argv, + JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, + int argc, JSValueConst *argv) +{ + JSValue func_obj; + func_obj = JS_GetProperty(ctx, this_val, atom); + if (JS_IsException(func_obj)) + return func_obj; + return JS_CallFree(ctx, func_obj, this_val, argc, argv); +} + +static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValueConst *argv) +{ + JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv); + JS_FreeValue(ctx, this_val); + return res; +} + +/* JSAsyncFunctionState (used by generator and async functions) */ +static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + JSObject *p; + JSFunctionBytecode *b; + JSStackFrame *sf; + int local_count, i, arg_buf_len, n; + + sf = &s->frame; + init_list_head(&sf->var_ref_list); + p = JS_VALUE_GET_OBJ(func_obj); + b = p->u.func.function_bytecode; + sf->is_strict_mode = b->is_strict_mode; + sf->cur_pc = b->byte_code_buf; + arg_buf_len = max_int(b->arg_count, argc); + local_count = arg_buf_len + b->var_count + b->stack_size; + sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1)); + if (!sf->arg_buf) + return -1; + sf->cur_func = js_dup(func_obj); + s->this_val = js_dup(this_obj); + s->argc = argc; + sf->arg_count = arg_buf_len; + sf->var_buf = sf->arg_buf + arg_buf_len; + sf->cur_sp = sf->var_buf + b->var_count; + for(i = 0; i < argc; i++) + sf->arg_buf[i] = js_dup(argv[i]); + n = arg_buf_len + b->var_count; + for(i = argc; i < n; i++) + sf->arg_buf[i] = JS_UNDEFINED; + return 0; +} + +static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, + JS_MarkFunc *mark_func) +{ + JSStackFrame *sf; + JSValue *sp; + + sf = &s->frame; + JS_MarkValue(rt, sf->cur_func, mark_func); + JS_MarkValue(rt, s->this_val, mark_func); + if (sf->cur_sp) { + /* if the function is running, cur_sp is not known so we + cannot mark the stack. Marking the variables is not needed + because a running function cannot be part of a removable + cycle */ + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) + JS_MarkValue(rt, *sp, mark_func); + } +} + +static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) +{ + JSStackFrame *sf; + JSValue *sp; + + sf = &s->frame; + + /* close the closure variables. */ + close_var_refs(rt, sf); + + if (sf->arg_buf) { + /* cannot free the function if it is running */ + assert(sf->cur_sp != NULL); + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) { + JS_FreeValueRT(rt, *sp); + } + js_free_rt(rt, sf->arg_buf); + } + JS_FreeValueRT(rt, sf->cur_func); + JS_FreeValueRT(rt, s->this_val); +} + +static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) +{ + JSValue func_obj; + + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); + + /* the tag does not matter provided it is not an object */ + func_obj = JS_MKPTR(JS_TAG_INT, s); + return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, + s->argc, vc(s->frame.arg_buf), + JS_CALL_FLAG_GENERATOR); +} + + +/* Generators */ + +typedef enum JSGeneratorStateEnum { + JS_GENERATOR_STATE_SUSPENDED_START, + JS_GENERATOR_STATE_SUSPENDED_YIELD, + JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_GENERATOR_STATE_EXECUTING, + JS_GENERATOR_STATE_COMPLETED, +} JSGeneratorStateEnum; + +typedef struct JSGeneratorData { + JSGeneratorStateEnum state; + JSAsyncFunctionState func_state; +} JSGeneratorData; + +static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s) +{ + if (s->state == JS_GENERATOR_STATE_COMPLETED) + return; + async_func_free(rt, &s->func_state); + s->state = JS_GENERATOR_STATE_COMPLETED; +} + +static void js_generator_finalizer(JSRuntime *rt, JSValueConst obj) +{ + JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR); + + if (s) { + free_generator_stack_rt(rt, s); + js_free_rt(rt, s); + } +} + +static void free_generator_stack(JSContext *ctx, JSGeneratorData *s) +{ + free_generator_stack_rt(ctx->rt, s); +} + +static void js_generator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSGeneratorData *s = p->u.generator_data; + + if (!s || s->state == JS_GENERATOR_STATE_COMPLETED) + return; + async_func_mark(rt, &s->func_state, mark_func); +} + +/* XXX: use enum */ +#define GEN_MAGIC_NEXT 0 +#define GEN_MAGIC_RETURN 1 +#define GEN_MAGIC_THROW 2 + +static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int *pdone, int magic) +{ + JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR); + JSStackFrame *sf; + JSValue ret, func_ret; + + *pdone = true; + if (!s) + return JS_ThrowTypeError(ctx, "not a generator"); + sf = &s->func_state.frame; + switch(s->state) { + default: + case JS_GENERATOR_STATE_SUSPENDED_START: + if (magic == GEN_MAGIC_NEXT) { + goto exec_no_arg; + } else { + free_generator_stack(ctx, s); + goto done; + } + break; + case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR: + case JS_GENERATOR_STATE_SUSPENDED_YIELD: + /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */ + ret = js_dup(argv[0]); + if (magic == GEN_MAGIC_THROW && + s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) { + JS_Throw(ctx, ret); + s->func_state.throw_flag = true; + } else { + sf->cur_sp[-1] = ret; + sf->cur_sp[0] = js_int32(magic); + sf->cur_sp++; + exec_no_arg: + s->func_state.throw_flag = false; + } + s->state = JS_GENERATOR_STATE_EXECUTING; + func_ret = async_func_resume(ctx, &s->func_state); + s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD; + if (JS_IsException(func_ret)) { + /* finalize the execution in case of exception */ + free_generator_stack(ctx, s); + return func_ret; + } + if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + /* get the returned yield value at the top of the stack */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) { + s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR; + /* return (value, done) object */ + *pdone = 2; + } else { + *pdone = false; + } + } else { + /* end of iterator */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + JS_FreeValue(ctx, func_ret); + free_generator_stack(ctx, s); + } + break; + case JS_GENERATOR_STATE_COMPLETED: + done: + /* execution is finished */ + switch(magic) { + default: + case GEN_MAGIC_NEXT: + ret = JS_UNDEFINED; + break; + case GEN_MAGIC_RETURN: + ret = js_dup(argv[0]); + break; + case GEN_MAGIC_THROW: + ret = JS_Throw(ctx, js_dup(argv[0])); + break; + } + break; + case JS_GENERATOR_STATE_EXECUTING: + ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator"); + break; + } + return ret; +} + +static JSValue js_call_generator_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSValue obj, func_ret; + JSGeneratorData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->state = JS_GENERATOR_STATE_SUSPENDED_START; + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->state = JS_GENERATOR_STATE_COMPLETED; + goto fail; + } + + /* execute the function up to 'OP_initial_yield' */ + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) + goto fail; + JS_FreeValue(ctx, func_ret); + + obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR); + if (JS_IsException(obj)) + goto fail; + JS_SetOpaqueInternal(obj, s); + return obj; + fail: + free_generator_stack_rt(ctx->rt, s); + js_free(ctx, s); + return JS_EXCEPTION; +} + +/* AsyncFunction */ + +static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s) +{ + if (s->is_active) { + async_func_free(rt, &s->func_state); + s->is_active = false; + } +} + +static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s) +{ + js_async_function_terminate(rt, s); + JS_FreeValueRT(rt, s->resolving_funcs[0]); + JS_FreeValueRT(rt, s->resolving_funcs[1]); + remove_gc_object(&s->header); + js_free_rt(rt, s); +} + +static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s) +{ + if (--s->header.ref_count == 0) { + js_async_function_free0(rt, s); + } +} + +static void js_async_function_resolve_finalizer(JSRuntime *rt, + JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + js_async_function_free(rt, s); + } +} + +static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + mark_func(rt, &s->header); + } +} + +static int js_async_function_resolve_create(JSContext *ctx, + JSAsyncFunctionData *s, + JSValue *resolving_funcs) +{ + int i; + JSObject *p; + + for(i = 0; i < 2; i++) { + resolving_funcs[i] = + JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_ASYNC_FUNCTION_RESOLVE + i); + if (JS_IsException(resolving_funcs[i])) { + if (i == 1) + JS_FreeValue(ctx, resolving_funcs[0]); + return -1; + } + p = JS_VALUE_GET_OBJ(resolving_funcs[i]); + s->header.ref_count++; + p->u.async_function_data = s; + } + return 0; +} + +static bool js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) +{ + bool is_success = true; + JSValue func_ret, ret2; + + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) { + fail: + if (unlikely(JS_IsUncatchableError(ctx->rt->current_exception))) { + is_success = false; + } else { + JSValue error = JS_GetException(ctx); + ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, + 1, vc(&error)); + JS_FreeValue(ctx, error); + resolved: + if (unlikely(JS_IsException(ret2))) { + if (JS_IsUncatchableError(ctx->rt->current_exception)) { + is_success = false; + } else { + abort(); /* BUG */ + } + } + JS_FreeValue(ctx, ret2); + } + js_async_function_terminate(ctx->rt, s); + } else { + JSValue value; + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + if (JS_IsUndefined(func_ret)) { + /* function returned */ + ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, + 1, vc(&value)); + JS_FreeValue(ctx, value); + goto resolved; + } else { + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; + + /* await */ + JS_FreeValue(ctx, func_ret); /* not used */ + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, vc(&value), 0); + JS_FreeValue(ctx, value); + if (JS_IsException(promise)) + goto fail; + if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { + JS_FreeValue(ctx, promise); + goto fail; + } + + /* Note: no need to create 'thrownawayCapability' as in + the spec */ + for(i = 0; i < 2; i++) + resolving_funcs1[i] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + vc(resolving_funcs), + vc(resolving_funcs1)); + JS_FreeValue(ctx, promise); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (res) + goto fail; + } + } + return is_success; +} + +static JSValue js_async_function_resolve_call(JSContext *ctx, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSObject *p = JS_VALUE_GET_OBJ(func_obj); + JSAsyncFunctionData *s = p->u.async_function_data; + bool is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; + JSValueConst arg; + + if (argc > 0) + arg = argv[0]; + else + arg = JS_UNDEFINED; + s->func_state.throw_flag = is_reject; + if (is_reject) { + JS_Throw(ctx, js_dup(arg)); + } else { + /* return value of await */ + s->func_state.frame.cur_sp[-1] = js_dup(arg); + } + if (!js_async_function_resume(ctx, s)) + return JS_EXCEPTION; + return JS_UNDEFINED; +} + +static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) +{ + JSValue promise; + JSAsyncFunctionData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->header.ref_count = 1; + add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); + s->is_active = false; + s->resolving_funcs[0] = JS_UNDEFINED; + s->resolving_funcs[1] = JS_UNDEFINED; + + promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); + if (JS_IsException(promise)) + goto fail; + + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + fail: + JS_FreeValue(ctx, promise); + js_async_function_free(ctx->rt, s); + return JS_EXCEPTION; + } + s->is_active = true; + + if (!js_async_function_resume(ctx, s)) + goto fail; + + js_async_function_free(ctx->rt, s); + + return promise; +} + +/* AsyncGenerator */ + +typedef enum JSAsyncGeneratorStateEnum { + JS_ASYNC_GENERATOR_STATE_SUSPENDED_START, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_ASYNC_GENERATOR_STATE_EXECUTING, + JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN, + JS_ASYNC_GENERATOR_STATE_COMPLETED, +} JSAsyncGeneratorStateEnum; + +typedef struct JSAsyncGeneratorRequest { + struct list_head link; + /* completion */ + int completion_type; /* GEN_MAGIC_x */ + JSValue result; + /* promise capability */ + JSValue promise; + JSValue resolving_funcs[2]; +} JSAsyncGeneratorRequest; + +typedef struct JSAsyncGeneratorData { + JSObject *generator; /* back pointer to the object (const) */ + JSAsyncGeneratorStateEnum state; + JSAsyncFunctionState func_state; + struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ +} JSAsyncGeneratorData; + +static void js_async_generator_free(JSRuntime *rt, + JSAsyncGeneratorData *s) +{ + struct list_head *el, *el1; + JSAsyncGeneratorRequest *req; + + list_for_each_safe(el, el1, &s->queue) { + req = list_entry(el, JSAsyncGeneratorRequest, link); + JS_FreeValueRT(rt, req->result); + JS_FreeValueRT(rt, req->promise); + JS_FreeValueRT(rt, req->resolving_funcs[0]); + JS_FreeValueRT(rt, req->resolving_funcs[1]); + js_free_rt(rt, req); + } + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && + s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { + async_func_free(rt, &s->func_state); + } + js_free_rt(rt, s); +} + +static void js_async_generator_finalizer(JSRuntime *rt, JSValueConst obj) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR); + + if (s) { + js_async_generator_free(rt, s); + } +} + +static void js_async_generator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR); + struct list_head *el; + JSAsyncGeneratorRequest *req; + if (s) { + list_for_each(el, &s->queue) { + req = list_entry(el, JSAsyncGeneratorRequest, link); + JS_MarkValue(rt, req->result, mark_func); + JS_MarkValue(rt, req->promise, mark_func); + JS_MarkValue(rt, req->resolving_funcs[0], mark_func); + JS_MarkValue(rt, req->resolving_funcs[1], mark_func); + } + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && + s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { + async_func_mark(rt, &s->func_state, mark_func); + } + } +} + +static JSValue js_async_generator_resolve_function(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int magic, JSValueConst *func_data); + +static int js_async_generator_resolve_function_create(JSContext *ctx, + JSValue generator, + JSValue *resolving_funcs, + bool is_resume_next) +{ + int i; + JSValue func; + + for(i = 0; i < 2; i++) { + func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1, + i + is_resume_next * 2, 1, vc(&generator)); + if (JS_IsException(func)) { + if (i == 1) + JS_FreeValue(ctx, resolving_funcs[0]); + return -1; + } + resolving_funcs[i] = func; + } + return 0; +} + +static int js_async_generator_await(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue value) +{ + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; + + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, vc(&value), 0); + if (JS_IsException(promise)) + goto fail; + + if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator), + resolving_funcs, false)) { + JS_FreeValue(ctx, promise); + goto fail; + } + + /* Note: no need to create 'thrownawayCapability' as in + the spec */ + for(i = 0; i < 2; i++) + resolving_funcs1[i] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + vc(resolving_funcs), + vc(resolving_funcs1)); + JS_FreeValue(ctx, promise); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (res) + goto fail; + return 0; + fail: + return -1; +} + +static void js_async_generator_resolve_or_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst result, + int is_reject) +{ + JSAsyncGeneratorRequest *next; + JSValue ret; + + next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); + list_del(&next->link); + ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1, + &result); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, next->result); + JS_FreeValue(ctx, next->promise); + JS_FreeValue(ctx, next->resolving_funcs[0]); + JS_FreeValue(ctx, next->resolving_funcs[1]); + js_free(ctx, next); +} + +static void js_async_generator_resolve(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst value, + bool done) +{ + JSValue result; + result = js_create_iterator_result(ctx, js_dup(value), done); + /* XXX: better exception handling ? */ + js_async_generator_resolve_or_reject(ctx, s, result, 0); + JS_FreeValue(ctx, result); + } + +static void js_async_generator_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst exception) +{ + js_async_generator_resolve_or_reject(ctx, s, exception, 1); +} + +static void js_async_generator_complete(JSContext *ctx, + JSAsyncGeneratorData *s) +{ + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) { + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + async_func_free(ctx->rt, &s->func_state); + } +} + +static int js_async_generator_completed_return(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue value) +{ + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int res; + + // Can fail looking up JS_ATOM_constructor when is_reject==0. + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&value), + /*is_reject*/0); + // A poisoned .constructor property is observable and the resulting + // exception should be delivered to the catch handler. + if (JS_IsException(promise)) { + JSValue err = JS_GetException(ctx); + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&err), + /*is_reject*/1); + JS_FreeValue(ctx, err); + if (JS_IsException(promise)) + return -1; + } + if (js_async_generator_resolve_function_create(ctx, + JS_MKPTR(JS_TAG_OBJECT, s->generator), + resolving_funcs1, + true)) { + JS_FreeValue(ctx, promise); + return -1; + } + resolving_funcs[0] = JS_UNDEFINED; + resolving_funcs[1] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + vc(resolving_funcs1), + vc(resolving_funcs)); + JS_FreeValue(ctx, resolving_funcs1[0]); + JS_FreeValue(ctx, resolving_funcs1[1]); + JS_FreeValue(ctx, promise); + return res; +} + +static void js_async_generator_resume_next(JSContext *ctx, + JSAsyncGeneratorData *s) +{ + JSAsyncGeneratorRequest *next; + JSValue func_ret, value; + + for(;;) { + if (list_empty(&s->queue)) + break; + next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); + switch(s->state) { + case JS_ASYNC_GENERATOR_STATE_EXECUTING: + /* only happens when restarting execution after await() */ + goto resume_exec; + case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN: + goto done; + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START: + if (next->completion_type == GEN_MAGIC_NEXT) { + goto exec_no_arg; + } else { + js_async_generator_complete(ctx, s); + } + break; + case JS_ASYNC_GENERATOR_STATE_COMPLETED: + if (next->completion_type == GEN_MAGIC_NEXT) { + js_async_generator_resolve(ctx, s, JS_UNDEFINED, true); + } else if (next->completion_type == GEN_MAGIC_RETURN) { + s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN; + js_async_generator_completed_return(ctx, s, next->result); + } else { + js_async_generator_reject(ctx, s, next->result); + } + goto done; + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD: + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR: + value = js_dup(next->result); + if (next->completion_type == GEN_MAGIC_THROW && + s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) { + JS_Throw(ctx, value); + s->func_state.throw_flag = true; + } else { + /* 'yield' returns a value. 'yield *' also returns a value + in case the 'throw' method is called */ + s->func_state.frame.cur_sp[-1] = value; + s->func_state.frame.cur_sp[0] = + js_int32(next->completion_type); + s->func_state.frame.cur_sp++; + exec_no_arg: + s->func_state.throw_flag = false; + } + s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING; + resume_exec: + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) { + value = JS_GetException(ctx); + js_async_generator_complete(ctx, s); + js_async_generator_reject(ctx, s, value); + JS_FreeValue(ctx, value); + } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + int func_ret_code, ret; + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + func_ret_code = JS_VALUE_GET_INT(func_ret); + switch(func_ret_code) { + case FUNC_RET_YIELD: + case FUNC_RET_YIELD_STAR: + if (func_ret_code == FUNC_RET_YIELD_STAR) + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR; + else + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD; + js_async_generator_resolve(ctx, s, value, false); + JS_FreeValue(ctx, value); + break; + case FUNC_RET_AWAIT: + ret = js_async_generator_await(ctx, s, value); + JS_FreeValue(ctx, value); + if (ret < 0) { + /* exception: throw it */ + s->func_state.throw_flag = true; + goto resume_exec; + } + goto done; + default: + abort(); + } + } else { + assert(JS_IsUndefined(func_ret)); + /* end of function */ + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + js_async_generator_complete(ctx, s); + js_async_generator_resolve(ctx, s, value, true); + JS_FreeValue(ctx, value); + } + break; + default: + abort(); + } + } + done: ; +} + +static JSValue js_async_generator_resolve_function(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int magic, JSValueConst *func_data) +{ + bool is_reject = magic & 1; + JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR); + JSValueConst arg = argv[0]; + + /* XXX: what if s == NULL */ + + if (magic >= 2) { + /* resume next case in AWAITING_RETURN state */ + assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN || + s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED); + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + if (is_reject) { + js_async_generator_reject(ctx, s, arg); + } else { + js_async_generator_resolve(ctx, s, arg, true); + } + } else { + /* restart function execution after await() */ + assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING); + s->func_state.throw_flag = is_reject; + if (is_reject) { + JS_Throw(ctx, js_dup(arg)); + } else { + /* return value of await */ + s->func_state.frame.cur_sp[-1] = js_dup(arg); + } + js_async_generator_resume_next(ctx, s); + } + return JS_UNDEFINED; +} + +/* magic = GEN_MAGIC_x */ +static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR); + JSValue promise, resolving_funcs[2]; + JSAsyncGeneratorRequest *req; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + if (!s) { + JSValue err, res2; + JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); + err = JS_GetException(ctx); + res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, vc(&err)); + JS_FreeValue(ctx, err); + JS_FreeValue(ctx, res2); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; + } + req = js_mallocz(ctx, sizeof(*req)); + if (!req) + goto fail; + req->completion_type = magic; + req->result = js_dup(argv[0]); + req->promise = js_dup(promise); + req->resolving_funcs[0] = resolving_funcs[0]; + req->resolving_funcs[1] = resolving_funcs[1]; + list_add_tail(&req->link, &s->queue); + if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) { + js_async_generator_resume_next(ctx, s); + } + return promise; + fail: + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; +} + +static JSValue js_async_generator_function_call(JSContext *ctx, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSValue obj, func_ret; + JSAsyncGeneratorData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START; + init_list_head(&s->queue); + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + goto fail; + } + + /* execute the function up to 'OP_initial_yield' (no yield nor + await are possible) */ + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) + goto fail; + JS_FreeValue(ctx, func_ret); + + obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR); + if (JS_IsException(obj)) + goto fail; + s->generator = JS_VALUE_GET_OBJ(obj); + JS_SetOpaqueInternal(obj, s); + return obj; + fail: + js_async_generator_free(ctx->rt, s); + return JS_EXCEPTION; +} + +/* JS parser */ + +enum { + TOK_NUMBER = -128, + TOK_STRING, + TOK_TEMPLATE, + TOK_IDENT, + TOK_REGEXP, + /* warning: order matters (see js_parse_assign_expr) */ + TOK_MUL_ASSIGN, + TOK_DIV_ASSIGN, + TOK_MOD_ASSIGN, + TOK_PLUS_ASSIGN, + TOK_MINUS_ASSIGN, + TOK_SHL_ASSIGN, + TOK_SAR_ASSIGN, + TOK_SHR_ASSIGN, + TOK_AND_ASSIGN, + TOK_XOR_ASSIGN, + TOK_OR_ASSIGN, + TOK_POW_ASSIGN, + TOK_LAND_ASSIGN, + TOK_LOR_ASSIGN, + TOK_DOUBLE_QUESTION_MARK_ASSIGN, + TOK_DEC, + TOK_INC, + TOK_SHL, + TOK_SAR, + TOK_SHR, + TOK_LT, + TOK_LTE, + TOK_GT, + TOK_GTE, + TOK_EQ, + TOK_STRICT_EQ, + TOK_NEQ, + TOK_STRICT_NEQ, + TOK_LAND, + TOK_LOR, + TOK_POW, + TOK_ARROW, + TOK_ELLIPSIS, + TOK_DOUBLE_QUESTION_MARK, + TOK_QUESTION_MARK_DOT, + TOK_ERROR, + TOK_PRIVATE_NAME, + TOK_EOF, + /* keywords: WARNING: same order as atoms */ + TOK_NULL, /* must be first */ + TOK_FALSE, + TOK_TRUE, + TOK_IF, + TOK_ELSE, + TOK_RETURN, + TOK_VAR, + TOK_THIS, + TOK_DELETE, + TOK_VOID, + TOK_TYPEOF, + TOK_NEW, + TOK_IN, + TOK_INSTANCEOF, + TOK_DO, + TOK_WHILE, + TOK_FOR, + TOK_BREAK, + TOK_CONTINUE, + TOK_SWITCH, + TOK_CASE, + TOK_DEFAULT, + TOK_THROW, + TOK_TRY, + TOK_CATCH, + TOK_FINALLY, + TOK_FUNCTION, + TOK_DEBUGGER, + TOK_WITH, + /* FutureReservedWord */ + TOK_CLASS, + TOK_CONST, + TOK_ENUM, + TOK_EXPORT, + TOK_EXTENDS, + TOK_IMPORT, + TOK_SUPER, + /* FutureReservedWords when parsing strict mode code */ + TOK_IMPLEMENTS, + TOK_INTERFACE, + TOK_LET, + TOK_PACKAGE, + TOK_PRIVATE, + TOK_PROTECTED, + TOK_PUBLIC, + TOK_STATIC, + TOK_YIELD, + TOK_AWAIT, /* must be last */ + TOK_OF, /* only used for js_parse_skip_parens_token() */ +}; + +#define TOK_FIRST_KEYWORD TOK_NULL +#define TOK_LAST_KEYWORD TOK_AWAIT + +/* unicode code points */ +#define CP_NBSP 0x00a0 +#define CP_BOM 0xfeff + +#define CP_LS 0x2028 +#define CP_PS 0x2029 + +typedef struct BlockEnv { + struct BlockEnv *prev; + JSAtom label_name; /* JS_ATOM_NULL if none */ + int label_break; /* -1 if none */ + int label_cont; /* -1 if none */ + int drop_count; /* number of stack elements to drop */ + int label_finally; /* -1 if none */ + int scope_level; + uint8_t has_iterator : 1; + uint8_t is_regular_stmt : 1; // i.e. not a loop statement +} BlockEnv; + +typedef struct JSGlobalVar { + int cpool_idx; /* if >= 0, index in the constant pool for hoisted + function defintion*/ + uint8_t force_init : 1; /* force initialization to undefined */ + uint8_t is_lexical : 1; /* global let/const definition */ + uint8_t is_const : 1; /* const definition */ + int scope_level; /* scope of definition */ + JSAtom var_name; /* variable name */ +} JSGlobalVar; + +typedef struct RelocEntry { + struct RelocEntry *next; + uint32_t addr; /* address to patch */ + int size; /* address size: 1, 2 or 4 bytes */ +} RelocEntry; + +typedef struct JumpSlot { + int op; + int size; + int pos; + int label; +} JumpSlot; + +typedef struct LabelSlot { + int ref_count; + int pos; /* phase 1 address, -1 means not resolved yet */ + int pos2; /* phase 2 address, -1 means not resolved yet */ + int addr; /* phase 3 address, -1 means not resolved yet */ + RelocEntry *first_reloc; +} LabelSlot; + +typedef struct SourceLocSlot { + uint32_t pc; + int line_num; + int col_num; +} SourceLocSlot; + +typedef enum JSParseFunctionEnum { + JS_PARSE_FUNC_STATEMENT, + JS_PARSE_FUNC_VAR, + JS_PARSE_FUNC_EXPR, + JS_PARSE_FUNC_ARROW, + JS_PARSE_FUNC_GETTER, + JS_PARSE_FUNC_SETTER, + JS_PARSE_FUNC_METHOD, + JS_PARSE_FUNC_CLASS_STATIC_INIT, + JS_PARSE_FUNC_CLASS_CONSTRUCTOR, + JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR, +} JSParseFunctionEnum; + +typedef enum JSParseExportEnum { + JS_PARSE_EXPORT_NONE, + JS_PARSE_EXPORT_NAMED, + JS_PARSE_EXPORT_DEFAULT, +} JSParseExportEnum; + +typedef struct JSFunctionDef { + JSContext *ctx; + struct JSFunctionDef *parent; + int parent_cpool_idx; /* index in the constant pool of the parent + or -1 if none */ + int parent_scope_level; /* scope level in parent at point of definition */ + struct list_head child_list; /* list of JSFunctionDef.link */ + struct list_head link; + + int eval_type; /* only valid if is_eval = true */ + + /* Pack all boolean flags together as 1-bit fields to reduce struct size + while avoiding padding and compiler deoptimization. */ + bool is_eval : 1; /* true if eval code */ + bool is_global_var : 1; /* true if variables are not defined locally: + eval global, eval module or non strict eval */ + bool is_func_expr : 1; /* true if function expression */ + bool has_home_object : 1; /* true if the home object is available */ + bool has_prototype : 1; /* true if a prototype field is necessary */ + bool has_simple_parameter_list : 1; + bool has_parameter_expressions : 1; /* if true, an argument scope is created */ + bool has_use_strict : 1; /* to reject directive in special cases */ + bool has_eval_call : 1; /* true if the function contains a call to eval() */ + bool has_arguments_binding : 1; /* true if the 'arguments' binding is + available in the function */ + bool has_this_binding : 1; /* true if the 'this' and new.target binding are + available in the function */ + bool new_target_allowed : 1; /* true if the 'new.target' does not + throw a syntax error */ + bool super_call_allowed : 1; /* true if super() is allowed */ + bool super_allowed : 1; /* true if super. or super[] is allowed */ + bool arguments_allowed : 1; /* true if the 'arguments' identifier is allowed */ + bool is_derived_class_constructor : 1; + bool in_function_body : 1; + bool backtrace_barrier : 1; + bool need_home_object : 1; + bool use_short_opcodes : 1; /* true if short opcodes are used in byte_code */ + bool has_await : 1; /* true if await is used (used in module eval) */ + + JSFunctionKindEnum func_kind : 8; + JSParseFunctionEnum func_type : 7; + uint8_t is_strict_mode : 1; + JSAtom func_name; /* JS_ATOM_NULL if no name */ + + JSVarDef *vars; + uint32_t *vars_htab; // indexes into vars[] + int var_size; /* allocated size for vars[] */ + int var_count; + JSVarDef *args; + int arg_size; /* allocated size for args[] */ + int arg_count; /* number of arguments */ + int defined_arg_count; + int var_object_idx; /* -1 if none */ + int arg_var_object_idx; /* -1 if none (var object for the argument scope) */ + int arguments_var_idx; /* -1 if none */ + int arguments_arg_idx; /* argument variable definition in argument scope, + -1 if none */ + int func_var_idx; /* variable containing the current function (-1 + if none, only used if is_func_expr is true) */ + int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */ + int this_var_idx; /* variable containg the 'this' value, -1 if none */ + int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */ + int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */ + int home_object_var_idx; + + int scope_level; /* index into fd->scopes if the current lexical scope */ + int scope_first; /* index into vd->vars of first lexically scoped variable */ + int scope_size; /* allocated size of fd->scopes array */ + int scope_count; /* number of entries used in the fd->scopes array */ + JSVarScope *scopes; + JSVarScope def_scope_array[4]; + int body_scope; /* scope of the body of the function or eval */ + + int global_var_count; + int global_var_size; + JSGlobalVar *global_vars; + + DynBuf byte_code; + int last_opcode_pos; /* -1 if no last opcode */ + + LabelSlot *label_slots; + int label_size; /* allocated size for label_slots[] */ + int label_count; + BlockEnv *top_break; /* break/continue label stack */ + + /* constant pool (strings, functions, numbers) */ + JSValue *cpool; + int cpool_count; + int cpool_size; + + /* list of variables in the closure */ + int closure_var_count; + int closure_var_size; + JSClosureVar *closure_var; + + JumpSlot *jump_slots; + int jump_size; + int jump_count; + + SourceLocSlot *source_loc_slots; + int source_loc_size; + int source_loc_count; + int line_number_last; + int line_number_last_pc; + int col_number_last; + + /* pc2line table */ + JSAtom filename; + int line_num; + int col_num; + DynBuf pc2line; + + char *source; /* raw source, utf-8 encoded */ + int source_len; + + JSModuleDef *module; /* != NULL when parsing a module */ +} JSFunctionDef; + +typedef struct JSToken { + int val; + int line_num; /* line number of token start */ + int col_num; /* column number of token start */ + const uint8_t *ptr; + union { + struct { + JSValue str; + int sep; + } str; + struct { + JSValue val; + } num; + struct { + JSAtom atom; + bool has_escape; + bool is_reserved; + } ident; + struct { + JSValue body; + JSValue flags; + } regexp; + } u; +} JSToken; + +typedef struct JSParseState { + JSContext *ctx; + int last_line_num; /* line number of last token */ + int last_col_num; /* column number of last token */ + int line_num; /* line number of current offset */ + int col_num; /* column number of current offset */ + const char *filename; + JSToken token; + bool got_lf; /* true if got line feed before the current token */ + const uint8_t *last_ptr; + const uint8_t *buf_start; + const uint8_t *buf_ptr; + const uint8_t *buf_end; + const uint8_t *eol; // most recently seen end-of-line character + const uint8_t *mark; // first token character, invariant: eol < mark + + /* current function code */ + JSFunctionDef *cur_func; + bool is_module; /* parsing a module */ + bool allow_html_comments; +} JSParseState; + +typedef struct JSOpCode { +#ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_* + const char *name; +#endif + uint8_t size; /* in bytes */ + /* the opcodes remove n_pop items from the top of the stack, then + pushes n_push items */ + uint8_t n_pop; + uint8_t n_push; + uint8_t fmt; +} JSOpCode; + +static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { +#define FMT(f) +#ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_* +#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f }, +#else +#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f }, +#endif +#include "quickjs-opcode.h" +#undef DEF +#undef FMT +}; + +/* After the final compilation pass, short opcodes are used. Their + opcodes overlap with the temporary opcodes which cannot appear in + the final bytecode. Their description is after the temporary + opcodes in opcode_info[]. */ +#define short_opcode_info(op) \ + opcode_info[(op) >= OP_TEMP_START ? \ + (op) + (OP_TEMP_END - OP_TEMP_START) : (op)] + +static void free_token(JSParseState *s, JSToken *token) +{ + switch(token->val) { + case TOK_NUMBER: + JS_FreeValue(s->ctx, token->u.num.val); + break; + case TOK_STRING: + case TOK_TEMPLATE: + JS_FreeValue(s->ctx, token->u.str.str); + break; + case TOK_REGEXP: + JS_FreeValue(s->ctx, token->u.regexp.body); + JS_FreeValue(s->ctx, token->u.regexp.flags); + break; + case TOK_IDENT: + case TOK_PRIVATE_NAME: + JS_FreeAtom(s->ctx, token->u.ident.atom); + break; + default: + if (token->val >= TOK_FIRST_KEYWORD && + token->val <= TOK_LAST_KEYWORD) { + JS_FreeAtom(s->ctx, token->u.ident.atom); + } + break; + } +} + +static void __attribute((unused)) dump_token(JSParseState *s, + const JSToken *token) +{ + printf("%d:%d ", token->line_num, token->col_num); + switch(token->val) { + case TOK_NUMBER: + { + double d; + JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */ + printf("number: %.14g\n", d); + } + break; + case TOK_IDENT: + dump_atom: + { + char buf[ATOM_GET_STR_BUF_SIZE]; + printf("ident: '%s'\n", + JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom)); + } + break; + case TOK_STRING: + { + const char *str; + /* XXX: quote the string */ + str = JS_ToCString(s->ctx, token->u.str.str); + printf("string: '%s'\n", str); + JS_FreeCString(s->ctx, str); + } + break; + case TOK_TEMPLATE: + { + const char *str; + str = JS_ToCString(s->ctx, token->u.str.str); + printf("template: `%s`\n", str); + JS_FreeCString(s->ctx, str); + } + break; + case TOK_REGEXP: + { + const char *str, *str2; + str = JS_ToCString(s->ctx, token->u.regexp.body); + str2 = JS_ToCString(s->ctx, token->u.regexp.flags); + printf("regexp: '%s' '%s'\n", str, str2); + JS_FreeCString(s->ctx, str); + JS_FreeCString(s->ctx, str2); + } + break; + case TOK_EOF: + printf("eof\n"); + break; + default: + if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) { + goto dump_atom; + } else if (s->token.val >= 256) { + printf("token: %d\n", token->val); + } else { + printf("token: '%c'\n", token->val); + } + break; + } +} + +int JS_PRINTF_FORMAT_ATTR(2, 3) js_parse_error(JSParseState *s, JS_PRINTF_FORMAT const char *fmt, ...) +{ + JSContext *ctx = s->ctx; + va_list ap; + int backtrace_flags; + + va_start(ap, fmt); + JS_ThrowError2(ctx, JS_SYNTAX_ERROR, false, fmt, ap); + va_end(ap); + backtrace_flags = 0; + if (s->cur_func && s->cur_func->backtrace_barrier) + backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; + build_backtrace(ctx, ctx->rt->current_exception, JS_UNDEFINED, s->filename, + s->line_num, s->col_num, backtrace_flags); + return -1; +} + +#ifndef QJS_DISABLE_PARSER + +static __exception int next_token(JSParseState *s); + +static int js_parse_expect(JSParseState *s, int tok) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + + if (s->token.val == tok) + return next_token(s); + + switch(s->token.val) { + case TOK_EOF: + return js_parse_error(s, "Unexpected end of input"); + case TOK_NUMBER: + return js_parse_error(s, "Unexpected number"); + case TOK_STRING: + return js_parse_error(s, "Unexpected string"); + case TOK_TEMPLATE: + return js_parse_error(s, "Unexpected string template"); + case TOK_REGEXP: + return js_parse_error(s, "Unexpected regexp"); + case TOK_IDENT: + return js_parse_error(s, "Unexpected identifier '%s'", + JS_AtomGetStr(s->ctx, buf, sizeof(buf), + s->token.u.ident.atom)); + case TOK_ERROR: + return js_parse_error(s, "Invalid or unexpected token"); + default: + return js_parse_error(s, "Unexpected token '%.*s'", + (int)(s->buf_ptr - s->token.ptr), + (const char *)s->token.ptr); + } +} + +static int js_parse_expect_semi(JSParseState *s) +{ + if (s->token.val != ';') { + /* automatic insertion of ';' */ + if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) { + return 0; + } + return js_parse_error(s, "expecting '%c'", ';'); + } + return next_token(s); +} + +static int js_parse_error_reserved_identifier(JSParseState *s) +{ + char buf1[ATOM_GET_STR_BUF_SIZE]; + return js_parse_error(s, "'%s' is a reserved identifier", + JS_AtomGetStr(s->ctx, buf1, sizeof(buf1), + s->token.u.ident.atom)); +} + +static __exception int js_parse_template_part(JSParseState *s, + const uint8_t *p) +{ + const uint8_t *p_next; + uint32_t c; + StringBuffer b_s, *b = &b_s; + JSValue str; + + /* p points to the first byte of the template part */ + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + for(;;) { + if (p >= s->buf_end) + goto unexpected_eof; + c = *p++; + if (c == '`') { + /* template end part */ + break; + } + if (c == '$' && *p == '{') { + /* template start or middle part */ + p++; + break; + } + if (c == '\\') { + if (string_buffer_putc8(b, c)) + goto fail; + if (p >= s->buf_end) + goto unexpected_eof; + c = *p++; + } + /* newline sequences are normalized as single '\n' bytes */ + if (c == '\r') { + if (*p == '\n') + p++; + c = '\n'; + } + if (c == '\n') { + s->line_num++; + s->eol = &p[-1]; + s->mark = p; + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) { + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + } + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + str = string_buffer_end(b); + if (JS_IsException(str)) + return -1; + s->token.val = TOK_TEMPLATE; + s->token.u.str.sep = c; + s->token.u.str.str = str; + s->buf_ptr = p; + return 0; + + unexpected_eof: + js_parse_error(s, "unexpected end of string"); + fail: + string_buffer_free(b); + return -1; +} + +static __exception int js_parse_string(JSParseState *s, int sep, + bool do_throw, const uint8_t *p, + JSToken *token, const uint8_t **pp) +{ + const uint8_t *p_next; + int ret; + uint32_t c; + StringBuffer b_s, *b = &b_s; + JSValue str; + + /* string */ + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + for(;;) { + if (p >= s->buf_end) + goto invalid_char; + c = *p; + if (c < 0x20) { + if (sep == '`') { + if (c == '\r') { + if (p[1] == '\n') + p++; + c = '\n'; + } + /* do not update s->line_num */ + } else if (c == '\n' || c == '\r') + goto invalid_char; + } + p++; + if (c == sep) + break; + if (c == '$' && *p == '{' && sep == '`') { + /* template start or middle part */ + p++; + break; + } + if (c == '\\') { + c = *p; + switch(c) { + case '\0': + if (p >= s->buf_end) { + if (sep != '`') + goto invalid_char; + if (do_throw) + js_parse_error(s, "Unexpected end of input"); + goto fail; + } + p++; + break; + case '\'': + case '\"': + case '\\': + p++; + break; + case '\r': /* accept DOS and MAC newline sequences */ + if (p[1] == '\n') { + p++; + } + /* fall thru */ + case '\n': + /* ignore escaped newline sequence */ + p++; + if (sep != '`') { + s->line_num++; + s->eol = &p[-1]; + s->mark = p; + } + continue; + default: + if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { + /* accept isolated \0 */ + p++; + c = '\0'; + } else + if ((c >= '0' && c <= '9') + && (s->cur_func->is_strict_mode || sep == '`')) { + if (do_throw) { + js_parse_error(s, "%s are not allowed in %s", + (c >= '8') ? "\\8 and \\9" : "Octal escape sequences", + (sep == '`') ? "template strings" : "strict mode"); + } + goto fail; + } else if (c >= 0x80) { + c = utf8_decode(p, &p_next); + if (p_next == p + 1) { + goto invalid_utf8; + } + p = p_next; + /* LS or PS are skipped */ + if (c == CP_LS || c == CP_PS) + continue; + } else { + ret = lre_parse_escape(&p, true); + if (ret == -1) { + if (do_throw) { + js_parse_error(s, "Invalid %s escape sequence", + c == 'u' ? "Unicode" : "hexadecimal"); + } + goto fail; + } else if (ret < 0) { + /* ignore the '\' (could output a warning) */ + p++; + } else { + c = ret; + } + } + break; + } + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) + goto invalid_utf8; + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + str = string_buffer_end(b); + if (JS_IsException(str)) + return -1; + token->val = TOK_STRING; + token->u.str.sep = c; + token->u.str.str = str; + *pp = p; + return 0; + + invalid_utf8: + if (do_throw) + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + invalid_char: + if (do_throw) + js_parse_error(s, "unexpected end of string"); + fail: + string_buffer_free(b); + return -1; +} + +static inline bool token_is_pseudo_keyword(JSParseState *s, JSAtom atom) { + return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom && + !s->token.u.ident.has_escape; +} + +static __exception int js_parse_regexp(JSParseState *s) +{ + const uint8_t *p, *p_next; + bool in_class; + StringBuffer b_s, *b = &b_s; + StringBuffer b2_s, *b2 = &b2_s; + uint32_t c; + JSValue body_str, flags_str; + + p = s->buf_ptr; + p++; + in_class = false; + if (string_buffer_init(s->ctx, b, 32)) + return -1; + if (string_buffer_init(s->ctx, b2, 1)) + goto fail; + for(;;) { + if (p >= s->buf_end) { + eof_error: + js_parse_error(s, "unexpected end of regexp"); + goto fail; + } + c = *p++; + if (c == '\n' || c == '\r') { + goto eol_error; + } else if (c == '/') { + if (!in_class) + break; + } else if (c == '[') { + in_class = true; + } else if (c == ']') { + /* XXX: incorrect as the first character in a class */ + in_class = false; + } else if (c == '\\') { + if (string_buffer_putc8(b, c)) + goto fail; + c = *p++; + if (c == '\n' || c == '\r') + goto eol_error; + else if (c == '\0' && p >= s->buf_end) + goto eof_error; + else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) { + goto invalid_utf8; + } + p = p_next; + if (c == CP_LS || c == CP_PS) + goto eol_error; + } + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) { + invalid_utf8: + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + } + p = p_next; + /* LS or PS are considered as line terminator */ + if (c == CP_LS || c == CP_PS) { + eol_error: + js_parse_error(s, "unexpected line terminator in regexp"); + goto fail; + } + } + if (string_buffer_putc(b, c)) + goto fail; + } + + /* flags */ + for(;;) { + c = utf8_decode(p, &p_next); + /* no need to test for invalid UTF-8, 0xFFFD is not ident_next */ + if (!lre_js_is_ident_next(c)) + break; + if (string_buffer_putc(b2, c)) + goto fail; + p = p_next; + } + + body_str = string_buffer_end(b); + flags_str = string_buffer_end(b2); + if (JS_IsException(body_str) || + JS_IsException(flags_str)) { + JS_FreeValue(s->ctx, body_str); + JS_FreeValue(s->ctx, flags_str); + return -1; + } + s->token.val = TOK_REGEXP; + s->token.u.regexp.body = body_str; + s->token.u.regexp.flags = flags_str; + s->buf_ptr = p; + return 0; + fail: + string_buffer_free(b); + string_buffer_free(b2); + return -1; +} + +#endif // QJS_DISABLE_PARSER + +static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, + char *static_buf) +{ + char *buf, *new_buf; + size_t size, new_size; + + buf = *pbuf; + size = *psize; + if (size >= (SIZE_MAX / 3) * 2) + new_size = SIZE_MAX; + else + new_size = size + (size >> 1); + if (buf == static_buf) { + new_buf = js_malloc(ctx, new_size); + if (!new_buf) + return -1; + memcpy(new_buf, buf, size); + } else { + new_buf = js_realloc(ctx, buf, new_size); + if (!new_buf) + return -1; + } + *pbuf = new_buf; + *psize = new_size; + return 0; +} + +#ifndef QJS_DISABLE_PARSER + +/* convert a TOK_IDENT to a keyword when needed */ +static void update_token_ident(JSParseState *s) +{ + if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD || + (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD && + s->cur_func->is_strict_mode) || + (s->token.u.ident.atom == JS_ATOM_yield && + ((s->cur_func->func_kind & JS_FUNC_GENERATOR) || + (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && + !s->cur_func->in_function_body && s->cur_func->parent && + (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) || + (s->token.u.ident.atom == JS_ATOM_await && + (s->is_module || + (s->cur_func->func_kind & JS_FUNC_ASYNC) || + s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT || + (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && + !s->cur_func->in_function_body && s->cur_func->parent && + ((s->cur_func->parent->func_kind & JS_FUNC_ASYNC) || + s->cur_func->parent->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))))) { + if (s->token.u.ident.has_escape) { + s->token.u.ident.is_reserved = true; + s->token.val = TOK_IDENT; + } else { + /* The keywords atoms are pre allocated */ + s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD; + } + } +} + +/* if the current token is an identifier or keyword, reparse it + according to the current function type */ +static void reparse_ident_token(JSParseState *s) +{ + if (s->token.val == TOK_IDENT || + (s->token.val >= TOK_FIRST_KEYWORD && + s->token.val <= TOK_LAST_KEYWORD)) { + s->token.val = TOK_IDENT; + s->token.u.ident.is_reserved = false; + update_token_ident(s); + } +} + +/* 'c' is the first character. Return JS_ATOM_NULL in case of error */ +static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, + bool *pident_has_escape, int c, bool is_private) +{ + const uint8_t *p, *p_next; + char ident_buf[128], *buf; + size_t ident_size, ident_pos; + JSAtom atom = JS_ATOM_NULL; + + p = *pp; + buf = ident_buf; + ident_size = sizeof(ident_buf); + ident_pos = 0; + if (is_private) + buf[ident_pos++] = '#'; + for(;;) { + if (c < 0x80) { + buf[ident_pos++] = c; + } else { + ident_pos += utf8_encode((uint8_t*)buf + ident_pos, c); + } + c = *p; + p_next = p + 1; + if (c == '\\' && *p_next == 'u') { + c = lre_parse_escape(&p_next, true); + *pident_has_escape = true; + } else if (c >= 0x80) { + c = utf8_decode(p, &p_next); + /* no need to test for invalid UTF-8, 0xFFFD is not ident_next */ + } + if (!lre_js_is_ident_next(c)) + break; + p = p_next; + if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { + if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) + goto done; + } + } + /* buf is pure ASCII or UTF-8 encoded */ + atom = JS_NewAtomLen(s->ctx, buf, ident_pos); + done: + if (unlikely(buf != ident_buf)) + js_free(s->ctx, buf); + *pp = p; + return atom; +} + + +static __exception int next_token(JSParseState *s) +{ + const uint8_t *p, *p_next; + int c; + bool ident_has_escape; + JSAtom atom; + + if (js_check_stack_overflow(s->ctx->rt, 1000)) { + JS_ThrowStackOverflow(s->ctx); + return -1; + } + + free_token(s, &s->token); + + p = s->last_ptr = s->buf_ptr; + s->got_lf = false; + s->last_line_num = s->token.line_num; + s->last_col_num = s->token.col_num; + redo: + s->token.line_num = s->line_num; + s->token.col_num = s->col_num; + s->token.ptr = p; + c = *p; + switch(c) { + case 0: + if (p >= s->buf_end) { + s->token.val = TOK_EOF; + } else { + goto def_token; + } + break; + case '`': + if (js_parse_template_part(s, p + 1)) + goto fail; + p = s->buf_ptr; + break; + case '\'': + case '\"': + if (js_parse_string(s, c, true, p + 1, &s->token, &p)) + goto fail; + break; + case '\r': /* accept DOS and MAC newline sequences */ + if (p[1] == '\n') { + p++; + } + /* fall thru */ + case '\n': + p++; + line_terminator: + s->eol = &p[-1]; + s->mark = p; + s->got_lf = true; + s->line_num++; + goto redo; + case '\f': + case '\v': + case ' ': + case '\t': + s->mark = ++p; + goto redo; + case '/': + if (p[1] == '*') { + /* comment */ + p += 2; + for(;;) { + if (*p == '\0' && p >= s->buf_end) { + js_parse_error(s, "unexpected end of comment"); + goto fail; + } + if (p[0] == '*' && p[1] == '/') { + p += 2; + break; + } + if (*p == '\n') { + s->line_num++; + s->got_lf = true; /* considered as LF for ASI */ + s->eol = p++; + s->mark = p; + } else if (*p == '\r') { + s->got_lf = true; /* considered as LF for ASI */ + p++; + } else if (*p >= 0x80) { + c = utf8_decode(p, &p); + /* ignore invalid UTF-8 in comments */ + if (c == CP_LS || c == CP_PS) { + s->got_lf = true; /* considered as LF for ASI */ + } + } else { + p++; + } + } + s->mark = p; + goto redo; + } else if (p[1] == '/') { + /* line comment */ + p += 2; + skip_line_comment: + for(;;) { + if (*p == '\0' && p >= s->buf_end) + break; + if (*p == '\r' || *p == '\n') + break; + if (*p >= 0x80) { + c = utf8_decode(p, &p); + /* ignore invalid UTF-8 in comments */ + /* LS or PS are considered as line terminator */ + if (c == CP_LS || c == CP_PS) { + break; + } + } else { + p++; + } + } + s->mark = p; + goto redo; + } else if (p[1] == '=') { + p += 2; + s->token.val = TOK_DIV_ASSIGN; + } else { + p++; + s->token.val = c; + } + break; + case '\\': + if (p[1] == 'u') { + const uint8_t *p1 = p + 1; + int c1 = lre_parse_escape(&p1, true); + if (c1 >= 0 && lre_js_is_ident_first(c1)) { + c = c1; + p = p1; + ident_has_escape = true; + goto has_ident; + } else { + /* XXX: syntax error? */ + } + } + goto def_token; + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '_': + case '$': + /* identifier */ + s->mark = p; + p++; + ident_has_escape = false; + has_ident: + atom = parse_ident(s, &p, &ident_has_escape, c, false); + if (atom == JS_ATOM_NULL) + goto fail; + s->token.u.ident.atom = atom; + s->token.u.ident.has_escape = ident_has_escape; + s->token.u.ident.is_reserved = false; + s->token.val = TOK_IDENT; + update_token_ident(s); + break; + case '#': + /* private name */ + { + p++; + c = *p; + p_next = p + 1; + if (c == '\\' && *p_next == 'u') { + c = lre_parse_escape(&p_next, true); + } else if (c >= 0x80) { + c = utf8_decode(p, &p_next); + if (p_next == p + 1) + goto invalid_utf8; + } + if (!lre_js_is_ident_first(c)) { + js_parse_error(s, "invalid first character of private name"); + goto fail; + } + p = p_next; + ident_has_escape = false; /* not used */ + atom = parse_ident(s, &p, &ident_has_escape, c, true); + if (atom == JS_ATOM_NULL) + goto fail; + s->token.u.ident.atom = atom; + s->token.val = TOK_PRIVATE_NAME; + } + break; + case '.': + if (p[1] == '.' && p[2] == '.') { + p += 3; + s->token.val = TOK_ELLIPSIS; + break; + } + if (p[1] >= '0' && p[1] <= '9') { + goto parse_number; + } else { + goto def_token; + } + break; + case '0': + /* in strict mode, octal literals are not accepted */ + if (is_digit(p[1]) && (s->cur_func->is_strict_mode)) { + js_parse_error(s, "Octal literals are not allowed in strict mode"); + goto fail; + } + goto parse_number; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': + /* number */ + parse_number: + { + JSValue ret; + const uint8_t *p1; + int flags; + flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL | + ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX; + ret = js_atof(s->ctx, (const char *)p, (const char **)&p, 0, + flags); + if (JS_IsException(ret)) + goto fail; + /* reject `10instanceof Number` */ + if (JS_VALUE_IS_NAN(ret) || + lre_js_is_ident_next(utf8_decode(p, &p1))) { + JS_FreeValue(s->ctx, ret); + js_parse_error(s, "invalid number literal"); + goto fail; + } + s->token.val = TOK_NUMBER; + s->token.u.num.val = ret; + } + break; + case '*': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_MUL_ASSIGN; + } else if (p[1] == '*') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_POW_ASSIGN; + } else { + p += 2; + s->token.val = TOK_POW; + } + } else { + goto def_token; + } + break; + case '%': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_MOD_ASSIGN; + } else { + goto def_token; + } + break; + case '+': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_PLUS_ASSIGN; + } else if (p[1] == '+') { + p += 2; + s->token.val = TOK_INC; + } else { + goto def_token; + } + break; + case '-': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_MINUS_ASSIGN; + } else if (p[1] == '-') { + if (s->allow_html_comments && p[2] == '>' && + (s->got_lf || s->last_ptr == s->buf_start)) { + /* Annex B: `-->` at beginning of line is an html comment end. + It extends to the end of the line. + */ + goto skip_line_comment; + } + p += 2; + s->token.val = TOK_DEC; + } else { + goto def_token; + } + break; + case '<': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_LTE; + } else if (p[1] == '<') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_SHL_ASSIGN; + } else { + p += 2; + s->token.val = TOK_SHL; + } + } else if (s->allow_html_comments && + p[1] == '!' && p[2] == '-' && p[3] == '-') { + /* Annex B: handle ` <----- 32 bit -----> +class wasm_addr_t { + public: + wasm_addr_t(uint32_t module_id, uint32_t offset) + : module_id_(module_id), offset_(offset) {} + explicit wasm_addr_t(uint64_t address) + : module_id_(address >> 32), offset_(address & 0xffffffff) {} + + inline uint32_t ModuleId() const { return module_id_; } + inline uint32_t Offset() const { return offset_; } + + inline operator uint64_t() const { + return static_cast(module_id_) << 32 | offset_; + } + + private: + uint32_t module_id_; + uint32_t offset_; +}; + +} // namespace gdb_server +} // namespace wasm +} // namespace internal +} // namespace v8 + +#endif // V8_DEBUG_WASM_GDB_SERVER_GDB_REMOTE_UTIL_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/gdb-server-thread.h b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/gdb-server-thread.h new file mode 100644 index 000000000..3d9a01a3a --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/gdb-server-thread.h @@ -0,0 +1,63 @@ +// 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 V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_THREAD_H_ +#define V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_THREAD_H_ + +#include "src/base/platform/platform.h" +#include "src/base/platform/semaphore.h" +#include "src/debug/wasm/gdb-server/target.h" +#include "src/debug/wasm/gdb-server/transport.h" + +namespace v8 { +namespace internal { +namespace wasm { +namespace gdb_server { + +class GdbServer; + +// class GdbServerThread spawns a thread where all communication with a debugger +// happens. +class GdbServerThread : public v8::base::Thread { + public: + explicit GdbServerThread(GdbServer* gdb_server); + GdbServerThread(const GdbServerThread&) = delete; + GdbServerThread& operator=(const GdbServerThread&) = delete; + + // base::Thread + void Run() override; + + // Starts the GDB-server thread and waits Run() method is called on the new + // thread and the initialization completes. + bool StartAndInitialize(); + + // Stops the GDB-server thread when the V8 process shuts down; gracefully + // closes any active debugging session. + void Stop(); + + Target& GetTarget() { return *target_; } + + private: + void CleanupThread(); + + GdbServer* gdb_server_; + + // Used to block the caller on StartAndInitialize() waiting for the new thread + // to have completed its initialization. + // (Note that Thread::StartSynchronously() wouldn't work in this case because + // it returns as soon as the new thread starts, but before Run() is called). + base::Semaphore start_semaphore_; + + base::Mutex mutex_; + // Protected by {mutex_}: + std::unique_ptr transport_; + std::unique_ptr target_; +}; + +} // namespace gdb_server +} // namespace wasm +} // namespace internal +} // namespace v8 + +#endif // V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_THREAD_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/gdb-server.h b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/gdb-server.h new file mode 100644 index 000000000..146877f73 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/gdb-server.h @@ -0,0 +1,216 @@ +// 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 V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_ +#define V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_ + +#include +#include +#include "src/debug/wasm/gdb-server/gdb-server-thread.h" +#include "src/debug/wasm/gdb-server/wasm-module-debug.h" + +namespace v8 { +namespace internal { +namespace wasm { +namespace gdb_server { + +class TaskRunner; + +// class GdbServer acts as a manager for the GDB-remote stub. It is instantiated +// as soon as the first Wasm module is loaded in the Wasm engine and spawns a +// separate thread to accept connections and exchange messages with a debugger. +// It will contain the logic to serve debugger queries and access the state of +// the Wasm engine. +class GdbServer { + public: + GdbServer(const GdbServer&) = delete; + GdbServer& operator=(const GdbServer&) = delete; + + // Factory method: creates and returns a GdbServer. Spawns a "GDB-remote" + // thread that will be used to communicate with the debugger. + // May return null on failure. + // This should be called once, the first time a Wasm module is loaded in the + // Wasm engine. + static std::unique_ptr Create(); + + // Stops the "GDB-remote" thread and waits for it to complete. This should be + // called once, when the Wasm engine shuts down. + ~GdbServer(); + + // Queries the set of the Wasm modules currently loaded. Each module is + // identified by a unique integer module id. + struct WasmModuleInfo { + uint32_t module_id; + std::string module_name; + }; + std::vector GetLoadedModules( + bool clear_module_list_changed_flag = false); + + bool HasModuleListChanged() const { return has_module_list_changed_; } + + // Queries the value of the {index} global value in the Wasm module identified + // by {frame_index}. + // + bool GetWasmGlobal(uint32_t frame_index, uint32_t index, uint8_t* buffer, + uint32_t buffer_size, uint32_t* size); + + // Queries the value of the {index} local value in the {frame_index}th stack + // frame in the Wasm module identified by {frame_index}. + // + bool GetWasmLocal(uint32_t frame_index, uint32_t index, uint8_t* buffer, + uint32_t buffer_size, uint32_t* size); + + // Queries the value of the {index} value in the operand stack. + // + bool GetWasmStackValue(uint32_t frame_index, uint32_t index, uint8_t* buffer, + uint32_t buffer_size, uint32_t* size); + + // Reads {size} bytes, starting from {offset}, from the Memory instance + // associated to the Wasm module identified by {module_id}. + // Returns the number of bytes copied to {buffer}, or 0 is case of error. + // Note: only one Memory for Module is currently supported. + // + uint32_t GetWasmMemory(uint32_t module_id, uint32_t offset, uint8_t* buffer, + uint32_t size); + + // Reads {size} bytes, starting from {offset}, from the first Data segment + // in the Wasm module identified by {module_id}. + // Returns the number of bytes copied to {buffer}, or 0 is case of error. + // Note: only one Memory for Module is currently supported. + // + uint32_t GetWasmData(uint32_t module_id, uint32_t offset, uint8_t* buffer, + uint32_t size); + + // Reads {size} bytes, starting from the low dword of {address}, from the Code + // space of th Wasm module identified by high dword of {address}. + // Returns the number of bytes copied to {buffer}, or 0 is case of error. + uint32_t GetWasmModuleBytes(wasm_addr_t address, uint8_t* buffer, + uint32_t size); + + // Inserts a breakpoint at the offset {offset} of the Wasm module identified + // by {wasm_module_id}. + // Returns true if the breakpoint was successfully added. + bool AddBreakpoint(uint32_t wasm_module_id, uint32_t offset); + + // Removes a breakpoint at the offset {offset} of the Wasm module identified + // by {wasm_module_id}. + // Returns true if the breakpoint was successfully removed. + bool RemoveBreakpoint(uint32_t wasm_module_id, uint32_t offset); + + // Returns the current call stack as a vector of program counters. + std::vector GetWasmCallStack() const; + + // Manage the set of Isolates for this GdbServer. + void AddIsolate(Isolate* isolate); + void RemoveIsolate(Isolate* isolate); + + // Requests that the thread suspend execution at the next Wasm instruction. + void Suspend(); + + // Handle stepping in wasm functions via the wasm interpreter. + void PrepareStep(); + + // Called when the target debuggee can resume execution (for example after + // having been suspended on a breakpoint). Terminates the task runner leaving + // all pending tasks in the queue. + void QuitMessageLoopOnPause(); + + private: + GdbServer(); + + // When the target debuggee is suspended for a breakpoint or exception, blocks + // the main (isolate) thread and enters in a message loop. Here it waits on a + // queue of Task objects that are posted by the GDB-stub thread and that + // represent queries received from the debugger via the GDB-remote protocol. + void RunMessageLoopOnPause(); + + // Post a task to run a callback in the isolate thread. + template + auto RunSyncTask(Callback&& callback) const; + + void AddWasmModule(uint32_t module_id, Local wasm_script); + + // Given a Wasm module id, retrieves the corresponding debugging WasmScript + // object. + bool GetModuleDebugHandler(uint32_t module_id, + WasmModuleDebug** wasm_module_debug); + + // Returns the debugging target. + Target& GetTarget() const; + + // Class DebugDelegate implements the debug::DebugDelegate interface to + // receive notifications when debug events happen in a given isolate, like a + // script being loaded, a breakpoint being hit, an exception being thrown. + class DebugDelegate : public debug::DebugDelegate { + public: + DebugDelegate(Isolate* isolate, GdbServer* gdb_server); + ~DebugDelegate(); + + // debug::DebugDelegate + void ScriptCompiled(Local script, bool is_live_edited, + bool has_compile_error) override; + void BreakProgramRequested( + Local paused_context, + const std::vector& inspector_break_points_hit, + v8::debug::BreakReasons break_reasons) override; + void ExceptionThrown(Local paused_context, + Local exception, Local promise, + bool is_uncaught, + debug::ExceptionType exception_type) override; + bool IsFunctionBlackboxed(Local script, + const debug::Location& start, + const debug::Location& end) override; + + private: + // Calculates module_id as: + // +--------------------+------------------- + + // | DebugDelegate::id_ | Script::Id() | + // +--------------------+------------------- + + // <----- 16 bit -----> <----- 16 bit -----> + uint32_t GetModuleId(uint32_t script_id) const { + DCHECK_LT(script_id, 0x10000); + DCHECK_LT(id_, 0x10000); + return id_ << 16 | script_id; + } + + Isolate* isolate_; + uint32_t id_; + GdbServer* gdb_server_; + + static std::atomic id_s; + }; + + // The GDB-stub thread where all the communication with the debugger happens. + std::unique_ptr thread_; + + // Used to transform the queries that arrive in the GDB-stub thread into + // tasks executed in the main (isolate) thread. + std::unique_ptr task_runner_; + + std::atomic has_module_list_changed_; + + ////////////////////////////////////////////////////////////////////////////// + // Always accessed in the isolate thread. + + // Set of breakpoints currently defines in Wasm code. + typedef std::map BreakpointsMap; + BreakpointsMap breakpoints_; + + typedef std::map ScriptsMap; + ScriptsMap scripts_; + + typedef std::map> + IsolateDebugDelegateMap; + IsolateDebugDelegateMap isolate_delegates_; + + // End of fields always accessed in the isolate thread. + ////////////////////////////////////////////////////////////////////////////// +}; + +} // namespace gdb_server +} // namespace wasm +} // namespace internal +} // namespace v8 + +#endif // V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/packet.h b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/packet.h new file mode 100644 index 000000000..87b5b6b91 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/packet.h @@ -0,0 +1,105 @@ +// 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 V8_DEBUG_WASM_GDB_SERVER_PACKET_H_ +#define V8_DEBUG_WASM_GDB_SERVER_PACKET_H_ + +#include + +#include "src/base/macros.h" + +namespace v8 { +namespace internal { +namespace wasm { +namespace gdb_server { + +class V8_EXPORT_PRIVATE Packet { + public: + Packet(); + + // Empty the vector and reset the read/write pointers. + void Clear(); + + // Reset the read pointer, allowing the packet to be re-read. + void Rewind(); + + // Return true of the read pointer has reached the write pointer. + bool EndOfPacket() const; + + // Store a single raw 8 bit value + void AddRawChar(char ch); + + // Store a block of data as hex pairs per byte + void AddBlock(const void* ptr, uint32_t len); + + // Store a byte as a 2 chars block. + void AddWord8(uint8_t val); + + // Store a number up to 64 bits, formatted as a big-endian hex string with + // preceeding zeros removed. Since zeros can be removed, the width of this + // number is unknown, and the number is always followed by a NULL or a + // separator (non hex digit). + void AddNumberSep(uint64_t val, char sep); + + // Add a raw string. + void AddString(const char* str); + + // Add a string stored as a stream of ASCII hex digit pairs. It is safe + // to use any non-null character in this stream. If this does not terminate + // the packet, there should be a separator (non hex digit) immediately + // following. + void AddHexString(const char* str); + + // Retrieve a single character if available + bool GetRawChar(char* ch); + + // Retrieve "len" ASCII character pairs. + bool GetBlock(void* ptr, uint32_t len); + + // Retrieve a 8, 16, 32, or 64 bit word as pairs of hex digits. These + // functions will always consume bits/4 characters from the stream. + bool GetWord8(uint8_t* val); + + // Retrieve a number (formatted as a big-endian hex string) and a separator. + // If 'sep' is null, the separator is consumed but thrown away. + bool GetNumberSep(uint64_t* val, char* sep); + + // Get a string from the stream + bool GetString(std::string* str); + bool GetHexString(std::string* str); + + // Return a pointer to the entire packet payload + const char* GetPayload() const; + size_t GetPayloadSize() const; + + // Returns true and the sequence number, or false if it is unset. + bool GetSequence(int32_t* seq) const; + + // Parses sequence number in package data and moves read pointer past it. + void ParseSequence(); + + // Set the sequence number. + void SetSequence(int32_t seq); + + enum class ErrDef { None = 0, BadFormat = 1, BadArgs = 2, Failed = 3 }; + void SetError(ErrDef); + + // Returns the full content of a GDB-remote packet, in the format: + // $payload#checksum + // where the two-digit checksum is computed as the modulo 256 sum of all + // characters between the leading ‘$’ and the trailing ‘#’. + std::string GetPacketData() const; + + private: + int32_t seq_; + std::string data_; + size_t read_index_; +}; + +} // namespace gdb_server +} // namespace wasm +} // namespace internal +} // namespace v8 + +#endif // V8_DEBUG_WASM_GDB_SERVER_PACKET_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/session.h b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/session.h new file mode 100644 index 000000000..ba052cab5 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/session.h @@ -0,0 +1,73 @@ +// 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 V8_DEBUG_WASM_GDB_SERVER_SESSION_H_ +#define V8_DEBUG_WASM_GDB_SERVER_SESSION_H_ + +#include "src/base/macros.h" + +namespace v8 { +namespace internal { +namespace wasm { +namespace gdb_server { + +class Packet; +class TransportBase; + +// Represents a gdb-remote debugging session. +class V8_EXPORT_PRIVATE Session { + public: + explicit Session(TransportBase* transport); + Session(const Session&) = delete; + Session& operator=(const Session&) = delete; + + // Attempt to send a packet and optionally wait for an ACK from the receiver. + bool SendPacket(Packet* packet, bool expect_ack = true); + + // Attempt to receive a packet. + bool GetPacket(Packet* packet); + + // Return true if there is data to read. + bool IsDataAvailable() const; + + // Return true if the connection is still valid. + bool IsConnected() const; + + // Shutdown the connection. + void Disconnect(); + + // When a debugging session is active, the GDB-remote thread can block waiting + // for events and it will resume execution when one of these two events arise: + // - A network event (a new packet arrives, or the connection is dropped) + // - A thread event (the execution stopped because of a trap or breakpoint). + void WaitForDebugStubEvent(); + + // Signal that the debuggee execution stopped because of a trap or breakpoint. + bool SignalThreadEvent(); + + // By default, when either the debugger or the GDB-stub sends a packet, + // the first response expected is an acknowledgment: either '+' (to indicate + // the packet was received correctly) or '-' (to request retransmission). + // When a transport is reliable, the debugger may request that acknowledgement + // be disabled by means of the 'QStartNoAckMode' packet. + void EnableAck(bool ack_enabled) { ack_enabled_ = ack_enabled; } + + private: + // Read a single character from the transport. + bool GetChar(char* ch); + + // Read the content of a packet, from a leading '$' to a trailing '#'. + bool GetPayload(Packet* pkt, uint8_t* checksum); + + TransportBase* io_; // Transport object not owned by the Session. + bool connected_; // Is the connection still valid. + bool ack_enabled_; // If true, emit or wait for '+' from RSP stream. +}; + +} // namespace gdb_server +} // namespace wasm +} // namespace internal +} // namespace v8 + +#endif // V8_DEBUG_WASM_GDB_SERVER_SESSION_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/target.h b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/target.h new file mode 100644 index 000000000..bba854931 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/target.h @@ -0,0 +1,142 @@ +// 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 V8_DEBUG_WASM_GDB_SERVER_TARGET_H_ +#define V8_DEBUG_WASM_GDB_SERVER_TARGET_H_ + +#include +#include + +#include "src/base/platform/mutex.h" +#include "src/base/platform/semaphore.h" +#include "src/debug/wasm/gdb-server/gdb-remote-util.h" + +namespace v8 { +namespace internal { +namespace wasm { +namespace gdb_server { + +class GdbServer; +class Packet; +class Session; + +// Class Target represents a debugging target. It contains the logic to decode +// incoming GDB-remote packets, execute them forwarding the debugger commands +// and queries to the Wasm engine, and send back GDB-remote packets. +class Target { + public: + // Contruct a Target object. + explicit Target(GdbServer* gdb_server); + Target(const Target&) = delete; + Target& operator=(const Target&) = delete; + + // This function spin on a debugging session, until it closes. + void Run(Session* ses); + + void Terminate(); + bool IsTerminated() const { return status_ == Status::Terminated; } + + // Notifies that the debuggee thread suspended at a breakpoint. + void OnProgramBreak(Isolate* isolate, + const std::vector& call_frames); + // Notifies that the debuggee thread suspended because of an unhandled + // exception. + void OnException(Isolate* isolate, + const std::vector& call_frames); + + // Returns the state at the moment of the thread suspension. + const std::vector GetCallStack() const; + wasm_addr_t GetCurrentPc() const; + Isolate* GetCurrentIsolate() const { return current_isolate_; } + + private: + void OnSuspended(Isolate* isolate, int signal, + const std::vector& call_frames); + + // Initializes a map used to make fast lookups when handling query packets + // that have a constant response. + void InitQueryPropertyMap(); + + // Blocks waiting for one of these two events to occur: + // - A network packet arrives from the debugger, or the debugger connection is + // closed; + // - The debuggee suspends execution because of a trap or breakpoint. + void WaitForDebugEvent(); + void ProcessDebugEvent(); + + // Processes GDB-remote packets that arrive from the debugger. + // This method should be called when the debuggee has suspended its execution. + void ProcessCommands(); + + // Requests that the thread suspends execution at the next Wasm instruction. + void Suspend(); + + enum class ErrorCode { None = 0, BadFormat = 1, BadArgs = 2, Failed = 3 }; + + enum class ProcessPacketResult { + Paused, // The command was processed, debuggee still paused. + Continue, // The debuggee should resume execution. + Detach, // Request to detach from the debugger. + Kill // Request to terminate the debuggee process. + }; + // This function always succeedes, since all errors are reported as an error + // string "Exx" where xx is a two digit number. + // The return value indicates if the target can resume execution or it is + // still paused. + ProcessPacketResult ProcessPacket(Packet* pkt_in, Packet* pkt_out); + + // Processes a general query packet + ErrorCode ProcessQueryPacket(const Packet* pkt_in, Packet* pkt_out); + + // Formats a 'Stop-reply' packet, which is sent in response of a 'c' + // (continue), 's' (step) and '?' (query halt reason) commands. + void SetStopReply(Packet* pkt_out) const; + + enum class Status { Running, WaitingForSuspension, Suspended, Terminated }; + + void SetStatus(Status status, int8_t signal = 0, + std::vector call_frames_ = {}, + Isolate* isolate = nullptr); + + GdbServer* gdb_server_; + + std::atomic status_; + + // Signal being processed. + std::atomic cur_signal_; + + // Session object not owned by the Target. + Session* session_; + + // Map used to make fast lookups when handling query packets. + typedef std::map QueryPropertyMap; + QueryPropertyMap query_properties_; + + bool debugger_initial_suspension_; + + // Used to block waiting for suspension + v8::base::Semaphore semaphore_; + + mutable v8::base::Mutex mutex_; + ////////////////////////////////////////////////////////////////////////////// + // Protected by {mutex_}: + + // Current isolate. This is not null only when the target is in a Suspended + // state and it is the isolate associated to the current call stack and used + // for all debugging activities. + Isolate* current_isolate_; + + // Call stack when the execution is suspended. + std::vector call_frames_; + + // End of fields protected by {mutex_}. + ////////////////////////////////////////////////////////////////////////////// +}; + +} // namespace gdb_server +} // namespace wasm +} // namespace internal +} // namespace v8 + +#endif // V8_DEBUG_WASM_GDB_SERVER_TARGET_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/transport.h b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/transport.h new file mode 100644 index 000000000..279c8d3d8 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/transport.h @@ -0,0 +1,192 @@ +// 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 V8_DEBUG_WASM_GDB_SERVER_TRANSPORT_H_ +#define V8_DEBUG_WASM_GDB_SERVER_TRANSPORT_H_ + +#include + +#include "src/base/macros.h" +#include "src/debug/wasm/gdb-server/gdb-remote-util.h" + +#if _WIN32 +#include +#include + +typedef SOCKET SocketHandle; + +#define CloseSocket closesocket +#define InvalidSocket INVALID_SOCKET +#define SocketGetLastError() WSAGetLastError() +static const int kErrInterrupt = WSAEINTR; +typedef int ssize_t; +typedef int socklen_t; + +#else // _WIN32 + +#include +#include +#include +#include +#include +#include + +typedef int SocketHandle; + +#define CloseSocket close +#define InvalidSocket (-1) +#define SocketGetLastError() errno +static const int kErrInterrupt = EINTR; + +#endif // _WIN32 + +namespace v8 { +namespace internal { +namespace wasm { +namespace gdb_server { + +class SocketTransport; + +// Acts as a factory for Transport objects bound to a specified TCP port. +class SocketBinding { + public: + // Wrap existing socket handle. + explicit SocketBinding(SocketHandle socket_handle); + + // Bind to the specified TCP port. + static SocketBinding Bind(uint16_t tcp_port); + + bool IsValid() const { return socket_handle_ != InvalidSocket; } + + // Create a transport object from this socket binding + std::unique_ptr CreateTransport(); + + // Get port the socket is bound to. + uint16_t GetBoundPort(); + + private: + SocketHandle socket_handle_; +}; + +class V8_EXPORT_PRIVATE TransportBase { + public: + virtual ~TransportBase() {} + + // Waits for an incoming connection on the bound port. + virtual bool AcceptConnection() = 0; + + // Read {len} bytes from this transport, possibly blocking until enough data + // is available. + // {dst} must point to a buffer large enough to contain {len} bytes. + // Returns true on success. + // Returns false if the connection is closed; in that case the {dst} may have + // been partially overwritten. + virtual bool Read(char* dst, int32_t len) = 0; + + // Write {len} bytes to this transport. + // Return true on success, false if the connection is closed. + virtual bool Write(const char* src, int32_t len) = 0; + + // Return true if there is data to read. + virtual bool IsDataAvailable() const = 0; + + // If we are connected to a debugger, gracefully closes the connection. + // This should be called when a debugging session gets closed. + virtual void Disconnect() = 0; + + // Shuts down this transport, gracefully closing the existing connection and + // also closing the listening socket. This should be called when the GDB stub + // shuts down, when the program terminates. + virtual void Close() = 0; + + // Blocks waiting for one of these two events to occur: + // - A network event (a new packet arrives, or the connection is dropped), + // - A thread event is signaled (the execution stopped because of a trap or + // breakpoint). + virtual void WaitForDebugStubEvent() = 0; + + // Signal that this transport should leave an alertable wait state because + // the execution of the debuggee was stopped because of a trap or breakpoint. + virtual bool SignalThreadEvent() = 0; +}; + +class Transport : public TransportBase { + public: + explicit Transport(SocketHandle s); + ~Transport() override; + + // TransportBase + bool Read(char* dst, int32_t len) override; + bool Write(const char* src, int32_t len) override; + bool IsDataAvailable() const override; + void Disconnect() override; + void Close() override; + + static const int kBufSize = 4096; + + protected: + // Copy buffered data to *dst up to len bytes and update dst and len. + void CopyFromBuffer(char** dst, int32_t* len); + + // Read available data from the socket. Return false on EOF or error. + virtual bool ReadSomeData() = 0; + + std::unique_ptr buf_; + int32_t pos_; + int32_t size_; + SocketHandle handle_bind_; + SocketHandle handle_accept_; +}; + +#if _WIN32 + +class SocketTransport : public Transport { + public: + explicit SocketTransport(SocketHandle s); + ~SocketTransport() override; + SocketTransport(const SocketTransport&) = delete; + SocketTransport& operator=(const SocketTransport&) = delete; + + // TransportBase + bool AcceptConnection() override; + void Disconnect() override; + void WaitForDebugStubEvent() override; + bool SignalThreadEvent() override; + + private: + bool ReadSomeData() override; + + HANDLE socket_event_; + HANDLE faulted_thread_event_; +}; + +#else // _WIN32 + +class SocketTransport : public Transport { + public: + explicit SocketTransport(SocketHandle s); + ~SocketTransport() override; + SocketTransport(const SocketTransport&) = delete; + SocketTransport& operator=(const SocketTransport&) = delete; + + // TransportBase + bool AcceptConnection() override; + void WaitForDebugStubEvent() override; + bool SignalThreadEvent() override; + + private: + bool ReadSomeData() override; + + int faulted_thread_fd_read_; + int faulted_thread_fd_write_; +}; + +#endif // _WIN32 + +} // namespace gdb_server +} // namespace wasm +} // namespace internal +} // namespace v8 + +#endif // V8_DEBUG_WASM_GDB_SERVER_TRANSPORT_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/wasm-module-debug.h b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/wasm-module-debug.h new file mode 100644 index 000000000..726e512a1 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/debug/wasm/gdb-server/wasm-module-debug.h @@ -0,0 +1,111 @@ +// 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 V8_DEBUG_WASM_GDB_SERVER_WASM_MODULE_DEBUG_H_ +#define V8_DEBUG_WASM_GDB_SERVER_WASM_MODULE_DEBUG_H_ + +#include "src/debug/debug-interface.h" +#include "src/debug/wasm/gdb-server/gdb-remote-util.h" +#include "src/execution/frames.h" + +namespace v8 { +namespace internal { +namespace wasm { + +class WasmValue; + +namespace gdb_server { + +// Represents the interface to access the Wasm engine state for a given module. +// For the moment it only works with interpreted functions, in the future it +// could be extended to also support Liftoff. +class WasmModuleDebug { + public: + WasmModuleDebug(v8::Isolate* isolate, Local script); + + std::string GetModuleName() const; + i::Isolate* GetIsolate() const { + return reinterpret_cast(isolate_); + } + + // Gets the value of the {index}th global value. + static bool GetWasmGlobal(Isolate* isolate, uint32_t frame_index, + uint32_t index, uint8_t* buffer, + uint32_t buffer_size, uint32_t* size); + + // Gets the value of the {index}th local value in the {frame_index}th stack + // frame. + static bool GetWasmLocal(Isolate* isolate, uint32_t frame_index, + uint32_t index, uint8_t* buffer, + uint32_t buffer_size, uint32_t* size); + + // Gets the value of the {index}th value in the operand stack. + static bool GetWasmStackValue(Isolate* isolate, uint32_t frame_index, + uint32_t index, uint8_t* buffer, + uint32_t buffer_size, uint32_t* size); + + // Reads {size} bytes, starting from {offset}, from the Memory instance + // associated to this module. + // Returns the number of byte copied to {buffer}, or 0 is case of error. + // Note: only one Memory for Module is currently supported. + uint32_t GetWasmMemory(Isolate* isolate, uint32_t offset, uint8_t* buffer, + uint32_t size); + + // Reads {size} bytes, starting from {offset}, from the first segment + // associated to this module. + // Returns the number of byte copied to {buffer}, or 0 is case of error. + // Note: only one Memory for Module is currently supported. + uint32_t GetWasmData(Isolate* isolate, uint32_t offset, uint8_t* buffer, + uint32_t size); + + // Gets {size} bytes, starting from {offset}, from the Code space of this + // module. + // Returns the number of byte copied to {buffer}, or 0 is case of error. + uint32_t GetWasmModuleBytes(wasm_addr_t wasm_addr, uint8_t* buffer, + uint32_t size); + + // Inserts a breakpoint at the offset {offset} of this module. + // Returns {true} if the breakpoint was successfully added. + bool AddBreakpoint(uint32_t offset, int* breakpoint_id); + + // Removes a breakpoint at the offset {offset} of the this module. + void RemoveBreakpoint(uint32_t offset, int breakpoint_id); + + // Handle stepping in wasm functions via the wasm interpreter. + void PrepareStep(); + + // Returns the current stack trace as a vector of instruction pointers. + static std::vector GetCallStack(uint32_t debug_context_id, + Isolate* isolate); + + private: + // Returns the module WasmInstance associated to the {frame_index}th frame + // in the call stack. + static Handle GetWasmInstance(Isolate* isolate, + uint32_t frame_index); + + // Returns its first WasmInstance for this Wasm module. + Handle GetFirstWasmInstance(); + + // Iterates on current stack frames and return frame information for the + // {frame_index} specified. + // Returns an empty array if the frame specified does not correspond to a Wasm + // stack frame. + static std::vector FindWasmFrame( + StackTraceFrameIterator* frame_it, uint32_t* frame_index); + + // Converts a WasmValue into an array of bytes. + static bool GetWasmValue(const wasm::WasmValue& wasm_value, uint8_t* buffer, + uint32_t buffer_size, uint32_t* size); + + v8::Isolate* isolate_; + Global wasm_script_; +}; + +} // namespace gdb_server +} // namespace wasm +} // namespace internal +} // namespace v8 + +#endif // V8_DEBUG_WASM_GDB_SERVER_WASM_MODULE_DEBUG_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/BUILD.gn b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/BUILD.gn new file mode 100644 index 000000000..87ae628aa --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/BUILD.gn @@ -0,0 +1,168 @@ +# 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. + +import("../../gni/v8.gni") + +_inspector_protocol = v8_path_prefix + "/third_party/inspector_protocol" +import("$_inspector_protocol/inspector_protocol.gni") + +_protocol_generated = [ + "protocol/Forward.h", + "protocol/Protocol.cpp", + "protocol/Protocol.h", + "protocol/Console.cpp", + "protocol/Console.h", + "protocol/Debugger.cpp", + "protocol/Debugger.h", + "protocol/HeapProfiler.cpp", + "protocol/HeapProfiler.h", + "protocol/Profiler.cpp", + "protocol/Profiler.h", + "protocol/Runtime.cpp", + "protocol/Runtime.h", + "protocol/Schema.cpp", + "protocol/Schema.h", + "../../include/inspector/Debugger.h", + "../../include/inspector/Runtime.h", + "../../include/inspector/Schema.h", +] + +action("protocol_compatibility") { + visibility = [ ":*" ] # Only targets in this file can depend on this. + script = "$_inspector_protocol/check_protocol_compatibility.py" + inputs = [ v8_inspector_js_protocol ] + _stamp = "$target_gen_dir/js_protocol.stamp" + outputs = [ _stamp ] + args = [ + "--stamp", + rebase_path(_stamp, root_build_dir), + rebase_path(v8_inspector_js_protocol, root_build_dir), + ] +} + +inspector_protocol_generate("protocol_generated_sources") { + deps = [ ":protocol_compatibility" ] + + inspector_protocol_dir = _inspector_protocol + out_dir = target_gen_dir + _protocol_path = rebase_path(v8_inspector_js_protocol, root_build_dir) + config_values = [ "protocol.path=$_protocol_path" ] + config_file = v8_path_prefix + "/src/inspector/inspector_protocol_config.json" + inputs = [ + v8_inspector_js_protocol, + config_file, + ] + outputs = _protocol_generated +} + +config("inspector_config") { + visibility = [ ":*" ] # Only targets in this file can depend on this. + + configs = [ "../../:internal_config" ] + include_dirs = [ "../../include" ] +} + +v8_header_set("inspector_test_headers") { + configs = [ ":inspector_config" ] + + public_deps = [ "../..:v8_headers" ] + + sources = [ "test-interface.h" ] +} + +v8_source_set("inspector_string_conversions") { + sources = [ + "v8-string-conversions.cc", + "v8-string-conversions.h", + ] + configs = [ "../..:internal_config_base" ] + deps = [ "../..:v8_libbase" ] +} + +v8_source_set("inspector") { + deps = [ + ":inspector_string_conversions", + "../..:v8_tracing", + "../..:v8_version", + "../../third_party/inspector_protocol:crdtp", + ] + + public_deps = [ + ":inspector_test_headers", + ":protocol_generated_sources", + "../../:v8_libbase", + ] + + configs = [ ":inspector_config" ] + + sources = rebase_path(_protocol_generated, ".", target_gen_dir) + sources += [ + "../../include/v8-inspector-protocol.h", + "../../include/v8-inspector.h", + ] + sources += [ + "crc32.cc", + "crc32.h", + "custom-preview.cc", + "custom-preview.h", + "injected-script.cc", + "injected-script.h", + "inspected-context.cc", + "inspected-context.h", + "remote-object-id.cc", + "remote-object-id.h", + "search-util.cc", + "search-util.h", + "string-16.cc", + "string-16.h", + "string-util.cc", + "string-util.h", + "test-interface.cc", + "v8-console-agent-impl.cc", + "v8-console-agent-impl.h", + "v8-console-message.cc", + "v8-console-message.h", + "v8-console.cc", + "v8-console.h", + "v8-debugger-agent-impl.cc", + "v8-debugger-agent-impl.h", + "v8-debugger-barrier.cc", + "v8-debugger-barrier.h", + "v8-debugger-id.cc", + "v8-debugger-id.h", + "v8-debugger-script.cc", + "v8-debugger-script.h", + "v8-debugger.cc", + "v8-debugger.h", + "v8-heap-profiler-agent-impl.cc", + "v8-heap-profiler-agent-impl.h", + "v8-inspector-impl.cc", + "v8-inspector-impl.h", + "v8-inspector-session-impl.cc", + "v8-inspector-session-impl.h", + "v8-profiler-agent-impl.cc", + "v8-profiler-agent-impl.h", + "v8-regex.cc", + "v8-regex.h", + "v8-runtime-agent-impl.cc", + "v8-runtime-agent-impl.h", + "v8-schema-agent-impl.cc", + "v8-schema-agent-impl.h", + "v8-stack-trace-impl.cc", + "v8-stack-trace-impl.h", + "v8-value-utils.cc", + "v8-value-utils.h", + "v8-webdriver-serializer.cc", + "v8-webdriver-serializer.h", + "value-mirror.cc", + "value-mirror.h", + ] +} + +#Target to generate all .cc files. +group("v8_generated_cc_files") { + testonly = true + + deps = [ ":protocol_generated_sources" ] +} diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/DEPS b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/DEPS new file mode 100644 index 000000000..0fc0768b6 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/DEPS @@ -0,0 +1,26 @@ +include_rules = [ + "-src", + "-include/v8-debug.h", + "+src/base/atomicops.h", + "+src/base/compiler-specific.h", + "+src/base/logging.h", + "+src/base/macros.h", + "+src/base/memory.h", + "+src/base/optional.h", + "+src/base/platform/mutex.h", + "+src/base/platform/platform.h", + "+src/base/platform/time.h", + "+src/base/safe_conversions.h", + "+src/base/template-utils.h", + "+src/base/v8-fallthrough.h", + "+src/numbers/conversions.h", + "+src/inspector", + "+src/tracing", + "+src/debug/debug-interface.h", + "+src/debug/interface-types.h", + "+src/base/vector.h", + "+src/base/enum-set.h", + "+src/utils/sha-256.h", + "+third_party/inspector_protocol/crdtp", + "+../../third_party/inspector_protocol/crdtp", +] diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/DIR_METADATA b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/DIR_METADATA new file mode 100644 index 000000000..3ba1106a5 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/DIR_METADATA @@ -0,0 +1,11 @@ +# 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: "Platform>DevTools>JavaScript" +} \ No newline at end of file diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/OWNERS b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/OWNERS new file mode 100644 index 000000000..d04f45a46 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/OWNERS @@ -0,0 +1,10 @@ +bmeurer@chromium.org +caseq@chromium.org +jarin@chromium.org +kimanh@chromium.org +leese@chromium.org +pfaffe@chromium.org +szuend@chromium.org +yangguo@chromium.org + +per-file PRESUBMIT.py=file:../../INFRA_OWNERS diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/crc32.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/crc32.h new file mode 100644 index 000000000..c20b56a66 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/crc32.h @@ -0,0 +1,16 @@ +// 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 V8_INSPECTOR_CRC32_H_ +#define V8_INSPECTOR_CRC32_H_ + +#include "src/inspector/string-16.h" + +namespace v8_inspector { + +int32_t computeCrc32(const String16&); + +} + +#endif // V8_INSPECTOR_CRC32_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/custom-preview.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/custom-preview.h new file mode 100644 index 000000000..d7b24adcc --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/custom-preview.h @@ -0,0 +1,24 @@ +// Copyright 2018 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_INSPECTOR_CUSTOM_PREVIEW_H_ +#define V8_INSPECTOR_CUSTOM_PREVIEW_H_ + +#include + +#include "src/inspector/protocol/Protocol.h" +#include "src/inspector/protocol/Runtime.h" + +namespace v8_inspector { + +const int kMaxCustomPreviewDepth = 20; + +void generateCustomPreview( + int sessionId, const String16& groupName, v8::Local object, + v8::MaybeLocal config, int maxDepth, + std::unique_ptr* preview); + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_CUSTOM_PREVIEW_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/injected-script.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/injected-script.h new file mode 100644 index 000000000..4f5bbf282 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/injected-script.h @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef V8_INSPECTOR_INJECTED_SCRIPT_H_ +#define V8_INSPECTOR_INJECTED_SCRIPT_H_ + +#include +#include +#include + +#include "include/v8-exception.h" +#include "include/v8-local-handle.h" +#include "include/v8-persistent-handle.h" +#include "src/base/macros.h" +#include "src/inspector/inspected-context.h" +#include "src/inspector/protocol/Forward.h" +#include "src/inspector/protocol/Runtime.h" +#include "src/inspector/v8-console.h" +#include "src/inspector/v8-debugger.h" + +namespace v8_inspector { + +class RemoteObjectId; +class V8InspectorImpl; +class V8InspectorSessionImpl; +class ValueMirror; +enum class WrapMode; + +using protocol::Maybe; +using protocol::Response; + +class EvaluateCallback { + public: + static void sendSuccess( + std::weak_ptr callback, InjectedScript* injectedScript, + std::unique_ptr result, + protocol::Maybe exceptionDetails); + static void sendFailure(std::weak_ptr callback, + InjectedScript* injectedScript, + const protocol::DispatchResponse& response); + + virtual ~EvaluateCallback() = default; + + private: + virtual void sendSuccess( + std::unique_ptr result, + protocol::Maybe + exceptionDetails) = 0; + virtual void sendFailure(const protocol::DispatchResponse& response) = 0; +}; + +class InjectedScript final { + public: + InjectedScript(InspectedContext*, int sessionId); + ~InjectedScript(); + InjectedScript(const InjectedScript&) = delete; + InjectedScript& operator=(const InjectedScript&) = delete; + + InspectedContext* context() const { return m_context; } + + Response getProperties( + v8::Local, const String16& groupName, bool ownProperties, + bool accessorPropertiesOnly, bool nonIndexedPropertiesOnly, + WrapMode wrapMode, + std::unique_ptr>* + result, + Maybe*); + + Response getInternalAndPrivateProperties( + v8::Local, const String16& groupName, + bool accessorPropertiesOnly, + std::unique_ptr< + protocol::Array>* + internalProperties, + std::unique_ptr< + protocol::Array>* + privateProperties); + + void releaseObject(const String16& objectId); + + Response wrapObject(v8::Local, const String16& groupName, + WrapMode wrapMode, + std::unique_ptr* result); + Response wrapObject(v8::Local, const String16& groupName, + WrapMode wrapMode, + v8::MaybeLocal customPreviewConfig, + int maxCustomPreviewDepth, + std::unique_ptr* result); + Response wrapObjectMirror( + const ValueMirror& mirror, const String16& groupName, WrapMode wrapMode, + v8::MaybeLocal customPreviewConfig, int maxCustomPreviewDepth, + std::unique_ptr* result); + std::unique_ptr wrapTable( + v8::Local table, v8::MaybeLocal columns); + + void addPromiseCallback(V8InspectorSessionImpl* session, + v8::MaybeLocal value, + const String16& objectGroup, WrapMode wrapMode, + bool replMode, bool throwOnSideEffect, + std::shared_ptr callback); + + Response findObject(const RemoteObjectId&, v8::Local*) const; + String16 objectGroupName(const RemoteObjectId&) const; + void releaseObjectGroup(const String16&); + void setCustomObjectFormatterEnabled(bool); + Response resolveCallArgument(protocol::Runtime::CallArgument*, + v8::Local* result); + + Response createExceptionDetails( + const v8::TryCatch&, const String16& groupName, + Maybe* result); + Response createExceptionDetails( + v8::Local message, v8::Local exception, + const String16& groupName, + Maybe* result); + + Response wrapEvaluateResult( + v8::MaybeLocal maybeResultValue, const v8::TryCatch&, + const String16& objectGroup, WrapMode wrapMode, bool throwOnSideEffect, + std::unique_ptr* result, + Maybe*); + v8::Local lastEvaluationResult() const; + void setLastEvaluationResult(v8::Local result); + + class Scope { + public: + Response initialize(); + void installCommandLineAPI(); + void ignoreExceptionsAndMuteConsole(); + void pretendUserGesture(); + void allowCodeGenerationFromStrings(); + v8::Local context() const { return m_context; } + InjectedScript* injectedScript() const { return m_injectedScript; } + const v8::TryCatch& tryCatch() const { return m_tryCatch; } + V8InspectorImpl* inspector() const { return m_inspector; } + + protected: + explicit Scope(V8InspectorSessionImpl*); + virtual ~Scope(); + virtual Response findInjectedScript(V8InspectorSessionImpl*) = 0; + + V8InspectorImpl* m_inspector; + InjectedScript* m_injectedScript; + + private: + void cleanup(); + v8::debug::ExceptionBreakState setPauseOnExceptionsState( + v8::debug::ExceptionBreakState); + + v8::HandleScope m_handleScope; + v8::TryCatch m_tryCatch; + v8::Local m_context; + std::unique_ptr m_commandLineAPIScope; + bool m_ignoreExceptionsAndMuteConsole; + v8::debug::ExceptionBreakState m_previousPauseOnExceptionsState; + bool m_userGesture; + bool m_allowEval; + int m_contextGroupId; + int m_sessionId; + }; + + class ContextScope : public Scope, + public V8InspectorSession::CommandLineAPIScope { + public: + ContextScope(V8InspectorSessionImpl*, int executionContextId); + ~ContextScope() override; + ContextScope(const ContextScope&) = delete; + ContextScope& operator=(const ContextScope&) = delete; + + private: + Response findInjectedScript(V8InspectorSessionImpl*) override; + int m_executionContextId; + }; + + class ObjectScope : public Scope { + public: + ObjectScope(V8InspectorSessionImpl*, const String16& remoteObjectId); + ~ObjectScope() override; + ObjectScope(const ObjectScope&) = delete; + ObjectScope& operator=(const ObjectScope&) = delete; + const String16& objectGroupName() const { return m_objectGroupName; } + v8::Local object() const { return m_object; } + + private: + Response findInjectedScript(V8InspectorSessionImpl*) override; + String16 m_remoteObjectId; + String16 m_objectGroupName; + v8::Local m_object; + }; + + class CallFrameScope : public Scope { + public: + CallFrameScope(V8InspectorSessionImpl*, const String16& remoteCallFrameId); + ~CallFrameScope() override; + CallFrameScope(const CallFrameScope&) = delete; + CallFrameScope& operator=(const CallFrameScope&) = delete; + size_t frameOrdinal() const { return m_frameOrdinal; } + + private: + Response findInjectedScript(V8InspectorSessionImpl*) override; + String16 m_remoteCallFrameId; + size_t m_frameOrdinal; + }; + String16 bindObject(v8::Local, const String16& groupName); + + private: + friend class EvaluateCallback; + + v8::Local commandLineAPI(); + void unbindObject(int id); + + static Response bindRemoteObjectIfNeeded( + int sessionId, v8::Local context, v8::Local, + const String16& groupName, protocol::Runtime::RemoteObject* remoteObject); + + class ProtocolPromiseHandler; + void discardEvaluateCallbacks(); + void deleteEvaluateCallback(std::shared_ptr callback); + Response addExceptionToDetails( + v8::Local exception, + protocol::Runtime::ExceptionDetails* exceptionDetails, + const String16& objectGroup); + + InspectedContext* m_context; + int m_sessionId; + v8::Global m_lastEvaluationResult; + v8::Global m_commandLineAPI; + int m_lastBoundObjectId = 1; + std::unordered_map> m_idToWrappedObject; + std::unordered_map m_idToObjectGroupName; + std::unordered_map> m_nameToObjectGroup; + std::unordered_set> m_evaluateCallbacks; + bool m_customPreviewEnabled = false; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_INJECTED_SCRIPT_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/inspected-context.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/inspected-context.h new file mode 100644 index 000000000..987d3a764 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/inspected-context.h @@ -0,0 +1,85 @@ +// 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_INSPECTOR_INSPECTED_CONTEXT_H_ +#define V8_INSPECTOR_INSPECTED_CONTEXT_H_ + +#include +#include +#include + +#include "include/v8-local-handle.h" +#include "include/v8-persistent-handle.h" +#include "src/base/macros.h" +#include "src/debug/debug-interface.h" +#include "src/inspector/string-16.h" +#include "src/inspector/v8-debugger-id.h" + +namespace v8 { +class Context; +class Object; +} // namespace v8 + +namespace v8_inspector { + +class InjectedScript; +class InjectedScriptHost; +class V8ContextInfo; +class V8InspectorImpl; + +enum class V8InternalValueType { kNone, kEntry, kScope, kScopeList }; + +class InspectedContext { + public: + ~InspectedContext(); + InspectedContext(const InspectedContext&) = delete; + InspectedContext& operator=(const InspectedContext&) = delete; + + static int contextId(v8::Local); + + v8::Local context() const; + int contextId() const { return m_contextId; } + int contextGroupId() const { return m_contextGroupId; } + String16 origin() const { return m_origin; } + String16 humanReadableName() const { return m_humanReadableName; } + internal::V8DebuggerId uniqueId() const { return m_uniqueId; } + String16 auxData() const { return m_auxData; } + + bool isReported(int sessionId) const; + void setReported(int sessionId, bool reported); + + v8::Isolate* isolate() const; + V8InspectorImpl* inspector() const { return m_inspector; } + + InjectedScript* getInjectedScript(int sessionId); + InjectedScript* createInjectedScript(int sessionId); + void discardInjectedScript(int sessionId); + + bool addInternalObject(v8::Local object, + V8InternalValueType type); + V8InternalValueType getInternalType(v8::Local object); + + private: + friend class V8InspectorImpl; + InspectedContext(V8InspectorImpl*, const V8ContextInfo&, int contextId); + + class WeakCallbackData; + + V8InspectorImpl* m_inspector; + v8::Global m_context; + int m_contextId; + int m_contextGroupId; + const String16 m_origin; + const String16 m_humanReadableName; + const String16 m_auxData; + const internal::V8DebuggerId m_uniqueId; + std::unordered_set m_reportedSessionIds; + std::unordered_map> m_injectedScripts; + WeakCallbackData* m_weakCallbackData; + v8::Global m_internalObjects; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_INSPECTED_CONTEXT_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/inspector_protocol_config.json b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/inspector_protocol_config.json new file mode 100644 index 000000000..ba96b3457 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/inspector_protocol_config.json @@ -0,0 +1,52 @@ +{ + "protocol": { + "package": "src/inspector/protocol", + "output": "protocol", + "namespace": ["v8_inspector", "protocol"], + "options": [ + { + "domain": "Schema", + "exported": ["Domain"] + }, + { + "domain": "Runtime", + "async": ["evaluate", "awaitPromise", "callFunctionOn", "runScript", "terminateExecution"], + "exported": ["StackTrace", "StackTraceId", "RemoteObject", "ExecutionContextId"] + }, + { + "domain": "Debugger", + "exported": ["SearchMatch", "paused.reason"] + }, + { + "domain": "Console" + }, + { + "domain": "Profiler" + }, + { + "domain": "HeapProfiler", + "async": ["collectGarbage"] + } + ] + }, + + "exported": { + "package": "include/inspector", + "output": "../../include/inspector", + "string_header": "v8-inspector.h", + "string_in": "StringView", + "string_out": "std::unique_ptr", + "to_string_out": "StringBufferFrom(std::move(%s))", + "export_macro": "V8_EXPORT" + }, + + "lib": { + "package": "src/inspector/protocol", + "output": "protocol", + "string_header": "src/inspector/string-util.h" + }, + + "crdtp": { + "namespace": "v8_crdtp" + } +} diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Console.cpp b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Console.cpp new file mode 100644 index 000000000..1246101a6 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Console.cpp @@ -0,0 +1,228 @@ +// This file is generated by TypeBuilder_cpp.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/inspector/protocol/Console.h" + +#include "src/inspector/protocol/Protocol.h" + +#include "third_party/inspector_protocol/crdtp/cbor.h" +#include "third_party/inspector_protocol/crdtp/find_by_first.h" +#include "third_party/inspector_protocol/crdtp/span.h" + +namespace v8_inspector { +namespace protocol { +namespace Console { + +using v8_crdtp::DeserializerState; +using v8_crdtp::ProtocolTypeTraits; + +// ------------- Enum values from types. + +const char Metainfo::domainName[] = "Console"; +const char Metainfo::commandPrefix[] = "Console."; +const char Metainfo::version[] = "1.3"; + + +const char* ConsoleMessage::SourceEnum::Xml = "xml"; +const char* ConsoleMessage::SourceEnum::Javascript = "javascript"; +const char* ConsoleMessage::SourceEnum::Network = "network"; +const char* ConsoleMessage::SourceEnum::ConsoleApi = "console-api"; +const char* ConsoleMessage::SourceEnum::Storage = "storage"; +const char* ConsoleMessage::SourceEnum::Appcache = "appcache"; +const char* ConsoleMessage::SourceEnum::Rendering = "rendering"; +const char* ConsoleMessage::SourceEnum::Security = "security"; +const char* ConsoleMessage::SourceEnum::Other = "other"; +const char* ConsoleMessage::SourceEnum::Deprecation = "deprecation"; +const char* ConsoleMessage::SourceEnum::Worker = "worker"; + +const char* ConsoleMessage::LevelEnum::Log = "log"; +const char* ConsoleMessage::LevelEnum::Warning = "warning"; +const char* ConsoleMessage::LevelEnum::Error = "error"; +const char* ConsoleMessage::LevelEnum::Debug = "debug"; +const char* ConsoleMessage::LevelEnum::Info = "info"; +V8_CRDTP_BEGIN_DESERIALIZER(ConsoleMessage) + V8_CRDTP_DESERIALIZE_FIELD_OPT("column", m_column), + V8_CRDTP_DESERIALIZE_FIELD("level", m_level), + V8_CRDTP_DESERIALIZE_FIELD_OPT("line", m_line), + V8_CRDTP_DESERIALIZE_FIELD("source", m_source), + V8_CRDTP_DESERIALIZE_FIELD("text", m_text), + V8_CRDTP_DESERIALIZE_FIELD_OPT("url", m_url), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(ConsoleMessage) + V8_CRDTP_SERIALIZE_FIELD("source", m_source); + V8_CRDTP_SERIALIZE_FIELD("level", m_level); + V8_CRDTP_SERIALIZE_FIELD("text", m_text); + V8_CRDTP_SERIALIZE_FIELD("url", m_url); + V8_CRDTP_SERIALIZE_FIELD("line", m_line); + V8_CRDTP_SERIALIZE_FIELD("column", m_column); +V8_CRDTP_END_SERIALIZER(); + + +// ------------- Enum values from params. + + +// ------------- Frontend notifications. + +void Frontend::messageAdded(std::unique_ptr message) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("message"), message); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Console.messageAdded", serializer.Finish())); +} + +void Frontend::flush() +{ + frontend_channel_->FlushProtocolNotifications(); +} + +void Frontend::sendRawNotification(std::unique_ptr notification) +{ + frontend_channel_->SendProtocolNotification(std::move(notification)); +} + +// --------------------- Dispatcher. + +class DomainDispatcherImpl : public protocol::DomainDispatcher { +public: + DomainDispatcherImpl(FrontendChannel* frontendChannel, Backend* backend) + : DomainDispatcher(frontendChannel) + , m_backend(backend) {} + ~DomainDispatcherImpl() override { } + + using CallHandler = void (DomainDispatcherImpl::*)(const v8_crdtp::Dispatchable& dispatchable); + + std::function Dispatch(v8_crdtp::span command_name) override; + + void clearMessages(const v8_crdtp::Dispatchable& dispatchable); + void disable(const v8_crdtp::Dispatchable& dispatchable); + void enable(const v8_crdtp::Dispatchable& dispatchable); + protected: + Backend* m_backend; +}; + +namespace { +// This helper method with a static map of command methods (instance methods +// of DomainDispatcherImpl declared just above) by their name is used immediately below, +// in the DomainDispatcherImpl::Dispatch method. +DomainDispatcherImpl::CallHandler CommandByName(v8_crdtp::span command_name) { + static auto* commands = [](){ + auto* commands = new std::vector, + DomainDispatcherImpl::CallHandler>>{ + { + v8_crdtp::SpanFrom("clearMessages"), + &DomainDispatcherImpl::clearMessages + }, + { + v8_crdtp::SpanFrom("disable"), + &DomainDispatcherImpl::disable + }, + { + v8_crdtp::SpanFrom("enable"), + &DomainDispatcherImpl::enable + }, + }; + return commands; + }(); + return v8_crdtp::FindByFirst(*commands, command_name, nullptr); +} +} // namespace + +std::function DomainDispatcherImpl::Dispatch(v8_crdtp::span command_name) { + CallHandler handler = CommandByName(command_name); + if (!handler) return nullptr; + + return [this, handler](const v8_crdtp::Dispatchable& dispatchable) { + (this->*handler)(dispatchable); + }; +} + + +namespace { + + +} // namespace + +void DomainDispatcherImpl::clearMessages(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->clearMessages(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Console.clearMessages"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::disable(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->disable(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Console.disable"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::enable(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->enable(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Console.enable"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { +// This helper method (with a static map of redirects) is used from Dispatcher::wire +// immediately below. +const std::vector, v8_crdtp::span>>& SortedRedirects() { + static auto* redirects = [](){ + auto* redirects = new std::vector, v8_crdtp::span>>{ + }; + return redirects; + }(); + return *redirects; +} +} // namespace + +// static +void Dispatcher::wire(UberDispatcher* uber, Backend* backend) +{ + auto dispatcher = std::make_unique(uber->channel(), backend); + uber->WireBackend(v8_crdtp::SpanFrom("Console"), SortedRedirects(), std::move(dispatcher)); +} + +} // Console +} // namespace v8_inspector +} // namespace protocol diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Console.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Console.h new file mode 100644 index 000000000..4a4b7f4e2 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Console.h @@ -0,0 +1,211 @@ +// This file is generated by TypeBuilder_h.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef v8_inspector_protocol_Console_h +#define v8_inspector_protocol_Console_h + +#include "src/inspector/protocol/Protocol.h" +// For each imported domain we generate a ValueConversions struct instead of a full domain definition +// and include Domain::API version from there. +#include "src/inspector/protocol/Runtime.h" + +namespace v8_inspector { +namespace protocol { +namespace Console { +class ConsoleMessage; + +// ------------- Forward and enum declarations. + +// ------------- Type and builder declarations. + +class ConsoleMessage : public ::v8_crdtp::ProtocolObject { +public: + ~ConsoleMessage() override { } + + struct SourceEnum { + static const char* Xml; + static const char* Javascript; + static const char* Network; + static const char* ConsoleApi; + static const char* Storage; + static const char* Appcache; + static const char* Rendering; + static const char* Security; + static const char* Other; + static const char* Deprecation; + static const char* Worker; + }; // SourceEnum + + String getSource() { return m_source; } + void setSource(const String& value) { m_source = value; } + + struct LevelEnum { + static const char* Log; + static const char* Warning; + static const char* Error; + static const char* Debug; + static const char* Info; + }; // LevelEnum + + String getLevel() { return m_level; } + void setLevel(const String& value) { m_level = value; } + + String getText() { return m_text; } + void setText(const String& value) { m_text = value; } + + bool hasUrl() { return m_url.isJust(); } + String getUrl(const String& defaultValue) { return m_url.isJust() ? m_url.fromJust() : defaultValue; } + void setUrl(const String& value) { m_url = value; } + + bool hasLine() { return m_line.isJust(); } + int getLine(int defaultValue) { return m_line.isJust() ? m_line.fromJust() : defaultValue; } + void setLine(int value) { m_line = value; } + + bool hasColumn() { return m_column.isJust(); } + int getColumn(int defaultValue) { return m_column.isJust() ? m_column.fromJust() : defaultValue; } + void setColumn(int value) { m_column = value; } + + template + class ConsoleMessageBuilder { + public: + enum { + NoFieldsSet = 0, + SourceSet = 1 << 1, + LevelSet = 1 << 2, + TextSet = 1 << 3, + AllFieldsSet = (SourceSet | LevelSet | TextSet | 0)}; + + + ConsoleMessageBuilder& setSource(const String& value) + { + static_assert(!(STATE & SourceSet), "property source should not be set yet"); + m_result->setSource(value); + return castState(); + } + + ConsoleMessageBuilder& setLevel(const String& value) + { + static_assert(!(STATE & LevelSet), "property level should not be set yet"); + m_result->setLevel(value); + return castState(); + } + + ConsoleMessageBuilder& setText(const String& value) + { + static_assert(!(STATE & TextSet), "property text should not be set yet"); + m_result->setText(value); + return castState(); + } + + ConsoleMessageBuilder& setUrl(const String& value) + { + m_result->setUrl(value); + return *this; + } + + ConsoleMessageBuilder& setLine(int value) + { + m_result->setLine(value); + return *this; + } + + ConsoleMessageBuilder& setColumn(int value) + { + m_result->setColumn(value); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class ConsoleMessage; + ConsoleMessageBuilder() : m_result(new ConsoleMessage()) { } + + template ConsoleMessageBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static ConsoleMessageBuilder<0> create() + { + return ConsoleMessageBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + ConsoleMessage() + { + } + + String m_source; + String m_level; + String m_text; + Maybe m_url; + Maybe m_line; + Maybe m_column; +}; + + +// ------------- Backend interface. + +class Backend { +public: + virtual ~Backend() { } + + virtual DispatchResponse clearMessages() = 0; + virtual DispatchResponse disable() = 0; + virtual DispatchResponse enable() = 0; + +}; + +// ------------- Frontend interface. + +class Frontend { +public: + explicit Frontend(FrontendChannel* frontend_channel) : frontend_channel_(frontend_channel) {} + void messageAdded(std::unique_ptr message); + + void flush(); + void sendRawNotification(std::unique_ptr); + private: + FrontendChannel* frontend_channel_; +}; + +// ------------- Dispatcher. + +class Dispatcher { +public: + static void wire(UberDispatcher*, Backend*); + +private: + Dispatcher() { } +}; + +// ------------- Metainfo. + +class Metainfo { +public: + using BackendClass = Backend; + using FrontendClass = Frontend; + using DispatcherClass = Dispatcher; + static const char domainName[]; + static const char commandPrefix[]; + static const char version[]; +}; + +} // namespace Console +} // namespace v8_inspector +} // namespace protocol + +#endif // !defined(v8_inspector_protocol_Console_h) diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Debugger.cpp b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Debugger.cpp new file mode 100644 index 000000000..642d65a30 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Debugger.cpp @@ -0,0 +1,1884 @@ +// This file is generated by TypeBuilder_cpp.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/inspector/protocol/Debugger.h" + +#include "src/inspector/protocol/Protocol.h" + +#include "third_party/inspector_protocol/crdtp/cbor.h" +#include "third_party/inspector_protocol/crdtp/find_by_first.h" +#include "third_party/inspector_protocol/crdtp/span.h" + +namespace v8_inspector { +namespace protocol { +namespace Debugger { + +using v8_crdtp::DeserializerState; +using v8_crdtp::ProtocolTypeTraits; + +// ------------- Enum values from types. + +const char Metainfo::domainName[] = "Debugger"; +const char Metainfo::commandPrefix[] = "Debugger."; +const char Metainfo::version[] = "1.3"; + + + +V8_CRDTP_BEGIN_DESERIALIZER(Location) + V8_CRDTP_DESERIALIZE_FIELD_OPT("columnNumber", m_columnNumber), + V8_CRDTP_DESERIALIZE_FIELD("lineNumber", m_lineNumber), + V8_CRDTP_DESERIALIZE_FIELD("scriptId", m_scriptId), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(Location) + V8_CRDTP_SERIALIZE_FIELD("scriptId", m_scriptId); + V8_CRDTP_SERIALIZE_FIELD("lineNumber", m_lineNumber); + V8_CRDTP_SERIALIZE_FIELD("columnNumber", m_columnNumber); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(ScriptPosition) + V8_CRDTP_DESERIALIZE_FIELD("columnNumber", m_columnNumber), + V8_CRDTP_DESERIALIZE_FIELD("lineNumber", m_lineNumber), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(ScriptPosition) + V8_CRDTP_SERIALIZE_FIELD("lineNumber", m_lineNumber); + V8_CRDTP_SERIALIZE_FIELD("columnNumber", m_columnNumber); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(LocationRange) + V8_CRDTP_DESERIALIZE_FIELD("end", m_end), + V8_CRDTP_DESERIALIZE_FIELD("scriptId", m_scriptId), + V8_CRDTP_DESERIALIZE_FIELD("start", m_start), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(LocationRange) + V8_CRDTP_SERIALIZE_FIELD("scriptId", m_scriptId); + V8_CRDTP_SERIALIZE_FIELD("start", m_start); + V8_CRDTP_SERIALIZE_FIELD("end", m_end); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(CallFrame) + V8_CRDTP_DESERIALIZE_FIELD("callFrameId", m_callFrameId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("canBeRestarted", m_canBeRestarted), + V8_CRDTP_DESERIALIZE_FIELD_OPT("functionLocation", m_functionLocation), + V8_CRDTP_DESERIALIZE_FIELD("functionName", m_functionName), + V8_CRDTP_DESERIALIZE_FIELD("location", m_location), + V8_CRDTP_DESERIALIZE_FIELD_OPT("returnValue", m_returnValue), + V8_CRDTP_DESERIALIZE_FIELD("scopeChain", m_scopeChain), + V8_CRDTP_DESERIALIZE_FIELD("this", m_this), + V8_CRDTP_DESERIALIZE_FIELD("url", m_url), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(CallFrame) + V8_CRDTP_SERIALIZE_FIELD("callFrameId", m_callFrameId); + V8_CRDTP_SERIALIZE_FIELD("functionName", m_functionName); + V8_CRDTP_SERIALIZE_FIELD("functionLocation", m_functionLocation); + V8_CRDTP_SERIALIZE_FIELD("location", m_location); + V8_CRDTP_SERIALIZE_FIELD("url", m_url); + V8_CRDTP_SERIALIZE_FIELD("scopeChain", m_scopeChain); + V8_CRDTP_SERIALIZE_FIELD("this", m_this); + V8_CRDTP_SERIALIZE_FIELD("returnValue", m_returnValue); + V8_CRDTP_SERIALIZE_FIELD("canBeRestarted", m_canBeRestarted); +V8_CRDTP_END_SERIALIZER(); + + + +const char* Scope::TypeEnum::Global = "global"; +const char* Scope::TypeEnum::Local = "local"; +const char* Scope::TypeEnum::With = "with"; +const char* Scope::TypeEnum::Closure = "closure"; +const char* Scope::TypeEnum::Catch = "catch"; +const char* Scope::TypeEnum::Block = "block"; +const char* Scope::TypeEnum::Script = "script"; +const char* Scope::TypeEnum::Eval = "eval"; +const char* Scope::TypeEnum::Module = "module"; +const char* Scope::TypeEnum::WasmExpressionStack = "wasm-expression-stack"; +V8_CRDTP_BEGIN_DESERIALIZER(Scope) + V8_CRDTP_DESERIALIZE_FIELD_OPT("endLocation", m_endLocation), + V8_CRDTP_DESERIALIZE_FIELD_OPT("name", m_name), + V8_CRDTP_DESERIALIZE_FIELD("object", m_object), + V8_CRDTP_DESERIALIZE_FIELD_OPT("startLocation", m_startLocation), + V8_CRDTP_DESERIALIZE_FIELD("type", m_type), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(Scope) + V8_CRDTP_SERIALIZE_FIELD("type", m_type); + V8_CRDTP_SERIALIZE_FIELD("object", m_object); + V8_CRDTP_SERIALIZE_FIELD("name", m_name); + V8_CRDTP_SERIALIZE_FIELD("startLocation", m_startLocation); + V8_CRDTP_SERIALIZE_FIELD("endLocation", m_endLocation); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(SearchMatch) + V8_CRDTP_DESERIALIZE_FIELD("lineContent", m_lineContent), + V8_CRDTP_DESERIALIZE_FIELD("lineNumber", m_lineNumber), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(SearchMatch) + V8_CRDTP_SERIALIZE_FIELD("lineNumber", m_lineNumber); + V8_CRDTP_SERIALIZE_FIELD("lineContent", m_lineContent); +V8_CRDTP_END_SERIALIZER(); + +// static +std::unique_ptr API::SearchMatch::fromBinary(const uint8_t* data, size_t length) +{ + return protocol::Debugger::SearchMatch::FromBinary(data, length); +} + + +const char* BreakLocation::TypeEnum::DebuggerStatement = "debuggerStatement"; +const char* BreakLocation::TypeEnum::Call = "call"; +const char* BreakLocation::TypeEnum::Return = "return"; +V8_CRDTP_BEGIN_DESERIALIZER(BreakLocation) + V8_CRDTP_DESERIALIZE_FIELD_OPT("columnNumber", m_columnNumber), + V8_CRDTP_DESERIALIZE_FIELD("lineNumber", m_lineNumber), + V8_CRDTP_DESERIALIZE_FIELD("scriptId", m_scriptId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("type", m_type), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(BreakLocation) + V8_CRDTP_SERIALIZE_FIELD("scriptId", m_scriptId); + V8_CRDTP_SERIALIZE_FIELD("lineNumber", m_lineNumber); + V8_CRDTP_SERIALIZE_FIELD("columnNumber", m_columnNumber); + V8_CRDTP_SERIALIZE_FIELD("type", m_type); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(WasmDisassemblyChunk) + V8_CRDTP_DESERIALIZE_FIELD("bytecodeOffsets", m_bytecodeOffsets), + V8_CRDTP_DESERIALIZE_FIELD("lines", m_lines), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(WasmDisassemblyChunk) + V8_CRDTP_SERIALIZE_FIELD("lines", m_lines); + V8_CRDTP_SERIALIZE_FIELD("bytecodeOffsets", m_bytecodeOffsets); +V8_CRDTP_END_SERIALIZER(); + + +namespace ScriptLanguageEnum { +const char JavaScript[] = "JavaScript"; +const char WebAssembly[] = "WebAssembly"; +} // namespace ScriptLanguageEnum + + + +const char* DebugSymbols::TypeEnum::None = "None"; +const char* DebugSymbols::TypeEnum::SourceMap = "SourceMap"; +const char* DebugSymbols::TypeEnum::EmbeddedDWARF = "EmbeddedDWARF"; +const char* DebugSymbols::TypeEnum::ExternalDWARF = "ExternalDWARF"; +V8_CRDTP_BEGIN_DESERIALIZER(DebugSymbols) + V8_CRDTP_DESERIALIZE_FIELD_OPT("externalURL", m_externalURL), + V8_CRDTP_DESERIALIZE_FIELD("type", m_type), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(DebugSymbols) + V8_CRDTP_SERIALIZE_FIELD("type", m_type); + V8_CRDTP_SERIALIZE_FIELD("externalURL", m_externalURL); +V8_CRDTP_END_SERIALIZER(); + + +// ------------- Enum values from params. + + +namespace ContinueToLocation { +namespace TargetCallFramesEnum { +const char* Any = "any"; +const char* Current = "current"; +} // namespace TargetCallFramesEnum +} // namespace ContinueToLocation + +namespace RestartFrame { +namespace ModeEnum { +const char* StepInto = "StepInto"; +} // namespace ModeEnum +} // namespace RestartFrame + +namespace SetInstrumentationBreakpoint { +namespace InstrumentationEnum { +const char* BeforeScriptExecution = "beforeScriptExecution"; +const char* BeforeScriptWithSourceMapExecution = "beforeScriptWithSourceMapExecution"; +} // namespace InstrumentationEnum +} // namespace SetInstrumentationBreakpoint + +namespace SetPauseOnExceptions { +namespace StateEnum { +const char* None = "none"; +const char* Caught = "caught"; +const char* Uncaught = "uncaught"; +const char* All = "all"; +} // namespace StateEnum +} // namespace SetPauseOnExceptions + +namespace SetScriptSource { +namespace StatusEnum { +const char* Ok = "Ok"; +const char* CompileError = "CompileError"; +const char* BlockedByActiveGenerator = "BlockedByActiveGenerator"; +const char* BlockedByActiveFunction = "BlockedByActiveFunction"; +} // namespace StatusEnum +} // namespace SetScriptSource + +namespace Paused { +namespace ReasonEnum { +const char* Ambiguous = "ambiguous"; +const char* Assert = "assert"; +const char* CSPViolation = "CSPViolation"; +const char* DebugCommand = "debugCommand"; +const char* DOM = "DOM"; +const char* EventListener = "EventListener"; +const char* Exception = "exception"; +const char* Instrumentation = "instrumentation"; +const char* OOM = "OOM"; +const char* Other = "other"; +const char* PromiseRejection = "promiseRejection"; +const char* XHR = "XHR"; +} // namespace ReasonEnum +} // namespace Paused + +namespace API { +namespace Paused { +namespace ReasonEnum { +const char* Ambiguous = "ambiguous"; +const char* Assert = "assert"; +const char* CSPViolation = "CSPViolation"; +const char* DebugCommand = "debugCommand"; +const char* DOM = "DOM"; +const char* EventListener = "EventListener"; +const char* Exception = "exception"; +const char* Instrumentation = "instrumentation"; +const char* OOM = "OOM"; +const char* Other = "other"; +const char* PromiseRejection = "promiseRejection"; +const char* XHR = "XHR"; +} // namespace ReasonEnum +} // namespace Paused +} // namespace API + +// ------------- Frontend notifications. + +void Frontend::breakpointResolved(const String& breakpointId, std::unique_ptr location) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("breakpointId"), breakpointId); + serializer.AddField(v8_crdtp::MakeSpan("location"), location); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Debugger.breakpointResolved", serializer.Finish())); +} + +void Frontend::paused(std::unique_ptr> callFrames, const String& reason, Maybe data, Maybe> hitBreakpoints, Maybe asyncStackTrace, Maybe asyncStackTraceId, Maybe asyncCallStackTraceId) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("callFrames"), callFrames); + serializer.AddField(v8_crdtp::MakeSpan("reason"), reason); + serializer.AddField(v8_crdtp::MakeSpan("data"), data); + serializer.AddField(v8_crdtp::MakeSpan("hitBreakpoints"), hitBreakpoints); + serializer.AddField(v8_crdtp::MakeSpan("asyncStackTrace"), asyncStackTrace); + serializer.AddField(v8_crdtp::MakeSpan("asyncStackTraceId"), asyncStackTraceId); + serializer.AddField(v8_crdtp::MakeSpan("asyncCallStackTraceId"), asyncCallStackTraceId); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Debugger.paused", serializer.Finish())); +} + +void Frontend::resumed() +{ + if (!frontend_channel_) + return; + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Debugger.resumed")); +} + +void Frontend::scriptFailedToParse(const String& scriptId, const String& url, int startLine, int startColumn, int endLine, int endColumn, int executionContextId, const String& hash, Maybe executionContextAuxData, Maybe sourceMapURL, Maybe hasSourceURL, Maybe isModule, Maybe length, Maybe stackTrace, Maybe codeOffset, Maybe scriptLanguage, Maybe embedderName) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("scriptId"), scriptId); + serializer.AddField(v8_crdtp::MakeSpan("url"), url); + serializer.AddField(v8_crdtp::MakeSpan("startLine"), startLine); + serializer.AddField(v8_crdtp::MakeSpan("startColumn"), startColumn); + serializer.AddField(v8_crdtp::MakeSpan("endLine"), endLine); + serializer.AddField(v8_crdtp::MakeSpan("endColumn"), endColumn); + serializer.AddField(v8_crdtp::MakeSpan("executionContextId"), executionContextId); + serializer.AddField(v8_crdtp::MakeSpan("hash"), hash); + serializer.AddField(v8_crdtp::MakeSpan("executionContextAuxData"), executionContextAuxData); + serializer.AddField(v8_crdtp::MakeSpan("sourceMapURL"), sourceMapURL); + serializer.AddField(v8_crdtp::MakeSpan("hasSourceURL"), hasSourceURL); + serializer.AddField(v8_crdtp::MakeSpan("isModule"), isModule); + serializer.AddField(v8_crdtp::MakeSpan("length"), length); + serializer.AddField(v8_crdtp::MakeSpan("stackTrace"), stackTrace); + serializer.AddField(v8_crdtp::MakeSpan("codeOffset"), codeOffset); + serializer.AddField(v8_crdtp::MakeSpan("scriptLanguage"), scriptLanguage); + serializer.AddField(v8_crdtp::MakeSpan("embedderName"), embedderName); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Debugger.scriptFailedToParse", serializer.Finish())); +} + +void Frontend::scriptParsed(const String& scriptId, const String& url, int startLine, int startColumn, int endLine, int endColumn, int executionContextId, const String& hash, Maybe executionContextAuxData, Maybe isLiveEdit, Maybe sourceMapURL, Maybe hasSourceURL, Maybe isModule, Maybe length, Maybe stackTrace, Maybe codeOffset, Maybe scriptLanguage, Maybe debugSymbols, Maybe embedderName) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("scriptId"), scriptId); + serializer.AddField(v8_crdtp::MakeSpan("url"), url); + serializer.AddField(v8_crdtp::MakeSpan("startLine"), startLine); + serializer.AddField(v8_crdtp::MakeSpan("startColumn"), startColumn); + serializer.AddField(v8_crdtp::MakeSpan("endLine"), endLine); + serializer.AddField(v8_crdtp::MakeSpan("endColumn"), endColumn); + serializer.AddField(v8_crdtp::MakeSpan("executionContextId"), executionContextId); + serializer.AddField(v8_crdtp::MakeSpan("hash"), hash); + serializer.AddField(v8_crdtp::MakeSpan("executionContextAuxData"), executionContextAuxData); + serializer.AddField(v8_crdtp::MakeSpan("isLiveEdit"), isLiveEdit); + serializer.AddField(v8_crdtp::MakeSpan("sourceMapURL"), sourceMapURL); + serializer.AddField(v8_crdtp::MakeSpan("hasSourceURL"), hasSourceURL); + serializer.AddField(v8_crdtp::MakeSpan("isModule"), isModule); + serializer.AddField(v8_crdtp::MakeSpan("length"), length); + serializer.AddField(v8_crdtp::MakeSpan("stackTrace"), stackTrace); + serializer.AddField(v8_crdtp::MakeSpan("codeOffset"), codeOffset); + serializer.AddField(v8_crdtp::MakeSpan("scriptLanguage"), scriptLanguage); + serializer.AddField(v8_crdtp::MakeSpan("debugSymbols"), debugSymbols); + serializer.AddField(v8_crdtp::MakeSpan("embedderName"), embedderName); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Debugger.scriptParsed", serializer.Finish())); +} + +void Frontend::flush() +{ + frontend_channel_->FlushProtocolNotifications(); +} + +void Frontend::sendRawNotification(std::unique_ptr notification) +{ + frontend_channel_->SendProtocolNotification(std::move(notification)); +} + +// --------------------- Dispatcher. + +class DomainDispatcherImpl : public protocol::DomainDispatcher { +public: + DomainDispatcherImpl(FrontendChannel* frontendChannel, Backend* backend) + : DomainDispatcher(frontendChannel) + , m_backend(backend) {} + ~DomainDispatcherImpl() override { } + + using CallHandler = void (DomainDispatcherImpl::*)(const v8_crdtp::Dispatchable& dispatchable); + + std::function Dispatch(v8_crdtp::span command_name) override; + + void continueToLocation(const v8_crdtp::Dispatchable& dispatchable); + void disable(const v8_crdtp::Dispatchable& dispatchable); + void enable(const v8_crdtp::Dispatchable& dispatchable); + void evaluateOnCallFrame(const v8_crdtp::Dispatchable& dispatchable); + void getPossibleBreakpoints(const v8_crdtp::Dispatchable& dispatchable); + void getScriptSource(const v8_crdtp::Dispatchable& dispatchable); + void disassembleWasmModule(const v8_crdtp::Dispatchable& dispatchable); + void nextWasmDisassemblyChunk(const v8_crdtp::Dispatchable& dispatchable); + void getWasmBytecode(const v8_crdtp::Dispatchable& dispatchable); + void getStackTrace(const v8_crdtp::Dispatchable& dispatchable); + void pause(const v8_crdtp::Dispatchable& dispatchable); + void pauseOnAsyncCall(const v8_crdtp::Dispatchable& dispatchable); + void removeBreakpoint(const v8_crdtp::Dispatchable& dispatchable); + void restartFrame(const v8_crdtp::Dispatchable& dispatchable); + void resume(const v8_crdtp::Dispatchable& dispatchable); + void searchInContent(const v8_crdtp::Dispatchable& dispatchable); + void setAsyncCallStackDepth(const v8_crdtp::Dispatchable& dispatchable); + void setBlackboxPatterns(const v8_crdtp::Dispatchable& dispatchable); + void setBlackboxedRanges(const v8_crdtp::Dispatchable& dispatchable); + void setBreakpoint(const v8_crdtp::Dispatchable& dispatchable); + void setInstrumentationBreakpoint(const v8_crdtp::Dispatchable& dispatchable); + void setBreakpointByUrl(const v8_crdtp::Dispatchable& dispatchable); + void setBreakpointOnFunctionCall(const v8_crdtp::Dispatchable& dispatchable); + void setBreakpointsActive(const v8_crdtp::Dispatchable& dispatchable); + void setPauseOnExceptions(const v8_crdtp::Dispatchable& dispatchable); + void setReturnValue(const v8_crdtp::Dispatchable& dispatchable); + void setScriptSource(const v8_crdtp::Dispatchable& dispatchable); + void setSkipAllPauses(const v8_crdtp::Dispatchable& dispatchable); + void setVariableValue(const v8_crdtp::Dispatchable& dispatchable); + void stepInto(const v8_crdtp::Dispatchable& dispatchable); + void stepOut(const v8_crdtp::Dispatchable& dispatchable); + void stepOver(const v8_crdtp::Dispatchable& dispatchable); + protected: + Backend* m_backend; +}; + +namespace { +// This helper method with a static map of command methods (instance methods +// of DomainDispatcherImpl declared just above) by their name is used immediately below, +// in the DomainDispatcherImpl::Dispatch method. +DomainDispatcherImpl::CallHandler CommandByName(v8_crdtp::span command_name) { + static auto* commands = [](){ + auto* commands = new std::vector, + DomainDispatcherImpl::CallHandler>>{ + { + v8_crdtp::SpanFrom("continueToLocation"), + &DomainDispatcherImpl::continueToLocation + }, + { + v8_crdtp::SpanFrom("disable"), + &DomainDispatcherImpl::disable + }, + { + v8_crdtp::SpanFrom("disassembleWasmModule"), + &DomainDispatcherImpl::disassembleWasmModule + }, + { + v8_crdtp::SpanFrom("enable"), + &DomainDispatcherImpl::enable + }, + { + v8_crdtp::SpanFrom("evaluateOnCallFrame"), + &DomainDispatcherImpl::evaluateOnCallFrame + }, + { + v8_crdtp::SpanFrom("getPossibleBreakpoints"), + &DomainDispatcherImpl::getPossibleBreakpoints + }, + { + v8_crdtp::SpanFrom("getScriptSource"), + &DomainDispatcherImpl::getScriptSource + }, + { + v8_crdtp::SpanFrom("getStackTrace"), + &DomainDispatcherImpl::getStackTrace + }, + { + v8_crdtp::SpanFrom("getWasmBytecode"), + &DomainDispatcherImpl::getWasmBytecode + }, + { + v8_crdtp::SpanFrom("nextWasmDisassemblyChunk"), + &DomainDispatcherImpl::nextWasmDisassemblyChunk + }, + { + v8_crdtp::SpanFrom("pause"), + &DomainDispatcherImpl::pause + }, + { + v8_crdtp::SpanFrom("pauseOnAsyncCall"), + &DomainDispatcherImpl::pauseOnAsyncCall + }, + { + v8_crdtp::SpanFrom("removeBreakpoint"), + &DomainDispatcherImpl::removeBreakpoint + }, + { + v8_crdtp::SpanFrom("restartFrame"), + &DomainDispatcherImpl::restartFrame + }, + { + v8_crdtp::SpanFrom("resume"), + &DomainDispatcherImpl::resume + }, + { + v8_crdtp::SpanFrom("searchInContent"), + &DomainDispatcherImpl::searchInContent + }, + { + v8_crdtp::SpanFrom("setAsyncCallStackDepth"), + &DomainDispatcherImpl::setAsyncCallStackDepth + }, + { + v8_crdtp::SpanFrom("setBlackboxPatterns"), + &DomainDispatcherImpl::setBlackboxPatterns + }, + { + v8_crdtp::SpanFrom("setBlackboxedRanges"), + &DomainDispatcherImpl::setBlackboxedRanges + }, + { + v8_crdtp::SpanFrom("setBreakpoint"), + &DomainDispatcherImpl::setBreakpoint + }, + { + v8_crdtp::SpanFrom("setBreakpointByUrl"), + &DomainDispatcherImpl::setBreakpointByUrl + }, + { + v8_crdtp::SpanFrom("setBreakpointOnFunctionCall"), + &DomainDispatcherImpl::setBreakpointOnFunctionCall + }, + { + v8_crdtp::SpanFrom("setBreakpointsActive"), + &DomainDispatcherImpl::setBreakpointsActive + }, + { + v8_crdtp::SpanFrom("setInstrumentationBreakpoint"), + &DomainDispatcherImpl::setInstrumentationBreakpoint + }, + { + v8_crdtp::SpanFrom("setPauseOnExceptions"), + &DomainDispatcherImpl::setPauseOnExceptions + }, + { + v8_crdtp::SpanFrom("setReturnValue"), + &DomainDispatcherImpl::setReturnValue + }, + { + v8_crdtp::SpanFrom("setScriptSource"), + &DomainDispatcherImpl::setScriptSource + }, + { + v8_crdtp::SpanFrom("setSkipAllPauses"), + &DomainDispatcherImpl::setSkipAllPauses + }, + { + v8_crdtp::SpanFrom("setVariableValue"), + &DomainDispatcherImpl::setVariableValue + }, + { + v8_crdtp::SpanFrom("stepInto"), + &DomainDispatcherImpl::stepInto + }, + { + v8_crdtp::SpanFrom("stepOut"), + &DomainDispatcherImpl::stepOut + }, + { + v8_crdtp::SpanFrom("stepOver"), + &DomainDispatcherImpl::stepOver + }, + }; + return commands; + }(); + return v8_crdtp::FindByFirst(*commands, command_name, nullptr); +} +} // namespace + +std::function DomainDispatcherImpl::Dispatch(v8_crdtp::span command_name) { + CallHandler handler = CommandByName(command_name); + if (!handler) return nullptr; + + return [this, handler](const v8_crdtp::Dispatchable& dispatchable) { + (this->*handler)(dispatchable); + }; +} + + +namespace { + +struct continueToLocationParams : public v8_crdtp::DeserializableProtocolObject { + std::unique_ptr location; + Maybe targetCallFrames; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(continueToLocationParams) + V8_CRDTP_DESERIALIZE_FIELD("location", location), + V8_CRDTP_DESERIALIZE_FIELD_OPT("targetCallFrames", targetCallFrames), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::continueToLocation(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + continueToLocationParams params; + if (!continueToLocationParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->continueToLocation(std::move(params.location), std::move(params.targetCallFrames)); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.continueToLocation"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::disable(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->disable(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.disable"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct enableParams : public v8_crdtp::DeserializableProtocolObject { + Maybe maxScriptsCacheSize; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(enableParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("maxScriptsCacheSize", maxScriptsCacheSize), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::enable(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + enableParams params; + if (!enableParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + String out_debuggerId; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->enable(std::move(params.maxScriptsCacheSize), &out_debuggerId); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.enable"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("debuggerId"), out_debuggerId); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct evaluateOnCallFrameParams : public v8_crdtp::DeserializableProtocolObject { + String callFrameId; + String expression; + Maybe objectGroup; + Maybe includeCommandLineAPI; + Maybe silent; + Maybe returnByValue; + Maybe generatePreview; + Maybe throwOnSideEffect; + Maybe timeout; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(evaluateOnCallFrameParams) + V8_CRDTP_DESERIALIZE_FIELD("callFrameId", callFrameId), + V8_CRDTP_DESERIALIZE_FIELD("expression", expression), + V8_CRDTP_DESERIALIZE_FIELD_OPT("generatePreview", generatePreview), + V8_CRDTP_DESERIALIZE_FIELD_OPT("includeCommandLineAPI", includeCommandLineAPI), + V8_CRDTP_DESERIALIZE_FIELD_OPT("objectGroup", objectGroup), + V8_CRDTP_DESERIALIZE_FIELD_OPT("returnByValue", returnByValue), + V8_CRDTP_DESERIALIZE_FIELD_OPT("silent", silent), + V8_CRDTP_DESERIALIZE_FIELD_OPT("throwOnSideEffect", throwOnSideEffect), + V8_CRDTP_DESERIALIZE_FIELD_OPT("timeout", timeout), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::evaluateOnCallFrame(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + evaluateOnCallFrameParams params; + if (!evaluateOnCallFrameParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + std::unique_ptr out_result; + Maybe out_exceptionDetails; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->evaluateOnCallFrame(params.callFrameId, params.expression, std::move(params.objectGroup), std::move(params.includeCommandLineAPI), std::move(params.silent), std::move(params.returnByValue), std::move(params.generatePreview), std::move(params.throwOnSideEffect), std::move(params.timeout), &out_result, &out_exceptionDetails); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.evaluateOnCallFrame"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("result"), out_result); + serializer.AddField(v8_crdtp::MakeSpan("exceptionDetails"), out_exceptionDetails); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct getPossibleBreakpointsParams : public v8_crdtp::DeserializableProtocolObject { + std::unique_ptr start; + Maybe end; + Maybe restrictToFunction; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(getPossibleBreakpointsParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("end", end), + V8_CRDTP_DESERIALIZE_FIELD_OPT("restrictToFunction", restrictToFunction), + V8_CRDTP_DESERIALIZE_FIELD("start", start), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::getPossibleBreakpoints(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + getPossibleBreakpointsParams params; + if (!getPossibleBreakpointsParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + std::unique_ptr> out_locations; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->getPossibleBreakpoints(std::move(params.start), std::move(params.end), std::move(params.restrictToFunction), &out_locations); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.getPossibleBreakpoints"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("locations"), out_locations); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct getScriptSourceParams : public v8_crdtp::DeserializableProtocolObject { + String scriptId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(getScriptSourceParams) + V8_CRDTP_DESERIALIZE_FIELD("scriptId", scriptId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::getScriptSource(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + getScriptSourceParams params; + if (!getScriptSourceParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + String out_scriptSource; + Maybe out_bytecode; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->getScriptSource(params.scriptId, &out_scriptSource, &out_bytecode); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.getScriptSource"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("scriptSource"), out_scriptSource); + serializer.AddField(v8_crdtp::MakeSpan("bytecode"), out_bytecode); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct disassembleWasmModuleParams : public v8_crdtp::DeserializableProtocolObject { + String scriptId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(disassembleWasmModuleParams) + V8_CRDTP_DESERIALIZE_FIELD("scriptId", scriptId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::disassembleWasmModule(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + disassembleWasmModuleParams params; + if (!disassembleWasmModuleParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + Maybe out_streamId; + int out_totalNumberOfLines; + std::unique_ptr> out_functionBodyOffsets; + std::unique_ptr out_chunk; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->disassembleWasmModule(params.scriptId, &out_streamId, &out_totalNumberOfLines, &out_functionBodyOffsets, &out_chunk); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.disassembleWasmModule"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("streamId"), out_streamId); + serializer.AddField(v8_crdtp::MakeSpan("totalNumberOfLines"), out_totalNumberOfLines); + serializer.AddField(v8_crdtp::MakeSpan("functionBodyOffsets"), out_functionBodyOffsets); + serializer.AddField(v8_crdtp::MakeSpan("chunk"), out_chunk); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct nextWasmDisassemblyChunkParams : public v8_crdtp::DeserializableProtocolObject { + String streamId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(nextWasmDisassemblyChunkParams) + V8_CRDTP_DESERIALIZE_FIELD("streamId", streamId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::nextWasmDisassemblyChunk(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + nextWasmDisassemblyChunkParams params; + if (!nextWasmDisassemblyChunkParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + std::unique_ptr out_chunk; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->nextWasmDisassemblyChunk(params.streamId, &out_chunk); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.nextWasmDisassemblyChunk"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("chunk"), out_chunk); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct getWasmBytecodeParams : public v8_crdtp::DeserializableProtocolObject { + String scriptId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(getWasmBytecodeParams) + V8_CRDTP_DESERIALIZE_FIELD("scriptId", scriptId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::getWasmBytecode(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + getWasmBytecodeParams params; + if (!getWasmBytecodeParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + Binary out_bytecode; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->getWasmBytecode(params.scriptId, &out_bytecode); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.getWasmBytecode"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("bytecode"), out_bytecode); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct getStackTraceParams : public v8_crdtp::DeserializableProtocolObject { + std::unique_ptr stackTraceId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(getStackTraceParams) + V8_CRDTP_DESERIALIZE_FIELD("stackTraceId", stackTraceId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::getStackTrace(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + getStackTraceParams params; + if (!getStackTraceParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + std::unique_ptr out_stackTrace; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->getStackTrace(std::move(params.stackTraceId), &out_stackTrace); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.getStackTrace"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("stackTrace"), out_stackTrace); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::pause(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->pause(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.pause"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct pauseOnAsyncCallParams : public v8_crdtp::DeserializableProtocolObject { + std::unique_ptr parentStackTraceId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(pauseOnAsyncCallParams) + V8_CRDTP_DESERIALIZE_FIELD("parentStackTraceId", parentStackTraceId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::pauseOnAsyncCall(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + pauseOnAsyncCallParams params; + if (!pauseOnAsyncCallParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->pauseOnAsyncCall(std::move(params.parentStackTraceId)); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.pauseOnAsyncCall"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct removeBreakpointParams : public v8_crdtp::DeserializableProtocolObject { + String breakpointId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(removeBreakpointParams) + V8_CRDTP_DESERIALIZE_FIELD("breakpointId", breakpointId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::removeBreakpoint(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + removeBreakpointParams params; + if (!removeBreakpointParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->removeBreakpoint(params.breakpointId); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.removeBreakpoint"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct restartFrameParams : public v8_crdtp::DeserializableProtocolObject { + String callFrameId; + Maybe mode; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(restartFrameParams) + V8_CRDTP_DESERIALIZE_FIELD("callFrameId", callFrameId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("mode", mode), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::restartFrame(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + restartFrameParams params; + if (!restartFrameParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + std::unique_ptr> out_callFrames; + Maybe out_asyncStackTrace; + Maybe out_asyncStackTraceId; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->restartFrame(params.callFrameId, std::move(params.mode), &out_callFrames, &out_asyncStackTrace, &out_asyncStackTraceId); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.restartFrame"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("callFrames"), out_callFrames); + serializer.AddField(v8_crdtp::MakeSpan("asyncStackTrace"), out_asyncStackTrace); + serializer.AddField(v8_crdtp::MakeSpan("asyncStackTraceId"), out_asyncStackTraceId); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct resumeParams : public v8_crdtp::DeserializableProtocolObject { + Maybe terminateOnResume; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(resumeParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("terminateOnResume", terminateOnResume), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::resume(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + resumeParams params; + if (!resumeParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->resume(std::move(params.terminateOnResume)); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.resume"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct searchInContentParams : public v8_crdtp::DeserializableProtocolObject { + String scriptId; + String query; + Maybe caseSensitive; + Maybe isRegex; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(searchInContentParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("caseSensitive", caseSensitive), + V8_CRDTP_DESERIALIZE_FIELD_OPT("isRegex", isRegex), + V8_CRDTP_DESERIALIZE_FIELD("query", query), + V8_CRDTP_DESERIALIZE_FIELD("scriptId", scriptId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::searchInContent(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + searchInContentParams params; + if (!searchInContentParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + std::unique_ptr> out_result; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->searchInContent(params.scriptId, params.query, std::move(params.caseSensitive), std::move(params.isRegex), &out_result); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.searchInContent"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("result"), out_result); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct setAsyncCallStackDepthParams : public v8_crdtp::DeserializableProtocolObject { + int maxDepth; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setAsyncCallStackDepthParams) + V8_CRDTP_DESERIALIZE_FIELD("maxDepth", maxDepth), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setAsyncCallStackDepth(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setAsyncCallStackDepthParams params; + if (!setAsyncCallStackDepthParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setAsyncCallStackDepth(params.maxDepth); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.setAsyncCallStackDepth"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct setBlackboxPatternsParams : public v8_crdtp::DeserializableProtocolObject { + std::unique_ptr> patterns; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setBlackboxPatternsParams) + V8_CRDTP_DESERIALIZE_FIELD("patterns", patterns), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setBlackboxPatterns(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setBlackboxPatternsParams params; + if (!setBlackboxPatternsParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setBlackboxPatterns(std::move(params.patterns)); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.setBlackboxPatterns"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct setBlackboxedRangesParams : public v8_crdtp::DeserializableProtocolObject { + String scriptId; + std::unique_ptr> positions; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setBlackboxedRangesParams) + V8_CRDTP_DESERIALIZE_FIELD("positions", positions), + V8_CRDTP_DESERIALIZE_FIELD("scriptId", scriptId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setBlackboxedRanges(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setBlackboxedRangesParams params; + if (!setBlackboxedRangesParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setBlackboxedRanges(params.scriptId, std::move(params.positions)); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.setBlackboxedRanges"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct setBreakpointParams : public v8_crdtp::DeserializableProtocolObject { + std::unique_ptr location; + Maybe condition; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setBreakpointParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("condition", condition), + V8_CRDTP_DESERIALIZE_FIELD("location", location), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setBreakpoint(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setBreakpointParams params; + if (!setBreakpointParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + String out_breakpointId; + std::unique_ptr out_actualLocation; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setBreakpoint(std::move(params.location), std::move(params.condition), &out_breakpointId, &out_actualLocation); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.setBreakpoint"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("breakpointId"), out_breakpointId); + serializer.AddField(v8_crdtp::MakeSpan("actualLocation"), out_actualLocation); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct setInstrumentationBreakpointParams : public v8_crdtp::DeserializableProtocolObject { + String instrumentation; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setInstrumentationBreakpointParams) + V8_CRDTP_DESERIALIZE_FIELD("instrumentation", instrumentation), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setInstrumentationBreakpoint(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setInstrumentationBreakpointParams params; + if (!setInstrumentationBreakpointParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + String out_breakpointId; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setInstrumentationBreakpoint(params.instrumentation, &out_breakpointId); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.setInstrumentationBreakpoint"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("breakpointId"), out_breakpointId); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct setBreakpointByUrlParams : public v8_crdtp::DeserializableProtocolObject { + int lineNumber; + Maybe url; + Maybe urlRegex; + Maybe scriptHash; + Maybe columnNumber; + Maybe condition; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setBreakpointByUrlParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("columnNumber", columnNumber), + V8_CRDTP_DESERIALIZE_FIELD_OPT("condition", condition), + V8_CRDTP_DESERIALIZE_FIELD("lineNumber", lineNumber), + V8_CRDTP_DESERIALIZE_FIELD_OPT("scriptHash", scriptHash), + V8_CRDTP_DESERIALIZE_FIELD_OPT("url", url), + V8_CRDTP_DESERIALIZE_FIELD_OPT("urlRegex", urlRegex), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setBreakpointByUrl(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setBreakpointByUrlParams params; + if (!setBreakpointByUrlParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + String out_breakpointId; + std::unique_ptr> out_locations; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setBreakpointByUrl(params.lineNumber, std::move(params.url), std::move(params.urlRegex), std::move(params.scriptHash), std::move(params.columnNumber), std::move(params.condition), &out_breakpointId, &out_locations); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.setBreakpointByUrl"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("breakpointId"), out_breakpointId); + serializer.AddField(v8_crdtp::MakeSpan("locations"), out_locations); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct setBreakpointOnFunctionCallParams : public v8_crdtp::DeserializableProtocolObject { + String objectId; + Maybe condition; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setBreakpointOnFunctionCallParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("condition", condition), + V8_CRDTP_DESERIALIZE_FIELD("objectId", objectId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setBreakpointOnFunctionCall(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setBreakpointOnFunctionCallParams params; + if (!setBreakpointOnFunctionCallParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + String out_breakpointId; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setBreakpointOnFunctionCall(params.objectId, std::move(params.condition), &out_breakpointId); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.setBreakpointOnFunctionCall"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("breakpointId"), out_breakpointId); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct setBreakpointsActiveParams : public v8_crdtp::DeserializableProtocolObject { + bool active; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setBreakpointsActiveParams) + V8_CRDTP_DESERIALIZE_FIELD("active", active), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setBreakpointsActive(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setBreakpointsActiveParams params; + if (!setBreakpointsActiveParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setBreakpointsActive(params.active); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.setBreakpointsActive"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct setPauseOnExceptionsParams : public v8_crdtp::DeserializableProtocolObject { + String state; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setPauseOnExceptionsParams) + V8_CRDTP_DESERIALIZE_FIELD("state", state), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setPauseOnExceptions(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setPauseOnExceptionsParams params; + if (!setPauseOnExceptionsParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setPauseOnExceptions(params.state); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.setPauseOnExceptions"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct setReturnValueParams : public v8_crdtp::DeserializableProtocolObject { + std::unique_ptr newValue; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setReturnValueParams) + V8_CRDTP_DESERIALIZE_FIELD("newValue", newValue), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setReturnValue(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setReturnValueParams params; + if (!setReturnValueParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setReturnValue(std::move(params.newValue)); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.setReturnValue"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct setScriptSourceParams : public v8_crdtp::DeserializableProtocolObject { + String scriptId; + String scriptSource; + Maybe dryRun; + Maybe allowTopFrameEditing; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setScriptSourceParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("allowTopFrameEditing", allowTopFrameEditing), + V8_CRDTP_DESERIALIZE_FIELD_OPT("dryRun", dryRun), + V8_CRDTP_DESERIALIZE_FIELD("scriptId", scriptId), + V8_CRDTP_DESERIALIZE_FIELD("scriptSource", scriptSource), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setScriptSource(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setScriptSourceParams params; + if (!setScriptSourceParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + Maybe> out_callFrames; + Maybe out_stackChanged; + Maybe out_asyncStackTrace; + Maybe out_asyncStackTraceId; + String out_status; + Maybe out_exceptionDetails; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setScriptSource(params.scriptId, params.scriptSource, std::move(params.dryRun), std::move(params.allowTopFrameEditing), &out_callFrames, &out_stackChanged, &out_asyncStackTrace, &out_asyncStackTraceId, &out_status, &out_exceptionDetails); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.setScriptSource"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("callFrames"), out_callFrames); + serializer.AddField(v8_crdtp::MakeSpan("stackChanged"), out_stackChanged); + serializer.AddField(v8_crdtp::MakeSpan("asyncStackTrace"), out_asyncStackTrace); + serializer.AddField(v8_crdtp::MakeSpan("asyncStackTraceId"), out_asyncStackTraceId); + serializer.AddField(v8_crdtp::MakeSpan("status"), out_status); + serializer.AddField(v8_crdtp::MakeSpan("exceptionDetails"), out_exceptionDetails); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct setSkipAllPausesParams : public v8_crdtp::DeserializableProtocolObject { + bool skip; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setSkipAllPausesParams) + V8_CRDTP_DESERIALIZE_FIELD("skip", skip), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setSkipAllPauses(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setSkipAllPausesParams params; + if (!setSkipAllPausesParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setSkipAllPauses(params.skip); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.setSkipAllPauses"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct setVariableValueParams : public v8_crdtp::DeserializableProtocolObject { + int scopeNumber; + String variableName; + std::unique_ptr newValue; + String callFrameId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setVariableValueParams) + V8_CRDTP_DESERIALIZE_FIELD("callFrameId", callFrameId), + V8_CRDTP_DESERIALIZE_FIELD("newValue", newValue), + V8_CRDTP_DESERIALIZE_FIELD("scopeNumber", scopeNumber), + V8_CRDTP_DESERIALIZE_FIELD("variableName", variableName), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setVariableValue(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setVariableValueParams params; + if (!setVariableValueParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setVariableValue(params.scopeNumber, params.variableName, std::move(params.newValue), params.callFrameId); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.setVariableValue"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct stepIntoParams : public v8_crdtp::DeserializableProtocolObject { + Maybe breakOnAsyncCall; + Maybe> skipList; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(stepIntoParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("breakOnAsyncCall", breakOnAsyncCall), + V8_CRDTP_DESERIALIZE_FIELD_OPT("skipList", skipList), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::stepInto(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + stepIntoParams params; + if (!stepIntoParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->stepInto(std::move(params.breakOnAsyncCall), std::move(params.skipList)); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.stepInto"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::stepOut(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->stepOut(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.stepOut"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct stepOverParams : public v8_crdtp::DeserializableProtocolObject { + Maybe> skipList; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(stepOverParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("skipList", skipList), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::stepOver(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + stepOverParams params; + if (!stepOverParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->stepOver(std::move(params.skipList)); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Debugger.stepOver"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { +// This helper method (with a static map of redirects) is used from Dispatcher::wire +// immediately below. +const std::vector, v8_crdtp::span>>& SortedRedirects() { + static auto* redirects = [](){ + auto* redirects = new std::vector, v8_crdtp::span>>{ + }; + return redirects; + }(); + return *redirects; +} +} // namespace + +// static +void Dispatcher::wire(UberDispatcher* uber, Backend* backend) +{ + auto dispatcher = std::make_unique(uber->channel(), backend); + uber->WireBackend(v8_crdtp::SpanFrom("Debugger"), SortedRedirects(), std::move(dispatcher)); +} + +} // Debugger +} // namespace v8_inspector +} // namespace protocol diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Debugger.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Debugger.h new file mode 100644 index 000000000..e0079dcd9 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Debugger.h @@ -0,0 +1,991 @@ +// This file is generated by TypeBuilder_h.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef v8_inspector_protocol_Debugger_h +#define v8_inspector_protocol_Debugger_h + +#include "src/inspector/protocol/Protocol.h" +// For each imported domain we generate a ValueConversions struct instead of a full domain definition +// and include Domain::API version from there. +#include "src/inspector/protocol/Runtime.h" +#include "include/inspector/Debugger.h" + +namespace v8_inspector { +namespace protocol { +namespace Debugger { +using BreakpointId = String; +using CallFrameId = String; +class Location; +class ScriptPosition; +class LocationRange; +class CallFrame; +class Scope; +class SearchMatch; +class BreakLocation; +class WasmDisassemblyChunk; +using ScriptLanguage = String; +class DebugSymbols; + +// ------------- Forward and enum declarations. + +namespace ScriptLanguageEnum { + extern const char JavaScript[]; + extern const char WebAssembly[]; +} // namespace ScriptLanguageEnum + +namespace ContinueToLocation { +namespace TargetCallFramesEnum { + extern const char* Any; + extern const char* Current; +} // TargetCallFramesEnum +} // ContinueToLocation + +namespace RestartFrame { +namespace ModeEnum { + extern const char* StepInto; +} // ModeEnum +} // RestartFrame + +namespace SetInstrumentationBreakpoint { +namespace InstrumentationEnum { + extern const char* BeforeScriptExecution; + extern const char* BeforeScriptWithSourceMapExecution; +} // InstrumentationEnum +} // SetInstrumentationBreakpoint + +namespace SetPauseOnExceptions { +namespace StateEnum { + extern const char* None; + extern const char* Caught; + extern const char* Uncaught; + extern const char* All; +} // StateEnum +} // SetPauseOnExceptions + +namespace SetScriptSource { +namespace StatusEnum { + extern const char* Ok; + extern const char* CompileError; + extern const char* BlockedByActiveGenerator; + extern const char* BlockedByActiveFunction; +} // StatusEnum +} // SetScriptSource + +namespace Paused { +namespace ReasonEnum { + extern const char* Ambiguous; + extern const char* Assert; + extern const char* CSPViolation; + extern const char* DebugCommand; + extern const char* DOM; + extern const char* EventListener; + extern const char* Exception; + extern const char* Instrumentation; + extern const char* OOM; + extern const char* Other; + extern const char* PromiseRejection; + extern const char* XHR; +} // ReasonEnum +} // Paused + +// ------------- Type and builder declarations. + +class Location : public ::v8_crdtp::ProtocolObject { +public: + ~Location() override { } + + String getScriptId() { return m_scriptId; } + void setScriptId(const String& value) { m_scriptId = value; } + + int getLineNumber() { return m_lineNumber; } + void setLineNumber(int value) { m_lineNumber = value; } + + bool hasColumnNumber() { return m_columnNumber.isJust(); } + int getColumnNumber(int defaultValue) { return m_columnNumber.isJust() ? m_columnNumber.fromJust() : defaultValue; } + void setColumnNumber(int value) { m_columnNumber = value; } + + template + class LocationBuilder { + public: + enum { + NoFieldsSet = 0, + ScriptIdSet = 1 << 1, + LineNumberSet = 1 << 2, + AllFieldsSet = (ScriptIdSet | LineNumberSet | 0)}; + + + LocationBuilder& setScriptId(const String& value) + { + static_assert(!(STATE & ScriptIdSet), "property scriptId should not be set yet"); + m_result->setScriptId(value); + return castState(); + } + + LocationBuilder& setLineNumber(int value) + { + static_assert(!(STATE & LineNumberSet), "property lineNumber should not be set yet"); + m_result->setLineNumber(value); + return castState(); + } + + LocationBuilder& setColumnNumber(int value) + { + m_result->setColumnNumber(value); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class Location; + LocationBuilder() : m_result(new Location()) { } + + template LocationBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static LocationBuilder<0> create() + { + return LocationBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + Location() + { + m_lineNumber = 0; + } + + String m_scriptId; + int m_lineNumber; + Maybe m_columnNumber; +}; + + +class ScriptPosition : public ::v8_crdtp::ProtocolObject { +public: + ~ScriptPosition() override { } + + int getLineNumber() { return m_lineNumber; } + void setLineNumber(int value) { m_lineNumber = value; } + + int getColumnNumber() { return m_columnNumber; } + void setColumnNumber(int value) { m_columnNumber = value; } + + template + class ScriptPositionBuilder { + public: + enum { + NoFieldsSet = 0, + LineNumberSet = 1 << 1, + ColumnNumberSet = 1 << 2, + AllFieldsSet = (LineNumberSet | ColumnNumberSet | 0)}; + + + ScriptPositionBuilder& setLineNumber(int value) + { + static_assert(!(STATE & LineNumberSet), "property lineNumber should not be set yet"); + m_result->setLineNumber(value); + return castState(); + } + + ScriptPositionBuilder& setColumnNumber(int value) + { + static_assert(!(STATE & ColumnNumberSet), "property columnNumber should not be set yet"); + m_result->setColumnNumber(value); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class ScriptPosition; + ScriptPositionBuilder() : m_result(new ScriptPosition()) { } + + template ScriptPositionBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static ScriptPositionBuilder<0> create() + { + return ScriptPositionBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + ScriptPosition() + { + m_lineNumber = 0; + m_columnNumber = 0; + } + + int m_lineNumber; + int m_columnNumber; +}; + + +class LocationRange : public ::v8_crdtp::ProtocolObject { +public: + ~LocationRange() override { } + + String getScriptId() { return m_scriptId; } + void setScriptId(const String& value) { m_scriptId = value; } + + protocol::Debugger::ScriptPosition* getStart() { return m_start.get(); } + void setStart(std::unique_ptr value) { m_start = std::move(value); } + + protocol::Debugger::ScriptPosition* getEnd() { return m_end.get(); } + void setEnd(std::unique_ptr value) { m_end = std::move(value); } + + template + class LocationRangeBuilder { + public: + enum { + NoFieldsSet = 0, + ScriptIdSet = 1 << 1, + StartSet = 1 << 2, + EndSet = 1 << 3, + AllFieldsSet = (ScriptIdSet | StartSet | EndSet | 0)}; + + + LocationRangeBuilder& setScriptId(const String& value) + { + static_assert(!(STATE & ScriptIdSet), "property scriptId should not be set yet"); + m_result->setScriptId(value); + return castState(); + } + + LocationRangeBuilder& setStart(std::unique_ptr value) + { + static_assert(!(STATE & StartSet), "property start should not be set yet"); + m_result->setStart(std::move(value)); + return castState(); + } + + LocationRangeBuilder& setEnd(std::unique_ptr value) + { + static_assert(!(STATE & EndSet), "property end should not be set yet"); + m_result->setEnd(std::move(value)); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class LocationRange; + LocationRangeBuilder() : m_result(new LocationRange()) { } + + template LocationRangeBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static LocationRangeBuilder<0> create() + { + return LocationRangeBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + LocationRange() + { + } + + String m_scriptId; + std::unique_ptr m_start; + std::unique_ptr m_end; +}; + + +class CallFrame : public ::v8_crdtp::ProtocolObject { +public: + ~CallFrame() override { } + + String getCallFrameId() { return m_callFrameId; } + void setCallFrameId(const String& value) { m_callFrameId = value; } + + String getFunctionName() { return m_functionName; } + void setFunctionName(const String& value) { m_functionName = value; } + + bool hasFunctionLocation() { return m_functionLocation.isJust(); } + protocol::Debugger::Location* getFunctionLocation(protocol::Debugger::Location* defaultValue) { return m_functionLocation.isJust() ? m_functionLocation.fromJust() : defaultValue; } + void setFunctionLocation(std::unique_ptr value) { m_functionLocation = std::move(value); } + + protocol::Debugger::Location* getLocation() { return m_location.get(); } + void setLocation(std::unique_ptr value) { m_location = std::move(value); } + + String getUrl() { return m_url; } + void setUrl(const String& value) { m_url = value; } + + protocol::Array* getScopeChain() { return m_scopeChain.get(); } + void setScopeChain(std::unique_ptr> value) { m_scopeChain = std::move(value); } + + protocol::Runtime::RemoteObject* getThis() { return m_this.get(); } + void setThis(std::unique_ptr value) { m_this = std::move(value); } + + bool hasReturnValue() { return m_returnValue.isJust(); } + protocol::Runtime::RemoteObject* getReturnValue(protocol::Runtime::RemoteObject* defaultValue) { return m_returnValue.isJust() ? m_returnValue.fromJust() : defaultValue; } + void setReturnValue(std::unique_ptr value) { m_returnValue = std::move(value); } + + bool hasCanBeRestarted() { return m_canBeRestarted.isJust(); } + bool getCanBeRestarted(bool defaultValue) { return m_canBeRestarted.isJust() ? m_canBeRestarted.fromJust() : defaultValue; } + void setCanBeRestarted(bool value) { m_canBeRestarted = value; } + + template + class CallFrameBuilder { + public: + enum { + NoFieldsSet = 0, + CallFrameIdSet = 1 << 1, + FunctionNameSet = 1 << 2, + LocationSet = 1 << 3, + UrlSet = 1 << 4, + ScopeChainSet = 1 << 5, + ThisSet = 1 << 6, + AllFieldsSet = (CallFrameIdSet | FunctionNameSet | LocationSet | UrlSet | ScopeChainSet | ThisSet | 0)}; + + + CallFrameBuilder& setCallFrameId(const String& value) + { + static_assert(!(STATE & CallFrameIdSet), "property callFrameId should not be set yet"); + m_result->setCallFrameId(value); + return castState(); + } + + CallFrameBuilder& setFunctionName(const String& value) + { + static_assert(!(STATE & FunctionNameSet), "property functionName should not be set yet"); + m_result->setFunctionName(value); + return castState(); + } + + CallFrameBuilder& setFunctionLocation(std::unique_ptr value) + { + m_result->setFunctionLocation(std::move(value)); + return *this; + } + + CallFrameBuilder& setLocation(std::unique_ptr value) + { + static_assert(!(STATE & LocationSet), "property location should not be set yet"); + m_result->setLocation(std::move(value)); + return castState(); + } + + CallFrameBuilder& setUrl(const String& value) + { + static_assert(!(STATE & UrlSet), "property url should not be set yet"); + m_result->setUrl(value); + return castState(); + } + + CallFrameBuilder& setScopeChain(std::unique_ptr> value) + { + static_assert(!(STATE & ScopeChainSet), "property scopeChain should not be set yet"); + m_result->setScopeChain(std::move(value)); + return castState(); + } + + CallFrameBuilder& setThis(std::unique_ptr value) + { + static_assert(!(STATE & ThisSet), "property this should not be set yet"); + m_result->setThis(std::move(value)); + return castState(); + } + + CallFrameBuilder& setReturnValue(std::unique_ptr value) + { + m_result->setReturnValue(std::move(value)); + return *this; + } + + CallFrameBuilder& setCanBeRestarted(bool value) + { + m_result->setCanBeRestarted(value); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class CallFrame; + CallFrameBuilder() : m_result(new CallFrame()) { } + + template CallFrameBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static CallFrameBuilder<0> create() + { + return CallFrameBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + CallFrame() + { + } + + String m_callFrameId; + String m_functionName; + Maybe m_functionLocation; + std::unique_ptr m_location; + String m_url; + std::unique_ptr> m_scopeChain; + std::unique_ptr m_this; + Maybe m_returnValue; + Maybe m_canBeRestarted; +}; + + +class Scope : public ::v8_crdtp::ProtocolObject { +public: + ~Scope() override { } + + struct TypeEnum { + static const char* Global; + static const char* Local; + static const char* With; + static const char* Closure; + static const char* Catch; + static const char* Block; + static const char* Script; + static const char* Eval; + static const char* Module; + static const char* WasmExpressionStack; + }; // TypeEnum + + String getType() { return m_type; } + void setType(const String& value) { m_type = value; } + + protocol::Runtime::RemoteObject* getObject() { return m_object.get(); } + void setObject(std::unique_ptr value) { m_object = std::move(value); } + + bool hasName() { return m_name.isJust(); } + String getName(const String& defaultValue) { return m_name.isJust() ? m_name.fromJust() : defaultValue; } + void setName(const String& value) { m_name = value; } + + bool hasStartLocation() { return m_startLocation.isJust(); } + protocol::Debugger::Location* getStartLocation(protocol::Debugger::Location* defaultValue) { return m_startLocation.isJust() ? m_startLocation.fromJust() : defaultValue; } + void setStartLocation(std::unique_ptr value) { m_startLocation = std::move(value); } + + bool hasEndLocation() { return m_endLocation.isJust(); } + protocol::Debugger::Location* getEndLocation(protocol::Debugger::Location* defaultValue) { return m_endLocation.isJust() ? m_endLocation.fromJust() : defaultValue; } + void setEndLocation(std::unique_ptr value) { m_endLocation = std::move(value); } + + template + class ScopeBuilder { + public: + enum { + NoFieldsSet = 0, + TypeSet = 1 << 1, + ObjectSet = 1 << 2, + AllFieldsSet = (TypeSet | ObjectSet | 0)}; + + + ScopeBuilder& setType(const String& value) + { + static_assert(!(STATE & TypeSet), "property type should not be set yet"); + m_result->setType(value); + return castState(); + } + + ScopeBuilder& setObject(std::unique_ptr value) + { + static_assert(!(STATE & ObjectSet), "property object should not be set yet"); + m_result->setObject(std::move(value)); + return castState(); + } + + ScopeBuilder& setName(const String& value) + { + m_result->setName(value); + return *this; + } + + ScopeBuilder& setStartLocation(std::unique_ptr value) + { + m_result->setStartLocation(std::move(value)); + return *this; + } + + ScopeBuilder& setEndLocation(std::unique_ptr value) + { + m_result->setEndLocation(std::move(value)); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class Scope; + ScopeBuilder() : m_result(new Scope()) { } + + template ScopeBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static ScopeBuilder<0> create() + { + return ScopeBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + Scope() + { + } + + String m_type; + std::unique_ptr m_object; + Maybe m_name; + Maybe m_startLocation; + Maybe m_endLocation; +}; + + +class SearchMatch : public ::v8_crdtp::ProtocolObject, + public API::SearchMatch { +public: + ~SearchMatch() override { } + + double getLineNumber() { return m_lineNumber; } + void setLineNumber(double value) { m_lineNumber = value; } + + String getLineContent() { return m_lineContent; } + void setLineContent(const String& value) { m_lineContent = value; } + + template + class SearchMatchBuilder { + public: + enum { + NoFieldsSet = 0, + LineNumberSet = 1 << 1, + LineContentSet = 1 << 2, + AllFieldsSet = (LineNumberSet | LineContentSet | 0)}; + + + SearchMatchBuilder& setLineNumber(double value) + { + static_assert(!(STATE & LineNumberSet), "property lineNumber should not be set yet"); + m_result->setLineNumber(value); + return castState(); + } + + SearchMatchBuilder& setLineContent(const String& value) + { + static_assert(!(STATE & LineContentSet), "property lineContent should not be set yet"); + m_result->setLineContent(value); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class SearchMatch; + SearchMatchBuilder() : m_result(new SearchMatch()) { } + + template SearchMatchBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static SearchMatchBuilder<0> create() + { + return SearchMatchBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + SearchMatch() + { + m_lineNumber = 0; + } + + double m_lineNumber; + String m_lineContent; +}; + + +class BreakLocation : public ::v8_crdtp::ProtocolObject { +public: + ~BreakLocation() override { } + + String getScriptId() { return m_scriptId; } + void setScriptId(const String& value) { m_scriptId = value; } + + int getLineNumber() { return m_lineNumber; } + void setLineNumber(int value) { m_lineNumber = value; } + + bool hasColumnNumber() { return m_columnNumber.isJust(); } + int getColumnNumber(int defaultValue) { return m_columnNumber.isJust() ? m_columnNumber.fromJust() : defaultValue; } + void setColumnNumber(int value) { m_columnNumber = value; } + + struct TypeEnum { + static const char* DebuggerStatement; + static const char* Call; + static const char* Return; + }; // TypeEnum + + bool hasType() { return m_type.isJust(); } + String getType(const String& defaultValue) { return m_type.isJust() ? m_type.fromJust() : defaultValue; } + void setType(const String& value) { m_type = value; } + + template + class BreakLocationBuilder { + public: + enum { + NoFieldsSet = 0, + ScriptIdSet = 1 << 1, + LineNumberSet = 1 << 2, + AllFieldsSet = (ScriptIdSet | LineNumberSet | 0)}; + + + BreakLocationBuilder& setScriptId(const String& value) + { + static_assert(!(STATE & ScriptIdSet), "property scriptId should not be set yet"); + m_result->setScriptId(value); + return castState(); + } + + BreakLocationBuilder& setLineNumber(int value) + { + static_assert(!(STATE & LineNumberSet), "property lineNumber should not be set yet"); + m_result->setLineNumber(value); + return castState(); + } + + BreakLocationBuilder& setColumnNumber(int value) + { + m_result->setColumnNumber(value); + return *this; + } + + BreakLocationBuilder& setType(const String& value) + { + m_result->setType(value); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class BreakLocation; + BreakLocationBuilder() : m_result(new BreakLocation()) { } + + template BreakLocationBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static BreakLocationBuilder<0> create() + { + return BreakLocationBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + BreakLocation() + { + m_lineNumber = 0; + } + + String m_scriptId; + int m_lineNumber; + Maybe m_columnNumber; + Maybe m_type; +}; + + +class WasmDisassemblyChunk : public ::v8_crdtp::ProtocolObject { +public: + ~WasmDisassemblyChunk() override { } + + protocol::Array* getLines() { return m_lines.get(); } + void setLines(std::unique_ptr> value) { m_lines = std::move(value); } + + protocol::Array* getBytecodeOffsets() { return m_bytecodeOffsets.get(); } + void setBytecodeOffsets(std::unique_ptr> value) { m_bytecodeOffsets = std::move(value); } + + template + class WasmDisassemblyChunkBuilder { + public: + enum { + NoFieldsSet = 0, + LinesSet = 1 << 1, + BytecodeOffsetsSet = 1 << 2, + AllFieldsSet = (LinesSet | BytecodeOffsetsSet | 0)}; + + + WasmDisassemblyChunkBuilder& setLines(std::unique_ptr> value) + { + static_assert(!(STATE & LinesSet), "property lines should not be set yet"); + m_result->setLines(std::move(value)); + return castState(); + } + + WasmDisassemblyChunkBuilder& setBytecodeOffsets(std::unique_ptr> value) + { + static_assert(!(STATE & BytecodeOffsetsSet), "property bytecodeOffsets should not be set yet"); + m_result->setBytecodeOffsets(std::move(value)); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class WasmDisassemblyChunk; + WasmDisassemblyChunkBuilder() : m_result(new WasmDisassemblyChunk()) { } + + template WasmDisassemblyChunkBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static WasmDisassemblyChunkBuilder<0> create() + { + return WasmDisassemblyChunkBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + WasmDisassemblyChunk() + { + } + + std::unique_ptr> m_lines; + std::unique_ptr> m_bytecodeOffsets; +}; + + +class DebugSymbols : public ::v8_crdtp::ProtocolObject { +public: + ~DebugSymbols() override { } + + struct TypeEnum { + static const char* None; + static const char* SourceMap; + static const char* EmbeddedDWARF; + static const char* ExternalDWARF; + }; // TypeEnum + + String getType() { return m_type; } + void setType(const String& value) { m_type = value; } + + bool hasExternalURL() { return m_externalURL.isJust(); } + String getExternalURL(const String& defaultValue) { return m_externalURL.isJust() ? m_externalURL.fromJust() : defaultValue; } + void setExternalURL(const String& value) { m_externalURL = value; } + + template + class DebugSymbolsBuilder { + public: + enum { + NoFieldsSet = 0, + TypeSet = 1 << 1, + AllFieldsSet = (TypeSet | 0)}; + + + DebugSymbolsBuilder& setType(const String& value) + { + static_assert(!(STATE & TypeSet), "property type should not be set yet"); + m_result->setType(value); + return castState(); + } + + DebugSymbolsBuilder& setExternalURL(const String& value) + { + m_result->setExternalURL(value); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class DebugSymbols; + DebugSymbolsBuilder() : m_result(new DebugSymbols()) { } + + template DebugSymbolsBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static DebugSymbolsBuilder<0> create() + { + return DebugSymbolsBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + DebugSymbols() + { + } + + String m_type; + Maybe m_externalURL; +}; + + +// ------------- Backend interface. + +class Backend { +public: + virtual ~Backend() { } + + virtual DispatchResponse continueToLocation(std::unique_ptr in_location, Maybe in_targetCallFrames) = 0; + virtual DispatchResponse disable() = 0; + virtual DispatchResponse enable(Maybe in_maxScriptsCacheSize, String* out_debuggerId) = 0; + virtual DispatchResponse evaluateOnCallFrame(const String& in_callFrameId, const String& in_expression, Maybe in_objectGroup, Maybe in_includeCommandLineAPI, Maybe in_silent, Maybe in_returnByValue, Maybe in_generatePreview, Maybe in_throwOnSideEffect, Maybe in_timeout, std::unique_ptr* out_result, Maybe* out_exceptionDetails) = 0; + virtual DispatchResponse getPossibleBreakpoints(std::unique_ptr in_start, Maybe in_end, Maybe in_restrictToFunction, std::unique_ptr>* out_locations) = 0; + virtual DispatchResponse getScriptSource(const String& in_scriptId, String* out_scriptSource, Maybe* out_bytecode) = 0; + virtual DispatchResponse disassembleWasmModule(const String& in_scriptId, Maybe* out_streamId, int* out_totalNumberOfLines, std::unique_ptr>* out_functionBodyOffsets, std::unique_ptr* out_chunk) = 0; + virtual DispatchResponse nextWasmDisassemblyChunk(const String& in_streamId, std::unique_ptr* out_chunk) = 0; + virtual DispatchResponse getWasmBytecode(const String& in_scriptId, Binary* out_bytecode) = 0; + virtual DispatchResponse getStackTrace(std::unique_ptr in_stackTraceId, std::unique_ptr* out_stackTrace) = 0; + virtual DispatchResponse pause() = 0; + virtual DispatchResponse pauseOnAsyncCall(std::unique_ptr in_parentStackTraceId) = 0; + virtual DispatchResponse removeBreakpoint(const String& in_breakpointId) = 0; + virtual DispatchResponse restartFrame(const String& in_callFrameId, Maybe in_mode, std::unique_ptr>* out_callFrames, Maybe* out_asyncStackTrace, Maybe* out_asyncStackTraceId) = 0; + virtual DispatchResponse resume(Maybe in_terminateOnResume) = 0; + virtual DispatchResponse searchInContent(const String& in_scriptId, const String& in_query, Maybe in_caseSensitive, Maybe in_isRegex, std::unique_ptr>* out_result) = 0; + virtual DispatchResponse setAsyncCallStackDepth(int in_maxDepth) = 0; + virtual DispatchResponse setBlackboxPatterns(std::unique_ptr> in_patterns) = 0; + virtual DispatchResponse setBlackboxedRanges(const String& in_scriptId, std::unique_ptr> in_positions) = 0; + virtual DispatchResponse setBreakpoint(std::unique_ptr in_location, Maybe in_condition, String* out_breakpointId, std::unique_ptr* out_actualLocation) = 0; + virtual DispatchResponse setInstrumentationBreakpoint(const String& in_instrumentation, String* out_breakpointId) = 0; + virtual DispatchResponse setBreakpointByUrl(int in_lineNumber, Maybe in_url, Maybe in_urlRegex, Maybe in_scriptHash, Maybe in_columnNumber, Maybe in_condition, String* out_breakpointId, std::unique_ptr>* out_locations) = 0; + virtual DispatchResponse setBreakpointOnFunctionCall(const String& in_objectId, Maybe in_condition, String* out_breakpointId) = 0; + virtual DispatchResponse setBreakpointsActive(bool in_active) = 0; + virtual DispatchResponse setPauseOnExceptions(const String& in_state) = 0; + virtual DispatchResponse setReturnValue(std::unique_ptr in_newValue) = 0; + virtual DispatchResponse setScriptSource(const String& in_scriptId, const String& in_scriptSource, Maybe in_dryRun, Maybe in_allowTopFrameEditing, Maybe>* out_callFrames, Maybe* out_stackChanged, Maybe* out_asyncStackTrace, Maybe* out_asyncStackTraceId, String* out_status, Maybe* out_exceptionDetails) = 0; + virtual DispatchResponse setSkipAllPauses(bool in_skip) = 0; + virtual DispatchResponse setVariableValue(int in_scopeNumber, const String& in_variableName, std::unique_ptr in_newValue, const String& in_callFrameId) = 0; + virtual DispatchResponse stepInto(Maybe in_breakOnAsyncCall, Maybe> in_skipList) = 0; + virtual DispatchResponse stepOut() = 0; + virtual DispatchResponse stepOver(Maybe> in_skipList) = 0; + +}; + +// ------------- Frontend interface. + +class Frontend { +public: + explicit Frontend(FrontendChannel* frontend_channel) : frontend_channel_(frontend_channel) {} + void breakpointResolved(const String& breakpointId, std::unique_ptr location); + void paused(std::unique_ptr> callFrames, const String& reason, Maybe data = Maybe(), Maybe> hitBreakpoints = Maybe>(), Maybe asyncStackTrace = Maybe(), Maybe asyncStackTraceId = Maybe(), Maybe asyncCallStackTraceId = Maybe()); + void resumed(); + void scriptFailedToParse(const String& scriptId, const String& url, int startLine, int startColumn, int endLine, int endColumn, int executionContextId, const String& hash, Maybe executionContextAuxData = Maybe(), Maybe sourceMapURL = Maybe(), Maybe hasSourceURL = Maybe(), Maybe isModule = Maybe(), Maybe length = Maybe(), Maybe stackTrace = Maybe(), Maybe codeOffset = Maybe(), Maybe scriptLanguage = Maybe(), Maybe embedderName = Maybe()); + void scriptParsed(const String& scriptId, const String& url, int startLine, int startColumn, int endLine, int endColumn, int executionContextId, const String& hash, Maybe executionContextAuxData = Maybe(), Maybe isLiveEdit = Maybe(), Maybe sourceMapURL = Maybe(), Maybe hasSourceURL = Maybe(), Maybe isModule = Maybe(), Maybe length = Maybe(), Maybe stackTrace = Maybe(), Maybe codeOffset = Maybe(), Maybe scriptLanguage = Maybe(), Maybe debugSymbols = Maybe(), Maybe embedderName = Maybe()); + + void flush(); + void sendRawNotification(std::unique_ptr); + private: + FrontendChannel* frontend_channel_; +}; + +// ------------- Dispatcher. + +class Dispatcher { +public: + static void wire(UberDispatcher*, Backend*); + +private: + Dispatcher() { } +}; + +// ------------- Metainfo. + +class Metainfo { +public: + using BackendClass = Backend; + using FrontendClass = Frontend; + using DispatcherClass = Dispatcher; + static const char domainName[]; + static const char commandPrefix[]; + static const char version[]; +}; + +} // namespace Debugger +} // namespace v8_inspector +} // namespace protocol + +#endif // !defined(v8_inspector_protocol_Debugger_h) diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Forward.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Forward.h new file mode 100644 index 000000000..f9e5b6985 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Forward.h @@ -0,0 +1,74 @@ +// This file is generated by Forward_h.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef v8_inspector_protocol_Forward_h +#define v8_inspector_protocol_Forward_h + + +#include +#include + +#include "third_party/inspector_protocol/crdtp/error_support.h" +#include "third_party/inspector_protocol/crdtp/dispatch.h" +#include "third_party/inspector_protocol/crdtp/frontend_channel.h" +#include "third_party/inspector_protocol/crdtp/protocol_core.h" + +#include "src/inspector/string-util.h" + +namespace v8_inspector { +namespace protocol { + +using DispatchResponse = v8_crdtp::DispatchResponse; +using ErrorSupport = v8_crdtp::ErrorSupport; +using Serializable = v8_crdtp::Serializable; +using FrontendChannel = v8_crdtp::FrontendChannel; +using DomainDispatcher = v8_crdtp::DomainDispatcher; +using UberDispatcher = v8_crdtp::UberDispatcher; +using Response = DispatchResponse; + +class DictionaryValue; +class FundamentalValue; +class ListValue; +class Object; +class SerializedValue; +class StringValue; +class Value; + +using v8_crdtp::detail::PtrMaybe; +using v8_crdtp::detail::ValueMaybe; + +template +using Maybe = v8_crdtp::Maybe; + +namespace detail { + +template +struct ArrayTypedef { typedef std::vector> type; }; + +template <> +struct ArrayTypedef { typedef std::vector type; }; + +template <> +struct ArrayTypedef { typedef std::vector type; }; + +template <> +struct ArrayTypedef { typedef std::vector type; }; + +template <> +struct ArrayTypedef { typedef std::vector type; }; + +template <> +struct ArrayTypedef { typedef std::vector type; }; + +} // namespace detail + +template +using Array = typename detail::ArrayTypedef::type; + +} // namespace v8_inspector +} // namespace protocol + +#endif // !defined(v8_inspector_protocol_Forward_h) diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/HeapProfiler.cpp b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/HeapProfiler.cpp new file mode 100644 index 000000000..c20c069c7 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/HeapProfiler.cpp @@ -0,0 +1,669 @@ +// This file is generated by TypeBuilder_cpp.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/inspector/protocol/HeapProfiler.h" + +#include "src/inspector/protocol/Protocol.h" + +#include "third_party/inspector_protocol/crdtp/cbor.h" +#include "third_party/inspector_protocol/crdtp/find_by_first.h" +#include "third_party/inspector_protocol/crdtp/span.h" + +namespace v8_inspector { +namespace protocol { +namespace HeapProfiler { + +using v8_crdtp::DeserializerState; +using v8_crdtp::ProtocolTypeTraits; + +// ------------- Enum values from types. + +const char Metainfo::domainName[] = "HeapProfiler"; +const char Metainfo::commandPrefix[] = "HeapProfiler."; +const char Metainfo::version[] = "1.3"; + + +V8_CRDTP_BEGIN_DESERIALIZER(SamplingHeapProfileNode) + V8_CRDTP_DESERIALIZE_FIELD("callFrame", m_callFrame), + V8_CRDTP_DESERIALIZE_FIELD("children", m_children), + V8_CRDTP_DESERIALIZE_FIELD("id", m_id), + V8_CRDTP_DESERIALIZE_FIELD("selfSize", m_selfSize), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(SamplingHeapProfileNode) + V8_CRDTP_SERIALIZE_FIELD("callFrame", m_callFrame); + V8_CRDTP_SERIALIZE_FIELD("selfSize", m_selfSize); + V8_CRDTP_SERIALIZE_FIELD("id", m_id); + V8_CRDTP_SERIALIZE_FIELD("children", m_children); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(SamplingHeapProfileSample) + V8_CRDTP_DESERIALIZE_FIELD("nodeId", m_nodeId), + V8_CRDTP_DESERIALIZE_FIELD("ordinal", m_ordinal), + V8_CRDTP_DESERIALIZE_FIELD("size", m_size), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(SamplingHeapProfileSample) + V8_CRDTP_SERIALIZE_FIELD("size", m_size); + V8_CRDTP_SERIALIZE_FIELD("nodeId", m_nodeId); + V8_CRDTP_SERIALIZE_FIELD("ordinal", m_ordinal); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(SamplingHeapProfile) + V8_CRDTP_DESERIALIZE_FIELD("head", m_head), + V8_CRDTP_DESERIALIZE_FIELD("samples", m_samples), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(SamplingHeapProfile) + V8_CRDTP_SERIALIZE_FIELD("head", m_head); + V8_CRDTP_SERIALIZE_FIELD("samples", m_samples); +V8_CRDTP_END_SERIALIZER(); + + +// ------------- Enum values from params. + + +// ------------- Frontend notifications. + +void Frontend::addHeapSnapshotChunk(const String& chunk) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("chunk"), chunk); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("HeapProfiler.addHeapSnapshotChunk", serializer.Finish())); +} + +void Frontend::heapStatsUpdate(std::unique_ptr> statsUpdate) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("statsUpdate"), statsUpdate); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("HeapProfiler.heapStatsUpdate", serializer.Finish())); +} + +void Frontend::lastSeenObjectId(int lastSeenObjectId, double timestamp) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("lastSeenObjectId"), lastSeenObjectId); + serializer.AddField(v8_crdtp::MakeSpan("timestamp"), timestamp); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("HeapProfiler.lastSeenObjectId", serializer.Finish())); +} + +void Frontend::reportHeapSnapshotProgress(int done, int total, Maybe finished) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("done"), done); + serializer.AddField(v8_crdtp::MakeSpan("total"), total); + serializer.AddField(v8_crdtp::MakeSpan("finished"), finished); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("HeapProfiler.reportHeapSnapshotProgress", serializer.Finish())); +} + +void Frontend::resetProfiles() +{ + if (!frontend_channel_) + return; + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("HeapProfiler.resetProfiles")); +} + +void Frontend::flush() +{ + frontend_channel_->FlushProtocolNotifications(); +} + +void Frontend::sendRawNotification(std::unique_ptr notification) +{ + frontend_channel_->SendProtocolNotification(std::move(notification)); +} + +// --------------------- Dispatcher. + +class DomainDispatcherImpl : public protocol::DomainDispatcher { +public: + DomainDispatcherImpl(FrontendChannel* frontendChannel, Backend* backend) + : DomainDispatcher(frontendChannel) + , m_backend(backend) {} + ~DomainDispatcherImpl() override { } + + using CallHandler = void (DomainDispatcherImpl::*)(const v8_crdtp::Dispatchable& dispatchable); + + std::function Dispatch(v8_crdtp::span command_name) override; + + void addInspectedHeapObject(const v8_crdtp::Dispatchable& dispatchable); + void collectGarbage(const v8_crdtp::Dispatchable& dispatchable); + void disable(const v8_crdtp::Dispatchable& dispatchable); + void enable(const v8_crdtp::Dispatchable& dispatchable); + void getHeapObjectId(const v8_crdtp::Dispatchable& dispatchable); + void getObjectByHeapObjectId(const v8_crdtp::Dispatchable& dispatchable); + void getSamplingProfile(const v8_crdtp::Dispatchable& dispatchable); + void startSampling(const v8_crdtp::Dispatchable& dispatchable); + void startTrackingHeapObjects(const v8_crdtp::Dispatchable& dispatchable); + void stopSampling(const v8_crdtp::Dispatchable& dispatchable); + void stopTrackingHeapObjects(const v8_crdtp::Dispatchable& dispatchable); + void takeHeapSnapshot(const v8_crdtp::Dispatchable& dispatchable); + protected: + Backend* m_backend; +}; + +namespace { +// This helper method with a static map of command methods (instance methods +// of DomainDispatcherImpl declared just above) by their name is used immediately below, +// in the DomainDispatcherImpl::Dispatch method. +DomainDispatcherImpl::CallHandler CommandByName(v8_crdtp::span command_name) { + static auto* commands = [](){ + auto* commands = new std::vector, + DomainDispatcherImpl::CallHandler>>{ + { + v8_crdtp::SpanFrom("addInspectedHeapObject"), + &DomainDispatcherImpl::addInspectedHeapObject + }, + { + v8_crdtp::SpanFrom("collectGarbage"), + &DomainDispatcherImpl::collectGarbage + }, + { + v8_crdtp::SpanFrom("disable"), + &DomainDispatcherImpl::disable + }, + { + v8_crdtp::SpanFrom("enable"), + &DomainDispatcherImpl::enable + }, + { + v8_crdtp::SpanFrom("getHeapObjectId"), + &DomainDispatcherImpl::getHeapObjectId + }, + { + v8_crdtp::SpanFrom("getObjectByHeapObjectId"), + &DomainDispatcherImpl::getObjectByHeapObjectId + }, + { + v8_crdtp::SpanFrom("getSamplingProfile"), + &DomainDispatcherImpl::getSamplingProfile + }, + { + v8_crdtp::SpanFrom("startSampling"), + &DomainDispatcherImpl::startSampling + }, + { + v8_crdtp::SpanFrom("startTrackingHeapObjects"), + &DomainDispatcherImpl::startTrackingHeapObjects + }, + { + v8_crdtp::SpanFrom("stopSampling"), + &DomainDispatcherImpl::stopSampling + }, + { + v8_crdtp::SpanFrom("stopTrackingHeapObjects"), + &DomainDispatcherImpl::stopTrackingHeapObjects + }, + { + v8_crdtp::SpanFrom("takeHeapSnapshot"), + &DomainDispatcherImpl::takeHeapSnapshot + }, + }; + return commands; + }(); + return v8_crdtp::FindByFirst(*commands, command_name, nullptr); +} +} // namespace + +std::function DomainDispatcherImpl::Dispatch(v8_crdtp::span command_name) { + CallHandler handler = CommandByName(command_name); + if (!handler) return nullptr; + + return [this, handler](const v8_crdtp::Dispatchable& dispatchable) { + (this->*handler)(dispatchable); + }; +} + + +namespace { + +struct addInspectedHeapObjectParams : public v8_crdtp::DeserializableProtocolObject { + String heapObjectId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(addInspectedHeapObjectParams) + V8_CRDTP_DESERIALIZE_FIELD("heapObjectId", heapObjectId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::addInspectedHeapObject(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + addInspectedHeapObjectParams params; + if (!addInspectedHeapObjectParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->addInspectedHeapObject(params.heapObjectId); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("HeapProfiler.addInspectedHeapObject"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +class CollectGarbageCallbackImpl : public Backend::CollectGarbageCallback, public DomainDispatcher::Callback { +public: + CollectGarbageCallbackImpl(std::unique_ptr backendImpl, int callId, v8_crdtp::span message) + : DomainDispatcher::Callback(std::move(backendImpl), callId, +v8_crdtp::SpanFrom("HeapProfiler.collectGarbage"), message) { } + + void sendSuccess() override + { + v8_crdtp::ObjectSerializer serializer; + sendIfActive(serializer.Finish(), DispatchResponse::Success()); + } + + void fallThrough() override + { + fallThroughIfActive(); + } + + void sendFailure(const DispatchResponse& response) override + { + DCHECK(response.IsError()); + sendIfActive(nullptr, response); + } +}; + +namespace { + + +} // namespace + +void DomainDispatcherImpl::collectGarbage(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + m_backend->collectGarbage(std::make_unique(weakPtr(), dispatchable.CallId(), dispatchable.Serialized())); +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::disable(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->disable(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("HeapProfiler.disable"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::enable(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->enable(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("HeapProfiler.enable"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct getHeapObjectIdParams : public v8_crdtp::DeserializableProtocolObject { + String objectId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(getHeapObjectIdParams) + V8_CRDTP_DESERIALIZE_FIELD("objectId", objectId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::getHeapObjectId(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + getHeapObjectIdParams params; + if (!getHeapObjectIdParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + String out_heapSnapshotObjectId; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->getHeapObjectId(params.objectId, &out_heapSnapshotObjectId); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("HeapProfiler.getHeapObjectId"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("heapSnapshotObjectId"), out_heapSnapshotObjectId); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct getObjectByHeapObjectIdParams : public v8_crdtp::DeserializableProtocolObject { + String objectId; + Maybe objectGroup; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(getObjectByHeapObjectIdParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("objectGroup", objectGroup), + V8_CRDTP_DESERIALIZE_FIELD("objectId", objectId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::getObjectByHeapObjectId(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + getObjectByHeapObjectIdParams params; + if (!getObjectByHeapObjectIdParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + std::unique_ptr out_result; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->getObjectByHeapObjectId(params.objectId, std::move(params.objectGroup), &out_result); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("HeapProfiler.getObjectByHeapObjectId"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("result"), out_result); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::getSamplingProfile(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + // Declare output parameters. + std::unique_ptr out_profile; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->getSamplingProfile(&out_profile); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("HeapProfiler.getSamplingProfile"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("profile"), out_profile); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct startSamplingParams : public v8_crdtp::DeserializableProtocolObject { + Maybe samplingInterval; + Maybe includeObjectsCollectedByMajorGC; + Maybe includeObjectsCollectedByMinorGC; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(startSamplingParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("includeObjectsCollectedByMajorGC", includeObjectsCollectedByMajorGC), + V8_CRDTP_DESERIALIZE_FIELD_OPT("includeObjectsCollectedByMinorGC", includeObjectsCollectedByMinorGC), + V8_CRDTP_DESERIALIZE_FIELD_OPT("samplingInterval", samplingInterval), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::startSampling(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + startSamplingParams params; + if (!startSamplingParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->startSampling(std::move(params.samplingInterval), std::move(params.includeObjectsCollectedByMajorGC), std::move(params.includeObjectsCollectedByMinorGC)); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("HeapProfiler.startSampling"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct startTrackingHeapObjectsParams : public v8_crdtp::DeserializableProtocolObject { + Maybe trackAllocations; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(startTrackingHeapObjectsParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("trackAllocations", trackAllocations), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::startTrackingHeapObjects(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + startTrackingHeapObjectsParams params; + if (!startTrackingHeapObjectsParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->startTrackingHeapObjects(std::move(params.trackAllocations)); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("HeapProfiler.startTrackingHeapObjects"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::stopSampling(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + // Declare output parameters. + std::unique_ptr out_profile; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->stopSampling(&out_profile); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("HeapProfiler.stopSampling"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("profile"), out_profile); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct stopTrackingHeapObjectsParams : public v8_crdtp::DeserializableProtocolObject { + Maybe reportProgress; + Maybe treatGlobalObjectsAsRoots; + Maybe captureNumericValue; + Maybe exposeInternals; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(stopTrackingHeapObjectsParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("captureNumericValue", captureNumericValue), + V8_CRDTP_DESERIALIZE_FIELD_OPT("exposeInternals", exposeInternals), + V8_CRDTP_DESERIALIZE_FIELD_OPT("reportProgress", reportProgress), + V8_CRDTP_DESERIALIZE_FIELD_OPT("treatGlobalObjectsAsRoots", treatGlobalObjectsAsRoots), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::stopTrackingHeapObjects(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + stopTrackingHeapObjectsParams params; + if (!stopTrackingHeapObjectsParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->stopTrackingHeapObjects(std::move(params.reportProgress), std::move(params.treatGlobalObjectsAsRoots), std::move(params.captureNumericValue), std::move(params.exposeInternals)); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("HeapProfiler.stopTrackingHeapObjects"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct takeHeapSnapshotParams : public v8_crdtp::DeserializableProtocolObject { + Maybe reportProgress; + Maybe treatGlobalObjectsAsRoots; + Maybe captureNumericValue; + Maybe exposeInternals; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(takeHeapSnapshotParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("captureNumericValue", captureNumericValue), + V8_CRDTP_DESERIALIZE_FIELD_OPT("exposeInternals", exposeInternals), + V8_CRDTP_DESERIALIZE_FIELD_OPT("reportProgress", reportProgress), + V8_CRDTP_DESERIALIZE_FIELD_OPT("treatGlobalObjectsAsRoots", treatGlobalObjectsAsRoots), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::takeHeapSnapshot(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + takeHeapSnapshotParams params; + if (!takeHeapSnapshotParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->takeHeapSnapshot(std::move(params.reportProgress), std::move(params.treatGlobalObjectsAsRoots), std::move(params.captureNumericValue), std::move(params.exposeInternals)); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("HeapProfiler.takeHeapSnapshot"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { +// This helper method (with a static map of redirects) is used from Dispatcher::wire +// immediately below. +const std::vector, v8_crdtp::span>>& SortedRedirects() { + static auto* redirects = [](){ + auto* redirects = new std::vector, v8_crdtp::span>>{ + }; + return redirects; + }(); + return *redirects; +} +} // namespace + +// static +void Dispatcher::wire(UberDispatcher* uber, Backend* backend) +{ + auto dispatcher = std::make_unique(uber->channel(), backend); + uber->WireBackend(v8_crdtp::SpanFrom("HeapProfiler"), SortedRedirects(), std::move(dispatcher)); +} + +} // HeapProfiler +} // namespace v8_inspector +} // namespace protocol diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/HeapProfiler.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/HeapProfiler.h new file mode 100644 index 000000000..9b0030c8e --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/HeapProfiler.h @@ -0,0 +1,346 @@ +// This file is generated by TypeBuilder_h.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef v8_inspector_protocol_HeapProfiler_h +#define v8_inspector_protocol_HeapProfiler_h + +#include "src/inspector/protocol/Protocol.h" +// For each imported domain we generate a ValueConversions struct instead of a full domain definition +// and include Domain::API version from there. +#include "src/inspector/protocol/Runtime.h" + +namespace v8_inspector { +namespace protocol { +namespace HeapProfiler { +using HeapSnapshotObjectId = String; +class SamplingHeapProfileNode; +class SamplingHeapProfileSample; +class SamplingHeapProfile; + +// ------------- Forward and enum declarations. + +// ------------- Type and builder declarations. + +class SamplingHeapProfileNode : public ::v8_crdtp::ProtocolObject { +public: + ~SamplingHeapProfileNode() override { } + + protocol::Runtime::CallFrame* getCallFrame() { return m_callFrame.get(); } + void setCallFrame(std::unique_ptr value) { m_callFrame = std::move(value); } + + double getSelfSize() { return m_selfSize; } + void setSelfSize(double value) { m_selfSize = value; } + + int getId() { return m_id; } + void setId(int value) { m_id = value; } + + protocol::Array* getChildren() { return m_children.get(); } + void setChildren(std::unique_ptr> value) { m_children = std::move(value); } + + template + class SamplingHeapProfileNodeBuilder { + public: + enum { + NoFieldsSet = 0, + CallFrameSet = 1 << 1, + SelfSizeSet = 1 << 2, + IdSet = 1 << 3, + ChildrenSet = 1 << 4, + AllFieldsSet = (CallFrameSet | SelfSizeSet | IdSet | ChildrenSet | 0)}; + + + SamplingHeapProfileNodeBuilder& setCallFrame(std::unique_ptr value) + { + static_assert(!(STATE & CallFrameSet), "property callFrame should not be set yet"); + m_result->setCallFrame(std::move(value)); + return castState(); + } + + SamplingHeapProfileNodeBuilder& setSelfSize(double value) + { + static_assert(!(STATE & SelfSizeSet), "property selfSize should not be set yet"); + m_result->setSelfSize(value); + return castState(); + } + + SamplingHeapProfileNodeBuilder& setId(int value) + { + static_assert(!(STATE & IdSet), "property id should not be set yet"); + m_result->setId(value); + return castState(); + } + + SamplingHeapProfileNodeBuilder& setChildren(std::unique_ptr> value) + { + static_assert(!(STATE & ChildrenSet), "property children should not be set yet"); + m_result->setChildren(std::move(value)); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class SamplingHeapProfileNode; + SamplingHeapProfileNodeBuilder() : m_result(new SamplingHeapProfileNode()) { } + + template SamplingHeapProfileNodeBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static SamplingHeapProfileNodeBuilder<0> create() + { + return SamplingHeapProfileNodeBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + SamplingHeapProfileNode() + { + m_selfSize = 0; + m_id = 0; + } + + std::unique_ptr m_callFrame; + double m_selfSize; + int m_id; + std::unique_ptr> m_children; +}; + + +class SamplingHeapProfileSample : public ::v8_crdtp::ProtocolObject { +public: + ~SamplingHeapProfileSample() override { } + + double getSize() { return m_size; } + void setSize(double value) { m_size = value; } + + int getNodeId() { return m_nodeId; } + void setNodeId(int value) { m_nodeId = value; } + + double getOrdinal() { return m_ordinal; } + void setOrdinal(double value) { m_ordinal = value; } + + template + class SamplingHeapProfileSampleBuilder { + public: + enum { + NoFieldsSet = 0, + SizeSet = 1 << 1, + NodeIdSet = 1 << 2, + OrdinalSet = 1 << 3, + AllFieldsSet = (SizeSet | NodeIdSet | OrdinalSet | 0)}; + + + SamplingHeapProfileSampleBuilder& setSize(double value) + { + static_assert(!(STATE & SizeSet), "property size should not be set yet"); + m_result->setSize(value); + return castState(); + } + + SamplingHeapProfileSampleBuilder& setNodeId(int value) + { + static_assert(!(STATE & NodeIdSet), "property nodeId should not be set yet"); + m_result->setNodeId(value); + return castState(); + } + + SamplingHeapProfileSampleBuilder& setOrdinal(double value) + { + static_assert(!(STATE & OrdinalSet), "property ordinal should not be set yet"); + m_result->setOrdinal(value); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class SamplingHeapProfileSample; + SamplingHeapProfileSampleBuilder() : m_result(new SamplingHeapProfileSample()) { } + + template SamplingHeapProfileSampleBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static SamplingHeapProfileSampleBuilder<0> create() + { + return SamplingHeapProfileSampleBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + SamplingHeapProfileSample() + { + m_size = 0; + m_nodeId = 0; + m_ordinal = 0; + } + + double m_size; + int m_nodeId; + double m_ordinal; +}; + + +class SamplingHeapProfile : public ::v8_crdtp::ProtocolObject { +public: + ~SamplingHeapProfile() override { } + + protocol::HeapProfiler::SamplingHeapProfileNode* getHead() { return m_head.get(); } + void setHead(std::unique_ptr value) { m_head = std::move(value); } + + protocol::Array* getSamples() { return m_samples.get(); } + void setSamples(std::unique_ptr> value) { m_samples = std::move(value); } + + template + class SamplingHeapProfileBuilder { + public: + enum { + NoFieldsSet = 0, + HeadSet = 1 << 1, + SamplesSet = 1 << 2, + AllFieldsSet = (HeadSet | SamplesSet | 0)}; + + + SamplingHeapProfileBuilder& setHead(std::unique_ptr value) + { + static_assert(!(STATE & HeadSet), "property head should not be set yet"); + m_result->setHead(std::move(value)); + return castState(); + } + + SamplingHeapProfileBuilder& setSamples(std::unique_ptr> value) + { + static_assert(!(STATE & SamplesSet), "property samples should not be set yet"); + m_result->setSamples(std::move(value)); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class SamplingHeapProfile; + SamplingHeapProfileBuilder() : m_result(new SamplingHeapProfile()) { } + + template SamplingHeapProfileBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static SamplingHeapProfileBuilder<0> create() + { + return SamplingHeapProfileBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + SamplingHeapProfile() + { + } + + std::unique_ptr m_head; + std::unique_ptr> m_samples; +}; + + +// ------------- Backend interface. + +class Backend { +public: + virtual ~Backend() { } + + virtual DispatchResponse addInspectedHeapObject(const String& in_heapObjectId) = 0; + class CollectGarbageCallback { + public: + virtual void sendSuccess() = 0; + virtual void sendFailure(const DispatchResponse&) = 0; + virtual void fallThrough() = 0; + virtual ~CollectGarbageCallback() { } + }; + virtual void collectGarbage(std::unique_ptr callback) = 0; + virtual DispatchResponse disable() = 0; + virtual DispatchResponse enable() = 0; + virtual DispatchResponse getHeapObjectId(const String& in_objectId, String* out_heapSnapshotObjectId) = 0; + virtual DispatchResponse getObjectByHeapObjectId(const String& in_objectId, Maybe in_objectGroup, std::unique_ptr* out_result) = 0; + virtual DispatchResponse getSamplingProfile(std::unique_ptr* out_profile) = 0; + virtual DispatchResponse startSampling(Maybe in_samplingInterval, Maybe in_includeObjectsCollectedByMajorGC, Maybe in_includeObjectsCollectedByMinorGC) = 0; + virtual DispatchResponse startTrackingHeapObjects(Maybe in_trackAllocations) = 0; + virtual DispatchResponse stopSampling(std::unique_ptr* out_profile) = 0; + virtual DispatchResponse stopTrackingHeapObjects(Maybe in_reportProgress, Maybe in_treatGlobalObjectsAsRoots, Maybe in_captureNumericValue, Maybe in_exposeInternals) = 0; + virtual DispatchResponse takeHeapSnapshot(Maybe in_reportProgress, Maybe in_treatGlobalObjectsAsRoots, Maybe in_captureNumericValue, Maybe in_exposeInternals) = 0; + +}; + +// ------------- Frontend interface. + +class Frontend { +public: + explicit Frontend(FrontendChannel* frontend_channel) : frontend_channel_(frontend_channel) {} + void addHeapSnapshotChunk(const String& chunk); + void heapStatsUpdate(std::unique_ptr> statsUpdate); + void lastSeenObjectId(int lastSeenObjectId, double timestamp); + void reportHeapSnapshotProgress(int done, int total, Maybe finished = Maybe()); + void resetProfiles(); + + void flush(); + void sendRawNotification(std::unique_ptr); + private: + FrontendChannel* frontend_channel_; +}; + +// ------------- Dispatcher. + +class Dispatcher { +public: + static void wire(UberDispatcher*, Backend*); + +private: + Dispatcher() { } +}; + +// ------------- Metainfo. + +class Metainfo { +public: + using BackendClass = Backend; + using FrontendClass = Frontend; + using DispatcherClass = Dispatcher; + static const char domainName[]; + static const char commandPrefix[]; + static const char version[]; +}; + +} // namespace HeapProfiler +} // namespace v8_inspector +} // namespace protocol + +#endif // !defined(v8_inspector_protocol_HeapProfiler_h) diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Profiler.cpp b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Profiler.cpp new file mode 100644 index 000000000..61c59cdd9 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Profiler.cpp @@ -0,0 +1,530 @@ +// This file is generated by TypeBuilder_cpp.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/inspector/protocol/Profiler.h" + +#include "src/inspector/protocol/Protocol.h" + +#include "third_party/inspector_protocol/crdtp/cbor.h" +#include "third_party/inspector_protocol/crdtp/find_by_first.h" +#include "third_party/inspector_protocol/crdtp/span.h" + +namespace v8_inspector { +namespace protocol { +namespace Profiler { + +using v8_crdtp::DeserializerState; +using v8_crdtp::ProtocolTypeTraits; + +// ------------- Enum values from types. + +const char Metainfo::domainName[] = "Profiler"; +const char Metainfo::commandPrefix[] = "Profiler."; +const char Metainfo::version[] = "1.3"; + +V8_CRDTP_BEGIN_DESERIALIZER(ProfileNode) + V8_CRDTP_DESERIALIZE_FIELD("callFrame", m_callFrame), + V8_CRDTP_DESERIALIZE_FIELD_OPT("children", m_children), + V8_CRDTP_DESERIALIZE_FIELD_OPT("deoptReason", m_deoptReason), + V8_CRDTP_DESERIALIZE_FIELD_OPT("hitCount", m_hitCount), + V8_CRDTP_DESERIALIZE_FIELD("id", m_id), + V8_CRDTP_DESERIALIZE_FIELD_OPT("positionTicks", m_positionTicks), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(ProfileNode) + V8_CRDTP_SERIALIZE_FIELD("id", m_id); + V8_CRDTP_SERIALIZE_FIELD("callFrame", m_callFrame); + V8_CRDTP_SERIALIZE_FIELD("hitCount", m_hitCount); + V8_CRDTP_SERIALIZE_FIELD("children", m_children); + V8_CRDTP_SERIALIZE_FIELD("deoptReason", m_deoptReason); + V8_CRDTP_SERIALIZE_FIELD("positionTicks", m_positionTicks); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(Profile) + V8_CRDTP_DESERIALIZE_FIELD("endTime", m_endTime), + V8_CRDTP_DESERIALIZE_FIELD("nodes", m_nodes), + V8_CRDTP_DESERIALIZE_FIELD_OPT("samples", m_samples), + V8_CRDTP_DESERIALIZE_FIELD("startTime", m_startTime), + V8_CRDTP_DESERIALIZE_FIELD_OPT("timeDeltas", m_timeDeltas), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(Profile) + V8_CRDTP_SERIALIZE_FIELD("nodes", m_nodes); + V8_CRDTP_SERIALIZE_FIELD("startTime", m_startTime); + V8_CRDTP_SERIALIZE_FIELD("endTime", m_endTime); + V8_CRDTP_SERIALIZE_FIELD("samples", m_samples); + V8_CRDTP_SERIALIZE_FIELD("timeDeltas", m_timeDeltas); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(PositionTickInfo) + V8_CRDTP_DESERIALIZE_FIELD("line", m_line), + V8_CRDTP_DESERIALIZE_FIELD("ticks", m_ticks), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(PositionTickInfo) + V8_CRDTP_SERIALIZE_FIELD("line", m_line); + V8_CRDTP_SERIALIZE_FIELD("ticks", m_ticks); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(CoverageRange) + V8_CRDTP_DESERIALIZE_FIELD("count", m_count), + V8_CRDTP_DESERIALIZE_FIELD("endOffset", m_endOffset), + V8_CRDTP_DESERIALIZE_FIELD("startOffset", m_startOffset), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(CoverageRange) + V8_CRDTP_SERIALIZE_FIELD("startOffset", m_startOffset); + V8_CRDTP_SERIALIZE_FIELD("endOffset", m_endOffset); + V8_CRDTP_SERIALIZE_FIELD("count", m_count); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(FunctionCoverage) + V8_CRDTP_DESERIALIZE_FIELD("functionName", m_functionName), + V8_CRDTP_DESERIALIZE_FIELD("isBlockCoverage", m_isBlockCoverage), + V8_CRDTP_DESERIALIZE_FIELD("ranges", m_ranges), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(FunctionCoverage) + V8_CRDTP_SERIALIZE_FIELD("functionName", m_functionName); + V8_CRDTP_SERIALIZE_FIELD("ranges", m_ranges); + V8_CRDTP_SERIALIZE_FIELD("isBlockCoverage", m_isBlockCoverage); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(ScriptCoverage) + V8_CRDTP_DESERIALIZE_FIELD("functions", m_functions), + V8_CRDTP_DESERIALIZE_FIELD("scriptId", m_scriptId), + V8_CRDTP_DESERIALIZE_FIELD("url", m_url), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(ScriptCoverage) + V8_CRDTP_SERIALIZE_FIELD("scriptId", m_scriptId); + V8_CRDTP_SERIALIZE_FIELD("url", m_url); + V8_CRDTP_SERIALIZE_FIELD("functions", m_functions); +V8_CRDTP_END_SERIALIZER(); + + +// ------------- Enum values from params. + + +// ------------- Frontend notifications. + +void Frontend::consoleProfileFinished(const String& id, std::unique_ptr location, std::unique_ptr profile, Maybe title) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("id"), id); + serializer.AddField(v8_crdtp::MakeSpan("location"), location); + serializer.AddField(v8_crdtp::MakeSpan("profile"), profile); + serializer.AddField(v8_crdtp::MakeSpan("title"), title); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Profiler.consoleProfileFinished", serializer.Finish())); +} + +void Frontend::consoleProfileStarted(const String& id, std::unique_ptr location, Maybe title) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("id"), id); + serializer.AddField(v8_crdtp::MakeSpan("location"), location); + serializer.AddField(v8_crdtp::MakeSpan("title"), title); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Profiler.consoleProfileStarted", serializer.Finish())); +} + +void Frontend::preciseCoverageDeltaUpdate(double timestamp, const String& occasion, std::unique_ptr> result) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("timestamp"), timestamp); + serializer.AddField(v8_crdtp::MakeSpan("occasion"), occasion); + serializer.AddField(v8_crdtp::MakeSpan("result"), result); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Profiler.preciseCoverageDeltaUpdate", serializer.Finish())); +} + +void Frontend::flush() +{ + frontend_channel_->FlushProtocolNotifications(); +} + +void Frontend::sendRawNotification(std::unique_ptr notification) +{ + frontend_channel_->SendProtocolNotification(std::move(notification)); +} + +// --------------------- Dispatcher. + +class DomainDispatcherImpl : public protocol::DomainDispatcher { +public: + DomainDispatcherImpl(FrontendChannel* frontendChannel, Backend* backend) + : DomainDispatcher(frontendChannel) + , m_backend(backend) {} + ~DomainDispatcherImpl() override { } + + using CallHandler = void (DomainDispatcherImpl::*)(const v8_crdtp::Dispatchable& dispatchable); + + std::function Dispatch(v8_crdtp::span command_name) override; + + void disable(const v8_crdtp::Dispatchable& dispatchable); + void enable(const v8_crdtp::Dispatchable& dispatchable); + void getBestEffortCoverage(const v8_crdtp::Dispatchable& dispatchable); + void setSamplingInterval(const v8_crdtp::Dispatchable& dispatchable); + void start(const v8_crdtp::Dispatchable& dispatchable); + void startPreciseCoverage(const v8_crdtp::Dispatchable& dispatchable); + void stop(const v8_crdtp::Dispatchable& dispatchable); + void stopPreciseCoverage(const v8_crdtp::Dispatchable& dispatchable); + void takePreciseCoverage(const v8_crdtp::Dispatchable& dispatchable); + protected: + Backend* m_backend; +}; + +namespace { +// This helper method with a static map of command methods (instance methods +// of DomainDispatcherImpl declared just above) by their name is used immediately below, +// in the DomainDispatcherImpl::Dispatch method. +DomainDispatcherImpl::CallHandler CommandByName(v8_crdtp::span command_name) { + static auto* commands = [](){ + auto* commands = new std::vector, + DomainDispatcherImpl::CallHandler>>{ + { + v8_crdtp::SpanFrom("disable"), + &DomainDispatcherImpl::disable + }, + { + v8_crdtp::SpanFrom("enable"), + &DomainDispatcherImpl::enable + }, + { + v8_crdtp::SpanFrom("getBestEffortCoverage"), + &DomainDispatcherImpl::getBestEffortCoverage + }, + { + v8_crdtp::SpanFrom("setSamplingInterval"), + &DomainDispatcherImpl::setSamplingInterval + }, + { + v8_crdtp::SpanFrom("start"), + &DomainDispatcherImpl::start + }, + { + v8_crdtp::SpanFrom("startPreciseCoverage"), + &DomainDispatcherImpl::startPreciseCoverage + }, + { + v8_crdtp::SpanFrom("stop"), + &DomainDispatcherImpl::stop + }, + { + v8_crdtp::SpanFrom("stopPreciseCoverage"), + &DomainDispatcherImpl::stopPreciseCoverage + }, + { + v8_crdtp::SpanFrom("takePreciseCoverage"), + &DomainDispatcherImpl::takePreciseCoverage + }, + }; + return commands; + }(); + return v8_crdtp::FindByFirst(*commands, command_name, nullptr); +} +} // namespace + +std::function DomainDispatcherImpl::Dispatch(v8_crdtp::span command_name) { + CallHandler handler = CommandByName(command_name); + if (!handler) return nullptr; + + return [this, handler](const v8_crdtp::Dispatchable& dispatchable) { + (this->*handler)(dispatchable); + }; +} + + +namespace { + + +} // namespace + +void DomainDispatcherImpl::disable(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->disable(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Profiler.disable"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::enable(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->enable(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Profiler.enable"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::getBestEffortCoverage(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + // Declare output parameters. + std::unique_ptr> out_result; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->getBestEffortCoverage(&out_result); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Profiler.getBestEffortCoverage"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("result"), out_result); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct setSamplingIntervalParams : public v8_crdtp::DeserializableProtocolObject { + int interval; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setSamplingIntervalParams) + V8_CRDTP_DESERIALIZE_FIELD("interval", interval), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setSamplingInterval(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setSamplingIntervalParams params; + if (!setSamplingIntervalParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setSamplingInterval(params.interval); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Profiler.setSamplingInterval"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::start(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->start(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Profiler.start"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct startPreciseCoverageParams : public v8_crdtp::DeserializableProtocolObject { + Maybe callCount; + Maybe detailed; + Maybe allowTriggeredUpdates; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(startPreciseCoverageParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("allowTriggeredUpdates", allowTriggeredUpdates), + V8_CRDTP_DESERIALIZE_FIELD_OPT("callCount", callCount), + V8_CRDTP_DESERIALIZE_FIELD_OPT("detailed", detailed), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::startPreciseCoverage(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + startPreciseCoverageParams params; + if (!startPreciseCoverageParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + double out_timestamp; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->startPreciseCoverage(std::move(params.callCount), std::move(params.detailed), std::move(params.allowTriggeredUpdates), &out_timestamp); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Profiler.startPreciseCoverage"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("timestamp"), out_timestamp); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::stop(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + // Declare output parameters. + std::unique_ptr out_profile; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->stop(&out_profile); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Profiler.stop"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("profile"), out_profile); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::stopPreciseCoverage(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->stopPreciseCoverage(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Profiler.stopPreciseCoverage"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::takePreciseCoverage(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + // Declare output parameters. + std::unique_ptr> out_result; + double out_timestamp; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->takePreciseCoverage(&out_result, &out_timestamp); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Profiler.takePreciseCoverage"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("result"), out_result); + serializer.AddField(v8_crdtp::MakeSpan("timestamp"), out_timestamp); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { +// This helper method (with a static map of redirects) is used from Dispatcher::wire +// immediately below. +const std::vector, v8_crdtp::span>>& SortedRedirects() { + static auto* redirects = [](){ + auto* redirects = new std::vector, v8_crdtp::span>>{ + }; + return redirects; + }(); + return *redirects; +} +} // namespace + +// static +void Dispatcher::wire(UberDispatcher* uber, Backend* backend) +{ + auto dispatcher = std::make_unique(uber->channel(), backend); + uber->WireBackend(v8_crdtp::SpanFrom("Profiler"), SortedRedirects(), std::move(dispatcher)); +} + +} // Profiler +} // namespace v8_inspector +} // namespace protocol diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Profiler.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Profiler.h new file mode 100644 index 000000000..a1eef3025 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Profiler.h @@ -0,0 +1,626 @@ +// This file is generated by TypeBuilder_h.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef v8_inspector_protocol_Profiler_h +#define v8_inspector_protocol_Profiler_h + +#include "src/inspector/protocol/Protocol.h" +// For each imported domain we generate a ValueConversions struct instead of a full domain definition +// and include Domain::API version from there. +#include "src/inspector/protocol/Runtime.h" +#include "src/inspector/protocol/Debugger.h" + +namespace v8_inspector { +namespace protocol { +namespace Profiler { +class ProfileNode; +class Profile; +class PositionTickInfo; +class CoverageRange; +class FunctionCoverage; +class ScriptCoverage; + +// ------------- Forward and enum declarations. + +// ------------- Type and builder declarations. + +class ProfileNode : public ::v8_crdtp::ProtocolObject { +public: + ~ProfileNode() override { } + + int getId() { return m_id; } + void setId(int value) { m_id = value; } + + protocol::Runtime::CallFrame* getCallFrame() { return m_callFrame.get(); } + void setCallFrame(std::unique_ptr value) { m_callFrame = std::move(value); } + + bool hasHitCount() { return m_hitCount.isJust(); } + int getHitCount(int defaultValue) { return m_hitCount.isJust() ? m_hitCount.fromJust() : defaultValue; } + void setHitCount(int value) { m_hitCount = value; } + + bool hasChildren() { return m_children.isJust(); } + protocol::Array* getChildren(protocol::Array* defaultValue) { return m_children.isJust() ? m_children.fromJust() : defaultValue; } + void setChildren(std::unique_ptr> value) { m_children = std::move(value); } + + bool hasDeoptReason() { return m_deoptReason.isJust(); } + String getDeoptReason(const String& defaultValue) { return m_deoptReason.isJust() ? m_deoptReason.fromJust() : defaultValue; } + void setDeoptReason(const String& value) { m_deoptReason = value; } + + bool hasPositionTicks() { return m_positionTicks.isJust(); } + protocol::Array* getPositionTicks(protocol::Array* defaultValue) { return m_positionTicks.isJust() ? m_positionTicks.fromJust() : defaultValue; } + void setPositionTicks(std::unique_ptr> value) { m_positionTicks = std::move(value); } + + template + class ProfileNodeBuilder { + public: + enum { + NoFieldsSet = 0, + IdSet = 1 << 1, + CallFrameSet = 1 << 2, + AllFieldsSet = (IdSet | CallFrameSet | 0)}; + + + ProfileNodeBuilder& setId(int value) + { + static_assert(!(STATE & IdSet), "property id should not be set yet"); + m_result->setId(value); + return castState(); + } + + ProfileNodeBuilder& setCallFrame(std::unique_ptr value) + { + static_assert(!(STATE & CallFrameSet), "property callFrame should not be set yet"); + m_result->setCallFrame(std::move(value)); + return castState(); + } + + ProfileNodeBuilder& setHitCount(int value) + { + m_result->setHitCount(value); + return *this; + } + + ProfileNodeBuilder& setChildren(std::unique_ptr> value) + { + m_result->setChildren(std::move(value)); + return *this; + } + + ProfileNodeBuilder& setDeoptReason(const String& value) + { + m_result->setDeoptReason(value); + return *this; + } + + ProfileNodeBuilder& setPositionTicks(std::unique_ptr> value) + { + m_result->setPositionTicks(std::move(value)); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class ProfileNode; + ProfileNodeBuilder() : m_result(new ProfileNode()) { } + + template ProfileNodeBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static ProfileNodeBuilder<0> create() + { + return ProfileNodeBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + ProfileNode() + { + m_id = 0; + } + + int m_id; + std::unique_ptr m_callFrame; + Maybe m_hitCount; + Maybe> m_children; + Maybe m_deoptReason; + Maybe> m_positionTicks; +}; + + +class Profile : public ::v8_crdtp::ProtocolObject { +public: + ~Profile() override { } + + protocol::Array* getNodes() { return m_nodes.get(); } + void setNodes(std::unique_ptr> value) { m_nodes = std::move(value); } + + double getStartTime() { return m_startTime; } + void setStartTime(double value) { m_startTime = value; } + + double getEndTime() { return m_endTime; } + void setEndTime(double value) { m_endTime = value; } + + bool hasSamples() { return m_samples.isJust(); } + protocol::Array* getSamples(protocol::Array* defaultValue) { return m_samples.isJust() ? m_samples.fromJust() : defaultValue; } + void setSamples(std::unique_ptr> value) { m_samples = std::move(value); } + + bool hasTimeDeltas() { return m_timeDeltas.isJust(); } + protocol::Array* getTimeDeltas(protocol::Array* defaultValue) { return m_timeDeltas.isJust() ? m_timeDeltas.fromJust() : defaultValue; } + void setTimeDeltas(std::unique_ptr> value) { m_timeDeltas = std::move(value); } + + template + class ProfileBuilder { + public: + enum { + NoFieldsSet = 0, + NodesSet = 1 << 1, + StartTimeSet = 1 << 2, + EndTimeSet = 1 << 3, + AllFieldsSet = (NodesSet | StartTimeSet | EndTimeSet | 0)}; + + + ProfileBuilder& setNodes(std::unique_ptr> value) + { + static_assert(!(STATE & NodesSet), "property nodes should not be set yet"); + m_result->setNodes(std::move(value)); + return castState(); + } + + ProfileBuilder& setStartTime(double value) + { + static_assert(!(STATE & StartTimeSet), "property startTime should not be set yet"); + m_result->setStartTime(value); + return castState(); + } + + ProfileBuilder& setEndTime(double value) + { + static_assert(!(STATE & EndTimeSet), "property endTime should not be set yet"); + m_result->setEndTime(value); + return castState(); + } + + ProfileBuilder& setSamples(std::unique_ptr> value) + { + m_result->setSamples(std::move(value)); + return *this; + } + + ProfileBuilder& setTimeDeltas(std::unique_ptr> value) + { + m_result->setTimeDeltas(std::move(value)); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class Profile; + ProfileBuilder() : m_result(new Profile()) { } + + template ProfileBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static ProfileBuilder<0> create() + { + return ProfileBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + Profile() + { + m_startTime = 0; + m_endTime = 0; + } + + std::unique_ptr> m_nodes; + double m_startTime; + double m_endTime; + Maybe> m_samples; + Maybe> m_timeDeltas; +}; + + +class PositionTickInfo : public ::v8_crdtp::ProtocolObject { +public: + ~PositionTickInfo() override { } + + int getLine() { return m_line; } + void setLine(int value) { m_line = value; } + + int getTicks() { return m_ticks; } + void setTicks(int value) { m_ticks = value; } + + template + class PositionTickInfoBuilder { + public: + enum { + NoFieldsSet = 0, + LineSet = 1 << 1, + TicksSet = 1 << 2, + AllFieldsSet = (LineSet | TicksSet | 0)}; + + + PositionTickInfoBuilder& setLine(int value) + { + static_assert(!(STATE & LineSet), "property line should not be set yet"); + m_result->setLine(value); + return castState(); + } + + PositionTickInfoBuilder& setTicks(int value) + { + static_assert(!(STATE & TicksSet), "property ticks should not be set yet"); + m_result->setTicks(value); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class PositionTickInfo; + PositionTickInfoBuilder() : m_result(new PositionTickInfo()) { } + + template PositionTickInfoBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static PositionTickInfoBuilder<0> create() + { + return PositionTickInfoBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + PositionTickInfo() + { + m_line = 0; + m_ticks = 0; + } + + int m_line; + int m_ticks; +}; + + +class CoverageRange : public ::v8_crdtp::ProtocolObject { +public: + ~CoverageRange() override { } + + int getStartOffset() { return m_startOffset; } + void setStartOffset(int value) { m_startOffset = value; } + + int getEndOffset() { return m_endOffset; } + void setEndOffset(int value) { m_endOffset = value; } + + int getCount() { return m_count; } + void setCount(int value) { m_count = value; } + + template + class CoverageRangeBuilder { + public: + enum { + NoFieldsSet = 0, + StartOffsetSet = 1 << 1, + EndOffsetSet = 1 << 2, + CountSet = 1 << 3, + AllFieldsSet = (StartOffsetSet | EndOffsetSet | CountSet | 0)}; + + + CoverageRangeBuilder& setStartOffset(int value) + { + static_assert(!(STATE & StartOffsetSet), "property startOffset should not be set yet"); + m_result->setStartOffset(value); + return castState(); + } + + CoverageRangeBuilder& setEndOffset(int value) + { + static_assert(!(STATE & EndOffsetSet), "property endOffset should not be set yet"); + m_result->setEndOffset(value); + return castState(); + } + + CoverageRangeBuilder& setCount(int value) + { + static_assert(!(STATE & CountSet), "property count should not be set yet"); + m_result->setCount(value); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class CoverageRange; + CoverageRangeBuilder() : m_result(new CoverageRange()) { } + + template CoverageRangeBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static CoverageRangeBuilder<0> create() + { + return CoverageRangeBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + CoverageRange() + { + m_startOffset = 0; + m_endOffset = 0; + m_count = 0; + } + + int m_startOffset; + int m_endOffset; + int m_count; +}; + + +class FunctionCoverage : public ::v8_crdtp::ProtocolObject { +public: + ~FunctionCoverage() override { } + + String getFunctionName() { return m_functionName; } + void setFunctionName(const String& value) { m_functionName = value; } + + protocol::Array* getRanges() { return m_ranges.get(); } + void setRanges(std::unique_ptr> value) { m_ranges = std::move(value); } + + bool getIsBlockCoverage() { return m_isBlockCoverage; } + void setIsBlockCoverage(bool value) { m_isBlockCoverage = value; } + + template + class FunctionCoverageBuilder { + public: + enum { + NoFieldsSet = 0, + FunctionNameSet = 1 << 1, + RangesSet = 1 << 2, + IsBlockCoverageSet = 1 << 3, + AllFieldsSet = (FunctionNameSet | RangesSet | IsBlockCoverageSet | 0)}; + + + FunctionCoverageBuilder& setFunctionName(const String& value) + { + static_assert(!(STATE & FunctionNameSet), "property functionName should not be set yet"); + m_result->setFunctionName(value); + return castState(); + } + + FunctionCoverageBuilder& setRanges(std::unique_ptr> value) + { + static_assert(!(STATE & RangesSet), "property ranges should not be set yet"); + m_result->setRanges(std::move(value)); + return castState(); + } + + FunctionCoverageBuilder& setIsBlockCoverage(bool value) + { + static_assert(!(STATE & IsBlockCoverageSet), "property isBlockCoverage should not be set yet"); + m_result->setIsBlockCoverage(value); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class FunctionCoverage; + FunctionCoverageBuilder() : m_result(new FunctionCoverage()) { } + + template FunctionCoverageBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static FunctionCoverageBuilder<0> create() + { + return FunctionCoverageBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + FunctionCoverage() + { + m_isBlockCoverage = false; + } + + String m_functionName; + std::unique_ptr> m_ranges; + bool m_isBlockCoverage; +}; + + +class ScriptCoverage : public ::v8_crdtp::ProtocolObject { +public: + ~ScriptCoverage() override { } + + String getScriptId() { return m_scriptId; } + void setScriptId(const String& value) { m_scriptId = value; } + + String getUrl() { return m_url; } + void setUrl(const String& value) { m_url = value; } + + protocol::Array* getFunctions() { return m_functions.get(); } + void setFunctions(std::unique_ptr> value) { m_functions = std::move(value); } + + template + class ScriptCoverageBuilder { + public: + enum { + NoFieldsSet = 0, + ScriptIdSet = 1 << 1, + UrlSet = 1 << 2, + FunctionsSet = 1 << 3, + AllFieldsSet = (ScriptIdSet | UrlSet | FunctionsSet | 0)}; + + + ScriptCoverageBuilder& setScriptId(const String& value) + { + static_assert(!(STATE & ScriptIdSet), "property scriptId should not be set yet"); + m_result->setScriptId(value); + return castState(); + } + + ScriptCoverageBuilder& setUrl(const String& value) + { + static_assert(!(STATE & UrlSet), "property url should not be set yet"); + m_result->setUrl(value); + return castState(); + } + + ScriptCoverageBuilder& setFunctions(std::unique_ptr> value) + { + static_assert(!(STATE & FunctionsSet), "property functions should not be set yet"); + m_result->setFunctions(std::move(value)); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class ScriptCoverage; + ScriptCoverageBuilder() : m_result(new ScriptCoverage()) { } + + template ScriptCoverageBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static ScriptCoverageBuilder<0> create() + { + return ScriptCoverageBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + ScriptCoverage() + { + } + + String m_scriptId; + String m_url; + std::unique_ptr> m_functions; +}; + + +// ------------- Backend interface. + +class Backend { +public: + virtual ~Backend() { } + + virtual DispatchResponse disable() = 0; + virtual DispatchResponse enable() = 0; + virtual DispatchResponse getBestEffortCoverage(std::unique_ptr>* out_result) = 0; + virtual DispatchResponse setSamplingInterval(int in_interval) = 0; + virtual DispatchResponse start() = 0; + virtual DispatchResponse startPreciseCoverage(Maybe in_callCount, Maybe in_detailed, Maybe in_allowTriggeredUpdates, double* out_timestamp) = 0; + virtual DispatchResponse stop(std::unique_ptr* out_profile) = 0; + virtual DispatchResponse stopPreciseCoverage() = 0; + virtual DispatchResponse takePreciseCoverage(std::unique_ptr>* out_result, double* out_timestamp) = 0; + +}; + +// ------------- Frontend interface. + +class Frontend { +public: + explicit Frontend(FrontendChannel* frontend_channel) : frontend_channel_(frontend_channel) {} + void consoleProfileFinished(const String& id, std::unique_ptr location, std::unique_ptr profile, Maybe title = Maybe()); + void consoleProfileStarted(const String& id, std::unique_ptr location, Maybe title = Maybe()); + void preciseCoverageDeltaUpdate(double timestamp, const String& occasion, std::unique_ptr> result); + + void flush(); + void sendRawNotification(std::unique_ptr); + private: + FrontendChannel* frontend_channel_; +}; + +// ------------- Dispatcher. + +class Dispatcher { +public: + static void wire(UberDispatcher*, Backend*); + +private: + Dispatcher() { } +}; + +// ------------- Metainfo. + +class Metainfo { +public: + using BackendClass = Backend; + using FrontendClass = Frontend; + using DispatcherClass = Dispatcher; + static const char domainName[]; + static const char commandPrefix[]; + static const char version[]; +}; + +} // namespace Profiler +} // namespace v8_inspector +} // namespace protocol + +#endif // !defined(v8_inspector_protocol_Profiler_h) diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Protocol.cpp b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Protocol.cpp new file mode 100644 index 000000000..04960ab56 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Protocol.cpp @@ -0,0 +1,726 @@ +// This file is generated by Protocol_cpp.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/inspector/protocol/Protocol.h" + +#include +#include +#include +#include + + +// This file is generated by Values_cpp.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//#include "Values.h" + +#include "third_party/inspector_protocol/crdtp/cbor.h" + +namespace v8_inspector { +namespace protocol { + +namespace { +using v8_crdtp::Status; +using v8_crdtp::ParserHandler; +using v8_crdtp::span; +namespace cbor { +using v8_crdtp::cbor::ParseCBOR; +using v8_crdtp::cbor::EncodeBinary; +using v8_crdtp::cbor::EncodeDouble; +using v8_crdtp::cbor::EncodeFalse; +using v8_crdtp::cbor::EncodeFromLatin1; +using v8_crdtp::cbor::EncodeFromUTF16; +using v8_crdtp::cbor::EncodeIndefiniteLengthArrayStart; +using v8_crdtp::cbor::EncodeIndefiniteLengthMapStart; +using v8_crdtp::cbor::EncodeInt32; +using v8_crdtp::cbor::EncodeNull; +using v8_crdtp::cbor::EncodeStop; +using v8_crdtp::cbor::EncodeString8; +using v8_crdtp::cbor::EncodeTrue; +using v8_crdtp::cbor::EnvelopeEncoder; +} // namespace cbor + +// Uses the parsing events received from driver of |ParserHandler| +// (e.g. cbor::ParseCBOR) into a protocol::Value instance. +class ValueParserHandler : public ParserHandler { + public: + // Provides the parsed protocol::Value. + std::unique_ptr ReleaseRoot() { return std::move(root_); } + + // The first parsing error encountered; |status().ok()| is the default. + Status status() const { return status_; } + + private: + // + // Implementation of ParserHandler. + // + void HandleMapBegin() override { + if (!status_.ok()) return; + std::unique_ptr dict = DictionaryValue::create(); + DictionaryValue* dict_ptr = dict.get(); + AddValueToParent(std::move(dict)); + stack_.emplace_back(dict_ptr); + } + + void HandleMapEnd() override { + if (!status_.ok()) return; + DCHECK(!stack_.empty()); + DCHECK(stack_.back().is_dict); + stack_.pop_back(); + } + + void HandleArrayBegin() override { + if (!status_.ok()) return; + std::unique_ptr list = ListValue::create(); + ListValue* list_ptr = list.get(); + AddValueToParent(std::move(list)); + stack_.emplace_back(list_ptr); + } + + void HandleArrayEnd() override { + if (!status_.ok()) return; + DCHECK(!stack_.empty()); + DCHECK(!stack_.back().is_dict); + stack_.pop_back(); + } + + void HandleString8(span chars) override { + AddStringToParent(StringUtil::fromUTF8(chars.data(), chars.size())); + } + + void HandleString16(span chars) override { + AddStringToParent( + StringUtil::fromUTF16LE(chars.data(), chars.size())); + } + + void HandleBinary(span bytes) override { + AddValueToParent( + BinaryValue::create(Binary::fromSpan(bytes.data(), bytes.size()))); + } + + void HandleDouble(double value) override { + AddValueToParent(FundamentalValue::create(value)); + } + + void HandleInt32(int32_t value) override { + AddValueToParent(FundamentalValue::create(value)); + } + + void HandleBool(bool value) override { + AddValueToParent(FundamentalValue::create(value)); + } + + void HandleNull() override { + AddValueToParent(Value::null()); + } + + void HandleError(Status error) override { + status_ = error; + } + + // + // Adding strings and values to the parent value. + // Strings are handled separately because they can be keys for + // dictionary values. + // + void AddStringToParent(String str) { + if (!status_.ok()) return; + if (!root_) { + DCHECK(!key_is_pending_); + root_ = StringValue::create(str); + } else if (stack_.back().is_dict) { + // If we already have a pending key, then this is the value of the + // key/value pair. Otherwise, it's the new pending key. + if (key_is_pending_) { + stack_.back().dict->setString(pending_key_, str); + key_is_pending_ = false; + } else { + pending_key_ = std::move(str); + key_is_pending_ = true; + } + } else { // Top of the stack is a list. + DCHECK(!key_is_pending_); + stack_.back().list->pushValue(StringValue::create(str)); + } + } + + void AddValueToParent(std::unique_ptr value) { + if (!status_.ok()) return; + if (!root_) { + DCHECK(!key_is_pending_); + root_ = std::move(value); + } else if (stack_.back().is_dict) { + DCHECK(key_is_pending_); + stack_.back().dict->setValue(pending_key_, std::move(value)); + key_is_pending_ = false; + } else { // Top of the stack is a list. + DCHECK(!key_is_pending_); + stack_.back().list->pushValue(std::move(value)); + } + } + + // |status_.ok()| is the default; if we receive an error event + // we keep the first one and stop modifying any other state. + Status status_; + + // The root of the parsed protocol::Value tree. + std::unique_ptr root_; + + // If root_ is a list or a dictionary, this stack keeps track of + // the container we're currently parsing as well as its ancestors. + struct ContainerState { + ContainerState(DictionaryValue* dict) : is_dict(true), dict(dict) {} + ContainerState(ListValue* list) : is_dict(false), list(list) {} + + bool is_dict; + union { + DictionaryValue* dict; + ListValue* list; + }; + }; + std::vector stack_; + + // For maps, keys and values are alternating events, so we keep the + // key around and process it when the value arrives. + bool key_is_pending_ = false; + String pending_key_; +}; +} // anonymous namespace + +// static +std::unique_ptr Value::parseBinary(const uint8_t* data, size_t size) { + ValueParserHandler handler; + cbor::ParseCBOR(span(data, size), &handler); + // TODO(johannes): We have decent error info in handler.status(); provide + // a richer interface that makes this available to client code. + if (handler.status().ok()) + return handler.ReleaseRoot(); + return nullptr; +} + +bool Value::asBoolean(bool*) const +{ + return false; +} + +bool Value::asDouble(double*) const +{ + return false; +} + +bool Value::asInteger(int*) const +{ + return false; +} + +bool Value::asString(String*) const +{ + return false; +} + +bool Value::asBinary(Binary*) const +{ + return false; +} + +void Value::AppendSerialized(std::vector* bytes) const { + DCHECK(m_type == TypeNull); + bytes->push_back(cbor::EncodeNull()); +} + +std::unique_ptr Value::clone() const +{ + return Value::null(); +} + +bool FundamentalValue::asBoolean(bool* output) const +{ + if (type() != TypeBoolean) + return false; + *output = m_boolValue; + return true; +} + +bool FundamentalValue::asDouble(double* output) const +{ + if (type() == TypeDouble) { + *output = m_doubleValue; + return true; + } + if (type() == TypeInteger) { + *output = m_integerValue; + return true; + } + return false; +} + +bool FundamentalValue::asInteger(int* output) const +{ + if (type() != TypeInteger) + return false; + *output = m_integerValue; + return true; +} + +void FundamentalValue::AppendSerialized(std::vector* bytes) const { + switch (type()) { + case TypeDouble: + cbor::EncodeDouble(m_doubleValue, bytes); + return; + case TypeInteger: + cbor::EncodeInt32(m_integerValue, bytes); + return; + case TypeBoolean: + bytes->push_back(m_boolValue ? cbor::EncodeTrue() : cbor::EncodeFalse()); + return; + default: + DCHECK(false); + } +} + +std::unique_ptr FundamentalValue::clone() const +{ + switch (type()) { + case TypeDouble: return FundamentalValue::create(m_doubleValue); + case TypeInteger: return FundamentalValue::create(m_integerValue); + case TypeBoolean: return FundamentalValue::create(m_boolValue); + default: + DCHECK(false); + } + return nullptr; +} + +bool StringValue::asString(String* output) const +{ + *output = m_stringValue; + return true; +} + +namespace { +// This routine distinguishes between the current encoding for a given +// string |s|, and calls encoding routines that will +// - Ensure that all ASCII strings end up being encoded as UTF8 in +// the wire format - e.g., EncodeFromUTF16 will detect ASCII and +// do the (trivial) transcode to STRING8 on the wire, but if it's +// not ASCII it'll do STRING16. +// - Select a format that's cheap to convert to. E.g., we don't +// have LATIN1 on the wire, so we call EncodeFromLatin1 which +// transcodes to UTF8 if needed. +void EncodeString(const String& s, std::vector* out) { + if (StringUtil::CharacterCount(s) == 0) { + cbor::EncodeString8(span(nullptr, 0), out); // Empty string. + } else if (StringUtil::CharactersLatin1(s)) { + cbor::EncodeFromLatin1(span(StringUtil::CharactersLatin1(s), + StringUtil::CharacterCount(s)), + out); + } else if (StringUtil::CharactersUTF16(s)) { + cbor::EncodeFromUTF16(span(StringUtil::CharactersUTF16(s), + StringUtil::CharacterCount(s)), + out); + } else if (StringUtil::CharactersUTF8(s)) { + cbor::EncodeString8(span(StringUtil::CharactersUTF8(s), + StringUtil::CharacterCount(s)), + out); + } +} +} // namespace +void StringValue::AppendSerialized(std::vector* bytes) const { + EncodeString(m_stringValue, bytes); +} + +std::unique_ptr StringValue::clone() const +{ + return StringValue::create(m_stringValue); +} + +bool BinaryValue::asBinary(Binary* output) const +{ + *output = m_binaryValue; + return true; +} + +void BinaryValue::AppendSerialized(std::vector* bytes) const { + cbor::EncodeBinary(span(m_binaryValue.data(), + m_binaryValue.size()), bytes); +} + +std::unique_ptr BinaryValue::clone() const +{ + return BinaryValue::create(m_binaryValue); +} + + +DictionaryValue::~DictionaryValue() +{ +} + +void DictionaryValue::setBoolean(const String& name, bool value) +{ + setValue(name, FundamentalValue::create(value)); +} + +void DictionaryValue::setInteger(const String& name, int value) +{ + setValue(name, FundamentalValue::create(value)); +} + +void DictionaryValue::setDouble(const String& name, double value) +{ + setValue(name, FundamentalValue::create(value)); +} + +void DictionaryValue::setString(const String& name, const String& value) +{ + setValue(name, StringValue::create(value)); +} + +void DictionaryValue::setValue(const String& name, std::unique_ptr value) +{ + set(name, value); +} + +void DictionaryValue::setObject(const String& name, std::unique_ptr value) +{ + set(name, value); +} + +void DictionaryValue::setArray(const String& name, std::unique_ptr value) +{ + set(name, value); +} + +bool DictionaryValue::getBoolean(const String& name, bool* output) const +{ + protocol::Value* value = get(name); + if (!value) + return false; + return value->asBoolean(output); +} + +bool DictionaryValue::getInteger(const String& name, int* output) const +{ + Value* value = get(name); + if (!value) + return false; + return value->asInteger(output); +} + +bool DictionaryValue::getDouble(const String& name, double* output) const +{ + Value* value = get(name); + if (!value) + return false; + return value->asDouble(output); +} + +bool DictionaryValue::getString(const String& name, String* output) const +{ + protocol::Value* value = get(name); + if (!value) + return false; + return value->asString(output); +} + +DictionaryValue* DictionaryValue::getObject(const String& name) const +{ + return DictionaryValue::cast(get(name)); +} + +protocol::ListValue* DictionaryValue::getArray(const String& name) const +{ + return ListValue::cast(get(name)); +} + +protocol::Value* DictionaryValue::get(const String& name) const +{ + Dictionary::const_iterator it = m_data.find(name); + if (it == m_data.end()) + return nullptr; + return it->second.get(); +} + +DictionaryValue::Entry DictionaryValue::at(size_t index) const +{ + const String key = m_order[index]; + return std::make_pair(key, m_data.find(key)->second.get()); +} + +bool DictionaryValue::booleanProperty(const String& name, bool defaultValue) const +{ + bool result = defaultValue; + getBoolean(name, &result); + return result; +} + +int DictionaryValue::integerProperty(const String& name, int defaultValue) const +{ + int result = defaultValue; + getInteger(name, &result); + return result; +} + +double DictionaryValue::doubleProperty(const String& name, double defaultValue) const +{ + double result = defaultValue; + getDouble(name, &result); + return result; +} + +void DictionaryValue::remove(const String& name) +{ + m_data.erase(name); + m_order.erase(std::remove(m_order.begin(), m_order.end(), name), m_order.end()); +} + +void DictionaryValue::AppendSerialized(std::vector* bytes) const { + cbor::EnvelopeEncoder encoder; + encoder.EncodeStart(bytes); + bytes->push_back(cbor::EncodeIndefiniteLengthMapStart()); + for (size_t i = 0; i < m_order.size(); ++i) { + const String& key = m_order[i]; + Dictionary::const_iterator value = m_data.find(key); + DCHECK(value != m_data.cend() && value->second); + EncodeString(key, bytes); + value->second->AppendSerialized(bytes); + } + bytes->push_back(cbor::EncodeStop()); + encoder.EncodeStop(bytes); +} + +std::unique_ptr DictionaryValue::clone() const +{ + std::unique_ptr result = DictionaryValue::create(); + for (size_t i = 0; i < m_order.size(); ++i) { + String key = m_order[i]; + Dictionary::const_iterator value = m_data.find(key); + DCHECK(value != m_data.cend() && value->second); + result->setValue(key, value->second->clone()); + } + return result; +} + +DictionaryValue::DictionaryValue() + : Value(TypeObject) +{ +} + +ListValue::~ListValue() +{ +} + +void ListValue::AppendSerialized(std::vector* bytes) const { + cbor::EnvelopeEncoder encoder; + encoder.EncodeStart(bytes); + bytes->push_back(cbor::EncodeIndefiniteLengthArrayStart()); + for (size_t i = 0; i < m_data.size(); ++i) { + m_data[i]->AppendSerialized(bytes); + } + bytes->push_back(cbor::EncodeStop()); + encoder.EncodeStop(bytes); +} + +std::unique_ptr ListValue::clone() const +{ + std::unique_ptr result = ListValue::create(); + for (const std::unique_ptr& value : m_data) + result->pushValue(value->clone()); + return result; +} + +ListValue::ListValue() + : Value(TypeArray) +{ +} + +void ListValue::pushValue(std::unique_ptr value) +{ + DCHECK(value); + m_data.push_back(std::move(value)); +} + +protocol::Value* ListValue::at(size_t index) +{ + DCHECK_LT(index, m_data.size()); + return m_data[index].get(); +} + +} // namespace v8_inspector +} // namespace protocol + + +// This file is generated by Object_cpp.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//#include "Object.h" + +namespace v8_inspector { +namespace protocol { + +std::unique_ptr Object::fromValue(protocol::Value* value, ErrorSupport* errors) +{ + protocol::DictionaryValue* dictionary = DictionaryValue::cast(value); + if (!dictionary) { + errors->AddError("object expected"); + return nullptr; + } + dictionary = static_cast(dictionary->clone().release()); + return std::unique_ptr(new Object(std::unique_ptr(dictionary))); +} + +// Implements Serializable. +void Object::AppendSerialized(std::vector* out) const { + m_object->AppendSerialized(out); +} + +std::unique_ptr Object::toValue() const +{ + return DictionaryValue::cast(m_object->clone()); +} + +std::unique_ptr Object::clone() const +{ + return std::unique_ptr(new Object(DictionaryValue::cast(m_object->clone()))); +} + +Object::Object(std::unique_ptr object) : m_object(std::move(object)) { } + +Object::~Object() { } + +} // namespace v8_inspector +} // namespace protocol + + +// This file is generated by ValueConversions_cpp.template. + +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/inspector/protocol/Protocol.h" + +#include +#include +#include + +//#include "ValueConversions.h" +//#include "Values.h" + +namespace v8_inspector { +namespace protocol { + +} // namespace +} // namespace + + +namespace v8_crdtp { + +namespace { + +using v8_inspector::protocol::Binary; +using v8_inspector::protocol::Object; +using v8_inspector::protocol::Value; +using v8_inspector::protocol::String; +using v8_inspector::protocol::DictionaryValue; +using v8_inspector::protocol::FundamentalValue; +using v8_inspector::protocol::StringValue; +using v8_inspector::protocol::StringUtil; +//using v8_inspector::protocol::EncodeString; + +std::unique_ptr ReadValue(DeserializerState* state) { + cbor::CBORTokenizer* tokenizer = state->tokenizer(); + switch (tokenizer->TokenTag()) { + case cbor::CBORTokenTag::TRUE_VALUE: + return FundamentalValue::create(true); + case cbor::CBORTokenTag::FALSE_VALUE: + return FundamentalValue::create(false); + case cbor::CBORTokenTag::NULL_VALUE: + return Value::null(); + case cbor::CBORTokenTag::INT32: + return FundamentalValue::create(tokenizer->GetInt32()); + case cbor::CBORTokenTag::DOUBLE: + return FundamentalValue::create(tokenizer->GetDouble()); + case cbor::CBORTokenTag::STRING8: { + const auto str = tokenizer->GetString8(); + return StringValue::create(StringUtil::fromUTF8(str.data(), str.size())); + } + case cbor::CBORTokenTag::STRING16: { + const auto str = tokenizer->GetString16WireRep(); + return StringValue::create(StringUtil::fromUTF16LE(reinterpret_cast(str.data()), str.size() / 2)); + } + case cbor::CBORTokenTag::ENVELOPE: { + const auto env = tokenizer->GetEnvelope(); + return Value::parseBinary(env.data(), env.size()); + } + // Intentionally not supported. + case cbor::CBORTokenTag::BINARY: + // Should not be encountered outside of envelope. + case cbor::CBORTokenTag::MAP_START: + case cbor::CBORTokenTag::ARRAY_START: + default: + state->RegisterError(Error::CBOR_UNSUPPORTED_VALUE); + return nullptr; + } +} + +} // namespace + +// static +bool ProtocolTypeTraits>::Deserialize( + DeserializerState* state, std::unique_ptr* value) { + auto result = ReadValue(state); + if (!result) + return false; + *value = std::move(result); + return true; +} + +// static +void ProtocolTypeTraits>::Serialize( + const std::unique_ptr& value, std::vector* bytes) { + value->AppendSerialized(bytes); +} + +// static +bool ProtocolTypeTraits>::Deserialize( + DeserializerState* state, std::unique_ptr* value) { + std::unique_ptr res; + if (!ProtocolTypeTraits>::Deserialize(state, &res)) + return false; + if (res->type() != Value::TypeObject) { + state->RegisterError(Error::BINDINGS_DICTIONARY_VALUE_EXPECTED); + return false; + } + *value = DictionaryValue::cast(std::move(res)); + return true; +} + +// static +void ProtocolTypeTraits>::Serialize( + const std::unique_ptr& value, std::vector* bytes) { + value->AppendSerialized(bytes); +} + +// static +bool ProtocolTypeTraits>::Deserialize(DeserializerState* state, std::unique_ptr* value) { + auto res = DictionaryValue::create(); + if (ProtocolTypeTraits>::Deserialize(state, &res)) { + *value = std::make_unique(std::move(res)); + return true; + } + return false; +} + +void ProtocolTypeTraits>::Serialize(const std::unique_ptr& value, std::vector* bytes) { + value->AppendSerialized(bytes); +} + +} // namespace v8_crdtp diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Protocol.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Protocol.h new file mode 100644 index 000000000..e4e3784ae --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Protocol.h @@ -0,0 +1,626 @@ +// This file is generated by Values_h.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef v8_inspector_protocol_Values_h +#define v8_inspector_protocol_Values_h + +//#include "Allocator.h" +//#include "Forward.h" + +#include +#include +#include +#include + +#include "src/inspector/protocol/Forward.h" + +namespace v8_inspector { +namespace protocol { + +class ListValue; +class DictionaryValue; +class Value; + +#define PROTOCOL_DISALLOW_COPY(ClassName) \ + private: \ + ClassName(const ClassName&) = delete; \ + ClassName& operator=(const ClassName&) = delete + +class Value : public Serializable { + PROTOCOL_DISALLOW_COPY(Value); +public: + virtual ~Value() override { } + + static std::unique_ptr null() + { + return std::unique_ptr(new Value()); + } + + static std::unique_ptr parseBinary(const uint8_t* data, size_t size); + + enum ValueType { + TypeNull = 0, + TypeBoolean, + TypeInteger, + TypeDouble, + TypeString, + TypeBinary, + TypeObject, + TypeArray, + TypeImported + }; + + ValueType type() const { return m_type; } + + bool isNull() const { return m_type == TypeNull; } + + virtual bool asBoolean(bool* output) const; + virtual bool asDouble(double* output) const; + virtual bool asInteger(int* output) const; + virtual bool asString(String* output) const; + virtual bool asBinary(Binary* output) const; + + virtual void AppendSerialized(std::vector* bytes) const override; + virtual std::unique_ptr clone() const; + +protected: + Value() : m_type(TypeNull) { } + explicit Value(ValueType type) : m_type(type) { } + +private: + friend class DictionaryValue; + friend class ListValue; + + ValueType m_type; +}; + +class FundamentalValue : public Value { +public: + static std::unique_ptr create(bool value) + { + return std::unique_ptr(new FundamentalValue(value)); + } + + static std::unique_ptr create(int value) + { + return std::unique_ptr(new FundamentalValue(value)); + } + + static std::unique_ptr create(double value) + { + return std::unique_ptr(new FundamentalValue(value)); + } + + bool asBoolean(bool* output) const override; + bool asDouble(double* output) const override; + bool asInteger(int* output) const override; + void AppendSerialized(std::vector* bytes) const override; + std::unique_ptr clone() const override; + +private: + explicit FundamentalValue(bool value) : Value(TypeBoolean), m_boolValue(value) { } + explicit FundamentalValue(int value) : Value(TypeInteger), m_integerValue(value) { } + explicit FundamentalValue(double value) : Value(TypeDouble), m_doubleValue(value) { } + + union { + bool m_boolValue; + double m_doubleValue; + int m_integerValue; + }; +}; + +class StringValue : public Value { +public: + static std::unique_ptr create(const String& value) + { + return std::unique_ptr(new StringValue(value)); + } + + static std::unique_ptr create(const char* value) + { + return std::unique_ptr(new StringValue(value)); + } + + bool asString(String* output) const override; + void AppendSerialized(std::vector* bytes) const override; + std::unique_ptr clone() const override; + +private: + explicit StringValue(const String& value) : Value(TypeString), m_stringValue(value) { } + explicit StringValue(const char* value) : Value(TypeString), m_stringValue(value) { } + + String m_stringValue; +}; + +class BinaryValue : public Value { +public: + static std::unique_ptr create(const Binary& value) + { + return std::unique_ptr(new BinaryValue(value)); + } + + bool asBinary(Binary* output) const override; + void AppendSerialized(std::vector* bytes) const override; + std::unique_ptr clone() const override; + +private: + explicit BinaryValue(const Binary& value) : Value(TypeBinary), m_binaryValue(value) { } + + Binary m_binaryValue; +}; + +class DictionaryValue : public Value { +public: + using Entry = std::pair; + static std::unique_ptr create() + { + return std::unique_ptr(new DictionaryValue()); + } + + static DictionaryValue* cast(Value* value) + { + if (!value || value->type() != TypeObject) + return nullptr; + return static_cast(value); + } + + static std::unique_ptr cast(std::unique_ptr value) + { + DictionaryValue* dictionaryValue = cast(value.get()); + if (dictionaryValue) value.release(); + return std::unique_ptr(dictionaryValue); + } + + void AppendSerialized(std::vector* bytes) const override; + std::unique_ptr clone() const override; + + size_t size() const { return m_data.size(); } + + void setBoolean(const String& name, bool); + void setInteger(const String& name, int); + void setDouble(const String& name, double); + void setString(const String& name, const String&); + void setValue(const String& name, std::unique_ptr); + void setObject(const String& name, std::unique_ptr); + void setArray(const String& name, std::unique_ptr); + + bool getBoolean(const String& name, bool* output) const; + bool getInteger(const String& name, int* output) const; + bool getDouble(const String& name, double* output) const; + bool getString(const String& name, String* output) const; + + DictionaryValue* getObject(const String& name) const; + ListValue* getArray(const String& name) const; + Value* get(const String& name) const; + Entry at(size_t index) const; + + bool booleanProperty(const String& name, bool defaultValue) const; + int integerProperty(const String& name, int defaultValue) const; + double doubleProperty(const String& name, double defaultValue) const; + void remove(const String& name); + + ~DictionaryValue() override; + +private: + DictionaryValue(); + template + void set(const String& key, std::unique_ptr& value) + { + DCHECK(value); + bool isNew = m_data.find(key) == m_data.end(); + m_data[key] = std::move(value); + if (isNew) + m_order.push_back(key); + } + + using Dictionary = std::unordered_map>; + Dictionary m_data; + std::vector m_order; +}; + +class ListValue : public Value { +public: + static std::unique_ptr create() + { + return std::unique_ptr(new ListValue()); + } + + static ListValue* cast(Value* value) + { + if (!value || value->type() != TypeArray) + return nullptr; + return static_cast(value); + } + + static std::unique_ptr cast(std::unique_ptr value) + { + ListValue* listValue = cast(value.get()); + if (listValue) value.release(); + return std::unique_ptr(listValue); + } + + ~ListValue() override; + + void AppendSerialized(std::vector* bytes) const override; + std::unique_ptr clone() const override; + + void pushValue(std::unique_ptr); + + Value* at(size_t index); + size_t size() const { return m_data.size(); } + void reserve(size_t capacity) { m_data.reserve(capacity); } + +private: + ListValue(); + std::vector> m_data; +}; + +} // namespace v8_inspector +} // namespace protocol + +#endif // v8_inspector_protocol_Values_h + + +// This file is generated by Object_h.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef v8_inspector_protocol_Object_h +#define v8_inspector_protocol_Object_h + +//#include "ErrorSupport.h" +//#include "Forward.h" +//#include "Values.h" + +#include "third_party/inspector_protocol/crdtp/serializable.h" + +namespace v8_inspector { +namespace protocol { + +class Object : public v8_crdtp::Serializable { +public: + static std::unique_ptr fromValue(protocol::Value*, ErrorSupport*); + explicit Object(std::unique_ptr); + ~Object(); + + // Implements Serializable. + void AppendSerialized(std::vector* out) const override; + + std::unique_ptr toValue() const; + std::unique_ptr clone() const; + +private: + Object() = default; + friend struct v8_crdtp::ProtocolTypeTraits, void>; + + std::unique_ptr m_object; +}; + +} // namespace v8_inspector +} // namespace protocol + +#endif // !defined(v8_inspector_protocol_Object_h) + + +// This file is generated by ValueConversions_h.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef v8_inspector_protocol_ValueConversions_h +#define v8_inspector_protocol_ValueConversions_h + +//#include "ErrorSupport.h" +//#include "Forward.h" +//#include "Values.h" + +namespace v8_inspector { +namespace protocol { + +template +struct ValueConversions { + static std::unique_ptr fromValue(protocol::Value* value, ErrorSupport* errors) + { + return T::fromValue(value, errors); + } + + static std::unique_ptr toValue(T* value) + { + return value->toValue(); + } + + static std::unique_ptr toValue(const std::unique_ptr& value) + { + return value->toValue(); + } +}; + +template<> +struct ValueConversions { + static bool fromValue(protocol::Value* value, ErrorSupport* errors) + { + bool result = false; + bool success = value ? value->asBoolean(&result) : false; + if (!success) + errors->AddError("boolean value expected"); + return result; + } + + static std::unique_ptr toValue(bool value) + { + return FundamentalValue::create(value); + } +}; + +template<> +struct ValueConversions { + static int fromValue(protocol::Value* value, ErrorSupport* errors) + { + int result = 0; + bool success = value ? value->asInteger(&result) : false; + if (!success) + errors->AddError("integer value expected"); + return result; + } + + static std::unique_ptr toValue(int value) + { + return FundamentalValue::create(value); + } +}; + +template<> +struct ValueConversions { + static double fromValue(protocol::Value* value, ErrorSupport* errors) + { + double result = 0; + bool success = value ? value->asDouble(&result) : false; + if (!success) + errors->AddError("double value expected"); + return result; + } + + static std::unique_ptr toValue(double value) + { + return FundamentalValue::create(value); + } +}; + +template<> +struct ValueConversions { + static String fromValue(protocol::Value* value, ErrorSupport* errors) + { + String result; + bool success = value ? value->asString(&result) : false; + if (!success) + errors->AddError("string value expected"); + return result; + } + + static std::unique_ptr toValue(const String& value) + { + return StringValue::create(value); + } +}; + +template<> +struct ValueConversions { + static Binary fromValue(protocol::Value* value, ErrorSupport* errors) + { + if (!value || + (value->type() != Value::TypeBinary && value->type() != Value::TypeString)) { + errors->AddError("Either string base64 or binary value expected"); + return Binary(); + } + Binary binary; + if (value->asBinary(&binary)) + return binary; + String result; + value->asString(&result); + bool success; + Binary out = Binary::fromBase64(result, &success); + if (!success) + errors->AddError("base64 decoding error"); + return out; + } + + static std::unique_ptr toValue(const Binary& value) + { + return BinaryValue::create(value); + } +}; + +template +struct ValueConversions>> { + static std::unique_ptr>> fromValue(protocol::Value* value, ErrorSupport* errors) { + protocol::ListValue* array = ListValue::cast(value); + if (!array) { + errors->AddError("array expected"); + return nullptr; + } + errors->Push(); + std::unique_ptr>> result( + new std::vector>()); + result->reserve(array->size()); + for (size_t i = 0; i < array->size(); ++i) { + errors->SetIndex(i); + auto item = ValueConversions::fromValue(array->at(i), errors); + result->emplace_back(std::move(item)); + } + errors->Pop(); + if (!errors->Errors().empty()) + return nullptr; + return result; + } + + static std::unique_ptr toValue(std::vector>* v) + { + std::unique_ptr result = ListValue::create(); + result->reserve(v->size()); + for (auto& item : *v) + result->pushValue(ValueConversions::toValue(item.get())); + return result; + } + +}; + +template +struct ValueConversions> { + static std::unique_ptr> fromValue(protocol::Value* value, ErrorSupport* errors) { + protocol::ListValue* array = ListValue::cast(value); + if (!array) { + errors->AddError("array expected"); + return nullptr; + } + errors->Push(); + std::unique_ptr> result(new std::vector()); + result->reserve(array->size()); + for (size_t i = 0; i < array->size(); ++i) { + errors->SetIndex(i); + auto item = ValueConversions::fromValue(array->at(i), errors); + result->emplace_back(std::move(item)); + } + errors->Pop(); + if (!errors->Errors().empty()) + return nullptr; + return result; + } + + static std::unique_ptr toValue(std::vector* v) + { + std::unique_ptr result = ListValue::create(); + result->reserve(v->size()); + for (auto& item : *v) + result->pushValue(ValueConversions::toValue(item)); + return result; + } +}; + +template<> +struct ValueConversions { + static std::unique_ptr fromValue(protocol::Value* value, ErrorSupport* errors) + { + bool success = !!value; + if (!success) { + errors->AddError("value expected"); + return nullptr; + } + return value->clone(); + } + + static std::unique_ptr toValue(Value* value) + { + return value->clone(); + } + + static std::unique_ptr toValue(const std::unique_ptr& value) + { + return value->clone(); + } +}; + +template<> +struct ValueConversions { + static std::unique_ptr fromValue(protocol::Value* value, ErrorSupport* errors) + { + bool success = value && value->type() == protocol::Value::TypeObject; + if (!success) + errors->AddError("object expected"); + return DictionaryValue::cast(value->clone()); + } + + static std::unique_ptr toValue(DictionaryValue* value) + { + return value->clone(); + } + + static std::unique_ptr toValue(const std::unique_ptr& value) + { + return value->clone(); + } +}; + +template<> +struct ValueConversions { + static std::unique_ptr fromValue(protocol::Value* value, ErrorSupport* errors) + { + bool success = value && value->type() == protocol::Value::TypeArray; + if (!success) + errors->AddError("list expected"); + return ListValue::cast(value->clone()); + } + + static std::unique_ptr toValue(ListValue* value) + { + return value->clone(); + } + + static std::unique_ptr toValue(const std::unique_ptr& value) + { + return value->clone(); + } +}; + +template struct ValueTypeConverter { + static std::unique_ptr FromValue(const protocol::Value& value) { + std::vector bytes; + value.AppendSerialized(&bytes); + return T::FromBinary(bytes.data(), bytes.size()); + } + + static std::unique_ptr ToValue(const T& obj) { + std::vector bytes; + obj.AppendSerialized(&bytes); + auto result = Value::parseBinary(bytes.data(), bytes.size()); + return DictionaryValue::cast(std::move(result)); + } +}; + +} // namespace v8_inspector +} // namespace protocol + +namespace v8_crdtp { + +template +struct ProtocolTypeTraits::value>::type> { + static void Serialize(const v8_inspector::protocol::Value& value, std::vector* bytes) { + value.AppendSerialized(bytes); + } +}; + +template <> +struct ProtocolTypeTraits> { + static bool Deserialize(DeserializerState* state, std::unique_ptr* value); + static void Serialize(const std::unique_ptr& value, std::vector* bytes); +}; + +template <> +struct ProtocolTypeTraits> { + static bool Deserialize(DeserializerState* state, std::unique_ptr* value); + static void Serialize(const std::unique_ptr& value, std::vector* bytes); +}; + +// TODO(caseq): get rid of it, it's just a DictionaryValue really. +template <> +struct ProtocolTypeTraits> { + static bool Deserialize(DeserializerState* state, std::unique_ptr* value); + static void Serialize(const std::unique_ptr& value, std::vector* bytes); +}; + +template<> +struct ProtocolTypeTraits { + static void Serialize(const v8_inspector::protocol::Object& value, std::vector* bytes) { + value.AppendSerialized(bytes); + } +}; + +} // namespace v8_crdtp + +#endif // !defined(v8_inspector_protocol_ValueConversions_h) diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Runtime.cpp b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Runtime.cpp new file mode 100644 index 000000000..811cfaecd --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Runtime.cpp @@ -0,0 +1,1625 @@ +// This file is generated by TypeBuilder_cpp.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/inspector/protocol/Runtime.h" + +#include "src/inspector/protocol/Protocol.h" + +#include "third_party/inspector_protocol/crdtp/cbor.h" +#include "third_party/inspector_protocol/crdtp/find_by_first.h" +#include "third_party/inspector_protocol/crdtp/span.h" + +namespace v8_inspector { +namespace protocol { +namespace Runtime { + +using v8_crdtp::DeserializerState; +using v8_crdtp::ProtocolTypeTraits; + +// ------------- Enum values from types. + +const char Metainfo::domainName[] = "Runtime"; +const char Metainfo::commandPrefix[] = "Runtime."; +const char Metainfo::version[] = "1.3"; + + + +const char* WebDriverValue::TypeEnum::Undefined = "undefined"; +const char* WebDriverValue::TypeEnum::Null = "null"; +const char* WebDriverValue::TypeEnum::String = "string"; +const char* WebDriverValue::TypeEnum::Number = "number"; +const char* WebDriverValue::TypeEnum::Boolean = "boolean"; +const char* WebDriverValue::TypeEnum::Bigint = "bigint"; +const char* WebDriverValue::TypeEnum::Regexp = "regexp"; +const char* WebDriverValue::TypeEnum::Date = "date"; +const char* WebDriverValue::TypeEnum::Symbol = "symbol"; +const char* WebDriverValue::TypeEnum::Array = "array"; +const char* WebDriverValue::TypeEnum::Object = "object"; +const char* WebDriverValue::TypeEnum::Function = "function"; +const char* WebDriverValue::TypeEnum::Map = "map"; +const char* WebDriverValue::TypeEnum::Set = "set"; +const char* WebDriverValue::TypeEnum::Weakmap = "weakmap"; +const char* WebDriverValue::TypeEnum::Weakset = "weakset"; +const char* WebDriverValue::TypeEnum::Error = "error"; +const char* WebDriverValue::TypeEnum::Proxy = "proxy"; +const char* WebDriverValue::TypeEnum::Promise = "promise"; +const char* WebDriverValue::TypeEnum::Typedarray = "typedarray"; +const char* WebDriverValue::TypeEnum::Arraybuffer = "arraybuffer"; +const char* WebDriverValue::TypeEnum::Node = "node"; +const char* WebDriverValue::TypeEnum::Window = "window"; +V8_CRDTP_BEGIN_DESERIALIZER(WebDriverValue) + V8_CRDTP_DESERIALIZE_FIELD_OPT("objectId", m_objectId), + V8_CRDTP_DESERIALIZE_FIELD("type", m_type), + V8_CRDTP_DESERIALIZE_FIELD_OPT("value", m_value), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(WebDriverValue) + V8_CRDTP_SERIALIZE_FIELD("type", m_type); + V8_CRDTP_SERIALIZE_FIELD("value", m_value); + V8_CRDTP_SERIALIZE_FIELD("objectId", m_objectId); +V8_CRDTP_END_SERIALIZER(); + + + + + +const char* RemoteObject::TypeEnum::Object = "object"; +const char* RemoteObject::TypeEnum::Function = "function"; +const char* RemoteObject::TypeEnum::Undefined = "undefined"; +const char* RemoteObject::TypeEnum::String = "string"; +const char* RemoteObject::TypeEnum::Number = "number"; +const char* RemoteObject::TypeEnum::Boolean = "boolean"; +const char* RemoteObject::TypeEnum::Symbol = "symbol"; +const char* RemoteObject::TypeEnum::Bigint = "bigint"; + +const char* RemoteObject::SubtypeEnum::Array = "array"; +const char* RemoteObject::SubtypeEnum::Null = "null"; +const char* RemoteObject::SubtypeEnum::Node = "node"; +const char* RemoteObject::SubtypeEnum::Regexp = "regexp"; +const char* RemoteObject::SubtypeEnum::Date = "date"; +const char* RemoteObject::SubtypeEnum::Map = "map"; +const char* RemoteObject::SubtypeEnum::Set = "set"; +const char* RemoteObject::SubtypeEnum::Weakmap = "weakmap"; +const char* RemoteObject::SubtypeEnum::Weakset = "weakset"; +const char* RemoteObject::SubtypeEnum::Iterator = "iterator"; +const char* RemoteObject::SubtypeEnum::Generator = "generator"; +const char* RemoteObject::SubtypeEnum::Error = "error"; +const char* RemoteObject::SubtypeEnum::Proxy = "proxy"; +const char* RemoteObject::SubtypeEnum::Promise = "promise"; +const char* RemoteObject::SubtypeEnum::Typedarray = "typedarray"; +const char* RemoteObject::SubtypeEnum::Arraybuffer = "arraybuffer"; +const char* RemoteObject::SubtypeEnum::Dataview = "dataview"; +const char* RemoteObject::SubtypeEnum::Webassemblymemory = "webassemblymemory"; +const char* RemoteObject::SubtypeEnum::Wasmvalue = "wasmvalue"; +V8_CRDTP_BEGIN_DESERIALIZER(RemoteObject) + V8_CRDTP_DESERIALIZE_FIELD_OPT("className", m_className), + V8_CRDTP_DESERIALIZE_FIELD_OPT("customPreview", m_customPreview), + V8_CRDTP_DESERIALIZE_FIELD_OPT("description", m_description), + V8_CRDTP_DESERIALIZE_FIELD_OPT("objectId", m_objectId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("preview", m_preview), + V8_CRDTP_DESERIALIZE_FIELD_OPT("subtype", m_subtype), + V8_CRDTP_DESERIALIZE_FIELD("type", m_type), + V8_CRDTP_DESERIALIZE_FIELD_OPT("unserializableValue", m_unserializableValue), + V8_CRDTP_DESERIALIZE_FIELD_OPT("value", m_value), + V8_CRDTP_DESERIALIZE_FIELD_OPT("webDriverValue", m_webDriverValue), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(RemoteObject) + V8_CRDTP_SERIALIZE_FIELD("type", m_type); + V8_CRDTP_SERIALIZE_FIELD("subtype", m_subtype); + V8_CRDTP_SERIALIZE_FIELD("className", m_className); + V8_CRDTP_SERIALIZE_FIELD("value", m_value); + V8_CRDTP_SERIALIZE_FIELD("unserializableValue", m_unserializableValue); + V8_CRDTP_SERIALIZE_FIELD("description", m_description); + V8_CRDTP_SERIALIZE_FIELD("webDriverValue", m_webDriverValue); + V8_CRDTP_SERIALIZE_FIELD("objectId", m_objectId); + V8_CRDTP_SERIALIZE_FIELD("preview", m_preview); + V8_CRDTP_SERIALIZE_FIELD("customPreview", m_customPreview); +V8_CRDTP_END_SERIALIZER(); + +// static +std::unique_ptr API::RemoteObject::fromBinary(const uint8_t* data, size_t length) +{ + return protocol::Runtime::RemoteObject::FromBinary(data, length); +} + +V8_CRDTP_BEGIN_DESERIALIZER(CustomPreview) + V8_CRDTP_DESERIALIZE_FIELD_OPT("bodyGetterId", m_bodyGetterId), + V8_CRDTP_DESERIALIZE_FIELD("header", m_header), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(CustomPreview) + V8_CRDTP_SERIALIZE_FIELD("header", m_header); + V8_CRDTP_SERIALIZE_FIELD("bodyGetterId", m_bodyGetterId); +V8_CRDTP_END_SERIALIZER(); + + + +const char* ObjectPreview::TypeEnum::Object = "object"; +const char* ObjectPreview::TypeEnum::Function = "function"; +const char* ObjectPreview::TypeEnum::Undefined = "undefined"; +const char* ObjectPreview::TypeEnum::String = "string"; +const char* ObjectPreview::TypeEnum::Number = "number"; +const char* ObjectPreview::TypeEnum::Boolean = "boolean"; +const char* ObjectPreview::TypeEnum::Symbol = "symbol"; +const char* ObjectPreview::TypeEnum::Bigint = "bigint"; + +const char* ObjectPreview::SubtypeEnum::Array = "array"; +const char* ObjectPreview::SubtypeEnum::Null = "null"; +const char* ObjectPreview::SubtypeEnum::Node = "node"; +const char* ObjectPreview::SubtypeEnum::Regexp = "regexp"; +const char* ObjectPreview::SubtypeEnum::Date = "date"; +const char* ObjectPreview::SubtypeEnum::Map = "map"; +const char* ObjectPreview::SubtypeEnum::Set = "set"; +const char* ObjectPreview::SubtypeEnum::Weakmap = "weakmap"; +const char* ObjectPreview::SubtypeEnum::Weakset = "weakset"; +const char* ObjectPreview::SubtypeEnum::Iterator = "iterator"; +const char* ObjectPreview::SubtypeEnum::Generator = "generator"; +const char* ObjectPreview::SubtypeEnum::Error = "error"; +const char* ObjectPreview::SubtypeEnum::Proxy = "proxy"; +const char* ObjectPreview::SubtypeEnum::Promise = "promise"; +const char* ObjectPreview::SubtypeEnum::Typedarray = "typedarray"; +const char* ObjectPreview::SubtypeEnum::Arraybuffer = "arraybuffer"; +const char* ObjectPreview::SubtypeEnum::Dataview = "dataview"; +const char* ObjectPreview::SubtypeEnum::Webassemblymemory = "webassemblymemory"; +const char* ObjectPreview::SubtypeEnum::Wasmvalue = "wasmvalue"; +V8_CRDTP_BEGIN_DESERIALIZER(ObjectPreview) + V8_CRDTP_DESERIALIZE_FIELD_OPT("description", m_description), + V8_CRDTP_DESERIALIZE_FIELD_OPT("entries", m_entries), + V8_CRDTP_DESERIALIZE_FIELD("overflow", m_overflow), + V8_CRDTP_DESERIALIZE_FIELD("properties", m_properties), + V8_CRDTP_DESERIALIZE_FIELD_OPT("subtype", m_subtype), + V8_CRDTP_DESERIALIZE_FIELD("type", m_type), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(ObjectPreview) + V8_CRDTP_SERIALIZE_FIELD("type", m_type); + V8_CRDTP_SERIALIZE_FIELD("subtype", m_subtype); + V8_CRDTP_SERIALIZE_FIELD("description", m_description); + V8_CRDTP_SERIALIZE_FIELD("overflow", m_overflow); + V8_CRDTP_SERIALIZE_FIELD("properties", m_properties); + V8_CRDTP_SERIALIZE_FIELD("entries", m_entries); +V8_CRDTP_END_SERIALIZER(); + + + +const char* PropertyPreview::TypeEnum::Object = "object"; +const char* PropertyPreview::TypeEnum::Function = "function"; +const char* PropertyPreview::TypeEnum::Undefined = "undefined"; +const char* PropertyPreview::TypeEnum::String = "string"; +const char* PropertyPreview::TypeEnum::Number = "number"; +const char* PropertyPreview::TypeEnum::Boolean = "boolean"; +const char* PropertyPreview::TypeEnum::Symbol = "symbol"; +const char* PropertyPreview::TypeEnum::Accessor = "accessor"; +const char* PropertyPreview::TypeEnum::Bigint = "bigint"; + +const char* PropertyPreview::SubtypeEnum::Array = "array"; +const char* PropertyPreview::SubtypeEnum::Null = "null"; +const char* PropertyPreview::SubtypeEnum::Node = "node"; +const char* PropertyPreview::SubtypeEnum::Regexp = "regexp"; +const char* PropertyPreview::SubtypeEnum::Date = "date"; +const char* PropertyPreview::SubtypeEnum::Map = "map"; +const char* PropertyPreview::SubtypeEnum::Set = "set"; +const char* PropertyPreview::SubtypeEnum::Weakmap = "weakmap"; +const char* PropertyPreview::SubtypeEnum::Weakset = "weakset"; +const char* PropertyPreview::SubtypeEnum::Iterator = "iterator"; +const char* PropertyPreview::SubtypeEnum::Generator = "generator"; +const char* PropertyPreview::SubtypeEnum::Error = "error"; +const char* PropertyPreview::SubtypeEnum::Proxy = "proxy"; +const char* PropertyPreview::SubtypeEnum::Promise = "promise"; +const char* PropertyPreview::SubtypeEnum::Typedarray = "typedarray"; +const char* PropertyPreview::SubtypeEnum::Arraybuffer = "arraybuffer"; +const char* PropertyPreview::SubtypeEnum::Dataview = "dataview"; +const char* PropertyPreview::SubtypeEnum::Webassemblymemory = "webassemblymemory"; +const char* PropertyPreview::SubtypeEnum::Wasmvalue = "wasmvalue"; +V8_CRDTP_BEGIN_DESERIALIZER(PropertyPreview) + V8_CRDTP_DESERIALIZE_FIELD("name", m_name), + V8_CRDTP_DESERIALIZE_FIELD_OPT("subtype", m_subtype), + V8_CRDTP_DESERIALIZE_FIELD("type", m_type), + V8_CRDTP_DESERIALIZE_FIELD_OPT("value", m_value), + V8_CRDTP_DESERIALIZE_FIELD_OPT("valuePreview", m_valuePreview), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(PropertyPreview) + V8_CRDTP_SERIALIZE_FIELD("name", m_name); + V8_CRDTP_SERIALIZE_FIELD("type", m_type); + V8_CRDTP_SERIALIZE_FIELD("value", m_value); + V8_CRDTP_SERIALIZE_FIELD("valuePreview", m_valuePreview); + V8_CRDTP_SERIALIZE_FIELD("subtype", m_subtype); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(EntryPreview) + V8_CRDTP_DESERIALIZE_FIELD_OPT("key", m_key), + V8_CRDTP_DESERIALIZE_FIELD("value", m_value), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(EntryPreview) + V8_CRDTP_SERIALIZE_FIELD("key", m_key); + V8_CRDTP_SERIALIZE_FIELD("value", m_value); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(PropertyDescriptor) + V8_CRDTP_DESERIALIZE_FIELD("configurable", m_configurable), + V8_CRDTP_DESERIALIZE_FIELD("enumerable", m_enumerable), + V8_CRDTP_DESERIALIZE_FIELD_OPT("get", m_get), + V8_CRDTP_DESERIALIZE_FIELD_OPT("isOwn", m_isOwn), + V8_CRDTP_DESERIALIZE_FIELD("name", m_name), + V8_CRDTP_DESERIALIZE_FIELD_OPT("set", m_set), + V8_CRDTP_DESERIALIZE_FIELD_OPT("symbol", m_symbol), + V8_CRDTP_DESERIALIZE_FIELD_OPT("value", m_value), + V8_CRDTP_DESERIALIZE_FIELD_OPT("wasThrown", m_wasThrown), + V8_CRDTP_DESERIALIZE_FIELD_OPT("writable", m_writable), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(PropertyDescriptor) + V8_CRDTP_SERIALIZE_FIELD("name", m_name); + V8_CRDTP_SERIALIZE_FIELD("value", m_value); + V8_CRDTP_SERIALIZE_FIELD("writable", m_writable); + V8_CRDTP_SERIALIZE_FIELD("get", m_get); + V8_CRDTP_SERIALIZE_FIELD("set", m_set); + V8_CRDTP_SERIALIZE_FIELD("configurable", m_configurable); + V8_CRDTP_SERIALIZE_FIELD("enumerable", m_enumerable); + V8_CRDTP_SERIALIZE_FIELD("wasThrown", m_wasThrown); + V8_CRDTP_SERIALIZE_FIELD("isOwn", m_isOwn); + V8_CRDTP_SERIALIZE_FIELD("symbol", m_symbol); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(InternalPropertyDescriptor) + V8_CRDTP_DESERIALIZE_FIELD("name", m_name), + V8_CRDTP_DESERIALIZE_FIELD_OPT("value", m_value), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(InternalPropertyDescriptor) + V8_CRDTP_SERIALIZE_FIELD("name", m_name); + V8_CRDTP_SERIALIZE_FIELD("value", m_value); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(PrivatePropertyDescriptor) + V8_CRDTP_DESERIALIZE_FIELD_OPT("get", m_get), + V8_CRDTP_DESERIALIZE_FIELD("name", m_name), + V8_CRDTP_DESERIALIZE_FIELD_OPT("set", m_set), + V8_CRDTP_DESERIALIZE_FIELD_OPT("value", m_value), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(PrivatePropertyDescriptor) + V8_CRDTP_SERIALIZE_FIELD("name", m_name); + V8_CRDTP_SERIALIZE_FIELD("value", m_value); + V8_CRDTP_SERIALIZE_FIELD("get", m_get); + V8_CRDTP_SERIALIZE_FIELD("set", m_set); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(CallArgument) + V8_CRDTP_DESERIALIZE_FIELD_OPT("objectId", m_objectId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("unserializableValue", m_unserializableValue), + V8_CRDTP_DESERIALIZE_FIELD_OPT("value", m_value), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(CallArgument) + V8_CRDTP_SERIALIZE_FIELD("value", m_value); + V8_CRDTP_SERIALIZE_FIELD("unserializableValue", m_unserializableValue); + V8_CRDTP_SERIALIZE_FIELD("objectId", m_objectId); +V8_CRDTP_END_SERIALIZER(); + + + +V8_CRDTP_BEGIN_DESERIALIZER(ExecutionContextDescription) + V8_CRDTP_DESERIALIZE_FIELD_OPT("auxData", m_auxData), + V8_CRDTP_DESERIALIZE_FIELD("id", m_id), + V8_CRDTP_DESERIALIZE_FIELD("name", m_name), + V8_CRDTP_DESERIALIZE_FIELD("origin", m_origin), + V8_CRDTP_DESERIALIZE_FIELD("uniqueId", m_uniqueId), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(ExecutionContextDescription) + V8_CRDTP_SERIALIZE_FIELD("id", m_id); + V8_CRDTP_SERIALIZE_FIELD("origin", m_origin); + V8_CRDTP_SERIALIZE_FIELD("name", m_name); + V8_CRDTP_SERIALIZE_FIELD("uniqueId", m_uniqueId); + V8_CRDTP_SERIALIZE_FIELD("auxData", m_auxData); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(ExceptionDetails) + V8_CRDTP_DESERIALIZE_FIELD("columnNumber", m_columnNumber), + V8_CRDTP_DESERIALIZE_FIELD_OPT("exception", m_exception), + V8_CRDTP_DESERIALIZE_FIELD("exceptionId", m_exceptionId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("exceptionMetaData", m_exceptionMetaData), + V8_CRDTP_DESERIALIZE_FIELD_OPT("executionContextId", m_executionContextId), + V8_CRDTP_DESERIALIZE_FIELD("lineNumber", m_lineNumber), + V8_CRDTP_DESERIALIZE_FIELD_OPT("scriptId", m_scriptId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("stackTrace", m_stackTrace), + V8_CRDTP_DESERIALIZE_FIELD("text", m_text), + V8_CRDTP_DESERIALIZE_FIELD_OPT("url", m_url), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(ExceptionDetails) + V8_CRDTP_SERIALIZE_FIELD("exceptionId", m_exceptionId); + V8_CRDTP_SERIALIZE_FIELD("text", m_text); + V8_CRDTP_SERIALIZE_FIELD("lineNumber", m_lineNumber); + V8_CRDTP_SERIALIZE_FIELD("columnNumber", m_columnNumber); + V8_CRDTP_SERIALIZE_FIELD("scriptId", m_scriptId); + V8_CRDTP_SERIALIZE_FIELD("url", m_url); + V8_CRDTP_SERIALIZE_FIELD("stackTrace", m_stackTrace); + V8_CRDTP_SERIALIZE_FIELD("exception", m_exception); + V8_CRDTP_SERIALIZE_FIELD("executionContextId", m_executionContextId); + V8_CRDTP_SERIALIZE_FIELD("exceptionMetaData", m_exceptionMetaData); +V8_CRDTP_END_SERIALIZER(); + + + + +V8_CRDTP_BEGIN_DESERIALIZER(CallFrame) + V8_CRDTP_DESERIALIZE_FIELD("columnNumber", m_columnNumber), + V8_CRDTP_DESERIALIZE_FIELD("functionName", m_functionName), + V8_CRDTP_DESERIALIZE_FIELD("lineNumber", m_lineNumber), + V8_CRDTP_DESERIALIZE_FIELD("scriptId", m_scriptId), + V8_CRDTP_DESERIALIZE_FIELD("url", m_url), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(CallFrame) + V8_CRDTP_SERIALIZE_FIELD("functionName", m_functionName); + V8_CRDTP_SERIALIZE_FIELD("scriptId", m_scriptId); + V8_CRDTP_SERIALIZE_FIELD("url", m_url); + V8_CRDTP_SERIALIZE_FIELD("lineNumber", m_lineNumber); + V8_CRDTP_SERIALIZE_FIELD("columnNumber", m_columnNumber); +V8_CRDTP_END_SERIALIZER(); + + +V8_CRDTP_BEGIN_DESERIALIZER(StackTrace) + V8_CRDTP_DESERIALIZE_FIELD("callFrames", m_callFrames), + V8_CRDTP_DESERIALIZE_FIELD_OPT("description", m_description), + V8_CRDTP_DESERIALIZE_FIELD_OPT("parent", m_parent), + V8_CRDTP_DESERIALIZE_FIELD_OPT("parentId", m_parentId), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(StackTrace) + V8_CRDTP_SERIALIZE_FIELD("description", m_description); + V8_CRDTP_SERIALIZE_FIELD("callFrames", m_callFrames); + V8_CRDTP_SERIALIZE_FIELD("parent", m_parent); + V8_CRDTP_SERIALIZE_FIELD("parentId", m_parentId); +V8_CRDTP_END_SERIALIZER(); + +// static +std::unique_ptr API::StackTrace::fromBinary(const uint8_t* data, size_t length) +{ + return protocol::Runtime::StackTrace::FromBinary(data, length); +} + + +V8_CRDTP_BEGIN_DESERIALIZER(StackTraceId) + V8_CRDTP_DESERIALIZE_FIELD_OPT("debuggerId", m_debuggerId), + V8_CRDTP_DESERIALIZE_FIELD("id", m_id), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(StackTraceId) + V8_CRDTP_SERIALIZE_FIELD("id", m_id); + V8_CRDTP_SERIALIZE_FIELD("debuggerId", m_debuggerId); +V8_CRDTP_END_SERIALIZER(); + +// static +std::unique_ptr API::StackTraceId::fromBinary(const uint8_t* data, size_t length) +{ + return protocol::Runtime::StackTraceId::FromBinary(data, length); +} + +// ------------- Enum values from params. + + +namespace ConsoleAPICalled { +namespace TypeEnum { +const char* Log = "log"; +const char* Debug = "debug"; +const char* Info = "info"; +const char* Error = "error"; +const char* Warning = "warning"; +const char* Dir = "dir"; +const char* Dirxml = "dirxml"; +const char* Table = "table"; +const char* Trace = "trace"; +const char* Clear = "clear"; +const char* StartGroup = "startGroup"; +const char* StartGroupCollapsed = "startGroupCollapsed"; +const char* EndGroup = "endGroup"; +const char* Assert = "assert"; +const char* Profile = "profile"; +const char* ProfileEnd = "profileEnd"; +const char* Count = "count"; +const char* TimeEnd = "timeEnd"; +} // namespace TypeEnum +} // namespace ConsoleAPICalled + +// ------------- Frontend notifications. + +void Frontend::bindingCalled(const String& name, const String& payload, int executionContextId) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("name"), name); + serializer.AddField(v8_crdtp::MakeSpan("payload"), payload); + serializer.AddField(v8_crdtp::MakeSpan("executionContextId"), executionContextId); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Runtime.bindingCalled", serializer.Finish())); +} + +void Frontend::consoleAPICalled(const String& type, std::unique_ptr> args, int executionContextId, double timestamp, Maybe stackTrace, Maybe context) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("type"), type); + serializer.AddField(v8_crdtp::MakeSpan("args"), args); + serializer.AddField(v8_crdtp::MakeSpan("executionContextId"), executionContextId); + serializer.AddField(v8_crdtp::MakeSpan("timestamp"), timestamp); + serializer.AddField(v8_crdtp::MakeSpan("stackTrace"), stackTrace); + serializer.AddField(v8_crdtp::MakeSpan("context"), context); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Runtime.consoleAPICalled", serializer.Finish())); +} + +void Frontend::exceptionRevoked(const String& reason, int exceptionId) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("reason"), reason); + serializer.AddField(v8_crdtp::MakeSpan("exceptionId"), exceptionId); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Runtime.exceptionRevoked", serializer.Finish())); +} + +void Frontend::exceptionThrown(double timestamp, std::unique_ptr exceptionDetails) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("timestamp"), timestamp); + serializer.AddField(v8_crdtp::MakeSpan("exceptionDetails"), exceptionDetails); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Runtime.exceptionThrown", serializer.Finish())); +} + +void Frontend::executionContextCreated(std::unique_ptr context) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("context"), context); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Runtime.executionContextCreated", serializer.Finish())); +} + +void Frontend::executionContextDestroyed(int executionContextId, const String& executionContextUniqueId) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("executionContextId"), executionContextId); + serializer.AddField(v8_crdtp::MakeSpan("executionContextUniqueId"), executionContextUniqueId); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Runtime.executionContextDestroyed", serializer.Finish())); +} + +void Frontend::executionContextsCleared() +{ + if (!frontend_channel_) + return; + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Runtime.executionContextsCleared")); +} + +void Frontend::inspectRequested(std::unique_ptr object, std::unique_ptr hints, Maybe executionContextId) +{ + if (!frontend_channel_) + return; + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("object"), object); + serializer.AddField(v8_crdtp::MakeSpan("hints"), hints); + serializer.AddField(v8_crdtp::MakeSpan("executionContextId"), executionContextId); + frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("Runtime.inspectRequested", serializer.Finish())); +} + +void Frontend::flush() +{ + frontend_channel_->FlushProtocolNotifications(); +} + +void Frontend::sendRawNotification(std::unique_ptr notification) +{ + frontend_channel_->SendProtocolNotification(std::move(notification)); +} + +// --------------------- Dispatcher. + +class DomainDispatcherImpl : public protocol::DomainDispatcher { +public: + DomainDispatcherImpl(FrontendChannel* frontendChannel, Backend* backend) + : DomainDispatcher(frontendChannel) + , m_backend(backend) {} + ~DomainDispatcherImpl() override { } + + using CallHandler = void (DomainDispatcherImpl::*)(const v8_crdtp::Dispatchable& dispatchable); + + std::function Dispatch(v8_crdtp::span command_name) override; + + void awaitPromise(const v8_crdtp::Dispatchable& dispatchable); + void callFunctionOn(const v8_crdtp::Dispatchable& dispatchable); + void compileScript(const v8_crdtp::Dispatchable& dispatchable); + void disable(const v8_crdtp::Dispatchable& dispatchable); + void discardConsoleEntries(const v8_crdtp::Dispatchable& dispatchable); + void enable(const v8_crdtp::Dispatchable& dispatchable); + void evaluate(const v8_crdtp::Dispatchable& dispatchable); + void getIsolateId(const v8_crdtp::Dispatchable& dispatchable); + void getHeapUsage(const v8_crdtp::Dispatchable& dispatchable); + void getProperties(const v8_crdtp::Dispatchable& dispatchable); + void globalLexicalScopeNames(const v8_crdtp::Dispatchable& dispatchable); + void queryObjects(const v8_crdtp::Dispatchable& dispatchable); + void releaseObject(const v8_crdtp::Dispatchable& dispatchable); + void releaseObjectGroup(const v8_crdtp::Dispatchable& dispatchable); + void runIfWaitingForDebugger(const v8_crdtp::Dispatchable& dispatchable); + void runScript(const v8_crdtp::Dispatchable& dispatchable); + void setCustomObjectFormatterEnabled(const v8_crdtp::Dispatchable& dispatchable); + void setMaxCallStackSizeToCapture(const v8_crdtp::Dispatchable& dispatchable); + void terminateExecution(const v8_crdtp::Dispatchable& dispatchable); + void addBinding(const v8_crdtp::Dispatchable& dispatchable); + void removeBinding(const v8_crdtp::Dispatchable& dispatchable); + void getExceptionDetails(const v8_crdtp::Dispatchable& dispatchable); + protected: + Backend* m_backend; +}; + +namespace { +// This helper method with a static map of command methods (instance methods +// of DomainDispatcherImpl declared just above) by their name is used immediately below, +// in the DomainDispatcherImpl::Dispatch method. +DomainDispatcherImpl::CallHandler CommandByName(v8_crdtp::span command_name) { + static auto* commands = [](){ + auto* commands = new std::vector, + DomainDispatcherImpl::CallHandler>>{ + { + v8_crdtp::SpanFrom("addBinding"), + &DomainDispatcherImpl::addBinding + }, + { + v8_crdtp::SpanFrom("awaitPromise"), + &DomainDispatcherImpl::awaitPromise + }, + { + v8_crdtp::SpanFrom("callFunctionOn"), + &DomainDispatcherImpl::callFunctionOn + }, + { + v8_crdtp::SpanFrom("compileScript"), + &DomainDispatcherImpl::compileScript + }, + { + v8_crdtp::SpanFrom("disable"), + &DomainDispatcherImpl::disable + }, + { + v8_crdtp::SpanFrom("discardConsoleEntries"), + &DomainDispatcherImpl::discardConsoleEntries + }, + { + v8_crdtp::SpanFrom("enable"), + &DomainDispatcherImpl::enable + }, + { + v8_crdtp::SpanFrom("evaluate"), + &DomainDispatcherImpl::evaluate + }, + { + v8_crdtp::SpanFrom("getExceptionDetails"), + &DomainDispatcherImpl::getExceptionDetails + }, + { + v8_crdtp::SpanFrom("getHeapUsage"), + &DomainDispatcherImpl::getHeapUsage + }, + { + v8_crdtp::SpanFrom("getIsolateId"), + &DomainDispatcherImpl::getIsolateId + }, + { + v8_crdtp::SpanFrom("getProperties"), + &DomainDispatcherImpl::getProperties + }, + { + v8_crdtp::SpanFrom("globalLexicalScopeNames"), + &DomainDispatcherImpl::globalLexicalScopeNames + }, + { + v8_crdtp::SpanFrom("queryObjects"), + &DomainDispatcherImpl::queryObjects + }, + { + v8_crdtp::SpanFrom("releaseObject"), + &DomainDispatcherImpl::releaseObject + }, + { + v8_crdtp::SpanFrom("releaseObjectGroup"), + &DomainDispatcherImpl::releaseObjectGroup + }, + { + v8_crdtp::SpanFrom("removeBinding"), + &DomainDispatcherImpl::removeBinding + }, + { + v8_crdtp::SpanFrom("runIfWaitingForDebugger"), + &DomainDispatcherImpl::runIfWaitingForDebugger + }, + { + v8_crdtp::SpanFrom("runScript"), + &DomainDispatcherImpl::runScript + }, + { + v8_crdtp::SpanFrom("setCustomObjectFormatterEnabled"), + &DomainDispatcherImpl::setCustomObjectFormatterEnabled + }, + { + v8_crdtp::SpanFrom("setMaxCallStackSizeToCapture"), + &DomainDispatcherImpl::setMaxCallStackSizeToCapture + }, + { + v8_crdtp::SpanFrom("terminateExecution"), + &DomainDispatcherImpl::terminateExecution + }, + }; + return commands; + }(); + return v8_crdtp::FindByFirst(*commands, command_name, nullptr); +} +} // namespace + +std::function DomainDispatcherImpl::Dispatch(v8_crdtp::span command_name) { + CallHandler handler = CommandByName(command_name); + if (!handler) return nullptr; + + return [this, handler](const v8_crdtp::Dispatchable& dispatchable) { + (this->*handler)(dispatchable); + }; +} + + +class AwaitPromiseCallbackImpl : public Backend::AwaitPromiseCallback, public DomainDispatcher::Callback { +public: + AwaitPromiseCallbackImpl(std::unique_ptr backendImpl, int callId, v8_crdtp::span message) + : DomainDispatcher::Callback(std::move(backendImpl), callId, +v8_crdtp::SpanFrom("Runtime.awaitPromise"), message) { } + + void sendSuccess(std::unique_ptr result, Maybe exceptionDetails) override + { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("result"), result); + serializer.AddField(v8_crdtp::MakeSpan("exceptionDetails"), exceptionDetails); + sendIfActive(serializer.Finish(), DispatchResponse::Success()); + } + + void fallThrough() override + { + fallThroughIfActive(); + } + + void sendFailure(const DispatchResponse& response) override + { + DCHECK(response.IsError()); + sendIfActive(nullptr, response); + } +}; + +namespace { + +struct awaitPromiseParams : public v8_crdtp::DeserializableProtocolObject { + String promiseObjectId; + Maybe returnByValue; + Maybe generatePreview; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(awaitPromiseParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("generatePreview", generatePreview), + V8_CRDTP_DESERIALIZE_FIELD("promiseObjectId", promiseObjectId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("returnByValue", returnByValue), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::awaitPromise(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + awaitPromiseParams params; + if (!awaitPromiseParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + m_backend->awaitPromise(params.promiseObjectId, std::move(params.returnByValue), std::move(params.generatePreview), std::make_unique(weakPtr(), dispatchable.CallId(), dispatchable.Serialized())); +} + +class CallFunctionOnCallbackImpl : public Backend::CallFunctionOnCallback, public DomainDispatcher::Callback { +public: + CallFunctionOnCallbackImpl(std::unique_ptr backendImpl, int callId, v8_crdtp::span message) + : DomainDispatcher::Callback(std::move(backendImpl), callId, +v8_crdtp::SpanFrom("Runtime.callFunctionOn"), message) { } + + void sendSuccess(std::unique_ptr result, Maybe exceptionDetails) override + { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("result"), result); + serializer.AddField(v8_crdtp::MakeSpan("exceptionDetails"), exceptionDetails); + sendIfActive(serializer.Finish(), DispatchResponse::Success()); + } + + void fallThrough() override + { + fallThroughIfActive(); + } + + void sendFailure(const DispatchResponse& response) override + { + DCHECK(response.IsError()); + sendIfActive(nullptr, response); + } +}; + +namespace { + +struct callFunctionOnParams : public v8_crdtp::DeserializableProtocolObject { + String functionDeclaration; + Maybe objectId; + Maybe> arguments; + Maybe silent; + Maybe returnByValue; + Maybe generatePreview; + Maybe userGesture; + Maybe awaitPromise; + Maybe executionContextId; + Maybe objectGroup; + Maybe throwOnSideEffect; + Maybe uniqueContextId; + Maybe generateWebDriverValue; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(callFunctionOnParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("arguments", arguments), + V8_CRDTP_DESERIALIZE_FIELD_OPT("awaitPromise", awaitPromise), + V8_CRDTP_DESERIALIZE_FIELD_OPT("executionContextId", executionContextId), + V8_CRDTP_DESERIALIZE_FIELD("functionDeclaration", functionDeclaration), + V8_CRDTP_DESERIALIZE_FIELD_OPT("generatePreview", generatePreview), + V8_CRDTP_DESERIALIZE_FIELD_OPT("generateWebDriverValue", generateWebDriverValue), + V8_CRDTP_DESERIALIZE_FIELD_OPT("objectGroup", objectGroup), + V8_CRDTP_DESERIALIZE_FIELD_OPT("objectId", objectId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("returnByValue", returnByValue), + V8_CRDTP_DESERIALIZE_FIELD_OPT("silent", silent), + V8_CRDTP_DESERIALIZE_FIELD_OPT("throwOnSideEffect", throwOnSideEffect), + V8_CRDTP_DESERIALIZE_FIELD_OPT("uniqueContextId", uniqueContextId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("userGesture", userGesture), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::callFunctionOn(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + callFunctionOnParams params; + if (!callFunctionOnParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + m_backend->callFunctionOn(params.functionDeclaration, std::move(params.objectId), std::move(params.arguments), std::move(params.silent), std::move(params.returnByValue), std::move(params.generatePreview), std::move(params.userGesture), std::move(params.awaitPromise), std::move(params.executionContextId), std::move(params.objectGroup), std::move(params.throwOnSideEffect), std::move(params.uniqueContextId), std::move(params.generateWebDriverValue), std::make_unique(weakPtr(), dispatchable.CallId(), dispatchable.Serialized())); +} + +namespace { + +struct compileScriptParams : public v8_crdtp::DeserializableProtocolObject { + String expression; + String sourceURL; + bool persistScript; + Maybe executionContextId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(compileScriptParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("executionContextId", executionContextId), + V8_CRDTP_DESERIALIZE_FIELD("expression", expression), + V8_CRDTP_DESERIALIZE_FIELD("persistScript", persistScript), + V8_CRDTP_DESERIALIZE_FIELD("sourceURL", sourceURL), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::compileScript(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + compileScriptParams params; + if (!compileScriptParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + Maybe out_scriptId; + Maybe out_exceptionDetails; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->compileScript(params.expression, params.sourceURL, params.persistScript, std::move(params.executionContextId), &out_scriptId, &out_exceptionDetails); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.compileScript"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("scriptId"), out_scriptId); + serializer.AddField(v8_crdtp::MakeSpan("exceptionDetails"), out_exceptionDetails); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::disable(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->disable(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.disable"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::discardConsoleEntries(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->discardConsoleEntries(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.discardConsoleEntries"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::enable(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->enable(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.enable"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +class EvaluateCallbackImpl : public Backend::EvaluateCallback, public DomainDispatcher::Callback { +public: + EvaluateCallbackImpl(std::unique_ptr backendImpl, int callId, v8_crdtp::span message) + : DomainDispatcher::Callback(std::move(backendImpl), callId, +v8_crdtp::SpanFrom("Runtime.evaluate"), message) { } + + void sendSuccess(std::unique_ptr result, Maybe exceptionDetails) override + { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("result"), result); + serializer.AddField(v8_crdtp::MakeSpan("exceptionDetails"), exceptionDetails); + sendIfActive(serializer.Finish(), DispatchResponse::Success()); + } + + void fallThrough() override + { + fallThroughIfActive(); + } + + void sendFailure(const DispatchResponse& response) override + { + DCHECK(response.IsError()); + sendIfActive(nullptr, response); + } +}; + +namespace { + +struct evaluateParams : public v8_crdtp::DeserializableProtocolObject { + String expression; + Maybe objectGroup; + Maybe includeCommandLineAPI; + Maybe silent; + Maybe contextId; + Maybe returnByValue; + Maybe generatePreview; + Maybe userGesture; + Maybe awaitPromise; + Maybe throwOnSideEffect; + Maybe timeout; + Maybe disableBreaks; + Maybe replMode; + Maybe allowUnsafeEvalBlockedByCSP; + Maybe uniqueContextId; + Maybe generateWebDriverValue; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(evaluateParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("allowUnsafeEvalBlockedByCSP", allowUnsafeEvalBlockedByCSP), + V8_CRDTP_DESERIALIZE_FIELD_OPT("awaitPromise", awaitPromise), + V8_CRDTP_DESERIALIZE_FIELD_OPT("contextId", contextId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("disableBreaks", disableBreaks), + V8_CRDTP_DESERIALIZE_FIELD("expression", expression), + V8_CRDTP_DESERIALIZE_FIELD_OPT("generatePreview", generatePreview), + V8_CRDTP_DESERIALIZE_FIELD_OPT("generateWebDriverValue", generateWebDriverValue), + V8_CRDTP_DESERIALIZE_FIELD_OPT("includeCommandLineAPI", includeCommandLineAPI), + V8_CRDTP_DESERIALIZE_FIELD_OPT("objectGroup", objectGroup), + V8_CRDTP_DESERIALIZE_FIELD_OPT("replMode", replMode), + V8_CRDTP_DESERIALIZE_FIELD_OPT("returnByValue", returnByValue), + V8_CRDTP_DESERIALIZE_FIELD_OPT("silent", silent), + V8_CRDTP_DESERIALIZE_FIELD_OPT("throwOnSideEffect", throwOnSideEffect), + V8_CRDTP_DESERIALIZE_FIELD_OPT("timeout", timeout), + V8_CRDTP_DESERIALIZE_FIELD_OPT("uniqueContextId", uniqueContextId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("userGesture", userGesture), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::evaluate(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + evaluateParams params; + if (!evaluateParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + m_backend->evaluate(params.expression, std::move(params.objectGroup), std::move(params.includeCommandLineAPI), std::move(params.silent), std::move(params.contextId), std::move(params.returnByValue), std::move(params.generatePreview), std::move(params.userGesture), std::move(params.awaitPromise), std::move(params.throwOnSideEffect), std::move(params.timeout), std::move(params.disableBreaks), std::move(params.replMode), std::move(params.allowUnsafeEvalBlockedByCSP), std::move(params.uniqueContextId), std::move(params.generateWebDriverValue), std::make_unique(weakPtr(), dispatchable.CallId(), dispatchable.Serialized())); +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::getIsolateId(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + // Declare output parameters. + String out_id; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->getIsolateId(&out_id); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.getIsolateId"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("id"), out_id); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::getHeapUsage(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + // Declare output parameters. + double out_usedSize; + double out_totalSize; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->getHeapUsage(&out_usedSize, &out_totalSize); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.getHeapUsage"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("usedSize"), out_usedSize); + serializer.AddField(v8_crdtp::MakeSpan("totalSize"), out_totalSize); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct getPropertiesParams : public v8_crdtp::DeserializableProtocolObject { + String objectId; + Maybe ownProperties; + Maybe accessorPropertiesOnly; + Maybe generatePreview; + Maybe nonIndexedPropertiesOnly; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(getPropertiesParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("accessorPropertiesOnly", accessorPropertiesOnly), + V8_CRDTP_DESERIALIZE_FIELD_OPT("generatePreview", generatePreview), + V8_CRDTP_DESERIALIZE_FIELD_OPT("nonIndexedPropertiesOnly", nonIndexedPropertiesOnly), + V8_CRDTP_DESERIALIZE_FIELD("objectId", objectId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("ownProperties", ownProperties), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::getProperties(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + getPropertiesParams params; + if (!getPropertiesParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + std::unique_ptr> out_result; + Maybe> out_internalProperties; + Maybe> out_privateProperties; + Maybe out_exceptionDetails; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->getProperties(params.objectId, std::move(params.ownProperties), std::move(params.accessorPropertiesOnly), std::move(params.generatePreview), std::move(params.nonIndexedPropertiesOnly), &out_result, &out_internalProperties, &out_privateProperties, &out_exceptionDetails); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.getProperties"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("result"), out_result); + serializer.AddField(v8_crdtp::MakeSpan("internalProperties"), out_internalProperties); + serializer.AddField(v8_crdtp::MakeSpan("privateProperties"), out_privateProperties); + serializer.AddField(v8_crdtp::MakeSpan("exceptionDetails"), out_exceptionDetails); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct globalLexicalScopeNamesParams : public v8_crdtp::DeserializableProtocolObject { + Maybe executionContextId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(globalLexicalScopeNamesParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("executionContextId", executionContextId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::globalLexicalScopeNames(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + globalLexicalScopeNamesParams params; + if (!globalLexicalScopeNamesParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + std::unique_ptr> out_names; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->globalLexicalScopeNames(std::move(params.executionContextId), &out_names); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.globalLexicalScopeNames"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("names"), out_names); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct queryObjectsParams : public v8_crdtp::DeserializableProtocolObject { + String prototypeObjectId; + Maybe objectGroup; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(queryObjectsParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("objectGroup", objectGroup), + V8_CRDTP_DESERIALIZE_FIELD("prototypeObjectId", prototypeObjectId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::queryObjects(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + queryObjectsParams params; + if (!queryObjectsParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + std::unique_ptr out_objects; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->queryObjects(params.prototypeObjectId, std::move(params.objectGroup), &out_objects); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.queryObjects"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("objects"), out_objects); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { + +struct releaseObjectParams : public v8_crdtp::DeserializableProtocolObject { + String objectId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(releaseObjectParams) + V8_CRDTP_DESERIALIZE_FIELD("objectId", objectId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::releaseObject(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + releaseObjectParams params; + if (!releaseObjectParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->releaseObject(params.objectId); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.releaseObject"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct releaseObjectGroupParams : public v8_crdtp::DeserializableProtocolObject { + String objectGroup; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(releaseObjectGroupParams) + V8_CRDTP_DESERIALIZE_FIELD("objectGroup", objectGroup), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::releaseObjectGroup(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + releaseObjectGroupParams params; + if (!releaseObjectGroupParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->releaseObjectGroup(params.objectGroup); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.releaseObjectGroup"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + + +} // namespace + +void DomainDispatcherImpl::runIfWaitingForDebugger(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->runIfWaitingForDebugger(); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.runIfWaitingForDebugger"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +class RunScriptCallbackImpl : public Backend::RunScriptCallback, public DomainDispatcher::Callback { +public: + RunScriptCallbackImpl(std::unique_ptr backendImpl, int callId, v8_crdtp::span message) + : DomainDispatcher::Callback(std::move(backendImpl), callId, +v8_crdtp::SpanFrom("Runtime.runScript"), message) { } + + void sendSuccess(std::unique_ptr result, Maybe exceptionDetails) override + { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("result"), result); + serializer.AddField(v8_crdtp::MakeSpan("exceptionDetails"), exceptionDetails); + sendIfActive(serializer.Finish(), DispatchResponse::Success()); + } + + void fallThrough() override + { + fallThroughIfActive(); + } + + void sendFailure(const DispatchResponse& response) override + { + DCHECK(response.IsError()); + sendIfActive(nullptr, response); + } +}; + +namespace { + +struct runScriptParams : public v8_crdtp::DeserializableProtocolObject { + String scriptId; + Maybe executionContextId; + Maybe objectGroup; + Maybe silent; + Maybe includeCommandLineAPI; + Maybe returnByValue; + Maybe generatePreview; + Maybe awaitPromise; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(runScriptParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("awaitPromise", awaitPromise), + V8_CRDTP_DESERIALIZE_FIELD_OPT("executionContextId", executionContextId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("generatePreview", generatePreview), + V8_CRDTP_DESERIALIZE_FIELD_OPT("includeCommandLineAPI", includeCommandLineAPI), + V8_CRDTP_DESERIALIZE_FIELD_OPT("objectGroup", objectGroup), + V8_CRDTP_DESERIALIZE_FIELD_OPT("returnByValue", returnByValue), + V8_CRDTP_DESERIALIZE_FIELD("scriptId", scriptId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("silent", silent), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::runScript(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + runScriptParams params; + if (!runScriptParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + m_backend->runScript(params.scriptId, std::move(params.executionContextId), std::move(params.objectGroup), std::move(params.silent), std::move(params.includeCommandLineAPI), std::move(params.returnByValue), std::move(params.generatePreview), std::move(params.awaitPromise), std::make_unique(weakPtr(), dispatchable.CallId(), dispatchable.Serialized())); +} + +namespace { + +struct setCustomObjectFormatterEnabledParams : public v8_crdtp::DeserializableProtocolObject { + bool enabled; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setCustomObjectFormatterEnabledParams) + V8_CRDTP_DESERIALIZE_FIELD("enabled", enabled), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setCustomObjectFormatterEnabled(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setCustomObjectFormatterEnabledParams params; + if (!setCustomObjectFormatterEnabledParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setCustomObjectFormatterEnabled(params.enabled); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.setCustomObjectFormatterEnabled"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct setMaxCallStackSizeToCaptureParams : public v8_crdtp::DeserializableProtocolObject { + int size; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(setMaxCallStackSizeToCaptureParams) + V8_CRDTP_DESERIALIZE_FIELD("size", size), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::setMaxCallStackSizeToCapture(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + setMaxCallStackSizeToCaptureParams params; + if (!setMaxCallStackSizeToCaptureParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->setMaxCallStackSizeToCapture(params.size); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.setMaxCallStackSizeToCapture"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +class TerminateExecutionCallbackImpl : public Backend::TerminateExecutionCallback, public DomainDispatcher::Callback { +public: + TerminateExecutionCallbackImpl(std::unique_ptr backendImpl, int callId, v8_crdtp::span message) + : DomainDispatcher::Callback(std::move(backendImpl), callId, +v8_crdtp::SpanFrom("Runtime.terminateExecution"), message) { } + + void sendSuccess() override + { + v8_crdtp::ObjectSerializer serializer; + sendIfActive(serializer.Finish(), DispatchResponse::Success()); + } + + void fallThrough() override + { + fallThroughIfActive(); + } + + void sendFailure(const DispatchResponse& response) override + { + DCHECK(response.IsError()); + sendIfActive(nullptr, response); + } +}; + +namespace { + + +} // namespace + +void DomainDispatcherImpl::terminateExecution(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + + m_backend->terminateExecution(std::make_unique(weakPtr(), dispatchable.CallId(), dispatchable.Serialized())); +} + +namespace { + +struct addBindingParams : public v8_crdtp::DeserializableProtocolObject { + String name; + Maybe executionContextId; + Maybe executionContextName; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(addBindingParams) + V8_CRDTP_DESERIALIZE_FIELD_OPT("executionContextId", executionContextId), + V8_CRDTP_DESERIALIZE_FIELD_OPT("executionContextName", executionContextName), + V8_CRDTP_DESERIALIZE_FIELD("name", name), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::addBinding(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + addBindingParams params; + if (!addBindingParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->addBinding(params.name, std::move(params.executionContextId), std::move(params.executionContextName)); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.addBinding"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct removeBindingParams : public v8_crdtp::DeserializableProtocolObject { + String name; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(removeBindingParams) + V8_CRDTP_DESERIALIZE_FIELD("name", name), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::removeBinding(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + removeBindingParams params; + if (!removeBindingParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->removeBinding(params.name); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.removeBinding"), dispatchable.Serialized()); + return; + } + if (weak->get()) + weak->get()->sendResponse(dispatchable.CallId(), response); + return; +} + +namespace { + +struct getExceptionDetailsParams : public v8_crdtp::DeserializableProtocolObject { + String errorObjectId; + DECLARE_DESERIALIZATION_SUPPORT(); +}; + +V8_CRDTP_BEGIN_DESERIALIZER(getExceptionDetailsParams) + V8_CRDTP_DESERIALIZE_FIELD("errorObjectId", errorObjectId), +V8_CRDTP_END_DESERIALIZER() + +} // namespace + +void DomainDispatcherImpl::getExceptionDetails(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + auto deserializer = v8_crdtp::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer(); + getExceptionDetailsParams params; + if (!getExceptionDetailsParams::Deserialize(&deserializer, ¶ms)) { + ReportInvalidParams(dispatchable, deserializer); + return; + } + // Declare output parameters. + Maybe out_exceptionDetails; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->getExceptionDetails(params.errorObjectId, &out_exceptionDetails); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Runtime.getExceptionDetails"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("exceptionDetails"), out_exceptionDetails); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { +// This helper method (with a static map of redirects) is used from Dispatcher::wire +// immediately below. +const std::vector, v8_crdtp::span>>& SortedRedirects() { + static auto* redirects = [](){ + auto* redirects = new std::vector, v8_crdtp::span>>{ + { v8_crdtp::SpanFrom("Runtime.setAsyncCallStackDepth"), v8_crdtp::SpanFrom("Debugger.setAsyncCallStackDepth") }, + }; + return redirects; + }(); + return *redirects; +} +} // namespace + +// static +void Dispatcher::wire(UberDispatcher* uber, Backend* backend) +{ + auto dispatcher = std::make_unique(uber->channel(), backend); + uber->WireBackend(v8_crdtp::SpanFrom("Runtime"), SortedRedirects(), std::move(dispatcher)); +} + +} // Runtime +} // namespace v8_inspector +} // namespace protocol diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Runtime.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Runtime.h new file mode 100644 index 000000000..86efaff65 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Runtime.h @@ -0,0 +1,1825 @@ +// This file is generated by TypeBuilder_h.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef v8_inspector_protocol_Runtime_h +#define v8_inspector_protocol_Runtime_h + +#include "src/inspector/protocol/Protocol.h" +// For each imported domain we generate a ValueConversions struct instead of a full domain definition +// and include Domain::API version from there. +#include "include/inspector/Runtime.h" + +namespace v8_inspector { +namespace protocol { +namespace Runtime { +using ScriptId = String; +class WebDriverValue; +using RemoteObjectId = String; +using UnserializableValue = String; +class RemoteObject; +class CustomPreview; +class ObjectPreview; +class PropertyPreview; +class EntryPreview; +class PropertyDescriptor; +class InternalPropertyDescriptor; +class PrivatePropertyDescriptor; +class CallArgument; +using ExecutionContextId = int; +class ExecutionContextDescription; +class ExceptionDetails; +using Timestamp = double; +using TimeDelta = double; +class CallFrame; +class StackTrace; +using UniqueDebuggerId = String; +class StackTraceId; + +// ------------- Forward and enum declarations. + +namespace ConsoleAPICalled { +namespace TypeEnum { + extern const char* Log; + extern const char* Debug; + extern const char* Info; + extern const char* Error; + extern const char* Warning; + extern const char* Dir; + extern const char* Dirxml; + extern const char* Table; + extern const char* Trace; + extern const char* Clear; + extern const char* StartGroup; + extern const char* StartGroupCollapsed; + extern const char* EndGroup; + extern const char* Assert; + extern const char* Profile; + extern const char* ProfileEnd; + extern const char* Count; + extern const char* TimeEnd; +} // TypeEnum +} // ConsoleAPICalled + +// ------------- Type and builder declarations. + +class WebDriverValue : public ::v8_crdtp::ProtocolObject { +public: + ~WebDriverValue() override { } + + struct TypeEnum { + static const char* Undefined; + static const char* Null; + static const char* String; + static const char* Number; + static const char* Boolean; + static const char* Bigint; + static const char* Regexp; + static const char* Date; + static const char* Symbol; + static const char* Array; + static const char* Object; + static const char* Function; + static const char* Map; + static const char* Set; + static const char* Weakmap; + static const char* Weakset; + static const char* Error; + static const char* Proxy; + static const char* Promise; + static const char* Typedarray; + static const char* Arraybuffer; + static const char* Node; + static const char* Window; + }; // TypeEnum + + String getType() { return m_type; } + void setType(const String& value) { m_type = value; } + + bool hasValue() { return m_value.isJust(); } + protocol::Value* getValue(protocol::Value* defaultValue) { return m_value.isJust() ? m_value.fromJust() : defaultValue; } + void setValue(std::unique_ptr value) { m_value = std::move(value); } + + bool hasObjectId() { return m_objectId.isJust(); } + String getObjectId(const String& defaultValue) { return m_objectId.isJust() ? m_objectId.fromJust() : defaultValue; } + void setObjectId(const String& value) { m_objectId = value; } + + template + class WebDriverValueBuilder { + public: + enum { + NoFieldsSet = 0, + TypeSet = 1 << 1, + AllFieldsSet = (TypeSet | 0)}; + + + WebDriverValueBuilder& setType(const String& value) + { + static_assert(!(STATE & TypeSet), "property type should not be set yet"); + m_result->setType(value); + return castState(); + } + + WebDriverValueBuilder& setValue(std::unique_ptr value) + { + m_result->setValue(std::move(value)); + return *this; + } + + WebDriverValueBuilder& setObjectId(const String& value) + { + m_result->setObjectId(value); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class WebDriverValue; + WebDriverValueBuilder() : m_result(new WebDriverValue()) { } + + template WebDriverValueBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static WebDriverValueBuilder<0> create() + { + return WebDriverValueBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + WebDriverValue() + { + } + + String m_type; + Maybe m_value; + Maybe m_objectId; +}; + + +class RemoteObject : public ::v8_crdtp::ProtocolObject, + public API::RemoteObject { +public: + ~RemoteObject() override { } + + struct TypeEnum { + static const char* Object; + static const char* Function; + static const char* Undefined; + static const char* String; + static const char* Number; + static const char* Boolean; + static const char* Symbol; + static const char* Bigint; + }; // TypeEnum + + String getType() { return m_type; } + void setType(const String& value) { m_type = value; } + + struct SubtypeEnum { + static const char* Array; + static const char* Null; + static const char* Node; + static const char* Regexp; + static const char* Date; + static const char* Map; + static const char* Set; + static const char* Weakmap; + static const char* Weakset; + static const char* Iterator; + static const char* Generator; + static const char* Error; + static const char* Proxy; + static const char* Promise; + static const char* Typedarray; + static const char* Arraybuffer; + static const char* Dataview; + static const char* Webassemblymemory; + static const char* Wasmvalue; + }; // SubtypeEnum + + bool hasSubtype() { return m_subtype.isJust(); } + String getSubtype(const String& defaultValue) { return m_subtype.isJust() ? m_subtype.fromJust() : defaultValue; } + void setSubtype(const String& value) { m_subtype = value; } + + bool hasClassName() { return m_className.isJust(); } + String getClassName(const String& defaultValue) { return m_className.isJust() ? m_className.fromJust() : defaultValue; } + void setClassName(const String& value) { m_className = value; } + + bool hasValue() { return m_value.isJust(); } + protocol::Value* getValue(protocol::Value* defaultValue) { return m_value.isJust() ? m_value.fromJust() : defaultValue; } + void setValue(std::unique_ptr value) { m_value = std::move(value); } + + bool hasUnserializableValue() { return m_unserializableValue.isJust(); } + String getUnserializableValue(const String& defaultValue) { return m_unserializableValue.isJust() ? m_unserializableValue.fromJust() : defaultValue; } + void setUnserializableValue(const String& value) { m_unserializableValue = value; } + + bool hasDescription() { return m_description.isJust(); } + String getDescription(const String& defaultValue) { return m_description.isJust() ? m_description.fromJust() : defaultValue; } + void setDescription(const String& value) { m_description = value; } + + bool hasWebDriverValue() { return m_webDriverValue.isJust(); } + protocol::Runtime::WebDriverValue* getWebDriverValue(protocol::Runtime::WebDriverValue* defaultValue) { return m_webDriverValue.isJust() ? m_webDriverValue.fromJust() : defaultValue; } + void setWebDriverValue(std::unique_ptr value) { m_webDriverValue = std::move(value); } + + bool hasObjectId() { return m_objectId.isJust(); } + String getObjectId(const String& defaultValue) { return m_objectId.isJust() ? m_objectId.fromJust() : defaultValue; } + void setObjectId(const String& value) { m_objectId = value; } + + bool hasPreview() { return m_preview.isJust(); } + protocol::Runtime::ObjectPreview* getPreview(protocol::Runtime::ObjectPreview* defaultValue) { return m_preview.isJust() ? m_preview.fromJust() : defaultValue; } + void setPreview(std::unique_ptr value) { m_preview = std::move(value); } + + bool hasCustomPreview() { return m_customPreview.isJust(); } + protocol::Runtime::CustomPreview* getCustomPreview(protocol::Runtime::CustomPreview* defaultValue) { return m_customPreview.isJust() ? m_customPreview.fromJust() : defaultValue; } + void setCustomPreview(std::unique_ptr value) { m_customPreview = std::move(value); } + + template + class RemoteObjectBuilder { + public: + enum { + NoFieldsSet = 0, + TypeSet = 1 << 1, + AllFieldsSet = (TypeSet | 0)}; + + + RemoteObjectBuilder& setType(const String& value) + { + static_assert(!(STATE & TypeSet), "property type should not be set yet"); + m_result->setType(value); + return castState(); + } + + RemoteObjectBuilder& setSubtype(const String& value) + { + m_result->setSubtype(value); + return *this; + } + + RemoteObjectBuilder& setClassName(const String& value) + { + m_result->setClassName(value); + return *this; + } + + RemoteObjectBuilder& setValue(std::unique_ptr value) + { + m_result->setValue(std::move(value)); + return *this; + } + + RemoteObjectBuilder& setUnserializableValue(const String& value) + { + m_result->setUnserializableValue(value); + return *this; + } + + RemoteObjectBuilder& setDescription(const String& value) + { + m_result->setDescription(value); + return *this; + } + + RemoteObjectBuilder& setWebDriverValue(std::unique_ptr value) + { + m_result->setWebDriverValue(std::move(value)); + return *this; + } + + RemoteObjectBuilder& setObjectId(const String& value) + { + m_result->setObjectId(value); + return *this; + } + + RemoteObjectBuilder& setPreview(std::unique_ptr value) + { + m_result->setPreview(std::move(value)); + return *this; + } + + RemoteObjectBuilder& setCustomPreview(std::unique_ptr value) + { + m_result->setCustomPreview(std::move(value)); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class RemoteObject; + RemoteObjectBuilder() : m_result(new RemoteObject()) { } + + template RemoteObjectBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static RemoteObjectBuilder<0> create() + { + return RemoteObjectBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + RemoteObject() + { + } + + String m_type; + Maybe m_subtype; + Maybe m_className; + Maybe m_value; + Maybe m_unserializableValue; + Maybe m_description; + Maybe m_webDriverValue; + Maybe m_objectId; + Maybe m_preview; + Maybe m_customPreview; +}; + + +class CustomPreview : public ::v8_crdtp::ProtocolObject { +public: + ~CustomPreview() override { } + + String getHeader() { return m_header; } + void setHeader(const String& value) { m_header = value; } + + bool hasBodyGetterId() { return m_bodyGetterId.isJust(); } + String getBodyGetterId(const String& defaultValue) { return m_bodyGetterId.isJust() ? m_bodyGetterId.fromJust() : defaultValue; } + void setBodyGetterId(const String& value) { m_bodyGetterId = value; } + + template + class CustomPreviewBuilder { + public: + enum { + NoFieldsSet = 0, + HeaderSet = 1 << 1, + AllFieldsSet = (HeaderSet | 0)}; + + + CustomPreviewBuilder& setHeader(const String& value) + { + static_assert(!(STATE & HeaderSet), "property header should not be set yet"); + m_result->setHeader(value); + return castState(); + } + + CustomPreviewBuilder& setBodyGetterId(const String& value) + { + m_result->setBodyGetterId(value); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class CustomPreview; + CustomPreviewBuilder() : m_result(new CustomPreview()) { } + + template CustomPreviewBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static CustomPreviewBuilder<0> create() + { + return CustomPreviewBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + CustomPreview() + { + } + + String m_header; + Maybe m_bodyGetterId; +}; + + +class ObjectPreview : public ::v8_crdtp::ProtocolObject { +public: + ~ObjectPreview() override { } + + struct TypeEnum { + static const char* Object; + static const char* Function; + static const char* Undefined; + static const char* String; + static const char* Number; + static const char* Boolean; + static const char* Symbol; + static const char* Bigint; + }; // TypeEnum + + String getType() { return m_type; } + void setType(const String& value) { m_type = value; } + + struct SubtypeEnum { + static const char* Array; + static const char* Null; + static const char* Node; + static const char* Regexp; + static const char* Date; + static const char* Map; + static const char* Set; + static const char* Weakmap; + static const char* Weakset; + static const char* Iterator; + static const char* Generator; + static const char* Error; + static const char* Proxy; + static const char* Promise; + static const char* Typedarray; + static const char* Arraybuffer; + static const char* Dataview; + static const char* Webassemblymemory; + static const char* Wasmvalue; + }; // SubtypeEnum + + bool hasSubtype() { return m_subtype.isJust(); } + String getSubtype(const String& defaultValue) { return m_subtype.isJust() ? m_subtype.fromJust() : defaultValue; } + void setSubtype(const String& value) { m_subtype = value; } + + bool hasDescription() { return m_description.isJust(); } + String getDescription(const String& defaultValue) { return m_description.isJust() ? m_description.fromJust() : defaultValue; } + void setDescription(const String& value) { m_description = value; } + + bool getOverflow() { return m_overflow; } + void setOverflow(bool value) { m_overflow = value; } + + protocol::Array* getProperties() { return m_properties.get(); } + void setProperties(std::unique_ptr> value) { m_properties = std::move(value); } + + bool hasEntries() { return m_entries.isJust(); } + protocol::Array* getEntries(protocol::Array* defaultValue) { return m_entries.isJust() ? m_entries.fromJust() : defaultValue; } + void setEntries(std::unique_ptr> value) { m_entries = std::move(value); } + + template + class ObjectPreviewBuilder { + public: + enum { + NoFieldsSet = 0, + TypeSet = 1 << 1, + OverflowSet = 1 << 2, + PropertiesSet = 1 << 3, + AllFieldsSet = (TypeSet | OverflowSet | PropertiesSet | 0)}; + + + ObjectPreviewBuilder& setType(const String& value) + { + static_assert(!(STATE & TypeSet), "property type should not be set yet"); + m_result->setType(value); + return castState(); + } + + ObjectPreviewBuilder& setSubtype(const String& value) + { + m_result->setSubtype(value); + return *this; + } + + ObjectPreviewBuilder& setDescription(const String& value) + { + m_result->setDescription(value); + return *this; + } + + ObjectPreviewBuilder& setOverflow(bool value) + { + static_assert(!(STATE & OverflowSet), "property overflow should not be set yet"); + m_result->setOverflow(value); + return castState(); + } + + ObjectPreviewBuilder& setProperties(std::unique_ptr> value) + { + static_assert(!(STATE & PropertiesSet), "property properties should not be set yet"); + m_result->setProperties(std::move(value)); + return castState(); + } + + ObjectPreviewBuilder& setEntries(std::unique_ptr> value) + { + m_result->setEntries(std::move(value)); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class ObjectPreview; + ObjectPreviewBuilder() : m_result(new ObjectPreview()) { } + + template ObjectPreviewBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static ObjectPreviewBuilder<0> create() + { + return ObjectPreviewBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + ObjectPreview() + { + m_overflow = false; + } + + String m_type; + Maybe m_subtype; + Maybe m_description; + bool m_overflow; + std::unique_ptr> m_properties; + Maybe> m_entries; +}; + + +class PropertyPreview : public ::v8_crdtp::ProtocolObject { +public: + ~PropertyPreview() override { } + + String getName() { return m_name; } + void setName(const String& value) { m_name = value; } + + struct TypeEnum { + static const char* Object; + static const char* Function; + static const char* Undefined; + static const char* String; + static const char* Number; + static const char* Boolean; + static const char* Symbol; + static const char* Accessor; + static const char* Bigint; + }; // TypeEnum + + String getType() { return m_type; } + void setType(const String& value) { m_type = value; } + + bool hasValue() { return m_value.isJust(); } + String getValue(const String& defaultValue) { return m_value.isJust() ? m_value.fromJust() : defaultValue; } + void setValue(const String& value) { m_value = value; } + + bool hasValuePreview() { return m_valuePreview.isJust(); } + protocol::Runtime::ObjectPreview* getValuePreview(protocol::Runtime::ObjectPreview* defaultValue) { return m_valuePreview.isJust() ? m_valuePreview.fromJust() : defaultValue; } + void setValuePreview(std::unique_ptr value) { m_valuePreview = std::move(value); } + + struct SubtypeEnum { + static const char* Array; + static const char* Null; + static const char* Node; + static const char* Regexp; + static const char* Date; + static const char* Map; + static const char* Set; + static const char* Weakmap; + static const char* Weakset; + static const char* Iterator; + static const char* Generator; + static const char* Error; + static const char* Proxy; + static const char* Promise; + static const char* Typedarray; + static const char* Arraybuffer; + static const char* Dataview; + static const char* Webassemblymemory; + static const char* Wasmvalue; + }; // SubtypeEnum + + bool hasSubtype() { return m_subtype.isJust(); } + String getSubtype(const String& defaultValue) { return m_subtype.isJust() ? m_subtype.fromJust() : defaultValue; } + void setSubtype(const String& value) { m_subtype = value; } + + template + class PropertyPreviewBuilder { + public: + enum { + NoFieldsSet = 0, + NameSet = 1 << 1, + TypeSet = 1 << 2, + AllFieldsSet = (NameSet | TypeSet | 0)}; + + + PropertyPreviewBuilder& setName(const String& value) + { + static_assert(!(STATE & NameSet), "property name should not be set yet"); + m_result->setName(value); + return castState(); + } + + PropertyPreviewBuilder& setType(const String& value) + { + static_assert(!(STATE & TypeSet), "property type should not be set yet"); + m_result->setType(value); + return castState(); + } + + PropertyPreviewBuilder& setValue(const String& value) + { + m_result->setValue(value); + return *this; + } + + PropertyPreviewBuilder& setValuePreview(std::unique_ptr value) + { + m_result->setValuePreview(std::move(value)); + return *this; + } + + PropertyPreviewBuilder& setSubtype(const String& value) + { + m_result->setSubtype(value); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class PropertyPreview; + PropertyPreviewBuilder() : m_result(new PropertyPreview()) { } + + template PropertyPreviewBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static PropertyPreviewBuilder<0> create() + { + return PropertyPreviewBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + PropertyPreview() + { + } + + String m_name; + String m_type; + Maybe m_value; + Maybe m_valuePreview; + Maybe m_subtype; +}; + + +class EntryPreview : public ::v8_crdtp::ProtocolObject { +public: + ~EntryPreview() override { } + + bool hasKey() { return m_key.isJust(); } + protocol::Runtime::ObjectPreview* getKey(protocol::Runtime::ObjectPreview* defaultValue) { return m_key.isJust() ? m_key.fromJust() : defaultValue; } + void setKey(std::unique_ptr value) { m_key = std::move(value); } + + protocol::Runtime::ObjectPreview* getValue() { return m_value.get(); } + void setValue(std::unique_ptr value) { m_value = std::move(value); } + + template + class EntryPreviewBuilder { + public: + enum { + NoFieldsSet = 0, + ValueSet = 1 << 1, + AllFieldsSet = (ValueSet | 0)}; + + + EntryPreviewBuilder& setKey(std::unique_ptr value) + { + m_result->setKey(std::move(value)); + return *this; + } + + EntryPreviewBuilder& setValue(std::unique_ptr value) + { + static_assert(!(STATE & ValueSet), "property value should not be set yet"); + m_result->setValue(std::move(value)); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class EntryPreview; + EntryPreviewBuilder() : m_result(new EntryPreview()) { } + + template EntryPreviewBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static EntryPreviewBuilder<0> create() + { + return EntryPreviewBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + EntryPreview() + { + } + + Maybe m_key; + std::unique_ptr m_value; +}; + + +class PropertyDescriptor : public ::v8_crdtp::ProtocolObject { +public: + ~PropertyDescriptor() override { } + + String getName() { return m_name; } + void setName(const String& value) { m_name = value; } + + bool hasValue() { return m_value.isJust(); } + protocol::Runtime::RemoteObject* getValue(protocol::Runtime::RemoteObject* defaultValue) { return m_value.isJust() ? m_value.fromJust() : defaultValue; } + void setValue(std::unique_ptr value) { m_value = std::move(value); } + + bool hasWritable() { return m_writable.isJust(); } + bool getWritable(bool defaultValue) { return m_writable.isJust() ? m_writable.fromJust() : defaultValue; } + void setWritable(bool value) { m_writable = value; } + + bool hasGet() { return m_get.isJust(); } + protocol::Runtime::RemoteObject* getGet(protocol::Runtime::RemoteObject* defaultValue) { return m_get.isJust() ? m_get.fromJust() : defaultValue; } + void setGet(std::unique_ptr value) { m_get = std::move(value); } + + bool hasSet() { return m_set.isJust(); } + protocol::Runtime::RemoteObject* getSet(protocol::Runtime::RemoteObject* defaultValue) { return m_set.isJust() ? m_set.fromJust() : defaultValue; } + void setSet(std::unique_ptr value) { m_set = std::move(value); } + + bool getConfigurable() { return m_configurable; } + void setConfigurable(bool value) { m_configurable = value; } + + bool getEnumerable() { return m_enumerable; } + void setEnumerable(bool value) { m_enumerable = value; } + + bool hasWasThrown() { return m_wasThrown.isJust(); } + bool getWasThrown(bool defaultValue) { return m_wasThrown.isJust() ? m_wasThrown.fromJust() : defaultValue; } + void setWasThrown(bool value) { m_wasThrown = value; } + + bool hasIsOwn() { return m_isOwn.isJust(); } + bool getIsOwn(bool defaultValue) { return m_isOwn.isJust() ? m_isOwn.fromJust() : defaultValue; } + void setIsOwn(bool value) { m_isOwn = value; } + + bool hasSymbol() { return m_symbol.isJust(); } + protocol::Runtime::RemoteObject* getSymbol(protocol::Runtime::RemoteObject* defaultValue) { return m_symbol.isJust() ? m_symbol.fromJust() : defaultValue; } + void setSymbol(std::unique_ptr value) { m_symbol = std::move(value); } + + template + class PropertyDescriptorBuilder { + public: + enum { + NoFieldsSet = 0, + NameSet = 1 << 1, + ConfigurableSet = 1 << 2, + EnumerableSet = 1 << 3, + AllFieldsSet = (NameSet | ConfigurableSet | EnumerableSet | 0)}; + + + PropertyDescriptorBuilder& setName(const String& value) + { + static_assert(!(STATE & NameSet), "property name should not be set yet"); + m_result->setName(value); + return castState(); + } + + PropertyDescriptorBuilder& setValue(std::unique_ptr value) + { + m_result->setValue(std::move(value)); + return *this; + } + + PropertyDescriptorBuilder& setWritable(bool value) + { + m_result->setWritable(value); + return *this; + } + + PropertyDescriptorBuilder& setGet(std::unique_ptr value) + { + m_result->setGet(std::move(value)); + return *this; + } + + PropertyDescriptorBuilder& setSet(std::unique_ptr value) + { + m_result->setSet(std::move(value)); + return *this; + } + + PropertyDescriptorBuilder& setConfigurable(bool value) + { + static_assert(!(STATE & ConfigurableSet), "property configurable should not be set yet"); + m_result->setConfigurable(value); + return castState(); + } + + PropertyDescriptorBuilder& setEnumerable(bool value) + { + static_assert(!(STATE & EnumerableSet), "property enumerable should not be set yet"); + m_result->setEnumerable(value); + return castState(); + } + + PropertyDescriptorBuilder& setWasThrown(bool value) + { + m_result->setWasThrown(value); + return *this; + } + + PropertyDescriptorBuilder& setIsOwn(bool value) + { + m_result->setIsOwn(value); + return *this; + } + + PropertyDescriptorBuilder& setSymbol(std::unique_ptr value) + { + m_result->setSymbol(std::move(value)); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class PropertyDescriptor; + PropertyDescriptorBuilder() : m_result(new PropertyDescriptor()) { } + + template PropertyDescriptorBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static PropertyDescriptorBuilder<0> create() + { + return PropertyDescriptorBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + PropertyDescriptor() + { + m_configurable = false; + m_enumerable = false; + } + + String m_name; + Maybe m_value; + Maybe m_writable; + Maybe m_get; + Maybe m_set; + bool m_configurable; + bool m_enumerable; + Maybe m_wasThrown; + Maybe m_isOwn; + Maybe m_symbol; +}; + + +class InternalPropertyDescriptor : public ::v8_crdtp::ProtocolObject { +public: + ~InternalPropertyDescriptor() override { } + + String getName() { return m_name; } + void setName(const String& value) { m_name = value; } + + bool hasValue() { return m_value.isJust(); } + protocol::Runtime::RemoteObject* getValue(protocol::Runtime::RemoteObject* defaultValue) { return m_value.isJust() ? m_value.fromJust() : defaultValue; } + void setValue(std::unique_ptr value) { m_value = std::move(value); } + + template + class InternalPropertyDescriptorBuilder { + public: + enum { + NoFieldsSet = 0, + NameSet = 1 << 1, + AllFieldsSet = (NameSet | 0)}; + + + InternalPropertyDescriptorBuilder& setName(const String& value) + { + static_assert(!(STATE & NameSet), "property name should not be set yet"); + m_result->setName(value); + return castState(); + } + + InternalPropertyDescriptorBuilder& setValue(std::unique_ptr value) + { + m_result->setValue(std::move(value)); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class InternalPropertyDescriptor; + InternalPropertyDescriptorBuilder() : m_result(new InternalPropertyDescriptor()) { } + + template InternalPropertyDescriptorBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static InternalPropertyDescriptorBuilder<0> create() + { + return InternalPropertyDescriptorBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + InternalPropertyDescriptor() + { + } + + String m_name; + Maybe m_value; +}; + + +class PrivatePropertyDescriptor : public ::v8_crdtp::ProtocolObject { +public: + ~PrivatePropertyDescriptor() override { } + + String getName() { return m_name; } + void setName(const String& value) { m_name = value; } + + bool hasValue() { return m_value.isJust(); } + protocol::Runtime::RemoteObject* getValue(protocol::Runtime::RemoteObject* defaultValue) { return m_value.isJust() ? m_value.fromJust() : defaultValue; } + void setValue(std::unique_ptr value) { m_value = std::move(value); } + + bool hasGet() { return m_get.isJust(); } + protocol::Runtime::RemoteObject* getGet(protocol::Runtime::RemoteObject* defaultValue) { return m_get.isJust() ? m_get.fromJust() : defaultValue; } + void setGet(std::unique_ptr value) { m_get = std::move(value); } + + bool hasSet() { return m_set.isJust(); } + protocol::Runtime::RemoteObject* getSet(protocol::Runtime::RemoteObject* defaultValue) { return m_set.isJust() ? m_set.fromJust() : defaultValue; } + void setSet(std::unique_ptr value) { m_set = std::move(value); } + + template + class PrivatePropertyDescriptorBuilder { + public: + enum { + NoFieldsSet = 0, + NameSet = 1 << 1, + AllFieldsSet = (NameSet | 0)}; + + + PrivatePropertyDescriptorBuilder& setName(const String& value) + { + static_assert(!(STATE & NameSet), "property name should not be set yet"); + m_result->setName(value); + return castState(); + } + + PrivatePropertyDescriptorBuilder& setValue(std::unique_ptr value) + { + m_result->setValue(std::move(value)); + return *this; + } + + PrivatePropertyDescriptorBuilder& setGet(std::unique_ptr value) + { + m_result->setGet(std::move(value)); + return *this; + } + + PrivatePropertyDescriptorBuilder& setSet(std::unique_ptr value) + { + m_result->setSet(std::move(value)); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class PrivatePropertyDescriptor; + PrivatePropertyDescriptorBuilder() : m_result(new PrivatePropertyDescriptor()) { } + + template PrivatePropertyDescriptorBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static PrivatePropertyDescriptorBuilder<0> create() + { + return PrivatePropertyDescriptorBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + PrivatePropertyDescriptor() + { + } + + String m_name; + Maybe m_value; + Maybe m_get; + Maybe m_set; +}; + + +class CallArgument : public ::v8_crdtp::ProtocolObject { +public: + ~CallArgument() override { } + + bool hasValue() { return m_value.isJust(); } + protocol::Value* getValue(protocol::Value* defaultValue) { return m_value.isJust() ? m_value.fromJust() : defaultValue; } + void setValue(std::unique_ptr value) { m_value = std::move(value); } + + bool hasUnserializableValue() { return m_unserializableValue.isJust(); } + String getUnserializableValue(const String& defaultValue) { return m_unserializableValue.isJust() ? m_unserializableValue.fromJust() : defaultValue; } + void setUnserializableValue(const String& value) { m_unserializableValue = value; } + + bool hasObjectId() { return m_objectId.isJust(); } + String getObjectId(const String& defaultValue) { return m_objectId.isJust() ? m_objectId.fromJust() : defaultValue; } + void setObjectId(const String& value) { m_objectId = value; } + + template + class CallArgumentBuilder { + public: + enum { + NoFieldsSet = 0, + AllFieldsSet = (0)}; + + + CallArgumentBuilder& setValue(std::unique_ptr value) + { + m_result->setValue(std::move(value)); + return *this; + } + + CallArgumentBuilder& setUnserializableValue(const String& value) + { + m_result->setUnserializableValue(value); + return *this; + } + + CallArgumentBuilder& setObjectId(const String& value) + { + m_result->setObjectId(value); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class CallArgument; + CallArgumentBuilder() : m_result(new CallArgument()) { } + + template CallArgumentBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static CallArgumentBuilder<0> create() + { + return CallArgumentBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + CallArgument() + { + } + + Maybe m_value; + Maybe m_unserializableValue; + Maybe m_objectId; +}; + + +class ExecutionContextDescription : public ::v8_crdtp::ProtocolObject { +public: + ~ExecutionContextDescription() override { } + + int getId() { return m_id; } + void setId(int value) { m_id = value; } + + String getOrigin() { return m_origin; } + void setOrigin(const String& value) { m_origin = value; } + + String getName() { return m_name; } + void setName(const String& value) { m_name = value; } + + String getUniqueId() { return m_uniqueId; } + void setUniqueId(const String& value) { m_uniqueId = value; } + + bool hasAuxData() { return m_auxData.isJust(); } + protocol::DictionaryValue* getAuxData(protocol::DictionaryValue* defaultValue) { return m_auxData.isJust() ? m_auxData.fromJust() : defaultValue; } + void setAuxData(std::unique_ptr value) { m_auxData = std::move(value); } + + template + class ExecutionContextDescriptionBuilder { + public: + enum { + NoFieldsSet = 0, + IdSet = 1 << 1, + OriginSet = 1 << 2, + NameSet = 1 << 3, + UniqueIdSet = 1 << 4, + AllFieldsSet = (IdSet | OriginSet | NameSet | UniqueIdSet | 0)}; + + + ExecutionContextDescriptionBuilder& setId(int value) + { + static_assert(!(STATE & IdSet), "property id should not be set yet"); + m_result->setId(value); + return castState(); + } + + ExecutionContextDescriptionBuilder& setOrigin(const String& value) + { + static_assert(!(STATE & OriginSet), "property origin should not be set yet"); + m_result->setOrigin(value); + return castState(); + } + + ExecutionContextDescriptionBuilder& setName(const String& value) + { + static_assert(!(STATE & NameSet), "property name should not be set yet"); + m_result->setName(value); + return castState(); + } + + ExecutionContextDescriptionBuilder& setUniqueId(const String& value) + { + static_assert(!(STATE & UniqueIdSet), "property uniqueId should not be set yet"); + m_result->setUniqueId(value); + return castState(); + } + + ExecutionContextDescriptionBuilder& setAuxData(std::unique_ptr value) + { + m_result->setAuxData(std::move(value)); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class ExecutionContextDescription; + ExecutionContextDescriptionBuilder() : m_result(new ExecutionContextDescription()) { } + + template ExecutionContextDescriptionBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static ExecutionContextDescriptionBuilder<0> create() + { + return ExecutionContextDescriptionBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + ExecutionContextDescription() + { + m_id = 0; + } + + int m_id; + String m_origin; + String m_name; + String m_uniqueId; + Maybe m_auxData; +}; + + +class ExceptionDetails : public ::v8_crdtp::ProtocolObject { +public: + ~ExceptionDetails() override { } + + int getExceptionId() { return m_exceptionId; } + void setExceptionId(int value) { m_exceptionId = value; } + + String getText() { return m_text; } + void setText(const String& value) { m_text = value; } + + int getLineNumber() { return m_lineNumber; } + void setLineNumber(int value) { m_lineNumber = value; } + + int getColumnNumber() { return m_columnNumber; } + void setColumnNumber(int value) { m_columnNumber = value; } + + bool hasScriptId() { return m_scriptId.isJust(); } + String getScriptId(const String& defaultValue) { return m_scriptId.isJust() ? m_scriptId.fromJust() : defaultValue; } + void setScriptId(const String& value) { m_scriptId = value; } + + bool hasUrl() { return m_url.isJust(); } + String getUrl(const String& defaultValue) { return m_url.isJust() ? m_url.fromJust() : defaultValue; } + void setUrl(const String& value) { m_url = value; } + + bool hasStackTrace() { return m_stackTrace.isJust(); } + protocol::Runtime::StackTrace* getStackTrace(protocol::Runtime::StackTrace* defaultValue) { return m_stackTrace.isJust() ? m_stackTrace.fromJust() : defaultValue; } + void setStackTrace(std::unique_ptr value) { m_stackTrace = std::move(value); } + + bool hasException() { return m_exception.isJust(); } + protocol::Runtime::RemoteObject* getException(protocol::Runtime::RemoteObject* defaultValue) { return m_exception.isJust() ? m_exception.fromJust() : defaultValue; } + void setException(std::unique_ptr value) { m_exception = std::move(value); } + + bool hasExecutionContextId() { return m_executionContextId.isJust(); } + int getExecutionContextId(int defaultValue) { return m_executionContextId.isJust() ? m_executionContextId.fromJust() : defaultValue; } + void setExecutionContextId(int value) { m_executionContextId = value; } + + bool hasExceptionMetaData() { return m_exceptionMetaData.isJust(); } + protocol::DictionaryValue* getExceptionMetaData(protocol::DictionaryValue* defaultValue) { return m_exceptionMetaData.isJust() ? m_exceptionMetaData.fromJust() : defaultValue; } + void setExceptionMetaData(std::unique_ptr value) { m_exceptionMetaData = std::move(value); } + + template + class ExceptionDetailsBuilder { + public: + enum { + NoFieldsSet = 0, + ExceptionIdSet = 1 << 1, + TextSet = 1 << 2, + LineNumberSet = 1 << 3, + ColumnNumberSet = 1 << 4, + AllFieldsSet = (ExceptionIdSet | TextSet | LineNumberSet | ColumnNumberSet | 0)}; + + + ExceptionDetailsBuilder& setExceptionId(int value) + { + static_assert(!(STATE & ExceptionIdSet), "property exceptionId should not be set yet"); + m_result->setExceptionId(value); + return castState(); + } + + ExceptionDetailsBuilder& setText(const String& value) + { + static_assert(!(STATE & TextSet), "property text should not be set yet"); + m_result->setText(value); + return castState(); + } + + ExceptionDetailsBuilder& setLineNumber(int value) + { + static_assert(!(STATE & LineNumberSet), "property lineNumber should not be set yet"); + m_result->setLineNumber(value); + return castState(); + } + + ExceptionDetailsBuilder& setColumnNumber(int value) + { + static_assert(!(STATE & ColumnNumberSet), "property columnNumber should not be set yet"); + m_result->setColumnNumber(value); + return castState(); + } + + ExceptionDetailsBuilder& setScriptId(const String& value) + { + m_result->setScriptId(value); + return *this; + } + + ExceptionDetailsBuilder& setUrl(const String& value) + { + m_result->setUrl(value); + return *this; + } + + ExceptionDetailsBuilder& setStackTrace(std::unique_ptr value) + { + m_result->setStackTrace(std::move(value)); + return *this; + } + + ExceptionDetailsBuilder& setException(std::unique_ptr value) + { + m_result->setException(std::move(value)); + return *this; + } + + ExceptionDetailsBuilder& setExecutionContextId(int value) + { + m_result->setExecutionContextId(value); + return *this; + } + + ExceptionDetailsBuilder& setExceptionMetaData(std::unique_ptr value) + { + m_result->setExceptionMetaData(std::move(value)); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class ExceptionDetails; + ExceptionDetailsBuilder() : m_result(new ExceptionDetails()) { } + + template ExceptionDetailsBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static ExceptionDetailsBuilder<0> create() + { + return ExceptionDetailsBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + ExceptionDetails() + { + m_exceptionId = 0; + m_lineNumber = 0; + m_columnNumber = 0; + } + + int m_exceptionId; + String m_text; + int m_lineNumber; + int m_columnNumber; + Maybe m_scriptId; + Maybe m_url; + Maybe m_stackTrace; + Maybe m_exception; + Maybe m_executionContextId; + Maybe m_exceptionMetaData; +}; + + +class CallFrame : public ::v8_crdtp::ProtocolObject { +public: + ~CallFrame() override { } + + String getFunctionName() { return m_functionName; } + void setFunctionName(const String& value) { m_functionName = value; } + + String getScriptId() { return m_scriptId; } + void setScriptId(const String& value) { m_scriptId = value; } + + String getUrl() { return m_url; } + void setUrl(const String& value) { m_url = value; } + + int getLineNumber() { return m_lineNumber; } + void setLineNumber(int value) { m_lineNumber = value; } + + int getColumnNumber() { return m_columnNumber; } + void setColumnNumber(int value) { m_columnNumber = value; } + + template + class CallFrameBuilder { + public: + enum { + NoFieldsSet = 0, + FunctionNameSet = 1 << 1, + ScriptIdSet = 1 << 2, + UrlSet = 1 << 3, + LineNumberSet = 1 << 4, + ColumnNumberSet = 1 << 5, + AllFieldsSet = (FunctionNameSet | ScriptIdSet | UrlSet | LineNumberSet | ColumnNumberSet | 0)}; + + + CallFrameBuilder& setFunctionName(const String& value) + { + static_assert(!(STATE & FunctionNameSet), "property functionName should not be set yet"); + m_result->setFunctionName(value); + return castState(); + } + + CallFrameBuilder& setScriptId(const String& value) + { + static_assert(!(STATE & ScriptIdSet), "property scriptId should not be set yet"); + m_result->setScriptId(value); + return castState(); + } + + CallFrameBuilder& setUrl(const String& value) + { + static_assert(!(STATE & UrlSet), "property url should not be set yet"); + m_result->setUrl(value); + return castState(); + } + + CallFrameBuilder& setLineNumber(int value) + { + static_assert(!(STATE & LineNumberSet), "property lineNumber should not be set yet"); + m_result->setLineNumber(value); + return castState(); + } + + CallFrameBuilder& setColumnNumber(int value) + { + static_assert(!(STATE & ColumnNumberSet), "property columnNumber should not be set yet"); + m_result->setColumnNumber(value); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class CallFrame; + CallFrameBuilder() : m_result(new CallFrame()) { } + + template CallFrameBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static CallFrameBuilder<0> create() + { + return CallFrameBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + CallFrame() + { + m_lineNumber = 0; + m_columnNumber = 0; + } + + String m_functionName; + String m_scriptId; + String m_url; + int m_lineNumber; + int m_columnNumber; +}; + + +class StackTrace : public ::v8_crdtp::ProtocolObject, + public API::StackTrace { +public: + ~StackTrace() override { } + + bool hasDescription() { return m_description.isJust(); } + String getDescription(const String& defaultValue) { return m_description.isJust() ? m_description.fromJust() : defaultValue; } + void setDescription(const String& value) { m_description = value; } + + protocol::Array* getCallFrames() { return m_callFrames.get(); } + void setCallFrames(std::unique_ptr> value) { m_callFrames = std::move(value); } + + bool hasParent() { return m_parent.isJust(); } + protocol::Runtime::StackTrace* getParent(protocol::Runtime::StackTrace* defaultValue) { return m_parent.isJust() ? m_parent.fromJust() : defaultValue; } + void setParent(std::unique_ptr value) { m_parent = std::move(value); } + + bool hasParentId() { return m_parentId.isJust(); } + protocol::Runtime::StackTraceId* getParentId(protocol::Runtime::StackTraceId* defaultValue) { return m_parentId.isJust() ? m_parentId.fromJust() : defaultValue; } + void setParentId(std::unique_ptr value) { m_parentId = std::move(value); } + + template + class StackTraceBuilder { + public: + enum { + NoFieldsSet = 0, + CallFramesSet = 1 << 1, + AllFieldsSet = (CallFramesSet | 0)}; + + + StackTraceBuilder& setDescription(const String& value) + { + m_result->setDescription(value); + return *this; + } + + StackTraceBuilder& setCallFrames(std::unique_ptr> value) + { + static_assert(!(STATE & CallFramesSet), "property callFrames should not be set yet"); + m_result->setCallFrames(std::move(value)); + return castState(); + } + + StackTraceBuilder& setParent(std::unique_ptr value) + { + m_result->setParent(std::move(value)); + return *this; + } + + StackTraceBuilder& setParentId(std::unique_ptr value) + { + m_result->setParentId(std::move(value)); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class StackTrace; + StackTraceBuilder() : m_result(new StackTrace()) { } + + template StackTraceBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static StackTraceBuilder<0> create() + { + return StackTraceBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + StackTrace() + { + } + + Maybe m_description; + std::unique_ptr> m_callFrames; + Maybe m_parent; + Maybe m_parentId; +}; + + +class StackTraceId : public ::v8_crdtp::ProtocolObject, + public API::StackTraceId { +public: + ~StackTraceId() override { } + + String getId() { return m_id; } + void setId(const String& value) { m_id = value; } + + bool hasDebuggerId() { return m_debuggerId.isJust(); } + String getDebuggerId(const String& defaultValue) { return m_debuggerId.isJust() ? m_debuggerId.fromJust() : defaultValue; } + void setDebuggerId(const String& value) { m_debuggerId = value; } + + template + class StackTraceIdBuilder { + public: + enum { + NoFieldsSet = 0, + IdSet = 1 << 1, + AllFieldsSet = (IdSet | 0)}; + + + StackTraceIdBuilder& setId(const String& value) + { + static_assert(!(STATE & IdSet), "property id should not be set yet"); + m_result->setId(value); + return castState(); + } + + StackTraceIdBuilder& setDebuggerId(const String& value) + { + m_result->setDebuggerId(value); + return *this; + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class StackTraceId; + StackTraceIdBuilder() : m_result(new StackTraceId()) { } + + template StackTraceIdBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static StackTraceIdBuilder<0> create() + { + return StackTraceIdBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + StackTraceId() + { + } + + String m_id; + Maybe m_debuggerId; +}; + + +// ------------- Backend interface. + +class Backend { +public: + virtual ~Backend() { } + + class AwaitPromiseCallback { + public: + virtual void sendSuccess(std::unique_ptr result, Maybe exceptionDetails) = 0; + virtual void sendFailure(const DispatchResponse&) = 0; + virtual void fallThrough() = 0; + virtual ~AwaitPromiseCallback() { } + }; + virtual void awaitPromise(const String& in_promiseObjectId, Maybe in_returnByValue, Maybe in_generatePreview, std::unique_ptr callback) = 0; + class CallFunctionOnCallback { + public: + virtual void sendSuccess(std::unique_ptr result, Maybe exceptionDetails) = 0; + virtual void sendFailure(const DispatchResponse&) = 0; + virtual void fallThrough() = 0; + virtual ~CallFunctionOnCallback() { } + }; + virtual void callFunctionOn(const String& in_functionDeclaration, Maybe in_objectId, Maybe> in_arguments, Maybe in_silent, Maybe in_returnByValue, Maybe in_generatePreview, Maybe in_userGesture, Maybe in_awaitPromise, Maybe in_executionContextId, Maybe in_objectGroup, Maybe in_throwOnSideEffect, Maybe in_uniqueContextId, Maybe in_generateWebDriverValue, std::unique_ptr callback) = 0; + virtual DispatchResponse compileScript(const String& in_expression, const String& in_sourceURL, bool in_persistScript, Maybe in_executionContextId, Maybe* out_scriptId, Maybe* out_exceptionDetails) = 0; + virtual DispatchResponse disable() = 0; + virtual DispatchResponse discardConsoleEntries() = 0; + virtual DispatchResponse enable() = 0; + class EvaluateCallback { + public: + virtual void sendSuccess(std::unique_ptr result, Maybe exceptionDetails) = 0; + virtual void sendFailure(const DispatchResponse&) = 0; + virtual void fallThrough() = 0; + virtual ~EvaluateCallback() { } + }; + virtual void evaluate(const String& in_expression, Maybe in_objectGroup, Maybe in_includeCommandLineAPI, Maybe in_silent, Maybe in_contextId, Maybe in_returnByValue, Maybe in_generatePreview, Maybe in_userGesture, Maybe in_awaitPromise, Maybe in_throwOnSideEffect, Maybe in_timeout, Maybe in_disableBreaks, Maybe in_replMode, Maybe in_allowUnsafeEvalBlockedByCSP, Maybe in_uniqueContextId, Maybe in_generateWebDriverValue, std::unique_ptr callback) = 0; + virtual DispatchResponse getIsolateId(String* out_id) = 0; + virtual DispatchResponse getHeapUsage(double* out_usedSize, double* out_totalSize) = 0; + virtual DispatchResponse getProperties(const String& in_objectId, Maybe in_ownProperties, Maybe in_accessorPropertiesOnly, Maybe in_generatePreview, Maybe in_nonIndexedPropertiesOnly, std::unique_ptr>* out_result, Maybe>* out_internalProperties, Maybe>* out_privateProperties, Maybe* out_exceptionDetails) = 0; + virtual DispatchResponse globalLexicalScopeNames(Maybe in_executionContextId, std::unique_ptr>* out_names) = 0; + virtual DispatchResponse queryObjects(const String& in_prototypeObjectId, Maybe in_objectGroup, std::unique_ptr* out_objects) = 0; + virtual DispatchResponse releaseObject(const String& in_objectId) = 0; + virtual DispatchResponse releaseObjectGroup(const String& in_objectGroup) = 0; + virtual DispatchResponse runIfWaitingForDebugger() = 0; + class RunScriptCallback { + public: + virtual void sendSuccess(std::unique_ptr result, Maybe exceptionDetails) = 0; + virtual void sendFailure(const DispatchResponse&) = 0; + virtual void fallThrough() = 0; + virtual ~RunScriptCallback() { } + }; + virtual void runScript(const String& in_scriptId, Maybe in_executionContextId, Maybe in_objectGroup, Maybe in_silent, Maybe in_includeCommandLineAPI, Maybe in_returnByValue, Maybe in_generatePreview, Maybe in_awaitPromise, std::unique_ptr callback) = 0; + virtual DispatchResponse setCustomObjectFormatterEnabled(bool in_enabled) = 0; + virtual DispatchResponse setMaxCallStackSizeToCapture(int in_size) = 0; + class TerminateExecutionCallback { + public: + virtual void sendSuccess() = 0; + virtual void sendFailure(const DispatchResponse&) = 0; + virtual void fallThrough() = 0; + virtual ~TerminateExecutionCallback() { } + }; + virtual void terminateExecution(std::unique_ptr callback) = 0; + virtual DispatchResponse addBinding(const String& in_name, Maybe in_executionContextId, Maybe in_executionContextName) = 0; + virtual DispatchResponse removeBinding(const String& in_name) = 0; + virtual DispatchResponse getExceptionDetails(const String& in_errorObjectId, Maybe* out_exceptionDetails) = 0; + +}; + +// ------------- Frontend interface. + +class Frontend { +public: + explicit Frontend(FrontendChannel* frontend_channel) : frontend_channel_(frontend_channel) {} + void bindingCalled(const String& name, const String& payload, int executionContextId); + void consoleAPICalled(const String& type, std::unique_ptr> args, int executionContextId, double timestamp, Maybe stackTrace = Maybe(), Maybe context = Maybe()); + void exceptionRevoked(const String& reason, int exceptionId); + void exceptionThrown(double timestamp, std::unique_ptr exceptionDetails); + void executionContextCreated(std::unique_ptr context); + void executionContextDestroyed(int executionContextId, const String& executionContextUniqueId); + void executionContextsCleared(); + void inspectRequested(std::unique_ptr object, std::unique_ptr hints, Maybe executionContextId = Maybe()); + + void flush(); + void sendRawNotification(std::unique_ptr); + private: + FrontendChannel* frontend_channel_; +}; + +// ------------- Dispatcher. + +class Dispatcher { +public: + static void wire(UberDispatcher*, Backend*); + +private: + Dispatcher() { } +}; + +// ------------- Metainfo. + +class Metainfo { +public: + using BackendClass = Backend; + using FrontendClass = Frontend; + using DispatcherClass = Dispatcher; + static const char domainName[]; + static const char commandPrefix[]; + static const char version[]; +}; + +} // namespace Runtime +} // namespace v8_inspector +} // namespace protocol + +#endif // !defined(v8_inspector_protocol_Runtime_h) diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Schema.cpp b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Schema.cpp new file mode 100644 index 000000000..79f559622 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Schema.cpp @@ -0,0 +1,159 @@ +// This file is generated by TypeBuilder_cpp.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/inspector/protocol/Schema.h" + +#include "src/inspector/protocol/Protocol.h" + +#include "third_party/inspector_protocol/crdtp/cbor.h" +#include "third_party/inspector_protocol/crdtp/find_by_first.h" +#include "third_party/inspector_protocol/crdtp/span.h" + +namespace v8_inspector { +namespace protocol { +namespace Schema { + +using v8_crdtp::DeserializerState; +using v8_crdtp::ProtocolTypeTraits; + +// ------------- Enum values from types. + +const char Metainfo::domainName[] = "Schema"; +const char Metainfo::commandPrefix[] = "Schema."; +const char Metainfo::version[] = "1.3"; + +V8_CRDTP_BEGIN_DESERIALIZER(Domain) + V8_CRDTP_DESERIALIZE_FIELD("name", m_name), + V8_CRDTP_DESERIALIZE_FIELD("version", m_version), +V8_CRDTP_END_DESERIALIZER() + +V8_CRDTP_BEGIN_SERIALIZER(Domain) + V8_CRDTP_SERIALIZE_FIELD("name", m_name); + V8_CRDTP_SERIALIZE_FIELD("version", m_version); +V8_CRDTP_END_SERIALIZER(); + +// static +std::unique_ptr API::Domain::fromBinary(const uint8_t* data, size_t length) +{ + return protocol::Schema::Domain::FromBinary(data, length); +} + +// ------------- Enum values from params. + + +// ------------- Frontend notifications. + +void Frontend::flush() +{ + frontend_channel_->FlushProtocolNotifications(); +} + +void Frontend::sendRawNotification(std::unique_ptr notification) +{ + frontend_channel_->SendProtocolNotification(std::move(notification)); +} + +// --------------------- Dispatcher. + +class DomainDispatcherImpl : public protocol::DomainDispatcher { +public: + DomainDispatcherImpl(FrontendChannel* frontendChannel, Backend* backend) + : DomainDispatcher(frontendChannel) + , m_backend(backend) {} + ~DomainDispatcherImpl() override { } + + using CallHandler = void (DomainDispatcherImpl::*)(const v8_crdtp::Dispatchable& dispatchable); + + std::function Dispatch(v8_crdtp::span command_name) override; + + void getDomains(const v8_crdtp::Dispatchable& dispatchable); + protected: + Backend* m_backend; +}; + +namespace { +// This helper method with a static map of command methods (instance methods +// of DomainDispatcherImpl declared just above) by their name is used immediately below, +// in the DomainDispatcherImpl::Dispatch method. +DomainDispatcherImpl::CallHandler CommandByName(v8_crdtp::span command_name) { + static auto* commands = [](){ + auto* commands = new std::vector, + DomainDispatcherImpl::CallHandler>>{ + { + v8_crdtp::SpanFrom("getDomains"), + &DomainDispatcherImpl::getDomains + }, + }; + return commands; + }(); + return v8_crdtp::FindByFirst(*commands, command_name, nullptr); +} +} // namespace + +std::function DomainDispatcherImpl::Dispatch(v8_crdtp::span command_name) { + CallHandler handler = CommandByName(command_name); + if (!handler) return nullptr; + + return [this, handler](const v8_crdtp::Dispatchable& dispatchable) { + (this->*handler)(dispatchable); + }; +} + + +namespace { + + +} // namespace + +void DomainDispatcherImpl::getDomains(const v8_crdtp::Dispatchable& dispatchable) +{ + // Prepare input parameters. + // Declare output parameters. + std::unique_ptr> out_domains; + + std::unique_ptr weak = weakPtr(); + DispatchResponse response = m_backend->getDomains(&out_domains); + if (response.IsFallThrough()) { + channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("Schema.getDomains"), dispatchable.Serialized()); + return; + } + if (weak->get()) { + std::unique_ptr result; + if (response.IsSuccess()) { + v8_crdtp::ObjectSerializer serializer; + serializer.AddField(v8_crdtp::MakeSpan("domains"), out_domains); + result = serializer.Finish(); + } else { + result = Serializable::From({}); + } + weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result)); + } + return; +} + +namespace { +// This helper method (with a static map of redirects) is used from Dispatcher::wire +// immediately below. +const std::vector, v8_crdtp::span>>& SortedRedirects() { + static auto* redirects = [](){ + auto* redirects = new std::vector, v8_crdtp::span>>{ + }; + return redirects; + }(); + return *redirects; +} +} // namespace + +// static +void Dispatcher::wire(UberDispatcher* uber, Backend* backend) +{ + auto dispatcher = std::make_unique(uber->channel(), backend); + uber->WireBackend(v8_crdtp::SpanFrom("Schema"), SortedRedirects(), std::move(dispatcher)); +} + +} // Schema +} // namespace v8_inspector +} // namespace protocol diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Schema.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Schema.h new file mode 100644 index 000000000..409d49483 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/protocol/Schema.h @@ -0,0 +1,146 @@ +// This file is generated by TypeBuilder_h.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef v8_inspector_protocol_Schema_h +#define v8_inspector_protocol_Schema_h + +#include "src/inspector/protocol/Protocol.h" +// For each imported domain we generate a ValueConversions struct instead of a full domain definition +// and include Domain::API version from there. +#include "include/inspector/Schema.h" + +namespace v8_inspector { +namespace protocol { +namespace Schema { +class Domain; + +// ------------- Forward and enum declarations. + +// ------------- Type and builder declarations. + +class Domain : public ::v8_crdtp::ProtocolObject, + public API::Domain { +public: + ~Domain() override { } + + String getName() { return m_name; } + void setName(const String& value) { m_name = value; } + + String getVersion() { return m_version; } + void setVersion(const String& value) { m_version = value; } + + template + class DomainBuilder { + public: + enum { + NoFieldsSet = 0, + NameSet = 1 << 1, + VersionSet = 1 << 2, + AllFieldsSet = (NameSet | VersionSet | 0)}; + + + DomainBuilder& setName(const String& value) + { + static_assert(!(STATE & NameSet), "property name should not be set yet"); + m_result->setName(value); + return castState(); + } + + DomainBuilder& setVersion(const String& value) + { + static_assert(!(STATE & VersionSet), "property version should not be set yet"); + m_result->setVersion(value); + return castState(); + } + + std::unique_ptr build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class Domain; + DomainBuilder() : m_result(new Domain()) { } + + template DomainBuilder& castState() + { + return *reinterpret_cast*>(this); + } + + std::unique_ptr m_result; + }; + + static DomainBuilder<0> create() + { + return DomainBuilder<0>(); + } + +private: + DECLARE_SERIALIZATION_SUPPORT(); + + Domain() + { + } + + String m_name; + String m_version; +}; + + +// ------------- Backend interface. + +class Backend { +public: + virtual ~Backend() { } + + virtual DispatchResponse getDomains(std::unique_ptr>* out_domains) = 0; + + virtual DispatchResponse disable() + { + return DispatchResponse::Success(); + } +}; + +// ------------- Frontend interface. + +class Frontend { +public: + explicit Frontend(FrontendChannel* frontend_channel) : frontend_channel_(frontend_channel) {} + + void flush(); + void sendRawNotification(std::unique_ptr); + private: + FrontendChannel* frontend_channel_; +}; + +// ------------- Dispatcher. + +class Dispatcher { +public: + static void wire(UberDispatcher*, Backend*); + +private: + Dispatcher() { } +}; + +// ------------- Metainfo. + +class Metainfo { +public: + using BackendClass = Backend; + using FrontendClass = Frontend; + using DispatcherClass = Dispatcher; + static const char domainName[]; + static const char commandPrefix[]; + static const char version[]; +}; + +} // namespace Schema +} // namespace v8_inspector +} // namespace protocol + +#endif // !defined(v8_inspector_protocol_Schema_h) diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/remote-object-id.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/remote-object-id.h new file mode 100644 index 000000000..1c6012412 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/remote-object-id.h @@ -0,0 +1,54 @@ +// Copyright 2015 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_INSPECTOR_REMOTE_OBJECT_ID_H_ +#define V8_INSPECTOR_REMOTE_OBJECT_ID_H_ + +#include + +#include "src/inspector/protocol/Forward.h" + +namespace v8_inspector { + +using protocol::Response; + +class RemoteObjectIdBase { + public: + uint64_t isolateId() const { return m_isolateId; } + int contextId() const { return m_injectedScriptId; } + + protected: + RemoteObjectIdBase(); + ~RemoteObjectIdBase() = default; + + bool parseId(const String16&); + + uint64_t m_isolateId; + int m_injectedScriptId; + int m_id; +}; + +class RemoteObjectId final : public RemoteObjectIdBase { + public: + static Response parse(const String16&, std::unique_ptr*); + ~RemoteObjectId() = default; + int id() const { return m_id; } + + static String16 serialize(uint64_t isolateId, int injectedScriptId, int id); +}; + +class RemoteCallFrameId final : public RemoteObjectIdBase { + public: + static Response parse(const String16&, std::unique_ptr*); + ~RemoteCallFrameId() = default; + + int frameOrdinal() const { return m_id; } + + static String16 serialize(uint64_t isolateId, int injectedScriptId, + int frameOrdinal); +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_REMOTE_OBJECT_ID_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/search-util.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/search-util.h new file mode 100644 index 000000000..11d511a30 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/search-util.h @@ -0,0 +1,26 @@ +// 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_INSPECTOR_SEARCH_UTIL_H_ +#define V8_INSPECTOR_SEARCH_UTIL_H_ + +#include + +#include "src/inspector/protocol/Debugger.h" +#include "src/inspector/string-util.h" + +namespace v8_inspector { + +class V8InspectorSession; + +String16 findSourceURL(const String16& content, bool multiline); +String16 findSourceMapURL(const String16& content, bool multiline); +std::vector> +searchInTextByLinesImpl(V8InspectorSession*, const String16& text, + const String16& query, bool caseSensitive, + bool isRegex); + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_SEARCH_UTIL_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/string-16.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/string-16.h new file mode 100644 index 000000000..29651ac95 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/string-16.h @@ -0,0 +1,172 @@ +// 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_INSPECTOR_STRING_16_H_ +#define V8_INSPECTOR_STRING_16_H_ + +#include + +#include +#include +#include +#include +#include + +#include "src/base/compiler-specific.h" + +namespace v8_inspector { + +using UChar = uint16_t; + +class String16 { + public: + static const size_t kNotFound = static_cast(-1); + + String16() = default; + String16(const String16&) V8_NOEXCEPT = default; + String16(String16&&) V8_NOEXCEPT = default; + String16(const UChar* characters, size_t size); + V8_EXPORT String16(const UChar* characters); + V8_EXPORT String16(const char* characters); + String16(const char* characters, size_t size); + explicit String16(const std::basic_string& impl); + explicit String16(std::basic_string&& impl); + + String16& operator=(const String16&) V8_NOEXCEPT = default; + String16& operator=(String16&&) V8_NOEXCEPT = default; + + static String16 fromInteger(int); + static String16 fromInteger(size_t); + static String16 fromInteger64(int64_t); + static String16 fromUInt64(uint64_t); + static String16 fromDouble(double); + static String16 fromDouble(double, int precision); + + int64_t toInteger64(bool* ok = nullptr) const; + uint64_t toUInt64(bool* ok = nullptr) const; + int toInteger(bool* ok = nullptr) const; + std::pair getTrimmedOffsetAndLength() const; + String16 stripWhiteSpace() const; + const UChar* characters16() const { return m_impl.c_str(); } + size_t length() const { return m_impl.length(); } + bool isEmpty() const { return !m_impl.length(); } + UChar operator[](size_t index) const { return m_impl[index]; } + String16 substring(size_t pos, size_t len = UINT_MAX) const { + return String16(m_impl.substr(pos, len)); + } + size_t find(const String16& str, size_t start = 0) const { + return m_impl.find(str.m_impl, start); + } + size_t reverseFind(const String16& str, size_t start = UINT_MAX) const { + return m_impl.rfind(str.m_impl, start); + } + size_t find(UChar c, size_t start = 0) const { return m_impl.find(c, start); } + size_t reverseFind(UChar c, size_t start = UINT_MAX) const { + return m_impl.rfind(c, start); + } + void swap(String16& other) { + m_impl.swap(other.m_impl); + std::swap(hash_code, other.hash_code); + } + + // Convenience methods. + V8_EXPORT std::string utf8() const; + V8_EXPORT static String16 fromUTF8(const char* stringStart, size_t length); + + // Instantiates a String16 in native endianness from UTF16 LE. + // On Big endian architectures, byte order needs to be flipped. + V8_EXPORT static String16 fromUTF16LE(const UChar* stringStart, + size_t length); + + std::size_t hash() const { + if (!hash_code) { + for (char c : m_impl) hash_code = 31 * hash_code + c; + // Map hash code 0 to 1. This double the number of hash collisions for 1, + // but avoids recomputing the hash code. + if (!hash_code) ++hash_code; + } + return hash_code; + } + + inline bool operator==(const String16& other) const { + return m_impl == other.m_impl; + } + inline bool operator<(const String16& other) const { + return m_impl < other.m_impl; + } + inline bool operator!=(const String16& other) const { + return m_impl != other.m_impl; + } + inline String16 operator+(const String16& other) const { + return String16(m_impl + other.m_impl); + } + inline String16& operator+=(const String16& other) { + m_impl += other.m_impl; + return *this; + } + + // Defined later, since it uses the String16Builder. + template + static String16 concat(T... args); + + private: + std::basic_string m_impl; + mutable std::size_t hash_code = 0; +}; + +inline String16 operator+(const char* a, const String16& b) { + return String16(a) + b; +} + +class String16Builder { + public: + String16Builder(); + void append(const String16&); + void append(UChar); + void append(char); + void append(const UChar*, size_t); + void append(const char*, size_t); + void appendNumber(int); + void appendNumber(size_t); + void appendUnsignedAsHex(uint64_t); + void appendUnsignedAsHex(uint32_t); + void appendUnsignedAsHex(uint8_t); + String16 toString(); + void reserveCapacity(size_t); + + template + void appendAll(T first, R... rest) { + append(first); + appendAll(rest...); + } + void appendAll() {} + + private: + std::vector m_buffer; +}; + +template +String16 String16::concat(T... args) { + String16Builder builder; + builder.appendAll(args...); + return builder.toString(); +} + +} // namespace v8_inspector + +#if !defined(__APPLE__) || defined(_LIBCPP_VERSION) + +namespace std { +template <> +struct hash { + std::size_t operator()(const v8_inspector::String16& string) const { + return string.hash(); + } +}; + +} // namespace std + +#endif // !defined(__APPLE__) || defined(_LIBCPP_VERSION) + +#endif // V8_INSPECTOR_STRING_16_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/string-util.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/string-util.h new file mode 100644 index 000000000..7de6206ab --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/string-util.h @@ -0,0 +1,126 @@ +// 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_INSPECTOR_STRING_UTIL_H_ +#define V8_INSPECTOR_STRING_UTIL_H_ + +#include + +#include + +#include "../../third_party/inspector_protocol/crdtp/protocol_core.h" +#include "include/v8-inspector.h" +#include "src/base/logging.h" +#include "src/base/macros.h" +#include "src/inspector/string-16.h" + +namespace v8_inspector { + +namespace protocol { + +class Value; + +using String = v8_inspector::String16; + +class StringUtil { + public: + static String fromUTF8(const uint8_t* data, size_t length) { + return String16::fromUTF8(reinterpret_cast(data), length); + } + + static String fromUTF16LE(const uint16_t* data, size_t length) { + return String16::fromUTF16LE(data, length); + } + + static const uint8_t* CharactersLatin1(const String& s) { return nullptr; } + static const uint8_t* CharactersUTF8(const String& s) { return nullptr; } + static const uint16_t* CharactersUTF16(const String& s) { + return s.characters16(); + } + static size_t CharacterCount(const String& s) { return s.length(); } +}; + +// A read-only sequence of uninterpreted bytes with reference-counted storage. +class V8_EXPORT Binary { + public: + Binary() = default; + + const uint8_t* data() const { return bytes_->data(); } + size_t size() const { return bytes_->size(); } + String toBase64() const; + static Binary fromBase64(const String& base64, bool* success); + static Binary fromSpan(const uint8_t* data, size_t size) { + return Binary(std::make_shared>(data, data + size)); + } + + private: + std::shared_ptr> bytes_; + + explicit Binary(std::shared_ptr> bytes) + : bytes_(bytes) {} +}; +} // namespace protocol + +v8::Local toV8String(v8::Isolate*, const String16&); +v8::Local toV8StringInternalized(v8::Isolate*, const String16&); +v8::Local toV8StringInternalized(v8::Isolate*, const char*); +v8::Local toV8String(v8::Isolate*, const StringView&); +// TODO(dgozman): rename to toString16. +String16 toProtocolString(v8::Isolate*, v8::Local); +String16 toProtocolStringWithTypeCheck(v8::Isolate*, v8::Local); +V8_EXPORT_PRIVATE String16 toString16(const StringView&); +V8_EXPORT_PRIVATE StringView toStringView(const String16&); +template +StringView toStringView(const char* str[N]) { + return StringView(reinterpret_cast(str), N); +} +bool stringViewStartsWith(const StringView&, const char*); + +// Creates a string buffer instance which owns |str|, a 16 bit string. +std::unique_ptr StringBufferFrom(String16 str); + +// Creates a string buffer instance which owns |str|, an 8 bit string. +// 8 bit strings are used for LATIN1 text (which subsumes 7 bit ASCII, e.g. +// our generated JSON), as well as for CBOR encoded binary messages. +std::unique_ptr StringBufferFrom(std::vector str); + +String16 stackTraceIdToString(uintptr_t id); + +} // namespace v8_inspector + +// See third_party/inspector_protocol/crdtp/serializer_traits.h. +namespace v8_crdtp { + +template <> +struct ProtocolTypeTraits { + static bool Deserialize(DeserializerState* state, + v8_inspector::String16* value); + static void Serialize(const v8_inspector::String16& value, + std::vector* bytes); +}; + +template <> +struct ProtocolTypeTraits { + static bool Deserialize(DeserializerState* state, + v8_inspector::protocol::Binary* value); + static void Serialize(const v8_inspector::protocol::Binary& value, + std::vector* bytes); +}; + +namespace detail { +template <> +struct MaybeTypedef { + typedef ValueMaybe type; +}; + +template <> +struct MaybeTypedef { + typedef ValueMaybe type; +}; + +} // namespace detail + +} // namespace v8_crdtp + +#endif // V8_INSPECTOR_STRING_UTIL_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/test-interface.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/test-interface.h new file mode 100644 index 000000000..406ba02fa --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/test-interface.h @@ -0,0 +1,19 @@ +// 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_INSPECTOR_TEST_INTERFACE_H_ +#define V8_INSPECTOR_TEST_INTERFACE_H_ + +#include "include/v8config.h" + +namespace v8_inspector { + +class V8Inspector; + +V8_EXPORT void SetMaxAsyncTaskStacksForTest(V8Inspector* inspector, int limit); +V8_EXPORT void DumpAsyncTaskStacksStateForTest(V8Inspector* inspector); + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_TEST_INTERFACE_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-console-agent-impl.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-console-agent-impl.h new file mode 100644 index 000000000..009375b04 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-console-agent-impl.h @@ -0,0 +1,48 @@ +// 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_INSPECTOR_V8_CONSOLE_AGENT_IMPL_H_ +#define V8_INSPECTOR_V8_CONSOLE_AGENT_IMPL_H_ + +#include "src/base/macros.h" +#include "src/inspector/protocol/Console.h" +#include "src/inspector/protocol/Forward.h" + +namespace v8_inspector { + +class V8ConsoleMessage; +class V8InspectorSessionImpl; + +using protocol::Response; + +class V8ConsoleAgentImpl : public protocol::Console::Backend { + public: + V8ConsoleAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, + protocol::DictionaryValue* state); + ~V8ConsoleAgentImpl() override; + V8ConsoleAgentImpl(const V8ConsoleAgentImpl&) = delete; + V8ConsoleAgentImpl& operator=(const V8ConsoleAgentImpl&) = delete; + + Response enable() override; + Response disable() override; + Response clearMessages() override; + + void restore(); + void messageAdded(V8ConsoleMessage*); + void reset(); + bool enabled(); + + private: + void reportAllMessages(); + bool reportMessage(V8ConsoleMessage*, bool generatePreview); + + V8InspectorSessionImpl* m_session; + protocol::DictionaryValue* m_state; + protocol::Console::Frontend m_frontend; + bool m_enabled; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_CONSOLE_AGENT_IMPL_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-console-message.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-console-message.h new file mode 100644 index 000000000..cd960cf79 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-console-message.h @@ -0,0 +1,150 @@ +// 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_INSPECTOR_V8_CONSOLE_MESSAGE_H_ +#define V8_INSPECTOR_V8_CONSOLE_MESSAGE_H_ + +#include +#include +#include +#include + +#include "include/v8-local-handle.h" +#include "include/v8-persistent-handle.h" +#include "src/inspector/protocol/Console.h" +#include "src/inspector/protocol/Forward.h" +#include "src/inspector/protocol/Runtime.h" + +namespace v8_inspector { + +class InspectedContext; +class V8InspectorImpl; +class V8InspectorSessionImpl; +class V8StackTraceImpl; + +enum class V8MessageOrigin { kConsole, kException, kRevokedException }; + +enum class ConsoleAPIType { + kLog, + kDebug, + kInfo, + kError, + kWarning, + kDir, + kDirXML, + kTable, + kTrace, + kStartGroup, + kStartGroupCollapsed, + kEndGroup, + kClear, + kAssert, + kTimeEnd, + kCount +}; + +class V8ConsoleMessage { + public: + ~V8ConsoleMessage(); + + static std::unique_ptr createForConsoleAPI( + v8::Local v8Context, int contextId, int groupId, + V8InspectorImpl* inspector, double timestamp, ConsoleAPIType, + const std::vector>& arguments, + const String16& consoleContext, std::unique_ptr); + + static std::unique_ptr createForException( + double timestamp, const String16& detailedMessage, const String16& url, + unsigned lineNumber, unsigned columnNumber, + std::unique_ptr, int scriptId, v8::Isolate*, + const String16& message, int contextId, v8::Local exception, + unsigned exceptionId); + + static std::unique_ptr createForRevokedException( + double timestamp, const String16& message, unsigned revokedExceptionId); + + V8MessageOrigin origin() const; + void reportToFrontend(protocol::Console::Frontend*) const; + void reportToFrontend(protocol::Runtime::Frontend*, V8InspectorSessionImpl*, + bool generatePreview) const; + ConsoleAPIType type() const; + void contextDestroyed(int contextId); + + int estimatedSize() const { + return m_v8Size + static_cast(m_message.length() * sizeof(UChar)); + } + + private: + V8ConsoleMessage(V8MessageOrigin, double timestamp, const String16& message); + + using Arguments = std::vector>>; + std::unique_ptr> + wrapArguments(V8InspectorSessionImpl*, bool generatePreview) const; + std::unique_ptr wrapException( + V8InspectorSessionImpl*, bool generatePreview) const; + void setLocation(const String16& url, unsigned lineNumber, + unsigned columnNumber, std::unique_ptr, + int scriptId); + std::unique_ptr getAssociatedExceptionData( + V8InspectorImpl* inspector, V8InspectorSessionImpl* session) const; + + V8MessageOrigin m_origin; + double m_timestamp; + String16 m_message; + String16 m_url; + unsigned m_lineNumber; + unsigned m_columnNumber; + std::unique_ptr m_stackTrace; + int m_scriptId; + int m_contextId; + ConsoleAPIType m_type; + unsigned m_exceptionId; + unsigned m_revokedExceptionId; + int m_v8Size = 0; + Arguments m_arguments; + String16 m_detailedMessage; + String16 m_consoleContext; +}; + +class V8ConsoleMessageStorage { + public: + V8ConsoleMessageStorage(V8InspectorImpl*, int contextGroupId); + ~V8ConsoleMessageStorage(); + + int contextGroupId() { return m_contextGroupId; } + const std::deque>& messages() const { + return m_messages; + } + + void addMessage(std::unique_ptr); + void contextDestroyed(int contextId); + void clear(); + + bool shouldReportDeprecationMessage(int contextId, const String16& method); + int count(int contextId, const String16& id); + bool countReset(int contextId, const String16& id); + void time(int contextId, const String16& id); + double timeLog(int contextId, const String16& id); + double timeEnd(int contextId, const String16& id); + bool hasTimer(int contextId, const String16& id); + + private: + V8InspectorImpl* m_inspector; + int m_contextGroupId; + int m_estimatedSize = 0; + std::deque> m_messages; + + struct PerContextData { + std::set m_reportedDeprecationMessages; + // Corresponds to https://console.spec.whatwg.org/#count-map + std::map m_count; + // Corresponds to https://console.spec.whatwg.org/#timer-table + std::map m_time; + }; + std::map m_data; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_CONSOLE_MESSAGE_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-console.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-console.h new file mode 100644 index 000000000..8d2258f71 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-console.h @@ -0,0 +1,249 @@ +// 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_INSPECTOR_V8_CONSOLE_H_ +#define V8_INSPECTOR_V8_CONSOLE_H_ + +#include + +#include "include/v8-array-buffer.h" +#include "include/v8-external.h" +#include "include/v8-local-handle.h" +#include "src/base/macros.h" +#include "src/debug/interface-types.h" + +namespace v8 { +class ObjectTemplate; +class Set; +} // namespace v8 + +namespace v8_inspector { + +class InspectedContext; +class TaskInfo; +class V8InspectorImpl; + +// Console API +// https://console.spec.whatwg.org/#console-namespace +class V8Console : public v8::debug::ConsoleDelegate { + public: + v8::Local createCommandLineAPI(v8::Local context, + int sessionId); + void installMemoryGetter(v8::Local context, + v8::Local console); + void installAsyncStackTaggingAPI(v8::Local context, + v8::Local console); + void cancelConsoleTask(TaskInfo* taskInfo); + + std::map>& AllConsoleTasksForTest() { + return m_tasks; + } + + class V8_NODISCARD CommandLineAPIScope { + public: + CommandLineAPIScope(v8::Local, + v8::Local commandLineAPI, + v8::Local global); + ~CommandLineAPIScope(); + CommandLineAPIScope(const CommandLineAPIScope&) = delete; + CommandLineAPIScope& operator=(const CommandLineAPIScope&) = delete; + + private: + static void accessorGetterCallback( + v8::Local, const v8::PropertyCallbackInfo&); + static void accessorSetterCallback(v8::Local, + v8::Local, + const v8::PropertyCallbackInfo&); + + v8::Local m_context; + v8::Local m_commandLineAPI; + v8::Local m_global; + v8::Local m_installedMethods; + v8::Local m_thisReference; + }; + + explicit V8Console(V8InspectorImpl* inspector); + + private: + friend class TaskInfo; + + void Debug(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void Error(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void Info(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void Log(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void Warn(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void Dir(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void DirXml(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void Table(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void Trace(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void Group(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void GroupCollapsed(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void GroupEnd(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void Clear(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void Count(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void CountReset(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void Assert(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void Profile(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void ProfileEnd(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void Time(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void TimeLog(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void TimeEnd(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + void TimeStamp(const v8::debug::ConsoleCallArguments&, + const v8::debug::ConsoleContext& consoleContext) override; + + template &)> + static void call(const v8::FunctionCallbackInfo& info) { + V8Console* console = + static_cast(info.Data().As()->Value()); + (console->*func)(info); + } + using CommandLineAPIData = std::pair; + template &, + int)> + static void call(const v8::FunctionCallbackInfo& info) { + CommandLineAPIData* data = static_cast( + info.Data().As()->GetBackingStore()->Data()); + (data->first->*func)(info, data->second); + } + template + static void call(const v8::FunctionCallbackInfo& info) { + CommandLineAPIData* data = static_cast( + info.Data().As()->GetBackingStore()->Data()); + v8::debug::ConsoleCallArguments args(info); + (data->first->*func)(args, v8::debug::ConsoleContext()); + } + + // TODO(foolip): There is no spec for the Memory Info API, see blink-dev: + // https://groups.google.com/a/chromium.org/d/msg/blink-dev/g5YRCGpC9vs/b4OJz71NmPwJ + void memoryGetterCallback(const v8::FunctionCallbackInfo&); + void memorySetterCallback(const v8::FunctionCallbackInfo&); + + void createTask(const v8::FunctionCallbackInfo&); + void runTask(const v8::FunctionCallbackInfo&); + + // CommandLineAPI + void keysCallback(const v8::FunctionCallbackInfo&, int sessionId); + void valuesCallback(const v8::FunctionCallbackInfo&, + int sessionId); + void debugFunctionCallback(const v8::FunctionCallbackInfo&, + int sessionId); + void undebugFunctionCallback(const v8::FunctionCallbackInfo&, + int sessionId); + void monitorFunctionCallback(const v8::FunctionCallbackInfo&, + int sessionId); + void unmonitorFunctionCallback(const v8::FunctionCallbackInfo&, + int sessionId); + void lastEvaluationResultCallback(const v8::FunctionCallbackInfo&, + int sessionId); + void inspectCallback(const v8::FunctionCallbackInfo&, + int sessionId); + void copyCallback(const v8::FunctionCallbackInfo&, int sessionId); + void inspectedObject(const v8::FunctionCallbackInfo&, + int sessionId, unsigned num); + void inspectedObject0(const v8::FunctionCallbackInfo& info, + int sessionId) { + inspectedObject(info, sessionId, 0); + } + void inspectedObject1(const v8::FunctionCallbackInfo& info, + int sessionId) { + inspectedObject(info, sessionId, 1); + } + void inspectedObject2(const v8::FunctionCallbackInfo& info, + int sessionId) { + inspectedObject(info, sessionId, 2); + } + void inspectedObject3(const v8::FunctionCallbackInfo& info, + int sessionId) { + inspectedObject(info, sessionId, 3); + } + void inspectedObject4(const v8::FunctionCallbackInfo& info, + int sessionId) { + inspectedObject(info, sessionId, 4); + } + void queryObjectsCallback(const v8::FunctionCallbackInfo& info, + int sessionId); + + // Lazily creates m_taskInfoKey and returns a local handle to it. We can't + // initialize m_taskInfoKey in the constructor as it would be part of + // Chromium's context snapshot. + v8::Local taskInfoKey(); + + // Lazily creates m_taskTemplate and returns a local handle to it. + // Similarly to m_taskInfoKey, we can't create the template upfront as to not + // be part of Chromium's context snapshot. + v8::Local taskTemplate(); + + V8InspectorImpl* m_inspector; + + // All currently alive tasks. We mark tasks immediately as weak when created + // but we need the finalizer to cancel the task when GC cleans them up. + std::map> m_tasks; + + // We use a private symbol to stash the `TaskInfo` as an v8::External on the + // JS task objects created by `console.createTask`. + v8::Global m_taskInfoKey; + + // We cache the task template for the async stack tagging API for faster + // instantiation. Use `taskTemplate()` to retrieve the lazily created + // template. + v8::Global m_taskTemplate; +}; + +/** + * Each JS task object created via `console.createTask` has a corresponding + * `TaskInfo` object on the C++ side (in a 1:1 relationship). + * + * The `TaskInfo` holds on weakly to the JS task object. + * The JS task objects uses a private symbol to store a pointer to the + * `TaskInfo` object (via v8::External). + * + * The `TaskInfo` objects holds all the necessary information we need to + * properly cancel the corresponding async task then the JS task object + * gets GC'ed. + */ +class TaskInfo { + public: + TaskInfo(v8::Isolate* isolate, V8Console* console, + v8::Local task); + + // For these task IDs we duplicate the ID logic from blink and use even + // pointers compared to the odd IDs we use for promises. This guarantees that + // we don't have any conflicts between task IDs. + void* Id() const { + return reinterpret_cast(reinterpret_cast(this) << 1); + } + + // After calling `Cancel` the `TaskInfo` instance is destroyed. + void Cancel() { m_console->cancelConsoleTask(this); } + + private: + v8::Global m_task; + V8Console* m_console = nullptr; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_CONSOLE_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger-agent-impl.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger-agent-impl.h new file mode 100644 index 000000000..9853312bc --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger-agent-impl.h @@ -0,0 +1,282 @@ +// Copyright 2015 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_INSPECTOR_V8_DEBUGGER_AGENT_IMPL_H_ +#define V8_INSPECTOR_V8_DEBUGGER_AGENT_IMPL_H_ + +#include +#include +#include +#include + +#include "src/base/enum-set.h" +#include "src/base/macros.h" +#include "src/debug/debug-interface.h" +#include "src/inspector/protocol/Debugger.h" +#include "src/inspector/protocol/Forward.h" + +namespace v8_inspector { + +struct ScriptBreakpoint; +class DisassemblyCollectorImpl; +class V8Debugger; +class V8DebuggerScript; +class V8InspectorImpl; +class V8InspectorSessionImpl; +class V8Regex; + +using protocol::Maybe; +using protocol::Response; + +class V8DebuggerAgentImpl : public protocol::Debugger::Backend { + public: + enum BreakpointSource { + UserBreakpointSource, + DebugCommandBreakpointSource, + MonitorCommandBreakpointSource + }; + + V8DebuggerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, + protocol::DictionaryValue* state); + ~V8DebuggerAgentImpl() override; + V8DebuggerAgentImpl(const V8DebuggerAgentImpl&) = delete; + V8DebuggerAgentImpl& operator=(const V8DebuggerAgentImpl&) = delete; + void restore(); + void stop(); + + // Part of the protocol. + Response enable(Maybe maxScriptsCacheSize, + String16* outDebuggerId) override; + Response disable() override; + Response setBreakpointsActive(bool active) override; + Response setSkipAllPauses(bool skip) override; + Response setBreakpointByUrl( + int lineNumber, Maybe optionalURL, + Maybe optionalURLRegex, Maybe optionalScriptHash, + Maybe optionalColumnNumber, Maybe optionalCondition, + String16*, + std::unique_ptr>* locations) + override; + Response setBreakpoint( + std::unique_ptr, + Maybe optionalCondition, String16*, + std::unique_ptr* actualLocation) override; + Response setBreakpointOnFunctionCall(const String16& functionObjectId, + Maybe optionalCondition, + String16* outBreakpointId) override; + Response setInstrumentationBreakpoint(const String16& instrumentation, + String16* outBreakpointId) override; + Response removeBreakpoint(const String16& breakpointId) override; + Response continueToLocation(std::unique_ptr, + Maybe targetCallFrames) override; + Response getStackTrace( + std::unique_ptr inStackTraceId, + std::unique_ptr* outStackTrace) override; + Response searchInContent( + const String16& scriptId, const String16& query, + Maybe optionalCaseSensitive, Maybe optionalIsRegex, + std::unique_ptr>*) + override; + Response getPossibleBreakpoints( + std::unique_ptr start, + Maybe end, Maybe restrictToFunction, + std::unique_ptr>* + locations) override; + Response setScriptSource( + const String16& inScriptId, const String16& inScriptSource, + Maybe dryRun, Maybe allowTopFrameEditing, + Maybe>* optOutCallFrames, + Maybe* optOutStackChanged, + Maybe* optOutAsyncStackTrace, + Maybe* optOutAsyncStackTraceId, + String16* outStatus, + Maybe* optOutCompileError) override; + Response restartFrame( + const String16& callFrameId, Maybe mode, + std::unique_ptr>* + newCallFrames, + Maybe* asyncStackTrace, + Maybe* asyncStackTraceId) override; + Response getScriptSource(const String16& scriptId, String16* scriptSource, + Maybe* bytecode) override; + Response disassembleWasmModule( + const String16& in_scriptId, Maybe* out_streamId, + int* out_totalNumberOfLines, + std::unique_ptr>* out_functionBodyOffsets, + std::unique_ptr* out_chunk) + override; + Response nextWasmDisassemblyChunk( + const String16& in_streamId, + std::unique_ptr* out_chunk) + override; + Response getWasmBytecode(const String16& scriptId, + protocol::Binary* bytecode) override; + Response pause() override; + Response resume(Maybe terminateOnResume) override; + Response stepOver(Maybe> + inSkipList) override; + Response stepInto(Maybe inBreakOnAsyncCall, + Maybe> + inSkipList) override; + Response stepOut() override; + Response pauseOnAsyncCall(std::unique_ptr + inParentStackTraceId) override; + Response setPauseOnExceptions(const String16& pauseState) override; + Response evaluateOnCallFrame( + const String16& callFrameId, const String16& expression, + Maybe objectGroup, Maybe includeCommandLineAPI, + Maybe silent, Maybe returnByValue, + Maybe generatePreview, Maybe throwOnSideEffect, + Maybe timeout, + std::unique_ptr* result, + Maybe*) override; + Response setVariableValue( + int scopeNumber, const String16& variableName, + std::unique_ptr newValue, + const String16& callFrame) override; + Response setReturnValue( + std::unique_ptr newValue) override; + Response setAsyncCallStackDepth(int depth) override; + Response setBlackboxPatterns( + std::unique_ptr> patterns) override; + Response setBlackboxedRanges( + const String16& scriptId, + std::unique_ptr> + positions) override; + + bool enabled() const { return m_enableState == kEnabled; } + + void setBreakpointFor(v8::Local function, + v8::Local condition, + BreakpointSource source); + void removeBreakpointFor(v8::Local function, + BreakpointSource source); + void schedulePauseOnNextStatement( + const String16& breakReason, + std::unique_ptr data); + void cancelPauseOnNextStatement(); + void breakProgram(const String16& breakReason, + std::unique_ptr data); + + void reset(); + + bool instrumentationFinished() { return m_instrumentationFinished; } + // Interface for V8InspectorImpl + void didPauseOnInstrumentation(v8::debug::BreakpointId instrumentationId); + + void didPause(int contextId, v8::Local exception, + const std::vector& hitBreakpoints, + v8::debug::ExceptionType exceptionType, bool isUncaught, + v8::debug::BreakReasons breakReasons); + void didContinue(); + void didParseSource(std::unique_ptr, bool success); + + bool isFunctionBlackboxed(const String16& scriptId, + const v8::debug::Location& start, + const v8::debug::Location& end); + bool shouldBeSkipped(const String16& scriptId, int line, int column); + + bool acceptsPause(bool isOOMBreak) const; + + void ScriptCollected(const V8DebuggerScript* script); + + v8::Isolate* isolate() { return m_isolate; } + + void clearBreakDetails(); + + private: + void enableImpl(); + + Response currentCallFrames( + std::unique_ptr>*); + std::unique_ptr currentAsyncStackTrace(); + std::unique_ptr currentExternalStackTrace(); + + void setPauseOnExceptionsImpl(int); + + std::unique_ptr setBreakpointImpl( + const String16& breakpointId, const String16& scriptId, + const String16& condition, int lineNumber, int columnNumber); + void setBreakpointImpl(const String16& breakpointId, + v8::Local function, + v8::Local condition); + void removeBreakpointImpl(const String16& breakpointId, + const std::vector& scripts); + + void internalSetAsyncCallStackDepth(int); + void increaseCachedSkipStackGeneration(); + + Response setBlackboxPattern(const String16& pattern); + void resetBlackboxedStateCache(); + + bool isPaused() const; + + void setScriptInstrumentationBreakpointIfNeeded(V8DebuggerScript* script); + + Response processSkipList( + protocol::Array* skipList); + + using ScriptsMap = + std::unordered_map>; + using BreakpointIdToDebuggerBreakpointIdsMap = + std::unordered_map>; + using DebuggerBreakpointIdToBreakpointIdMap = + std::unordered_map; + + enum EnableState { + kDisabled, + kEnabled, + kStopping, // This is the same as 'disabled', but it cannot become enabled + // again. + }; + + V8InspectorImpl* m_inspector; + V8Debugger* m_debugger; + V8InspectorSessionImpl* m_session; + EnableState m_enableState; + protocol::DictionaryValue* m_state; + protocol::Debugger::Frontend m_frontend; + v8::Isolate* m_isolate; + ScriptsMap m_scripts; + BreakpointIdToDebuggerBreakpointIdsMap m_breakpointIdToDebuggerBreakpointIds; + DebuggerBreakpointIdToBreakpointIdMap m_debuggerBreakpointIdToBreakpointId; + std::map> + m_wasmDisassemblies; + size_t m_nextWasmDisassemblyStreamId = 0; + + size_t m_maxScriptCacheSize = 0; + size_t m_cachedScriptSize = 0; + struct CachedScript { + String16 scriptId; + String16 source; + std::vector bytecode; + + size_t size() const { + return source.length() * sizeof(UChar) + bytecode.size(); + } + }; + std::deque m_cachedScripts; + + using BreakReason = + std::pair>; + std::vector m_breakReason; + + void pushBreakDetails( + const String16& breakReason, + std::unique_ptr breakAuxData); + void popBreakDetails(); + + bool m_skipAllPauses = false; + bool m_breakpointsActive = false; + bool m_instrumentationFinished = true; + + std::unique_ptr m_blackboxPattern; + std::unordered_map>> + m_blackboxedPositions; + std::unordered_map>> m_skipList; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_DEBUGGER_AGENT_IMPL_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger-barrier.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger-barrier.h new file mode 100644 index 000000000..f4dc29611 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger-barrier.h @@ -0,0 +1,28 @@ +// 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 V8_INSPECTOR_V8_DEBUGGER_BARRIER_H_ +#define V8_INSPECTOR_V8_DEBUGGER_BARRIER_H_ + +namespace v8_inspector { + +class V8InspectorClient; + +// This class is used to synchronize multiple sessions issuing +// `Runtime.runIfWaitingForDebbuger` so that the global client +// `runIfWaitingForDebugger` method is only invoked when all +// sessions have invoked `Runtime.runIfWaitingForDebugger`. +class V8DebuggerBarrier { + public: + V8DebuggerBarrier(V8InspectorClient* client, int contextGroupId); + ~V8DebuggerBarrier(); + + private: + V8InspectorClient* const m_client; + int m_contextGroupId; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_DEBUGGER_BARRIER_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger-id.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger-id.h new file mode 100644 index 000000000..757976d5b --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger-id.h @@ -0,0 +1,41 @@ +// 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 V8_INSPECTOR_V8_DEBUGGER_ID_H_ +#define V8_INSPECTOR_V8_DEBUGGER_ID_H_ + +#include + +#include "include/v8-inspector.h" +#include "src/base/macros.h" +#include "src/inspector/protocol/Forward.h" + +namespace v8_inspector { +class V8InspectorImpl; + +namespace internal { + +class V8DebuggerId { + public: + V8DebuggerId() = default; + explicit V8DebuggerId(std::pair); + explicit V8DebuggerId(const String16&); + V8DebuggerId(const V8DebuggerId&) V8_NOEXCEPT = default; + V8DebuggerId& operator=(const V8DebuggerId&) V8_NOEXCEPT = default; + + static V8DebuggerId generate(V8InspectorImpl*); + + v8_inspector::V8DebuggerId toV8DebuggerId() const { return m_debugger_id; } + String16 toString() const; + bool isValid() const; + std::pair pair() const; + + private: + v8_inspector::V8DebuggerId m_debugger_id; +}; + +} // namespace internal +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_DEBUGGER_ID_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger-script.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger-script.h new file mode 100644 index 000000000..80b900c9b --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger-script.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef V8_INSPECTOR_V8_DEBUGGER_SCRIPT_H_ +#define V8_INSPECTOR_V8_DEBUGGER_SCRIPT_H_ + +#include + +#include "include/v8-local-handle.h" +#include "include/v8-maybe.h" +#include "src/base/macros.h" +#include "src/debug/debug-interface.h" +#include "src/inspector/string-16.h" +#include "src/inspector/string-util.h" + +namespace v8 { +class Isolate; +} + +namespace v8_inspector { + +class V8DebuggerAgentImpl; +class V8InspectorClient; + +class V8DebuggerScript { + public: + enum class Language { JavaScript, WebAssembly }; + static std::unique_ptr Create( + v8::Isolate* isolate, v8::Local script, + bool isLiveEdit, V8DebuggerAgentImpl* agent, V8InspectorClient* client); + + virtual ~V8DebuggerScript(); + V8DebuggerScript(const V8DebuggerScript&) = delete; + V8DebuggerScript& operator=(const V8DebuggerScript&) = delete; + + v8::Local scriptSource(); + const String16& scriptId() const { return m_id; } + bool hasSourceURLComment() const { return m_hasSourceURLComment; } + const String16& sourceURL() const { return m_url; } + const String16& embedderName() const { return m_embedderName; } + + virtual const String16& sourceMappingURL() const = 0; + virtual String16 source(size_t pos, size_t len = UINT_MAX) const = 0; + virtual Language getLanguage() const = 0; + virtual const String16& hash() const = 0; + virtual int startLine() const = 0; + virtual int startColumn() const = 0; + virtual int endLine() const = 0; + virtual int endColumn() const = 0; + virtual int codeOffset() const = 0; + int executionContextId() const { return m_executionContextId; } + virtual bool isLiveEdit() const = 0; + virtual bool isModule() const = 0; + virtual int length() const = 0; + + void setSourceURL(const String16&); + virtual void setSourceMappingURL(const String16&) = 0; + virtual void setSource(const String16& source, bool preview, + bool allowTopFrameLiveEditing, + v8::debug::LiveEditResult* result) = 0; + + virtual bool getPossibleBreakpoints( + const v8::debug::Location& start, const v8::debug::Location& end, + bool ignoreNestedFunctions, + std::vector* locations) = 0; + virtual void resetBlackboxedStateCache() = 0; + + virtual v8::Maybe offset(int lineNumber, int columnNumber) const = 0; + virtual v8::debug::Location location(int offset) const = 0; + + virtual bool setBreakpoint(const String16& condition, + v8::debug::Location* location, int* id) const = 0; + virtual void MakeWeak() = 0; + virtual bool setInstrumentationBreakpoint(int* id) const = 0; + +#if V8_ENABLE_WEBASSEMBLY + virtual v8::Maybe> wasmBytecode() const = 0; + virtual v8::Maybe + getDebugSymbolsType() const = 0; + virtual v8::Maybe getExternalDebugSymbolsURL() const = 0; + void removeWasmBreakpoint(int id); + virtual void Disassemble(v8::debug::DisassemblyCollector* collector, + std::vector* function_body_offsets) const = 0; +#endif // V8_ENABLE_WEBASSEMBLY + + protected: + V8DebuggerScript(v8::Isolate*, String16 id, String16 url, + String16 embedderName); + + virtual v8::Local script() const = 0; + + String16 m_id; + String16 m_url; + bool m_hasSourceURLComment = false; + int m_executionContextId = 0; + + v8::Isolate* m_isolate; + String16 m_embedderName; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_DEBUGGER_SCRIPT_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger.h new file mode 100644 index 000000000..7ec698d3e --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-debugger.h @@ -0,0 +1,308 @@ +// 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_INSPECTOR_V8_DEBUGGER_H_ +#define V8_INSPECTOR_V8_DEBUGGER_H_ + +#include +#include +#include +#include +#include + +#include "include/v8-inspector.h" +#include "src/base/macros.h" +#include "src/inspector/inspected-context.h" +#include "src/inspector/protocol/Debugger.h" +#include "src/inspector/protocol/Forward.h" +#include "src/inspector/protocol/Runtime.h" +#include "src/inspector/v8-debugger-id.h" +#include "src/inspector/v8-debugger-script.h" + +namespace v8_inspector { + +class AsyncStackTrace; +class StackFrame; +class V8Debugger; +class V8DebuggerAgentImpl; +class V8InspectorImpl; +class V8RuntimeAgentImpl; +class V8StackTraceImpl; +struct V8StackTraceId; + +enum class WrapMode { + kForceValue, + kNoPreview, + kWithPreview, + kGenerateWebDriverValue +}; + +using protocol::Response; +using TerminateExecutionCallback = + protocol::Runtime::Backend::TerminateExecutionCallback; + +class V8Debugger : public v8::debug::DebugDelegate, + public v8::debug::AsyncEventDelegate { + public: + V8Debugger(v8::Isolate*, V8InspectorImpl*); + ~V8Debugger() override; + V8Debugger(const V8Debugger&) = delete; + V8Debugger& operator=(const V8Debugger&) = delete; + + bool enabled() const; + v8::Isolate* isolate() const { return m_isolate; } + + void setBreakpointsActive(bool); + + v8::debug::ExceptionBreakState getPauseOnExceptionsState(); + void setPauseOnExceptionsState(v8::debug::ExceptionBreakState); + bool canBreakProgram(); + bool isInInstrumentationPause() const; + void breakProgram(int targetContextGroupId); + void interruptAndBreak(int targetContextGroupId); + void requestPauseAfterInstrumentation(); + void continueProgram(int targetContextGroupId, + bool terminateOnResume = false); + void breakProgramOnAssert(int targetContextGroupId); + + void setPauseOnNextCall(bool, int targetContextGroupId); + void stepIntoStatement(int targetContextGroupId, bool breakOnAsyncCall); + void stepOverStatement(int targetContextGroupId); + void stepOutOfFunction(int targetContextGroupId); + + void terminateExecution(v8::Local context, + std::unique_ptr callback); + + Response continueToLocation(int targetContextGroupId, + V8DebuggerScript* script, + std::unique_ptr, + const String16& targetCallFramess); + bool restartFrame(int targetContextGroupId, int callFrameOrdinal); + + // Each script inherits debug data from v8::Context where it has been + // compiled. + // Only scripts whose debug data matches |contextGroupId| will be reported. + // Passing 0 will result in reporting all scripts. + std::vector> getCompiledScripts( + int contextGroupId, V8DebuggerAgentImpl* agent); + void enable(); + void disable(); + + bool isPaused() const { return m_pausedContextGroupId; } + bool isPausedInContextGroup(int contextGroupId) const; + + int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; } + void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int); + + int maxCallStackSizeToCapture() const { return m_maxCallStackSizeToCapture; } + void setMaxCallStackSizeToCapture(V8RuntimeAgentImpl*, int); + + std::shared_ptr currentAsyncParent(); + V8StackTraceId currentExternalParent(); + + std::shared_ptr symbolize(v8::Local v8Frame); + + std::unique_ptr createStackTrace(v8::Local); + std::unique_ptr captureStackTrace(bool fullStack); + + v8::MaybeLocal internalProperties(v8::Local, + v8::Local); + + v8::Local queryObjects(v8::Local context, + v8::Local prototype); + + void asyncTaskScheduled(const StringView& taskName, void* task, + bool recurring); + void asyncTaskCanceled(void* task); + void asyncTaskStarted(void* task); + void asyncTaskFinished(void* task); + void allAsyncTasksCanceled(); + + V8StackTraceId storeCurrentStackTrace(const StringView& description); + void externalAsyncTaskStarted(const V8StackTraceId& parent); + void externalAsyncTaskFinished(const V8StackTraceId& parent); + + uintptr_t storeStackTrace(std::shared_ptr stack); + + void muteScriptParsedEvents(); + void unmuteScriptParsedEvents(); + + V8InspectorImpl* inspector() { return m_inspector; } + + void setMaxAsyncTaskStacksForTest(int limit); + void dumpAsyncTaskStacksStateForTest(); + + internal::V8DebuggerId debuggerIdFor(int contextGroupId); + std::shared_ptr stackTraceFor(int contextGroupId, + const V8StackTraceId& id); + + void reportTermination(); + + private: + bool addInternalObject(v8::Local context, + v8::Local object, + V8InternalValueType type); + + void clearContinueToLocation(); + bool shouldContinueToCurrentLocation(); + + static size_t nearHeapLimitCallback(void* data, size_t current_heap_limit, + size_t initial_heap_limit); + static void terminateExecutionCompletedCallback(v8::Isolate* isolate); + static void terminateExecutionCompletedCallbackIgnoringData( + v8::Isolate* isolate, void*); + void handleProgramBreak( + v8::Local pausedContext, v8::Local exception, + const std::vector& hitBreakpoints, + v8::debug::BreakReasons break_reasons, + v8::debug::ExceptionType exception_type = v8::debug::kException, + bool isUncaught = false); + + enum ScopeTargetKind { + FUNCTION, + GENERATOR, + }; + v8::MaybeLocal getTargetScopes(v8::Local, + v8::Local, + ScopeTargetKind); + + v8::MaybeLocal functionScopes(v8::Local, + v8::Local); + v8::MaybeLocal generatorScopes(v8::Local, + v8::Local); + v8::MaybeLocal collectionsEntries(v8::Local context, + v8::Local value); + + void asyncTaskScheduledForStack(const StringView& taskName, void* task, + bool recurring, bool skipTopFrame = false); + void asyncTaskCanceledForStack(void* task); + void asyncTaskStartedForStack(void* task); + void asyncTaskFinishedForStack(void* task); + + void asyncTaskCandidateForStepping(void* task); + void asyncTaskStartedForStepping(void* task); + void asyncTaskFinishedForStepping(void* task); + void asyncTaskCanceledForStepping(void* task); + + // v8::debug::DebugEventListener implementation. + void AsyncEventOccurred(v8::debug::DebugAsyncActionType type, int id, + bool isBlackboxed) override; + void ScriptCompiled(v8::Local script, bool is_live_edited, + bool has_compile_error) override; + void BreakProgramRequested( + v8::Local paused_context, + const std::vector& break_points_hit, + v8::debug::BreakReasons break_reasons) override; + ActionAfterInstrumentation BreakOnInstrumentation( + v8::Local paused_context, v8::debug::BreakpointId) override; + void ExceptionThrown(v8::Local paused_context, + v8::Local exception, + v8::Local promise, bool is_uncaught, + v8::debug::ExceptionType exception_type) override; + bool IsFunctionBlackboxed(v8::Local script, + const v8::debug::Location& start, + const v8::debug::Location& end) override; + + bool ShouldBeSkipped(v8::Local script, int line, + int column) override; + + int currentContextGroupId(); + + bool hasScheduledBreakOnNextFunctionCall() const; + + void quitMessageLoopIfAgentsFinishedInstrumentation(); + + v8::Isolate* m_isolate; + V8InspectorImpl* m_inspector; + int m_enableCount; + + int m_breakpointsActiveCount = 0; + int m_ignoreScriptParsedEventsCounter; + size_t m_originalHeapLimit = 0; + bool m_scheduledOOMBreak = false; + int m_targetContextGroupId = 0; + int m_pausedContextGroupId = 0; + bool m_instrumentationPause = false; + bool m_requestedPauseAfterInstrumentation = false; + int m_continueToLocationBreakpointId; + String16 m_continueToLocationTargetCallFrames; + std::unique_ptr m_continueToLocationStack; + + // We cache symbolized stack frames by (scriptId,lineNumber,columnNumber) + // to reduce memory pressure for huge web apps with lots of deep async + // stacks. + struct CachedStackFrameKey { + int scriptId; + int lineNumber; + int columnNumber; + + struct Equal { + bool operator()(CachedStackFrameKey const& a, + CachedStackFrameKey const& b) const { + return a.scriptId == b.scriptId && a.lineNumber == b.lineNumber && + a.columnNumber == b.columnNumber; + } + }; + + struct Hash { + size_t operator()(CachedStackFrameKey const& key) const { + size_t code = 0; + code = code * 31 + key.scriptId; + code = code * 31 + key.lineNumber; + code = code * 31 + key.columnNumber; + return code; + } + }; + }; + std::unordered_map, + CachedStackFrameKey::Hash, CachedStackFrameKey::Equal> + m_cachedStackFrames; + + using AsyncTaskToStackTrace = + std::unordered_map>; + AsyncTaskToStackTrace m_asyncTaskStacks; + std::unordered_set m_recurringTasks; + + size_t m_maxAsyncCallStacks; + int m_maxAsyncCallStackDepth; + int m_maxCallStackSizeToCapture; + + std::vector m_currentTasks; + std::vector> m_currentAsyncParent; + std::vector m_currentExternalParent; + + void collectOldAsyncStacksIfNeeded(); + // V8Debugger owns all the async stacks, while most of the other references + // are weak, which allows to collect some stacks when there are too many. + std::list> m_allAsyncStacks; + + std::unordered_map m_maxAsyncCallStackDepthMap; + std::unordered_map m_maxCallStackSizeToCaptureMap; + void* m_taskWithScheduledBreak = nullptr; + + // If any of the following three is true, we schedule pause on next JS + // execution using SetBreakOnNextFunctionCall. + bool m_externalAsyncTaskPauseRequested = false; // External async task. + bool m_taskWithScheduledBreakPauseRequested = false; // Local async task. + bool m_pauseOnNextCallRequested = false; // setPauseOnNextCall API call. + + v8::debug::ExceptionBreakState m_pauseOnExceptionsState; + // Whether we should pause on async call execution (if any) while stepping in. + // See Debugger.stepInto for details. + bool m_pauseOnAsyncCall = false; + + using StackTraceIdToStackTrace = + std::unordered_map>; + StackTraceIdToStackTrace m_storedStackTraces; + uintptr_t m_lastStackTraceId = 0; + + std::unordered_map m_contextGroupIdToDebuggerId; + + std::unique_ptr m_terminateExecutionCallback; + v8::Global m_terminateExecutionCallbackContext; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_DEBUGGER_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-heap-profiler-agent-impl.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-heap-profiler-agent-impl.h new file mode 100644 index 000000000..61c6b6af5 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-heap-profiler-agent-impl.h @@ -0,0 +1,86 @@ +// 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_INSPECTOR_V8_HEAP_PROFILER_AGENT_IMPL_H_ +#define V8_INSPECTOR_V8_HEAP_PROFILER_AGENT_IMPL_H_ + +#include + +#include "src/base/macros.h" +#include "src/inspector/protocol/Forward.h" +#include "src/inspector/protocol/HeapProfiler.h" + +namespace v8 { +class Isolate; +} + +namespace v8_inspector { + +class V8InspectorSessionImpl; + +using protocol::Maybe; +using protocol::Response; + +class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend { + public: + V8HeapProfilerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, + protocol::DictionaryValue* state); + ~V8HeapProfilerAgentImpl() override; + V8HeapProfilerAgentImpl(const V8HeapProfilerAgentImpl&) = delete; + V8HeapProfilerAgentImpl& operator=(const V8HeapProfilerAgentImpl&) = delete; + void restore(); + + void collectGarbage( + std::unique_ptr callback) override; + + Response enable() override; + Response startTrackingHeapObjects(Maybe trackAllocations) override; + Response stopTrackingHeapObjects(Maybe reportProgress, + Maybe treatGlobalObjectsAsRoots, + Maybe captureNumericValue, + Maybe exposeInternals) override; + + Response disable() override; + + Response takeHeapSnapshot(Maybe reportProgress, + Maybe treatGlobalObjectsAsRoots, + Maybe captureNumericValue, + Maybe exposeInternals) override; + + Response getObjectByHeapObjectId( + const String16& heapSnapshotObjectId, Maybe objectGroup, + std::unique_ptr* result) override; + Response addInspectedHeapObject( + const String16& inspectedHeapObjectId) override; + Response getHeapObjectId(const String16& objectId, + String16* heapSnapshotObjectId) override; + + Response startSampling(Maybe samplingInterval, + Maybe includeObjectsCollectedByMajorGC, + Maybe includeObjectsCollectedByMinorGC) override; + Response stopSampling( + std::unique_ptr*) override; + Response getSamplingProfile( + std::unique_ptr*) override; + + private: + struct AsyncGC; + class GCTask; + + void startTrackingHeapObjectsInternal(bool trackAllocations); + void stopTrackingHeapObjectsInternal(); + void requestHeapStatsUpdate(); + static void onTimer(void*); + + V8InspectorSessionImpl* m_session; + v8::Isolate* m_isolate; + protocol::HeapProfiler::Frontend m_frontend; + protocol::DictionaryValue* m_state; + bool m_hasTimer; + std::shared_ptr m_async_gc; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_HEAP_PROFILER_AGENT_IMPL_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-inspector-impl.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-inspector-impl.h new file mode 100644 index 000000000..0d30cc59f --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-inspector-impl.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef V8_INSPECTOR_V8_INSPECTOR_IMPL_H_ +#define V8_INSPECTOR_V8_INSPECTOR_IMPL_H_ + +#include +#include +#include +#include + +#include "include/v8-inspector.h" +#include "src/base/macros.h" +#include "src/base/platform/mutex.h" +#include "src/inspector/injected-script.h" +#include "src/inspector/protocol/Protocol.h" + +namespace v8_inspector { + +class InspectedContext; +class V8Console; +class V8ConsoleMessageStorage; +class V8Debugger; +class V8DebuggerAgentImpl; +class V8DebuggerBarrier; +class V8InspectorSessionImpl; +class V8ProfilerAgentImpl; +class V8RuntimeAgentImpl; +class V8StackTraceImpl; + +class V8InspectorImpl : public V8Inspector { + public: + V8_EXPORT_PRIVATE V8InspectorImpl(v8::Isolate*, V8InspectorClient*); + ~V8InspectorImpl() override; + V8InspectorImpl(const V8InspectorImpl&) = delete; + V8InspectorImpl& operator=(const V8InspectorImpl&) = delete; + + v8::Isolate* isolate() const { return m_isolate; } + V8InspectorClient* client() { return m_client; } + V8Debugger* debugger() { return m_debugger.get(); } + int contextGroupId(v8::Local) const; + int contextGroupId(int contextId) const; + uint64_t isolateId() const { return m_isolateId; } + int resolveUniqueContextId(internal::V8DebuggerId uniqueId) const; + + v8::MaybeLocal compileAndRunInternalScript(v8::Local, + v8::Local); + v8::MaybeLocal compileScript(v8::Local, + const String16& code, + const String16& fileName); + v8::MaybeLocal regexContext(); + v8::MaybeLocal exceptionMetaDataContext(); + + // V8Inspector implementation. + std::unique_ptr connect(int contextGroupId, + V8Inspector::Channel*, + StringView state, + ClientTrustLevel, + SessionPauseState) override; + void contextCreated(const V8ContextInfo&) override; + void contextDestroyed(v8::Local) override; + v8::MaybeLocal contextById(int contextId) override; + V8DebuggerId uniqueDebuggerId(int contextId) override; + void contextCollected(int contextGroupId, int contextId); + void resetContextGroup(int contextGroupId) override; + void idleStarted() override; + void idleFinished() override; + unsigned exceptionThrown(v8::Local, StringView message, + v8::Local exception, + StringView detailedMessage, StringView url, + unsigned lineNumber, unsigned columnNumber, + std::unique_ptr, + int scriptId) override; + void exceptionRevoked(v8::Local, unsigned exceptionId, + StringView message) override; + std::unique_ptr createStackTrace( + v8::Local) override; + std::unique_ptr captureStackTrace(bool fullStack) override; + void asyncTaskScheduled(StringView taskName, void* task, + bool recurring) override; + void asyncTaskCanceled(void* task) override; + void asyncTaskStarted(void* task) override; + void asyncTaskFinished(void* task) override; + void allAsyncTasksCanceled() override; + + V8StackTraceId storeCurrentStackTrace(StringView description) override; + void externalAsyncTaskStarted(const V8StackTraceId& parent) override; + void externalAsyncTaskFinished(const V8StackTraceId& parent) override; + + V8_EXPORT_PRIVATE bool associateExceptionData( + v8::Local, v8::Local exception, + v8::Local key, v8::Local value) override; + + unsigned nextExceptionId() { return ++m_lastExceptionId; } + void muteExceptions(int contextGroupId); + void unmuteExceptions(int contextGroupId); + V8ConsoleMessageStorage* ensureConsoleMessageStorage(int contextGroupId); + bool hasConsoleMessageStorage(int contextGroupId); + void discardInspectedContext(int contextGroupId, int contextId); + void disconnect(V8InspectorSessionImpl*); + V8InspectorSessionImpl* sessionById(int contextGroupId, int sessionId); + InspectedContext* getContext(int groupId, int contextId) const; + InspectedContext* getContext(int contextId) const; + V8_EXPORT_PRIVATE V8Console* console(); + void forEachContext(int contextGroupId, + const std::function& callback); + void forEachSession( + int contextGroupId, + const std::function& callback); + int64_t generateUniqueId(); + V8_EXPORT_PRIVATE v8::MaybeLocal getAssociatedExceptionData( + v8::Local exception); + std::unique_ptr + getAssociatedExceptionDataForProtocol(v8::Local exception); + + class EvaluateScope { + public: + explicit EvaluateScope(const InjectedScript::Scope& scope); + ~EvaluateScope(); + + protocol::Response setTimeout(double timeout); + + private: + class TerminateTask; + struct CancelToken; + + const InjectedScript::Scope& m_scope; + v8::Isolate* m_isolate; + std::shared_ptr m_cancelToken; + v8::Isolate::SafeForTerminationScope m_safeForTerminationScope; + }; + + private: + v8::Isolate* m_isolate; + V8InspectorClient* m_client; + std::unique_ptr m_debugger; + v8::Global m_regexContext; + v8::Global m_exceptionMetaDataContext; + v8::Global m_exceptionMetaData; + unsigned m_lastExceptionId; + int m_lastContextId; + int m_lastSessionId = 0; + uint64_t m_isolateId; + + using MuteExceptionsMap = std::unordered_map; + MuteExceptionsMap m_muteExceptionsMap; + + using ContextByIdMap = + std::unordered_map>; + using ContextsByGroupMap = + std::unordered_map>; + ContextsByGroupMap m_contexts; + + // contextGroupId -> sessionId -> session + std::unordered_map> m_sessions; + // contextGroupId -> debugger barrier + std::unordered_map> m_debuggerBarriers; + + using ConsoleStorageMap = + std::unordered_map>; + ConsoleStorageMap m_consoleStorageMap; + + std::unordered_map m_contextIdToGroupIdMap; + std::map, int> m_uniqueIdToContextId; + + std::unique_ptr m_console; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_INSPECTOR_IMPL_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-inspector-session-impl.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-inspector-session-impl.h new file mode 100644 index 000000000..9e443161c --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-inspector-session-impl.h @@ -0,0 +1,150 @@ +// 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_INSPECTOR_V8_INSPECTOR_SESSION_IMPL_H_ +#define V8_INSPECTOR_V8_INSPECTOR_SESSION_IMPL_H_ + +#include +#include + +#include "src/base/macros.h" +#include "src/inspector/protocol/Forward.h" +#include "src/inspector/protocol/Runtime.h" +#include "src/inspector/protocol/Schema.h" + +#include "include/v8-inspector.h" + +namespace v8_inspector { + +class InjectedScript; +class RemoteObjectIdBase; +class V8ConsoleAgentImpl; +class V8DebuggerAgentImpl; +class V8DebuggerBarrier; +class V8InspectorImpl; +class V8HeapProfilerAgentImpl; +class V8ProfilerAgentImpl; +class V8RuntimeAgentImpl; +class V8SchemaAgentImpl; + +using protocol::Response; + +class V8InspectorSessionImpl : public V8InspectorSession, + public protocol::FrontendChannel { + public: + static std::unique_ptr create( + V8InspectorImpl*, int contextGroupId, int sessionId, + V8Inspector::Channel*, StringView state, + v8_inspector::V8Inspector::ClientTrustLevel, + std::shared_ptr); + ~V8InspectorSessionImpl() override; + V8InspectorSessionImpl(const V8InspectorSessionImpl&) = delete; + V8InspectorSessionImpl& operator=(const V8InspectorSessionImpl&) = delete; + + V8InspectorImpl* inspector() const { return m_inspector; } + V8ConsoleAgentImpl* consoleAgent() { return m_consoleAgent.get(); } + V8DebuggerAgentImpl* debuggerAgent() { return m_debuggerAgent.get(); } + V8SchemaAgentImpl* schemaAgent() { return m_schemaAgent.get(); } + V8ProfilerAgentImpl* profilerAgent() { return m_profilerAgent.get(); } + V8RuntimeAgentImpl* runtimeAgent() { return m_runtimeAgent.get(); } + int contextGroupId() const { return m_contextGroupId; } + int sessionId() const { return m_sessionId; } + + std::unique_ptr + initializeCommandLineAPIScope(int executionContextId) override; + + Response findInjectedScript(int contextId, InjectedScript*&); + Response findInjectedScript(RemoteObjectIdBase*, InjectedScript*&); + void reset(); + void discardInjectedScripts(); + void reportAllContexts(V8RuntimeAgentImpl*); + void setCustomObjectFormatterEnabled(bool); + std::unique_ptr wrapObject( + v8::Local, v8::Local, const String16& groupName, + bool generatePreview); + std::unique_ptr wrapTable( + v8::Local, v8::Local table, + v8::MaybeLocal columns); + std::vector> supportedDomainsImpl(); + Response unwrapObject(const String16& objectId, v8::Local*, + v8::Local*, String16* objectGroup); + void releaseObjectGroup(const String16& objectGroup); + + // V8InspectorSession implementation. + void dispatchProtocolMessage(StringView message) override; + std::vector state() override; + std::vector> supportedDomains() + override; + void addInspectedObject( + std::unique_ptr) override; + void schedulePauseOnNextStatement(StringView breakReason, + StringView breakDetails) override; + void cancelPauseOnNextStatement() override; + void breakProgram(StringView breakReason, StringView breakDetails) override; + void setSkipAllPauses(bool) override; + void resume(bool terminateOnResume = false) override; + void stepOver() override; + std::vector> + searchInTextByLines(StringView text, StringView query, bool caseSensitive, + bool isRegex) override; + void releaseObjectGroup(StringView objectGroup) override; + bool unwrapObject(std::unique_ptr*, StringView objectId, + v8::Local*, v8::Local*, + std::unique_ptr* objectGroup) override; + std::unique_ptr wrapObject( + v8::Local, v8::Local, StringView groupName, + bool generatePreview) override; + + V8InspectorSession::Inspectable* inspectedObject(unsigned num); + static const unsigned kInspectedObjectBufferSize = 5; + + void triggerPreciseCoverageDeltaUpdate(StringView occasion) override; + void stop() override; + + V8Inspector::ClientTrustLevel clientTrustLevel() { + return m_clientTrustLevel; + } + + private: + V8InspectorSessionImpl(V8InspectorImpl*, int contextGroupId, int sessionId, + V8Inspector::Channel*, StringView state, + V8Inspector::ClientTrustLevel, + std::shared_ptr); + protocol::DictionaryValue* agentState(const String16& name); + + // protocol::FrontendChannel implementation. + void SendProtocolResponse( + int callId, std::unique_ptr message) override; + void SendProtocolNotification( + std::unique_ptr message) override; + void FallThrough(int callId, v8_crdtp::span method, + v8_crdtp::span message) override; + void FlushProtocolNotifications() override; + + std::unique_ptr serializeForFrontend( + std::unique_ptr message); + int m_contextGroupId; + int m_sessionId; + V8InspectorImpl* m_inspector; + V8Inspector::Channel* m_channel; + bool m_customObjectFormatterEnabled; + + protocol::UberDispatcher m_dispatcher; + std::unique_ptr m_state; + + std::unique_ptr m_runtimeAgent; + std::unique_ptr m_debuggerAgent; + std::unique_ptr m_heapProfilerAgent; + std::unique_ptr m_profilerAgent; + std::unique_ptr m_consoleAgent; + std::unique_ptr m_schemaAgent; + std::vector> + m_inspectedObjects; + bool use_binary_protocol_ = false; + V8Inspector::ClientTrustLevel m_clientTrustLevel = V8Inspector::kUntrusted; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_INSPECTOR_SESSION_IMPL_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-profiler-agent-impl.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-profiler-agent-impl.h new file mode 100644 index 000000000..6e76ff57f --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-profiler-agent-impl.h @@ -0,0 +1,83 @@ +// Copyright 2015 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_INSPECTOR_V8_PROFILER_AGENT_IMPL_H_ +#define V8_INSPECTOR_V8_PROFILER_AGENT_IMPL_H_ + +#include +#include + +#include "src/base/macros.h" +#include "src/inspector/protocol/Forward.h" +#include "src/inspector/protocol/Profiler.h" + +namespace v8 { +class CpuProfiler; +class Isolate; +} // namespace v8 + +namespace v8_inspector { + +class V8InspectorSessionImpl; + +using protocol::Maybe; +using protocol::Response; + +class V8ProfilerAgentImpl : public protocol::Profiler::Backend { + public: + V8ProfilerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, + protocol::DictionaryValue* state); + ~V8ProfilerAgentImpl() override; + V8ProfilerAgentImpl(const V8ProfilerAgentImpl&) = delete; + V8ProfilerAgentImpl& operator=(const V8ProfilerAgentImpl&) = delete; + + bool enabled() const { return m_enabled; } + void restore(); + + Response enable() override; + Response disable() override; + Response setSamplingInterval(int) override; + Response start() override; + Response stop(std::unique_ptr*) override; + + Response startPreciseCoverage(Maybe binary, Maybe detailed, + Maybe allow_triggered_updates, + double* out_timestamp) override; + Response stopPreciseCoverage() override; + Response takePreciseCoverage( + std::unique_ptr>* + out_result, + double* out_timestamp) override; + Response getBestEffortCoverage( + std::unique_ptr>* + out_result) override; + + void consoleProfile(const String16& title); + void consoleProfileEnd(const String16& title); + + void triggerPreciseCoverageDeltaUpdate(const String16& occasion); + + private: + String16 nextProfileId(); + + void startProfiling(const String16& title); + std::unique_ptr stopProfiling( + const String16& title, bool serialize); + + V8InspectorSessionImpl* m_session; + v8::Isolate* m_isolate; + v8::CpuProfiler* m_profiler = nullptr; + protocol::DictionaryValue* m_state; + protocol::Profiler::Frontend m_frontend; + bool m_enabled = false; + bool m_recordingCPUProfile = false; + class ProfileDescriptor; + std::vector m_startedProfiles; + String16 m_frontendInitiatedProfileId; + int m_startedProfilesCount = 0; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_PROFILER_AGENT_IMPL_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-regex.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-regex.h new file mode 100644 index 000000000..75d972f15 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-regex.h @@ -0,0 +1,41 @@ +// 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_INSPECTOR_V8_REGEX_H_ +#define V8_INSPECTOR_V8_REGEX_H_ + +#include "include/v8-persistent-handle.h" +#include "src/base/macros.h" +#include "src/inspector/string-16.h" + +namespace v8 { +class RegExp; +} + +namespace v8_inspector { + +class V8InspectorImpl; + +enum MultilineMode { MultilineDisabled, MultilineEnabled }; + +class V8Regex { + public: + V8Regex(V8InspectorImpl*, const String16&, bool caseSensitive, + bool multiline = false); + V8Regex(const V8Regex&) = delete; + V8Regex& operator=(const V8Regex&) = delete; + int match(const String16&, int startFrom = 0, + int* matchLength = nullptr) const; + bool isValid() const { return !m_regex.IsEmpty(); } + const String16& errorMessage() const { return m_errorMessage; } + + private: + V8InspectorImpl* m_inspector; + v8::Global m_regex; + String16 m_errorMessage; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_REGEX_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-runtime-agent-impl.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-runtime-agent-impl.h new file mode 100644 index 000000000..4bb0e8711 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-runtime-agent-impl.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef V8_INSPECTOR_V8_RUNTIME_AGENT_IMPL_H_ +#define V8_INSPECTOR_V8_RUNTIME_AGENT_IMPL_H_ + +#include +#include + +#include "include/v8-persistent-handle.h" +#include "src/base/macros.h" +#include "src/inspector/protocol/Forward.h" +#include "src/inspector/protocol/Runtime.h" + +namespace v8 { +class Script; +} // namespace v8 + +namespace v8_inspector { + +class InjectedScript; +class InspectedContext; +class RemoteObjectIdBase; +class V8ConsoleMessage; +class V8DebuggerBarrier; +class V8InspectorImpl; +class V8InspectorSessionImpl; + +using protocol::Response; +using protocol::Maybe; + +class V8RuntimeAgentImpl : public protocol::Runtime::Backend { + public: + V8RuntimeAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, + protocol::DictionaryValue* state, + std::shared_ptr); + ~V8RuntimeAgentImpl() override; + V8RuntimeAgentImpl(const V8RuntimeAgentImpl&) = delete; + V8RuntimeAgentImpl& operator=(const V8RuntimeAgentImpl&) = delete; + void restore(); + + // Part of the protocol. + Response enable() override; + Response disable() override; + void evaluate(const String16& expression, Maybe objectGroup, + Maybe includeCommandLineAPI, Maybe silent, + Maybe executionContextId, Maybe returnByValue, + Maybe generatePreview, Maybe userGesture, + Maybe awaitPromise, Maybe throwOnSideEffect, + Maybe timeout, Maybe disableBreaks, + Maybe replMode, Maybe allowUnsafeEvalBlockedByCSP, + Maybe uniqueContextId, + Maybe generateWebDriverValue, + std::unique_ptr) override; + void awaitPromise(const String16& promiseObjectId, Maybe returnByValue, + Maybe generatePreview, + std::unique_ptr) override; + void callFunctionOn( + const String16& expression, Maybe objectId, + Maybe> optionalArguments, + Maybe silent, Maybe returnByValue, + Maybe generatePreview, Maybe userGesture, + Maybe awaitPromise, Maybe executionContextId, + Maybe objectGroup, Maybe throwOnSideEffect, + Maybe uniqueContextId, Maybe generateWebDriverValue, + std::unique_ptr) override; + Response releaseObject(const String16& objectId) override; + Response getProperties( + const String16& objectId, Maybe ownProperties, + Maybe accessorPropertiesOnly, Maybe generatePreview, + Maybe nonIndexedPropertiesOnly, + std::unique_ptr>* + result, + Maybe>* + internalProperties, + Maybe>* + privateProperties, + Maybe*) override; + Response releaseObjectGroup(const String16& objectGroup) override; + Response runIfWaitingForDebugger() override; + Response setCustomObjectFormatterEnabled(bool) override; + Response setMaxCallStackSizeToCapture(int) override; + Response discardConsoleEntries() override; + Response compileScript(const String16& expression, const String16& sourceURL, + bool persistScript, Maybe executionContextId, + Maybe*, + Maybe*) override; + void runScript(const String16&, Maybe executionContextId, + Maybe objectGroup, Maybe silent, + Maybe includeCommandLineAPI, Maybe returnByValue, + Maybe generatePreview, Maybe awaitPromise, + std::unique_ptr) override; + Response queryObjects( + const String16& prototypeObjectId, Maybe objectGroup, + std::unique_ptr* objects) override; + Response globalLexicalScopeNames( + Maybe executionContextId, + std::unique_ptr>* outNames) override; + Response getIsolateId(String16* outIsolateId) override; + Response getHeapUsage(double* out_usedSize, double* out_totalSize) override; + void terminateExecution( + std::unique_ptr callback) override; + + Response addBinding(const String16& name, Maybe executionContextId, + Maybe executionContextName) override; + Response removeBinding(const String16& name) override; + void addBindings(InspectedContext* context); + Response getExceptionDetails(const String16& errorObjectId, + Maybe* + out_exceptionDetails) override; + + void reset(); + void reportExecutionContextCreated(InspectedContext*); + void reportExecutionContextDestroyed(InspectedContext*); + void inspect(std::unique_ptr objectToInspect, + std::unique_ptr hints, + int executionContextId); + void messageAdded(V8ConsoleMessage*); + bool enabled() const { return m_enabled; } + + private: + bool reportMessage(V8ConsoleMessage*, bool generatePreview); + + static void bindingCallback(const v8::FunctionCallbackInfo& args); + void bindingCalled(const String16& name, const String16& payload, + int executionContextId); + void addBinding(InspectedContext* context, const String16& name); + + V8InspectorSessionImpl* m_session; + protocol::DictionaryValue* m_state; + protocol::Runtime::Frontend m_frontend; + V8InspectorImpl* m_inspector; + std::shared_ptr m_debuggerBarrier; + bool m_enabled; + std::unordered_map>> + m_compiledScripts; + // Binding name -> executionContextIds mapping. + std::unordered_map> m_activeBindings; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_RUNTIME_AGENT_IMPL_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-schema-agent-impl.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-schema-agent-impl.h new file mode 100644 index 000000000..448529514 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-schema-agent-impl.h @@ -0,0 +1,38 @@ +// 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_INSPECTOR_V8_SCHEMA_AGENT_IMPL_H_ +#define V8_INSPECTOR_V8_SCHEMA_AGENT_IMPL_H_ + +#include + +#include "src/base/macros.h" +#include "src/inspector/protocol/Forward.h" +#include "src/inspector/protocol/Schema.h" + +namespace v8_inspector { + +class V8InspectorSessionImpl; + +using protocol::Response; + +class V8SchemaAgentImpl : public protocol::Schema::Backend { + public: + V8SchemaAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, + protocol::DictionaryValue* state); + ~V8SchemaAgentImpl() override; + V8SchemaAgentImpl(const V8SchemaAgentImpl&) = delete; + V8SchemaAgentImpl& operator=(const V8SchemaAgentImpl&) = delete; + + Response getDomains( + std::unique_ptr>*) override; + + private: + V8InspectorSessionImpl* m_session; + protocol::Schema::Frontend m_frontend; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_SCHEMA_AGENT_IMPL_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-stack-trace-impl.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-stack-trace-impl.h new file mode 100644 index 000000000..bf793b0b5 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-stack-trace-impl.h @@ -0,0 +1,153 @@ +// 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_INSPECTOR_V8_STACK_TRACE_IMPL_H_ +#define V8_INSPECTOR_V8_STACK_TRACE_IMPL_H_ + +#include +#include + +#include "include/v8-inspector.h" +#include "include/v8-local-handle.h" +#include "src/base/macros.h" +#include "src/inspector/protocol/Runtime.h" +#include "src/inspector/string-16.h" + +namespace v8 { +class StackFrame; +class StackTrace; +} // namespace v8 + +namespace v8_inspector { + +class AsyncStackTrace; +class V8Debugger; +struct V8StackTraceId; + +class StackFrame { + public: + StackFrame(String16&& functionName, int scriptId, String16&& sourceURL, + int lineNumber, int columnNumber, bool hasSourceURLComment); + ~StackFrame() = default; + + const String16& functionName() const; + int scriptId() const; + const String16& sourceURL() const; + int lineNumber() const; // 0-based. + int columnNumber() const; // 0-based. + std::unique_ptr buildInspectorObject( + V8InspectorClient* client) const; + bool isEqual(StackFrame* frame) const; + + private: + String16 m_functionName; + int m_scriptId; + String16 m_sourceURL; + int m_lineNumber; // 0-based. + int m_columnNumber; // 0-based. + bool m_hasSourceURLComment; +}; + +class V8StackTraceImpl : public V8StackTrace { + public: + static constexpr int kDefaultMaxCallStackSizeToCapture = 200; + + static std::unique_ptr create(V8Debugger*, + v8::Local, + int maxStackSize); + static std::unique_ptr capture(V8Debugger*, + int maxStackSize); + + ~V8StackTraceImpl() override; + V8StackTraceImpl(const V8StackTraceImpl&) = delete; + V8StackTraceImpl& operator=(const V8StackTraceImpl&) = delete; + std::unique_ptr buildInspectorObjectImpl( + V8Debugger* debugger) const; + + std::unique_ptr buildInspectorObjectImpl( + V8Debugger* debugger, int maxAsyncDepth) const; + + // V8StackTrace implementation. + // This method drops the async stack trace. + std::unique_ptr clone() override; + StringView firstNonEmptySourceURL() const override; + bool isEmpty() const override; + StringView topSourceURL() const override; + int topLineNumber() const override; // 1-based. + int topColumnNumber() const override; // 1-based. + int topScriptId() const override; + StringView topFunctionName() const override; + std::unique_ptr buildInspectorObject( + int maxAsyncDepth) const override; + std::unique_ptr toString() const override; + + bool isEqualIgnoringTopFrame(V8StackTraceImpl* stackTrace) const; + + std::vector frames() const override; + + private: + V8StackTraceImpl(std::vector> frames, + int maxAsyncDepth, + std::shared_ptr asyncParent, + const V8StackTraceId& externalParent); + + class StackFrameIterator { + public: + explicit StackFrameIterator(const V8StackTraceImpl* stackTrace); + + void next(); + StackFrame* frame(); + bool done(); + + private: + std::vector>::const_iterator m_currentIt; + std::vector>::const_iterator m_currentEnd; + AsyncStackTrace* m_parent; + }; + + std::vector> m_frames; + int m_maxAsyncDepth; + std::weak_ptr m_asyncParent; + V8StackTraceId m_externalParent; +}; + +class AsyncStackTrace { + public: + AsyncStackTrace(const AsyncStackTrace&) = delete; + AsyncStackTrace& operator=(const AsyncStackTrace&) = delete; + static std::shared_ptr capture(V8Debugger*, + const String16& description, + bool skipTopFrame = false); + static uintptr_t store(V8Debugger* debugger, + std::shared_ptr stack); + + std::unique_ptr buildInspectorObject( + V8Debugger* debugger, int maxAsyncDepth) const; + + const String16& description() const; + std::weak_ptr parent() const; + bool isEmpty() const; + const V8StackTraceId& externalParent() const { return m_externalParent; } + + const std::vector>& frames() const { + return m_frames; + } + + private: + AsyncStackTrace(const String16& description, + std::vector> frames, + std::shared_ptr asyncParent, + const V8StackTraceId& externalParent); + + uintptr_t m_id; + String16 m_description; + + std::vector> m_frames; + std::weak_ptr m_asyncParent; + V8StackTraceId m_externalParent; +}; + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_STACK_TRACE_IMPL_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-string-conversions.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-string-conversions.h new file mode 100644 index 000000000..eb33c6816 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-string-conversions.h @@ -0,0 +1,18 @@ +// Copyright 2019 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_INSPECTOR_V8_STRING_CONVERSIONS_H_ +#define V8_INSPECTOR_V8_STRING_CONVERSIONS_H_ + +#include +#include + +// Conversion routines between UT8 and UTF16, used by string-16.{h,cc}. You may +// want to use string-16.h directly rather than these. +namespace v8_inspector { +std::basic_string UTF8ToUTF16(const char* stringStart, size_t length); +std::string UTF16ToUTF8(const uint16_t* stringStart, size_t length); +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_STRING_CONVERSIONS_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-value-utils.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-value-utils.h new file mode 100644 index 000000000..7eae23d9b --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-value-utils.h @@ -0,0 +1,22 @@ +// 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_INSPECTOR_V8_VALUE_UTILS_H_ +#define V8_INSPECTOR_V8_VALUE_UTILS_H_ + +#include "include/v8-local-handle.h" +#include "src/inspector/protocol/Protocol.h" + +namespace v8_inspector { + +v8::Maybe createDataProperty(v8::Local, + v8::Local, + v8::Local key, + v8::Local); +v8::Maybe createDataProperty(v8::Local, v8::Local, + int index, v8::Local); + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_VALUE_UTILS_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-webdriver-serializer.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-webdriver-serializer.h new file mode 100644 index 000000000..1a9c2396a --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/v8-webdriver-serializer.h @@ -0,0 +1,24 @@ +// 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 V8_INSPECTOR_V8_WEBDRIVER_SERIALIZER_H_ +#define V8_INSPECTOR_V8_WEBDRIVER_SERIALIZER_H_ + +#include "include/v8-container.h" +#include "include/v8-context.h" +#include "include/v8-exception.h" +#include "include/v8-regexp.h" +#include "src/inspector/protocol/Runtime.h" +#include "src/inspector/v8-value-utils.h" + +namespace v8_inspector { +class V8WebDriverSerializer { + public: + static std::unique_ptr serializeV8Value( + v8::Local value, v8::Local context, + int max_depth); +}; +} // namespace v8_inspector + +#endif // V8_INSPECTOR_V8_WEBDRIVER_SERIALIZER_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/value-mirror.h b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/value-mirror.h new file mode 100644 index 000000000..71555680a --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/src/inspector/value-mirror.h @@ -0,0 +1,102 @@ +// Copyright 2018 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_INSPECTOR_VALUE_MIRROR_H_ +#define V8_INSPECTOR_VALUE_MIRROR_H_ + +#include + +#include "include/v8-inspector.h" +#include "include/v8-local-handle.h" +#include "src/base/macros.h" +#include "src/inspector/protocol/Protocol.h" +#include "src/inspector/protocol/Runtime.h" +#include "src/inspector/string-16.h" + +namespace v8_inspector { + +class ValueMirror; +enum class WrapMode; + +struct PrivatePropertyMirror { + String16 name; + std::unique_ptr value; + std::unique_ptr getter; + std::unique_ptr setter; +}; + +struct InternalPropertyMirror { + String16 name; + std::unique_ptr value; +}; + +struct PropertyMirror { + String16 name; + bool writable; + bool configurable; + bool enumerable; + bool isOwn; + bool isIndex; + bool isSynthetic; + std::unique_ptr value; + std::unique_ptr getter; + std::unique_ptr setter; + std::unique_ptr symbol; + std::unique_ptr exception; +}; + +class ValueMirror { + public: + virtual ~ValueMirror(); + + static std::unique_ptr create(v8::Local context, + v8::Local value); + virtual protocol::Response buildRemoteObject( + v8::Local context, WrapMode mode, + std::unique_ptr* result) const = 0; + virtual void buildPropertyPreview( + v8::Local context, const String16& name, + std::unique_ptr*) const {} + virtual void buildObjectPreview( + v8::Local context, bool generatePreviewForTable, + int* nameLimit, int* indexLimit, + std::unique_ptr*) const {} + virtual void buildEntryPreview( + v8::Local context, int* nameLimit, int* indexLimit, + std::unique_ptr*) const {} + virtual v8::Local v8Value() const = 0; + virtual std::unique_ptr + buildWebDriverValue(v8::Local context, int max_depth) const = 0; + + class PropertyAccumulator { + public: + virtual ~PropertyAccumulator() = default; + virtual bool Add(PropertyMirror mirror) = 0; + }; + static bool getProperties(v8::Local context, + v8::Local object, bool ownProperties, + bool accessorPropertiesOnly, + bool nonIndexedPropertiesOnly, + PropertyAccumulator* accumulator); + static void getInternalProperties( + v8::Local context, v8::Local object, + std::vector* mirrors); + static std::vector getPrivateProperties( + v8::Local context, v8::Local object, + bool accessorPropertiesOnly); +}; + +protocol::Response toProtocolValue(v8::Local context, + v8::Local value, int maxDepth, + std::unique_ptr* result); +protocol::Response arrayToProtocolValue( + v8::Local context, v8::Local array, int maxDepth, + std::unique_ptr* result); +protocol::Response objectToProtocolValue( + v8::Local context, v8::Local object, int maxDepth, + std::unique_ptr* result); + +} // namespace v8_inspector + +#endif // V8_INSPECTOR_VALUE_MIRROR_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/cbor.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/cbor.h new file mode 100644 index 000000000..9e71e0716 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/cbor.h @@ -0,0 +1,326 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_CBOR_H_ +#define V8_CRDTP_CBOR_H_ + +#include +#include +#include +#include +#include + +#include "export.h" +#include "parser_handler.h" +#include "span.h" + +namespace v8_crdtp { +namespace cbor { +// The binary encoding for the inspector protocol follows the CBOR specification +// (RFC 7049). Additional constraints: +// - Only indefinite length maps and arrays are supported. +// - Maps and arrays are wrapped with an envelope, that is, a +// CBOR tag with value 24 followed by a byte string specifying +// the byte length of the enclosed map / array. The byte string +// must use a 32 bit wide length. +// - At the top level, a message must be an indefinite length map +// wrapped by an envelope. +// - Maximal size for messages is 2^32 (4 GB). +// - For scalars, we support only the int32_t range, encoded as +// UNSIGNED/NEGATIVE (major types 0 / 1). +// - UTF16 strings, including with unbalanced surrogate pairs, are encoded +// as CBOR BYTE_STRING (major type 2). For such strings, the number of +// bytes encoded must be even. +// - UTF8 strings (major type 3) are supported. +// - 7 bit US-ASCII strings must always be encoded as UTF8 strings, never +// as UTF16 strings. +// - Arbitrary byte arrays, in the inspector protocol called 'binary', +// are encoded as BYTE_STRING (major type 2), prefixed with a byte +// indicating base64 when rendered as JSON. + +// ============================================================================= +// Detecting CBOR content +// ============================================================================= + +// Checks whether |msg| is a cbor message. +bool IsCBORMessage(span msg); + +// Performs a leightweight check of |msg|. +// Disallows: +// - Empty message +// - Not starting with the two bytes 0xd8, 0x5a +// - Empty envelope (all length bytes are 0) +// - Not starting with a map after the envelope stanza +// DevTools messages should pass this check. +Status CheckCBORMessage(span msg); + +// ============================================================================= +// Encoding individual CBOR items +// ============================================================================= + +// Some constants for CBOR tokens that only take a single byte on the wire. +uint8_t EncodeTrue(); +uint8_t EncodeFalse(); +uint8_t EncodeNull(); +uint8_t EncodeIndefiniteLengthArrayStart(); +uint8_t EncodeIndefiniteLengthMapStart(); +uint8_t EncodeStop(); + +// Encodes |value| as |UNSIGNED| (major type 0) iff >= 0, or |NEGATIVE| +// (major type 1) iff < 0. +void EncodeInt32(int32_t value, std::vector* out); + +// Encodes a UTF16 string as a BYTE_STRING (major type 2). Each utf16 +// character in |in| is emitted with most significant byte first, +// appending to |out|. +void EncodeString16(span in, std::vector* out); + +// Encodes a UTF8 string |in| as STRING (major type 3). +void EncodeString8(span in, std::vector* out); + +// Encodes the given |latin1| string as STRING8. +// If any non-ASCII character is present, it will be represented +// as a 2 byte UTF8 sequence. +void EncodeFromLatin1(span latin1, std::vector* out); + +// Encodes the given |utf16| string as STRING8 if it's entirely US-ASCII. +// Otherwise, encodes as STRING16. +void EncodeFromUTF16(span utf16, std::vector* out); + +// Encodes arbitrary binary data in |in| as a BYTE_STRING (major type 2) with +// definitive length, prefixed with tag 22 indicating expected conversion to +// base64 (see RFC 7049, Table 3 and Section 2.4.4.2). +void EncodeBinary(span in, std::vector* out); + +// Encodes / decodes a double as Major type 7 (SIMPLE_VALUE), +// with additional info = 27, followed by 8 bytes in big endian. +void EncodeDouble(double value, std::vector* out); + +// ============================================================================= +// cbor::EnvelopeEncoder - for wrapping submessages +// ============================================================================= + +// An envelope indicates the byte length of a wrapped item. +// We use this for maps and array, which allows the decoder +// to skip such (nested) values whole sale. +// It's implemented as a CBOR tag (major type 6) with additional +// info = 24, followed by a byte string with a 32 bit length value; +// so the maximal structure that we can wrap is 2^32 bits long. +// See also: https://tools.ietf.org/html/rfc7049#section-2.4.4.1 +class EnvelopeEncoder { + public: + // Emits the envelope start bytes and records the position for the + // byte size in |byte_size_pos_|. Also emits empty bytes for the + // byte sisze so that encoding can continue. + void EncodeStart(std::vector* out); + // This records the current size in |out| at position byte_size_pos_. + // Returns true iff successful. + bool EncodeStop(std::vector* out); + + private: + size_t byte_size_pos_ = 0; +}; + +class EnvelopeHeader { + public: + EnvelopeHeader() = default; + ~EnvelopeHeader() = default; + + // Parse envelope. Implies that `in` accomodates the entire size of envelope. + static StatusOr Parse(span in); + // Parse envelope, but allow `in` to only include the beginning of the + // envelope. + static StatusOr ParseFromFragment(span in); + + size_t header_size() const { return header_size_; } + size_t content_size() const { return content_size_; } + size_t outer_size() const { return header_size_ + content_size_; } + + private: + EnvelopeHeader(size_t header_size, size_t content_size) + : header_size_(header_size), content_size_(content_size) {} + + size_t header_size_ = 0; + size_t content_size_ = 0; +}; + +// ============================================================================= +// cbor::NewCBOREncoder - for encoding from a streaming parser +// ============================================================================= + +// This can be used to convert to CBOR, by passing the return value to a parser +// that drives it. The handler will encode into |out|, and iff an error occurs +// it will set |status| to an error and clear |out|. Otherwise, |status.ok()| +// will be |true|. +std::unique_ptr NewCBOREncoder(std::vector* out, + Status* status); + +// ============================================================================= +// cbor::CBORTokenizer - for parsing individual CBOR items +// ============================================================================= + +// Tags for the tokens within a CBOR message that CBORTokenizer understands. +// Note that this is not the same terminology as the CBOR spec (RFC 7049), +// but rather, our adaptation. For instance, we lump unsigned and signed +// major type into INT32 here (and disallow values outside the int32_t range). +enum class CBORTokenTag { + // Encountered an error in the structure of the message. Consult + // status() for details. + ERROR_VALUE, + // Booleans and NULL. + TRUE_VALUE, + FALSE_VALUE, + NULL_VALUE, + // An int32_t (signed 32 bit integer). + INT32, + // A double (64 bit floating point). + DOUBLE, + // A UTF8 string. + STRING8, + // A UTF16 string. + STRING16, + // A binary string. + BINARY, + // Starts an indefinite length map; after the map start we expect + // alternating keys and values, followed by STOP. + MAP_START, + // Starts an indefinite length array; after the array start we + // expect values, followed by STOP. + ARRAY_START, + // Ends a map or an array. + STOP, + // An envelope indicator, wrapping a map or array. + // Internally this carries the byte length of the wrapped + // map or array. While CBORTokenizer::Next() will read / skip the entire + // envelope, CBORTokenizer::EnterEnvelope() reads the tokens + // inside of it. + ENVELOPE, + // We've reached the end there is nothing else to read. + DONE, +}; + +// The major types from RFC 7049 Section 2.1. +enum class MajorType { + UNSIGNED = 0, + NEGATIVE = 1, + BYTE_STRING = 2, + STRING = 3, + ARRAY = 4, + MAP = 5, + TAG = 6, + SIMPLE_VALUE = 7 +}; + +// CBORTokenizer segments a CBOR message, presenting the tokens therein as +// numbers, strings, etc. This is not a complete CBOR parser, but makes it much +// easier to implement one (e.g. ParseCBOR, above). It can also be used to parse +// messages partially. +class CBORTokenizer { + public: + explicit CBORTokenizer(span bytes); + ~CBORTokenizer(); + + // Identifies the current token that we're looking at, + // or ERROR_VALUE (in which ase ::Status() has details) + // or DONE (if we're past the last token). + CBORTokenTag TokenTag() const; + + // Advances to the next token. + void Next(); + // Can only be called if TokenTag() == CBORTokenTag::ENVELOPE. + // While Next() would skip past the entire envelope / what it's + // wrapping, EnterEnvelope positions the cursor inside of the envelope, + // letting the client explore the nested structure. + void EnterEnvelope(); + + // If TokenTag() is CBORTokenTag::ERROR_VALUE, then Status().error describes + // the error more precisely; otherwise it'll be set to Error::OK. + // In either case, Status().pos is the current position. + struct Status Status() const; + + // The following methods retrieve the token values. They can only + // be called if TokenTag() matches. + + // To be called only if ::TokenTag() == CBORTokenTag::INT32. + int32_t GetInt32() const; + + // To be called only if ::TokenTag() == CBORTokenTag::DOUBLE. + double GetDouble() const; + + // To be called only if ::TokenTag() == CBORTokenTag::STRING8. + span GetString8() const; + + // Wire representation for STRING16 is low byte first (little endian). + // To be called only if ::TokenTag() == CBORTokenTag::STRING16. + span GetString16WireRep() const; + + // To be called only if ::TokenTag() == CBORTokenTag::BINARY. + span GetBinary() const; + + // To be called only if ::TokenTag() == CBORTokenTag::ENVELOPE. + // Returns the envelope including its payload; message which + // can be passed to the CBORTokenizer constructor, which will + // then see the envelope token first (looking at it a second time, + // basically). + span GetEnvelope() const; + + // To be called only if ::TokenTag() == CBORTokenTag::ENVELOPE. + // Returns only the payload inside the envelope, e.g., a map + // or an array. This is not a complete message by our + // IsCBORMessage definition, since it doesn't include the + // enclosing envelope (the header, basically). + span GetEnvelopeContents() const; + + // To be called only if ::TokenTag() == CBORTokenTag::ENVELOPE. + // Returns the envelope header. + const EnvelopeHeader& GetEnvelopeHeader() const; + + private: + void ReadNextToken(); + void SetToken(CBORTokenTag token, size_t token_byte_length); + void SetError(Error error); + + const span bytes_; + CBORTokenTag token_tag_; + struct Status status_; + size_t token_byte_length_ = 0; + MajorType token_start_type_; + uint64_t token_start_internal_value_; + EnvelopeHeader envelope_header_; +}; + +// ============================================================================= +// cbor::ParseCBOR - for receiving streaming parser events for CBOR messages +// ============================================================================= + +// Parses a CBOR encoded message from |bytes|, sending events to +// |out|. If an error occurs, sends |out->HandleError|, and parsing stops. +// The client is responsible for discarding the already received information in +// that case. +void ParseCBOR(span bytes, ParserHandler* out); + +// ============================================================================= +// cbor::AppendString8EntryToMap - for limited in-place editing of messages +// ============================================================================= + +// Modifies the |cbor| message by appending a new key/value entry at the end +// of the map. Patches up the envelope size; Status.ok() iff successful. +// If not successful, |cbor| may be corrupted after this call. +Status AppendString8EntryToCBORMap(span string8_key, + span string8_value, + std::vector* cbor); + +namespace internals { // Exposed only for writing tests. +size_t ReadTokenStart(span bytes, + cbor::MajorType* type, + uint64_t* value); + +void WriteTokenStart(cbor::MajorType type, + uint64_t value, + std::vector* encoded); +} // namespace internals +} // namespace cbor +} // namespace v8_crdtp + +#endif // V8_CRDTP_CBOR_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/dispatch.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/dispatch.h new file mode 100644 index 000000000..b035e2193 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/dispatch.h @@ -0,0 +1,313 @@ +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_DISPATCH_H_ +#define V8_CRDTP_DISPATCH_H_ + +#include +#include +#include +#include +#include +#include "export.h" +#include "serializable.h" +#include "span.h" +#include "status.h" + +namespace v8_crdtp { +class DeserializerState; +class ErrorSupport; +class FrontendChannel; +namespace cbor { +class CBORTokenizer; +} // namespace cbor + +// ============================================================================= +// DispatchResponse - Error status and chaining / fall through +// ============================================================================= +enum class DispatchCode { + SUCCESS = 1, + FALL_THROUGH = 2, + // For historical reasons, these error codes correspond to commonly used + // XMLRPC codes (e.g. see METHOD_NOT_FOUND in + // https://github.com/python/cpython/blob/main/Lib/xmlrpc/client.py). + PARSE_ERROR = -32700, + INVALID_REQUEST = -32600, + METHOD_NOT_FOUND = -32601, + INVALID_PARAMS = -32602, + INTERNAL_ERROR = -32603, + SERVER_ERROR = -32000, + SESSION_NOT_FOUND = SERVER_ERROR - 1, +}; + +// Information returned by command handlers. Usually returned after command +// execution attempts. +class DispatchResponse { + public: + const std::string& Message() const { return message_; } + + DispatchCode Code() const { return code_; } + + bool IsSuccess() const { return code_ == DispatchCode::SUCCESS; } + bool IsFallThrough() const { return code_ == DispatchCode::FALL_THROUGH; } + bool IsError() const { return code_ < DispatchCode::SUCCESS; } + + static DispatchResponse Success(); + static DispatchResponse FallThrough(); + + // Indicates that a message could not be parsed. E.g., malformed JSON. + static DispatchResponse ParseError(std::string message); + + // Indicates that a request is lacking required top-level properties + // ('id', 'method'), has top-level properties of the wrong type, or has + // unknown top-level properties. + static DispatchResponse InvalidRequest(std::string message); + + // Indicates that a protocol method such as "Page.bringToFront" could not be + // dispatched because it's not known to the (domain) dispatcher. + static DispatchResponse MethodNotFound(std::string message); + + // Indicates that the params sent to a domain handler are invalid. + static DispatchResponse InvalidParams(std::string message); + + // Used for application level errors, e.g. within protocol agents. + static DispatchResponse InternalError(); + + // Used for application level errors, e.g. within protocol agents. + static DispatchResponse ServerError(std::string message); + + // Indicate that session with the id specified in the protocol message + // was not found (e.g. because it has already been detached). + static DispatchResponse SessionNotFound(std::string message); + + private: + DispatchResponse() = default; + DispatchCode code_; + std::string message_; +}; + +// ============================================================================= +// Dispatchable - a shallow parser for CBOR encoded DevTools messages +// ============================================================================= + +// This parser extracts only the known top-level fields from a CBOR encoded map; +// method, id, sessionId, and params. +class Dispatchable { + public: + // This constructor parses the |serialized| message. If successful, + // |ok()| will yield |true|, and |Method()|, |SessionId()|, |CallId()|, + // |Params()| can be used to access, the extracted contents. Otherwise, + // |ok()| will yield |false|, and |DispatchError()| can be + // used to send a response or notification to the client. + explicit Dispatchable(span serialized); + + // The serialized message that we just parsed. + span Serialized() const { return serialized_; } + + // Yields true if parsing was successful. This is cheaper than calling + // ::DispatchError(). + bool ok() const; + + // If !ok(), returns a DispatchResponse with appropriate code and error + // which can be sent to the client as a response or notification. + DispatchResponse DispatchError() const; + + // Top level field: the command to be executed, fully qualified by + // domain. E.g. "Page.createIsolatedWorld". + span Method() const { return method_; } + // Used to identify protocol connections attached to a specific + // target. See Target.attachToTarget, Target.setAutoAttach. + span SessionId() const { return session_id_; } + // The call id, a sequence number that's used in responses to indicate + // the request to which the response belongs. + int32_t CallId() const { return call_id_; } + bool HasCallId() const { return has_call_id_; } + // The payload of the request in CBOR format. The |Dispatchable| parser does + // not parse into this; it only provides access to its raw contents here. + span Params() const { return params_; } + + private: + bool MaybeParseProperty(cbor::CBORTokenizer* tokenizer); + bool MaybeParseCallId(cbor::CBORTokenizer* tokenizer); + bool MaybeParseMethod(cbor::CBORTokenizer* tokenizer); + bool MaybeParseParams(cbor::CBORTokenizer* tokenizer); + bool MaybeParseSessionId(cbor::CBORTokenizer* tokenizer); + + span serialized_; + + Status status_; + + bool has_call_id_ = false; + int32_t call_id_; + span method_; + bool params_seen_ = false; + span params_; + span session_id_; +}; + +// ============================================================================= +// Helpers for creating protocol cresponses and notifications. +// ============================================================================= + +// The resulting notifications can be sent to a protocol client, +// usually via a FrontendChannel (see frontend_channel.h). + +std::unique_ptr CreateErrorResponse( + int callId, + DispatchResponse dispatch_response); + +std::unique_ptr CreateErrorNotification( + DispatchResponse dispatch_response); + +std::unique_ptr CreateResponse( + int callId, + std::unique_ptr params); + +std::unique_ptr CreateNotification( + const char* method, + std::unique_ptr params = nullptr); + +// ============================================================================= +// DomainDispatcher - Dispatching betwen protocol methods within a domain. +// ============================================================================= + +// This class is subclassed by |DomainDispatcherImpl|, which we generate per +// DevTools domain. It contains routines called from the generated code, +// e.g. ::MaybeReportInvalidParams, which are optimized for small code size. +// The most important method is ::Dispatch, which implements method dispatch +// by command name lookup. +class DomainDispatcher { + public: + class WeakPtr { + public: + explicit WeakPtr(DomainDispatcher*); + ~WeakPtr(); + DomainDispatcher* get() { return dispatcher_; } + void dispose() { dispatcher_ = nullptr; } + + private: + DomainDispatcher* dispatcher_; + }; + + class Callback { + public: + virtual ~Callback(); + void dispose(); + + protected: + // |method| must point at static storage (a C++ string literal in practice). + Callback(std::unique_ptr backend_impl, + int call_id, + span method, + span message); + + void sendIfActive(std::unique_ptr partialMessage, + const DispatchResponse& response); + void fallThroughIfActive(); + + private: + std::unique_ptr backend_impl_; + int call_id_; + // Subclasses of this class are instantiated from generated code which + // passes a string literal for the method name to the constructor. So the + // storage for |method| is the binary of the running process. + span method_; + std::vector message_; + }; + + explicit DomainDispatcher(FrontendChannel*); + virtual ~DomainDispatcher(); + + // Given a |command_name| without domain qualification, looks up the + // corresponding method. If the method is not found, returns nullptr. + // Otherwise, Returns a closure that will parse the provided + // Dispatchable.params() to a protocol object and execute the + // apprpropriate method. If the parsing fails it will issue an + // error response on the frontend channel, otherwise it will execute the + // command. + virtual std::function Dispatch( + span command_name) = 0; + + // Sends a response to the client via the channel. + void sendResponse(int call_id, + const DispatchResponse&, + std::unique_ptr result = nullptr); + + void ReportInvalidParams(const Dispatchable& dispatchable, + const DeserializerState& state); + + FrontendChannel* channel() { return frontend_channel_; } + + void clearFrontend(); + + std::unique_ptr weakPtr(); + + private: + FrontendChannel* frontend_channel_; + std::unordered_set weak_ptrs_; +}; + +// ============================================================================= +// UberDispatcher - dispatches between domains (backends). +// ============================================================================= +class UberDispatcher { + public: + // Return type for ::Dispatch. + class DispatchResult { + public: + DispatchResult(bool method_found, std::function runnable); + + // Indicates whether the method was found, that is, it could be dispatched + // to a backend registered with this dispatcher. + bool MethodFound() const { return method_found_; } + + // Runs the dispatched result. This will send the appropriate error + // responses if the method wasn't found or if something went wrong during + // parameter parsing. + void Run(); + + private: + bool method_found_; + std::function runnable_; + }; + + // |frontend_hannel| can't be nullptr. + explicit UberDispatcher(FrontendChannel* frontend_channel); + virtual ~UberDispatcher(); + + // Dispatches the provided |dispatchable| considering all redirects and domain + // handlers registered with this uber dispatcher. Also see |DispatchResult|. + // |dispatchable.ok()| must hold - callers must check this separately and + // deal with errors. + DispatchResult Dispatch(const Dispatchable& dispatchable) const; + + // Invoked from generated code for wiring domain backends; that is, + // connecting domain handlers to an uber dispatcher. + // See ::Dispatcher::Wire(UberDispatcher*,Backend*). + FrontendChannel* channel() const { + assert(frontend_channel_); + return frontend_channel_; + } + + // Invoked from generated code for wiring domain backends; that is, + // connecting domain handlers to an uber dispatcher. + // See ::Dispatcher::Wire(UberDispatcher*,Backend*). + void WireBackend(span domain, + const std::vector, span>>&, + std::unique_ptr dispatcher); + + private: + DomainDispatcher* findDispatcher(span method); + FrontendChannel* const frontend_channel_; + // Pairs of ascii strings of the form ("Domain1.method1","Domain2.method2") + // indicating that the first element of each pair redirects to the second. + // Sorted by first element. + std::vector, span>> redirects_; + // Domain dispatcher instances, sorted by their domain name. + std::vector, std::unique_ptr>> + dispatchers_; +}; +} // namespace v8_crdtp + +#endif // V8_CRDTP_DISPATCH_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/error_support.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/error_support.h new file mode 100644 index 000000000..45d2ef7d9 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/error_support.h @@ -0,0 +1,62 @@ +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_ERROR_SUPPORT_H_ +#define V8_CRDTP_ERROR_SUPPORT_H_ + +#include +#include +#include +#include "export.h" +#include "span.h" + +namespace v8_crdtp { +// ============================================================================= +// ErrorSupport - For tracking errors in tree structures. +// ============================================================================= + +// This abstraction is used when converting between Values and inspector +// objects, e.g. in lib/ValueConversions_{h,cc}.template. As the processing +// enters and exits a branch, we call Push / Pop. Within the branch, +// we either set the name or an index (in case we're processing the element of a +// list/vector). Only once an error is seen, the path which is now on the +// stack is materialized and prefixes the error message. E.g., +// "foo.bar.2: some error". After error collection, ::Errors() is used to +// access the message. +class ErrorSupport { + public: + // Push / Pop operations for the path segments; after Push, either SetName or + // SetIndex must be called exactly once. + void Push(); + void Pop(); + + // Sets the name of the current segment on the stack; e.g. a field name. + // |name| must be a C++ string literal in 7 bit US-ASCII. + void SetName(const char* name); + // Sets the index of the current segment on the stack; e.g. an array index. + void SetIndex(size_t index); + + // Materializes the error internally. |error| must be a C++ string literal + // in 7 bit US-ASCII. + void AddError(const char* error); + + // Returns the semicolon-separated list of errors as in 7 bit ASCII. + span Errors() const; + + private: + enum SegmentType { EMPTY, NAME, INDEX }; + struct Segment { + SegmentType type = EMPTY; + union { + const char* name; + size_t index; + }; + }; + std::vector stack_; + std::string errors_; +}; + +} // namespace v8_crdtp + +#endif // V8_CRDTP_ERROR_SUPPORT_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/export.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/export.h new file mode 100644 index 000000000..b814e16e5 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/export.h @@ -0,0 +1,6 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is V8 specific. It's not rolled from the upstream project. +// CRDTP doesn't export symbols from V8, so it's empty. diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/find_by_first.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/find_by_first.h new file mode 100644 index 000000000..2469bd765 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/find_by_first.h @@ -0,0 +1,58 @@ +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_FIND_BY_FIRST_H_ +#define V8_CRDTP_FIND_BY_FIRST_H_ + +#include +#include +#include +#include + +#include "export.h" +#include "span.h" + +namespace v8_crdtp { +// ============================================================================= +// FindByFirst - Retrieval from a sorted vector that's keyed by span. +// ============================================================================= + +// Given a vector of pairs sorted by the first element of each pair, find +// the corresponding value given a key to be compared to the first element. +// Together with std::inplace_merge and pre-sorting or std::sort, this can +// be used to implement a minimalistic equivalent of Chromium's flat_map. + +// In this variant, the template parameter |T| is a value type and a +// |default_value| is provided. +template +T FindByFirst(const std::vector, T>>& sorted_by_first, + span key, + T default_value) { + auto it = std::lower_bound( + sorted_by_first.begin(), sorted_by_first.end(), key, + [](const std::pair, T>& left, span right) { + return SpanLessThan(left.first, right); + }); + return (it != sorted_by_first.end() && SpanEquals(it->first, key)) + ? it->second + : default_value; +} + +// In this variant, the template parameter |T| is a class or struct that's +// instantiated in std::unique_ptr, and we return either a T* or a nullptr. +template +T* FindByFirst(const std::vector, std::unique_ptr>>& + sorted_by_first, + span key) { + auto it = std::lower_bound( + sorted_by_first.begin(), sorted_by_first.end(), key, + [](const std::pair, std::unique_ptr>& left, + span right) { return SpanLessThan(left.first, right); }); + return (it != sorted_by_first.end() && SpanEquals(it->first, key)) + ? it->second.get() + : nullptr; +} +} // namespace v8_crdtp + +#endif // V8_CRDTP_FIND_BY_FIRST_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/frontend_channel.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/frontend_channel.h new file mode 100644 index 000000000..9f12930a1 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/frontend_channel.h @@ -0,0 +1,47 @@ +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_FRONTEND_CHANNEL_H_ +#define V8_CRDTP_FRONTEND_CHANNEL_H_ + +#include +#include +#include "export.h" +#include "serializable.h" +#include "span.h" + +namespace v8_crdtp { +// ============================================================================= +// FrontendChannel - For sending notifications and responses to protocol clients +// ============================================================================= +class FrontendChannel { + public: + virtual ~FrontendChannel() = default; + + // Sends protocol responses and notifications. The |call_id| parameter is + // seemingly redundant because it's also included in the message, but + // responses may be sent from an untrusted source to a trusted process (e.g. + // from Chromium's renderer (blink) to the browser process), which needs + // to be able to match the response to an earlier request without parsing the + // messsage. + virtual void SendProtocolResponse(int call_id, + std::unique_ptr message) = 0; + virtual void SendProtocolNotification( + std::unique_ptr message) = 0; + + // FallThrough indicates that |message| should be handled in another layer. + // Usually this means the layer responding to the message didn't handle it, + // but in some cases messages are handled by multiple layers (e.g. both + // the embedder and the content layer in Chromium). + virtual void FallThrough(int call_id, + span method, + span message) = 0; + + // Session implementations may queue notifications for performance or + // other considerations; this is a hook for domain handlers to manually flush. + virtual void FlushProtocolNotifications() = 0; +}; +} // namespace v8_crdtp + +#endif // V8_CRDTP_FRONTEND_CHANNEL_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/glue.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/glue.h new file mode 100644 index 000000000..3a69a5409 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/glue.h @@ -0,0 +1,80 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_GLUE_H_ +#define V8_CRDTP_GLUE_H_ + +#include +#include + +namespace v8_crdtp { +namespace glue { +// ============================================================================= +// glue::detail::PtrMaybe, glue::detail::ValueMaybe, templates for optional +// pointers / values which are used in ../lib/Forward_h.template. +// ============================================================================= +namespace detail { +template +class PtrMaybe { + public: + PtrMaybe() = default; + PtrMaybe(std::unique_ptr value) : value_(std::move(value)) {} + PtrMaybe(PtrMaybe&& other) noexcept : value_(std::move(other.value_)) {} + void operator=(std::unique_ptr value) { value_ = std::move(value); } + T* fromJust() const { + assert(value_); + return value_.get(); + } + T* fromMaybe(T* default_value) const { + return value_ ? value_.get() : default_value; + } + bool isJust() const { return value_ != nullptr; } + std::unique_ptr takeJust() { + assert(value_); + return std::move(value_); + } + + private: + std::unique_ptr value_; +}; + +template +class ValueMaybe { + public: + ValueMaybe() : is_just_(false), value_() {} + ValueMaybe(T value) : is_just_(true), value_(std::move(value)) {} + ValueMaybe(ValueMaybe&& other) noexcept + : is_just_(other.is_just_), value_(std::move(other.value_)) {} + void operator=(T value) { + value_ = value; + is_just_ = true; + } + const T& fromJust() const { + assert(is_just_); + return value_; + } + const T& fromMaybe(const T& default_value) const { + return is_just_ ? value_ : default_value; + } + bool isJust() const { return is_just_; } + T takeJust() { + assert(is_just_); + is_just_ = false; + return std::move(value_); + } + + private: + bool is_just_; + T value_; +}; +} // namespace detail +} // namespace glue +} // namespace v8_crdtp + +#define PROTOCOL_DISALLOW_COPY(ClassName) \ + private: \ + ClassName(const ClassName&) = delete; \ + ClassName& operator=(const ClassName&) = delete + +#endif // V8_CRDTP_GLUE_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/json.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/json.h new file mode 100644 index 000000000..3600b9038 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/json.h @@ -0,0 +1,52 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_JSON_H_ +#define V8_CRDTP_JSON_H_ + +#include +#include +#include "export.h" +#include "parser_handler.h" + +namespace v8_crdtp { +namespace json { +// ============================================================================= +// json::NewJSONEncoder - for encoding streaming parser events as JSON +// ============================================================================= + +// Returns a handler object which will write ascii characters to |out|. +// |status->ok()| will be false iff the handler routine HandleError() is called. +// In that case, we'll stop emitting output. +// Except for calling the HandleError routine at any time, the client +// code must call the Handle* methods in an order in which they'd occur +// in valid JSON; otherwise we may crash (the code uses assert). +std::unique_ptr NewJSONEncoder(std::vector* out, + Status* status); + +std::unique_ptr NewJSONEncoder(std::string* out, Status* status); + +// ============================================================================= +// json::ParseJSON - for receiving streaming parser events for JSON +// ============================================================================= + +void ParseJSON(span chars, ParserHandler* handler); + +void ParseJSON(span chars, ParserHandler* handler); + +// ============================================================================= +// json::ConvertCBORToJSON, json::ConvertJSONToCBOR - for transcoding +// ============================================================================= + +Status ConvertCBORToJSON(span cbor, std::string* json); + +Status ConvertCBORToJSON(span cbor, std::vector* json); + +Status ConvertJSONToCBOR(span json, std::vector* cbor); + +Status ConvertJSONToCBOR(span json, std::vector* cbor); +} // namespace json +} // namespace v8_crdtp + +#endif // V8_CRDTP_JSON_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/json_platform.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/json_platform.h new file mode 100644 index 000000000..80e57df25 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/json_platform.h @@ -0,0 +1,26 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_JSON_PLATFORM_H_ +#define V8_CRDTP_JSON_PLATFORM_H_ + +#include + +namespace v8_crdtp { +namespace json { +// These routines are implemented in json_platform.cc, or in a +// platform-dependent (code-base dependent) custom replacement. +// E.g., json_platform_chromium.cc, json_platform_v8.cc. +namespace platform { +// Parses |str| into |result|. Returns false iff there are +// leftover characters or parsing errors. +bool StrToD(const char* str, double* result); + +// Prints |value| in a format suitable for JSON. +std::string DToStr(double value); +} // namespace platform +} // namespace json +} // namespace v8_crdtp + +#endif // V8_CRDTP_JSON_PLATFORM_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/maybe.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/maybe.h new file mode 100644 index 000000000..a476dd581 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/maybe.h @@ -0,0 +1,104 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef V8_CRDTP_MAYBE_H_ +#define V8_CRDTP_MAYBE_H_ + +#include +#include + +namespace v8_crdtp { + +// ============================================================================= +// detail::PtrMaybe, detail::ValueMaybe, templates for optional +// pointers / values which are used in ../lib/Forward_h.template. +// ============================================================================= + +namespace detail { +template +class PtrMaybe { + public: + PtrMaybe() = default; + PtrMaybe(std::unique_ptr value) : value_(std::move(value)) {} + PtrMaybe(PtrMaybe&& other) noexcept : value_(std::move(other.value_)) {} + void operator=(std::unique_ptr value) { value_ = std::move(value); } + T* fromJust() const { + assert(value_); + return value_.get(); + } + T* fromMaybe(T* default_value) const { + return value_ ? value_.get() : default_value; + } + bool isJust() const { return value_ != nullptr; } + std::unique_ptr takeJust() { + assert(value_); + return std::move(value_); + } + + private: + std::unique_ptr value_; +}; + +template +class ValueMaybe { + public: + ValueMaybe() : is_just_(false), value_() {} + ValueMaybe(T value) : is_just_(true), value_(std::move(value)) {} + ValueMaybe(ValueMaybe&& other) noexcept + : is_just_(other.is_just_), value_(std::move(other.value_)) {} + void operator=(T value) { + value_ = value; + is_just_ = true; + } + const T& fromJust() const { + assert(is_just_); + return value_; + } + const T& fromMaybe(const T& default_value) const { + return is_just_ ? value_ : default_value; + } + bool isJust() const { return is_just_; } + T takeJust() { + assert(is_just_); + is_just_ = false; + return std::move(value_); + } + + private: + bool is_just_; + T value_; +}; + +template +struct MaybeTypedef { + typedef PtrMaybe type; +}; + +template <> +struct MaybeTypedef { + typedef ValueMaybe type; +}; + +template <> +struct MaybeTypedef { + typedef ValueMaybe type; +}; + +template <> +struct MaybeTypedef { + typedef ValueMaybe type; +}; + +template <> +struct MaybeTypedef { + typedef ValueMaybe type; +}; + +} // namespace detail + +template +using Maybe = typename detail::MaybeTypedef::type; + +} // namespace v8_crdtp + +#endif // V8_CRDTP_MAYBE_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/parser_handler.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/parser_handler.h new file mode 100644 index 000000000..c583e35ad --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/parser_handler.h @@ -0,0 +1,39 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_PARSER_HANDLER_H_ +#define V8_CRDTP_PARSER_HANDLER_H_ + +#include +#include "span.h" +#include "status.h" + +namespace v8_crdtp { +// Handler interface for parser events emitted by a streaming parser. +// See cbor::NewCBOREncoder, cbor::ParseCBOR, json::NewJSONEncoder, +// json::ParseJSON. +class ParserHandler { + public: + virtual ~ParserHandler() = default; + virtual void HandleMapBegin() = 0; + virtual void HandleMapEnd() = 0; + virtual void HandleArrayBegin() = 0; + virtual void HandleArrayEnd() = 0; + virtual void HandleString8(span chars) = 0; + virtual void HandleString16(span chars) = 0; + virtual void HandleBinary(span bytes) = 0; + virtual void HandleDouble(double value) = 0; + virtual void HandleInt32(int32_t value) = 0; + virtual void HandleBool(bool value) = 0; + virtual void HandleNull() = 0; + + // The parser may send one error even after other events have already + // been received. Client code is reponsible to then discard the + // already processed events. + // |error| must be an eror, as in, |error.is_ok()| can't be true. + virtual void HandleError(Status error) = 0; +}; +} // namespace v8_crdtp + +#endif // V8_CRDTP_PARSER_HANDLER_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/protocol_core.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/protocol_core.h new file mode 100644 index 000000000..486bf7b2d --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/protocol_core.h @@ -0,0 +1,417 @@ +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_PROTOCOL_CORE_H_ +#define V8_CRDTP_PROTOCOL_CORE_H_ + +#include + +#include +#include +#include +#include + +#include "cbor.h" +#include "maybe.h" +#include "serializable.h" +#include "span.h" +#include "status.h" + +namespace v8_crdtp { + +class DeserializerState { + public: + using Storage = std::shared_ptr>; + + // Creates a state from the raw bytes received from the peer. + explicit DeserializerState(std::vector bytes); + // Creates the state from the part of another message. + DeserializerState(Storage storage, span span); + DeserializerState(const DeserializerState& r) = delete; + DeserializerState(DeserializerState&& r) = default; + + // Registers |error|, unless the tokenizer's status is already an error. + void RegisterError(Error error); + // Registers |name| as a segment of the field path. + void RegisterFieldPath(span name); + + // Produces an error message considering |tokenizer.Status()|, + // status_, and field_path_. + std::string ErrorMessage(span message_name) const; + Status status() const; + const Storage& storage() const { return storage_; } + cbor::CBORTokenizer* tokenizer() { return &tokenizer_; } + + private: + const Storage storage_; + cbor::CBORTokenizer tokenizer_; + Status status_; + std::vector> field_path_; +}; + +template +struct ProtocolTypeTraits {}; + +template <> +struct ProtocolTypeTraits { + static bool Deserialize(DeserializerState* state, bool* value); + static void Serialize(bool value, std::vector* bytes); +}; + +template <> +struct ProtocolTypeTraits { + static bool Deserialize(DeserializerState* state, int* value); + static void Serialize(int value, std::vector* bytes); +}; + +template <> +struct ProtocolTypeTraits { + static bool Deserialize(DeserializerState* state, double* value); + static void Serialize(double value, std::vector* bytes); +}; + +class ContainerSerializer { + public: + ContainerSerializer(std::vector* bytes, uint8_t tag); + + template + void AddField(span field_name, const T& value) { + cbor::EncodeString8( + span(reinterpret_cast(field_name.data()), + field_name.size()), + bytes_); + ProtocolTypeTraits::Serialize(value, bytes_); + } + template + void AddField(span field_name, const detail::ValueMaybe& value) { + if (!value.isJust()) + return; + AddField(field_name, value.fromJust()); + } + template + void AddField(span field_name, const detail::PtrMaybe& value) { + if (!value.isJust()) + return; + AddField(field_name, *value.fromJust()); + } + + void EncodeStop(); + + private: + std::vector* const bytes_; + cbor::EnvelopeEncoder envelope_; +}; + +class ObjectSerializer { + public: + ObjectSerializer(); + ~ObjectSerializer(); + + template + void AddField(span name, const T& field) { + serializer_.AddField(name, field); + } + std::unique_ptr Finish(); + + private: + std::vector owned_bytes_; + ContainerSerializer serializer_; +}; + +class DeserializerDescriptor { + public: + struct Field { + span name; + bool is_optional; + bool (*deserializer)(DeserializerState* state, void* obj); + }; + + DeserializerDescriptor(const Field* fields, size_t field_count); + + bool Deserialize(DeserializerState* state, void* obj) const; + + private: + bool DeserializeField(DeserializerState* state, + span name, + int* seen_mandatory_fields, + void* obj) const; + + const Field* const fields_; + const size_t field_count_; + const int mandatory_field_mask_; +}; + +template +struct ProtocolTypeTraits> { + static bool Deserialize(DeserializerState* state, std::vector* value) { + auto* tokenizer = state->tokenizer(); + if (tokenizer->TokenTag() == cbor::CBORTokenTag::ENVELOPE) + tokenizer->EnterEnvelope(); + if (tokenizer->TokenTag() != cbor::CBORTokenTag::ARRAY_START) { + state->RegisterError(Error::CBOR_ARRAY_START_EXPECTED); + return false; + } + assert(value->empty()); + tokenizer->Next(); + for (; tokenizer->TokenTag() != cbor::CBORTokenTag::STOP; + tokenizer->Next()) { + value->emplace_back(); + if (!ProtocolTypeTraits::Deserialize(state, &value->back())) + return false; + } + return true; + } + + static void Serialize(const std::vector& value, + std::vector* bytes) { + ContainerSerializer container_serializer( + bytes, cbor::EncodeIndefiniteLengthArrayStart()); + for (const auto& item : value) + ProtocolTypeTraits::Serialize(item, bytes); + container_serializer.EncodeStop(); + } +}; + +template +struct ProtocolTypeTraits>> { + static bool Deserialize(DeserializerState* state, + std::unique_ptr>* value) { + auto res = std::make_unique>(); + if (!ProtocolTypeTraits>::Deserialize(state, res.get())) + return false; + *value = std::move(res); + return true; + } + static void Serialize(const std::unique_ptr>& value, + std::vector* bytes) { + ProtocolTypeTraits>::Serialize(*value, bytes); + } +}; + +class DeferredMessage : public Serializable { + public: + static std::unique_ptr FromSerializable( + std::unique_ptr serializeable); + static std::unique_ptr FromSpan(span bytes); + + ~DeferredMessage() override = default; + virtual DeserializerState MakeDeserializer() const = 0; + + protected: + DeferredMessage() = default; +}; + +template <> +struct ProtocolTypeTraits> { + static bool Deserialize(DeserializerState* state, + std::unique_ptr* value); + static void Serialize(const std::unique_ptr& value, + std::vector* bytes); +}; + +template +struct ProtocolTypeTraits> { + static bool Deserialize(DeserializerState* state, + detail::ValueMaybe* value) { + T res; + if (!ProtocolTypeTraits::Deserialize(state, &res)) + return false; + *value = std::move(res); + return true; + } + + static void Serialize(const detail::ValueMaybe& value, + std::vector* bytes) { + ProtocolTypeTraits::Serialize(value.fromJust(), bytes); + } +}; + +template +struct ProtocolTypeTraits> { + static bool Deserialize(DeserializerState* state, + detail::PtrMaybe* value) { + std::unique_ptr res; + if (!ProtocolTypeTraits>::Deserialize(state, &res)) + return false; + *value = std::move(res); + return true; + } + + static void Serialize(const detail::PtrMaybe& value, + std::vector* bytes) { + ProtocolTypeTraits::Serialize(*value.fromJust(), bytes); + } +}; + +template +class DeserializableProtocolObject { + public: + static StatusOr> ReadFrom( + const DeferredMessage& deferred_message) { + auto state = deferred_message.MakeDeserializer(); + if (auto res = Deserialize(&state)) + return StatusOr>(std::move(res)); + return StatusOr>(state.status()); + } + + static StatusOr> ReadFrom(std::vector bytes) { + auto state = DeserializerState(std::move(bytes)); + if (auto res = Deserialize(&state)) + return StatusOr>(std::move(res)); + return StatusOr>(state.status()); + } + + // Short-hand for legacy clients. This would swallow any errors, consider + // using ReadFrom. + static std::unique_ptr FromBinary(const uint8_t* bytes, size_t size) { + std::unique_ptr value(new T()); + auto deserializer = DeferredMessage::FromSpan(span(bytes, size)) + ->MakeDeserializer(); + std::ignore = Deserialize(&deserializer, value.get()); + return value; + } + + [[nodiscard]] static bool Deserialize(DeserializerState* state, T* value) { + return T::deserializer_descriptor().Deserialize(state, value); + } + + protected: + // This is for the sake of the macros used by derived classes thay may be in + // a different namespace; + using ProtocolType = T; + using DeserializerDescriptorType = DeserializerDescriptor; + template + using DeserializableBase = DeserializableProtocolObject; + + DeserializableProtocolObject() = default; + ~DeserializableProtocolObject() = default; + + private: + friend struct ProtocolTypeTraits>; + + static std::unique_ptr Deserialize(DeserializerState* state) { + std::unique_ptr value(new T()); + if (Deserialize(state, value.get())) + return value; + return nullptr; + } +}; + +template +class ProtocolObject : public Serializable, + public DeserializableProtocolObject { + public: + std::unique_ptr Clone() const { + std::vector serialized; + AppendSerialized(&serialized); + return T::ReadFrom(std::move(serialized)).value(); + } + // TODO(caseq): compatibility only, remove. + std::unique_ptr clone() const { return Clone(); } + + protected: + using ProtocolType = T; + + ProtocolObject() = default; +}; + +template +struct ProtocolTypeTraits< + T, + typename std::enable_if< + std::is_base_of, T>::value>::type> { + static bool Deserialize(DeserializerState* state, T* value) { + return T::Deserialize(state, value); + } + + static void Serialize(const T& value, std::vector* bytes) { + value.AppendSerialized(bytes); + } +}; + +template +struct ProtocolTypeTraits< + std::unique_ptr, + typename std::enable_if< + std::is_base_of, T>::value>::type> { + static bool Deserialize(DeserializerState* state, std::unique_ptr* value) { + std::unique_ptr res = T::Deserialize(state); + if (!res) + return false; + *value = std::move(res); + return true; + } + + static void Serialize(const std::unique_ptr& value, + std::vector* bytes) { + ProtocolTypeTraits::Serialize(*value, bytes); + } +}; + +template +bool ConvertProtocolValue(const F& from, T* to) { + std::vector bytes; + ProtocolTypeTraits::Serialize(from, &bytes); + auto deserializer = + DeferredMessage::FromSpan(span(bytes.data(), bytes.size())) + ->MakeDeserializer(); + return ProtocolTypeTraits::Deserialize(&deserializer, to); +} + +#define DECLARE_DESERIALIZATION_SUPPORT() \ + friend DeserializableBase; \ + static const DeserializerDescriptorType& deserializer_descriptor() + +#define DECLARE_SERIALIZATION_SUPPORT() \ + public: \ + void AppendSerialized(std::vector* bytes) const override; \ + \ + private: \ + friend DeserializableBase; \ + static const DeserializerDescriptorType& deserializer_descriptor() + +#define V8_CRDTP_DESERIALIZE_FILED_IMPL(name, field, is_optional) \ + { \ + MakeSpan(name), is_optional, \ + [](DeserializerState* __state, void* __obj) -> bool { \ + return ProtocolTypeTraits::Deserialize( \ + __state, &static_cast(__obj)->field); \ + } \ + } + +// clang-format off +#define V8_CRDTP_BEGIN_DESERIALIZER(type) \ + const type::DeserializerDescriptorType& type::deserializer_descriptor() { \ + using namespace v8_crdtp; \ + static const DeserializerDescriptorType::Field fields[] = { + +#define V8_CRDTP_END_DESERIALIZER() \ + }; \ + static const DeserializerDescriptorType s_desc( \ + fields, sizeof fields / sizeof fields[0]); \ + return s_desc; \ + } + +#define V8_CRDTP_DESERIALIZE_FIELD(name, field) \ + V8_CRDTP_DESERIALIZE_FILED_IMPL(name, field, false) +#define V8_CRDTP_DESERIALIZE_FIELD_OPT(name, field) \ + V8_CRDTP_DESERIALIZE_FILED_IMPL(name, field, true) + +#define V8_CRDTP_BEGIN_SERIALIZER(type) \ + void type::AppendSerialized(std::vector* bytes) const { \ + using namespace v8_crdtp; \ + ContainerSerializer __serializer(bytes, \ + cbor::EncodeIndefiniteLengthMapStart()); + +#define V8_CRDTP_SERIALIZE_FIELD(name, field) \ + __serializer.AddField(MakeSpan(name), field) + +#define V8_CRDTP_END_SERIALIZER() \ + __serializer.EncodeStop(); \ + } class __cddtp_dummy_name +// clang-format on + +} // namespace v8_crdtp + +#endif // V8_CRDTP_PROTOCOL_CORE_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/serializable.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/serializable.h new file mode 100644 index 000000000..01c1f8b46 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/serializable.h @@ -0,0 +1,32 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_SERIALIZABLE_H_ +#define V8_CRDTP_SERIALIZABLE_H_ + +#include +#include +#include +#include "export.h" + +namespace v8_crdtp { +// ============================================================================= +// Serializable - An object to be emitted as a sequence of bytes. +// ============================================================================= +class Serializable { + public: + // Convenience: Invokes |AppendSerialized| on an empty vector. + std::vector Serialize() const; + + virtual void AppendSerialized(std::vector* out) const = 0; + + virtual ~Serializable() = default; + + // Wraps a vector of |bytes| into a Serializable for situations in which we + // eagerly serialize a structure. + static std::unique_ptr From(std::vector bytes); +}; +} // namespace v8_crdtp + +#endif // V8_CRDTP_SERIALIZABLE_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/span.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/span.h new file mode 100644 index 000000000..8f132636f --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/span.h @@ -0,0 +1,99 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_SPAN_H_ +#define V8_CRDTP_SPAN_H_ + +#include +#include +#include + +#include "export.h" + +namespace v8_crdtp { +// ============================================================================= +// span - sequence of bytes +// ============================================================================= + +// This template is similar to std::span, which will be included in C++20. +template +class span { + public: + using index_type = size_t; + + constexpr span() : data_(nullptr), size_(0) {} + constexpr span(const T* data, index_type size) : data_(data), size_(size) {} + + constexpr const T* data() const { return data_; } + + constexpr const T* begin() const { return data_; } + constexpr const T* end() const { return data_ + size_; } + + constexpr const T& operator[](index_type idx) const { return data_[idx]; } + + constexpr span subspan(index_type offset, index_type count) const { + return span(data_ + offset, count); + } + + constexpr span subspan(index_type offset) const { + return span(data_ + offset, size_ - offset); + } + + constexpr bool empty() const { return size_ == 0; } + + constexpr index_type size() const { return size_; } + constexpr index_type size_bytes() const { return size_ * sizeof(T); } + + private: + const T* data_; + index_type size_; +}; + +template +constexpr span MakeSpan(const char (&str)[N]) { + return span(str, N - 1); +} + +template +constexpr span SpanFrom(const char (&str)[N]) { + return span(reinterpret_cast(str), N - 1); +} + +constexpr inline span SpanFrom(const char* str) { + return str ? span(reinterpret_cast(str), strlen(str)) + : span(); +} + +inline span SpanFrom(const std::string& v) { + return span(reinterpret_cast(v.data()), v.size()); +} + +// This SpanFrom routine works for std::vector and +// std::vector, but also for base::span in Chromium. +template {} && + std::is_member_function_pointer{}>> +inline span SpanFrom(const C& v) { + return span(v.data(), v.size()); +} + +// Less than / equality comparison functions for sorting / searching for byte +// spans. +bool SpanLessThan(span x, span y) noexcept; +bool SpanEquals(span x, span y) noexcept; + +// Less than / equality comparison functions for sorting / searching for byte +// spans. +bool SpanLessThan(span x, span y) noexcept; +bool SpanEquals(span x, span y) noexcept; + +struct SpanLt { + bool operator()(span l, span r) const { + return SpanLessThan(l, r); + } +}; +} // namespace v8_crdtp + +#endif // V8_CRDTP_SPAN_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/status.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/status.h new file mode 100644 index 000000000..10745f83e --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/status.h @@ -0,0 +1,140 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_STATUS_H_ +#define V8_CRDTP_STATUS_H_ + +#include +#include +#include +#include + +#include "export.h" + +namespace v8_crdtp { +// ============================================================================= +// Status and Error codes +// ============================================================================= + +enum class Error { + OK = 0, + + // JSON parsing errors; checked when parsing / converting from JSON. + // See json.{h,cc}. + JSON_PARSER_UNPROCESSED_INPUT_REMAINS = 0x01, + JSON_PARSER_STACK_LIMIT_EXCEEDED = 0x02, + JSON_PARSER_NO_INPUT = 0x03, + JSON_PARSER_INVALID_TOKEN = 0x04, + JSON_PARSER_INVALID_NUMBER = 0x05, + JSON_PARSER_INVALID_STRING = 0x06, + JSON_PARSER_UNEXPECTED_ARRAY_END = 0x07, + JSON_PARSER_COMMA_OR_ARRAY_END_EXPECTED = 0x08, + JSON_PARSER_STRING_LITERAL_EXPECTED = 0x09, + JSON_PARSER_COLON_EXPECTED = 0x0a, + JSON_PARSER_UNEXPECTED_MAP_END = 0x0b, + JSON_PARSER_COMMA_OR_MAP_END_EXPECTED = 0x0c, + JSON_PARSER_VALUE_EXPECTED = 0x0d, + + // CBOR parsing errors; checked when parsing / converting from CBOR. + CBOR_INVALID_INT32 = 0x0e, + CBOR_INVALID_DOUBLE = 0x0f, + CBOR_INVALID_ENVELOPE = 0x10, + CBOR_ENVELOPE_CONTENTS_LENGTH_MISMATCH = 0x11, + CBOR_MAP_OR_ARRAY_EXPECTED_IN_ENVELOPE = 0x12, + CBOR_INVALID_STRING8 = 0x13, + CBOR_INVALID_STRING16 = 0x14, + CBOR_INVALID_BINARY = 0x15, + CBOR_UNSUPPORTED_VALUE = 0x16, + CBOR_UNEXPECTED_EOF_IN_ENVELOPE = 0x17, + CBOR_INVALID_START_BYTE = 0x18, + CBOR_UNEXPECTED_EOF_EXPECTED_VALUE = 0x19, + CBOR_UNEXPECTED_EOF_IN_ARRAY = 0x1a, + CBOR_UNEXPECTED_EOF_IN_MAP = 0x1b, + CBOR_INVALID_MAP_KEY = 0x1c, + CBOR_DUPLICATE_MAP_KEY = 0x1d, + CBOR_STACK_LIMIT_EXCEEDED = 0x1e, + CBOR_TRAILING_JUNK = 0x1f, + CBOR_MAP_START_EXPECTED = 0x20, + CBOR_MAP_STOP_EXPECTED = 0x21, + CBOR_ARRAY_START_EXPECTED = 0x22, + CBOR_ENVELOPE_SIZE_LIMIT_EXCEEDED = 0x23, + + // Message errors are constraints we place on protocol messages coming + // from a protocol client; these are checked in crdtp::Dispatchable + // (see dispatch.h) as it performs a shallow parse. + MESSAGE_MUST_BE_AN_OBJECT = 0x24, + MESSAGE_MUST_HAVE_INTEGER_ID_PROPERTY = 0x25, + MESSAGE_MUST_HAVE_STRING_METHOD_PROPERTY = 0x26, + MESSAGE_MAY_HAVE_STRING_SESSION_ID_PROPERTY = 0x27, + MESSAGE_MAY_HAVE_OBJECT_PARAMS_PROPERTY = 0x28, + MESSAGE_HAS_UNKNOWN_PROPERTY = 0x29, + + BINDINGS_MANDATORY_FIELD_MISSING = 0x30, + BINDINGS_BOOL_VALUE_EXPECTED = 0x31, + BINDINGS_INT32_VALUE_EXPECTED = 0x32, + BINDINGS_DOUBLE_VALUE_EXPECTED = 0x33, + BINDINGS_STRING_VALUE_EXPECTED = 0x34, + BINDINGS_STRING8_VALUE_EXPECTED = 0x35, + BINDINGS_BINARY_VALUE_EXPECTED = 0x36, + BINDINGS_DICTIONARY_VALUE_EXPECTED = 0x37, + BINDINGS_INVALID_BASE64_STRING = 0x38, +}; + +// A status value with position that can be copied. The default status +// is OK. Usually, error status values should come with a valid position. +struct Status { + static constexpr size_t npos() { return std::numeric_limits::max(); } + + bool ok() const { return error == Error::OK; } + + Error error = Error::OK; + size_t pos = npos(); + Status(Error error, size_t pos) : error(error), pos(pos) {} + Status() = default; + + bool IsMessageError() const { + return error >= Error::MESSAGE_MUST_BE_AN_OBJECT && + error <= Error::MESSAGE_HAS_UNKNOWN_PROPERTY; + } + + // Returns 7 bit US-ASCII string, either "OK" or an error message without + // position. + std::string Message() const; + + // Returns a 7 bit US-ASCII string, either "OK" or an error message that + // includes the position. + std::string ToASCIIString() const; +}; + +template +class StatusOr { + public: + explicit StatusOr(const T& value) : value_(value) {} + explicit StatusOr(T&& value) : value_(std::move(value)) {} + explicit StatusOr(const Status& status) : status_(status) {} + + bool ok() const { return status_.ok(); } + T& operator*() & { + assert(ok()); + return value_; + } + const T& operator*() const& { return value(); } + T&& operator*() && { return value(); } + const Status& status() const { return status_; } + + T& value() & { return *this; } + T&& value() && { + assert(ok()); + return std::move(value_); + } + const T& value() const& { return *this; } + + private: + Status status_; + T value_; +}; + +} // namespace v8_crdtp + +#endif // V8_CRDTP_STATUS_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/status_test_support.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/status_test_support.h new file mode 100644 index 000000000..3ae8eaa3d --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/status_test_support.h @@ -0,0 +1,32 @@ +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_CRDTP_STATUS_TEST_SUPPORT_H_ +#define V8_CRDTP_STATUS_TEST_SUPPORT_H_ + +#include +#include "status.h" +#include "test_platform.h" + +namespace v8_crdtp { +// Supports gtest, to conveniently match Status objects and +// get useful error messages when tests fail. +// Typically used with EXPECT_THAT, e.g. +// +// EXPECT_THAT(status, StatusIs(Error::JSON_PARSER_COLON_EXPECTED, 42)); +// +// EXPECT_THAT(status, StatusIsOk()); + +// Prints a |status|, including its generated error message, error code, and +// position. This is used by gtest for pretty printing actual vs. expected. +void PrintTo(const Status& status, std::ostream* os); + +// Matches any status with |status.ok()|. +testing::Matcher StatusIsOk(); + +// Matches any status with |error| and |pos|. +testing::Matcher StatusIs(Error error, size_t pos); +} // namespace v8_crdtp + +#endif // V8_CRDTP_STATUS_TEST_SUPPORT_H_ diff --git a/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/test_platform.h b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/test_platform.h new file mode 100644 index 000000000..e6698f252 --- /dev/null +++ b/NativeScript/napi/android/v8-11/v8_inspector/third_party/inspector_protocol/crdtp/test_platform.h @@ -0,0 +1,26 @@ +// Copyright 2019 The V8 Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is V8 specific. It's not rolled from the upstream project. + +#ifndef V8_INSPECTOR_PROTOCOL_CRDTP_TEST_PLATFORM_H_ +#define V8_INSPECTOR_PROTOCOL_CRDTP_TEST_PLATFORM_H_ + +#include +#include + +#include "span.h" +#include "src/base/logging.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace v8_crdtp { + +std::string UTF16ToUTF8(span in); + +std::vector UTF8ToUTF16(span in); + +} // namespace v8_crdtp + +#endif // V8_INSPECTOR_PROTOCOL_CRDTP_TEST_PLATFORM_H_ diff --git a/NativeScript/napi/android/v8-13/include/APIDesign.md b/NativeScript/napi/android/v8-13/include/APIDesign.md new file mode 100644 index 000000000..fe42c8ed5 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/APIDesign.md @@ -0,0 +1,72 @@ +# 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/android/v8-13/include/DEPS b/NativeScript/napi/android/v8-13/include/DEPS new file mode 100644 index 000000000..c6d10cd4d --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/DEPS @@ -0,0 +1,13 @@ +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/conditional-stack-allocated.h", + "+cppgc/internal/write-barrier.h", + "+cppgc/type-traits.h", + "+cppgc/visitor.h", + "+perfetto", +] diff --git a/NativeScript/napi/android/v8-13/include/DIR_METADATA b/NativeScript/napi/android/v8-13/include/DIR_METADATA new file mode 100644 index 000000000..d65ff2a39 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/DIR_METADATA @@ -0,0 +1,14 @@ +# 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" +} +buganizer_public: { + component_id: 1456124 +} diff --git a/NativeScript/napi/android/v8-13/include/OWNERS b/NativeScript/napi/android/v8-13/include/OWNERS new file mode 100644 index 000000000..be9fad30a --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/OWNERS @@ -0,0 +1,24 @@ +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 + +per-file v8-profiler.h=file:../src/profiler/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=vahl@chromium.org diff --git a/NativeScript/napi/android/v8-13/include/cppgc/DEPS b/NativeScript/napi/android/v8-13/include/cppgc/DEPS new file mode 100644 index 000000000..2ec7ebbd4 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/DEPS @@ -0,0 +1,9 @@ +include_rules = [ + "-include", + "+v8config.h", + "+v8-platform.h", + "+v8-source-location.h", + "+cppgc", + "-src", + "+libplatform/libplatform.h", +] diff --git a/NativeScript/napi/android/v8-13/include/cppgc/OWNERS b/NativeScript/napi/android/v8-13/include/cppgc/OWNERS new file mode 100644 index 000000000..6ccabf60b --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/OWNERS @@ -0,0 +1,2 @@ +bikineev@chromium.org +omerkatz@chromium.org \ No newline at end of file diff --git a/NativeScript/napi/android/v8-13/include/cppgc/README.md b/NativeScript/napi/android/v8-13/include/cppgc/README.md new file mode 100644 index 000000000..d825ea5bd --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/README.md @@ -0,0 +1,135 @@ +# 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/android/v8-13/include/cppgc/allocation.h b/NativeScript/napi/android/v8-13/include/cppgc/allocation.h new file mode 100644 index 000000000..a7955fd10 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/allocation.h @@ -0,0 +1,308 @@ +// 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 { + +using AlignVal = std::align_val_t; + +class 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_v, + "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_v, + "Custom space must inherit from CustomSpaceBase."); + return MakeGarbageCollectedTraitInternal::Allocate( + handle, size, internal::GCInfoTrait::Index(), + CustomSpace::kSpaceIndex); + } + }; + + private: + V8_EXPORT static void* CPPGC_DEFAULT_ALIGNED + Allocate(cppgc::AllocationHandle&, size_t, GCInfoIndex); + V8_EXPORT static void* CPPGC_DOUBLE_WORD_ALIGNED + Allocate(cppgc::AllocationHandle&, size_t, AlignVal, GCInfoIndex); + V8_EXPORT static void* CPPGC_DEFAULT_ALIGNED + Allocate(cppgc::AllocationHandle&, size_t, GCInfoIndex, CustomSpaceIndex); + V8_EXPORT 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_v, + "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/android/v8-13/include/cppgc/common.h b/NativeScript/napi/android/v8-13/include/cppgc/common.h new file mode 100644 index 000000000..961038360 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/common.h @@ -0,0 +1,28 @@ +// 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/android/v8-13/include/cppgc/cross-thread-persistent.h b/NativeScript/napi/android/v8-13/include/cppgc/cross-thread-persistent.h new file mode 100644 index 000000000..df172b3a4 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/cross-thread-persistent.h @@ -0,0 +1,466 @@ +// 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 >> + 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 >> + 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 >> + 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 >> + 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>> + 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/android/v8-13/include/cppgc/custom-space.h b/NativeScript/napi/android/v8-13/include/cppgc/custom-space.h new file mode 100644 index 000000000..757c4fde1 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/custom-space.h @@ -0,0 +1,97 @@ +// 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/android/v8-13/include/cppgc/default-platform.h b/NativeScript/napi/android/v8-13/include/cppgc/default-platform.h new file mode 100644 index 000000000..07ce55bbf --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/default-platform.h @@ -0,0 +1,68 @@ +// 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( + TaskPriority priority) 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, priority); + } + + 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/android/v8-13/include/cppgc/explicit-management.h b/NativeScript/napi/android/v8-13/include/cppgc/explicit-management.h new file mode 100644 index 000000000..0290328dc --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/explicit-management.h @@ -0,0 +1,100 @@ +// 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/android/v8-13/include/cppgc/garbage-collected.h b/NativeScript/napi/android/v8-13/include/cppgc/garbage-collected.h new file mode 100644 index 000000000..dfd758a35 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/garbage-collected.h @@ -0,0 +1,114 @@ +// 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; + + // 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. + // Not override the non-array varaint of `delete` to not conflict with the + // operator in GarbageCollected above. + void operator delete[](void*) = delete; + + /** + * 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/android/v8-13/include/cppgc/heap-consistency.h b/NativeScript/napi/android/v8-13/include/cppgc/heap-consistency.h new file mode 100644 index 000000000..23b5d9099 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/heap-consistency.h @@ -0,0 +1,309 @@ +// 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 + * 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/android/v8-13/include/cppgc/heap-handle.h b/NativeScript/napi/android/v8-13/include/cppgc/heap-handle.h new file mode 100644 index 000000000..0d1d21e65 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/heap-handle.h @@ -0,0 +1,48 @@ +// 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/android/v8-13/include/cppgc/heap-state.h b/NativeScript/napi/android/v8-13/include/cppgc/heap-state.h new file mode 100644 index 000000000..28212589f --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/heap-state.h @@ -0,0 +1,82 @@ +// 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/android/v8-13/include/cppgc/heap-statistics.h b/NativeScript/napi/android/v8-13/include/cppgc/heap-statistics.h new file mode 100644 index 000000000..c357f916f --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/heap-statistics.h @@ -0,0 +1,122 @@ +// 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; + /** Memory retained in the page pool, not used directly by the heap. */ + size_t pooled_memory_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/android/v8-13/include/cppgc/heap.h b/NativeScript/napi/android/v8-13/include/cppgc/heap.h new file mode 100644 index 000000000..02ee12eab --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/heap.h @@ -0,0 +1,202 @@ +// 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/android/v8-13/include/cppgc/internal/api-constants.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/api-constants.h new file mode 100644 index 000000000..f3b0d8571 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/api-constants.h @@ -0,0 +1,75 @@ +// 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 kPageSizeBits = 17; +static constexpr size_t kPageSize = size_t{1} << kPageSizeBits; + +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) +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) +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/android/v8-13/include/cppgc/internal/atomic-entry-flag.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/atomic-entry-flag.h new file mode 100644 index 000000000..5a7d3b8f8 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/atomic-entry-flag.h @@ -0,0 +1,48 @@ +// 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/android/v8-13/include/cppgc/internal/base-page-handle.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/base-page-handle.h new file mode 100644 index 000000000..a5308dc34 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/base-page-handle.h @@ -0,0 +1,43 @@ +// 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)); + } + 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 == + 0); + } + + HeapHandle& heap_handle_; +}; + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_BASE_PAGE_HANDLE_H_ diff --git a/NativeScript/napi/android/v8-13/include/cppgc/internal/caged-heap-local-data.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/caged-heap-local-data.h new file mode 100644 index 000000000..ad717d744 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/caged-heap-local-data.h @@ -0,0 +1,117 @@ +// 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. + 12; +#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/android/v8-13/include/cppgc/internal/caged-heap.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/caged-heap.h new file mode 100644 index 000000000..46fb529f6 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/caged-heap.h @@ -0,0 +1,64 @@ +// 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_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) + 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/android/v8-13/include/cppgc/internal/compiler-specific.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/compiler-specific.h new file mode 100644 index 000000000..175156ca6 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/compiler-specific.h @@ -0,0 +1,46 @@ +// 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_ + +#include "v8config.h" // NOLINT(build/include_directory) + +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 defined(V8_CC_MSVC) && CPPGC_HAS_CPP_ATTRIBUTE(msvc::no_unique_address) +// Unfortunately MSVC ignores [[no_unique_address]] (see +// https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/#msvc-extensions-and-abi), +// and clang-cl matches it for ABI compatibility reasons. We need to prefer +// [[msvc::no_unique_address]] when available if we actually want any effect. +#define CPPGC_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#elif 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/android/v8-13/include/cppgc/internal/conditional-stack-allocated.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/conditional-stack-allocated.h new file mode 100644 index 000000000..21c89378f --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/conditional-stack-allocated.h @@ -0,0 +1,41 @@ +// Copyright 2025 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_CONDITIONAL_STACK_ALLOCATED_H_ +#define INCLUDE_CPPGC_INTERNAL_CONDITIONAL_STACK_ALLOCATED_H_ + +#include + +#include "cppgc/macros.h" // NOLINT(build/include_directory) +#include "cppgc/type-traits.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +// Base class that is marked as stack allocated if T is either marked as stack +// allocated or a traceable type. +template +class ConditionalStackAllocatedBase; + +template +concept RequiresStackAllocated = + !std::is_void_v && + (cppgc::IsStackAllocatedType || cppgc::internal::IsTraceableV || + cppgc::IsGarbageCollectedOrMixinTypeV); + +template + requires(RequiresStackAllocated) +class ConditionalStackAllocatedBase { + public: + CPPGC_STACK_ALLOCATED(); +}; + +template + requires(!RequiresStackAllocated) +class ConditionalStackAllocatedBase {}; + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_CONDITIONAL_STACK_ALLOCATED_H_ diff --git a/NativeScript/napi/android/v8-13/include/cppgc/internal/finalizer-trait.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/finalizer-trait.h new file mode 100644 index 000000000..b62da059f --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/finalizer-trait.h @@ -0,0 +1,93 @@ +// 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_v>; + + 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/android/v8-13/include/cppgc/internal/gc-info.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/gc-info.h new file mode 100644 index 000000000..b2fbd1fee --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/gc-info.h @@ -0,0 +1,150 @@ +// 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 void CheckCallbacksAreDefined() { + // No USE() macro available. + (void)static_cast(TraceTrait::Trace); + (void)static_cast(FinalizerTrait::kCallback); + (void)static_cast(NameTrait::GetName); + } +}; + +// 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_v; + static constexpr bool kBothTypesAreTriviallyDestructible = + std::is_trivially_destructible_v && + std::is_trivially_destructible_v; + 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 WantToFold() { + if constexpr ((kHasVirtualDestructorAtBase || + kBothTypesAreTriviallyDestructible || + kHasCustomFinalizerDispatchAtBase) && + !kWantsDetailedObjectNames) { + GCInfoTrait::CheckCallbacksAreDefined(); + GCInfoTrait::CheckCallbacksAreDefined(); + return true; + } + return false; + } + + // 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/android/v8-13/include/cppgc/internal/logging.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/logging.h new file mode 100644 index 000000000..2129e85f2 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/logging.h @@ -0,0 +1,50 @@ +// 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 {}; + +#ifdef CPPGC_ENABLE_API_CHECKS +#define CPPGC_DCHECK_MSG(condition, message) \ + do { \ + if (V8_UNLIKELY(!(condition))) { \ + ::cppgc::internal::DCheckImpl(message); \ + } \ + } while (false) +#else // !CPPGC_ENABLE_API_CHECKS +#define CPPGC_DCHECK_MSG(condition, message) \ + (static_cast(::cppgc::internal::EatParams(condition), message)>{})) +#endif // !CPPGC_ENABLE_API_CHECKS + +#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/android/v8-13/include/cppgc/internal/member-storage.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/member-storage.h new file mode 100644 index 000000000..f862740db --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/member-storage.h @@ -0,0 +1,305 @@ +// 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/caged-heap.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: + struct AtomicInitializerTag {}; + + using IntegralType = uint32_t; + static constexpr auto kWriteBarrierSlotType = + WriteBarrierSlotType::kCompressed; + + V8_INLINE CompressedPointer() : value_(0u) {} + V8_INLINE explicit CompressedPointer(const void* value, + AtomicInitializerTag) { + StoreAtomic(value); + } + 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); + + const auto uptr = reinterpret_cast(ptr); + // Shift the pointer and truncate. + auto compressed = static_cast( + uptr >> api_constants::kPointerCompressionShift); + // Normal compressed pointers must have the MSB set. This is guaranteed by + // the cage alignment. + 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(); + return Decompress(ptr, base); + } + + static V8_INLINE void* Decompress(IntegralType ptr, uintptr_t base) { + CPPGC_DCHECK(CageBaseGlobal::IsSet()); + CPPGC_DCHECK(base == CageBaseGlobal::Get()); + // Sign-extend compressed pointer to full width. This ensure that normal + // pointers have only 1s in the base part of the address. It's also + // 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; + // Set the base part of the address for normal compressed pointers. Note + // that nullptr and the sentinel value do not have 1s in the base part and + // remain as-is in this operation. + return reinterpret_cast(mask & base); + } + + // For a given memory `address`, this method iterates all possible pointers + // that can be reasonably recovered with the current compression scheme and + // passes them to `callback`. + template + static V8_INLINE void VisitPossiblePointers(const void* address, + Callback callback); + + private: + static constexpr IntegralType kCompressedSentinel = + SentinelPointer::kSentinelValue >> + api_constants::kPointerCompressionShift; + // 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_; +}; + +template +// static +void CompressedPointer::VisitPossiblePointers(const void* address, + Callback callback) { + const uintptr_t base = CageBaseGlobal::Get(); + CPPGC_DCHECK(base); + // We may have random compressed pointers on stack (e.g. due to inlined + // collections). These could be present in both halfwords. + const uint32_t compressed_low = + static_cast(reinterpret_cast(address)); + callback(CompressedPointer::Decompress(compressed_low, base)); + const uint32_t compressed_high = static_cast( + reinterpret_cast(address) >> (sizeof(uint32_t) * CHAR_BIT)); + callback(CompressedPointer::Decompress(compressed_high, base)); + // Iterate possible intermediate values, see `Decompress()`. The intermediate + // value of decompressing is a 64-bit value where 35 bits are the offset. We + // don't assume sign extension is stored and recover that part. + // + // Note that this case conveniently also recovers the full pointer. + static constexpr uintptr_t kBitForIntermediateValue = + (sizeof(uint32_t) * CHAR_BIT) + api_constants::kPointerCompressionShift; + static constexpr uintptr_t kSignExtensionMask = + ~((uintptr_t{1} << kBitForIntermediateValue) - 1); + const uintptr_t intermediate_sign_extended = + reinterpret_cast(address) | kSignExtensionMask; + callback(reinterpret_cast(intermediate_sign_extended & base)); +} + +#endif // defined(CPPGC_POINTER_COMPRESSION) + +class V8_TRIVIAL_ABI RawPointer final { + public: + struct AtomicInitializerTag {}; + + using IntegralType = uintptr_t; + static constexpr auto kWriteBarrierSlotType = + WriteBarrierSlotType::kUncompressed; + + V8_INLINE RawPointer() : ptr_(nullptr) {} + V8_INLINE explicit RawPointer(const void* ptr, AtomicInitializerTag) { + StoreAtomic(ptr); + } + 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_; + } + + template + static V8_INLINE void VisitPossiblePointers(const void* address, + Callback callback) { + // Pass along the full pointer. + return callback(const_cast(address)); + } + + 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/android/v8-13/include/cppgc/internal/name-trait.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/name-trait.h new file mode 100644 index 000000000..6e63a7282 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/name-trait.h @@ -0,0 +1,141 @@ +// 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_v; +#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 + // We wanted to use a class name but were unable to provide one due to + // compiler limitations or build configuration. As such, return the hidden + // name with name_was_hidden=false, which will cause this object to be + // visible in the snapshot. + return {NameProvider::kHiddenName, false}; +#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/android/v8-13/include/cppgc/internal/persistent-node.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/persistent-node.h new file mode 100644 index 000000000..413ddd918 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/persistent-node.h @@ -0,0 +1,219 @@ +// 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 HeapBase; +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 heap. +class V8_EXPORT PersistentRegion final : public PersistentRegionBase { + public: + V8_INLINE PersistentRegion(const HeapBase& heap, + const FatalOutOfMemoryHandler& oom_handler) + : PersistentRegionBase(oom_handler), heap_(heap) { + CPPGC_DCHECK(IsCreationThread()); + } + // 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(); + + const HeapBase& heap_; +}; + +// 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/android/v8-13/include/cppgc/internal/pointer-policies.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/pointer-policies.h new file mode 100644 index 000000000..bba484237 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/pointer-policies.h @@ -0,0 +1,280 @@ +// 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 { + // Since in initializing writes the source object is always white, having no + // barrier doesn't break the tri-color invariant. + V8_INLINE static void InitializingBarrier(const void*, const void*) {} + V8_INLINE static void InitializingBarrier(const void*, RawPointer storage) { + } +#if defined(CPPGC_POINTER_COMPRESSION) + V8_INLINE static void InitializingBarrier(const void*, + CompressedPointer storage) {} +#endif + + 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*) {} + V8_INLINE static void InitializingBarrier(const void*, RawPointer storage) {} +#if defined(CPPGC_POINTER_COMPRESSION) + V8_INLINE static void InitializingBarrier(const void*, + CompressedPointer storage) {} +#endif + 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 + V8_INLINE void CheckPointer(RawPointer raw_pointer) { + if (raw_pointer.IsCleared() || raw_pointer.IsSentinel()) { + return; + } + CheckPointersImplTrampoline::Call( + this, static_cast(raw_pointer.Load())); + } +#if defined(CPPGC_POINTER_COMPRESSION) + template + V8_INLINE void CheckPointer(CompressedPointer compressed_pointer) { + if (compressed_pointer.IsCleared() || compressed_pointer.IsSentinel()) { + return; + } + CheckPointersImplTrampoline::Call( + this, static_cast(compressed_pointer.Load())); + } +#endif + 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: + template + V8_INLINE void CheckPointer(T*) {} + template + V8_INLINE void CheckPointer(RawPointer) {} +#if defined(CPPGC_POINTER_COMPRESSION) + template + V8_INLINE void CheckPointer(CompressedPointer) {} +#endif +}; + +#ifdef CPPGC_ENABLE_SLOW_API_CHECKS +// Off heap members are not connected to object graph and thus cannot ressurect +// dead objects. +using DefaultMemberCheckingPolicy = + SameThreadEnabledCheckingPolicy; +using DefaultPersistentCheckingPolicy = + SameThreadEnabledCheckingPolicy; +#else // !CPPGC_ENABLE_SLOW_API_CHECKS +using DefaultMemberCheckingPolicy = DisabledCheckingPolicy; +using DefaultPersistentCheckingPolicy = DisabledCheckingPolicy; +#endif // !CPPGC_ENABLE_SLOW_API_CHECKS +// 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/android/v8-13/include/cppgc/internal/write-barrier.h b/NativeScript/napi/android/v8-13/include/cppgc/internal/write-barrier.h new file mode 100644 index 000000000..566724d30 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/internal/write-barrier.h @@ -0,0 +1,487 @@ +// 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/android/v8-13/include/cppgc/liveness-broker.h b/NativeScript/napi/android/v8-13/include/cppgc/liveness-broker.h new file mode 100644 index 000000000..2c94f1c0f --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/liveness-broker.h @@ -0,0 +1,78 @@ +// 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/android/v8-13/include/cppgc/macros.h b/NativeScript/napi/android/v8-13/include/cppgc/macros.h new file mode 100644 index 000000000..10a6f8eba --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/macros.h @@ -0,0 +1,56 @@ +// 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 { + +#define CPPGC_DISALLOW_NEW() \ + public: \ + using IsDisallowNewMarker CPPGC_UNUSED = int; \ + void* operator new(size_t, void* location) { return location; } \ + void* operator new(size_t) = delete; \ + static_assert(true, "Force semicolon.") + +// 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"))) + +#define CPPGC_PLUGIN_IGNORE(bug_or_reason) \ + __attribute__((annotate("blink_gc_plugin_ignore"), \ + annotate("stack_allocated_ignore"))) + +#else // !defined(__clang__) + +#define CPPGC_STACK_ALLOCATED() static_assert(true, "Force semicolon.") +#define CPPGC_STACK_ALLOCATED_IGNORE(bug_or_reason) +#define CPPGC_PLUGIN_IGNORE(bug_or_reason) + +#endif // !defined(__clang__) + +template +concept IsStackAllocatedType = + requires { typename T::IsStackAllocatedTypeMarker; }; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_MACROS_H_ diff --git a/NativeScript/napi/android/v8-13/include/cppgc/member.h b/NativeScript/napi/android/v8-13/include/cppgc/member.h new file mode 100644 index 000000000..fb457dd58 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/member.h @@ -0,0 +1,663 @@ +// 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) + : raw_(value, typename RawStorage::AtomicInitializerTag{}) {} + + 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); + CheckPointer(raw); + } + 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); + CheckPointer(raw); + } + 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 >> + 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 (IsDecayedSameV) { + return operator=(other.GetRawStorage()); + } else { + static_assert(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 (IsDecayedSameV) { + operator=(other.GetRawStorage()); + } else { + static_assert(IsStrictlyBaseOfV); + operator=(other.Get()); + } + other.Clear(); + return *this; + } + + // Assignment from Persistent. + template >> + V8_INLINE BasicMember& operator=( + const BasicPersistent& + other) { + return operator=(other.Get()); + } + + V8_INLINE BasicMember& operator=(T* other) { + Base::SetRawAtomic(other); + AssigningWriteBarrier(other); + CheckPointer(other); + 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(); + CheckPointer(); + } + + V8_INLINE BasicMember& operator=(RawStorage other) { + Base::SetRawStorageAtomic(other); + AssigningWriteBarrier(); + CheckPointer(); + 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 InitializingWriteBarrier() const { + WriteBarrierPolicy::InitializingBarrier(Base::GetRawSlot(), + Base::GetRawStorage()); + } + 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 CheckPointer(T* value) { + CheckingPolicy::template CheckPointer(value); + } + V8_INLINE void CheckPointer() { + CheckingPolicy::template CheckPointer(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 (IsDecayedSameV) { + // Check compressed pointers if types are the same. + return member1.GetRawStorage() == member2.GetRawStorage(); + } else { + static_assert(IsStrictlyBaseOfV || 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(!IsDecayedSameV); + + if constexpr (IsDecayedSameV) { + // Check compressed pointers if types are the same. + return member.GetRawStorage() == StorageType(raw); + } else if constexpr (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( + 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( + 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( + 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( + 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 + +// Mark `BasicMember` and `T*` as having a common reference type of `T*` (the +// type to which both can be converted or bound). This makes them satisfy +// `std::equality_comparable`, which allows usage like the following: +// ``` +// HeapVector> v; +// T* e; +// auto it = std::ranges::find(v, e); +// ``` +// Without this, the `find()` call above would fail to compile with an error +// about being unable to invoke `std::ranges::equal_to()`. +template typename TQ, template typename UQ> +struct std::basic_common_reference< + cppgc::internal::BasicMember, + T*, TQ, UQ> { + using type = T*; +}; + +template typename TQ, template typename UQ> +struct std::basic_common_reference< + T*, + cppgc::internal::BasicMember, + TQ, UQ> { + using type = T*; +}; + +#endif // INCLUDE_CPPGC_MEMBER_H_ diff --git a/NativeScript/napi/android/v8-13/include/cppgc/name-provider.h b/NativeScript/napi/android/v8-13/include/cppgc/name-provider.h new file mode 100644 index 000000000..849176fde --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/name-provider.h @@ -0,0 +1,75 @@ +// 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. + * + * Implementations of this function must not allocate garbage-collected + * objects or otherwise modify the cppgc heap. + * + * V8 may call this function while generating a heap snapshot or at other + * times. If V8 is currently generating a heap snapshot (according to + * HeapProfiler::IsTakingSnapshot), then the returned string must stay alive + * until the snapshot generation has completed. Otherwise, the returned string + * must stay alive forever. If you need a place to store a temporary string + * during snapshot generation, use HeapProfiler::CopyNameForHeapSnapshot. + * + * @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/android/v8-13/include/cppgc/object-size-trait.h b/NativeScript/napi/android/v8-13/include/cppgc/object-size-trait.h new file mode 100644 index 000000000..35795596d --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/object-size-trait.h @@ -0,0 +1,58 @@ +// 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/android/v8-13/include/cppgc/persistent.h b/NativeScript/napi/android/v8-13/include/cppgc/persistent.h new file mode 100644 index 000000000..b330e87fe --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/persistent.h @@ -0,0 +1,377 @@ +// 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 >> + // 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 >> + // 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 >> + 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 >> + 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/android/v8-13/include/cppgc/platform.h b/NativeScript/napi/android/v8-13/include/cppgc/platform.h new file mode 100644 index 000000000..917fa25e0 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/platform.h @@ -0,0 +1,174 @@ +// 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 GetForegroundTaskRunner(TaskPriority::kUserBlocking); + } + + /** + * Returns a TaskRunner with a specific |priority| which can be used to post a + * task on the foreground thread. + */ + virtual std::shared_ptr GetForegroundTaskRunner( + TaskPriority priority) { + 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(); +}; + +V8_EXPORT bool IsInitialized(); + +/** + * 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/android/v8-13/include/cppgc/prefinalizer.h b/NativeScript/napi/android/v8-13/include/cppgc/prefinalizer.h new file mode 100644 index 000000000..51f2eac8e --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/prefinalizer.h @@ -0,0 +1,75 @@ +// 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/android/v8-13/include/cppgc/process-heap-statistics.h b/NativeScript/napi/android/v8-13/include/cppgc/process-heap-statistics.h new file mode 100644 index 000000000..774cc92f4 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/process-heap-statistics.h @@ -0,0 +1,36 @@ +// 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/android/v8-13/include/cppgc/sentinel-pointer.h b/NativeScript/napi/android/v8-13/include/cppgc/sentinel-pointer.h new file mode 100644 index 000000000..bee96c776 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/sentinel-pointer.h @@ -0,0 +1,39 @@ +// 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/android/v8-13/include/cppgc/source-location.h b/NativeScript/napi/android/v8-13/include/cppgc/source-location.h new file mode 100644 index 000000000..0dc28aedd --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/source-location.h @@ -0,0 +1,16 @@ +// 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/android/v8-13/include/cppgc/tagged-member.h b/NativeScript/napi/android/v8-13/include/cppgc/tagged-member.h new file mode 100644 index 000000000..7a2dfbb4c --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/tagged-member.h @@ -0,0 +1,111 @@ +// Copyright 2025 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_TAGGED_MEMBER_H_ +#define INCLUDE_CPPGC_TAGGED_MEMBER_H_ + +#include +#include +#include + +#include "cppgc/internal/api-constants.h" +#include "cppgc/macros.h" +#include "cppgc/member.h" +#include "cppgc/visitor.h" + +namespace cppgc::subtle { + +// The class allows to store a Member along with a single bit tag. It uses +// distinct tag types, Tag1 and Tag2, to represent the two states of the tag. +// The tag is stored in the least significant bit of the pointer. +// +// Example usage: +// struct ParentTag {}; +// struct ShadowHostTag {}; +// +// /* Constructs a member with the pointer to parent tag: */ +// TaggedUncompressedMember +// m(ParentTag{}, parent); +template +struct TaggedUncompressedMember final { + CPPGC_DISALLOW_NEW(); + static constexpr uintptr_t kTagBit = 0b1; + static_assert(kTagBit < internal::api_constants::kAllocationGranularity, + "The tag must live in the alignment bits of the pointer."); + + public: + TaggedUncompressedMember(Tag1, Pointee* ptr) : ptr_(ptr) {} + TaggedUncompressedMember(Tag2, Pointee* ptr) + : ptr_(reinterpret_cast(reinterpret_cast(ptr) | + kTagBit)) {} + + template + Pointee* GetAs() const { + auto* raw = ptr_.Get(); + if constexpr (std::same_as) { + CPPGC_DCHECK(Is()); + return raw; + } else { + static_assert(std::same_as); + CPPGC_DCHECK(Is()); + return GetUntagged(); + } + } + + template + Pointee* TryGetAs() const { + auto* raw = ptr_.Get(); + if constexpr (std::same_as) { + return (reinterpret_cast(raw) & kTagBit) ? nullptr : raw; + } else { + static_assert(std::same_as); + return (reinterpret_cast(raw) & kTagBit) + ? reinterpret_cast(reinterpret_cast(raw) & + ~kTagBit) + : nullptr; + } + } + + Pointee* GetUntagged() const { + return reinterpret_cast(reinterpret_cast(ptr_.Get()) & + ~kTagBit); + } + + template + void SetAs(Pointee* pointee) { + if constexpr (std::same_as) { + ptr_ = pointee; + } else { + static_assert(std::same_as); + ptr_ = reinterpret_cast(reinterpret_cast(pointee) | + kTagBit); + } + } + + template + bool Is() const { + const bool tag_set = reinterpret_cast(ptr_.Get()) & kTagBit; + if constexpr (std::same_as) { + return !tag_set; + } else { + static_assert(std::same_as); + return tag_set; + } + } + + void Trace(Visitor* v) const { + // Construct an untagged pointer and pass it to Visitor::Trace(). The plugin + // would warn that ptr_ is untraced, which is why CPPGC_PLUGIN_IGNORE is + // used. + UncompressedMember temp(GetUntagged()); + v->Trace(temp); + } + + private: + CPPGC_PLUGIN_IGNORE("See Trace()") UncompressedMember ptr_; +}; + +} // namespace cppgc::subtle + +#endif // INCLUDE_CPPGC_TAGGED_MEMBER_H_ diff --git a/NativeScript/napi/android/v8-13/include/cppgc/testing.h b/NativeScript/napi/android/v8-13/include/cppgc/testing.h new file mode 100644 index 000000000..bddd1fc16 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/testing.h @@ -0,0 +1,106 @@ +// 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/android/v8-13/include/cppgc/trace-trait.h b/NativeScript/napi/android/v8-13/include/cppgc/trace-trait.h new file mode 100644 index 000000000..9b768a152 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/trace-trait.h @@ -0,0 +1,127 @@ +// 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 >> +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/android/v8-13/include/cppgc/type-traits.h b/NativeScript/napi/android/v8-13/include/cppgc/type-traits.h new file mode 100644 index 000000000..1685c31df --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/type-traits.h @@ -0,0 +1,272 @@ +// 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 +#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( + const 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; + +template +constexpr bool IsAnyMemberTypeV = false; + +template +constexpr bool IsAnyMemberTypeV> = true; + +} // 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; + +/** + * Value is true for member types `Member` and `WeakMember`. + */ +template +constexpr bool IsMemberOrWeakMemberTypeV = + IsMemberTypeV || IsWeakMemberTypeV; + +/** + * Value is true for any member type. + */ +template +constexpr bool IsAnyMemberTypeV = internal::IsAnyMemberTypeV>; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_TYPE_TRAITS_H_ diff --git a/NativeScript/napi/android/v8-13/include/cppgc/visitor.h b/NativeScript/napi/android/v8-13/include/cppgc/visitor.h new file mode 100644 index 000000000..e0b1d19be --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/cppgc/visitor.h @@ -0,0 +1,528 @@ +// 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/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/macros.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*); + +/** + * 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 { + CPPGC_DISALLOW_NEW(); + + EphemeronPair(K* k, V* v) : key(k), value(v) {} + WeakMember key; + Member value; + + void ClearValueIfKeyIsDead(const LivenessBroker& broker) { + if (!broker.IsHeapObjectAlive(key)) value = nullptr; + } + + void Trace(Visitor* visitor) const; +}; + +/** + * 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; +}; + +template +void EphemeronPair::Trace(Visitor* visitor) const { + visitor->TraceEphemeron(key, value); +} + +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/android/v8-13/include/inspector/Debugger.h b/NativeScript/napi/android/v8-13/include/inspector/Debugger.h new file mode 100644 index 000000000..e4ba9839f --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/inspector/Debugger.h @@ -0,0 +1,60 @@ +// This file is generated by Exported_h.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef v8_inspector_protocol_Debugger_api_h +#define v8_inspector_protocol_Debugger_api_h + +#include "v8-inspector.h" + +namespace v8_inspector { +namespace protocol { + +#ifndef v8_inspector_protocol_exported_api_h +#define v8_inspector_protocol_exported_api_h +class V8_EXPORT Exported { +public: + virtual void AppendSerialized(std::vector* out) const = 0; + + virtual ~Exported() { } +}; +#endif // !defined(v8_inspector_protocol_exported_api_h) + +namespace Debugger { +namespace API { + +// ------------- Enums. + +namespace Paused { +namespace ReasonEnum { +V8_EXPORT extern const char* Ambiguous; +V8_EXPORT extern const char* Assert; +V8_EXPORT extern const char* CSPViolation; +V8_EXPORT extern const char* DebugCommand; +V8_EXPORT extern const char* DOM; +V8_EXPORT extern const char* EventListener; +V8_EXPORT extern const char* Exception; +V8_EXPORT extern const char* Instrumentation; +V8_EXPORT extern const char* OOM; +V8_EXPORT extern const char* Other; +V8_EXPORT extern const char* PromiseRejection; +V8_EXPORT extern const char* XHR; +V8_EXPORT extern const char* Step; +} // ReasonEnum +} // Paused + +// ------------- Types. + +class V8_EXPORT SearchMatch : public Exported { +public: + static std::unique_ptr fromBinary(const uint8_t* data, size_t length); +}; + +} // namespace API +} // namespace Debugger +} // namespace v8_inspector +} // namespace protocol + +#endif // !defined(v8_inspector_protocol_Debugger_api_h) diff --git a/NativeScript/napi/android/v8-13/include/inspector/Runtime.h b/NativeScript/napi/android/v8-13/include/inspector/Runtime.h new file mode 100644 index 000000000..86471c2ea --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/inspector/Runtime.h @@ -0,0 +1,52 @@ +// This file is generated by Exported_h.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef v8_inspector_protocol_Runtime_api_h +#define v8_inspector_protocol_Runtime_api_h + +#include "v8-inspector.h" + +namespace v8_inspector { +namespace protocol { + +#ifndef v8_inspector_protocol_exported_api_h +#define v8_inspector_protocol_exported_api_h +class V8_EXPORT Exported { +public: + virtual void AppendSerialized(std::vector* out) const = 0; + + virtual ~Exported() { } +}; +#endif // !defined(v8_inspector_protocol_exported_api_h) + +namespace Runtime { +namespace API { + +// ------------- Enums. + +// ------------- Types. + +class V8_EXPORT RemoteObject : public Exported { +public: + static std::unique_ptr fromBinary(const uint8_t* data, size_t length); +}; + +class V8_EXPORT StackTrace : public Exported { +public: + static std::unique_ptr fromBinary(const uint8_t* data, size_t length); +}; + +class V8_EXPORT StackTraceId : public Exported { +public: + static std::unique_ptr fromBinary(const uint8_t* data, size_t length); +}; + +} // namespace API +} // namespace Runtime +} // namespace v8_inspector +} // namespace protocol + +#endif // !defined(v8_inspector_protocol_Runtime_api_h) diff --git a/NativeScript/napi/android/v8-13/include/inspector/Schema.h b/NativeScript/napi/android/v8-13/include/inspector/Schema.h new file mode 100644 index 000000000..ce23732ee --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/inspector/Schema.h @@ -0,0 +1,42 @@ +// This file is generated by Exported_h.template. + +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef v8_inspector_protocol_Schema_api_h +#define v8_inspector_protocol_Schema_api_h + +#include "v8-inspector.h" + +namespace v8_inspector { +namespace protocol { + +#ifndef v8_inspector_protocol_exported_api_h +#define v8_inspector_protocol_exported_api_h +class V8_EXPORT Exported { +public: + virtual void AppendSerialized(std::vector* out) const = 0; + + virtual ~Exported() { } +}; +#endif // !defined(v8_inspector_protocol_exported_api_h) + +namespace Schema { +namespace API { + +// ------------- Enums. + +// ------------- Types. + +class V8_EXPORT Domain : public Exported { +public: + static std::unique_ptr fromBinary(const uint8_t* data, size_t length); +}; + +} // namespace API +} // namespace Schema +} // namespace v8_inspector +} // namespace protocol + +#endif // !defined(v8_inspector_protocol_Schema_api_h) diff --git a/NativeScript/napi/android/v8-13/include/js_protocol-1.2.json b/NativeScript/napi/android/v8-13/include/js_protocol-1.2.json new file mode 100644 index 000000000..aff680622 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/js_protocol-1.2.json @@ -0,0 +1,997 @@ +{ + "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/android/v8-13/include/js_protocol-1.3.json b/NativeScript/napi/android/v8-13/include/js_protocol-1.3.json new file mode 100644 index 000000000..a998d4611 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/js_protocol-1.3.json @@ -0,0 +1,1159 @@ +{ + "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/android/v8-13/include/js_protocol.pdl b/NativeScript/napi/android/v8-13/include/js_protocol.pdl new file mode 100644 index 000000000..73da9149b --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/js_protocol.pdl @@ -0,0 +1,1835 @@ +# 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 execution contexts with passed ones. Forces backend to skip + # stepping/pausing in scripts in these execution contexts. VM will try to leave blackboxed script by + # performing 'step in' several times, finally resorting to 'step out' if unsuccessful. + experimental command setBlackboxExecutionContexts + parameters + # Array of execution context unique ids for the debugger to ignore. + array of string uniqueIds + + # 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 + # If true, also ignore scripts with no source url. + optional boolean skipAnonymous + + # 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. + # Deprecated in favor of `resolvedBreakpoints` in the `scriptParsed` event. + deprecated 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 + SourceMap + EmbeddedDWARF + ExternalDWARF + # URL of the external symbol source. + optional string externalURL + + type ResolvedBreakpoint extends object + properties + # Breakpoint unique identifier. + BreakpointId breakpointId + # Actual breakpoint location. + Location location + + # 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 + # For Wasm modules, the content of the `build_id` custom section. For JavaScript the `debugId` magic comment. + string buildId + # 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 + # For Wasm modules, the content of the `build_id` custom section. For JavaScript the `debugId` magic comment. + string buildId + # 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 array of Debugger.DebugSymbols debugSymbols + # The name the embedder supplied for this script. + experimental optional string embedderName + # The list of set breakpoints in this script if calls to `setBreakpointByUrl` + # matches this script's URL or hash. Clients that use this list can ignore the + # `breakpointResolved` event. They are equivalent. + experimental optional array of ResolvedBreakpoint resolvedBreakpoints + +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 JavaScript heap size in bytes. + number usedSize + # Allocated JavaScript heap size in bytes. + number totalSize + # Used size in bytes in the embedder's garbage-collected heap. + number embedderHeapUsedSize + # Size in bytes of backing storage for array buffers and external strings. + number backingStorageSize + + # 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. + 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. + experimental 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`. + optional string executionContextName + + # This method does not remove binding function from global object but + # unsubscribes current runtime agent from Runtime.bindingCalled notifications. + 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/android/v8-13/include/libplatform/DEPS b/NativeScript/napi/android/v8-13/include/libplatform/DEPS new file mode 100644 index 000000000..d8bcf9988 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/libplatform/DEPS @@ -0,0 +1,9 @@ +include_rules = [ + "+libplatform/libplatform-export.h", +] + +specific_include_rules = { + "libplatform\.h": [ + "+libplatform/v8-tracing.h", + ], +} diff --git a/NativeScript/napi/android/v8-13/include/libplatform/libplatform-export.h b/NativeScript/napi/android/v8-13/include/libplatform/libplatform-export.h new file mode 100644 index 000000000..285db86ab --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/libplatform/libplatform-export.h @@ -0,0 +1,29 @@ +// 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. +#if defined(BUILDING_V8_PLATFORM_SHARED) || USING_V8_PLATFORM_SHARED +#define V8_PLATFORM_EXPORT __attribute__((visibility("default"))) +#else +#define V8_PLATFORM_EXPORT +#endif // defined(BUILDING_V8_PLATFORM_SHARED) || ... + +#endif // defined(_WIN32) + +#endif // V8_LIBPLATFORM_LIBPLATFORM_EXPORT_H_ diff --git a/NativeScript/napi/android/v8-13/include/libplatform/libplatform.h b/NativeScript/napi/android/v8-13/include/libplatform/libplatform.h new file mode 100644 index 000000000..6a34f4324 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/libplatform/libplatform.h @@ -0,0 +1,112 @@ +// 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/android/v8-13/include/libplatform/v8-tracing.h b/NativeScript/napi/android/v8-13/include/libplatform/v8-tracing.h new file mode 100644 index 000000000..227172924 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/libplatform/v8-tracing.h @@ -0,0 +1,332 @@ +// 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 "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/android/v8-13/include/v8-array-buffer.h b/NativeScript/napi/android/v8-13/include/v8-array-buffer.h new file mode 100644 index 000000000..1d9345762 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-array-buffer.h @@ -0,0 +1,615 @@ +// 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-memory-span.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8-platform.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class SharedArrayBuffer; + +#if defined(V8_COMPRESS_POINTERS) && \ + !defined(V8_COMPRESS_POINTERS_IN_SHARED_CAGE) +class IsolateGroup; +#endif + +#ifndef V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT +// Defined using gn arg `v8_array_buffer_internal_field_count`. +#define V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT 2 +#endif + +enum class ArrayBufferCreationMode { kInternalized, kExternalized }; +enum class BackingStoreInitializationMode { kZeroInitialized, kUninitialized }; +enum class BackingStoreOnFailureMode { kReturnNull, kOutOfMemory }; + +/** + * 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); } + + /** + * 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; + + /** + * Returns a size_t that determines the largest ArrayBuffer that can be + * allocated. Override if your Allocator is more restrictive than the + * default. Will only be called once, and the value returned will be + * cached. + * Should not return a value that is larger than kMaxByteLength. + */ + virtual size_t MaxAllocationSize() const { return kMaxByteLength; } + + /** + * 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 }; + + /** + * Returns page allocator used by this Allocator instance. + * + * When the sandbox used by Allocator it is expected that this returns + * sandbox's page allocator. + * Otherwise, it should return system page allocator. + */ + virtual PageAllocator* GetPageAllocator() { return nullptr; } + +#if defined(V8_COMPRESS_POINTERS) && \ + !defined(V8_COMPRESS_POINTERS_IN_SHARED_CAGE) + /** + * Convenience allocator. + * + * When the sandbox is enabled, this allocator will allocate its backing + * memory inside the sandbox that belongs to passed isolate group. + * 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(const IsolateGroup& group); +#endif // defined(V8_COMPRESS_POINTERS) && + // !defined(V8_COMPRESS_POINTERS_IN_SHARED_CAGE) + + /** + * Convenience allocator. + * + * When the sandbox is enabled, this allocator will allocate its backing + * memory inside the default global 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; + + /** + * Attempt to 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. If allocation fails, the Maybe + * returned will be empty. + */ + static MaybeLocal MaybeNew( + Isolate* isolate, size_t byte_length, + BackingStoreInitializationMode initialization_mode = + BackingStoreInitializationMode::kZeroInitialized); + + /** + * Create a new ArrayBuffer. Allocate |byte_length| bytes, which are either + * zero-initialized or uninitialized. 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, + BackingStoreInitializationMode initialization_mode = + BackingStoreInitializationMode::kZeroInitialized); + + /** + * 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 allocation can either be zero + * initialized, or uninitialized. 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 and on_failure is kOutOfMemory, then the + * function will crash with an out-of-memory error. + * + * Otherwise if GCs do not help (or the allocation is too large for GCs to + * help) and on_failure is kReturnNull, then a null result is returned. + */ + static std::unique_ptr NewBackingStore( + Isolate* isolate, size_t byte_length, + BackingStoreInitializationMode initialization_mode = + BackingStoreInitializationMode::kZeroInitialized, + BackingStoreOnFailureMode on_failure = + BackingStoreOnFailureMode::kOutOfMemory); + + /** + * 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_DEPRECATED( + "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()->IsResizableByUserJavaScript(). + */ + bool IsResizableByUserJavaScript() const; + + /** + * 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 constexpr int kInternalFieldCount = + V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT; + static constexpr int kEmbedderFieldCount = kInternalFieldCount; + +#if V8_ENABLE_SANDBOX + static constexpr size_t kMaxByteLength = + internal::kMaxSafeBufferSizeForSandbox; +#elif V8_HOST_ARCH_32_BIT + static constexpr size_t kMaxByteLength = std::numeric_limits::max(); +#else + // The maximum safe integer (2^53 - 1). + static constexpr size_t kMaxByteLength = + static_cast((uint64_t{1} << 53) - 1); +#endif + + private: + ArrayBuffer(); + static void CheckCast(Value* obj); + friend class TypedArray; +}; + +#ifndef V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT +// Defined using gn arg `v8_array_buffer_view_internal_field_count`. +#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 the contents of the ArrayBufferView's buffer as a MemorySpan. If + * the contents are on the V8 heap, they get copied into `storage`. Otherwise + * a view into the off-heap backing store is returned. The provided storage + * should be at least as large as the maximum on-heap size of a TypedArray, + * was defined in gn with `typed_array_max_size_in_heap`. The default value is + * 64 bytes. + */ + v8::MemorySpan GetContents(v8::MemorySpan storage); + + /** + * 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 constexpr int kInternalFieldCount = + V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT; + static const int kEmbedderFieldCount = kInternalFieldCount; + + 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, which are + * either zero-initialized or uninitialized. 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, + BackingStoreInitializationMode initialization_mode = + BackingStoreInitializationMode::kZeroInitialized); + + /** + * Create a new SharedArrayBuffer. Allocate |byte_length| bytes, which are + * either zero-initialized or uninitialized. Allocated memory will be owned by + * a created SharedArrayBuffer and will be deallocated when it is + * garbage-collected, unless the object is externalized. If allocation + * fails, the Maybe returned will be empty. + */ + static MaybeLocal MaybeNew( + Isolate* isolate, size_t byte_length, + BackingStoreInitializationMode initialization_mode = + BackingStoreInitializationMode::kZeroInitialized); + + /** + * 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 allocation can either be zero + * initialized, or uninitialized. 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 on_failure is kOutOfMemory and GCs do not help, then the function will + * crash with an out-of-memory error. + * + * Otherwise, if on_failure is kReturnNull and GCs do not help (or the + * byte_length is so large that the allocation cannot succeed), then a null + * result is returned. + */ + static std::unique_ptr NewBackingStore( + Isolate* isolate, size_t byte_length, + BackingStoreInitializationMode initialization_mode = + BackingStoreInitializationMode::kZeroInitialized, + BackingStoreOnFailureMode on_failure = + BackingStoreOnFailureMode::kOutOfMemory); + + /** + * 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 constexpr 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/android/v8-13/include/v8-callbacks.h b/NativeScript/napi/android/v8-13/include/v8-callbacks.h new file mode 100644 index 000000000..a9f380e38 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-callbacks.h @@ -0,0 +1,521 @@ +// 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, + + kLastJitCodeEventOption = kJitCodeEventEnumExisting +}; + +/** + * 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, + 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); + +using PrintCurrentStackTraceFilterCallback = + bool (*)(Isolate* isolate, Local script_name); + +/** + * 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, kLog = 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*); + +// --- Modify Code Generation From Strings Callback --- +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; +}; + +/** + * 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); + +// --- Failed Access Check Callback --- + +/** + * Access type specification. + */ +enum AccessType { + ACCESS_GET, + ACCESS_SET, + ACCESS_HAS, + ACCESS_DELETE, + ACCESS_KEYS +}; + +using FailedAccessCheckCallback = void (*)(Local target, + AccessType type, Local data); + +// --- 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 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 WebAssembly JSPI is enabled --- +using WasmJSPIEnabledCallback = bool (*)(Local context); + +/** + * Import phases in import requests. + */ +enum class ModuleImportPhase { + kSource, + kEvaluation, +}; + +/** + * 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_attributes are import attributes 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::GetImportAttributes(), this array does not + * contain the source Locations of the attributes. + * + * 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 HostImportModuleDynamicallyCallback = MaybeLocal (*)( + Local context, Local host_defined_options, + Local resource_name, Local specifier, + Local import_attributes); + +/** + * HostImportModuleWithPhaseDynamicallyCallback is called when we + * require the embedder to load a module with a specific phase. 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 phase is the phase of the import requested. + * + * The import_attributes are import attributes 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::GetImportAttributes(), this array does not + * contain the source Locations of the attributes. + * + * The Promise returned from this function is forwarded to userland + * JavaScript. The embedder must resolve this promise according to the phase + * requested: + * - For ModuleImportPhase::kSource, the promise must be resolved with a + * compiled ModuleSource object, or rejected with a SyntaxError if the + * module does not support source representation. + * - For ModuleImportPhase::kEvaluation, the promise must be resolved with a + * ModuleNamespace object of a module that has been compiled, instantiated, + * and evaluated. + * + * 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. + * + * This callback is still experimental and is only invoked for source phase + * imports. + */ +using HostImportModuleWithPhaseDynamicallyCallback = MaybeLocal (*)( + Local context, Local host_defined_options, + Local resource_name, Local specifier, + ModuleImportPhase phase, Local import_attributes); + +/** + * 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); + +/** + * IsJSApiWrapperNativeErrorCallback is called on an JSApiWrapper object to + * determine if Error.isError should return true or false. For instance, in an + * HTML embedder, DOMExceptions return true when passed to Error.isError. + */ +using IsJSApiWrapperNativeErrorCallback = bool (*)(Isolate* isolate, + Local obj); + +/** + * 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": "2.0", + * "filtered_urls": [ + * "https:\/\/.*\.chromium\.org\/.*", "https://v8.dev/";, "..." + * ], + * "trace_interpreter_frames": true + * } + */ + +using FilterETWSessionByURLCallback = + bool (*)(Local context, const std::string& etw_filter_payload); + +struct FilterETWSessionByURLResult { + // If true, enable ETW tracing for the current isolate. + bool enable_etw_tracing; + + // If true, also enables ETW tracing for interpreter stack frames. + bool trace_interpreter_frames; +}; +using FilterETWSessionByURL2Callback = FilterETWSessionByURLResult (*)( + 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/android/v8-13/include/v8-container.h b/NativeScript/napi/android/v8-13/include/v8-container.h new file mode 100644 index 000000000..380999e5e --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-container.h @@ -0,0 +1,183 @@ +// 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 + +#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); + } + + /** + * Creates a JavaScript array from a provided callback. + * + * \param context The v8::Context to create the array in. + * \param length The length of the array to be created. + * \param next_value_callback The callback that is invoked to retrieve + * elements for the array. The embedder can signal that the array + * initialization should be aborted by throwing an exception and returning + * an empty MaybeLocal. + * \returns The v8::Array if all elements were constructed successfully and an + * empty MaybeLocal otherwise. + */ + static MaybeLocal New( + Local context, size_t length, + std::function()> next_value_callback); + + 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/android/v8-13/include/v8-context.h b/NativeScript/napi/android/v8-13/include/v8-context.h new file mode 100644 index 000000000..43dadadea --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-context.h @@ -0,0 +1,518 @@ +// 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. + * + * \param internal_fields_deserializer An optional callback used + * to deserialize fields set by + * v8::Object::SetAlignedPointerInInternalField() in wrapper objects + * from the default context snapshot. It should match the + * SerializeInternalFieldsCallback() used by + * v8::SnapshotCreator::SetDefaultContext() when the default context + * snapshot is created. It does not need to be configured if the default + * context snapshot contains no wrapper objects with pointer internal + * fields, or if no custom startup snapshot is configured + * in the v8::CreateParams used to create the isolate. + * + * \param microtask_queue An optional microtask queue used to manage + * the microtasks created in this context. If not set the per-isolate + * default microtask queue would be used. + * + * \param context_data_deserializer An optional callback used + * to deserialize embedder data set by + * v8::Context::SetAlignedPointerInEmbedderData() in the default + * context from the default context snapshot. It does not need to be + * configured if the default context snapshot contains no pointer embedder + * data, or if no custom startup snapshot is configured in the + * v8::CreateParams used to create the isolate. + * + * \param api_wrapper_deserializer An optional callback used to deserialize + * API wrapper objects that was initially set with v8::Object::Wrap() and then + * serialized using SerializeAPIWrapperCallback. + */ + 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, + DeserializeContextDataCallback context_data_deserializer = + DeserializeContextDataCallback(), + DeserializeAPIWrapperCallback api_wrapper_deserializer = + DeserializeAPIWrapperCallback()); + + /** + * 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 internal_fields_deserializer An optional callback used + * to deserialize fields set by + * v8::Object::SetAlignedPointerInInternalField() in wrapper objects + * from the default context snapshot. It does not need to be + * configured if there are no wrapper objects with no internal + * pointer fields in the default context snapshot or if no startup + * snapshot is configured when the isolate is created. + * + * \param extensions See v8::Context::New(). + * + * \param global_object See v8::Context::New(). + * + * \param internal_fields_deserializer Similar to + * internal_fields_deserializer in v8::Context::New() but applies to + * the context specified by the context_snapshot_index. + * + * \param microtask_queue See v8::Context::New(). + * + * \param context_data_deserializer Similar to + * context_data_deserializer in v8::Context::New() but applies to + * the context specified by the context_snapshot_index. + * + *\param api_wrapper_deserializer Similar to api_wrapper_deserializer in + * v8::Context::New() but applies to the context specified by the + * context_snapshot_index. + */ + static MaybeLocal FromSnapshot( + Isolate* isolate, size_t context_snapshot_index, + DeserializeInternalFieldsCallback internal_fields_deserializer = + DeserializeInternalFieldsCallback(), + ExtensionConfiguration* extensions = nullptr, + MaybeLocal global_object = MaybeLocal(), + MicrotaskQueue* microtask_queue = nullptr, + DeserializeContextDataCallback context_data_deserializer = + DeserializeContextDataCallback(), + DeserializeAPIWrapperCallback api_wrapper_deserializer = + DeserializeAPIWrapperCallback()); + + /** + * 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, LocalVector& 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(Isolate* isolate, + int index); + 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::ModifyCodeGenerationFromStringsCallback 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); + + /** + * 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::ValueHelper::InternalRepresentationType 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(Isolate* isolate, 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; + return reinterpret_cast( + I::ReadExternalPointerField( + isolate, embedder_data, value_offset)); +#else + return SlowGetAlignedPointerFromEmbedderData(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) { + if (auto repr = GetDataFromSnapshotOnce(index); + repr != internal::ValueHelper::kEmpty) { + internal::PerformCastCheck(internal::ValueHelper::ReprAsValue(repr)); + return Local::FromRepr(repr); + } + return {}; +} + +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/android/v8-13/include/v8-cpp-heap-external.h b/NativeScript/napi/android/v8-13/include/v8-cpp-heap-external.h new file mode 100644 index 000000000..92a89c455 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-cpp-heap-external.h @@ -0,0 +1,56 @@ +// Copyright 2025 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_HEAP_EXTERNAL_H_ +#define INCLUDE_V8_HEAP_EXTERNAL_H_ + +#include "cppgc/type-traits.h" // NOLINT(build/include_directory) +#include "v8-sandbox.h" // NOLINT(build/include_directory) +#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 `cppgc::GarbageCollected` object allocated + * on the managed C++ heap (CppHeap). This type of value is mainly used to + * associate C++ data structures which aren't exposed to JavaScript with + * JavaScript objects. + */ +class V8_EXPORT CppHeapExternal : public Data { + public: + template + static Local New(Isolate* isolate, T* value, + CppHeapPointerTag tag) { + static_assert(cppgc::IsGarbageCollectedTypeV, + "Object must be of type GarbageCollected."); + return NewImpl(isolate, value, tag); + } + + V8_INLINE static CppHeapExternal* Cast(Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + template + T* Value(Isolate* isolate, CppHeapPointerTagRange tag_range) const { + static_assert(cppgc::IsGarbageCollectedTypeV, + "Object must be of type GarbageCollected."); + return static_cast(ValueImpl(isolate, tag_range)); + } + + private: + static void CheckCast(v8::Data* obj); + static Local NewImpl(Isolate* isolate, void* value, + CppHeapPointerTag tag); + void* ValueImpl(Isolate*, CppHeapPointerTagRange tag_range) const; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_HEAP_EXTERNAL_H_ diff --git a/NativeScript/napi/android/v8-13/include/v8-cppgc.h b/NativeScript/napi/android/v8-13/include/v8-cppgc.h new file mode 100644 index 000000000..aa3813b95 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-cppgc.h @@ -0,0 +1,194 @@ +// 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; + +struct V8_EXPORT CppHeapCreateParams { + explicit CppHeapCreateParams( + std::vector> custom_spaces) + : custom_spaces(std::move(custom_spaces)) {} + + CppHeapCreateParams(const CppHeapCreateParams&) = delete; + CppHeapCreateParams& operator=(const CppHeapCreateParams&) = delete; + + std::vector> custom_spaces; + /** + * 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. + */ + V8_DEPRECATED("Terminate gets automatically called in the CppHeap destructor") + 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 indices. + * \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); + + 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/android/v8-13/include/v8-data.h b/NativeScript/napi/android/v8-13/include/v8-data.h new file mode 100644 index 000000000..f9c60f4dc --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-data.h @@ -0,0 +1,90 @@ +// 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 true if this data is a |v8::ModuleRequest|. + */ + bool IsModuleRequest() 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; + + /** + * Returns true if this value is a `CppHeapExternal` object. + */ + bool IsCppHeapExternal() 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/android/v8-13/include/v8-date.h b/NativeScript/napi/android/v8-13/include/v8-date.h new file mode 100644 index 000000000..5c3cbd91c --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-date.h @@ -0,0 +1,57 @@ +// 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); + + static V8_WARN_UNUSED_RESULT MaybeLocal Parse( + Local context, + Local date_string); + + /** + * 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; + + /** + * Generates UTC string representation. + */ + v8::Local ToUTCString() 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/android/v8-13/include/v8-debug.h b/NativeScript/napi/android/v8-13/include/v8-debug.h new file mode 100644 index 000000000..23b392c7d --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-debug.h @@ -0,0 +1,179 @@ +// 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 zero based source position (character offset) for the associated + * function. + */ + int GetSourcePosition() const; + + /** + * 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 the (unique) ID of this stack trace. + */ + int GetID() const; + + /** + * 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/android/v8-13/include/v8-embedder-heap.h b/NativeScript/napi/android/v8-13/include/v8-embedder-heap.h new file mode 100644 index 000000000..929b3c396 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-embedder-heap.h @@ -0,0 +1,54 @@ +// 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 { +namespace internal { +class TracedHandles; +} // namespace internal + +class Isolate; +class Value; + +/** + * Handler for embedder roots on non-unified heap garbage collections. + */ +class V8_EXPORT EmbedderRootsHandler { + public: + virtual ~EmbedderRootsHandler() = default; + + EmbedderRootsHandler() = default; + + /** + * 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) { + return false; + } + + private: + friend class internal::TracedHandles; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_EMBEDDER_HEAP_H_ diff --git a/NativeScript/napi/android/v8-13/include/v8-embedder-state-scope.h b/NativeScript/napi/android/v8-13/include/v8-embedder-state-scope.h new file mode 100644 index 000000000..ec8da4573 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-embedder-state-scope.h @@ -0,0 +1,52 @@ +// 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-internal.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; + +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/android/v8-13/include/v8-exception.h b/NativeScript/napi/android/v8-13/include/v8-exception.h new file mode 100644 index 000000000..5441a0ab6 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-exception.h @@ -0,0 +1,295 @@ +// 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 "v8-object.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 WasmSuspendError(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); + + /** + * Captures the current stack trace and attaches it to the given object in the + * form of `stack` property. + */ + static Maybe CaptureStackTrace(Local context, + Local object); +}; + +/** + * This is a part of experimental Api and might be changed without further + * notice. + * Do not use it. + */ +enum class ExceptionContext : uint32_t { + kUnknown, + kConstructor, + kOperation, + kAttributeGet, + kAttributeSet, + kIndexedQuery, + kIndexedGetter, + kIndexedDescriptor, + kIndexedSetter, + kIndexedDefiner, + kIndexedDeleter, + kNamedQuery, + kNamedGetter, + kNamedDescriptor, + kNamedSetter, + kNamedDefiner, + kNamedDeleter, + kNamedEnumerator +}; + +/** + * This is a part of experimental Api and might be changed without further + * notice. + * Do not use it. + */ +class ExceptionPropagationMessage { + public: + ExceptionPropagationMessage(v8::Isolate* isolate, Local exception, + Local interface_name, + Local property_name, + ExceptionContext exception_context) + : isolate_(isolate), + exception_(exception), + interface_name_(interface_name), + property_name_(property_name), + exception_context_(exception_context) {} + + V8_INLINE Isolate* GetIsolate() const { return isolate_; } + V8_INLINE Local GetException() const { return exception_; } + V8_INLINE Local GetInterfaceName() const { return interface_name_; } + V8_INLINE Local GetPropertyName() const { return property_name_; } + V8_INLINE ExceptionContext GetExceptionContext() const { + return exception_context_; + } + + private: + Isolate* isolate_; + Local exception_; + Local interface_name_; + Local property_name_; + ExceptionContext exception_context_; +}; + +using ExceptionPropagationCallback = + void (*)(ExceptionPropagationMessage message); + +/** + * 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; + + friend class internal::Isolate; + friend class internal::ThreadLocalTop; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_EXCEPTION_H_ diff --git a/NativeScript/napi/android/v8-13/include/v8-extension.h b/NativeScript/napi/android/v8-13/include/v8-extension.h new file mode 100644 index 000000000..0705e2afb --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-extension.h @@ -0,0 +1,62 @@ +// 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/android/v8-13/include/v8-external-memory-accounter.h b/NativeScript/napi/android/v8-13/include/v8-external-memory-accounter.h new file mode 100644 index 000000000..70df8f4a8 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-external-memory-accounter.h @@ -0,0 +1,60 @@ +// Copyright 2024 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_EXTERNAL_MEMORY_ACCOUNTER_H_ +#define INCLUDE_EXTERNAL_MEMORY_ACCOUNTER_H_ + +#include + +#include "v8-isolate.h" + +namespace v8 { + +/** + * This class is used to give V8 an indication of the amount of externally + * allocated memory that is kept alive by JavaScript objects. V8 uses this to + * decide when to perform garbage collections. Registering externally allocated + * memory will trigger garbage collections more often than it would otherwise in + * an attempt to garbage collect the JavaScript objects that keep the externally + * allocated memory alive. Instances of ExternalMemoryAccounter check that the + * reported external memory is back to 0 on destruction. + */ +class V8_EXPORT ExternalMemoryAccounter { + public: + /** + * Returns the amount of external memory registered for `isolate`. + */ + static int64_t GetTotalAmountOfExternalAllocatedMemoryForTesting( + const Isolate* isolate); + + ExternalMemoryAccounter() = default; + ~ExternalMemoryAccounter(); + ExternalMemoryAccounter(ExternalMemoryAccounter&&); + ExternalMemoryAccounter& operator=(ExternalMemoryAccounter&&); + ExternalMemoryAccounter(const ExternalMemoryAccounter&) = delete; + ExternalMemoryAccounter& operator=(const ExternalMemoryAccounter&) = delete; + + /** + * Reports an increase of `size` bytes of external memory. + */ + void Increase(Isolate* isolate, size_t size); + /** + * Reports an update of `delta` bytes of external memory. + */ + void Update(Isolate* isolate, int64_t delta); + /** + * Reports an decrease of `size` bytes of external memory. + */ + void Decrease(Isolate* isolate, size_t size); + + private: +#ifdef V8_ENABLE_MEMORY_ACCOUNTING_CHECKS + size_t amount_of_external_memory_ = 0; + v8::Isolate* isolate_ = nullptr; +#endif +}; + +} // namespace v8 + +#endif // INCLUDE_EXTERNAL_MEMORY_ACCOUNTER_H_ diff --git a/NativeScript/napi/android/v8-13/include/v8-external.h b/NativeScript/napi/android/v8-13/include/v8-external.h new file mode 100644 index 000000000..2e245036f --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-external.h @@ -0,0 +1,37 @@ +// 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/android/v8-13/include/v8-fast-api-calls.h b/NativeScript/napi/android/v8-13/include/v8-fast-api-calls.h new file mode 100644 index 000000000..c40594081 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-fast-api-calls.h @@ -0,0 +1,816 @@ +// 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_FAST_API_CALLS_H_ +#define INCLUDE_V8_FAST_API_CALLS_H_ + +/** + * 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. + */ + +#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; + +START_ALLOW_USE_DEPRECATED() +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 AnyCType 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 V8_DEPRECATE_SOON( + "There is no special support in V8 anymore, there is no need to" + "use a SequenceType") SequenceType : uint8_t { + kScalar, + kIsSequence, // sequence + 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, Flags flags = Flags::kNone) + : type_(type), sequence_type_(SequenceType::kScalar), flags_(flags) {} + + V8_DEPRECATE_SOON("Use CTypeInfo(Type, Flags) instead") + constexpr CTypeInfo(Type type, SequenceType sequence_type, + 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_; } + V8_DEPRECATE_SOON("Use the constant SequenceType::kScalar instead") + 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_; +}; +END_ALLOW_USE_DEPRECATED() + +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. +union V8_TRIVIAL_ABI AnyCType { + AnyCType() : int64_value(0) {} + +#if defined(V8_ENABLE_LOCAL_OFF_STACK_CHECK) && V8_HAS_ATTRIBUTE_TRIVIAL_ABI + // In this case, Local is not trivially copyable and the implicit + // copy constructor and copy assignment for the union are deleted. + AnyCType(const AnyCType& other) : int64_value(other.int64_value) {} + AnyCType& operator=(const AnyCType& other) { + int64_value = other.int64_value; + return *this; + } +#endif + + 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 FastOneByteString* string_value; + FastApiCallbackOptions* options_value; +}; + +static_assert( + sizeof(AnyCType) == 8, + "The union AnyCType 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 }; + + template + static CFunction Make(F* func, + CFunctionInfo::Int64Representation int64_rep = + CFunctionInfo::Int64Representation::kNumber) { + CFunction result = ArgUnwrap::Make(func, int64_rep); + result.GetInt64Representation(); + return result; + } + + // Provided for testing purposes. + template + static CFunction Make(R (*func)(Args...), + R_Patch (*patching_func)(Args_Patch...), + CFunctionInfo::Int64Representation int64_rep = + CFunctionInfo::Int64Representation::kNumber) { + CFunction c_func = ArgUnwrap::Make(func, int64_rep); + 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...), + CFunctionInfo::Int64Representation int64_rep = + CFunctionInfo::Int64Representation::kNumber); + }; +}; + +/** + * 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 {}; + } + + v8::Isolate* isolate = nullptr; + + /** + * The `data` passed to the FunctionTemplate constructor, or `undefined`. + */ + v8::Local data; +}; + +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; } \ + }; + +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(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 + +#undef TYPED_ARRAY_C_TYPES + +template <> +struct TypeInfoHelper { + static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } + + static constexpr CTypeInfo::Type Type() { + return CTypeInfo::kCallbackOptionsType; + } +}; + +template <> +struct TypeInfoHelper { + static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } + + static constexpr CTypeInfo::Type Type() { + return CTypeInfo::Type::kSeqOneByteString; + } +}; + +#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(); + + 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."); + + // Return the same type with the merged flags. + return CTypeInfo(internal::TypeInfoHelper::Type(), 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 = std::tuple_element_t>; + }; + + // 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_t>::BaseType, + std::tuple_element_t>::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...), CFunctionInfo::Int64Representation int64_rep) { + if (int64_rep == CFunctionInfo::Int64Representation::kNumber) { + return internal::CFunctionBuilder().Fn(func).Build(); + } + return internal::CFunctionBuilder() + .Fn(func) + .template 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/android/v8-13/include/v8-forward.h b/NativeScript/napi/android/v8-13/include/v8-forward.h new file mode 100644 index 000000000..435fe856d --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-forward.h @@ -0,0 +1,82 @@ +// 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 DictionaryTemplate; +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/android/v8-13/include/v8-function-callback.h b/NativeScript/napi/android/v8-13/include/v8-function-callback.h new file mode 100644 index 000000000..0a4de4647 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-function-callback.h @@ -0,0 +1,719 @@ +// 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 +#include + +#include "v8-internal.h" // NOLINT(build/include_directory) +#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 + +namespace api_internal { +V8_EXPORT v8::Local GetFunctionTemplateData( + v8::Isolate* isolate, v8::Local raw_target); +} // namespace api_internal + +template +class ReturnValue { + public: + template + V8_INLINE ReturnValue(const ReturnValue& that) : value_(that.value_) { + static_assert(std::is_base_of_v, "type check"); + } + // Handle-based setters. + template + V8_INLINE void Set(const Global& handle); + template + V8_INLINE void SetNonEmpty(const Global& handle); + template + V8_INLINE void Set(const BasicTracedReference& handle); + template + V8_INLINE void SetNonEmpty(const BasicTracedReference& handle); + template + V8_INLINE void Set(const Local handle); + template + V8_INLINE void SetNonEmpty(const Local handle); + // Fast primitive number setters. + V8_INLINE void Set(bool value); + V8_INLINE void Set(double i); + V8_INLINE void Set(int16_t i); + V8_INLINE void Set(int32_t i); + V8_INLINE void Set(int64_t i); + V8_INLINE void Set(uint16_t i); + V8_INLINE void Set(uint32_t i); + V8_INLINE void Set(uint64_t i); + // Fast JS primitive setters + V8_INLINE void SetNull(); + V8_INLINE void SetUndefined(); + V8_INLINE void SetFalse(); + 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); + // Default value depends on : + // - -> true_value, + // - -> true_value, + // - -> 0, + // - -> undefined_value, + // - -> undefined_value. + V8_INLINE void SetDefaultValue(); + 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; + /** 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; + friend void internal::PrintFunctionCallbackInfo(void*); + + // TODO(ishell, http://crbug.com/326505377): in case of non-constructor + // call, don't pass kNewTarget and kUnused. Add IsConstructCall flag to + // kIsolate field. + static constexpr int kUnusedIndex = 0; + static constexpr int kIsolateIndex = 1; + static constexpr int kContextIndex = 2; + static constexpr int kReturnValueIndex = 3; + static constexpr int kTargetIndex = 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); + + // TODO(https://crbug.com/326505377): flatten the v8::FunctionCallbackInfo + // object to avoid indirect loads through values_ and implicit_args_ and + // reduce the number of instructions in the CallApiCallback builtin. + internal::Address* implicit_args_; + internal::Address* values_; + internal::Address 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_DEPRECATE_SOON( + "V8 will stop providing access to hidden prototype (i.e. " + "JSGlobalObject). Use HolderV2() instead. \n" + "DO NOT try to workaround this by accessing JSGlobalObject via " + "v8::Object::GetPrototype() - it'll be deprecated soon too. \n" + "See http://crbug.com/333672197. ") + V8_INLINE Local Holder() 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`. + * In case the property is installed on the global object the Holder() + * would return the global proxy. + */ + V8_INLINE Local HolderV2() 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: + template + friend class PropertyCallbackInfo; + friend class MacroAssembler; + friend class internal::PropertyCallbackArguments; + friend class internal::CustomArguments; + friend void internal::PrintPropertyCallbackInfo(void*); + + static constexpr int kPropertyKeyIndex = 0; + static constexpr int kShouldThrowOnErrorIndex = 1; + static constexpr int kHolderIndex = 2; + static constexpr int kIsolateIndex = 3; + static constexpr int kHolderV2Index = 4; + static constexpr int kReturnValueIndex = 5; + static constexpr int kDataIndex = 6; + static constexpr int kThisIndex = 7; + static constexpr int kArgsLength = 8; + + static constexpr int kSize = kArgsLength * internal::kApiSystemPointerSize; + + PropertyCallbackInfo() = default; + + mutable internal::Address args_[kArgsLength]; +}; + +using FunctionCallback = void (*)(const FunctionCallbackInfo& info); + +// --- Implementation --- + +template +ReturnValue::ReturnValue(internal::Address* slot) : value_(slot) {} + +template +void ReturnValue::SetInternal(internal::Address value) { +#if V8_STATIC_ROOTS_BOOL + using I = internal::Internals; + // Ensure that the upper 32-bits are not modified. Compiler should be + // able to optimize this to a store of a lower 32-bits of the value. + // This is fine since the callback can return only JavaScript values which + // are either Smis or heap objects allocated in the main cage. + *value_ = I::DecompressTaggedField(*value_, I::CompressTagged(value)); +#else + *value_ = value; +#endif // V8_STATIC_ROOTS_BOOL +} + +template +template +void ReturnValue::Set(const Global& handle) { + static_assert(std::is_base_of_v, "type check"); + if (V8_UNLIKELY(handle.IsEmpty())) { + SetDefaultValue(); + } else { + SetInternal(handle.ptr()); + } +} + +template +template +void ReturnValue::SetNonEmpty(const Global& handle) { + static_assert(std::is_base_of_v, "type check"); +#ifdef V8_ENABLE_CHECKS + internal::VerifyHandleIsNonEmpty(handle.IsEmpty()); +#endif // V8_ENABLE_CHECKS + SetInternal(handle.ptr()); +} + +template +template +void ReturnValue::Set(const BasicTracedReference& handle) { + static_assert(std::is_base_of_v, "type check"); + if (V8_UNLIKELY(handle.IsEmpty())) { + SetDefaultValue(); + } else { + SetInternal(handle.ptr()); + } +} + +template +template +void ReturnValue::SetNonEmpty(const BasicTracedReference& handle) { + static_assert(std::is_base_of_v, "type check"); +#ifdef V8_ENABLE_CHECKS + internal::VerifyHandleIsNonEmpty(handle.IsEmpty()); +#endif // V8_ENABLE_CHECKS + SetInternal(handle.ptr()); +} + +template +template +void ReturnValue::Set(const Local handle) { + // "V8_DEPRECATE_SOON" this method if |T| is |void|. +#ifdef V8_IMMINENT_DEPRECATION_WARNINGS + static constexpr bool is_allowed_void = false; + static_assert(!std::is_void_v, + "ReturnValue::Set(const Local) is deprecated. " + "Do nothing to indicate that the operation succeeded or use " + "SetFalse() to indicate that the operation failed (don't " + "forget to handle info.ShouldThrowOnError()). " + "See http://crbug.com/348660658 for details."); +#else + static constexpr bool is_allowed_void = std::is_void_v; +#endif // V8_IMMINENT_DEPRECATION_WARNINGS + static_assert(is_allowed_void || std::is_base_of_v, "type check"); + if (V8_UNLIKELY(handle.IsEmpty())) { + SetDefaultValue(); + } else if constexpr (is_allowed_void) { + // Simulate old behaviour for "v8::AccessorSetterCallback" for which + // it was possible to set the return value even for ReturnValue. + Set(handle->BooleanValue(GetIsolate())); + } else { + SetInternal(handle.ptr()); + } +} + +template +template +void ReturnValue::SetNonEmpty(const Local handle) { + // "V8_DEPRECATE_SOON" this method if |T| is |void|. +#ifdef V8_IMMINENT_DEPRECATION_WARNINGS + static constexpr bool is_allowed_void = false; + static_assert(!std::is_void_v, + "ReturnValue::SetNonEmpty(const Local) is deprecated. " + "Do nothing to indicate that the operation succeeded or use " + "SetFalse() to indicate that the operation failed (don't " + "forget to handle info.ShouldThrowOnError()). " + "See http://crbug.com/348660658 for details."); +#else + static constexpr bool is_allowed_void = std::is_void_v; +#endif // V8_IMMINENT_DEPRECATION_WARNINGS + static_assert(is_allowed_void || std::is_base_of_v, "type check"); +#ifdef V8_ENABLE_CHECKS + internal::VerifyHandleIsNonEmpty(handle.IsEmpty()); +#endif // V8_ENABLE_CHECKS + if constexpr (is_allowed_void) { + // Simulate old behaviour for "v8::AccessorSetterCallback" for which + // it was possible to set the return value even for ReturnValue. + Set(handle->BooleanValue(GetIsolate())); + } else { + SetInternal(handle.ptr()); + } +} + +template +void ReturnValue::Set(double i) { + static_assert(std::is_base_of_v, "type check"); + SetNonEmpty(Number::New(GetIsolate(), i)); +} + +template +void ReturnValue::Set(int16_t i) { + static_assert(std::is_base_of_v, "type check"); + using I = internal::Internals; + static_assert(I::IsValidSmi(std::numeric_limits::min())); + static_assert(I::IsValidSmi(std::numeric_limits::max())); + SetInternal(I::IntegralToSmi(i)); +} + +template +void ReturnValue::Set(int32_t i) { + static_assert(std::is_base_of_v, "type check"); + if (const auto result = internal::Internals::TryIntegralToSmi(i)) { + SetInternal(*result); + return; + } + SetNonEmpty(Integer::New(GetIsolate(), i)); +} + +template +void ReturnValue::Set(int64_t i) { + static_assert(std::is_base_of_v, "type check"); + if (const auto result = internal::Internals::TryIntegralToSmi(i)) { + SetInternal(*result); + return; + } + SetNonEmpty(Number::New(GetIsolate(), static_cast(i))); +} + +template +void ReturnValue::Set(uint16_t i) { + static_assert(std::is_base_of_v, "type check"); + using I = internal::Internals; + static_assert(I::IsValidSmi(std::numeric_limits::min())); + static_assert(I::IsValidSmi(std::numeric_limits::max())); + SetInternal(I::IntegralToSmi(i)); +} + +template +void ReturnValue::Set(uint32_t i) { + static_assert(std::is_base_of_v, "type check"); + if (const auto result = internal::Internals::TryIntegralToSmi(i)) { + SetInternal(*result); + return; + } + SetNonEmpty(Integer::NewFromUnsigned(GetIsolate(), i)); +} + +template +void ReturnValue::Set(uint64_t i) { + static_assert(std::is_base_of_v, "type check"); + if (const auto result = internal::Internals::TryIntegralToSmi(i)) { + SetInternal(*result); + return; + } + SetNonEmpty(Number::New(GetIsolate(), static_cast(i))); +} + +template +void ReturnValue::Set(bool value) { + static_assert(std::is_void_v || std::is_base_of_v, + "type check"); + using I = internal::Internals; +#if V8_STATIC_ROOTS_BOOL +#ifdef V8_ENABLE_CHECKS + internal::PerformCastCheck( + internal::ValueHelper::SlotAsValue(value_)); +#endif // V8_ENABLE_CHECKS + SetInternal(value ? I::StaticReadOnlyRoot::kTrueValue + : I::StaticReadOnlyRoot::kFalseValue); +#else + int root_index; + if (value) { + root_index = I::kTrueValueRootIndex; + } else { + root_index = I::kFalseValueRootIndex; + } + *value_ = I::GetRoot(GetIsolate(), root_index); +#endif // V8_STATIC_ROOTS_BOOL +} + +template +void ReturnValue::SetDefaultValue() { + using I = internal::Internals; + if constexpr (std::is_same_v || std::is_same_v) { + Set(true); + } else if constexpr (std::is_same_v) { + SetInternal(I::IntegralToSmi(0)); + } else { + static_assert(std::is_same_v || std::is_same_v); +#if V8_STATIC_ROOTS_BOOL + SetInternal(I::StaticReadOnlyRoot::kUndefinedValue); +#else + *value_ = I::GetRoot(GetIsolate(), I::kUndefinedValueRootIndex); +#endif // V8_STATIC_ROOTS_BOOL + } +} + +template +void ReturnValue::SetNull() { + static_assert(std::is_base_of_v, "type check"); + using I = internal::Internals; +#if V8_STATIC_ROOTS_BOOL +#ifdef V8_ENABLE_CHECKS + internal::PerformCastCheck( + internal::ValueHelper::SlotAsValue(value_)); +#endif // V8_ENABLE_CHECKS + SetInternal(I::StaticReadOnlyRoot::kNullValue); +#else + *value_ = I::GetRoot(GetIsolate(), I::kNullValueRootIndex); +#endif // V8_STATIC_ROOTS_BOOL +} + +template +void ReturnValue::SetUndefined() { + static_assert(std::is_base_of_v, "type check"); + using I = internal::Internals; +#if V8_STATIC_ROOTS_BOOL +#ifdef V8_ENABLE_CHECKS + internal::PerformCastCheck( + internal::ValueHelper::SlotAsValue(value_)); +#endif // V8_ENABLE_CHECKS + SetInternal(I::StaticReadOnlyRoot::kUndefinedValue); +#else + *value_ = I::GetRoot(GetIsolate(), I::kUndefinedValueRootIndex); +#endif // V8_STATIC_ROOTS_BOOL +} + +template +void ReturnValue::SetFalse() { + static_assert(std::is_void_v || std::is_base_of_v, + "type check"); + using I = internal::Internals; +#if V8_STATIC_ROOTS_BOOL +#ifdef V8_ENABLE_CHECKS + internal::PerformCastCheck( + internal::ValueHelper::SlotAsValue(value_)); +#endif // V8_ENABLE_CHECKS + SetInternal(I::StaticReadOnlyRoot::kFalseValue); +#else + *value_ = I::GetRoot(GetIsolate(), I::kFalseValueRootIndex); +#endif // V8_STATIC_ROOTS_BOOL +} + +template +void ReturnValue::SetEmptyString() { + static_assert(std::is_base_of_v, "type check"); + using I = internal::Internals; +#if V8_STATIC_ROOTS_BOOL +#ifdef V8_ENABLE_CHECKS + internal::PerformCastCheck( + internal::ValueHelper::SlotAsValue(value_)); +#endif // V8_ENABLE_CHECKS + SetInternal(I::StaticReadOnlyRoot::kEmptyString); +#else + *value_ = I::GetRoot(GetIsolate(), I::kEmptyStringRootIndex); +#endif // V8_STATIC_ROOTS_BOOL +} + +template +Isolate* ReturnValue::GetIsolate() const { + return *reinterpret_cast(&value_[kIsolateValueIndex]); +} + +template +Local ReturnValue::Get() const { + return Local::New(GetIsolate(), + internal::ValueHelper::SlotAsValue(value_)); +} + +template +template +void ReturnValue::Set(S* whatever) { + static_assert(sizeof(S) < 0, "incompilable to prevent inadvertent misuse"); +} + +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::NewTarget() const { + return Local::FromSlot(&implicit_args_[kNewTargetIndex]); +} + +template +Local FunctionCallbackInfo::Data() const { + auto target = Local::FromSlot(&implicit_args_[kTargetIndex]); + return api_internal::GetFunctionTemplateData(GetIsolate(), target); +} + +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 static_cast(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]); +} + +namespace api_internal { +// Returns JSGlobalProxy if holder is JSGlobalObject or unmodified holder +// otherwise. +V8_EXPORT internal::Address ConvertToJSGlobalProxyIfNecessary( + internal::Address holder); +} // namespace api_internal + +template +Local PropertyCallbackInfo::HolderV2() const { + using I = internal::Internals; + if (!I::HasHeapObjectTag(args_[kHolderV2Index])) { + args_[kHolderV2Index] = + api_internal::ConvertToJSGlobalProxyIfNecessary(args_[kHolderIndex]); + } + return Local::FromSlot(&args_[kHolderV2Index]); +} + +template +ReturnValue PropertyCallbackInfo::GetReturnValue() const { + return ReturnValue(&args_[kReturnValueIndex]); +} + +template +bool PropertyCallbackInfo::ShouldThrowOnError() const { + using I = internal::Internals; + if (args_[kShouldThrowOnErrorIndex] != + I::IntegralToSmi(I::kInferShouldThrowMode)) { + return args_[kShouldThrowOnErrorIndex] != I::IntegralToSmi(I::kDontThrow); + } + return v8::internal::ShouldThrowOnError( + reinterpret_cast(GetIsolate())); +} + +} // namespace v8 + +#endif // INCLUDE_V8_FUNCTION_CALLBACK_H_ diff --git a/NativeScript/napi/android/v8-13/include/v8-function.h b/NativeScript/napi/android/v8-13/include/v8-function.h new file mode 100644 index 000000000..9ee77596c --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-function.h @@ -0,0 +1,148 @@ +// 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 Location; +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(v8::Isolate* isolate, + Local context, + Local recv, int argc, + Local argv[]); + V8_WARN_UNUSED_RESULT MaybeLocal Call(Local context, + Local recv, int argc, + Local argv[]); + + void SetName(Local name); + Local GetName() 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 line and column number of function body, else returns + * {-1, -1}. + */ + Location GetScriptLocation() 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/android/v8-13/include/v8-handle-base.h b/NativeScript/napi/android/v8-13/include/v8-handle-base.h new file mode 100644 index 000000000..9149f8fb0 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-handle-base.h @@ -0,0 +1,155 @@ +// 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::api_internal { + +template +class StackAllocated { + public: + V8_INLINE StackAllocated() = default; + + protected: + struct no_checking_tag {}; + static constexpr no_checking_tag do_not_check{}; + + V8_INLINE explicit StackAllocated(no_checking_tag) {} + V8_INLINE explicit StackAllocated(const StackAllocated& other, + no_checking_tag) {} + + V8_INLINE void VerifyOnStack() const {} +}; + +template <> +class V8_TRIVIAL_ABI StackAllocated : public StackAllocated { + public: + V8_INLINE StackAllocated() { VerifyOnStack(); } + +#if V8_HAS_ATTRIBUTE_TRIVIAL_ABI + // In this case, StackAllocated becomes not trivially copyable. + V8_INLINE StackAllocated(const StackAllocated& other) { VerifyOnStack(); } + StackAllocated& operator=(const StackAllocated&) = default; +#endif + + protected: + V8_INLINE explicit StackAllocated(no_checking_tag tag) + : StackAllocated(tag) {} + V8_INLINE explicit StackAllocated(const StackAllocated& other, + no_checking_tag tag) + : StackAllocated(other, tag) {} + +#ifdef ENABLE_SLOW_DCHECKS + V8_EXPORT void VerifyOnStack() const; +#else + V8_INLINE V8_EXPORT void VerifyOnStack() const {} +#endif +}; + +/** + * A base class for abstract handles containing indirect pointers. + * These are useful regardless of whether direct local support is enabled. + */ +class IndirectHandleBase { + public: + // Returns true if the handle is empty. + V8_INLINE bool IsEmpty() const { return location_ == nullptr; } + + // Sets the handle to be empty. IsEmpty() will then return true. + V8_INLINE void Clear() { location_ = nullptr; } + + protected: + friend class internal::ValueHelper; + friend class internal::HandleHelper; + + V8_INLINE IndirectHandleBase() = default; + V8_INLINE IndirectHandleBase(const IndirectHandleBase& other) = default; + V8_INLINE IndirectHandleBase& operator=(const IndirectHandleBase& that) = + default; + + V8_INLINE explicit IndirectHandleBase(internal::Address* location) + : location_(location) {} + + // Returns the address of the actual heap object (tagged). + // This method must be called only if the handle is not empty, otherwise it + // will crash. + V8_INLINE internal::Address ptr() const { return *location_; } + + // Returns a reference to the slot (indirect pointer). + V8_INLINE internal::Address* const& slot() const { return location_; } + V8_INLINE internal::Address*& slot() { return location_; } + + // Returns the handler's "value" (direct or indirect pointer, depending on + // whether direct local support is enabled). + template + V8_INLINE T* value() const { + return internal::ValueHelper::SlotAsValue(slot()); + } + +#ifdef V8_ENABLE_DIRECT_HANDLE + V8_INLINE internal::ValueHelper::InternalRepresentationType repr() const { + return location_ ? *location_ : internal::ValueHelper::kEmpty; + } +#else + V8_INLINE internal::ValueHelper::InternalRepresentationType repr() const { + return location_; + } +#endif // V8_ENABLE_DIRECT_HANDLE + + private: + internal::Address* location_ = nullptr; +}; + +#ifdef V8_ENABLE_DIRECT_HANDLE + +/** + * A base class for abstract handles containing direct pointers. + * These are only possible when conservative stack scanning is enabled. + */ +class DirectHandleBase { + public: + // Returns true if the handle is empty. + V8_INLINE bool IsEmpty() const { + return ptr_ == internal::ValueHelper::kEmpty; + } + + // Sets the handle to be empty. IsEmpty() will then return true. + V8_INLINE void Clear() { ptr_ = internal::ValueHelper::kEmpty; } + + protected: + friend class internal::ValueHelper; + friend class internal::HandleHelper; + + V8_INLINE DirectHandleBase() = default; + V8_INLINE DirectHandleBase(const DirectHandleBase& other) = default; + V8_INLINE DirectHandleBase& operator=(const DirectHandleBase& that) = default; + + V8_INLINE explicit DirectHandleBase(internal::Address ptr) : ptr_(ptr) {} + + // Returns the address of the referenced object. + V8_INLINE internal::Address ptr() const { return ptr_; } + + // Returns the handler's "value" (direct pointer, as direct local support + // is guaranteed to be enabled here). + template + V8_INLINE T* value() const { + return reinterpret_cast(ptr_); + } + + V8_INLINE internal::ValueHelper::InternalRepresentationType repr() const { + return ptr_; + } + + private: + internal::Address ptr_ = internal::ValueHelper::kEmpty; +}; + +#endif // V8_ENABLE_DIRECT_HANDLE + +} // namespace v8::api_internal + +#endif // INCLUDE_V8_HANDLE_BASE_H_ diff --git a/NativeScript/napi/android/v8-13/include/v8-initialization.h b/NativeScript/napi/android/v8-13/include/v8-initialization.h new file mode 100644 index 000000000..46a21a02c --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-initialization.h @@ -0,0 +1,314 @@ +// 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_INITIALIZATION_H_ +#define INCLUDE_V8_INITIALIZATION_H_ + +#include +#include + +#include "v8-callbacks.h" // NOLINT(build/include_directory) +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-isolate.h" // NOLINT(build/include_directory) +#include "v8-platform.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +// We reserve the V8_* prefix for macros defined in V8 public API and +// assume there are no name conflicts with the embedder's code. + +/** + * The v8 JavaScript engine. + */ +namespace v8 { + +class PageAllocator; +class Platform; +template +class PersistentValueMapBase; + +/** + * EntropySource is used as a callback function when v8 needs a source + * of entropy. + */ +using EntropySource = bool (*)(unsigned char* buffer, size_t length); + +/** + * ReturnAddressLocationResolver is used as a callback function when v8 is + * resolving the location of a return address on the stack. Profilers that + * change the return address on the stack can use this to resolve the stack + * location to wherever the profiler stashed the original return address. + * + * \param return_addr_location A location on stack where a machine + * return address resides. + * \returns Either return_addr_location, or else a pointer to the profiler's + * copy of the original return address. + * + * \note The resolver function must not cause garbage collection. + */ +using ReturnAddressLocationResolver = + uintptr_t (*)(uintptr_t return_addr_location); + +using DcheckErrorCallback = void (*)(const char* file, int line, + const char* message); + +using V8FatalErrorCallback = void (*)(const char* file, int line, + const char* message); + +/** + * Container class for static utility functions. + */ +class V8_EXPORT V8 { + public: + /** + * Hand startup data to V8, in case the embedder has chosen to build + * V8 with external startup data. + * + * Note: + * - By default the startup data is linked into the V8 library, in which + * case this function is not meaningful. + * - If this needs to be called, it needs to be called before V8 + * tries to make use of its built-ins. + * - To avoid unnecessary copies of data, V8 will point directly into the + * given data blob, so pretty please keep it around until V8 exit. + * - Compression of the startup blob might be useful, but needs to + * handled entirely on the embedders' side. + * - The call will abort if the data is invalid. + */ + static void SetSnapshotDataBlob(StartupData* startup_blob); + + /** Set the callback to invoke in case of Dcheck failures. */ + static void SetDcheckErrorHandler(DcheckErrorCallback that); + + /** Set the callback to invoke in the case of CHECK failures or fatal + * errors. This is distinct from Isolate::SetFatalErrorHandler, which + * is invoked in response to API usage failures. + * */ + static void SetFatalErrorHandler(V8FatalErrorCallback that); + + /** + * Sets V8 flags from a string. + */ + static void SetFlagsFromString(const char* str); + static void SetFlagsFromString(const char* str, size_t length); + + /** + * Sets V8 flags from the command line. + */ + static void SetFlagsFromCommandLine(int* argc, char** argv, + bool remove_flags); + + /** Get the version string. */ + static const char* GetVersion(); + + /** + * Initializes V8. This function needs to be called before the first Isolate + * is created. It always returns true. + */ + V8_INLINE static bool Initialize() { +#ifdef V8_TARGET_OS_ANDROID + const bool kV8TargetOsIsAndroid = true; +#else + const bool kV8TargetOsIsAndroid = false; +#endif + +#ifdef V8_ENABLE_CHECKS + const bool kV8EnableChecks = true; +#else + const bool kV8EnableChecks = false; +#endif + + const int kBuildConfiguration = + (internal::PointerCompressionIsEnabled() ? kPointerCompression : 0) | + (internal::SmiValuesAre31Bits() ? k31BitSmis : 0) | + (internal::SandboxIsEnabled() ? kSandbox : 0) | + (kV8TargetOsIsAndroid ? kTargetOsIsAndroid : 0) | + (kV8EnableChecks ? kEnableChecks : 0); + return Initialize(kBuildConfiguration); + } + + /** + * Allows the host application to provide a callback which can be used + * as a source of entropy for random number generators. + */ + static void SetEntropySource(EntropySource source); + + /** + * Allows the host application to provide a callback that allows v8 to + * cooperate with a profiler that rewrites return addresses on stack. + */ + static void SetReturnAddressLocationResolver( + ReturnAddressLocationResolver return_address_resolver); + + /** + * Releases any resources used by v8 and stops any utility threads + * that may be running. Note that disposing v8 is permanent, it + * cannot be reinitialized. + * + * It should generally not be necessary to dispose v8 before exiting + * a process, this should happen automatically. It is only necessary + * to use if the process needs the resources taken up by v8. + */ + static bool Dispose(); + + /** + * Initialize the ICU library bundled with V8. The embedder should only + * invoke this method when using the bundled ICU. Returns true on success. + * + * If V8 was compiled with the ICU data in an external file, the location + * of the data file has to be provided. + */ + static bool InitializeICU(const char* icu_data_file = nullptr); + + /** + * Initialize the ICU library bundled with V8. The embedder should only + * invoke this method when using the bundled ICU. If V8 was compiled with + * the ICU data in an external file and when the default location of that + * file should be used, a path to the executable must be provided. + * Returns true on success. + * + * The default is a file called icudtl.dat side-by-side with the executable. + * + * Optionally, the location of the data file can be provided to override the + * default. + */ + static bool InitializeICUDefaultLocation(const char* exec_path, + const char* icu_data_file = nullptr); + + /** + * Initialize the external startup data. The embedder only needs to + * invoke this method when external startup data was enabled in a build. + * + * If V8 was compiled with the startup data in an external file, then + * V8 needs to be given those external files during startup. There are + * three ways to do this: + * - InitializeExternalStartupData(const char*) + * This will look in the given directory for the file "snapshot_blob.bin". + * - InitializeExternalStartupDataFromFile(const char*) + * As above, but will directly use the given file name. + * - Call SetSnapshotDataBlob. + * This will read the blobs from the given data structure and will + * not perform any file IO. + */ + static void InitializeExternalStartupData(const char* directory_path); + static void InitializeExternalStartupDataFromFile(const char* snapshot_blob); + + /** + * Sets the v8::Platform to use. This should be invoked before V8 is + * initialized. + */ + static void InitializePlatform(Platform* platform); + + /** + * Clears all references to the v8::Platform. This should be invoked after + * V8 was disposed. + */ + static void DisposePlatform(); + +#if defined(V8_ENABLE_SANDBOX) + /** + * Returns true if the sandbox is configured securely. + * + * If V8 cannot create a regular sandbox during initialization, for example + * because not enough virtual address space can be reserved, it will instead + * create a fallback sandbox that still allows it to function normally but + * does not have the same security properties as a regular sandbox. This API + * can be used to determine if such a fallback sandbox is being used, in + * which case it will return false. + */ + static bool IsSandboxConfiguredSecurely(); + + /** + * Provides access to the virtual address subspace backing the sandbox. + * + * This can be used to allocate pages inside the sandbox, for example to + * obtain virtual memory for ArrayBuffer backing stores, which must be + * located inside the sandbox. + * + * It should be assumed that an attacker can corrupt data inside the sandbox, + * and so in particular the contents of pages allocagted in this virtual + * address space, arbitrarily and concurrently. Due to this, it is + * recommended to to only place pure data buffers in them. + */ + static VirtualAddressSpace* GetSandboxAddressSpace(); + + /** + * Returns the size of the sandbox in bytes. + * + * This represents the size of the address space that V8 can directly address + * and in which it allocates its objects. + */ + static size_t GetSandboxSizeInBytes(); + + /** + * Returns the size of the address space reservation backing the sandbox. + * + * This may be larger than the sandbox (i.e. |GetSandboxSizeInBytes()|) due + * to surrounding guard regions, or may be smaller than the sandbox in case a + * fallback sandbox is being used, which will use a smaller virtual address + * space reservation. In the latter case this will also be different from + * |GetSandboxAddressSpace()->size()| as that will cover a larger part of the + * address space than what has actually been reserved. + */ + static size_t GetSandboxReservationSizeInBytes(); +#endif // V8_ENABLE_SANDBOX + + /** + * Activate trap-based bounds checking for WebAssembly. + * + * \param use_v8_signal_handler Whether V8 should install its own signal + * handler or rely on the embedder's. + */ + static bool EnableWebAssemblyTrapHandler(bool use_v8_signal_handler); + +#if defined(V8_OS_WIN) + /** + * On Win64, by default V8 does not emit unwinding data for jitted code, + * which means the OS cannot walk the stack frames and the system Structured + * Exception Handling (SEH) cannot unwind through V8-generated code: + * https://code.google.com/p/v8/issues/detail?id=3598. + * + * This function allows embedders to register a custom exception handler for + * exceptions in V8-generated code. + */ + static void SetUnhandledExceptionCallback( + UnhandledExceptionCallback callback); +#endif + + /** + * Allows the host application to provide a callback that will be called when + * v8 has encountered a fatal failure to allocate memory and is about to + * terminate. + */ + static void SetFatalMemoryErrorCallback(OOMErrorCallback callback); + + /** + * Get statistics about the shared memory usage. + */ + static void GetSharedMemoryStatistics(SharedMemoryStatistics* statistics); + + private: + V8(); + + enum BuildConfigurationFeatures { + kPointerCompression = 1 << 0, + k31BitSmis = 1 << 1, + kSandbox = 1 << 2, + kTargetOsIsAndroid = 1 << 3, + kEnableChecks = 1 << 4, + }; + + /** + * Checks that the embedder build configuration is compatible with + * the V8 binary and if so initializes V8. + */ + static bool Initialize(int build_config); + + friend class Context; + template + friend class PersistentValueMapBase; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_INITIALIZATION_H_ diff --git a/NativeScript/napi/android/v8-13/include/v8-inspector-protocol.h b/NativeScript/napi/android/v8-13/include/v8-inspector-protocol.h new file mode 100644 index 000000000..a5ffb7d69 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-inspector-protocol.h @@ -0,0 +1,13 @@ +// 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_V8_INSPECTOR_PROTOCOL_H_ +#define V8_V8_INSPECTOR_PROTOCOL_H_ + +#include "inspector/Debugger.h" // NOLINT(build/include_directory) +#include "inspector/Runtime.h" // NOLINT(build/include_directory) +#include "inspector/Schema.h" // NOLINT(build/include_directory) +#include "v8-inspector.h" // NOLINT(build/include_directory) + +#endif // V8_V8_INSPECTOR_PROTOCOL_H_ diff --git a/NativeScript/napi/android/v8-13/include/v8-inspector.h b/NativeScript/napi/android/v8-13/include/v8-inspector.h new file mode 100644 index 000000000..0992cb1c3 --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-inspector.h @@ -0,0 +1,434 @@ +// 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_V8_INSPECTOR_H_ +#define V8_V8_INSPECTOR_H_ + +#include + +#include +#include + +#include "v8-isolate.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) + +namespace v8 { +class Context; +class Name; +class Object; +class StackTrace; +class Value; +} // namespace v8 + +namespace v8_inspector { + +namespace internal { +class V8DebuggerId; +} // namespace internal + +namespace protocol { +namespace Debugger { +namespace API { +class SearchMatch; +} +} // namespace Debugger +namespace Runtime { +namespace API { +class RemoteObject; +class StackTrace; +class StackTraceId; +} // namespace API +} // namespace Runtime +namespace Schema { +namespace API { +class Domain; +} +} // namespace Schema +} // namespace protocol + +class V8_EXPORT StringView { + public: + StringView() : m_is8Bit(true), m_length(0), m_characters8(nullptr) {} + + StringView(const uint8_t* characters, size_t length) + : m_is8Bit(true), m_length(length), m_characters8(characters) {} + + StringView(const uint16_t* characters, size_t length) + : m_is8Bit(false), m_length(length), m_characters16(characters) {} + + bool is8Bit() const { return m_is8Bit; } + size_t length() const { return m_length; } + + // TODO(dgozman): add DCHECK(m_is8Bit) to accessors once platform can be used + // here. + const uint8_t* characters8() const { return m_characters8; } + const uint16_t* characters16() const { return m_characters16; } + + private: + bool m_is8Bit; + size_t m_length; + union { + const uint8_t* m_characters8; + const uint16_t* m_characters16; + }; +}; + +class V8_EXPORT StringBuffer { + public: + virtual ~StringBuffer() = default; + virtual StringView string() const = 0; + // This method copies contents. + static std::unique_ptr create(StringView); +}; + +class V8_EXPORT V8ContextInfo { + public: + V8ContextInfo(v8::Local context, int contextGroupId, + StringView humanReadableName) + : context(context), + contextGroupId(contextGroupId), + humanReadableName(humanReadableName), + hasMemoryOnConsole(false) {} + + v8::Local context; + // Each v8::Context is a part of a group. The group id must be non-zero. + int contextGroupId; + StringView humanReadableName; + StringView origin; + StringView auxData; + bool hasMemoryOnConsole; + + static int executionContextId(v8::Local context); + + // Disallow copying and allocating this one. + enum NotNullTagEnum { NotNullLiteral }; + void* operator new(size_t) = delete; + void* operator new(size_t, NotNullTagEnum, void*) = delete; + void* operator new(size_t, void*) = delete; + V8ContextInfo(const V8ContextInfo&) = delete; + V8ContextInfo& operator=(const V8ContextInfo&) = delete; +}; + +// This debugger id tries to be unique by generating two random +// numbers, which should most likely avoid collisions. +// Debugger id has a 1:1 mapping to context group. It is used to +// attribute stack traces to a particular debugging, when doing any +// cross-debugger operations (e.g. async step in). +// See also Runtime.UniqueDebuggerId in the protocol. +class V8_EXPORT V8DebuggerId { + public: + V8DebuggerId() = default; + V8DebuggerId(const V8DebuggerId&) = default; + V8DebuggerId& operator=(const V8DebuggerId&) = default; + + std::unique_ptr toString() const; + bool isValid() const; + std::pair pair() const; + + private: + friend class internal::V8DebuggerId; + explicit V8DebuggerId(std::pair); + + int64_t m_first = 0; + int64_t m_second = 0; +}; + +struct V8_EXPORT V8StackFrame { + StringView sourceURL; + StringView functionName; + int lineNumber; + int columnNumber; + int scriptId; +}; + +class V8_EXPORT V8StackTrace { + public: + virtual StringView firstNonEmptySourceURL() const = 0; + virtual bool isEmpty() const = 0; + virtual StringView topSourceURL() const = 0; + virtual int topLineNumber() const = 0; + virtual int topColumnNumber() const = 0; + virtual int topScriptId() const = 0; + virtual StringView topFunctionName() const = 0; + + virtual ~V8StackTrace() = default; + virtual std::unique_ptr + buildInspectorObject(int maxAsyncDepth) const = 0; + virtual std::unique_ptr toString() const = 0; + + // Safe to pass between threads, drops async chain. + virtual std::unique_ptr clone() = 0; + + virtual std::vector frames() const = 0; +}; + +class V8_EXPORT V8InspectorSession { + public: + virtual ~V8InspectorSession() = default; + + // Cross-context inspectable values (DOM nodes in different worlds, etc.). + class V8_EXPORT Inspectable { + public: + virtual v8::Local get(v8::Local) = 0; + virtual ~Inspectable() = default; + }; + virtual void addInspectedObject(std::unique_ptr) = 0; + + // Dispatching protocol messages. + static bool canDispatchMethod(StringView method); + virtual void dispatchProtocolMessage(StringView message) = 0; + virtual std::vector state() = 0; + virtual std::vector> + supportedDomains() = 0; + + // Debugger actions. + virtual void schedulePauseOnNextStatement(StringView breakReason, + StringView breakDetails) = 0; + virtual void cancelPauseOnNextStatement() = 0; + virtual void breakProgram(StringView breakReason, + StringView breakDetails) = 0; + virtual void setSkipAllPauses(bool) = 0; + virtual void resume(bool setTerminateOnResume = false) = 0; + virtual void stepOver() = 0; + virtual std::vector> + searchInTextByLines(StringView text, StringView query, bool caseSensitive, + bool isRegex) = 0; + + // Remote objects. + virtual std::unique_ptr wrapObject( + v8::Local, v8::Local, StringView groupName, + bool generatePreview) = 0; + + virtual bool unwrapObject(std::unique_ptr* error, + StringView objectId, v8::Local*, + v8::Local*, + std::unique_ptr* objectGroup) = 0; + virtual void releaseObjectGroup(StringView) = 0; + virtual void triggerPreciseCoverageDeltaUpdate(StringView occasion) = 0; + + struct V8_EXPORT EvaluateResult { + enum class ResultType { + kNotRun, + kSuccess, + kException, + }; + + ResultType type; + v8::Local value; + }; + // Evalaute 'expression' in the provided context. Does the same as + // Runtime#evaluate under-the-hood but exposed on the C++ side. + virtual EvaluateResult evaluate(v8::Local context, + StringView expression, + bool includeCommandLineAPI = false) = 0; + + // Prepare for shutdown (disables debugger pausing, etc.). + virtual void stop() = 0; +}; + +struct V8_EXPORT DeepSerializedValue { + explicit DeepSerializedValue(std::unique_ptr type, + v8::MaybeLocal value = {}) + : type(std::move(type)), value(value) {} + std::unique_ptr type; + v8::MaybeLocal value; +}; + +struct V8_EXPORT DeepSerializationResult { + explicit DeepSerializationResult( + std::unique_ptr serializedValue) + : serializedValue(std::move(serializedValue)), isSuccess(true) {} + explicit DeepSerializationResult(std::unique_ptr errorMessage) + : errorMessage(std::move(errorMessage)), isSuccess(false) {} + + // Use std::variant when available. + std::unique_ptr serializedValue; + std::unique_ptr errorMessage; + bool isSuccess; +}; + +class V8_EXPORT V8InspectorClient { + public: + virtual ~V8InspectorClient() = default; + + virtual void runMessageLoopOnPause(int contextGroupId) {} + virtual void runMessageLoopOnInstrumentationPause(int contextGroupId) { + runMessageLoopOnPause(contextGroupId); + } + virtual void quitMessageLoopOnPause() {} + virtual void runIfWaitingForDebugger(int contextGroupId) {} + + virtual void muteMetrics(int contextGroupId) {} + virtual void unmuteMetrics(int contextGroupId) {} + + virtual void beginUserGesture() {} + virtual void endUserGesture() {} + + virtual std::unique_ptr deepSerialize( + v8::Local v8Value, int maxDepth, + v8::Local additionalParameters) { + return nullptr; + } + virtual std::unique_ptr valueSubtype(v8::Local) { + return nullptr; + } + virtual std::unique_ptr descriptionForValueSubtype( + v8::Local, v8::Local) { + return nullptr; + } + virtual bool isInspectableHeapObject(v8::Local) { return true; } + + virtual v8::Local ensureDefaultContextInGroup( + int contextGroupId) { + return v8::Local(); + } + virtual void beginEnsureAllContextsInGroup(int contextGroupId) {} + virtual void endEnsureAllContextsInGroup(int contextGroupId) {} + + virtual void installAdditionalCommandLineAPI(v8::Local, + v8::Local) {} + virtual void consoleAPIMessage(int contextGroupId, + v8::Isolate::MessageErrorLevel level, + const StringView& message, + const StringView& url, unsigned lineNumber, + unsigned columnNumber, V8StackTrace*) {} + virtual v8::MaybeLocal memoryInfo(v8::Isolate*, + v8::Local) { + return v8::MaybeLocal(); + } + + virtual void consoleTime(v8::Isolate* isolate, v8::Local label) {} + virtual void consoleTimeEnd(v8::Isolate* isolate, + v8::Local label) {} + virtual void consoleTimeStamp(v8::Isolate* isolate, + v8::Local label) {} + virtual void consoleTimeStampWithArgs( + v8::Isolate* isolate, v8::Local label, + const v8::LocalVector& args) {} + virtual void consoleClear(int contextGroupId) {} + virtual double currentTimeMS() { return 0; } + typedef void (*TimerCallback)(void*); + virtual void startRepeatingTimer(double, TimerCallback, void* data) {} + virtual void cancelTimer(void* data) {} + + // TODO(dgozman): this was added to support service worker shadow page. We + // should not connect at all. + virtual bool canExecuteScripts(int contextGroupId) { return true; } + + virtual void maxAsyncCallStackDepthChanged(int depth) {} + + virtual std::unique_ptr resourceNameToUrl( + const StringView& resourceName) { + return nullptr; + } + + // The caller would defer to generating a random 64 bit integer if + // this method returns 0. + virtual int64_t generateUniqueId() { return 0; } + + virtual void dispatchError(v8::Local, v8::Local, + v8::Local) {} +}; + +// These stack trace ids are intended to be passed between debuggers and be +// resolved later. This allows to track cross-debugger calls and step between +// them if a single client connects to multiple debuggers. +struct V8_EXPORT V8StackTraceId { + uintptr_t id; + std::pair debugger_id; + bool should_pause = false; + + V8StackTraceId(); + V8StackTraceId(const V8StackTraceId&) = default; + V8StackTraceId(uintptr_t id, const std::pair debugger_id); + V8StackTraceId(uintptr_t id, const std::pair debugger_id, + bool should_pause); + explicit V8StackTraceId(StringView); + V8StackTraceId& operator=(const V8StackTraceId&) = default; + V8StackTraceId& operator=(V8StackTraceId&&) noexcept = default; + ~V8StackTraceId() = default; + + bool IsInvalid() const; + std::unique_ptr ToString(); +}; + +class V8_EXPORT V8Inspector { + public: + static std::unique_ptr create(v8::Isolate*, V8InspectorClient*); + virtual ~V8Inspector() = default; + + // Contexts instrumentation. + virtual void contextCreated(const V8ContextInfo&) = 0; + virtual void contextDestroyed(v8::Local) = 0; + virtual void resetContextGroup(int contextGroupId) = 0; + virtual v8::MaybeLocal contextById(int contextId) = 0; + virtual V8DebuggerId uniqueDebuggerId(int contextId) = 0; + virtual uint64_t isolateId() = 0; + + // Various instrumentation. + virtual void idleStarted() = 0; + virtual void idleFinished() = 0; + + // Async stack traces instrumentation. + virtual void asyncTaskScheduled(StringView taskName, void* task, + bool recurring) = 0; + virtual void asyncTaskCanceled(void* task) = 0; + virtual void asyncTaskStarted(void* task) = 0; + virtual void asyncTaskFinished(void* task) = 0; + virtual void allAsyncTasksCanceled() = 0; + + virtual V8StackTraceId storeCurrentStackTrace(StringView description) = 0; + virtual void externalAsyncTaskStarted(const V8StackTraceId& parent) = 0; + virtual void externalAsyncTaskFinished(const V8StackTraceId& parent) = 0; + + // Exceptions instrumentation. + virtual unsigned exceptionThrown(v8::Local, StringView message, + v8::Local exception, + StringView detailedMessage, StringView url, + unsigned lineNumber, unsigned columnNumber, + std::unique_ptr, + int scriptId) = 0; + virtual void exceptionRevoked(v8::Local, unsigned exceptionId, + StringView message) = 0; + virtual bool associateExceptionData(v8::Local, + v8::Local exception, + v8::Local key, + v8::Local value) = 0; + + // Connection. + class V8_EXPORT Channel { + public: + virtual ~Channel() = default; + virtual void sendResponse(int callId, + std::unique_ptr message) = 0; + virtual void sendNotification(std::unique_ptr message) = 0; + virtual void flushProtocolNotifications() = 0; + }; + enum ClientTrustLevel { kUntrusted, kFullyTrusted }; + enum SessionPauseState { kWaitingForDebugger, kNotWaitingForDebugger }; + // TODO(chromium:1352175): remove default value once downstream change lands. + // Deprecated: Use `connectShared` instead. + virtual std::unique_ptr connect( + int contextGroupId, Channel*, StringView state, + ClientTrustLevel client_trust_level, + SessionPauseState = kNotWaitingForDebugger) = 0; + + // Same as `connect` but returns a std::shared_ptr instead. + // Embedders should not deconstruct V8 sessions while the nested run loop + // (V8InspectorClient::runMessageLoopOnPause) is running. To partially ensure + // this, we defer session deconstruction until no "dispatchProtocolMessages" + // remains on the stack. + virtual std::shared_ptr connectShared( + int contextGroupId, Channel* channel, StringView state, + ClientTrustLevel clientTrustLevel, SessionPauseState pauseState) = 0; + + // API methods. + virtual std::unique_ptr createStackTrace( + v8::Local) = 0; + virtual std::unique_ptr captureStackTrace(bool fullStack) = 0; +}; + +} // namespace v8_inspector + +#endif // V8_V8_INSPECTOR_H_ diff --git a/NativeScript/napi/android/v8-13/include/v8-internal.h b/NativeScript/napi/android/v8-13/include/v8-internal.h new file mode 100644 index 000000000..95f6fa0dc --- /dev/null +++ b/NativeScript/napi/android/v8-13/include/v8-internal.h @@ -0,0 +1,1746 @@ +// Copyright 2018 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_INTERNAL_H_ +#define INCLUDE_V8_INTERNAL_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "v8config.h" // NOLINT(build/include_directory) + +// TODO(pkasting): Use /spaceship unconditionally after dropping +// support for old libstdc++ versions. +#if __has_include() +#include +#endif +#if defined(__cpp_lib_three_way_comparison) && \ + __cpp_lib_three_way_comparison >= 201711L && \ + defined(__cpp_lib_concepts) && __cpp_lib_concepts >= 202002L +#include +#include + +#define V8_HAVE_SPACESHIP_OPERATOR 1 +#else +#define V8_HAVE_SPACESHIP_OPERATOR 0 +#endif + +namespace v8 { + +class Array; +class Context; +class Data; +class Isolate; + +namespace internal { + +class Heap; +class LocalHeap; +class Isolate; +class IsolateGroup; +class LocalIsolate; + +typedef uintptr_t Address; +static constexpr Address kNullAddress = 0; + +constexpr int KB = 1024; +constexpr int MB = KB * 1024; +constexpr int GB = MB * 1024; +#ifdef V8_TARGET_ARCH_X64 +constexpr size_t TB = size_t{GB} * 1024; +#endif + +/** + * Configuration of tagging scheme. + */ +const int kApiSystemPointerSize = sizeof(void*); +const int kApiDoubleSize = sizeof(double); +const int kApiInt32Size = sizeof(int32_t); +const int kApiInt64Size = sizeof(int64_t); +const int kApiSizetSize = sizeof(size_t); + +// Tag information for HeapObject. +const int kHeapObjectTag = 1; +const int kWeakHeapObjectTag = 3; +const int kHeapObjectTagSize = 2; +const intptr_t kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1; +const intptr_t kHeapObjectReferenceTagMask = 1 << (kHeapObjectTagSize - 1); + +// Tag information for fowarding pointers stored in object headers. +// 0b00 at the lowest 2 bits in the header indicates that the map word is a +// forwarding pointer. +const int kForwardingTag = 0; +const int kForwardingTagSize = 2; +const intptr_t kForwardingTagMask = (1 << kForwardingTagSize) - 1; + +// Tag information for Smi. +const int kSmiTag = 0; +const int kSmiTagSize = 1; +const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1; + +template +struct SmiTagging; + +constexpr intptr_t kIntptrAllBitsSet = intptr_t{-1}; +constexpr uintptr_t kUintptrAllBitsSet = + static_cast(kIntptrAllBitsSet); + +// Smi constants for systems where tagged pointer is a 32-bit value. +template <> +struct SmiTagging<4> { + enum { kSmiShiftSize = 0, kSmiValueSize = 31 }; + + static constexpr intptr_t kSmiMinValue = + static_cast(kUintptrAllBitsSet << (kSmiValueSize - 1)); + static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1); + + V8_INLINE static constexpr int SmiToInt(Address value) { + int shift_bits = kSmiTagSize + kSmiShiftSize; + // Truncate and shift down (requires >> to be sign extending). + return static_cast(static_cast(value)) >> shift_bits; + } + + template && + std::is_signed_v>* = nullptr> + V8_INLINE static constexpr bool IsValidSmi(T value) { + // Is value in range [kSmiMinValue, kSmiMaxValue]. + // Use unsigned operations in order to avoid undefined behaviour in case of + // signed integer overflow. + return (static_cast(value) - + static_cast(kSmiMinValue)) <= + (static_cast(kSmiMaxValue) - + static_cast(kSmiMinValue)); + } + + template && + std::is_unsigned_v>* = nullptr> + V8_INLINE static constexpr bool IsValidSmi(T value) { + static_assert(kSmiMaxValue <= std::numeric_limits::max()); + return value <= static_cast(kSmiMaxValue); + } + + // Same as the `intptr_t` version but works with int64_t on 32-bit builds + // without slowing down anything else. + V8_INLINE static constexpr bool IsValidSmi(int64_t value) { + return (static_cast(value) - + static_cast(kSmiMinValue)) <= + (static_cast(kSmiMaxValue) - + static_cast(kSmiMinValue)); + } + + V8_INLINE static constexpr bool IsValidSmi(uint64_t value) { + static_assert(kSmiMaxValue <= std::numeric_limits::max()); + return value <= static_cast(kSmiMaxValue); + } +}; + +// Smi constants for systems where tagged pointer is a 64-bit value. +template <> +struct SmiTagging<8> { + enum { kSmiShiftSize = 31, kSmiValueSize = 32 }; + + static constexpr intptr_t kSmiMinValue = + static_cast(kUintptrAllBitsSet << (kSmiValueSize - 1)); + static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1); + + V8_INLINE static constexpr int SmiToInt(Address value) { + int shift_bits = kSmiTagSize + kSmiShiftSize; + // Shift down and throw away top 32 bits. + return static_cast(static_cast(value) >> shift_bits); + } + + template && + std::is_signed_v>* = nullptr> + V8_INLINE static constexpr bool IsValidSmi(T value) { + // To be representable as a long smi, the value must be a 32-bit integer. + return std::numeric_limits::min() <= value && + value <= std::numeric_limits::max(); + } + + template && + std::is_unsigned_v>* = nullptr> + V8_INLINE static constexpr bool IsValidSmi(T value) { + return value <= std::numeric_limits::max(); + } +}; + +#ifdef V8_COMPRESS_POINTERS +// See v8:7703 or src/common/ptr-compr-inl.h for details about pointer +// compression. +constexpr size_t kPtrComprCageReservationSize = size_t{1} << 32; +constexpr size_t kPtrComprCageBaseAlignment = size_t{1} << 32; + +static_assert( + kApiSystemPointerSize == kApiInt64Size, + "Pointer compression can be enabled only for 64-bit architectures"); +const int kApiTaggedSize = kApiInt32Size; +#else +const int kApiTaggedSize = kApiSystemPointerSize; +#endif + +constexpr bool PointerCompressionIsEnabled() { + return kApiTaggedSize != kApiSystemPointerSize; +} + +#ifdef V8_31BIT_SMIS_ON_64BIT_ARCH +using PlatformSmiTagging = SmiTagging; +#else +using PlatformSmiTagging = SmiTagging; +#endif + +// TODO(ishell): Consinder adding kSmiShiftBits = kSmiShiftSize + kSmiTagSize +// since it's used much more often than the inividual constants. +const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize; +const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize; +const int kSmiMinValue = static_cast(PlatformSmiTagging::kSmiMinValue); +const int kSmiMaxValue = static_cast(PlatformSmiTagging::kSmiMaxValue); +constexpr bool SmiValuesAre31Bits() { return kSmiValueSize == 31; } +constexpr bool SmiValuesAre32Bits() { return kSmiValueSize == 32; } +constexpr bool Is64() { return kApiSystemPointerSize == sizeof(int64_t); } + +V8_INLINE static constexpr Address IntToSmi(int value) { + return (static_cast
(value) << (kSmiTagSize + kSmiShiftSize)) | + kSmiTag; +} + +/* + * Sandbox related types, constants, and functions. + */ +constexpr bool SandboxIsEnabled() { +#ifdef V8_ENABLE_SANDBOX + return true; +#else + return false; +#endif +} + +// SandboxedPointers are guaranteed to point into the sandbox. This is achieved +// for example by storing them as offset rather than as raw pointers. +using SandboxedPointer_t = Address; + +#ifdef V8_ENABLE_SANDBOX + +// Size of the sandbox, excluding the guard regions surrounding it. +#if defined(V8_TARGET_OS_ANDROID) +// On Android, most 64-bit devices seem to be configured with only 39 bits of +// virtual address space for userspace. As such, limit the sandbox to 128GB (a +// quarter of the total available address space). +constexpr size_t kSandboxSizeLog2 = 37; // 128 GB +#else +// Everywhere else use a 1TB sandbox. +constexpr size_t kSandboxSizeLog2 = 40; // 1 TB +#endif // V8_TARGET_OS_ANDROID +constexpr size_t kSandboxSize = 1ULL << kSandboxSizeLog2; + +// Required alignment of the sandbox. For simplicity, we require the +// size of the guard regions to be a multiple of this, so that this specifies +// the alignment of the sandbox including and excluding surrounding guard +// regions. The alignment requirement is due to the pointer compression cage +// being located at the start of the sandbox. +constexpr size_t kSandboxAlignment = kPtrComprCageBaseAlignment; + +// Sandboxed pointers are stored inside the heap as offset from the sandbox +// base shifted to the left. This way, it is guaranteed that the offset is +// smaller than the sandbox size after shifting it to the right again. This +// constant specifies the shift amount. +constexpr uint64_t kSandboxedPointerShift = 64 - kSandboxSizeLog2; + +// Size of the guard regions surrounding the sandbox. This assumes a worst-case +// scenario of a 32-bit unsigned index used to access an array of 64-bit values +// with an additional 4GB (compressed pointer) offset. In particular, accesses +// to TypedArrays are effectively computed as +// `entry_pointer = array->base + array->offset + index * array->element_size`. +// See also https://crbug.com/40070746 for more details. +constexpr size_t kSandboxGuardRegionSize = 32ULL * GB + 4ULL * GB; + +static_assert((kSandboxGuardRegionSize % kSandboxAlignment) == 0, + "The size of the guard regions around the sandbox must be a " + "multiple of its required alignment."); + +// On OSes where reserving virtual memory is too expensive to reserve the +// entire address space backing the sandbox, notably Windows pre 8.1, we create +// a partially reserved sandbox that doesn't actually reserve most of the +// memory, and so doesn't have the desired security properties as unrelated +// memory allocations could end up inside of it, but which still ensures that +// objects that should be located inside the sandbox are allocated within +// kSandboxSize bytes from the start of the sandbox. The minimum size of the +// region that is actually reserved for such a sandbox is specified by this +// constant and should be big enough to contain the pointer compression cage as +// well as the ArrayBuffer partition. +constexpr size_t kSandboxMinimumReservationSize = 8ULL * GB; + +static_assert(kSandboxMinimumReservationSize > kPtrComprCageReservationSize, + "The minimum reservation size for a sandbox must be larger than " + "the pointer compression cage contained within it."); + +// The maximum buffer size allowed inside the sandbox. This is mostly dependent +// on the size of the guard regions around the sandbox: an attacker must not be +// able to construct a buffer that appears larger than the guard regions and +// thereby "reach out of" the sandbox. +constexpr size_t kMaxSafeBufferSizeForSandbox = 32ULL * GB - 1; +static_assert(kMaxSafeBufferSizeForSandbox <= kSandboxGuardRegionSize, + "The maximum allowed buffer size must not be larger than the " + "sandbox's guard regions"); + +constexpr size_t kBoundedSizeShift = 29; +static_assert(1ULL << (64 - kBoundedSizeShift) == + kMaxSafeBufferSizeForSandbox + 1, + "The maximum size of a BoundedSize must be synchronized with the " + "kMaxSafeBufferSizeForSandbox"); + +#endif // V8_ENABLE_SANDBOX + +#ifdef V8_COMPRESS_POINTERS + +#ifdef V8_TARGET_OS_ANDROID +// The size of the virtual memory reservation for an external pointer table. +// This determines the maximum number of entries in a table. Using a maximum +// size allows omitting bounds checks on table accesses if the indices are +// guaranteed (e.g. through shifting) to be below the maximum index. This +// value must be a power of two. +constexpr size_t kExternalPointerTableReservationSize = 256 * MB; + +// The external pointer table indices stored in HeapObjects as external +// pointers are shifted to the left by this amount to guarantee that they are +// smaller than the maximum table size even after the C++ compiler multiplies +// them by 8 to be used as indexes into a table of 64 bit pointers. +constexpr uint32_t kExternalPointerIndexShift = 7; +#else +constexpr size_t kExternalPointerTableReservationSize = 512 * MB; +constexpr uint32_t kExternalPointerIndexShift = 6; +#endif // V8_TARGET_OS_ANDROID + +// The maximum number of entries in an external pointer table. +constexpr int kExternalPointerTableEntrySize = 8; +constexpr int kExternalPointerTableEntrySizeLog2 = 3; +constexpr size_t kMaxExternalPointers = + kExternalPointerTableReservationSize / kExternalPointerTableEntrySize; +static_assert((1 << (32 - kExternalPointerIndexShift)) == kMaxExternalPointers, + "kExternalPointerTableReservationSize and " + "kExternalPointerIndexShift don't match"); + +#else // !V8_COMPRESS_POINTERS + +// Needed for the V8.SandboxedExternalPointersCount histogram. +constexpr size_t kMaxExternalPointers = 0; + +#endif // V8_COMPRESS_POINTERS + +constexpr uint64_t kExternalPointerMarkBit = 1ULL << 48; +constexpr uint64_t kExternalPointerTagShift = 49; +constexpr uint64_t kExternalPointerTagMask = 0x00fe000000000000ULL; +constexpr uint64_t kExternalPointerShiftedTagMask = + kExternalPointerTagMask >> kExternalPointerTagShift; +static_assert(kExternalPointerShiftedTagMask << kExternalPointerTagShift == + kExternalPointerTagMask); +constexpr uint64_t kExternalPointerTagAndMarkbitMask = 0x00ff000000000000ULL; +constexpr uint64_t kExternalPointerPayloadMask = 0xff00ffffffffffffULL; + +// A ExternalPointerHandle represents a (opaque) reference to an external +// pointer that can be stored inside the sandbox. A ExternalPointerHandle has +// meaning only in combination with an (active) Isolate as it references an +// external pointer stored in the currently active Isolate's +// ExternalPointerTable. Internally, an ExternalPointerHandles is simply an +// index into an ExternalPointerTable that is shifted to the left to guarantee +// that it is smaller than the size of the table. +using ExternalPointerHandle = uint32_t; + +// ExternalPointers point to objects located outside the sandbox. When the V8 +// sandbox is enabled, these are stored on heap as ExternalPointerHandles, +// otherwise they are simply raw pointers. +#ifdef V8_ENABLE_SANDBOX +using ExternalPointer_t = ExternalPointerHandle; +#else +using ExternalPointer_t = Address; +#endif + +constexpr ExternalPointer_t kNullExternalPointer = 0; +constexpr ExternalPointerHandle kNullExternalPointerHandle = 0; + +// See `ExternalPointerHandle` for the main documentation. The difference to +// `ExternalPointerHandle` is that the handle does not represent an arbitrary +// external pointer but always refers to an object managed by `CppHeap`. The +// handles are using in combination with a dedicated table for `CppHeap` +// references. +using CppHeapPointerHandle = uint32_t; + +// The actual pointer to objects located on the `CppHeap`. When pointer +// compression is enabled these pointers are stored as `CppHeapPointerHandle`. +// In non-compressed configurations the pointers are simply stored as raw +// pointers. +#ifdef V8_COMPRESS_POINTERS +using CppHeapPointer_t = CppHeapPointerHandle; +#else +using CppHeapPointer_t = Address; +#endif + +constexpr CppHeapPointer_t kNullCppHeapPointer = 0; +constexpr CppHeapPointerHandle kNullCppHeapPointerHandle = 0; + +constexpr uint64_t kCppHeapPointerMarkBit = 1ULL; +constexpr uint64_t kCppHeapPointerTagShift = 1; +constexpr uint64_t kCppHeapPointerPayloadShift = 16; + +#ifdef V8_COMPRESS_POINTERS +// CppHeapPointers use a dedicated pointer table. These constants control the +// size and layout of the table. See the corresponding constants for the +// external pointer table for further details. +constexpr size_t kCppHeapPointerTableReservationSize = + kExternalPointerTableReservationSize; +constexpr uint32_t kCppHeapPointerIndexShift = kExternalPointerIndexShift; + +constexpr int kCppHeapPointerTableEntrySize = 8; +constexpr int kCppHeapPointerTableEntrySizeLog2 = 3; +constexpr size_t kMaxCppHeapPointers = + kCppHeapPointerTableReservationSize / kCppHeapPointerTableEntrySize; +static_assert((1 << (32 - kCppHeapPointerIndexShift)) == kMaxCppHeapPointers, + "kCppHeapPointerTableReservationSize and " + "kCppHeapPointerIndexShift don't match"); + +#else // !V8_COMPRESS_POINTERS + +// Needed for the V8.SandboxedCppHeapPointersCount histogram. +constexpr size_t kMaxCppHeapPointers = 0; + +#endif // V8_COMPRESS_POINTERS + +// Generic tag range struct to represent ranges of type tags. +// +// When referencing external objects via pointer tables, type tags are +// frequently necessary to guarantee type safety for the external objects. When +// support for subtyping is necessary, range-based type checks are used in +// which all subtypes of a given supertype use contiguous tags. This struct can +// then be used to represent such a type range. +// +// As an example, consider the following type hierarchy: +// +// A F +// / \ +// B E +// / \ +// C D +// +// A potential type id assignment for range-based type checks is +// {A: 0, B: 1, C: 2, D: 3, E: 4, F: 5}. With that, the type check for type A +// would check for the range [A, E], while the check for B would check range +// [B, D], and for F it would simply check [F, F]. +// +// In addition, there is an option for performance tweaks: if the size of the +// type range corresponding to a supertype is a power of two and starts at a +// power of two (e.g. [0x100, 0x13f]), then the compiler can often optimize +// the type check to use even fewer instructions (essentially replace a AND + +// SUB with a single AND). +// +template +struct TagRange { + static_assert(std::is_enum_v && + std::is_same_v, uint16_t>, + "Tag parameter must be an enum with base type uint16_t"); + + // Construct the inclusive tag range [first, last]. + constexpr TagRange(Tag first, Tag last) : first(first), last(last) {} + + // Construct a tag range consisting of a single tag. + // + // A single tag is always implicitly convertible to a tag range. This greatly + // increases readability as most of the time, the exact tag of a field is + // known and so no tag range needs to explicitly be created for it. + constexpr TagRange(Tag tag) // NOLINT(runtime/explicit) + : first(tag), last(tag) {} + + // Construct an empty tag range. + constexpr TagRange() : TagRange(static_cast(0)) {} + + // A tag range is considered empty if it only contains the null tag. + constexpr bool IsEmpty() const { return first == 0 && last == 0; } + + constexpr size_t Size() const { + if (IsEmpty()) { + return 0; + } else { + return last - first + 1; + } + } + + constexpr bool Contains(Tag tag) const { + // Need to perform the math with uint32_t. Otherwise, the uint16_ts would + // be promoted to (signed) int, allowing the compiler to (wrongly) assume + // that an underflow cannot happen as that would be undefined behavior. + return static_cast(tag) - first <= + static_cast(last) - first; + } + + constexpr bool Contains(TagRange tag_range) const { + return tag_range.first >= first && tag_range.last <= last; + } + + constexpr bool operator==(const TagRange other) const { + return first == other.first && last == other.last; + } + + constexpr size_t hash_value() const { + static_assert(std::is_same_v, uint16_t>); + return (static_cast(first) << 16) | last; + } + + // Internally we represent tag ranges as half-open ranges [first, last). + const Tag first; + const Tag last; +}; + +// +// External Pointers. +// +// When the sandbox is enabled, external pointers are stored in an external +// pointer table and are referenced from HeapObjects through an index (a +// "handle"). When stored in the table, the pointers are tagged with per-type +// tags to prevent type confusion attacks between different external objects. +// +// When loading an external pointer, a range of allowed tags can be specified. +// This way, type hierarchies can be supported. The main requirement for that +// is that all (transitive) child classes of a given parent class have type ids +// in the same range, and that there are no unrelated types in that range. For +// more details about how to assign type tags to types, see the TagRange class. +// +// The external pointer sandboxing mechanism ensures that every access to an +// external pointer field will result in a valid pointer of the expected type +// even in the presence of an attacker able to corrupt memory inside the +// sandbox. However, if any data related to the external object is stored +// inside the sandbox it may still be corrupted and so must be validated before +// use or moved into the external object. Further, an attacker will always be +// able to substitute different external pointers of the same type for each +// other. Therefore, code using external pointers must be written in a +// "substitution-safe" way, i.e. it must always be possible to substitute +// external pointers of the same type without causing memory corruption outside +// of the sandbox. Generally this is achieved by referencing any group of +// related external objects through a single external pointer. +// +// Currently we use bit 62 for the marking bit which should always be unused as +// it's part of the non-canonical address range. When Arm's top-byte ignore +// (TBI) is enabled, this bit will be part of the ignored byte, and we assume +// that the Embedder is not using this byte (really only this one bit) for any +// other purpose. This bit also does not collide with the memory tagging +// extension (MTE) which would use bits [56, 60). +// +// External pointer tables are also available even when the sandbox is off but +// pointer compression is on. In that case, the mechanism can be used to ease +// alignment requirements as it turns unaligned 64-bit raw pointers into +// aligned 32-bit indices. To "opt-in" to the external pointer table mechanism +// for this purpose, instead of using the ExternalPointer accessors one needs to +// use ExternalPointerHandles directly and use them to access the pointers in an +// ExternalPointerTable. +// +// The tag is currently in practice limited to 15 bits since it needs to fit +// together with a marking bit into the unused parts of a pointer. +enum ExternalPointerTag : uint16_t { + kFirstExternalPointerTag = 0, + kExternalPointerNullTag = 0, + + // When adding new tags, please ensure that the code using these tags is + // "substitution-safe", i.e. still operate safely if external pointers of the + // same type are swapped by an attacker. See comment above for more details. + + // Shared external pointers are owned by the shared Isolate and stored in the + // shared external pointer table associated with that Isolate, where they can + // be accessed from multiple threads at the same time. The objects referenced + // in this way must therefore always be thread-safe. + kFirstSharedExternalPointerTag, + kWaiterQueueNodeTag = kFirstSharedExternalPointerTag, + kExternalStringResourceTag, + kExternalStringResourceDataTag, + kLastSharedExternalPointerTag = kExternalStringResourceDataTag, + + // External pointers using these tags are kept in a per-Isolate external + // pointer table and can only be accessed when this Isolate is active. + kNativeContextMicrotaskQueueTag, + kEmbedderDataSlotPayloadTag, + // This tag essentially stands for a `void*` pointer in the V8 API, and it is + // the Embedder's responsibility to ensure type safety (against substitution) + // and lifetime validity of these objects. + kExternalObjectValueTag, + kFirstMaybeReadOnlyExternalPointerTag, + kFunctionTemplateInfoCallbackTag = kFirstMaybeReadOnlyExternalPointerTag, + kAccessorInfoGetterTag, + kAccessorInfoSetterTag, + + // InterceptorInfo external pointers. + kFirstInterceptorInfoExternalPointerTag, + kApiNamedPropertyQueryCallbackTag = kFirstInterceptorInfoExternalPointerTag, + kApiNamedPropertyGetterCallbackTag, + kApiNamedPropertySetterCallbackTag, + kApiNamedPropertyDescriptorCallbackTag, + kApiNamedPropertyDefinerCallbackTag, + kApiNamedPropertyDeleterCallbackTag, + kApiNamedPropertyEnumeratorCallbackTag, + kApiIndexedPropertyQueryCallbackTag, + kApiIndexedPropertyGetterCallbackTag, + kApiIndexedPropertySetterCallbackTag, + kApiIndexedPropertyDescriptorCallbackTag, + kApiIndexedPropertyDefinerCallbackTag, + kApiIndexedPropertyDeleterCallbackTag, + kApiIndexedPropertyEnumeratorCallbackTag, + kLastInterceptorInfoExternalPointerTag = + kApiIndexedPropertyEnumeratorCallbackTag, + + kLastMaybeReadOnlyExternalPointerTag = kLastInterceptorInfoExternalPointerTag, + + kWasmInternalFunctionCallTargetTag, + kWasmTypeInfoNativeTypeTag, + kWasmExportedFunctionDataSignatureTag, + kWasmStackMemoryTag, + kWasmIndirectFunctionTargetTag, + + // Foreigns + kFirstForeignExternalPointerTag, + kGenericForeignTag = kFirstForeignExternalPointerTag, + + kApiAccessCheckCallbackTag, + kApiAbortScriptExecutionCallbackTag, + kSyntheticModuleTag, + kMicrotaskCallbackTag, + kMicrotaskCallbackDataTag, + kCFunctionTag, + kCFunctionInfoTag, + kMessageListenerTag, + kWaiterQueueForeignTag, + + // Managed + kFirstManagedResourceTag, + kFirstManagedExternalPointerTag = kFirstManagedResourceTag, + kGenericManagedTag = kFirstManagedExternalPointerTag, + kWasmWasmStreamingTag, + kWasmFuncDataTag, + kWasmManagedDataTag, + kWasmNativeModuleTag, + kIcuBreakIteratorTag, + kIcuUnicodeStringTag, + kIcuListFormatterTag, + kIcuLocaleTag, + kIcuSimpleDateFormatTag, + kIcuDateIntervalFormatTag, + kIcuRelativeDateTimeFormatterTag, + kIcuLocalizedNumberFormatterTag, + kIcuPluralRulesTag, + kIcuCollatorTag, + kDisplayNamesInternalTag, + kD8WorkerTag, + kD8ModuleEmbedderDataTag, + kLastForeignExternalPointerTag = kD8ModuleEmbedderDataTag, + kLastManagedExternalPointerTag = kLastForeignExternalPointerTag, + // External resources whose lifetime is tied to their entry in the external + // pointer table but which are not referenced via a Managed + kArrayBufferExtensionTag, + kLastManagedResourceTag = kArrayBufferExtensionTag, + + kExternalPointerZappedEntryTag = 0x7d, + kExternalPointerEvacuationEntryTag = 0x7e, + kExternalPointerFreeEntryTag = 0x7f, + // The tags are limited to 7 bits, so the last tag is 0x7f. + kLastExternalPointerTag = 0x7f, +}; + +using ExternalPointerTagRange = TagRange; + +constexpr ExternalPointerTagRange kAnyExternalPointerTagRange( + kFirstExternalPointerTag, kLastExternalPointerTag); +constexpr ExternalPointerTagRange kAnySharedExternalPointerTagRange( + kFirstSharedExternalPointerTag, kLastSharedExternalPointerTag); +constexpr ExternalPointerTagRange kAnyForeignExternalPointerTagRange( + kFirstForeignExternalPointerTag, kLastForeignExternalPointerTag); +constexpr ExternalPointerTagRange kAnyInterceptorInfoExternalPointerTagRange( + kFirstInterceptorInfoExternalPointerTag, + kLastInterceptorInfoExternalPointerTag); +constexpr ExternalPointerTagRange kAnyManagedExternalPointerTagRange( + kFirstManagedExternalPointerTag, kLastManagedExternalPointerTag); +constexpr ExternalPointerTagRange kAnyMaybeReadOnlyExternalPointerTagRange( + kFirstMaybeReadOnlyExternalPointerTag, + kLastMaybeReadOnlyExternalPointerTag); +constexpr ExternalPointerTagRange kAnyManagedResourceExternalPointerTag( + kFirstManagedResourceTag, kLastManagedResourceTag); + +// True if the external pointer must be accessed from the shared isolate's +// external pointer table. +V8_INLINE static constexpr bool IsSharedExternalPointerType( + ExternalPointerTagRange tag_range) { + return kAnySharedExternalPointerTagRange.Contains(tag_range); +} + +// True if the external pointer may live in a read-only object, in which case +// the table entry will be in the shared read-only segment of the external +// pointer table. +V8_INLINE static constexpr bool IsMaybeReadOnlyExternalPointerType( + ExternalPointerTagRange tag_range) { + return kAnyMaybeReadOnlyExternalPointerTagRange.Contains(tag_range); +} + +// True if the external pointer references an external object whose lifetime is +// tied to the entry in the external pointer table. +// In this case, the entry in the ExternalPointerTable always points to an +// object derived from ExternalPointerTable::ManagedResource. +V8_INLINE static constexpr bool IsManagedExternalPointerType( + ExternalPointerTagRange tag_range) { + return kAnyManagedResourceExternalPointerTag.Contains(tag_range); +} + +// When an external poiner field can contain the null external pointer handle, +// the type checking mechanism needs to also check for null. +// TODO(saelo): this is mostly a temporary workaround to introduce range-based +// type checks. In the future, we should either (a) change the type tagging +// scheme so that null always passes or (b) (more likely) introduce dedicated +// null entries for those tags that need them (similar to other well-known +// empty value constants such as the empty fixed array). +V8_INLINE static constexpr bool ExternalPointerCanBeEmpty( + ExternalPointerTagRange tag_range) { + return tag_range.Contains(kArrayBufferExtensionTag) || + tag_range.Contains(kEmbedderDataSlotPayloadTag) || + kAnyInterceptorInfoExternalPointerTagRange.Contains(tag_range); +} + +// Indirect Pointers. +// +// When the sandbox is enabled, indirect pointers are used to reference +// HeapObjects that live outside of the sandbox (but are still managed by V8's +// garbage collector). When object A references an object B through an indirect +// pointer, object A will contain a IndirectPointerHandle, i.e. a shifted +// 32-bit index, which identifies an entry in a pointer table (either the +// trusted pointer table for TrustedObjects, or the code pointer table if it is +// a Code object). This table entry then contains the actual pointer to object +// B. Further, object B owns this pointer table entry, and it is responsible +// for updating the "self-pointer" in the entry when it is relocated in memory. +// This way, in contrast to "normal" pointers, indirect pointers never need to +// be tracked by the GC (i.e. there is no remembered set for them). +// These pointers do not exist when the sandbox is disabled. + +// An IndirectPointerHandle represents a 32-bit index into a pointer table. +using IndirectPointerHandle = uint32_t; + +// A null handle always references an entry that contains nullptr. +constexpr IndirectPointerHandle kNullIndirectPointerHandle = 0; + +// When the sandbox is enabled, indirect pointers are used to implement: +// - TrustedPointers: an indirect pointer using the trusted pointer table (TPT) +// and referencing a TrustedObject in one of the trusted heap spaces. +// - CodePointers, an indirect pointer using the code pointer table (CPT) and +// referencing a Code object together with its instruction stream. + +// +// Trusted Pointers. +// +// A pointer to a TrustedObject. +// When the sandbox is enabled, these are indirect pointers using the trusted +// pointer table (TPT). They are used to reference trusted objects (located in +// one of V8's trusted heap spaces, outside of the sandbox) from inside the +// sandbox in a memory-safe way. When the sandbox is disabled, these are +// regular tagged pointers. +using TrustedPointerHandle = IndirectPointerHandle; + +// The size of the virtual memory reservation for the trusted pointer table. +// As with the external pointer table, a maximum table size in combination with +// shifted indices allows omitting bounds checks. +constexpr size_t kTrustedPointerTableReservationSize = 64 * MB; + +// The trusted pointer handles are stored shifted to the left by this amount +// to guarantee that they are smaller than the maximum table size. +constexpr uint32_t kTrustedPointerHandleShift = 9; + +// A null handle always references an entry that contains nullptr. +constexpr TrustedPointerHandle kNullTrustedPointerHandle = + kNullIndirectPointerHandle; + +// The maximum number of entries in an trusted pointer table. +constexpr int kTrustedPointerTableEntrySize = 8; +constexpr int kTrustedPointerTableEntrySizeLog2 = 3; +constexpr size_t kMaxTrustedPointers = + kTrustedPointerTableReservationSize / kTrustedPointerTableEntrySize; +static_assert((1 << (32 - kTrustedPointerHandleShift)) == kMaxTrustedPointers, + "kTrustedPointerTableReservationSize and " + "kTrustedPointerHandleShift don't match"); + +// +// Code Pointers. +// +// A pointer to a Code object. +// Essentially a specialized version of a trusted pointer that (when the +// sandbox is enabled) uses the code pointer table (CPT) instead of the TPT. +// Each entry in the CPT contains both a pointer to a Code object as well as a +// pointer to the Code's entrypoint. This allows calling/jumping into Code with +// one fewer memory access (compared to the case where the entrypoint pointer +// first needs to be loaded from the Code object). As such, a CodePointerHandle +// can be used both to obtain the referenced Code object and to directly load +// its entrypoint. +// +// When the sandbox is disabled, these are regular tagged pointers. +using CodePointerHandle = IndirectPointerHandle; + +// The size of the virtual memory reservation for the code pointer table. +// As with the other tables, a maximum table size in combination with shifted +// indices allows omitting bounds checks. +constexpr size_t kCodePointerTableReservationSize = 128 * MB; + +// Code pointer handles are shifted by a different amount than indirect pointer +// handles as the tables have a different maximum size. +constexpr uint32_t kCodePointerHandleShift = 9; + +// A null handle always references an entry that contains nullptr. +constexpr CodePointerHandle kNullCodePointerHandle = kNullIndirectPointerHandle; + +// It can sometimes be necessary to distinguish a code pointer handle from a +// trusted pointer handle. A typical example would be a union trusted pointer +// field that can refer to both Code objects and other trusted objects. To +// support these use-cases, we use a simple marking scheme where some of the +// low bits of a code pointer handle are set, while they will be unset on a +// trusted pointer handle. This way, the correct table to resolve the handle +// can be determined even in the absence of a type tag. +constexpr uint32_t kCodePointerHandleMarker = 0x1; +static_assert(kCodePointerHandleShift > 0); +static_assert(kTrustedPointerHandleShift > 0); + +// The maximum number of entries in a code pointer table. +constexpr int kCodePointerTableEntrySize = 16; +constexpr int kCodePointerTableEntrySizeLog2 = 4; +constexpr size_t kMaxCodePointers = + kCodePointerTableReservationSize / kCodePointerTableEntrySize; +static_assert( + (1 << (32 - kCodePointerHandleShift)) == kMaxCodePointers, + "kCodePointerTableReservationSize and kCodePointerHandleShift don't match"); + +constexpr int kCodePointerTableEntryEntrypointOffset = 0; +constexpr int kCodePointerTableEntryCodeObjectOffset = 8; + +// Constants that can be used to mark places that should be modified once +// certain types of objects are moved out of the sandbox and into trusted space. +constexpr bool kRuntimeGeneratedCodeObjectsLiveInTrustedSpace = true; +constexpr bool kBuiltinCodeObjectsLiveInTrustedSpace = false; +constexpr bool kAllCodeObjectsLiveInTrustedSpace = + kRuntimeGeneratedCodeObjectsLiveInTrustedSpace && + kBuiltinCodeObjectsLiveInTrustedSpace; + +// {obj} must be the raw tagged pointer representation of a HeapObject +// that's guaranteed to never be in ReadOnlySpace. +V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj); + +// Returns if we need to throw when an error occurs. This infers the language +// mode based on the current context and the closure. This returns true if the +// language mode is strict. +V8_EXPORT bool ShouldThrowOnError(internal::Isolate* isolate); +/** + * This class exports constants and functionality from within v8 that + * is necessary to implement inline functions in the v8 api. Don't + * depend on functions and constants defined here. + */ +class Internals { +#ifdef V8_MAP_PACKING + V8_INLINE static constexpr Address UnpackMapWord(Address mapword) { + // TODO(wenyuzhao): Clear header metadata. + return mapword ^ kMapWordXorMask; + } +#endif + + public: + // These values match non-compiler-dependent values defined within + // the implementation of v8. + static const int kHeapObjectMapOffset = 0; + static const int kMapInstanceTypeOffset = 1 * kApiTaggedSize + kApiInt32Size; + static const int kStringResourceOffset = + 1 * kApiTaggedSize + 2 * kApiInt32Size; + + static const int kOddballKindOffset = 4 * kApiTaggedSize + kApiDoubleSize; + static const int kJSObjectHeaderSize = 3 * kApiTaggedSize; +#ifdef V8_COMPRESS_POINTERS + static const int kJSAPIObjectWithEmbedderSlotsHeaderSize = + kJSObjectHeaderSize + kApiInt32Size; +#else // !V8_COMPRESS_POINTERS + static const int kJSAPIObjectWithEmbedderSlotsHeaderSize = + kJSObjectHeaderSize + kApiTaggedSize; +#endif // !V8_COMPRESS_POINTERS + static const int kFixedArrayHeaderSize = 2 * kApiTaggedSize; + static const int kEmbedderDataArrayHeaderSize = 2 * kApiTaggedSize; + static const int kEmbedderDataSlotSize = kApiSystemPointerSize; +#ifdef V8_ENABLE_SANDBOX + static const int kEmbedderDataSlotExternalPointerOffset = kApiTaggedSize; +#else + static const int kEmbedderDataSlotExternalPointerOffset = 0; +#endif + static const int kNativeContextEmbedderDataOffset = 6 * kApiTaggedSize; + static const int kStringRepresentationAndEncodingMask = 0x0f; + static const int kStringEncodingMask = 0x8; + static const int kExternalTwoByteRepresentationTag = 0x02; + static const int kExternalOneByteRepresentationTag = 0x0a; + + static const uint32_t kNumIsolateDataSlots = 4; + static const int kStackGuardSize = 8 * kApiSystemPointerSize; + static const int kNumberOfBooleanFlags = 6; + static const int kErrorMessageParamSize = 1; + static const int kTablesAlignmentPaddingSize = 1; + static const int kRegExpStaticResultOffsetsVectorSize = kApiSystemPointerSize; + static const int kBuiltinTier0EntryTableSize = 7 * kApiSystemPointerSize; + static const int kBuiltinTier0TableSize = 7 * kApiSystemPointerSize; + static const int kLinearAllocationAreaSize = 3 * kApiSystemPointerSize; + static const int kThreadLocalTopSize = 30 * kApiSystemPointerSize; + static const int kHandleScopeDataSize = + 2 * kApiSystemPointerSize + 2 * kApiInt32Size; + + // ExternalPointerTable and TrustedPointerTable layout guarantees. + static const int kExternalPointerTableBasePointerOffset = 0; + static const int kExternalPointerTableSize = 2 * kApiSystemPointerSize; + static const int kTrustedPointerTableSize = 2 * kApiSystemPointerSize; + static const int kTrustedPointerTableBasePointerOffset = 0; + + // IsolateData layout guarantees. + static const int kIsolateCageBaseOffset = 0; + static const int kIsolateStackGuardOffset = + kIsolateCageBaseOffset + kApiSystemPointerSize; + static const int kVariousBooleanFlagsOffset = + kIsolateStackGuardOffset + kStackGuardSize; + static const int kErrorMessageParamOffset = + kVariousBooleanFlagsOffset + kNumberOfBooleanFlags; + static const int kBuiltinTier0EntryTableOffset = + kErrorMessageParamOffset + kErrorMessageParamSize + + kTablesAlignmentPaddingSize + kRegExpStaticResultOffsetsVectorSize; + static const int kBuiltinTier0TableOffset = + kBuiltinTier0EntryTableOffset + kBuiltinTier0EntryTableSize; + static const int kNewAllocationInfoOffset = + kBuiltinTier0TableOffset + kBuiltinTier0TableSize; + static const int kOldAllocationInfoOffset = + kNewAllocationInfoOffset + kLinearAllocationAreaSize; + + static const int kFastCCallAlignmentPaddingSize = + kApiSystemPointerSize == 8 ? 5 * kApiSystemPointerSize + : 1 * kApiSystemPointerSize; + static const int kIsolateFastCCallCallerPcOffset = + kOldAllocationInfoOffset + kLinearAllocationAreaSize + + kFastCCallAlignmentPaddingSize; + static const int kIsolateFastCCallCallerFpOffset = + kIsolateFastCCallCallerPcOffset + kApiSystemPointerSize; + static const int kIsolateFastApiCallTargetOffset = + kIsolateFastCCallCallerFpOffset + kApiSystemPointerSize; + static const int kIsolateLongTaskStatsCounterOffset = + kIsolateFastApiCallTargetOffset + kApiSystemPointerSize; + static const int kIsolateThreadLocalTopOffset = + kIsolateLongTaskStatsCounterOffset + kApiSizetSize; + static const int kIsolateHandleScopeDataOffset = + kIsolateThreadLocalTopOffset + kThreadLocalTopSize; + static const int kIsolateEmbedderDataOffset = + kIsolateHandleScopeDataOffset + kHandleScopeDataSize; +#ifdef V8_COMPRESS_POINTERS + static const int kIsolateExternalPointerTableOffset = + kIsolateEmbedderDataOffset + kNumIsolateDataSlots * kApiSystemPointerSize; + static const int kIsolateSharedExternalPointerTableAddressOffset = + kIsolateExternalPointerTableOffset + kExternalPointerTableSize; + static const int kIsolateCppHeapPointerTableOffset = + kIsolateSharedExternalPointerTableAddressOffset + kApiSystemPointerSize; +#ifdef V8_ENABLE_SANDBOX + static const int kIsolateTrustedCageBaseOffset = + kIsolateCppHeapPointerTableOffset + kExternalPointerTableSize; + static const int kIsolateTrustedPointerTableOffset = + kIsolateTrustedCageBaseOffset + kApiSystemPointerSize; + static const int kIsolateSharedTrustedPointerTableAddressOffset = + kIsolateTrustedPointerTableOffset + kTrustedPointerTableSize; + static const int kIsolateTrustedPointerPublishingScopeOffset = + kIsolateSharedTrustedPointerTableAddressOffset + kApiSystemPointerSize; + static const int kIsolateCodePointerTableBaseAddressOffset = + kIsolateTrustedPointerPublishingScopeOffset + kApiSystemPointerSize; + static const int kIsolateApiCallbackThunkArgumentOffset = + kIsolateCodePointerTableBaseAddressOffset + kApiSystemPointerSize; +#else + static const int kIsolateApiCallbackThunkArgumentOffset = + kIsolateCppHeapPointerTableOffset + kExternalPointerTableSize; +#endif // V8_ENABLE_SANDBOX +#else + static const int kIsolateApiCallbackThunkArgumentOffset = + kIsolateEmbedderDataOffset + kNumIsolateDataSlots * kApiSystemPointerSize; +#endif // V8_COMPRESS_POINTERS + static const int kIsolateRegexpExecVectorArgumentOffset = + kIsolateApiCallbackThunkArgumentOffset + kApiSystemPointerSize; + static const int kContinuationPreservedEmbedderDataOffset = + kIsolateRegexpExecVectorArgumentOffset + kApiSystemPointerSize; + static const int kIsolateRootsOffset = + kContinuationPreservedEmbedderDataOffset + kApiSystemPointerSize; + + // Assert scopes + static const int kDisallowGarbageCollectionAlign = alignof(uint32_t); + static const int kDisallowGarbageCollectionSize = sizeof(uint32_t); + +#if V8_STATIC_ROOTS_BOOL + +// These constants are copied from static-roots.h and guarded by static asserts. +#define EXPORTED_STATIC_ROOTS_PTR_LIST(V) \ + V(UndefinedValue, 0x11) \ + V(NullValue, 0x2d) \ + V(TrueValue, 0x71) \ + V(FalseValue, 0x55) \ + V(EmptyString, 0x49) \ + V(TheHoleValue, 0x761) + + using Tagged_t = uint32_t; + struct StaticReadOnlyRoot { +#define DEF_ROOT(name, value) static constexpr Tagged_t k##name = value; + EXPORTED_STATIC_ROOTS_PTR_LIST(DEF_ROOT) +#undef DEF_ROOT + + // Use 0 for kStringMapLowerBound since string maps are the first maps. + static constexpr Tagged_t kStringMapLowerBound = 0; + static constexpr Tagged_t kStringMapUpperBound = 0x425; + +#define PLUSONE(...) +1 + static constexpr size_t kNumberOfExportedStaticRoots = + 2 + EXPORTED_STATIC_ROOTS_PTR_LIST(PLUSONE); +#undef PLUSONE + }; + +#endif // V8_STATIC_ROOTS_BOOL + + static const int kUndefinedValueRootIndex = 4; + static const int kTheHoleValueRootIndex = 5; + static const int kNullValueRootIndex = 6; + static const int kTrueValueRootIndex = 7; + static const int kFalseValueRootIndex = 8; + static const int kEmptyStringRootIndex = 9; + + static const int kNodeClassIdOffset = 1 * kApiSystemPointerSize; + static const int kNodeFlagsOffset = 1 * kApiSystemPointerSize + 3; + static const int kNodeStateMask = 0x3; + static const int kNodeStateIsWeakValue = 2; + + static const int kFirstNonstringType = 0x80; + static const int kOddballType = 0x83; + static const int kForeignType = 0xcc; + static const int kJSSpecialApiObjectType = 0x410; + static const int kJSObjectType = 0x421; + static const int kFirstJSApiObjectType = 0x422; + static const int kLastJSApiObjectType = 0x80A; + // Defines a range [kFirstEmbedderJSApiObjectType, kJSApiObjectTypesCount] + // of JSApiObject instance type values that an embedder can use. + static const int kFirstEmbedderJSApiObjectType = 0; + static const int kLastEmbedderJSApiObjectType = + kLastJSApiObjectType - kFirstJSApiObjectType; + + static const int kUndefinedOddballKind = 4; + static const int kNullOddballKind = 3; + + // Constants used by PropertyCallbackInfo to check if we should throw when an + // error occurs. + static const int kDontThrow = 0; + static const int kThrowOnError = 1; + static const int kInferShouldThrowMode = 2; + + // Soft limit for AdjustAmountofExternalAllocatedMemory. Trigger an + // incremental GC once the external memory reaches this limit. + static constexpr size_t kExternalAllocationSoftLimit = 64 * 1024 * 1024; + +#ifdef V8_MAP_PACKING + static const uintptr_t kMapWordMetadataMask = 0xffffULL << 48; + // The lowest two bits of mapwords are always `0b10` + static const uintptr_t kMapWordSignature = 0b10; + // XORing a (non-compressed) map with this mask ensures that the two + // low-order bits are 0b10. The 0 at the end makes this look like a Smi, + // although real Smis have all lower 32 bits unset. We only rely on these + // values passing as Smis in very few places. + static const int kMapWordXorMask = 0b11; +#endif + + V8_EXPORT static void CheckInitializedImpl(v8::Isolate* isolate); + V8_INLINE static void CheckInitialized(v8::Isolate* isolate) { +#ifdef V8_ENABLE_CHECKS + CheckInitializedImpl(isolate); +#endif + } + + V8_INLINE static constexpr bool HasHeapObjectTag(Address value) { + return (value & kHeapObjectTagMask) == static_cast
(kHeapObjectTag); + } + + V8_INLINE static constexpr int SmiValue(Address value) { + return PlatformSmiTagging::SmiToInt(value); + } + + V8_INLINE static constexpr Address AddressToSmi(Address value) { + return (value << (kSmiTagSize + PlatformSmiTagging::kSmiShiftSize)) | + kSmiTag; + } + + V8_INLINE static constexpr Address IntToSmi(int value) { + return AddressToSmi(static_cast
(value)); + } + + template >* = nullptr> + V8_INLINE static constexpr Address IntegralToSmi(T value) { + return AddressToSmi(static_cast
(value)); + } + + template >* = nullptr> + V8_INLINE static constexpr bool IsValidSmi(T value) { + return PlatformSmiTagging::IsValidSmi(value); + } + + template >* = nullptr> + static constexpr std::optional
TryIntegralToSmi(T value) { + if (V8_LIKELY(PlatformSmiTagging::IsValidSmi(value))) { + return {AddressToSmi(static_cast
(value))}; + } + return {}; + } + +#if V8_STATIC_ROOTS_BOOL + V8_INLINE static bool is_identical(Address obj, Tagged_t constant) { + return static_cast(obj) == constant; + } + + V8_INLINE static bool CheckInstanceMapRange(Address obj, Tagged_t first_map, + Tagged_t last_map) { + auto map = ReadRawField(obj, kHeapObjectMapOffset); +#ifdef V8_MAP_PACKING + map = UnpackMapWord(map); +#endif + return map >= first_map && map <= last_map; + } +#endif + + V8_INLINE static int GetInstanceType(Address obj) { + Address map = ReadTaggedPointerField(obj, kHeapObjectMapOffset); +#ifdef V8_MAP_PACKING + map = UnpackMapWord(map); +#endif + return ReadRawField(map, kMapInstanceTypeOffset); + } + + V8_INLINE static Address LoadMap(Address obj) { + if (!HasHeapObjectTag(obj)) return kNullAddress; + Address map = ReadTaggedPointerField(obj, kHeapObjectMapOffset); +#ifdef V8_MAP_PACKING + map = UnpackMapWord(map); +#endif + return map; + } + + V8_INLINE static int GetOddballKind(Address obj) { + return SmiValue(ReadTaggedSignedField(obj, kOddballKindOffset)); + } + + V8_INLINE static bool IsExternalTwoByteString(int instance_type) { + int representation = (instance_type & kStringRepresentationAndEncodingMask); + return representation == kExternalTwoByteRepresentationTag; + } + + V8_INLINE static constexpr bool CanHaveInternalField(int instance_type) { + static_assert(kJSObjectType + 1 == kFirstJSApiObjectType); + static_assert(kJSObjectType < kLastJSApiObjectType); + static_assert(kFirstJSApiObjectType < kLastJSApiObjectType); + // Check for IsJSObject() || IsJSSpecialApiObject() || IsJSApiObject() + return instance_type == kJSSpecialApiObjectType || + // inlined version of base::IsInRange + (static_cast(static_cast(instance_type) - + static_cast(kJSObjectType)) <= + static_cast(kLastJSApiObjectType - kJSObjectType)); + } + + V8_INLINE static uint8_t GetNodeFlag(Address* obj, int shift) { + uint8_t* addr = reinterpret_cast(obj) + kNodeFlagsOffset; + return *addr & static_cast(1U << shift); + } + + V8_INLINE static void UpdateNodeFlag(Address* obj, bool value, int shift) { + uint8_t* addr = reinterpret_cast(obj) + kNodeFlagsOffset; + uint8_t mask = static_cast(1U << shift); + *addr = static_cast((*addr & ~mask) | (value << shift)); + } + + V8_INLINE static uint8_t GetNodeState(Address* obj) { + uint8_t* addr = reinterpret_cast(obj) + kNodeFlagsOffset; + return *addr & kNodeStateMask; + } + + V8_INLINE static void UpdateNodeState(Address* obj, uint8_t value) { + uint8_t* addr = reinterpret_cast(obj) + kNodeFlagsOffset; + *addr = static_cast((*addr & ~kNodeStateMask) | value); + } + + V8_INLINE static void SetEmbedderData(v8::Isolate* isolate, uint32_t slot, + void* data) { + Address addr = reinterpret_cast
(isolate) + + kIsolateEmbedderDataOffset + slot * kApiSystemPointerSize; + *reinterpret_cast(addr) = data; + } + + V8_INLINE static void* GetEmbedderData(const v8::Isolate* isolate, + uint32_t slot) { + Address addr = reinterpret_cast
(isolate) + + kIsolateEmbedderDataOffset + slot * kApiSystemPointerSize; + return *reinterpret_cast(addr); + } + + V8_INLINE static void IncrementLongTasksStatsCounter(v8::Isolate* isolate) { + Address addr = + reinterpret_cast
(isolate) + kIsolateLongTaskStatsCounterOffset; + ++(*reinterpret_cast(addr)); + } + + V8_INLINE static Address* GetRootSlot(v8::Isolate* isolate, int index) { + Address addr = reinterpret_cast
(isolate) + kIsolateRootsOffset + + index * kApiSystemPointerSize; + return reinterpret_cast(addr); + } + + V8_INLINE static Address GetRoot(v8::Isolate* isolate, int index) { +#if V8_STATIC_ROOTS_BOOL + Address base = *reinterpret_cast( + reinterpret_cast(isolate) + kIsolateCageBaseOffset); + switch (index) { +#define DECOMPRESS_ROOT(name, ...) \ + case k##name##RootIndex: \ + return base + StaticReadOnlyRoot::k##name; + EXPORTED_STATIC_ROOTS_PTR_LIST(DECOMPRESS_ROOT) +#undef DECOMPRESS_ROOT +#undef EXPORTED_STATIC_ROOTS_PTR_LIST + default: + break; + } +#endif // V8_STATIC_ROOTS_BOOL + return *GetRootSlot(isolate, index); + } + +#ifdef V8_ENABLE_SANDBOX + V8_INLINE static Address* GetExternalPointerTableBase(v8::Isolate* isolate) { + Address addr = reinterpret_cast
(isolate) + + kIsolateExternalPointerTableOffset + + kExternalPointerTableBasePointerOffset; + return *reinterpret_cast(addr); + } + + V8_INLINE static Address* GetSharedExternalPointerTableBase( + v8::Isolate* isolate) { + Address addr = reinterpret_cast
(isolate) + + kIsolateSharedExternalPointerTableAddressOffset; + addr = *reinterpret_cast(addr); + addr += kExternalPointerTableBasePointerOffset; + return *reinterpret_cast(addr); + } +#endif + + template + V8_INLINE static T ReadRawField(Address heap_object_ptr, int offset) { + Address addr = heap_object_ptr + offset - kHeapObjectTag; +#ifdef V8_COMPRESS_POINTERS + if (sizeof(T) > kApiTaggedSize) { + // TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size + // fields (external pointers, doubles and BigInt data) are only + // kTaggedSize aligned so we have to use unaligned pointer friendly way of + // accessing them in order to avoid undefined behavior in C++ code. + T r; + memcpy(&r, reinterpret_cast(addr), sizeof(T)); + return r; + } +#endif + return *reinterpret_cast(addr); + } + + V8_INLINE static Address ReadTaggedPointerField(Address heap_object_ptr, + int offset) { +#ifdef V8_COMPRESS_POINTERS + uint32_t value = ReadRawField(heap_object_ptr, offset); + Address base = GetPtrComprCageBaseFromOnHeapAddress(heap_object_ptr); + return base + static_cast
(static_cast(value)); +#else + return ReadRawField
(heap_object_ptr, offset); +#endif + } + + V8_INLINE static Address ReadTaggedSignedField(Address heap_object_ptr, + int offset) { +#ifdef V8_COMPRESS_POINTERS + uint32_t value = ReadRawField(heap_object_ptr, offset); + return static_cast
(static_cast(value)); +#else + return ReadRawField
(heap_object_ptr, offset); +#endif + } + + V8_INLINE static v8::Isolate* GetIsolateForSandbox(Address obj) { +#ifdef V8_ENABLE_SANDBOX + return reinterpret_cast( + internal::IsolateFromNeverReadOnlySpaceObject(obj)); +#else + // Not used in non-sandbox mode. + return nullptr; +#endif + } + + template + V8_INLINE static Address ReadExternalPointerField(v8::Isolate* isolate, + Address heap_object_ptr, + int offset) { +#ifdef V8_ENABLE_SANDBOX + static_assert(!tag_range.IsEmpty()); + // See src/sandbox/external-pointer-table.h. Logic duplicated here so + // it can be inlined and doesn't require an additional call. + Address* table = IsSharedExternalPointerType(tag_range) + ? GetSharedExternalPointerTableBase(isolate) + : GetExternalPointerTableBase(isolate); + internal::ExternalPointerHandle handle = + ReadRawField(heap_object_ptr, offset); + uint32_t index = handle >> kExternalPointerIndexShift; + std::atomic
* ptr = + reinterpret_cast*>(&table[index]); + Address entry = std::atomic_load_explicit(ptr, std::memory_order_relaxed); + ExternalPointerTag actual_tag = static_cast( + (entry & kExternalPointerTagMask) >> kExternalPointerTagShift); + if (V8_LIKELY(tag_range.Contains(actual_tag))) { + return entry & kExternalPointerPayloadMask; + } else { + return 0; + } + return entry; +#else + return ReadRawField
(heap_object_ptr, offset); +#endif // V8_ENABLE_SANDBOX + } + +#ifdef V8_COMPRESS_POINTERS + V8_INLINE static Address GetPtrComprCageBaseFromOnHeapAddress(Address addr) { + return addr & -static_cast(kPtrComprCageBaseAlignment); + } + + V8_INLINE static uint32_t CompressTagged(Address value) { + return static_cast(value); + } + + V8_INLINE static Address DecompressTaggedField(Address heap_object_ptr, + uint32_t value) { + Address base = GetPtrComprCageBaseFromOnHeapAddress(heap_object_ptr); + return base + static_cast
(static_cast(value)); + } + +#endif // V8_COMPRESS_POINTERS +}; + +// Only perform cast check for types derived from v8::Data since +// other types do not implement the Cast method. +template +struct CastCheck { + template + static void Perform(T* data); +}; + +template <> +template +void CastCheck::Perform(T* data) { + T::Cast(data); +} + +template <> +template +void CastCheck::Perform(T* data) {} + +template +V8_INLINE void PerformCastCheck(T* data) { + CastCheck && + !std::is_same_v>>::Perform(data); +} + +// A base class for backing stores, which is needed due to vagaries of +// how static casts work with std::shared_ptr. +class BackingStoreBase {}; + +// The maximum value in enum GarbageCollectionReason, defined in heap.h. +// This is needed for histograms sampling garbage collection reasons. +constexpr int kGarbageCollectionReasonMaxValue = 30; + +// Base class for the address block allocator compatible with standard +// containers, which registers its allocated range as strong roots. +class V8_EXPORT StrongRootAllocatorBase { + public: + Heap* heap() const { return heap_; } + + friend bool operator==(const StrongRootAllocatorBase& a, + const StrongRootAllocatorBase& b) { + // TODO(pkasting): Replace this body with `= default` after dropping support + // for old gcc versions. + return a.heap_ == b.heap_; + } + + protected: + explicit StrongRootAllocatorBase(Heap* heap) : heap_(heap) {} + explicit StrongRootAllocatorBase(LocalHeap* heap); + explicit StrongRootAllocatorBase(Isolate* isolate); + explicit StrongRootAllocatorBase(v8::Isolate* isolate); + explicit StrongRootAllocatorBase(LocalIsolate* isolate); + + // Allocate/deallocate a range of n elements of type internal::Address. + Address* allocate_impl(size_t n); + void deallocate_impl(Address* p, size_t n) noexcept; + + private: + Heap* heap_; +}; + +// The general version of this template behaves just as std::allocator, with +// the exception that the constructor takes the isolate as parameter. Only +// specialized versions, e.g., internal::StrongRootAllocator +// and internal::StrongRootAllocator> register the allocated range +// as strong roots. +template +class StrongRootAllocator : private std::allocator { + public: + using value_type = T; + + template + explicit StrongRootAllocator(HeapOrIsolateT*) {} + template + StrongRootAllocator(const StrongRootAllocator& other) noexcept {} + + using std::allocator::allocate; + using std::allocator::deallocate; +}; + +// TODO(pkasting): Replace with `requires` clauses after dropping support for +// old gcc versions. +template +inline constexpr bool kHaveIteratorConcept = false; +template +inline constexpr bool kHaveIteratorConcept< + Iterator, std::void_t> = true; + +template +inline constexpr bool kHaveIteratorCategory = false; +template +inline constexpr bool kHaveIteratorCategory< + Iterator, std::void_t> = true; + +// Helper struct that contains an `iterator_concept` type alias only when either +// `Iterator` or `std::iterator_traits` do. +// Default: no alias. +template +struct MaybeDefineIteratorConcept {}; +// Use `Iterator::iterator_concept` if available. +template +struct MaybeDefineIteratorConcept< + Iterator, std::enable_if_t>> { + using iterator_concept = typename Iterator::iterator_concept; +}; +// Otherwise fall back to `std::iterator_traits` if possible. +template +struct MaybeDefineIteratorConcept< + Iterator, std::enable_if_t && + !kHaveIteratorConcept>> { + // There seems to be no feature-test macro covering this, so use the + // presence of `` as a crude proxy, since it was added to the + // standard as part of the Ranges papers. + // TODO(pkasting): Add this unconditionally after dropping support for old + // libstdc++ versions. +#if __has_include() + using iterator_concept = + typename std::iterator_traits::iterator_concept; +#endif +}; + +// A class of iterators that wrap some different iterator type. +// If specified, ElementType is the type of element accessed by the wrapper +// iterator; in this case, the actual reference and pointer types of Iterator +// must be convertible to ElementType& and ElementType*, respectively. +template +class WrappedIterator : public MaybeDefineIteratorConcept { + public: + static_assert( + std::is_void_v || + (std::is_convertible_v::pointer, + std::add_pointer_t> && + std::is_convertible_v::reference, + std::add_lvalue_reference_t>)); + + using difference_type = + typename std::iterator_traits::difference_type; + using value_type = + std::conditional_t, + typename std::iterator_traits::value_type, + ElementType>; + using pointer = + std::conditional_t, + typename std::iterator_traits::pointer, + std::add_pointer_t>; + using reference = + std::conditional_t, + typename std::iterator_traits::reference, + std::add_lvalue_reference_t>; + using iterator_category = + typename std::iterator_traits::iterator_category; + + constexpr WrappedIterator() noexcept = default; + constexpr explicit WrappedIterator(Iterator it) noexcept : it_(it) {} + + // TODO(pkasting): Switch to `requires` and concepts after dropping support + // for old gcc and libstdc++ versions. + template >> + constexpr WrappedIterator( + const WrappedIterator& other) noexcept + : it_(other.base()) {} + + [[nodiscard]] constexpr reference operator*() const noexcept { return *it_; } + [[nodiscard]] constexpr pointer operator->() const noexcept { + if constexpr (std::is_pointer_v) { + return it_; + } else { + return it_.operator->(); + } + } + + template + [[nodiscard]] constexpr bool operator==( + const WrappedIterator& other) + const noexcept { + return it_ == other.base(); + } +#if V8_HAVE_SPACESHIP_OPERATOR + template + [[nodiscard]] constexpr auto operator<=>( + const WrappedIterator& other) + const noexcept { + if constexpr (std::three_way_comparable_with) { + return it_ <=> other.base(); + } else if constexpr (std::totally_ordered_with) { + if (it_ < other.base()) { + return std::strong_ordering::less; + } + return (it_ > other.base()) ? std::strong_ordering::greater + : std::strong_ordering::equal; + } else { + if (it_ < other.base()) { + return std::partial_ordering::less; + } + if (other.base() < it_) { + return std::partial_ordering::greater; + } + return (it_ == other.base()) ? std::partial_ordering::equivalent + : std::partial_ordering::unordered; + } + } +#else + // Assume that if spaceship isn't present, operator rewriting might not be + // either. + template + [[nodiscard]] constexpr bool operator!=( + const WrappedIterator& other) + const noexcept { + return it_ != other.base(); + } + + template + [[nodiscard]] constexpr bool operator<( + const WrappedIterator& other) + const noexcept { + return it_ < other.base(); + } + template + [[nodiscard]] constexpr bool operator<=( + const WrappedIterator& other) + const noexcept { + return it_ <= other.base(); + } + template + [[nodiscard]] constexpr bool operator>( + const WrappedIterator& other) + const noexcept { + return it_ > other.base(); + } + template + [[nodiscard]] constexpr bool operator>=( + const WrappedIterator& other) + const noexcept { + return it_ >= other.base(); + } +#endif + + constexpr WrappedIterator& operator++() noexcept { + ++it_; + return *this; + } + constexpr WrappedIterator operator++(int) noexcept { + WrappedIterator result(*this); + ++(*this); + return result; + } + + constexpr WrappedIterator& operator--() noexcept { + --it_; + return *this; + } + constexpr WrappedIterator operator--(int) noexcept { + WrappedIterator result(*this); + --(*this); + return result; + } + [[nodiscard]] constexpr WrappedIterator operator+( + difference_type n) const noexcept { + WrappedIterator result(*this); + result += n; + return result; + } + [[nodiscard]] friend constexpr WrappedIterator operator+( + difference_type n, const WrappedIterator& x) noexcept { + return x + n; + } + constexpr WrappedIterator& operator+=(difference_type n) noexcept { + it_ += n; + return *this; + } + [[nodiscard]] constexpr WrappedIterator operator-( + difference_type n) const noexcept { + return *this + -n; + } + constexpr WrappedIterator& operator-=(difference_type n) noexcept { + return *this += -n; + } + template + [[nodiscard]] constexpr auto operator-( + const WrappedIterator& other) + const noexcept { + return it_ - other.base(); + } + [[nodiscard]] constexpr reference operator[]( + difference_type n) const noexcept { + return it_[n]; + } + + [[nodiscard]] constexpr const Iterator& base() const noexcept { return it_; } + + private: + Iterator it_; +}; + +// 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: + // ValueHelper::InternalRepresentationType is an abstract type that + // corresponds to the internal representation of v8::Local and essentially + // to what T* really is (these two are always in sync). This type is used in + // methods like GetDataFromSnapshotOnce that need access to a handle's + // internal representation. In particular, if `x` is a `v8::Local`, then + // `v8::Local::FromRepr(x.repr())` gives exactly the same handle as `x`. +#ifdef V8_ENABLE_DIRECT_HANDLE + static constexpr Address kTaggedNullAddress = 1; + + using InternalRepresentationType = internal::Address; + static constexpr InternalRepresentationType kEmpty = kTaggedNullAddress; +#else + using InternalRepresentationType = internal::Address*; + static constexpr InternalRepresentationType kEmpty = nullptr; +#endif // V8_ENABLE_DIRECT_HANDLE + + template + V8_INLINE static bool IsEmpty(T* value) { + return ValueAsRepr(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