mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-03-29 07:48:17 +08:00
Move more new bridge code into OSS
Reviewed By: mhorowitz Differential Revision: D3296231 fbshipit-source-id: 5a05b1ddacdfecda067a08398dd5652df76c1f14
This commit is contained in:
committed by
Facebook Github Bot 2
parent
5e91a2a312
commit
830197847a
48
ReactAndroid/src/main/jni/xreact/jni/BUCK
Normal file
48
ReactAndroid/src/main/jni/xreact/jni/BUCK
Normal file
@@ -0,0 +1,48 @@
|
||||
include_defs('//ReactAndroid/DEFS')
|
||||
|
||||
# We depend on JSC, support the same platforms
|
||||
SUPPORTED_PLATFORMS = '^android-(armv7|x86)$'
|
||||
|
||||
EXPORTED_HEADERS = [
|
||||
'CxxModuleWrapper.h',
|
||||
]
|
||||
|
||||
cxx_library(
|
||||
name='jni',
|
||||
soname = 'libreactnativejnifb.so',
|
||||
header_namespace = 'react/jni',
|
||||
supported_platforms_regex = SUPPORTED_PLATFORMS,
|
||||
deps = JSC_DEPS + [
|
||||
'//native/fb:fb',
|
||||
'//native/third-party/android-ndk:android',
|
||||
'//xplat/folly:molly',
|
||||
'//xplat/fbsystrace:fbsystrace',
|
||||
'//xplat/react/module:module',
|
||||
react_native_target('jni/react/jni:jni'),
|
||||
react_native_xplat_target('bridge:bridge'),
|
||||
],
|
||||
srcs = glob(['*.cpp']),
|
||||
exported_headers = EXPORTED_HEADERS,
|
||||
headers = glob(['*.h'], excludes=EXPORTED_HEADERS),
|
||||
preprocessor_flags = [
|
||||
'-DLOG_TAG="ReactNativeJNI"',
|
||||
'-DWITH_FBSYSTRACE=1',
|
||||
],
|
||||
compiler_flags = [
|
||||
'-Wall',
|
||||
'-Werror',
|
||||
'-fexceptions',
|
||||
'-std=c++11',
|
||||
'-fvisibility=hidden',
|
||||
'-frtti',
|
||||
'-Wno-pessimizing-move',
|
||||
'-Wno-inconsistent-missing-override',
|
||||
],
|
||||
visibility = [
|
||||
'PUBLIC',
|
||||
],
|
||||
)
|
||||
|
||||
project_config(
|
||||
src_target = ':jni',
|
||||
)
|
||||
228
ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp
Normal file
228
ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "CatalystInstanceImpl.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include <folly/dynamic.h>
|
||||
#include <folly/Memory.h>
|
||||
|
||||
#include <fb/log.h>
|
||||
|
||||
#include <jni/Countable.h>
|
||||
#include <jni/LocalReference.h>
|
||||
|
||||
#include <react/jni/NativeArray.h>
|
||||
|
||||
#include <cxxreact/Instance.h>
|
||||
#include <cxxreact/MethodCall.h>
|
||||
#include <cxxreact/ModuleRegistry.h>
|
||||
|
||||
#include "JSLoader.h"
|
||||
#include "JavaScriptExecutorHolder.h"
|
||||
#include "JniJSModulesUnbundle.h"
|
||||
#include "ModuleRegistryHolder.h"
|
||||
#include "JNativeRunnable.h"
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
class Exception : public jni::JavaClass<Exception> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/Exception;";
|
||||
};
|
||||
|
||||
class JInstanceCallback : public InstanceCallback {
|
||||
public:
|
||||
explicit JInstanceCallback(alias_ref<ReactCallback::javaobject> jobj)
|
||||
: jobj_(make_global(jobj)) {}
|
||||
|
||||
void onBatchComplete() override {
|
||||
static auto method =
|
||||
ReactCallback::javaClassStatic()->getMethod<void()>("onBatchComplete");
|
||||
method(jobj_);
|
||||
}
|
||||
|
||||
void incrementPendingJSCalls() override {
|
||||
static auto method =
|
||||
ReactCallback::javaClassStatic()->getMethod<void()>("incrementPendingJSCalls");
|
||||
method(jobj_);
|
||||
}
|
||||
|
||||
void decrementPendingJSCalls() override {
|
||||
static auto method =
|
||||
ReactCallback::javaClassStatic()->getMethod<void()>("decrementPendingJSCalls");
|
||||
method(jobj_);
|
||||
}
|
||||
|
||||
void onNativeException(const std::string& what) override {
|
||||
static auto exCtor =
|
||||
Exception::javaClassStatic()->getConstructor<Exception::javaobject(jstring)>();
|
||||
static auto method =
|
||||
ReactCallback::javaClassStatic()->getMethod<void(Exception::javaobject)>("onNativeException");
|
||||
|
||||
method(jobj_, Exception::javaClassStatic()->newObject(
|
||||
exCtor, jni::make_jstring(what).get()).get());
|
||||
}
|
||||
|
||||
ExecutorToken createExecutorToken() override {
|
||||
auto jobj = JExecutorToken::newObjectCxxArgs();
|
||||
return jobj->cthis()->getExecutorToken(jobj);
|
||||
}
|
||||
|
||||
void onExecutorStopped(ExecutorToken) override {
|
||||
// TODO(cjhopman): implement this.
|
||||
}
|
||||
|
||||
private:
|
||||
global_ref<ReactCallback::javaobject> jobj_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
jni::local_ref<CatalystInstanceImpl::jhybriddata> CatalystInstanceImpl::initHybrid(
|
||||
jni::alias_ref<jclass>) {
|
||||
return makeCxxInstance();
|
||||
}
|
||||
|
||||
CatalystInstanceImpl::CatalystInstanceImpl()
|
||||
: instance_(folly::make_unique<Instance>()) {}
|
||||
|
||||
void CatalystInstanceImpl::registerNatives() {
|
||||
registerHybrid({
|
||||
makeNativeMethod("initHybrid", CatalystInstanceImpl::initHybrid),
|
||||
makeNativeMethod("initializeBridge", CatalystInstanceImpl::initializeBridge),
|
||||
makeNativeMethod("loadScriptFromAssets",
|
||||
"(Landroid/content/res/AssetManager;Ljava/lang/String;)V",
|
||||
CatalystInstanceImpl::loadScriptFromAssets),
|
||||
makeNativeMethod("loadScriptFromFile", CatalystInstanceImpl::loadScriptFromFile),
|
||||
makeNativeMethod("callJSFunction", CatalystInstanceImpl::callJSFunction),
|
||||
makeNativeMethod("callJSCallback", CatalystInstanceImpl::callJSCallback),
|
||||
makeNativeMethod("getMainExecutorToken", CatalystInstanceImpl::getMainExecutorToken),
|
||||
makeNativeMethod("setGlobalVariable", CatalystInstanceImpl::setGlobalVariable),
|
||||
makeNativeMethod("supportsProfiling", CatalystInstanceImpl::supportsProfiling),
|
||||
makeNativeMethod("startProfiler", CatalystInstanceImpl::startProfiler),
|
||||
makeNativeMethod("stopProfiler", CatalystInstanceImpl::stopProfiler),
|
||||
});
|
||||
|
||||
JNativeRunnable::registerNatives();
|
||||
}
|
||||
|
||||
void CatalystInstanceImpl::initializeBridge(
|
||||
jni::alias_ref<ReactCallback::javaobject> callback,
|
||||
// This executor is actually a factory holder.
|
||||
JavaScriptExecutorHolder* jseh,
|
||||
jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
|
||||
jni::alias_ref<JavaMessageQueueThread::javaobject> moduleQueue,
|
||||
ModuleRegistryHolder* mrh) {
|
||||
// TODO mhorowitz: how to assert here?
|
||||
// Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");
|
||||
|
||||
// This used to be:
|
||||
//
|
||||
// Java CatalystInstanceImpl -> C++ CatalystInstanceImpl -> Bridge -> Bridge::Callback
|
||||
// --weak--> ReactCallback -> Java CatalystInstanceImpl
|
||||
//
|
||||
// Now the weak ref is a global ref. So breaking the loop depends on
|
||||
// CatalystInstanceImpl#destroy() calling mHybridData.resetNative(), which
|
||||
// should cause all the C++ pointers to be cleaned up (except C++
|
||||
// CatalystInstanceImpl might be kept alive for a short time by running
|
||||
// callbacks). This also means that all native calls need to be pre-checked
|
||||
// to avoid NPE.
|
||||
|
||||
// See the comment in callJSFunction. Once js calls switch to strings, we
|
||||
// don't need jsModuleDescriptions any more, all the way up and down the
|
||||
// stack.
|
||||
|
||||
instance_->initializeBridge(folly::make_unique<JInstanceCallback>(callback),
|
||||
jseh->getExecutorFactory(),
|
||||
folly::make_unique<JMessageQueueThread>(jsQueue),
|
||||
folly::make_unique<JMessageQueueThread>(moduleQueue),
|
||||
mrh->getModuleRegistry());
|
||||
}
|
||||
|
||||
void CatalystInstanceImpl::loadScriptFromAssets(jobject assetManager,
|
||||
const std::string& assetURL) {
|
||||
const int kAssetsLength = 9; // strlen("assets://");
|
||||
auto sourceURL = assetURL.substr(kAssetsLength);
|
||||
|
||||
auto manager = react::extractAssetManager(assetManager);
|
||||
auto script = react::loadScriptFromAssets(manager, sourceURL);
|
||||
if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {
|
||||
instance_->loadUnbundle(
|
||||
folly::make_unique<JniJSModulesUnbundle>(manager, sourceURL),
|
||||
std::move(script),
|
||||
sourceURL);
|
||||
} else {
|
||||
instance_->loadScriptFromString(std::move(script), std::move(sourceURL));
|
||||
}
|
||||
}
|
||||
|
||||
void CatalystInstanceImpl::loadScriptFromFile(jni::alias_ref<jstring> fileName,
|
||||
const std::string& sourceURL) {
|
||||
return instance_->loadScriptFromFile(fileName ? fileName->toStdString() : "",
|
||||
sourceURL);
|
||||
}
|
||||
|
||||
void CatalystInstanceImpl::callJSFunction(
|
||||
JExecutorToken* token, std::string module, std::string method, NativeArray* arguments,
|
||||
const std::string& tracingName) {
|
||||
// We want to share the C++ code, and on iOS, modules pass module/method
|
||||
// names as strings all the way through to JS, and there's no way to do
|
||||
// string -> id mapping on the objc side. So on Android, we convert the
|
||||
// number to a string, here which gets passed as-is to JS. There, they they
|
||||
// used as ids if isFinite(), which handles this case, and looked up as
|
||||
// strings otherwise. Eventually, we'll probably want to modify the stack
|
||||
// from the JS proxy through here to use strings, too.
|
||||
instance_->callJSFunction(token->getExecutorToken(nullptr),
|
||||
module,
|
||||
method,
|
||||
std::move(arguments->array),
|
||||
tracingName);
|
||||
}
|
||||
|
||||
void CatalystInstanceImpl::callJSCallback(JExecutorToken* token, jint callbackId, NativeArray* arguments) {
|
||||
instance_->callJSCallback(token->getExecutorToken(nullptr), callbackId, std::move(arguments->array));
|
||||
}
|
||||
|
||||
local_ref<JExecutorToken::JavaPart> CatalystInstanceImpl::getMainExecutorToken() {
|
||||
return JExecutorToken::extractJavaPartFromToken(instance_->getMainExecutorToken());
|
||||
}
|
||||
|
||||
void CatalystInstanceImpl::setGlobalVariable(std::string propName,
|
||||
std::string&& jsonValue) {
|
||||
// This is only ever called from Java with short strings, and only
|
||||
// for testing, so no need to try hard for zero-copy here.
|
||||
|
||||
instance_->setGlobalVariable(std::move(propName),
|
||||
folly::make_unique<JSBigStdString>(std::move(jsonValue)));
|
||||
}
|
||||
|
||||
jboolean CatalystInstanceImpl::supportsProfiling() {
|
||||
if (!instance_) {
|
||||
return false;
|
||||
}
|
||||
return instance_->supportsProfiling();
|
||||
}
|
||||
|
||||
void CatalystInstanceImpl::startProfiler(const std::string& title) {
|
||||
if (!instance_) {
|
||||
return;
|
||||
}
|
||||
return instance_->startProfiler(title);
|
||||
}
|
||||
|
||||
void CatalystInstanceImpl::stopProfiler(const std::string& title, const std::string& filename) {
|
||||
if (!instance_) {
|
||||
return;
|
||||
}
|
||||
return instance_->stopProfiler(title, filename);
|
||||
}
|
||||
|
||||
}}
|
||||
67
ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h
Normal file
67
ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <folly/Memory.h>
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
|
||||
#include "JMessageQueueThread.h"
|
||||
#include "JExecutorToken.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class Instance;
|
||||
class JavaScriptExecutorHolder;
|
||||
class ModuleRegistryHolder;
|
||||
class NativeArray;
|
||||
|
||||
struct ReactCallback : public jni::JavaClass<ReactCallback> {
|
||||
static constexpr auto kJavaDescriptor =
|
||||
"Lcom/facebook/react/cxxbridge/ReactCallback;";
|
||||
};
|
||||
|
||||
class CatalystInstanceImpl : public jni::HybridClass<CatalystInstanceImpl> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor =
|
||||
"Lcom/facebook/react/cxxbridge/CatalystInstanceImpl;";
|
||||
|
||||
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>);
|
||||
|
||||
static void registerNatives();
|
||||
|
||||
std::shared_ptr<Instance> getInstance() {
|
||||
return instance_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend HybridBase;
|
||||
|
||||
CatalystInstanceImpl();
|
||||
|
||||
void initializeBridge(
|
||||
jni::alias_ref<ReactCallback::javaobject> callback,
|
||||
// This executor is actually a factory holder.
|
||||
JavaScriptExecutorHolder* jseh,
|
||||
jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
|
||||
jni::alias_ref<JavaMessageQueueThread::javaobject> moduleQueue,
|
||||
ModuleRegistryHolder* mrh);
|
||||
void loadScriptFromAssets(jobject assetManager, const std::string& assetURL);
|
||||
void loadScriptFromFile(jni::alias_ref<jstring> fileName, const std::string& sourceURL);
|
||||
void callJSFunction(JExecutorToken* token, std::string module, std::string method, NativeArray* arguments,
|
||||
const std::string& tracingName);
|
||||
void callJSCallback(JExecutorToken* token, jint callbackId, NativeArray* arguments);
|
||||
local_ref<JExecutorToken::JavaPart> getMainExecutorToken();
|
||||
void setGlobalVariable(std::string propName,
|
||||
std::string&& jsonValue);
|
||||
jboolean supportsProfiling();
|
||||
void startProfiler(const std::string& title);
|
||||
void stopProfiler(const std::string& title, const std::string& filename);
|
||||
|
||||
// This should be the only long-lived strong reference, but every C++ class
|
||||
// will have a weak reference.
|
||||
std::shared_ptr<Instance> instance_;
|
||||
};
|
||||
|
||||
}}
|
||||
219
ReactAndroid/src/main/jni/xreact/jni/CxxModuleWrapper.cpp
Normal file
219
ReactAndroid/src/main/jni/xreact/jni/CxxModuleWrapper.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "CxxModuleWrapper.h"
|
||||
|
||||
#include <react/jni/ReadableNativeArray.h>
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fb/Environment.h>
|
||||
#include <jni/LocalString.h>
|
||||
#include <jni/Registration.h>
|
||||
|
||||
#include <Module/JsArgumentHelpers.h>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#include <folly/json.h>
|
||||
#include <folly/ScopeGuard.h>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <dlfcn.h>
|
||||
|
||||
using namespace facebook::jni;
|
||||
using namespace facebook::xplat::module;
|
||||
using namespace facebook::react;
|
||||
|
||||
namespace {
|
||||
|
||||
class ExecutorToken : public HybridClass<ExecutorToken> {
|
||||
public:
|
||||
constexpr static const char *const kJavaDescriptor = "Lcom/facebook/react/bridge/ExecutorToken;";
|
||||
};
|
||||
|
||||
class CxxMethodWrapper : public HybridClass<CxxMethodWrapper> {
|
||||
public:
|
||||
constexpr static const char *const kJavaDescriptor =
|
||||
"Lcom/facebook/react/cxxbridge/CxxModuleWrapper$MethodWrapper;";
|
||||
|
||||
static local_ref<jhybriddata> initHybrid(alias_ref<jhybridobject>) {
|
||||
return makeCxxInstance();
|
||||
}
|
||||
|
||||
static void registerNatives() {
|
||||
registerHybrid({
|
||||
makeNativeMethod("initHybrid", CxxMethodWrapper::initHybrid),
|
||||
makeNativeMethod("invoke",
|
||||
"(Lcom/facebook/react/bridge/CatalystInstance;Lcom/facebook/react/bridge/ExecutorToken;Lcom/facebook/react/bridge/ReadableNativeArray;)V",
|
||||
CxxMethodWrapper::invoke),
|
||||
});
|
||||
}
|
||||
|
||||
void invoke(jobject catalystinstance, ExecutorToken::jhybridobject executorToken, NativeArray* args);
|
||||
|
||||
const CxxModule::Method* method_;
|
||||
};
|
||||
|
||||
void CxxMethodWrapper::invoke(jobject jCatalystInstance, ExecutorToken::jhybridobject jExecutorToken, NativeArray* arguments) {
|
||||
CxxModule::Callback first;
|
||||
CxxModule::Callback second;
|
||||
|
||||
if (method_->callbacks >= 1) {
|
||||
auto catalystInstance = make_global(adopt_local(jCatalystInstance));
|
||||
global_ref<ExecutorToken::jhybridobject> executorToken = make_global(jExecutorToken);
|
||||
// TODO(10184774): Support ExecutorToken in CxxModules
|
||||
static auto sCatalystInstanceInvokeCallback =
|
||||
catalystInstance->getClass()->getMethod<void(ExecutorToken::jhybridobject, jint, NativeArray::jhybridobject)>(
|
||||
"invokeCallback");
|
||||
|
||||
int id1;
|
||||
|
||||
if (!arguments->array[arguments->array.size() - 1].isInt()) {
|
||||
throwNewJavaException(gJavaLangIllegalArgumentException,
|
||||
"Expected callback as last argument");
|
||||
}
|
||||
|
||||
if (method_->callbacks == 2) {
|
||||
if (!arguments->array[arguments->array.size() - 2].isInt()) {
|
||||
throwNewJavaException(gJavaLangIllegalArgumentException,
|
||||
"Expected callback as penultimate argument");
|
||||
return;
|
||||
}
|
||||
|
||||
id1 = arguments->array[arguments->array.size() - 2].getInt();
|
||||
int id2 = arguments->array[arguments->array.size() - 1].getInt();
|
||||
second = [catalystInstance, executorToken, id2](std::vector<folly::dynamic> args) mutable {
|
||||
ThreadScope guard;
|
||||
sCatalystInstanceInvokeCallback(
|
||||
catalystInstance.get(), executorToken.get(), id2,
|
||||
ReadableNativeArray::newObjectCxxArgs(std::move(args)).get());
|
||||
catalystInstance.reset();
|
||||
executorToken.reset();
|
||||
};
|
||||
} else {
|
||||
id1 = arguments->array[arguments->array.size() - 1].getInt();
|
||||
}
|
||||
|
||||
first = [catalystInstance, executorToken, id1](std::vector<folly::dynamic> args) mutable {
|
||||
ThreadScope guard;
|
||||
sCatalystInstanceInvokeCallback(
|
||||
catalystInstance.get(), executorToken.get(), id1,
|
||||
ReadableNativeArray::newObjectCxxArgs(std::move(args)).get());
|
||||
// This is necessary because by the time the lambda's dtor runs,
|
||||
// the guard has been destroyed, and it may not be possible to
|
||||
// get a JNIEnv* to clean up the captured global_ref.
|
||||
catalystInstance.reset();
|
||||
executorToken.reset();
|
||||
};
|
||||
}
|
||||
|
||||
// I've got a few flawed options here. I can catch C++ exceptions
|
||||
// here, and log/convert them to java exceptions. This lets all the
|
||||
// java handling work ok, but the only info I can capture about the
|
||||
// C++ exception is the what() string, not the stack. I can let the
|
||||
// C++ exception escape, crashing 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. 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
|
||||
|
||||
folly::dynamic dargs = arguments->array;
|
||||
dargs.resize(arguments->array.size() - method_->callbacks);
|
||||
|
||||
try {
|
||||
method_->func(dargs, first, second);
|
||||
} catch (const facebook::xplat::JsArgumentException& ex) {
|
||||
throwNewJavaException(gJavaLangIllegalArgumentException, ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// CxxModuleWrapper
|
||||
|
||||
void CxxModuleWrapper::registerNatives() {
|
||||
registerHybrid({
|
||||
makeNativeMethod("initHybrid", CxxModuleWrapper::initHybrid),
|
||||
makeNativeMethod("getName", CxxModuleWrapper::getName),
|
||||
makeNativeMethod("getConstantsJson", CxxModuleWrapper::getConstantsJson),
|
||||
makeNativeMethod("getMethods", "()Ljava/util/Map;", CxxModuleWrapper::getMethods),
|
||||
});
|
||||
|
||||
CxxMethodWrapper::registerNatives();
|
||||
}
|
||||
|
||||
CxxModuleWrapper::CxxModuleWrapper(const std::string& soPath, const std::string& fname) {
|
||||
// soPath is the path of a library which has already been loaded by
|
||||
// java SoLoader.loadLibrary(). So this returns the same handle,
|
||||
// and increments the reference counter. We can't just use
|
||||
// dlsym(RTLD_DEFAULT, ...), because that crashes on 4.4.2 and
|
||||
// earlier: https://code.google.com/p/android/issues/detail?id=61799
|
||||
void* handle = dlopen(soPath.c_str(), RTLD_NOW);
|
||||
if (!handle) {
|
||||
throwNewJavaException(gJavaLangIllegalArgumentException,
|
||||
"module shared library %s is not found", soPath.c_str());
|
||||
}
|
||||
// Now, arrange to close the handle so the counter is decremented.
|
||||
// The handle will remain valid until java closes it. There's no
|
||||
// way to do this on Android, but that's no reason to be sloppy
|
||||
// here.
|
||||
auto guard = folly::makeGuard([&] { FBASSERT(dlclose(handle) == 0); });
|
||||
|
||||
void* sym = dlsym(handle, fname.c_str());
|
||||
if (!sym) {
|
||||
throwNewJavaException(gJavaLangIllegalArgumentException,
|
||||
"module function %s in shared library %s is not found",
|
||||
fname.c_str(), soPath.c_str());
|
||||
}
|
||||
auto factory = reinterpret_cast<CxxModule* (*)()>(sym);
|
||||
module_.reset((*factory)());
|
||||
methods_ = module_->getMethods();
|
||||
}
|
||||
|
||||
std::string CxxModuleWrapper::getName() {
|
||||
return module_->getName();
|
||||
}
|
||||
|
||||
std::string CxxModuleWrapper::getConstantsJson() {
|
||||
std::map<std::string, folly::dynamic> constants = module_->getConstants();
|
||||
folly::dynamic constsobject = folly::dynamic::object;
|
||||
|
||||
for (auto& c : constants) {
|
||||
constsobject.insert(std::move(c.first), std::move(c.second));
|
||||
}
|
||||
|
||||
return folly::toJson(constsobject);
|
||||
}
|
||||
|
||||
jobject CxxModuleWrapper::getMethods() {
|
||||
static auto hashmap = findClassStatic("java/util/HashMap");
|
||||
static auto hashmap_put = hashmap->getMethod<jobject(jobject, jobject)>("put");
|
||||
|
||||
auto methods = hashmap->newObject(hashmap->getConstructor<jobject()>());
|
||||
|
||||
std::unordered_set<std::string> names;
|
||||
for (const auto& m : methods_) {
|
||||
if (names.find(m.name) != names.end()) {
|
||||
throwNewJavaException(gJavaLangIllegalArgumentException,
|
||||
"C++ Module %s method name already registered: %s",
|
||||
module_->getName().c_str(), m.name.c_str());
|
||||
}
|
||||
names.insert(m.name);
|
||||
auto name = make_jstring(m.name);
|
||||
static auto ctor =
|
||||
CxxMethodWrapper::javaClassStatic()->getConstructor<CxxMethodWrapper::jhybridobject()>();
|
||||
auto method = CxxMethodWrapper::javaClassStatic()->newObject(ctor);
|
||||
cthis(method)->method_ = &m;
|
||||
hashmap_put(methods.get(), name.get(), method.get());
|
||||
}
|
||||
|
||||
return methods.release();
|
||||
}
|
||||
53
ReactAndroid/src/main/jni/xreact/jni/CxxModuleWrapper.h
Normal file
53
ReactAndroid/src/main/jni/xreact/jni/CxxModuleWrapper.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Module/CxxModule.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class CxxModuleWrapper : public jni::HybridClass<CxxModuleWrapper> {
|
||||
public:
|
||||
constexpr static const char *const kJavaDescriptor =
|
||||
"Lcom/facebook/react/cxxbridge/CxxModuleWrapper;";
|
||||
|
||||
static void registerNatives();
|
||||
|
||||
CxxModuleWrapper(const std::string& soPath, const std::string& fname);
|
||||
|
||||
static jni::local_ref<jhybriddata> initHybrid(
|
||||
jni::alias_ref<jhybridobject>, const std::string& soPath, const std::string& fname) {
|
||||
return makeCxxInstance(soPath, fname);
|
||||
}
|
||||
|
||||
// JNI methods
|
||||
std::string getName();
|
||||
std::string getConstantsJson();
|
||||
jobject getMethods();
|
||||
|
||||
// This steals ownership of the underlying module for use by the C++ bridge
|
||||
std::unique_ptr<xplat::module::CxxModule> getModule() {
|
||||
// TODO mhorowitz: remove this (and a lot of other code) once the java
|
||||
// bridge is dead.
|
||||
methods_.clear();
|
||||
return std::move(module_);
|
||||
}
|
||||
|
||||
protected:
|
||||
friend HybridBase;
|
||||
|
||||
explicit CxxModuleWrapper(std::unique_ptr<xplat::module::CxxModule> module)
|
||||
: module_(std::move(module))
|
||||
, methods_(module_->getMethods()) {}
|
||||
|
||||
std::unique_ptr<xplat::module::CxxModule> module_;
|
||||
std::vector<xplat::module::CxxModule::Method> methods_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
44
ReactAndroid/src/main/jni/xreact/jni/JCallback.h
Normal file
44
ReactAndroid/src/main/jni/xreact/jni/JCallback.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <folly/dynamic.h>
|
||||
#include <fb/fbjni.h>
|
||||
|
||||
#include <react/jni/NativeArray.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class Instance;
|
||||
|
||||
struct JCallback : public jni::JavaClass<JCallback> {
|
||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/react/bridge/Callback;";
|
||||
};
|
||||
|
||||
class JCallbackImpl : public jni::HybridClass<JCallbackImpl, JCallback> {
|
||||
public:
|
||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/react/cxxbridge/CallbackImpl;";
|
||||
|
||||
static void registerNatives() {
|
||||
javaClassStatic()->registerNatives({
|
||||
makeNativeMethod("nativeInvoke", JCallbackImpl::invoke),
|
||||
});
|
||||
}
|
||||
private:
|
||||
friend HybridBase;
|
||||
|
||||
using Callback = std::function<void(folly::dynamic)>;
|
||||
JCallbackImpl(Callback callback) : callback_(std::move(callback)) {}
|
||||
|
||||
void invoke(NativeArray* arguments) {
|
||||
callback_(std::move(arguments->array));
|
||||
}
|
||||
|
||||
Callback callback_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
25
ReactAndroid/src/main/jni/xreact/jni/JExecutorToken.cpp
Normal file
25
ReactAndroid/src/main/jni/xreact/jni/JExecutorToken.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "JExecutorToken.h"
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
ExecutorToken JExecutorToken::getExecutorToken(alias_ref<JExecutorToken::javaobject> jobj) {
|
||||
std::lock_guard<std::mutex> guard(createTokenGuard_);
|
||||
auto sharedOwner = owner_.lock();
|
||||
if (!sharedOwner) {
|
||||
sharedOwner = std::shared_ptr<PlatformExecutorToken>(new JExecutorTokenHolder(jobj));
|
||||
owner_ = sharedOwner;
|
||||
}
|
||||
return ExecutorToken(sharedOwner);
|
||||
}
|
||||
|
||||
local_ref<JExecutorToken::JavaPart> JExecutorToken::extractJavaPartFromToken(ExecutorToken token) {
|
||||
return make_local(static_cast<JExecutorTokenHolder*>(token.getPlatformExecutorToken().get())->getJobj());
|
||||
}
|
||||
|
||||
|
||||
} }
|
||||
60
ReactAndroid/src/main/jni/xreact/jni/JExecutorToken.h
Normal file
60
ReactAndroid/src/main/jni/xreact/jni/JExecutorToken.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
|
||||
#include <cxxreact/ExecutorToken.h>
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class JExecutorTokenHolder;
|
||||
class JExecutorToken : public HybridClass<JExecutorToken> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/ExecutorToken;";
|
||||
|
||||
ExecutorToken getExecutorToken(alias_ref<JExecutorToken::javaobject> jobj);
|
||||
|
||||
static local_ref<JavaPart> extractJavaPartFromToken(ExecutorToken token);
|
||||
private:
|
||||
friend HybridBase;
|
||||
friend JExecutorTokenHolder;
|
||||
|
||||
JExecutorToken() {}
|
||||
|
||||
std::weak_ptr<PlatformExecutorToken> owner_;
|
||||
std::mutex createTokenGuard_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper class to hold references to both the c++ and Java parts of the
|
||||
* ExecutorToken object. The goal is to allow a reference to a token from either
|
||||
* c++ or Java to keep both the Java object and c++ hybrid part alive. For c++
|
||||
* references, we accomplish this by having JExecutorTokenHolder keep a reference
|
||||
* to the Java object (which has a reference to the JExecutorToken hybrid part).
|
||||
* For Java references, we allow the JExecutorTokenHolder to be deallocated if there
|
||||
* are no references to it in c++ from a PlatformExecutorToken, but will dynamically
|
||||
* create a new one in JExecutorToken.getExecutorToken if needed.
|
||||
*/
|
||||
class JExecutorTokenHolder : public PlatformExecutorToken, public noncopyable {
|
||||
public:
|
||||
explicit JExecutorTokenHolder(alias_ref<JExecutorToken::javaobject> jobj) :
|
||||
jobj_(make_global(jobj)),
|
||||
impl_(cthis(jobj)) {
|
||||
}
|
||||
|
||||
JExecutorToken::javaobject getJobj() {
|
||||
return jobj_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
global_ref<JExecutorToken::javaobject> jobj_;
|
||||
JExecutorToken *impl_;
|
||||
};
|
||||
|
||||
} }
|
||||
66
ReactAndroid/src/main/jni/xreact/jni/JMessageQueueThread.cpp
Normal file
66
ReactAndroid/src/main/jni/xreact/jni/JMessageQueueThread.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "JMessageQueueThread.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include <fb/log.h>
|
||||
#include <folly/Memory.h>
|
||||
#include <fb/fbjni.h>
|
||||
|
||||
#include "JNativeRunnable.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
JMessageQueueThread::JMessageQueueThread(alias_ref<JavaMessageQueueThread::javaobject> jobj) :
|
||||
m_jobj(make_global(jobj)) {
|
||||
}
|
||||
|
||||
void JMessageQueueThread::runOnQueue(std::function<void()>&& runnable) {
|
||||
static auto method = JavaMessageQueueThread::javaClassStatic()->
|
||||
getMethod<void(Runnable::javaobject)>("runOnQueue");
|
||||
method(m_jobj, JNativeRunnable::newObjectCxxArgs(runnable).get());
|
||||
}
|
||||
|
||||
void JMessageQueueThread::runOnQueueSync(std::function<void()>&& runnable) {
|
||||
static auto jIsOnThread = JavaMessageQueueThread::javaClassStatic()->
|
||||
getMethod<jboolean()>("isOnThread");
|
||||
|
||||
if (jIsOnThread(m_jobj)) {
|
||||
runnable();
|
||||
} else {
|
||||
std::mutex signalMutex;
|
||||
std::condition_variable signalCv;
|
||||
bool runnableComplete = false;
|
||||
|
||||
runOnQueue([&] () mutable {
|
||||
std::lock_guard<std::mutex> lock(signalMutex);
|
||||
|
||||
runnable();
|
||||
runnableComplete = true;
|
||||
|
||||
signalCv.notify_one();
|
||||
});
|
||||
|
||||
std::unique_lock<std::mutex> lock(signalMutex);
|
||||
signalCv.wait(lock, [&runnableComplete] { return runnableComplete; });
|
||||
}
|
||||
}
|
||||
|
||||
void JMessageQueueThread::quitSynchronous() {
|
||||
static auto method = JavaMessageQueueThread::javaClassStatic()->
|
||||
getMethod<void()>("quitSynchronous");
|
||||
method(m_jobj);
|
||||
}
|
||||
|
||||
/* static */
|
||||
std::unique_ptr<JMessageQueueThread> JMessageQueueThread::currentMessageQueueThread() {
|
||||
static auto method = MessageQueueThreadRegistry::javaClassStatic()->
|
||||
getStaticMethod<JavaMessageQueueThread::javaobject()>("myMessageQueueThread");
|
||||
return folly::make_unique<JMessageQueueThread>(method(MessageQueueThreadRegistry::javaClassStatic()));
|
||||
}
|
||||
|
||||
} }
|
||||
|
||||
60
ReactAndroid/src/main/jni/xreact/jni/JMessageQueueThread.h
Normal file
60
ReactAndroid/src/main/jni/xreact/jni/JMessageQueueThread.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <cxxreact/MessageQueueThread.h>
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class JavaMessageQueueThread : public jni::JavaClass<JavaMessageQueueThread> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/queue/MessageQueueThread;";
|
||||
};
|
||||
|
||||
class JMessageQueueThread : public MessageQueueThread {
|
||||
public:
|
||||
JMessageQueueThread(alias_ref<JavaMessageQueueThread::javaobject> jobj);
|
||||
|
||||
/**
|
||||
* Enqueues the given function to run on this MessageQueueThread.
|
||||
*/
|
||||
void runOnQueue(std::function<void()>&& runnable) override;
|
||||
|
||||
/**
|
||||
* Synchronously executes the given function to run on this
|
||||
* MessageQueueThread, waiting until it completes. Can be called from any
|
||||
* thread, but will block if not called on this MessageQueueThread.
|
||||
*/
|
||||
void runOnQueueSync(std::function<void()>&& runnable) override;
|
||||
|
||||
/**
|
||||
* Synchronously quits the current MessageQueueThread. Can be called from any thread, but will
|
||||
* block if not called on this MessageQueueThread.
|
||||
*/
|
||||
void quitSynchronous() override;
|
||||
|
||||
JavaMessageQueueThread::javaobject jobj() {
|
||||
return m_jobj.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current MessageQueueThread that owns this thread.
|
||||
*/
|
||||
static std::unique_ptr<JMessageQueueThread> currentMessageQueueThread();
|
||||
private:
|
||||
global_ref<JavaMessageQueueThread::javaobject> m_jobj;
|
||||
};
|
||||
|
||||
class MessageQueueThreadRegistry : public jni::JavaClass<MessageQueueThreadRegistry> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/queue/MessageQueueThreadRegistry;";
|
||||
};
|
||||
|
||||
} }
|
||||
44
ReactAndroid/src/main/jni/xreact/jni/JNativeRunnable.h
Normal file
44
ReactAndroid/src/main/jni/xreact/jni/JNativeRunnable.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class Runnable : public JavaClass<Runnable> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor = "Ljava/lang/Runnable;";
|
||||
};
|
||||
|
||||
/**
|
||||
* The c++ interface for the Java NativeRunnable class
|
||||
*/
|
||||
class JNativeRunnable : public HybridClass<JNativeRunnable, Runnable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/react/bridge/queue/NativeRunnable;";
|
||||
|
||||
void run() {
|
||||
m_runnable();
|
||||
}
|
||||
|
||||
static void registerNatives() {
|
||||
javaClassStatic()->registerNatives({
|
||||
makeNativeMethod("run", JNativeRunnable::run),
|
||||
});
|
||||
}
|
||||
private:
|
||||
friend HybridBase;
|
||||
|
||||
JNativeRunnable(std::function<void()> runnable)
|
||||
: m_runnable(std::move(runnable)) {}
|
||||
|
||||
std::function<void()> m_runnable;
|
||||
};
|
||||
|
||||
} }
|
||||
244
ReactAndroid/src/main/jni/xreact/jni/JSCPerfLogging.cpp
Normal file
244
ReactAndroid/src/main/jni/xreact/jni/JSCPerfLogging.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "JSCPerfLogging.h"
|
||||
|
||||
#include <fb/log.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <react/JSCHelpers.h>
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
struct _jqplProvider : _jobject {};
|
||||
using jqplProvider = _jqplProvider*;
|
||||
|
||||
struct _jqpl : _jobject {};
|
||||
using jqpl = _jqpl*;
|
||||
|
||||
namespace facebook { namespace jni {
|
||||
|
||||
|
||||
template<>
|
||||
class JObjectWrapper<jqpl> : public JObjectWrapper<jobject> {
|
||||
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Lcom/facebook/quicklog/QuickPerformanceLogger;";
|
||||
|
||||
using JObjectWrapper<jobject>::JObjectWrapper;
|
||||
|
||||
void markerStart(int markerId, int instanceKey, long timestamp) {
|
||||
static auto markerStartMethod =
|
||||
qplClass()->getMethod<void(int32_t, int32_t, int64_t)>("markerStart");
|
||||
markerStartMethod(this_, markerId, instanceKey, timestamp);
|
||||
}
|
||||
|
||||
void markerEnd(int markerId, int instanceKey, short actionId, long timestamp) {
|
||||
static auto markerEndMethod =
|
||||
qplClass()->getMethod<void(int32_t, int32_t, int16_t, int64_t)>("markerEnd");
|
||||
markerEndMethod(this_, markerId, instanceKey, actionId, timestamp);
|
||||
}
|
||||
|
||||
void markerNote(int markerId, int instanceKey, short actionId, long timestamp) {
|
||||
static auto markerNoteMethod =
|
||||
qplClass()->getMethod<void(int32_t, int32_t, int16_t, int64_t)>("markerNote");
|
||||
markerNoteMethod(this_, markerId, instanceKey, actionId, timestamp);
|
||||
}
|
||||
|
||||
void markerCancel(int markerId, int instanceKey) {
|
||||
static auto markerCancelMethod =
|
||||
qplClass()->getMethod<void(int32_t, int32_t)>("markerCancel");
|
||||
markerCancelMethod(this_, markerId, instanceKey);
|
||||
}
|
||||
|
||||
int64_t currentMonotonicTimestamp() {
|
||||
static auto currentTimestampMethod =
|
||||
qplClass()->getMethod<int64_t()>("currentMonotonicTimestamp");
|
||||
return currentTimestampMethod(this_);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static alias_ref<jclass> qplClass() {
|
||||
static auto cls = findClassStatic("com/facebook/quicklog/QuickPerformanceLogger");
|
||||
return cls;
|
||||
}
|
||||
|
||||
};
|
||||
using JQuickPerformanceLogger = JObjectWrapper<jqpl>;
|
||||
|
||||
|
||||
template<>
|
||||
class JObjectWrapper<jqplProvider> : public JObjectWrapper<jobject> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor =
|
||||
"Lcom/facebook/quicklog/QuickPerformanceLoggerProvider;";
|
||||
|
||||
using JObjectWrapper<jobject>::JObjectWrapper;
|
||||
|
||||
static global_ref<jqpl> get() {
|
||||
static auto getQPLInstMethod = qplProviderClass()->getStaticMethod<jqpl()>("getQPLInstance");
|
||||
static global_ref<jqpl> theQpl = make_global(getQPLInstMethod(qplProviderClass().get()));
|
||||
return theQpl;
|
||||
}
|
||||
|
||||
static bool check() {
|
||||
static auto getQPLInstMethod = qplProviderClass()->getStaticMethod<jqpl()>("getQPLInstance");
|
||||
auto theQpl = getQPLInstMethod(qplProviderClass().get());
|
||||
return (theQpl.get() != nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static alias_ref<jclass> qplProviderClass() {
|
||||
static auto cls = findClassStatic("com/facebook/quicklog/QuickPerformanceLoggerProvider");
|
||||
return cls;
|
||||
}
|
||||
};
|
||||
using JQuickPerformanceLoggerProvider = JObjectWrapper<jqplProvider>;
|
||||
|
||||
}}
|
||||
|
||||
static bool isReady() {
|
||||
static bool ready = false;
|
||||
if (!ready) {
|
||||
try {
|
||||
findClassStatic("com/facebook/quicklog/QuickPerformanceLoggerProvider");
|
||||
} catch(...) {
|
||||
// Swallow this exception - we don't want to crash the app, an error is enough.
|
||||
FBLOGE("Calling QPL from JS before class has been loaded in Java. Ignored.");
|
||||
return false;
|
||||
}
|
||||
if (JQuickPerformanceLoggerProvider::check()) {
|
||||
ready = true;
|
||||
} else {
|
||||
FBLOGE("Calling QPL from JS before it has been initialized in Java. Ignored.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return ready;
|
||||
}
|
||||
|
||||
// After having read the implementation of PNaN that is returned from JSValueToNumber, and some
|
||||
// more material on how NaNs are constructed, I think this is the most consistent way to verify
|
||||
// NaN with how we generate it.
|
||||
// Once the integration completes, I'll play around with it some more and potentially change this
|
||||
// implementation to use std::isnan() if it is exactly commensurate with our usage.
|
||||
static bool isNan(double value) {
|
||||
return (value != value);
|
||||
}
|
||||
|
||||
// Safely translates JSValues to an array of doubles.
|
||||
static bool grabDoubles(
|
||||
size_t targetsCount,
|
||||
double targets[],
|
||||
JSContextRef ctx,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
if (argumentCount < targetsCount) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0 ; i < targetsCount ; i++) {
|
||||
targets[i] = JSValueToNumber(ctx, arguments[i], exception);
|
||||
if (isNan(targets[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSValueRef nativeQPLMarkerStart(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
double targets[3];
|
||||
if (isReady() && grabDoubles(3, targets, ctx, argumentCount, arguments, exception)) {
|
||||
int32_t markerId = (int32_t) targets[0];
|
||||
int32_t instanceKey = (int32_t) targets[1];
|
||||
int64_t timestamp = (int64_t) targets[2];
|
||||
JQuickPerformanceLoggerProvider::get()->markerStart(markerId, instanceKey, timestamp);
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeQPLMarkerEnd(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
double targets[4];
|
||||
if (isReady() && grabDoubles(4, targets, ctx, argumentCount, arguments, exception)) {
|
||||
int32_t markerId = (int32_t) targets[0];
|
||||
int32_t instanceKey = (int32_t) targets[1];
|
||||
int16_t actionId = (int16_t) targets[2];
|
||||
int64_t timestamp = (int64_t) targets[3];
|
||||
JQuickPerformanceLoggerProvider::get()->markerEnd(markerId, instanceKey, actionId, timestamp);
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeQPLMarkerNote(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
double targets[4];
|
||||
if (isReady() && grabDoubles(4, targets, ctx, argumentCount, arguments, exception)) {
|
||||
int32_t markerId = (int32_t) targets[0];
|
||||
int32_t instanceKey = (int32_t) targets[1];
|
||||
int16_t actionId = (int16_t) targets[2];
|
||||
int64_t timestamp = (int64_t) targets[3];
|
||||
JQuickPerformanceLoggerProvider::get()->markerNote(markerId, instanceKey, actionId, timestamp);
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeQPLMarkerCancel(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
double targets[2];
|
||||
if (isReady() && grabDoubles(2, targets, ctx, argumentCount, arguments, exception)) {
|
||||
int32_t markerId = (int32_t) targets[0];
|
||||
int32_t instanceKey = (int32_t) targets[1];
|
||||
JQuickPerformanceLoggerProvider::get()->markerCancel(markerId, instanceKey);
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeQPLTimestamp(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
if (!isReady()) {
|
||||
return JSValueMakeNumber(ctx, 0);
|
||||
}
|
||||
int64_t timestamp = JQuickPerformanceLoggerProvider::get()->currentMonotonicTimestamp();
|
||||
// Since this is monotonic time, I assume the 52 bits of mantissa are enough in the double value.
|
||||
return JSValueMakeNumber(ctx, timestamp);
|
||||
}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void addNativePerfLoggingHooks(JSGlobalContextRef ctx) {
|
||||
installGlobalFunction(ctx, "nativeQPLMarkerStart", nativeQPLMarkerStart);
|
||||
installGlobalFunction(ctx, "nativeQPLMarkerEnd", nativeQPLMarkerEnd);
|
||||
installGlobalFunction(ctx, "nativeQPLMarkerNote", nativeQPLMarkerNote);
|
||||
installGlobalFunction(ctx, "nativeQPLMarkerCancel", nativeQPLMarkerCancel);
|
||||
installGlobalFunction(ctx, "nativeQPLTimestamp", nativeQPLTimestamp);
|
||||
}
|
||||
|
||||
} }
|
||||
11
ReactAndroid/src/main/jni/xreact/jni/JSCPerfLogging.h
Normal file
11
ReactAndroid/src/main/jni/xreact/jni/JSCPerfLogging.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JavaScriptCore/JSContextRef.h>
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void addNativePerfLoggingHooks(JSGlobalContextRef ctx);
|
||||
|
||||
} }
|
||||
99
ReactAndroid/src/main/jni/xreact/jni/JSLoader.cpp
Normal file
99
ReactAndroid/src/main/jni/xreact/jni/JSLoader.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "JSLoader.h"
|
||||
|
||||
#include <folly/Memory.h>
|
||||
#include <android/asset_manager_jni.h>
|
||||
#include <fb/Environment.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <streambuf>
|
||||
#include <string>
|
||||
#include <fb/log.h>
|
||||
#ifdef WITH_FBSYSTRACE
|
||||
#include <fbsystrace.h>
|
||||
using fbsystrace::FbSystraceSection;
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
static jclass gApplicationHolderClass;
|
||||
static jmethodID gGetApplicationMethod;
|
||||
static jmethodID gGetAssetManagerMethod;
|
||||
|
||||
std::unique_ptr<const JSBigString> loadScriptFromAssets(const std::string& assetName) {
|
||||
JNIEnv *env = jni::Environment::current();
|
||||
jobject application = env->CallStaticObjectMethod(
|
||||
gApplicationHolderClass,
|
||||
gGetApplicationMethod);
|
||||
jobject assetManager = env->CallObjectMethod(application, gGetAssetManagerMethod);
|
||||
return loadScriptFromAssets(AAssetManager_fromJava(env, assetManager), assetName);
|
||||
}
|
||||
|
||||
AAssetManager *extractAssetManager(jobject jassetManager) {
|
||||
auto env = jni::Environment::current();
|
||||
return AAssetManager_fromJava(env, jassetManager);
|
||||
}
|
||||
|
||||
std::unique_ptr<const JSBigString> loadScriptFromAssets(
|
||||
AAssetManager *manager,
|
||||
const std::string& assetName) {
|
||||
#ifdef WITH_FBSYSTRACE
|
||||
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "reactbridge_jni_loadScriptFromAssets",
|
||||
"assetName", assetName);
|
||||
#endif
|
||||
if (manager) {
|
||||
auto asset = AAssetManager_open(
|
||||
manager,
|
||||
assetName.c_str(),
|
||||
AASSET_MODE_STREAMING); // Optimized for sequential read: see AssetManager.java for docs
|
||||
if (asset) {
|
||||
auto buf = folly::make_unique<JSBigBufferString>(AAsset_getLength(asset));
|
||||
size_t offset = 0;
|
||||
int readbytes;
|
||||
while ((readbytes = AAsset_read(asset, buf->data() + offset, buf->size() - offset)) > 0) {
|
||||
offset += readbytes;
|
||||
}
|
||||
AAsset_close(asset);
|
||||
if (offset == buf->size()) {
|
||||
return std::move(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
FBLOGE("Unable to load script from assets: %s", assetName.c_str());
|
||||
return folly::make_unique<JSBigStdString>("");
|
||||
}
|
||||
|
||||
std::string loadScriptFromFile(const std::string& fileName) {
|
||||
#ifdef WITH_FBSYSTRACE
|
||||
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "reactbridge_jni_loadScriptFromFile",
|
||||
"fileName", fileName);
|
||||
#endif
|
||||
std::ifstream jsfile(fileName);
|
||||
if (jsfile) {
|
||||
std::string output;
|
||||
jsfile.seekg(0, std::ios::end);
|
||||
output.reserve(jsfile.tellg());
|
||||
jsfile.seekg(0, std::ios::beg);
|
||||
output.assign(
|
||||
(std::istreambuf_iterator<char>(jsfile)),
|
||||
std::istreambuf_iterator<char>());
|
||||
return output;
|
||||
}
|
||||
|
||||
FBLOGE("Unable to load script from file: %s", fileName.c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
void registerJSLoaderNatives() {
|
||||
JNIEnv *env = jni::Environment::current();
|
||||
jclass applicationHolderClass = env->FindClass("com/facebook/react/common/ApplicationHolder");
|
||||
gApplicationHolderClass = (jclass)env->NewGlobalRef(applicationHolderClass);
|
||||
gGetApplicationMethod = env->GetStaticMethodID(applicationHolderClass, "getApplication", "()Landroid/app/Application;");
|
||||
|
||||
jclass appClass = env->FindClass("android/app/Application");
|
||||
gGetAssetManagerMethod = env->GetMethodID(appClass, "getAssets", "()Landroid/content/res/AssetManager;");
|
||||
}
|
||||
|
||||
} }
|
||||
33
ReactAndroid/src/main/jni/xreact/jni/JSLoader.h
Normal file
33
ReactAndroid/src/main/jni/xreact/jni/JSLoader.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cxxreact/Executor.h>
|
||||
#include <android/asset_manager.h>
|
||||
#include <string>
|
||||
#include <jni.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/**
|
||||
* Helper method for loading a JS script from Android assets without
|
||||
* a reference to an AssetManager.
|
||||
*/
|
||||
std::unique_ptr<const JSBigString> loadScriptFromAssets(const std::string& assetName);
|
||||
|
||||
/**
|
||||
* Helper method for loading JS script from android asset
|
||||
*/
|
||||
AAssetManager *extractAssetManager(jobject jassetManager);
|
||||
|
||||
std::unique_ptr<const JSBigString> loadScriptFromAssets(AAssetManager *assetManager, const std::string& assetName);
|
||||
|
||||
/**
|
||||
* Helper method for loading JS script from a file
|
||||
*/
|
||||
std::string loadScriptFromFile(const std::string& fileName);
|
||||
|
||||
void registerJSLoaderNatives();
|
||||
|
||||
} }
|
||||
36
ReactAndroid/src/main/jni/xreact/jni/JSLogging.cpp
Normal file
36
ReactAndroid/src/main/jni/xreact/jni/JSLogging.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "JSLogging.h"
|
||||
|
||||
#include <android/log.h>
|
||||
#include <algorithm>
|
||||
#include <react/Value.h>
|
||||
#include <fb/log.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
JSValueRef nativeLoggingHook(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[], JSValueRef *exception) {
|
||||
android_LogPriority logLevel = ANDROID_LOG_DEBUG;
|
||||
if (argumentCount > 1) {
|
||||
int level = (int) JSValueToNumber(ctx, arguments[1], NULL);
|
||||
// The lowest log level we get from JS is 0. We shift and cap it to be
|
||||
// in the range the Android logging method expects.
|
||||
logLevel = std::min(
|
||||
static_cast<android_LogPriority>(level + ANDROID_LOG_DEBUG),
|
||||
ANDROID_LOG_FATAL);
|
||||
}
|
||||
if (argumentCount > 0) {
|
||||
JSStringRef jsString = JSValueToStringCopy(ctx, arguments[0], NULL);
|
||||
String message = String::adopt(jsString);
|
||||
FBLOG_PRI(logLevel, "ReactNativeJS", "%s", message.str().c_str());
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
}};
|
||||
15
ReactAndroid/src/main/jni/xreact/jni/JSLogging.h
Normal file
15
ReactAndroid/src/main/jni/xreact/jni/JSLogging.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JavaScriptCore/JSContextRef.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
JSValueRef nativeLoggingHook(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[], JSValueRef *exception);
|
||||
}}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
|
||||
#include <cxxreact/Executor.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class JavaScriptExecutorHolder : public jni::HybridClass<JavaScriptExecutorHolder> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor =
|
||||
"Lcom/facebook/react/cxxbridge/JavaScriptExecutor;";
|
||||
|
||||
std::shared_ptr<JSExecutorFactory> getExecutorFactory() {
|
||||
return mExecutorFactory;
|
||||
}
|
||||
|
||||
protected:
|
||||
JavaScriptExecutorHolder(std::shared_ptr<JSExecutorFactory> factory)
|
||||
: mExecutorFactory(factory) {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<JSExecutorFactory> mExecutorFactory;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "JniJSModulesUnbundle.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <fb/assert.h>
|
||||
#include <libgen.h>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <sys/endian.h>
|
||||
#include <utility>
|
||||
|
||||
using magic_number_t = uint32_t;
|
||||
const magic_number_t MAGIC_FILE_HEADER = 0xFB0BD1E5;
|
||||
const std::string MAGIC_FILE_NAME = "UNBUNDLE";
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
using asset_ptr =
|
||||
std::unique_ptr<AAsset, std::function<decltype(AAsset_close)>>;
|
||||
|
||||
static std::string jsModulesDir(const std::string& entryFile) {
|
||||
std::string dir = dirname(entryFile.c_str());
|
||||
|
||||
// android's asset manager does not work with paths that start with a dot
|
||||
return dir == "." ? "js-modules/" : dir + "/js-modules/";
|
||||
}
|
||||
|
||||
static asset_ptr openAsset(
|
||||
AAssetManager *manager,
|
||||
const std::string& fileName,
|
||||
int mode = AASSET_MODE_STREAMING) {
|
||||
return asset_ptr(
|
||||
AAssetManager_open(manager, fileName.c_str(), mode),
|
||||
AAsset_close);
|
||||
}
|
||||
|
||||
JniJSModulesUnbundle::JniJSModulesUnbundle(AAssetManager *assetManager, const std::string& entryFile) :
|
||||
m_assetManager(assetManager),
|
||||
m_moduleDirectory(jsModulesDir(entryFile)) {}
|
||||
|
||||
bool JniJSModulesUnbundle::isUnbundle(
|
||||
AAssetManager *assetManager,
|
||||
const std::string& assetName) {
|
||||
if (!assetManager) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto magicFileName = jsModulesDir(assetName) + MAGIC_FILE_NAME;
|
||||
auto asset = openAsset(assetManager, magicFileName.c_str());
|
||||
if (asset == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
magic_number_t fileHeader = 0;
|
||||
AAsset_read(asset.get(), &fileHeader, sizeof(fileHeader));
|
||||
return fileHeader == htole32(MAGIC_FILE_HEADER);
|
||||
}
|
||||
|
||||
JSModulesUnbundle::Module JniJSModulesUnbundle::getModule(uint32_t moduleId) const {
|
||||
// can be nullptr for default constructor.
|
||||
FBASSERTMSGF(m_assetManager != nullptr, "Unbundle has not been initialized with an asset manager");
|
||||
|
||||
std::ostringstream sourceUrlBuilder;
|
||||
sourceUrlBuilder << moduleId << ".js";
|
||||
auto sourceUrl = sourceUrlBuilder.str();
|
||||
|
||||
auto fileName = m_moduleDirectory + sourceUrl;
|
||||
auto asset = openAsset(m_assetManager, fileName, AASSET_MODE_BUFFER);
|
||||
|
||||
const char *buffer = nullptr;
|
||||
if (asset != nullptr) {
|
||||
buffer = static_cast<const char *>(AAsset_getBuffer(asset.get()));
|
||||
}
|
||||
if (buffer == nullptr) {
|
||||
throw ModuleNotFound("Module not found: " + sourceUrl);
|
||||
}
|
||||
return {sourceUrl, std::string(buffer, AAsset_getLength(asset.get()))};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
31
ReactAndroid/src/main/jni/xreact/jni/JniJSModulesUnbundle.h
Normal file
31
ReactAndroid/src/main/jni/xreact/jni/JniJSModulesUnbundle.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/asset_manager.h>
|
||||
#include <cxxreact/JSModulesUnbundle.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class JniJSModulesUnbundle : public JSModulesUnbundle {
|
||||
/**
|
||||
* This implementation reads modules as single file from the assets of an apk.
|
||||
*/
|
||||
public:
|
||||
JniJSModulesUnbundle() = default;
|
||||
JniJSModulesUnbundle(AAssetManager *assetManager, const std::string& entryFile);
|
||||
JniJSModulesUnbundle(JniJSModulesUnbundle&& other) = delete;
|
||||
JniJSModulesUnbundle& operator= (JSModulesUnbundle&& other) = delete;
|
||||
|
||||
static bool isUnbundle(
|
||||
AAssetManager *assetManager,
|
||||
const std::string& assetName);
|
||||
virtual Module getModule(uint32_t moduleId) const override;
|
||||
private:
|
||||
AAssetManager *m_assetManager = nullptr;
|
||||
std::string m_moduleDirectory;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
32
ReactAndroid/src/main/jni/xreact/jni/JniWebWorkers.h
Normal file
32
ReactAndroid/src/main/jni/xreact/jni/JniWebWorkers.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <jni.h>
|
||||
#include <folly/Memory.h>
|
||||
|
||||
#include "JMessageQueueThread.h"
|
||||
#include <react/MessageQueue.h>
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class JniWebWorkers : public JavaClass<JniWebWorkers> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/webworkers/WebWorkers;";
|
||||
|
||||
static std::unique_ptr<MessageQueue> createWebWorkerQueue(int id, MessageQueue* ownerMessageQueue) {
|
||||
static auto method = JniWebWorkers::javaClassStatic()->
|
||||
getStaticMethod<MessageQueueThread::javaobject(jint, MessageQueueThread::javaobject)>("createWebWorkerThread");
|
||||
|
||||
JMessageQueueThread* ownerMessageQueueThread = static_cast<JMessageQueueThread*>(ownerMessageQueue);
|
||||
auto res = method(JniWebWorkers::javaClassStatic(), id, ownerMessageQueueThread->jobj());
|
||||
return folly::make_unique<JMessageQueueThread>(res);
|
||||
}
|
||||
};
|
||||
|
||||
} }
|
||||
230
ReactAndroid/src/main/jni/xreact/jni/MethodInvoker.cpp
Normal file
230
ReactAndroid/src/main/jni/xreact/jni/MethodInvoker.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "MethodInvoker.h"
|
||||
|
||||
#include <react/jni/ReadableNativeArray.h>
|
||||
#ifdef WITH_FBSYSTRACE
|
||||
#include <fbsystrace.h>
|
||||
#endif
|
||||
|
||||
#include "ModuleRegistryHolder.h"
|
||||
#include "JCallback.h"
|
||||
#include "JExecutorToken.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
namespace {
|
||||
|
||||
using dynamic_iterator = folly::dynamic::const_iterator;
|
||||
|
||||
struct JPromiseImpl : public jni::JavaClass<JPromiseImpl> {
|
||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/react/bridge/PromiseImpl;";
|
||||
|
||||
static jni::local_ref<javaobject> create(jni::local_ref<JCallback::javaobject> resolve, jni::local_ref<JCallback::javaobject> reject) {
|
||||
return newInstance(resolve, reject);
|
||||
}
|
||||
};
|
||||
|
||||
// HACK: Exposes constructor
|
||||
struct ExposedReadableNativeArray : public ReadableNativeArray {
|
||||
explicit ExposedReadableNativeArray(folly::dynamic array)
|
||||
: ReadableNativeArray(std::move(array)) {}
|
||||
};
|
||||
|
||||
jdouble extractDouble(const folly::dynamic& value) {
|
||||
if (value.isInt()) {
|
||||
return static_cast<jdouble>(value.getInt());
|
||||
} else {
|
||||
return static_cast<jdouble>(value.getDouble());
|
||||
}
|
||||
}
|
||||
|
||||
jni::local_ref<JCallbackImpl::jhybridobject> extractCallback(std::weak_ptr<Instance>& instance, ExecutorToken token, const folly::dynamic& value) {
|
||||
if (value.isNull()) {
|
||||
return jni::local_ref<JCallbackImpl::jhybridobject>(nullptr);
|
||||
} else {
|
||||
return JCallbackImpl::newObjectCxxArgs(makeCallback(instance, token, value));
|
||||
}
|
||||
}
|
||||
|
||||
jni::local_ref<JPromiseImpl::javaobject> extractPromise(std::weak_ptr<Instance>& instance, ExecutorToken token, dynamic_iterator& it, dynamic_iterator& end) {
|
||||
auto resolve = extractCallback(instance, token, *it++);
|
||||
CHECK(it != end);
|
||||
auto reject = extractCallback(instance, token, *it++);
|
||||
return JPromiseImpl::create(resolve, reject);
|
||||
}
|
||||
|
||||
jobject valueOf(jboolean value) {
|
||||
static auto kClass = jni::findClassStatic("java/lang/Boolean");
|
||||
static auto kValueOf = kClass->getStaticMethod<jobject(jboolean)>("valueOf");
|
||||
return kValueOf(kClass, value).release();
|
||||
}
|
||||
|
||||
jobject valueOf(jint value) {
|
||||
static auto kClass = jni::findClassStatic("java/lang/Integer");
|
||||
static auto kValueOf = kClass->getStaticMethod<jobject(jint)>("valueOf");
|
||||
return kValueOf(kClass, value).release();
|
||||
}
|
||||
|
||||
jobject valueOf(jdouble value) {
|
||||
static auto kClass = jni::findClassStatic("java/lang/Double");
|
||||
static auto kValueOf = kClass->getStaticMethod<jobject(jdouble)>("valueOf");
|
||||
return kValueOf(kClass, value).release();
|
||||
}
|
||||
|
||||
jobject valueOf(jfloat value) {
|
||||
static auto kClass = jni::findClassStatic("java/lang/Float");
|
||||
static auto kValueOf = kClass->getStaticMethod<jobject(jfloat)>("valueOf");
|
||||
return kValueOf(kClass, value).release();
|
||||
}
|
||||
|
||||
bool isNullable(char type) {
|
||||
switch (type) {
|
||||
case 'Z':
|
||||
case 'I':
|
||||
case 'F':
|
||||
case 'S':
|
||||
case 'A':
|
||||
case 'M':
|
||||
case 'X':
|
||||
return true;
|
||||
default:
|
||||
return false;;
|
||||
}
|
||||
}
|
||||
|
||||
jvalue extract(std::weak_ptr<Instance>& instance, ExecutorToken token, char type, dynamic_iterator& it, dynamic_iterator& end) {
|
||||
CHECK(it != end);
|
||||
jvalue value;
|
||||
if (type == 'P') {
|
||||
value.l = extractPromise(instance, token, it, end).release();
|
||||
return value;
|
||||
} else if (type == 'T') {
|
||||
value.l = JExecutorToken::extractJavaPartFromToken(token).release();
|
||||
return value;
|
||||
}
|
||||
|
||||
const auto& arg = *it++;
|
||||
if (isNullable(type) && arg.isNull()) {
|
||||
value.l = nullptr;
|
||||
return value;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'z':
|
||||
value.z = static_cast<jboolean>(arg.getBool());
|
||||
break;
|
||||
case 'Z':
|
||||
value.l = valueOf(static_cast<jboolean>(arg.getBool()));
|
||||
break;
|
||||
case 'i':
|
||||
value.i = static_cast<jint>(arg.getInt());
|
||||
break;
|
||||
case 'I':
|
||||
value.l = valueOf(static_cast<jint>(arg.getInt()));
|
||||
break;
|
||||
case 'f':
|
||||
value.f = static_cast<jfloat>(extractDouble(arg));
|
||||
break;
|
||||
case 'F':
|
||||
value.l = valueOf(static_cast<jfloat>(extractDouble(arg)));
|
||||
break;
|
||||
case 'd':
|
||||
value.d = extractDouble(arg);
|
||||
break;
|
||||
case 'D':
|
||||
value.l = valueOf(extractDouble(arg));
|
||||
break;
|
||||
case 'S':
|
||||
value.l = jni::make_jstring(arg.getString()).release();
|
||||
break;
|
||||
case 'A':
|
||||
value.l = ReadableNativeArray::newObjectCxxArgs(arg).release();
|
||||
break;
|
||||
case 'M':
|
||||
// HACK: Workaround for constructing ReadableNativeMap
|
||||
value.l = ExposedReadableNativeArray(folly::dynamic::array(arg)).getMap(0);
|
||||
break;
|
||||
case 'X':
|
||||
value.l = extractCallback(instance, token, arg).release();
|
||||
break;
|
||||
default:
|
||||
LOG(FATAL) << "Unknown param type: " << type;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
std::size_t countJsArgs(const std::string& signature) {
|
||||
std::size_t count = 0;
|
||||
for (char c : signature) {
|
||||
switch (c) {
|
||||
case 'T':
|
||||
break;
|
||||
case 'P':
|
||||
count += 2;
|
||||
break;
|
||||
default:
|
||||
count += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MethodInvoker::MethodInvoker(jni::alias_ref<JReflectMethod::javaobject> method, std::string signature, std::string traceName, bool isSync)
|
||||
: method_(method->getMethodID()),
|
||||
jsArgCount_(countJsArgs(signature) - 2),
|
||||
signature_(std::move(signature)),
|
||||
traceName_(std::move(traceName)),
|
||||
isSync_(isSync) {
|
||||
CHECK(signature_.at(1) == '.') << "Improper module method signature";
|
||||
CHECK(!isSync || signature_.at(0) == 'v') << "Non-sync hooks cannot have a non-void return type";
|
||||
}
|
||||
|
||||
MethodCallResult MethodInvoker::invoke(std::weak_ptr<Instance>& instance, JBaseJavaModule::javaobject module, ExecutorToken token, const folly::dynamic& params) {
|
||||
#ifdef WITH_FBSYSTRACE
|
||||
fbsystrace::FbSystraceSection s(
|
||||
TRACE_TAG_REACT_CXX_BRIDGE,
|
||||
isSync_ ? "callJavaSyncHook" : "callJavaModuleMethod",
|
||||
"method",
|
||||
traceName_);
|
||||
#endif
|
||||
if (params.size() != jsArgCount_) {
|
||||
throw std::invalid_argument(folly::to<std::string>("expected ", jsArgCount_, " arguments, got ", params.size()));
|
||||
}
|
||||
auto argCount = signature_.size() - 2;
|
||||
jni::JniLocalScope scope(jni::Environment::current(), argCount);
|
||||
jvalue args[argCount];
|
||||
std::transform(
|
||||
signature_.begin() + 2,
|
||||
signature_.end(),
|
||||
args,
|
||||
[&instance, token, it = params.begin(), end = params.end()] (char type) mutable {
|
||||
return extract(instance, token, type, it, end);
|
||||
});
|
||||
|
||||
// TODO(t10768795): Use fbjni here
|
||||
folly::dynamic ret = folly::dynamic::object();
|
||||
bool isReturnUndefined = false;
|
||||
char returnType = signature_.at(0);
|
||||
switch (returnType) {
|
||||
case 'v':
|
||||
jni::Environment::current()->CallVoidMethodA(module, method_, args);
|
||||
ret = nullptr;
|
||||
isReturnUndefined = true;
|
||||
break;
|
||||
default:
|
||||
LOG(FATAL) << "Unknown return type: " << returnType;
|
||||
// TODO: other cases
|
||||
}
|
||||
|
||||
jni::throwPendingJniExceptionAsCppException();
|
||||
|
||||
return MethodCallResult{ret, isReturnUndefined};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
37
ReactAndroid/src/main/jni/xreact/jni/MethodInvoker.h
Normal file
37
ReactAndroid/src/main/jni/xreact/jni/MethodInvoker.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <folly/dynamic.h>
|
||||
|
||||
#include <cxxreact/ExecutorToken.h>
|
||||
|
||||
#include "ModuleRegistryHolder.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class Instance;
|
||||
|
||||
class MethodInvoker {
|
||||
public:
|
||||
MethodInvoker(jni::alias_ref<JReflectMethod::javaobject> method, std::string signature, std::string traceName, bool isSync);
|
||||
|
||||
MethodCallResult invoke(std::weak_ptr<Instance>& instance, JBaseJavaModule::javaobject module, ExecutorToken token, const folly::dynamic& params);
|
||||
|
||||
bool isSyncHook() const {
|
||||
return isSync_;
|
||||
}
|
||||
private:
|
||||
jmethodID method_;
|
||||
std::size_t jsArgCount_;
|
||||
std::string signature_;
|
||||
std::string traceName_;
|
||||
bool isSync_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
344
ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryHolder.cpp
Normal file
344
ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryHolder.cpp
Normal file
@@ -0,0 +1,344 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "ModuleRegistryHolder.h"
|
||||
|
||||
#include <folly/json.h>
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
|
||||
#include <Module/CxxModule.h>
|
||||
#include <Module/JsArgumentHelpers.h>
|
||||
|
||||
#include <cxxreact/Instance.h>
|
||||
#include <cxxreact/NativeModule.h>
|
||||
#include <react/jni/ReadableNativeArray.h>
|
||||
|
||||
#include "MethodInvoker.h"
|
||||
|
||||
#include "CatalystInstanceImpl.h"
|
||||
|
||||
using facebook::xplat::module::CxxModule;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
namespace {
|
||||
|
||||
class JavaNativeModule : public NativeModule {
|
||||
public:
|
||||
JavaNativeModule(jni::alias_ref<JavaModuleWrapper::javaobject> wrapper)
|
||||
: wrapper_(make_global(wrapper)) {}
|
||||
|
||||
std::string getName() override {
|
||||
static auto getNameMethod = wrapper_->getClass()->getMethod<jstring()>("getName");
|
||||
return getNameMethod(wrapper_)->toStdString();
|
||||
}
|
||||
|
||||
std::vector<MethodDescriptor> getMethods() override {
|
||||
static auto getMDMethod =
|
||||
wrapper_->getClass()->getMethod<jni::JList<JMethodDescriptor::javaobject>::javaobject()>(
|
||||
"getMethodDescriptors");
|
||||
|
||||
std::vector<MethodDescriptor> ret;
|
||||
auto descs = getMDMethod(wrapper_);
|
||||
for (const auto& desc : *descs) {
|
||||
static auto nameField =
|
||||
JMethodDescriptor::javaClassStatic()->getField<jstring>("name");
|
||||
static auto typeField =
|
||||
JMethodDescriptor::javaClassStatic()->getField<jstring>("type");
|
||||
|
||||
ret.emplace_back(
|
||||
desc->getFieldValue(nameField)->toStdString(),
|
||||
desc->getFieldValue(typeField)->toStdString()
|
||||
);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
folly::dynamic getConstants() override {
|
||||
static auto constantsMethod =
|
||||
wrapper_->getClass()->getMethod<NativeArray::javaobject()>("getConstants");
|
||||
auto constants = constantsMethod(wrapper_);
|
||||
if (!constants) {
|
||||
return nullptr;
|
||||
} else {
|
||||
// See JavaModuleWrapper#getConstants for the other side of this hack.
|
||||
return cthis(constants)->array[0];
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool supportsWebWorkers() override {
|
||||
static auto supportsWebWorkersMethod =
|
||||
wrapper_->getClass()->getMethod<jboolean()>("supportsWebWorkers");
|
||||
return supportsWebWorkersMethod(wrapper_);
|
||||
}
|
||||
|
||||
void invoke(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) override {
|
||||
static auto invokeMethod =
|
||||
wrapper_->getClass()->getMethod<void(JExecutorToken::javaobject, jint, ReadableNativeArray::javaobject)>("invoke");
|
||||
invokeMethod(wrapper_, JExecutorToken::extractJavaPartFromToken(token).get(), static_cast<jint>(reactMethodId),
|
||||
ReadableNativeArray::newObjectCxxArgs(std::move(params)).get());
|
||||
}
|
||||
|
||||
MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) override {
|
||||
throw std::runtime_error("Unsupported operation.");
|
||||
}
|
||||
|
||||
private:
|
||||
jni::global_ref<JavaModuleWrapper::javaobject> wrapper_;
|
||||
};
|
||||
|
||||
class NewJavaNativeModule : public NativeModule {
|
||||
public:
|
||||
NewJavaNativeModule(std::weak_ptr<Instance> instance, jni::alias_ref<JavaModuleWrapper::javaobject> wrapper)
|
||||
: instance_(std::move(instance)),
|
||||
wrapper_(make_global(wrapper)),
|
||||
module_(make_global(wrapper->getModule())) {
|
||||
auto descs = wrapper_->getMethodDescriptors();
|
||||
std::string moduleName = getName();
|
||||
methods_.reserve(descs->size());
|
||||
|
||||
for (const auto& desc : *descs) {
|
||||
auto type = desc->getType();
|
||||
auto name = desc->getName();
|
||||
methods_.emplace_back(
|
||||
desc->getMethod(),
|
||||
desc->getSignature(),
|
||||
moduleName + "." + name,
|
||||
type == "syncHook");
|
||||
|
||||
methodDescriptors_.emplace_back(name, type);
|
||||
}
|
||||
}
|
||||
|
||||
std::string getName() override {
|
||||
static auto getNameMethod = wrapper_->getClass()->getMethod<jstring()>("getName");
|
||||
return getNameMethod(wrapper_)->toStdString();
|
||||
}
|
||||
|
||||
std::vector<MethodDescriptor> getMethods() override {
|
||||
return methodDescriptors_;
|
||||
}
|
||||
|
||||
folly::dynamic getConstants() override {
|
||||
static auto constantsMethod =
|
||||
wrapper_->getClass()->getMethod<NativeArray::javaobject()>("getConstants");
|
||||
auto constants = constantsMethod(wrapper_);
|
||||
if (!constants) {
|
||||
return nullptr;
|
||||
} else {
|
||||
// See JavaModuleWrapper#getConstants for the other side of this hack.
|
||||
return cthis(constants)->array[0];
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool supportsWebWorkers() override {
|
||||
static auto supportsWebWorkersMethod =
|
||||
wrapper_->getClass()->getMethod<jboolean()>("supportsWebWorkers");
|
||||
return supportsWebWorkersMethod(wrapper_);
|
||||
}
|
||||
|
||||
void invoke(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) override {
|
||||
if (reactMethodId >= methods_.size()) {
|
||||
throw std::invalid_argument(
|
||||
folly::to<std::string>("methodId ", reactMethodId, " out of range [0..", methods_.size(), "]"));
|
||||
}
|
||||
CHECK(!methods_[reactMethodId].isSyncHook()) << "Trying to invoke a synchronous hook asynchronously";
|
||||
invokeInner(token, reactMethodId, std::move(params));
|
||||
}
|
||||
|
||||
MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) override {
|
||||
if (reactMethodId >= methods_.size()) {
|
||||
throw std::invalid_argument(
|
||||
folly::to<std::string>("methodId ", reactMethodId, " out of range [0..", methods_.size(), "]"));
|
||||
}
|
||||
CHECK(methods_[reactMethodId].isSyncHook()) << "Trying to invoke a asynchronous method as synchronous hook";
|
||||
return invokeInner(token, reactMethodId, std::move(params));
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<Instance> instance_;
|
||||
jni::global_ref<JavaModuleWrapper::javaobject> wrapper_;
|
||||
jni::global_ref<JBaseJavaModule::javaobject> module_;
|
||||
std::vector<MethodInvoker> methods_;
|
||||
std::vector<MethodDescriptor> methodDescriptors_;
|
||||
|
||||
MethodCallResult invokeInner(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) {
|
||||
if (!params.isArray()) {
|
||||
throw std::invalid_argument(
|
||||
folly::to<std::string>("method parameters should be array, but are ", params.typeName()));
|
||||
}
|
||||
return methods_[reactMethodId].invoke(instance_, module_.get(), token, params);
|
||||
}
|
||||
};
|
||||
|
||||
class CxxNativeModule : public NativeModule {
|
||||
public:
|
||||
CxxNativeModule(std::weak_ptr<Instance> instance,
|
||||
std::unique_ptr<CxxModule> module)
|
||||
: instance_(instance)
|
||||
, module_(std::move(module))
|
||||
, methods_(module_->getMethods()) {}
|
||||
|
||||
std::string getName() override {
|
||||
return module_->getName();
|
||||
}
|
||||
|
||||
virtual std::vector<MethodDescriptor> getMethods() override {
|
||||
// Same as MessageQueue.MethodTypes.remote
|
||||
static const auto kMethodTypeRemote = "remote";
|
||||
|
||||
std::vector<MethodDescriptor> descs;
|
||||
for (auto& method : methods_) {
|
||||
descs.emplace_back(method.name, kMethodTypeRemote);
|
||||
}
|
||||
return descs;
|
||||
}
|
||||
|
||||
virtual folly::dynamic getConstants() override {
|
||||
folly::dynamic constants = folly::dynamic::object();
|
||||
for (auto& pair : module_->getConstants()) {
|
||||
constants.insert(std::move(pair.first), std::move(pair.second));
|
||||
}
|
||||
return constants;
|
||||
}
|
||||
|
||||
virtual bool supportsWebWorkers() override {
|
||||
// TODO(andrews): web worker support in cxxmodules
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO mhorowitz: do we need initialize()/onCatalystInstanceDestroy() in C++
|
||||
// or only Java?
|
||||
virtual void invoke(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) override {
|
||||
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 (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 = makeCallback(instance_, token, params[params.size() - 1]);
|
||||
} else if (method.callbacks == 2) {
|
||||
first = makeCallback(instance_, token, params[params.size() - 2]);
|
||||
second = makeCallback(instance_, token, params[params.size() - 1]);
|
||||
}
|
||||
|
||||
params.resize(params.size() - method.callbacks);
|
||||
|
||||
// I've got a few flawed options here. I can let the C++ exception
|
||||
// propogate, 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
|
||||
|
||||
try {
|
||||
method.func(std::move(params), first, second);
|
||||
} catch (const facebook::xplat::JsArgumentException& ex) {
|
||||
// This ends up passed to the onNativeException callback.
|
||||
throw;
|
||||
} catch (...) {
|
||||
// This means some C++ code is buggy. As above, we fail hard so the C++
|
||||
// developer can debug and fix it.
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int hookId, folly::dynamic&& args) override {
|
||||
throw std::runtime_error("Not supported");
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<Instance> instance_;
|
||||
std::unique_ptr<CxxModule> module_;
|
||||
std::vector<CxxModule::Method> methods_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
jni::local_ref<JReflectMethod::javaobject> JMethodDescriptor::getMethod() const {
|
||||
static auto method = javaClassStatic()->getField<JReflectMethod::javaobject>("method");
|
||||
return getFieldValue(method);
|
||||
}
|
||||
|
||||
std::string JMethodDescriptor::getSignature() const {
|
||||
static auto signature = javaClassStatic()->getField<jstring>("signature");
|
||||
return getFieldValue(signature)->toStdString();
|
||||
}
|
||||
|
||||
std::string JMethodDescriptor::getName() const {
|
||||
static auto name = javaClassStatic()->getField<jstring>("name");
|
||||
return getFieldValue(name)->toStdString();
|
||||
}
|
||||
|
||||
std::string JMethodDescriptor::getType() const {
|
||||
static auto type = javaClassStatic()->getField<jstring>("type");
|
||||
return getFieldValue(type)->toStdString();
|
||||
}
|
||||
|
||||
void ModuleRegistryHolder::registerNatives() {
|
||||
registerHybrid({
|
||||
makeNativeMethod("initHybrid", ModuleRegistryHolder::initHybrid),
|
||||
});
|
||||
}
|
||||
|
||||
ModuleRegistryHolder::ModuleRegistryHolder(
|
||||
CatalystInstanceImpl* catalystInstanceImpl,
|
||||
jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
|
||||
jni::alias_ref<jni::JCollection<CxxModuleWrapper::javaobject>::javaobject> cxxModules) {
|
||||
std::vector<std::unique_ptr<NativeModule>> modules;
|
||||
std::weak_ptr<Instance> winstance(catalystInstanceImpl->getInstance());
|
||||
for (const auto& jm : *javaModules) {
|
||||
modules.emplace_back(folly::make_unique<JavaNativeModule>(jm));
|
||||
}
|
||||
for (const auto& cm : *cxxModules) {
|
||||
modules.emplace_back(
|
||||
folly::make_unique<CxxNativeModule>(winstance, std::move(cthis(cm)->getModule())));
|
||||
}
|
||||
|
||||
registry_ = std::make_shared<ModuleRegistry>(std::move(modules));
|
||||
}
|
||||
|
||||
Callback makeCallback(std::weak_ptr<Instance> instance, ExecutorToken token, const folly::dynamic& callbackId) {
|
||||
if (!callbackId.isInt()) {
|
||||
throw std::invalid_argument("Expected callback(s) as final argument");
|
||||
}
|
||||
|
||||
auto id = callbackId.getInt();
|
||||
return [winstance = std::move(instance), token, id](folly::dynamic args) {
|
||||
if (auto instance = winstance.lock()) {
|
||||
jni::ThreadScope guard;
|
||||
instance->callJSCallback(token, id, std::move(args));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
94
ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryHolder.h
Normal file
94
ReactAndroid/src/main/jni/xreact/jni/ModuleRegistryHolder.h
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
|
||||
#include <cxxreact/ModuleRegistry.h>
|
||||
#include <react/jni/CxxModuleWrapper.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class Instance;
|
||||
class CatalystInstanceImpl;
|
||||
|
||||
struct JReflectMethod : public jni::JavaClass<JReflectMethod> {
|
||||
static constexpr auto kJavaDescriptor = "Ljava/lang/reflect/Method;";
|
||||
|
||||
jmethodID getMethodID() {
|
||||
auto id = jni::Environment::current()->FromReflectedMethod(self());
|
||||
jni::throwPendingJniExceptionAsCppException();
|
||||
return id;
|
||||
}
|
||||
};
|
||||
|
||||
struct JMethodDescriptor : public jni::JavaClass<JMethodDescriptor> {
|
||||
static constexpr auto kJavaDescriptor =
|
||||
"Lcom/facebook/react/cxxbridge/JavaModuleWrapper$MethodDescriptor;";
|
||||
|
||||
jni::local_ref<JReflectMethod::javaobject> getMethod() const;
|
||||
std::string getSignature() const;
|
||||
std::string getName() const;
|
||||
std::string getType() const;
|
||||
};
|
||||
|
||||
struct JBaseJavaModule : public jni::JavaClass<JBaseJavaModule> {
|
||||
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/BaseJavaModule;";
|
||||
};
|
||||
|
||||
struct JavaModuleWrapper : jni::JavaClass<JavaModuleWrapper> {
|
||||
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/cxxbridge/JavaModuleWrapper;";
|
||||
|
||||
jni::local_ref<JBaseJavaModule::javaobject> getModule() {
|
||||
static auto getModule = javaClassStatic()->getMethod<JBaseJavaModule::javaobject()>("getModule");
|
||||
return getModule(self());
|
||||
}
|
||||
|
||||
jni::local_ref<jni::JList<JMethodDescriptor::javaobject>::javaobject> getMethodDescriptors() {
|
||||
static auto getMethods =
|
||||
getClass()->getMethod<jni::JList<JMethodDescriptor::javaobject>::javaobject()>("getMethodDescriptors");
|
||||
return getMethods(self());
|
||||
}
|
||||
|
||||
jni::local_ref<jni::JList<JMethodDescriptor::javaobject>::javaobject> newGetMethodDescriptors() {
|
||||
static auto getMethods =
|
||||
getClass()->getMethod<jni::JList<JMethodDescriptor::javaobject>::javaobject()>("newGetMethodDescriptors");
|
||||
return getMethods(self());
|
||||
}
|
||||
};
|
||||
|
||||
class ModuleRegistryHolder : public jni::HybridClass<ModuleRegistryHolder> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/cxxbridge/ModuleRegistryHolder;";
|
||||
|
||||
std::shared_ptr<ModuleRegistry> getModuleRegistry() {
|
||||
return registry_;
|
||||
}
|
||||
|
||||
static jni::local_ref<jhybriddata> initHybrid(
|
||||
jni::alias_ref<jclass>,
|
||||
CatalystInstanceImpl* catalystInstanceImpl,
|
||||
jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
|
||||
jni::alias_ref<jni::JCollection<CxxModuleWrapper::javaobject>::javaobject> cxxModules) {
|
||||
return makeCxxInstance(catalystInstanceImpl, javaModules, cxxModules);
|
||||
}
|
||||
|
||||
static void registerNatives();
|
||||
|
||||
private:
|
||||
friend HybridBase;
|
||||
ModuleRegistryHolder(
|
||||
CatalystInstanceImpl* catalystInstanceImpl,
|
||||
jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
|
||||
jni::alias_ref<jni::JCollection<CxxModuleWrapper::javaobject>::javaobject> cxxModules);
|
||||
|
||||
facebook::xplat::module::CxxModule::Callback makeCallback(const folly::dynamic& callbackId);
|
||||
|
||||
std::shared_ptr<ModuleRegistry> registry_;
|
||||
};
|
||||
|
||||
using Callback = std::function<void(folly::dynamic)>;
|
||||
Callback makeCallback(std::weak_ptr<Instance> instance, ExecutorToken token, const folly::dynamic& callbackId);
|
||||
|
||||
}}
|
||||
186
ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp
Normal file
186
ReactAndroid/src/main/jni/xreact/jni/OnLoad.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include <folly/dynamic.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <fb/log.h>
|
||||
#include <cxxreact/Executor.h>
|
||||
#include <cxxreact/JSCExecutor.h>
|
||||
#include <cxxreact/Platform.h>
|
||||
#include <cxxreact/Value.h>
|
||||
#include <react/jni/ReadableNativeArray.h>
|
||||
#include "CatalystInstanceImpl.h"
|
||||
#include "JavaScriptExecutorHolder.h"
|
||||
#include "JSCPerfLogging.h"
|
||||
#include "JSLoader.h"
|
||||
#include "ModuleRegistryHolder.h"
|
||||
#include "ProxyExecutor.h"
|
||||
#include "WebWorkers.h"
|
||||
#include "JCallback.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
namespace {
|
||||
|
||||
static std::string getApplicationDir(const char* methodName) {
|
||||
// Get the Application Context object
|
||||
auto getApplicationClass = findClassLocal(
|
||||
"com/facebook/react/common/ApplicationHolder");
|
||||
auto getApplicationMethod = getApplicationClass->getStaticMethod<jobject()>(
|
||||
"getApplication",
|
||||
"()Landroid/app/Application;"
|
||||
);
|
||||
auto application = getApplicationMethod(getApplicationClass);
|
||||
|
||||
// Get getCacheDir() from the context
|
||||
auto getDirMethod = findClassLocal("android/app/Application")
|
||||
->getMethod<jobject()>(methodName,
|
||||
"()Ljava/io/File;"
|
||||
);
|
||||
auto dirObj = getDirMethod(application);
|
||||
|
||||
// Call getAbsolutePath() on the returned File object
|
||||
auto getAbsolutePathMethod = findClassLocal("java/io/File")
|
||||
->getMethod<jstring()>("getAbsolutePath");
|
||||
return getAbsolutePathMethod(dirObj)->toStdString();
|
||||
}
|
||||
|
||||
static std::string getApplicationCacheDir() {
|
||||
return getApplicationDir("getCacheDir");
|
||||
}
|
||||
|
||||
static std::string getApplicationPersistentDir() {
|
||||
return getApplicationDir("getFilesDir");
|
||||
}
|
||||
|
||||
static JSValueRef nativeLoggingHook(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[], JSValueRef *exception) {
|
||||
android_LogPriority logLevel = ANDROID_LOG_DEBUG;
|
||||
if (argumentCount > 1) {
|
||||
int level = (int) JSValueToNumber(ctx, arguments[1], NULL);
|
||||
// The lowest log level we get from JS is 0. We shift and cap it to be
|
||||
// in the range the Android logging method expects.
|
||||
logLevel = std::min(
|
||||
static_cast<android_LogPriority>(level + ANDROID_LOG_DEBUG),
|
||||
ANDROID_LOG_FATAL);
|
||||
}
|
||||
if (argumentCount > 0) {
|
||||
JSStringRef jsString = JSValueToStringCopy(ctx, arguments[0], NULL);
|
||||
String message = String::adopt(jsString);
|
||||
FBLOG_PRI(logLevel, "ReactNativeJS", "%s", message.str().c_str());
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativePerformanceNow(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[], JSValueRef *exception) {
|
||||
static const int64_t NANOSECONDS_IN_SECOND = 1000000000LL;
|
||||
static const int64_t NANOSECONDS_IN_MILLISECOND = 1000000LL;
|
||||
|
||||
// This is equivalent to android.os.SystemClock.elapsedRealtime() in native
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &now);
|
||||
int64_t nano = now.tv_sec * NANOSECONDS_IN_SECOND + now.tv_nsec;
|
||||
return JSValueMakeNumber(ctx, (nano / (double)NANOSECONDS_IN_MILLISECOND));
|
||||
}
|
||||
|
||||
class JSCJavaScriptExecutorHolder : public HybridClass<JSCJavaScriptExecutorHolder,
|
||||
JavaScriptExecutorHolder> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/cxxbridge/JSCJavaScriptExecutor;";
|
||||
|
||||
static local_ref<jhybriddata> initHybrid(alias_ref<jclass>, ReadableNativeArray* jscConfigArray) {
|
||||
// See JSCJavaScriptExecutor.Factory() for the other side of this hack.
|
||||
folly::dynamic jscConfigMap = jscConfigArray->array[0];
|
||||
jscConfigMap["PersistentDirectory"] = getApplicationPersistentDir();
|
||||
return makeCxxInstance(
|
||||
std::make_shared<JSCExecutorFactory>(getApplicationCacheDir(), std::move(jscConfigMap)));
|
||||
}
|
||||
|
||||
static void registerNatives() {
|
||||
registerHybrid({
|
||||
makeNativeMethod("initHybrid", JSCJavaScriptExecutorHolder::initHybrid),
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
friend HybridBase;
|
||||
using HybridBase::HybridBase;
|
||||
};
|
||||
|
||||
struct JavaJSExecutor : public JavaClass<JavaJSExecutor> {
|
||||
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/JavaJSExecutor;";
|
||||
};
|
||||
|
||||
class ProxyJavaScriptExecutorHolder : public HybridClass<ProxyJavaScriptExecutorHolder,
|
||||
JavaScriptExecutorHolder> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/cxxbridge/ProxyJavaScriptExecutor;";
|
||||
|
||||
static local_ref<jhybriddata> initHybrid(
|
||||
alias_ref<jclass>, alias_ref<JavaJSExecutor::javaobject> executorInstance) {
|
||||
return makeCxxInstance(
|
||||
std::make_shared<ProxyExecutorOneTimeFactory>(
|
||||
make_global(executorInstance)));
|
||||
}
|
||||
|
||||
static void registerNatives() {
|
||||
registerHybrid({
|
||||
makeNativeMethod("initHybrid", ProxyJavaScriptExecutorHolder::initHybrid),
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
friend HybridBase;
|
||||
using HybridBase::HybridBase;
|
||||
};
|
||||
|
||||
|
||||
class JReactMarker : public JavaClass<JReactMarker> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/ReactMarker;";
|
||||
static void logMarker(const std::string& marker) {
|
||||
static auto cls = javaClassStatic();
|
||||
static auto meth = cls->getStaticMethod<void(std::string)>("logMarker");
|
||||
meth(cls, marker);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
return initialize(vm, [] {
|
||||
// Inject some behavior into react/
|
||||
ReactMarker::logMarker = JReactMarker::logMarker;
|
||||
WebWorkerUtil::createWebWorkerThread = WebWorkers::createWebWorkerThread;
|
||||
WebWorkerUtil::loadScriptFromAssets =
|
||||
[] (const std::string& assetName) {
|
||||
return loadScriptFromAssets(assetName);
|
||||
};
|
||||
WebWorkerUtil::loadScriptFromNetworkSync = WebWorkers::loadScriptFromNetworkSync;
|
||||
PerfLogging::installNativeHooks = addNativePerfLoggingHooks;
|
||||
JSNativeHooks::loggingHook = nativeLoggingHook;
|
||||
JSNativeHooks::nowHook = nativePerformanceNow;
|
||||
JSCJavaScriptExecutorHolder::registerNatives();
|
||||
ProxyJavaScriptExecutorHolder::registerNatives();
|
||||
CatalystInstanceImpl::registerNatives();
|
||||
ModuleRegistryHolder::registerNatives();
|
||||
CxxModuleWrapper::registerNatives();
|
||||
JCallbackImpl::registerNatives();
|
||||
registerJSLoaderNatives();
|
||||
});
|
||||
}
|
||||
|
||||
}}
|
||||
14
ReactAndroid/src/main/jni/xreact/jni/OnLoad.h
Normal file
14
ReactAndroid/src/main/jni/xreact/jni/OnLoad.h
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
#include <jni/Countable.h>
|
||||
#include <cxxreact/Executor.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
jmethodID getLogMarkerMethod();
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
116
ReactAndroid/src/main/jni/xreact/jni/ProxyExecutor.cpp
Normal file
116
ReactAndroid/src/main/jni/xreact/jni/ProxyExecutor.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "ProxyExecutor.h"
|
||||
|
||||
#include <fb/assert.h>
|
||||
#include <fb/Environment.h>
|
||||
#include <jni/LocalReference.h>
|
||||
#include <jni/LocalString.h>
|
||||
#include <folly/json.h>
|
||||
#include <folly/Memory.h>
|
||||
|
||||
#include <cxxreact/SystraceSection.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
const auto EXECUTOR_BASECLASS = "com/facebook/react/bridge/JavaJSExecutor";
|
||||
|
||||
static std::string executeJSCallWithProxy(
|
||||
jobject executor,
|
||||
const std::string& methodName,
|
||||
const std::vector<folly::dynamic>& arguments) {
|
||||
static auto executeJSCall =
|
||||
jni::findClassStatic(EXECUTOR_BASECLASS)->getMethod<jstring(jstring, jstring)>("executeJSCall");
|
||||
|
||||
auto result = executeJSCall(
|
||||
executor,
|
||||
jni::make_jstring(methodName).get(),
|
||||
jni::make_jstring(folly::toJson(arguments).c_str()).get());
|
||||
return result->toString();
|
||||
}
|
||||
|
||||
std::unique_ptr<JSExecutor> ProxyExecutorOneTimeFactory::createJSExecutor(
|
||||
std::shared_ptr<ExecutorDelegate> delegate, std::shared_ptr<MessageQueueThread>) {
|
||||
return folly::make_unique<ProxyExecutor>(std::move(m_executor), delegate);
|
||||
}
|
||||
|
||||
ProxyExecutor::ProxyExecutor(jni::global_ref<jobject>&& executorInstance,
|
||||
std::shared_ptr<ExecutorDelegate> delegate)
|
||||
: m_executor(std::move(executorInstance))
|
||||
, m_delegate(delegate) {
|
||||
|
||||
folly::dynamic nativeModuleConfig = folly::dynamic::array;
|
||||
|
||||
{
|
||||
SystraceSection s("collectNativeModuleDescriptions");
|
||||
for (const auto& name : delegate->moduleNames()) {
|
||||
nativeModuleConfig.push_back(delegate->getModuleConfig(name));
|
||||
}
|
||||
}
|
||||
|
||||
folly::dynamic config =
|
||||
folly::dynamic::object
|
||||
("remoteModuleConfig", std::move(nativeModuleConfig));
|
||||
|
||||
SystraceSection t("setGlobalVariable");
|
||||
setGlobalVariable(
|
||||
"__fbBatchedBridgeConfig",
|
||||
folly::make_unique<JSBigStdString>(folly::toJson(config)));
|
||||
}
|
||||
|
||||
ProxyExecutor::~ProxyExecutor() {
|
||||
m_executor.reset();
|
||||
}
|
||||
|
||||
void ProxyExecutor::loadApplicationScript(
|
||||
std::unique_ptr<const JSBigString>,
|
||||
std::string sourceURL) {
|
||||
static auto loadApplicationScript =
|
||||
jni::findClassStatic(EXECUTOR_BASECLASS)->getMethod<void(jstring)>("loadApplicationScript");
|
||||
|
||||
// The proxy ignores the script data passed in.
|
||||
|
||||
loadApplicationScript(
|
||||
m_executor.get(),
|
||||
jni::make_jstring(sourceURL).get());
|
||||
executeJSCallWithProxy(m_executor.get(), "flushedQueue", std::vector<folly::dynamic>());
|
||||
}
|
||||
|
||||
void ProxyExecutor::setJSModulesUnbundle(std::unique_ptr<JSModulesUnbundle>) {
|
||||
jni::throwNewJavaException(
|
||||
"java/lang/UnsupportedOperationException",
|
||||
"Loading application unbundles is not supported for proxy executors");
|
||||
}
|
||||
|
||||
void ProxyExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
|
||||
std::vector<folly::dynamic> call{
|
||||
moduleId,
|
||||
methodId,
|
||||
std::move(arguments),
|
||||
};
|
||||
std::string result = executeJSCallWithProxy(m_executor.get(), "callFunctionReturnFlushedQueue", std::move(call));
|
||||
m_delegate->callNativeModules(*this, result, true);
|
||||
}
|
||||
|
||||
void ProxyExecutor::invokeCallback(const double callbackId, const folly::dynamic& arguments) {
|
||||
std::vector<folly::dynamic> call{
|
||||
(double) callbackId,
|
||||
std::move(arguments)
|
||||
};
|
||||
std::string result = executeJSCallWithProxy(m_executor.get(), "invokeCallbackAndReturnFlushedQueue", std::move(call));
|
||||
m_delegate->callNativeModules(*this, result, true);
|
||||
}
|
||||
|
||||
void ProxyExecutor::setGlobalVariable(std::string propName,
|
||||
std::unique_ptr<const JSBigString> jsonValue) {
|
||||
static auto setGlobalVariable =
|
||||
jni::findClassStatic(EXECUTOR_BASECLASS)->getMethod<void(jstring, jstring)>("setGlobalVariable");
|
||||
|
||||
setGlobalVariable(
|
||||
m_executor.get(),
|
||||
jni::make_jstring(propName).get(),
|
||||
jni::make_jstring(jsonValue->c_str()).get());
|
||||
}
|
||||
|
||||
} }
|
||||
56
ReactAndroid/src/main/jni/xreact/jni/ProxyExecutor.h
Normal file
56
ReactAndroid/src/main/jni/xreact/jni/ProxyExecutor.h
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cxxreact/Executor.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <jni.h>
|
||||
#include <jni/GlobalReference.h>
|
||||
#include "OnLoad.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/**
|
||||
* This executor factory can only create a single executor instance because it moves
|
||||
* executorInstance global reference to the executor instance it creates.
|
||||
*/
|
||||
class ProxyExecutorOneTimeFactory : public JSExecutorFactory {
|
||||
public:
|
||||
ProxyExecutorOneTimeFactory(jni::global_ref<jobject>&& executorInstance) :
|
||||
m_executor(std::move(executorInstance)) {}
|
||||
virtual std::unique_ptr<JSExecutor> createJSExecutor(
|
||||
std::shared_ptr<ExecutorDelegate> delegate,
|
||||
std::shared_ptr<MessageQueueThread> queue) override;
|
||||
|
||||
private:
|
||||
jni::global_ref<jobject> m_executor;
|
||||
};
|
||||
|
||||
class ProxyExecutor : public JSExecutor {
|
||||
public:
|
||||
ProxyExecutor(jni::global_ref<jobject>&& executorInstance,
|
||||
std::shared_ptr<ExecutorDelegate> delegate);
|
||||
virtual ~ProxyExecutor() override;
|
||||
virtual void loadApplicationScript(
|
||||
std::unique_ptr<const JSBigString> script,
|
||||
std::string sourceURL) override;
|
||||
virtual void setJSModulesUnbundle(
|
||||
std::unique_ptr<JSModulesUnbundle> bundle) 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;
|
||||
|
||||
private:
|
||||
jni::global_ref<jobject> m_executor;
|
||||
std::shared_ptr<ExecutorDelegate> m_delegate;
|
||||
};
|
||||
|
||||
} }
|
||||
50
ReactAndroid/src/main/jni/xreact/jni/WebWorkers.h
Normal file
50
ReactAndroid/src/main/jni/xreact/jni/WebWorkers.h
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <jni.h>
|
||||
#include <folly/Memory.h>
|
||||
|
||||
#include "JMessageQueueThread.h"
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class WebWorkers : public JavaClass<WebWorkers> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/webworkers/WebWorkers;";
|
||||
|
||||
static std::unique_ptr<JMessageQueueThread> createWebWorkerThread(int id, MessageQueueThread *ownerMessageQueueThread) {
|
||||
static auto method = WebWorkers::javaClassStatic()->
|
||||
getStaticMethod<JavaMessageQueueThread::javaobject(jint, JavaMessageQueueThread::javaobject)>("createWebWorkerThread");
|
||||
auto res = method(WebWorkers::javaClassStatic(), id, static_cast<JMessageQueueThread*>(ownerMessageQueueThread)->jobj());
|
||||
return folly::make_unique<JMessageQueueThread>(res);
|
||||
}
|
||||
|
||||
static std::string loadScriptFromNetworkSync(const std::string& url, const std::string& tempfileName) {
|
||||
static auto method = WebWorkers::javaClassStatic()->
|
||||
getStaticMethod<void(jstring, jstring)>("downloadScriptToFileSync");
|
||||
method(
|
||||
WebWorkers::javaClassStatic(),
|
||||
jni::make_jstring(url).get(),
|
||||
jni::make_jstring(tempfileName).get());
|
||||
|
||||
std::ifstream tempFile(tempfileName);
|
||||
if (!tempFile.good()) {
|
||||
throw std::runtime_error("Didn't find worker script file at " + tempfileName);
|
||||
}
|
||||
std::stringstream buffer;
|
||||
buffer << tempFile.rdbuf();
|
||||
std::remove(tempfileName.c_str());
|
||||
return buffer.str();
|
||||
}
|
||||
};
|
||||
|
||||
} }
|
||||
21
ReactAndroid/src/main/jni/xreact/perftests/BUCK
Normal file
21
ReactAndroid/src/main/jni/xreact/perftests/BUCK
Normal file
@@ -0,0 +1,21 @@
|
||||
cxx_library(
|
||||
name = 'perftests',
|
||||
srcs = [ 'OnLoad.cpp' ],
|
||||
soname = 'libnativereactperftests.so',
|
||||
compiler_flags = [
|
||||
'-fexceptions',
|
||||
],
|
||||
deps = [
|
||||
'//native:base',
|
||||
'//native/fb:fb',
|
||||
'//xplat/folly:molly',
|
||||
'//xplat/react/module:module',
|
||||
],
|
||||
visibility = [
|
||||
'//instrumentation_tests/com/facebook/react/...',
|
||||
],
|
||||
)
|
||||
|
||||
project_config(
|
||||
src_target = ':perftests',
|
||||
)
|
||||
156
ReactAndroid/src/main/jni/xreact/perftests/OnLoad.cpp
Normal file
156
ReactAndroid/src/main/jni/xreact/perftests/OnLoad.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include <fb/log.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <Module/CxxModule.h>
|
||||
#include <Module/JsArgumentHelpers.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
using facebook::jni::alias_ref;
|
||||
|
||||
namespace {
|
||||
|
||||
// This is a wrapper around the Java proxy to the javascript module. This
|
||||
// allows us to call functions on the js module from c++.
|
||||
class JavaJSModule : public jni::JavaClass<JavaJSModule> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor =
|
||||
"Lcom/facebook/react/CatalystBridgeBenchmarks$BridgeBenchmarkModule;";
|
||||
|
||||
static void bounceCxx(alias_ref<javaobject> obj, int iters) {
|
||||
static auto method = javaClassLocal()->getMethod<void(jint)>("bounceCxx");
|
||||
method(obj, iters);
|
||||
}
|
||||
|
||||
static void bounceArgsCxx(
|
||||
alias_ref<javaobject> obj,
|
||||
int iters,
|
||||
int a, int b,
|
||||
double x, double y,
|
||||
const std::string& s, const std::string& t) {
|
||||
static auto method =
|
||||
javaClassLocal()->getMethod<void(jint, jint, jint, jdouble, jdouble, jstring, jstring)>("bounceArgsCxx");
|
||||
method(obj, iters, a, b, x, y, jni::make_jstring(s).get(), jni::make_jstring(t).get());
|
||||
}
|
||||
};
|
||||
|
||||
// This is just the test instance itself. Used only to countdown the latch.
|
||||
class CatalystBridgeBenchmarks : public jni::JavaClass<CatalystBridgeBenchmarks> {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor =
|
||||
"Lcom/facebook/react/CatalystBridgeBenchmarks;";
|
||||
|
||||
static void countDown(alias_ref<javaobject> obj) {
|
||||
static auto method = javaClassLocal()->getMethod<void()>("countDown");
|
||||
method(obj);
|
||||
}
|
||||
};
|
||||
|
||||
// This is the shared data for two cxx bounce threads.
|
||||
struct Data {
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
bool leftActive;
|
||||
Data() : leftActive(true) {}
|
||||
};
|
||||
Data data;
|
||||
|
||||
void runBounce(jni::alias_ref<jclass>, bool isLeft, int iters) {
|
||||
for (int i = 0; i < iters; i++) {
|
||||
std::unique_lock<std::mutex> lk(data.m);
|
||||
data.cv.wait(lk, [&]{ return data.leftActive == isLeft; });
|
||||
data.leftActive = !isLeft;
|
||||
data.cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
static jni::global_ref<JavaJSModule::javaobject> jsModule;
|
||||
static jni::global_ref<CatalystBridgeBenchmarks::javaobject> javaTestInstance;
|
||||
|
||||
class CxxBenchmarkModule : public xplat::module::CxxModule {
|
||||
public:
|
||||
virtual std::string getName() override {
|
||||
return "CxxBenchmarkModule";
|
||||
}
|
||||
|
||||
virtual auto getConstants() -> std::map<std::string, folly::dynamic> override {
|
||||
return std::map<std::string, folly::dynamic>();
|
||||
}
|
||||
|
||||
virtual auto getMethods() -> std::vector<Method> override {
|
||||
return std::vector<Method>{
|
||||
Method("bounce", [this] (folly::dynamic args) {
|
||||
this->bounce(xplat::jsArgAsInt(args, 0));
|
||||
}),
|
||||
Method("bounceArgs", [this] (folly::dynamic args) {
|
||||
this->bounceArgs(
|
||||
xplat::jsArgAsInt(args, 0),
|
||||
xplat::jsArgAsInt(args, 1),
|
||||
xplat::jsArgAsInt(args, 2),
|
||||
xplat::jsArgAsDouble(args, 3),
|
||||
xplat::jsArgAsDouble(args, 4),
|
||||
xplat::jsArgAsString(args, 5),
|
||||
xplat::jsArgAsString(args, 6));
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
void bounce(int iters) {
|
||||
if (iters == 0) {
|
||||
CatalystBridgeBenchmarks::countDown(javaTestInstance);
|
||||
} else {
|
||||
JavaJSModule::bounceCxx(jsModule, iters - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void bounceArgs(
|
||||
int iters,
|
||||
int a, int b,
|
||||
double x, double y,
|
||||
const std::string& s, const std::string& t) {
|
||||
if (iters == 0) {
|
||||
CatalystBridgeBenchmarks::countDown(javaTestInstance);
|
||||
} else {
|
||||
JavaJSModule::bounceArgsCxx(jsModule, iters - 1, a, b, x, y, s, t);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void setUp(
|
||||
alias_ref<CatalystBridgeBenchmarks::javaobject> obj,
|
||||
alias_ref<JavaJSModule::javaobject> mod) {
|
||||
javaTestInstance = jni::make_global(obj);
|
||||
jsModule = jni::make_global(mod);
|
||||
}
|
||||
|
||||
void tearDown(
|
||||
alias_ref<CatalystBridgeBenchmarks::javaobject>) {
|
||||
javaTestInstance.reset();
|
||||
jsModule.reset();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" facebook::xplat::module::CxxModule* CxxBenchmarkModule() {
|
||||
return new facebook::react::CxxBenchmarkModule();
|
||||
}
|
||||
|
||||
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
|
||||
return facebook::jni::initialize(vm, [] {
|
||||
facebook::jni::registerNatives(
|
||||
"com/facebook/react/CatalystBridgeBenchmarks", {
|
||||
makeNativeMethod("runNativeBounce", facebook::react::runBounce),
|
||||
makeNativeMethod("nativeSetUp", facebook::react::setUp),
|
||||
makeNativeMethod("nativeTearDown", facebook::react::tearDown),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user