Fabric: New UIManager registration process (beginning)

Summary:
This diff introduces a new integration concept (called RuntimeExecutor) which consolidates `Runtime` and the dispatching mechanism.
As simple as that:
`using RuntimeExecutor = std::function<void(std::function<void(facebook::jsi::Runtime &runtime)> &&callback)>;`

Reviewed By: fkgozali

Differential Revision: D12816746

fbshipit-source-id: 9e27ef16b98af861d494fe50c7e50bd0536e6aaf
This commit is contained in:
Valentin Shergin 2018-10-29 13:02:27 -07:00 committed by Facebook Github Bot
parent 7a914fcef4
commit 8f04699c4c
9 changed files with 421 additions and 8 deletions

View File

@ -144,6 +144,8 @@ RCT_EXTERN void RCTRegisterModule(Class);
@interface RCTCxxBridge : RCTBridge
@property (nonatomic) void *runtime;
- (instancetype)initWithParentBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
@end

View File

@ -57,6 +57,7 @@ static NSString *const RCTJSThreadName = @"com.facebook.react.JavaScript";
typedef void (^RCTPendingCall)();
using namespace facebook::jsc;
using namespace facebook::jsi;
using namespace facebook::react;
/**
@ -1229,4 +1230,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
return _wasBatchActive;
}
- (void *)runtime
{
if (!_reactInstance) {
return nullptr;
}
return _reactInstance->getJavaScriptContext();
}
@end

View File

@ -79,9 +79,9 @@ NS_ASSUME_NONNULL_BEGIN
@end
@interface RCTBridge (RCTSurfacePresenter)
@interface RCTBridge (Deprecated)
- (RCTSurfacePresenter *)surfacePresenter;
@property (nonatomic) RCTSurfacePresenter *surfacePresenter;
@end

View File

@ -7,7 +7,10 @@
#import "RCTSurfacePresenter.h"
#import <objc/runtime.h>
#import <mutex>
#import <jsi/jsi.h>
#import <cxxreact/MessageQueueThread.h>
#import <React/RCTAssert.h>
#import <React/RCTBridge+Private.h>
@ -153,6 +156,14 @@ using namespace facebook::react;
auto contextContainer = std::make_shared<ContextContainer>();
auto messageQueueThread = _batchedBridge.jsMessageThread;
auto runtime = (facebook::jsi::Runtime *)((RCTCxxBridge *)_batchedBridge).runtime;
RuntimeExecutor runtimeExecutor =
[runtime, messageQueueThread](std::function<void(facebook::jsi::Runtime &runtime)> &&callback) {
messageQueueThread->runOnQueue([runtime, callback = std::move(callback)]() {
callback(*runtime);
});
};
EventBeatFactory synchronousBeatFactory = [messageQueueThread]() {
return std::make_unique<MainRunLoopEventBeat>(messageQueueThread);
@ -165,8 +176,7 @@ using namespace facebook::react;
contextContainer->registerInstance<EventBeatFactory>(synchronousBeatFactory, "synchronous");
contextContainer->registerInstance<EventBeatFactory>(asynchronousBeatFactory, "asynchronous");
contextContainer->registerInstance(_uiManagerInstaller, "uimanager-installer");
contextContainer->registerInstance(_uiManagerUninstaller, "uimanager-uninstaller");
contextContainer->registerInstance(runtimeExecutor, "runtime-executor");
contextContainer->registerInstance(std::make_shared<ImageManager>((__bridge void *)[_bridge imageLoader]));
@ -307,3 +317,17 @@ using namespace facebook::react;
}
@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

View File

@ -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"),

View File

@ -10,6 +10,7 @@
#include <memory>
#include <folly/dynamic.h>
#include <jsi/jsi.h>
#include <fabric/core/ShadowNode.h>
#include <fabric/events/EventBeatBasedExecutor.h>
@ -22,6 +23,9 @@ namespace react {
class FabricUIManager;
using UIManager = FabricUIManager;
using RuntimeExecutor = std::function<void(
std::function<void(facebook::jsi::Runtime &runtime)> &&callback)>;
/*
* Particular implementations of those functions should capture references to
* the runtime and ensure proper threading.

View File

@ -0,0 +1,312 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSIFabricUIManager.h"
#include <fabric/uimanager/FabricUIManager.h>
#include <fabric/core/ShadowNode.h>
#include <jsi/JSIDynamic.h>
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<EventTargetWrapper>(jsi::WeakObject(runtime, arguments[4].getObject(runtime)));
SharedShadowNode node = uiManager.createNode(
reactTag,
viewName,
rootTag,
props,
eventTarget
);
auto shadowNodeWrapper = std::make_shared<ShadowNodeWrapper>(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<ShadowNodeWrapper>(runtime)->shadowNode;
auto newNode = uiManager.cloneNode(previousNode);
auto wrapper = std::make_shared<ShadowNodeWrapper>(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<ShadowNodeWrapper>(runtime)->shadowNode;
auto newNode = uiManager.cloneNodeWithNewChildren(previousNode);
auto wrapper = std::make_shared<ShadowNodeWrapper>(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<ShadowNodeWrapper>(runtime)->shadowNode;
auto props = dynamicFromValue(runtime, arguments[1]);
auto newNode = uiManager.cloneNodeWithNewProps(previousNode, props);
auto wrapper = std::make_shared<ShadowNodeWrapper>(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<ShadowNodeWrapper>(runtime)->shadowNode;
auto props = dynamicFromValue(runtime, arguments[1]);
auto newNode = uiManager.cloneNodeWithNewChildrenAndProps(previousNode, props);
auto wrapper = std::make_shared<ShadowNodeWrapper>(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<ShadowNodeWrapper>(runtime)->shadowNode;
auto childNode = arguments[1].getObject(runtime).getHostObject<ShadowNodeWrapper>(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<ShadowNodeListWrapper>(childSet)
);
}
jsi::Value appendChildToSet(const UIManager &uiManager, jsi::Runtime &runtime, const jsi::Value *arguments, size_t count) {
SharedShadowNodeUnsharedList childSet = arguments[0].getObject(runtime).getHostObject<ShadowNodeListWrapper>(runtime)->shadowNodeList;
SharedShadowNode childNode = arguments[1].getObject(runtime).getHostObject<ShadowNodeWrapper>(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<ShadowNodeListWrapper>(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<EventHandlerWrapper>(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<const EventHandlerWrapper &>(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<const EventHandlerWrapper &>(eventHandler);
auto &eventTargetWrapper = static_cast<const EventTargetWrapper &>(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

View File

@ -0,0 +1,49 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <fabric/uimanager/FabricUIManager.h>
#include <folly/dynamic.h>
#include <jsi/jsi.h>
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
);
}
}

View File

@ -5,9 +5,12 @@
#include "Scheduler.h"
#include <jsi/jsi.h>
#include <fabric/core/LayoutContext.h>
#include <fabric/uimanager/ComponentDescriptorRegistry.h>
#include <fabric/uimanager/FabricUIManager.h>
#include <fabric/uimanager/JSIFabricUIManager.h>
#include <fabric/uimanager/TemplateRenderer.h>
#include "ComponentDescriptorFactory.h"
@ -23,12 +26,17 @@ Scheduler::Scheduler(const SharedContextContainer &contextContainer)
const auto synchronousEventBeatFactory =
contextContainer->getInstance<EventBeatFactory>("synchronous");
const auto runtimeExecutor =
contextContainer->getInstance<RuntimeExecutor>("runtime-executor");
uiManager_ = std::make_shared<FabricUIManager>(
std::make_unique<EventBeatBasedExecutor>(asynchronousEventBeatFactory()),
contextContainer->getInstance<std::function<UIManagerInstaller>>(
"uimanager-installer"),
contextContainer->getInstance<std::function<UIManagerUninstaller>>(
"uimanager-uninstaller"));
[](UIManager &uiManager) { /* Not implemented. */ },
[]() { /* Not implemented. */ });
runtimeExecutor([this](jsi::Runtime &runtime) {
JSIInstallFabricUIManager(runtime, *uiManager_);
});
auto eventDispatcher = std::make_shared<EventDispatcher>(
std::bind(
@ -42,6 +50,7 @@ Scheduler::Scheduler(const SharedContextContainer &contextContainer)
componentDescriptorRegistry_ = ComponentDescriptorFactory::buildRegistry(
eventDispatcher, contextContainer);
uiManager_->setComponentDescriptorRegistry(
componentDescriptorRegistry_
);