Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions NativeScript/ffi/objc/hermes/NativeApiJsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,42 @@ void InstallNativeApiJSI(
facebook::jsi::Runtime& runtime,
const NativeApiJsiConfig& config = NativeApiJsiConfig{});

// M1 (ARCHITECTURE.md §4.3): the two ObjC<->JSI helpers that make the
// by-reference Fabric handoff possible; "the Fabric boundary must hand JS
// a real bridge-wrapped object, not a string handle"
// (CLEANUP_AND_REARCHITECTURE_PLAN.md §2.0). Both wrap the SAME
// NativeApiObjectHostObject mechanism every other native object crossing in
// this bridge already uses (Object.mm/Class.mm); so a wrapped value
// round-trips through the identical `nativeValue(...)`-style method dispatch
// as any other bridged object, not a bespoke RPC.
//
// `object`/the return value are `void*`-typed ObjC `id`s, kept untyped here
// (not `id`) so this header stays includable from a plain C++ translation
// unit that never imports Objective-C (e.g. runtime/apple/Runtime.cpp,
// which includes this header under `#ifdef TARGET_ENGINE_HERMES` without
// itself being compiled as Objective-C++); the same convention
// NativeApiBackendConfig.h already follows.
//
// Only implemented for the Hermes backend (this header/its .mm are
// Hermes-only, ffi/objc/hermes/); RN only ever uses Hermes, so this does not
// touch the V8/JSC/QuickJS engine backends or their standalone builds.
//
// If `ownsObject` is true, the wrapper takes over a +1 retain already held
// by the caller (matching `makeNativeObjectValue`'s `ownsObject` semantics
// used everywhere else in the bridge); if false, the wrapper retains its own
// reference and the caller's reference is untouched.
facebook::jsi::Value NativeScriptWrapNativeObject(facebook::jsi::Runtime& runtime,
void* object,
bool ownsObject = false);

// Reverse direction: given a JSI value produced by NativeScriptWrapNativeObject
// (or any other native-object-wrapping mechanism the bridge already uses --
// NativeApiObjectHostObject, NativeApiPointerHostObject,
// NativeApiReferenceHostObject), returns the underlying native pointer, or
// nullptr if `value` does not wrap a live native object.
void* NativeScriptUnwrapNativeObject(facebook::jsi::Runtime& runtime,
const facebook::jsi::Value& value);

} // namespace nativescript

extern "C" void NativeScriptInstallNativeApiJSI(
Expand Down
45 changes: 44 additions & 1 deletion NativeScript/ffi/objc/hermes/NativeApiJsi.mm
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ NativeApiSelectorGroupState state(
}

// GSD fast path: read jsi args directly, call objc_msgSend with a
// typed cast, produce the jsi return value bypassing all generic
// typed cast, produce the jsi return value , bypassing all generic
// marshalling. Only engages for plain calls (no super dispatch, init
// disown handling, or implicit NSError-out argument).
if (call.prepared->gsdEngineCallable && call.dispatchClass == Nil &&
Expand Down Expand Up @@ -215,6 +215,49 @@ void InstallNativeApiJSI(Runtime& runtime, const NativeApiJsiConfig& config) {
InstallNativeApi(runtime, config);
}

namespace {
// The bridge for a given runtime is reached the same way every other
// caller finds it: the `NativeApiHostObject` stashed under the well-known
// global name every InstallNativeApi call uses by default
// (NativeApiBackendConfig::globalName, "__nativeScriptNativeApi") --
// identical to how `nativeApiInstalled()` in NativeScriptNativeApiModule.mm
// already probes for this same global.
std::shared_ptr<NativeApiBridge> NativeScriptBridgeForRuntime(Runtime& runtime) {
Value apiValue = runtime.global().getProperty(runtime, "__nativeScriptNativeApi");
if (!apiValue.isObject()) {
return nullptr;
}
Object apiObject = apiValue.asObject(runtime);
if (!apiObject.isHostObject<NativeApiHostObject>(runtime)) {
return nullptr;
}
return apiObject.getHostObject<NativeApiHostObject>(runtime)->bridge();
}
} // namespace

Value NativeScriptWrapNativeObject(Runtime& runtime, void* object, bool ownsObject) {
if (object == nullptr) {
return Value::null();
}
auto bridge = NativeScriptBridgeForRuntime(runtime);
if (bridge == nullptr) {
return Value::null();
}
// __bridge: a plain ownership-neutral cast, valid identically whether this
// translation unit is compiled ARC or MRC (unlike a raw C-style cast,
// which ARC rejects for void* <-> id without an explicit bridge
// annotation).
return makeNativeObjectValue(runtime, bridge, (__bridge id)object, ownsObject);
}

void* NativeScriptUnwrapNativeObject(Runtime& runtime, const Value& value) {
void* pointer = nullptr;
if (readPointerLikeValue(runtime, value, &pointer)) {
return pointer;
}
return nullptr;
}

} // namespace nativescript

extern "C" void NativeScriptInstallNativeApiJSI(facebook::jsi::Runtime* runtime,
Expand Down
8 changes: 8 additions & 0 deletions NativeScript/ffi/objc/shared/bridge/HostObject.mm
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ inline bool InstallNativeApiLazyGlobal(
explicit NativeApiHostObject(std::shared_ptr<NativeApiBridge> bridge)
: bridge_(std::move(bridge)) {}

// General accessor (not RN-specific): lets any caller holding the
// per-runtime `__nativeScriptNativeApi` global's HostObject recover the
// underlying bridge, e.g. to wrap/unwrap a native object into an engine
// value via the same mechanism every other crossing already uses (see the
// Hermes-backend wrap/unwrap helpers, ffi/objc/hermes/; this file stays
// engine-neutral and does not itself reference any engine-specific type).
const std::shared_ptr<NativeApiBridge>& bridge() const { return bridge_; }

Value get(Runtime& runtime, const PropNameID& name) override {
std::string property = name.utf8(runtime);
if (property == "runtime") {
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/NativeScriptNativeApi.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.homepage = "https://github.com/NativeScript/napi-ios"
s.license = "Apache-2.0"
s.author = package["author"]
s.platforms = { :ios => "13.0" }
s.platforms = { :ios => "15.1" }
s.source = { :git => "https://github.com/NativeScript/napi-ios.git", :tag => "react-native-v#{s.version}" }
s.requires_arc = false

Expand Down
Loading
Loading