diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK
index ce91a2147..a88b8ccb4 100644
--- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK
+++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK
@@ -28,6 +28,7 @@ rn_android_library(
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/devsupport:interfaces"),
react_native_target("java/com/facebook/react/fabric:fabric"),
+ react_native_target("java/com/facebook/react/jscexecutor:jscexecutor"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target("java/com/facebook/react/module/model:model"),
react_native_target("java/com/facebook/react/modules/core:core"),
diff --git a/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/BUCK b/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/BUCK
new file mode 100644
index 000000000..788857470
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/BUCK
@@ -0,0 +1,35 @@
+load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "FBJNI_TARGET", "rn_xplat_cxx_library", "react_native_xplat_target", "react_native_target", "react_native_dep", "rn_android_library")
+
+rn_android_library(
+ name = "jscexecutor",
+ srcs = glob(["*.java"]),
+ visibility = [
+ "PUBLIC",
+ ],
+ deps = [
+ ":jni",
+ react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
+ react_native_target("java/com/facebook/react/bridge:bridge"),
+ ],
+)
+
+rn_xplat_cxx_library(
+ name = "jni",
+ srcs = glob(["*.cpp"]),
+ headers = glob(["*.h"]),
+ header_namespace = "",
+ compiler_flags = ["-fexceptions"],
+ platforms = ANDROID,
+ fbandroid_allow_jni_merging = True,
+ soname = "libjscexecutor.$(ext)",
+ visibility = [
+ react_native_target("java/com/facebook/react/jscexecutor:jscexecutor"),
+ ],
+ deps = [
+ "xplat//folly:molly",
+ FBJNI_TARGET,
+ react_native_target("jni/react/jni:jni"),
+ react_native_xplat_target("jsi:JSCRuntime"),
+ react_native_xplat_target("jsiexecutor:jsiexecutor"),
+ ],
+)
diff --git a/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/JSCExecutor.java b/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/JSCExecutor.java
new file mode 100644
index 000000000..7455dffb4
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/JSCExecutor.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ *
This source code is licensed under the MIT license found in the LICENSE file in the root
+ * directory of this source tree.
+ */
+
+package com.facebook.react.jscexecutor;
+
+import com.facebook.jni.HybridData;
+import com.facebook.proguard.annotations.DoNotStrip;
+import com.facebook.react.bridge.JavaScriptExecutor;
+import com.facebook.react.bridge.ReadableNativeMap;
+import com.facebook.soloader.SoLoader;
+
+@DoNotStrip
+/* package */ class JSCExecutor extends JavaScriptExecutor {
+ static {
+ SoLoader.loadLibrary("jscexecutor");
+ }
+
+ /* package */ JSCExecutor(ReadableNativeMap jscConfig) {
+ super(initHybrid(jscConfig));
+ }
+
+ @Override
+ public String getName() {
+ return "JSCExecutor";
+ }
+
+ private static native HybridData initHybrid(ReadableNativeMap jscConfig);
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/JSCExecutorFactory.java b/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/JSCExecutorFactory.java
new file mode 100644
index 000000000..a71b9bcc8
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/JSCExecutorFactory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.facebook.react.jscexecutor;
+
+import com.facebook.react.bridge.JavaScriptExecutor;
+import com.facebook.react.bridge.JavaScriptExecutorFactory;
+import com.facebook.react.bridge.WritableNativeMap;
+
+public class JSCExecutorFactory implements JavaScriptExecutorFactory {
+ private final String mAppName;
+ private final String mDeviceName;
+
+ public JSCExecutorFactory(String appName, String deviceName) {
+ this.mAppName = appName;
+ this.mDeviceName = deviceName;
+ }
+
+ @Override
+ public JavaScriptExecutor create() throws Exception {
+ WritableNativeMap jscConfig = new WritableNativeMap();
+ jscConfig.putString("OwnerIdentity", "ReactNative");
+ jscConfig.putString("AppIdentity", mAppName);
+ jscConfig.putString("DeviceIdentity", mDeviceName);
+ return new JSCExecutor(jscConfig);
+ }
+
+ @Override
+ public String toString() {
+ return "JSIExecutor+JSCRuntime";
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/OnLoad.cpp b/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/OnLoad.cpp
new file mode 100644
index 000000000..c6f185aac
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/OnLoad.cpp
@@ -0,0 +1,70 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace facebook {
+namespace react {
+
+namespace {
+
+class JSCExecutorFactory : public JSExecutorFactory {
+public:
+ std::unique_ptr createJSExecutor(
+ std::shared_ptr delegate,
+ std::shared_ptr jsQueue) override {
+ return folly::make_unique(
+ jsc::makeJSCRuntime(),
+ delegate,
+ [](const std::string& message, unsigned int logLevel) {
+ reactAndroidLoggingHook(message, logLevel);
+ },
+ JSIExecutor::defaultTimeoutInvoker,
+ nullptr);
+ }
+};
+
+}
+
+// This is not like JSCJavaScriptExecutor, which calls JSC directly. This uses
+// JSIExecutor with JSCRuntime.
+class JSCExecutorHolder
+ : public jni::HybridClass {
+ public:
+ static constexpr auto kJavaDescriptor = "Lcom/facebook/react/jscexecutor/JSCExecutor;";
+
+ static jni::local_ref initHybrid(
+ jni::alias_ref, ReadableNativeMap*) {
+ // This is kind of a weird place for stuff, but there's no other
+ // good place for initialization which is specific to JSC on
+ // Android.
+ JReactMarker::setLogPerfMarkerIfNeeded();
+ // TODO mhorowitz T28461666 fill in some missing nice to have glue
+ return makeCxxInstance(folly::make_unique());
+ }
+
+ static void registerNatives() {
+ registerHybrid({
+ makeNativeMethod("initHybrid", JSCExecutorHolder::initHybrid),
+ });
+ }
+
+ private:
+ friend HybridBase;
+ using HybridBase::HybridBase;
+};
+
+} // namespace react
+} // namespace facebook
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
+ return facebook::jni::initialize(vm, [] {
+ facebook::react::JSCExecutorHolder::registerNatives();
+ });
+}
diff --git a/ReactCommon/jsiexecutor/BUCK b/ReactCommon/jsiexecutor/BUCK
new file mode 100644
index 000000000..e6e6334be
--- /dev/null
+++ b/ReactCommon/jsiexecutor/BUCK
@@ -0,0 +1,39 @@
+load("//tools/build_defs/oss:rn_defs.bzl", "cxx_library", "react_native_xplat_dep", "react_native_xplat_target")
+
+cxx_library(
+ name = "jsiexecutor",
+ srcs = [
+ "jsireact/JSIExecutor.cpp",
+ "jsireact/JSINativeModules.cpp",
+ ],
+ header_namespace = "",
+ exported_headers = {
+ "jsireact/JSIExecutor.h": "jsireact/JSIExecutor.h",
+ "jsireact/JSINativeModules.h": "jsireact/JSINativeModules.h",
+ },
+ compiler_flags = [
+ "-fexceptions",
+ "-frtti",
+ ],
+ fbandroid_deps = [
+ "xplat//folly:molly",
+ "xplat//third-party/glog:glog",
+ "xplat//third-party/linker_lib:atomic",
+ ],
+ fbobjc_force_static = True,
+ fbobjc_header_path_prefix = "",
+ platforms = (ANDROID, APPLE),
+ preprocessor_flags = [
+ "-DLOG_TAG=\"ReactNative\"",
+ "-DWITH_FBSYSTRACE=1",
+ ],
+ visibility = [
+ "PUBLIC",
+ ],
+ xcode_public_headers_symlinks = True,
+ deps = [
+ react_native_xplat_dep("jsi:jsi"),
+ react_native_xplat_dep("jsi:JSIDynamic"),
+ react_native_xplat_target("cxxreact:bridge"),
+ ],
+)
diff --git a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp
new file mode 100644
index 000000000..f243d8d7e
--- /dev/null
+++ b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp
@@ -0,0 +1,405 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#include "jsireact/JSIExecutor.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+using namespace facebook::jsi;
+
+namespace facebook {
+namespace react {
+
+JSIExecutorFactory::JSIExecutorFactory(
+ std::shared_ptr runtime,
+ JSIExecutor::Logger logger,
+ JSIExecutor::RuntimeInstaller runtimeInstaller)
+ : runtime_(runtime),
+ logger_(logger),
+ runtimeInstaller_(runtimeInstaller) {}
+
+std::unique_ptr JSIExecutorFactory::createJSExecutor(
+ std::shared_ptr delegate,
+ std::shared_ptr) {
+ return std::make_unique(
+ runtime_,
+ delegate,
+ logger_,
+ JSIExecutor::defaultTimeoutInvoker,
+ runtimeInstaller_);
+}
+
+class JSIExecutor::NativeModuleProxy : public jsi::HostObject {
+ public:
+ NativeModuleProxy(JSIExecutor& executor) : executor_(executor) {}
+
+ Value get(Runtime& rt, const PropNameID& name) override {
+ if (name.utf8(rt) == "name") {
+ return jsi::String::createFromAscii(rt, "NativeModules");
+ }
+
+ return executor_.nativeModules_.getModule(rt, name);
+ }
+
+ void set(Runtime&, const PropNameID&, const Value&) override {
+ throw std::runtime_error(
+ "Unable to put on NativeModules: Operation unsupported");
+ }
+
+ private:
+ JSIExecutor& executor_;
+};
+
+namespace {
+
+// basename_r isn't in all iOS SDKs, so use this simple version instead.
+std::string simpleBasename(const std::string& path) {
+ size_t pos = path.rfind("/");
+ return (pos != std::string::npos) ? path.substr(pos) : path;
+}
+
+} // namespace
+
+JSIExecutor::JSIExecutor(
+ std::shared_ptr runtime,
+ std::shared_ptr delegate,
+ Logger logger,
+ const JSIScopedTimeoutInvoker& scopedTimeoutInvoker,
+ RuntimeInstaller runtimeInstaller)
+ : runtime_(runtime),
+ delegate_(delegate),
+ nativeModules_(delegate ? delegate->getModuleRegistry() : nullptr),
+ logger_(logger),
+ scopedTimeoutInvoker_(scopedTimeoutInvoker),
+ runtimeInstaller_(runtimeInstaller) {
+ runtime_->global().setProperty(
+ *runtime, "__jsiExecutorDescription", runtime->description());
+}
+
+void JSIExecutor::loadApplicationScript(
+ std::unique_ptr script,
+ std::string sourceURL) {
+ SystraceSection s("JSIExecutor::loadApplicationScript");
+
+ // TODO: check for and use precompiled HBC
+
+ runtime_->global().setProperty(
+ *runtime_,
+ "nativeModuleProxy",
+ Object::createFromHostObject(
+ *runtime_, std::make_shared(*this)));
+
+ runtime_->global().setProperty(
+ *runtime_,
+ "nativeFlushQueueImmediate",
+ Function::createFromHostFunction(
+ *runtime_,
+ PropNameID::forAscii(*runtime_, "nativeFlushQueueImmediate"),
+ 1,
+ [this](
+ jsi::Runtime&,
+ const jsi::Value&,
+ const jsi::Value* args,
+ size_t count) {
+ if (count != 1) {
+ throw std::invalid_argument(
+ "nativeFlushQueueImmediate arg count must be 1");
+ }
+ callNativeModules(args[0], false);
+ return Value::undefined();
+ }));
+
+ runtime_->global().setProperty(
+ *runtime_,
+ "nativeCallSyncHook",
+ Function::createFromHostFunction(
+ *runtime_,
+ PropNameID::forAscii(*runtime_, "nativeCallSyncHook"),
+ 1,
+ [this](
+ jsi::Runtime&,
+ const jsi::Value&,
+ const jsi::Value* args,
+ size_t count) { return nativeCallSyncHook(args, count); }));
+
+ if (logger_) {
+ // Only inject the logging function if it was supplied by the caller.
+ runtime_->global().setProperty(
+ *runtime_,
+ "nativeLoggingHook",
+ Function::createFromHostFunction(
+ *runtime_,
+ PropNameID::forAscii(*runtime_, "nativeLoggingHook"),
+ 2,
+ [this](
+ jsi::Runtime&,
+ const jsi::Value&,
+ const jsi::Value* args,
+ size_t count) {
+ if (count != 2) {
+ throw std::invalid_argument(
+ "nativeLoggingHook takes 2 arguments");
+ }
+ logger_(
+ args[0].asString(*runtime_).utf8(*runtime_),
+ folly::to(args[1].asNumber()));
+ return Value::undefined();
+ }));
+ }
+
+ if (runtimeInstaller_) {
+ runtimeInstaller_(*runtime_);
+ }
+
+ bool hasLogger(ReactMarker::logTaggedMarker);
+ std::string scriptName = simpleBasename(sourceURL);
+ if (hasLogger) {
+ ReactMarker::logTaggedMarker(
+ ReactMarker::RUN_JS_BUNDLE_START, scriptName.c_str());
+ }
+ runtime_->evaluateJavaScript(
+ std::make_unique(std::move(script)), sourceURL);
+ flush();
+ if (hasLogger) {
+ ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP);
+ ReactMarker::logTaggedMarker(
+ ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
+ }
+}
+
+void JSIExecutor::setBundleRegistry(std::unique_ptr r) {
+ if (!bundleRegistry_) {
+ runtime_->global().setProperty(
+ *runtime_,
+ "nativeRequire",
+ Function::createFromHostFunction(
+ *runtime_,
+ PropNameID::forAscii(*runtime_, "nativeRequire"),
+ 2,
+ [this](
+ Runtime& rt,
+ const facebook::jsi::Value&,
+ const facebook::jsi::Value* args,
+ size_t count) { return nativeRequire(args, count); }));
+ }
+ bundleRegistry_ = std::move(r);
+}
+
+void JSIExecutor::registerBundle(
+ uint32_t bundleId,
+ const std::string& bundlePath) {
+ const auto tag = folly::to(bundleId);
+ ReactMarker::logTaggedMarker(
+ ReactMarker::REGISTER_JS_SEGMENT_START, tag.c_str());
+ if (bundleRegistry_) {
+ bundleRegistry_->registerBundle(bundleId, bundlePath);
+ } else {
+ auto script = JSBigFileString::fromPath(bundlePath);
+ runtime_->evaluateJavaScript(
+ std::make_unique(std::move(script)),
+ JSExecutor::getSyntheticBundlePath(bundleId, bundlePath));
+ }
+ ReactMarker::logTaggedMarker(
+ ReactMarker::REGISTER_JS_SEGMENT_STOP, tag.c_str());
+}
+
+void JSIExecutor::callFunction(
+ const std::string& moduleId,
+ const std::string& methodId,
+ const folly::dynamic& arguments) {
+ SystraceSection s(
+ "JSIExecutor::callFunction", "moduleId", moduleId, "methodId", methodId);
+ if (!callFunctionReturnFlushedQueue_) {
+ bindBridge();
+ }
+
+ // Construct the error message producer in case this times out.
+ // This is executed on a background thread, so it must capture its parameters
+ // by value.
+ auto errorProducer = [=] {
+ std::stringstream ss;
+ ss << "moduleID: " << moduleId << " methodID: " << methodId
+ << " arguments: " << folly::toJson(arguments);
+ return ss.str();
+ };
+
+ Value ret = Value::undefined();
+ try {
+ scopedTimeoutInvoker_(
+ [&] {
+ ret = callFunctionReturnFlushedQueue_->call(
+ *runtime_,
+ moduleId,
+ methodId,
+ valueFromDynamic(*runtime_, arguments));
+ },
+ std::move(errorProducer));
+ } catch (...) {
+ std::throw_with_nested(
+ std::runtime_error("Error calling " + moduleId + "." + methodId));
+ }
+
+ callNativeModules(ret, true);
+}
+
+void JSIExecutor::invokeCallback(
+ const double callbackId,
+ const folly::dynamic& arguments) {
+ SystraceSection s("JSIExecutor::invokeCallback", "callbackId", callbackId);
+ if (!invokeCallbackAndReturnFlushedQueue_) {
+ bindBridge();
+ }
+ Value ret;
+ try {
+ ret = invokeCallbackAndReturnFlushedQueue_->call(
+ *runtime_, callbackId, valueFromDynamic(*runtime_, arguments));
+ } catch (...) {
+ std::throw_with_nested(std::runtime_error(
+ folly::to("Error invoking callback ", callbackId)));
+ }
+
+ callNativeModules(ret, true);
+}
+
+void JSIExecutor::setGlobalVariable(
+ std::string propName,
+ std::unique_ptr jsonValue) {
+ SystraceSection s("JSIExecutor::setGlobalVariable", "propName", propName);
+ runtime_->global().setProperty(
+ *runtime_,
+ propName.c_str(),
+ Value::createFromJsonUtf8(
+ *runtime_,
+ reinterpret_cast(jsonValue->c_str()),
+ jsonValue->size()));
+}
+
+std::string JSIExecutor::getDescription() {
+ return "JSI " + runtime_->description();
+}
+
+void* JSIExecutor::getJavaScriptContext() {
+ return runtime_.get();
+}
+
+bool JSIExecutor::isInspectable() {
+ return runtime_->isInspectable();
+}
+
+void JSIExecutor::bindBridge() {
+ std::call_once(bindFlag_, [this] {
+ SystraceSection s("JSIExecutor::bindBridge (once)");
+ Value batchedBridgeValue =
+ runtime_->global().getProperty(*runtime_, "__fbBatchedBridge");
+ if (batchedBridgeValue.isUndefined()) {
+ Function requireBatchedBridge = runtime_->global().getPropertyAsFunction(
+ *runtime_, "__fbRequireBatchedBridge");
+ batchedBridgeValue = requireBatchedBridge.call(*runtime_);
+ if (batchedBridgeValue.isUndefined()) {
+ throw JSINativeException(
+ "Could not get BatchedBridge, make sure your bundle is packaged correctly");
+ }
+ }
+
+ Object batchedBridge = batchedBridgeValue.asObject(*runtime_);
+ callFunctionReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
+ *runtime_, "callFunctionReturnFlushedQueue");
+ invokeCallbackAndReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
+ *runtime_, "invokeCallbackAndReturnFlushedQueue");
+ flushedQueue_ =
+ batchedBridge.getPropertyAsFunction(*runtime_, "flushedQueue");
+ callFunctionReturnResultAndFlushedQueue_ =
+ batchedBridge.getPropertyAsFunction(
+ *runtime_, "callFunctionReturnResultAndFlushedQueue");
+ });
+}
+
+void JSIExecutor::callNativeModules(const Value& queue, bool isEndOfBatch) {
+ SystraceSection s("JSIExecutor::callNativeModules");
+ // If this fails, you need to pass a fully functional delegate with a
+ // module registry to the factory/ctor.
+ CHECK(delegate_) << "Attempting to use native modules without a delegate";
+#if 0 // maybe useful for debugging
+ std::string json = runtime_->global().getPropertyAsObject(*runtime_, "JSON")
+ .getPropertyAsFunction(*runtime_, "stringify").call(*runtime_, queue)
+ .getString(*runtime_).utf8(*runtime_);
+#endif
+ delegate_->callNativeModules(
+ *this, dynamicFromValue(*runtime_, queue), isEndOfBatch);
+}
+
+void JSIExecutor::flush() {
+ SystraceSection s("JSIExecutor::flush");
+ if (flushedQueue_) {
+ callNativeModules(flushedQueue_->call(*runtime_), true);
+ return;
+ }
+
+ // When a native module is called from JS, BatchedBridge.enqueueNativeCall()
+ // is invoked. For that to work, require('BatchedBridge') has to be called,
+ // and when that happens, __fbBatchedBridge is set as a side effect.
+ Value batchedBridge =
+ runtime_->global().getProperty(*runtime_, "__fbBatchedBridge");
+ // So here, if __fbBatchedBridge doesn't exist, then we know no native calls
+ // have happened, and we were able to determine this without forcing
+ // BatchedBridge to be loaded as a side effect.
+ if (!batchedBridge.isUndefined()) {
+ // If calls were made, we bind to the JS bridge methods, and use them to
+ // get the pending queue of native calls.
+ bindBridge();
+ callNativeModules(flushedQueue_->call(*runtime_), true);
+ } else if (delegate_) {
+ // If we have a delegate, we need to call it; we pass a null list to
+ // callNativeModules, since we know there are no native calls, without
+ // calling into JS again. If no calls were made and there's no delegate,
+ // nothing happens, which is correct.
+ callNativeModules(nullptr, true);
+ }
+}
+
+Value JSIExecutor::nativeRequire(const Value* args, size_t count) {
+ if (count > 2 || count == 0) {
+ throw std::invalid_argument("Got wrong number of args");
+ }
+
+ uint32_t moduleId = folly::to(args[0].getNumber());
+ uint32_t bundleId = count == 2 ? folly::to(args[1].getNumber()) : 0;
+ auto module = bundleRegistry_->getModule(bundleId, moduleId);
+
+ runtime_->evaluateJavaScript(
+ std::make_unique(module.code), module.name);
+ return facebook::jsi::Value();
+}
+
+Value JSIExecutor::nativeCallSyncHook(const Value* args, size_t count) {
+ if (count != 3) {
+ throw std::invalid_argument("nativeCallSyncHook arg count must be 3");
+ }
+
+ if (!args[2].asObject(*runtime_).isArray(*runtime_)) {
+ throw std::invalid_argument(
+ folly::to("method parameters should be array"));
+ }
+
+ MethodCallResult result = delegate_->callSerializableNativeHook(
+ *this,
+ static_cast(args[0].getNumber()), // moduleId
+ static_cast(args[1].getNumber()), // methodId
+ dynamicFromValue(*runtime_, args[2])); // args
+
+ if (!result.hasValue()) {
+ return Value::undefined();
+ }
+ return valueFromDynamic(*runtime_, result.value());
+}
+
+} // namespace react
+} // namespace facebook
diff --git a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.h b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.h
new file mode 100644
index 000000000..51c1d86b8
--- /dev/null
+++ b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.h
@@ -0,0 +1,149 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#pragma once
+
+#include "JSINativeModules.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace facebook {
+namespace react {
+
+// A JSIScopedTimeoutInvoker is a trampoline-type function for introducing
+// timeouts. Call the TimeoutInvoker with a function to execute, the invokee.
+// The TimeoutInvoker will immediately invoke it, synchronously on the same
+// thread. If the invokee fails to return after some timeout (private to the
+// TimeoutInvoker), a soft error may be reported.
+//
+// If a soft error is reported, the second parameter errorMessageProducer will
+// be invoked to produce an error message, which will be included in the soft
+// error report. Note that the errorMessageProducer will be invoked
+// asynchronously on a different thread.
+//
+// The timeout behavior does NOT caues the invokee to aborted. If the invokee
+// blocks forever, so will the ScopedTimeoutInvoker (but the soft error may
+// still be reported).
+//
+// The invokee is passed by const ref because it is executed synchronously, but
+// the errorMessageProducer is passed by value because it must be copied or
+// moved for async execution.
+//
+// Example usage:
+//
+// int param = ...;
+// timeoutInvoker(
+// [&]{ someBigWork(param); },
+// [=] -> std::string {
+// return "someBigWork, param " + std::to_string(param);
+// })
+//
+using JSIScopedTimeoutInvoker = std::function& invokee,
+ std::function errorMessageProducer)>;
+
+class BigStringBuffer : public jsi::Buffer {
+ public:
+ BigStringBuffer(std::unique_ptr script)
+ : script_(std::move(script)) {}
+
+ size_t size() const override {
+ return script_->size();
+ }
+
+ const uint8_t* data() const override {
+ return reinterpret_cast(script_->c_str());
+ }
+
+ private:
+ std::unique_ptr script_;
+};
+
+class JSIExecutor : public JSExecutor {
+ public:
+ using Logger =
+ std::function;
+
+ using RuntimeInstaller = std::function;
+
+ JSIExecutor(
+ std::shared_ptr runtime,
+ std::shared_ptr delegate,
+ Logger logger,
+ const JSIScopedTimeoutInvoker& timeoutInvoker,
+ RuntimeInstaller runtimeInstaller);
+ void loadApplicationScript(
+ std::unique_ptr script,
+ std::string sourceURL) override;
+ void setBundleRegistry(std::unique_ptr) override;
+ void registerBundle(uint32_t bundleId, const std::string& bundlePath)
+ override;
+ void callFunction(
+ const std::string& moduleId,
+ const std::string& methodId,
+ const folly::dynamic& arguments) override;
+ void invokeCallback(const double callbackId, const folly::dynamic& arguments)
+ override;
+ void setGlobalVariable(
+ std::string propName,
+ std::unique_ptr jsonValue) override;
+ std::string getDescription() override;
+ void* getJavaScriptContext() override;
+ bool isInspectable() override;
+
+ // An implementation of JSIScopedTimeoutInvoker that simply runs the
+ // invokee, with no timeout.
+ static void defaultTimeoutInvoker(
+ const std::function& invokee,
+ std::function errorMessageProducer) {
+ (void)errorMessageProducer;
+ invokee();
+ }
+
+ private:
+ class NativeModuleProxy;
+
+ void flush();
+ void bindBridge();
+ void callNativeModules(const jsi::Value& queue, bool isEndOfBatch);
+ jsi::Value nativeCallSyncHook(const jsi::Value* args, size_t count);
+ jsi::Value nativeRequire(const jsi::Value* args, size_t count);
+
+ std::shared_ptr runtime_;
+ std::shared_ptr delegate_;
+ JSINativeModules nativeModules_;
+ std::once_flag bindFlag_;
+ std::unique_ptr bundleRegistry_;
+ Logger logger_;
+ JSIScopedTimeoutInvoker scopedTimeoutInvoker_;
+ RuntimeInstaller runtimeInstaller_;
+
+ folly::Optional callFunctionReturnFlushedQueue_;
+ folly::Optional invokeCallbackAndReturnFlushedQueue_;
+ folly::Optional flushedQueue_;
+ folly::Optional callFunctionReturnResultAndFlushedQueue_;
+};
+
+class JSIExecutorFactory : public JSExecutorFactory {
+ public:
+ explicit JSIExecutorFactory(
+ std::shared_ptr runtime,
+ JSIExecutor::Logger logger,
+ JSIExecutor::RuntimeInstaller);
+
+ std::unique_ptr createJSExecutor(
+ std::shared_ptr delegate,
+ std::shared_ptr jsQueue) override;
+
+ private:
+ std::shared_ptr runtime_;
+ JSIExecutor::Logger logger_;
+ JSIExecutor::RuntimeInstaller runtimeInstaller_;
+};
+
+} // namespace react
+} // namespace facebook
diff --git a/ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp b/ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp
new file mode 100644
index 000000000..7a5e0673c
--- /dev/null
+++ b/ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp
@@ -0,0 +1,86 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#include "jsireact/JSINativeModules.h"
+
+#include
+
+#include
+
+#include
+
+using namespace facebook::jsi;
+
+namespace facebook {
+namespace react {
+
+JSINativeModules::JSINativeModules(
+ std::shared_ptr moduleRegistry)
+ : m_moduleRegistry(std::move(moduleRegistry)) {}
+
+Value JSINativeModules::getModule(Runtime& rt, const PropNameID& name) {
+ if (!m_moduleRegistry) {
+ return nullptr;
+ }
+
+ std::string moduleName = name.utf8(rt);
+
+ const auto it = m_objects.find(moduleName);
+ if (it != m_objects.end()) {
+ return Value(rt, it->second);
+ }
+
+ auto module = createModule(rt, moduleName);
+ if (!module.hasValue()) {
+ // Allow lookup to continue in the objects own properties, which allows for
+ // overrides of NativeModules
+ return nullptr;
+ }
+
+ auto result =
+ m_objects.emplace(std::move(moduleName), std::move(*module)).first;
+ return Value(rt, result->second);
+}
+
+void JSINativeModules::reset() {
+ m_genNativeModuleJS = nullptr;
+ m_objects.clear();
+}
+
+folly::Optional