initial commit taken from gitlab.lrz.de

This commit is contained in:
privatereese
2018-08-24 18:09:42 +02:00
parent ae54ed4c48
commit fc05486403
28494 changed files with 2159823 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
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)

167
node_modules/react-native/ReactCommon/cxxreact/BUCK generated vendored Normal file
View File

@@ -0,0 +1,167 @@
load("@xplat//configurations/buck/apple:flag_defs.bzl", "get_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", "get_static_library_ios_flags", "flags")
APPLE_COMPILER_FLAGS = flags.get_flag_value(get_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 = get_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"),
],
)

View File

@@ -0,0 +1,196 @@
// 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_;
};
}}}

View File

@@ -0,0 +1,187 @@
// 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_);
}
}
}
}

View File

@@ -0,0 +1,50 @@
// 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_;
};
}
}

View File

@@ -0,0 +1,180 @@
// 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

View File

@@ -0,0 +1,92 @@
// 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);
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

View File

@@ -0,0 +1,26 @@
// 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

View File

@@ -0,0 +1,178 @@
// 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.
};
} }

View File

@@ -0,0 +1,37 @@
// 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

View File

@@ -0,0 +1,61 @@
// 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

View File

@@ -0,0 +1,720 @@
// 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));
}
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());
}
} }

View File

@@ -0,0 +1,144 @@
// 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;
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);
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[]);
};
} }

View File

@@ -0,0 +1,70 @@
// 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
}
} }

View File

@@ -0,0 +1,12 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <jschelpers/JavaScriptCore.h>
namespace facebook {
namespace react {
void addNativeTracingLegacyHooks(JSGlobalContextRef ctx);
} }

View File

@@ -0,0 +1,47 @@
// 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
}
} }

View File

@@ -0,0 +1,12 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <jschelpers/JavaScriptCore.h>
namespace facebook {
namespace react {
void addJSCMemoryHooks(JSGlobalContextRef ctx);
} }

View File

@@ -0,0 +1,72 @@
// 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;
}
} }

View File

@@ -0,0 +1,34 @@
// 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);
};
}
}

View File

@@ -0,0 +1,95 @@
// 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
}
} }

View File

@@ -0,0 +1,12 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <jschelpers/JavaScriptCore.h>
namespace facebook {
namespace react {
void addJSCPerfStatsHooks(JSGlobalContextRef ctx);
} }

View File

@@ -0,0 +1,28 @@
// 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);
}
} }

View File

@@ -0,0 +1,12 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <jschelpers/JavaScriptCore.h>
namespace facebook {
namespace react {
void initSamplingProfilerOnMainJSCThread(JSGlobalContextRef ctx);
} }

View File

@@ -0,0 +1,320 @@
// 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
}
} }

View File

@@ -0,0 +1,12 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <jschelpers/JavaScriptCore.h>
namespace facebook {
namespace react {
void addNativeTracingHooks(JSGlobalContextRef ctx);
} }

View File

@@ -0,0 +1,19 @@
// 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());
}
}
}
}

View File

@@ -0,0 +1,15 @@
// 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);
}
}

View File

@@ -0,0 +1,104 @@
// 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() {}
};
} }

View File

@@ -0,0 +1,98 @@
// 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

View File

@@ -0,0 +1,65 @@
// 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

View File

@@ -0,0 +1,35 @@
// 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;
};
}
}

View File

@@ -0,0 +1,90 @@
// 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);
}
}}

View File

@@ -0,0 +1,106 @@
// 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>

View File

@@ -0,0 +1,23 @@
// 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;
};
}}

View File

@@ -0,0 +1,77 @@
// 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;
}
}}

View File

@@ -0,0 +1,29 @@
// 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);
} }

View File

@@ -0,0 +1,163 @@
// 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));
}
}}

View File

@@ -0,0 +1,69 @@
// 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_;
};
}
}

View File

@@ -0,0 +1,37 @@
// 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;
};
}
}

View File

@@ -0,0 +1,236 @@
// 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());
});
}
} }

View File

@@ -0,0 +1,102 @@
// 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);
/**
* 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
};
} }

View File

@@ -0,0 +1,34 @@
// 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
} }

View File

@@ -0,0 +1,62 @@
// 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;
}
} }

View File

@@ -0,0 +1,53 @@
// 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

View File

@@ -0,0 +1,46 @@
// 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

View File

@@ -0,0 +1,45 @@
// 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

View File

@@ -0,0 +1,140 @@
// 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>());
}

View File

@@ -0,0 +1,50 @@
// 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();

View File

@@ -0,0 +1,35 @@
// 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_;
};
}
}
}

View File

@@ -0,0 +1,45 @@
// 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
}}

View File

@@ -0,0 +1,54 @@
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//build_defs:fb_xplat_cxx.bzl", "cxx_test")
load("@xplat//configurations/buck/android:jni_instrumentation_test", "jni_instrumentation_test_lib")
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/...'),
],
)

View File

@@ -0,0 +1,36 @@
// 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");
}
}

View File

@@ -0,0 +1,100 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <cxxreact/JsArgumentHelpers.h>
#include <folly/dynamic.h>
#include <gtest/gtest.h>
#include <algorithm>
using namespace std;
using namespace folly;
using namespace facebook::xplat;
#define EXPECT_JSAE(statement, exstr) do { \
try { \
statement; \
FAIL() << "Expected JsArgumentException(" << (exstr) << ") not thrown"; \
} catch (const JsArgumentException& ex) { \
EXPECT_EQ(ex.what(), std::string(exstr)); \
} \
} while(0) // let any other exception escape, gtest will deal.
TEST(JsArgumentHelpersTest, args) {
const bool aBool = true;
const int64_t anInt = 17;
const double aDouble = 3.14;
const string aString = "word";
const dynamic anArray = dynamic::array("a", "b", "c");
const dynamic anObject = dynamic::object("k1", "v1")("k2", "v2");
const string aNumericString = to<string>(anInt);
folly::dynamic args = dynamic::array(aBool, anInt, aDouble, aString, anArray, anObject, aNumericString);
EXPECT_EQ(jsArgAsBool(args, 0), aBool);
EXPECT_EQ(jsArgAsInt(args, 1), anInt);
EXPECT_EQ(jsArgAsDouble(args, 2), aDouble);
EXPECT_EQ(jsArgAsString(args, 3), aString);
EXPECT_EQ(jsArgAsArray(args, 4), anArray);
EXPECT_EQ(jsArgAsObject(args, 5), anObject);
// const args
const folly::dynamic& cargs = args;
const folly::dynamic& a4 = jsArgAsArray(cargs, 4);
EXPECT_EQ(a4, anArray);
EXPECT_EQ(jsArgAsObject(cargs, 5), anObject);
// helpers returning dynamic should return same object without copying
EXPECT_EQ(&jsArgAsArray(args, 4), &(args[4]));
EXPECT_EQ(&jsArgAsArray(cargs, 4), &(args[4]));
// dynamics returned for mutable args should be mutable. The test is that
// this compiles.
jsArgAsArray(args, 4)[2] = "d";
jsArgAsArray(args, 4)[2] = "c";
// These fail to compile due to constness.
// jsArgAsArray(cargs, 4)[2] = "d";
// jsArgAsArray(cargs, 4)[2] = "c";
// ref-qualified member function tests
EXPECT_EQ(jsArgN(args, 3, &folly::dynamic::getString), aString);
EXPECT_EQ(jsArg(args[3], &folly::dynamic::getString), aString);
// conversions
EXPECT_EQ(jsArgAsDouble(args, 1), anInt * 1.0);
EXPECT_EQ(jsArgAsString(args, 1), aNumericString);
EXPECT_EQ(jsArgAsInt(args, 6), anInt);
// Test exception messages.
// out_of_range
EXPECT_JSAE(jsArgAsBool(args, 7),
"JavaScript provided 7 arguments for C++ method which references at least "
"8 arguments: out of range in dynamic array");
// Conv range_error (invalid value conversion)
const std::string exhead = "Could not convert argument 3 to required type: ";
const std::string extail = ": Invalid leading character: \"word\"";
try {
jsArgAsInt(args, 3);
FAIL() << "Expected JsArgumentException(" << exhead << "..." << extail << ") not thrown";
} catch (const JsArgumentException& ex) {
const std::string exwhat = ex.what();
EXPECT_GT(exwhat.size(), exhead.size());
EXPECT_GT(exwhat.size(), extail.size());
EXPECT_TRUE(std::equal(exhead.cbegin(), exhead.cend(), exwhat.cbegin()))
<< "JsArgumentException('" << exwhat << "') does not begin with '" << exhead << "'";
EXPECT_TRUE(std::equal(extail.crbegin(), extail.crend(), exwhat.crbegin()))
<< "JsArgumentException('" << exwhat << "') does not end with '" << extail << "'";
}
// inconvertible types
EXPECT_JSAE(jsArgAsArray(args, 2),
"Argument 3 of type double is not required type Array");
EXPECT_JSAE(jsArgAsInt(args, 4),
"Error converting javascript arg 4 to C++: "
"TypeError: expected dynamic type `int/double/bool/string', but had type `array'");
// type predicate failure
EXPECT_JSAE(jsArgAsObject(args, 4),
"Argument 5 of type array is not required type Object");
}

View File

@@ -0,0 +1,56 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <sys/mman.h>
#include <fcntl.h>
#include <folly/File.h>
#include <gtest/gtest.h>
#include <cxxreact/JSBigString.h>
using namespace facebook;
using namespace facebook::react;
namespace {
int tempFileFromString(std::string contents)
{
std::string tmp {getenv("TMPDIR")};
tmp += "/temp.XXXXXX";
std::vector<char> tmpBuf {tmp.begin(), tmp.end()};
tmpBuf.push_back('\0');
const int fd = mkstemp(tmpBuf.data());
write(fd, contents.c_str(), contents.size() + 1);
return fd;
}
};
TEST(JSBigFileString, MapWholeFileTest) {
std::string data {"Hello, world"};
const auto size = data.length() + 1;
// Initialise Big String
int fd = tempFileFromString("Hello, world");
JSBigFileString bigStr {fd, size};
// Test
ASSERT_STREQ(data.c_str(), bigStr.c_str());
}
TEST(JSBigFileString, MapPartTest) {
std::string data {"Hello, world"};
// Sub-string to actually map
std::string needle {"or"};
off_t offset = data.find(needle);
// Initialise Big String
int fd = tempFileFromString(data);
JSBigFileString bigStr {fd, needle.size(), offset};
// Test
ASSERT_EQ(needle.length(), bigStr.size());
for (unsigned int i = 0; i < needle.length(); ++i) {
ASSERT_EQ(needle[i], bigStr.c_str()[i]);
}
}

View File

@@ -0,0 +1,270 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <gtest/gtest.h>
#include <cxxreact/JSCExecutor.h>
#include <cxxreact/MessageQueueThread.h>
#include <cxxreact/MethodCall.h>
using namespace facebook;
using namespace facebook::react;
// TODO(12340362): Fix these tests. And add checks for sizes.
/*
namespace {
std::string capturedMethodCalls;
struct NullDelegate : ExecutorDelegate {
virtual void registerExecutor(std::unique_ptr<JSExecutor> executor,
std::shared_ptr<MessageQueueThread> queue) {
std::terminate();
}
virtual std::unique_ptr<JSExecutor> unregisterExecutor(JSExecutor& executor) {
std::terminate();
}
virtual std::vector<std::string> moduleNames() {
return std::vector<std::string>{};
}
virtual folly::dynamic getModuleConfig(const std::string& name) {
std::terminate();
}
virtual void callNativeModules(
JSExecutor& executor, std::string callJSON, bool isEndOfBatch) {
// TODO: capture calljson
std::terminate();
}
virtual MethodCallResult callSerializableNativeHook(
JSExecutor& executor, unsigned int moduleId, unsigned int methodId, folly::dynamic&& args) {
std::terminate();
}
};
struct FakeMessageQueue : MessageQueueThread {
virtual void runOnQueue(std::function<void()>&& runnable) {
// This is wrong, but oh well.
runnable();
}
virtual void runOnQueueSync(std::function<void()>&& runnable) {
runnable();
}
virtual void quitSynchronous() {
std::terminate();
}
};
std::vector<MethodCall> executeForMethodCalls(
JSCExecutor& e,
int moduleId,
int methodId,
folly::dynamic args = folly::dynamic::array()) {
e.callFunction(folly::to<std::string>(moduleId), folly::to<std::string>(methodId), std::move(args));
return parseMethodCalls(capturedMethodCalls);
}
void loadApplicationScript(JSCExecutor& e, std::string jsText) {
e.loadApplicationScript(std::unique_ptr<JSBigString>(new JSBigStdString(jsText)), "");
}
void setGlobalVariable(JSCExecutor& e, std::string name, std::string jsonObject) {
e.setGlobalVariable(name, std::unique_ptr<JSBigString>(new JSBigStdString(jsonObject)));
}
}
TEST(JSCExecutor, Initialize) {
JSCExecutor executor(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
}
TEST(JSCExecutor, Two) {
JSCExecutor exec1(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
JSCExecutor exec2(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
}
TEST(JSCExecutor, CallFunction) {
auto jsText = ""
"var Bridge = {"
" callFunctionReturnFlushedQueue: function (module, method, args) {"
" return [[module + 1], [method + 1], [args]];"
" },"
"};"
"function require() { return Bridge; }"
"";
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
loadApplicationScript(e, jsText);
folly::dynamic args = folly::dynamic::array();
args.push_back(true);
args.push_back(0.4);
args.push_back("hello, world");
args.push_back(4.0);
auto returnedCalls = executeForMethodCalls(e, 10, 9, args);
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
EXPECT_EQ(11, returnedCall.moduleId);
EXPECT_EQ(10, returnedCall.methodId);
ASSERT_EQ(4, returnedCall.arguments.size());
EXPECT_EQ(args[0], returnedCall.arguments[0]);
EXPECT_EQ(args[1], returnedCall.arguments[1]);
EXPECT_EQ(args[2], returnedCall.arguments[2]);
EXPECT_EQ(folly::dynamic(4.0), returnedCall.arguments[3]);
}
TEST(JSCExecutor, CallFunctionWithMap) {
auto jsText = ""
"var Bridge = {"
" callFunctionReturnFlushedQueue: function (module, method, args) {"
" var s = args[0].foo + args[0].bar + args[0].baz;"
" return [[module], [method], [[s]]];"
" },"
"};"
"function require() { return Bridge; }"
"";
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
loadApplicationScript(e, jsText);
folly::dynamic args = folly::dynamic::array();
folly::dynamic map = folly::dynamic::object
("foo", folly::dynamic("hello"))
("bar", folly::dynamic(4.0))
("baz", folly::dynamic(true))
;
args.push_back(std::move(map));
auto returnedCalls = executeForMethodCalls(e, 10, 9, args);
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size());
EXPECT_EQ("hello4true", returnedCall.arguments[0].getString());
}
TEST(JSCExecutor, CallFunctionReturningMap) {
auto jsText = ""
"var Bridge = {"
" callFunctionReturnFlushedQueue: function (module, method, args) {"
" var s = { foo: 4, bar: true };"
" return [[module], [method], [[s]]];"
" },"
"};"
"function require() { return Bridge; }"
"";
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
loadApplicationScript(e, jsText);
auto returnedCalls = executeForMethodCalls(e, 10, 9);
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size());
ASSERT_EQ(folly::dynamic::OBJECT, returnedCall.arguments[0].type());
auto& returnedMap = returnedCall.arguments[0];
auto foo = returnedMap.at("foo");
EXPECT_EQ(folly::dynamic(4.0), foo);
auto bar = returnedMap.at("bar");
EXPECT_EQ(folly::dynamic(true), bar);
}
TEST(JSCExecutor, CallFunctionWithArray) {
auto jsText = ""
"var Bridge = {"
" callFunctionReturnFlushedQueue: function (module, method, args) {"
" var s = args[0][0]+ args[0][1] + args[0][2] + args[0].length;"
" return [[module], [method], [[s]]];"
" },"
"};"
"function require() { return Bridge; }"
"";
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
loadApplicationScript(e, jsText);
std::vector<folly::dynamic> args;
std::vector<folly::dynamic> array {
folly::dynamic("hello"),
folly::dynamic(4.0),
folly::dynamic(true),
};
args.push_back(std::move(array));
auto returnedCalls = executeForMethodCalls(e, 10, 9, args);
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size());
EXPECT_EQ("hello4true3", returnedCall.arguments[0].getString());
}
TEST(JSCExecutor, CallFunctionReturningNumberArray) {
auto jsText = ""
"var Bridge = {"
" callFunctionReturnFlushedQueue: function (module, method, args) {"
" var s = [3, 1, 4];"
" return [[module], [method], [[s]]];"
" },"
"};"
"function require() { return Bridge; }"
"";
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
loadApplicationScript(e, jsText);
auto returnedCalls = executeForMethodCalls(e, 10, 9);
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size());
ASSERT_EQ(folly::dynamic::ARRAY, returnedCall.arguments[0].type());
auto& array = returnedCall.arguments[0];
EXPECT_EQ(3, array.size());
EXPECT_EQ(folly::dynamic(3.0), array[0]);
EXPECT_EQ(folly::dynamic(4.0), array[2]);
}
TEST(JSCExecutor, SetSimpleGlobalVariable) {
auto jsText = ""
"var Bridge = {"
" callFunctionReturnFlushedQueue: function (module, method, args) {"
" return [[module], [method], [[__foo]]];"
" },"
"};"
"function require() { return Bridge; }"
"";
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
loadApplicationScript(e, jsText);
setGlobalVariable(e, "__foo", "42");
auto returnedCalls = executeForMethodCalls(e, 10, 9);
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size());
ASSERT_EQ(42.0, returnedCall.arguments[0].getDouble());
}
TEST(JSCExecutor, SetObjectGlobalVariable) {
auto jsText = ""
"var Bridge = {"
" callFunctionReturnFlushedQueue: function (module, method, args) {"
" return [[module], [method], [[__foo]]];"
" },"
"};"
"function require() { return Bridge; }"
"";
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
loadApplicationScript(e, jsText);
auto jsonObject = ""
"{"
" \"foo\": \"hello\","
" \"bar\": 4,"
" \"baz\": true"
"}"
"";
setGlobalVariable(e, "__foo", jsonObject);
auto returnedCalls = executeForMethodCalls(e, 10, 9);
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size());
ASSERT_EQ(folly::dynamic::OBJECT, returnedCall.arguments[0].type());
auto& returnedMap = returnedCall.arguments[0];
auto foo = returnedMap.at("foo");
EXPECT_EQ(folly::dynamic("hello"), foo);
auto bar = returnedMap.at("bar");
EXPECT_EQ(folly::dynamic(4.0), bar);
auto baz = returnedMap.at("baz");
EXPECT_EQ(folly::dynamic(true), baz);
}
*/

View File

@@ -0,0 +1,44 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <gtest/gtest.h>
#include <cxxreact/JSCExecutor.h>
using namespace facebook;
using namespace facebook::react;
/*
static const char* expectedLogMessageSubstring = NULL;
static bool hasSeenExpectedLogMessage = false;
static void mockLogHandler(int pri, const char *tag, const char *msg) {
if (expectedLogMessageSubstring == NULL) {
return;
}
hasSeenExpectedLogMessage |= (strstr(msg, expectedLogMessageSubstring) != NULL);
}
class JSCLoggingTest : public testing::Test {
protected:
virtual void SetUp() override {
setLogHandler(&mockLogHandler);
}
virtual void TearDown() override {
setLogHandler(NULL);
expectedLogMessageSubstring = NULL;
hasSeenExpectedLogMessage = false;
}
};
TEST_F(JSCLoggingTest, LogException) {
auto jsText = "throw new Error('I am a banana!');";
expectedLogMessageSubstring = "I am a banana!";
JSCExecutor e;
e.loadApplicationScript(jsText, "");
ASSERT_TRUE(hasSeenExpectedLogMessage);
}
*/

View File

@@ -0,0 +1,152 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <cxxreact/MethodCall.h>
#include <folly/json.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-compare"
#include <gtest/gtest.h>
#pragma GCC diagnostic pop
using namespace facebook;
using namespace facebook::react;
using namespace folly;
TEST(parseMethodCalls, SingleReturnCallNoArgs) {
auto jsText = "[[7],[3],[[]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(0, returnedCall.arguments.size());
ASSERT_EQ(7, returnedCall.moduleId);
ASSERT_EQ(3, returnedCall.methodId);
}
TEST(parseMethodCalls, InvalidReturnFormat) {
try {
auto input = dynamic::object("foo", 1);
parseMethodCalls(std::move(input));
ADD_FAILURE();
} catch (const std::invalid_argument&) {
// ignored
}
try {
auto input = dynamic::array(dynamic::object("foo", 1));
parseMethodCalls(std::move(input));
ADD_FAILURE();
} catch (const std::invalid_argument&) {
// ignored
}
try {
auto input = dynamic::array(1, 4, dynamic::object("foo", 2));
parseMethodCalls(std::move(input));
ADD_FAILURE();
} catch (const std::invalid_argument&) {
// ignored
}
try {
auto input = dynamic::array(dynamic::array(1),
dynamic::array(4),
dynamic::object("foo", 2));
parseMethodCalls(std::move(input));
ADD_FAILURE();
} catch (const std::invalid_argument&) {
// ignored
}
try {
auto input = dynamic::array(dynamic::array(1),
dynamic::array(4),
dynamic::array());
parseMethodCalls(std::move(input));
ADD_FAILURE();
} catch (const std::invalid_argument&) {
// ignored
}
}
TEST(parseMethodCalls, NumberReturn) {
auto jsText = "[[0],[0],[[\"foobar\"]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size());
ASSERT_EQ(folly::dynamic::STRING, returnedCall.arguments[0].type());
ASSERT_EQ("foobar", returnedCall.arguments[0].asString());
}
TEST(parseMethodCalls, StringReturn) {
auto jsText = "[[0],[0],[[42.16]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size());
ASSERT_EQ(folly::dynamic::DOUBLE, returnedCall.arguments[0].type());
ASSERT_EQ(42.16, returnedCall.arguments[0].asDouble());
}
TEST(parseMethodCalls, BooleanReturn) {
auto jsText = "[[0],[0],[[false]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size());
ASSERT_EQ(folly::dynamic::BOOL, returnedCall.arguments[0].type());
ASSERT_FALSE(returnedCall.arguments[0].asBool());
}
TEST(parseMethodCalls, NullReturn) {
auto jsText = "[[0],[0],[[null]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size());
ASSERT_EQ(folly::dynamic::NULLT, returnedCall.arguments[0].type());
}
TEST(parseMethodCalls, MapReturn) {
auto jsText = "[[0],[0],[[{\"foo\": \"hello\", \"bar\": 4.0, \"baz\": true}]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size());
ASSERT_EQ(folly::dynamic::OBJECT, returnedCall.arguments[0].type());
auto& returnedMap = returnedCall.arguments[0];
auto foo = returnedMap.at("foo");
EXPECT_EQ(folly::dynamic("hello"), foo);
auto bar = returnedMap.at("bar");
EXPECT_EQ(folly::dynamic(4.0), bar);
auto baz = returnedMap.at("baz");
EXPECT_EQ(folly::dynamic(true), baz);
}
TEST(parseMethodCalls, ArrayReturn) {
auto jsText = "[[0],[0],[[[\"foo\", 42.0, false]]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(1, returnedCall.arguments.size());
ASSERT_EQ(folly::dynamic::ARRAY, returnedCall.arguments[0].type());
auto& returnedArray = returnedCall.arguments[0];
ASSERT_EQ(3, returnedArray.size());
ASSERT_EQ(folly::dynamic("foo"), returnedArray[0]);
ASSERT_EQ(folly::dynamic(42.0), returnedArray[1]);
ASSERT_EQ(folly::dynamic(false), returnedArray[2]);
}
TEST(parseMethodCalls, ReturnMultipleParams) {
auto jsText = "[[0],[0],[[\"foo\", 14, null, false]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(1, returnedCalls.size());
auto returnedCall = returnedCalls[0];
ASSERT_EQ(4, returnedCall.arguments.size());
ASSERT_EQ(folly::dynamic::STRING, returnedCall.arguments[0].type());
ASSERT_EQ(folly::dynamic::INT64, returnedCall.arguments[1].type());
ASSERT_EQ(folly::dynamic::NULLT, returnedCall.arguments[2].type());
ASSERT_EQ(folly::dynamic::BOOL, returnedCall.arguments[3].type());
}
TEST(parseMethodCalls, ParseTwoCalls) {
auto jsText = "[[0,0],[1,1],[[],[]]]";
auto returnedCalls = parseMethodCalls(folly::parseJson(jsText));
ASSERT_EQ(2, returnedCalls.size());
}

View File

@@ -0,0 +1,105 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <string>
#include <gtest/gtest.h>
#include <folly/json.h>
#include <jschelpers/Value.h>
#ifdef WITH_FBJSCEXTENSION
#undef ASSERT
#include <JavaScriptCore/config.h>
#include "OpaqueJSString.h"
#endif
#include <stdexcept>
using namespace facebook::react;
#ifdef ANDROID
#include <android/looper.h>
static void prepare() {
ALooper_prepare(0);
}
#else
static void prepare() {}
#endif
TEST(Value, Undefined) {
prepare();
JSGlobalContextRef ctx = JSC_JSGlobalContextCreateInGroup(false, nullptr, nullptr);
auto v = Value::makeUndefined(ctx);
auto s = String::adopt(ctx, JSC_JSValueToStringCopy(ctx, v, nullptr));
EXPECT_EQ("undefined", s.str());
JSC_JSGlobalContextRelease(ctx);
}
TEST(Value, FromJSON) {
prepare();
JSGlobalContextRef ctx = JSC_JSGlobalContextCreateInGroup(false, nullptr, nullptr);
String s(ctx, "{\"a\": 4}");
Value v(Value::fromJSON(s));
EXPECT_TRUE(v.isObject());
JSC_JSGlobalContextRelease(ctx);
}
TEST(Value, ToJSONString) {
prepare();
JSGlobalContextRef ctx = JSC_JSGlobalContextCreateInGroup(false, nullptr, nullptr);
String s(ctx, "{\"a\": 4}");
Value v(Value::fromJSON(s));
folly::dynamic dyn = folly::parseJson(v.toJSONString());
ASSERT_NE(nullptr, dyn);
EXPECT_TRUE(dyn.isObject());
auto val = dyn.at("a");
ASSERT_NE(nullptr, val);
ASSERT_TRUE(val.isNumber());
EXPECT_EQ(4, val.asInt());
EXPECT_EQ(4.0f, val.asDouble());
JSC_JSGlobalContextRelease(ctx);
}
#ifdef WITH_FBJSCEXTENSION
// Just test that handling invalid data doesn't crash.
TEST(Value, FromBadUtf8) {
prepare();
JSGlobalContextRef ctx = JSC_JSGlobalContextCreateInGroup(false, nullptr, nullptr);
// 110xxxxx 10xxxxxx
auto dyn = folly::dynamic("\xC0");
Value::fromDynamic(ctx, dyn);
dyn = folly::dynamic("\xC0\x00");
Value::fromDynamic(ctx, dyn);
// 1110xxxx 10xxxxxx 10xxxxxx
dyn = "\xE0";
Value::fromDynamic(ctx, dyn);
Value(ctx, Value::fromDynamic(ctx, dyn)).toJSONString();
dyn = "\xE0\x00";
Value::fromDynamic(ctx, dyn);
Value(ctx, Value::fromDynamic(ctx, dyn)).toJSONString();
dyn = "\xE0\x00\x00";
Value::fromDynamic(ctx, dyn);
Value(ctx, Value::fromDynamic(ctx, dyn)).toJSONString();
dyn = "\xE0\xA0\x00";
Value::fromDynamic(ctx, dyn);
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
dyn = "\xF0";
Value::fromDynamic(ctx, dyn);
Value(ctx, Value::fromDynamic(ctx, dyn)).toJSONString();
dyn = "\xF0\x00\x00\x00";
Value::fromDynamic(ctx, dyn);
dyn = "\xF0\x80\x80\x00";
Value::fromDynamic(ctx, dyn);
Value(ctx, Value::fromDynamic(ctx, dyn)).toJSONString();
JSC_JSGlobalContextRelease(ctx);
}
// Just test that handling invalid data doesn't crash.
TEST(Value, BadUtf16) {
prepare();
JSGlobalContextRef ctx = JSC_JSGlobalContextCreateInGroup(false, nullptr, nullptr);
UChar buf[] = { 0xDD00, 0xDD00, 0xDD00, 0x1111 };
JSStringRef ref = OpaqueJSString::create(buf, 4).leakRef();
Value v(ctx, ref);
v.toJSONString(0);
JSC_JSGlobalContextRelease(ctx);
}
#endif

44
node_modules/react-native/ReactCommon/fabric/BUCK generated vendored Normal file
View File

@@ -0,0 +1,44 @@
load("@xplat//configurations/buck/apple:flag_defs.bzl", "get_debug_preprocessor_flags")
load("//ReactNative:DEFS.bzl", "IS_OSS_BUILD", "react_native_xplat_target", "rn_xplat_cxx_library", "APPLE_INSPECTOR_FLAGS")
APPLE_COMPILER_FLAGS = []
if not IS_OSS_BUILD:
load("@xplat//configurations/buck/apple:flag_defs.bzl", "get_static_library_ios_flags", "flags")
APPLE_COMPILER_FLAGS = flags.get_flag_value(get_static_library_ios_flags(), 'compiler_flags')
rn_xplat_cxx_library(
name = "fabric",
srcs = glob(["*.cpp"]),
header_namespace = "",
exported_headers = subdir_glob(
[
("", "*.h"),
],
prefix = "fabric",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_preprocessor_flags = get_debug_preprocessor_flags() + APPLE_INSPECTOR_FLAGS,
force_static = True,
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
visibility = [
"PUBLIC",
],
deps = [
"xplat//fbsystrace:fbsystrace",
"xplat//folly:headers_only",
"xplat//folly:memory",
"xplat//folly:molly",
"xplat//third-party/glog:glog",
react_native_xplat_target("fabric/core:core"),
],
)

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "FabricUIManager.h"
#include "IFabricPlatformUIOperationManager.h"
#include "ShadowNode.h"
namespace facebook {
namespace react {
FabricUIManager::FabricUIManager(const std::shared_ptr<IFabricPlatformUIOperationManager> &platformUIOperationManager) :
platformUIOperationManager_(platformUIOperationManager) {};
ShadowNodeRef FabricUIManager::createNode(int reactTag, std::string viewName, int rootTag, folly::dynamic props, void *instanceHandle) {
platformUIOperationManager_->performUIOperation();
return std::make_shared<ShadowNode>(reactTag, viewName, rootTag, props, instanceHandle);
}
ShadowNodeRef FabricUIManager::cloneNode(const ShadowNodeRef &node) {
return nullptr;
}
ShadowNodeRef FabricUIManager::cloneNodeWithNewChildren(const ShadowNodeRef &node) {
return nullptr;
}
ShadowNodeRef FabricUIManager::cloneNodeWithNewProps(const ShadowNodeRef &node, folly::dynamic props) {
return nullptr;
}
ShadowNodeRef FabricUIManager::cloneNodeWithNewChildrenAndProps(const ShadowNodeRef &node, folly::dynamic newProps) {
return nullptr;
}
void FabricUIManager::appendChild(const ShadowNodeRef &parentNode, const ShadowNodeRef &childNode) {
}
ShadowNodeSetRef FabricUIManager::createChildSet(int rootTag) {
return nullptr;
}
void FabricUIManager::appendChildToSet(const ShadowNodeSetRef &childSet, const ShadowNodeRef &childNode) {
}
void FabricUIManager::completeRoot(int rootTag, const ShadowNodeSetRef &childSet) {
}
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,43 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <folly/dynamic.h>
#include <folly/FBVector.h>
#include <memory>
namespace facebook {
namespace react {
class ShadowNode;
class IFabricPlatformUIOperationManager;
typedef std::shared_ptr<const ShadowNode> ShadowNodeRef;
typedef folly::fbvector<const ShadowNodeRef> ShadowNodeSet;
typedef std::shared_ptr<const ShadowNodeSet> ShadowNodeSetRef;
class FabricUIManager {
public:
FabricUIManager(const std::shared_ptr<IFabricPlatformUIOperationManager> &platformUIOperationManager);
ShadowNodeRef createNode(int reactTag, std::string viewName, int rootTag, folly::dynamic props, void *instanceHandle);
ShadowNodeRef cloneNode(const ShadowNodeRef &node);
ShadowNodeRef cloneNodeWithNewChildren(const ShadowNodeRef &node);
ShadowNodeRef cloneNodeWithNewProps(const ShadowNodeRef &node, folly::dynamic props);
ShadowNodeRef cloneNodeWithNewChildrenAndProps(const ShadowNodeRef &node, folly::dynamic newProps);
void appendChild(const ShadowNodeRef &parentNode, const ShadowNodeRef &childNode);
ShadowNodeSetRef createChildSet(int rootTag);
void appendChildToSet(const ShadowNodeSetRef &childSet, const ShadowNodeRef &childNode);
void completeRoot(int rootTag, const ShadowNodeSetRef &childSet);
private:
std::shared_ptr<IFabricPlatformUIOperationManager> platformUIOperationManager_;
};
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
namespace facebook {
namespace react {
/**
* An interface for FabricUIManager to perform platform-specific UI operations, like updating native UIView's in iOS.
*/
class IFabricPlatformUIOperationManager {
public:
// TODO: add meaningful methods
virtual void performUIOperation() = 0;
};
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ShadowNode.h"
namespace facebook {
namespace react {
ShadowNode::ShadowNode(int reactTag, std::string viewName, int rootTag, folly::dynamic props, void *instanceHandle) :
reactTag_(reactTag),
viewName_(viewName),
rootTag_(rootTag),
props_(props),
instanceHandle_(instanceHandle) {}
}}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <folly/dynamic.h>
#include <memory>
namespace facebook {
namespace react {
class ShadowNode {
public:
int reactTag_;
std::string viewName_;
int rootTag_;
folly::dynamic props_;
void *instanceHandle_;
ShadowNode(int reactTag, std::string viewName, int rootTag, folly::dynamic props, void *instanceHandle);
};
}}

51
node_modules/react-native/ReactCommon/fabric/core/BUCK generated vendored Normal file
View File

@@ -0,0 +1,51 @@
load("@xplat//configurations/buck/apple:flag_defs.bzl", "get_debug_preprocessor_flags")
load("//ReactNative:DEFS.bzl", "IS_OSS_BUILD", "react_native_xplat_target", "rn_xplat_cxx_library", "APPLE_INSPECTOR_FLAGS")
APPLE_COMPILER_FLAGS = []
if not IS_OSS_BUILD:
load("@xplat//configurations/buck/apple:flag_defs.bzl", "get_static_library_ios_flags", "flags")
APPLE_COMPILER_FLAGS = flags.get_flag_value(get_static_library_ios_flags(), 'compiler_flags')
rn_xplat_cxx_library(
name = "core",
srcs = glob(
[
"**/*.cpp",
],
),
headers = glob(
[
"**/*.h",
],
),
header_namespace = "",
exported_headers = subdir_glob(
[
("primitives", "*.h"),
],
prefix = "fabric/core",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_preprocessor_flags = get_debug_preprocessor_flags() + APPLE_INSPECTOR_FLAGS,
force_static = True,
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
visibility = ["PUBLIC"],
deps = [
"xplat//fbsystrace:fbsystrace",
"xplat//folly:headers_only",
"xplat//folly:memory",
"xplat//folly:molly",
"xplat//third-party/glog:glog",
react_native_xplat_target("fabric/debug:debug"),
],
)

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "Sealable.h"
#include <stdexcept>
namespace facebook {
namespace react {
void Sealable::seal() const {
sealed_ = true;
}
bool Sealable::getSealed() const {
return sealed_;
}
void Sealable::ensureUnsealed() const {
if (sealed_) {
throw std::runtime_error("Attempt to mutate a sealed object.");
}
}
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
namespace facebook {
namespace react {
/*
* Represents something which can be *sealed* (imperatively marked as immutable).
*
* Why do we need this? In Fabric, some objects are semi-immutable
* even if they explicitly marked as `const`. It means that in some special
* cases those objects can be const-cast-away and then mutated. That comes from
* the fact that we share some object's life-cycle responsibilities with React
* and the immutability is guaranteed by some logic splitted between native and
* JavaScript worlds (which makes impossible to fully use immutability
* enforcement at a language level).
* To detect possible errors as early as possible we additionally mark objects
* as *sealed* after some stage and then enforce this at run-time.
*
* How to use:
* 1. Inherit your class from `Sealable`.
* 2. Call `ensureUnsealed()` from all non-const methods.
* 3. Call `seal()` at some point from which any modifications
* must be prevented.
*/
class Sealable {
public:
/*
* Seals the object. This operation is irreversible;
* the object cannot be "unsealed" after being sealing.
*/
void seal() const;
/*
* Returns if the object already sealed or not.
*/
bool getSealed() const;
protected:
/*
* Throws an exception if the object is sealed.
* Call this from all non-`const` methods.
*/
void ensureUnsealed() const;
private:
mutable bool sealed_ {false};
};
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,46 @@
load("@xplat//configurations/buck/apple:flag_defs.bzl", "get_debug_preprocessor_flags")
load("//ReactNative:DEFS.bzl", "IS_OSS_BUILD", "react_native_xplat_target", "rn_xplat_cxx_library", "APPLE_INSPECTOR_FLAGS")
APPLE_COMPILER_FLAGS = []
if not IS_OSS_BUILD:
load("@xplat//configurations/buck/apple:flag_defs.bzl", "get_static_library_ios_flags", "flags")
APPLE_COMPILER_FLAGS = flags.get_flag_value(get_static_library_ios_flags(), 'compiler_flags')
rn_xplat_cxx_library(
name = "debug",
srcs = glob(
[
"**/*.cpp",
],
),
headers = glob(
[
"**/*.h",
],
),
header_namespace = "",
exported_headers = subdir_glob(
[
("", "*.h"),
],
prefix = "fabric/debug",
),
compiler_flags = [
"-std=c++14",
"-Wall",
"-fexceptions",
"-frtti",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_preprocessor_flags = get_debug_preprocessor_flags() + APPLE_INSPECTOR_FLAGS,
force_static = True,
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
visibility = ["PUBLIC"],
deps = [
"xplat//folly:headers_only",
],
)

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "DebugStringConvertible.h"
namespace facebook {
namespace react {
std::string DebugStringConvertible::getDebugChildrenDescription(int level) const {
std::string childrenString = "";
for (auto child : getDebugChildren()) {
childrenString += child->getDebugDescription(level + 1);
}
return childrenString;
}
std::string DebugStringConvertible::getDebugPropsDescription(int level) const {
std::string propsString = "";
for (auto prop : getDebugProps()) {
auto name = prop->getDebugName();
auto value = prop->getDebugValue();
auto children = prop->getDebugPropsDescription(level + 1);
auto valueAndChildren = value + (children.empty() ? "" : "(" + children + ")");
propsString += " " + name + (valueAndChildren.empty() ? "" : "=" + valueAndChildren);
}
if (!propsString.empty()) {
// Removing leading space character.
propsString.erase(propsString.begin());
}
return propsString;
}
std::string DebugStringConvertible::getDebugDescription(int level) const {
std::string nameString = getDebugName();
std::string valueString = getDebugValue();
std::string childrenString = getDebugChildrenDescription(level);
std::string propsString = getDebugPropsDescription(level);
return "<" + nameString +
(valueString.empty() ? "" : "=" + valueString) +
(propsString.empty() ? "" : " " + propsString) +
(childrenString.empty() ? "/>" : ">" + childrenString + "</" + nameString + ">");
}
std::string DebugStringConvertible::getDebugName() const {
return "Node";
}
std::string DebugStringConvertible::getDebugValue() const {
return "";
}
SharedDebugStringConvertibleList DebugStringConvertible::getDebugChildren() const {
return SharedDebugStringConvertibleList();
}
SharedDebugStringConvertibleList DebugStringConvertible::getDebugProps() const {
return SharedDebugStringConvertibleList();
}
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,59 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <string>
#include <vector>
namespace facebook {
namespace react {
class DebugStringConvertible;
using SharedDebugStringConvertible = std::shared_ptr<const DebugStringConvertible>;
using SharedDebugStringConvertibleList = std::vector<const SharedDebugStringConvertible>;
// Abstract class describes conformance to DebugStringConvertible concept
// and implements basic recursive debug string assembly algorithm.
// Use this as a base class for providing a debugging textual representation
// of your class.
// TODO (#26770211): Clear up the naming.
class DebugStringConvertible {
public:
// Returns a name of the object.
// Default implementation returns "Node".
virtual std::string getDebugName() const;
// Returns a value assosiate with the object.
// Default implementation returns an empty string.
virtual std::string getDebugValue() const;
// Returns a list of `DebugStringConvertible` objects which can be considered
// as *children* of the object.
// Default implementation returns an empty list.
virtual SharedDebugStringConvertibleList getDebugChildren() const;
// Returns a list of `DebugStringConvertible` objects which can be considered
// as *properties* of the object.
// Default implementation returns an empty list.
virtual SharedDebugStringConvertibleList getDebugProps() const;
// Returns a string which represents the object in a human-readable way.
// Default implementation returns a description of the subtree
// rooted at this node, represented in XML-like format.
virtual std::string getDebugDescription(int level = 0) const;
// Do same as `getDebugDescription` but return only *children* and
// *properties* parts (which are used in `getDebugDescription`).
virtual std::string getDebugPropsDescription(int level = 0) const;
virtual std::string getDebugChildrenDescription(int level = 0) const;
};
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "DebugStringConvertibleItem.h"
namespace facebook {
namespace react {
DebugStringConvertibleItem::DebugStringConvertibleItem(
const std::string &name,
const std::string &value,
const SharedDebugStringConvertibleList &props,
const SharedDebugStringConvertibleList &children
):
name_(name),
value_(value),
props_(props),
children_(children) {}
std::string DebugStringConvertibleItem::getDebugName() const {
return name_;
}
std::string DebugStringConvertibleItem::getDebugValue() const {
return value_;
}
SharedDebugStringConvertibleList DebugStringConvertibleItem::getDebugProps() const {
return props_;
}
SharedDebugStringConvertibleList DebugStringConvertibleItem::getDebugChildren() const {
return children_;
}
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <fabric/debug/DebugStringConvertible.h>
namespace facebook {
namespace react {
// Trivial implementation of `DebugStringConvertible` abstract class
// with a stored output; useful for assembling `DebugStringConvertible` values
// in custom implementations of `getDebugChildren` and `getDebugProps`.
class DebugStringConvertibleItem:
public DebugStringConvertible {
public:
DebugStringConvertibleItem() = default;
DebugStringConvertibleItem(const DebugStringConvertibleItem &item) = default;
DebugStringConvertibleItem(
const std::string &name = "",
const std::string &value = "",
const SharedDebugStringConvertibleList &props = {},
const SharedDebugStringConvertibleList &children = {}
);
std::string getDebugName() const override;
std::string getDebugValue() const override;
SharedDebugStringConvertibleList getDebugChildren() const override;
SharedDebugStringConvertibleList getDebugProps() const override;
private:
std::string name_;
std::string value_;
SharedDebugStringConvertibleList props_;
SharedDebugStringConvertibleList children_;
};
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,30 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := jschelpers
LOCAL_SRC_FILES := \
JSCHelpers.cpp \
Unicode.cpp \
Value.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_SHARED_LIBRARIES := libfolly_json libjsc libglog
include $(BUILD_STATIC_LIBRARY)
$(call import-module,folly)
$(call import-module,jsc)
$(call import-module,glog)
$(call import-module,privatedata)

71
node_modules/react-native/ReactCommon/jschelpers/BUCK generated vendored Normal file
View File

@@ -0,0 +1,71 @@
load("//ReactNative:DEFS.bzl", "rn_xplat_cxx_library", "react_native_xplat_target", "ANDROID_JSC_INTERNAL_DEPS", "APPLE_JSC_INTERNAL_DEPS")
EXPORTED_HEADERS = [
"JavaScriptCore.h",
"JSCHelpers.h",
"JSCWrapper.h",
"noncopyable.h",
"Unicode.h",
"Value.h",
]
rn_xplat_cxx_library(
name = "jscinternalhelpers",
srcs = glob(
["*.cpp"],
excludes = ["systemJSCWrapper.cpp"],
),
headers = glob(
["*.h"],
excludes = EXPORTED_HEADERS,
),
header_namespace = "",
exported_headers = dict([
(
"jschelpers/%s" % header,
header,
)
for header in EXPORTED_HEADERS
]),
compiler_flags = [
"-Wall",
"-fexceptions",
"-frtti",
"-fvisibility=hidden",
"-std=c++1y",
],
exported_deps = [
react_native_xplat_target("privatedata:privatedata"),
],
fbandroid_deps = ANDROID_JSC_INTERNAL_DEPS,
fbobjc_deps = APPLE_JSC_INTERNAL_DEPS,
force_static = True,
visibility = [
"PUBLIC",
],
deps = [
"xplat//folly:molly",
"xplat//third-party/glog:glog",
react_native_xplat_target("privatedata:privatedata"),
],
)
rn_xplat_cxx_library(
name = "jschelpers",
srcs = [],
compiler_flags = [
"-Wall",
"-fexceptions",
"-fvisibility=hidden",
"-std=c++1y",
],
fbobjc_frameworks = [
"$SDKROOT/System/Library/Frameworks/JavaScriptCore.framework",
],
fbobjc_srcs = ["systemJSCWrapper.cpp"],
force_static = True,
visibility = [
"PUBLIC",
],
deps = [":jscinternalhelpers"],
)

View File

@@ -0,0 +1,296 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCHelpers.h"
#ifdef WITH_FBSYSTRACE
#include <fbsystrace.h>
#endif
#include <glog/logging.h>
#if WITH_FBJSCEXTENSIONS
#include <pthread.h>
#endif
#include "JavaScriptCore.h"
#include "Value.h"
#include <privatedata/PrivateDataBase.h>
#if WITH_FBJSCEXTENSIONS
#undef ASSERT
#undef WTF_EXPORT_PRIVATE
#include <JavaScriptCore/config.h>
#include <wtf/WTFThreadData.h>
#undef TRUE
#undef FALSE
#endif
namespace facebook {
namespace react {
namespace {
class JSFunctionPrivateData : public PrivateDataBase {
public:
explicit JSFunctionPrivateData(JSFunction&& function) : jsFunction_{std::move(function)} {}
JSFunction& getJSFunction() {
return jsFunction_;
}
private:
JSFunction jsFunction_;
};
JSValueRef functionCaller(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
const bool isCustomJSC = isCustomJSCPtr(ctx);
auto* privateData = PrivateDataBase::cast<JSFunctionPrivateData>(
JSC_JSObjectGetPrivate(isCustomJSC, function));
return (privateData->getJSFunction())(ctx, thisObject, argumentCount, arguments);
}
JSClassRef createFuncClass(JSContextRef ctx) {
JSClassDefinition definition = kJSClassDefinitionEmpty;
definition.attributes |= kJSClassAttributeNoAutomaticPrototype;
// Need to duplicate the two different finalizer blocks, since there's no way
// for it to capture this static information.
const bool isCustomJSC = isCustomJSCPtr(ctx);
if (isCustomJSC) {
definition.finalize = [](JSObjectRef object) {
auto* privateData = PrivateDataBase::cast<JSFunctionPrivateData>(
JSC_JSObjectGetPrivate(true, object));
delete privateData;
};
} else {
definition.finalize = [](JSObjectRef object) {
auto* privateData = PrivateDataBase::cast<JSFunctionPrivateData>(
JSC_JSObjectGetPrivate(false, object));
delete privateData;
};
}
definition.callAsFunction = exceptionWrapMethod<&functionCaller>();
return JSC_JSClassCreate(isCustomJSC, &definition);
}
JSObjectRef makeFunction(
JSContextRef ctx,
JSStringRef name,
JSFunction function) {
static JSClassRef kClassDef = NULL, kCustomJSCClassDef = NULL;
JSClassRef *classRef = isCustomJSCPtr(ctx) ? &kCustomJSCClassDef : &kClassDef;
if (!*classRef) {
*classRef = createFuncClass(ctx);
}
// dealloc in kClassDef.finalize
JSFunctionPrivateData *functionDataPtr = new JSFunctionPrivateData(std::move(function));
auto functionObject = Object(ctx, JSC_JSObjectMake(ctx, *classRef, functionDataPtr));
functionObject.setProperty("name", Value(ctx, name));
return functionObject;
}
}
void JSException::buildMessage(JSContextRef ctx, JSValueRef exn, JSStringRef sourceURL, const char* errorMsg) {
std::ostringstream msgBuilder;
if (errorMsg && strlen(errorMsg) > 0) {
msgBuilder << errorMsg << ": ";
}
Object exnObject = Value(ctx, exn).asObject();
Value exnMessage = exnObject.getProperty("message");
msgBuilder << (exnMessage.isString() ? exnMessage : (Value)exnObject).toString().str();
// The null/empty-ness of source tells us if the JS came from a
// file/resource, or was a constructed statement. The location
// info will include that source, if any.
std::string locationInfo = sourceURL != nullptr ? String::ref(ctx, sourceURL).str() : "";
auto line = exnObject.getProperty("line");
if (line != nullptr && line.isNumber()) {
if (locationInfo.empty() && line.asInteger() != 1) {
// If there is a non-trivial line number, but there was no
// location info, we include a placeholder, and the line
// number.
locationInfo = folly::to<std::string>("<unknown file>:", line.asInteger());
} else if (!locationInfo.empty()) {
// If there is location info, we always include the line
// number, regardless of its value.
locationInfo += folly::to<std::string>(":", line.asInteger());
}
}
if (!locationInfo.empty()) {
msgBuilder << " (" << locationInfo << ")";
}
auto exceptionText = msgBuilder.str();
LOG(ERROR) << "Got JS Exception: " << exceptionText;
msg_ = std::move(exceptionText);
Value jsStack = exnObject.getProperty("stack");
if (jsStack.isString()) {
auto stackText = jsStack.toString().str();
LOG(ERROR) << "Got JS Stack: " << stackText;
stack_ = std::move(stackText);
}
}
namespace ExceptionHandling {
PlatformErrorExtractor platformErrorExtractor;
}
JSObjectRef makeFunction(
JSContextRef ctx,
const char* name,
JSFunction function) {
return makeFunction(ctx, String(ctx, name), std::move(function));
}
void installGlobalFunction(
JSGlobalContextRef ctx,
const char* name,
JSFunction function) {
auto jsName = String(ctx, name);
auto functionObj = makeFunction(ctx, jsName, std::move(function));
Object::getGlobalObject(ctx).setProperty(jsName, Value(ctx, functionObj));
}
JSObjectRef makeFunction(
JSGlobalContextRef ctx,
const char* name,
JSObjectCallAsFunctionCallback callback) {
auto jsName = String(ctx, name);
return JSC_JSObjectMakeFunctionWithCallback(ctx, jsName, callback);
}
void installGlobalFunction(
JSGlobalContextRef ctx,
const char* name,
JSObjectCallAsFunctionCallback callback) {
String jsName(ctx, name);
JSObjectRef functionObj = JSC_JSObjectMakeFunctionWithCallback(
ctx, jsName, callback);
Object::getGlobalObject(ctx).setProperty(jsName, Value(ctx, functionObj));
}
void installGlobalProxy(
JSGlobalContextRef ctx,
const char* name,
JSObjectGetPropertyCallback callback) {
JSClassDefinition proxyClassDefintion = kJSClassDefinitionEmpty;
proxyClassDefintion.attributes |= kJSClassAttributeNoAutomaticPrototype;
proxyClassDefintion.getProperty = callback;
const bool isCustomJSC = isCustomJSCPtr(ctx);
JSClassRef proxyClass = JSC_JSClassCreate(isCustomJSC, &proxyClassDefintion);
JSObjectRef proxyObj = JSC_JSObjectMake(ctx, proxyClass, nullptr);
JSC_JSClassRelease(isCustomJSC, proxyClass);
Object::getGlobalObject(ctx).setProperty(name, Value(ctx, proxyObj));
}
void removeGlobal(JSGlobalContextRef ctx, const char* name) {
Object::getGlobalObject(ctx).setProperty(name, Value::makeUndefined(ctx));
}
JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef sourceURL) {
JSValueRef exn, result;
result = JSC_JSEvaluateScript(context, script, NULL, sourceURL, 0, &exn);
if (result == nullptr) {
throw JSException(context, exn, sourceURL);
}
return result;
}
#if WITH_FBJSCEXTENSIONS
JSValueRef evaluateSourceCode(JSContextRef context, JSSourceCodeRef source, JSStringRef sourceURL) {
JSValueRef exn, result;
result = JSEvaluateSourceCode(context, source, NULL, &exn);
if (result == nullptr) {
throw JSException(context, exn, sourceURL);
}
return result;
}
#endif
JSContextLock::JSContextLock(JSGlobalContextRef ctx) noexcept
#if WITH_FBJSCEXTENSIONS
: ctx_(ctx),
globalLock_(PTHREAD_MUTEX_INITIALIZER)
{
WTFThreadData& threadData = wtfThreadData();
// Code below is responsible for acquiring locks. It should execute
// atomically, thus none of the functions invoked from now on are allowed to
// throw an exception
try {
if (!threadData.isDebuggerThread()) {
CHECK(0 == pthread_mutex_lock(&globalLock_));
}
JSLock(ctx_);
} catch (...) {
abort();
}
}
#else
{}
#endif
JSContextLock::~JSContextLock() noexcept {
#if WITH_FBJSCEXTENSIONS
WTFThreadData& threadData = wtfThreadData();
JSUnlock(ctx_);
if (!threadData.isDebuggerThread()) {
CHECK(0 == pthread_mutex_unlock(&globalLock_));
}
#endif
}
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation) {
try {
throw;
} catch (const std::bad_alloc& ex) {
throw; // We probably shouldn't try to handle this in JS
} catch (const std::exception& ex) {
if (ExceptionHandling::platformErrorExtractor) {
auto extractedEror = ExceptionHandling::platformErrorExtractor(ex, exceptionLocation);
if (extractedEror.message.length() > 0) {
return Value::makeError(ctx, extractedEror.message.c_str(), extractedEror.stack.c_str());
}
}
auto msg = folly::to<std::string>("C++ exception in '", exceptionLocation, "'\n\n", ex.what());
return Value::makeError(ctx, msg.c_str());
} catch (const char* ex) {
auto msg = folly::to<std::string>("C++ exception (thrown as a char*) in '", exceptionLocation, "'\n\n", ex);
return Value::makeError(ctx, msg.c_str());
} catch (...) {
auto msg = folly::to<std::string>("Unknown C++ exception in '", exceptionLocation, "'");
return Value::makeError(ctx, msg.c_str());
}
}
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, JSObjectRef jsFunctionCause) {
try {
auto functionName = Object(ctx, jsFunctionCause).getProperty("name").toString().str();
return translatePendingCppExceptionToJSError(ctx, functionName.c_str());
} catch (...) {
return Value::makeError(ctx, "Failed to translate native exception");
}
}
} }

View File

@@ -0,0 +1,146 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <algorithm>
#include <functional>
#include <stdexcept>
#include <jschelpers/JavaScriptCore.h>
#include <jschelpers/Value.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
class RN_EXPORT JSException : public std::exception {
public:
explicit JSException(const char* msg)
: msg_(msg) {}
explicit JSException(JSContextRef ctx, JSValueRef exn, const char* msg) {
buildMessage(ctx, exn, nullptr, msg);
}
explicit JSException(JSContextRef ctx, JSValueRef exn, JSStringRef sourceURL) {
buildMessage(ctx, exn, sourceURL, nullptr);
}
const std::string& getStack() const {
return stack_;
}
virtual const char* what() const noexcept override {
return msg_.c_str();
}
private:
std::string msg_;
std::string stack_;
void buildMessage(JSContextRef ctx, JSValueRef exn, JSStringRef sourceURL, const char* errorMsg);
};
namespace ExceptionHandling {
struct ExtractedEror {
std::string message;
// Stacktrace formatted like JS stack
// method@filename[:line[:column]]
std::string stack;
};
typedef ExtractedEror(*PlatformErrorExtractor)(const std::exception &ex, const char *context);
extern PlatformErrorExtractor platformErrorExtractor;
}
using JSFunction = std::function<JSValueRef(JSContextRef, JSObjectRef, size_t, const JSValueRef[])>;
JSObjectRef makeFunction(
JSContextRef ctx,
const char* name,
JSFunction function);
RN_EXPORT void installGlobalFunction(
JSGlobalContextRef ctx,
const char* name,
JSFunction function);
JSObjectRef makeFunction(
JSGlobalContextRef ctx,
const char* name,
JSObjectCallAsFunctionCallback callback);
RN_EXPORT void installGlobalFunction(
JSGlobalContextRef ctx,
const char* name,
JSObjectCallAsFunctionCallback callback);
void installGlobalProxy(
JSGlobalContextRef ctx,
const char* name,
JSObjectGetPropertyCallback callback);
void removeGlobal(JSGlobalContextRef ctx, const char* name);
JSValueRef evaluateScript(
JSContextRef ctx,
JSStringRef script,
JSStringRef sourceURL);
#if WITH_FBJSCEXTENSIONS
JSValueRef evaluateSourceCode(
JSContextRef ctx,
JSSourceCodeRef source,
JSStringRef sourceURL);
#endif
/**
* A lock for protecting accesses to the JSGlobalContext
* This will be a no-op for most compilations, where #if WITH_FBJSCEXTENSIONS is false,
* but avoids deadlocks in execution environments with advanced locking requirements,
* particularly with uses of the pthread mutex lock
**/
class JSContextLock {
public:
JSContextLock(JSGlobalContextRef ctx) noexcept;
~JSContextLock() noexcept;
private:
#if WITH_FBJSCEXTENSIONS
JSGlobalContextRef ctx_;
pthread_mutex_t globalLock_;
#endif
};
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation);
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, JSObjectRef jsFunctionCause);
template<JSValueRef (method)(JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception)>
inline JSObjectCallAsFunctionCallback exceptionWrapMethod() {
struct funcWrapper {
static JSValueRef call(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
try {
return (*method)(ctx, function, thisObject, argumentCount, arguments, exception);
} catch (...) {
*exception = translatePendingCppExceptionToJSError(ctx, function);
return JSC_JSValueMakeUndefined(ctx);
}
}
};
return &funcWrapper::call;
}
} }

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "JSCWrapper.h"
#if defined(__APPLE__)
// TODO: use glog in OSS too
#if __has_include(<glog/logging.h>)
#define USE_GLOG 1
#include <glog/logging.h>
#else
#define USE_GLOG 0
#endif
namespace facebook {
namespace react {
static const JSCWrapper* s_customWrapper = nullptr;
bool isCustomJSCWrapperSet() {
return s_customWrapper != nullptr;
}
const JSCWrapper* customJSCWrapper() {
#if USE_GLOG
CHECK(s_customWrapper != nullptr) << "Accessing custom JSC wrapper before it's set";
#endif
return s_customWrapper;
}
void setCustomJSCWrapper(const JSCWrapper* wrapper) {
#if USE_GLOG
CHECK(s_customWrapper == nullptr) << "Can't set custom JSC wrapper multiple times";
#endif
s_customWrapper = wrapper;
}
} }
#endif

View File

@@ -0,0 +1,188 @@
/**
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <functional>
#include <string>
#include <JavaScriptCore/JavaScript.h>
#if defined(JSCINTERNAL) || (!defined(__APPLE__))
#define JSC_IMPORT extern "C"
#else
#define JSC_IMPORT extern
#endif
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
class IInspector;
}
}
JSC_IMPORT void JSGlobalContextEnableDebugger(
JSGlobalContextRef ctx,
facebook::react::IInspector &globalInspector,
const char *title,
const std::function<bool()> &checkIsInspectedRemote);
JSC_IMPORT void JSGlobalContextDisableDebugger(
JSGlobalContextRef ctx,
facebook::react::IInspector &globalInspector);
// This is used to substitute an alternate JSC implementation for
// testing. These calls must all be ABI compatible with the standard JSC.
JSC_IMPORT JSValueRef JSEvaluateBytecodeBundle(JSContextRef, JSObjectRef, int, JSStringRef, JSValueRef*);
JSC_IMPORT bool JSSamplingProfilerEnabled();
JSC_IMPORT void JSStartSamplingProfilingOnMainJSCThread(JSGlobalContextRef);
JSC_IMPORT JSValueRef JSPokeSamplingProfiler(JSContextRef);
#ifdef __cplusplus
extern "C" {
#endif
JSC_IMPORT void configureJSCForIOS(std::string); // TODO: replace with folly::dynamic once supported
JSC_IMPORT void FBJSContextStartGCTimers(JSContextRef);
#ifdef __cplusplus
}
#endif
#if defined(__APPLE__)
#include <objc/objc.h>
#include <JavaScriptCore/JSStringRefCF.h>
#include <string>
/**
* JSNoBytecodeFileFormatVersion
*
* Version number indicating that bytecode is not supported by this runtime.
*/
RN_EXPORT extern const int32_t JSNoBytecodeFileFormatVersion;
namespace facebook {
namespace react {
#define JSC_WRAPPER_METHOD(m) decltype(&m) m
struct JSCWrapper {
// JSGlobalContext
JSC_WRAPPER_METHOD(JSGlobalContextCreateInGroup);
JSC_WRAPPER_METHOD(JSGlobalContextRelease);
JSC_WRAPPER_METHOD(JSGlobalContextSetName);
// JSContext
JSC_WRAPPER_METHOD(JSContextGetGlobalContext);
JSC_WRAPPER_METHOD(JSContextGetGlobalObject);
JSC_WRAPPER_METHOD(FBJSContextStartGCTimers);
// JSEvaluate
JSC_WRAPPER_METHOD(JSEvaluateScript);
JSC_WRAPPER_METHOD(JSEvaluateBytecodeBundle);
// JSString
JSC_WRAPPER_METHOD(JSStringCreateWithUTF8CString);
JSC_WRAPPER_METHOD(JSStringCreateWithCFString);
#if WITH_FBJSCEXTENSIONS
JSC_WRAPPER_METHOD(JSStringCreateWithUTF8CStringExpectAscii);
#endif
JSC_WRAPPER_METHOD(JSStringCopyCFString);
JSC_WRAPPER_METHOD(JSStringGetCharactersPtr);
JSC_WRAPPER_METHOD(JSStringGetLength);
JSC_WRAPPER_METHOD(JSStringGetMaximumUTF8CStringSize);
JSC_WRAPPER_METHOD(JSStringIsEqualToUTF8CString);
JSC_WRAPPER_METHOD(JSStringRelease);
JSC_WRAPPER_METHOD(JSStringRetain);
// JSClass
JSC_WRAPPER_METHOD(JSClassCreate);
JSC_WRAPPER_METHOD(JSClassRelease);
// JSObject
JSC_WRAPPER_METHOD(JSObjectCallAsConstructor);
JSC_WRAPPER_METHOD(JSObjectCallAsFunction);
JSC_WRAPPER_METHOD(JSObjectGetPrivate);
JSC_WRAPPER_METHOD(JSObjectGetProperty);
JSC_WRAPPER_METHOD(JSObjectGetPropertyAtIndex);
JSC_WRAPPER_METHOD(JSObjectIsConstructor);
JSC_WRAPPER_METHOD(JSObjectIsFunction);
JSC_WRAPPER_METHOD(JSObjectMake);
JSC_WRAPPER_METHOD(JSObjectMakeArray);
JSC_WRAPPER_METHOD(JSObjectMakeDate);
JSC_WRAPPER_METHOD(JSObjectMakeError);
JSC_WRAPPER_METHOD(JSObjectMakeFunctionWithCallback);
JSC_WRAPPER_METHOD(JSObjectSetPrivate);
JSC_WRAPPER_METHOD(JSObjectSetProperty);
JSC_WRAPPER_METHOD(JSObjectSetPropertyAtIndex);
// JSPropertyNameArray
JSC_WRAPPER_METHOD(JSObjectCopyPropertyNames);
JSC_WRAPPER_METHOD(JSPropertyNameArrayGetCount);
JSC_WRAPPER_METHOD(JSPropertyNameArrayGetNameAtIndex);
JSC_WRAPPER_METHOD(JSPropertyNameArrayRelease);
// JSValue
JSC_WRAPPER_METHOD(JSValueCreateJSONString);
JSC_WRAPPER_METHOD(JSValueGetType);
JSC_WRAPPER_METHOD(JSValueMakeFromJSONString);
JSC_WRAPPER_METHOD(JSValueMakeBoolean);
JSC_WRAPPER_METHOD(JSValueMakeNull);
JSC_WRAPPER_METHOD(JSValueMakeNumber);
JSC_WRAPPER_METHOD(JSValueMakeString);
JSC_WRAPPER_METHOD(JSValueMakeUndefined);
JSC_WRAPPER_METHOD(JSValueProtect);
JSC_WRAPPER_METHOD(JSValueToBoolean);
JSC_WRAPPER_METHOD(JSValueToNumber);
JSC_WRAPPER_METHOD(JSValueToObject);
JSC_WRAPPER_METHOD(JSValueToStringCopy);
JSC_WRAPPER_METHOD(JSValueUnprotect);
JSC_WRAPPER_METHOD(JSValueIsNull);
// Sampling profiler
JSC_WRAPPER_METHOD(JSSamplingProfilerEnabled);
JSC_WRAPPER_METHOD(JSPokeSamplingProfiler);
JSC_WRAPPER_METHOD(JSStartSamplingProfilingOnMainJSCThread);
JSC_WRAPPER_METHOD(JSGlobalContextEnableDebugger);
JSC_WRAPPER_METHOD(JSGlobalContextDisableDebugger);
JSC_WRAPPER_METHOD(configureJSCForIOS);
// Objective-C API
Class JSContext;
Class JSValue;
int32_t JSBytecodeFileFormatVersion;
};
template <typename T>
bool isCustomJSCPtr(T *x) {
return (uintptr_t)x & 0x1;
}
RN_EXPORT bool isCustomJSCWrapperSet();
RN_EXPORT void setCustomJSCWrapper(const JSCWrapper* wrapper);
// This will return a single value for the whole life of the process.
RN_EXPORT const JSCWrapper *systemJSCWrapper();
RN_EXPORT const JSCWrapper *customJSCWrapper();
} }
#else
namespace facebook {
namespace react {
template <typename T>
bool isCustomJSCPtr(T *x) {
// Always use system JSC pointers
return false;
}
} }
#endif

View File

@@ -0,0 +1,211 @@
/**
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <jschelpers/JSCWrapper.h>
#if defined(__APPLE__)
// Use for methods that are taking JSContextRef as a first param
#define __jsc_wrapper(method, ctx, ...) \
(facebook::react::isCustomJSCPtr(ctx) ? \
facebook::react::customJSCWrapper() : \
facebook::react::systemJSCWrapper() \
)->method(ctx, ## __VA_ARGS__)
// Use for methods that don't take a JSContextRef as a first param. The wrapped version
// of this method will require context as an additional param, but it will be dropped
// before calling into the JSC method.
#define __jsc_drop_ctx_wrapper(method, ctx, ...) \
(facebook::react::isCustomJSCPtr(ctx) ? \
facebook::react::customJSCWrapper() : \
facebook::react::systemJSCWrapper() \
)->method(__VA_ARGS__)
// Use for methods were access to a JSContextRef is impractical. The first bool param
// will be dropped before the JSC method is invoked.
#define __jsc_ensure_bool(field) \
static_assert(std::is_same<typename std::decay<decltype(field)>::type, bool>::value, "useCustomJSC must be bool");
#define __jsc_bool_wrapper(method, useCustomJSC, ...) \
([]{ __jsc_ensure_bool(useCustomJSC) }, useCustomJSC ? \
facebook::react::customJSCWrapper() : \
facebook::react::systemJSCWrapper() \
)->method(__VA_ARGS__)
// Used for wrapping properties
#define __jsc_prop_wrapper(prop, ctx) \
(facebook::react::isCustomJSCPtr(ctx) ? \
facebook::react::customJSCWrapper() : \
facebook::react::systemJSCWrapper() \
)->prop
// Poison all regular versions of the JSC API in shared code. This prevents accidental
// mixed usage of regular and custom JSC methods.
// See https://gcc.gnu.org/onlinedocs/gcc-3.3/cpp/Pragmas.html for details
#define jsc_pragma(x) _Pragma(#x)
#ifndef NO_JSC_POISON
#define jsc_poison(methods) jsc_pragma(GCC poison methods)
#else
#define jsc_poison(methods)
#endif
#else
#define __jsc_wrapper(method, ctx, ...) method(ctx, ## __VA_ARGS__)
#define __jsc_drop_ctx_wrapper(method, ctx, ...) ((void)ctx, method(__VA_ARGS__))
#define __jsc_bool_wrapper(method, useCustomJSC, ...) \
((void)useCustomJSC, method(__VA_ARGS__))
#define __jsc_prop_wrapper(prop, ctx) prop
#define jsc_pragma(x)
#define jsc_poison(methods)
#endif
// JSGlobalContext
#define JSC_JSGlobalContextCreateInGroup(...) __jsc_bool_wrapper(JSGlobalContextCreateInGroup, __VA_ARGS__)
#define JSC_JSGlobalContextRelease(...) __jsc_wrapper(JSGlobalContextRelease, __VA_ARGS__)
#define JSC_JSGlobalContextSetName(...) __jsc_wrapper(JSGlobalContextSetName, __VA_ARGS__)
jsc_poison(JSContextGroupCreate JSContextGroupRelease JSContextGroupRetain
JSGlobalContextCreate JSGlobalContextCreateInGroup JSGlobalContextCopyName
JSGlobalContextRelease JSGlobalContextRetain JSGlobalContextSetName)
// JSContext
#define JSC_JSContextGetGlobalContext(...) __jsc_wrapper(JSContextGetGlobalContext, __VA_ARGS__)
#define JSC_JSContextGetGlobalObject(...) __jsc_wrapper(JSContextGetGlobalObject, __VA_ARGS__)
#define JSC_FBJSContextStartGCTimers(...) __jsc_wrapper(FBJSContextStartGCTimers, __VA_ARGS__)
jsc_poison(JSContextGetGlobalContext JSContextGetGlobalObject JSContextGetGroup FBJSContextStartGCTimers)
// JSEvaluate
#define JSC_JSEvaluateScript(...) __jsc_wrapper(JSEvaluateScript, __VA_ARGS__)
#define JSC_JSEvaluateBytecodeBundle(...) __jsc_wrapper(JSEvaluateBytecodeBundle, __VA_ARGS__)
jsc_poison(JSCheckScriptSyntax JSEvaluateScript JSEvaluateBytecodeBundle JSGarbageCollect)
// JSString
#define JSC_JSStringCreateWithCFString(...) __jsc_drop_ctx_wrapper(JSStringCreateWithCFString, __VA_ARGS__)
#define JSC_JSStringCreateWithUTF8CString(...) __jsc_drop_ctx_wrapper(JSStringCreateWithUTF8CString, __VA_ARGS__)
#define JSC_JSStringCreateWithUTF8CStringExpectAscii(...) __jsc_drop_ctx_wrapper(JSStringCreateWithUTF8CStringExpectAscii, __VA_ARGS__)
#define JSC_JSStringCopyCFString(...) __jsc_drop_ctx_wrapper(JSStringCopyCFString, __VA_ARGS__)
#define JSC_JSStringGetCharactersPtr(...) __jsc_drop_ctx_wrapper(JSStringGetCharactersPtr, __VA_ARGS__)
#define JSC_JSStringGetLength(...) __jsc_drop_ctx_wrapper(JSStringGetLength, __VA_ARGS__)
#define JSC_JSStringGetMaximumUTF8CStringSize(...) __jsc_drop_ctx_wrapper(JSStringGetMaximumUTF8CStringSize, __VA_ARGS__)
#define JSC_JSStringIsEqualToUTF8CString(...) __jsc_drop_ctx_wrapper(JSStringIsEqualToUTF8CString, __VA_ARGS__)
#define JSC_JSStringRelease(...) __jsc_drop_ctx_wrapper(JSStringRelease, __VA_ARGS__)
#define JSC_JSStringRetain(...) __jsc_drop_ctx_wrapper(JSStringRetain, __VA_ARGS__)
jsc_poison(JSStringCopyCFString JSStringCreateWithCharacters JSStringCreateWithCFString
JSStringCreateWithUTF8CString JSStringCreateWithUTF8CStringExpectAscii
JSStringGetCharactersPtr JSStringGetLength JSStringGetMaximumUTF8CStringSize
JSStringGetUTF8CString JSStringIsEqual JSStringIsEqualToUTF8CString
JSStringRelease JSStringRetain)
// JSValueRef
#define JSC_JSValueCreateJSONString(...) __jsc_wrapper(JSValueCreateJSONString, __VA_ARGS__)
#define JSC_JSValueGetType(...) __jsc_wrapper(JSValueGetType, __VA_ARGS__)
#define JSC_JSValueMakeFromJSONString(...) __jsc_wrapper(JSValueMakeFromJSONString, __VA_ARGS__)
#define JSC_JSValueMakeBoolean(...) __jsc_wrapper(JSValueMakeBoolean, __VA_ARGS__)
#define JSC_JSValueMakeNull(...) __jsc_wrapper(JSValueMakeNull, __VA_ARGS__)
#define JSC_JSValueMakeNumber(...) __jsc_wrapper(JSValueMakeNumber, __VA_ARGS__)
#define JSC_JSValueMakeString(...) __jsc_wrapper(JSValueMakeString, __VA_ARGS__)
#define JSC_JSValueMakeUndefined(...) __jsc_wrapper(JSValueMakeUndefined, __VA_ARGS__)
#define JSC_JSValueProtect(...) __jsc_wrapper(JSValueProtect, __VA_ARGS__)
#define JSC_JSValueToBoolean(...) __jsc_wrapper(JSValueToBoolean, __VA_ARGS__)
#define JSC_JSValueToNumber(...) __jsc_wrapper(JSValueToNumber, __VA_ARGS__)
#define JSC_JSValueToObject(...) __jsc_wrapper(JSValueToObject, __VA_ARGS__)
#define JSC_JSValueToStringCopy(...) __jsc_wrapper(JSValueToStringCopy, __VA_ARGS__)
#define JSC_JSValueUnprotect(...) __jsc_wrapper(JSValueUnprotect, __VA_ARGS__)
#define JSC_JSValueIsNull(...) __jsc_wrapper(JSValueIsNull, __VA_ARGS__)
jsc_poison(JSValueCreateJSONString JSValueGetType JSValueGetTypedArrayType JSValueIsArray
JSValueIsBoolean JSValueIsDate JSValueIsEqual JSValueIsInstanceOfConstructor
JSValueIsNull JSValueIsNumber JSValueIsObject JSValueIsObjectOfClass
JSValueIsStrictEqual JSValueIsString JSValueIsString JSValueIsUndefined
JSValueMakeBoolean JSValueMakeFromJSONString JSValueMakeNull JSValueMakeNumber
JSValueMakeString JSValueMakeUndefined JSValueProtect JSValueToBoolean
JSValueToNumber JSValueToObject JSValueToStringCopy JSValueUnprotect)
// JSClass
#define JSC_JSClassCreate(...) __jsc_bool_wrapper(JSClassCreate, __VA_ARGS__)
#define JSC_JSClassRelease(...) __jsc_bool_wrapper(JSClassRelease, __VA_ARGS__)
jsc_poison(JSClassCreate JSClassRelease JSClassRetain)
// JSObject
#define JSC_JSObjectCallAsConstructor(...) __jsc_wrapper(JSObjectCallAsConstructor, __VA_ARGS__)
#define JSC_JSObjectCallAsFunction(...) __jsc_wrapper(JSObjectCallAsFunction, __VA_ARGS__)
#define JSC_JSObjectGetPrivate(...) __jsc_bool_wrapper(JSObjectGetPrivate, __VA_ARGS__)
#define JSC_JSObjectGetProperty(...) __jsc_wrapper(JSObjectGetProperty, __VA_ARGS__)
#define JSC_JSObjectGetPropertyAtIndex(...) __jsc_wrapper(JSObjectGetPropertyAtIndex, __VA_ARGS__)
#define JSC_JSObjectIsConstructor(...) __jsc_wrapper(JSObjectIsConstructor, __VA_ARGS__)
#define JSC_JSObjectIsFunction(...) __jsc_wrapper(JSObjectIsFunction, __VA_ARGS__)
#define JSC_JSObjectMake(...) __jsc_wrapper(JSObjectMake, __VA_ARGS__)
#define JSC_JSObjectMakeArray(...) __jsc_wrapper(JSObjectMakeArray, __VA_ARGS__)
#define JSC_JSObjectMakeDate(...) __jsc_wrapper(JSObjectMakeDate, __VA_ARGS__)
#define JSC_JSObjectMakeError(...) __jsc_wrapper(JSObjectMakeError, __VA_ARGS__)
#define JSC_JSObjectMakeFunctionWithCallback(...) __jsc_wrapper(JSObjectMakeFunctionWithCallback, __VA_ARGS__)
#define JSC_JSObjectSetPrivate(...) __jsc_bool_wrapper(JSObjectSetPrivate, __VA_ARGS__)
#define JSC_JSObjectSetProperty(...) __jsc_wrapper(JSObjectSetProperty, __VA_ARGS__)
#define JSC_JSObjectSetPropertyAtIndex(...) __jsc_wrapper(JSObjectSetPropertyAtIndex, __VA_ARGS__)
jsc_poison(JSObjectCallAsConstructor JSObjectCallAsFunction JSObjectDeleteProperty
JSObjectGetPrivate JSObjectGetProperty JSObjectGetPropertyAtIndex
JSObjectGetPrototype JSObjectHasProperty JSObjectIsConstructor
JSObjectIsFunction JSObjectMake JSObjectMakeArray JSObjectMakeConstructor
JSObjectMakeDate JSObjectMakeError JSObjectMakeFunction
JSObjectMakeFunctionWithCallback JSObjectMakeRegExp JSObjectSetPrivate
JSObjectSetPrototype JSObjectSetProperty JSObjectSetPropertyAtIndex)
// JSPropertyNameArray
#define JSC_JSObjectCopyPropertyNames(...) __jsc_wrapper(JSObjectCopyPropertyNames, __VA_ARGS__)
#define JSC_JSPropertyNameArrayGetCount(...) __jsc_drop_ctx_wrapper(JSPropertyNameArrayGetCount, __VA_ARGS__)
#define JSC_JSPropertyNameArrayGetNameAtIndex(...) __jsc_drop_ctx_wrapper(JSPropertyNameArrayGetNameAtIndex, __VA_ARGS__)
#define JSC_JSPropertyNameArrayRelease(...) __jsc_drop_ctx_wrapper(JSPropertyNameArrayRelease, __VA_ARGS__)
jsc_poison(JSObjectCopyPropertyNames JSPropertyNameAccumulatorAddName
JSPropertyNameArrayGetCount JSPropertyNameArrayGetNameAtIndex
JSPropertyNameArrayRelease JSPropertyNameArrayRetain)
// JSTypedArray
jsc_poison(JSObjectMakeArrayBufferWithBytesNoCopy JSObjectMakeTypedArray
JSObjectMakeTypedArrayWithArrayBuffer
JSObjectMakeTypedArrayWithArrayBufferAndOffset
JSObjectMakeTypedArrayWithBytesNoCopy JSObjectGetTypedArrayByteLength
JSObjectGetTypedArrayByteOffset JSObjectGetTypedArrayBytesPtr
JSObjectGetTypedArrayBuffer JSObjectGetTypedArrayLength
JSObjectGetArrayBufferBytesPtr JSObjectGetArrayBufferByteLength)
// Sampling profiler
#define JSC_JSSamplingProfilerEnabled(...) __jsc_drop_ctx_wrapper(JSSamplingProfilerEnabled, __VA_ARGS__)
#define JSC_JSPokeSamplingProfiler(...) __jsc_wrapper(JSPokeSamplingProfiler, __VA_ARGS__)
#define JSC_JSStartSamplingProfilingOnMainJSCThread(...) __jsc_wrapper(JSStartSamplingProfilingOnMainJSCThread, __VA_ARGS__)
jsc_poison(JSSamplingProfilerEnabled JSPokeSamplingProfiler
JSStartSamplingProfilingOnMainJSCThread)
#define JSC_JSGlobalContextEnableDebugger(...) __jsc_wrapper(JSGlobalContextEnableDebugger, __VA_ARGS__)
// no need to poison JSGlobalContextEnableDebugger because it's not defined for System JSC / standard SDK header
// jsc_poison(JSGlobalContextEnableDebugger)
#define JSC_JSGlobalContextDisableDebugger(...) __jsc_wrapper(JSGlobalContextDisableDebugger, __VA_ARGS__)
// no need to poison JSGlobalContextDisableDebugger because it's not defined for System JSC / standard SDK header
// jsc_poison(JSGlobalContextDisableDebugger)
#define JSC_configureJSCForIOS(...) __jsc_bool_wrapper(configureJSCForIOS, __VA_ARGS__)
jsc_poison(configureJSCForIOS)
// Objective-C API
#define JSC_JSContext(ctx) __jsc_prop_wrapper(JSContext, ctx)
#define JSC_JSValue(ctx) __jsc_prop_wrapper(JSValue, ctx)
#undef jsc_poison
#undef jsc_pragma

View File

@@ -0,0 +1,88 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Unicode.h"
namespace facebook {
namespace react {
namespace unicode {
namespace {
// TODO(12827176): Don't duplicate this code here and fbjni.
const uint16_t kUtf8OneByteBoundary = 0x80;
const uint16_t kUtf8TwoBytesBoundary = 0x800;
const uint16_t kUtf16HighSubLowBoundary = 0xD800;
const uint16_t kUtf16HighSubHighBoundary = 0xDC00;
const uint16_t kUtf16LowSubHighBoundary = 0xE000;
// Calculate how many bytes are needed to convert an UTF16 string into UTF8
// UTF16 string
size_t utf16toUTF8Length(const uint16_t* utf16String, size_t utf16StringLen) {
if (!utf16String || utf16StringLen == 0) {
return 0;
}
uint32_t utf8StringLen = 0;
auto utf16StringEnd = utf16String + utf16StringLen;
auto idx16 = utf16String;
while (idx16 < utf16StringEnd) {
auto ch = *idx16++;
if (ch < kUtf8OneByteBoundary) {
utf8StringLen++;
} else if (ch < kUtf8TwoBytesBoundary) {
utf8StringLen += 2;
} else if (
(ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&
(idx16 < utf16StringEnd) &&
(*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {
utf8StringLen += 4;
idx16++;
} else {
utf8StringLen += 3;
}
}
return utf8StringLen;
}
} // namespace
std::string utf16toUTF8(const uint16_t* utf16String, size_t utf16StringLen) noexcept {
if (!utf16String || utf16StringLen <= 0) {
return "";
}
std::string utf8String(utf16toUTF8Length(utf16String, utf16StringLen), '\0');
auto idx8 = utf8String.begin();
auto idx16 = utf16String;
auto utf16StringEnd = utf16String + utf16StringLen;
while (idx16 < utf16StringEnd) {
auto ch = *idx16++;
if (ch < kUtf8OneByteBoundary) {
*idx8++ = (ch & 0x7F);
} else if (ch < kUtf8TwoBytesBoundary) {
*idx8++ = 0b11000000 | (ch >> 6);
*idx8++ = 0b10000000 | (ch & 0x3F);
} else if (
(ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&
(idx16 < utf16StringEnd) &&
(*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {
auto ch2 = *idx16++;
uint8_t trunc_byte = (((ch >> 6) & 0x0F) + 1);
*idx8++ = 0b11110000 | (trunc_byte >> 2);
*idx8++ = 0b10000000 | ((trunc_byte & 0x03) << 4) | ((ch >> 2) & 0x0F);
*idx8++ = 0b10000000 | ((ch & 0x03) << 4) | ((ch2 >> 6) & 0x0F);
*idx8++ = 0b10000000 | (ch2 & 0x3F);
} else {
*idx8++ = 0b11100000 | (ch >> 12);
*idx8++ = 0b10000000 | ((ch >> 6) & 0x3F);
*idx8++ = 0b10000000 | (ch & 0x3F);
}
}
return utf8String;
}
} // namespace unicode
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,14 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <string>
#include <cstdint>
namespace facebook {
namespace react {
namespace unicode {
__attribute__((visibility("default"))) std::string utf16toUTF8(const uint16_t* utf16, size_t length) noexcept;
}
}
}

View File

@@ -0,0 +1,322 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Value.h"
#include <folly/json.h>
#include <folly/Conv.h>
#include "JSCHelpers.h"
#include "JavaScriptCore.h"
// See the comment under Value::fromDynamic()
#if !defined(__APPLE__) && defined(WITH_FB_JSC_TUNING)
#define USE_FAST_FOLLY_DYNAMIC_CONVERSION 1
#else
#define USE_FAST_FOLLY_DYNAMIC_CONVERSION 0
#endif
namespace facebook {
namespace react {
/* static */
Object Object::makeDate(JSContextRef ctx, Object::TimeType time) {
using std::chrono::duration_cast;
using std::chrono::milliseconds;
JSValueRef arguments[1];
arguments[0] = JSC_JSValueMakeNumber(
ctx,
duration_cast<milliseconds>(time.time_since_epoch()).count());
JSValueRef exn;
auto result = JSC_JSObjectMakeDate(ctx, 1, arguments, &exn);
if (!result) {
throw JSException(ctx, exn, "Failed to create Date");
}
return Object(ctx, result);
}
Object Object::makeArray(JSContextRef ctx, JSValueRef* elements, unsigned length) {
JSValueRef exn;
auto arr = JSC_JSObjectMakeArray(ctx, length, elements, &exn);
if (!arr) {
throw JSException(ctx, exn, "Failed to create an Array");
}
return Object(ctx, arr);
}
Value::Value(JSContextRef context, JSValueRef value)
: m_context(context), m_value(value) {}
Value::Value(JSContextRef context, JSStringRef str)
: m_context(context), m_value(JSC_JSValueMakeString(context, str)) {}
JSContextRef Value::context() const {
return m_context;
}
/* static */
std::string Value::toJSONString(unsigned indent) const {
JSValueRef exn;
auto stringToAdopt = JSC_JSValueCreateJSONString(m_context, m_value, indent, &exn);
if (!stringToAdopt) {
throw JSException(m_context, exn, "Exception creating JSON string");
}
return String::adopt(m_context, stringToAdopt).str();
}
/* static */
Value Value::fromJSON(const String& json) {
JSContextRef ctx = json.context();
auto result = JSC_JSValueMakeFromJSONString(ctx, json);
if (!result) {
throw JSException(folly::to<std::string>(
"Failed to create Value from JSON: ", json.str()).c_str());
}
return Value(ctx, result);
}
Value Value::fromDynamic(JSContextRef ctx, const folly::dynamic& value) {
// JavaScriptCore's iOS APIs have their own version of this direct conversion.
// In addition, using this requires exposing some of JSC's private APIs,
// so it's limited to non-apple platforms and to builds that use the custom JSC.
// Otherwise, we use the old way of converting through JSON.
#if USE_FAST_FOLLY_DYNAMIC_CONVERSION
// Defer GC during the creation of the JSValue, as we don't want
// intermediate objects to be collected.
// We could use JSValueProtect(), but it will make the process much slower.
JSDeferredGCRef deferGC = JSDeferGarbageCollection(ctx);
// Set a global lock for the whole process,
// instead of re-acquiring the lock for each operation.
JSLock(ctx);
JSValueRef jsVal = Value::fromDynamicInner(ctx, value);
JSUnlock(ctx);
JSResumeGarbageCollection(ctx, deferGC);
return Value(ctx, jsVal);
#else
auto json = folly::toJson(value);
return fromJSON(String(ctx, json.c_str()));
#endif
}
JSValueRef Value::fromDynamicInner(JSContextRef ctx, const folly::dynamic& obj) {
switch (obj.type()) {
// For primitive types (and strings), just create and return an equivalent JSValue
case folly::dynamic::Type::NULLT:
return JSC_JSValueMakeNull(ctx);
case folly::dynamic::Type::BOOL:
return JSC_JSValueMakeBoolean(ctx, obj.getBool());
case folly::dynamic::Type::DOUBLE:
return JSC_JSValueMakeNumber(ctx, obj.getDouble());
case folly::dynamic::Type::INT64:
return JSC_JSValueMakeNumber(ctx, obj.asDouble());
case folly::dynamic::Type::STRING:
return JSC_JSValueMakeString(ctx, String(ctx, obj.getString().c_str()));
case folly::dynamic::Type::ARRAY: {
// Collect JSValue for every element in the array
JSValueRef vals[obj.size()];
for (size_t i = 0; i < obj.size(); ++i) {
vals[i] = fromDynamicInner(ctx, obj[i]);
}
// Create a JSArray with the values
JSValueRef arr = JSC_JSObjectMakeArray(ctx, obj.size(), vals, nullptr);
return arr;
}
case folly::dynamic::Type::OBJECT: {
// Create an empty object
JSObjectRef jsObj = JSC_JSObjectMake(ctx, nullptr, nullptr);
// Create a JSValue for each of the object's children and set them in the object
for (auto it = obj.items().begin(); it != obj.items().end(); ++it) {
JSC_JSObjectSetProperty(
ctx,
jsObj,
String(ctx, it->first.asString().c_str()),
fromDynamicInner(ctx, it->second),
kJSPropertyAttributeNone,
nullptr);
}
return jsObj;
}
default:
// Assert not reached
LOG(FATAL) << "Trying to convert a folly object of unsupported type.";
return JSC_JSValueMakeNull(ctx);
}
}
Object Value::asObject() const {
JSValueRef exn;
JSObjectRef jsObj = JSC_JSValueToObject(context(), m_value, &exn);
if (!jsObj) {
throw JSException(m_context, exn, "Failed to convert to object");
}
return Object(context(), jsObj);
}
String Value::toString() const {
JSValueRef exn;
JSStringRef jsStr = JSC_JSValueToStringCopy(context(), m_value, &exn);
if (!jsStr) {
throw JSException(m_context, exn, "Failed to convert to string");
}
return String::adopt(context(), jsStr);
}
Value Value::makeError(JSContextRef ctx, const char *error, const char *stack)
{
auto errorMsg = Value(ctx, String(ctx, error));
JSValueRef args[] = {errorMsg};
if (stack) {
// Using this instead of JSObjectMakeError to actually get a stack property.
// MakeError only sets it stack when returning from the invoked function, so we
// can't extend it here.
auto errorConstructor = Object::getGlobalObject(ctx).getProperty("Error").asObject();
auto jsError = errorConstructor.callAsConstructor({errorMsg});
auto fullStack = std::string(stack) + jsError.getProperty("stack").toString().str();
jsError.setProperty("stack", String(ctx, fullStack.c_str()));
return jsError;
} else {
JSValueRef exn;
JSObjectRef errorObj = JSC_JSObjectMakeError(ctx, 1, args, &exn);
if (!errorObj) {
throw JSException(ctx, exn, "Exception making error");
}
return Value(ctx, errorObj);
}
}
void Value::throwTypeException(const std::string &expectedType) const {
std::string wat("TypeError: Expected ");
wat += expectedType;
wat += ", instead got '";
wat += toString().str();
wat += "'";
throw JSException(wat.c_str());
}
Object::operator Value() const {
return Value(m_context, m_obj);
}
Value Object::callAsFunction(std::initializer_list<JSValueRef> args) const {
return callAsFunction(nullptr, args.size(), args.begin());
}
Value Object::callAsFunction(const Object& thisObj, std::initializer_list<JSValueRef> args) const {
return callAsFunction((JSObjectRef)thisObj, args.size(), args.begin());
}
Value Object::callAsFunction(int nArgs, const JSValueRef args[]) const {
return callAsFunction(nullptr, nArgs, args);
}
Value Object::callAsFunction(const Object& thisObj, int nArgs, const JSValueRef args[]) const {
return callAsFunction(static_cast<JSObjectRef>(thisObj), nArgs, args);
}
Value Object::callAsFunction(JSObjectRef thisObj, int nArgs, const JSValueRef args[]) const {
JSValueRef exn;
JSValueRef result = JSC_JSObjectCallAsFunction(m_context, m_obj, thisObj, nArgs, args, &exn);
if (!result) {
throw JSException(m_context, exn, "Exception calling object as function");
}
return Value(m_context, result);
}
Object Object::callAsConstructor(std::initializer_list<JSValueRef> args) const {
JSValueRef exn;
JSObjectRef result = JSC_JSObjectCallAsConstructor(m_context, m_obj, args.size(), args.begin(), &exn);
if (!result) {
throw JSException(m_context, exn, "Exception calling object as constructor");
}
return Object(m_context, result);
}
Value Object::getProperty(const String& propName) const {
JSValueRef exn;
JSValueRef property = JSC_JSObjectGetProperty(m_context, m_obj, propName, &exn);
if (!property) {
throw JSException(m_context, exn, folly::to<std::string>(
"Failed to get property '", propName.str(), "'").c_str());
}
return Value(m_context, property);
}
Value Object::getPropertyAtIndex(unsigned int index) const {
JSValueRef exn;
JSValueRef property = JSC_JSObjectGetPropertyAtIndex(m_context, m_obj, index, &exn);
if (!property) {
throw JSException(m_context, exn, folly::to<std::string>(
"Failed to get property at index ", index).c_str());
}
return Value(m_context, property);
}
Value Object::getProperty(const char *propName) const {
return getProperty(String(m_context, propName));
}
void Object::setProperty(const String& propName, const Value& value) {
JSValueRef exn = nullptr;
JSC_JSObjectSetProperty(m_context, m_obj, propName, value, kJSPropertyAttributeNone, &exn);
if (exn) {
throw JSException(m_context, exn, folly::to<std::string>(
"Failed to set property '", propName.str(), "'").c_str());
}
}
void Object::setPropertyAtIndex(unsigned int index, const Value& value) {
JSValueRef exn = nullptr;
JSC_JSObjectSetPropertyAtIndex(m_context, m_obj, index, value, &exn);
if (exn) {
throw JSException(m_context, exn, folly::to<std::string>(
"Failed to set property at index ", index).c_str());
}
}
void Object::setProperty(const char *propName, const Value& value) {
setProperty(String(m_context, propName), value);
}
std::vector<String> Object::getPropertyNames() const {
auto namesRef = JSC_JSObjectCopyPropertyNames(m_context, m_obj);
size_t count = JSC_JSPropertyNameArrayGetCount(m_context, namesRef);
std::vector<String> names;
names.reserve(count);
for (size_t i = 0; i < count; i++) {
names.emplace_back(String::ref(m_context,
JSC_JSPropertyNameArrayGetNameAtIndex(m_context, namesRef, i)));
}
JSC_JSPropertyNameArrayRelease(m_context, namesRef);
return names;
}
std::unordered_map<std::string, std::string> Object::toJSONMap() const {
std::unordered_map<std::string, std::string> map;
auto namesRef = JSC_JSObjectCopyPropertyNames(m_context, m_obj);
size_t count = JSC_JSPropertyNameArrayGetCount(m_context, namesRef);
for (size_t i = 0; i < count; i++) {
auto key = String::ref(m_context,
JSC_JSPropertyNameArrayGetNameAtIndex(m_context, namesRef, i));
map.emplace(key.str(), getProperty(key).toJSONString());
}
JSC_JSPropertyNameArrayRelease(m_context, namesRef);
return map;
}
/* static */
Object Object::create(JSContextRef ctx) {
JSObjectRef newObj = JSC_JSObjectMake(
ctx,
NULL, // create instance of default object class
NULL); // no private data
return Object(ctx, newObj);
}
} }

View File

@@ -0,0 +1,370 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <chrono>
#include <memory>
#include <sstream>
#include <unordered_map>
#include <vector>
#include <folly/dynamic.h>
#include <jschelpers/JavaScriptCore.h>
#include <jschelpers/Unicode.h>
#include <jschelpers/noncopyable.h>
#include <privatedata/PrivateDataBase.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
class Value;
// C++ object wrapper for JSStringRef
class String : public noncopyable {
public:
explicit String(): m_context(nullptr), m_string(nullptr) {} // dummy empty constructor
explicit String(JSContextRef context, const char* utf8)
: m_context(context), m_string(JSC_JSStringCreateWithUTF8CString(context, utf8)) {}
String(String&& other) :
m_context(other.m_context), m_string(other.m_string)
{
other.m_string = nullptr;
}
String(const String& other) :
m_context(other.m_context), m_string(other.m_string)
{
if (m_string) {
JSC_JSStringRetain(m_context, m_string);
}
}
~String() {
if (m_string) {
JSC_JSStringRelease(m_context, m_string);
}
}
String& operator=(String&& other) {
if (m_string) {
JSC_JSStringRelease(m_context, m_string);
}
m_context = other.m_context;
m_string = other.m_string;
other.m_string = nullptr;
return *this;
}
operator JSStringRef() const {
return m_string;
}
JSContextRef context() const {
return m_context;
}
// Length in characters
size_t length() const {
return m_string ? JSC_JSStringGetLength(m_context, m_string) : 0;
}
// Length in bytes of a nul-terminated utf8 encoded value
size_t utf8Size() const {
return m_string ? JSC_JSStringGetMaximumUTF8CStringSize(m_context, m_string) : 0;
}
/*
* JavaScriptCore is built with strict utf16 -> utf8 conversion.
* This means if JSC's built-in conversion function encounters a JavaScript
* string which contains half of a 32-bit UTF-16 symbol, it produces an error
* rather than returning a string.
*
* Instead of relying on this, we use our own utf16 -> utf8 conversion function
* which is more lenient and always returns a string. When an invalid UTF-16
* string is provided, it'll likely manifest as a rendering glitch in the app for
* the invalid symbol.
*
* For details on JavaScript's unicode support see:
* https://mathiasbynens.be/notes/javascript-unicode
*/
std::string str() const {
if (!m_string) {
return "";
}
const JSChar* utf16 = JSC_JSStringGetCharactersPtr(m_context, m_string);
size_t stringLength = JSC_JSStringGetLength(m_context, m_string);
return unicode::utf16toUTF8(utf16, stringLength);
}
// Assumes that utf8 is nul-terminated
bool equals(const char* utf8) {
return m_string ? JSC_JSStringIsEqualToUTF8CString(m_context, m_string, utf8) : false;
}
// This assumes ascii is nul-terminated.
static String createExpectingAscii(JSContextRef context, const char* ascii, size_t len) {
#if WITH_FBJSCEXTENSIONS
return String(context, JSC_JSStringCreateWithUTF8CStringExpectAscii(context, ascii, len), true);
#else
return String(context, JSC_JSStringCreateWithUTF8CString(context, ascii), true);
#endif
}
static String createExpectingAscii(JSContextRef context, std::string const &ascii) {
return createExpectingAscii(context, ascii.c_str(), ascii.size());
}
// Creates a String wrapper and increases the refcount of the JSStringRef
static String ref(JSContextRef context, JSStringRef string) {
return String(context, string, false);
}
// Creates a String wrapper that takes over ownership of the string. The
// JSStringRef passed in must previously have been created or retained.
static String adopt(JSContextRef context, JSStringRef string) {
return String(context, string, true);
}
private:
explicit String(JSContextRef context, JSStringRef string, bool adopt) :
m_context(context), m_string(string)
{
if (!adopt && string) {
JSC_JSStringRetain(context, string);
}
}
JSContextRef m_context;
JSStringRef m_string;
};
// C++ object wrapper for JSObjectRef. The underlying JSObjectRef can be
// optionally protected. You must protect the object if it is ever
// heap-allocated, since otherwise you may end up with an invalid reference.
class Object : public noncopyable {
public:
using TimeType = std::chrono::time_point<std::chrono::system_clock>;
Object(JSContextRef context, JSObjectRef obj) :
m_context(context),
m_obj(obj)
{}
Object(Object&& other) :
m_context(other.m_context),
m_obj(other.m_obj),
m_isProtected(other.m_isProtected) {
other.m_obj = nullptr;
other.m_isProtected = false;
}
~Object() {
if (m_isProtected && m_obj) {
JSC_JSValueUnprotect(m_context, m_obj);
}
}
Object& operator=(Object&& other) {
std::swap(m_context, other.m_context);
std::swap(m_obj, other.m_obj);
std::swap(m_isProtected, other.m_isProtected);
return *this;
}
operator JSObjectRef() const {
return m_obj;
}
operator Value() const;
bool isFunction() const {
return JSC_JSObjectIsFunction(m_context, m_obj);
}
Value callAsFunction(std::initializer_list<JSValueRef> args) const;
Value callAsFunction(const Object& thisObj, std::initializer_list<JSValueRef> args) const;
Value callAsFunction(int nArgs, const JSValueRef args[]) const;
Value callAsFunction(const Object& thisObj, int nArgs, const JSValueRef args[]) const;
Object callAsConstructor(std::initializer_list<JSValueRef> args) const;
Value getProperty(const String& propName) const;
Value getProperty(const char *propName) const;
Value getPropertyAtIndex(unsigned int index) const;
void setProperty(const String& propName, const Value& value);
void setProperty(const char *propName, const Value& value);
void setPropertyAtIndex(unsigned int index, const Value& value);
std::vector<String> getPropertyNames() const;
std::unordered_map<std::string, std::string> toJSONMap() const;
void makeProtected() {
if (!m_isProtected && m_obj) {
JSC_JSValueProtect(m_context, m_obj);
m_isProtected = true;
}
}
RN_EXPORT static Object makeArray(JSContextRef ctx, JSValueRef* elements, unsigned length);
RN_EXPORT static Object makeDate(JSContextRef ctx, TimeType time);
template<typename ReturnType>
ReturnType* getPrivate() const {
const bool isCustomJSC = isCustomJSCPtr(m_context);
return PrivateDataBase::cast<ReturnType>(JSC_JSObjectGetPrivate(isCustomJSC, m_obj));
}
void setPrivate(PrivateDataBase* data) const {
const bool isCustomJSC = isCustomJSCPtr(m_context);
JSC_JSObjectSetPrivate(isCustomJSC, m_obj, data);
}
JSContextRef context() const {
return m_context;
}
static Object getGlobalObject(JSContextRef ctx) {
auto globalObj = JSC_JSContextGetGlobalObject(ctx);
return Object(ctx, globalObj);
}
/**
* Creates an instance of the default object class.
*/
static Object create(JSContextRef ctx);
private:
JSContextRef m_context;
JSObjectRef m_obj;
bool m_isProtected = false;
Value callAsFunction(JSObjectRef thisObj, int nArgs, const JSValueRef args[]) const;
};
// C++ object wrapper for JSValueRef. The underlying JSValueRef is not
// protected, so this class should always be used as a stack-allocated
// variable.
class Value : public noncopyable {
public:
RN_EXPORT Value(JSContextRef context, JSValueRef value);
RN_EXPORT Value(JSContextRef context, JSStringRef value);
RN_EXPORT Value(const Value &o) : Value(o.m_context, o.m_value) {}
RN_EXPORT Value(const String &o) : Value(o.context(), o) {}
Value& operator=(Value&& other) {
m_context = other.m_context;
m_value = other.m_value;
other.m_value = NULL;
return *this;
};
operator JSValueRef() const {
return m_value;
}
JSType getType() const {
return JSC_JSValueGetType(m_context, m_value);
}
bool isBoolean() const {
return getType() == kJSTypeBoolean;
}
bool asBoolean() const {
return JSC_JSValueToBoolean(context(), m_value);
}
bool isNumber() const {
return getType() == kJSTypeNumber;
}
bool isNull() const {
return getType() == kJSTypeNull;
}
bool isUndefined() const {
return getType() == kJSTypeUndefined;
}
double asNumber() const {
if (isNumber()) {
return JSC_JSValueToNumber(context(), m_value, nullptr);
} else {
return 0.0f;
}
}
double getNumberOrThrow() const {
if (!isNumber()) {
throwTypeException("Number");
}
return JSC_JSValueToNumber(context(), m_value, nullptr);
}
int32_t asInteger() const {
return static_cast<int32_t>(asNumber());
}
uint32_t asUnsignedInteger() const {
return static_cast<uint32_t>(asNumber());
}
bool isObject() const {
return getType() == kJSTypeObject;
}
RN_EXPORT Object asObject() const;
bool isString() const {
return getType() == kJSTypeString;
}
RN_EXPORT String toString() const;
// Create an error, optionally adding an additional number of lines to the stack.
// Stack must be empty or newline terminated.
RN_EXPORT static Value makeError(JSContextRef ctx, const char *error, const char *stack = nullptr);
static Value makeNumber(JSContextRef ctx, double value) {
return Value(ctx, JSC_JSValueMakeNumber(ctx, value));
}
static Value makeUndefined(JSContextRef ctx) {
return Value(ctx, JSC_JSValueMakeUndefined(ctx));
}
static Value makeNull(JSContextRef ctx) {
return Value(ctx, JSC_JSValueMakeNull(ctx));
}
static Value makeBoolean(JSContextRef ctx, bool value) {
return Value(ctx, JSC_JSValueMakeBoolean(ctx, value));
}
static Value makeString(JSContextRef ctx, const char* utf8) {
return Value(ctx, String(ctx, utf8));
}
RN_EXPORT std::string toJSONString(unsigned indent = 0) const;
RN_EXPORT static Value fromJSON(const String& json);
RN_EXPORT static Value fromDynamic(JSContextRef ctx, const folly::dynamic& value);
RN_EXPORT JSContextRef context() const;
private:
JSContextRef m_context;
JSValueRef m_value;
void throwTypeException(const std::string &expectedType) const;
static JSValueRef fromDynamicInner(JSContextRef ctx, const folly::dynamic& obj);
};
} }

View File

@@ -0,0 +1,12 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
namespace facebook {
namespace react {
struct noncopyable {
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable&) = delete;
protected:
noncopyable() = default;
};
}}

View File

@@ -0,0 +1,153 @@
/**
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <jschelpers/JSCWrapper.h>
#if defined(__APPLE__)
#include <mutex>
#include <objc/runtime.h>
// Crash the app (with a descriptive stack trace) if a function that is not supported by
// the system JSC is called.
#define UNIMPLEMENTED_SYSTEM_JSC_FUNCTION(FUNC_NAME) \
static void Unimplemented_##FUNC_NAME(__unused void* args...) { \
assert(false); \
}
UNIMPLEMENTED_SYSTEM_JSC_FUNCTION(JSEvaluateBytecodeBundle)
#if WITH_FBJSCEXTENSIONS
UNIMPLEMENTED_SYSTEM_JSC_FUNCTION(JSStringCreateWithUTF8CStringExpectAscii)
#endif
UNIMPLEMENTED_SYSTEM_JSC_FUNCTION(JSPokeSamplingProfiler)
UNIMPLEMENTED_SYSTEM_JSC_FUNCTION(JSStartSamplingProfilingOnMainJSCThread)
UNIMPLEMENTED_SYSTEM_JSC_FUNCTION(JSGlobalContextEnableDebugger)
UNIMPLEMENTED_SYSTEM_JSC_FUNCTION(JSGlobalContextDisableDebugger)
UNIMPLEMENTED_SYSTEM_JSC_FUNCTION(configureJSCForIOS)
UNIMPLEMENTED_SYSTEM_JSC_FUNCTION(FBJSContextStartGCTimers)
bool JSSamplingProfilerEnabled() {
return false;
}
const int32_t JSNoBytecodeFileFormatVersion = -1;
namespace facebook {
namespace react {
static JSCWrapper s_systemWrapper = {};
const JSCWrapper* systemJSCWrapper() {
// Note that this is not used on Android. All methods are statically linked instead.
// Some fields are lazily initialized
static std::once_flag flag;
std::call_once(flag, []() {
s_systemWrapper = {
.JSGlobalContextCreateInGroup = JSGlobalContextCreateInGroup,
.JSGlobalContextRelease = JSGlobalContextRelease,
.JSGlobalContextSetName = JSGlobalContextSetName,
.JSContextGetGlobalContext = JSContextGetGlobalContext,
.JSContextGetGlobalObject = JSContextGetGlobalObject,
.FBJSContextStartGCTimers =
(decltype(&FBJSContextStartGCTimers))
Unimplemented_FBJSContextStartGCTimers,
.JSEvaluateScript = JSEvaluateScript,
.JSEvaluateBytecodeBundle =
(decltype(&JSEvaluateBytecodeBundle))
Unimplemented_JSEvaluateBytecodeBundle,
.JSStringCreateWithUTF8CString = JSStringCreateWithUTF8CString,
.JSStringCreateWithCFString = JSStringCreateWithCFString,
#if WITH_FBJSCEXTENSIONS
.JSStringCreateWithUTF8CStringExpectAscii =
(decltype(&JSStringCreateWithUTF8CStringExpectAscii))
Unimplemented_JSStringCreateWithUTF8CStringExpectAscii,
#endif
.JSStringCopyCFString = JSStringCopyCFString,
.JSStringGetCharactersPtr = JSStringGetCharactersPtr,
.JSStringGetLength = JSStringGetLength,
.JSStringGetMaximumUTF8CStringSize = JSStringGetMaximumUTF8CStringSize,
.JSStringIsEqualToUTF8CString = JSStringIsEqualToUTF8CString,
.JSStringRelease = JSStringRelease,
.JSStringRetain = JSStringRetain,
.JSClassCreate = JSClassCreate,
.JSClassRelease = JSClassRelease,
.JSObjectCallAsConstructor = JSObjectCallAsConstructor,
.JSObjectCallAsFunction = JSObjectCallAsFunction,
.JSObjectGetPrivate = JSObjectGetPrivate,
.JSObjectGetProperty = JSObjectGetProperty,
.JSObjectGetPropertyAtIndex = JSObjectGetPropertyAtIndex,
.JSObjectIsConstructor = JSObjectIsConstructor,
.JSObjectIsFunction = JSObjectIsFunction,
.JSObjectMake = JSObjectMake,
.JSObjectMakeArray = JSObjectMakeArray,
.JSObjectMakeDate = JSObjectMakeDate,
.JSObjectMakeError = JSObjectMakeError,
.JSObjectMakeFunctionWithCallback = JSObjectMakeFunctionWithCallback,
.JSObjectSetPrivate = JSObjectSetPrivate,
.JSObjectSetProperty = JSObjectSetProperty,
.JSObjectSetPropertyAtIndex = JSObjectSetPropertyAtIndex,
.JSObjectCopyPropertyNames = JSObjectCopyPropertyNames,
.JSPropertyNameArrayGetCount = JSPropertyNameArrayGetCount,
.JSPropertyNameArrayGetNameAtIndex = JSPropertyNameArrayGetNameAtIndex,
.JSPropertyNameArrayRelease = JSPropertyNameArrayRelease,
.JSValueCreateJSONString = JSValueCreateJSONString,
.JSValueGetType = JSValueGetType,
.JSValueMakeFromJSONString = JSValueMakeFromJSONString,
.JSValueMakeBoolean = JSValueMakeBoolean,
.JSValueMakeNull = JSValueMakeNull,
.JSValueMakeNumber = JSValueMakeNumber,
.JSValueMakeString = JSValueMakeString,
.JSValueMakeUndefined = JSValueMakeUndefined,
.JSValueProtect = JSValueProtect,
.JSValueToBoolean = JSValueToBoolean,
.JSValueToNumber = JSValueToNumber,
.JSValueToObject = JSValueToObject,
.JSValueToStringCopy = JSValueToStringCopy,
.JSValueUnprotect = JSValueUnprotect,
.JSValueIsNull = JSValueIsNull,
.JSSamplingProfilerEnabled = JSSamplingProfilerEnabled,
.JSPokeSamplingProfiler =
(decltype(&JSPokeSamplingProfiler))
Unimplemented_JSPokeSamplingProfiler,
.JSStartSamplingProfilingOnMainJSCThread =
(decltype(&JSStartSamplingProfilingOnMainJSCThread))
Unimplemented_JSStartSamplingProfilingOnMainJSCThread,
.JSGlobalContextEnableDebugger =
(decltype(&JSGlobalContextEnableDebugger))
Unimplemented_JSGlobalContextEnableDebugger,
.JSGlobalContextDisableDebugger =
(decltype(&JSGlobalContextDisableDebugger))
Unimplemented_JSGlobalContextDisableDebugger,
.configureJSCForIOS =
(decltype(&configureJSCForIOS))Unimplemented_configureJSCForIOS,
.JSContext = objc_getClass("JSContext"),
.JSValue = objc_getClass("JSValue"),
.JSBytecodeFileFormatVersion = JSNoBytecodeFileFormatVersion,
};
});
return &s_systemWrapper;
}
} }
#endif

View File

@@ -0,0 +1,18 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := jsinspector
LOCAL_SRC_FILES := \
InspectorInterfaces.cpp
LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_CFLAGS += -Wall -Werror -fexceptions
CXX11_FLAGS := -std=c++11
LOCAL_CFLAGS += $(CXX11_FLAGS)
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
include $(BUILD_SHARED_LIBRARY)

36
node_modules/react-native/ReactCommon/jsinspector/BUCK generated vendored Normal file
View File

@@ -0,0 +1,36 @@
load("//ReactNative:DEFS.bzl", "rn_xplat_cxx_library")
EXPORTED_HEADERS = [
"InspectorInterfaces.h",
]
rn_xplat_cxx_library(
name = "jsinspector",
srcs = glob(
["*.cpp"],
),
headers = subdir_glob(
[
("", "*.h"),
],
excludes = EXPORTED_HEADERS,
prefix = "jsinspector",
),
header_namespace = "",
exported_headers = subdir_glob(
[
("", header)
for header in EXPORTED_HEADERS
],
prefix = "jsinspector",
),
compiler_flags = [
"-Wall",
"-fexceptions",
"-std=c++1y",
],
fbandroid_preferred_linkage = "shared",
visibility = [
"PUBLIC",
],
)

View File

@@ -0,0 +1,101 @@
/**
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "InspectorInterfaces.h"
#include <mutex>
#include <unordered_map>
#include <tuple>
namespace facebook {
namespace react {
// pure destructors in C++ are odd. You would think they don't want an
// implementation, but in fact the linker requires one. Define them to be
// empty so that people don't count on them for any particular behaviour.
IDestructible::~IDestructible() {}
ILocalConnection::~ILocalConnection() {}
IRemoteConnection::~IRemoteConnection() {}
IInspector::~IInspector() {}
namespace {
class InspectorImpl : public IInspector {
public:
int addPage(const std::string& title, const std::string& vm, ConnectFunc connectFunc) override;
void removePage(int pageId) override;
std::vector<InspectorPage> getPages() const override;
std::unique_ptr<ILocalConnection> connect(
int pageId,
std::unique_ptr<IRemoteConnection> remote) override;
private:
mutable std::mutex mutex_;
int nextPageId_{1};
std::unordered_map<int, std::tuple<std::string, std::string>> titles_;
std::unordered_map<int, ConnectFunc> connectFuncs_;
};
int InspectorImpl::addPage(const std::string& title, const std::string& vm, ConnectFunc connectFunc) {
std::lock_guard<std::mutex> lock(mutex_);
int pageId = nextPageId_++;
titles_[pageId] = std::make_tuple(title, vm);
connectFuncs_[pageId] = std::move(connectFunc);
return pageId;
}
void InspectorImpl::removePage(int pageId) {
std::lock_guard<std::mutex> lock(mutex_);
titles_.erase(pageId);
connectFuncs_.erase(pageId);
}
std::vector<InspectorPage> InspectorImpl::getPages() const {
std::lock_guard<std::mutex> lock(mutex_);
std::vector<InspectorPage> inspectorPages;
for (auto& it : titles_) {
inspectorPages.push_back(InspectorPage{it.first, std::get<0>(it.second), std::get<1>(it.second)});
}
return inspectorPages;
}
std::unique_ptr<ILocalConnection> InspectorImpl::connect(
int pageId,
std::unique_ptr<IRemoteConnection> remote) {
IInspector::ConnectFunc connectFunc;
{
std::lock_guard<std::mutex> lock(mutex_);
auto it = connectFuncs_.find(pageId);
if (it != connectFuncs_.end()) {
connectFunc = it->second;
}
}
return connectFunc ? connectFunc(std::move(remote)) : nullptr;
}
} // namespace
IInspector& getInspectorInstance() {
static InspectorImpl instance;
return instance;
}
std::unique_ptr<IInspector> makeTestInspectorInstance() {
return std::make_unique<InspectorImpl>();
}
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <vector>
namespace facebook {
namespace react {
class IDestructible {
public:
virtual ~IDestructible() = 0;
};
struct InspectorPage {
const int id;
const std::string title;
const std::string vm;
};
/// IRemoteConnection allows the VM to send debugger messages to the client.
class IRemoteConnection : public IDestructible {
public:
virtual ~IRemoteConnection() = 0;
virtual void onMessage(std::string message) = 0;
virtual void onDisconnect() = 0;
};
/// ILocalConnection allows the client to send debugger messages to the VM.
class ILocalConnection : public IDestructible {
public:
virtual ~ILocalConnection() = 0;
virtual void sendMessage(std::string message) = 0;
virtual void disconnect() = 0;
};
/// IInspector tracks debuggable JavaScript targets (pages).
class IInspector : public IDestructible {
public:
using ConnectFunc = std::function<std::unique_ptr<ILocalConnection>(
std::unique_ptr<IRemoteConnection>)>;
virtual ~IInspector() = 0;
/// addPage is called by the VM to add a page to the list of debuggable pages.
virtual int addPage(const std::string& title, const std::string& vm, ConnectFunc connectFunc) = 0;
/// removePage is called by the VM to remove a page from the list of
/// debuggable pages.
virtual void removePage(int pageId) = 0;
/// getPages is called by the client to list all debuggable pages.
virtual std::vector<InspectorPage> getPages() const = 0;
/// connect is called by the client to initiate a debugging session on the
/// given page.
virtual std::unique_ptr<ILocalConnection> connect(
int pageId,
std::unique_ptr<IRemoteConnection> remote) = 0;
};
/// getInspectorInstance retrieves the singleton inspector that tracks all
/// debuggable pages in this process.
extern IInspector& getInspectorInstance();
/// makeTestInspectorInstance creates an independent inspector instance that
/// should only be used in tests.
extern std::unique_ptr<IInspector> makeTestInspectorInstance();
} // namespace react
} // namespace facebook

View File

@@ -0,0 +1,26 @@
load("//ReactNative:DEFS.bzl", "cxx_library", "GLOG_DEP")
cxx_library(
name = "microprofiler",
srcs = [
"MicroProfiler.cpp",
],
header_namespace = "microprofiler",
exported_headers = [
"MicroProfiler.h",
],
compiler_flags = [
"-Wall",
"-Werror",
"-std=c++11",
"-fexceptions",
"-fno-data-sections",
],
force_static = True,
visibility = [
"PUBLIC",
],
deps = [
GLOG_DEP,
],
)

View File

@@ -0,0 +1,224 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <algorithm>
#include <chrono>
#include <mutex>
#include <vector>
#include <time.h>
#include <glog/logging.h>
#include "MicroProfiler.h"
// iOS doesn't support 'thread_local'. If we reimplement this to use pthread_setspecific
// we can get rid of this
#if defined(__APPLE__)
#define MICRO_PROFILER_STUB_IMPLEMENTATION 1
#else
#define MICRO_PROFILER_STUB_IMPLEMENTATION 0
#endif
namespace facebook {
namespace react {
#if !MICRO_PROFILER_STUB_IMPLEMENTATION
struct TraceData {
TraceData();
~TraceData();
void addTime(MicroProfilerName name, uint_fast64_t time, uint_fast32_t internalClockCalls);
std::thread::id threadId_;
uint_fast64_t startTime_;
std::atomic_uint_fast64_t times_[MicroProfilerName::__LENGTH__] = {};
std::atomic_uint_fast32_t calls_[MicroProfilerName::__LENGTH__] = {};
std::atomic_uint_fast32_t childProfileSections_[MicroProfilerName::__LENGTH__] = {};
};
struct ProfilingImpl {
std::mutex mutex_;
std::vector<TraceData*> allTraceData_;
bool isProfiling_ = false;
uint_fast64_t startTime_;
uint_fast64_t endTime_;
uint_fast64_t clockOverhead_;
uint_fast64_t profileSectionOverhead_;
};
static ProfilingImpl profiling;
thread_local TraceData myTraceData;
thread_local uint_fast32_t profileSections = 0;
static uint_fast64_t nowNs() {
struct timespec time;
clock_gettime(CLOCK_REALTIME, &time);
return uint_fast64_t(1000000000) * time.tv_sec + time.tv_nsec;
}
static uint_fast64_t diffNs(uint_fast64_t start, uint_fast64_t end) {
return end - start;
}
static std::string formatTimeNs(uint_fast64_t timeNs) {
std::ostringstream out;
out.precision(2);
if (timeNs < 1000) {
out << timeNs << "ns";
} else if (timeNs < 1000000) {
out << timeNs / 1000.0 << "us";
} else {
out << std::fixed << timeNs / 1000000.0 << "ms";
}
return out.str();
}
MicroProfilerSection::MicroProfilerSection(MicroProfilerName name) :
isProfiling_(profiling.isProfiling_),
name_(name),
startNumProfileSections_(profileSections) {
if (!isProfiling_) {
return;
}
profileSections++;
startTime_ = nowNs();
}
MicroProfilerSection::~MicroProfilerSection() {
if (!isProfiling_ || !profiling.isProfiling_) {
return;
}
auto endTime = nowNs();
auto endNumProfileSections = profileSections;
myTraceData.addTime(name_, endTime - startTime_, endNumProfileSections - startNumProfileSections_ - 1);
}
TraceData::TraceData() :
threadId_(std::this_thread::get_id()) {
std::lock_guard<std::mutex> lock(profiling.mutex_);
profiling.allTraceData_.push_back(this);
}
TraceData::~TraceData() {
std::lock_guard<std::mutex> lock(profiling.mutex_);
auto& infos = profiling.allTraceData_;
infos.erase(std::remove(infos.begin(), infos.end(), this), infos.end());
}
void TraceData::addTime(MicroProfilerName name, uint_fast64_t time, uint_fast32_t childprofileSections) {
times_[name] += time;
calls_[name]++;
childProfileSections_[name] += childprofileSections;
}
static void printReport() {
LOG(ERROR) << "======= MICRO PROFILER REPORT =======";
LOG(ERROR) << "- Total Time: " << formatTimeNs(diffNs(profiling.startTime_, profiling.endTime_));
LOG(ERROR) << "- Clock Overhead: " << formatTimeNs(profiling.clockOverhead_);
LOG(ERROR) << "- Profiler Section Overhead: " << formatTimeNs(profiling.profileSectionOverhead_);
for (auto info : profiling.allTraceData_) {
LOG(ERROR) << "--- Thread ID 0x" << std::hex << info->threadId_ << " ---";
for (int i = 0; i < MicroProfilerName::__LENGTH__; i++) {
if (info->times_[i] > 0) {
auto totalTime = info->times_[i].load();
auto calls = info->calls_[i].load();
auto clockOverhead = profiling.clockOverhead_ * calls + profiling.profileSectionOverhead_ * info->childProfileSections_[i].load();
if (totalTime < clockOverhead) {
LOG(ERROR) << "- " << MicroProfiler::profilingNameToString(static_cast<MicroProfilerName>(i)) << ": "
<< "ERROR: Total time was " << totalTime << "ns but clock overhead was calculated to be " << clockOverhead << "ns!";
} else {
auto correctedTime = totalTime - clockOverhead;
auto timePerCall = correctedTime / calls;
LOG(ERROR) << "- " << MicroProfiler::profilingNameToString(static_cast<MicroProfilerName>(i)) << ": "
<< formatTimeNs(correctedTime) << " (" << calls << " calls, " << formatTimeNs(timePerCall) << "/call)";
}
}
}
}
}
static void clearProfiling() {
CHECK(!profiling.isProfiling_) << "Trying to clear profiling but profiling was already started!";
for (auto info : profiling.allTraceData_) {
for (unsigned int i = 0; i < MicroProfilerName::__LENGTH__; i++) {
info->times_[i] = 0;
info->calls_[i] = 0;
info->childProfileSections_[i] = 0;
}
}
}
static uint_fast64_t calculateClockOverhead() {
int numCalls = 1000000;
uint_fast64_t start = nowNs();
for (int i = 0; i < numCalls; i++) {
nowNs();
}
uint_fast64_t end = nowNs();
return (end - start) / numCalls;
}
static uint_fast64_t calculateProfileSectionOverhead() {
int numCalls = 1000000;
uint_fast64_t start = nowNs();
profiling.isProfiling_ = true;
for (int i = 0; i < numCalls; i++) {
MICRO_PROFILER_SECTION(static_cast<MicroProfilerName>(0));
}
uint_fast64_t end = nowNs();
profiling.isProfiling_ = false;
return (end - start) / numCalls;
}
void MicroProfiler::startProfiling() {
CHECK(!profiling.isProfiling_) << "Trying to start profiling but profiling was already started!";
profiling.clockOverhead_ = calculateClockOverhead();
profiling.profileSectionOverhead_ = calculateProfileSectionOverhead();
std::lock_guard<std::mutex> lock(profiling.mutex_);
clearProfiling();
profiling.startTime_ = nowNs();
profiling.isProfiling_ = true;
}
void MicroProfiler::stopProfiling() {
CHECK(profiling.isProfiling_) << "Trying to stop profiling but profiling hasn't been started!";
profiling.isProfiling_ = false;
profiling.endTime_ = nowNs();
std::lock_guard<std::mutex> lock(profiling.mutex_);
printReport();
clearProfiling();
}
bool MicroProfiler::isProfiling() {
return profiling.isProfiling_;
}
void MicroProfiler::runInternalBenchmark() {
MicroProfiler::startProfiling();
for (int i = 0; i < 1000000; i++) {
MICRO_PROFILER_SECTION_NAMED(outer, __INTERNAL_BENCHMARK_OUTER);
{
MICRO_PROFILER_SECTION_NAMED(inner, __INTERNAL_BENCHMARK_INNER);
}
}
MicroProfiler::stopProfiling();
}
#else
void MicroProfiler::startProfiling() {
CHECK(false) << "This platform has a stub implementation of the micro profiler and cannot collect traces";
}
void MicroProfiler::stopProfiling() {
}
bool MicroProfiler::isProfiling() {
return false;
}
void MicroProfiler::runInternalBenchmark() {
}
#endif
} }

View File

@@ -0,0 +1,79 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <atomic>
#include <thread>
// #define WITH_MICRO_PROFILER 1
#ifdef WITH_MICRO_PROFILER
#define MICRO_PROFILER_SECTION(name) MicroProfilerSection __b(name)
#define MICRO_PROFILER_SECTION_NAMED(var_name, name) MicroProfilerSection var_name(name)
#else
#define MICRO_PROFILER_SECTION(name)
#define MICRO_PROFILER_SECTION_NAMED(var_name, name)
#endif
namespace facebook {
namespace react {
enum MicroProfilerName {
__INTERNAL_BENCHMARK_INNER,
__INTERNAL_BENCHMARK_OUTER,
__LENGTH__,
};
/**
* MicroProfiler is a performance profiler for measuring the cumulative impact of
* a large number of small-ish calls. This is normally a problem for standard profilers
* like Systrace because the overhead of the profiler itself skews the timings you
* are able to collect. This is especially a problem when doing nested calls to
* profiled functions, as the parent calls will contain the overhead of their profiling
* plus the overhead of all their childrens' profiling.
*
* MicroProfiler attempts to be low overhead by 1) aggregating timings in memory and
* 2) trying to remove estimated profiling overhead from the returned timings.
*
* To remove estimated overhead, at the beginning of each trace we calculate the
* average cost of profiling a no-op code section, as well as invoking the average
* cost of invoking the system clock. The former is subtracted out for each child
* profiler section that is invoked within a parent profiler section. The latter is
* subtracted from each section, child or not.
*
* After MicroProfiler::stopProfiling() is called, a table of tracing data is emitted
* to glog (which shows up in logcat on Android).
*/
struct MicroProfiler {
static const char* profilingNameToString(MicroProfilerName name) {
switch (name) {
case __INTERNAL_BENCHMARK_INNER:
return "__INTERNAL_BENCHMARK_INNER";
case __INTERNAL_BENCHMARK_OUTER:
return "__INTERNAL_BENCHMARK_OUTER";
case __LENGTH__:
throw std::runtime_error("__LENGTH__ has no name");
default:
throw std::runtime_error("Trying to convert unknown MicroProfilerName to string");
}
}
static void startProfiling();
static void stopProfiling();
static bool isProfiling();
static void runInternalBenchmark();
};
class MicroProfilerSection {
public:
MicroProfilerSection(MicroProfilerName name);
~MicroProfilerSection();
private:
bool isProfiling_;
MicroProfilerName name_;
uint_fast64_t startTime_;
uint_fast32_t startNumProfileSections_;
};
} }

View File

@@ -0,0 +1,21 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := privatedata
LOCAL_SRC_FILES := \
PrivateDataBase.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)
include $(BUILD_SHARED_LIBRARY)

23
node_modules/react-native/ReactCommon/privatedata/BUCK generated vendored Normal file
View File

@@ -0,0 +1,23 @@
load("//ReactNative:DEFS.bzl", "rn_xplat_cxx_library")
rn_xplat_cxx_library(
name = "privatedata",
srcs = glob(["**/*.cpp"]),
header_namespace = "",
exported_headers = subdir_glob(
[
("", "**/*.h"),
],
prefix = "privatedata",
),
compiler_flags = [
"-Wall",
"-fexceptions",
"-frtti",
"-fvisibility=hidden",
"-std=c++1y",
],
visibility = [
"PUBLIC",
],
)

View File

@@ -0,0 +1,10 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "PrivateDataBase.h"
namespace facebook {
namespace react {
PrivateDataBase::~PrivateDataBase() {}
} }

View File

@@ -0,0 +1,42 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <cassert>
#include <cstdlib>
#include <type_traits>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook {
namespace react {
// Base class for private data used to implement hybrid JS-native objects. A common root class,
// rtti and dynamic_cast allow us to do some runtime type checking that makes it possible
// for multiple hybrid object implementations to co-exist.
class RN_EXPORT PrivateDataBase {
public:
virtual ~PrivateDataBase();
// Casts given void* to PrivateDataBase and performs dynamic_cast to desired type. Returns null on
// failure.
template <typename T>
static typename std::enable_if<std::is_base_of<PrivateDataBase, T>::value, T>::type* tryCast(void* ptr) {
return dynamic_cast<T*>(reinterpret_cast<PrivateDataBase*>(ptr));
}
// Like tryCast, but aborts on failure.
template <typename T>
static typename std::enable_if<std::is_base_of<PrivateDataBase, T>::value, T>::type* cast(void* ptr) {
auto result = tryCast<T>(ptr);
if (!result) {
assert(false && "could not cast to desired type");
abort();
}
return result;
}
};
} }

14
node_modules/react-native/ReactCommon/yoga/Android.mk generated vendored Normal file
View File

@@ -0,0 +1,14 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := yogacore
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/yoga/*.cpp)
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_CFLAGS := -Wall -Werror -fexceptions -frtti -std=c++1y -O3
include $(BUILD_STATIC_LIBRARY)

20
node_modules/react-native/ReactCommon/yoga/BUCK generated vendored Normal file
View File

@@ -0,0 +1,20 @@
load("//ReactNative:DEFS.bzl", "rn_xplat_cxx_library")
rn_xplat_cxx_library(
name = "yoga",
srcs = glob(["yoga/*.cpp"]),
header_namespace = "",
exported_headers = glob(["yoga/*.h"]),
compiler_flags = [
"-fno-omit-frame-pointer",
"-fexceptions",
"-Wall",
"-Werror",
"-std=c++1y",
"-O3",
],
force_static = True,
visibility = ["PUBLIC"],
deps = [
],
)

View File

@@ -0,0 +1,44 @@
package = JSON.parse(File.read(File.expand_path('../../package.json', __dir__)))
version = package['version']
source = { :git => ENV['INSTALL_YOGA_FROM_LOCATION'] || 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip
else
source[:tag] = "v#{version}"
end
Pod::Spec.new do |spec|
spec.name = 'yoga'
spec.version = "#{version}.React"
spec.license = { :type => 'MIT' }
spec.homepage = 'https://facebook.github.io/yoga/'
spec.documentation_url = 'https://facebook.github.io/yoga/docs/api/c/'
spec.summary = 'Yoga is a cross-platform layout engine which implements Flexbox.'
spec.description = 'Yoga is a cross-platform layout engine enabling maximum collaboration within your team by implementing an API many designers are familiar with, and opening it up to developers across different platforms.'
spec.authors = 'Facebook'
spec.source = source
spec.module_name = 'yoga'
spec.requires_arc = false
spec.compiler_flags = [
'-fno-omit-frame-pointer',
'-fexceptions',
'-Wall',
'-Werror',
'-std=c++1y',
'-fPIC'
]
# Pinning to the same version as React.podspec.
spec.platforms = { :ios => "8.0", :tvos => "9.2" }
# Set this environment variable when not using the `:path` option to install the pod.
# E.g. when publishing this spec to a spec repo.
source_files = 'yoga/**/*.{cpp,h}'
source_files = File.join('ReactCommon/yoga', source_files) if ENV['INSTALL_YOGA_WITHOUT_PATH_OPTION']
spec.source_files = source_files
end

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "Utils.h"
YGFlexDirection YGFlexDirectionCross(
const YGFlexDirection flexDirection,
const YGDirection direction) {
return YGFlexDirectionIsColumn(flexDirection)
? YGResolveFlexDirection(YGFlexDirectionRow, direction)
: YGFlexDirectionColumn;
}
float YGFloatMax(const float a, const float b) {
if (!YGFloatIsUndefined(a) && !YGFloatIsUndefined(b)) {
return fmaxf(a, b);
}
return YGFloatIsUndefined(a) ? b : a;
}
float YGFloatMin(const float a, const float b) {
if (!YGFloatIsUndefined(a) && !YGFloatIsUndefined(b)) {
return fminf(a, b);
}
return YGFloatIsUndefined(a) ? b : a;
}
bool YGValueEqual(const YGValue a, const YGValue b) {
if (a.unit != b.unit) {
return false;
}
if (a.unit == YGUnitUndefined ||
(YGFloatIsUndefined(a.value) && YGFloatIsUndefined(b.value))) {
return true;
}
return fabs(a.value - b.value) < 0.0001f;
}
bool YGFloatsEqual(const float a, const float b) {
if (!YGFloatIsUndefined(a) && !YGFloatIsUndefined(b)) {
return fabs(a - b) < 0.0001f;
}
return YGFloatIsUndefined(a) && YGFloatIsUndefined(b);
}

Some files were not shown because too many files have changed in this diff Show More