diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.java index 1091ce0ba..2045a4b23 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.java @@ -22,15 +22,13 @@ public abstract class NativeArray { SoLoader.loadLibrary(ReactBridge.REACT_NATIVE_LIB); } - public NativeArray() { - mHybridData = initHybrid(); + protected NativeArray(HybridData hybridData) { + mHybridData = hybridData; } @Override public native String toString(); - private native HybridData initHybrid(); - @DoNotStrip private HybridData mHybridData; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.java index cf4a31cb4..68dfa21b2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.java @@ -9,6 +9,7 @@ package com.facebook.react.bridge; +import com.facebook.jni.HybridData; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.soloader.SoLoader; @@ -23,6 +24,10 @@ public class ReadableNativeArray extends NativeArray implements ReadableArray { SoLoader.loadLibrary(ReactBridge.REACT_NATIVE_LIB); } + protected ReadableNativeArray(HybridData hybridData) { + super(hybridData); + } + @Override public native int size(); @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.java index ce748c0db..c0f65d072 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.java @@ -10,6 +10,7 @@ package com.facebook.react.bridge; import com.facebook.infer.annotation.Assertions; +import com.facebook.jni.HybridData; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.soloader.SoLoader; @@ -25,6 +26,10 @@ public class WritableNativeArray extends ReadableNativeArray implements Writable SoLoader.loadLibrary(ReactBridge.REACT_NATIVE_LIB); } + public WritableNativeArray() { + super(initHybrid()); + } + @Override public native void pushNull(); @Override @@ -52,6 +57,7 @@ public class WritableNativeArray extends ReadableNativeArray implements Writable pushNativeMap((WritableNativeMap) map); } + private native static HybridData initHybrid(); private native void pushNativeArray(WritableNativeArray array); private native void pushNativeMap(WritableNativeMap map); } diff --git a/ReactAndroid/src/main/jni/react/jni/NativeArray.cpp b/ReactAndroid/src/main/jni/react/jni/NativeArray.cpp index ea17aa39a..cc5e4a0b2 100644 --- a/ReactAndroid/src/main/jni/react/jni/NativeArray.cpp +++ b/ReactAndroid/src/main/jni/react/jni/NativeArray.cpp @@ -8,25 +8,12 @@ namespace facebook { namespace react { -jni::local_ref -createReadableNativeArrayWithContents(folly::dynamic array) { - if (array.isNull()) { - return jni::local_ref(); - } - +NativeArray::NativeArray(folly::dynamic a) + : array(std::move(a)) { if (!array.isArray()) { jni::throwNewJavaException("com/facebook/react/bridge/UnexpectedNativeTypeException", "expected Array, got a %s", array.typeName()); } - - static auto readableNativeArrayClass = - jni::findClassStatic("com/facebook/react/bridge/ReadableNativeArray"); - static auto readableNativeArrayCtor = - readableNativeArrayClass->getConstructor(); - - auto jnewArray = readableNativeArrayClass->newObject(readableNativeArrayCtor); - jni::cthis(jnewArray)->array = std::move(array); - return jnewArray; } jstring NativeArray::toString() { @@ -37,5 +24,11 @@ jstring NativeArray::toString() { return jni::make_jstring(folly::toJson(array).c_str()).release(); } +void NativeArray::registerNatives() { + registerHybrid({ + makeNativeMethod("toString", NativeArray::toString), + }); +} + } } diff --git a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp index fde064b85..9e37dfe47 100644 --- a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp +++ b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp @@ -14,7 +14,7 @@ #include #include #include "JSLoader.h" -#include "NativeArray.h" +#include "ReadableNativeArray.h" #include "ProxyExecutor.h" #ifdef WITH_FBSYSTRACE @@ -131,83 +131,105 @@ static jobject getType(folly::dynamic::Type type) { } -struct ReadableNativeArray : public NativeArray { - static void mapException(const std::exception& ex) { - if (dynamic_cast(&ex) != 0) { - throwNewJavaException(exceptions::gUnexpectedNativeTypeExceptionClass, ex.what()); - } +// 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(&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::value, + "folly::dynamic int is not int64_t"); + jint javaint = static_cast(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::getArray(jint index) { + auto& elem = array.at(index); + if (elem.isNull()) { + return jni::local_ref(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 { + static constexpr const char* kJavaDescriptor = "Lcom/facebook/react/bridge/WritableNativeArray;"; + + WritableNativeArray() + : HybridBase(folly::dynamic({})) {} + + static local_ref initHybrid(alias_ref) { + return makeCxxInstance(); } - jint getSize() { - return array.size(); - } - - jboolean isNull(jint index) { - return array.at(index).isNull() ? JNI_TRUE : JNI_FALSE; - } - - jboolean getBoolean(jint index) { - return array.at(index).getBool() ? JNI_TRUE : JNI_FALSE; - } - - jdouble getDouble(jint index) { - const folly::dynamic& val = array.at(index); - if (val.isInt()) { - return val.getInt(); - } - return val.getDouble(); - } - - jint getInt(jint index) { - auto integer = array.at(index).getInt(); - jint javaint = static_cast(integer); - if (integer != javaint) { - throwNewJavaException( - exceptions::gUnexpectedNativeTypeExceptionClass, - "Value '%lld' doesn't fit into a 32 bit signed int", integer); - } - return javaint; - } - - jstring getString(jint index) { - const folly::dynamic& dyn = array.at(index); - if (dyn.isNull()) { - return nullptr; - } - return make_jstring(dyn.getString().c_str()).release(); - } - - jobject getArray(jint index) { - return createReadableNativeArrayWithContents(array.at(index)).release(); - } - - jobject getMap(jint index) { - return createReadableNativeMapWithContents(Environment::current(), array.at(index)); - } - - jobject getType(jint index) { - return type::getType(array.at(index).type()); - } - - static void 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", "(I)Lcom/facebook/react/bridge/ReadableNativeArray;", - ReadableNativeArray::getArray), - makeNativeMethod("getMap", "(I)Lcom/facebook/react/bridge/ReadableNativeMap;", - ReadableNativeArray::getMap), - makeNativeMethod("getType", "(I)Lcom/facebook/react/bridge/ReadableType;", - ReadableNativeArray::getType), - }); - } -}; - -struct WritableNativeArray : public ReadableNativeArray { void pushNull() { exceptions::throwIfObjectAlreadyConsumed(this, "Array already consumed"); array.push_back(nullptr); @@ -237,7 +259,7 @@ struct WritableNativeArray : public ReadableNativeArray { array.push_back(wrap_alias(value)->toStdString()); } - void pushArray(NativeArray* otherArray) { + void pushNativeArray(WritableNativeArray* otherArray) { if (otherArray == NULL) { pushNull(); return; @@ -248,7 +270,7 @@ struct WritableNativeArray : public ReadableNativeArray { otherArray->isConsumed = true; } - void pushMap(jobject jmap) { + void pushNativeMap(jobject jmap) { if (jmap == NULL) { pushNull(); return; @@ -262,19 +284,21 @@ struct WritableNativeArray : public ReadableNativeArray { 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", "(Lcom/facebook/react/bridge/WritableNativeArray;)V", - WritableNativeArray::pushArray), + makeNativeMethod("pushNativeArray", WritableNativeArray::pushNativeArray), makeNativeMethod("pushNativeMap", "(Lcom/facebook/react/bridge/WritableNativeMap;)V", - WritableNativeArray::pushMap), + WritableNativeArray::pushNativeMap), }); } }; +} + namespace map { static void initialize(JNIEnv* env, jobject obj) { @@ -326,7 +350,8 @@ static void putString(JNIEnv* env, jobject obj, jstring key, jstring value) { map->map.insert(fromJString(env, key), fromJString(env, value)); } -static void putArray(JNIEnv* env, jobject obj, jstring key, NativeArray::jhybridobject value) { +static void putArray(JNIEnv* env, jobject obj, jstring key, + WritableNativeArray::jhybridobject value) { if (value == NULL) { putNull(env, obj, key); return; @@ -441,8 +466,14 @@ static jstring getStringKey(JNIEnv* env, jobject obj, jstring keyName) { } } -static jobject getArrayKey(JNIEnv* env, jobject obj, jstring keyName) { - return createReadableNativeArrayWithContents(getMapValue(env, obj, keyName)).release(); +static jni::local_ref getArrayKey( + jni::alias_ref obj, jstring keyName) { + auto& value = getMapValue(Environment::current(), obj.get(), keyName); + if (value.isNull()) { + return jni::local_ref(nullptr); + } else { + return ReadableNativeArray::newObjectCxxArgs(value); + } } static jobject getMapKey(JNIEnv* env, jobject obj, jstring keyName) { @@ -526,7 +557,7 @@ static void makeJavaCall(JNIEnv* env, jobject callback, MethodCall&& call) { if (call.arguments.isNull()) { return; } - auto newArray = createReadableNativeArrayWithContents(std::move(call.arguments)); + auto newArray = ReadableNativeArray::newObjectCxxArgs(std::move(call.arguments)); env->CallVoidMethod(callback, gCallbackMethod, call.moduleId, call.methodId, newArray.get()); } @@ -708,9 +739,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { makeNativeMethod("getDouble", map::readable::getDoubleKey), makeNativeMethod("getInt", map::readable::getIntKey), makeNativeMethod("getString", map::readable::getStringKey), - makeNativeMethod( - "getArray", "(Ljava/lang/String;)Lcom/facebook/react/bridge/ReadableNativeArray;", - map::readable::getArrayKey), + makeNativeMethod("getArray", map::readable::getArrayKey), makeNativeMethod( "getMap", "(Ljava/lang/String;)Lcom/facebook/react/bridge/ReadableNativeMap;", map::readable::getMapKey), @@ -725,9 +754,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { makeNativeMethod("putDouble", map::writable::putDouble), makeNativeMethod("putInt", map::writable::putInt), makeNativeMethod("putString", map::writable::putString), - makeNativeMethod( - "putNativeArray", "(Ljava/lang/String;Lcom/facebook/react/bridge/WritableNativeArray;)V", - map::writable::putArray), + makeNativeMethod("putNativeArray", map::writable::putArray), makeNativeMethod( "putNativeMap", "(Ljava/lang/String;Lcom/facebook/react/bridge/WritableNativeMap;)V", map::writable::putMap),