From 94faff83b980901d783962e6342200723179912e Mon Sep 17 00:00:00 2001 From: Phillip Pan Date: Wed, 14 Feb 2024 17:01:08 -0800 Subject: [PATCH 1/2] build backwards compat API for runtime pointer Summary: Changelog: [Android][Added] This is a pre-deprecated API to give access to the jsi::Runtime in Android in bridgeless. In bridge, this value is exposed via the ReactContext, but is not implemented in the BridgelessReactContext. We do that here. This should work out of the box in bridgeless if you are already retrieveing the pointer via ReactContext. However, we recommend users to eventually migrate towards C++ TurboModule or the RuntimeExecutor if possible. This will be removed in the future. Reviewed By: RSNara Differential Revision: D53645247 --- .../react/runtime/BridgelessReactContext.java | 8 ++++++++ .../com/facebook/react/runtime/ReactHostImpl.java | 10 ++++++++++ .../com/facebook/react/runtime/ReactInstance.java | 12 ++++++++++++ .../main/jni/react/runtime/jni/JReactInstance.cpp | 6 ++++++ .../src/main/jni/react/runtime/jni/JReactInstance.h | 2 ++ .../ReactCommon/react/runtime/ReactInstance.cpp | 4 ++++ .../ReactCommon/react/runtime/ReactInstance.h | 2 ++ 7 files changed, 44 insertions(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java index 742775ea32bd..5fa394795910 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java @@ -12,6 +12,7 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.CatalystInstance; +import com.facebook.react.bridge.JavaScriptContextHolder; import com.facebook.react.bridge.JavaScriptModule; import com.facebook.react.bridge.JavaScriptModuleRegistry; import com.facebook.react.bridge.NativeArray; @@ -163,6 +164,13 @@ public Collection getNativeModules() { return mReactHost.getRuntimeExecutor(); } + @Override + @FrameworkAPI + @UnstableReactNativeAPI + public @Nullable JavaScriptContextHolder getJavaScriptContextHolder() { + return mReactHost.getJavaScriptContextHolder(); + } + @Override public void handleException(Exception e) { mReactHost.handleHostException(e); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java index 08dca74feed5..674aeb42f9f7 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java @@ -29,6 +29,7 @@ import com.facebook.react.ReactInstanceEventListener; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.JSBundleLoader; +import com.facebook.react.bridge.JavaScriptContextHolder; import com.facebook.react.bridge.MemoryPressureListener; import com.facebook.react.bridge.NativeArray; import com.facebook.react.bridge.NativeModule; @@ -599,6 +600,15 @@ RuntimeExecutor getRuntimeExecutor() { return null; } + @Nullable + JavaScriptContextHolder getJavaScriptContextHolder() { + final ReactInstance reactInstance = mReactInstanceTaskRef.get().getResult(); + if (reactInstance != null) { + return reactInstance.getJavaScriptContextHolder(); + } + return null; + } + /* package */ DefaultHardwareBackBtnHandler getDefaultBackButtonHandler() { return () -> { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java index d0d42462bf80..37d8d44d830a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java @@ -20,6 +20,7 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.JSBundleLoader; import com.facebook.react.bridge.JSBundleLoaderDelegate; +import com.facebook.react.bridge.JavaScriptContextHolder; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.NativeArray; import com.facebook.react.bridge.NativeMap; @@ -95,6 +96,8 @@ final class ReactInstance { private final JavaTimerManager mJavaTimerManager; private final BridgelessViewManagerResolver mViewManagerResolver; + private JavaScriptContextHolder mJavaScriptContextHolder; + @DoNotStrip @Nullable private ComponentNameResolverManager mComponentNameResolverManager; @DoNotStrip @Nullable private UIConstantsProviderManager mUIConstantsProviderManager; @@ -181,6 +184,8 @@ public void onHostDestroy() { bindingsInstaller, isProfiling); + mJavaScriptContextHolder = new JavaScriptContextHolder(getJavaScriptContext()); + // Set up TurboModules Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactInstance.initialize#initTurboModules"); @@ -430,6 +435,10 @@ public Collection getNativeModules() { mFabricUIManager.stopSurface(surface.getSurfaceHandler()); } + /* package */ JavaScriptContextHolder getJavaScriptContextHolder() { + return mJavaScriptContextHolder; + } + /* --- Lifecycle methods --- */ @ThreadConfined("ReactHost") /* package */ void destroy() { @@ -440,6 +449,7 @@ public Collection getNativeModules() { mHybridData.resetNative(); mComponentNameResolverManager = null; mUIConstantsProviderManager = null; + mJavaScriptContextHolder.clear(); } /* --- Native methods --- */ @@ -475,6 +485,8 @@ private native HybridData initHybrid( private native RuntimeScheduler getRuntimeScheduler(); + private native long getJavaScriptContext(); + /* package */ native void callFunctionOnModule( String moduleName, String methodName, NativeArray args); diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp index 0adccf64d7f1..ce3bc7b613e0 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp @@ -201,6 +201,10 @@ void JReactInstance::handleMemoryPressureJs(jint level) { instance_->handleMemoryPressureJs(level); } +jlong JReactInstance::getJavaScriptContext() { + return (jlong)(intptr_t)instance_->getJavaScriptContext(); +} + void JReactInstance::registerNatives() { registerHybrid({ makeNativeMethod("initHybrid", JReactInstance::initHybrid), @@ -230,6 +234,8 @@ void JReactInstance::registerNatives() { "registerSegmentNative", JReactInstance::registerSegment), makeNativeMethod( "handleMemoryPressureJs", JReactInstance::handleMemoryPressureJs), + makeNativeMethod( + "getJavaScriptContext", JReactInstance::getJavaScriptContext), }); } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.h b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.h index b07ed7377fdf..6a28ea50c737 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.h @@ -105,6 +105,8 @@ class JReactInstance : public jni::HybridClass { nativeMethodCallInvokerHolder_; jni::global_ref jReactExceptionManager_; jni::global_ref jBindingsInstaller_; + + jlong getJavaScriptContext(); }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp index afefd87605c4..b4d34b28fd4c 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp @@ -465,4 +465,8 @@ void ReactInstance::handleMemoryPressureJs(int pressureLevel) { } } +void* ReactInstance::getJavaScriptContext() { + return &runtime_->getRuntime(); +} + } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.h b/packages/react-native/ReactCommon/react/runtime/ReactInstance.h index 50a4f17a1ed7..c82656c77679 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.h +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.h @@ -71,6 +71,8 @@ class ReactInstance final : private jsinspector_modern::InstanceTargetDelegate { */ void unregisterFromInspector(); + void* getJavaScriptContext(); + private: std::shared_ptr runtime_; std::shared_ptr jsMessageQueueThread_; From a866533cf8c9976c7f9027c49a3f4c28d26ab094 Mon Sep 17 00:00:00 2001 From: Phillip Pan Date: Wed, 14 Feb 2024 17:01:08 -0800 Subject: [PATCH 2/2] build backwards compat API for runtime pointer Summary: Changelog: [iOS][Added] This implements the functionality to give access to the jsi::Runtime in iOS in bridgeless. In bridge, this value is a private selector on RCTBridge that is exposed via category. We build this into the backwards compatible RCTBridgeProxy here. This should work out of the box in bridgeless if you are already retrieveing the pointer via the bridge. However, we recommend users to eventually migrate towards C++ TurboModule or the RuntimeExecutor if possible. This will be removed in the future. Reviewed By: RSNara Differential Revision: D53646413 --- .../react-native/React/Base/RCTBridgeProxy.h | 3 ++- .../react-native/React/Base/RCTBridgeProxy.mm | 23 +++++++++++-------- .../platform/ios/ReactCommon/RCTInstance.mm | 3 ++- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/react-native/React/Base/RCTBridgeProxy.h b/packages/react-native/React/Base/RCTBridgeProxy.h index f8639c418e0d..79c75c3b491a 100644 --- a/packages/react-native/React/Base/RCTBridgeProxy.h +++ b/packages/react-native/React/Base/RCTBridgeProxy.h @@ -20,7 +20,8 @@ bundleManager:(RCTBundleManager *)bundleManager callableJSModules:(RCTCallableJSModules *)callableJSModules dispatchToJSThread:(void (^)(dispatch_block_t))dispatchToJSThread - registerSegmentWithId:(void (^)(NSNumber *, NSString *))registerSegmentWithId NS_DESIGNATED_INITIALIZER; + registerSegmentWithId:(void (^)(NSNumber *, NSString *))registerSegmentWithId + runtime:(void *)runtime NS_DESIGNATED_INITIALIZER; - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel; - (void)forwardInvocation:(NSInvocation *)invocation; diff --git a/packages/react-native/React/Base/RCTBridgeProxy.mm b/packages/react-native/React/Base/RCTBridgeProxy.mm index e4ece263b12b..af6b7afb687a 100644 --- a/packages/react-native/React/Base/RCTBridgeProxy.mm +++ b/packages/react-native/React/Base/RCTBridgeProxy.mm @@ -28,6 +28,7 @@ @implementation RCTBridgeProxy { RCTCallableJSModules *_callableJSModules; void (^_dispatchToJSThread)(dispatch_block_t); void (^_registerSegmentWithId)(NSNumber *, NSString *); + void *_runtime; } - (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry @@ -36,15 +37,17 @@ - (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry callableJSModules:(RCTCallableJSModules *)callableJSModules dispatchToJSThread:(void (^)(dispatch_block_t))dispatchToJSThread registerSegmentWithId:(void (^)(NSNumber *, NSString *))registerSegmentWithId + runtime:(void *)runtime { self = [super self]; if (self) { - self->_uiManagerProxy = [[RCTUIManagerProxy alloc] initWithViewRegistry:viewRegistry]; - self->_moduleRegistry = moduleRegistry; - self->_bundleManager = bundleManager; - self->_callableJSModules = callableJSModules; - self->_dispatchToJSThread = dispatchToJSThread; - self->_registerSegmentWithId = registerSegmentWithId; + _uiManagerProxy = [[RCTUIManagerProxy alloc] initWithViewRegistry:viewRegistry]; + _moduleRegistry = moduleRegistry; + _bundleManager = bundleManager; + _callableJSModules = callableJSModules; + _dispatchToJSThread = dispatchToJSThread; + _registerSegmentWithId = registerSegmentWithId; + _runtime = runtime; } return self; } @@ -75,10 +78,10 @@ - (Class)executorClass * Used By: * - RCTBlobCollector */ -- (jsi::Runtime *)runtime +- (void *)runtime { - [self logWarning:@"This method is unsupported. Returning nullptr." cmd:_cmd]; - return nullptr; + [self logWarning:@"Please migrate to C++ TurboModule or RuntimeExecutor." cmd:_cmd]; + return _runtime; } /** @@ -162,7 +165,7 @@ - (void)enqueueJSCall:(NSString *)module - (void)registerSegmentWithId:(NSUInteger)segmentId path:(NSString *)path { - self->_registerSegmentWithId(@(segmentId), path); + _registerSegmentWithId(@(segmentId), path); } - (id)delegate diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm index 5bcec41c63da..a123105a672c 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm @@ -263,7 +263,8 @@ - (void)_start if (strongSelf && strongSelf->_valid) { [strongSelf registerSegmentWithId:segmentId path:path]; } - }]; + } + runtime:_reactInstance->getJavaScriptContext()]; [RCTBridge setCurrentBridge:(RCTBridge *)bridgeProxy]; // Set up TurboModules