Files
react-native/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp
Chris Hopman 2c8802f316 Make JSExecutorFactory not derive from fbjni::Countable
Summary:
This adds a CountableJSExecutorFactory that derives from Countable. And uses that. Basically it allows code that doesn't need to know about jni or Countable not depend on it.
public

Reviewed By: astreet

Differential Revision: D2905163

fb-gh-sync-id: f2bfd5589a3185bb175ad51660b17be08ba62424
2016-02-05 18:10:55 -08:00

912 lines
32 KiB
C++

// Copyright 2004-present Facebook. All Rights Reserved.
#include <android/asset_manager_jni.h>
#include <android/input.h>
#include <fb/log.h>
#include <folly/json.h>
#include <jni/Countable.h>
#include <jni/Environment.h>
#include <jni/fbjni.h>
#include <jni/LocalReference.h>
#include <jni/LocalString.h>
#include <jni/WeakReference.h>
#include <jni/fbjni/Exceptions.h>
#include <react/Bridge.h>
#include <react/Executor.h>
#include <react/JSCExecutor.h>
#include <react/JSModulesUnbundle.h>
#include "JNativeRunnable.h"
#include "JSLoader.h"
#include "ReadableNativeArray.h"
#include "ProxyExecutor.h"
#include "OnLoad.h"
#include <algorithm>
#ifdef WITH_FBSYSTRACE
#include <fbsystrace.h>
using fbsystrace::FbSystraceSection;
#endif
using namespace facebook::jni;
namespace facebook {
namespace react {
static jclass gReadableNativeMapClass;
static jmethodID gReadableNativeMapCtor;
namespace exceptions {
static const char *gUnexpectedNativeTypeExceptionClass =
"com/facebook/react/bridge/UnexpectedNativeTypeException";
template <typename T>
void throwIfObjectAlreadyConsumed(const T& t, const char* msg) {
if (t->isConsumed) {
throwNewJavaException("com/facebook/react/bridge/ObjectAlreadyConsumedException", msg);
}
}
}
struct NativeMap : public Countable {
// Whether this map has been added to another array or map and no longer has a valid map value
bool isConsumed = false;
folly::dynamic map = folly::dynamic::object;
};
struct ReadableNativeMapKeySetIterator : public Countable {
folly::dynamic::const_item_iterator iterator;
RefPtr<NativeMap> mapRef;
ReadableNativeMapKeySetIterator(folly::dynamic::const_item_iterator&& it,
const RefPtr<NativeMap>& mapRef_)
: iterator(std::move(it))
, mapRef(mapRef_) {}
};
static jobject createReadableNativeMapWithContents(JNIEnv* env, folly::dynamic map) {
if (map.isNull()) {
return nullptr;
}
if (!map.isObject()) {
throwNewJavaException(exceptions::gUnexpectedNativeTypeExceptionClass,
"expected Map, got a %s", map.typeName());
}
jobject jnewMap = env->NewObject(gReadableNativeMapClass, gReadableNativeMapCtor);
if (env->ExceptionCheck()) {
return nullptr;
}
auto nativeMap = extractRefPtr<NativeMap>(env, jnewMap);
nativeMap->map = std::move(map);
return jnewMap;
}
namespace type {
static jclass gReadableReactType;
static jobject gTypeNullValue;
static jobject gTypeBooleanValue;
static jobject gTypeNumberValue;
static jobject gTypeStringValue;
static jobject gTypeMapValue;
static jobject gTypeArrayValue;
static jobject getTypeValue(JNIEnv* env, const char* fieldName) {
jfieldID fieldID = env->GetStaticFieldID(
gReadableReactType, fieldName, "Lcom/facebook/react/bridge/ReadableType;");
jobject typeValue = env->GetStaticObjectField(gReadableReactType, fieldID);
return env->NewGlobalRef(typeValue);
}
static void initialize(JNIEnv* env) {
gTypeNullValue = getTypeValue(env, "Null");
gTypeBooleanValue = getTypeValue(env, "Boolean");
gTypeNumberValue = getTypeValue(env, "Number");
gTypeStringValue = getTypeValue(env, "String");
gTypeMapValue = getTypeValue(env, "Map");
gTypeArrayValue = getTypeValue(env, "Array");
}
static jobject getType(folly::dynamic::Type type) {
switch (type) {
case folly::dynamic::Type::NULLT:
return type::gTypeNullValue;
case folly::dynamic::Type::BOOL:
return type::gTypeBooleanValue;
case folly::dynamic::Type::DOUBLE:
case folly::dynamic::Type::INT64:
return type::gTypeNumberValue;
case folly::dynamic::Type::STRING:
return type::gTypeStringValue;
case folly::dynamic::Type::OBJECT:
return type::gTypeMapValue;
case folly::dynamic::Type::ARRAY:
return type::gTypeArrayValue;
default:
throwNewJavaException(exceptions::gUnexpectedNativeTypeExceptionClass, "Unknown type");
}
}
}
// This attribute exports the ctor symbol, so ReadableNativeArray to be
// constructed from other DSOs.
__attribute__((visibility("default")))
ReadableNativeArray::ReadableNativeArray(folly::dynamic array)
: HybridBase(std::move(array)) {}
void ReadableNativeArray::mapException(const std::exception& ex) {
if (dynamic_cast<const folly::TypeError*>(&ex) != nullptr) {
throwNewJavaException(exceptions::gUnexpectedNativeTypeExceptionClass, ex.what());
}
}
jint ReadableNativeArray::getSize() {
return array.size();
}
jboolean ReadableNativeArray::isNull(jint index) {
return array.at(index).isNull() ? JNI_TRUE : JNI_FALSE;
}
jboolean ReadableNativeArray::getBoolean(jint index) {
return array.at(index).getBool() ? JNI_TRUE : JNI_FALSE;
}
jdouble ReadableNativeArray::getDouble(jint index) {
const folly::dynamic& val = array.at(index);
if (val.isInt()) {
return val.getInt();
}
return val.getDouble();
}
jint ReadableNativeArray::getInt(jint index) {
auto integer = array.at(index).getInt();
static_assert(std::is_same<decltype(integer), int64_t>::value,
"folly::dynamic int is not int64_t");
jint javaint = static_cast<jint>(integer);
if (integer != javaint) {
throwNewJavaException(
exceptions::gUnexpectedNativeTypeExceptionClass,
"Value '%lld' doesn't fit into a 32 bit signed int", integer);
}
return javaint;
}
const char* ReadableNativeArray::getString(jint index) {
const folly::dynamic& dyn = array.at(index);
if (dyn.isNull()) {
return nullptr;
}
return dyn.getString().c_str();
}
jni::local_ref<ReadableNativeArray::jhybridobject> ReadableNativeArray::getArray(jint index) {
auto& elem = array.at(index);
if (elem.isNull()) {
return jni::local_ref<ReadableNativeArray::jhybridobject>(nullptr);
} else {
return ReadableNativeArray::newObjectCxxArgs(elem);
}
}
jobject ReadableNativeArray::getMap(jint index) {
return createReadableNativeMapWithContents(Environment::current(), array.at(index));
}
jobject ReadableNativeArray::getType(jint index) {
return type::getType(array.at(index).type());
}
void ReadableNativeArray::registerNatives() {
jni::registerNatives("com/facebook/react/bridge/ReadableNativeArray", {
makeNativeMethod("size", ReadableNativeArray::getSize),
makeNativeMethod("isNull", ReadableNativeArray::isNull),
makeNativeMethod("getBoolean", ReadableNativeArray::getBoolean),
makeNativeMethod("getDouble", ReadableNativeArray::getDouble),
makeNativeMethod("getInt", ReadableNativeArray::getInt),
makeNativeMethod("getString", ReadableNativeArray::getString),
makeNativeMethod("getArray", ReadableNativeArray::getArray),
makeNativeMethod("getMap", "(I)Lcom/facebook/react/bridge/ReadableNativeMap;",
ReadableNativeArray::getMap),
makeNativeMethod("getType", "(I)Lcom/facebook/react/bridge/ReadableType;",
ReadableNativeArray::getType),
});
}
namespace {
struct WritableNativeArray
: public jni::HybridClass<WritableNativeArray, ReadableNativeArray> {
static constexpr const char* kJavaDescriptor = "Lcom/facebook/react/bridge/WritableNativeArray;";
WritableNativeArray()
: HybridBase(folly::dynamic({})) {}
static local_ref<jhybriddata> initHybrid(alias_ref<jclass>) {
return makeCxxInstance();
}
void pushNull() {
exceptions::throwIfObjectAlreadyConsumed(this, "Array already consumed");
array.push_back(nullptr);
}
void pushBoolean(jboolean value) {
exceptions::throwIfObjectAlreadyConsumed(this, "Array already consumed");
array.push_back(value == JNI_TRUE);
}
void pushDouble(jdouble value) {
exceptions::throwIfObjectAlreadyConsumed(this, "Receiving array already consumed");
array.push_back(value);
}
void pushInt(jint value) {
exceptions::throwIfObjectAlreadyConsumed(this, "Receiving array already consumed");
array.push_back(value);
}
void pushString(jstring value) {
if (value == NULL) {
pushNull();
return;
}
exceptions::throwIfObjectAlreadyConsumed(this, "Receiving array already consumed");
array.push_back(wrap_alias(value)->toStdString());
}
void pushNativeArray(WritableNativeArray* otherArray) {
if (otherArray == NULL) {
pushNull();
return;
}
exceptions::throwIfObjectAlreadyConsumed(this, "Receiving array already consumed");
exceptions::throwIfObjectAlreadyConsumed(otherArray, "Array to push already consumed");
array.push_back(std::move(otherArray->array));
otherArray->isConsumed = true;
}
void pushNativeMap(jobject jmap) {
if (jmap == NULL) {
pushNull();
return;
}
exceptions::throwIfObjectAlreadyConsumed(this, "Receiving array already consumed");
auto map = extractRefPtr<NativeMap>(Environment::current(), jmap);
exceptions::throwIfObjectAlreadyConsumed(map, "Map to push already consumed");
array.push_back(std::move(map->map));
map->isConsumed = true;
}
static void registerNatives() {
jni::registerNatives("com/facebook/react/bridge/WritableNativeArray", {
makeNativeMethod("initHybrid", WritableNativeArray::initHybrid),
makeNativeMethod("pushNull", WritableNativeArray::pushNull),
makeNativeMethod("pushBoolean", WritableNativeArray::pushBoolean),
makeNativeMethod("pushDouble", WritableNativeArray::pushDouble),
makeNativeMethod("pushInt", WritableNativeArray::pushInt),
makeNativeMethod("pushString", WritableNativeArray::pushString),
makeNativeMethod("pushNativeArray", WritableNativeArray::pushNativeArray),
makeNativeMethod("pushNativeMap", "(Lcom/facebook/react/bridge/WritableNativeMap;)V",
WritableNativeArray::pushNativeMap),
});
}
};
}
namespace map {
static void initialize(JNIEnv* env, jobject obj) {
auto map = createNew<NativeMap>();
setCountableForJava(env, obj, std::move(map));
}
static jstring toString(JNIEnv* env, jobject obj) {
auto nativeMap = extractRefPtr<NativeMap>(env, obj);
exceptions::throwIfObjectAlreadyConsumed(nativeMap, "Map already consumed");
LocalString string(
("{ NativeMap: " + folly::toJson(nativeMap->map) + " }").c_str());
return static_cast<jstring>(env->NewLocalRef(string.string()));
}
namespace writable {
static void putNull(JNIEnv* env, jobject obj, jstring key) {
auto map = extractRefPtr<NativeMap>(env, obj);
exceptions::throwIfObjectAlreadyConsumed(map, "Receiving map already consumed");
map->map.insert(fromJString(env, key), nullptr);
}
static void putBoolean(JNIEnv* env, jobject obj, jstring key, jboolean value) {
auto map = extractRefPtr<NativeMap>(env, obj);
exceptions::throwIfObjectAlreadyConsumed(map, "Receiving map already consumed");
map->map.insert(fromJString(env, key), value == JNI_TRUE);
}
static void putDouble(JNIEnv* env, jobject obj, jstring key, jdouble value) {
auto map = extractRefPtr<NativeMap>(env, obj);
exceptions::throwIfObjectAlreadyConsumed(map, "Receiving map already consumed");
map->map.insert(fromJString(env, key), value);
}
static void putInt(JNIEnv* env, jobject obj, jstring key, jint value) {
auto map = extractRefPtr<NativeMap>(env, obj);
exceptions::throwIfObjectAlreadyConsumed(map, "Receiving map already consumed");
map->map.insert(fromJString(env, key), value);
}
static void putString(JNIEnv* env, jobject obj, jstring key, jstring value) {
if (value == NULL) {
putNull(env, obj, key);
return;
}
auto map = extractRefPtr<NativeMap>(env, obj);
exceptions::throwIfObjectAlreadyConsumed(map, "Receiving map already consumed");
map->map.insert(fromJString(env, key), fromJString(env, value));
}
static void putArray(JNIEnv* env, jobject obj, jstring key,
WritableNativeArray::jhybridobject value) {
if (value == NULL) {
putNull(env, obj, key);
return;
}
auto parentMap = extractRefPtr<NativeMap>(env, obj);
exceptions::throwIfObjectAlreadyConsumed(parentMap, "Receiving map already consumed");
auto arrayValue = cthis(wrap_alias(value));
exceptions::throwIfObjectAlreadyConsumed(arrayValue, "Array to put already consumed");
parentMap->map.insert(fromJString(env, key), std::move(arrayValue->array));
arrayValue->isConsumed = true;
}
static void putMap(JNIEnv* env, jobject obj, jstring key, jobject value) {
if (value == NULL) {
putNull(env, obj, key);
return;
}
auto parentMap = extractRefPtr<NativeMap>(env, obj);
exceptions::throwIfObjectAlreadyConsumed(parentMap, "Receiving map already consumed");
auto mapValue = extractRefPtr<NativeMap>(env, value);
exceptions::throwIfObjectAlreadyConsumed(mapValue, "Map to put already consumed");
parentMap->map.insert(fromJString(env, key), std::move(mapValue->map));
mapValue->isConsumed = true;
}
static void mergeMap(JNIEnv* env, jobject obj, jobject source) {
auto sourceMap = extractRefPtr<NativeMap>(env, source);
exceptions::throwIfObjectAlreadyConsumed(sourceMap, "Source map already consumed");
auto destMap = extractRefPtr<NativeMap>(env, obj);
exceptions::throwIfObjectAlreadyConsumed(destMap, "Destination map already consumed");
// std::map#insert doesn't overwrite the value, therefore we need to clean values for keys
// that already exists before merging dest map into source map
for (auto sourceIt : sourceMap->map.items()) {
destMap->map.erase(sourceIt.first);
destMap->map.insert(std::move(sourceIt.first), std::move(sourceIt.second));
}
}
} // namespace writable
namespace readable {
static const char *gNoSuchKeyExceptionClass = "com/facebook/react/bridge/NoSuchKeyException";
static jboolean hasKey(JNIEnv* env, jobject obj, jstring keyName) {
auto nativeMap = extractRefPtr<NativeMap>(env, obj);
auto& map = nativeMap->map;
bool found = map.find(fromJString(env, keyName)) != map.items().end();
return found ? JNI_TRUE : JNI_FALSE;
}
static const folly::dynamic& getMapValue(JNIEnv* env, jobject obj, jstring keyName) {
auto nativeMap = extractRefPtr<NativeMap>(env, obj);
std::string key = fromJString(env, keyName);
try {
return nativeMap->map.at(key);
} catch (const std::out_of_range& ex) {
throwNewJavaException(gNoSuchKeyExceptionClass, ex.what());
}
}
static jboolean isNull(JNIEnv* env, jobject obj, jstring keyName) {
return getMapValue(env, obj, keyName).isNull() ? JNI_TRUE : JNI_FALSE;
}
static jboolean getBooleanKey(JNIEnv* env, jobject obj, jstring keyName) {
try {
return getMapValue(env, obj, keyName).getBool() ? JNI_TRUE : JNI_FALSE;
} catch (const folly::TypeError& ex) {
throwNewJavaException(exceptions::gUnexpectedNativeTypeExceptionClass, ex.what());
}
}
static jdouble getDoubleKey(JNIEnv* env, jobject obj, jstring keyName) {
const folly::dynamic& val = getMapValue(env, obj, keyName);
if (val.isInt()) {
return val.getInt();
}
try {
return val.getDouble();
} catch (const folly::TypeError& ex) {
throwNewJavaException(exceptions::gUnexpectedNativeTypeExceptionClass, ex.what());
}
}
static jint getIntKey(JNIEnv* env, jobject obj, jstring keyName) {
try {
auto integer = getMapValue(env, obj, keyName).getInt();
jint javaint = static_cast<jint>(integer);
if (integer != javaint) {
throwNewJavaException(
exceptions::gUnexpectedNativeTypeExceptionClass,
"Value '%lld' doesn't fit into a 32 bit signed int", integer);
}
return javaint;
} catch (const folly::TypeError& ex) {
throwNewJavaException(exceptions::gUnexpectedNativeTypeExceptionClass, ex.what());
}
}
static jstring getStringKey(JNIEnv* env, jobject obj, jstring keyName) {
const folly::dynamic& val = getMapValue(env, obj, keyName);
if (val.isNull()) {
return nullptr;
}
try {
LocalString value(val.getString().c_str());
return static_cast<jstring>(env->NewLocalRef(value.string()));
} catch (const folly::TypeError& ex) {
throwNewJavaException(exceptions::gUnexpectedNativeTypeExceptionClass, ex.what());
}
}
static jni::local_ref<ReadableNativeArray::jhybridobject> getArrayKey(
jni::alias_ref<jobject> obj, jstring keyName) {
auto& value = getMapValue(Environment::current(), obj.get(), keyName);
if (value.isNull()) {
return jni::local_ref<ReadableNativeArray::jhybridobject>(nullptr);
} else {
return ReadableNativeArray::newObjectCxxArgs(value);
}
}
static jobject getMapKey(JNIEnv* env, jobject obj, jstring keyName) {
return createReadableNativeMapWithContents(env, getMapValue(env, obj, keyName));
}
static jobject getValueType(JNIEnv* env, jobject obj, jstring keyName) {
return type::getType(getMapValue(env, obj, keyName).type());
}
} // namespace readable
namespace iterator {
static void initialize(JNIEnv* env, jobject obj, jobject nativeMapObj) {
auto nativeMap = extractRefPtr<NativeMap>(env, nativeMapObj);
auto mapIterator = createNew<ReadableNativeMapKeySetIterator>(
nativeMap->map.items().begin(), nativeMap);
setCountableForJava(env, obj, std::move(mapIterator));
}
static jboolean hasNextKey(JNIEnv* env, jobject obj) {
auto nativeIterator = extractRefPtr<ReadableNativeMapKeySetIterator>(env, obj);
return ((nativeIterator->iterator != nativeIterator->mapRef.get()->map.items().end())
? JNI_TRUE : JNI_FALSE);
}
static jstring getNextKey(JNIEnv* env, jobject obj) {
auto nativeIterator = extractRefPtr<ReadableNativeMapKeySetIterator>(env, obj);
if (JNI_FALSE == hasNextKey(env, obj)) {
throwNewJavaException("com/facebook/react/bridge/InvalidIteratorException",
"No such element exists");
}
LocalString value(nativeIterator->iterator->first.c_str());
++nativeIterator->iterator;
return static_cast<jstring>(env->NewLocalRef(value.string()));
}
} // namespace iterator
} // namespace map
namespace {
namespace runnable {
struct NativeRunnable : public Countable {
std::function<void()> callable;
};
static jclass gNativeRunnableClass;
static jmethodID gNativeRunnableCtor;
static LocalReference<jobject> createNativeRunnable(JNIEnv* env, decltype(NativeRunnable::callable)&& callable) {
LocalReference<jobject> jRunnable{env->NewObject(gNativeRunnableClass, gNativeRunnableCtor)};
if (env->ExceptionCheck()) {
return nullptr;
}
auto nativeRunnable = createNew<NativeRunnable>();
nativeRunnable->callable = std::move(callable);
setCountableForJava(env, jRunnable.get(), std::move(nativeRunnable));
return jRunnable;
}
static void run(JNIEnv* env, jobject jNativeRunnable) {
auto nativeRunnable = extractRefPtr<NativeRunnable>(env, jNativeRunnable);
nativeRunnable->callable();
}
} // namespace runnable
namespace queue {
static jmethodID gRunOnQueueThreadMethod;
static void enqueueNativeRunnableOnQueue(JNIEnv* env, jobject callbackQueueThread, jobject nativeRunnable) {
env->CallVoidMethod(callbackQueueThread, gRunOnQueueThreadMethod, nativeRunnable);
}
} // namespace queue
namespace bridge {
static jmethodID gCallbackMethod;
static jmethodID gOnBatchCompleteMethod;
static jmethodID gLogMarkerMethod;
static void makeJavaCall(JNIEnv* env, jobject callback, MethodCall&& call) {
if (call.arguments.isNull()) {
return;
}
#ifdef WITH_FBSYSTRACE
if (call.callId != -1) {
fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", call.callId);
}
#endif
auto newArray = ReadableNativeArray::newObjectCxxArgs(std::move(call.arguments));
env->CallVoidMethod(callback, gCallbackMethod, call.moduleId, call.methodId, newArray.get());
}
static void signalBatchComplete(JNIEnv* env, jobject callback) {
env->CallVoidMethod(callback, gOnBatchCompleteMethod);
}
static void dispatchCallbacksToJava(const RefPtr<WeakReference>& weakCallback,
const RefPtr<WeakReference>& weakCallbackQueueThread,
std::vector<MethodCall>&& calls,
bool isEndOfBatch) {
auto env = Environment::current();
if (env->ExceptionCheck()) {
FBLOGW("Dropped calls because of pending exception");
return;
}
ResolvedWeakReference callbackQueueThread(weakCallbackQueueThread);
if (!callbackQueueThread) {
FBLOGW("Dropped calls because of callback queue thread went away");
return;
}
auto runnableFunction = std::bind([weakCallback, isEndOfBatch] (std::vector<MethodCall>& calls) {
auto env = Environment::current();
if (env->ExceptionCheck()) {
FBLOGW("Dropped calls because of pending exception");
return;
}
ResolvedWeakReference callback(weakCallback);
if (callback) {
for (auto&& call : calls) {
makeJavaCall(env, callback, std::move(call));
if (env->ExceptionCheck()) {
return;
}
}
if (isEndOfBatch) {
signalBatchComplete(env, callback);
}
}
}, std::move(calls));
auto jNativeRunnable = runnable::createNativeRunnable(env, std::move(runnableFunction));
queue::enqueueNativeRunnableOnQueue(env, callbackQueueThread, jNativeRunnable.get());
}
static void create(JNIEnv* env, jobject obj, jobject executor, jobject callback,
jobject callbackQueueThread) {
auto weakCallback = createNew<WeakReference>(callback);
auto weakCallbackQueueThread = createNew<WeakReference>(callbackQueueThread);
auto bridgeCallback = [weakCallback, weakCallbackQueueThread] (std::vector<MethodCall> calls, bool isEndOfBatch) {
dispatchCallbacksToJava(weakCallback, weakCallbackQueueThread, std::move(calls), isEndOfBatch);
};
auto nativeExecutorFactory = extractRefPtr<CountableJSExecutorFactory>(env, executor);
auto bridge = createNew<Bridge>(nativeExecutorFactory, bridgeCallback);
setCountableForJava(env, obj, std::move(bridge));
}
static void executeApplicationScript(
const RefPtr<Bridge>& bridge,
const std::string& script,
const std::string& sourceUri) {
try {
// Execute the application script and collect/dispatch any native calls that might have occured
bridge->executeApplicationScript(script, sourceUri);
bridge->flush();
} catch (...) {
translatePendingCppExceptionToJavaException();
}
}
static void loadApplicationUnbundle(
const RefPtr<Bridge>& bridge,
AAssetManager *assetManager,
const std::string& startupCode,
const std::string& startupFileName) {
try {
// Load the application unbundle and collect/dispatch any native calls that might have occured
bridge->loadApplicationUnbundle(
JSModulesUnbundle(assetManager, startupFileName),
startupCode,
startupFileName);
bridge->flush();
} catch (...) {
translatePendingCppExceptionToJavaException();
}
}
static void loadScriptFromAssets(JNIEnv* env, jobject obj, jobject assetManager,
jstring assetName) {
jclass markerClass = env->FindClass("com/facebook/react/bridge/ReactMarker");
auto manager = AAssetManager_fromJava(env, assetManager);
auto bridge = extractRefPtr<Bridge>(env, obj);
auto assetNameStr = fromJString(env, assetName);
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromAssets_start"));
auto script = react::loadScriptFromAssets(manager, assetNameStr);
#ifdef WITH_FBSYSTRACE
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "reactbridge_jni_"
"executeApplicationScript",
"assetName", assetNameStr);
#endif
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromAssets_read"));
if (JSModulesUnbundle::isUnbundle(manager, assetNameStr)) {
loadApplicationUnbundle(bridge, manager, script, assetNameStr);
} else {
executeApplicationScript(bridge, script, assetNameStr);
}
if (env->ExceptionCheck()) {
return;
}
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromAssets_done"));
}
static void loadScriptFromFile(JNIEnv* env, jobject obj, jstring fileName, jstring sourceURL) {
jclass markerClass = env->FindClass("com/facebook/react/bridge/ReactMarker");
auto bridge = jni::extractRefPtr<Bridge>(env, obj);
auto fileNameStr = fileName == NULL ? "" : fromJString(env, fileName);
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromFile_start"));
auto script = fileName == NULL ? "" : react::loadScriptFromFile(fileNameStr);
#ifdef WITH_FBSYSTRACE
auto sourceURLStr = sourceURL == NULL ? fileNameStr : fromJString(env, sourceURL);
FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "reactbridge_jni_"
"executeApplicationScript",
"sourceURL", sourceURLStr);
#endif
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromFile_read"));
executeApplicationScript(bridge, script, jni::fromJString(env, sourceURL));
if (env->ExceptionCheck()) {
return;
}
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromFile_exec"));
}
static void callFunction(JNIEnv* env, jobject obj, jint moduleId, jint methodId,
NativeArray::jhybridobject args) {
auto bridge = extractRefPtr<Bridge>(env, obj);
auto arguments = cthis(wrap_alias(args));
try {
bridge->callFunction(
(double) moduleId,
(double) methodId,
std::move(arguments->array)
);
} catch (...) {
translatePendingCppExceptionToJavaException();
}
}
static void invokeCallback(JNIEnv* env, jobject obj, jint callbackId,
NativeArray::jhybridobject args) {
auto bridge = extractRefPtr<Bridge>(env, obj);
auto arguments = cthis(wrap_alias(args));
try {
bridge->invokeCallback(
(double) callbackId,
std::move(arguments->array)
);
} catch (...) {
translatePendingCppExceptionToJavaException();
}
}
static void setGlobalVariable(JNIEnv* env, jobject obj, jstring propName, jstring jsonValue) {
auto bridge = extractRefPtr<Bridge>(env, obj);
bridge->setGlobalVariable(fromJString(env, propName), fromJString(env, jsonValue));
}
static jboolean supportsProfiling(JNIEnv* env, jobject obj) {
auto bridge = extractRefPtr<Bridge>(env, obj);
return bridge->supportsProfiling() ? JNI_TRUE : JNI_FALSE;
}
static void startProfiler(JNIEnv* env, jobject obj, jstring title) {
auto bridge = extractRefPtr<Bridge>(env, obj);
bridge->startProfiler(fromJString(env, title));
}
static void stopProfiler(JNIEnv* env, jobject obj, jstring title, jstring filename) {
auto bridge = extractRefPtr<Bridge>(env, obj);
bridge->stopProfiler(fromJString(env, title), fromJString(env, filename));
}
static void handleMemoryPressureModerate(JNIEnv* env, jobject obj) {
auto bridge = extractRefPtr<Bridge>(env, obj);
bridge->handleMemoryPressureModerate();
}
static void handleMemoryPressureCritical(JNIEnv* env, jobject obj) {
auto bridge = extractRefPtr<Bridge>(env, obj);
bridge->handleMemoryPressureCritical();
}
} // namespace bridge
namespace executors {
struct CountableJSCExecutorFactory : CountableJSExecutorFactory {
virtual std::unique_ptr<JSExecutor> createJSExecutor(FlushImmediateCallback cb) override {
return JSCExecutorFactory().createJSExecutor(cb);
}
};
static void createJSCExecutor(JNIEnv *env, jobject obj) {
auto executor = createNew<CountableJSCExecutorFactory>();
setCountableForJava(env, obj, std::move(executor));
}
static void createProxyExecutor(JNIEnv *env, jobject obj, jobject executorInstance) {
auto executor =
createNew<ProxyExecutorOneTimeFactory>(jni::make_global(jni::adopt_local(executorInstance)));
setCountableForJava(env, obj, std::move(executor));
}
} // namespace executors
}
jmethodID getLogMarkerMethod() {
return bridge::gLogMarkerMethod;
}
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
return initialize(vm, [] {
// get the current env
JNIEnv* env = Environment::current();
auto readableTypeClass = findClassLocal("com/facebook/react/bridge/ReadableType");
type::gReadableReactType = (jclass)env->NewGlobalRef(readableTypeClass.get());
type::initialize(env);
NativeArray::registerNatives();
ReadableNativeArray::registerNatives();
WritableNativeArray::registerNatives();
JNativeRunnable::registerNatives();
registerJSLoaderNatives();
registerNatives("com/facebook/react/bridge/NativeMap", {
makeNativeMethod("initialize", map::initialize),
makeNativeMethod("toString", map::toString),
});
jclass readableMapClass = env->FindClass("com/facebook/react/bridge/ReadableNativeMap");
gReadableNativeMapClass = (jclass)env->NewGlobalRef(readableMapClass);
gReadableNativeMapCtor = env->GetMethodID(readableMapClass, "<init>", "()V");
wrap_alias(readableMapClass)->registerNatives({
makeNativeMethod("hasKey", map::readable::hasKey),
makeNativeMethod("isNull", map::readable::isNull),
makeNativeMethod("getBoolean", map::readable::getBooleanKey),
makeNativeMethod("getDouble", map::readable::getDoubleKey),
makeNativeMethod("getInt", map::readable::getIntKey),
makeNativeMethod("getString", map::readable::getStringKey),
makeNativeMethod("getArray", map::readable::getArrayKey),
makeNativeMethod(
"getMap", "(Ljava/lang/String;)Lcom/facebook/react/bridge/ReadableNativeMap;",
map::readable::getMapKey),
makeNativeMethod(
"getType", "(Ljava/lang/String;)Lcom/facebook/react/bridge/ReadableType;",
map::readable::getValueType),
});
registerNatives("com/facebook/react/bridge/WritableNativeMap", {
makeNativeMethod("putNull", map::writable::putNull),
makeNativeMethod("putBoolean", map::writable::putBoolean),
makeNativeMethod("putDouble", map::writable::putDouble),
makeNativeMethod("putInt", map::writable::putInt),
makeNativeMethod("putString", map::writable::putString),
makeNativeMethod("putNativeArray", map::writable::putArray),
makeNativeMethod(
"putNativeMap", "(Ljava/lang/String;Lcom/facebook/react/bridge/WritableNativeMap;)V",
map::writable::putMap),
makeNativeMethod(
"mergeNativeMap", "(Lcom/facebook/react/bridge/ReadableNativeMap;)V",
map::writable::mergeMap)
});
registerNatives("com/facebook/react/bridge/ReadableNativeMap$ReadableNativeMapKeySetIterator", {
makeNativeMethod("initialize", "(Lcom/facebook/react/bridge/ReadableNativeMap;)V",
map::iterator::initialize),
makeNativeMethod("hasNextKey", map::iterator::hasNextKey),
makeNativeMethod("nextKey", map::iterator::getNextKey),
});
registerNatives("com/facebook/react/bridge/JSCJavaScriptExecutor", {
makeNativeMethod("initialize", executors::createJSCExecutor),
});
registerNatives("com/facebook/react/bridge/ProxyJavaScriptExecutor", {
makeNativeMethod(
"initialize", "(Lcom/facebook/react/bridge/JavaJSExecutor;)V",
executors::createProxyExecutor),
});
jclass callbackClass = env->FindClass("com/facebook/react/bridge/ReactCallback");
bridge::gCallbackMethod = env->GetMethodID(callbackClass, "call", "(IILcom/facebook/react/bridge/ReadableNativeArray;)V");
bridge::gOnBatchCompleteMethod = env->GetMethodID(callbackClass, "onBatchComplete", "()V");
jclass markerClass = env->FindClass("com/facebook/react/bridge/ReactMarker");
bridge::gLogMarkerMethod = env->GetStaticMethodID(markerClass, "logMarker", "(Ljava/lang/String;)V");
registerNatives("com/facebook/react/bridge/ReactBridge", {
makeNativeMethod("initialize", "(Lcom/facebook/react/bridge/JavaScriptExecutor;Lcom/facebook/react/bridge/ReactCallback;Lcom/facebook/react/bridge/queue/MessageQueueThread;)V", bridge::create),
makeNativeMethod(
"loadScriptFromAssets", "(Landroid/content/res/AssetManager;Ljava/lang/String;)V",
bridge::loadScriptFromAssets),
makeNativeMethod("loadScriptFromFile", bridge::loadScriptFromFile),
makeNativeMethod("callFunction", bridge::callFunction),
makeNativeMethod("invokeCallback", bridge::invokeCallback),
makeNativeMethod("setGlobalVariable", bridge::setGlobalVariable),
makeNativeMethod("supportsProfiling", bridge::supportsProfiling),
makeNativeMethod("startProfiler", bridge::startProfiler),
makeNativeMethod("stopProfiler", bridge::stopProfiler),
makeNativeMethod("handleMemoryPressureModerate", bridge::handleMemoryPressureModerate),
makeNativeMethod("handleMemoryPressureCritical", bridge::handleMemoryPressureCritical),
});
jclass nativeRunnableClass = env->FindClass("com/facebook/react/bridge/queue/NativeRunnableDeprecated");
runnable::gNativeRunnableClass = (jclass)env->NewGlobalRef(nativeRunnableClass);
runnable::gNativeRunnableCtor = env->GetMethodID(nativeRunnableClass, "<init>", "()V");
wrap_alias(nativeRunnableClass)->registerNatives({
makeNativeMethod("run", runnable::run),
});
jclass messageQueueThreadClass =
env->FindClass("com/facebook/react/bridge/queue/MessageQueueThread");
queue::gRunOnQueueThreadMethod =
env->GetMethodID(messageQueueThreadClass, "runOnQueue", "(Ljava/lang/Runnable;)V");
});
}
} }