Authored by 张丽霞

add files

Showing 52 changed files with 4754 additions and 0 deletions

Too many changes to show.

To preserve performance only 52 of 52+ files are displayed.

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := reactnative
LOCAL_SRC_FILES := \
CxxNativeModule.cpp \
Instance.cpp \
JSBigString.cpp \
JSBundleType.cpp \
JSCExecutor.cpp \
JSCLegacyTracing.cpp \
JSCMemory.cpp \
JSCNativeModules.cpp \
JSCPerfStats.cpp \
JSCSamplingProfiler.cpp \
JSCTracing.cpp \
JSCUtils.cpp \
JSIndexedRAMBundle.cpp \
MethodCall.cpp \
ModuleRegistry.cpp \
NativeToJsBridge.cpp \
Platform.cpp \
RAMBundleRegistry.cpp \
LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_CFLAGS := \
-DLOG_TAG=\"ReactNative\"
LOCAL_CFLAGS += -Wall -Werror -fexceptions -frtti
CXX11_FLAGS := -std=c++11
LOCAL_CFLAGS += $(CXX11_FLAGS)
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
LOCAL_STATIC_LIBRARIES := jschelpers
LOCAL_SHARED_LIBRARIES := libfb libfolly_json libjsc libglog
include $(BUILD_STATIC_LIBRARY)
$(call import-module,fb)
$(call import-module,folly)
$(call import-module,jsc)
$(call import-module,glog)
$(call import-module,jschelpers)
$(call import-module,jsinspector)
$(call import-module,privatedata)
... ...
load("@xplat//configurations/buck/apple:flag_defs.bzl", "DEBUG_PREPROCESSOR_FLAGS")
load("//ReactNative:DEFS.bzl", "IS_OSS_BUILD", "rn_xplat_cxx_library", "ANDROID_INSPECTOR_FLAGS", "APPLE_INSPECTOR_FLAGS", "ANDROID_JSC_DEPS", "APPLE_JSC_DEPS", "react_native_xplat_target")
CXX_LIBRARY_COMPILER_FLAGS = [
"-std=c++14",
"-Wall",
]
APPLE_COMPILER_FLAGS = []
if not IS_OSS_BUILD:
load("@xplat//configurations/buck/apple:flag_defs.bzl", "STATIC_LIBRARY_IOS_FLAGS", "flags")
APPLE_COMPILER_FLAGS = flags.get_flag_value(STATIC_LIBRARY_IOS_FLAGS, 'compiler_flags')
rn_xplat_cxx_library(
name = "module",
header_namespace = "",
exported_headers = subdir_glob(
[
("", "CxxModule.h"),
("", "JsArgumentHelpers.h"),
("", "JsArgumentHelpers-inl.h"),
],
prefix = "cxxreact",
),
compiler_flags = CXX_LIBRARY_COMPILER_FLAGS,
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
force_static = True,
visibility = [
"PUBLIC",
],
deps = [
"xplat//folly:molly",
],
)
rn_xplat_cxx_library(
name = "jsbigstring",
srcs = [
"JSBigString.cpp",
],
header_namespace = "",
exported_headers = subdir_glob(
[("", "JSBigString.h")],
prefix = "cxxreact",
),
compiler_flags = CXX_LIBRARY_COMPILER_FLAGS + [
"-fexceptions",
"-frtti",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
force_static = True,
visibility = [
"PUBLIC",
],
deps = [
"xplat//folly:memory",
"xplat//folly:molly",
"xplat//folly:scope_guard",
],
)
rn_xplat_cxx_library(
name = "samplemodule",
srcs = ["SampleCxxModule.cpp"],
header_namespace = "",
exported_headers = ["SampleCxxModule.h"],
compiler_flags = CXX_LIBRARY_COMPILER_FLAGS + [
"-fno-omit-frame-pointer",
"-fexceptions",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
soname = "libxplat_react_module_samplemodule.$(ext)",
visibility = [
"PUBLIC",
],
deps = [
"xplat//folly:memory",
"xplat//folly:molly",
"xplat//third-party/glog:glog",
":module",
],
)
CXXREACT_PUBLIC_HEADERS = [
"CxxNativeModule.h",
"Instance.h",
"JSBundleType.h",
"JSExecutor.h",
"JSCExecutor.h",
"JSCNativeModules.h",
"JSIndexedRAMBundle.h",
"JSModulesUnbundle.h",
"MessageQueueThread.h",
"MethodCall.h",
"ModuleRegistry.h",
"NativeModule.h",
"NativeToJsBridge.h",
"Platform.h",
"RAMBundleRegistry.h",
"RecoverableError.h",
"SharedProxyCxxModule.h",
"SystraceSection.h",
]
rn_xplat_cxx_library(
name = "bridge",
srcs = glob(
["*.cpp"],
excludes = [
"JSBigString.cpp",
"SampleCxxModule.cpp",
],
),
headers = glob(
["*.h"],
excludes = CXXREACT_PUBLIC_HEADERS,
),
header_namespace = "",
exported_headers = dict([
(
"cxxreact/%s" % header,
header,
)
for header in CXXREACT_PUBLIC_HEADERS
]),
compiler_flags = CXX_LIBRARY_COMPILER_FLAGS + [
"-fexceptions",
"-frtti",
],
fbandroid_deps = ANDROID_JSC_DEPS,
fbandroid_preprocessor_flags = ANDROID_INSPECTOR_FLAGS + [
"-DWITH_JSC_EXTRA_TRACING=1",
"-DWITH_JSC_MEMORY_PRESSURE=1",
"-DWITH_FB_MEMORY_PROFILING=1",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_deps = APPLE_JSC_DEPS,
fbobjc_frameworks = [
"$SDKROOT/System/Library/Frameworks/JavaScriptCore.framework",
],
fbobjc_preprocessor_flags = DEBUG_PREPROCESSOR_FLAGS + APPLE_INSPECTOR_FLAGS,
force_static = True,
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
tests = [
react_native_xplat_target("cxxreact/tests:tests"),
],
visibility = ["PUBLIC"],
deps = [
"xplat//fbsystrace:fbsystrace",
"xplat//folly:headers_only",
"xplat//folly:memory",
"xplat//folly:molly",
":jsbigstring",
":module",
react_native_xplat_target("jschelpers:jschelpers"),
react_native_xplat_target("jsinspector:jsinspector"),
react_native_xplat_target("microprofiler:microprofiler"),
"xplat//folly:optional",
"xplat//third-party/glog:glog",
react_native_xplat_target("jschelpers:jscinternalhelpers"),
react_native_xplat_target("privatedata:privatedata"),
],
)
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <functional>
#include <map>
#include <tuple>
#include <vector>
#include <folly/dynamic.h>
using namespace std::placeholders;
namespace facebook {
namespace react {
class Instance;
}}
namespace facebook {
namespace xplat {
namespace module {
/**
* Base class for Catalyst native modules whose implementations are
* written in C++. Native methods are represented by instances of the
* Method struct. Generally, a derived class will manage an instance
* which represents the data for the module, and non-Catalyst-specific
* methods can be wrapped in lambdas which convert between
* folly::dynamic and native C++ objects. The Callback arguments will
* pass through to js functions passed to the analogous javascript
* methods. At most two callbacks will be converted. Results should
* be passed to the first callback, and errors to the second callback.
* Exceptions thrown by a method will be converted to platform
* exceptions, and handled however they are handled on that platform.
* (TODO mhorowitz #7128529: this exception behavior is not yet
* implemented.)
*
* There are two sets of constructors here. The first set initializes
* a Method using a name and anything convertible to a std::function.
* This is most useful for registering a lambda as a RN method. There
* are overloads to support functions which take no arguments,
* arguments only, and zero, one, or two callbacks.
*
* The second set of methods is similar, but instead of taking a
* function, takes the method name, an object, and a pointer to a
* method on that object.
*/
class CxxModule {
class AsyncTagType {};
class SyncTagType {};
public:
typedef std::function<std::unique_ptr<CxxModule>()> Provider;
typedef std::function<void(std::vector<folly::dynamic>)> Callback;
constexpr static AsyncTagType AsyncTag = AsyncTagType();
constexpr static SyncTagType SyncTag = SyncTagType();
struct Method {
std::string name;
size_t callbacks;
std::function<void(folly::dynamic, Callback, Callback)> func;
std::function<folly::dynamic(folly::dynamic)> syncFunc;
const char *getType() {
assert(func || syncFunc);
return func ? (callbacks == 2 ? "promise" : "async") : "sync";
}
// std::function/lambda ctors
Method(std::string aname,
std::function<void()>&& afunc)
: name(std::move(aname))
, callbacks(0)
, func(std::bind(std::move(afunc))) {}
Method(std::string aname,
std::function<void(folly::dynamic)>&& afunc)
: name(std::move(aname))
, callbacks(0)
, func(std::bind(std::move(afunc), _1)) {}
Method(std::string aname,
std::function<void(folly::dynamic, Callback)>&& afunc)
: name(std::move(aname))
, callbacks(1)
, func(std::bind(std::move(afunc), _1, _2)) {}
Method(std::string aname,
std::function<void(folly::dynamic, Callback, Callback)>&& afunc)
: name(std::move(aname))
, callbacks(2)
, func(std::move(afunc)) {}
// method pointer ctors
template <typename T>
Method(std::string aname, T* t, void (T::*method)())
: name(std::move(aname))
, callbacks(0)
, func(std::bind(method, t)) {}
template <typename T>
Method(std::string aname, T* t, void (T::*method)(folly::dynamic))
: name(std::move(aname))
, callbacks(0)
, func(std::bind(method, t, _1)) {}
template <typename T>
Method(std::string aname, T* t, void (T::*method)(folly::dynamic, Callback))
: name(std::move(aname))
, callbacks(1)
, func(std::bind(method, t, _1, _2)) {}
template <typename T>
Method(std::string aname, T* t, void (T::*method)(folly::dynamic, Callback, Callback))
: name(std::move(aname))
, callbacks(2)
, func(std::bind(method, t, _1, _2, _3)) {}
// sync std::function/lambda ctors
// Overloads for functions returning void give ambiguity errors.
// I am not sure if this is a runtime/compiler bug, or a
// limitation I do not understand.
Method(std::string aname,
std::function<folly::dynamic()>&& afunc,
SyncTagType)
: name(std::move(aname))
, callbacks(0)
, syncFunc([afunc=std::move(afunc)] (const folly::dynamic&)
{ return afunc(); })
{}
Method(std::string aname,
std::function<folly::dynamic(folly::dynamic)>&& afunc,
SyncTagType)
: name(std::move(aname))
, callbacks(0)
, syncFunc(std::move(afunc))
{}
};
/**
* This may block, if necessary to complete cleanup before the
* object is destroyed.
*/
virtual ~CxxModule() {}
/**
* @return the name of this module. This will be the name used to {@code require()} this module
* from javascript.
*/
virtual std::string getName() = 0;
/**
* Each entry in the map will be exported as a property to JS. The
* key is the property name, and the value can be anything.
*/
virtual auto getConstants() -> std::map<std::string, folly::dynamic> { return {}; };
/**
* @return a list of methods this module exports to JS.
*/
virtual auto getMethods() -> std::vector<Method> = 0;
/**
* Called during the construction of CxxNativeModule.
*/
void setInstance(std::weak_ptr<react::Instance> instance) {
instance_ = instance;
}
/**
* @return a weak_ptr to the current instance of the bridge.
* When used with CxxNativeModule, this gives Cxx modules access to functions
* such as `callJSFunction`, allowing them to communicate back to JS outside
* of the regular callbacks.
*/
std::weak_ptr<react::Instance> getInstance() {
return instance_;
}
private:
std::weak_ptr<react::Instance> instance_;
};
}}}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "CxxNativeModule.h"
#include "Instance.h"
#include <iterator>
#include <glog/logging.h>
#include <folly/json.h>
#include "JsArgumentHelpers.h"
#include "SystraceSection.h"
#include "MessageQueueThread.h"
using facebook::xplat::module::CxxModule;
namespace facebook {
namespace react {
std::function<void(folly::dynamic)> makeCallback(
std::weak_ptr<Instance> instance, const folly::dynamic& callbackId) {
if (!callbackId.isNumber()) {
throw std::invalid_argument("Expected callback(s) as final argument");
}
auto id = callbackId.asInt();
return [winstance = std::move(instance), id](folly::dynamic args) {
if (auto instance = winstance.lock()) {
instance->callJSCallback(id, std::move(args));
}
};
}
namespace {
/**
* CxxModule::Callback accepts a vector<dynamic>, makeCallback returns
* a callback that accepts a dynamic, adapt the second into the first.
* TODO: Callback types should be made equal (preferably
* function<void(dynamic)>) to avoid the extra copy and indirect call.
*/
CxxModule::Callback convertCallback(
std::function<void(folly::dynamic)> callback) {
return [callback = std::move(callback)](std::vector<folly::dynamic> args) {
callback(folly::dynamic(std::make_move_iterator(args.begin()),
std::make_move_iterator(args.end())));
};
}
}
std::string CxxNativeModule::getName() {
return name_;
}
std::vector<MethodDescriptor> CxxNativeModule::getMethods() {
lazyInit();
std::vector<MethodDescriptor> descs;
for (auto& method : methods_) {
descs.emplace_back(method.name, method.getType());
}
return descs;
}
folly::dynamic CxxNativeModule::getConstants() {
lazyInit();
if (!module_) {
return nullptr;
}
folly::dynamic constants = folly::dynamic::object();
for (auto& pair : module_->getConstants()) {
constants.insert(std::move(pair.first), std::move(pair.second));
}
return constants;
}
void CxxNativeModule::invoke(unsigned int reactMethodId, folly::dynamic&& params, int callId) {
if (reactMethodId >= methods_.size()) {
throw std::invalid_argument(folly::to<std::string>("methodId ", reactMethodId,
" out of range [0..", methods_.size(), "]"));
}
if (!params.isArray()) {
throw std::invalid_argument(
folly::to<std::string>("method parameters should be array, but are ", params.typeName()));
}
CxxModule::Callback first;
CxxModule::Callback second;
const auto& method = methods_[reactMethodId];
if (!method.func) {
throw std::runtime_error(folly::to<std::string>("Method ", method.name,
" is synchronous but invoked asynchronously"));
}
if (params.size() < method.callbacks) {
throw std::invalid_argument(folly::to<std::string>("Expected ", method.callbacks,
" callbacks, but only ", params.size(), " parameters provided"));
}
if (method.callbacks == 1) {
first = convertCallback(makeCallback(instance_, params[params.size() - 1]));
} else if (method.callbacks == 2) {
first = convertCallback(makeCallback(instance_, params[params.size() - 2]));
second = convertCallback(makeCallback(instance_, params[params.size() - 1]));
}
params.resize(params.size() - method.callbacks);
// I've got a few flawed options here. I can let the C++ exception
// propagate, and the registry will log/convert them to java exceptions.
// This lets all the java and red box handling work ok, but the only info I
// can capture about the C++ exception is the what() string, not the stack.
// I can std::terminate() the app. This causes the full, accurate C++
// stack trace to be added to logcat by debuggerd. The java state is lost,
// but in practice, the java stack is always the same in this case since
// the javascript stack is not visible, and the crash is unfriendly to js
// developers, but crucial to C++ developers. The what() value is also
// lost. Finally, I can catch, log the java stack, then rethrow the C++
// exception. In this case I get java and C++ stack data, but the C++
// stack is as of the rethrow, not the original throw, both the C++ and
// java stacks always look the same.
//
// I am going with option 2, since that seems like the most useful
// choice. It would be nice to be able to get what() and the C++
// stack. I'm told that will be possible in the future. TODO
// mhorowitz #7128529: convert C++ exceptions to Java
messageQueueThread_->runOnQueue([method, params=std::move(params), first, second, callId] () {
#ifdef WITH_FBSYSTRACE
if (callId != -1) {
fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId);
}
#endif
SystraceSection s(method.name.c_str());
try {
method.func(std::move(params), first, second);
} catch (const facebook::xplat::JsArgumentException& ex) {
throw;
} catch (std::exception& e) {
LOG(ERROR) << "std::exception. Method call " << method.name.c_str() << " failed: " << e.what();
std::terminate();
} catch (std::string& error) {
LOG(ERROR) << "std::string. Method call " << method.name.c_str() << " failed: " << error.c_str();
std::terminate();
} catch (...) {
LOG(ERROR) << "Method call " << method.name.c_str() << " failed. unknown error";
std::terminate();
}
});
}
MethodCallResult CxxNativeModule::callSerializableNativeHook(unsigned int hookId, folly::dynamic&& args) {
if (hookId >= methods_.size()) {
throw std::invalid_argument(
folly::to<std::string>("methodId ", hookId, " out of range [0..", methods_.size(), "]"));
}
const auto& method = methods_[hookId];
if (!method.syncFunc) {
throw std::runtime_error(
folly::to<std::string>("Method ", method.name,
" is asynchronous but invoked synchronously"));
}
return method.syncFunc(std::move(args));
}
void CxxNativeModule::lazyInit() {
if (module_ || !provider_) {
return;
}
// TODO 17216751: providers should never return null modules
module_ = provider_();
provider_ = nullptr;
if (module_) {
methods_ = module_->getMethods();
module_->setInstance(instance_);
}
}
}
}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <cxxreact/CxxModule.h>
#include <cxxreact/NativeModule.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
class Instance;
class MessageQueueThread;
std::function<void(folly::dynamic)> makeCallback(
std::weak_ptr<Instance> instance, const folly::dynamic& callbackId);
class RN_EXPORT CxxNativeModule : public NativeModule {
public:
CxxNativeModule(std::weak_ptr<Instance> instance,
std::string name,
xplat::module::CxxModule::Provider provider,
std::shared_ptr<MessageQueueThread> messageQueueThread)
: instance_(instance)
, name_(std::move(name))
, provider_(provider)
, messageQueueThread_(messageQueueThread) {}
std::string getName() override;
std::vector<MethodDescriptor> getMethods() override;
folly::dynamic getConstants() override;
void invoke(unsigned int reactMethodId, folly::dynamic&& params, int callId) override;
MethodCallResult callSerializableNativeHook(unsigned int hookId, folly::dynamic&& args) override;
private:
void lazyInit();
std::weak_ptr<Instance> instance_;
std::string name_;
xplat::module::CxxModule::Provider provider_;
std::shared_ptr<MessageQueueThread> messageQueueThread_;
std::unique_ptr<xplat::module::CxxModule> module_;
std::vector<xplat::module::CxxModule::Method> methods_;
};
}
}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Instance.h"
#include "JSBigString.h"
#include "JSBundleType.h"
#include "JSExecutor.h"
#include "MessageQueueThread.h"
#include "MethodCall.h"
#include "NativeToJsBridge.h"
#include "RAMBundleRegistry.h"
#include "RecoverableError.h"
#include "SystraceSection.h"
#include <cxxreact/JSIndexedRAMBundle.h>
#include <folly/Memory.h>
#include <folly/MoveWrapper.h>
#include <folly/json.h>
#include <glog/logging.h>
#include <condition_variable>
#include <fstream>
#include <mutex>
#include <string>
namespace facebook {
namespace react {
Instance::~Instance() {
if (nativeToJsBridge_) {
nativeToJsBridge_->destroy();
}
}
void Instance::initializeBridge(
std::unique_ptr<InstanceCallback> callback,
std::shared_ptr<JSExecutorFactory> jsef,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<ModuleRegistry> moduleRegistry) {
callback_ = std::move(callback);
moduleRegistry_ = std::move(moduleRegistry);
jsQueue->runOnQueueSync([this, &jsef, jsQueue]() mutable {
nativeToJsBridge_ = folly::make_unique<NativeToJsBridge>(
jsef.get(), moduleRegistry_, jsQueue, callback_);
std::lock_guard<std::mutex> lock(m_syncMutex);
m_syncReady = true;
m_syncCV.notify_all();
});
CHECK(nativeToJsBridge_);
}
void Instance::loadApplication(std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> string,
std::string sourceURL) {
callback_->incrementPendingJSCalls();
SystraceSection s("Instance::loadApplication", "sourceURL",
sourceURL);
nativeToJsBridge_->loadApplication(std::move(bundleRegistry), std::move(string),
std::move(sourceURL));
}
void Instance::loadApplicationSync(std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> string,
std::string sourceURL) {
std::unique_lock<std::mutex> lock(m_syncMutex);
m_syncCV.wait(lock, [this] { return m_syncReady; });
SystraceSection s("Instance::loadApplicationSync", "sourceURL",
sourceURL);
nativeToJsBridge_->loadApplicationSync(std::move(bundleRegistry), std::move(string),
std::move(sourceURL));
}
void Instance::setSourceURL(std::string sourceURL) {
callback_->incrementPendingJSCalls();
SystraceSection s("Instance::setSourceURL", "sourceURL", sourceURL);
nativeToJsBridge_->loadApplication(nullptr, nullptr, std::move(sourceURL));
}
void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,
std::string sourceURL,
bool loadSynchronously) {
SystraceSection s("Instance::loadScriptFromString", "sourceURL",
sourceURL);
if (loadSynchronously) {
loadApplicationSync(nullptr, std::move(string), std::move(sourceURL));
} else {
loadApplication(nullptr, std::move(string), std::move(sourceURL));
}
}
bool Instance::isIndexedRAMBundle(const char *sourcePath) {
std::ifstream bundle_stream(sourcePath, std::ios_base::in);
BundleHeader header;
if (!bundle_stream ||
!bundle_stream.read(reinterpret_cast<char *>(&header), sizeof(header))) {
return false;
}
return parseTypeFromHeader(header) == ScriptTag::RAMBundle;
}
void Instance::loadRAMBundleFromFile(const std::string& sourcePath,
const std::string& sourceURL,
bool loadSynchronously) {
auto bundle = folly::make_unique<JSIndexedRAMBundle>(sourcePath.c_str());
auto startupScript = bundle->getStartupCode();
auto registry = RAMBundleRegistry::multipleBundlesRegistry(std::move(bundle), JSIndexedRAMBundle::buildFactory());
loadRAMBundle(
std::move(registry),
std::move(startupScript),
sourceURL,
loadSynchronously);
}
void Instance::loadRAMBundle(std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL,
bool loadSynchronously) {
if (loadSynchronously) {
loadApplicationSync(std::move(bundleRegistry), std::move(startupScript),
std::move(startupScriptSourceURL));
} else {
loadApplication(std::move(bundleRegistry), std::move(startupScript),
std::move(startupScriptSourceURL));
}
}
void Instance::setGlobalVariable(std::string propName,
std::unique_ptr<const JSBigString> jsonValue) {
nativeToJsBridge_->setGlobalVariable(std::move(propName),
std::move(jsonValue));
}
void *Instance::getJavaScriptContext() {
return nativeToJsBridge_ ? nativeToJsBridge_->getJavaScriptContext()
: nullptr;
}
bool Instance::isInspectable() {
return nativeToJsBridge_ ? nativeToJsBridge_->isInspectable() : false;
}
void Instance::callJSFunction(std::string &&module, std::string &&method,
folly::dynamic &&params) {
callback_->incrementPendingJSCalls();
nativeToJsBridge_->callFunction(std::move(module), std::move(method),
std::move(params));
}
void Instance::callJSCallback(uint64_t callbackId, folly::dynamic &&params) {
SystraceSection s("Instance::callJSCallback");
callback_->incrementPendingJSCalls();
nativeToJsBridge_->invokeCallback((double)callbackId, std::move(params));
}
void Instance::registerBundle(uint32_t bundleId, const std::string& bundlePath) {
nativeToJsBridge_->registerBundle(bundleId, bundlePath);
}
const ModuleRegistry &Instance::getModuleRegistry() const {
return *moduleRegistry_;
}
ModuleRegistry &Instance::getModuleRegistry() { return *moduleRegistry_; }
#ifdef WITH_JSC_MEMORY_PRESSURE
void Instance::handleMemoryPressure(int pressureLevel) {
nativeToJsBridge_->handleMemoryPressure(pressureLevel);
}
#endif
} // namespace react
} // namespace facebook
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <condition_variable>
#include <memory>
#include <cxxreact/NativeToJsBridge.h>
#include <jschelpers/Value.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace folly {
struct dynamic;
}
namespace facebook {
namespace react {
class JSBigString;
class JSExecutorFactory;
class MessageQueueThread;
class ModuleRegistry;
class RAMBundleRegistry;
struct InstanceCallback {
virtual ~InstanceCallback() {}
virtual void onBatchComplete() {}
virtual void incrementPendingJSCalls() {}
virtual void decrementPendingJSCalls() {}
};
class RN_EXPORT Instance {
public:
~Instance();
void initializeBridge(std::unique_ptr<InstanceCallback> callback,
std::shared_ptr<JSExecutorFactory> jsef,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<ModuleRegistry> moduleRegistry);
void setSourceURL(std::string sourceURL);
void loadScriptFromString(std::unique_ptr<const JSBigString> string,
std::string sourceURL, bool loadSynchronously);
static bool isIndexedRAMBundle(const char *sourcePath);
void loadRAMBundleFromFile(const std::string& sourcePath,
const std::string& sourceURL,
bool loadSynchronously);
void loadRAMBundle(std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL, bool loadSynchronously);
bool supportsProfiling();
void setGlobalVariable(std::string propName,
std::unique_ptr<const JSBigString> jsonValue);
void *getJavaScriptContext();
bool isInspectable();
void callJSFunction(std::string &&module, std::string &&method,
folly::dynamic &&params);
void callJSCallback(uint64_t callbackId, folly::dynamic &&params);
// This method is experimental, and may be modified or removed.
void registerBundle(uint32_t bundleId, const std::string& bundlePath);
// This method is experimental, and may be modified or removed.
template <typename T>
Value callFunctionSync(const std::string &module, const std::string &method,
T &&args) {
CHECK(nativeToJsBridge_);
return nativeToJsBridge_->callFunctionSync(module, method,
std::forward<T>(args));
}
const ModuleRegistry &getModuleRegistry() const;
ModuleRegistry &getModuleRegistry();
#ifdef WITH_JSC_MEMORY_PRESSURE
void handleMemoryPressure(int pressureLevel);
#endif
private:
void callNativeModules(folly::dynamic &&calls, bool isEndOfBatch);
void loadApplication(std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL);
void loadApplicationSync(std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL);
std::shared_ptr<InstanceCallback> callback_;
std::unique_ptr<NativeToJsBridge> nativeToJsBridge_;
std::shared_ptr<ModuleRegistry> moduleRegistry_;
std::mutex m_syncMutex;
std::condition_variable m_syncCV;
bool m_syncReady = false;
};
} // namespace react
} // namespace facebook
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSBigString.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <folly/Memory.h>
#include <folly/ScopeGuard.h>
namespace facebook {
namespace react {
std::unique_ptr<const JSBigFileString> JSBigFileString::fromPath(const std::string& sourceURL) {
int fd = ::open(sourceURL.c_str(), O_RDONLY);
folly::checkUnixError(fd, "Could not open file", sourceURL);
SCOPE_EXIT { CHECK(::close(fd) == 0); };
struct stat fileInfo;
folly::checkUnixError(::fstat(fd, &fileInfo), "fstat on bundle failed.");
return folly::make_unique<const JSBigFileString>(fd, fileInfo.st_size);
}
} // namespace react
} // namespace facebook
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <fcntl.h>
#include <sys/mman.h>
#include <folly/Exception.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
// JSExecutor functions sometimes take large strings, on the order of
// megabytes. Copying these can be expensive. Introducing a
// move-only, non-CopyConstructible type will let the compiler ensure
// that no copies occur. folly::MoveWrapper should be used when a
// large string needs to be curried into a std::function<>, which must
// by CopyConstructible.
class JSBigString {
public:
JSBigString() = default;
// Not copyable
JSBigString(const JSBigString&) = delete;
JSBigString& operator=(const JSBigString&) = delete;
virtual ~JSBigString() {}
virtual bool isAscii() const = 0;
// This needs to be a \0 terminated string
virtual const char* c_str() const = 0;
// Length of the c_str without the NULL byte.
virtual size_t size() const = 0;
};
// Concrete JSBigString implementation which holds a std::string
// instance.
class JSBigStdString : public JSBigString {
public:
JSBigStdString(std::string str, bool isAscii=false)
: m_isAscii(isAscii)
, m_str(std::move(str)) {}
bool isAscii() const override {
return m_isAscii;
}
const char* c_str() const override {
return m_str.c_str();
}
size_t size() const override {
return m_str.size();
}
private:
bool m_isAscii;
std::string m_str;
};
// Concrete JSBigString implementation which holds a heap-allocated
// buffer, and provides an accessor for writing to it. This can be
// used to construct a JSBigString in place, such as by reading from a
// file.
class JSBigBufferString : public JSBigString {
public:
JSBigBufferString(size_t size)
: m_data(new char[size + 1])
, m_size(size) {
// Guarantee nul-termination. The caller is responsible for
// filling in the rest of m_data.
m_data[m_size] = '\0';
}
~JSBigBufferString() {
delete[] m_data;
}
bool isAscii() const override {
return true;
}
const char* c_str() const override {
return m_data;
}
size_t size() const override {
return m_size;
}
char* data() {
return m_data;
}
private:
char* m_data;
size_t m_size;
};
// JSBigString interface implemented by a file-backed mmap region.
class RN_EXPORT JSBigFileString : public JSBigString {
public:
JSBigFileString(int fd, size_t size, off_t offset = 0)
: m_fd {-1}
, m_data {nullptr}
{
folly::checkUnixError(m_fd = dup(fd),
"Could not duplicate file descriptor");
// Offsets given to mmap must be page aligend. We abstract away that
// restriction by sending a page aligned offset to mmap, and keeping track
// of the offset within the page that we must alter the mmap pointer by to
// get the final desired offset.
if (offset != 0) {
const static auto ps = getpagesize();
auto d = lldiv(offset, ps);
m_mapOff = d.quot;
m_pageOff = d.rem;
m_size = size + m_pageOff;
} else {
m_mapOff = 0;
m_pageOff = 0;
m_size = size;
}
}
~JSBigFileString() {
if (m_data) {
munmap((void *)m_data, m_size);
}
close(m_fd);
}
bool isAscii() const override {
return true;
}
const char *c_str() const override {
if (!m_data) {
m_data =
(const char *)mmap(0, m_size, PROT_READ, MAP_PRIVATE, m_fd, m_mapOff);
CHECK(m_data != MAP_FAILED)
<< " fd: " << m_fd
<< " size: " << m_size
<< " offset: " << m_mapOff
<< " error: " << std::strerror(errno);
}
return m_data + m_pageOff;
}
size_t size() const override {
return m_size - m_pageOff;
}
int fd() const {
return m_fd;
}
static std::unique_ptr<const JSBigFileString> fromPath(const std::string& sourceURL);
private:
int m_fd; // The file descriptor being mmaped
size_t m_size; // The size of the mmaped region
size_t m_pageOff; // The offset in the mmaped region to the data.
off_t m_mapOff; // The offset in the file to the mmaped region.
mutable const char *m_data; // Pointer to the mmaped region.
};
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSBundleType.h"
#include <folly/Bits.h>
namespace facebook {
namespace react {
static uint32_t constexpr RAMBundleMagicNumber = 0xFB0BD1E5;
static uint32_t constexpr BCBundleMagicNumber = 0x6D657300;
ScriptTag parseTypeFromHeader(const BundleHeader& header) {
switch (folly::Endian::little(header.magic)) {
case RAMBundleMagicNumber:
return ScriptTag::RAMBundle;
case BCBundleMagicNumber:
return ScriptTag::BCBundle;
default:
return ScriptTag::String;
}
}
const char *stringForScriptTag(const ScriptTag& tag) {
switch (tag) {
case ScriptTag::String:
return "String";
case ScriptTag::RAMBundle:
return "RAM Bundle";
case ScriptTag::BCBundle:
return "BC Bundle";
}
return "";
}
} // namespace react
} // namespace facebook
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <cstdint>
#include <cstring>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
/*
* ScriptTag
*
* Scripts given to the JS Executors to run could be in any of the following
* formats. They are tagged so the executor knows how to run them.
*/
enum struct ScriptTag {
String = 0,
RAMBundle,
BCBundle,
};
/**
* BundleHeader
*
* RAM bundles and BC bundles begin with headers. For RAM bundles this is
* 4 bytes, for BC bundles this is 12 bytes. This structure holds the first 12
* bytes from a bundle in a way that gives access to that information.
*/
struct __attribute__((packed)) BundleHeader {
BundleHeader() {
std::memset(this, 0, sizeof(BundleHeader));
}
uint32_t magic;
uint32_t reserved_;
uint32_t version;
};
/**
* parseTypeFromHeader
*
* Takes the first 8 bytes of a bundle, and returns a tag describing the
* bundle's format.
*/
RN_EXPORT ScriptTag parseTypeFromHeader(const BundleHeader& header);
/**
* stringForScriptTag
*
* Convert an `ScriptTag` enum into a string, useful for emitting in errors
* and diagnostic messages.
*/
RN_EXPORT const char* stringForScriptTag(const ScriptTag& tag);
} // namespace react
} // namespace facebook
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCExecutor.h"
#include <algorithm>
#include <condition_variable>
#include <fcntl.h>
#include <mutex>
#include <sstream>
#include <string>
#include <sys/time.h>
#include <sys/socket.h>
#include <system_error>
#include <arpa/inet.h>
#include <folly/Conv.h>
#include <folly/Exception.h>
#include <folly/json.h>
#include <folly/Memory.h>
#include <folly/String.h>
#include <glog/logging.h>
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
#include <jsinspector/InspectorInterfaces.h>
#include "JSBigString.h"
#include "JSBundleType.h"
#include "JSCLegacyTracing.h"
#include "JSCMemory.h"
#include "JSCNativeModules.h"
#include "JSCPerfStats.h"
#include "JSCSamplingProfiler.h"
#include "JSCTracing.h"
#include "JSCUtils.h"
#include "JSModulesUnbundle.h"
#include "ModuleRegistry.h"
#include "Platform.h"
#include "RAMBundleRegistry.h"
#include "RecoverableError.h"
#include "SystraceSection.h"
#if defined(WITH_FB_JSC_TUNING) && defined(__ANDROID__)
#include <jsc_config_android.h>
#endif
namespace facebook {
namespace react {
namespace {
template<JSValueRef (JSCExecutor::*method)(size_t, const JSValueRef[])>
inline JSObjectCallAsFunctionCallback exceptionWrapMethod() {
struct funcWrapper {
static JSValueRef call(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
try {
auto executor = Object::getGlobalObject(ctx).getPrivate<JSCExecutor>();
if (executor && executor->getJavaScriptContext()) { // Executor not invalidated
return (executor->*method)(argumentCount, arguments);
}
} catch (...) {
*exception = translatePendingCppExceptionToJSError(ctx, function);
}
return Value::makeUndefined(ctx);
}
};
return &funcWrapper::call;
}
template<JSValueRef (JSCExecutor::*method)(JSObjectRef object, JSStringRef propertyName)>
inline JSObjectGetPropertyCallback exceptionWrapMethod() {
struct funcWrapper {
static JSValueRef call(
JSContextRef ctx,
JSObjectRef object,
JSStringRef propertyName,
JSValueRef *exception) {
try {
auto executor = Object::getGlobalObject(ctx).getPrivate<JSCExecutor>();
if (executor && executor->getJavaScriptContext()) { // Executor not invalidated
return (executor->*method)(object, propertyName);
}
} catch (...) {
*exception = translatePendingCppExceptionToJSError(ctx, object);
}
return Value::makeUndefined(ctx);
}
};
return &funcWrapper::call;
}
}
#if DEBUG
static JSValueRef nativeInjectHMRUpdate(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
String execJSString = Value(ctx, arguments[0]).toString();
String jsURL = Value(ctx, arguments[1]).toString();
evaluateScript(ctx, execJSString, jsURL);
return Value::makeUndefined(ctx);
}
#endif
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate, std::shared_ptr<MessageQueueThread> jsQueue) {
return folly::make_unique<JSCExecutor>(delegate, jsQueue, m_jscConfig, m_nativeExtensionsProvider);
}
JSCExecutor::JSCExecutor(std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> messageQueueThread,
const folly::dynamic& jscConfig,
std::function<folly::dynamic(const std::string &)> nativeExtensionsProvider) throw(JSException) :
m_delegate(delegate),
m_messageQueueThread(messageQueueThread),
m_nativeModules(delegate ? delegate->getModuleRegistry() : nullptr),
m_jscConfig(jscConfig),
m_nativeExtensionsProvider(nativeExtensionsProvider) {
initOnJSVMThread();
{
SystraceSection s("nativeModuleProxy object");
installGlobalProxy(m_context, "nativeModuleProxy",
exceptionWrapMethod<&JSCExecutor::getNativeModule>());
}
installGlobalProxy(m_context, "nativeExtensions",
exceptionWrapMethod<&JSCExecutor::getNativeExtension>());
}
JSCExecutor::~JSCExecutor() {
CHECK(*m_isDestroyed) << "JSCExecutor::destroy() must be called before its destructor!";
}
void JSCExecutor::destroy() {
*m_isDestroyed = true;
if (m_messageQueueThread.get()) {
m_messageQueueThread->runOnQueueSync([this] () {
terminateOnJSVMThread();
});
} else {
terminateOnJSVMThread();
}
}
void JSCExecutor::setContextName(const std::string& name) {
String jsName = String(m_context, name.c_str());
JSC_JSGlobalContextSetName(m_context, jsName);
}
static bool canUseInspector(JSContextRef context) {
#ifdef WITH_INSPECTOR
#if defined(__APPLE__)
return isCustomJSCPtr(context); // WITH_INSPECTOR && Apple
#else
return true; // WITH_INSPECTOR && Android
#endif
#else
return false; // !WITH_INSPECTOR
#endif
}
static bool canUseSamplingProfiler(JSContextRef context) {
#if defined(__APPLE__) || defined(WITH_JSC_EXTRA_TRACING)
return JSC_JSSamplingProfilerEnabled(context);
#else
return false;
#endif
}
void JSCExecutor::initOnJSVMThread() throw(JSException) {
SystraceSection s("JSCExecutor::initOnJSVMThread");
#if defined(__APPLE__)
const bool useCustomJSC = m_jscConfig.getDefault("UseCustomJSC", false).getBool();
if (useCustomJSC) {
JSC_configureJSCForIOS(true, toJson(m_jscConfig));
}
#else
const bool useCustomJSC = false;
#endif
#if defined(WITH_FB_JSC_TUNING) && defined(__ANDROID__)
configureJSCForAndroid(m_jscConfig);
#endif
// Create a custom global class, so we can store data in it later using JSObjectSetPrivate
JSClassRef globalClass = nullptr;
{
SystraceSection s_("JSClassCreate");
JSClassDefinition definition = kJSClassDefinitionEmpty;
definition.attributes |= kJSClassAttributeNoAutomaticPrototype;
globalClass = JSC_JSClassCreate(useCustomJSC, &definition);
}
{
SystraceSection s_("JSGlobalContextCreateInGroup");
m_context = JSC_JSGlobalContextCreateInGroup(useCustomJSC, nullptr, globalClass);
}
JSC_JSClassRelease(useCustomJSC, globalClass);
// Add a pointer to ourselves so we can retrieve it later in our hooks
Object::getGlobalObject(m_context).setPrivate(this);
if (canUseInspector(m_context)) {
const std::string ownerId = m_jscConfig.getDefault("OwnerIdentity", "unknown").getString();
const std::string appId = m_jscConfig.getDefault("AppIdentity", "unknown").getString();
const std::string deviceId = m_jscConfig.getDefault("DeviceIdentity", "unknown").getString();
auto checkIsInspectedRemote = [ownerId, appId, deviceId]() {
return isNetworkInspected(ownerId, appId, deviceId);
};
auto& globalInspector = facebook::react::getInspectorInstance();
JSC_JSGlobalContextEnableDebugger(m_context, globalInspector, ownerId.c_str(), checkIsInspectedRemote);
}
installNativeHook<&JSCExecutor::nativeFlushQueueImmediate>("nativeFlushQueueImmediate");
installNativeHook<&JSCExecutor::nativeCallSyncHook>("nativeCallSyncHook");
installGlobalFunction(m_context, "nativeLoggingHook", JSCNativeHooks::loggingHook);
installGlobalFunction(m_context, "nativePerformanceNow", JSCNativeHooks::nowHook);
#if DEBUG
installGlobalFunction(m_context, "nativeInjectHMRUpdate", nativeInjectHMRUpdate);
#endif
addNativeTracingHooks(m_context);
addNativeTracingLegacyHooks(m_context);
addJSCMemoryHooks(m_context);
addJSCPerfStatsHooks(m_context);
JSCNativeHooks::installPerfHooks(m_context);
if (canUseSamplingProfiler(m_context)) {
initSamplingProfilerOnMainJSCThread(m_context);
}
}
bool JSCExecutor::isNetworkInspected(const std::string &owner, const std::string &app, const std::string &device) {
#ifdef WITH_FB_DBG_ATTACH_BEFORE_EXEC
auto connect_socket = [](int socket_desc, std::string address, int port) {
if (socket_desc < 0) {
::close(socket_desc);
return false;
}
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
auto sock_opt_rcv_resp = setsockopt(socket_desc, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval));
if (sock_opt_rcv_resp < 0) {
::close(socket_desc);
return false;
}
auto sock_opt_snd_resp = setsockopt(socket_desc, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(struct timeval));
if (sock_opt_snd_resp < 0) {
::close(socket_desc);
return false;
}
struct sockaddr_in server;
server.sin_addr.s_addr = inet_addr(address.c_str());
server.sin_family = AF_INET;
server.sin_port = htons(port);
auto connect_resp = ::connect(socket_desc, (struct sockaddr *)&server, sizeof(server));
if (connect_resp < 0) {
::close(socket_desc);
return false;
}
return true;
};
int socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (!connect_socket(socket_desc, "127.0.0.1", 8082)) {
#if defined(__ANDROID__)
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (!connect_socket(socket_desc, "10.0.2.2", 8082) /* emulator */) {
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (!connect_socket(socket_desc, "10.0.3.2", 8082) /* genymotion */) {
return false;
}
}
#else //!defined(__ANDROID__)
return false;
#endif //defined(__ANDROID__)
}
std::string escapedOwner = folly::uriEscape<std::string>(owner, folly::UriEscapeMode::QUERY);
std::string escapedApp = folly::uriEscape<std::string>(app, folly::UriEscapeMode::QUERY);
std::string escapedDevice = folly::uriEscape<std::string>(device, folly::UriEscapeMode::QUERY);
std::string msg = folly::to<std::string>(
"GET /autoattach?title=", escapedOwner,
"&app=" , escapedApp,
"&device=" , escapedDevice,
" HTTP/1.1\r\n\r\n");
auto send_resp = ::send(socket_desc, msg.c_str(), msg.length(), 0);
if (send_resp < 0) {
::close(socket_desc);
return false;
}
char server_reply[200];
server_reply[199] = '\0';
auto recv_resp = ::recv(socket_desc, server_reply,
sizeof(server_reply) - 1, 0);
if (recv_resp < 0) {
::close(socket_desc);
return false;
}
std::string response(server_reply);
if (response.size() < 25) {
::close(socket_desc);
return false;
}
auto responseCandidate = response.substr(response.size() - 25);
auto found = responseCandidate.find("{\"autoattach\":true}") != std::string::npos;
::close(socket_desc);
return found;
#else //!WITH_FB_DBG_ATTACH_BEFORE_EXEC
return false;
#endif //WITH_FB_DBG_ATTACH_BEFORE_EXEC
}
void JSCExecutor::terminateOnJSVMThread() {
JSGlobalContextRef context = m_context;
m_context = nullptr;
Object::getGlobalObject(context).setPrivate(nullptr);
m_nativeModules.reset();
if (canUseInspector(context)) {
auto &globalInspector = facebook::react::getInspectorInstance();
JSC_JSGlobalContextDisableDebugger(context, globalInspector);
}
JSC_JSGlobalContextRelease(context);
}
#ifdef WITH_FBJSCEXTENSIONS
static const char* explainLoadSourceStatus(JSLoadSourceStatus status) {
switch (status) {
case JSLoadSourceIsCompiled:
return "No error encountered during source load";
case JSLoadSourceErrorOnRead:
return "Error reading source";
case JSLoadSourceIsNotCompiled:
return "Source is not compiled";
case JSLoadSourceErrorVersionMismatch:
return "Source version not supported";
default:
return "Bad error code";
}
}
#endif
// basename_r isn't in all iOS SDKs, so use this simple version instead.
static std::string simpleBasename(const std::string &path) {
size_t pos = path.rfind("/");
return (pos != std::string::npos) ? path.substr(pos) : path;
}
void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) {
SystraceSection s("JSCExecutor::loadApplicationScript",
"sourceURL", sourceURL);
std::string scriptName = simpleBasename(sourceURL);
ReactMarker::logTaggedMarker(ReactMarker::RUN_JS_BUNDLE_START, scriptName.c_str());
String jsSourceURL(m_context, sourceURL.c_str());
// TODO t15069155: reduce the number of overrides here
#ifdef WITH_FBJSCEXTENSIONS
if (auto fileStr = dynamic_cast<const JSBigFileString *>(script.get())) {
JSContextLock lock(m_context);
JSLoadSourceStatus jsStatus;
auto bcSourceCode = JSCreateSourceCodeFromFile(fileStr->fd(), jsSourceURL, nullptr, &jsStatus);
switch (jsStatus) {
case JSLoadSourceIsCompiled:
if (!bcSourceCode) {
throw std::runtime_error("Unexpected error opening compiled bundle");
}
evaluateSourceCode(m_context, bcSourceCode, jsSourceURL);
flush();
ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP);
ReactMarker::logTaggedMarker(ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
return;
case JSLoadSourceErrorVersionMismatch:
throw RecoverableError(explainLoadSourceStatus(jsStatus));
case JSLoadSourceErrorOnRead:
case JSLoadSourceIsNotCompiled:
// Not bytecode, fall through.
break;
}
}
#elif defined(__APPLE__)
BundleHeader header;
memcpy(&header, script->c_str(), std::min(script->size(), sizeof(BundleHeader)));
auto scriptTag = parseTypeFromHeader(header);
if (scriptTag == ScriptTag::BCBundle) {
using file_ptr = std::unique_ptr<FILE, decltype(&fclose)>;
file_ptr source(fopen(sourceURL.c_str(), "r"), fclose);
int sourceFD = fileno(source.get());
JSValueRef jsError;
JSValueRef result = JSC_JSEvaluateBytecodeBundle(m_context, NULL, sourceFD, jsSourceURL, &jsError);
if (result == nullptr) {
throw JSException(m_context, jsError, jsSourceURL);
}
} else
#endif
{
String jsScript;
JSContextLock lock(m_context);
{
SystraceSection s_("JSCExecutor::loadApplicationScript-createExpectingAscii");
ReactMarker::logMarker(ReactMarker::JS_BUNDLE_STRING_CONVERT_START);
jsScript = adoptString(std::move(script));
ReactMarker::logMarker(ReactMarker::JS_BUNDLE_STRING_CONVERT_STOP);
}
SystraceSection s_("JSCExecutor::loadApplicationScript-evaluateScript");
evaluateScript(m_context, jsScript, jsSourceURL);
}
flush();
ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP);
ReactMarker::logTaggedMarker(ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
}
void JSCExecutor::setBundleRegistry(std::unique_ptr<RAMBundleRegistry> bundleRegistry) {
if (!m_bundleRegistry) {
installNativeHook<&JSCExecutor::nativeRequire>("nativeRequire");
}
m_bundleRegistry = std::move(bundleRegistry);
}
void JSCExecutor::registerBundle(uint32_t bundleId, const std::string& bundlePath) {
if (m_bundleRegistry) {
m_bundleRegistry->registerBundle(bundleId, bundlePath);
} else {
auto sourceUrl = String(m_context, bundlePath.c_str());
auto source = adoptString(JSBigFileString::fromPath(bundlePath));
evaluateScript(m_context, source, sourceUrl);
}
}
void JSCExecutor::bindBridge() throw(JSException) {
SystraceSection s("JSCExecutor::bindBridge");
std::call_once(m_bindFlag, [this] {
auto global = Object::getGlobalObject(m_context);
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
if (batchedBridgeValue.isUndefined()) {
auto requireBatchedBridge = global.getProperty("__fbRequireBatchedBridge");
if (!requireBatchedBridge.isUndefined()) {
batchedBridgeValue = requireBatchedBridge.asObject().callAsFunction({});
}
if (batchedBridgeValue.isUndefined()) {
throw JSException("Could not get BatchedBridge, make sure your bundle is packaged correctly");
}
}
auto batchedBridge = batchedBridgeValue.asObject();
m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
});
}
void JSCExecutor::callNativeModules(Value&& value) {
SystraceSection s("JSCExecutor::callNativeModules");
// If this fails, you need to pass a fully functional delegate with a
// module registry to the factory/ctor.
CHECK(m_delegate) << "Attempting to use native modules without a delegate";
try {
auto calls = value.toJSONString();
m_delegate->callNativeModules(*this, folly::parseJson(calls), true);
} catch (...) {
std::string message = "Error in callNativeModules()";
try {
message += ":" + value.toString().str();
} catch (...) {
// ignored
}
std::throw_with_nested(std::runtime_error(message));
}
}
void JSCExecutor::flush() {
SystraceSection s("JSCExecutor::flush");
if (m_flushedQueueJS) {
callNativeModules(m_flushedQueueJS->callAsFunction({}));
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.
auto global = Object::getGlobalObject(m_context);
auto batchedBridgeValue = global.getProperty("__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 (!batchedBridgeValue.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(m_flushedQueueJS->callAsFunction({}));
} else if (m_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(Value::makeNull(m_context));
}
}
void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
SystraceSection s("JSCExecutor::callFunction");
// This weird pattern is because Value is not default constructible.
// The lambda is inlined, so there's no overhead.
auto result = [&] {
JSContextLock lock(m_context);
try {
if (!m_callFunctionReturnResultAndFlushedQueueJS) {
bindBridge();
}
return m_callFunctionReturnFlushedQueueJS->callAsFunction({
Value(m_context, String::createExpectingAscii(m_context, moduleId)),
Value(m_context, String::createExpectingAscii(m_context, methodId)),
Value::fromDynamic(m_context, std::move(arguments))
});
} catch (...) {
std::throw_with_nested(
std::runtime_error("Error calling " + moduleId + "." + methodId));
}
}();
callNativeModules(std::move(result));
}
void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic& arguments) {
SystraceSection s("JSCExecutor::invokeCallback");
auto result = [&] {
JSContextLock lock(m_context);
try {
if (!m_invokeCallbackAndReturnFlushedQueueJS) {
bindBridge();
}
return m_invokeCallbackAndReturnFlushedQueueJS->callAsFunction({
Value::makeNumber(m_context, callbackId),
Value::fromDynamic(m_context, std::move(arguments))
});
} catch (...) {
std::throw_with_nested(
std::runtime_error(folly::to<std::string>("Error invoking callback ", callbackId)));
}
}();
callNativeModules(std::move(result));
}
Value JSCExecutor::callFunctionSyncWithValue(
const std::string& module, const std::string& method, Value args) {
SystraceSection s("JSCExecutor::callFunction");
Object result = [&] {
JSContextLock lock(m_context);
if (!m_callFunctionReturnResultAndFlushedQueueJS) {
bindBridge();
}
return m_callFunctionReturnResultAndFlushedQueueJS->callAsFunction({
Value(m_context, String::createExpectingAscii(m_context, module)),
Value(m_context, String::createExpectingAscii(m_context, method)),
std::move(args),
}).asObject();
}();
Value length = result.getProperty("length");
if (!length.isNumber() || length.asInteger() != 2) {
std::runtime_error("Return value of a callFunction must be an array of size 2");
}
callNativeModules(result.getPropertyAtIndex(1));
return result.getPropertyAtIndex(0);
}
void JSCExecutor::setGlobalVariable(std::string propName, std::unique_ptr<const JSBigString> jsonValue) {
try {
SystraceSection s("JSCExecutor::setGlobalVariable", "propName", propName);
auto valueToInject = Value::fromJSON(adoptString(std::move(jsonValue)));
Object::getGlobalObject(m_context).setProperty(propName.c_str(), valueToInject);
} catch (...) {
std::throw_with_nested(std::runtime_error("Error setting global variable: " + propName));
}
}
std::string JSCExecutor::getDescription() {
#if defined(__APPLE__)
if (isCustomJSCPtr(m_context)) {
return "Custom JSC";
} else {
return "System JSC";
}
#else
return "JSC";
#endif
}
String JSCExecutor::adoptString(std::unique_ptr<const JSBigString> script) {
#if defined(WITH_FBJSCEXTENSIONS)
const JSBigString* string = script.release();
auto jsString = JSStringCreateAdoptingExternal(string->c_str(), string->size(), (void*)string, [](void* s) {
delete static_cast<JSBigString*>(s);
});
return String::adopt(m_context, jsString);
#else
return script->isAscii()
? String::createExpectingAscii(m_context, script->c_str(), script->size())
: String(m_context, script->c_str());
#endif
}
void* JSCExecutor::getJavaScriptContext() {
return m_context;
}
bool JSCExecutor::isInspectable() {
return canUseInspector(m_context);
}
#ifdef WITH_JSC_MEMORY_PRESSURE
void JSCExecutor::handleMemoryPressure(int pressureLevel) {
JSHandleMemoryPressure(this, m_context, static_cast<JSMemoryPressure>(pressureLevel));
}
#endif
void JSCExecutor::flushQueueImmediate(Value&& queue) {
auto queueStr = queue.toJSONString();
m_delegate->callNativeModules(*this, folly::parseJson(queueStr), false);
}
void JSCExecutor::loadModule(uint32_t bundleId, uint32_t moduleId) {
auto module = m_bundleRegistry->getModule(bundleId, moduleId);
auto sourceUrl = String::createExpectingAscii(m_context, module.name);
auto source = adoptString(std::unique_ptr<JSBigString>(new JSBigStdString(module.code)));
evaluateScript(m_context, source, sourceUrl);
}
// Native JS hooks
template<JSValueRef (JSCExecutor::*method)(size_t, const JSValueRef[])>
void JSCExecutor::installNativeHook(const char* name) {
installGlobalFunction(m_context, name, exceptionWrapMethod<method>());
}
JSValueRef JSCExecutor::getNativeModule(JSObjectRef object, JSStringRef propertyName) {
if (JSC_JSStringIsEqualToUTF8CString(m_context, propertyName, "name")) {
return Value(m_context, String(m_context, "NativeModules"));
}
return m_nativeModules.getModule(m_context, propertyName);
}
JSValueRef JSCExecutor::getNativeExtension(JSObjectRef object, JSStringRef propertyName) {
if (m_nativeExtensionsProvider) {
folly::dynamic value = m_nativeExtensionsProvider(String::ref(m_context, propertyName).str());
return Value::fromDynamic(m_context, std::move(value));
}
return JSC_JSValueMakeUndefined(m_context);
}
JSValueRef JSCExecutor::nativeRequire(size_t count, const JSValueRef arguments[]) {
if (count > 2 || count == 0) {
throw std::invalid_argument("Got wrong number of args");
}
uint32_t moduleId = folly::to<uint32_t>(Value(m_context, arguments[0]).getNumberOrThrow());
uint32_t bundleId = count == 2 ? folly::to<uint32_t>(Value(m_context, arguments[1]).getNumberOrThrow()) : 0;
ReactMarker::logMarker(ReactMarker::NATIVE_REQUIRE_START);
loadModule(bundleId, moduleId);
ReactMarker::logMarker(ReactMarker::NATIVE_REQUIRE_STOP);
return Value::makeUndefined(m_context);
}
JSValueRef JSCExecutor::nativeFlushQueueImmediate(
size_t argumentCount,
const JSValueRef arguments[]) {
if (argumentCount != 1) {
throw std::invalid_argument("Got wrong number of args");
}
flushQueueImmediate(Value(m_context, arguments[0]));
return Value::makeUndefined(m_context);
}
JSValueRef JSCExecutor::nativeCallSyncHook(
size_t argumentCount,
const JSValueRef arguments[]) {
if (argumentCount != 3) {
throw std::invalid_argument("Got wrong number of args");
}
unsigned int moduleId = Value(m_context, arguments[0]).asUnsignedInteger();
unsigned int methodId = Value(m_context, arguments[1]).asUnsignedInteger();
folly::dynamic args = folly::parseJson(Value(m_context, arguments[2]).toJSONString());
if (!args.isArray()) {
throw std::invalid_argument(
folly::to<std::string>("method parameters should be array, but are ", args.typeName()));
}
MethodCallResult result = m_delegate->callSerializableNativeHook(
*this,
moduleId,
methodId,
std::move(args));
if (!result.hasValue()) {
return Value::makeUndefined(m_context);
}
return Value::fromDynamic(m_context, result.value());
}
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <cstdint>
#include <memory>
#include <mutex>
#include <cxxreact/JSCNativeModules.h>
#include <cxxreact/JSExecutor.h>
#include <folly/Optional.h>
#include <folly/json.h>
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/JavaScriptCore.h>
#include <jschelpers/Value.h>
#include <privatedata/PrivateDataBase.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
class MessageQueueThread;
class RAMBundleRegistry;
class RN_EXPORT JSCExecutorFactory : public JSExecutorFactory {
public:
JSCExecutorFactory(const folly::dynamic& jscConfig, std::function<folly::dynamic(const std::string &)> provider) :
m_jscConfig(jscConfig), m_nativeExtensionsProvider(provider) {}
std::unique_ptr<JSExecutor> createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) override;
private:
std::string m_cacheDir;
folly::dynamic m_jscConfig;
std::function<folly::dynamic(const std::string &)> m_nativeExtensionsProvider;
};
template<typename T>
struct JSCValueEncoder {
// If you get a build error here, it means the compiler can't see the template instantation of toJSCValue
// applicable to your type.
static const Value toJSCValue(JSGlobalContextRef ctx, T&& value);
};
template<>
struct JSCValueEncoder<folly::dynamic> {
static const Value toJSCValue(JSGlobalContextRef ctx, const folly::dynamic &&value) {
return Value::fromDynamic(ctx, value);
}
};
class RN_EXPORT JSCExecutor : public JSExecutor, public PrivateDataBase {
public:
/**
* Must be invoked from thread this Executor will run on.
*/
explicit JSCExecutor(std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> messageQueueThread,
const folly::dynamic& jscConfig,
std::function<folly::dynamic(const std::string &)> nativeExtensionsProvider) throw(JSException);
~JSCExecutor() override;
virtual void loadApplicationScript(
std::unique_ptr<const JSBigString> script,
std::string sourceURL) override;
virtual void setBundleRegistry(std::unique_ptr<RAMBundleRegistry> bundleRegistry) override;
virtual void registerBundle(uint32_t bundleId, const std::string& bundlePath) override;
virtual void callFunction(
const std::string& moduleId,
const std::string& methodId,
const folly::dynamic& arguments) override;
virtual void invokeCallback(
const double callbackId,
const folly::dynamic& arguments) override;
template <typename T>
Value callFunctionSync(
const std::string& module, const std::string& method, T&& args) {
return callFunctionSyncWithValue(
module, method, JSCValueEncoder<typename std::decay<T>::type>::toJSCValue(
m_context, std::forward<T>(args)));
}
virtual void setGlobalVariable(
std::string propName,
std::unique_ptr<const JSBigString> jsonValue) override;
virtual std::string getDescription() override;
virtual void* getJavaScriptContext() override;
virtual bool isInspectable() override;
#ifdef WITH_JSC_MEMORY_PRESSURE
virtual void handleMemoryPressure(int pressureLevel) override;
#endif
virtual void destroy() override;
void setContextName(const std::string& name);
private:
JSGlobalContextRef m_context;
std::shared_ptr<ExecutorDelegate> m_delegate;
std::shared_ptr<bool> m_isDestroyed = std::shared_ptr<bool>(new bool(false));
std::shared_ptr<MessageQueueThread> m_messageQueueThread;
std::unique_ptr<RAMBundleRegistry> m_bundleRegistry;
JSCNativeModules m_nativeModules;
folly::dynamic m_jscConfig;
std::once_flag m_bindFlag;
std::function<folly::dynamic(const std::string &)> m_nativeExtensionsProvider;
folly::Optional<Object> m_invokeCallbackAndReturnFlushedQueueJS;
folly::Optional<Object> m_callFunctionReturnFlushedQueueJS;
folly::Optional<Object> m_flushedQueueJS;
folly::Optional<Object> m_callFunctionReturnResultAndFlushedQueueJS;
void initOnJSVMThread() throw(JSException);
static bool isNetworkInspected(const std::string &owner, const std::string &app, const std::string &device);
// This method is experimental, and may be modified or removed.
Value callFunctionSyncWithValue(
const std::string& module, const std::string& method, Value value);
void terminateOnJSVMThread();
void bindBridge() throw(JSException);
void callNativeModules(Value&&);
void flush();
void flushQueueImmediate(Value&&);
void loadModule(uint32_t bundleId, uint32_t moduleId);
String adoptString(std::unique_ptr<const JSBigString>);
template<JSValueRef (JSCExecutor::*method)(size_t, const JSValueRef[])>
void installNativeHook(const char* name);
JSValueRef getNativeModule(JSObjectRef object, JSStringRef propertyName);
JSValueRef getNativeExtension(JSObjectRef object, JSStringRef propertyName);
JSValueRef nativeRequire(
size_t argumentCount,
const JSValueRef arguments[]);
JSValueRef nativeFlushQueueImmediate(
size_t argumentCount,
const JSValueRef arguments[]);
JSValueRef nativeCallSyncHook(
size_t argumentCount,
const JSValueRef arguments[]);
};
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCLegacyTracing.h"
#if defined(WITH_JSC_EXTRA_TRACING)
#include <fbsystrace.h>
#include <JavaScriptCore/API/JSProfilerPrivate.h>
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
static const char *ENABLED_FBSYSTRACE_PROFILE_NAME = "__fbsystrace__";
using namespace facebook::react;
static int64_t int64FromJSValue(JSContextRef ctx, JSValueRef value, JSValueRef* exception) {
return static_cast<int64_t>(JSC_JSValueToNumber(ctx, value, exception));
}
static JSValueRef nativeTraceBeginLegacy(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (FBSYSTRACE_LIKELY(argumentCount >= 1)) {
uint64_t tag = int64FromJSValue(ctx, arguments[0], exception);
if (!fbsystrace_is_tracing(tag)) {
return Value::makeUndefined(ctx);
}
}
JSStartProfiling(ctx, String(ctx, ENABLED_FBSYSTRACE_PROFILE_NAME), true);
return Value::makeUndefined(ctx);
}
static JSValueRef nativeTraceEndLegacy(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (FBSYSTRACE_LIKELY(argumentCount >= 1)) {
uint64_t tag = int64FromJSValue(ctx, arguments[0], exception);
if (!fbsystrace_is_tracing(tag)) {
return Value::makeUndefined(ctx);
}
}
JSEndProfiling(ctx, String(ctx, ENABLED_FBSYSTRACE_PROFILE_NAME));
return Value::makeUndefined(ctx);
}
#endif
namespace facebook {
namespace react {
void addNativeTracingLegacyHooks(JSGlobalContextRef ctx) {
#if defined(WITH_JSC_EXTRA_TRACING)
installGlobalFunction(ctx, "nativeTraceBeginLegacy", nativeTraceBeginLegacy);
installGlobalFunction(ctx, "nativeTraceEndLegacy", nativeTraceEndLegacy);
#endif
}
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <jschelpers/JavaScriptCore.h>
namespace facebook {
namespace react {
void addNativeTracingLegacyHooks(JSGlobalContextRef ctx);
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCMemory.h"
#ifdef WITH_FB_MEMORY_PROFILING
#include <stdio.h>
#include <string.h>
#include <JavaScriptCore/API/JSProfilerPrivate.h>
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
using namespace facebook::react;
static JSValueRef nativeCaptureHeap(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (argumentCount < 1) {
if (exception) {
*exception = Value::makeError(
ctx,
"nativeCaptureHeap requires the path to save the capture");
}
return Value::makeUndefined(ctx);
}
auto outputFilename = Value(ctx, arguments[0]).toString();
JSCaptureHeap(ctx, outputFilename.str().c_str(), exception);
return Value::makeUndefined(ctx);
}
#endif // WITH_FB_MEMORY_PROFILING
namespace facebook {
namespace react {
void addJSCMemoryHooks(JSGlobalContextRef ctx) {
#ifdef WITH_FB_MEMORY_PROFILING
installGlobalFunction(ctx, "nativeCaptureHeap", nativeCaptureHeap);
#endif // WITH_FB_MEMORY_PROFILING
}
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <jschelpers/JavaScriptCore.h>
namespace facebook {
namespace react {
void addJSCMemoryHooks(JSGlobalContextRef ctx);
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCNativeModules.h"
#include <string>
#include "Platform.h"
namespace facebook {
namespace react {
JSCNativeModules::JSCNativeModules(std::shared_ptr<ModuleRegistry> moduleRegistry) :
m_moduleRegistry(std::move(moduleRegistry)) {}
JSValueRef JSCNativeModules::getModule(JSContextRef context, JSStringRef jsName) {
if (!m_moduleRegistry) {
return nullptr;
}
std::string moduleName = String::ref(context, jsName).str();
const auto it = m_objects.find(moduleName);
if (it != m_objects.end()) {
return static_cast<JSObjectRef>(it->second);
}
auto module = createModule(moduleName, context);
if (!module.hasValue()) {
// Allow lookup to continue in the objects own properties, which allows for overrides of NativeModules
return nullptr;
}
// Protect since we'll be holding on to this value, even though JS may not
module->makeProtected();
auto result = m_objects.emplace(std::move(moduleName), std::move(*module)).first;
return static_cast<JSObjectRef>(result->second);
}
void JSCNativeModules::reset() {
m_genNativeModuleJS = nullptr;
m_objects.clear();
}
folly::Optional<Object> JSCNativeModules::createModule(const std::string& name, JSContextRef context) {
ReactMarker::logTaggedMarker(ReactMarker::NATIVE_MODULE_SETUP_START, name.c_str());
if (!m_genNativeModuleJS) {
auto global = Object::getGlobalObject(context);
m_genNativeModuleJS = global.getProperty("__fbGenNativeModule").asObject();
m_genNativeModuleJS->makeProtected();
}
auto result = m_moduleRegistry->getConfig(name);
if (!result.hasValue()) {
return nullptr;
}
Value moduleInfo = m_genNativeModuleJS->callAsFunction({
Value::fromDynamic(context, result->config),
Value::makeNumber(context, result->index)
});
CHECK(!moduleInfo.isNull()) << "Module returned from genNativeModule is null";
folly::Optional<Object> module(moduleInfo.asObject().getProperty("module").asObject());
ReactMarker::logTaggedMarker(ReactMarker::NATIVE_MODULE_SETUP_STOP, name.c_str());
return module;
}
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <memory>
#include <string>
#include <cxxreact/ModuleRegistry.h>
#include <folly/Optional.h>
#include <jschelpers/Value.h>
namespace facebook {
namespace react {
/**
* Holds and creates JS representations of the modules in ModuleRegistry
*/
class JSCNativeModules {
public:
explicit JSCNativeModules(std::shared_ptr<ModuleRegistry> moduleRegistry);
JSValueRef getModule(JSContextRef context, JSStringRef name);
void reset();
private:
folly::Optional<Object> m_genNativeModuleJS;
std::shared_ptr<ModuleRegistry> m_moduleRegistry;
std::unordered_map<std::string, Object> m_objects;
folly::Optional<Object> createModule(const std::string& name, JSContextRef context);
};
}
}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCPerfStats.h"
#ifdef JSC_HAS_PERF_STATS_API
#include <cstdint>
#include <sys/time.h>
#include <sys/resource.h>
#include <JavaScriptCore/JSPerfStats.h>
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
using namespace facebook::react;
static uint64_t toMillis(struct timeval tv) {
return tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
}
static JSValueRef nativeGetProcessPerfStats(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
struct rusage usage{};
if (getrusage(RUSAGE_SELF, &usage) != 0) {
return Value::makeUndefined(ctx);
}
auto result = Object::create(ctx);
uint64_t cpu_time_ms = toMillis(usage.ru_utime) + toMillis(usage.ru_stime);
result.setProperty("major_faults", Value::makeNumber(ctx, usage.ru_majflt));
result.setProperty("minor_faults", Value::makeNumber(ctx, usage.ru_minflt));
result.setProperty("cpu_time_ms", Value::makeNumber(ctx, cpu_time_ms));
result.setProperty("input_blocks", Value::makeNumber(ctx, usage.ru_inblock));
result.setProperty("output_blocks", Value::makeNumber(ctx, usage.ru_oublock));
return static_cast<JSObjectRef>(result);
}
static JSValueRef nativeGetHeapStats(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
JSHeapStats heapStats = {0};
JSGetHeapStats(ctx, &heapStats);
auto result = Object::create(ctx);
result.setProperty("size", Value::makeNumber(ctx, heapStats.size));
result.setProperty("extra_size", Value::makeNumber(ctx, heapStats.extraSize));
result.setProperty("capacity", Value::makeNumber(ctx, heapStats.capacity));
result.setProperty("object_count", Value::makeNumber(ctx, heapStats.objectCount));
result.setProperty("object_size", Value::makeNumber(ctx, heapStats.objectSizeAfterLastCollect));
result.setProperty("object_capacity", Value::makeNumber(ctx, heapStats.objectCapacityAfterLastCollect));
result.setProperty("block_size", Value::makeNumber(ctx, heapStats.blockSize));
result.setProperty("malloc_size", Value::makeNumber(ctx, heapStats.mallocSize));
return static_cast<JSObjectRef>(result);
}
static JSValueRef nativeGetGCStats(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
JSGCStats gcStats = {0};
JSGetGCStats(ctx, &gcStats);
auto result = Object::create(ctx);
result.setProperty("last_full_gc_length", Value::makeNumber(ctx, gcStats.lastFullGCLength));
result.setProperty("last_eden_gc_length", Value::makeNumber(ctx, gcStats.lastEdenGCLength));
return static_cast<JSObjectRef>(result);
}
#endif
namespace facebook {
namespace react {
void addJSCPerfStatsHooks(JSGlobalContextRef ctx) {
#ifdef JSC_HAS_PERF_STATS_API
installGlobalFunction(ctx, "nativeGetProcessPerfStats", nativeGetProcessPerfStats);
installGlobalFunction(ctx, "nativeGetHeapStats", nativeGetHeapStats);
installGlobalFunction(ctx, "nativeGetGCStats", nativeGetGCStats);
#endif
}
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <jschelpers/JavaScriptCore.h>
namespace facebook {
namespace react {
void addJSCPerfStatsHooks(JSGlobalContextRef ctx);
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCSamplingProfiler.h"
#include <jschelpers/JSCHelpers.h>
static JSValueRef pokeSamplingProfiler(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
return JSC_JSPokeSamplingProfiler(ctx);
}
namespace facebook {
namespace react {
void initSamplingProfilerOnMainJSCThread(JSGlobalContextRef ctx) {
JSC_JSStartSamplingProfilingOnMainJSCThread(ctx);
// Allow the profiler to be poked from JS as well
// (see SamplingProfiler.js for an example of how it could be used with the JSCSamplingProfiler module).
installGlobalFunction(ctx, "pokeSamplingProfiler", pokeSamplingProfiler);
}
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <jschelpers/JavaScriptCore.h>
namespace facebook {
namespace react {
void initSamplingProfilerOnMainJSCThread(JSGlobalContextRef ctx);
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCTracing.h"
#if defined(WITH_FBSYSTRACE) && (defined(WITH_JSC_EXTRA_TRACING) || DEBUG)
#define USE_JSCTRACING 1
#else
#define USE_JSCTRACING 0
#endif
#if USE_JSCTRACING
#include <algorithm>
#include <fbsystrace.h>
#include <sys/types.h>
#include <unistd.h>
#include <jschelpers/JavaScriptCore.h>
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
using std::min;
using namespace facebook::react;
static int64_t int64FromJSValue(JSContextRef ctx, JSValueRef value, JSValueRef* exception) {
return static_cast<int64_t>(JSC_JSValueToNumber(ctx, value, exception));
}
static size_t copyTruncatedAsciiChars(
char* buf,
size_t bufLen,
JSContextRef ctx,
JSValueRef value,
size_t maxLen) {
JSStringRef jsString = JSC_JSValueToStringCopy(ctx, value, NULL);
size_t stringLen = JSC_JSStringGetLength(ctx, jsString);
// Unlike the Java version, we truncate from the end of the string,
// rather than the beginning.
size_t toWrite = min(stringLen, min(bufLen, maxLen));
const char *startBuf = buf;
const JSChar* chars = JSC_JSStringGetCharactersPtr(ctx, jsString);
while (toWrite-- > 0) {
*(buf++) = (char)*(chars++);
}
JSC_JSStringRelease(ctx, jsString);
// Return the number of bytes written
return buf - startBuf;
}
static size_t copyArgsToBuffer(
char* buf,
size_t bufLen,
size_t pos,
JSContextRef ctx,
size_t argumentCount,
const JSValueRef arguments[]) {
char separator = '|';
for (
size_t idx = 0;
idx + 1 < argumentCount; // Make sure key and value are present.
idx += 2) {
JSValueRef key = arguments[idx];
JSValueRef value = arguments[idx+1];
buf[pos++] = separator;
separator = ';';
if (FBSYSTRACE_UNLIKELY(pos >= bufLen)) { break; }
pos += copyTruncatedAsciiChars(
buf + pos, bufLen - pos, ctx, key, FBSYSTRACE_MAX_MESSAGE_LENGTH);
if (FBSYSTRACE_UNLIKELY(pos >= bufLen)) { break; }
buf[pos++] = '=';
if (FBSYSTRACE_UNLIKELY(pos >= bufLen)) { break; }
pos += copyTruncatedAsciiChars(
buf + pos, bufLen - pos, ctx, value, FBSYSTRACE_MAX_MESSAGE_LENGTH);
if (FBSYSTRACE_UNLIKELY(pos >= bufLen)) { break; }
}
return pos;
}
static JSValueRef nativeTraceBeginSection(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (FBSYSTRACE_UNLIKELY(argumentCount < 2)) {
if (exception) {
*exception = Value::makeError(
ctx,
"nativeTraceBeginSection: requires at least 2 arguments");
}
return Value::makeUndefined(ctx);
}
uint64_t tag = int64FromJSValue(ctx, arguments[0], exception);
if (!fbsystrace_is_tracing(tag)) {
return Value::makeUndefined(ctx);
}
char buf[FBSYSTRACE_MAX_MESSAGE_LENGTH];
size_t pos = 0;
pos += snprintf(buf + pos, sizeof(buf) - pos, "B|%d|", getpid());
// Skip the overflow check here because the int will be small.
pos += copyTruncatedAsciiChars(buf + pos, sizeof(buf) - pos, ctx, arguments[1], FBSYSTRACE_MAX_SECTION_NAME_LENGTH);
// Skip the overflow check here because the section name will be small-ish.
pos = copyArgsToBuffer(buf, sizeof(buf), pos, ctx, argumentCount - 2, arguments + 2);
if (FBSYSTRACE_UNLIKELY(pos >= sizeof(buf))) {
goto flush;
}
flush:
fbsystrace_trace_raw(buf, min(pos, sizeof(buf)-1));
return Value::makeUndefined(ctx);
}
static JSValueRef nativeTraceEndSection(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (FBSYSTRACE_UNLIKELY(argumentCount < 1)) {
if (exception) {
*exception = Value::makeError(
ctx,
"nativeTraceEndSection: requires at least 1 argument");
}
return Value::makeUndefined(ctx);
}
uint64_t tag = int64FromJSValue(ctx, arguments[0], exception);
if (!fbsystrace_is_tracing(tag)) {
return Value::makeUndefined(ctx);
}
if (FBSYSTRACE_LIKELY(argumentCount == 1)) {
fbsystrace_end_section(tag);
} else {
char buf[FBSYSTRACE_MAX_MESSAGE_LENGTH];
size_t pos = 0;
buf[pos++] = 'E';
buf[pos++] = '|';
buf[pos++] = '|';
pos = copyArgsToBuffer(buf, sizeof(buf), pos, ctx, argumentCount - 1, arguments + 1);
if (FBSYSTRACE_UNLIKELY(pos >= sizeof(buf))) {
goto flush;
}
flush:
fbsystrace_trace_raw(buf, min(pos, sizeof(buf)-1));
}
return Value::makeUndefined(ctx);
}
static JSValueRef beginOrEndAsync(
bool isEnd,
bool isFlow,
JSContextRef ctx,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (FBSYSTRACE_UNLIKELY(argumentCount < 3)) {
if (exception) {
*exception = Value::makeError(
ctx,
"beginOrEndAsync: requires at least 3 arguments");
}
return Value::makeUndefined(ctx);
}
uint64_t tag = int64FromJSValue(ctx, arguments[0], exception);
if (!fbsystrace_is_tracing(tag)) {
return Value::makeUndefined(ctx);
}
char buf[FBSYSTRACE_MAX_MESSAGE_LENGTH];
size_t pos = 0;
// This uses an if-then-else instruction in ARMv7, which should be cheaper
// than a full branch.
buf[pos++] = ((isFlow) ? (isEnd ? 'f' : 's') : (isEnd ? 'F' : 'S'));
pos += snprintf(buf + pos, sizeof(buf) - pos, "|%d|", getpid());
// Skip the overflow check here because the int will be small.
pos += copyTruncatedAsciiChars(buf + pos, sizeof(buf) - pos, ctx, arguments[1], FBSYSTRACE_MAX_SECTION_NAME_LENGTH);
// Skip the overflow check here because the section name will be small-ish.
// I tried some trickery to avoid a branch here, but gcc did not cooperate.
// We could consider changing the implementation to be lest branchy in the
// future.
// This is not required for flow use an or to avoid introducing another branch
if (!(isEnd | isFlow)) {
buf[pos++] = '<';
buf[pos++] = '0';
buf[pos++] = '>';
}
buf[pos++] = '|';
// Append the cookie. It should be an integer, but copyTruncatedAsciiChars
// will automatically convert it to a string. We might be able to get more
// performance by just getting the number and doing to string
// conversion ourselves. We truncate to FBSYSTRACE_MAX_SECTION_NAME_LENGTH
// just to make sure we can avoid the overflow check even if the caller
// passes in something bad.
pos += copyTruncatedAsciiChars(buf + pos, sizeof(buf) - pos, ctx, arguments[2], FBSYSTRACE_MAX_SECTION_NAME_LENGTH);
pos = copyArgsToBuffer(buf, sizeof(buf), pos, ctx, argumentCount - 3, arguments + 3);
if (FBSYSTRACE_UNLIKELY(pos >= sizeof(buf))) {
goto flush;
}
flush:
fbsystrace_trace_raw(buf, min(pos, sizeof(buf)-1));
return Value::makeUndefined(ctx);
}
static JSValueRef nativeTraceBeginAsyncSection(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
return beginOrEndAsync(false /* isEnd */, false /* isFlow */,
ctx, argumentCount, arguments, exception);
}
static JSValueRef nativeTraceEndAsyncSection(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
return beginOrEndAsync(true /* isEnd */, false /* isFlow */,
ctx, argumentCount, arguments, exception);
}
static JSValueRef nativeTraceBeginAsyncFlow(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
return beginOrEndAsync(false /* isEnd */, true /* isFlow */,
ctx, argumentCount, arguments, exception);
}
static JSValueRef nativeTraceEndAsyncFlow(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
return beginOrEndAsync(true /* isEnd */, true /* isFlow */,
ctx, argumentCount, arguments, exception);
}
static JSValueRef nativeTraceCounter(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
if (FBSYSTRACE_UNLIKELY(argumentCount < 3)) {
if (exception) {
*exception = Value::makeError(
ctx,
"nativeTraceCounter: requires at least 3 arguments");
}
return Value::makeUndefined(ctx);
}
uint64_t tag = int64FromJSValue(ctx, arguments[0], exception);
if (!fbsystrace_is_tracing(tag)) {
return Value::makeUndefined(ctx);
}
char buf[FBSYSTRACE_MAX_MESSAGE_LENGTH];
size_t len = copyTruncatedAsciiChars(buf, sizeof(buf), ctx,
arguments[1], FBSYSTRACE_MAX_SECTION_NAME_LENGTH);
buf[min(len,(FBSYSTRACE_MAX_MESSAGE_LENGTH-1))] = 0;
int64_t value = int64FromJSValue(ctx, arguments[2], exception);
fbsystrace_counter(tag, buf, value);
return Value::makeUndefined(ctx);
}
#endif
namespace facebook {
namespace react {
void addNativeTracingHooks(JSGlobalContextRef ctx) {
#if USE_JSCTRACING
installGlobalFunction(ctx, "nativeTraceBeginSection", nativeTraceBeginSection);
installGlobalFunction(ctx, "nativeTraceEndSection", nativeTraceEndSection);
installGlobalFunction(ctx, "nativeTraceBeginAsyncSection", nativeTraceBeginAsyncSection);
installGlobalFunction(ctx, "nativeTraceEndAsyncSection", nativeTraceEndAsyncSection);
installGlobalFunction(ctx, "nativeTraceBeginAsyncFlow", nativeTraceBeginAsyncFlow);
installGlobalFunction(ctx, "nativeTraceEndAsyncFlow", nativeTraceEndAsyncFlow);
installGlobalFunction(ctx, "nativeTraceCounter", nativeTraceCounter);
#endif
}
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <jschelpers/JavaScriptCore.h>
namespace facebook {
namespace react {
void addNativeTracingHooks(JSGlobalContextRef ctx);
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCUtils.h"
#include <folly/Conv.h>
namespace facebook {
namespace react {
String jsStringFromBigString(JSContextRef ctx, const JSBigString& bigstr) {
if (bigstr.isAscii()) {
return String::createExpectingAscii(ctx, bigstr.c_str(), bigstr.size());
} else {
return String(ctx, bigstr.c_str());
}
}
}
}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <cxxreact/JSBigString.h>
#include <jschelpers/JavaScriptCore.h>
#include <jschelpers/Value.h>
namespace facebook {
namespace react {
String jsStringFromBigString(JSContextRef ctx, const JSBigString& bigstr);
}
}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <memory>
#include <string>
#include <cxxreact/NativeModule.h>
#include <folly/dynamic.h>
namespace facebook {
namespace react {
class JSBigString;
class JSExecutor;
class JSModulesUnbundle;
class MessageQueueThread;
class ModuleRegistry;
class RAMBundleRegistry;
// This interface describes the delegate interface required by
// Executor implementations to call from JS into native code.
class ExecutorDelegate {
public:
virtual ~ExecutorDelegate() {}
virtual std::shared_ptr<ModuleRegistry> getModuleRegistry() = 0;
virtual void callNativeModules(
JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) = 0;
virtual MethodCallResult callSerializableNativeHook(
JSExecutor& executor, unsigned int moduleId, unsigned int methodId, folly::dynamic&& args) = 0;
};
class JSExecutorFactory {
public:
virtual std::unique_ptr<JSExecutor> createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) = 0;
virtual ~JSExecutorFactory() {}
};
class JSExecutor {
public:
/**
* Execute an application script bundle in the JS context.
*/
virtual void loadApplicationScript(std::unique_ptr<const JSBigString> script,
std::string sourceURL) = 0;
/**
* Add an application "RAM" bundle registry
*/
virtual void setBundleRegistry(std::unique_ptr<RAMBundleRegistry> bundleRegistry) = 0;
/**
* Register a file path for an additional "RAM" bundle
*/
virtual void registerBundle(uint32_t bundleId, const std::string& bundlePath) = 0;
/**
* Executes BatchedBridge.callFunctionReturnFlushedQueue with the module ID,
* method ID and optional additional arguments in JS. The executor is responsible
* for using Bridge->callNativeModules to invoke any necessary native modules methods.
*/
virtual void callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) = 0;
/**
* Executes BatchedBridge.invokeCallbackAndReturnFlushedQueue with the cbID,
* and optional additional arguments in JS and returns the next queue. The executor
* is responsible for using Bridge->callNativeModules to invoke any necessary
* native modules methods.
*/
virtual void invokeCallback(const double callbackId, const folly::dynamic& arguments) = 0;
virtual void setGlobalVariable(std::string propName,
std::unique_ptr<const JSBigString> jsonValue) = 0;
virtual void* getJavaScriptContext() {
return nullptr;
}
/**
* Returns whether or not the underlying executor supports debugging via the
* Chrome remote debugging protocol.
*/
virtual bool isInspectable() {
return false;
}
/**
* The description is displayed in the dev menu, if there is one in
* this build. There is a default, but if this method returns a
* non-empty string, it will be used instead.
*/
virtual std::string getDescription() = 0;
virtual void handleMemoryPressure(int pressureLevel) {}
virtual void destroy() {}
virtual ~JSExecutor() {}
};
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSIndexedRAMBundle.h"
#include <folly/Memory.h>
namespace facebook {
namespace react {
std::function<std::unique_ptr<JSModulesUnbundle>(std::string)> JSIndexedRAMBundle::buildFactory() {
return [](const std::string& bundlePath){
return folly::make_unique<JSIndexedRAMBundle>(bundlePath.c_str());
};
}
JSIndexedRAMBundle::JSIndexedRAMBundle(const char *sourcePath) :
m_bundle (sourcePath, std::ios_base::in) {
if (!m_bundle) {
throw std::ios_base::failure(
folly::to<std::string>("Bundle ", sourcePath,
"cannot be opened: ", m_bundle.rdstate()));
}
// read in magic header, number of entries, and length of the startup section
uint32_t header[3];
static_assert(
sizeof(header) == 12,
"header size must exactly match the input file format");
readBundle(reinterpret_cast<char *>(header), sizeof(header));
const size_t numTableEntries = folly::Endian::little(header[1]);
const size_t startupCodeSize = folly::Endian::little(header[2]);
// allocate memory for meta data and lookup table.
m_table = ModuleTable(numTableEntries);
m_baseOffset = sizeof(header) + m_table.byteLength();
// read the lookup table from the file
readBundle(
reinterpret_cast<char *>(m_table.data.get()), m_table.byteLength());
// read the startup code
m_startupCode = std::unique_ptr<JSBigBufferString>(new JSBigBufferString{startupCodeSize - 1});
readBundle(m_startupCode->data(), startupCodeSize - 1);
}
JSIndexedRAMBundle::Module JSIndexedRAMBundle::getModule(uint32_t moduleId) const {
Module ret;
ret.name = folly::to<std::string>(moduleId, ".js");
ret.code = getModuleCode(moduleId);
return ret;
}
std::unique_ptr<const JSBigString> JSIndexedRAMBundle::getStartupCode() {
CHECK(m_startupCode) << "startup code for a RAM Bundle can only be retrieved once";
return std::move(m_startupCode);
}
std::string JSIndexedRAMBundle::getModuleCode(const uint32_t id) const {
const auto moduleData = id < m_table.numEntries ? &m_table.data[id] : nullptr;
// entries without associated code have offset = 0 and length = 0
const uint32_t length = moduleData ? folly::Endian::little(moduleData->length) : 0;
if (length == 0) {
throw std::ios_base::failure(
folly::to<std::string>("Error loading module", id, "from RAM Bundle"));
}
std::string ret(length - 1, '\0');
readBundle(&ret.front(), length - 1, m_baseOffset + folly::Endian::little(moduleData->offset));
return ret;
}
void JSIndexedRAMBundle::readBundle(char *buffer, const std::streamsize bytes) const {
if (!m_bundle.read(buffer, bytes)) {
if (m_bundle.rdstate() & std::ios::eofbit) {
throw std::ios_base::failure("Unexpected end of RAM Bundle file");
}
throw std::ios_base::failure(
folly::to<std::string>("Error reading RAM Bundle: ", m_bundle.rdstate()));
}
}
void JSIndexedRAMBundle::readBundle(
char *buffer,
const std::streamsize bytes,
const std::ifstream::pos_type position) const {
if (!m_bundle.seekg(position)) {
throw std::ios_base::failure(
folly::to<std::string>("Error reading RAM Bundle: ", m_bundle.rdstate()));
}
readBundle(buffer, bytes);
}
} // namespace react
} // namespace facebook
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <fstream>
#include <memory>
#include <cxxreact/JSBigString.h>
#include <cxxreact/JSModulesUnbundle.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
class RN_EXPORT JSIndexedRAMBundle : public JSModulesUnbundle {
public:
static std::function<std::unique_ptr<JSModulesUnbundle>(std::string)> buildFactory();
// Throws std::runtime_error on failure.
JSIndexedRAMBundle(const char *sourceURL);
// Throws std::runtime_error on failure.
std::unique_ptr<const JSBigString> getStartupCode();
// Throws std::runtime_error on failure.
Module getModule(uint32_t moduleId) const override;
private:
struct ModuleData {
uint32_t offset;
uint32_t length;
};
static_assert(
sizeof(ModuleData) == 8,
"ModuleData must not have any padding and use sizes matching input files");
struct ModuleTable {
size_t numEntries;
std::unique_ptr<ModuleData[]> data;
ModuleTable() : numEntries(0) {};
ModuleTable(size_t entries) :
numEntries(entries),
data(std::unique_ptr<ModuleData[]>(new ModuleData[numEntries])) {};
size_t byteLength() const {
return numEntries * sizeof(ModuleData);
}
};
std::string getModuleCode(const uint32_t id) const;
void readBundle(char *buffer, const std::streamsize bytes) const;
void readBundle(
char *buffer, const
std::streamsize bytes,
const std::ifstream::pos_type position) const;
mutable std::ifstream m_bundle;
ModuleTable m_table;
size_t m_baseOffset;
std::unique_ptr<JSBigBufferString> m_startupCode;
};
} // namespace react
} // namespace facebook
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <cstdint>
#include <string>
#include <stdexcept>
#include <jschelpers/noncopyable.h>
namespace facebook {
namespace react {
class JSModulesUnbundle : noncopyable {
/**
* Represents the set of JavaScript modules that the application consists of.
* The source code of each module can be retrieved by module ID.
*
* The class is non-copyable because copying instances might involve copying
* several megabytes of memory.
*/
public:
class ModuleNotFound : public std::out_of_range {
using std::out_of_range::out_of_range;
};
struct Module {
std::string name;
std::string code;
};
virtual ~JSModulesUnbundle() {}
virtual Module getModule(uint32_t moduleId) const = 0;
};
}
}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
namespace facebook {
namespace xplat {
namespace detail {
template <typename R, typename M, typename... T>
R jsArg1(const folly::dynamic& arg, M asFoo, const T&... desc) {
try {
return (arg.*asFoo)();
} catch (const folly::TypeError& ex) {
throw JsArgumentException(
folly::to<std::string>(
"Error converting javascript arg ", desc..., " to C++: ", ex.what()));
} catch (const std::range_error& ex) {
throw JsArgumentException(
folly::to<std::string>(
"Could not convert argument ", desc..., " to required type: ", ex.what()));
}
}
}
template <typename R, typename... T>
R jsArg(const folly::dynamic& arg, R (folly::dynamic::*asFoo)() const, const T&... desc) {
return detail::jsArg1<R>(arg, asFoo, desc...);
}
template <typename R, typename... T>
R jsArg(const folly::dynamic& arg, R (folly::dynamic::*asFoo)() const&, const T&... desc) {
return detail::jsArg1<R>(arg, asFoo, desc...);
}
template <typename T>
typename detail::is_dynamic<T>::type& jsArgAsDynamic(T&& args, size_t n) {
try {
return args[n];
} catch (const std::out_of_range& ex) {
// Use 1-base counting for argument description.
throw JsArgumentException(
folly::to<std::string>(
"JavaScript provided ", args.size(),
" arguments for C++ method which references at least ", n + 1,
" arguments: ", ex.what()));
}
}
template <typename R>
R jsArgN(const folly::dynamic& args, size_t n, R (folly::dynamic::*asFoo)() const) {
return jsArg(jsArgAsDynamic(args, n), asFoo, n);
}
template <typename R>
R jsArgN(const folly::dynamic& args, size_t n, R (folly::dynamic::*asFoo)() const&) {
return jsArg(jsArgAsDynamic(args, n), asFoo, n);
}
namespace detail {
// This is a helper for jsArgAsArray and jsArgAsObject.
template <typename T>
typename detail::is_dynamic<T>::type& jsArgAsType(T&& args, size_t n, const char* required,
bool (folly::dynamic::*isFoo)() const) {
T& ret = jsArgAsDynamic(args, n);
if ((ret.*isFoo)()) {
return ret;
}
// Use 1-base counting for argument description.
throw JsArgumentException(
folly::to<std::string>(
"Argument ", n + 1, " of type ", ret.typeName(), " is not required type ", required));
}
} // end namespace detail
template <typename T>
typename detail::is_dynamic<T>::type& jsArgAsArray(T&& args, size_t n) {
return detail::jsArgAsType(args, n, "Array", &folly::dynamic::isArray);
}
template <typename T>
typename detail::is_dynamic<T>::type& jsArgAsObject(T&& args, size_t n) {
return detail::jsArgAsType(args, n, "Object", &folly::dynamic::isObject);
}
}}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <exception>
#include <string>
#include <folly/Conv.h>
#include <folly/dynamic.h>
// When building a cross-platform module for React Native, arguments passed
// from JS are represented as a folly::dynamic. This class provides helpers to
// extract arguments from the folly::dynamic to concrete types usable by
// cross-platform code, and converting exceptions to a JsArgumentException so
// they can be caught and reported to RN consistently. The goal is to make the
// jsArgAs... methods at the end simple to use should be most common, but any
// non-detail method can be used when needed.
namespace facebook {
namespace xplat {
class JsArgumentException : public std::logic_error {
public:
JsArgumentException(const std::string& msg) : std::logic_error(msg) {}
};
// This extracts a single argument by calling the given method pointer on it.
// If an exception is thrown, the additional arguments are passed to
// folly::to<> to be included in the exception string. This will be most
// commonly used when extracting values from non-scalar argument. The second
// overload accepts ref-qualified member functions.
template <typename R, typename... T>
R jsArg(const folly::dynamic& arg, R (folly::dynamic::*asFoo)() const, const T&... desc);
template <typename R, typename... T>
R jsArg(const folly::dynamic& arg, R (folly::dynamic::*asFoo)() const&, const T&... desc);
// This is like jsArg, but a operates on a dynamic representing an array of
// arguments. The argument n is used both to index the array and build the
// exception message, if any. It can be used directly, but will more often be
// used by the type-specific methods following.
template <typename R>
R jsArgN(const folly::dynamic& args, size_t n, R (folly::dynamic::*asFoo)() const);
template <typename R>
R jsArgN(const folly::dynamic& args, size_t n, R (folly::dynamic::*asFoo)() const&);
namespace detail {
// This is a type helper to implement functions which should work on both const
// and non-const folly::dynamic arguments, and return a type with the same
// constness. Basically, it causes the templates which use it to be defined
// only for types compatible with folly::dynamic.
template <typename T>
struct is_dynamic {
typedef typename std::enable_if<std::is_assignable<folly::dynamic, T>::value, T>::type type;
};
} // end namespace detail
// Easy to use conversion helpers are here:
// Extract the n'th arg from the given dynamic, as a dynamic. Throws a
// JsArgumentException if there is no n'th arg in the input.
template <typename T>
typename detail::is_dynamic<T>::type& jsArgAsDynamic(T&& args, size_t n);
// Extract the n'th arg from the given dynamic, as a dynamic Array. Throws a
// JsArgumentException if there is no n'th arg in the input, or it is not an
// Array.
template <typename T>
typename detail::is_dynamic<T>::type& jsArgAsArray(T&& args, size_t n);
// Extract the n'th arg from the given dynamic, as a dynamic Object. Throws a
// JsArgumentException if there is no n'th arg in the input, or it is not an
// Object.
template <typename T>
typename detail::is_dynamic<T>::type& jsArgAsObject(T&& args, size_t n);
// Extract the n'th arg from the given dynamic, as a bool. Throws a
// JsArgumentException if this fails for some reason.
inline bool jsArgAsBool(const folly::dynamic& args, size_t n) {
return jsArgN(args, n, &folly::dynamic::asBool);
}
// Extract the n'th arg from the given dynamic, as an integer. Throws a
// JsArgumentException if this fails for some reason.
inline int64_t jsArgAsInt(const folly::dynamic& args, size_t n) {
return jsArgN(args, n, &folly::dynamic::asInt);
}
// Extract the n'th arg from the given dynamic, as a double. Throws a
// JsArgumentException if this fails for some reason.
inline double jsArgAsDouble(const folly::dynamic& args, size_t n) {
return jsArgN(args, n, &folly::dynamic::asDouble);
}
// Extract the n'th arg from the given dynamic, as a string. Throws a
// JsArgumentException if this fails for some reason.
inline std::string jsArgAsString(const folly::dynamic& args, size_t n) {
return jsArgN(args, n, &folly::dynamic::asString);
}
}}
#include <cxxreact/JsArgumentHelpers-inl.h>
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <condition_variable>
#include <functional>
#include <mutex>
namespace facebook {
namespace react {
class MessageQueueThread {
public:
virtual ~MessageQueueThread() {}
virtual void runOnQueue(std::function<void()>&&) = 0;
// runOnQueueSync and quitSynchronous are dangerous. They should only be
// used for initialization and cleanup.
virtual void runOnQueueSync(std::function<void()>&&) = 0;
// Once quitSynchronous() returns, no further work should run on the queue.
virtual void quitSynchronous() = 0;
};
}}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "MethodCall.h"
#include <folly/json.h>
#include <stdexcept>
namespace facebook {
namespace react {
#define REQUEST_MODULE_IDS 0
#define REQUEST_METHOD_IDS 1
#define REQUEST_PARAMSS 2
#define REQUEST_CALLID 3
static const char *errorPrefix = "Malformed calls from JS: ";
std::vector<MethodCall> parseMethodCalls(folly::dynamic&& jsonData) throw(std::invalid_argument) {
if (jsonData.isNull()) {
return {};
}
if (!jsonData.isArray()) {
throw std::invalid_argument(
folly::to<std::string>(errorPrefix, "input isn't array but ", jsonData.typeName()));
}
if (jsonData.size() < REQUEST_PARAMSS + 1) {
throw std::invalid_argument(
folly::to<std::string>(errorPrefix, "size == ", jsonData.size()));
}
auto& moduleIds = jsonData[REQUEST_MODULE_IDS];
auto& methodIds = jsonData[REQUEST_METHOD_IDS];
auto& params = jsonData[REQUEST_PARAMSS];
int callId = -1;
if (!moduleIds.isArray() || !methodIds.isArray() || !params.isArray()) {
throw std::invalid_argument(
folly::to<std::string>(errorPrefix, "not all fields are arrays.\n\n", folly::toJson(jsonData)));
}
if (moduleIds.size() != methodIds.size() || moduleIds.size() != params.size()) {
throw std::invalid_argument(
folly::to<std::string>(errorPrefix, "field sizes are different.\n\n", folly::toJson(jsonData)));
}
if (jsonData.size() > REQUEST_CALLID) {
if (!jsonData[REQUEST_CALLID].isNumber()) {
throw std::invalid_argument(
folly::to<std::string>(errorPrefix, "invalid callId", jsonData[REQUEST_CALLID].typeName()));
}
callId = jsonData[REQUEST_CALLID].asInt();
}
std::vector<MethodCall> methodCalls;
for (size_t i = 0; i < moduleIds.size(); i++) {
if (!params[i].isArray()) {
throw std::invalid_argument(
folly::to<std::string>(errorPrefix, "method arguments isn't array but ", params[i].typeName()));
}
methodCalls.emplace_back(
moduleIds[i].asInt(),
methodIds[i].asInt(),
std::move(params[i]),
callId);
// only incremement callid if contains valid callid as callid is optional
callId += (callId != -1) ? 1 : 0;
}
return methodCalls;
}
}}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <map>
#include <string>
#include <vector>
#include <folly/dynamic.h>
namespace facebook {
namespace react {
struct MethodCall {
int moduleId;
int methodId;
folly::dynamic arguments;
int callId;
MethodCall(int mod, int meth, folly::dynamic&& args, int cid)
: moduleId(mod)
, methodId(meth)
, arguments(std::move(args))
, callId(cid) {}
};
std::vector<MethodCall> parseMethodCalls(folly::dynamic&& calls) throw(std::invalid_argument);
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "ModuleRegistry.h"
#include <glog/logging.h>
#include "NativeModule.h"
#include "SystraceSection.h"
namespace facebook {
namespace react {
namespace {
std::string normalizeName(std::string name) {
// TODO mhorowitz #10487027: This is super ugly. We should just
// change iOS to emit normalized names, drop the "RK..." from
// names hardcoded in Android, and then delete this and the
// similar hacks in js.
if (name.compare(0, 3, "RCT") == 0) {
return name.substr(3);
} else if (name.compare(0, 2, "RK") == 0) {
return name.substr(2);
}
return name;
}
}
ModuleRegistry::ModuleRegistry(std::vector<std::unique_ptr<NativeModule>> modules, ModuleNotFoundCallback callback)
: modules_{std::move(modules)}, moduleNotFoundCallback_{callback} {}
void ModuleRegistry::updateModuleNamesFromIndex(size_t index) {
for (; index < modules_.size(); index++ ) {
std::string name = normalizeName(modules_[index]->getName());
modulesByName_[name] = index;
}
}
void ModuleRegistry::registerModules(std::vector<std::unique_ptr<NativeModule>> modules) {
if (modules_.empty() && unknownModules_.empty()) {
modules_ = std::move(modules);
} else {
size_t modulesSize = modules_.size();
size_t addModulesSize = modules.size();
bool addToNames = !modulesByName_.empty();
modules_.reserve(modulesSize + addModulesSize);
std::move(modules.begin(), modules.end(), std::back_inserter(modules_));
if (!unknownModules_.empty()) {
for (size_t index = modulesSize; index < modulesSize + addModulesSize; index++) {
std::string name = normalizeName(modules_[index]->getName());
auto it = unknownModules_.find(name);
if (it != unknownModules_.end()) {
throw std::runtime_error(
folly::to<std::string>("module ", name, " was required without being registered and is now being registered."));
} else if (addToNames) {
modulesByName_[name] = index;
}
}
} else if (addToNames) {
updateModuleNamesFromIndex(modulesSize);
}
}
}
std::vector<std::string> ModuleRegistry::moduleNames() {
std::vector<std::string> names;
for (size_t i = 0; i < modules_.size(); i++) {
std::string name = normalizeName(modules_[i]->getName());
modulesByName_[name] = i;
names.push_back(std::move(name));
}
return names;
}
folly::Optional<ModuleConfig> ModuleRegistry::getConfig(const std::string& name) {
SystraceSection s("ModuleRegistry::getConfig", "module", name);
// Initialize modulesByName_
if (modulesByName_.empty() && !modules_.empty()) {
moduleNames();
}
auto it = modulesByName_.find(name);
if (it == modulesByName_.end()) {
if (unknownModules_.find(name) != unknownModules_.end()) {
return nullptr;
}
if (!moduleNotFoundCallback_ ||
!moduleNotFoundCallback_(name) ||
(it = modulesByName_.find(name)) == modulesByName_.end()) {
unknownModules_.insert(name);
return nullptr;
}
}
size_t index = it->second;
CHECK(index < modules_.size());
NativeModule *module = modules_[index].get();
// string name, object constants, array methodNames (methodId is index), [array promiseMethodIds], [array syncMethodIds]
folly::dynamic config = folly::dynamic::array(name);
{
SystraceSection s_("getConstants");
config.push_back(module->getConstants());
}
{
SystraceSection s_("getMethods");
std::vector<MethodDescriptor> methods = module->getMethods();
folly::dynamic methodNames = folly::dynamic::array;
folly::dynamic promiseMethodIds = folly::dynamic::array;
folly::dynamic syncMethodIds = folly::dynamic::array;
for (auto& descriptor : methods) {
// TODO: #10487027 compare tags instead of doing string comparison?
methodNames.push_back(std::move(descriptor.name));
if (descriptor.type == "promise") {
promiseMethodIds.push_back(methodNames.size() - 1);
} else if (descriptor.type == "sync") {
syncMethodIds.push_back(methodNames.size() - 1);
}
}
if (!methodNames.empty()) {
config.push_back(std::move(methodNames));
if (!promiseMethodIds.empty() || !syncMethodIds.empty()) {
config.push_back(std::move(promiseMethodIds));
if (!syncMethodIds.empty()) {
config.push_back(std::move(syncMethodIds));
}
}
}
}
if (config.size() == 2 && config[1].empty()) {
// no constants or methods
return nullptr;
} else {
return ModuleConfig{index, config};
}
}
void ModuleRegistry::callNativeMethod(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params, int callId) {
if (moduleId >= modules_.size()) {
throw std::runtime_error(
folly::to<std::string>("moduleId ", moduleId, " out of range [0..", modules_.size(), ")"));
}
modules_[moduleId]->invoke(methodId, std::move(params), callId);
}
MethodCallResult ModuleRegistry::callSerializableNativeHook(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params) {
if (moduleId >= modules_.size()) {
throw std::runtime_error(
folly::to<std::string>("moduleId ", moduleId, "out of range [0..", modules_.size(), ")"));
}
return modules_[moduleId]->callSerializableNativeHook(methodId, std::move(params));
}
}}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <memory>
#include <unordered_set>
#include <vector>
#include <cxxreact/JSExecutor.h>
#include <folly/Optional.h>
#include <folly/dynamic.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
class NativeModule;
struct ModuleConfig {
size_t index;
folly::dynamic config;
};
class RN_EXPORT ModuleRegistry {
public:
// not implemented:
// onBatchComplete: see https://our.intern.facebook.com/intern/tasks/?t=5279396
// getModule: only used by views
// getAllModules: only used for cleanup; use RAII instead
// notifyCatalystInstanceInitialized: this is really only used by view-related code
// notifyCatalystInstanceDestroy: use RAII instead
using ModuleNotFoundCallback = std::function<bool(const std::string &name)>;
ModuleRegistry(std::vector<std::unique_ptr<NativeModule>> modules, ModuleNotFoundCallback callback = nullptr);
void registerModules(std::vector<std::unique_ptr<NativeModule>> modules);
std::vector<std::string> moduleNames();
folly::Optional<ModuleConfig> getConfig(const std::string& name);
void callNativeMethod(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params, int callId);
MethodCallResult callSerializableNativeHook(unsigned int moduleId, unsigned int methodId, folly::dynamic&& args);
private:
// This is always populated
std::vector<std::unique_ptr<NativeModule>> modules_;
// This is used to extend the population of modulesByName_ if registerModules is called after moduleNames
void updateModuleNamesFromIndex(size_t size);
// This is only populated if moduleNames() is called. Values are indices into modules_.
std::unordered_map<std::string, size_t> modulesByName_;
// This is populated with modules that are requested via getConfig but are unknown.
// An error will be thrown if they are subsequently added to the registry.
std::unordered_set<std::string> unknownModules_;
// Function will be called if a module was requested but was not found.
// If the function returns true, ModuleRegistry will try to find the module again (assuming it's registered)
// If the functon returns false, ModuleRegistry will not try to find the module and return nullptr instead.
ModuleNotFoundCallback moduleNotFoundCallback_;
};
}
}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <string>
#include <vector>
#include <folly/Optional.h>
#include <folly/dynamic.h>
namespace facebook {
namespace react {
struct MethodDescriptor {
std::string name;
// type is one of js MessageQueue.MethodTypes
std::string type;
MethodDescriptor(std::string n, std::string t)
: name(std::move(n))
, type(std::move(t)) {}
};
using MethodCallResult = folly::Optional<folly::dynamic>;
class NativeModule {
public:
virtual ~NativeModule() {}
virtual std::string getName() = 0;
virtual std::vector<MethodDescriptor> getMethods() = 0;
virtual folly::dynamic getConstants() = 0;
virtual void invoke(unsigned int reactMethodId, folly::dynamic&& params, int callId) = 0;
virtual MethodCallResult callSerializableNativeHook(unsigned int reactMethodId, folly::dynamic&& args) = 0;
};
}
}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "NativeToJsBridge.h"
#include <folly/json.h>
#include <folly/Memory.h>
#include <folly/MoveWrapper.h>
#include "Instance.h"
#include "JSBigString.h"
#include "SystraceSection.h"
#include "MethodCall.h"
#include "MessageQueueThread.h"
#include "RAMBundleRegistry.h"
#ifdef WITH_FBSYSTRACE
#include <fbsystrace.h>
using fbsystrace::FbSystraceAsyncFlow;
#endif
namespace facebook {
namespace react {
// This class manages calls from JS to native code.
class JsToNativeBridge : public react::ExecutorDelegate {
public:
JsToNativeBridge(std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<InstanceCallback> callback)
: m_registry(registry)
, m_callback(callback) {}
std::shared_ptr<ModuleRegistry> getModuleRegistry() override {
return m_registry;
}
void callNativeModules(
JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override {
CHECK(m_registry || calls.empty()) <<
"native module calls cannot be completed with no native modules";
m_batchHadNativeModuleCalls = m_batchHadNativeModuleCalls || !calls.empty();
// An exception anywhere in here stops processing of the batch. This
// was the behavior of the Android bridge, and since exception handling
// terminates the whole bridge, there's not much point in continuing.
for (auto& call : parseMethodCalls(std::move(calls))) {
m_registry->callNativeMethod(call.moduleId, call.methodId, std::move(call.arguments), call.callId);
}
if (isEndOfBatch) {
// onBatchComplete will be called on the native (module) queue, but
// decrementPendingJSCalls will be called sync. Be aware that the bridge may still
// be processing native calls when the birdge idle signaler fires.
if (m_batchHadNativeModuleCalls) {
m_callback->onBatchComplete();
m_batchHadNativeModuleCalls = false;
}
m_callback->decrementPendingJSCalls();
}
}
MethodCallResult callSerializableNativeHook(
JSExecutor& executor, unsigned int moduleId, unsigned int methodId,
folly::dynamic&& args) override {
return m_registry->callSerializableNativeHook(moduleId, methodId, std::move(args));
}
private:
// These methods are always invoked from an Executor. The NativeToJsBridge
// keeps a reference to the executor, and when destroy() is called, the
// executor is destroyed synchronously on its queue.
std::shared_ptr<ModuleRegistry> m_registry;
std::shared_ptr<InstanceCallback> m_callback;
bool m_batchHadNativeModuleCalls = false;
};
NativeToJsBridge::NativeToJsBridge(
JSExecutorFactory* jsExecutorFactory,
std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<InstanceCallback> callback)
: m_destroyed(std::make_shared<bool>(false))
, m_delegate(std::make_shared<JsToNativeBridge>(registry, callback))
, m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue))
, m_executorMessageQueueThread(std::move(jsQueue)) {}
// This must be called on the same thread on which the constructor was called.
NativeToJsBridge::~NativeToJsBridge() {
CHECK(*m_destroyed) <<
"NativeToJsBridge::destroy() must be called before deallocating the NativeToJsBridge!";
}
void NativeToJsBridge::loadApplication(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
runOnExecutorQueue(
[bundleRegistryWrap=folly::makeMoveWrapper(std::move(bundleRegistry)),
startupScript=folly::makeMoveWrapper(std::move(startupScript)),
startupScriptSourceURL=std::move(startupScriptSourceURL)]
(JSExecutor* executor) mutable {
auto bundleRegistry = bundleRegistryWrap.move();
if (bundleRegistry) {
executor->setBundleRegistry(std::move(bundleRegistry));
}
executor->loadApplicationScript(std::move(*startupScript),
std::move(startupScriptSourceURL));
});
}
void NativeToJsBridge::loadApplicationSync(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
if (bundleRegistry) {
m_executor->setBundleRegistry(std::move(bundleRegistry));
}
m_executor->loadApplicationScript(std::move(startupScript),
std::move(startupScriptSourceURL));
}
void NativeToJsBridge::callFunction(
std::string&& module,
std::string&& method,
folly::dynamic&& arguments) {
int systraceCookie = -1;
#ifdef WITH_FBSYSTRACE
systraceCookie = m_systraceCookie++;
FbSystraceAsyncFlow::begin(
TRACE_TAG_REACT_CXX_BRIDGE,
"JSCall",
systraceCookie);
#endif
runOnExecutorQueue([module = std::move(module), method = std::move(method), arguments = std::move(arguments), systraceCookie]
(JSExecutor* executor) {
#ifdef WITH_FBSYSTRACE
FbSystraceAsyncFlow::end(
TRACE_TAG_REACT_CXX_BRIDGE,
"JSCall",
systraceCookie);
SystraceSection s("NativeToJsBridge::callFunction", "module", module, "method", method);
#endif
// This is safe because we are running on the executor's thread: it won't
// destruct until after it's been unregistered (which we check above) and
// that will happen on this thread
executor->callFunction(module, method, arguments);
});
}
void NativeToJsBridge::invokeCallback(double callbackId, folly::dynamic&& arguments) {
int systraceCookie = -1;
#ifdef WITH_FBSYSTRACE
systraceCookie = m_systraceCookie++;
FbSystraceAsyncFlow::begin(
TRACE_TAG_REACT_CXX_BRIDGE,
"<callback>",
systraceCookie);
#endif
runOnExecutorQueue([callbackId, arguments = std::move(arguments), systraceCookie]
(JSExecutor* executor) {
#ifdef WITH_FBSYSTRACE
FbSystraceAsyncFlow::end(
TRACE_TAG_REACT_CXX_BRIDGE,
"<callback>",
systraceCookie);
SystraceSection s("NativeToJsBridge::invokeCallback");
#endif
executor->invokeCallback(callbackId, arguments);
});
}
void NativeToJsBridge::registerBundle(uint32_t bundleId, const std::string& bundlePath) {
runOnExecutorQueue([bundleId, bundlePath] (JSExecutor* executor) {
executor->registerBundle(bundleId, bundlePath);
});
}
void NativeToJsBridge::setGlobalVariable(std::string propName,
std::unique_ptr<const JSBigString> jsonValue) {
runOnExecutorQueue([propName=std::move(propName), jsonValue=folly::makeMoveWrapper(std::move(jsonValue))]
(JSExecutor* executor) mutable {
executor->setGlobalVariable(propName, jsonValue.move());
});
}
void* NativeToJsBridge::getJavaScriptContext() {
// TODO(cjhopman): this seems unsafe unless we require that it is only called on the main js queue.
return m_executor->getJavaScriptContext();
}
bool NativeToJsBridge::isInspectable() {
return m_executor->isInspectable();
}
#ifdef WITH_JSC_MEMORY_PRESSURE
void NativeToJsBridge::handleMemoryPressure(int pressureLevel) {
runOnExecutorQueue([=] (JSExecutor* executor) {
executor->handleMemoryPressure(pressureLevel);
});
}
#endif
void NativeToJsBridge::destroy() {
// All calls made through runOnExecutorQueue have an early exit if
// m_destroyed is true. Setting this before the runOnQueueSync will cause
// pending work to be cancelled and we won't have to wait for it.
*m_destroyed = true;
m_executorMessageQueueThread->runOnQueueSync([this] {
m_executor->destroy();
m_executorMessageQueueThread->quitSynchronous();
m_executor = nullptr;
});
}
void NativeToJsBridge::runOnExecutorQueue(std::function<void(JSExecutor*)> task) {
if (*m_destroyed) {
return;
}
std::shared_ptr<bool> isDestroyed = m_destroyed;
m_executorMessageQueueThread->runOnQueue([this, isDestroyed, task=std::move(task)] {
if (*isDestroyed) {
return;
}
// The executor is guaranteed to be valid for the duration of the task because:
// 1. the executor is only destroyed after it is unregistered
// 2. the executor is unregistered on this queue
// 3. we just confirmed that the executor hasn't been unregistered above
task(m_executor.get());
});
}
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <atomic>
#include <functional>
#include <map>
#include <vector>
#include <cxxreact/JSCExecutor.h>
#include <cxxreact/JSExecutor.h>
namespace folly {
struct dynamic;
}
namespace facebook {
namespace react {
struct InstanceCallback;
class JsToNativeBridge;
class MessageQueueThread;
class ModuleRegistry;
class RAMBundleRegistry;
// This class manages calls from native code to JS. It also manages
// executors and their threads. All functions here can be called from
// any thread.
//
// Except for loadApplicationScriptSync(), all void methods will queue
// work to run on the jsQueue passed to the ctor, and return
// immediately.
class NativeToJsBridge {
public:
friend class JsToNativeBridge;
/**
* This must be called on the main JS thread.
*/
NativeToJsBridge(
JSExecutorFactory* jsExecutorFactory,
std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<InstanceCallback> callback);
virtual ~NativeToJsBridge();
/**
* Executes a function with the module ID and method ID and any additional
* arguments in JS.
*/
void callFunction(std::string&& module, std::string&& method, folly::dynamic&& args);
/**
* Invokes a callback with the cbID, and optional additional arguments in JS.
*/
void invokeCallback(double callbackId, folly::dynamic&& args);
/**
* Executes a JS method on the given executor synchronously, returning its
* return value. JSException will be thrown if JS throws an exception;
* another standard exception may be thrown for C++ bridge failures, or if
* the executor is not capable of synchronous calls.
*
* This method is experimental, and may be modified or removed.
*
* loadApplicationScriptSync() must be called and finished executing
* before callFunctionSync().
*/
template <typename T>
Value callFunctionSync(const std::string& module, const std::string& method, T&& args) {
if (*m_destroyed) {
throw std::logic_error(
folly::to<std::string>("Synchronous call to ", module, ".", method,
" after bridge is destroyed"));
}
JSCExecutor *jscExecutor = dynamic_cast<JSCExecutor*>(m_executor.get());
if (!jscExecutor) {
throw std::invalid_argument(
folly::to<std::string>("Executor type ", typeid(m_executor.get()).name(),
" does not support synchronous calls"));
}
return jscExecutor->callFunctionSync(module, method, std::forward<T>(args));
}
/**
* Starts the JS application. If bundleRegistry is non-null, then it is
* used to fetch JavaScript modules as individual scripts.
* Otherwise, the script is assumed to include all the modules.
*/
void loadApplication(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupCode,
std::string sourceURL);
void loadApplicationSync(
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupCode,
std::string sourceURL);
void registerBundle(uint32_t bundleId, const std::string& bundlePath);
void setGlobalVariable(std::string propName, std::unique_ptr<const JSBigString> jsonValue);
void* getJavaScriptContext();
bool isInspectable();
#ifdef WITH_JSC_MEMORY_PRESSURE
void handleMemoryPressure(int pressureLevel);
#endif
/**
* Synchronously tears down the bridge and the main executor.
*/
void destroy();
private:
void runOnExecutorQueue(std::function<void(JSExecutor*)> task);
// This is used to avoid a race condition where a proxyCallback gets queued
// after ~NativeToJsBridge(), on the same thread. In that case, the callback
// will try to run the task on m_callback which will have been destroyed
// within ~NativeToJsBridge(), thus causing a SIGSEGV.
std::shared_ptr<bool> m_destroyed;
std::shared_ptr<JsToNativeBridge> m_delegate;
std::unique_ptr<JSExecutor> m_executor;
std::shared_ptr<MessageQueueThread> m_executorMessageQueueThread;
#ifdef WITH_FBSYSTRACE
std::atomic_uint_least32_t m_systraceCookie = ATOMIC_VAR_INIT();
#endif
};
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Platform.h"
namespace facebook {
namespace react {
#if __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
namespace ReactMarker {
LogTaggedMarker logTaggedMarker = nullptr;
void logMarker(const ReactMarkerId markerId) {
logTaggedMarker(markerId, nullptr);
}
}
namespace JSCNativeHooks {
Hook loggingHook = nullptr;
Hook nowHook = nullptr;
ConfigurationHook installPerfHooks = nullptr;
}
#if __clang__
#pragma clang diagnostic pop
#endif
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <cxxreact/JSExecutor.h>
#include <cxxreact/MessageQueueThread.h>
#include <jschelpers/JavaScriptCore.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
namespace ReactMarker {
enum ReactMarkerId {
NATIVE_REQUIRE_START,
NATIVE_REQUIRE_STOP,
RUN_JS_BUNDLE_START,
RUN_JS_BUNDLE_STOP,
CREATE_REACT_CONTEXT_STOP,
JS_BUNDLE_STRING_CONVERT_START,
JS_BUNDLE_STRING_CONVERT_STOP,
NATIVE_MODULE_SETUP_START,
NATIVE_MODULE_SETUP_STOP,
};
#ifdef __APPLE__
using LogTaggedMarker = std::function<void(const ReactMarkerId, const char* tag)>;
#else
typedef void(*LogTaggedMarker)(const ReactMarkerId, const char* tag);
#endif
extern RN_EXPORT LogTaggedMarker logTaggedMarker;
extern void logMarker(const ReactMarkerId markerId);
}
namespace JSCNativeHooks {
using Hook = JSValueRef(*)(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
extern RN_EXPORT Hook loggingHook;
extern RN_EXPORT Hook nowHook;
typedef void(*ConfigurationHook)(JSGlobalContextRef);
extern RN_EXPORT ConfigurationHook installPerfHooks;
}
} }
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "RAMBundleRegistry.h"
#include <folly/Memory.h>
#include <libgen.h>
namespace facebook {
namespace react {
constexpr uint32_t RAMBundleRegistry::MAIN_BUNDLE_ID;
std::unique_ptr<RAMBundleRegistry> RAMBundleRegistry::singleBundleRegistry(std::unique_ptr<JSModulesUnbundle> mainBundle) {
RAMBundleRegistry *registry = new RAMBundleRegistry(std::move(mainBundle));
return std::unique_ptr<RAMBundleRegistry>(registry);
}
std::unique_ptr<RAMBundleRegistry> RAMBundleRegistry::multipleBundlesRegistry(std::unique_ptr<JSModulesUnbundle> mainBundle, std::function<std::unique_ptr<JSModulesUnbundle>(std::string)> factory) {
RAMBundleRegistry *registry = new RAMBundleRegistry(std::move(mainBundle), std::move(factory));
return std::unique_ptr<RAMBundleRegistry>(registry);
}
RAMBundleRegistry::RAMBundleRegistry(std::unique_ptr<JSModulesUnbundle> mainBundle, std::function<std::unique_ptr<JSModulesUnbundle>(std::string)> factory): m_factory(factory) {
m_bundles.emplace(MAIN_BUNDLE_ID, std::move(mainBundle));
}
void RAMBundleRegistry::registerBundle(uint32_t bundleId, std::string bundlePath) {
m_bundlePaths.emplace(bundleId, bundlePath);
}
JSModulesUnbundle::Module RAMBundleRegistry::getModule(uint32_t bundleId, uint32_t moduleId) {
if (m_bundles.find(bundleId) == m_bundles.end()) {
if (!m_factory) {
throw std::runtime_error("You need to register factory function in order to support multiple RAM bundles.");
}
auto bundlePath = m_bundlePaths.find(bundleId);
if (bundlePath == m_bundlePaths.end()) {
throw std::runtime_error("In order to fetch RAM bundle from the registry, its file path needs to be registered first.");
}
m_bundles.emplace(bundleId, m_factory(bundlePath->second));
}
return getBundle(bundleId)->getModule(moduleId);
}
JSModulesUnbundle *RAMBundleRegistry::getBundle(uint32_t bundleId) const {
return m_bundles.at(bundleId).get();
}
} // namespace react
} // namespace facebook
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <cstdint>
#include <functional>
#include <memory>
#include <unordered_map>
#include <utility>
#include <cxxreact/JSModulesUnbundle.h>
#include <jschelpers/noncopyable.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
class RN_EXPORT RAMBundleRegistry : noncopyable {
public:
using unique_ram_bundle = std::unique_ptr<JSModulesUnbundle>;
using bundle_path = std::string;
constexpr static uint32_t MAIN_BUNDLE_ID = 0;
static std::unique_ptr<RAMBundleRegistry> singleBundleRegistry(unique_ram_bundle mainBundle);
static std::unique_ptr<RAMBundleRegistry> multipleBundlesRegistry(unique_ram_bundle mainBundle, std::function<unique_ram_bundle(bundle_path)> factory);
RAMBundleRegistry(RAMBundleRegistry&&) = default;
RAMBundleRegistry& operator=(RAMBundleRegistry&&) = default;
void registerBundle(uint32_t bundleId, bundle_path bundlePath);
JSModulesUnbundle::Module getModule(uint32_t bundleId, uint32_t moduleId);
virtual ~RAMBundleRegistry() {};
private:
explicit RAMBundleRegistry(unique_ram_bundle mainBundle, std::function<unique_ram_bundle(bundle_path)> factory = {});
JSModulesUnbundle *getBundle(uint32_t bundleId) const;
std::function<unique_ram_bundle(bundle_path)> m_factory;
std::unordered_map<uint32_t, bundle_path> m_bundlePaths;
std::unordered_map<uint32_t, unique_ram_bundle> m_bundles;
};
} // namespace react
} // namespace facebook
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <exception>
#include <functional>
#include <string>
namespace facebook {
namespace react {
/**
* RecoverableError
*
* An exception that it is expected we should be able to recover from.
*/
struct RecoverableError : public std::exception {
explicit RecoverableError(const std::string &what_)
: m_what { "facebook::react::Recoverable: " + what_ }
{}
virtual const char* what() const throw() override { return m_what.c_str(); }
/**
* runRethrowingAsRecoverable
*
* Helper function that converts any exception of type `E`, thrown within the
* `act` routine into a recoverable error with the same message.
*/
template <typename E>
inline static void runRethrowingAsRecoverable(std::function<void()> act) {
try {
act();
} catch(const E &err) {
throw RecoverableError(err.what());
}
}
private:
std::string m_what;
};
} // namespace react
} // namespace facebook
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include "SampleCxxModule.h"
#include <cxxreact/JsArgumentHelpers.h>
#include <folly/Memory.h>
#include <glog/logging.h>
#include <thread>
using namespace folly;
namespace facebook { namespace xplat { namespace samples {
std::string Sample::hello() {
LOG(WARNING) << "glog: hello, world";
return "hello";
}
double Sample::add(double a, double b) {
return a + b;
}
std::string Sample::concat(const std::string& a, const std::string& b) {
return a + b;
}
std::string Sample::repeat(int count, const std::string& str) {
std::string ret;
for (int i = 0; i < count; i++) {
ret += str;
}
return ret;
}
void Sample::save(std::map<std::string, std::string> dict)
{
state_ = std::move(dict);
}
std::map<std::string, std::string> Sample::load() {
return state_;
}
void Sample::except() {
// TODO mhorowitz #7128529: There's no way to automatically test this
// right now.
// throw std::runtime_error("oops");
}
void Sample::call_later(int msec, std::function<void()> f) {
std::thread t([=] {
std::this_thread::sleep_for(std::chrono::milliseconds(msec));
f();
});
t.detach();
}
double Sample::twice(double n) {
return n * 2;
}
SampleCxxModule::SampleCxxModule(std::unique_ptr<Sample> sample)
: sample_(std::move(sample)) {}
std::string SampleCxxModule::getName() {
return "Sample";
}
auto SampleCxxModule::getConstants() -> std::map<std::string, folly::dynamic> {
return {
{ "one", 1 },
{ "two", 2 },
{ "animal", "fox" },
};
}
auto SampleCxxModule::getMethods() -> std::vector<Method> {
return {
Method("hello", [this] {
sample_->hello();
}),
Method("add", [this](dynamic args, Callback cb) {
LOG(WARNING) << "Sample: add => "
<< sample_->add(jsArgAsDouble(args, 0), jsArgAsDouble(args, 1));
cb({sample_->add(jsArgAsDouble(args, 0), jsArgAsDouble(args, 1))});
}),
Method("concat", [this](dynamic args, Callback cb) {
cb({sample_->concat(jsArgAsString(args, 0),
jsArgAsString(args, 1))});
}),
Method("repeat", [this](dynamic args, Callback cb) {
cb({sample_->repeat(jsArgAsInt(args, 0),
jsArgAsString(args, 1))});
}),
Method("save", this, &SampleCxxModule::save),
Method("load", this, &SampleCxxModule::load),
Method("call_later", [this](dynamic args, Callback cb) {
sample_->call_later(jsArgAsInt(args, 0), [cb] {
cb({});
});
}),
Method("except", [this] {
sample_->except();
}),
Method("twice", [this](dynamic args) -> dynamic {
return sample_->twice(jsArgAsDouble(args, 0));
}, SyncTag),
Method("syncHello", [this]() -> dynamic {
sample_->hello();
return nullptr;
}, SyncTag),
};
}
void SampleCxxModule::save(folly::dynamic args) {
std::map<std::string, std::string> m;
for (const auto& p : jsArgN(args, 0, &dynamic::items)) {
m.emplace(jsArg(p.first, &dynamic::asString, "map key"),
jsArg(p.second, &dynamic::asString, "map value"));
}
sample_->save(std::move(m));
}
void SampleCxxModule::load(folly::dynamic args, Callback cb) {
dynamic d = dynamic::object;
for (const auto& p : sample_->load()) {
d.insert(p.first, p.second);
}
cb({d});
}
}}}
// By convention, the function name should be the same as the class name.
facebook::xplat::module::CxxModule *SampleCxxModule() {
return new facebook::xplat::samples::SampleCxxModule(
folly::make_unique<facebook::xplat::samples::Sample>());
}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <memory>
#include <vector>
#include <cxxreact/CxxModule.h>
namespace facebook { namespace xplat { namespace samples {
// In a less contrived example, Sample would be part of a traditional
// C++ library.
class Sample {
public:
std::string hello();
double add(double a, double b);
std::string concat(const std::string& a, const std::string& b);
std::string repeat(int count, const std::string& str);
void save(std::map<std::string, std::string> dict);
std::map<std::string, std::string> load();
void call_later(int msec, std::function<void()> f);
void except();
double twice(double n);
private:
std::map<std::string, std::string> state_;
};
class SampleCxxModule : public module::CxxModule {
public:
SampleCxxModule(std::unique_ptr<Sample> sample);
std::string getName();
virtual auto getConstants() -> std::map<std::string, folly::dynamic>;
virtual auto getMethods() -> std::vector<Method>;
private:
void save(folly::dynamic args);
void load(folly::dynamic args, Callback cb);
std::unique_ptr<Sample> sample_;
};
}}}
extern "C" facebook::xplat::module::CxxModule *SampleCxxModule();
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include <memory>
#include <cxxreact/CxxModule.h>
namespace facebook { namespace xplat { namespace module {
// Allows a Cxx-module to be shared or reused across multiple React instances
// Caveat: the setInstance call is not forwarded, so usages of getInstance inside your
// module (e.g. dispatching events) will always be nullptr.
class SharedProxyCxxModule : public CxxModule {
public:
explicit SharedProxyCxxModule(std::shared_ptr<CxxModule> shared)
: shared_(shared) {}
std::string getName() override {
return shared_->getName();
}
auto getConstants() -> std::map<std::string, folly::dynamic> override {
return shared_->getConstants();
}
auto getMethods() -> std::vector<Method> override {
return shared_->getMethods();
}
private:
std::shared_ptr<CxxModule> shared_;
};
}
}
}
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#ifdef WITH_FBSYSTRACE
#include <fbsystrace.h>
#endif
namespace facebook {
namespace react {
/**
* This is a convenience class to avoid lots of verbose profiling
* #ifdefs. If WITH_FBSYSTRACE is not defined, the optimizer will
* remove this completely. If it is defined, it will behave as
* FbSystraceSection, with the right tag provided. Use two separate classes to
* to ensure that the ODR rule isn't violated, that is, if WITH_FBSYSTRACE has
* different values in different files, there is no inconsistency in the sizes
* of defined symbols.
*/
#ifdef WITH_FBSYSTRACE
struct ConcreteSystraceSection {
public:
template<typename... ConvertsToStringPiece>
explicit
ConcreteSystraceSection(const char* name, ConvertsToStringPiece&&... args)
: m_section(TRACE_TAG_REACT_CXX_BRIDGE, name, args...)
{}
private:
fbsystrace::FbSystraceSection m_section;
};
using SystraceSection = ConcreteSystraceSection;
#else
struct DummySystraceSection {
public:
template<typename... ConvertsToStringPiece>
explicit
DummySystraceSection(const char* name, ConvertsToStringPiece&&... args)
{}
};
using SystraceSection = DummySystraceSection;
#endif
}}
... ...
load("//ReactNative:DEFS.bzl", "IS_OSS_BUILD", "react_native_xplat_target")
TEST_SRCS = [
"RecoverableErrorTest.cpp",
"jsarg_helpers.cpp",
"jsbigstring.cpp",
"jscexecutor.cpp",
"jsclogging.cpp",
"methodcall.cpp",
"value.cpp",
]
if not IS_OSS_BUILD:
load("@xplat//configurations/buck/android:jni_instrumentation_test", "jni_instrumentation_test_lib")
load("@xplat//configurations/buck:fb_xplat_cxx.bzl", "cxx_test")
load("@xplat//configurations/buck:default_platform_defs.bzl", "APPLE")
jni_instrumentation_test_lib(
name = 'tests',
class_under_test = 'com/facebook/react/XplatBridgeTest',
soname = 'libxplat-bridge.so',
srcs = TEST_SRCS,
compiler_flags = [
'-fexceptions',
'-frtti',
'-std=c++14',
],
deps = [
'xplat//third-party/linker_lib:android',
'xplat//third-party/gmock:gtest',
react_native_xplat_target('cxxreact:bridge'),
],
visibility = [
'fbandroid//instrumentation_tests/...'
],
)
cxx_test(
name = 'tests',
srcs = TEST_SRCS,
compiler_flags = [
'-fexceptions',
'-frtti',
],
platforms = APPLE,
deps = [
'xplat//folly:molly',
'xplat//third-party/gmock:gtest',
react_native_xplat_target('cxxreact:bridge'),
react_native_xplat_target('jschelpers:jschelpers'),
],
visibility = [
react_native_xplat_target('cxxreact/...'),
],
)
... ...
// Copyright 2004-present Facebook. All Rights Reserved.
#include <gtest/gtest.h>
#include <exception>
#include <stdexcept>
#include <cxxreact/RecoverableError.h>
using namespace facebook::react;
TEST(RecoverableError, RunRethrowingAsRecoverableRecoverTest) {
try {
RecoverableError::runRethrowingAsRecoverable<std::runtime_error>([]() {
throw std::runtime_error("catch me");
});
FAIL() << "Unthrown exception";
} catch (const RecoverableError &err) {
ASSERT_STREQ(err.what(), "facebook::react::Recoverable: catch me");
} catch (...) {
FAIL() << "Uncaught exception";
}
}
TEST(RecoverableError, RunRethrowingAsRecoverableFallthroughTest) {
try {
RecoverableError::runRethrowingAsRecoverable<std::runtime_error>([]() {
throw std::logic_error("catch me");
});
FAIL() << "Unthrown exception";
} catch (const RecoverableError &err) {
FAIL() << "Recovered exception that should have fallen through";
} catch (const std::exception &err) {
ASSERT_STREQ(err.what(), "catch me");
}
}
... ...