diff --git a/React/Base/RCTBridge+Private.h b/React/Base/RCTBridge+Private.h index 0e4ef5762b2295..f72ea6fe2eee1d 100644 --- a/React/Base/RCTBridge+Private.h +++ b/React/Base/RCTBridge+Private.h @@ -144,6 +144,8 @@ RCT_EXTERN void RCTRegisterModule(Class); @interface RCTCxxBridge : RCTBridge +@property (nonatomic) void *runtime; + - (instancetype)initWithParentBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; @end diff --git a/React/CxxBridge/RCTCxxBridge.mm b/React/CxxBridge/RCTCxxBridge.mm index a15d61ea28ead7..9124fc185aaea8 100644 --- a/React/CxxBridge/RCTCxxBridge.mm +++ b/React/CxxBridge/RCTCxxBridge.mm @@ -57,6 +57,7 @@ typedef void (^RCTPendingCall)(); using namespace facebook::jsc; +using namespace facebook::jsi; using namespace facebook::react; /** @@ -1229,4 +1230,13 @@ - (BOOL)isBatchActive return _wasBatchActive; } +- (void *)runtime +{ + if (!_reactInstance) { + return nullptr; + } + + return _reactInstance->getJavaScriptContext(); +} + @end diff --git a/React/Fabric/RCTSurfacePresenter.h b/React/Fabric/RCTSurfacePresenter.h index 9e19318359d181..7d3ef66e988d9c 100644 --- a/React/Fabric/RCTSurfacePresenter.h +++ b/React/Fabric/RCTSurfacePresenter.h @@ -79,9 +79,9 @@ NS_ASSUME_NONNULL_BEGIN @end -@interface RCTBridge (RCTSurfacePresenter) +@interface RCTBridge (Deprecated) -- (RCTSurfacePresenter *)surfacePresenter; +@property (nonatomic) RCTSurfacePresenter *surfacePresenter; @end diff --git a/React/Fabric/RCTSurfacePresenter.mm b/React/Fabric/RCTSurfacePresenter.mm index 2b102cbc0d296b..a91a631908d4c7 100644 --- a/React/Fabric/RCTSurfacePresenter.mm +++ b/React/Fabric/RCTSurfacePresenter.mm @@ -7,7 +7,10 @@ #import "RCTSurfacePresenter.h" +#import #import +#import +#import #import #import @@ -153,6 +156,14 @@ - (RCTScheduler *)_scheduler auto contextContainer = std::make_shared(); auto messageQueueThread = _batchedBridge.jsMessageThread; + auto runtime = (facebook::jsi::Runtime *)((RCTCxxBridge *)_batchedBridge).runtime; + + RuntimeExecutor runtimeExecutor = + [runtime, messageQueueThread](std::function &&callback) { + messageQueueThread->runOnQueue([runtime, callback = std::move(callback)]() { + callback(*runtime); + }); + }; EventBeatFactory synchronousBeatFactory = [messageQueueThread]() { return std::make_unique(messageQueueThread); @@ -165,8 +176,7 @@ - (RCTScheduler *)_scheduler contextContainer->registerInstance(synchronousBeatFactory, "synchronous"); contextContainer->registerInstance(asynchronousBeatFactory, "asynchronous"); - contextContainer->registerInstance(_uiManagerInstaller, "uimanager-installer"); - contextContainer->registerInstance(_uiManagerUninstaller, "uimanager-uninstaller"); + contextContainer->registerInstance(runtimeExecutor, "runtime-executor"); contextContainer->registerInstance(std::make_shared((__bridge void *)[_bridge imageLoader])); @@ -307,3 +317,17 @@ - (RCTBridge *)bridge_DO_NOT_USE } @end + +@implementation RCTBridge (Deprecated) + +- (void)setSurfacePresenter:(RCTSurfacePresenter *)surfacePresenter +{ + objc_setAssociatedObject(self, @selector(surfacePresenter), surfacePresenter, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (RCTSurfacePresenter *)surfacePresenter +{ + return objc_getAssociatedObject(self, @selector(surfacePresenter)); +} + +@end diff --git a/ReactCommon/fabric/uimanager/BUCK b/ReactCommon/fabric/uimanager/BUCK index 83c98d133f4da2..2859b33ca8d045 100644 --- a/ReactCommon/fabric/uimanager/BUCK +++ b/ReactCommon/fabric/uimanager/BUCK @@ -55,7 +55,10 @@ rn_xplat_cxx_library( "xplat//folly:headers_only", "xplat//folly:memory", "xplat//folly:molly", + "xplat//jsi:JSIDynamic", + "xplat//jsi:jsi", "xplat//third-party/glog:glog", + react_native_xplat_target("cxxreact:bridge"), react_native_xplat_target("fabric/components/root:root"), react_native_xplat_target("fabric/components/view:view"), react_native_xplat_target("fabric/core:core"), diff --git a/ReactCommon/fabric/uimanager/FabricUIManager.h b/ReactCommon/fabric/uimanager/FabricUIManager.h index 000119968498ec..4b47dfae62313e 100644 --- a/ReactCommon/fabric/uimanager/FabricUIManager.h +++ b/ReactCommon/fabric/uimanager/FabricUIManager.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -22,6 +23,9 @@ namespace react { class FabricUIManager; using UIManager = FabricUIManager; +using RuntimeExecutor = std::function &&callback)>; + /* * Particular implementations of those functions should capture references to * the runtime and ensure proper threading. diff --git a/ReactCommon/fabric/uimanager/JSIFabricUIManager.cpp b/ReactCommon/fabric/uimanager/JSIFabricUIManager.cpp new file mode 100644 index 00000000000000..773ee76ba9008b --- /dev/null +++ b/ReactCommon/fabric/uimanager/JSIFabricUIManager.cpp @@ -0,0 +1,312 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#include "JSIFabricUIManager.h" + +#include +#include +#include + +namespace facebook { +namespace react { + +namespace { + +struct EventTargetWrapper: public EventTarget { + EventTargetWrapper(jsi::WeakObject instanceHandle): + instanceHandle(std::move(instanceHandle)) {} + + mutable jsi::WeakObject instanceHandle; +}; + +struct EventHandlerWrapper: public EventHandler { + EventHandlerWrapper(jsi::Function eventHandler): + callback(std::move(eventHandler)) {} + + jsi::Function callback; +}; + +struct ShadowNodeWrapper: public jsi::HostObject { + ShadowNodeWrapper(SharedShadowNode shadowNode): + shadowNode(std::move(shadowNode)) {} + + SharedShadowNode shadowNode; +}; + +struct ShadowNodeListWrapper: public jsi::HostObject { + ShadowNodeListWrapper(SharedShadowNodeUnsharedList shadowNodeList): + shadowNodeList(shadowNodeList) {} + + SharedShadowNodeUnsharedList shadowNodeList; +}; + +jsi::Value createNode(const UIManager &uiManager, jsi::Runtime &runtime, const jsi::Value *arguments, size_t count) { + auto reactTag = (Tag)arguments[0].getNumber(); + auto viewName = arguments[1].getString(runtime).utf8(runtime); + auto rootTag = (Tag)arguments[2].getNumber(); + auto props = folly::dynamic {arguments[3].isNull() ? nullptr : jsi::dynamicFromValue(runtime, arguments[3])}; + auto eventTarget = std::make_shared(jsi::WeakObject(runtime, arguments[4].getObject(runtime))); + + SharedShadowNode node = uiManager.createNode( + reactTag, + viewName, + rootTag, + props, + eventTarget + ); + + auto shadowNodeWrapper = std::make_shared(node); + return jsi::Object::createFromHostObject(runtime, shadowNodeWrapper); +} + +jsi::Value cloneNode(const UIManager &uiManager, jsi::Runtime &runtime, const jsi::Value *arguments, size_t count) { + auto previousNode = arguments[0].getObject(runtime).getHostObject(runtime)->shadowNode; + auto newNode = uiManager.cloneNode(previousNode); + auto wrapper = std::make_shared(std::move(newNode)); + return jsi::Object::createFromHostObject(runtime, std::move(wrapper)); +} + +jsi::Value cloneNodeWithNewChildren(const UIManager &uiManager, jsi::Runtime &runtime, const jsi::Value *arguments, size_t count) { + auto previousNode = arguments[0].getObject(runtime).getHostObject(runtime)->shadowNode; + auto newNode = uiManager.cloneNodeWithNewChildren(previousNode); + auto wrapper = std::make_shared(std::move(newNode)); + return jsi::Object::createFromHostObject(runtime, std::move(wrapper)); +} + +jsi::Value cloneNodeWithNewProps(const UIManager &uiManager, jsi::Runtime &runtime, const jsi::Value *arguments, size_t count) { + auto previousNode = arguments[0].getObject(runtime).getHostObject(runtime)->shadowNode; + auto props = dynamicFromValue(runtime, arguments[1]); + auto newNode = uiManager.cloneNodeWithNewProps(previousNode, props); + auto wrapper = std::make_shared(std::move(newNode)); + return jsi::Object::createFromHostObject(runtime, std::move(wrapper)); +} + +jsi::Value cloneNodeWithNewChildrenAndProps(const UIManager &uiManager, jsi::Runtime &runtime, const jsi::Value *arguments, size_t count) { + auto previousNode = arguments[0].getObject(runtime).getHostObject(runtime)->shadowNode; + auto props = dynamicFromValue(runtime, arguments[1]); + auto newNode = uiManager.cloneNodeWithNewChildrenAndProps(previousNode, props); + auto wrapper = std::make_shared(std::move(newNode)); + return jsi::Object::createFromHostObject(runtime, std::move(wrapper)); +} + +jsi::Value appendChild(const UIManager &uiManager, jsi::Runtime &runtime, const jsi::Value *arguments, size_t count) { + auto parentNode = arguments[0].getObject(runtime).getHostObject(runtime)->shadowNode; + auto childNode = arguments[1].getObject(runtime).getHostObject(runtime)->shadowNode; + uiManager.appendChild(parentNode, childNode); + return jsi::Value::undefined(); +} + +jsi::Value createChildSet(const UIManager &uiManager, jsi::Runtime &runtime, const jsi::Value *arguments, size_t count) { + auto rootTag = (Tag)arguments[0].getNumber(); + + SharedShadowNodeUnsharedList childSet = uiManager.createChildSet(rootTag); + + return jsi::Object::createFromHostObject( + runtime, + std::make_unique(childSet) + ); +} + +jsi::Value appendChildToSet(const UIManager &uiManager, jsi::Runtime &runtime, const jsi::Value *arguments, size_t count) { + SharedShadowNodeUnsharedList childSet = arguments[0].getObject(runtime).getHostObject(runtime)->shadowNodeList; + SharedShadowNode childNode = arguments[1].getObject(runtime).getHostObject(runtime)->shadowNode; + + uiManager.appendChildToSet(childSet, childNode); + + return jsi::Value::undefined(); +} + +jsi::Value completeRoot(const UIManager &uiManager, jsi::Runtime &runtime, const jsi::Value *arguments, size_t count) { + auto rootTag = (Tag)arguments[0].getNumber(); + SharedShadowNodeUnsharedList childSet = arguments[1].getObject(runtime).getHostObject(runtime)->shadowNodeList; + + uiManager.completeRoot(rootTag, childSet); + + return jsi::Value::undefined(); +} + +jsi::Value registerEventHandler(const UIManager &uiManager, jsi::Runtime &runtime, const jsi::Value *arguments, size_t count) { + auto eventHandler = arguments[0].getObject(runtime).getFunction(runtime); + auto eventHandlerWrapper = std::make_unique(std::move(eventHandler)); + + uiManager.registerEventHandler(std::move(eventHandlerWrapper)); + + return jsi::Value::undefined(); +} + +using Callback = jsi::Value (const UIManager &uiManager, jsi::Runtime &runtime, const jsi::Value *arguments, size_t count); + +void addMethod( + const UIManager &uiManager, + jsi::Runtime &runtime, + jsi::Object &module, + const char *name, + Callback &callback +) { + module.setProperty( + runtime, + name, + jsi::Function::createFromHostFunction( + runtime, + jsi::PropNameID::forAscii(runtime, name), + 1, + [&uiManager, &callback](jsi::Runtime &runtime, const jsi::Value &value, const jsi::Value *args, size_t count) { + return callback(uiManager, runtime, args, count); + } + ) + ); +} + +void removeMethod( + jsi::Runtime &runtime, + jsi::Object &module, + const char *name +) { + // Step 1: Find and replace the body of the method with noop. + auto propertyValue = module.getProperty(runtime, name); + auto propertyObject = propertyValue.asObject(runtime); + auto propertyFunction = propertyObject.asFunction(runtime); + auto &propertyHostFunction = propertyFunction.getHostFunction(runtime); + propertyHostFunction = [](jsi::Runtime& runtime, const jsi::Value& thisVal, const jsi::Value* args, size_t count) { + // Noop. + return jsi::Value::undefined(); + }; + + // Step 2: Remove the reference to the method from the module. + module.setProperty(runtime, name, nullptr); +} + +jsi::Object getModule(jsi::Runtime &runtime, const std::string &moduleName) { + auto batchedBridge = runtime.global().getPropertyAsObject(runtime, "__fbBatchedBridge"); + auto getCallableModule = batchedBridge.getPropertyAsFunction(runtime, "getCallableModule"); + auto module = getCallableModule.callWithThis(runtime, batchedBridge, { jsi::String::createFromUtf8(runtime, moduleName) }).asObject(runtime); + return module; +} + +} // namespace + +void JSIDispatchFabricEventToEmptyTarget( + jsi::Runtime &runtime, + const EventHandler &eventHandler, + const std::string &type, + const folly::dynamic &payload +) { + auto &eventHandlerWrapper = static_cast(eventHandler); + eventHandlerWrapper.callback.call(runtime, { + jsi::Value::null(), + jsi::String::createFromUtf8(runtime, type), + jsi::valueFromDynamic(runtime, payload) + }); +} + +void JSIDispatchFabricEventToTarget( + jsi::Runtime &runtime, + const EventHandler &eventHandler, + const EventTarget &eventTarget, + const std::string &type, + const folly::dynamic &payload +) { + auto &eventHandlerWrapper = static_cast(eventHandler); + auto &eventTargetWrapper = static_cast(eventTarget); + auto eventTargetValue = eventTargetWrapper.instanceHandle.lock(runtime); + + if (eventTargetValue.isUndefined()) { + return; + } + + eventHandlerWrapper.callback.call(runtime, { + std::move(eventTargetValue), + jsi::String::createFromUtf8(runtime, type), + jsi::valueFromDynamic(runtime, payload) + }); +} + +const char *kUIManagerModuleName = "nativeFabricUIManager"; + +void JSIInstallFabricUIManager( + jsi::Runtime &runtime, + UIManager &uiManager +) { + auto module = jsi::Object(runtime); + + addMethod(uiManager, runtime, module, "createNode", createNode); + addMethod(uiManager, runtime, module, "cloneNode", cloneNode); + addMethod(uiManager, runtime, module, "cloneNodeWithNewChildren", cloneNodeWithNewChildren); + addMethod(uiManager, runtime, module, "cloneNodeWithNewProps", cloneNodeWithNewProps); + addMethod(uiManager, runtime, module, "cloneNodeWithNewChildrenAndProps", cloneNodeWithNewChildrenAndProps); + addMethod(uiManager, runtime, module, "appendChild", appendChild); + addMethod(uiManager, runtime, module, "createChildSet", createChildSet); + addMethod(uiManager, runtime, module, "appendChildToSet", appendChildToSet); + addMethod(uiManager, runtime, module, "completeRoot", completeRoot); + addMethod(uiManager, runtime, module, "registerEventHandler", registerEventHandler); + + uiManager.setDispatchEventToEmptyTargetFunction([&runtime](const EventHandler &eventHandler, const std::string &type, const folly::dynamic &payload) { + return JSIDispatchFabricEventToEmptyTarget(runtime, eventHandler, type, payload); + }); + + uiManager.setDispatchEventToTargetFunction([&runtime](const EventHandler &eventHandler, const EventTarget &eventTarget, const std::string &type, const folly::dynamic &payload) { + return JSIDispatchFabricEventToTarget(runtime, eventHandler, eventTarget, type, payload); + }); + + uiManager.setStartSurfaceFunction([&runtime](SurfaceId surfaceId, const std::string &moduleName, const folly::dynamic &initialProps) { + return JSIStartSurface(runtime, surfaceId, moduleName, initialProps); + }); + + uiManager.setStopSurfaceFunction([&runtime](SurfaceId surfaceId) { + return JSIStopSurface(runtime, surfaceId); + }); + + runtime.global().setProperty(runtime, kUIManagerModuleName, module); +} + +void JSIUninstallFabricUIManager( + jsi::Runtime &runtime +) { + auto module = runtime.global().getPropertyAsObject(runtime, kUIManagerModuleName); + + removeMethod(runtime, module, "createNode"); + removeMethod(runtime, module, "cloneNode"); + removeMethod(runtime, module, "cloneNodeWithNewChildren"); + removeMethod(runtime, module, "cloneNodeWithNewProps"); + removeMethod(runtime, module, "cloneNodeWithNewChildrenAndProps"); + removeMethod(runtime, module, "appendChild"); + removeMethod(runtime, module, "createChildSet"); + removeMethod(runtime, module, "appendChildToSet"); + removeMethod(runtime, module, "completeRoot"); + removeMethod(runtime, module, "registerEventHandler"); + + runtime.global().setProperty(runtime, kUIManagerModuleName, nullptr); +} + +void JSIStartSurface( + jsi::Runtime &runtime, + SurfaceId surfaceId, + const std::string &moduleName, + const folly::dynamic &initalProps +) { + folly::dynamic parameters = folly::dynamic::object(); + parameters["rootTag"] = surfaceId; + parameters["initialProps"] = initalProps; + + auto module = getModule(runtime, "AppRegistry"); + auto method = module.getPropertyAsFunction(runtime, "runApplication"); + + method.callWithThis(runtime, module, { + jsi::String::createFromUtf8(runtime, moduleName), + jsi::valueFromDynamic(runtime, parameters) + }); +} + +void JSIStopSurface( + jsi::Runtime &runtime, + SurfaceId surfaceId +) { + auto module = getModule(runtime, "ReactFabric"); + auto method = module.getPropertyAsFunction(runtime, "unmountComponentAtNode"); + + method.callWithThis(runtime, module, { + jsi::Value {surfaceId} + }); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/uimanager/JSIFabricUIManager.h b/ReactCommon/fabric/uimanager/JSIFabricUIManager.h new file mode 100644 index 00000000000000..c1a1e47c1392a7 --- /dev/null +++ b/ReactCommon/fabric/uimanager/JSIFabricUIManager.h @@ -0,0 +1,49 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace react { + +void JSIDispatchFabricEventToEmptyTarget( + jsi::Runtime &runtime, + const EventHandler &eventHandler, + const std::string &type, + const folly::dynamic &payload +); + +void JSIDispatchFabricEventToTarget( + jsi::Runtime &runtime, + const EventHandler &eventHandler, + const EventTarget &eventTarget, + const std::string &type, + const folly::dynamic &payload +); + +void JSIInstallFabricUIManager( + jsi::Runtime &runtime, + UIManager &uiManager +); + +void JSIUninstallFabricUIManager( + jsi::Runtime &runtime +); + +void JSIStartSurface( + jsi::Runtime &runtime, + SurfaceId surfaceId, + const std::string &moduleName, + const folly::dynamic &initalProps +); + +void JSIStopSurface( + jsi::Runtime &runtime, + SurfaceId surfaceId +); + +} +} diff --git a/ReactCommon/fabric/uimanager/Scheduler.cpp b/ReactCommon/fabric/uimanager/Scheduler.cpp index 6867e3f889d627..330145b51b6101 100644 --- a/ReactCommon/fabric/uimanager/Scheduler.cpp +++ b/ReactCommon/fabric/uimanager/Scheduler.cpp @@ -5,9 +5,12 @@ #include "Scheduler.h" +#include + #include #include #include +#include #include #include "ComponentDescriptorFactory.h" @@ -23,12 +26,17 @@ Scheduler::Scheduler(const SharedContextContainer &contextContainer) const auto synchronousEventBeatFactory = contextContainer->getInstance("synchronous"); + const auto runtimeExecutor = + contextContainer->getInstance("runtime-executor"); + uiManager_ = std::make_shared( std::make_unique(asynchronousEventBeatFactory()), - contextContainer->getInstance>( - "uimanager-installer"), - contextContainer->getInstance>( - "uimanager-uninstaller")); + [](UIManager &uiManager) { /* Not implemented. */ }, + []() { /* Not implemented. */ }); + + runtimeExecutor([this](jsi::Runtime &runtime) { + JSIInstallFabricUIManager(runtime, *uiManager_); + }); auto eventDispatcher = std::make_shared( std::bind( @@ -42,6 +50,7 @@ Scheduler::Scheduler(const SharedContextContainer &contextContainer) componentDescriptorRegistry_ = ComponentDescriptorFactory::buildRegistry( eventDispatcher, contextContainer); + uiManager_->setComponentDescriptorRegistry( componentDescriptorRegistry_ );