Skip to content

Commit 8301978

Browse files
cjhopmanFacebook Github Bot 2
authored andcommitted
Move more new bridge code into OSS
Reviewed By: mhorowitz Differential Revision: D3296231 fbshipit-source-id: 5a05b1ddacdfecda067a08398dd5652df76c1f14
1 parent 5e91a2a commit 8301978

32 files changed

Lines changed: 2831 additions & 0 deletions
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
include_defs('//ReactAndroid/DEFS')
2+
3+
# We depend on JSC, support the same platforms
4+
SUPPORTED_PLATFORMS = '^android-(armv7|x86)$'
5+
6+
EXPORTED_HEADERS = [
7+
'CxxModuleWrapper.h',
8+
]
9+
10+
cxx_library(
11+
name='jni',
12+
soname = 'libreactnativejnifb.so',
13+
header_namespace = 'react/jni',
14+
supported_platforms_regex = SUPPORTED_PLATFORMS,
15+
deps = JSC_DEPS + [
16+
'//native/fb:fb',
17+
'//native/third-party/android-ndk:android',
18+
'//xplat/folly:molly',
19+
'//xplat/fbsystrace:fbsystrace',
20+
'//xplat/react/module:module',
21+
react_native_target('jni/react/jni:jni'),
22+
react_native_xplat_target('bridge:bridge'),
23+
],
24+
srcs = glob(['*.cpp']),
25+
exported_headers = EXPORTED_HEADERS,
26+
headers = glob(['*.h'], excludes=EXPORTED_HEADERS),
27+
preprocessor_flags = [
28+
'-DLOG_TAG="ReactNativeJNI"',
29+
'-DWITH_FBSYSTRACE=1',
30+
],
31+
compiler_flags = [
32+
'-Wall',
33+
'-Werror',
34+
'-fexceptions',
35+
'-std=c++11',
36+
'-fvisibility=hidden',
37+
'-frtti',
38+
'-Wno-pessimizing-move',
39+
'-Wno-inconsistent-missing-override',
40+
],
41+
visibility = [
42+
'PUBLIC',
43+
],
44+
)
45+
46+
project_config(
47+
src_target = ':jni',
48+
)
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
// Copyright 2004-present Facebook. All Rights Reserved.
2+
3+
#include "CatalystInstanceImpl.h"
4+
5+
#include <mutex>
6+
#include <condition_variable>
7+
8+
#include <folly/dynamic.h>
9+
#include <folly/Memory.h>
10+
11+
#include <fb/log.h>
12+
13+
#include <jni/Countable.h>
14+
#include <jni/LocalReference.h>
15+
16+
#include <react/jni/NativeArray.h>
17+
18+
#include <cxxreact/Instance.h>
19+
#include <cxxreact/MethodCall.h>
20+
#include <cxxreact/ModuleRegistry.h>
21+
22+
#include "JSLoader.h"
23+
#include "JavaScriptExecutorHolder.h"
24+
#include "JniJSModulesUnbundle.h"
25+
#include "ModuleRegistryHolder.h"
26+
#include "JNativeRunnable.h"
27+
28+
using namespace facebook::jni;
29+
30+
namespace facebook {
31+
namespace react {
32+
33+
namespace {
34+
35+
36+
class Exception : public jni::JavaClass<Exception> {
37+
public:
38+
static auto constexpr kJavaDescriptor = "Ljava/lang/Exception;";
39+
};
40+
41+
class JInstanceCallback : public InstanceCallback {
42+
public:
43+
explicit JInstanceCallback(alias_ref<ReactCallback::javaobject> jobj)
44+
: jobj_(make_global(jobj)) {}
45+
46+
void onBatchComplete() override {
47+
static auto method =
48+
ReactCallback::javaClassStatic()->getMethod<void()>("onBatchComplete");
49+
method(jobj_);
50+
}
51+
52+
void incrementPendingJSCalls() override {
53+
static auto method =
54+
ReactCallback::javaClassStatic()->getMethod<void()>("incrementPendingJSCalls");
55+
method(jobj_);
56+
}
57+
58+
void decrementPendingJSCalls() override {
59+
static auto method =
60+
ReactCallback::javaClassStatic()->getMethod<void()>("decrementPendingJSCalls");
61+
method(jobj_);
62+
}
63+
64+
void onNativeException(const std::string& what) override {
65+
static auto exCtor =
66+
Exception::javaClassStatic()->getConstructor<Exception::javaobject(jstring)>();
67+
static auto method =
68+
ReactCallback::javaClassStatic()->getMethod<void(Exception::javaobject)>("onNativeException");
69+
70+
method(jobj_, Exception::javaClassStatic()->newObject(
71+
exCtor, jni::make_jstring(what).get()).get());
72+
}
73+
74+
ExecutorToken createExecutorToken() override {
75+
auto jobj = JExecutorToken::newObjectCxxArgs();
76+
return jobj->cthis()->getExecutorToken(jobj);
77+
}
78+
79+
void onExecutorStopped(ExecutorToken) override {
80+
// TODO(cjhopman): implement this.
81+
}
82+
83+
private:
84+
global_ref<ReactCallback::javaobject> jobj_;
85+
};
86+
87+
}
88+
89+
jni::local_ref<CatalystInstanceImpl::jhybriddata> CatalystInstanceImpl::initHybrid(
90+
jni::alias_ref<jclass>) {
91+
return makeCxxInstance();
92+
}
93+
94+
CatalystInstanceImpl::CatalystInstanceImpl()
95+
: instance_(folly::make_unique<Instance>()) {}
96+
97+
void CatalystInstanceImpl::registerNatives() {
98+
registerHybrid({
99+
makeNativeMethod("initHybrid", CatalystInstanceImpl::initHybrid),
100+
makeNativeMethod("initializeBridge", CatalystInstanceImpl::initializeBridge),
101+
makeNativeMethod("loadScriptFromAssets",
102+
"(Landroid/content/res/AssetManager;Ljava/lang/String;)V",
103+
CatalystInstanceImpl::loadScriptFromAssets),
104+
makeNativeMethod("loadScriptFromFile", CatalystInstanceImpl::loadScriptFromFile),
105+
makeNativeMethod("callJSFunction", CatalystInstanceImpl::callJSFunction),
106+
makeNativeMethod("callJSCallback", CatalystInstanceImpl::callJSCallback),
107+
makeNativeMethod("getMainExecutorToken", CatalystInstanceImpl::getMainExecutorToken),
108+
makeNativeMethod("setGlobalVariable", CatalystInstanceImpl::setGlobalVariable),
109+
makeNativeMethod("supportsProfiling", CatalystInstanceImpl::supportsProfiling),
110+
makeNativeMethod("startProfiler", CatalystInstanceImpl::startProfiler),
111+
makeNativeMethod("stopProfiler", CatalystInstanceImpl::stopProfiler),
112+
});
113+
114+
JNativeRunnable::registerNatives();
115+
}
116+
117+
void CatalystInstanceImpl::initializeBridge(
118+
jni::alias_ref<ReactCallback::javaobject> callback,
119+
// This executor is actually a factory holder.
120+
JavaScriptExecutorHolder* jseh,
121+
jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
122+
jni::alias_ref<JavaMessageQueueThread::javaobject> moduleQueue,
123+
ModuleRegistryHolder* mrh) {
124+
// TODO mhorowitz: how to assert here?
125+
// Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");
126+
127+
// This used to be:
128+
//
129+
// Java CatalystInstanceImpl -> C++ CatalystInstanceImpl -> Bridge -> Bridge::Callback
130+
// --weak--> ReactCallback -> Java CatalystInstanceImpl
131+
//
132+
// Now the weak ref is a global ref. So breaking the loop depends on
133+
// CatalystInstanceImpl#destroy() calling mHybridData.resetNative(), which
134+
// should cause all the C++ pointers to be cleaned up (except C++
135+
// CatalystInstanceImpl might be kept alive for a short time by running
136+
// callbacks). This also means that all native calls need to be pre-checked
137+
// to avoid NPE.
138+
139+
// See the comment in callJSFunction. Once js calls switch to strings, we
140+
// don't need jsModuleDescriptions any more, all the way up and down the
141+
// stack.
142+
143+
instance_->initializeBridge(folly::make_unique<JInstanceCallback>(callback),
144+
jseh->getExecutorFactory(),
145+
folly::make_unique<JMessageQueueThread>(jsQueue),
146+
folly::make_unique<JMessageQueueThread>(moduleQueue),
147+
mrh->getModuleRegistry());
148+
}
149+
150+
void CatalystInstanceImpl::loadScriptFromAssets(jobject assetManager,
151+
const std::string& assetURL) {
152+
const int kAssetsLength = 9; // strlen("assets://");
153+
auto sourceURL = assetURL.substr(kAssetsLength);
154+
155+
auto manager = react::extractAssetManager(assetManager);
156+
auto script = react::loadScriptFromAssets(manager, sourceURL);
157+
if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {
158+
instance_->loadUnbundle(
159+
folly::make_unique<JniJSModulesUnbundle>(manager, sourceURL),
160+
std::move(script),
161+
sourceURL);
162+
} else {
163+
instance_->loadScriptFromString(std::move(script), std::move(sourceURL));
164+
}
165+
}
166+
167+
void CatalystInstanceImpl::loadScriptFromFile(jni::alias_ref<jstring> fileName,
168+
const std::string& sourceURL) {
169+
return instance_->loadScriptFromFile(fileName ? fileName->toStdString() : "",
170+
sourceURL);
171+
}
172+
173+
void CatalystInstanceImpl::callJSFunction(
174+
JExecutorToken* token, std::string module, std::string method, NativeArray* arguments,
175+
const std::string& tracingName) {
176+
// We want to share the C++ code, and on iOS, modules pass module/method
177+
// names as strings all the way through to JS, and there's no way to do
178+
// string -> id mapping on the objc side. So on Android, we convert the
179+
// number to a string, here which gets passed as-is to JS. There, they they
180+
// used as ids if isFinite(), which handles this case, and looked up as
181+
// strings otherwise. Eventually, we'll probably want to modify the stack
182+
// from the JS proxy through here to use strings, too.
183+
instance_->callJSFunction(token->getExecutorToken(nullptr),
184+
module,
185+
method,
186+
std::move(arguments->array),
187+
tracingName);
188+
}
189+
190+
void CatalystInstanceImpl::callJSCallback(JExecutorToken* token, jint callbackId, NativeArray* arguments) {
191+
instance_->callJSCallback(token->getExecutorToken(nullptr), callbackId, std::move(arguments->array));
192+
}
193+
194+
local_ref<JExecutorToken::JavaPart> CatalystInstanceImpl::getMainExecutorToken() {
195+
return JExecutorToken::extractJavaPartFromToken(instance_->getMainExecutorToken());
196+
}
197+
198+
void CatalystInstanceImpl::setGlobalVariable(std::string propName,
199+
std::string&& jsonValue) {
200+
// This is only ever called from Java with short strings, and only
201+
// for testing, so no need to try hard for zero-copy here.
202+
203+
instance_->setGlobalVariable(std::move(propName),
204+
folly::make_unique<JSBigStdString>(std::move(jsonValue)));
205+
}
206+
207+
jboolean CatalystInstanceImpl::supportsProfiling() {
208+
if (!instance_) {
209+
return false;
210+
}
211+
return instance_->supportsProfiling();
212+
}
213+
214+
void CatalystInstanceImpl::startProfiler(const std::string& title) {
215+
if (!instance_) {
216+
return;
217+
}
218+
return instance_->startProfiler(title);
219+
}
220+
221+
void CatalystInstanceImpl::stopProfiler(const std::string& title, const std::string& filename) {
222+
if (!instance_) {
223+
return;
224+
}
225+
return instance_->stopProfiler(title, filename);
226+
}
227+
228+
}}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2004-present Facebook. All Rights Reserved.
2+
3+
#include <string>
4+
5+
#include <folly/Memory.h>
6+
7+
#include <fb/fbjni.h>
8+
9+
#include "JMessageQueueThread.h"
10+
#include "JExecutorToken.h"
11+
12+
namespace facebook {
13+
namespace react {
14+
15+
class Instance;
16+
class JavaScriptExecutorHolder;
17+
class ModuleRegistryHolder;
18+
class NativeArray;
19+
20+
struct ReactCallback : public jni::JavaClass<ReactCallback> {
21+
static constexpr auto kJavaDescriptor =
22+
"Lcom/facebook/react/cxxbridge/ReactCallback;";
23+
};
24+
25+
class CatalystInstanceImpl : public jni::HybridClass<CatalystInstanceImpl> {
26+
public:
27+
static constexpr auto kJavaDescriptor =
28+
"Lcom/facebook/react/cxxbridge/CatalystInstanceImpl;";
29+
30+
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>);
31+
32+
static void registerNatives();
33+
34+
std::shared_ptr<Instance> getInstance() {
35+
return instance_;
36+
}
37+
38+
private:
39+
friend HybridBase;
40+
41+
CatalystInstanceImpl();
42+
43+
void initializeBridge(
44+
jni::alias_ref<ReactCallback::javaobject> callback,
45+
// This executor is actually a factory holder.
46+
JavaScriptExecutorHolder* jseh,
47+
jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
48+
jni::alias_ref<JavaMessageQueueThread::javaobject> moduleQueue,
49+
ModuleRegistryHolder* mrh);
50+
void loadScriptFromAssets(jobject assetManager, const std::string& assetURL);
51+
void loadScriptFromFile(jni::alias_ref<jstring> fileName, const std::string& sourceURL);
52+
void callJSFunction(JExecutorToken* token, std::string module, std::string method, NativeArray* arguments,
53+
const std::string& tracingName);
54+
void callJSCallback(JExecutorToken* token, jint callbackId, NativeArray* arguments);
55+
local_ref<JExecutorToken::JavaPart> getMainExecutorToken();
56+
void setGlobalVariable(std::string propName,
57+
std::string&& jsonValue);
58+
jboolean supportsProfiling();
59+
void startProfiler(const std::string& title);
60+
void stopProfiler(const std::string& title, const std::string& filename);
61+
62+
// This should be the only long-lived strong reference, but every C++ class
63+
// will have a weak reference.
64+
std::shared_ptr<Instance> instance_;
65+
};
66+
67+
}}

0 commit comments

Comments
 (0)