From c27a2e04b805e6fabd6697e32790bffb55e9f992 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Thu, 28 May 2026 02:27:40 -0400 Subject: [PATCH] refactor: organize direct FFI backends --- .gitignore | 4 +- NativeScript/CMakeLists.txt | 41 +- .../{shared => }/direct/EmbeddedMetadata.mm | 0 .../ffi/{shared => }/direct/NativeApiDirect.h | 0 .../NativeApiDirectBridge.h} | 114 ++--- .../NativeApiDirectCallbacks.h} | 484 +++++++++--------- .../NativeApiDirectClassBuilder.h} | 208 ++++---- .../NativeApiDirectConversion.h} | 274 +++++----- .../NativeApiDirectHostObject.h} | 50 +- .../NativeApiDirectHostObjects.h} | 132 ++--- .../NativeApiDirectInstall.h} | 36 +- .../NativeApiDirectInvocation.h} | 204 +++++--- .../direct/NativeApiDirectSignatureDispatch.h | 85 +++ .../ffi/hermes/{jsi => }/NativeApiJsi.h | 0 .../ffi/hermes/{jsi => }/NativeApiJsi.mm | 33 +- .../{jsi => }/NativeApiJsiReactNative.h | 0 .../hermes/NativeApiJsiSignatureDispatch.h | 91 ++++ NativeScript/ffi/hermes/README.md | 22 + NativeScript/ffi/hermes/jsi/README.md | 62 --- NativeScript/ffi/jsc/NativeApiJSC.h | 2 +- NativeScript/ffi/jsc/NativeApiJSC.mm | 50 +- .../ffi/jsc/NativeApiJSCHostObjects.mm | 8 +- NativeScript/ffi/jsc/NativeApiJSCRuntime.h | 10 +- NativeScript/ffi/jsc/NativeApiJSCRuntime.mm | 8 +- NativeScript/ffi/jsc/NativeApiJSCValue.mm | 8 +- NativeScript/ffi/napi/CallbackThreading.h | 12 +- NativeScript/ffi/napi/Cif.mm | 180 +------ NativeScript/ffi/napi/SignatureDispatch.h | 94 +--- NativeScript/ffi/quickjs/NativeApiQuickJS.h | 2 +- NativeScript/ffi/quickjs/NativeApiQuickJS.mm | 58 +-- .../quickjs/NativeApiQuickJSHostObjects.mm | 8 +- .../ffi/quickjs/NativeApiQuickJSRuntime.h | 10 +- .../ffi/quickjs/NativeApiQuickJSRuntime.mm | 8 +- .../ffi/quickjs/NativeApiQuickJSValue.mm | 8 +- .../ffi/shared/SignatureDispatchCore.h | 298 +++++++++++ NativeScript/ffi/v8/NativeApiV8.h | 2 +- NativeScript/ffi/v8/NativeApiV8.mm | 64 +-- NativeScript/ffi/v8/NativeApiV8HostObjects.mm | 8 +- NativeScript/ffi/v8/NativeApiV8Runtime.h | 10 +- NativeScript/ffi/v8/NativeApiV8Runtime.mm | 8 +- NativeScript/ffi/v8/NativeApiV8Value.mm | 8 +- NativeScript/napi/hermes/jsr.cpp | 28 + NativeScript/napi/hermes/jsr.h | 27 +- NativeScript/runtime/Runtime.cpp | 7 +- benchmarks/objc-dispatch/run.js | 5 +- .../src/SignatureDispatchEmitter.cpp | 51 +- .../src/SignatureDispatchEmitter/Napi.cpp | 49 +- .../NativeScriptNativeApi.podspec | 2 +- scripts/build_nativescript.sh | 54 +- scripts/build_react_native_turbomodule.sh | 32 +- scripts/check_ffi_boundaries.sh | 100 +++- scripts/run-tests-macos.js | 32 +- .../runner/app/tests/NativeApiJsiTests.js | 27 +- 53 files changed, 1801 insertions(+), 1317 deletions(-) rename NativeScript/ffi/{shared => }/direct/EmbeddedMetadata.mm (100%) rename NativeScript/ffi/{shared => }/direct/NativeApiDirect.h (100%) rename NativeScript/ffi/{shared/jsi/NativeApiJsiBridge.h => direct/NativeApiDirectBridge.h} (94%) rename NativeScript/ffi/{shared/jsi/NativeApiJsiCallbacks.h => direct/NativeApiDirectCallbacks.h} (78%) rename NativeScript/ffi/{shared/jsi/NativeApiJsiClassBuilder.h => direct/NativeApiDirectClassBuilder.h} (79%) rename NativeScript/ffi/{shared/jsi/NativeApiJsiConversion.h => direct/NativeApiDirectConversion.h} (90%) rename NativeScript/ffi/{shared/jsi/NativeApiJsiHostObject.h => direct/NativeApiDirectHostObject.h} (94%) rename NativeScript/ffi/{shared/jsi/NativeApiJsiHostObjects.h => direct/NativeApiDirectHostObjects.h} (94%) rename NativeScript/ffi/{shared/jsi/NativeApiJsiInstall.h => direct/NativeApiDirectInstall.h} (98%) rename NativeScript/ffi/{shared/jsi/NativeApiJsiInvocation.h => direct/NativeApiDirectInvocation.h} (71%) create mode 100644 NativeScript/ffi/direct/NativeApiDirectSignatureDispatch.h rename NativeScript/ffi/hermes/{jsi => }/NativeApiJsi.h (100%) rename NativeScript/ffi/hermes/{jsi => }/NativeApiJsi.mm (63%) rename NativeScript/ffi/hermes/{jsi => }/NativeApiJsiReactNative.h (100%) create mode 100644 NativeScript/ffi/hermes/NativeApiJsiSignatureDispatch.h create mode 100644 NativeScript/ffi/hermes/README.md delete mode 100644 NativeScript/ffi/hermes/jsi/README.md create mode 100644 NativeScript/ffi/shared/SignatureDispatchCore.h diff --git a/.gitignore b/.gitignore index edc6abe5..b032b593 100644 --- a/.gitignore +++ b/.gitignore @@ -65,8 +65,8 @@ 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 # React Native TurboModule package staging packages/react-native/dist/ diff --git a/NativeScript/CMakeLists.txt b/NativeScript/CMakeLists.txt index b9386f46..235f204a 100644 --- a/NativeScript/CMakeLists.txt +++ b/NativeScript/CMakeLists.txt @@ -157,13 +157,6 @@ 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")) - 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.") -endif() - if(NS_GSD_BACKEND STREQUAL "auto") if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "direct") set(NS_EFFECTIVE_GSD_BACKEND "none") @@ -200,6 +193,12 @@ 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(NS_EFFECTIVE_FFI_BACKEND STREQUAL "direct" AND + NS_EFFECTIVE_GSD_BACKEND STREQUAL "napi") + message(FATAL_ERROR + "NS_FFI_BACKEND=direct 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})") @@ -240,12 +239,12 @@ set(FFI_NAPI_SOURCE_FILES ) set(FFI_DIRECT_SHARED_SOURCE_FILES - ffi/shared/direct/EmbeddedMetadata.mm + ffi/direct/EmbeddedMetadata.mm ) set(FFI_HERMES_DIRECT_SOURCE_FILES ${FFI_DIRECT_SHARED_SOURCE_FILES} - ffi/hermes/jsi/NativeApiJsi.mm + ffi/hermes/NativeApiJsi.mm ) set(FFI_V8_DIRECT_SOURCE_FILES @@ -516,15 +515,31 @@ 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_GSD_BACKEND_ENGINE_DIRECT_VALUE 0) +set(NS_GSD_BACKEND_DIRECT_PREPARED_VALUE 0) set(NS_FFI_BACKEND_DIRECT_VALUE 0) set(NS_FFI_BACKEND_NAPI_VALUE 0) if(NS_EFFECTIVE_GSD_BACKEND STREQUAL "v8") - set(NS_GSD_BACKEND_V8_VALUE 1) + if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "direct") + set(NS_GSD_BACKEND_DIRECT_PREPARED_VALUE 1) + else() + set(NS_GSD_BACKEND_V8_VALUE 1) + endif() elseif(NS_EFFECTIVE_GSD_BACKEND STREQUAL "jsc") - set(NS_GSD_BACKEND_JSC_VALUE 1) + if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "direct") + set(NS_GSD_BACKEND_DIRECT_PREPARED_VALUE 1) + else() + set(NS_GSD_BACKEND_JSC_VALUE 1) + set(NS_GSD_BACKEND_ENGINE_DIRECT_VALUE 1) + endif() elseif(NS_EFFECTIVE_GSD_BACKEND STREQUAL "quickjs") - set(NS_GSD_BACKEND_QUICKJS_VALUE 1) + if(NS_EFFECTIVE_FFI_BACKEND STREQUAL "direct") + set(NS_GSD_BACKEND_DIRECT_PREPARED_VALUE 1) + else() + set(NS_GSD_BACKEND_QUICKJS_VALUE 1) + set(NS_GSD_BACKEND_ENGINE_DIRECT_VALUE 1) + endif() elseif(NS_EFFECTIVE_GSD_BACKEND STREQUAL "hermes") set(NS_GSD_BACKEND_HERMES_VALUE 1) elseif(NS_EFFECTIVE_GSD_BACKEND STREQUAL "napi") @@ -543,6 +558,8 @@ target_compile_definitions(${NAME} PRIVATE 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_GSD_BACKEND_ENGINE_DIRECT=${NS_GSD_BACKEND_ENGINE_DIRECT_VALUE} + NS_GSD_BACKEND_DIRECT_PREPARED=${NS_GSD_BACKEND_DIRECT_PREPARED_VALUE} NS_FFI_BACKEND_DIRECT=${NS_FFI_BACKEND_DIRECT_VALUE} NS_FFI_BACKEND_NAPI=${NS_FFI_BACKEND_NAPI_VALUE} ) diff --git a/NativeScript/ffi/shared/direct/EmbeddedMetadata.mm b/NativeScript/ffi/direct/EmbeddedMetadata.mm similarity index 100% rename from NativeScript/ffi/shared/direct/EmbeddedMetadata.mm rename to NativeScript/ffi/direct/EmbeddedMetadata.mm diff --git a/NativeScript/ffi/shared/direct/NativeApiDirect.h b/NativeScript/ffi/direct/NativeApiDirect.h similarity index 100% rename from NativeScript/ffi/shared/direct/NativeApiDirect.h rename to NativeScript/ffi/direct/NativeApiDirect.h diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiBridge.h b/NativeScript/ffi/direct/NativeApiDirectBridge.h similarity index 94% rename from NativeScript/ffi/shared/jsi/NativeApiJsiBridge.h rename to NativeScript/ffi/direct/NativeApiDirectBridge.h index d543b1ec..a0e08fa3 100644 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiBridge.h +++ b/NativeScript/ffi/direct/NativeApiDirectBridge.h @@ -1,23 +1,23 @@ thread_local bool gDispatchNativeCallsToUI = false; thread_local bool gExecutingDispatchedUINativeCall = false; thread_local int gSynchronousNativeInvocationDepth = 0; -thread_local int gNativeCallerThreadJsiCallbackDepth = 0; +thread_local int gNativeCallerThreadDirectCallbackDepth = 0; thread_local std::vector gNativeCallbackExceptionCaptureStack; std::atomic gActiveSynchronousNativeInvocationDepth{0}; -static char gNativeApiJsiExtendedClassKey; +static char gNativeApiDirectExtendedClassKey; -void markNativeApiJsiExtendedClass(Class cls) { +void markNativeApiDirectExtendedClass(Class cls) { if (cls == Nil) { return; } - objc_setAssociatedObject(cls, &gNativeApiJsiExtendedClassKey, @YES, + objc_setAssociatedObject(cls, &gNativeApiDirectExtendedClassKey, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -bool isNativeApiJsiExtendedClass(Class cls) { +bool isNativeApiDirectExtendedClass(Class cls) { Class current = cls; while (current != Nil) { - if (objc_getAssociatedObject(current, &gNativeApiJsiExtendedClassKey) != nil) { + if (objc_getAssociatedObject(current, &gNativeApiDirectExtendedClassKey) != nil) { return true; } current = class_getSuperclass(current); @@ -59,20 +59,20 @@ class ScopedNativeApiSynchronousInvocation final { } }; -class ScopedNativeCallerThreadJsiCallback final { +class ScopedNativeCallerThreadDirectCallback final { public: - ScopedNativeCallerThreadJsiCallback() { - gNativeCallerThreadJsiCallbackDepth += 1; + ScopedNativeCallerThreadDirectCallback() { + gNativeCallerThreadDirectCallbackDepth += 1; } - ~ScopedNativeCallerThreadJsiCallback() { - gNativeCallerThreadJsiCallbackDepth -= 1; + ~ScopedNativeCallerThreadDirectCallback() { + gNativeCallerThreadDirectCallbackDepth -= 1; } - ScopedNativeCallerThreadJsiCallback( - const ScopedNativeCallerThreadJsiCallback&) = delete; - ScopedNativeCallerThreadJsiCallback& operator=( - const ScopedNativeCallerThreadJsiCallback&) = delete; + ScopedNativeCallerThreadDirectCallback( + const ScopedNativeCallerThreadDirectCallback&) = delete; + ScopedNativeCallerThreadDirectCallback& operator=( + const ScopedNativeCallerThreadDirectCallback&) = delete; }; class ScopedNativeCallbackExceptionCapture final { @@ -132,7 +132,7 @@ void performNativeInvocation(Runtime& runtime, } }; - bool skipInvoker = gNativeCallerThreadJsiCallbackDepth > 0; + bool skipInvoker = gNativeCallerThreadDirectCallbackDepth > 0; if (shouldDispatchNativeCallToUI()) { dispatch_sync(dispatch_get_main_queue(), ^{ bool previous = gExecutingDispatchedUINativeCall; @@ -153,10 +153,10 @@ void performNativeInvocation(Runtime& runtime, if (exceptionDescription != nil) { std::string message = exceptionDescription.UTF8String ?: ""; [exceptionDescription release]; - throw facebook::jsi::JSError(runtime, message); + throw JSError(runtime, message); } if (!callbackException.empty()) { - throw facebook::jsi::JSError(runtime, callbackException); + throw JSError(runtime, callbackException); } } @@ -189,13 +189,13 @@ struct NativeApiMember { bool readonly = false; }; -struct NativeApiJsiAggregateInfo; +struct NativeApiDirectAggregateInfo; -struct NativeApiJsiFfiType { +struct NativeApiDirectFfiType { ffi_type type = {}; std::vector elements; - NativeApiJsiFfiType() { + NativeApiDirectFfiType() { type.type = FFI_TYPE_STRUCT; type.size = 0; type.alignment = 0; @@ -208,7 +208,7 @@ struct NativeApiJsiFfiType { } }; -struct NativeApiJsiType { +struct NativeApiDirectType { MDTypeKind kind = metagen::mdTypeVoid; ffi_type* ffiType = &ffi_type_void; bool supported = true; @@ -217,24 +217,24 @@ struct NativeApiJsiType { 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; + std::shared_ptr elementType; + std::shared_ptr aggregateInfo; + std::shared_ptr ownedFfiType; }; -struct NativeApiJsiAggregateField { +struct NativeApiDirectAggregateField { std::string name; uint16_t offset = 0; - NativeApiJsiType type; + NativeApiDirectType type; }; -struct NativeApiJsiAggregateInfo { +struct NativeApiDirectAggregateInfo { std::string name; uint16_t size = 0; bool isUnion = false; MDSectionOffset offset = MD_SECTION_OFFSET_NULL; - std::vector fields; - std::shared_ptr ffi; + std::vector fields; + std::shared_ptr ffi; }; std::string jsifySelector(const char* selector) { @@ -432,7 +432,7 @@ const NativeApiMember* selectWritablePropertyMember( return fallback; } -void skipMetadataJsiType(MDMetadataReader* metadata, MDSectionOffset* offset); +void skipMetadataDirectType(MDMetadataReader* metadata, MDSectionOffset* offset); Protocol* lookupProtocolByNativeName(const std::string& name); inline uintptr_t normalizeRuntimePointer(uintptr_t pointer) { @@ -443,9 +443,9 @@ inline uintptr_t normalizeRuntimePointer(uintptr_t pointer) { #endif } -class NativeApiJsiBridge { +class NativeApiDirectBridge { public: - explicit NativeApiJsiBridge(const NativeApiJsiConfig& config) + explicit NativeApiDirectBridge(const NativeApiDirectConfig& config) : metadata_(loadMetadata(config)), scheduler_(config.scheduler), nativeInvocationInvoker_(config.nativeInvocationInvoker), @@ -457,7 +457,7 @@ class NativeApiJsiBridge { buildSymbolIndexes(); } - ~NativeApiJsiBridge() { + ~NativeApiDirectBridge() { if (selfDl_ != nullptr) { dlclose(selfDl_); } @@ -705,7 +705,7 @@ class NativeApiJsiBridge { 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_; } + std::shared_ptr scheduler() const { return scheduler_; } const std::function)>& nativeInvocationInvoker() const { return nativeInvocationInvoker_; @@ -723,7 +723,7 @@ class NativeApiJsiBridge { } std::thread::id jsThreadId() const { return jsThreadId_; } - void retainJsiLifetime(std::shared_ptr lifetime) { + void retainDirectLifetime(std::shared_ptr lifetime) { if (lifetime == nullptr) { return; } @@ -767,10 +767,10 @@ class NativeApiJsiBridge { return inserted.first->second; } - std::shared_ptr aggregateInfoFor( + std::shared_ptr aggregateInfoFor( MDSectionOffset aggregateOffset, bool isUnion); - std::shared_ptr aggregateInfoFor( + std::shared_ptr aggregateInfoFor( const NativeApiSymbol& symbol) { return aggregateInfoFor(symbol.offset, symbol.kind == NativeApiSymbolKind::Union); @@ -810,7 +810,7 @@ class NativeApiJsiBridge { } static std::unique_ptr loadMetadata( - const NativeApiJsiConfig& config) { + const NativeApiDirectConfig& config) { if (config.metadataPtr != nullptr && *static_cast(config.metadataPtr) != '\0') { #ifdef EMBED_METADATA_SIZE @@ -979,7 +979,7 @@ class NativeApiJsiBridge { metagen::MDVariableEvalKind evalKind) { switch (evalKind) { case metagen::mdEvalNone: - skipMetadataJsiType(metadata, &offset); + skipMetadataDirectType(metadata, &offset); break; case metagen::mdEvalInt64: offset += sizeof(int64_t); @@ -1124,7 +1124,7 @@ class NativeApiJsiBridge { if (!isUnion) { offset += sizeof(uint16_t); } - skipMetadataJsiType(metadata_.get(), &offset); + skipMetadataDirectType(metadata_.get(), &offset); } } @@ -1551,7 +1551,7 @@ class NativeApiJsiBridge { std::vector enumNames_; std::vector structNames_; std::vector unionNames_; - std::shared_ptr scheduler_; + std::shared_ptr scheduler_; std::function)> nativeInvocationInvoker_; std::function)> nativeCallbackInvoker_; std::function)> jsThreadCallbackInvoker_; @@ -1564,7 +1564,7 @@ class NativeApiJsiBridge { membersByProtocolOffset_; std::unordered_map structSymbolsByOffset_; std::unordered_map unionSymbolsByOffset_; - std::unordered_map> + std::unordered_map> aggregateInfoByOffset_; std::unordered_set aggregateInfoInProgress_; std::thread::id jsThreadId_ = std::this_thread::get_id(); @@ -1579,7 +1579,7 @@ Value makeString(Runtime& runtime, const std::string& 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( + throw JSError( runtime, std::string(argumentName) + " must be a string."); } return args[index].asString(runtime).utf8(runtime); @@ -1622,15 +1622,15 @@ class NativeApiPointerHostObject; class NativeApiObjectHostObject; class NativeApiClassHostObject; class NativeApiProtocolHostObject; -class NativeApiJsiArgumentFrame; +class NativeApiDirectArgumentFrame; Value callCFunction(Runtime& runtime, - const std::shared_ptr& bridge, + const std::shared_ptr& bridge, const NativeApiSymbol& symbol, const Value* args, size_t count); Value callObjCSelector(Runtime& runtime, - const std::shared_ptr& bridge, + const std::shared_ptr& bridge, id receiver, bool receiverIsClass, const std::string& selectorName, const NativeApiMember* member, @@ -1638,11 +1638,11 @@ Value callObjCSelector(Runtime& runtime, Class dispatchSuperClass = Nil); Value makeNativeObjectValue(Runtime& runtime, - const std::shared_ptr& bridge, + const std::shared_ptr& bridge, id object, bool ownsObject); Value makeNativeClassValue(Runtime& runtime, - const std::shared_ptr& bridge, + const std::shared_ptr& bridge, NativeApiSymbol symbol); Object symbolToObject(Runtime& runtime, const NativeApiSymbol& symbol) { @@ -1670,19 +1670,19 @@ Object symbolToObject(Runtime& runtime, const NativeApiSymbol& symbol) { return result; } -size_t nativeSizeForType(const NativeApiJsiType& type); +size_t nativeSizeForType(const NativeApiDirectType& type); std::optional parseArrayIndexProperty(const std::string& property); -NativeApiJsiType nativeObjectReturnType( +NativeApiDirectType nativeObjectReturnType( MDTypeKind kind = metagen::mdTypeAnyObject) { - NativeApiJsiType type; + NativeApiDirectType type; type.kind = kind; type.ffiType = &ffi_type_pointer; type.supported = true; return type; } -NativeApiJsiType nativeObjectReturnTypeForClass(Class cls) { +NativeApiDirectType nativeObjectReturnTypeForClass(Class cls) { if (cls != Nil) { const char* name = class_getName(cls); if (name != nullptr && std::strcmp(name, "NSString") == 0) { @@ -1696,10 +1696,10 @@ NativeApiJsiType nativeObjectReturnTypeForClass(Class cls) { } Value convertNativeReturnValue(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* value); + const std::shared_ptr& bridge, + const NativeApiDirectType& type, void* value); Object createPointer(Runtime& runtime, - const std::shared_ptr& bridge, + const std::shared_ptr& bridge, void* pointer, bool adopted = false); -NativeApiJsiType primitiveInteropType(MDTypeKind kind); +NativeApiDirectType primitiveInteropType(MDTypeKind kind); diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiCallbacks.h b/NativeScript/ffi/direct/NativeApiDirectCallbacks.h similarity index 78% rename from NativeScript/ffi/shared/jsi/NativeApiJsiCallbacks.h rename to NativeScript/ffi/direct/NativeApiDirectCallbacks.h index 7df14ed0..9279561b 100644 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiCallbacks.h +++ b/NativeScript/ffi/direct/NativeApiDirectCallbacks.h @@ -1,4 +1,4 @@ -bool isObjectiveCObjectType(const NativeApiJsiType& type) { +bool isObjectiveCObjectType(const NativeApiDirectType& type) { switch (type.kind) { case metagen::mdTypeAnyObject: case metagen::mdTypeProtocolObject: @@ -13,56 +13,58 @@ bool isObjectiveCObjectType(const NativeApiJsiType& type) { } #ifndef NATIVESCRIPT_NATIVE_API_RETAIN_RUNTIME -std::shared_ptr retainNativeApiJsiRuntime(Runtime& runtime) { +std::shared_ptr retainNativeApiDirectRuntime(Runtime& runtime) { return std::shared_ptr(&runtime, [](Runtime*) {}); } #endif #ifndef NATIVESCRIPT_NATIVE_API_RUNTIME_SCOPE -class NativeApiJsiRuntimeScope final { +class NativeApiDirectRuntimeScope final { public: - explicit NativeApiJsiRuntimeScope(Runtime&) {} + explicit NativeApiDirectRuntimeScope(Runtime&) {} }; #endif -struct NativeApiJsiSignature { +struct NativeApiDirectSignature { ffi_cif cif = {}; - NativeApiJsiType returnType; - std::vector argumentTypes; + NativeApiDirectType 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 NativeApiJsiCallbackThreadPolicy { +enum class NativeApiDirectCallbackThreadPolicy { Default, UI, JS, }; -NativeApiJsiCallbackThreadPolicy readJsiCallbackThreadPolicy( +NativeApiDirectCallbackThreadPolicy readDirectCallbackThreadPolicy( Runtime& runtime, Object& functionObject) { constexpr const char* propertyName = "__nativeScriptCallbackThread"; try { if (!functionObject.hasProperty(runtime, propertyName)) { - return NativeApiJsiCallbackThreadPolicy::Default; + return NativeApiDirectCallbackThreadPolicy::Default; } Value policyValue = functionObject.getProperty(runtime, propertyName); if (!policyValue.isString()) { - return NativeApiJsiCallbackThreadPolicy::Default; + return NativeApiDirectCallbackThreadPolicy::Default; } std::string policy = policyValue.asString(runtime).utf8(runtime); if (policy == "ui") { - return NativeApiJsiCallbackThreadPolicy::UI; + return NativeApiDirectCallbackThreadPolicy::UI; } if (policy == "js") { - return NativeApiJsiCallbackThreadPolicy::JS; + return NativeApiDirectCallbackThreadPolicy::JS; } } catch (const std::exception&) { } - return NativeApiJsiCallbackThreadPolicy::Default; + return NativeApiDirectCallbackThreadPolicy::Default; } bool selectorEndsWithNSErrorParam(const std::string& selectorName) { @@ -73,7 +75,7 @@ bool selectorEndsWithNSErrorParam(const std::string& selectorName) { suffix) == 0; } -bool isNSErrorOutJsiMethodSignature(const NativeApiJsiSignature& signature) { +bool isNSErrorOutDirectMethodSignature(const NativeApiDirectSignature& signature) { if (signature.argumentTypes.empty() || signature.variadic || !selectorEndsWithNSErrorParam(signature.selectorName)) { return false; @@ -82,17 +84,17 @@ bool isNSErrorOutJsiMethodSignature(const NativeApiJsiSignature& signature) { return signature.argumentTypes.back().kind == metagen::mdTypePointer; } -bool isNSErrorOutJsiMethodCallback(const NativeApiJsiSignature& signature) { +bool isNSErrorOutDirectMethodCallback(const NativeApiDirectSignature& signature) { return signature.returnType.kind == metagen::mdTypeBool && signature.implicitArgumentCount >= 2 && - isNSErrorOutJsiMethodSignature(signature); + isNSErrorOutDirectMethodSignature(signature); } -class NativeApiJsiArgumentFrame { +class NativeApiDirectArgumentFrame { public: - explicit NativeApiJsiArgumentFrame(size_t count) : storage_(count), values_(count) {} + explicit NativeApiDirectArgumentFrame(size_t count) : storage_(count), values_(count) {} - ~NativeApiJsiArgumentFrame() { + ~NativeApiDirectArgumentFrame() { for (char* string : ownedCStrings_) { free(string); } @@ -132,7 +134,7 @@ class NativeApiJsiArgumentFrame { } } void rememberRoundTripValue( - const std::shared_ptr& bridge, Runtime& runtime, + const std::shared_ptr& bridge, Runtime& runtime, const void* native, const Value& value) { if (bridge == nullptr || native == nullptr) { return; @@ -149,7 +151,7 @@ class NativeApiJsiArgumentFrame { std::vector ownedBuffers_; std::vector ownedObjects_; std::vector> ownedLifetimes_; - std::vector, const void*>> + std::vector, const void*>> temporaryRoundTripValues_; }; @@ -169,24 +171,24 @@ class NativeApiMutableBuffer final : public MutableBuffer { std::vector data_; }; -void convertJsiArgument(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, +void convertDirectArgument(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiDirectType& type, const Value& value, void* target, - NativeApiJsiArgumentFrame& frame); + NativeApiDirectArgumentFrame& frame); Value convertNativeReturnValue(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* value); + const std::shared_ptr& bridge, + const NativeApiDirectType& type, void* value); Value wrapNativeFunctionPointer(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* pointer, + const std::shared_ptr& bridge, + const NativeApiDirectType& type, void* pointer, bool block); -bool isObjectiveCObjectType(const NativeApiJsiType& type); +bool isObjectiveCObjectType(const NativeApiDirectType& type); -struct NativeApiJsiBlockDescriptor { +struct NativeApiDirectBlockDescriptor { unsigned long reserved = 0; unsigned long size = 0; void (*copyHelper)(void*, void*) = nullptr; @@ -194,29 +196,29 @@ struct NativeApiJsiBlockDescriptor { const char* signature = nullptr; }; -struct NativeApiJsiBlockLiteral { +struct NativeApiDirectBlockLiteral { void* isa = nullptr; int flags = 0; int reserved = 0; void* invoke = nullptr; - NativeApiJsiBlockDescriptor* descriptor = nullptr; + NativeApiDirectBlockDescriptor* 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); +constexpr int kNativeApiDirectBlockNeedsFree = (1 << 24); +constexpr int kNativeApiDirectBlockHasCopyDispose = (1 << 25); +constexpr int kNativeApiDirectBlockRefCountOne = (1 << 1); +constexpr int kNativeApiDirectBlockHasSignature = (1 << 30); -void* nativeApiJsiStackBlockIsa() { +void* nativeApiDirectStackBlockIsa() { static void* isa = dlsym(RTLD_DEFAULT, "_NSConcreteStackBlock"); return isa; } -void nativeApiJsiBlockCopy(void* dst, void* src); -void nativeApiJsiBlockDispose(void* src); +void nativeApiDirectBlockCopy(void* dst, void* src); +void nativeApiDirectBlockDispose(void* src); -std::string objcEncodingForJsiType(const NativeApiJsiType& type) { +std::string objcEncodingForDirectType(const NativeApiDirectType& type) { switch (type.kind) { case metagen::mdTypeVoid: return "v"; @@ -266,7 +268,7 @@ std::string objcEncodingForJsiType(const NativeApiJsiType& type) { case metagen::mdTypeOpaquePointer: if (type.elementType != nullptr && type.elementType->kind != metagen::mdTypeVoid) { - return "^" + objcEncodingForJsiType(*type.elementType); + return "^" + objcEncodingForDirectType(*type.elementType); } return "^v"; case metagen::mdTypeStruct: @@ -276,65 +278,65 @@ std::string objcEncodingForJsiType(const NativeApiJsiType& type) { "=}"; case metagen::mdTypeArray: return "[" + std::to_string(type.arraySize) + - (type.elementType != nullptr ? objcEncodingForJsiType(*type.elementType) + (type.elementType != nullptr ? objcEncodingForDirectType(*type.elementType) : std::string("?")) + "]"; case metagen::mdTypeVector: case metagen::mdTypeExtVector: case metagen::mdTypeComplex: - return type.elementType != nullptr ? objcEncodingForJsiType(*type.elementType) + return type.elementType != nullptr ? objcEncodingForDirectType(*type.elementType) : "?"; default: return "?"; } } -std::string objcBlockSignatureForJsiSignature( - const NativeApiJsiSignature& signature) { - std::string encoding = objcEncodingForJsiType(signature.returnType); +std::string objcBlockSignatureForDirectSignature( + const NativeApiDirectSignature& signature) { + std::string encoding = objcEncodingForDirectType(signature.returnType); encoding += "@?"; for (const auto& argType : signature.argumentTypes) { - encoding += objcEncodingForJsiType(argType); + encoding += objcEncodingForDirectType(argType); } return encoding; } -std::string objcMethodSignatureForJsiSignature( - const NativeApiJsiSignature& signature) { - std::string encoding = objcEncodingForJsiType(signature.returnType); +std::string objcMethodSignatureForDirectSignature( + const NativeApiDirectSignature& signature) { + std::string encoding = objcEncodingForDirectType(signature.returnType); encoding += "@:"; for (const auto& argType : signature.argumentTypes) { - encoding += objcEncodingForJsiType(argType); + encoding += objcEncodingForDirectType(argType); } return encoding; } -[[noreturn]] void throwNativeApiJsiCallbackException( +[[noreturn]] void throwNativeApiDirectCallbackException( const std::string& message) { NSString* reason = [NSString stringWithUTF8String:message.c_str()]; - @throw [NSException exceptionWithName:@"NativeScriptJSICallbackException" + @throw [NSException exceptionWithName:@"NativeScriptDirectCallbackException" reason:reason userInfo:nil]; } -class NativeApiJsiCallback; +class NativeApiDirectCallback; -void nativeApiJsiCallbackTrampoline(ffi_cif* cif, void* ret, void* args[], +void nativeApiDirectCallbackTrampoline(ffi_cif* cif, void* ret, void* args[], void* data); -std::atomic gActiveNativeThreadJsiCallbacks{0}; +std::atomic gActiveNativeThreadDirectCallbacks{0}; -class NativeApiJsiCallback final - : public std::enable_shared_from_this { +class NativeApiDirectCallback final + : public std::enable_shared_from_this { public: - NativeApiJsiCallback(Runtime& runtime, - std::shared_ptr bridge, - std::shared_ptr signature, + NativeApiDirectCallback(Runtime& runtime, + std::shared_ptr bridge, + std::shared_ptr signature, Function function, bool block, - NativeApiJsiCallbackThreadPolicy threadPolicy = - NativeApiJsiCallbackThreadPolicy::Default, + NativeApiDirectCallbackThreadPolicy threadPolicy = + NativeApiDirectCallbackThreadPolicy::Default, bool bindThis = false) - : runtimeOwner_(retainNativeApiJsiRuntime(runtime)), + : runtimeOwner_(retainNativeApiDirectRuntime(runtime)), runtime_(runtimeOwner_.get()), bridge_(std::move(bridge)), signature_(std::move(signature)), @@ -346,41 +348,41 @@ class NativeApiJsiCallback final 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."); + throw JSError(runtime, + "Unable to allocate native Direct callback."); } ffi_status status = ffi_prep_closure_loc( - closure_, &signature_->cif, nativeApiJsiCallbackTrampoline, this, + closure_, &signature_->cif, nativeApiDirectCallbackTrampoline, 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."); + throw JSError(runtime, + "Unable to prepare native Direct callback."); } if (block_) { - blockSignature_ = objcBlockSignatureForJsiSignature(*signature_); - descriptor_ = std::make_unique(); + blockSignature_ = objcBlockSignatureForDirectSignature(*signature_); + descriptor_ = std::make_unique(); descriptor_->reserved = 0; - descriptor_->size = sizeof(NativeApiJsiBlockLiteral); - descriptor_->copyHelper = nativeApiJsiBlockCopy; - descriptor_->disposeHelper = nativeApiJsiBlockDispose; + descriptor_->size = sizeof(NativeApiDirectBlockLiteral); + descriptor_->copyHelper = nativeApiDirectBlockCopy; + descriptor_->disposeHelper = nativeApiDirectBlockDispose; descriptor_->signature = blockSignature_.c_str(); - blockLiteral_ = std::make_unique(); - blockLiteral_->isa = nativeApiJsiStackBlockIsa(); - blockLiteral_->flags = kNativeApiJsiBlockHasCopyDispose | - kNativeApiJsiBlockHasSignature; + blockLiteral_ = std::make_unique(); + blockLiteral_->isa = nativeApiDirectStackBlockIsa(); + blockLiteral_->flags = kNativeApiDirectBlockHasCopyDispose | + kNativeApiDirectBlockHasSignature; blockLiteral_->invoke = executable_; blockLiteral_->descriptor = descriptor_.get(); blockLiteral_->callback = this; } } - ~NativeApiJsiCallback() { + ~NativeApiDirectCallback() { if (closure_ != nullptr) { ffi_closure_free(closure_); closure_ = nullptr; @@ -394,7 +396,7 @@ class NativeApiJsiCallback final : executable_; } - const NativeApiJsiSignature& signature() const { return *signature_; } + const NativeApiDirectSignature& signature() const { return *signature_; } void retainBlockCopy(const void* blockPointer) { if (!block_) { @@ -414,7 +416,7 @@ class NativeApiJsiCallback final if (!block_) { return false; } - std::shared_ptr keepAlive; + std::shared_ptr keepAlive; try { keepAlive = shared_from_this(); } catch (const std::bad_weak_ptr&) { @@ -441,7 +443,7 @@ class NativeApiJsiCallback final void invoke(void* ret, void* args[]) { if (runtime_ == nullptr || function_ == nullptr || signature_ == nullptr) { - throwNativeApiJsiCallbackException("Invalid JSI callback."); + throwNativeApiDirectCallbackException("Invalid Direct callback."); } std::string error; @@ -452,7 +454,7 @@ class NativeApiJsiCallback final std::this_thread::get_id() == bridge_->jsThreadId(); auto callOnNativeCallerThread = [&]() { - ScopedNativeCallerThreadJsiCallback callbackScope; + ScopedNativeCallerThreadDirectCallback callbackScope; if (nativeCallbackInvoker) { nativeCallbackInvoker(call); } else { @@ -495,20 +497,20 @@ class NativeApiJsiCallback final error = "Native callback was invoked off the JS thread without a JS scheduler."; }; - if (threadPolicy_ == NativeApiJsiCallbackThreadPolicy::UI) { + if (threadPolicy_ == NativeApiDirectCallbackThreadPolicy::UI) { callOnUIThread(); if (!error.empty()) { if (!recordNativeCallbackException(error)) { - throwNativeApiJsiCallbackException(error); + throwNativeApiDirectCallbackException(error); } } return; } - if (threadPolicy_ == NativeApiJsiCallbackThreadPolicy::JS) { + if (threadPolicy_ == NativeApiDirectCallbackThreadPolicy::JS) { callOnJSThread(); if (!error.empty()) { if (!recordNativeCallbackException(error)) { - throwNativeApiJsiCallbackException(error); + throwNativeApiDirectCallbackException(error); } } return; @@ -522,17 +524,15 @@ class NativeApiJsiCallback final bridge_->invokeCallbacksOnNativeCallerThread(); bool nativeCallerThreadCallback = nativeCallerThreadCallbacks && !currentThreadIsJs && - (block_ || bindThis_ || - (activeSynchronousNativeInvocation && !returnsVoid)); + activeSynchronousNativeInvocation && + (block_ || bindThis_ || !returnsVoid); bool direct = currentThreadIsJs || gExecutingDispatchedUINativeCall || gSynchronousNativeInvocationDepth > 0 || - nativeCallerThreadCallback || - (nativeCallerThreadCallbacks && !nativeCallbackInvoker && - activeSynchronousNativeInvocation); + nativeCallerThreadCallback; bool waitForNativeThreadCallback = currentThreadIsJs && nativeCallbackInvoker && - gActiveNativeThreadJsiCallbacks.load(std::memory_order_acquire) > 0; + gActiveNativeThreadDirectCallbacks.load(std::memory_order_acquire) > 0; if (direct && !waitForNativeThreadCallback) { if (nativeCallerThreadCallback) { callOnNativeCallerThread(); @@ -547,20 +547,20 @@ class NativeApiJsiCallback final } else if (nativeCallbackInvoker) { bool nativeThreadCallback = !currentThreadIsJs; if (nativeThreadCallback) { - gActiveNativeThreadJsiCallbacks.fetch_add(1, + gActiveNativeThreadDirectCallbacks.fetch_add(1, std::memory_order_acq_rel); } try { nativeCallbackInvoker(call); } catch (...) { if (nativeThreadCallback) { - gActiveNativeThreadJsiCallbacks.fetch_sub( + gActiveNativeThreadDirectCallbacks.fetch_sub( 1, std::memory_order_acq_rel); } throw; } if (nativeThreadCallback) { - gActiveNativeThreadJsiCallbacks.fetch_sub(1, + gActiveNativeThreadDirectCallbacks.fetch_sub(1, std::memory_order_acq_rel); } } else if (auto scheduler = bridge_->scheduler()) { @@ -576,7 +576,7 @@ class NativeApiJsiCallback final if (!error.empty()) { if (!recordNativeCallbackException(error)) { - throwNativeApiJsiCallbackException(error); + throwNativeApiDirectCallbackException(error); } } } @@ -584,7 +584,7 @@ class NativeApiJsiCallback final private: void invokeOnCurrentThread(void* ret, void* args[], std::string* error) { try { - NativeApiJsiRuntimeScope runtimeScope(*runtime_); + NativeApiDirectRuntimeScope runtimeScope(*runtime_); size_t nativeArgOffset = signature_->implicitArgumentCount; std::vector jsArgs; jsArgs.reserve(signature_->argumentTypes.size()); @@ -622,7 +622,7 @@ class NativeApiJsiCallback final runtime_->drainMicrotasks(); } } catch (const std::exception& exception) { - if (isNSErrorOutJsiMethodCallback(*signature_)) { + if (isNSErrorOutDirectMethodCallback(*signature_)) { zeroReturnValue(ret); populateNSErrorOutArgument(args, exception.what()); return; @@ -632,13 +632,13 @@ class NativeApiJsiCallback final } zeroReturnValue(ret); } catch (...) { - if (isNSErrorOutJsiMethodCallback(*signature_)) { + if (isNSErrorOutDirectMethodCallback(*signature_)) { zeroReturnValue(ret); - populateNSErrorOutArgument(args, "Unknown exception in native JSI callback."); + populateNSErrorOutArgument(args, "Unknown exception in native Direct callback."); return; } if (error != nullptr) { - *error = "Unknown exception in native JSI callback."; + *error = "Unknown exception in native Direct callback."; } zeroReturnValue(ret); } @@ -706,8 +706,8 @@ class NativeApiJsiCallback final return; } - NativeApiJsiArgumentFrame frame(1); - convertJsiArgument(*runtime_, bridge_, returnType, result, ret, frame); + NativeApiDirectArgumentFrame frame(1); + convertDirectArgument(*runtime_, bridge_, returnType, result, ret, frame); if (isObjectiveCObjectType(returnType)) { id object = *static_cast(ret); if (object != nil) { @@ -719,53 +719,53 @@ class NativeApiJsiCallback final std::shared_ptr runtimeOwner_; Runtime* runtime_ = nullptr; - std::shared_ptr bridge_; - std::shared_ptr signature_; + std::shared_ptr bridge_; + std::shared_ptr signature_; std::shared_ptr function_; bool block_ = false; - NativeApiJsiCallbackThreadPolicy threadPolicy_ = - NativeApiJsiCallbackThreadPolicy::Default; + NativeApiDirectCallbackThreadPolicy threadPolicy_ = + NativeApiDirectCallbackThreadPolicy::Default; bool bindThis_ = false; ffi_closure* closure_ = nullptr; void* executable_ = nullptr; std::string blockSignature_; - std::unique_ptr descriptor_; - std::unique_ptr blockLiteral_; + std::unique_ptr descriptor_; + std::unique_ptr blockLiteral_; struct RetainedBlockCopy { const void* blockPointer = nullptr; - std::shared_ptr lifetime; + 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); +void nativeApiDirectBlockCopy(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) + static_cast(srcBlock->callback) ->retainBlockCopy(dstBlock); } -void nativeApiJsiBlockDispose(void* src) { - auto* block = static_cast(src); +void nativeApiDirectBlockDispose(void* src) { + auto* block = static_cast(src); if (block == nullptr || block->callback == nullptr) { return; } bool released = - static_cast(block->callback)->releaseBlockCopy(block); + static_cast(block->callback)->releaseBlockCopy(block); if (released) { block->callback = nullptr; } } -void nativeApiJsiCallbackTrampoline(ffi_cif*, void* ret, void* args[], +void nativeApiDirectCallbackTrampoline(ffi_cif*, void* ret, void* args[], void* data) { - auto callback = static_cast(data); + auto callback = static_cast(data); if (callback == nullptr) { return; } @@ -776,14 +776,14 @@ void nativeApiJsiCallbackTrampoline(ffi_cif*, void* ret, void* args[], exception.description != nil ? exception.description.UTF8String : nullptr; std::string message = description != nullptr ? description - : "Objective-C exception in native JSI callback."; + : "Objective-C exception in native Direct callback."; if (!recordNativeCallbackException(message)) { @throw; } } } -size_t nativeSizeForType(const NativeApiJsiType& type) { +size_t nativeSizeForType(const NativeApiDirectType& type) { switch (type.kind) { case metagen::mdTypeStruct: if (type.aggregateInfo != nullptr) { @@ -818,7 +818,7 @@ size_t nativeSizeForType(const NativeApiJsiType& type) { return sizeof(void*); } -Value signedInteger64ToJsiValue(Runtime& runtime, int64_t value) { +Value signedInteger64ToDirectValue(Runtime& runtime, int64_t value) { constexpr int64_t maxSafeInteger = 9007199254740991LL; constexpr int64_t minSafeInteger = -9007199254740991LL; if (value >= minSafeInteger && value <= maxSafeInteger) { @@ -827,7 +827,7 @@ Value signedInteger64ToJsiValue(Runtime& runtime, int64_t value) { return BigInt::fromInt64(runtime, value); } -Value unsignedInteger64ToJsiValue(Runtime& runtime, uint64_t value) { +Value unsignedInteger64ToDirectValue(Runtime& runtime, uint64_t value) { constexpr uint64_t maxSafeInteger = 9007199254740991ULL; if (value <= maxSafeInteger) { return static_cast(value); @@ -873,7 +873,7 @@ bool parseBigIntToUintptr(Runtime& runtime, const BigInt& bigint, address); } -bool readJsiBuffer(Runtime& runtime, const Object& object, const uint8_t** data, +bool readDirectBuffer(Runtime& runtime, const Object& object, const uint8_t** data, size_t* byteLength) { if (data == nullptr || byteLength == nullptr) { return false; @@ -936,7 +936,7 @@ size_t alignUp(size_t value, size_t alignment) { return ((value + alignment - 1) / alignment) * alignment; } -ffi_type* ffiTypeForJsiKind(MDTypeKind kind) { +ffi_type* ffiTypeForDirectKind(MDTypeKind kind) { switch (kind) { case metagen::mdTypeChar: return &ffi_type_sint8; @@ -983,23 +983,23 @@ ffi_type* ffiTypeForJsiKind(MDTypeKind kind) { } } -bool isSupportedJsiKind(MDTypeKind kind) { +bool isSupportedDirectKind(MDTypeKind kind) { switch (kind) { default: - return ffiTypeForJsiKind(kind) != nullptr; + return ffiTypeForDirectKind(kind) != nullptr; } } -void skipMetadataJsiTypePayload(MDMetadataReader* metadata, MDSectionOffset* offset, +void skipMetadataDirectTypePayload(MDMetadataReader* metadata, MDSectionOffset* offset, MDTypeKind kind); -void skipMetadataJsiType(MDMetadataReader* metadata, MDSectionOffset* offset) { +void skipMetadataDirectType(MDMetadataReader* metadata, MDSectionOffset* offset) { MDTypeKind kind = stripTypeFlags(metadata->getTypeKind(*offset)); *offset += sizeof(MDTypeKind); - skipMetadataJsiTypePayload(metadata, offset, kind); + skipMetadataDirectTypePayload(metadata, offset, kind); } -void skipMetadataJsiTypePayload(MDMetadataReader* metadata, MDSectionOffset* offset, +void skipMetadataDirectTypePayload(MDMetadataReader* metadata, MDSectionOffset* offset, MDTypeKind kind) { switch (kind) { case metagen::mdTypeClassObject: { @@ -1027,13 +1027,13 @@ void skipMetadataJsiTypePayload(MDMetadataReader* metadata, MDSectionOffset* off case metagen::mdTypeExtVector: case metagen::mdTypeComplex: *offset += sizeof(uint16_t); - skipMetadataJsiType(metadata, offset); + skipMetadataDirectType(metadata, offset); break; case metagen::mdTypeStruct: *offset += sizeof(MDSectionOffset); break; case metagen::mdTypePointer: - skipMetadataJsiType(metadata, offset); + skipMetadataDirectType(metadata, offset); break; case metagen::mdTypeBlock: case metagen::mdTypeFunctionPointer: @@ -1044,14 +1044,14 @@ void skipMetadataJsiTypePayload(MDMetadataReader* metadata, MDSectionOffset* off } } -NativeApiJsiType parseMetadataJsiType(MDMetadataReader* metadata, +NativeApiDirectType parseMetadataDirectType(MDMetadataReader* metadata, MDSectionOffset* offset, - NativeApiJsiBridge* bridge) { + NativeApiDirectBridge* bridge) { MDTypeKind rawKind = metadata->getTypeKind(*offset); MDTypeKind kind = stripTypeFlags(rawKind); *offset += sizeof(MDTypeKind); - NativeApiJsiType type; + NativeApiDirectType type; type.kind = kind; switch (kind) { @@ -1059,9 +1059,9 @@ NativeApiJsiType parseMetadataJsiType(MDMetadataReader* metadata, type.arraySize = metadata->getArraySize(*offset); *offset += sizeof(uint16_t); type.elementType = - std::make_shared( - parseMetadataJsiType(metadata, offset, bridge)); - auto ffiOwner = std::make_shared(); + std::make_shared( + parseMetadataDirectType(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 @@ -1081,9 +1081,9 @@ NativeApiJsiType parseMetadataJsiType(MDMetadataReader* metadata, type.arraySize = metadata->getArraySize(*offset); *offset += sizeof(uint16_t); type.elementType = - std::make_shared( - parseMetadataJsiType(metadata, offset, bridge)); - auto ffiOwner = std::make_shared(); + std::make_shared( + parseMetadataDirectType(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; @@ -1143,8 +1143,8 @@ NativeApiJsiType parseMetadataJsiType(MDMetadataReader* metadata, } case metagen::mdTypePointer: type.elementType = - std::make_shared( - parseMetadataJsiType(metadata, offset, bridge)); + std::make_shared( + parseMetadataDirectType(metadata, offset, bridge)); type.ffiType = &ffi_type_pointer; type.supported = true; return type; @@ -1179,12 +1179,12 @@ NativeApiJsiType parseMetadataJsiType(MDMetadataReader* metadata, break; } - type.ffiType = ffiTypeForJsiKind(kind); - type.supported = type.ffiType != nullptr && isSupportedJsiKind(kind); + type.ffiType = ffiTypeForDirectKind(kind); + type.supported = type.ffiType != nullptr && isSupportedDirectKind(kind); return type; } -std::shared_ptr NativeApiJsiBridge::aggregateInfoFor( +std::shared_ptr NativeApiDirectBridge::aggregateInfoFor( MDSectionOffset aggregateOffset, bool isUnion) { if (metadata_ == nullptr || aggregateOffset == MD_SECTION_OFFSET_NULL) { return nullptr; @@ -1195,14 +1195,14 @@ std::shared_ptr NativeApiJsiBridge::aggregateInfoFor( return cached->second; } - auto info = std::make_shared(); + 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(); + auto ffiOwner = std::make_shared(); ffiOwner->elements.push_back(&ffi_type_pointer); ffiOwner->finalize(); info->ffi = ffiOwner; @@ -1228,18 +1228,18 @@ std::shared_ptr NativeApiJsiBridge::aggregateInfoFor( break; } - NativeApiJsiAggregateField field; + NativeApiDirectAggregateField 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); + field.type = parseMetadataDirectType(metadata_.get(), &offset, this); info->fields.push_back(std::move(field)); } - auto ffiOwner = std::make_shared(); + auto ffiOwner = std::make_shared(); if (isUnion) { ffi_type* largest = &ffi_type_uint8; size_t largestSize = 0; @@ -1267,7 +1267,7 @@ std::shared_ptr NativeApiJsiBridge::aggregateInfoFor( return info; } -ffi_type* ffiTypeForJsiArgument(const NativeApiJsiType& type) { +ffi_type* ffiTypeForDirectArgument(const NativeApiDirectType& type) { switch (type.kind) { case metagen::mdTypeArray: return &ffi_type_pointer; @@ -1276,16 +1276,18 @@ ffi_type* ffiTypeForJsiArgument(const NativeApiJsiType& type) { } } -std::optional parseMetadataJsiSignature( +std::optional parseMetadataDirectSignature( MDMetadataReader* metadata, MDSectionOffset signatureOffset, - unsigned int implicitArgumentCount, NativeApiJsiBridge* bridge, + unsigned int implicitArgumentCount, NativeApiDirectBridge* bridge, bool returnOwned = false) { if (metadata == nullptr || signatureOffset == MD_SECTION_OFFSET_NULL) { return std::nullopt; } - NativeApiJsiSignature signature; + NativeApiDirectSignature signature; signature.implicitArgumentCount = implicitArgumentCount; + signature.signatureHash = metadataSignatureHash(metadata, signatureOffset); + signature.dispatchFlags = returnOwned ? 1 : 0; MDSectionOffset offset = signatureOffset; MDTypeKind returnKind = metadata->getTypeKind(offset); @@ -1294,14 +1296,14 @@ std::optional parseMetadataJsiSignature( (returnKindRaw & static_cast(metagen::mdTypeFlagNext)) != 0; signature.variadic = (returnKindRaw & static_cast(metagen::mdTypeFlagVariadic)) != 0; - signature.returnType = parseMetadataJsiType(metadata, &offset, bridge); + signature.returnType = parseMetadataDirectType(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.argumentTypes.push_back(parseMetadataDirectType(metadata, &offset, bridge)); } signature.ffiTypes.reserve(signature.argumentTypes.size() + @@ -1310,7 +1312,7 @@ std::optional parseMetadataJsiSignature( signature.ffiTypes.push_back(&ffi_type_pointer); } for (const auto& argType : signature.argumentTypes) { - signature.ffiTypes.push_back(ffiTypeForJsiArgument(argType)); + signature.ffiTypes.push_back(ffiTypeForDirectArgument(argType)); } ffi_status status = ffi_prep_cif( @@ -1386,7 +1388,7 @@ std::vector knownObjCAggregateFieldNames( } const NativeApiSymbol* findObjCAggregateSymbol( - NativeApiJsiBridge* bridge, const std::string& name, bool isUnion) { + NativeApiDirectBridge* bridge, const std::string& name, bool isUnion) { if (bridge == nullptr || name.empty()) { return nullptr; } @@ -1424,7 +1426,7 @@ const NativeApiSymbol* findObjCAggregateSymbol( } void applyObjCEncodingSizeAndAlignment(const char* encoding, - NativeApiJsiFfiType* ffiType, + NativeApiDirectFfiType* ffiType, uint16_t* sizeOut = nullptr) { if (encoding == nullptr || ffiType == nullptr) { return; @@ -1445,15 +1447,15 @@ void applyObjCEncodingSizeAndAlignment(const char* encoding, } } -NativeApiJsiType parseObjCEncodedJsiType( - const char* encoding, NativeApiJsiBridge* bridge = nullptr, +NativeApiDirectType parseObjCEncodedDirectType( + const char* encoding, NativeApiDirectBridge* bridge = nullptr, const char** endEncoding = nullptr); -bool unsupportedJsiType(const NativeApiJsiType& type); +bool unsupportedDirectType(const NativeApiDirectType& type); -NativeApiJsiType parseObjCEncodedAggregateJsiType( - const char* encoding, NativeApiJsiBridge* bridge, const char** endEncoding) { - NativeApiJsiType type; +NativeApiDirectType parseObjCEncodedAggregateDirectType( + const char* encoding, NativeApiDirectBridge* bridge, const char** endEncoding) { + NativeApiDirectType type; type.kind = metagen::mdTypeStruct; const bool isUnion = *encoding == '('; @@ -1491,7 +1493,7 @@ NativeApiJsiType parseObjCEncodedAggregateJsiType( return type; } - auto info = std::make_shared(); + auto info = std::make_shared(); info->name = aggregateName; info->isUnion = isUnion; info->offset = MD_SECTION_OFFSET_NULL; @@ -1504,13 +1506,13 @@ NativeApiJsiType parseObjCEncodedAggregateJsiType( size_t maxFieldSize = 0; size_t fieldIndex = 0; while (*cursor != '\0' && *cursor != close) { - NativeApiJsiAggregateField field; + NativeApiDirectAggregateField 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)) { + field.type = parseObjCEncodedDirectType(cursor, bridge, &fieldEnd); + if (fieldEnd == fieldStart || unsupportedDirectType(field.type)) { type.supported = false; type.ffiType = nullptr; if (endEncoding != nullptr) { @@ -1559,7 +1561,7 @@ NativeApiJsiType parseObjCEncodedAggregateJsiType( info->fields[i].name = knownNames[i]; } - auto ffiOwner = std::make_shared(); + auto ffiOwner = std::make_shared(); if (isUnion) { ffi_type* largest = &ffi_type_uint8; size_t largestSize = 0; @@ -1599,9 +1601,9 @@ NativeApiJsiType parseObjCEncodedAggregateJsiType( return type; } -NativeApiJsiType parseObjCEncodedArrayJsiType( - const char* encoding, NativeApiJsiBridge* bridge, const char** endEncoding) { - NativeApiJsiType type; +NativeApiDirectType parseObjCEncodedArrayDirectType( + const char* encoding, NativeApiDirectBridge* bridge, const char** endEncoding) { + NativeApiDirectType type; type.kind = metagen::mdTypeArray; const char* cursor = encoding + 1; @@ -1615,8 +1617,8 @@ NativeApiJsiType parseObjCEncodedArrayJsiType( type.arraySize = count; const char* elementEnd = cursor; - type.elementType = std::make_shared( - parseObjCEncodedJsiType(cursor, bridge, &elementEnd)); + type.elementType = std::make_shared( + parseObjCEncodedDirectType(cursor, bridge, &elementEnd)); cursor = elementEnd; if (*cursor == ']') { cursor++; @@ -1625,7 +1627,7 @@ NativeApiJsiType parseObjCEncodedArrayJsiType( *endEncoding = cursor; } - auto ffiOwner = std::make_shared(); + auto ffiOwner = std::make_shared(); ffi_type* elementFfiType = type.elementType != nullptr && type.elementType->ffiType != nullptr ? type.elementType->ffiType @@ -1645,10 +1647,10 @@ NativeApiJsiType parseObjCEncodedArrayJsiType( return type; } -NativeApiJsiType parseObjCEncodedJsiType( - const char* encoding, NativeApiJsiBridge* bridge, const char** endEncoding) { +NativeApiDirectType parseObjCEncodedDirectType( + const char* encoding, NativeApiDirectBridge* bridge, const char** endEncoding) { encoding = skipObjCTypeQualifiers(encoding); - NativeApiJsiType type; + NativeApiDirectType type; if (encoding == nullptr || *encoding == '\0') { type.kind = metagen::mdTypePointer; @@ -1660,7 +1662,7 @@ NativeApiJsiType parseObjCEncodedJsiType( } auto finishPrimitive = [&](const char* end) { - type.ffiType = ffiTypeForJsiKind(type.kind); + type.ffiType = ffiTypeForDirectKind(type.kind); type.supported = type.ffiType != nullptr; if (endEncoding != nullptr) { *endEncoding = end; @@ -1745,8 +1747,8 @@ NativeApiJsiType parseObjCEncodedJsiType( type.kind = metagen::mdTypePointer; { const char* elementEnd = encoding + 1; - type.elementType = std::make_shared( - parseObjCEncodedJsiType(encoding + 1, bridge, &elementEnd)); + type.elementType = std::make_shared( + parseObjCEncodedDirectType(encoding + 1, bridge, &elementEnd)); type.ffiType = &ffi_type_pointer; type.supported = true; if (elementEnd == encoding + 1 && encoding[1] != '\0') { @@ -1759,9 +1761,9 @@ NativeApiJsiType parseObjCEncodedJsiType( return type; case '{': case '(': - return parseObjCEncodedAggregateJsiType(encoding, bridge, endEncoding); + return parseObjCEncodedAggregateDirectType(encoding, bridge, endEncoding); case '[': - return parseObjCEncodedArrayJsiType(encoding, bridge, endEncoding); + return parseObjCEncodedArrayDirectType(encoding, bridge, endEncoding); case 'b': { type.kind = metagen::mdTypeUInt; const char* cursor = encoding + 1; @@ -1781,17 +1783,17 @@ NativeApiJsiType parseObjCEncodedJsiType( return finishPrimitive(encoding + 1); } -std::optional parseObjCMethodJsiSignature( - Method method, NativeApiJsiBridge* bridge = nullptr) { +std::optional parseObjCMethodDirectSignature( + Method method, NativeApiDirectBridge* bridge = nullptr) { if (method == nullptr) { return std::nullopt; } - NativeApiJsiSignature signature; + NativeApiDirectSignature signature; signature.implicitArgumentCount = 2; char* returnEncoding = method_copyReturnType(method); - signature.returnType = parseObjCEncodedJsiType(returnEncoding, bridge); + signature.returnType = parseObjCEncodedDirectType(returnEncoding, bridge); if (returnEncoding != nullptr) { free(returnEncoding); } @@ -1799,7 +1801,7 @@ std::optional parseObjCMethodJsiSignature( 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)); + signature.argumentTypes.push_back(parseObjCEncodedDirectType(argEncoding, bridge)); if (argEncoding != nullptr) { free(argEncoding); } @@ -1809,7 +1811,7 @@ std::optional parseObjCMethodJsiSignature( 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)); + signature.ffiTypes.push_back(ffiTypeForDirectArgument(argType)); } ffi_status status = ffi_prep_cif( @@ -1822,7 +1824,7 @@ std::optional parseObjCMethodJsiSignature( return signature; } -bool prepareJsiMethodSignature(NativeApiJsiSignature* signature) { +bool prepareDirectMethodSignature(NativeApiDirectSignature* signature) { if (signature == nullptr) { return false; } @@ -1832,7 +1834,7 @@ bool prepareJsiMethodSignature(NativeApiJsiSignature* signature) { 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); + ffi_type* ffiType = ffiTypeForDirectArgument(argType); if (ffiType == nullptr) { signature->prepared = false; return false; @@ -1849,8 +1851,8 @@ bool prepareJsiMethodSignature(NativeApiJsiSignature* signature) { return signature->prepared; } -bool reconcileObjCMethodRuntimeSignature(NativeApiJsiSignature* signature, - const NativeApiJsiSignature& runtime) { +bool reconcileObjCMethodRuntimeSignature(NativeApiDirectSignature* signature, + const NativeApiDirectSignature& runtime) { if (signature == nullptr || signature->argumentTypes.size() != runtime.argumentTypes.size()) { return false; @@ -1858,8 +1860,8 @@ bool reconcileObjCMethodRuntimeSignature(NativeApiJsiSignature* signature, bool changed = false; for (size_t i = 0; i < signature->argumentTypes.size(); i++) { - NativeApiJsiType& metadataType = signature->argumentTypes[i]; - const NativeApiJsiType& runtimeType = runtime.argumentTypes[i]; + NativeApiDirectType& metadataType = signature->argumentTypes[i]; + const NativeApiDirectType& runtimeType = runtime.argumentTypes[i]; if (runtimeType.kind == metagen::mdTypeBlock && metadataType.kind == metagen::mdTypeFunctionPointer) { metadataType.kind = metagen::mdTypeBlock; @@ -1869,10 +1871,10 @@ bool reconcileObjCMethodRuntimeSignature(NativeApiJsiSignature* signature, } } - return !changed || prepareJsiMethodSignature(signature); + return !changed || prepareDirectMethodSignature(signature); } -bool unsupportedJsiType(const NativeApiJsiType& type) { +bool unsupportedDirectType(const NativeApiDirectType& type) { if (type.kind == metagen::mdTypeStruct && type.aggregateInfo != nullptr && type.aggregateInfo->ffi != nullptr) { return false; @@ -1880,93 +1882,93 @@ bool unsupportedJsiType(const NativeApiJsiType& type) { return !type.supported || type.ffiType == nullptr; } -bool signatureSupportedForJsiCallback(const NativeApiJsiSignature& signature) { +bool signatureSupportedForDirectCallback(const NativeApiDirectSignature& signature) { if (!signature.prepared || signature.variadic || - unsupportedJsiType(signature.returnType)) { + unsupportedDirectType(signature.returnType)) { return false; } for (const auto& argType : signature.argumentTypes) { - if (unsupportedJsiType(argType)) { + if (unsupportedDirectType(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) { +std::shared_ptr createDirectCallback( + Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiDirectType& type, Function function, bool block, + NativeApiDirectCallbackThreadPolicy threadPolicy = + NativeApiDirectCallbackThreadPolicy::Default) { if (bridge == nullptr || bridge->metadata() == nullptr || type.signatureOffset == MD_SECTION_OFFSET_NULL) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Native callback metadata is unavailable."); } - auto parsed = parseMetadataJsiSignature( + auto parsed = parseMetadataDirectSignature( 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."); + if (!parsed || !signatureSupportedForDirectCallback(*parsed)) { + throw JSError( + runtime, "Native callback signature is not supported by direct engine."); } auto signature = - std::make_shared(std::move(*parsed)); - auto callback = std::make_shared( + 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); + bridge->retainDirectLifetime(callback); } return callback; } -std::shared_ptr createJsiMethodCallback( - Runtime& runtime, const std::shared_ptr& bridge, +std::shared_ptr createDirectMethodCallback( + 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( + throw JSError( runtime, "Native method callback metadata is unavailable."); } - auto parsed = parseMetadataJsiSignature( + auto parsed = parseMetadataDirectSignature( 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."); + if (!parsed || !signatureSupportedForDirectCallback(*parsed)) { + throw JSError( + runtime, "Native method callback signature is not supported by direct engine."); } parsed->selectorName = selectorName; auto signature = - std::make_shared(std::move(*parsed)); - auto threadPolicy = readJsiCallbackThreadPolicy(runtime, function); - auto callback = std::make_shared( + std::make_shared(std::move(*parsed)); + auto threadPolicy = readDirectCallbackThreadPolicy(runtime, function); + auto callback = std::make_shared( runtime, bridge, std::move(signature), std::move(function), false, threadPolicy, true); - bridge->retainJsiLifetime(callback); + bridge->retainDirectLifetime(callback); return callback; } -std::shared_ptr createJsiMethodCallback( - Runtime& runtime, const std::shared_ptr& bridge, - const std::string& selectorName, NativeApiJsiSignature signature, +std::shared_ptr createDirectMethodCallback( + Runtime& runtime, const std::shared_ptr& bridge, + const std::string& selectorName, NativeApiDirectSignature 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."); + prepareDirectMethodSignature(&signature); + if (!signatureSupportedForDirectCallback(signature)) { + throw JSError( + runtime, "Native method callback signature is not supported by direct engine."); } auto sharedSignature = - std::make_shared(std::move(signature)); - auto threadPolicy = readJsiCallbackThreadPolicy(runtime, function); - auto callback = std::make_shared( + std::make_shared(std::move(signature)); + auto threadPolicy = readDirectCallbackThreadPolicy(runtime, function); + auto callback = std::make_shared( runtime, bridge, std::move(sharedSignature), std::move(function), false, threadPolicy, true); - bridge->retainJsiLifetime(callback); + bridge->retainDirectLifetime(callback); return callback; } diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiClassBuilder.h b/NativeScript/ffi/direct/NativeApiDirectClassBuilder.h similarity index 79% rename from NativeScript/ffi/shared/jsi/NativeApiJsiClassBuilder.h rename to NativeScript/ffi/direct/NativeApiDirectClassBuilder.h index bf4d3b2f..0a59565c 100644 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiClassBuilder.h +++ b/NativeScript/ffi/direct/NativeApiDirectClassBuilder.h @@ -7,72 +7,72 @@ std::string readOptionalStringProperty(Runtime& runtime, const Object& object, return value.isString() ? value.asString(runtime).utf8(runtime) : ""; } -struct NativeApiJsiClassBuilderRegistration { +struct NativeApiDirectClassBuilderRegistration { std::shared_ptr runtimeOwner; Runtime* runtime = nullptr; - std::shared_ptr bridge; + std::shared_ptr bridge; }; -std::mutex gNativeApiJsiClassBuilderMutex; -std::unordered_map - gNativeApiJsiClassBuilders; -struct NativeApiJsiKnownExposedMethod { +std::mutex gNativeApiDirectClassBuilderMutex; +std::unordered_map + gNativeApiDirectClassBuilders; +struct NativeApiDirectKnownExposedMethod { std::string selectorName; - NativeApiJsiSignature signature; + NativeApiDirectSignature signature; }; -std::mutex gNativeApiJsiKnownExposedMethodsMutex; -std::unordered_map - gNativeApiJsiKnownExposedMethods; +std::mutex gNativeApiDirectKnownExposedMethodsMutex; +std::unordered_map + gNativeApiDirectKnownExposedMethods; -void rememberNativeApiJsiClassBuilder( - Runtime& runtime, const std::shared_ptr& bridge, +void rememberNativeApiDirectClassBuilder( + 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{ + std::lock_guard lock(gNativeApiDirectClassBuilderMutex); + auto runtimeOwner = retainNativeApiDirectRuntime(runtime); + gNativeApiDirectClassBuilders[cls] = NativeApiDirectClassBuilderRegistration{ .runtimeOwner = runtimeOwner, .runtime = runtimeOwner.get(), .bridge = bridge, }; } -void rememberNativeApiJsiKnownExposedMethod( - const std::string& selectorName, const NativeApiJsiSignature& signature) { +void rememberNativeApiDirectKnownExposedMethod( + const std::string& selectorName, const NativeApiDirectSignature& signature) { if (selectorName.empty()) { return; } - NativeApiJsiKnownExposedMethod method{ + NativeApiDirectKnownExposedMethod method{ .selectorName = selectorName, .signature = signature, }; - std::lock_guard lock(gNativeApiJsiKnownExposedMethodsMutex); - gNativeApiJsiKnownExposedMethods[selectorName] = method; - gNativeApiJsiKnownExposedMethods[jsifySelector(selectorName.c_str())] = + std::lock_guard lock(gNativeApiDirectKnownExposedMethodsMutex); + gNativeApiDirectKnownExposedMethods[selectorName] = method; + gNativeApiDirectKnownExposedMethods[jsifySelector(selectorName.c_str())] = std::move(method); } -std::optional knownNativeApiJsiExposedMethod( +std::optional knownNativeApiDirectExposedMethod( const std::string& name) { - std::lock_guard lock(gNativeApiJsiKnownExposedMethodsMutex); - auto it = gNativeApiJsiKnownExposedMethods.find(name); - if (it == gNativeApiJsiKnownExposedMethods.end()) { + std::lock_guard lock(gNativeApiDirectKnownExposedMethodsMutex); + auto it = gNativeApiDirectKnownExposedMethods.find(name); + if (it == gNativeApiDirectKnownExposedMethods.end()) { return std::nullopt; } - NativeApiJsiKnownExposedMethod method = it->second; - prepareJsiMethodSignature(&method.signature); + NativeApiDirectKnownExposedMethod method = it->second; + prepareDirectMethodSignature(&method.signature); return method; } -std::optional -findNativeApiJsiClassBuilder(id object) { +std::optional +findNativeApiDirectClassBuilder(id object) { Class cls = object != nil ? object_getClass(object) : Nil; - std::lock_guard lock(gNativeApiJsiClassBuilderMutex); + std::lock_guard lock(gNativeApiDirectClassBuilderMutex); while (cls != Nil) { - auto it = gNativeApiJsiClassBuilders.find(cls); - if (it != gNativeApiJsiClassBuilders.end()) { + auto it = gNativeApiDirectClassBuilders.find(cls); + if (it != gNativeApiDirectClassBuilders.end()) { return it->second; } cls = class_getSuperclass(cls); @@ -80,7 +80,7 @@ findNativeApiJsiClassBuilder(id object) { return std::nullopt; } -const char* nativeApiJsiFastEnumerationEncoding() { +const char* nativeApiDirectFastEnumerationEncoding() { static const char* encoding = nullptr; if (encoding == nullptr) { struct objc_method_description desc = protocol_getMethodDescription( @@ -91,21 +91,21 @@ const char* nativeApiJsiFastEnumerationEncoding() { return encoding; } -NSUInteger nativeApiJsiSymbolIteratorCountByEnumerating( +NSUInteger nativeApiDirectSymbolIteratorCountByEnumerating( id self, SEL, NSFastEnumerationState* state, id __unsafe_unretained stackbuf[], NSUInteger len) { if (len == 0 || state == nullptr || stackbuf == nullptr) { return 0; } - auto registration = findNativeApiJsiClassBuilder(self); + auto registration = findNativeApiDirectClassBuilder(self); if (!registration || registration->runtime == nullptr || registration->bridge == nullptr) { return 0; } Runtime& runtime = *registration->runtime; - NativeApiJsiRuntimeScope runtimeScope(runtime); + NativeApiDirectRuntimeScope runtimeScope(runtime); auto bridge = registration->bridge; try { Value receiver = makeNativeObjectValue(runtime, bridge, self, false); @@ -170,8 +170,8 @@ NSUInteger nativeApiJsiSymbolIteratorCountByEnumerating( } Value value = nextObject.getProperty(runtime, "value"); - NativeApiJsiArgumentFrame frame(1); - id nativeValue = objectFromJsiValue(runtime, bridge, value, frame, false); + NativeApiDirectArgumentFrame frame(1); + id nativeValue = objectFromDirectValue(runtime, bridge, value, frame, false); if (nativeValue != nil) { [nativeValue retain]; [nativeValue autorelease]; @@ -190,7 +190,7 @@ NSUInteger nativeApiJsiSymbolIteratorCountByEnumerating( } NativeApiSymbol runtimeSymbolForClass( - const std::shared_ptr& bridge, Class cls) { + const std::shared_ptr& bridge, Class cls) { if (bridge != nullptr) { if (const NativeApiSymbol* symbol = bridge->findClassForRuntimeClass(cls)) { return *symbol; @@ -206,7 +206,7 @@ NativeApiSymbol runtimeSymbolForClass( }; } -std::string nextAvailableJsiClassName(const std::string& requestedName) { +std::string nextAvailableDirectClassName(const std::string& requestedName) { if (requestedName.empty()) { return ""; } @@ -256,8 +256,8 @@ const NativeApiMember* propertyOverrideForName( return fallback; } -void addJsiOverrideMethod(Runtime& runtime, - const std::shared_ptr& bridge, +void addDirectOverrideMethod(Runtime& runtime, + const std::shared_ptr& bridge, Class nativeClass, Class baseClass, const std::string& selectorName, MDSectionOffset signatureOffset, @@ -266,12 +266,12 @@ void addJsiOverrideMethod(Runtime& runtime, return; } - auto callback = createJsiMethodCallback(runtime, bridge, selectorName, + auto callback = createDirectMethodCallback(runtime, bridge, selectorName, signatureOffset, std::move(function), returnOwned); SEL selector = sel_registerName(selectorName.c_str()); std::string metadataEncoding = - objcMethodSignatureForJsiSignature(callback->signature()); + objcMethodSignatureForDirectSignature(callback->signature()); class_replaceMethod(nativeClass, selector, reinterpret_cast(callback->functionPointer()), metadataEncoding.c_str()); @@ -284,7 +284,7 @@ Value getObjectPropertyOrUndefined(Runtime& runtime, const Object& object, : Value::undefined(); } -Class dispatchSuperclassForJsiDerivedReceiver(id receiver, Class fallback) { +Class dispatchSuperclassForDirectDerivedReceiver(id receiver, Class fallback) { if (receiver == nil) { return Nil; } @@ -292,7 +292,7 @@ Class dispatchSuperclassForJsiDerivedReceiver(id receiver, Class fallback) { Class receiverClass = object_getClass(receiver); if (receiverClass == Nil || !class_conformsToProtocol(receiverClass, - @protocol(NativeApiJsiClassBuilderProtocol))) { + @protocol(NativeApiDirectClassBuilderProtocol))) { return Nil; } @@ -316,8 +316,8 @@ std::optional functionForSelector(Runtime& runtime, return value.asObject(runtime).asFunction(runtime); } -std::optional readExposedType( - Runtime& runtime, const std::shared_ptr& bridge, +std::optional readExposedType( + Runtime& runtime, const std::shared_ptr& bridge, const Object& descriptor, const char* propertyName) { if (!descriptor.hasProperty(runtime, propertyName)) { return std::nullopt; @@ -326,10 +326,10 @@ std::optional readExposedType( descriptor.getProperty(runtime, propertyName)); } -std::optional exposedMethodSignature( - Runtime& runtime, const std::shared_ptr& bridge, +std::optional exposedMethodSignature( + Runtime& runtime, const std::shared_ptr& bridge, const std::string& selectorName, const Object& descriptor) { - NativeApiJsiSignature signature; + NativeApiDirectSignature signature; if (auto returnType = readExposedType(runtime, bridge, descriptor, "returns")) { signature.returnType = *returnType; } else { @@ -339,7 +339,7 @@ std::optional exposedMethodSignature( Value paramsValue = getObjectPropertyOrUndefined(runtime, descriptor, "params"); if (!paramsValue.isUndefined() && !paramsValue.isNull()) { if (!paramsValue.isObject() || !paramsValue.asObject(runtime).isArray(runtime)) { - throw facebook::jsi::JSError( + throw JSError( runtime, "exposedMethods params must be an array."); } Array params = paramsValue.asObject(runtime).getArray(runtime); @@ -347,7 +347,7 @@ std::optional exposedMethodSignature( Value typeValue = params.getValueAtIndex(runtime, i); auto type = interopTypeFromValue(runtime, bridge, typeValue); if (!type) { - throw facebook::jsi::JSError( + throw JSError( runtime, "exposedMethods contains an unsupported parameter type."); } signature.argumentTypes.push_back(*type); @@ -355,15 +355,15 @@ std::optional exposedMethodSignature( } if (selectorArgumentCount(selectorName) != signature.argumentTypes.size()) { - throw facebook::jsi::JSError( + throw JSError( runtime, "exposedMethods selector argument count does not match params."); } - prepareJsiMethodSignature(&signature); + prepareDirectMethodSignature(&signature); return signature; } -std::optional runtimeProtocolMethodSignature( +std::optional runtimeProtocolMethodSignature( const char* types) { if (types == nullptr) { return std::nullopt; @@ -375,27 +375,27 @@ std::optional runtimeProtocolMethodSignature( return std::nullopt; } - NativeApiJsiSignature signature; + NativeApiDirectSignature signature; signature.implicitArgumentCount = 2; signature.returnType = - parseObjCEncodedJsiType(methodSignature.methodReturnType); + parseObjCEncodedDirectType(methodSignature.methodReturnType); for (NSUInteger i = 2; i < methodSignature.numberOfArguments; i++) { signature.argumentTypes.push_back( - parseObjCEncodedJsiType([methodSignature getArgumentTypeAtIndex:i])); + parseObjCEncodedDirectType([methodSignature getArgumentTypeAtIndex:i])); } - if (unsupportedJsiType(signature.returnType)) { + if (unsupportedDirectType(signature.returnType)) { return std::nullopt; } for (const auto& argumentType : signature.argumentTypes) { - if (unsupportedJsiType(argumentType)) { + if (unsupportedDirectType(argumentType)) { return std::nullopt; } } return signature; } -std::optional protocolSymbolFromJsiValue( - Runtime& runtime, const std::shared_ptr& bridge, +std::optional protocolSymbolFromDirectValue( + Runtime& runtime, const std::shared_ptr& bridge, const Value& value) { if (value.isString()) { std::string name = value.asString(runtime).utf8(runtime); @@ -434,23 +434,23 @@ std::optional protocolSymbolFromJsiValue( return std::nullopt; } -void addJsiExposedMethod(Runtime& runtime, - const std::shared_ptr& bridge, +void addDirectExposedMethod(Runtime& runtime, + const std::shared_ptr& bridge, Class nativeClass, const std::string& selectorName, - NativeApiJsiSignature signature, Function function) { + NativeApiDirectSignature signature, Function function) { if (selectorName.empty()) { return; } - auto callback = createJsiMethodCallback(runtime, bridge, selectorName, + auto callback = createDirectMethodCallback(runtime, bridge, selectorName, std::move(signature), std::move(function)); - std::string encoding = objcMethodSignatureForJsiSignature(callback->signature()); + std::string encoding = objcMethodSignatureForDirectSignature(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, + Runtime& runtime, const std::shared_ptr& bridge, Class nativeClass, const std::vector& protocols, const std::string& propertyName, Function function) { std::unordered_set visited; @@ -487,7 +487,7 @@ bool addRuntimeProtocolOverrideForName( } auto signature = runtimeProtocolMethodSignature(descriptions[i].types); if (signature) { - addJsiExposedMethod(runtime, bridge, nativeClass, selectorName, + addDirectExposedMethod(runtime, bridge, nativeClass, selectorName, std::move(*signature), std::move(function)); free(descriptions); return true; @@ -519,22 +519,22 @@ Object getOwnPropertyDescriptor(Runtime& runtime, const Object& object, : Object(runtime); } -Value extendNativeApiJsiClass( - Runtime& runtime, const std::shared_ptr& bridge, +Value extendNativeApiDirectClass( + 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( + throw JSError( runtime, "extendClass expects a native class and method object."); } - Class baseClass = classFromJsiValue(runtime, args[0]); + Class baseClass = classFromDirectValue(runtime, args[0]); if (baseClass == Nil) { - throw facebook::jsi::JSError( + throw JSError( runtime, "extendClass can only extend native class constructors."); } if (class_conformsToProtocol(baseClass, - @protocol(NativeApiJsiClassBuilderProtocol))) { - throw facebook::jsi::JSError(runtime, + @protocol(NativeApiDirectClassBuilderProtocol))) { + throw JSError(runtime, "Cannot extend an already extended class."); } @@ -549,15 +549,15 @@ Value extendNativeApiJsiClass( "_Extended_" + std::to_string(rand()); } - std::string className = nextAvailableJsiClassName(requestedName); + std::string className = nextAvailableDirectClassName(requestedName); Class nativeClass = objc_allocateClassPair(baseClass, className.c_str(), 0); if (nativeClass == Nil) { - throw facebook::jsi::JSError(runtime, "Failed to allocate Objective-C class."); + throw JSError(runtime, "Failed to allocate Objective-C class."); } - markNativeApiJsiExtendedClass(nativeClass); - class_addProtocol(nativeClass, @protocol(NativeApiJsiClassBuilderProtocol)); - rememberNativeApiJsiClassBuilder(runtime, bridge, nativeClass); + markNativeApiDirectExtendedClass(nativeClass); + class_addProtocol(nativeClass, @protocol(NativeApiDirectClassBuilderProtocol)); + rememberNativeApiDirectClassBuilder(runtime, bridge, nativeClass); NativeApiSymbol baseSymbol = runtimeSymbolForClass(bridge, baseClass); std::vector extensionMembers = @@ -569,9 +569,9 @@ Value extendNativeApiJsiClass( 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); + Protocol* protocol = protocolFromDirectValue(runtime, protocolValue); std::optional protocolSymbol = - protocolSymbolFromJsiValue(runtime, bridge, protocolValue); + protocolSymbolFromDirectValue(runtime, bridge, protocolValue); if (protocol != nullptr) { optionProtocols.push_back(protocol); class_addProtocol(nativeClass, protocol); @@ -611,7 +611,7 @@ Value extendNativeApiJsiClass( member.signatureOffset == 0) { continue; } - addJsiOverrideMethod( + addDirectOverrideMethod( runtime, bridge, nativeClass, baseClass, member.selectorName, member.signatureOffset, (member.flags & metagen::mdMemberReturnOwned) != 0, @@ -623,8 +623,8 @@ Value extendNativeApiJsiClass( runtime, bridge, nativeClass, optionProtocols, propertyName, value.asObject(runtime).asFunction(runtime)); if (!addedRuntimeProtocolOverride) { - if (auto known = knownNativeApiJsiExposedMethod(propertyName)) { - addJsiExposedMethod(runtime, bridge, nativeClass, + if (auto known = knownNativeApiDirectExposedMethod(propertyName)) { + addDirectExposedMethod(runtime, bridge, nativeClass, known->selectorName, std::move(known->signature), value.asObject(runtime).asFunction(runtime)); @@ -639,7 +639,7 @@ Value extendNativeApiJsiClass( Value getter = descriptor.getProperty(runtime, "get"); if (propertyMember != nullptr && getter.isObject() && getter.asObject(runtime).isFunction(runtime)) { - addJsiOverrideMethod( + addDirectOverrideMethod( runtime, bridge, nativeClass, baseClass, propertyMember->selectorName, propertyMember->signatureOffset, (propertyMember->flags & metagen::mdMemberReturnOwned) != 0, @@ -651,7 +651,7 @@ Value extendNativeApiJsiClass( if (selectorArgumentCount(member.selectorName) != 0) { continue; } - addJsiOverrideMethod( + addDirectOverrideMethod( runtime, bridge, nativeClass, baseClass, member.selectorName, member.signatureOffset, (member.flags & metagen::mdMemberReturnOwned) != 0, @@ -663,7 +663,7 @@ Value extendNativeApiJsiClass( if (propertyMember != nullptr && setter.isObject() && setter.asObject(runtime).isFunction(runtime) && !propertyMember->setterSelectorName.empty()) { - addJsiOverrideMethod(runtime, bridge, nativeClass, baseClass, + addDirectOverrideMethod(runtime, bridge, nativeClass, baseClass, propertyMember->setterSelectorName, propertyMember->setterSignatureOffset, false, setter.asObject(runtime).asFunction(runtime)); @@ -697,8 +697,8 @@ Value extendNativeApiJsiClass( auto signature = exposedMethodSignature( runtime, bridge, selectorName, descriptorValue.asObject(runtime)); if (signature) { - rememberNativeApiJsiKnownExposedMethod(selectorName, *signature); - addJsiExposedMethod(runtime, bridge, nativeClass, selectorName, + rememberNativeApiDirectKnownExposedMethod(selectorName, *signature); + addDirectExposedMethod(runtime, bridge, nativeClass, selectorName, std::move(*signature), std::move(*function)); } } @@ -708,11 +708,11 @@ Value extendNativeApiJsiClass( getObjectPropertyOrUndefined(runtime, options, "__hasIterator"); if (hasIteratorValue.isBool() && hasIteratorValue.getBool()) { class_addProtocol(nativeClass, @protocol(NSFastEnumeration)); - if (const char* encoding = nativeApiJsiFastEnumerationEncoding()) { + if (const char* encoding = nativeApiDirectFastEnumerationEncoding()) { class_replaceMethod( nativeClass, @selector(countByEnumeratingWithState:objects:count:), - reinterpret_cast(nativeApiJsiSymbolIteratorCountByEnumerating), + reinterpret_cast(nativeApiDirectSymbolIteratorCountByEnumerating), encoding); } } @@ -726,23 +726,23 @@ Value extendNativeApiJsiClass( return makeNativeClassValue(runtime, bridge, std::move(newSymbol)); } -Value invokeNativeApiJsiBaseMethod( - Runtime& runtime, const std::shared_ptr& bridge, +Value invokeNativeApiDirectBaseMethod( + 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( + throw JSError( runtime, "__invokeBase expects base class, receiver, and member name."); } - Class baseClass = classFromJsiValue(runtime, args[0]); + Class baseClass = classFromDirectValue(runtime, args[0]); if (baseClass == Nil) { - throw facebook::jsi::JSError(runtime, "__invokeBase base class is invalid."); + throw 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."); + throw JSError(runtime, "__invokeBase receiver is not native."); } id receiver = @@ -759,7 +759,7 @@ Value invokeNativeApiJsiBaseMethod( selectWritablePropertyMember(members, memberName, false)) { if (actualArgc == 0) { Class dispatchClass = - dispatchSuperclassForJsiDerivedReceiver(receiver, baseClass); + dispatchSuperclassForDirectDerivedReceiver(receiver, baseClass); return callObjCSelector(runtime, bridge, receiver, false, propertyMember->selectorName, propertyMember, nullptr, 0, dispatchClass); @@ -767,7 +767,7 @@ Value invokeNativeApiJsiBaseMethod( if (actualArgc == 1 && !propertyMember->setterSelectorName.empty() && !propertyMember->readonly) { Class dispatchClass = - dispatchSuperclassForJsiDerivedReceiver(receiver, baseClass); + dispatchSuperclassForDirectDerivedReceiver(receiver, baseClass); NativeApiMember setterMember = *propertyMember; setterMember.selectorName = propertyMember->setterSelectorName; setterMember.signatureOffset = propertyMember->setterSignatureOffset; @@ -778,12 +778,12 @@ Value invokeNativeApiJsiBaseMethod( } } if (member == nullptr) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Objective-C base selector is not available: " + memberName); } Class dispatchClass = - dispatchSuperclassForJsiDerivedReceiver(receiver, baseClass); + dispatchSuperclassForDirectDerivedReceiver(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/direct/NativeApiDirectConversion.h similarity index 90% rename from NativeScript/ffi/shared/jsi/NativeApiJsiConversion.h rename to NativeScript/ffi/direct/NativeApiDirectConversion.h index 9e37ec0a..6186426f 100644 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiConversion.h +++ b/NativeScript/ffi/direct/NativeApiDirectConversion.h @@ -2,9 +2,9 @@ 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, +id objectFromDirectValue(Runtime& runtime, + const std::shared_ptr& bridge, + const Value& value, NativeApiDirectArgumentFrame& frame, bool mutableString) { if (value.isNull() || value.isUndefined()) { return nil; @@ -32,7 +32,7 @@ id objectFromJsiValue(Runtime& runtime, if (object.isHostObject(runtime)) { return object.getHostObject(runtime)->object(); } - if (Class cls = nativeClassFromJsiObject(runtime, object)) { + if (Class cls = nativeClassFromDirectObject(runtime, object)) { return static_cast(cls); } if (object.isHostObject(runtime)) { @@ -80,14 +80,14 @@ id objectFromJsiValue(Runtime& runtime, .callWithThis(runtime, object, nullptr, 0); if (primitiveValue.isString() || primitiveValue.isBool() || primitiveValue.isNumber()) { - return objectFromJsiValue(runtime, bridge, primitiveValue, frame, + return objectFromDirectValue(runtime, bridge, primitiveValue, frame, mutableString); } } const uint8_t* bytes = nullptr; size_t byteLength = 0; - if (readJsiBuffer(runtime, object, &bytes, &byteLength)) { + if (readDirectBuffer(runtime, object, &bytes, &byteLength)) { NSData* data = [NSData dataWithBytes:bytes length:byteLength]; bridge->rememberRoundTripValue(runtime, data, value); return data; @@ -98,7 +98,7 @@ id objectFromJsiValue(Runtime& runtime, NSMutableArray* nativeArray = [NSMutableArray arrayWithCapacity:array.size(runtime)]; for (size_t i = 0; i < array.size(runtime); i++) { - id element = objectFromJsiValue(runtime, bridge, + id element = objectFromDirectValue(runtime, bridge, array.getValueAtIndex(runtime, i), frame, false); [nativeArray addObject:element != nil ? element : [NSNull null]]; @@ -114,7 +114,7 @@ id objectFromJsiValue(Runtime& runtime, NSMutableArray* nativeArray = [NSMutableArray arrayWithCapacity:length]; for (size_t i = 0; i < length; i++) { std::string key = std::to_string(i); - id element = objectFromJsiValue( + id element = objectFromDirectValue( runtime, bridge, object.getProperty(runtime, key.c_str()), frame, false); [nativeArray addObject:element != nil ? element : [NSNull null]]; @@ -150,10 +150,10 @@ id objectFromJsiValue(Runtime& runtime, if (pair.size(runtime) < 2) { continue; } - id key = objectFromJsiValue(runtime, bridge, + id key = objectFromDirectValue(runtime, bridge, pair.getValueAtIndex(runtime, 0), frame, false); - id nativeValue = objectFromJsiValue(runtime, bridge, + id nativeValue = objectFromDirectValue(runtime, bridge, pair.getValueAtIndex(runtime, 1), frame, false); if (key != nil) { @@ -179,7 +179,7 @@ id objectFromJsiValue(Runtime& runtime, continue; } id nativeValue = - objectFromJsiValue(runtime, bridge, propertyValue, frame, false); + objectFromDirectValue(runtime, bridge, propertyValue, frame, false); NSString* nativeKey = [NSString stringWithUTF8String:key.c_str()]; if (nativeKey != nil) { [dictionary setObject:nativeValue != nil ? nativeValue : [NSNull null] @@ -189,7 +189,7 @@ id objectFromJsiValue(Runtime& runtime, bridge->rememberRoundTripValue(runtime, dictionary, value); return dictionary; } - throw facebook::jsi::JSError(runtime, + throw JSError(runtime, "Value cannot be converted to Objective-C object."); } @@ -281,8 +281,8 @@ void* pointerFromSymbolLikeObject(Runtime& runtime, const Object& object) { return lookupProtocolByNativeName(runtimeName); } -void* pointerFromJsiValue(Runtime& runtime, const Value& value, - NativeApiJsiArgumentFrame& frame) { +void* pointerFromDirectValue(Runtime& runtime, const Value& value, + NativeApiDirectArgumentFrame& frame) { if (value.isNull() || value.isUndefined()) { return nullptr; } @@ -297,7 +297,7 @@ void* pointerFromJsiValue(Runtime& runtime, const Value& value, if (object.isHostObject(runtime)) { return object.getHostObject(runtime)->object(); } - if (Class cls = nativeClassFromJsiObject(runtime, object)) { + if (Class cls = nativeClassFromDirectObject(runtime, object)) { return cls; } if (object.isHostObject(runtime)) { @@ -324,7 +324,7 @@ void* pointerFromJsiValue(Runtime& runtime, const Value& value, } const uint8_t* bytes = nullptr; size_t byteLength = 0; - if (readJsiBuffer(runtime, object, &bytes, &byteLength)) { + if (readDirectBuffer(runtime, object, &bytes, &byteLength)) { return const_cast(bytes); } } @@ -333,7 +333,7 @@ void* pointerFromJsiValue(Runtime& runtime, const Value& value, char* string = strdup(utf8.c_str()); return string; } - throw facebook::jsi::JSError(runtime, "Value cannot be converted to pointer."); + throw JSError(runtime, "Value cannot be converted to pointer."); } bool readPointerLikeValue(Runtime& runtime, const Value& value, void** pointer) { @@ -357,7 +357,7 @@ bool readPointerLikeValue(Runtime& runtime, const Value& value, void** pointer) *pointer = object.getHostObject(runtime)->object(); return true; } - if (Class cls = nativeClassFromJsiObject(runtime, object)) { + if (Class cls = nativeClassFromDirectObject(runtime, object)) { *pointer = cls; return true; } @@ -391,7 +391,7 @@ void writeNumericArgument(Runtime& runtime, const Value& value, void* target, } if (!numericValue->isNumber() && !numericValue->isBool()) { - throw facebook::jsi::JSError(runtime, + throw JSError(runtime, std::string("Expected numeric ") + typeName + " argument."); } @@ -400,18 +400,18 @@ void writeNumericArgument(Runtime& runtime, const Value& value, void* target, *static_cast(target) = static_cast(number); } -void convertJsiArgument(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, +void convertDirectArgument(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiDirectType& type, const Value& value, void* target, - NativeApiJsiArgumentFrame& frame); + NativeApiDirectArgumentFrame& frame); Value convertNativeReturnValue(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* value); + const std::shared_ptr& bridge, + const NativeApiDirectType& type, void* value); -Class classFromJsiValue(Runtime& runtime, const Value& value); -Protocol* protocolFromJsiValue(Runtime& runtime, const Value& value); +Class classFromDirectValue(Runtime& runtime, const Value& value); +Protocol* protocolFromDirectValue(Runtime& runtime, const Value& value); std::optional parseArrayIndexProperty(const std::string& property) { if (property.empty()) { @@ -431,15 +431,15 @@ std::optional parseArrayIndexProperty(const std::string& property) { return index; } -size_t referenceElementStride(const NativeApiJsiType& type) { +size_t referenceElementStride(const NativeApiDirectType& type) { return std::max(nativeSizeForType(type), 1); } void convertAggregateArgument(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, + const std::shared_ptr& bridge, + const NativeApiDirectType& type, const Value& value, void* target, - NativeApiJsiArgumentFrame& frame) { + NativeApiDirectArgumentFrame& frame) { size_t size = nativeSizeForType(type); if (size == 0) { return; @@ -477,7 +477,7 @@ void convertAggregateArgument(Runtime& runtime, const uint8_t* bytes = nullptr; size_t byteLength = 0; - if (readJsiBuffer(runtime, object, &bytes, &byteLength)) { + if (readDirectBuffer(runtime, object, &bytes, &byteLength)) { if (bytes != nullptr) { std::memcpy(target, bytes, std::min(byteLength, size)); } @@ -486,10 +486,10 @@ void convertAggregateArgument(Runtime& runtime, } if (type.aggregateInfo == nullptr) { - throw facebook::jsi::JSError(runtime, "Missing native struct metadata."); + throw JSError(runtime, "Missing native struct metadata."); } if (!value.isObject()) { - throw facebook::jsi::JSError(runtime, "Expected struct descriptor object."); + throw JSError(runtime, "Expected struct descriptor object."); } Object object = value.asObject(runtime); @@ -500,16 +500,16 @@ void convertAggregateArgument(Runtime& runtime, } Value fieldValue = object.getProperty(runtime, field.name.c_str()); void* fieldTarget = static_cast(target) + field.offset; - convertJsiArgument(runtime, bridge, field.type, fieldValue, fieldTarget, + convertDirectArgument(runtime, bridge, field.type, fieldValue, fieldTarget, frame); } } void convertIndexedAggregateArgument(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, + const std::shared_ptr& bridge, + const NativeApiDirectType& type, const Value& value, void* target, - NativeApiJsiArgumentFrame& frame) { + NativeApiDirectArgumentFrame& frame) { size_t size = nativeSizeForType(type); std::memset(target, 0, size); if (value.isNull() || value.isUndefined()) { @@ -518,7 +518,7 @@ void convertIndexedAggregateArgument(Runtime& runtime, if (value.isObject()) { const uint8_t* bytes = nullptr; size_t byteLength = 0; - if (readJsiBuffer(runtime, value.asObject(runtime), &bytes, &byteLength)) { + if (readDirectBuffer(runtime, value.asObject(runtime), &bytes, &byteLength)) { if (bytes != nullptr) { std::memcpy(target, bytes, std::min(byteLength, size)); } @@ -526,28 +526,28 @@ void convertIndexedAggregateArgument(Runtime& runtime, } } if (!value.isObject() || !value.asObject(runtime).isArray(runtime)) { - throw facebook::jsi::JSError(runtime, "Expected array, ArrayBuffer, or typed array."); + 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 facebook::jsi::JSError(runtime, "Invalid native array element type."); + 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); - convertJsiArgument(runtime, bridge, *type.elementType, + convertDirectArgument(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) { +void convertDirectFfiArgument(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiDirectType& type, const Value& value, + void* target, NativeApiDirectArgumentFrame& frame) { if (type.kind != metagen::mdTypeArray) { - convertJsiArgument(runtime, bridge, type, value, target, frame); + convertDirectArgument(runtime, bridge, type, value, target, frame); return; } @@ -558,7 +558,7 @@ void convertJsiFfiArgument(Runtime& runtime, if (!readPointerLikeValue(runtime, value, &pointer)) { const uint8_t* bytes = nullptr; size_t byteLength = 0; - if (readJsiBuffer(runtime, object, &bytes, &byteLength)) { + if (readDirectBuffer(runtime, object, &bytes, &byteLength)) { pointer = const_cast(bytes); } } @@ -576,21 +576,21 @@ void convertJsiFfiArgument(Runtime& runtime, *static_cast(target) = pointer; } -void convertJsiArgument(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, +void convertDirectArgument(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiDirectType& type, const Value& value, void* target, - NativeApiJsiArgumentFrame& frame) { - if (unsupportedJsiType(type)) { - throw facebook::jsi::JSError(runtime, + NativeApiDirectArgumentFrame& frame) { + if (unsupportedDirectType(type)) { + throw JSError(runtime, "This native signature is not supported by " - "the pure JSI bridge yet."); + "the direct engine bridge yet."); } switch (type.kind) { case metagen::mdTypeBool: if (!value.isNumber() && !value.isBool()) { - throw facebook::jsi::JSError(runtime, + throw JSError(runtime, "Expected boolean or numeric argument."); } *static_cast(target) = @@ -611,7 +611,7 @@ void convertJsiArgument(Runtime& runtime, if (value.isString()) { std::string text = value.asString(runtime).utf8(runtime); if (text.size() != 1) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Expected a single-character string."); } *static_cast(target) = @@ -654,7 +654,7 @@ void convertJsiArgument(Runtime& runtime, } const uint8_t* bytes = nullptr; size_t byteLength = 0; - if (readJsiBuffer(runtime, object, &bytes, &byteLength)) { + if (readDirectBuffer(runtime, object, &bytes, &byteLength)) { *static_cast(target) = reinterpret_cast(const_cast(bytes)); break; @@ -674,7 +674,7 @@ void convertJsiArgument(Runtime& runtime, } } if (!value.isString()) { - throw facebook::jsi::JSError(runtime, "Expected string argument."); + throw JSError(runtime, "Expected string argument."); } std::string utf8 = value.asString(runtime).utf8(runtime); char* string = strdup(utf8.c_str()); @@ -687,14 +687,14 @@ void convertJsiArgument(Runtime& runtime, case metagen::mdTypeInstanceObject: case metagen::mdTypeNSStringObject: case metagen::mdTypeNSMutableStringObject: { - id object = objectFromJsiValue( + id object = objectFromDirectValue( runtime, bridge, value, frame, type.kind == metagen::mdTypeNSMutableStringObject); *static_cast(target) = object; break; } case metagen::mdTypeClass: { - *static_cast(target) = classFromJsiValue(runtime, value); + *static_cast(target) = classFromDirectValue(runtime, value); break; } case metagen::mdTypeSelector: { @@ -703,7 +703,7 @@ void convertJsiArgument(Runtime& runtime, break; } if (!value.isString()) { - throw facebook::jsi::JSError(runtime, "Expected selector string."); + throw JSError(runtime, "Expected selector string."); } std::string selectorName = value.asString(runtime).utf8(runtime); *static_cast(target) = sel_registerName(selectorName.c_str()); @@ -734,17 +734,17 @@ void convertJsiArgument(Runtime& runtime, } const uint8_t* bytes = nullptr; size_t byteLength = 0; - if (readJsiBuffer(runtime, object, &bytes, &byteLength)) { + if (readDirectBuffer(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); + *static_cast(target) = pointerFromDirectValue(runtime, value, frame); break; case metagen::mdTypeOpaquePointer: - *static_cast(target) = pointerFromJsiValue(runtime, value, frame); + *static_cast(target) = pointerFromDirectValue(runtime, value, frame); break; case metagen::mdTypeBlock: case metagen::mdTypeFunctionPointer: { @@ -761,9 +761,9 @@ void convertJsiArgument(Runtime& runtime, } } - auto threadPolicy = readJsiCallbackThreadPolicy(runtime, object); + auto threadPolicy = readDirectCallbackThreadPolicy(runtime, object); auto callback = - createJsiCallback(runtime, bridge, type, object.asFunction(runtime), + createDirectCallback(runtime, bridge, type, object.asFunction(runtime), type.kind == metagen::mdTypeBlock, threadPolicy); void* pointer = callback->functionPointer(); if (type.kind == metagen::mdTypeBlock) { @@ -784,7 +784,7 @@ void convertJsiArgument(Runtime& runtime, break; } } - *static_cast(target) = pointerFromJsiValue(runtime, value, frame); + *static_cast(target) = pointerFromDirectValue(runtime, value, frame); break; } case metagen::mdTypeStruct: @@ -798,17 +798,17 @@ void convertJsiArgument(Runtime& runtime, frame); break; default: - throw facebook::jsi::JSError(runtime, "Unsupported JSI argument type."); + throw JSError(runtime, "Unsupported Direct argument type."); } } Value convertNativeReturnValue(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* value) { - if (unsupportedJsiType(type)) { - throw facebook::jsi::JSError(runtime, + const std::shared_ptr& bridge, + const NativeApiDirectType& type, void* value) { + if (unsupportedDirectType(type)) { + throw JSError(runtime, "This native return type is not supported by " - "the pure JSI bridge yet."); + "the direct engine bridge yet."); } switch (type.kind) { @@ -837,10 +837,10 @@ Value convertNativeReturnValue(Runtime& runtime, return static_cast(*static_cast(value)); case metagen::mdTypeSLong: case metagen::mdTypeSInt64: - return signedInteger64ToJsiValue(runtime, *static_cast(value)); + return signedInteger64ToDirectValue(runtime, *static_cast(value)); case metagen::mdTypeULong: case metagen::mdTypeUInt64: - return unsignedInteger64ToJsiValue(runtime, + return unsignedInteger64ToDirectValue(runtime, *static_cast(value)); case metagen::mdTypeFloat: return static_cast(*static_cast(value)); @@ -851,7 +851,7 @@ Value convertNativeReturnValue(Runtime& runtime, if (string == nullptr) { return Value::null(); } - NativeApiJsiType cStringType = + NativeApiDirectType cStringType = primitiveInteropType(metagen::mdTypeChar); return Object::createFromHostObject( runtime, std::make_shared( @@ -1007,15 +1007,15 @@ Value convertNativeReturnValue(Runtime& runtime, return result; } default: - throw facebook::jsi::JSError(runtime, "Unsupported JSI return type."); + throw JSError(runtime, "Unsupported Direct return type."); } } void NativeApiReferenceHostObject::ensureStorage( - Runtime& runtime, NativeApiJsiType type, NativeApiJsiArgumentFrame& frame, + Runtime& runtime, NativeApiDirectType type, NativeApiDirectArgumentFrame& frame, size_t elements) { size_t elementCount = std::max(elements, 1); - NativeApiJsiType storageType = std::move(type); + NativeApiDirectType 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); @@ -1037,7 +1037,7 @@ void NativeApiReferenceHostObject::ensureStorage( if (data_ != nullptr && pendingValue_ != nullptr) { Value pending(runtime, *pendingValue_); - convertJsiArgument(runtime, bridge_, type_, pending, data_, frame); + convertDirectArgument(runtime, bridge_, type_, pending, data_, frame); pendingValue_.reset(); } } @@ -1091,7 +1091,7 @@ void NativeApiReferenceHostObject::set(Runtime& runtime, return; } size_t slotIndex = index.value_or(0); - NativeApiJsiArgumentFrame frame(1); + NativeApiDirectArgumentFrame frame(1); if (data_ == nullptr) { if (slotIndex == 0) { pendingValue_ = std::make_shared(runtime, value); @@ -1102,7 +1102,7 @@ void NativeApiReferenceHostObject::set(Runtime& runtime, pendingValue_.reset(); void* slot = static_cast(data_) + (slotIndex * referenceElementStride(type_)); - convertJsiArgument(runtime, bridge_, type_, value, slot, frame); + convertDirectArgument(runtime, bridge_, type_, value, slot, frame); } Value NativeApiStructObjectHostObject::get(Runtime& runtime, @@ -1126,7 +1126,7 @@ Value NativeApiStructObjectHostObject::get(Runtime& runtime, runtime, PropNameID::forAscii(runtime, "toString"), 0, [info](Runtime& runtime, const Value&, const Value*, size_t) -> Value { return makeString(runtime, - std::string("[NativeApiJsi ") + + std::string("[NativeApiDirect ") + (info != nullptr && info->isUnion ? "Union " : "Struct ") + (info != nullptr ? info->name : "") + "]"); }); @@ -1156,18 +1156,18 @@ void NativeApiStructObjectHostObject::set(Runtime& runtime, const Value& value) { std::string property = name.utf8(runtime); if (info_ == nullptr || data_ == nullptr) { - throw facebook::jsi::JSError(runtime, "Struct is not initialized."); + throw 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, + NativeApiDirectArgumentFrame frame(1); + convertDirectArgument(runtime, bridge_, field.type, value, static_cast(data_) + field.offset, frame); return; } - throw facebook::jsi::JSError(runtime, "No native struct field: " + property); + throw JSError(runtime, "No native struct field: " + property); } std::vector NativeApiStructObjectHostObject::getPropertyNames( @@ -1186,15 +1186,15 @@ std::vector NativeApiStructObjectHostObject::getPropertyNames( return names; } -NativeApiJsiType primitiveInteropType(MDTypeKind kind) { - NativeApiJsiType type; +NativeApiDirectType primitiveInteropType(MDTypeKind kind) { + NativeApiDirectType type; type.kind = kind; - type.ffiType = ffiTypeForJsiKind(kind); + type.ffiType = ffiTypeForDirectKind(kind); type.supported = type.ffiType != nullptr; return type; } -std::optional primitiveInteropTypeFromCode(int32_t code) { +std::optional primitiveInteropTypeFromCode(int32_t code) { MDTypeKind kind = static_cast(code); switch (kind) { case metagen::mdTypeVoid: @@ -1227,8 +1227,8 @@ std::optional primitiveInteropTypeFromCode(int32_t code) { } } -std::optional interopTypeFromValue( - Runtime& runtime, const std::shared_ptr& bridge, +std::optional interopTypeFromValue( + Runtime& runtime, const std::shared_ptr& bridge, const Value& value) { if (value.isNumber()) { return primitiveInteropTypeFromCode(static_cast(value.getNumber())); @@ -1256,7 +1256,7 @@ std::optional interopTypeFromValue( } } - Class descriptorClass = nativeClassFromJsiObject(runtime, object); + Class descriptorClass = nativeClassFromDirectObject(runtime, object); if (descriptorClass == Nil && stringPropertyOrEmpty(runtime, object, "kind") == "class") { descriptorClass = @@ -1268,7 +1268,7 @@ std::optional interopTypeFromValue( if (object.isHostObject(runtime)) { auto structObject = object.getHostObject(runtime); - NativeApiJsiType type; + NativeApiDirectType type; type.kind = metagen::mdTypeStruct; type.aggregateInfo = structObject->info(); type.aggregateOffset = type.aggregateInfo != nullptr @@ -1318,7 +1318,7 @@ std::optional interopTypeFromValue( bool isUnion = kindName == "union"; auto info = bridge->aggregateInfoFor( static_cast(offsetValue.getNumber()), isUnion); - NativeApiJsiType type; + NativeApiDirectType type; type.kind = metagen::mdTypeStruct; type.aggregateInfo = info; type.aggregateOffset = info != nullptr ? info->offset : MD_SECTION_OFFSET_NULL; @@ -1333,7 +1333,7 @@ std::optional interopTypeFromValue( } Value makeAggregateConstructor(Runtime& runtime, - const std::shared_ptr& bridge, + const std::shared_ptr& bridge, const NativeApiSymbol& symbol) { auto info = bridge->aggregateInfoFor(symbol); auto constructor = Function::createFromHostFunction( @@ -1341,12 +1341,12 @@ Value makeAggregateConstructor(Runtime& runtime, [bridge, symbol, info](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { if (info == nullptr) { - throw facebook::jsi::JSError(runtime, + throw JSError(runtime, "Native aggregate metadata is unavailable: " + symbol.name); } - NativeApiJsiType type; + NativeApiDirectType type; type.kind = metagen::mdTypeStruct; type.aggregateInfo = info; type.aggregateOffset = info->offset; @@ -1366,7 +1366,7 @@ Value makeAggregateConstructor(Runtime& runtime, std::vector storage(info->size, 0); if (count > 0) { - NativeApiJsiArgumentFrame frame(1); + NativeApiDirectArgumentFrame frame(1); convertAggregateArgument(runtime, bridge, type, args[0], storage.data(), frame); } @@ -1393,7 +1393,7 @@ Value makeAggregateConstructor(Runtime& runtime, return false; } - NativeApiJsiType type; + NativeApiDirectType type; type.kind = metagen::mdTypeStruct; type.aggregateInfo = info; type.aggregateOffset = info->offset; @@ -1404,10 +1404,10 @@ Value makeAggregateConstructor(Runtime& runtime, std::vector left(info->size, 0); std::vector right(info->size, 0); try { - NativeApiJsiArgumentFrame leftFrame(1); + NativeApiDirectArgumentFrame leftFrame(1); convertAggregateArgument(runtime, bridge, type, args[0], left.data(), leftFrame); - NativeApiJsiArgumentFrame rightFrame(1); + NativeApiDirectArgumentFrame rightFrame(1); convertAggregateArgument(runtime, bridge, type, args[1], right.data(), rightFrame); } catch (const std::exception&) { @@ -1427,7 +1427,7 @@ Value makeAggregateConstructor(Runtime& runtime, } size_t sizeofInteropType(Runtime& runtime, - const std::shared_ptr& bridge, + const std::shared_ptr& bridge, const Value& value) { if (auto type = interopTypeFromValue(runtime, bridge, value)) { return nativeSizeForType(*type); @@ -1438,7 +1438,7 @@ size_t sizeofInteropType(Runtime& runtime, if (object.isHostObject(runtime) || object.isHostObject(runtime) || object.isHostObject(runtime) || - nativeClassFromJsiObject(runtime, object) != Nil) { + nativeClassFromDirectObject(runtime, object) != Nil) { return sizeof(void*); } void* nativePointer = nullptr; @@ -1451,11 +1451,11 @@ size_t sizeofInteropType(Runtime& runtime, } } - throw facebook::jsi::JSError(runtime, "Invalid type for interop.sizeof."); + throw JSError(runtime, "Invalid type for interop.sizeof."); } Object createPointer(Runtime& runtime, - const std::shared_ptr& bridge, + const std::shared_ptr& bridge, void* pointer, bool adopted) { if (!adopted && bridge != nullptr) { Value cached = bridge->findPointerValue(runtime, pointer); @@ -1513,7 +1513,7 @@ void installInteropHasInstance(Runtime& runtime, Function& constructor, } } -Class classFromJsiValue(Runtime& runtime, const Value& value) { +Class classFromDirectValue(Runtime& runtime, const Value& value) { if (value.isString()) { std::string name = value.asString(runtime).utf8(runtime); return objc_lookUpClass(name.c_str()); @@ -1522,7 +1522,7 @@ Class classFromJsiValue(Runtime& runtime, const Value& value) { return Nil; } Object object = value.asObject(runtime); - if (Class cls = nativeClassFromJsiObject(runtime, object)) { + if (Class cls = nativeClassFromDirectObject(runtime, object)) { return cls; } if (stringPropertyOrEmpty(runtime, object, "kind") == "class") { @@ -1537,7 +1537,7 @@ Class classFromJsiValue(Runtime& runtime, const Value& value) { return Nil; } -Protocol* protocolFromJsiValue(Runtime& runtime, const Value& value) { +Protocol* protocolFromDirectValue(Runtime& runtime, const Value& value) { if (value.isString()) { std::string name = value.asString(runtime).utf8(runtime); Protocol* protocol = objc_getProtocol(name.c_str()); @@ -1573,13 +1573,13 @@ Protocol* protocolFromJsiValue(Runtime& runtime, const Value& value) { } Value nameValue = object.getProperty(runtime, "name"); if (nameValue.isString()) { - return protocolFromJsiValue(runtime, nameValue); + return protocolFromDirectValue(runtime, nameValue); } return nullptr; } Object createInteropObject(Runtime& runtime, - const std::shared_ptr& bridge) { + const std::shared_ptr& bridge) { Object interop(runtime); Object types(runtime); auto setType = [&](const char* name, MDTypeKind kind) { @@ -1711,7 +1711,7 @@ Object createInteropObject(Runtime& runtime, uintptr_t address = 0; if (!readAddress(args[0], &address)) { - throw facebook::jsi::JSError(runtime, + throw JSError(runtime, "Pointer expects a numeric address."); } pointer = reinterpret_cast(address); @@ -1732,13 +1732,13 @@ Object createInteropObject(Runtime& runtime, [](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { if (count < 1 || !args[0].isObject()) { - throw facebook::jsi::JSError( + throw JSError( runtime, "FunctionReference expects a function."); } Object object = args[0].asObject(runtime); if (!object.isFunction(runtime)) { - throw facebook::jsi::JSError( + throw JSError( runtime, "FunctionReference expects a function."); } @@ -1768,7 +1768,7 @@ Object createInteropObject(Runtime& runtime, runtime, PropNameID::forAscii(runtime, "Reference"), 2, [bridge](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { - NativeApiJsiType type = primitiveInteropType(metagen::mdTypePointer); + NativeApiDirectType type = primitiveInteropType(metagen::mdTypePointer); bool firstArgumentIsType = false; if (count > 1) { firstArgumentIsType = true; @@ -1779,12 +1779,12 @@ Object createInteropObject(Runtime& runtime, Value kindValue = object.getProperty(runtime, "kind"); firstArgumentIsType = typeCodeValue.isNumber() || object.isFunction(runtime) || - nativeClassFromJsiObject(runtime, object) != Nil || + nativeClassFromDirectObject(runtime, object) != Nil || (kindValue.isString() && (kindValue.asString(runtime).utf8(runtime) == "class" || kindValue.asString(runtime).utf8(runtime) == "protocol")); } - std::optional requestedType = + std::optional requestedType = firstArgumentIsType ? interopTypeFromValue(runtime, bridge, args[0]) : std::nullopt; @@ -1851,8 +1851,8 @@ Object createInteropObject(Runtime& runtime, } ownsData = true; if (count > 1) { - NativeApiJsiArgumentFrame frame(1); - convertJsiArgument(runtime, bridge, type, valueToStore, data, + NativeApiDirectArgumentFrame frame(1); + convertDirectArgument(runtime, bridge, type, valueToStore, data, frame); } } @@ -1885,7 +1885,7 @@ Object createInteropObject(Runtime& runtime, [bridge](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { if (count < 1) { - throw facebook::jsi::JSError(runtime, "sizeof expects a type."); + throw JSError(runtime, "sizeof expects a type."); } return static_cast(sizeofInteropType(runtime, bridge, args[0])); })); @@ -1897,7 +1897,7 @@ Object createInteropObject(Runtime& runtime, [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."); + 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); @@ -1932,11 +1932,11 @@ Object createInteropObject(Runtime& runtime, [](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."); + throw 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."); + throw JSError(runtime, "adopt expects a Pointer."); } object.getHostObject(runtime)->adopt(); return Value(runtime, object); @@ -1967,7 +1967,7 @@ Object createInteropObject(Runtime& runtime, void* data = object.getHostObject(runtime)->data(); if (data == nullptr) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Cannot get handle of empty Reference."); } return createPointer(runtime, bridge, data); @@ -1986,7 +1986,7 @@ Object createInteropObject(Runtime& runtime, object.getHostObject(runtime) ->object()); } - if (Class cls = nativeClassFromJsiObject(runtime, object)) { + if (Class cls = nativeClassFromDirectObject(runtime, object)) { return createPointer(runtime, bridge, cls); } if (object.isHostObject(runtime)) { @@ -2005,7 +2005,7 @@ Object createInteropObject(Runtime& runtime, Value kindValue = object.getProperty(runtime, "kind"); if (kindValue.isString() && kindValue.asString(runtime).utf8(runtime) == "functionReference") { - throw facebook::jsi::JSError( + throw JSError( runtime, "Cannot get handle of uninitialized FunctionReference."); } Value nativeName = object.getProperty(runtime, "nativeName"); @@ -2028,9 +2028,9 @@ Object createInteropObject(Runtime& runtime, if (count < 1 || args[0].isNull() || args[0].isUndefined()) { return Value::null(); } - NativeApiJsiArgumentFrame frame(1); + NativeApiDirectArgumentFrame frame(1); const char* data = - static_cast(pointerFromJsiValue(runtime, args[0], frame)); + static_cast(pointerFromDirectValue(runtime, args[0], frame)); if (data == nullptr) { return Value::null(); } @@ -2050,7 +2050,7 @@ Object createInteropObject(Runtime& runtime, [](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { if (count < 1 || !args[0].isObject()) { - throw facebook::jsi::JSError(runtime, "Invalid data."); + throw JSError(runtime, "Invalid data."); } Object object = args[0].asObject(runtime); if (object.isArrayBuffer(runtime)) { @@ -2064,7 +2064,7 @@ Object createInteropObject(Runtime& runtime, object.getHostObject(runtime)->pointer()); } if (native == nil || ![native isKindOfClass:[NSData class]]) { - throw facebook::jsi::JSError(runtime, "Invalid data."); + throw JSError(runtime, "Invalid data."); } NSData* data = static_cast(native); return ArrayBuffer( @@ -2077,9 +2077,9 @@ Object createInteropObject(Runtime& runtime, Function::createFromHostFunction( runtime, PropNameID::forAscii(runtime, "addMethod"), 2, [](Runtime& runtime, const Value&, const Value*, size_t) -> Value { - throw facebook::jsi::JSError( + throw JSError( runtime, - "interop.addMethod requires the JSI class builder layer."); + "interop.addMethod requires the Direct class builder layer."); })); interop.setProperty( runtime, "addProtocol", @@ -2088,11 +2088,11 @@ Object createInteropObject(Runtime& runtime, [](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { if (count < 2) { - throw facebook::jsi::JSError( + throw JSError( runtime, "interop.addProtocol expects class and protocol."); } - Class cls = classFromJsiValue(runtime, args[0]); - Protocol* protocol = protocolFromJsiValue(runtime, args[1]); + Class cls = classFromDirectValue(runtime, args[0]); + Protocol* protocol = protocolFromDirectValue(runtime, args[1]); if (cls == Nil || protocol == nullptr) { return false; } diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiHostObject.h b/NativeScript/ffi/direct/NativeApiDirectHostObject.h similarity index 94% rename from NativeScript/ffi/shared/jsi/NativeApiJsiHostObject.h rename to NativeScript/ffi/direct/NativeApiDirectHostObject.h index 06b82774..2e5fa179 100644 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiHostObject.h +++ b/NativeScript/ffi/direct/NativeApiDirectHostObject.h @@ -1,6 +1,14 @@ +#ifndef NATIVESCRIPT_NATIVE_API_DIRECT_BACKEND_NAME +#define NATIVESCRIPT_NATIVE_API_DIRECT_BACKEND_NAME "direct" +#endif + +#ifndef NATIVESCRIPT_NATIVE_API_DIRECT_RUNTIME_NAME +#define NATIVESCRIPT_NATIVE_API_DIRECT_RUNTIME_NAME "direct" +#endif + #ifndef NATIVESCRIPT_NATIVE_API_HAS_ENGINE_LAZY_GLOBALS inline bool InstallNativeApiEngineLazyGlobal( - Runtime&, std::shared_ptr, const std::string&, + Runtime&, std::shared_ptr, const std::string&, const std::string&, bool) { return false; } @@ -8,16 +16,16 @@ inline bool InstallNativeApiEngineLazyGlobal( class NativeApiHostObject final : public HostObject { public: - explicit NativeApiHostObject(std::shared_ptr bridge) + 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"); + return makeString(runtime, NATIVESCRIPT_NATIVE_API_DIRECT_RUNTIME_NAME); } if (property == "backend") { - return makeString(runtime, "hermes"); + return makeString(runtime, NATIVESCRIPT_NATIVE_API_DIRECT_BACKEND_NAME); } if (property == "metadata") { return metadataObject(runtime); @@ -50,16 +58,16 @@ class NativeApiHostObject final : public HostObject { [bridge](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { if (count < 1 || !args[0].isObject()) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Fast enumeration expects a native object."); } id object = NativeApiObjectHostObject::nativeObjectFromValue(runtime, args[0]); if (object == nil) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Fast enumeration expects a native object."); } if (![object conformsToProtocol:@protocol(NSFastEnumeration)]) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Object does not conform to NSFastEnumeration."); } return Object::createFromHostObject( @@ -76,21 +84,21 @@ class NativeApiHostObject final : public HostObject { size_t count) -> Value { auto scheduler = bridge->scheduler(); if (scheduler == nullptr) { - throw facebook::jsi::JSError( + throw JSError( runtime, - "NativeApiJsi was installed without a UI scheduler."); + "NativeApiDirect 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( + throw JSError( runtime, "runOnUI expects a function callback."); } Object callbackObject = args[0].asObject(runtime); if (!callbackObject.isFunction(runtime)) { - throw facebook::jsi::JSError( + throw JSError( runtime, "runOnUI expects a function callback."); } callback = std::make_shared( @@ -162,7 +170,7 @@ class NativeApiHostObject final : public HostObject { NSBundle* bundle = [NSBundle bundleWithPath:[NSString stringWithUTF8String:frameworkPath.c_str()]]; if (bundle == nil || ![bundle load]) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Could not load bundle: " + frameworkPath); } return true; @@ -216,7 +224,7 @@ class NativeApiHostObject final : public HostObject { runtime, PropNameID::forAscii(runtime, "__extendClass"), 2, [bridge](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { - return extendNativeApiJsiClass(runtime, bridge, args, count); + return extendNativeApiDirectClass(runtime, bridge, args, count); }); } if (property == "__invokeBase") { @@ -225,7 +233,7 @@ class NativeApiHostObject final : public HostObject { runtime, PropNameID::forAscii(runtime, "__invokeBase"), 3, [bridge](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { - return invokeNativeApiJsiBaseMethod(runtime, bridge, args, count); + return invokeNativeApiDirectBaseMethod(runtime, bridge, args, count); }); } if (property == "__rememberClassWrapper") { @@ -237,7 +245,7 @@ class NativeApiHostObject final : public HostObject { if (count < 2) { return Value::undefined(); } - Class cls = classFromJsiValue(runtime, args[0]); + Class cls = classFromDirectValue(runtime, args[0]); if (cls == Nil) { return Value::undefined(); } @@ -275,7 +283,7 @@ class NativeApiHostObject final : public HostObject { [bridge](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { if (count < 3 || !args[1].isNumber()) { - throw facebook::jsi::JSError( + throw JSError( runtime, "CC_SHA256 expects data, length, and output."); } void* commonCrypto = @@ -288,12 +296,12 @@ class NativeApiHostObject final : public HostObject { symbol = dlsym(commonCrypto, "_CC_SHA256"); } if (symbol == nullptr) { - throw facebook::jsi::JSError(runtime, + throw JSError(runtime, "CC_SHA256 is not available."); } - NativeApiJsiArgumentFrame frame(3); - void* data = pointerFromJsiValue(runtime, args[0], frame); - void* output = pointerFromJsiValue(runtime, args[2], frame); + NativeApiDirectArgumentFrame frame(3); + void* data = pointerFromDirectValue(runtime, args[0], frame); + void* output = pointerFromDirectValue(runtime, args[2], frame); using CC_SHA256_Fn = unsigned char* (*)(const void*, unsigned long, unsigned char*); auto fn = reinterpret_cast(symbol); @@ -556,5 +564,5 @@ class NativeApiHostObject final : public HostObject { return metadata; } - std::shared_ptr bridge_; + std::shared_ptr bridge_; }; diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiHostObjects.h b/NativeScript/ffi/direct/NativeApiDirectHostObjects.h similarity index 94% rename from NativeScript/ffi/shared/jsi/NativeApiJsiHostObjects.h rename to NativeScript/ffi/direct/NativeApiDirectHostObjects.h index a1dbf289..c9f0d2ed 100644 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiHostObjects.h +++ b/NativeScript/ffi/direct/NativeApiDirectHostObjects.h @@ -2,7 +2,7 @@ class NativeApiPointerHostObject final : public HostObject, public std::enable_shared_from_this { public: - NativeApiPointerHostObject(std::shared_ptr bridge, + NativeApiPointerHostObject(std::shared_ptr bridge, void* pointer, std::string kind = "pointer", bool adopted = false) : bridge_(std::move(bridge)), @@ -51,7 +51,7 @@ class NativeApiPointerHostObject final 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."); + throw JSError(runtime, "Unmanaged value has already been consumed."); } id object = static_cast(self->pointer_); self->consumed_ = true; @@ -69,7 +69,7 @@ class NativeApiPointerHostObject final [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."); + throw JSError(runtime, "Pointer offset must be a number."); } intptr_t offset = static_cast(args[0].getNumber()); intptr_t base = reinterpret_cast(pointer); @@ -128,7 +128,7 @@ class NativeApiPointerHostObject final return makeString(runtime, ""); } - return makeString(runtime, "[NativeApiJsi " + kind + " " + + return makeString(runtime, "[NativeApiDirect " + kind + " " + std::string(address) + "]"); }); } @@ -154,7 +154,7 @@ class NativeApiPointerHostObject final } private: - std::shared_ptr bridge_; + std::shared_ptr bridge_; void* pointer_ = nullptr; std::string kind_; bool adopted_ = false; @@ -163,8 +163,8 @@ class NativeApiPointerHostObject final class NativeApiReferenceHostObject final : public HostObject { public: - NativeApiReferenceHostObject(std::shared_ptr bridge, - NativeApiJsiType type, void* data, bool ownsData, + NativeApiReferenceHostObject(std::shared_ptr bridge, + NativeApiDirectType type, void* data, bool ownsData, size_t byteLength = 0, std::shared_ptr pendingValue = nullptr, std::shared_ptr backingValue = nullptr) @@ -184,9 +184,9 @@ class NativeApiReferenceHostObject final : public HostObject { } void* data() const { return data_; } - const NativeApiJsiType& type() const { return type_; } - void ensureStorage(Runtime& runtime, NativeApiJsiType type, - NativeApiJsiArgumentFrame& frame, size_t elements = 1); + const NativeApiDirectType& type() const { return type_; } + void ensureStorage(Runtime& runtime, NativeApiDirectType type, + NativeApiDirectArgumentFrame& frame, size_t elements = 1); Value get(Runtime& runtime, const PropNameID& name) override; void set(Runtime& runtime, const PropNameID& name, const Value& value) override; @@ -200,8 +200,8 @@ class NativeApiReferenceHostObject final : public HostObject { } private: - std::shared_ptr bridge_; - NativeApiJsiType type_; + std::shared_ptr bridge_; + NativeApiDirectType type_; void* data_ = nullptr; bool ownsData_ = false; size_t byteLength_ = 0; @@ -212,8 +212,8 @@ class NativeApiReferenceHostObject final : public HostObject { class NativeApiStructObjectHostObject final : public HostObject { public: NativeApiStructObjectHostObject( - std::shared_ptr bridge, - std::shared_ptr info, + 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) @@ -238,7 +238,7 @@ class NativeApiStructObjectHostObject final : public HostObject { } void* data() const { return data_; } - std::shared_ptr info() const { return info_; } + std::shared_ptr info() const { return info_; } std::shared_ptr> storageOwner() const { return ownedData_; } @@ -249,8 +249,8 @@ class NativeApiStructObjectHostObject final : public HostObject { std::vector getPropertyNames(Runtime& runtime) override; private: - std::shared_ptr bridge_; - std::shared_ptr info_; + std::shared_ptr bridge_; + std::shared_ptr info_; std::shared_ptr> ownedData_; std::shared_ptr backingValue_; void* data_ = nullptr; @@ -260,7 +260,7 @@ class NativeApiStructObjectHostObject final : public HostObject { class NativeApiFastEnumerationIteratorHostObject final : public HostObject { public: NativeApiFastEnumerationIteratorHostObject( - std::shared_ptr bridge, id collection) + std::shared_ptr bridge, id collection) : bridge_(std::move(bridge)), collection_(collection) { [(id)collection_ retain]; } @@ -309,14 +309,14 @@ class NativeApiFastEnumerationIteratorHostObject final : public HostObject { } id value = state_.itemsPtr[stackIndex_++]; - NativeApiJsiType valueType = nativeObjectReturnTypeForClass(object_getClass(value)); + NativeApiDirectType 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_; + std::shared_ptr bridge_; id collection_ = nil; NSFastEnumerationState state_ = {}; id __unsafe_unretained stack_[16] = {}; @@ -326,7 +326,7 @@ class NativeApiFastEnumerationIteratorHostObject final : public HostObject { }; NativeApiSymbol nativeApiSymbolForRuntimeClass( - const std::shared_ptr& bridge, Class cls) { + 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)) { @@ -352,7 +352,7 @@ NativeApiSymbol nativeApiSymbolForRuntimeClass( class NativeApiSuperHostObject final : public HostObject { public: - NativeApiSuperHostObject(std::shared_ptr bridge, + NativeApiSuperHostObject(std::shared_ptr bridge, id receiver, Class dispatchClass) : bridge_(std::move(bridge)), receiver_(receiver), @@ -378,7 +378,7 @@ class NativeApiSuperHostObject final : public HostObject { return Function::createFromHostFunction( runtime, PropNameID::forAscii(runtime, "toString"), 0, [](Runtime& runtime, const Value&, const Value*, size_t) -> Value { - return makeString(runtime, "[NativeApiJsiSuper]"); + return makeString(runtime, "[NativeApiDirectSuper]"); }); } if (receiver_ == nil || dispatchClass_ == Nil) { @@ -411,13 +411,13 @@ class NativeApiSuperHostObject final : public HostObject { const NativeApiSymbol* symbol = bridge->findClassForRuntimeClass(dispatchClass); if (symbol == nullptr) { - throw facebook::jsi::JSError( + 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 facebook::jsi::JSError( + throw JSError( runtime, "Objective-C super selector is not available: " + memberName); } @@ -450,7 +450,7 @@ class NativeApiSuperHostObject final : public HostObject { 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."); + throw JSError(runtime, "Cannot set property on nil super."); } if (const NativeApiSymbol* symbol = @@ -460,7 +460,7 @@ class NativeApiSuperHostObject final : public HostObject { selectWritablePropertyMember(members, property, false)) { if (propertyMember->readonly || propertyMember->setterSelectorName.empty()) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Attempted to assign to readonly property."); } NativeApiMember setterMember = *propertyMember; @@ -483,7 +483,7 @@ class NativeApiSuperHostObject final : public HostObject { return; } - throw facebook::jsi::JSError(runtime, + throw JSError(runtime, "No writable native super property: " + property); } @@ -496,7 +496,7 @@ class NativeApiSuperHostObject final : public HostObject { } private: - std::shared_ptr bridge_; + std::shared_ptr bridge_; id receiver_ = nil; Class dispatchClass_ = Nil; }; @@ -505,7 +505,7 @@ class NativeApiObjectHostObject final : public HostObject, public std::enable_shared_from_this { public: - NativeApiObjectHostObject(std::shared_ptr bridge, + NativeApiObjectHostObject(std::shared_ptr bridge, id object, bool ownsObject) : bridge_(std::move(bridge)), object_(object), ownsObject_(ownsObject) { if (object_ != nil && !ownsObject_) { @@ -550,7 +550,7 @@ class NativeApiObjectHostObject final size_t count, Class dispatchSuperClass = Nil) { id receiver = object_; if (receiver == nil) { - throw facebook::jsi::JSError(runtime, + throw JSError(runtime, "Cannot send Objective-C selector to nil."); } @@ -673,7 +673,7 @@ class NativeApiObjectHostObject final 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."); + throw JSError(runtime, "Unmanaged value has already been consumed."); } id object = self->object_; @@ -714,7 +714,7 @@ class NativeApiObjectHostObject final size_t) -> Value { if (object == nil || ![object conformsToProtocol:@protocol(NSFastEnumeration)]) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Object does not conform to NSFastEnumeration."); } return Object::createFromHostObject( @@ -747,7 +747,7 @@ class NativeApiObjectHostObject final selectorName, nullptr, args, count); } } - throw facebook::jsi::JSError( + throw JSError( runtime, "NSColor RGB initializer is not available."); }); } @@ -764,7 +764,7 @@ class NativeApiObjectHostObject final [bridge, timerClass](Runtime& runtime, const Value&, const Value* args, size_t count) -> Value { if (count < 6) { - throw facebook::jsi::JSError( + throw JSError( runtime, "NSTimer initializer expects six arguments."); } return callObjCSelector( @@ -836,7 +836,7 @@ class NativeApiObjectHostObject final return Value::undefined(); } id element = [array objectAtIndex:*index]; - NativeApiJsiType elementType = nativeObjectReturnType(); + NativeApiDirectType elementType = nativeObjectReturnType(); return convertNativeReturnValue(runtime, bridge_, elementType, &element); } } @@ -881,13 +881,13 @@ class NativeApiObjectHostObject final const NativeApiSymbol* symbol = bridge->findClassForRuntimeClass(object_getClass(object)); if (symbol == nullptr) { - throw facebook::jsi::JSError( + throw 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( + throw JSError( runtime, "Objective-C selector is not available: " + memberName); } @@ -933,7 +933,7 @@ class NativeApiObjectHostObject final if (key != nil) { id value = [static_cast(object_) objectForKey:key]; if (value != nil) { - NativeApiJsiType valueType = nativeObjectReturnType(); + NativeApiDirectType valueType = nativeObjectReturnType(); return convertNativeReturnValue(runtime, bridge_, valueType, &value); } } @@ -946,7 +946,7 @@ class NativeApiObjectHostObject final 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."); + throw JSError(runtime, "Cannot set property on nil object."); } if (const NativeApiSymbol* symbol = @@ -955,7 +955,7 @@ class NativeApiObjectHostObject final if (const NativeApiMember* propertyMember = selectWritablePropertyMember(members, property, false)) { if (propertyMember->readonly) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Attempted to assign to readonly property."); } NativeApiMember setterMember = *propertyMember; @@ -998,7 +998,7 @@ class NativeApiObjectHostObject final } private: - std::shared_ptr bridge_; + std::shared_ptr bridge_; id object_ = nil; bool ownsObject_ = false; bool consumed_ = false; @@ -1006,7 +1006,7 @@ class NativeApiObjectHostObject final class NativeApiClassHostObject final : public HostObject { public: - NativeApiClassHostObject(std::shared_ptr bridge, + NativeApiClassHostObject(std::shared_ptr bridge, NativeApiSymbol symbol) : bridge_(std::move(bridge)), symbol_(std::move(symbol)) {} @@ -1078,7 +1078,7 @@ class NativeApiClassHostObject final : public HostObject { [symbol = symbol_](Runtime& runtime, const Value&, const Value*, size_t) -> Value { return makeString(runtime, - "[NativeApiJsiClass " + symbol.name + "]"); + "[NativeApiDirectClass " + symbol.name + "]"); }); } if (property == "construct" || property == "alloc" || property == "new") { @@ -1090,7 +1090,7 @@ class NativeApiClassHostObject final : public HostObject { const Value* args, size_t count) -> Value { Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); if (cls == nil) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Objective-C class is not available: " + symbol.name); } @@ -1127,14 +1127,14 @@ class NativeApiClassHostObject final : public HostObject { if (property == "new") { if (count != 0) { - throw facebook::jsi::JSError( + throw 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( + throw JSError( runtime, "alloc does not take arguments; call invoke on the " "allocated object for an explicit init selector."); } @@ -1155,7 +1155,7 @@ class NativeApiClassHostObject final : public HostObject { readStringArg(runtime, args, count, 0, "selector"); Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); if (cls == nil) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Objective-C class is not available: " + symbol.name); } return callObjCSelector(runtime, bridge, static_cast(cls), true, @@ -1171,7 +1171,7 @@ class NativeApiClassHostObject final : public HostObject { auto symbol = symbol_; Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); if (cls == nil) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Objective-C class is not available: " + symbol.name); } SEL selector = sel_getUid(propertyMember->selectorName.c_str()); @@ -1193,13 +1193,13 @@ class NativeApiClassHostObject final : public HostObject { size_t count) -> Value { Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); if (cls == nil) { - throw facebook::jsi::JSError( + throw 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( + throw JSError( runtime, "Objective-C selector is not available: " + memberName); } @@ -1239,7 +1239,7 @@ class NativeApiClassHostObject final : public HostObject { std::string property = name.utf8(runtime); Class cls = objc_lookUpClass(symbol_.runtimeName.c_str()); if (cls == nil) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Objective-C class is not available: " + symbol_.name); } @@ -1247,7 +1247,7 @@ class NativeApiClassHostObject final : public HostObject { if (const NativeApiMember* propertyMember = selectPropertyMember(members, property, true)) { if (propertyMember->readonly) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Attempted to assign to readonly property."); } NativeApiMember setterMember = *propertyMember; @@ -1268,7 +1268,7 @@ class NativeApiClassHostObject final : public HostObject { return; } - throw facebook::jsi::JSError(runtime, + throw JSError(runtime, "No writable native property: " + property); } @@ -1290,12 +1290,12 @@ class NativeApiClassHostObject final : public HostObject { } private: - std::shared_ptr bridge_; + std::shared_ptr bridge_; NativeApiSymbol symbol_; }; Value makeNativeObjectValue(Runtime& runtime, - const std::shared_ptr& bridge, + const std::shared_ptr& bridge, id object, bool ownsObject) { if (object == nil) { return Value::null(); @@ -1424,7 +1424,7 @@ Value globalNativeSymbolValue(Runtime& runtime, const NativeApiSymbol& symbol, } Value makeNativeClassValue(Runtime& runtime, - const std::shared_ptr& bridge, + const std::shared_ptr& bridge, NativeApiSymbol symbol) { Class cls = objc_lookUpClass(symbol.runtimeName.c_str()); Value cachedClass = bridge->findClassValue(runtime, cls); @@ -1457,7 +1457,7 @@ Protocol* lookupProtocolByNativeName(const std::string& name) { class NativeApiProtocolHostObject final : public HostObject { public: - NativeApiProtocolHostObject(std::shared_ptr bridge, + NativeApiProtocolHostObject(std::shared_ptr bridge, NativeApiSymbol symbol) : bridge_(std::move(bridge)), symbol_(std::move(symbol)) {} @@ -1514,7 +1514,7 @@ class NativeApiProtocolHostObject final : public HostObject { runtime, PropNameID::forAscii(runtime, "toString"), 0, [symbol](Runtime& runtime, const Value&, const Value*, size_t) -> Value { return makeString(runtime, - "[NativeApiJsiProtocol " + symbol.name + "]"); + "[NativeApiDirectProtocol " + symbol.name + "]"); }); } const auto& members = bridge_->membersForProtocol(symbol_); @@ -1625,7 +1625,7 @@ class NativeApiProtocolHostObject final : public HostObject { } if (receiver == nil) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Protocol member requires a native receiver."); } return callObjCSelector(runtime, bridge, receiver, receiverIsClass, @@ -1654,7 +1654,7 @@ class NativeApiProtocolHostObject final : public HostObject { } if (receiver == nil) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Protocol property requires a native receiver."); } return callObjCSelector(runtime, bridge, receiver, receiverIsClass, @@ -1685,11 +1685,11 @@ class NativeApiProtocolHostObject final : public HostObject { } if (receiver == nil) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Protocol property requires a native receiver."); } if (count < 1) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Protocol property setter expects a value."); } @@ -1726,12 +1726,12 @@ class NativeApiProtocolHostObject final : public HostObject { } } - std::shared_ptr bridge_; + std::shared_ptr bridge_; NativeApiSymbol symbol_; }; Value makeNativeProtocolValue(Runtime& runtime, - const std::shared_ptr& bridge, + const std::shared_ptr& bridge, NativeApiSymbol symbol) { Value globalValue = globalNativeSymbolValue(runtime, symbol, "protocol"); if (!globalValue.isUndefined()) { @@ -1742,7 +1742,7 @@ Value makeNativeProtocolValue(Runtime& runtime, std::make_shared(bridge, std::move(symbol))); } -Class nativeClassFromJsiObject(Runtime& runtime, const Object& object) { +Class nativeClassFromDirectObject(Runtime& runtime, const Object& object) { if (object.isHostObject(runtime)) { return object.getHostObject(runtime)->nativeClass(); } diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiInstall.h b/NativeScript/ffi/direct/NativeApiDirectInstall.h similarity index 98% rename from NativeScript/ffi/shared/jsi/NativeApiJsiInstall.h rename to NativeScript/ffi/direct/NativeApiDirectInstall.h index 6e711d5b..fab2e9fd 100644 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiInstall.h +++ b/NativeScript/ffi/direct/NativeApiDirectInstall.h @@ -1,10 +1,10 @@ -Object CreateNativeApiJSI(Runtime& runtime, const NativeApiJsiConfig& config) { - auto bridge = std::make_shared(config); +Object CreateNativeApiDirect(Runtime& runtime, const NativeApiDirectConfig& config) { + auto bridge = std::make_shared(config); return Object::createFromHostObject( runtime, std::make_shared(std::move(bridge))); } -void NativeApiJsiWriteSmokeStage(const char* stage) { +void NativeApiDirectWriteSmokeStage(const char* stage) { const char* enabled = getenv("NATIVESCRIPT_RN_TURBO_SMOKE_MARKER"); if (enabled == nullptr || enabled[0] == '\0') { return; @@ -88,9 +88,9 @@ std::string jsStringLiteral(const char* value) { return result; } -void InstallNativeApiJsiGlobalSymbols(Runtime& runtime, const char* globalName) { - NativeApiJsiWriteSmokeStage("jsi:globals:before-eval"); - static const char* GlobalInstaller = R"JSI_GLOBALS( +void InstallNativeApiDirectGlobalSymbols(Runtime& runtime, const char* globalName) { + NativeApiDirectWriteSmokeStage("direct:globals:before-eval"); + static const char* GlobalInstaller = R"Direct_GLOBALS( (function(nativeApiGlobalName) { 'use strict'; var api = globalThis[nativeApiGlobalName]; @@ -1638,38 +1638,38 @@ void InstallNativeApiJsiGlobalSymbols(Runtime& runtime, const char* globalName) } catch (_) { } }) -)JSI_GLOBALS"; +)Direct_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"); + "NativeApiDirectGlobals.js"); + NativeApiDirectWriteSmokeStage("direct:globals:after-eval"); } -void InstallNativeApiJSI(Runtime& runtime, const NativeApiJsiConfig& config) { +void InstallNativeApiDirect(Runtime& runtime, const NativeApiDirectConfig& config) { const char* globalName = config.globalName != nullptr && config.globalName[0] != '\0' ? config.globalName : "__nativeScriptNativeApi"; - NativeApiJsiWriteSmokeStage("jsi:create-api"); - Object api = CreateNativeApiJSI(runtime, config); + NativeApiDirectWriteSmokeStage("direct:create-api"); + Object api = CreateNativeApiDirect(runtime, config); Object global = runtime.global(); - NativeApiJsiWriteSmokeStage("jsi:set-global"); + NativeApiDirectWriteSmokeStage("direct:set-global"); global.setProperty(runtime, globalName, api); - NativeApiJsiWriteSmokeStage("jsi:set-interop"); + NativeApiDirectWriteSmokeStage("direct: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); + NativeApiDirectWriteSmokeStage("direct:install-globals"); + InstallNativeApiDirectGlobalSymbols(runtime, globalName); } else { - NativeApiJsiWriteSmokeStage("jsi:install-aggregate-globals"); + NativeApiDirectWriteSmokeStage("direct:install-aggregate-globals"); InstallAggregateGlobals(runtime, api, "protocolNames"); } - NativeApiJsiWriteSmokeStage("jsi:installed"); + NativeApiDirectWriteSmokeStage("direct:installed"); } diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiInvocation.h b/NativeScript/ffi/direct/NativeApiDirectInvocation.h similarity index 71% rename from NativeScript/ffi/shared/jsi/NativeApiJsiInvocation.h rename to NativeScript/ffi/direct/NativeApiDirectInvocation.h index fe45f29f..abeaa2ea 100644 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiInvocation.h +++ b/NativeScript/ffi/direct/NativeApiDirectInvocation.h @@ -114,7 +114,7 @@ Value enumToObject(Runtime& runtime, MDMetadataReader* metadata, } Value constantToValue(Runtime& runtime, - const std::shared_ptr& bridge, + const std::shared_ptr& bridge, const NativeApiSymbol& symbol) { MDMetadataReader* metadata = bridge->metadata(); if (metadata == nullptr || symbol.offset == MD_SECTION_OFFSET_NULL) { @@ -141,7 +141,7 @@ Value constantToValue(Runtime& runtime, return Value::undefined(); } - NativeApiJsiType stringObjectType; + NativeApiDirectType stringObjectType; stringObjectType.kind = metagen::mdTypeNSStringObject; stringObjectType.ffiType = &ffi_type_pointer; stringObjectType.supported = true; @@ -153,10 +153,10 @@ Value constantToValue(Runtime& runtime, } 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: " + + NativeApiDirectType type = parseMetadataDirectType(metadata, &typeOffset, bridge.get()); + if (unsupportedDirectType(type)) { + throw JSError( + runtime, "Native constant type is not supported by direct engine: " + symbol.name); } @@ -167,59 +167,68 @@ Value constantToValue(Runtime& runtime, 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); +void prepareDirectArgument(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiDirectType& type, const Value& arg, + size_t index, NativeApiDirectArgumentFrame& frame) { + ffi_type* ffiType = ffiTypeForDirectArgument(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); + convertDirectFfiArgument(runtime, bridge, type, arg, target, frame); } -void prepareJsiArguments(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiSignature& signature, +void prepareDirectArguments(Runtime& runtime, + const std::shared_ptr& bridge, + const NativeApiDirectSignature& signature, const Value* args, size_t count, - NativeApiJsiArgumentFrame& frame) { + NativeApiDirectArgumentFrame& frame) { if (count != signature.argumentTypes.size()) { - throw facebook::jsi::JSError( + 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++) { - prepareJsiArgument(runtime, bridge, signature.argumentTypes[i], args[i], i, + prepareDirectArgument(runtime, bridge, signature.argumentTypes[i], args[i], i, frame); } } +inline uint64_t dispatchIdForDirectSignature( + const NativeApiDirectSignature& signature, SignatureCallKind kind) { + if (signature.signatureHash == 0) { + return 0; + } + return composeSignatureDispatchId(signature.signatureHash, kind, + signature.dispatchFlags); +} + Value callNativeFunctionPointer( - Runtime& runtime, const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* pointer, bool block, const Value* args, + Runtime& runtime, const std::shared_ptr& bridge, + const NativeApiDirectType& type, void* pointer, bool block, const Value* args, size_t count) { if (pointer == nullptr) { - throw facebook::jsi::JSError(runtime, "Native function pointer is null."); + throw JSError(runtime, "Native function pointer is null."); } if (bridge == nullptr || bridge->metadata() == nullptr || type.signatureOffset == MD_SECTION_OFFSET_NULL) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Native function pointer metadata is unavailable."); } - auto signature = parseMetadataJsiSignature( + auto signature = parseMetadataDirectSignature( bridge->metadata(), type.signatureOffset, block ? 1 : 0, bridge.get()); if (!signature || !signature->prepared || signature->variadic || - unsupportedJsiType(signature->returnType)) { - throw facebook::jsi::JSError( + unsupportedDirectType(signature->returnType)) { + throw JSError( runtime, - "Native function pointer signature is not supported by pure JSI."); + "Native function pointer signature is not supported by direct engine."); } - NativeApiJsiArgumentFrame frame(signature->argumentTypes.size()); - prepareJsiArguments(runtime, bridge, *signature, args, count, frame); + NativeApiDirectArgumentFrame frame(signature->argumentTypes.size()); + prepareDirectArguments(runtime, bridge, *signature, args, count, frame); std::vector values; if (block) { @@ -232,9 +241,9 @@ Value callNativeFunctionPointer( void* callable = pointer; if (block) { - auto literal = static_cast(pointer); + auto literal = static_cast(pointer); if (literal == nullptr || literal->invoke == nullptr) { - throw facebook::jsi::JSError(runtime, "Native block invoke pointer is null."); + throw JSError(runtime, "Native block invoke pointer is null."); } callable = literal->invoke; } @@ -242,6 +251,21 @@ Value callNativeFunctionPointer( std::vector returnStorage( std::max(nativeSizeForType(signature->returnType), sizeof(void*)), 0); performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + if (block) { + auto preparedInvoker = lookupBlockPreparedInvoker( + dispatchIdForDirectSignature(*signature, SignatureCallKind::BlockInvoke)); + if (preparedInvoker != nullptr) { + preparedInvoker(callable, values.data(), returnStorage.data()); + return; + } + } else { + auto preparedInvoker = lookupCFunctionPreparedInvoker( + dispatchIdForDirectSignature(*signature, SignatureCallKind::CFunction)); + if (preparedInvoker != nullptr) { + preparedInvoker(callable, frame.values(), returnStorage.data()); + return; + } + } ffi_call(&signature->cif, FFI_FN(callable), returnStorage.data(), block ? values.data() : frame.values()); }); @@ -251,10 +275,10 @@ Value callNativeFunctionPointer( } Value wrapNativeFunctionPointer(Runtime& runtime, - const std::shared_ptr& bridge, - const NativeApiJsiType& type, void* pointer, + const std::shared_ptr& bridge, + const NativeApiDirectType& type, void* pointer, bool block) { - const char* functionName = block ? "NativeApiJsiBlock" : "NativeApiJsiFunctionPointer"; + const char* functionName = block ? "NativeApiDirectBlock" : "NativeApiDirectFunctionPointer"; auto function = Function::createFromHostFunction( runtime, PropNameID::forAscii(runtime, functionName), 0, [bridge, type, pointer, block](Runtime& runtime, const Value&, @@ -284,7 +308,7 @@ Value wrapNativeFunctionPointer(Runtime& runtime, char address[32] = {}; snprintf(address, sizeof(address), "%p", pointer); return makeString(runtime, - std::string("[NativeApiJsi ") + + std::string("[NativeApiDirect ") + (block ? "Block " : "FunctionPointer ") + address + "]"); })); @@ -292,17 +316,17 @@ Value wrapNativeFunctionPointer(Runtime& runtime, } Value callCFunction(Runtime& runtime, - const std::shared_ptr& bridge, + 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."); + throw JSError(runtime, "Native metadata is not loaded."); } void* fnptr = dlsym(bridge->selfDl(), symbol.name.c_str()); if (fnptr == nullptr) { - throw facebook::jsi::JSError(runtime, + throw JSError(runtime, "Native function is not available: " + symbol.name); } @@ -310,19 +334,19 @@ Value callCFunction(Runtime& runtime, MDSectionOffset signatureOffset = metadata->signaturesOffset + metadata->getOffset(symbol.offset + sizeof(MDSectionOffset)); - auto signature = parseMetadataJsiSignature( + auto signature = parseMetadataDirectSignature( 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: " + + unsupportedDirectType(signature->returnType)) { + throw JSError( + runtime, "Native function signature is not supported by direct engine: " + symbol.name); } - NativeApiJsiArgumentFrame frame(signature->argumentTypes.size()); - prepareJsiArguments(runtime, bridge, *signature, args, count, frame); + NativeApiDirectArgumentFrame frame(signature->argumentTypes.size()); + prepareDirectArguments(runtime, bridge, *signature, args, count, frame); if (symbol.name == "NSApplicationMain" || symbol.name == "UIApplicationMain") { @@ -334,8 +358,14 @@ Value callCFunction(Runtime& runtime, bool dispatchingNativeCallToUI = shouldDispatchNativeCallToUI(); bool retainedReturn = false; performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { - ffi_call(&signature->cif, FFI_FN(fnptr), returnStorage.data(), - frame.values()); + auto preparedInvoker = lookupCFunctionPreparedInvoker( + dispatchIdForDirectSignature(*signature, SignatureCallKind::CFunction)); + if (preparedInvoker != nullptr) { + preparedInvoker(fnptr, frame.values(), returnStorage.data()); + } else { + ffi_call(&signature->cif, FFI_FN(fnptr), returnStorage.data(), + frame.values()); + } if (dispatchingNativeCallToUI && !signature->returnType.returnOwned && isObjectiveCObjectType(signature->returnType)) { @@ -347,7 +377,7 @@ Value callCFunction(Runtime& runtime, } }); - NativeApiJsiType returnType = signature->returnType; + NativeApiDirectType returnType = signature->returnType; if (retainedReturn) { returnType.returnOwned = true; } @@ -361,14 +391,14 @@ Value callCFunction(Runtime& runtime, returnStorage.data()); } -bool signatureSupportedForJsiInvocation( - const std::optional& signature) { +bool signatureSupportedForDirectInvocation( + const std::optional& signature) { if (!signature || !signature->prepared || signature->variadic || - unsupportedJsiType(signature->returnType)) { + unsupportedDirectType(signature->returnType)) { return false; } for (const auto& argType : signature->argumentTypes) { - if (unsupportedJsiType(argType)) { + if (unsupportedDirectType(argType)) { return false; } } @@ -376,14 +406,14 @@ bool signatureSupportedForJsiInvocation( } Value callObjCSelector(Runtime& runtime, - const std::shared_ptr& bridge, + 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, + throw JSError(runtime, "Cannot send Objective-C selector to nil."); } @@ -395,44 +425,44 @@ Value callObjCSelector(Runtime& runtime, : class_getInstanceMethod(lookupClass, selector); if (method == nullptr && (dispatchSuperClass != Nil || ![receiver respondsToSelector:selector])) { - throw facebook::jsi::JSError(runtime, + throw JSError(runtime, "Objective-C selector is not available: " + selectorName); } - std::optional signature; - std::optional runtimeSignature; + std::optional signature; + std::optional runtimeSignature; if (member != nullptr && member->signatureOffset != MD_SECTION_OFFSET_NULL && member->signatureOffset != 0) { - signature = parseMetadataJsiSignature( + signature = parseMetadataDirectSignature( bridge->metadata(), member->signatureOffset, 2, bridge.get(), (member->flags & metagen::mdMemberReturnOwned) != 0); } if (method != nullptr) { - runtimeSignature = parseObjCMethodJsiSignature(method, bridge.get()); + runtimeSignature = parseObjCMethodDirectSignature(method, bridge.get()); } - if (signatureSupportedForJsiInvocation(signature) && - signatureSupportedForJsiInvocation(runtimeSignature)) { + if (signatureSupportedForDirectInvocation(signature) && + signatureSupportedForDirectInvocation(runtimeSignature)) { reconcileObjCMethodRuntimeSignature(&*signature, *runtimeSignature); } - if (!signatureSupportedForJsiInvocation(signature) && runtimeSignature) { + if (!signatureSupportedForDirectInvocation(signature) && runtimeSignature) { signature = std::move(runtimeSignature); } - if (!signatureSupportedForJsiInvocation(signature)) { - throw facebook::jsi::JSError( - runtime, "Objective-C signature is not supported by pure JSI: " + + if (!signatureSupportedForDirectInvocation(signature)) { + throw JSError( + runtime, "Objective-C signature is not supported by direct engine: " + selectorName); } signature->selectorName = selectorName; - NativeApiJsiArgumentFrame frame(signature->argumentTypes.size()); - const bool isNSErrorOutMethod = isNSErrorOutJsiMethodSignature(*signature); + NativeApiDirectArgumentFrame frame(signature->argumentTypes.size()); + const bool isNSErrorOutMethod = isNSErrorOutDirectMethodSignature(*signature); if (isNSErrorOutMethod) { size_t expected = signature->argumentTypes.size(); if (count > expected || count + 1 < expected) { - throw facebook::jsi::JSError( + throw JSError( runtime, "Actual arguments count: \"" + std::to_string(count) + "\". Expected: \"" + std::to_string(expected) + "\"."); } @@ -443,7 +473,7 @@ Value callObjCSelector(Runtime& runtime, NSError* implicitNSError = nil; if (hasImplicitNSErrorOutArg) { for (size_t i = 0; i < count; i++) { - prepareJsiArgument(runtime, bridge, signature->argumentTypes[i], args[i], i, + prepareDirectArgument(runtime, bridge, signature->argumentTypes[i], args[i], i, frame); } @@ -452,7 +482,7 @@ Value callObjCSelector(Runtime& runtime, NSError** implicitNSErrorOutArg = &implicitNSError; *static_cast(target) = implicitNSErrorOutArg; } else { - prepareJsiArguments(runtime, bridge, *signature, args, count, frame); + prepareDirectArguments(runtime, bridge, *signature, args, count, frame); } std::vector values; @@ -474,21 +504,31 @@ Value callObjCSelector(Runtime& runtime, bool dispatchingNativeCallToUI = shouldDispatchNativeCallToUI(); bool retainedReturn = false; performNativeInvocation(runtime, bridge->nativeInvocationInvoker(), [&]() { + auto preparedInvoker = + dispatchSuperClass == Nil + ? lookupObjCPreparedInvoker(dispatchIdForDirectSignature( + *signature, SignatureCallKind::ObjCMethod)) + : nullptr; + 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 = 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()); + 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()); + 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)) { @@ -500,7 +540,7 @@ Value callObjCSelector(Runtime& runtime, } }); - NativeApiJsiType returnType = signature->returnType; + NativeApiDirectType returnType = signature->returnType; if ((selectorName == "valueForKey:" || selectorName == "valueForKeyPath:") && isObjectiveCObjectType(returnType)) { returnType.kind = metagen::mdTypeAnyObject; @@ -510,7 +550,7 @@ Value callObjCSelector(Runtime& runtime, } if (hasImplicitNSErrorOutArg && implicitNSError != nil) { const char* errorMessage = [[implicitNSError description] UTF8String]; - throw facebook::jsi::JSError( + throw JSError( runtime, errorMessage != nullptr ? errorMessage : "Unknown NSError"); } return convertNativeReturnValue(runtime, bridge, returnType, diff --git a/NativeScript/ffi/direct/NativeApiDirectSignatureDispatch.h b/NativeScript/ffi/direct/NativeApiDirectSignatureDispatch.h new file mode 100644 index 00000000..44e49a0e --- /dev/null +++ b/NativeScript/ffi/direct/NativeApiDirectSignatureDispatch.h @@ -0,0 +1,85 @@ +#ifndef NATIVESCRIPT_FFI_DIRECT_NATIVE_API_DIRECT_SIGNATURE_DISPATCH_H +#define NATIVESCRIPT_FFI_DIRECT_NATIVE_API_DIRECT_SIGNATURE_DISPATCH_H + +#include "SignatureDispatchCore.h" + +#ifndef NS_GSD_BACKEND_DIRECT_PREPARED +#define NS_GSD_BACKEND_DIRECT_PREPARED 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_NAPI +#define NS_GSD_BACKEND_NAPI 0 +#endif + +#ifndef NS_GSD_BACKEND_ENGINE_DIRECT +#define NS_GSD_BACKEND_ENGINE_DIRECT 0 +#endif + +#ifndef NS_HAS_GENERATED_SIGNATURE_DISPATCH +#define NS_HAS_GENERATED_SIGNATURE_DISPATCH 0 +#endif + +#if defined(__has_include) +#if __has_include("GeneratedSignatureDispatch.inc") +#include "GeneratedSignatureDispatch.inc" +#endif +#endif + +#if !NS_HAS_GENERATED_SIGNATURE_DISPATCH +namespace nativescript { +inline constexpr ObjCDispatchEntry kGeneratedObjCDispatchEntries[] = { + {0, nullptr}}; +inline constexpr CFunctionDispatchEntry kGeneratedCFunctionDispatchEntries[] = { + {0, nullptr}}; +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); +} + +} // namespace nativescript + +#endif // NATIVESCRIPT_FFI_DIRECT_NATIVE_API_DIRECT_SIGNATURE_DISPATCH_H diff --git a/NativeScript/ffi/hermes/jsi/NativeApiJsi.h b/NativeScript/ffi/hermes/NativeApiJsi.h similarity index 100% rename from NativeScript/ffi/hermes/jsi/NativeApiJsi.h rename to NativeScript/ffi/hermes/NativeApiJsi.h diff --git a/NativeScript/ffi/hermes/jsi/NativeApiJsi.mm b/NativeScript/ffi/hermes/NativeApiJsi.mm similarity index 63% rename from NativeScript/ffi/hermes/jsi/NativeApiJsi.mm rename to NativeScript/ffi/hermes/NativeApiJsi.mm index 70a80bbf..6d13a382 100644 --- a/NativeScript/ffi/hermes/jsi/NativeApiJsi.mm +++ b/NativeScript/ffi/hermes/NativeApiJsi.mm @@ -32,8 +32,9 @@ #include "Metadata.h" #include "MetadataReader.h" #include "ffi.h" +#include "NativeApiJsiSignatureDispatch.h" -@protocol NativeApiJsiClassBuilderProtocol +@protocol NativeApiDirectClassBuilderProtocol @end #ifdef EMBED_METADATA_SIZE @@ -55,24 +56,38 @@ @protocol NativeApiJsiClassBuilderProtocol using facebook::jsi::String; using facebook::jsi::StringBuffer; using facebook::jsi::Value; +using facebook::jsi::JSError; + +using NativeApiDirectConfig = NativeApiJsiConfig; +using NativeApiDirectScheduler = NativeApiJsiScheduler; 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" +#define NATIVESCRIPT_NATIVE_API_DIRECT_RUNTIME_NAME "jsi" +#define NATIVESCRIPT_NATIVE_API_DIRECT_BACKEND_NAME "hermes" +#include "ffi/direct/NativeApiDirectBridge.h" +#include "ffi/direct/NativeApiDirectHostObjects.h" +#include "ffi/direct/NativeApiDirectCallbacks.h" +#include "ffi/direct/NativeApiDirectConversion.h" +#include "ffi/direct/NativeApiDirectInvocation.h" +#include "ffi/direct/NativeApiDirectClassBuilder.h" +#include "ffi/direct/NativeApiDirectHostObject.h" // clang-format on } // namespace -#include "jsi/NativeApiJsiInstall.h" +#include "ffi/direct/NativeApiDirectInstall.h" + +Object CreateNativeApiJSI(Runtime& runtime, const NativeApiJsiConfig& config) { + return CreateNativeApiDirect(runtime, config); +} + +void InstallNativeApiJSI(Runtime& runtime, const NativeApiJsiConfig& config) { + InstallNativeApiDirect(runtime, config); +} } // namespace nativescript diff --git a/NativeScript/ffi/hermes/jsi/NativeApiJsiReactNative.h b/NativeScript/ffi/hermes/NativeApiJsiReactNative.h similarity index 100% rename from NativeScript/ffi/hermes/jsi/NativeApiJsiReactNative.h rename to NativeScript/ffi/hermes/NativeApiJsiReactNative.h diff --git a/NativeScript/ffi/hermes/NativeApiJsiSignatureDispatch.h b/NativeScript/ffi/hermes/NativeApiJsiSignatureDispatch.h new file mode 100644 index 00000000..e1e393ab --- /dev/null +++ b/NativeScript/ffi/hermes/NativeApiJsiSignatureDispatch.h @@ -0,0 +1,91 @@ +#ifndef NS_FFI_HERMES_NATIVE_API_JSI_SIGNATURE_DISPATCH_H +#define NS_FFI_HERMES_NATIVE_API_JSI_SIGNATURE_DISPATCH_H + +#include + +#include "SignatureDispatchCore.h" + +#ifndef NS_GSD_BACKEND_HERMES +#define NS_GSD_BACKEND_HERMES 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_NAPI +#define NS_GSD_BACKEND_NAPI 0 +#endif + +#ifndef NS_GSD_BACKEND_ENGINE_DIRECT +#define NS_GSD_BACKEND_ENGINE_DIRECT 0 +#endif + +#ifndef NS_GSD_BACKEND_DIRECT_PREPARED +#define NS_GSD_BACKEND_DIRECT_PREPARED 0 +#endif + +#ifndef NS_GSD_BACKEND_HERMES_EXPERIMENTAL_DIRECT_RETURN +#define NS_GSD_BACKEND_HERMES_EXPERIMENTAL_DIRECT_RETURN 0 +#endif + +#ifndef NS_HAS_GENERATED_SIGNATURE_DISPATCH +#define NS_HAS_GENERATED_SIGNATURE_DISPATCH 0 +#endif + +#if defined(__has_include) +#if __has_include("GeneratedSignatureDispatch.inc") +#include "GeneratedSignatureDispatch.inc" +#endif +#endif + +#if !NS_HAS_GENERATED_SIGNATURE_DISPATCH +namespace nativescript { +inline constexpr ObjCDispatchEntry kGeneratedObjCDispatchEntries[] = { + {0, nullptr}}; +inline constexpr CFunctionDispatchEntry kGeneratedCFunctionDispatchEntries[] = { + {0, nullptr}}; +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); +} + +} // namespace nativescript + +#endif // NS_FFI_HERMES_NATIVE_API_JSI_SIGNATURE_DISPATCH_H diff --git a/NativeScript/ffi/hermes/README.md b/NativeScript/ffi/hermes/README.md new file mode 100644 index 00000000..8dc34fbb --- /dev/null +++ b/NativeScript/ffi/hermes/README.md @@ -0,0 +1,22 @@ +# 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 shared direct bridge core. +- `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 direct native invocation. + +The reusable bridge implementation lives under `../direct` with `NativeApiDirect` +names. V8, JSC, QuickJS, and Hermes all compile that direct bridge against their +own runtime facade; only Hermes exposes the real `facebook::jsi` API. + +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/hermes/jsi/README.md b/NativeScript/ffi/hermes/jsi/README.md deleted file mode 100644 index ca07222b..00000000 --- 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/jsc/NativeApiJSC.h b/NativeScript/ffi/jsc/NativeApiJSC.h index 0bf60c96..d1bdde0b 100644 --- a/NativeScript/ffi/jsc/NativeApiJSC.h +++ b/NativeScript/ffi/jsc/NativeApiJSC.h @@ -1,7 +1,7 @@ #ifndef NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_H #define NATIVESCRIPT_FFI_JSC_NATIVE_API_JSC_H -#include "ffi/shared/direct/NativeApiDirect.h" +#include "ffi/direct/NativeApiDirect.h" #include namespace nativescript { diff --git a/NativeScript/ffi/jsc/NativeApiJSC.mm b/NativeScript/ffi/jsc/NativeApiJSC.mm index b1a6fa39..781e07c4 100644 --- a/NativeScript/ffi/jsc/NativeApiJSC.mm +++ b/NativeScript/ffi/jsc/NativeApiJSC.mm @@ -3,59 +3,59 @@ #ifdef TARGET_ENGINE_JSC #include "NativeApiJSCRuntime.h" +#include "ffi/direct/NativeApiDirectSignatureDispatch.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 nativescript::direct::Array; +using nativescript::direct::ArrayBuffer; +using nativescript::direct::BigInt; +using nativescript::direct::Function; +using nativescript::direct::HostObject; +using nativescript::direct::MutableBuffer; +using nativescript::direct::Object; +using nativescript::direct::PropNameID; +using nativescript::direct::Runtime; +using nativescript::direct::String; +using nativescript::direct::StringBuffer; +using nativescript::direct::Value; +using nativescript::direct::JSError; using metagen::MDMemberFlag; using metagen::MDMetadataReader; using metagen::MDSectionOffset; using metagen::MDTypeKind; // clang-format off -#include "jsi/NativeApiJsiBridge.h" -#include "jsi/NativeApiJsiHostObjects.h" +#define NATIVESCRIPT_NATIVE_API_DIRECT_BACKEND_NAME "jsc" +#include "ffi/direct/NativeApiDirectBridge.h" +#include "ffi/direct/NativeApiDirectHostObjects.h" // clang-format on #define NATIVESCRIPT_NATIVE_API_RETAIN_RUNTIME 1 -std::shared_ptr retainNativeApiJsiRuntime(Runtime& runtime) { +std::shared_ptr retainNativeApiDirectRuntime(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" +#include "ffi/direct/NativeApiDirectCallbacks.h" +#include "ffi/direct/NativeApiDirectConversion.h" +#include "ffi/direct/NativeApiDirectInvocation.h" +#include "ffi/direct/NativeApiDirectClassBuilder.h" +#include "ffi/direct/NativeApiDirectHostObject.h" // clang-format on } // namespace -#include "jsi/NativeApiJsiInstall.h" +#include "ffi/direct/NativeApiDirectInstall.h" void InstallNativeApiJSC(JSGlobalContextRef context, const NativeApiJSCConfig& config) { if (context == nullptr) { return; } Runtime runtime(context); - InstallNativeApiJSI(runtime, config); + InstallNativeApiDirect(runtime, config); } } // namespace nativescript diff --git a/NativeScript/ffi/jsc/NativeApiJSCHostObjects.mm b/NativeScript/ffi/jsc/NativeApiJSCHostObjects.mm index ab2da20a..b7d8c808 100644 --- a/NativeScript/ffi/jsc/NativeApiJSCHostObjects.mm +++ b/NativeScript/ffi/jsc/NativeApiJSCHostObjects.mm @@ -2,8 +2,8 @@ #ifdef TARGET_ENGINE_JSC -namespace facebook { -namespace jsi { +namespace nativescript { +namespace direct { namespace jscdirect { @@ -176,7 +176,7 @@ void setFunctionPrototype(JSGlobalContextRef context, JSObjectRef function) { return Function(Object::fromValueStorage(Value(runtime, function).storage_)); } -} // namespace jsi -} // namespace facebook +} // namespace direct +} // namespace nativescript #endif // TARGET_ENGINE_JSC diff --git a/NativeScript/ffi/jsc/NativeApiJSCRuntime.h b/NativeScript/ffi/jsc/NativeApiJSCRuntime.h index 305399f4..f5c5252f 100644 --- a/NativeScript/ffi/jsc/NativeApiJSCRuntime.h +++ b/NativeScript/ffi/jsc/NativeApiJSCRuntime.h @@ -37,15 +37,15 @@ #include "MetadataReader.h" #include "ffi.h" -@protocol NativeApiJsiClassBuilderProtocol +@protocol NativeApiDirectClassBuilderProtocol @end #ifdef EMBED_METADATA_SIZE extern const unsigned char embedded_metadata[EMBED_METADATA_SIZE]; #endif -namespace facebook { -namespace jsi { +namespace nativescript { +namespace direct { class Runtime; class Value; @@ -831,8 +831,8 @@ class ArrayBuffer : public Object { JSObjectGetArrayBufferBytesPtr(runtime.context(), local(runtime), &exception)); } }; -} // namespace jsi -} // namespace facebook +} // namespace direct +} // namespace nativescript #endif // TARGET_ENGINE_JSC diff --git a/NativeScript/ffi/jsc/NativeApiJSCRuntime.mm b/NativeScript/ffi/jsc/NativeApiJSCRuntime.mm index 3396af69..3f9aaa2a 100644 --- a/NativeScript/ffi/jsc/NativeApiJSCRuntime.mm +++ b/NativeScript/ffi/jsc/NativeApiJSCRuntime.mm @@ -2,8 +2,8 @@ #ifdef TARGET_ENGINE_JSC -namespace facebook { -namespace jsi { +namespace nativescript { +namespace direct { Object Runtime::global() { return Object::fromValueStorage(Value(*this, JSContextGetGlobalObject(context())).storage_); @@ -24,7 +24,7 @@ return Value(*this, result); } -} // namespace jsi -} // namespace facebook +} // namespace direct +} // namespace nativescript #endif // TARGET_ENGINE_JSC diff --git a/NativeScript/ffi/jsc/NativeApiJSCValue.mm b/NativeScript/ffi/jsc/NativeApiJSCValue.mm index ca407156..c5c836e4 100644 --- a/NativeScript/ffi/jsc/NativeApiJSCValue.mm +++ b/NativeScript/ffi/jsc/NativeApiJSCValue.mm @@ -2,8 +2,8 @@ #ifdef TARGET_ENGINE_JSC -namespace facebook { -namespace jsi { +namespace nativescript { +namespace direct { Value HostObject::get(Runtime&, const PropNameID&) { return Value::undefined(); } void HostObject::set(Runtime&, const PropNameID&, const Value&) {} @@ -77,7 +77,7 @@ setProperty(runtime, name, Value(runtime, value)); } -} // namespace jsi -} // namespace facebook +} // namespace direct +} // namespace nativescript #endif // TARGET_ENGINE_JSC diff --git a/NativeScript/ffi/napi/CallbackThreading.h b/NativeScript/ffi/napi/CallbackThreading.h index 63c17990..08ffcea2 100644 --- a/NativeScript/ffi/napi/CallbackThreading.h +++ b/NativeScript/ffi/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.mm b/NativeScript/ffi/napi/Cif.mm index dfe18c8c..9278ba52 100644 --- a/NativeScript/ffi/napi/Cif.mm +++ b/NativeScript/ffi/napi/Cif.mm @@ -9,53 +9,13 @@ #include "Metadata.h" #include "MetadataReader.h" #include "ObjCBridge.h" +#include "SignatureDispatchCore.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; @@ -123,135 +83,6 @@ inline void updateGeneratedNapiDispatchCompatibility(Cif* cif) { } } -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, @@ -497,14 +328,7 @@ bool appendMetadataSignatureHash(MDMetadataReader* reader, MDSectionOffset signa 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; - } - } + signatureHash = metadataSignatureHash(reader, signatureStart); updateGeneratedNapiDispatchCompatibility(this); } diff --git a/NativeScript/ffi/napi/SignatureDispatch.h b/NativeScript/ffi/napi/SignatureDispatch.h index c42a8237..5eda3d55 100644 --- a/NativeScript/ffi/napi/SignatureDispatch.h +++ b/NativeScript/ffi/napi/SignatureDispatch.h @@ -3,47 +3,18 @@ #include -#include -#include -#include - #include "Cif.h" +#include "SignatureDispatchCore.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; @@ -54,29 +25,6 @@ struct CFunctionNapiDispatchEntry { 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 @@ -111,6 +59,10 @@ inline uint64_t composeSignatureDispatchId(uint64_t signatureHash, #define NS_GSD_BACKEND_ENGINE_DIRECT 0 #endif +#ifndef NS_GSD_BACKEND_DIRECT_PREPARED +#define NS_GSD_BACKEND_DIRECT_PREPARED 0 +#endif + #if defined(__has_include) #if __has_include("GeneratedSignatureDispatch.inc") #include "GeneratedSignatureDispatch.inc" @@ -139,42 +91,6 @@ inline constexpr CFunctionNapiDispatchEntry 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; diff --git a/NativeScript/ffi/quickjs/NativeApiQuickJS.h b/NativeScript/ffi/quickjs/NativeApiQuickJS.h index 73c31984..dd6c5e5a 100644 --- a/NativeScript/ffi/quickjs/NativeApiQuickJS.h +++ b/NativeScript/ffi/quickjs/NativeApiQuickJS.h @@ -1,7 +1,7 @@ #ifndef NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_H #define NATIVESCRIPT_FFI_QUICKJS_NATIVE_API_QUICKJS_H -#include "ffi/shared/direct/NativeApiDirect.h" +#include "ffi/direct/NativeApiDirect.h" #include "quickjs.h" namespace nativescript { diff --git a/NativeScript/ffi/quickjs/NativeApiQuickJS.mm b/NativeScript/ffi/quickjs/NativeApiQuickJS.mm index 57988a26..ed6b117e 100644 --- a/NativeScript/ffi/quickjs/NativeApiQuickJS.mm +++ b/NativeScript/ffi/quickjs/NativeApiQuickJS.mm @@ -3,33 +3,33 @@ #ifdef TARGET_ENGINE_QUICKJS #include "NativeApiQuickJSRuntime.h" +#include "ffi/direct/NativeApiDirectSignatureDispatch.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 nativescript::direct::Array; +using nativescript::direct::ArrayBuffer; +using nativescript::direct::BigInt; +using nativescript::direct::Function; +using nativescript::direct::HostObject; +using nativescript::direct::MutableBuffer; +using nativescript::direct::Object; +using nativescript::direct::PropNameID; +using nativescript::direct::Runtime; +using nativescript::direct::String; +using nativescript::direct::StringBuffer; +using nativescript::direct::Value; +using nativescript::direct::JSError; using metagen::MDMemberFlag; using metagen::MDMetadataReader; using metagen::MDSectionOffset; using metagen::MDTypeKind; // clang-format off -#include "jsi/NativeApiJsiBridge.h" +#define NATIVESCRIPT_NATIVE_API_DIRECT_BACKEND_NAME "quickjs" +#include "ffi/direct/NativeApiDirectBridge.h" // clang-format on #define NATIVESCRIPT_NATIVE_API_HAS_ENGINE_LAZY_GLOBALS 1 @@ -63,7 +63,7 @@ static JSValue NativeApiQuickJSLazyGlobalGetter(JSContext* context, JSValueConst return result; } -bool InstallNativeApiEngineLazyGlobal(Runtime& runtime, std::shared_ptr, +bool InstallNativeApiEngineLazyGlobal(Runtime& runtime, std::shared_ptr, const std::string& name, const std::string& kind, bool force) { if (name.empty() || kind.empty()) { @@ -119,33 +119,33 @@ bool InstallNativeApiEngineLazyGlobal(Runtime& runtime, std::shared_ptr retainNativeApiJsiRuntime(Runtime& runtime) { +std::shared_ptr retainNativeApiDirectRuntime(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" +#include "ffi/direct/NativeApiDirectCallbacks.h" +#include "ffi/direct/NativeApiDirectConversion.h" +#include "ffi/direct/NativeApiDirectInvocation.h" +#include "ffi/direct/NativeApiDirectClassBuilder.h" +#include "ffi/direct/NativeApiDirectHostObject.h" // clang-format on } // namespace -#include "jsi/NativeApiJsiInstall.h" +#include "ffi/direct/NativeApiDirectInstall.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); + auto state = direct::quickjsdirect::stateForContext(context); + nativescript::direct::Runtime runtime(state); + direct::quickjsdirect::ensureClasses(runtime); + InstallNativeApiDirect(runtime, config); } } // namespace nativescript diff --git a/NativeScript/ffi/quickjs/NativeApiQuickJSHostObjects.mm b/NativeScript/ffi/quickjs/NativeApiQuickJSHostObjects.mm index 1cd706a3..b2de0a44 100644 --- a/NativeScript/ffi/quickjs/NativeApiQuickJSHostObjects.mm +++ b/NativeScript/ffi/quickjs/NativeApiQuickJSHostObjects.mm @@ -2,8 +2,8 @@ #ifdef TARGET_ENGINE_QUICKJS -namespace facebook { -namespace jsi { +namespace nativescript { +namespace direct { namespace quickjsdirect { @@ -233,7 +233,7 @@ void ensureClasses(Runtime& runtime) { return result; } -} // namespace jsi -} // namespace facebook +} // namespace direct +} // namespace nativescript #endif // TARGET_ENGINE_QUICKJS diff --git a/NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.h b/NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.h index 438a7816..f3da9478 100644 --- a/NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.h +++ b/NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.h @@ -36,15 +36,15 @@ #include "ffi.h" #include "quickjs.h" -@protocol NativeApiJsiClassBuilderProtocol +@protocol NativeApiDirectClassBuilderProtocol @end #ifdef EMBED_METADATA_SIZE extern const unsigned char embedded_metadata[EMBED_METADATA_SIZE]; #endif -namespace facebook { -namespace jsi { +namespace nativescript { +namespace direct { class Runtime; class Value; @@ -737,8 +737,8 @@ class ArrayBuffer : public Object { return data; } }; -} // namespace jsi -} // namespace facebook +} // namespace direct +} // namespace nativescript #endif // TARGET_ENGINE_QUICKJS diff --git a/NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.mm b/NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.mm index 6a64b8e5..6e7d143c 100644 --- a/NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.mm +++ b/NativeScript/ffi/quickjs/NativeApiQuickJSRuntime.mm @@ -2,8 +2,8 @@ #ifdef TARGET_ENGINE_QUICKJS -namespace facebook { -namespace jsi { +namespace nativescript { +namespace direct { String BigInt::toString(Runtime& runtime, int) const { JSValue value = local(runtime); @@ -34,7 +34,7 @@ return value; } -} // namespace jsi -} // namespace facebook +} // namespace direct +} // namespace nativescript #endif // TARGET_ENGINE_QUICKJS diff --git a/NativeScript/ffi/quickjs/NativeApiQuickJSValue.mm b/NativeScript/ffi/quickjs/NativeApiQuickJSValue.mm index 94ae05e0..694cc650 100644 --- a/NativeScript/ffi/quickjs/NativeApiQuickJSValue.mm +++ b/NativeScript/ffi/quickjs/NativeApiQuickJSValue.mm @@ -2,8 +2,8 @@ #ifdef TARGET_ENGINE_QUICKJS -namespace facebook { -namespace jsi { +namespace nativescript { +namespace direct { Value HostObject::get(Runtime&, const PropNameID&) { return Value::undefined(); } void HostObject::set(Runtime&, const PropNameID&, const Value&) {} @@ -82,7 +82,7 @@ setProperty(runtime, name, Value(runtime, value)); } -} // namespace jsi -} // namespace facebook +} // namespace direct +} // namespace nativescript #endif // TARGET_ENGINE_QUICKJS diff --git a/NativeScript/ffi/shared/SignatureDispatchCore.h b/NativeScript/ffi/shared/SignatureDispatchCore.h new file mode 100644 index 00000000..229347a7 --- /dev/null +++ b/NativeScript/ffi/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/v8/NativeApiV8.h b/NativeScript/ffi/v8/NativeApiV8.h index cf8c4054..946ca551 100644 --- a/NativeScript/ffi/v8/NativeApiV8.h +++ b/NativeScript/ffi/v8/NativeApiV8.h @@ -1,7 +1,7 @@ #ifndef NATIVESCRIPT_FFI_V8_NATIVE_API_V8_H #define NATIVESCRIPT_FFI_V8_NATIVE_API_V8_H -#include "ffi/shared/direct/NativeApiDirect.h" +#include "ffi/direct/NativeApiDirect.h" #include "v8.h" namespace nativescript { diff --git a/NativeScript/ffi/v8/NativeApiV8.mm b/NativeScript/ffi/v8/NativeApiV8.mm index 154a1194..3cc785ab 100644 --- a/NativeScript/ffi/v8/NativeApiV8.mm +++ b/NativeScript/ffi/v8/NativeApiV8.mm @@ -3,33 +3,33 @@ #ifdef TARGET_ENGINE_V8 #include "NativeApiV8Runtime.h" +#include "ffi/direct/NativeApiDirectSignatureDispatch.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 nativescript::direct::Array; +using nativescript::direct::ArrayBuffer; +using nativescript::direct::BigInt; +using nativescript::direct::Function; +using nativescript::direct::HostObject; +using nativescript::direct::MutableBuffer; +using nativescript::direct::Object; +using nativescript::direct::PropNameID; +using nativescript::direct::Runtime; +using nativescript::direct::String; +using nativescript::direct::StringBuffer; +using nativescript::direct::Value; +using nativescript::direct::JSError; using metagen::MDMemberFlag; using metagen::MDMetadataReader; using metagen::MDSectionOffset; using metagen::MDTypeKind; // clang-format off -#include "jsi/NativeApiJsiBridge.h" +#define NATIVESCRIPT_NATIVE_API_DIRECT_BACKEND_NAME "v8" +#include "ffi/direct/NativeApiDirectBridge.h" // clang-format on #define NATIVESCRIPT_NATIVE_API_HAS_ENGINE_LAZY_GLOBALS 1 @@ -39,8 +39,8 @@ 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)); + nameValue.Reset(isolate, direct::v8direct::makeV8String(isolate, name)); + kindValue.Reset(isolate, direct::v8direct::makeV8String(isolate, kind)); } ~NativeApiV8LazyGlobalData() { @@ -52,13 +52,13 @@ v8::Global kindValue; }; -std::shared_ptr retainNativeApiJsiRuntime(Runtime& runtime) { +std::shared_ptr retainNativeApiDirectRuntime(Runtime& runtime) { return std::make_shared(runtime.state()); } -class NativeApiJsiRuntimeScope final { +class NativeApiDirectRuntimeScope final { public: - explicit NativeApiJsiRuntimeScope(Runtime& runtime) + explicit NativeApiDirectRuntimeScope(Runtime& runtime) : locker_(runtime.isolate()), isolateScope_(runtime.isolate()), handleScope_(runtime.isolate()), @@ -92,7 +92,7 @@ void NativeApiV8LazyGlobalGetter(v8::Local, v8::Local global = context->Global(); v8::Local resolverValue; if (!global - ->Get(context, facebook::jsi::v8direct::makeV8String( + ->Get(context, direct::v8direct::makeV8String( isolate, "__nativeScriptResolveNativeApiLazyGlobal")) .ToLocal(&resolverValue) || !resolverValue->IsFunction()) { @@ -114,7 +114,7 @@ void NativeApiV8LazyGlobalGetter(v8::Local, info.GetReturnValue().Set(result); } -bool InstallNativeApiEngineLazyGlobal(Runtime& runtime, std::shared_ptr, +bool InstallNativeApiEngineLazyGlobal(Runtime& runtime, std::shared_ptr, const std::string& name, const std::string& kind, bool force) { if (name.empty() || kind.empty()) { @@ -125,7 +125,7 @@ bool InstallNativeApiEngineLazyGlobal(Runtime& runtime, std::shared_ptr context = runtime.context(); v8::Local global = context->Global(); - v8::Local property = facebook::jsi::v8direct::makeV8String(isolate, name); + v8::Local property = direct::v8direct::makeV8String(isolate, name); if (!force && global->HasOwnProperty(context, property).FromMaybe(false)) { return false; } @@ -144,17 +144,17 @@ bool InstallNativeApiEngineLazyGlobal(Runtime& runtime, std::shared_ptr context, const NativeApiV8Config& config) { @@ -166,7 +166,7 @@ void InstallNativeApiV8(v8::Isolate* isolate, v8::Local context, v8::HandleScope handleScope(isolate); v8::Context::Scope contextScope(context); Runtime runtime(isolate, context); - InstallNativeApiJSI(runtime, config); + InstallNativeApiDirect(runtime, config); } } // namespace nativescript diff --git a/NativeScript/ffi/v8/NativeApiV8HostObjects.mm b/NativeScript/ffi/v8/NativeApiV8HostObjects.mm index 8a11b15c..0b26b735 100644 --- a/NativeScript/ffi/v8/NativeApiV8HostObjects.mm +++ b/NativeScript/ffi/v8/NativeApiV8HostObjects.mm @@ -2,8 +2,8 @@ #ifdef TARGET_ENGINE_V8 -namespace facebook { -namespace jsi { +namespace nativescript { +namespace direct { namespace v8direct { @@ -181,7 +181,7 @@ void functionWeakCallback(const v8::WeakCallbackInfo& info) { return Function(Object::fromValueStorage(Value(runtime, function).storage_)); } -} // namespace jsi -} // namespace facebook +} // namespace direct +} // namespace nativescript #endif // TARGET_ENGINE_V8 diff --git a/NativeScript/ffi/v8/NativeApiV8Runtime.h b/NativeScript/ffi/v8/NativeApiV8Runtime.h index 81c95a08..b87ac02a 100644 --- a/NativeScript/ffi/v8/NativeApiV8Runtime.h +++ b/NativeScript/ffi/v8/NativeApiV8Runtime.h @@ -36,15 +36,15 @@ #include "ffi.h" #include "v8.h" -@protocol NativeApiJsiClassBuilderProtocol +@protocol NativeApiDirectClassBuilderProtocol @end #ifdef EMBED_METADATA_SIZE extern const unsigned char embedded_metadata[EMBED_METADATA_SIZE]; #endif -namespace facebook { -namespace jsi { +namespace nativescript { +namespace direct { class Runtime; class Value; @@ -728,8 +728,8 @@ class ArrayBuffer : public Object { return value; } }; -} // namespace jsi -} // namespace facebook +} // namespace direct +} // namespace nativescript #endif // TARGET_ENGINE_V8 diff --git a/NativeScript/ffi/v8/NativeApiV8Runtime.mm b/NativeScript/ffi/v8/NativeApiV8Runtime.mm index 681a211b..db7b9b30 100644 --- a/NativeScript/ffi/v8/NativeApiV8Runtime.mm +++ b/NativeScript/ffi/v8/NativeApiV8Runtime.mm @@ -2,8 +2,8 @@ #ifdef TARGET_ENGINE_V8 -namespace facebook { -namespace jsi { +namespace nativescript { +namespace direct { Object Runtime::global() { return Object::fromValueStorage(Value(*this, context()->Global()).storage_); @@ -30,7 +30,7 @@ return Value(*this, result); } -} // namespace jsi -} // namespace facebook +} // namespace direct +} // namespace nativescript #endif // TARGET_ENGINE_V8 diff --git a/NativeScript/ffi/v8/NativeApiV8Value.mm b/NativeScript/ffi/v8/NativeApiV8Value.mm index d8b34428..19ebca40 100644 --- a/NativeScript/ffi/v8/NativeApiV8Value.mm +++ b/NativeScript/ffi/v8/NativeApiV8Value.mm @@ -2,8 +2,8 @@ #ifdef TARGET_ENGINE_V8 -namespace facebook { -namespace jsi { +namespace nativescript { +namespace direct { Value HostObject::get(Runtime&, const PropNameID&) { return Value::undefined(); } @@ -171,7 +171,7 @@ setProperty(runtime, name, Value(runtime, value)); } -} // namespace jsi -} // namespace facebook +} // namespace direct +} // namespace nativescript #endif // TARGET_ENGINE_V8 diff --git a/NativeScript/napi/hermes/jsr.cpp b/NativeScript/napi/hermes/jsr.cpp index ad23e9b0..0817b96a 100644 --- a/NativeScript/napi/hermes/jsr.cpp +++ b/NativeScript/napi/hermes/jsr.cpp @@ -6,6 +6,8 @@ using namespace facebook::jsi; std::unordered_map JSR::env_to_jsr_cache; namespace { +thread_local std::unordered_map g_runtime_lock_depth; + class RuntimeLockGuard { public: explicit RuntimeLockGuard(JSR* runtime) : runtime_(runtime) { @@ -19,6 +21,32 @@ class RuntimeLockGuard { }; } // namespace +void JSR::lock() { + runtime->lock(); + js_mutex.lock(); + g_runtime_lock_depth[this] += 1; +} + +void JSR::unlock() { + auto depth = g_runtime_lock_depth.find(this); + if (depth != g_runtime_lock_depth.end()) { + depth->second -= 1; + if (depth->second <= 0) { + g_runtime_lock_depth.erase(depth); + } + } + js_mutex.unlock(); + runtime->unlock(); +} + +int JSR::currentLockDepth() const { + auto depth = g_runtime_lock_depth.find(const_cast(this)); + if (depth == g_runtime_lock_depth.end()) { + return 0; + } + return depth->second; +} + int js_current_env_lock_depth(napi_env env) { auto itFound = JSR::env_to_jsr_cache.find(env); if (itFound == JSR::env_to_jsr_cache.end() || itFound->second == nullptr) { diff --git a/NativeScript/napi/hermes/jsr.h b/NativeScript/napi/hermes/jsr.h index cb6221ab..bee928a5 100644 --- a/NativeScript/napi/hermes/jsr.h +++ b/NativeScript/napi/hermes/jsr.h @@ -17,30 +17,9 @@ class JSR { std::unique_ptr runtime; facebook::jsi::Runtime* rt; std::recursive_mutex js_mutex; - static inline thread_local std::unordered_map lock_depth; - void lock() { - runtime->lock(); - js_mutex.lock(); - lock_depth[this] += 1; - } - void unlock() { - auto depth = lock_depth.find(this); - if (depth != lock_depth.end()) { - depth->second -= 1; - if (depth->second <= 0) { - lock_depth.erase(depth); - } - } - js_mutex.unlock(); - runtime->unlock(); - } - int currentLockDepth() const { - auto depth = lock_depth.find(const_cast(this)); - if (depth == lock_depth.end()) { - return 0; - } - return depth->second; - } + void lock(); + void unlock(); + int currentLockDepth() const; static std::unordered_map env_to_jsr_cache; }; diff --git a/NativeScript/runtime/Runtime.cpp b/NativeScript/runtime/Runtime.cpp index 89791d8d..d0c06d58 100644 --- a/NativeScript/runtime/Runtime.cpp +++ b/NativeScript/runtime/Runtime.cpp @@ -15,7 +15,7 @@ #include "v8-api.h" #endif // TARGET_ENGINE_V8 #ifdef TARGET_ENGINE_HERMES -#include "ffi/hermes/jsi/NativeApiJsi.h" +#include "ffi/hermes/NativeApiJsi.h" #endif // TARGET_ENGINE_HERMES #ifdef TARGET_ENGINE_JSC #include "ffi/jsc/NativeApiJSC.h" @@ -707,11 +707,6 @@ void Runtime::Init(bool isWorker) { [env = env_](std::function task) { InvokeWithUnlockedHermesRuntime(env, task); }; - nativeApiJsiConfig.nativeCallbackInvoker = - [env = env_](std::function task) { - NapiScope scope(env); - task(); - }; nativeApiJsiConfig.jsThreadCallbackInvoker = [env = env_, runLoop = runtimeLoop_](std::function task) { ExecuteOnRunLoop( diff --git a/benchmarks/objc-dispatch/run.js b/benchmarks/objc-dispatch/run.js index 7d8be2dc..b599b5c5 100644 --- a/benchmarks/objc-dispatch/run.js +++ b/benchmarks/objc-dispatch/run.js @@ -448,8 +448,7 @@ function runNapiNode(options, variant) { const env = { ...process.env, METADATA_PATH: options.metadataPath }; if (variant === "gsd-off") { - // Current runtime disables generated signature dispatch when this value is exactly "0". - env.NS_DISABLE_GSD = "0"; + env.NS_DISABLE_GSD = "1"; } else { delete env.NS_DISABLE_GSD; } @@ -900,7 +899,7 @@ async function runNapiIOS(options, variant, packageTgz, reportVariant = variant) copyDirectoryContents(app.appDir, path.join(appPath, "app")); } installApp(udid, appPath, app.bundleId); - const launchEnv = variant === "gsd-off" ? { NS_DISABLE_GSD: "0" } : {}; + const launchEnv = variant === "gsd-off" ? { NS_DISABLE_GSD: "1" } : {}; return await launchAndCollect(udid, app.bundleId, options, launchEnv); } diff --git a/metadata-generator/src/SignatureDispatchEmitter.cpp b/metadata-generator/src/SignatureDispatchEmitter.cpp index da50820e..fb4d9165 100644 --- a/metadata-generator/src/SignatureDispatchEmitter.cpp +++ b/metadata-generator/src/SignatureDispatchEmitter.cpp @@ -59,6 +59,9 @@ void writeSignatureDispatchBindings(const MDMetadataWriter& writer, wrappersByKey; std::unordered_map> preparedWrappersByKey; + std::unordered_map objcPreparedEntries; + std::unordered_map cFunctionPreparedEntries; + std::unordered_map blockPreparedEntries; std::unordered_map objcNapiEntries; std::unordered_map cFunctionNapiEntries; std::unordered_map objcEngineDirectEntries; @@ -70,7 +73,6 @@ void writeSignatureDispatchBindings(const MDMetadataWriter& writer, std::unordered_map objcHermesFrameDirectReturnEntries; std::unordered_map cFunctionHermesFrameDirectReturnEntries; std::unordered_map blockHermesFrameDirectReturnEntries; - std::unordered_map blockPreparedEntries; std::unordered_map dispatchEncoding; std::unordered_set collidedDispatchIds; @@ -98,6 +100,9 @@ void writeSignatureDispatchBindings(const MDMetadataWriter& writer, if (encodedIt != dispatchEncoding.end() && encodedIt->second != canonicalSignatureKey) { collidedDispatchIds.insert(dispatchId); + objcPreparedEntries.erase(dispatchId); + cFunctionPreparedEntries.erase(dispatchId); + blockPreparedEntries.erase(dispatchId); objcNapiEntries.erase(dispatchId); cFunctionNapiEntries.erase(dispatchId); objcEngineDirectEntries.erase(dispatchId); @@ -109,7 +114,6 @@ void writeSignatureDispatchBindings(const MDMetadataWriter& writer, objcHermesFrameDirectReturnEntries.erase(dispatchId); cFunctionHermesFrameDirectReturnEntries.erase(dispatchId); blockHermesFrameDirectReturnEntries.erase(dispatchId); - blockPreparedEntries.erase(dispatchId); dispatchEncoding.erase(dispatchId); continue; } @@ -125,6 +129,9 @@ void writeSignatureDispatchBindings(const MDMetadataWriter& writer, if (use.kind == DispatchKind::ObjCMethod) { wrappersByKey.emplace(wrapperKey, std::make_pair(use.kind, signature)); + preparedWrappersByKey.emplace(wrapperKey, + std::make_pair(use.kind, signature)); + objcPreparedEntries.emplace(dispatchId, wrapperKey); objcNapiEntries.emplace(dispatchId, wrapperKey); objcEngineDirectEntries.emplace(dispatchId, wrapperKey); objcV8Entries.emplace(dispatchId, wrapperKey); @@ -138,6 +145,9 @@ void writeSignatureDispatchBindings(const MDMetadataWriter& writer, } } else if (use.kind == DispatchKind::CFunction) { wrappersByKey.emplace(wrapperKey, std::make_pair(use.kind, signature)); + preparedWrappersByKey.emplace(wrapperKey, + std::make_pair(use.kind, signature)); + cFunctionPreparedEntries.emplace(dispatchId, wrapperKey); cFunctionNapiEntries.emplace(dispatchId, wrapperKey); cFunctionEngineDirectEntries.emplace(dispatchId, wrapperKey); cFunctionV8Entries.emplace(dispatchId, wrapperKey); @@ -255,6 +265,20 @@ void writeSignatureDispatchBindings(const MDMetadataWriter& writer, sortedCFunctionNapiEntries.begin(), sortedCFunctionNapiEntries.end(), [](const auto& lhs, const auto& rhs) { return lhs.first < rhs.first; }); + std::vector> sortedObjCPreparedEntries( + objcPreparedEntries.begin(), objcPreparedEntries.end()); + std::sort( + sortedObjCPreparedEntries.begin(), sortedObjCPreparedEntries.end(), + [](const auto& lhs, const auto& rhs) { return lhs.first < rhs.first; }); + + std::vector> sortedCFunctionPreparedEntries( + cFunctionPreparedEntries.begin(), cFunctionPreparedEntries.end()); + std::sort(sortedCFunctionPreparedEntries.begin(), + sortedCFunctionPreparedEntries.end(), + [](const auto& lhs, const auto& rhs) { + return lhs.first < rhs.first; + }); + std::vector> sortedObjCEngineDirectEntries( objcEngineDirectEntries.begin(), objcEngineDirectEntries.end()); std::sort(sortedObjCEngineDirectEntries.begin(), @@ -344,7 +368,8 @@ void writeSignatureDispatchBindings(const MDMetadataWriter& writer, generated << "#ifndef NS_GENERATED_SIGNATURE_DISPATCH_INC\n"; generated << "#define NS_GENERATED_SIGNATURE_DISPATCH_INC\n\n"; generated << "#if NS_GSD_BACKEND_V8 || NS_GSD_BACKEND_NAPI || " - "NS_GSD_BACKEND_ENGINE_DIRECT\n"; + "NS_GSD_BACKEND_ENGINE_DIRECT || NS_GSD_BACKEND_HERMES || " + "NS_GSD_BACKEND_DIRECT_PREPARED\n"; generated << "#undef NS_HAS_GENERATED_SIGNATURE_DISPATCH\n"; generated << "#define NS_HAS_GENERATED_SIGNATURE_DISPATCH 1\n"; generated << "#endif\n"; @@ -360,7 +385,7 @@ void writeSignatureDispatchBindings(const MDMetadataWriter& writer, generated << "#undef NS_HAS_GENERATED_SIGNATURE_ENGINE_DIRECT_DISPATCH\n"; generated << "#define NS_HAS_GENERATED_SIGNATURE_ENGINE_DIRECT_DISPATCH 1\n"; generated << "#endif\n\n"; - generated << "#if NS_GSD_BACKEND_HERMES\n"; + generated << "#if NS_GSD_BACKEND_HERMES_EXPERIMENTAL_DIRECT_RETURN\n"; generated << "#undef NS_HAS_GENERATED_SIGNATURE_HERMES_DIRECT_RETURN_DISPATCH\n"; generated << "#define NS_HAS_GENERATED_SIGNATURE_HERMES_DIRECT_RETURN_DISPATCH 1\n"; generated << "#undef " @@ -375,7 +400,8 @@ void writeSignatureDispatchBindings(const MDMetadataWriter& writer, generated << "namespace nativescript {\n\n"; generated << "#if NS_GSD_BACKEND_V8 || NS_GSD_BACKEND_NAPI || " - "NS_GSD_BACKEND_ENGINE_DIRECT\n"; + "NS_GSD_BACKEND_ENGINE_DIRECT || NS_GSD_BACKEND_HERMES || " + "NS_GSD_BACKEND_DIRECT_PREPARED\n"; for (const auto& wrapper : preparedWrappers) { writePreparedWrapper(generated, wrapper.second.first, preparedWrapperNameByKey.at(wrapper.first), @@ -400,7 +426,7 @@ void writeSignatureDispatchBindings(const MDMetadataWriter& writer, writeEngineDirectConverterUndefs(generated); generated << "#endif\n\n"; - generated << "#if NS_GSD_BACKEND_HERMES\n"; + generated << "#if NS_GSD_BACKEND_HERMES_EXPERIMENTAL_DIRECT_RETURN\n"; writeHermesEngineDirectConverterMacros(generated); for (const auto& wrapper : wrappers) { writeHermesDirectReturnWrapper( @@ -431,15 +457,24 @@ void writeSignatureDispatchBindings(const MDMetadataWriter& writer, generated << "#endif\n\n"; generated << "#if NS_GSD_BACKEND_V8 || NS_GSD_BACKEND_NAPI || " - "NS_GSD_BACKEND_ENGINE_DIRECT\n"; + "NS_GSD_BACKEND_ENGINE_DIRECT || NS_GSD_BACKEND_HERMES || " + "NS_GSD_BACKEND_DIRECT_PREPARED\n"; generated << "inline constexpr ObjCDispatchEntry " "kGeneratedObjCDispatchEntries[] = {\n"; generated << " {0, nullptr},\n"; + for (const auto& entry : sortedObjCPreparedEntries) { + generated << " {" << toHexLiteral(entry.first) << ", &" + << preparedWrapperNameByKey.at(entry.second) << "},\n"; + } generated << "};\n\n"; generated << "inline constexpr CFunctionDispatchEntry " "kGeneratedCFunctionDispatchEntries[] = {\n"; generated << " {0, nullptr},\n"; + for (const auto& entry : sortedCFunctionPreparedEntries) { + generated << " {" << toHexLiteral(entry.first) << ", &" + << preparedWrapperNameByKey.at(entry.second) << "},\n"; + } generated << "};\n\n"; generated << "inline constexpr BlockDispatchEntry " @@ -472,7 +507,7 @@ void writeSignatureDispatchBindings(const MDMetadataWriter& writer, generated << "};\n"; generated << "#endif\n\n"; - generated << "#if NS_GSD_BACKEND_HERMES\n"; + generated << "#if NS_GSD_BACKEND_HERMES_EXPERIMENTAL_DIRECT_RETURN\n"; generated << "inline constexpr ObjCHermesDirectReturnDispatchEntry " "kGeneratedObjCHermesDirectReturnDispatchEntries[] = {\n"; generated << " {0, nullptr},\n"; diff --git a/metadata-generator/src/SignatureDispatchEmitter/Napi.cpp b/metadata-generator/src/SignatureDispatchEmitter/Napi.cpp index efcd6a40..41d48b6c 100644 --- a/metadata-generator/src/SignatureDispatchEmitter/Napi.cpp +++ b/metadata-generator/src/SignatureDispatchEmitter/Napi.cpp @@ -376,10 +376,6 @@ void writeNapiWrapper(std::ostringstream& out, DispatchKind kind, void writePreparedWrapper(std::ostringstream& out, DispatchKind kind, const std::string& wrapperName, const MDSignature* signature) { - if (kind != DispatchKind::BlockInvoke) { - return; - } - std::string returnType; if (!mapTypeToCpp(signature->returnType, &returnType, true)) { return; @@ -397,22 +393,55 @@ void writePreparedWrapper(std::ostringstream& out, DispatchKind kind, out << "static inline void " << wrapperName << "(void* fnptr, void** avalues, void* rvalue) {\n"; - out << " using Fn = " << returnType << " (*)(void*"; + out << " using Fn = " << returnType << " (*)("; + bool first = true; + if (kind == DispatchKind::ObjCMethod) { + out << "id, SEL"; + first = false; + } else if (kind == DispatchKind::BlockInvoke) { + out << "void*"; + first = false; + } for (const auto& argType : argTypes) { - out << ", " << argType; + if (!first) { + out << ", "; + } + out << argType; + first = false; } out << ");\n"; out << " auto fn = reinterpret_cast(fnptr);\n"; - out << " void* block = *reinterpret_cast(avalues[0]);\n"; + size_t implicitArgumentCount = 0; + if (kind == DispatchKind::ObjCMethod) { + out << " id self = *reinterpret_cast(avalues[0]);\n"; + out << " SEL selector = *reinterpret_cast(avalues[1]);\n"; + implicitArgumentCount = 2; + } else if (kind == DispatchKind::BlockInvoke) { + out << " void* block = *reinterpret_cast(avalues[0]);\n"; + implicitArgumentCount = 1; + } for (size_t i = 0; i < argTypes.size(); i++) { out << " " << argTypes[i] << " arg" << i << " = *reinterpret_cast<" - << argTypes[i] << "*>(avalues[" << (i + 1) << "]);\n"; + << argTypes[i] << "*>(avalues[" << (i + implicitArgumentCount) + << "]);\n"; } std::ostringstream callExpr; - callExpr << "fn(block"; + callExpr << "fn("; + bool hasAnyCallArg = false; + if (kind == DispatchKind::ObjCMethod) { + callExpr << "self, selector"; + hasAnyCallArg = true; + } else if (kind == DispatchKind::BlockInvoke) { + callExpr << "block"; + hasAnyCallArg = true; + } for (size_t i = 0; i < argTypes.size(); i++) { - callExpr << ", arg" << i; + if (hasAnyCallArg) { + callExpr << ", "; + } + callExpr << "arg" << i; + hasAnyCallArg = true; } callExpr << ")"; diff --git a/packages/react-native/NativeScriptNativeApi.podspec b/packages/react-native/NativeScriptNativeApi.podspec index d65543a7..a4b78999 100644 --- a/packages/react-native/NativeScriptNativeApi.podspec +++ b/packages/react-native/NativeScriptNativeApi.podspec @@ -31,7 +31,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", "CLANG_CXX_LIBRARY" => "libc++", - "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) TARGET_ENGINE_HERMES=1", + "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) TARGET_ENGINE_HERMES=1 NS_FFI_BACKEND_DIRECT=1 NS_GSD_BACKEND_HERMES=1", "HEADER_SEARCH_PATHS" => [ "\"$(PODS_TARGET_SRCROOT)/ios\"", "\"$(PODS_TARGET_SRCROOT)/native-api-jsi\"", diff --git a/scripts/build_nativescript.sh b/scripts/build_nativescript.sh index 7f775ac8..f1b1187b 100755 --- a/scripts/build_nativescript.sh +++ b/scripts/build_nativescript.sh @@ -17,8 +17,7 @@ TARGET_ENGINE=${TARGET_ENGINE:=v8} # default to v8 for compat NS_FFI_BACKEND=${NS_FFI_BACKEND:=auto} NS_GSD_BACKEND=${NS_GSD_BACKEND:=auto} METADATA_SIZE=${METADATA_SIZE:=0} -GENERATED_SIGNATURE_DISPATCH=${NS_SIGNATURE_BINDINGS_CPP_PATH:-${TNS_SIGNATURE_BINDINGS_CPP_PATH:-./NativeScript/ffi/napi/GeneratedSignatureDispatch.inc}} -GENERATED_SIGNATURE_DISPATCH_STAMP="${GENERATED_SIGNATURE_DISPATCH}.stamp" +REQUESTED_SIGNATURE_DISPATCH=${NS_SIGNATURE_BINDINGS_CPP_PATH:-${TNS_SIGNATURE_BINDINGS_CPP_PATH:-}} for arg in $@; do case $arg in @@ -95,7 +94,14 @@ function effective_gsd_backend () { local is_macos_napi="${1:-false}" if [ "$(effective_ffi_backend "$is_macos_napi")" == "direct" ]; then - echo none + case "$NS_GSD_BACKEND" in + auto) + echo none + ;; + *) + echo "$NS_GSD_BACKEND" + ;; + esac return fi @@ -135,6 +141,33 @@ function effective_ffi_backend () { esac } +function signature_dispatch_path () { + local is_macos_napi="${1:-false}" + if [ -n "$REQUESTED_SIGNATURE_DISPATCH" ]; then + echo "$REQUESTED_SIGNATURE_DISPATCH" + return + fi + + local backend + backend=$(effective_gsd_backend "$is_macos_napi") + local ffi_backend + ffi_backend=$(effective_ffi_backend "$is_macos_napi") + + if [ "$ffi_backend" == "direct" ]; then + case "$backend" in + v8|jsc|quickjs) echo "./NativeScript/ffi/direct/GeneratedSignatureDispatch.inc"; return ;; + esac + fi + + case "$backend" in + hermes) echo "./NativeScript/ffi/hermes/GeneratedSignatureDispatch.inc" ;; + v8) echo "./NativeScript/ffi/v8/GeneratedSignatureDispatch.inc" ;; + jsc) echo "./NativeScript/ffi/jsc/GeneratedSignatureDispatch.inc" ;; + quickjs) echo "./NativeScript/ffi/quickjs/GeneratedSignatureDispatch.inc" ;; + *) echo "./NativeScript/ffi/napi/GeneratedSignatureDispatch.inc" ;; + esac +} + function signature_dispatch_stamp () { local platform="$1" local is_macos_napi="${2:-false}" @@ -164,9 +197,12 @@ function ensure_signature_dispatch_bindings () { local expected_stamp expected_stamp=$(signature_dispatch_stamp "$platform" "$is_macos_napi") - if [ -f "$GENERATED_SIGNATURE_DISPATCH" ] && \ - [ -f "$GENERATED_SIGNATURE_DISPATCH_STAMP" ] && \ - [ "$(cat "$GENERATED_SIGNATURE_DISPATCH_STAMP")" == "$expected_stamp" ]; then + local generated_signature_dispatch + generated_signature_dispatch=$(signature_dispatch_path "$is_macos_napi") + local generated_signature_dispatch_stamp="${generated_signature_dispatch}.stamp" + if [ -f "$generated_signature_dispatch" ] && \ + [ -f "$generated_signature_dispatch_stamp" ] && \ + [ "$(cat "$generated_signature_dispatch_stamp")" == "$expected_stamp" ]; then return fi @@ -175,9 +211,9 @@ function ensure_signature_dispatch_bindings () { fi checkpoint "Generating signature dispatch bindings for $platform ($backend)..." - NS_SIGNATURE_BINDINGS_CPP_PATH="$GENERATED_SIGNATURE_DISPATCH" npm run metagen "$platform" - mkdir -p "$(dirname "$GENERATED_SIGNATURE_DISPATCH_STAMP")" - printf "%s" "$expected_stamp" > "$GENERATED_SIGNATURE_DISPATCH_STAMP" + NS_SIGNATURE_BINDINGS_CPP_PATH="$generated_signature_dispatch" npm run metagen "$platform" + mkdir -p "$(dirname "$generated_signature_dispatch_stamp")" + printf "%s" "$expected_stamp" > "$generated_signature_dispatch_stamp" } DEV_TEAM=${DEVELOPMENT_TEAM:-} diff --git a/scripts/build_react_native_turbomodule.sh b/scripts/build_react_native_turbomodule.sh index f64f23bf..733ac354 100755 --- a/scripts/build_react_native_turbomodule.sh +++ b/scripts/build_react_native_turbomodule.sh @@ -6,6 +6,9 @@ PACKAGE_DIR="packages/react-native" OUTPUT_DIR="$PACKAGE_DIR/dist" PACK_DESTINATION=${NPM_PACK_DESTINATION:-"$REPO_ROOT/build/npm-tarballs"} VERSION_OVERRIDE=${NPM_PACKAGE_VERSION:-} +GENERATED_SIGNATURE_DISPATCH_OVERRIDE=${NS_SIGNATURE_BINDINGS_CPP_PATH:-${TNS_SIGNATURE_BINDINGS_CPP_PATH:-}} +GENERATED_SIGNATURE_DISPATCH=${GENERATED_SIGNATURE_DISPATCH_OVERRIDE:-"$REPO_ROOT/dist/intermediates/react-native/GeneratedSignatureDispatch.ios-sim.tmp.inc"} +DEVICE_SIGNATURE_DISPATCH="$REPO_ROOT/dist/intermediates/react-native/GeneratedSignatureDispatch.ios-device.tmp.inc" SKIP_PACK=false while [[ $# -gt 0 ]]; do @@ -23,13 +26,26 @@ done checkpoint "Preparing @nativescript/react-native TurboModule package..." +if [ ! -x "$REPO_ROOT/metadata-generator/dist/arm64/bin/objc-metadata-generator" ]; then + "$SCRIPT_DIR/build_metadata_generator.sh" +fi + +checkpoint "Generating iOS device metadata for the TurboModule..." +mkdir -p "$(dirname "$DEVICE_SIGNATURE_DISPATCH")" +NS_SIGNATURE_BINDINGS_CPP_PATH="$DEVICE_SIGNATURE_DISPATCH" npm run metagen ios +rm -f "$DEVICE_SIGNATURE_DISPATCH" "$DEVICE_SIGNATURE_DISPATCH.stamp" + +checkpoint "Generating Hermes signature dispatch bindings for the TurboModule..." +mkdir -p "$(dirname "$GENERATED_SIGNATURE_DISPATCH")" +NS_SIGNATURE_BINDINGS_CPP_PATH="$GENERATED_SIGNATURE_DISPATCH" npm run metagen ios-sim + rm -rf \ "$PACKAGE_DIR/native-api-jsi" \ "$PACKAGE_DIR/metadata" \ "$PACKAGE_DIR/ios/vendor" \ "$PACKAGE_DIR/types" mkdir -p \ - "$PACKAGE_DIR/native-api-jsi/jsi" \ + "$PACKAGE_DIR/native-api-jsi/ffi/direct" \ "$PACKAGE_DIR/native-api-jsi/metadata/include" \ "$PACKAGE_DIR/metadata" \ "$PACKAGE_DIR/ios/vendor/libffi/include" \ @@ -37,10 +53,16 @@ mkdir -p \ "$PACKAGE_DIR/types/objc-node-api" \ "$PACK_DESTINATION" -cp NativeScript/ffi/hermes/jsi/NativeApiJsi.h "$PACKAGE_DIR/native-api-jsi/" -cp NativeScript/ffi/hermes/jsi/NativeApiJsi.mm "$PACKAGE_DIR/native-api-jsi/" -cp NativeScript/ffi/shared/jsi/NativeApiJsi*.h "$PACKAGE_DIR/native-api-jsi/jsi/" -cp NativeScript/ffi/hermes/jsi/NativeApiJsiReactNative.h "$PACKAGE_DIR/native-api-jsi/" +cp NativeScript/ffi/hermes/NativeApiJsi.h "$PACKAGE_DIR/native-api-jsi/" +cp NativeScript/ffi/hermes/NativeApiJsi.mm "$PACKAGE_DIR/native-api-jsi/" +cp NativeScript/ffi/hermes/NativeApiJsi*.h "$PACKAGE_DIR/native-api-jsi/" +cp NativeScript/ffi/hermes/NativeApiJsiReactNative.h "$PACKAGE_DIR/native-api-jsi/" +cp NativeScript/ffi/direct/NativeApiDirect*.h "$PACKAGE_DIR/native-api-jsi/ffi/direct/" +cp NativeScript/ffi/shared/SignatureDispatchCore.h "$PACKAGE_DIR/native-api-jsi/" +cp "$GENERATED_SIGNATURE_DISPATCH" "$PACKAGE_DIR/native-api-jsi/GeneratedSignatureDispatch.inc" +if [ -z "$GENERATED_SIGNATURE_DISPATCH_OVERRIDE" ]; then + rm -f "$GENERATED_SIGNATURE_DISPATCH" "$GENERATED_SIGNATURE_DISPATCH.stamp" +fi cp metadata-generator/include/Metadata.h "$PACKAGE_DIR/native-api-jsi/metadata/include/" cp metadata-generator/include/MetadataReader.h "$PACKAGE_DIR/native-api-jsi/metadata/include/" cp NativeScript/libffi/iphonesimulator-universal/include/ffi.h "$PACKAGE_DIR/ios/vendor/libffi/include/" diff --git a/scripts/check_ffi_boundaries.sh b/scripts/check_ffi_boundaries.sh index 20e4f6e1..6dd3c93f 100755 --- a/scripts/check_ffi_boundaries.sh +++ b/scripts/check_ffi_boundaries.sh @@ -3,28 +3,38 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" NAPI_ENGINE_DIR="$ROOT_DIR/NativeScript/ffi/napi/engine" -DIRECT_DIRS=( - "$ROOT_DIR/NativeScript/ffi/hermes" - "$ROOT_DIR/NativeScript/ffi/v8" - "$ROOT_DIR/NativeScript/ffi/jsc" - "$ROOT_DIR/NativeScript/ffi/quickjs" - "$ROOT_DIR/NativeScript/ffi/shared" - "$ROOT_DIR/packages/react-native/native-api-jsi" -) +FFI_DIR="$ROOT_DIR/NativeScript/ffi" +SHARED_DIR="$FFI_DIR/shared" +DIRECT_DIR="$FFI_DIR/direct" +NAPI_DIR="$FFI_DIR/napi" +HERMES_DIR="$FFI_DIR/hermes" +V8_DIR="$FFI_DIR/v8" +JSC_DIR="$FFI_DIR/jsc" +QUICKJS_DIR="$FFI_DIR/quickjs" +REACT_NATIVE_JSI_DIR="$ROOT_DIR/packages/react-native/native-api-jsi" if [ -d "$NAPI_ENGINE_DIR" ] && find "$NAPI_ENGINE_DIR" -type f | grep -q .; then echo "ffi/napi must remain a pure Node-API backend; do not add ffi/napi/engine." >&2 exit 1 fi -EXISTING_DIRECT_DIRS=() -for dir in "${DIRECT_DIRS[@]}"; do +ENGINE_AND_SHARED_DIRS=( + "$SHARED_DIR" + "$DIRECT_DIR" + "$HERMES_DIR" + "$V8_DIR" + "$JSC_DIR" + "$QUICKJS_DIR" +) + +EXISTING_ENGINE_AND_SHARED_DIRS=() +for dir in "${ENGINE_AND_SHARED_DIRS[@]}"; do if [ -d "$dir" ]; then - EXISTING_DIRECT_DIRS+=("$dir") + EXISTING_ENGINE_AND_SHARED_DIRS+=("$dir") fi done -if [ "${#EXISTING_DIRECT_DIRS[@]}" -eq 0 ]; then +if [ "${#EXISTING_ENGINE_AND_SHARED_DIRS[@]}" -eq 0 ]; then exit 0 fi @@ -33,7 +43,8 @@ search_sources() { shift if command -v rg >/dev/null 2>&1; then - rg -n "$pattern" "$@" -g '*.{h,hh,hpp,c,cc,cpp,m,mm,inc}' + rg -n "$pattern" "$@" -g '*.{h,hh,hpp,c,cc,cpp,m,mm,inc}' \ + -g '!GeneratedSignatureDispatch.inc' return fi @@ -48,15 +59,74 @@ search_sources() { -name '*.m' -o \ -name '*.mm' -o \ -name '*.inc' \ - \) -print0 | xargs -0 grep -nE "$pattern" + \) ! -name 'GeneratedSignatureDispatch.inc' -print0 | xargs -0 grep -nE "$pattern" } if search_sources '(^|[^[:alnum:]_])(napi_|napi_env|napi_value|js_native_api|node_api)($|[^[:alnum:]_])' \ - "${EXISTING_DIRECT_DIRS[@]}"; then + "${EXISTING_ENGINE_AND_SHARED_DIRS[@]}"; then echo "Node-API symbols are not allowed in shared or direct engine FFI folders." >&2 exit 1 fi +ENGINE_NEUTRAL_DIRS=() +for dir in "$SHARED_DIR" "$DIRECT_DIR"; do + if [ -d "$dir" ]; then + ENGINE_NEUTRAL_DIRS+=("$dir") + fi +done + +if [ "${#ENGINE_NEUTRAL_DIRS[@]}" -gt 0 ] && + search_sources '(^|[^[:alnum:]_])(napi_|napi_env|napi_value|js_native_api|node_api|facebook::jsi|v8::|JSContextRef|JSValueRef|JSContext|JSValue|JSRuntime|quickjs)($|[^[:alnum:]_])|(&2 + exit 1 +fi + +check_no_backend_dependency() { + local owner_name="$1" + local owner_dir="$2" + shift 2 + + if [ ! -d "$owner_dir" ]; then + return + fi + + local pattern="" + local backend + for backend in "$@"; do + if [ -n "$pattern" ]; then + pattern="$pattern|" + fi + pattern="${pattern}(ffi/${backend}/|\"${backend}/)" + done + + if [ -n "$pattern" ] && search_sources "$pattern" "$owner_dir"; then + echo "ffi/$owner_name must not include another FFI backend's private files." >&2 + exit 1 + fi +} + +check_no_backend_dependency "napi" "$NAPI_DIR" hermes v8 jsc quickjs +check_no_backend_dependency "direct" "$DIRECT_DIR" napi hermes v8 jsc quickjs +check_no_backend_dependency "hermes" "$HERMES_DIR" napi v8 jsc quickjs +check_no_backend_dependency "v8" "$V8_DIR" napi hermes jsc quickjs +check_no_backend_dependency "jsc" "$JSC_DIR" napi hermes v8 quickjs +check_no_backend_dependency "quickjs" "$QUICKJS_DIR" napi hermes v8 jsc + +NON_HERMES_JSI_DIRS=() +for dir in "$SHARED_DIR" "$DIRECT_DIR" "$NAPI_DIR" "$V8_DIR" "$JSC_DIR" "$QUICKJS_DIR"; do + if [ -d "$dir" ]; then + NON_HERMES_JSI_DIRS+=("$dir") + fi +done + +if [ "${#NON_HERMES_JSI_DIRS[@]}" -gt 0 ] && + search_sources '(NativeApiJsi|facebook::jsi|&2 + exit 1 +fi + if search_sources '(^|[^[:alnum:]_])(EngineDirect|FastNative|HermesFast|V8Fast|JSCFast|QuickJSFast)($|[^[:alnum:]_])' \ "$ROOT_DIR/NativeScript/ffi/napi" | grep -v 'GeneratedSignatureDispatch.inc'; then echo "Direct-engine FFI code is not allowed in ffi/napi." >&2 diff --git a/scripts/run-tests-macos.js b/scripts/run-tests-macos.js index 09435c34..07c99ce7 100644 --- a/scripts/run-tests-macos.js +++ b/scripts/run-tests-macos.js @@ -9,6 +9,8 @@ // artifacts need rebuilding. Supported: v8, hermes, quickjs, jsc. Defaults to v8. // - MACOS_TEST_FFI_BACKEND selects the FFI backend build to use when runtime // artifacts need rebuilding. Supported: auto, napi, direct. Defaults to auto. +// - MACOS_TEST_GSD_BACKEND selects generated signature dispatch backend. +// Supported: auto, v8, jsc, quickjs, hermes, napi, none. Defaults to auto. // - MACOS_COMMAND_TIMEOUT_MS overrides timeout for build commands (default: 10 minutes). // - MACOS_COMMAND_MAX_BUFFER_BYTES overrides spawnSync maxBuffer for captured command output (default: 64 MiB). // - MACOS_TEST_TIMEOUT_MS overrides max test runtime after launch (default: 2 minutes). @@ -95,6 +97,7 @@ const requestedSpecs = (process.env.MACOS_TEST_SPECS || "").trim(); const verboseSpecs = process.env.MACOS_TEST_VERBOSE_SPECS === "1"; const requestedEngine = (process.env.MACOS_TEST_ENGINE || "v8").trim().toLowerCase(); const requestedFfiBackend = (process.env.MACOS_TEST_FFI_BACKEND || "auto").trim().toLowerCase(); +const requestedGsdBackend = (process.env.MACOS_TEST_GSD_BACKEND || process.env.NS_GSD_BACKEND || "auto").trim().toLowerCase(); const launchedMarker = "Application Start!"; const junitPrefix = "TKUnit: "; @@ -103,7 +106,17 @@ const consoleLogMarker = "CONSOLE LOG:"; const crashReportsDir = path.join(os.homedir(), "Library", "Logs", "DiagnosticReports"); const generatedRuntimeBuildOutputs = new Set([ path.join(nativeScriptSourceRoot, "ffi", "napi", "GeneratedSignatureDispatch.inc"), - path.join(nativeScriptSourceRoot, "ffi", "napi", "GeneratedSignatureDispatch.inc.stamp") + path.join(nativeScriptSourceRoot, "ffi", "napi", "GeneratedSignatureDispatch.inc.stamp"), + path.join(nativeScriptSourceRoot, "ffi", "hermes", "GeneratedSignatureDispatch.inc"), + path.join(nativeScriptSourceRoot, "ffi", "hermes", "GeneratedSignatureDispatch.inc.stamp"), + path.join(nativeScriptSourceRoot, "ffi", "direct", "GeneratedSignatureDispatch.inc"), + path.join(nativeScriptSourceRoot, "ffi", "direct", "GeneratedSignatureDispatch.inc.stamp"), + path.join(nativeScriptSourceRoot, "ffi", "v8", "GeneratedSignatureDispatch.inc"), + path.join(nativeScriptSourceRoot, "ffi", "v8", "GeneratedSignatureDispatch.inc.stamp"), + path.join(nativeScriptSourceRoot, "ffi", "jsc", "GeneratedSignatureDispatch.inc"), + path.join(nativeScriptSourceRoot, "ffi", "jsc", "GeneratedSignatureDispatch.inc.stamp"), + path.join(nativeScriptSourceRoot, "ffi", "quickjs", "GeneratedSignatureDispatch.inc"), + path.join(nativeScriptSourceRoot, "ffi", "quickjs", "GeneratedSignatureDispatch.inc.stamp") ]); function parseArgs() { @@ -499,6 +512,7 @@ function ensureMacOSRuntimeArtifactsBuilt() { const artifactMtime = getPathStats(nativeScriptXCFramework).maxMtimeMs; let configuredEngine = null; let configuredFfiBackend = null; + let configuredGsdBackend = null; if (fs.existsSync(cachePath)) { try { @@ -511,6 +525,10 @@ function ensureMacOSRuntimeArtifactsBuilt() { if (ffiBackendMatch) { configuredFfiBackend = ffiBackendMatch[1].trim().toLowerCase(); } + const gsdBackendMatch = cache.match(/^NS_GSD_BACKEND:STRING=(.+)$/m); + if (gsdBackendMatch) { + configuredGsdBackend = gsdBackendMatch[1].trim().toLowerCase(); + } } catch (_) { configuredEngine = null; configuredFfiBackend = null; @@ -520,7 +538,8 @@ function ensureMacOSRuntimeArtifactsBuilt() { if (artifactMtime > 0 && artifactMtime >= sourceMtime && configuredEngine === requestedEngine && - configuredFfiBackend === requestedFfiBackend) { + configuredFfiBackend === requestedFfiBackend && + configuredGsdBackend === requestedGsdBackend) { return; } @@ -534,10 +553,15 @@ function ensureMacOSRuntimeArtifactsBuilt() { throw new Error(`Unsupported MACOS_TEST_FFI_BACKEND: ${requestedFfiBackend}`); } - console.log(`NativeScript macOS artifacts are missing, stale, or built for '${configuredEngine ?? "unknown"}/${configuredFfiBackend ?? "unknown"}'; running ${requestedEngine}/${requestedFfiBackend} build...`); + const supportedGsdBackends = new Set(["auto", "v8", "jsc", "quickjs", "hermes", "napi", "none"]); + if (!supportedGsdBackends.has(requestedGsdBackend)) { + throw new Error(`Unsupported MACOS_TEST_GSD_BACKEND: ${requestedGsdBackend}`); + } + + console.log(`NativeScript macOS artifacts are missing, stale, or built for '${configuredEngine ?? "unknown"}/${configuredFfiBackend ?? "unknown"}/${configuredGsdBackend ?? "unknown"}'; running ${requestedEngine}/${requestedFfiBackend}/${requestedGsdBackend} build...`); runBuildAndRequireSuccess( path.join(__dirname, "build_nativescript.sh"), - ["--macos", "--no-iphone", "--no-simulator", `--${requestedEngine}`, `--ffi-backend=${requestedFfiBackend}`], + ["--macos", "--no-iphone", "--no-simulator", `--${requestedEngine}`, `--ffi-backend=${requestedFfiBackend}`, `--gsd-backend=${requestedGsdBackend}`], commandTimeoutMs ); } diff --git a/test/runtime/runner/app/tests/NativeApiJsiTests.js b/test/runtime/runner/app/tests/NativeApiJsiTests.js index 562017eb..f07ba464 100644 --- a/test/runtime/runner/app/tests/NativeApiJsiTests.js +++ b/test/runtime/runner/app/tests/NativeApiJsiTests.js @@ -1,27 +1,36 @@ -describe("Native API JSI bridge", function () { +describe("Native API direct bridge", function () { function apiOrPending() { var api = global.__nativeScriptNativeApi; if (!api) { - pending("Native API JSI bridge is only installed for Hermes."); + pending("Native API direct bridge is only installed for direct FFI backends."); } return api; } + function expectBridgeIdentity(api) { + if (api.runtime === "jsi") { + expect(api.backend).toBe("hermes"); + return; + } + + expect(api.runtime).toBe("direct"); + expect(["v8", "jsc", "quickjs"]).toContain(api.backend); + } + afterEach(function () { TNSClearOutput(); }); - it("exposes the Hermes JSI host object", function () { + it("exposes the native API host object", function () { var api = apiOrPending(); - expect(api.runtime).toBe("jsi"); - expect(api.backend).toBe("hermes"); + expectBridgeIdentity(api); expect(api.metadata.classes).toBeGreaterThan(0); expect(api.metadata.functions).toBeGreaterThan(0); expect(api.getClass("NSObject").available).toBe(true); }); - it("calls metadata-backed C functions through pure JSI", function () { + it("calls metadata-backed C functions through the direct bridge", function () { var api = apiOrPending(); var fn = api.getFunction("functionWithInt"); @@ -30,7 +39,7 @@ describe("Native API JSI bridge", function () { expect(TNSGetOutput()).toBe("42"); }); - it("sends Objective-C selectors through pure JSI", function () { + it("sends Objective-C selectors through the direct bridge", function () { var api = apiOrPending(); var primitives = api.getClass("TNSPrimitives").alloc().invoke("init"); @@ -38,7 +47,7 @@ describe("Native API JSI bridge", function () { expect(TNSGetOutput()).toBe("24"); }); - it("decodes Objective-C runtime struct signatures through pure JSI", function () { + it("decodes Objective-C runtime struct signatures through the direct bridge", function () { apiOrPending(); if (typeof UIView === "undefined" || typeof CGRectMake !== "function") { pending("UIKit CGRect runtime selector fallback is only available on iOS."); @@ -60,7 +69,7 @@ describe("Native API JSI bridge", function () { expect(bounds.size.height).toBe(4); }); - it("decodes metadata-less Objective-C runtime struct signatures through pure JSI", function () { + it("decodes metadata-less Objective-C runtime struct signatures through the direct bridge", function () { apiOrPending(); var provider = TNSRuntimeOnlyStructProviderMake(); var pair = provider.invoke("runtimeOnlyPair");