Moved TurboModule C++ core to github
Summary: This is only the core C++ part of TurboModule - moving to github to make integration with existing NativeModules system slightly easier. Other bindings for iOS/Android are not yet ready to move. Notes: * TurboModules is not ready to use at the moment. * Build configuration is not yet provided (cocoapods/.xcodeproj/gradle), just like Fabric. * No effort was done to make this lib C++17 strictly compliant yet (there will be in the future). Reviewed By: RSNara Differential Revision: D13551211 fbshipit-source-id: cd3b458e6746ee9218451962ca65b1ad641a32db
This commit is contained in:
Родитель
b748e83fbf
Коммит
acc2ed2488
|
@ -0,0 +1,46 @@
|
|||
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_debug_preprocessor_flags")
|
||||
load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "get_apple_compiler_flags", "get_apple_inspector_flags", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob")
|
||||
|
||||
APPLE_COMPILER_FLAGS = get_apple_compiler_flags()
|
||||
|
||||
rn_xplat_cxx_library(
|
||||
name = "core",
|
||||
srcs = glob(["**/*.cpp"]),
|
||||
header_namespace = "",
|
||||
exported_headers = subdir_glob(
|
||||
[
|
||||
("", "**/*.h"),
|
||||
],
|
||||
prefix = "jsireact",
|
||||
),
|
||||
compiler_flags = [
|
||||
"-fexceptions",
|
||||
"-frtti",
|
||||
"-std=c++14",
|
||||
"-Wall",
|
||||
],
|
||||
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
|
||||
fbobjc_preprocessor_flags = get_debug_preprocessor_flags() + get_apple_inspector_flags(),
|
||||
force_static = True,
|
||||
platforms = (ANDROID, APPLE),
|
||||
preprocessor_flags = [
|
||||
"-DLOG_TAG=\"ReactNative\"",
|
||||
"-DWITH_FBSYSTRACE=1",
|
||||
],
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
],
|
||||
deps = [
|
||||
"xplat//fbsystrace:fbsystrace",
|
||||
"xplat//folly:headers_only",
|
||||
"xplat//folly:memory",
|
||||
"xplat//folly:molly",
|
||||
"xplat//jsi:JSIDynamic",
|
||||
"xplat//third-party/glog:glog",
|
||||
react_native_xplat_target("cxxreact:bridge"),
|
||||
react_native_xplat_target("cxxreact:module"),
|
||||
],
|
||||
exported_deps = [
|
||||
"xplat//jsi:jsi",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "JSCallInvoker.h"
|
||||
|
||||
#include <cxxreact/MessageQueueThread.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
JSCallInvoker::JSCallInvoker(std::shared_ptr<MessageQueueThread> jsThread)
|
||||
: jsThread_(jsThread) {}
|
||||
|
||||
void JSCallInvoker::invokeAsync(std::function<void()>&& func) {
|
||||
jsThread_->runOnQueue(std::move(func));
|
||||
}
|
||||
|
||||
void JSCallInvoker::invokeSync(std::function<void()>&& func) {
|
||||
jsThread_->runOnQueueSync(std::move(func));
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class MessageQueueThread;
|
||||
|
||||
/**
|
||||
* A generic native-to-JS call invoker. It guarantees that any calls from any
|
||||
* thread are queued on the right JS thread.
|
||||
*
|
||||
* For now, this is a thin-wrapper around existing MessageQueueThread. Eventually,
|
||||
* it should be consolidated with Fabric implementation so there's only one
|
||||
* API to call JS from native, whether synchronously or asynchronously.
|
||||
*/
|
||||
class JSCallInvoker {
|
||||
public:
|
||||
JSCallInvoker(std::shared_ptr<MessageQueueThread> jsThread);
|
||||
|
||||
void invokeAsync(std::function<void()>&& func);
|
||||
void invokeSync(std::function<void()>&& func);
|
||||
|
||||
private:
|
||||
std::shared_ptr<MessageQueueThread> jsThread_;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "LongLivedObject.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
// LongLivedObjectCollection
|
||||
LongLivedObjectCollection& LongLivedObjectCollection::get() {
|
||||
static LongLivedObjectCollection instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
LongLivedObjectCollection::LongLivedObjectCollection() {}
|
||||
|
||||
void LongLivedObjectCollection::add(std::shared_ptr<LongLivedObject> so) {
|
||||
collection_.insert(so);
|
||||
}
|
||||
|
||||
void LongLivedObjectCollection::remove(const LongLivedObject *o) {
|
||||
auto p = collection_.begin();
|
||||
for (; p != collection_.end(); p++) {
|
||||
if (p->get() == o) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (p != collection_.end()) {
|
||||
collection_.erase(p);
|
||||
}
|
||||
}
|
||||
|
||||
void LongLivedObjectCollection::clear() {
|
||||
collection_.clear();
|
||||
}
|
||||
|
||||
// LongLivedObject
|
||||
LongLivedObject::LongLivedObject() {}
|
||||
|
||||
void LongLivedObject::allowRelease() {
|
||||
LongLivedObjectCollection::get().remove(this);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/**
|
||||
* A simple wrapper class that can be registered to a collection that keep it alive for extended period of time.
|
||||
* This object can be removed from the collection when needed.
|
||||
*
|
||||
* The subclass of this class must be created using std::make_shared<T>().
|
||||
* After creation, add it to the `LongLivedObjectCollection`.
|
||||
* When done with the object, call `allowRelease()` to allow the OS to release it.
|
||||
*/
|
||||
class LongLivedObject {
|
||||
public:
|
||||
void allowRelease();
|
||||
|
||||
protected:
|
||||
LongLivedObject();
|
||||
};
|
||||
|
||||
/**
|
||||
* A singleton collection for the `LongLivedObject`s.
|
||||
*/
|
||||
class LongLivedObjectCollection {
|
||||
public:
|
||||
static LongLivedObjectCollection& get();
|
||||
|
||||
LongLivedObjectCollection(LongLivedObjectCollection const&) = delete;
|
||||
void operator=(LongLivedObjectCollection const&) = delete;
|
||||
|
||||
void add(std::shared_ptr<LongLivedObject> o);
|
||||
void remove(const LongLivedObject *o);
|
||||
void clear();
|
||||
|
||||
private:
|
||||
LongLivedObjectCollection();
|
||||
std::unordered_set<std::shared_ptr<LongLivedObject>> collection_;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "TurboCxxModule.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <jsi/JSIDynamic.h>
|
||||
#include <jsireact/TurboModuleUtils.h>
|
||||
|
||||
using namespace facebook;
|
||||
using namespace facebook::xplat::module;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
static CxxModule::Callback makeTurboCxxModuleCallback(
|
||||
jsi::Runtime &runtime,
|
||||
std::shared_ptr<CallbackWrapper> callbackWrapper) {
|
||||
return [callbackWrapper](std::vector<folly::dynamic> args) {
|
||||
callbackWrapper->jsInvoker->invokeAsync([callbackWrapper, args]() {
|
||||
std::vector<jsi::Value> innerArgs;
|
||||
for (auto &a : args) {
|
||||
innerArgs.push_back(jsi::valueFromDynamic(callbackWrapper->runtime, a));
|
||||
}
|
||||
callbackWrapper->callback.call(callbackWrapper->runtime, (const jsi::Value *)innerArgs.data(), innerArgs.size());
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
TurboCxxModule::TurboCxxModule(std::unique_ptr<CxxModule> cxxModule, std::shared_ptr<JSCallInvoker> jsInvoker)
|
||||
: TurboModule(cxxModule->getName(), jsInvoker),
|
||||
cxxMethods_(cxxModule->getMethods()),
|
||||
cxxModule_(std::move(cxxModule)) {}
|
||||
|
||||
jsi::Value TurboCxxModule::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
|
||||
std::string propNameUtf8 = propName.utf8(runtime);
|
||||
|
||||
if (propNameUtf8 == "getConstants") {
|
||||
// This is special cased because `getConstants()` is already a part of CxxModule.
|
||||
return jsi::Function::createFromHostFunction(
|
||||
runtime,
|
||||
propName,
|
||||
0,
|
||||
[this](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) {
|
||||
jsi::Object result(rt);
|
||||
auto constants = cxxModule_->getConstants();
|
||||
for (auto &pair : constants) {
|
||||
result.setProperty(rt, pair.first.c_str(), jsi::valueFromDynamic(rt, pair.second));
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
for (auto &method : cxxMethods_) {
|
||||
if (method.name == propNameUtf8) {
|
||||
return jsi::Function::createFromHostFunction(
|
||||
runtime,
|
||||
propName,
|
||||
0,
|
||||
[this, &propNameUtf8](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) {
|
||||
return invokeMethod(rt, VoidKind, propNameUtf8, args, count);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("Function '" + propNameUtf8 + "' cannot be found on cxxmodule: " + name_);
|
||||
}
|
||||
|
||||
jsi::Value TurboCxxModule::invokeMethod(
|
||||
jsi::Runtime &runtime,
|
||||
TurboModuleMethodValueKind valueKind,
|
||||
const std::string &methodName,
|
||||
const jsi::Value *args,
|
||||
size_t count) {
|
||||
|
||||
auto it = cxxMethods_.begin();
|
||||
for (; it != cxxMethods_.end(); it++) {
|
||||
auto method = *it;
|
||||
if (method.name == methodName) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (it == cxxMethods_.end()) {
|
||||
throw std::runtime_error("Function '" + methodName + "' cannot be found on cxxmodule: " + name_);
|
||||
}
|
||||
|
||||
auto method = *it;
|
||||
|
||||
if (method.syncFunc) {
|
||||
auto innerArgs = folly::dynamic::array();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
innerArgs.push_back(jsi::dynamicFromValue(runtime, args[i]));
|
||||
}
|
||||
return jsi::valueFromDynamic(runtime, method.syncFunc(std::move(innerArgs)));
|
||||
} else if (method.func && !method.isPromise) {
|
||||
// Async method.
|
||||
CxxModule::Callback first;
|
||||
CxxModule::Callback second;
|
||||
|
||||
if (count < method.callbacks) {
|
||||
throw std::invalid_argument(folly::to<std::string>("Expected ", method.callbacks,
|
||||
" callbacks, but only ", count, " parameters provided"));
|
||||
}
|
||||
|
||||
if (method.callbacks == 1) {
|
||||
auto wrapper = std::make_shared<CallbackWrapper>(args[count - 1].getObject(runtime).getFunction(runtime), runtime, jsInvoker_);
|
||||
first = makeTurboCxxModuleCallback(runtime, wrapper);
|
||||
} else if (method.callbacks == 2) {
|
||||
auto wrapper1 = std::make_shared<CallbackWrapper>(args[count - 2].getObject(runtime).getFunction(runtime), runtime, jsInvoker_);
|
||||
auto wrapper2 = std::make_shared<CallbackWrapper>(args[count - 1].getObject(runtime).getFunction(runtime), runtime, jsInvoker_);
|
||||
first = makeTurboCxxModuleCallback(runtime, wrapper1);
|
||||
second = makeTurboCxxModuleCallback(runtime, wrapper2);
|
||||
}
|
||||
|
||||
auto innerArgs = folly::dynamic::array();
|
||||
for (size_t i = 0; i < count - method.callbacks; i++) {
|
||||
innerArgs.push_back(jsi::dynamicFromValue(runtime, args[i]));
|
||||
}
|
||||
|
||||
method.func(std::move(innerArgs), first, second);
|
||||
} else if (method.isPromise) {
|
||||
return createPromiseAsJSIValue(runtime, [method, args, count, this](jsi::Runtime &rt, std::shared_ptr<Promise> promise) {
|
||||
auto resolveWrapper = std::make_shared<CallbackWrapper>(promise->resolve_.getFunction(rt), rt, jsInvoker_);
|
||||
auto rejectWrapper = std::make_shared<CallbackWrapper>(promise->reject_.getFunction(rt), rt, jsInvoker_);
|
||||
CxxModule::Callback resolve = makeTurboCxxModuleCallback(rt, resolveWrapper);
|
||||
CxxModule::Callback reject = makeTurboCxxModuleCallback(rt, rejectWrapper);
|
||||
|
||||
auto innerArgs = folly::dynamic::array();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
innerArgs.push_back(jsi::dynamicFromValue(rt, args[i]));
|
||||
}
|
||||
|
||||
method.func(std::move(innerArgs), resolve, reject);
|
||||
});
|
||||
}
|
||||
|
||||
return jsi::Value::undefined();
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <cxxreact/CxxModule.h>
|
||||
|
||||
#include "TurboModule.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/**
|
||||
* A helper class to convert the legacy CxxModule instance to a TurboModule instance.
|
||||
* This should be used only for migration purpose (to TurboModule), since it's not very performant
|
||||
* due to a lot of back-and-forth value conversions between folly::dynamic and jsi::Value.
|
||||
*/
|
||||
class JSI_EXPORT TurboCxxModule : public TurboModule {
|
||||
public:
|
||||
TurboCxxModule(std::unique_ptr<facebook::xplat::module::CxxModule> cxxModule, std::shared_ptr<JSCallInvoker> jsInvoker);
|
||||
|
||||
virtual facebook::jsi::Value get(facebook::jsi::Runtime& runtime, const facebook::jsi::PropNameID& propName) override;
|
||||
|
||||
virtual jsi::Value invokeMethod(
|
||||
jsi::Runtime &runtime,
|
||||
TurboModuleMethodValueKind valueKind,
|
||||
const std::string &methodName,
|
||||
const jsi::Value *args,
|
||||
size_t count) override;
|
||||
|
||||
private:
|
||||
std::vector<facebook::xplat::module::CxxModule::Method> cxxMethods_;
|
||||
std::unique_ptr<facebook::xplat::module::CxxModule> cxxModule_;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "TurboModule.h"
|
||||
|
||||
using namespace facebook;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
TurboModule::TurboModule(const std::string &name, std::shared_ptr<JSCallInvoker> jsInvoker)
|
||||
: name_(name),
|
||||
jsInvoker_(jsInvoker) {}
|
||||
|
||||
TurboModule::~TurboModule() {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void TurboModule::invalidate() {}
|
||||
|
||||
jsi::Value TurboModule::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
|
||||
std::string propNameUtf8 = propName.utf8(runtime);
|
||||
auto p = methodMap_.find(propNameUtf8);
|
||||
if (p == methodMap_.end()) {
|
||||
throw std::runtime_error("Function '" + propNameUtf8 + "' cannot be found on module: " + name_);
|
||||
}
|
||||
MethodMetadata meta = p->second;
|
||||
return jsi::Function::createFromHostFunction(
|
||||
runtime,
|
||||
propName,
|
||||
meta.argCount,
|
||||
[this, meta](facebook::jsi::Runtime &rt, const facebook::jsi::Value &thisVal, const facebook::jsi::Value *args, size_t count) {
|
||||
return meta.invoker(rt, *this, args, count);
|
||||
});
|
||||
}
|
||||
|
||||
jsi::Value TurboModule::invokeMethod(
|
||||
jsi::Runtime &runtime,
|
||||
TurboModuleMethodValueKind valueKind,
|
||||
const std::string &methodName,
|
||||
const jsi::Value *args,
|
||||
size_t count) {
|
||||
return jsi::Value::undefined();
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <jsi/jsi.h>
|
||||
|
||||
#include "JSCallInvoker.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/**
|
||||
* For now, support the same set of return types as existing impl.
|
||||
* This can be improved to support richer typed objects.
|
||||
*/
|
||||
enum TurboModuleMethodValueKind {
|
||||
VoidKind,
|
||||
BooleanKind,
|
||||
NumberKind,
|
||||
StringKind,
|
||||
ObjectKind,
|
||||
ArrayKind,
|
||||
FunctionKind,
|
||||
PromiseKind,
|
||||
};
|
||||
|
||||
/**
|
||||
* Base HostObject class for every module to be exposed to JS
|
||||
*/
|
||||
class JSI_EXPORT TurboModule : public facebook::jsi::HostObject {
|
||||
public:
|
||||
TurboModule(const std::string &name, std::shared_ptr<JSCallInvoker> jsInvoker);
|
||||
virtual ~TurboModule();
|
||||
|
||||
/**
|
||||
* Instruct this module to invalidate itself.
|
||||
*/
|
||||
virtual void invalidate();
|
||||
|
||||
virtual facebook::jsi::Value get(facebook::jsi::Runtime& runtime, const facebook::jsi::PropNameID& propName) override;
|
||||
|
||||
/**
|
||||
* General method invocation mechanism.
|
||||
* Each subclass decides how the invocation should be, and whether it should be platform-specific.
|
||||
*/
|
||||
virtual jsi::Value invokeMethod(
|
||||
jsi::Runtime &runtime,
|
||||
TurboModuleMethodValueKind valueKind,
|
||||
const std::string &methodName,
|
||||
const jsi::Value *args,
|
||||
size_t count);
|
||||
|
||||
const std::string name_;
|
||||
std::shared_ptr<JSCallInvoker> jsInvoker_;
|
||||
|
||||
protected:
|
||||
struct MethodMetadata {
|
||||
size_t argCount;
|
||||
facebook::jsi::Value (*invoker)(
|
||||
facebook::jsi::Runtime& rt,
|
||||
TurboModule &turboModule,
|
||||
const facebook::jsi::Value* args,
|
||||
size_t count);
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, MethodMetadata> methodMap_;
|
||||
};
|
||||
|
||||
/**
|
||||
* An app/platform-specific provider function to get an instance of a module given a name.
|
||||
*/
|
||||
using TurboModuleProviderFunctionType = std::function<std::shared_ptr<TurboModule>(
|
||||
const std::string &name)>;
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "TurboModuleBinding.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <cxxreact/SystraceSection.h>
|
||||
|
||||
using namespace facebook;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/**
|
||||
* Public API to install the TurboModule system.
|
||||
*/
|
||||
TurboModuleBinding::TurboModuleBinding(const TurboModuleProviderFunctionType &moduleProvider)
|
||||
: moduleProvider_(moduleProvider) {}
|
||||
|
||||
void TurboModuleBinding::install(
|
||||
jsi::Runtime &runtime,
|
||||
std::shared_ptr<TurboModuleBinding> binding) {
|
||||
runtime.global().setProperty(
|
||||
runtime,
|
||||
"__turboModuleProxy",
|
||||
jsi::Function::createFromHostFunction(
|
||||
runtime,
|
||||
jsi::PropNameID::forAscii(runtime, "__turboModuleProxy"),
|
||||
1,
|
||||
[binding](jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count) {
|
||||
return binding->jsProxy(rt, thisVal, args, count);
|
||||
}));
|
||||
}
|
||||
|
||||
void TurboModuleBinding::invalidate() const {
|
||||
// Nothing for now.
|
||||
}
|
||||
|
||||
std::shared_ptr<TurboModule> TurboModuleBinding::getModule(const std::string &name) {
|
||||
std::shared_ptr<TurboModule> module = nullptr;
|
||||
{
|
||||
SystraceSection s("TurboModuleBinding::getModule", "module", name);
|
||||
module = moduleProvider_(name);
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
jsi::Value TurboModuleBinding::jsProxy(
|
||||
jsi::Runtime& runtime,
|
||||
const jsi::Value& thisVal,
|
||||
const jsi::Value* args,
|
||||
size_t count) {
|
||||
if (count != 1) {
|
||||
throw std::invalid_argument("TurboModuleBinding::jsProxy arg count must be 1");
|
||||
}
|
||||
std::string moduleName = args[0].getString(runtime).utf8(runtime);
|
||||
std::shared_ptr<TurboModule> module = getModule(moduleName);
|
||||
|
||||
if (module == nullptr) {
|
||||
return jsi::Value::null();
|
||||
}
|
||||
|
||||
return jsi::Object::createFromHostObject(runtime, std::move(module));
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <jsi/jsi.h>
|
||||
#include <jsireact/TurboModule.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class JSCallInvoker;
|
||||
|
||||
/**
|
||||
* Represents the JavaScript binding for the TurboModule system.
|
||||
*/
|
||||
class TurboModuleBinding {
|
||||
public:
|
||||
/*
|
||||
* Installs TurboModuleBinding into JavaScript runtime.
|
||||
* Thread synchronization must be enforced externally.
|
||||
*/
|
||||
static void install(
|
||||
jsi::Runtime &runtime,
|
||||
std::shared_ptr<TurboModuleBinding> binding);
|
||||
|
||||
TurboModuleBinding(const TurboModuleProviderFunctionType &moduleProvider);
|
||||
|
||||
/*
|
||||
* Invalidates the binding.
|
||||
* Can be called in any thread.
|
||||
*/
|
||||
void invalidate() const;
|
||||
|
||||
/**
|
||||
* Get an TurboModule instance for the given module name.
|
||||
*/
|
||||
std::shared_ptr<TurboModule> getModule(const std::string &name);
|
||||
|
||||
private:
|
||||
/**
|
||||
* A lookup function exposed to JS to get an instance of a TurboModule
|
||||
* for the given name.
|
||||
*/
|
||||
jsi::Value jsProxy(
|
||||
jsi::Runtime& runtime,
|
||||
const jsi::Value& thisVal,
|
||||
const jsi::Value* args,
|
||||
size_t count);
|
||||
|
||||
TurboModuleProviderFunctionType moduleProvider_;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "TurboModuleUtils.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
static jsi::Value deepCopyJSIValue(jsi::Runtime &rt, const jsi::Value &value) {
|
||||
if (value.isNull()) {
|
||||
return jsi::Value::null();
|
||||
}
|
||||
|
||||
if (value.isBool()) {
|
||||
return jsi::Value(value.getBool());
|
||||
}
|
||||
|
||||
if (value.isNumber()) {
|
||||
return jsi::Value(value.getNumber());
|
||||
}
|
||||
|
||||
if (value.isString()) {
|
||||
return value.getString(rt);
|
||||
}
|
||||
|
||||
if (value.isObject()) {
|
||||
jsi::Object o = value.getObject(rt);
|
||||
if (o.isArray(rt)) {
|
||||
return deepCopyJSIArray(rt, o.getArray(rt));
|
||||
}
|
||||
if (o.isFunction(rt)) {
|
||||
return o.getFunction(rt);
|
||||
}
|
||||
return deepCopyJSIObject(rt, o);
|
||||
}
|
||||
|
||||
return jsi::Value::undefined();
|
||||
}
|
||||
|
||||
jsi::Object deepCopyJSIObject(jsi::Runtime &rt, const jsi::Object &obj) {
|
||||
jsi::Object copy(rt);
|
||||
jsi::Array propertyNames = obj.getPropertyNames(rt);
|
||||
size_t size = propertyNames.size(rt);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
jsi::String name = propertyNames.getValueAtIndex(rt, i).getString(rt);
|
||||
jsi::Value value = obj.getProperty(rt, name);
|
||||
copy.setProperty(rt, name, deepCopyJSIValue(rt, value));
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
jsi::Array deepCopyJSIArray(jsi::Runtime &rt, const jsi::Array &arr) {
|
||||
size_t size = arr.size(rt);
|
||||
jsi::Array copy(rt, size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
copy.setValueAtIndex(rt, i, deepCopyJSIValue(rt, arr.getValueAtIndex(rt, i)));
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
Promise::Promise(jsi::Runtime &rt, jsi::Function resolve, jsi::Function reject)
|
||||
: runtime_(rt),
|
||||
resolve_(std::move(resolve)),
|
||||
reject_(std::move(reject)) {}
|
||||
|
||||
void Promise::resolve(const jsi::Value &result) {
|
||||
resolve_.call(runtime_, result);
|
||||
}
|
||||
|
||||
void Promise::reject(const std::string &message) {
|
||||
jsi::Object error(runtime_);
|
||||
error.setProperty(runtime_, "message", jsi::String::createFromUtf8(runtime_, message));
|
||||
reject_.call(runtime_, error);
|
||||
}
|
||||
|
||||
jsi::Value createPromiseAsJSIValue(jsi::Runtime &rt, const PromiseSetupFunctionType func) {
|
||||
jsi::Function JSPromise = rt.global().getPropertyAsFunction(rt, "Promise");
|
||||
jsi::Function fn = jsi::Function::createFromHostFunction(
|
||||
rt,
|
||||
jsi::PropNameID::forAscii(rt, "fn"),
|
||||
2,
|
||||
[func](jsi::Runtime &rt2, const jsi::Value &thisVal, const jsi::Value *args, size_t count) {
|
||||
jsi::Function resolve = args[0].getObject(rt2).getFunction(rt2);
|
||||
jsi::Function reject = args[1].getObject(rt2).getFunction(rt2);
|
||||
auto wrapper = std::make_shared<Promise>(rt2, std::move(resolve), std::move(reject));
|
||||
func(rt2, wrapper);
|
||||
return jsi::Value::undefined();
|
||||
});
|
||||
|
||||
return JSPromise.callAsConstructor(rt, fn);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <jsi/jsi.h>
|
||||
|
||||
#include "JSCallInvoker.h"
|
||||
|
||||
using namespace facebook;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
jsi::Object deepCopyJSIObject(jsi::Runtime &rt, const jsi::Object &obj);
|
||||
jsi::Array deepCopyJSIArray(jsi::Runtime &rt, const jsi::Array &arr);
|
||||
|
||||
struct Promise {
|
||||
Promise(jsi::Runtime &rt, jsi::Function resolve, jsi::Function reject);
|
||||
|
||||
void resolve(const jsi::Value &result);
|
||||
void reject(const std::string &error);
|
||||
|
||||
jsi::Runtime &runtime_;
|
||||
jsi::Function resolve_;
|
||||
jsi::Function reject_;
|
||||
};
|
||||
|
||||
using PromiseSetupFunctionType = std::function<void(jsi::Runtime &rt, std::shared_ptr<Promise>)>;
|
||||
jsi::Value createPromiseAsJSIValue(jsi::Runtime &rt, const PromiseSetupFunctionType func);
|
||||
|
||||
// Helper for passing jsi::Function arg to other methods.
|
||||
struct CallbackWrapper {
|
||||
CallbackWrapper(jsi::Function callback, jsi::Runtime &runtime, std::shared_ptr<react::JSCallInvoker> jsInvoker)
|
||||
: callback(std::move(callback)),
|
||||
runtime(runtime),
|
||||
jsInvoker(jsInvoker) {}
|
||||
jsi::Function callback;
|
||||
jsi::Runtime &runtime;
|
||||
std::shared_ptr<react::JSCallInvoker> jsInvoker;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
Загрузка…
Ссылка в новой задаче