Extract all NativeArray/NativeMap from OnLoad.cpp

Summary: These will, eventually, need to be moved to the new bridge and so must become standalone things. For *NativeArray, this is almost just moving them out into their own .h/.cpp files. The *NativeMaps are updated to be hybrids instead of countables (in addition to getting their own .h/.cpp).

Reviewed By: mhorowitz

Differential Revision: D3325169

fbshipit-source-id: 40cfcab92b3fb2310bcd4de8f39e82f85d404abd
This commit is contained in:
Chris Hopman 2016-06-01 16:16:15 -07:00 committed by Facebook Github Bot 2
parent 0d1d7ba2b7
commit 886a558f75
22 changed files with 823 additions and 578 deletions

View File

@ -9,7 +9,7 @@
package com.facebook.react.bridge;
import com.facebook.jni.Countable;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
@ -17,18 +17,18 @@ import com.facebook.soloader.SoLoader;
* Base class for a Map whose keys and values are stored in native code (C++).
*/
@DoNotStrip
public abstract class NativeMap extends Countable {
public abstract class NativeMap {
static {
SoLoader.loadLibrary(ReactBridge.REACT_NATIVE_LIB);
}
public NativeMap() {
initialize();
public NativeMap(HybridData hybridData) {
mHybridData = hybridData;
}
@Override
public native String toString();
private native void initialize();
@DoNotStrip
private HybridData mHybridData;
}

View File

@ -9,7 +9,7 @@
package com.facebook.react.bridge;
import com.facebook.jni.Countable;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
@ -27,6 +27,10 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
SoLoader.loadLibrary(ReactBridge.REACT_NATIVE_LIB);
}
protected ReadableNativeMap(HybridData hybridData) {
super(hybridData);
}
@Override
public native boolean hasKey(String name);
@Override
@ -51,7 +55,7 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
return new ReadableNativeMapKeySetIterator(this);
}
public HashMap<String, Object>toHashMap() {
public HashMap<String, Object> toHashMap() {
ReadableMapKeySetIterator iterator = keySetIterator();
HashMap<String, Object> hashMap = new HashMap<>();
@ -87,14 +91,17 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
* Implementation of a {@link ReadableNativeMap} iterator in native memory.
*/
@DoNotStrip
private static class ReadableNativeMapKeySetIterator extends Countable
implements ReadableMapKeySetIterator {
private static class ReadableNativeMapKeySetIterator implements ReadableMapKeySetIterator {
@DoNotStrip
private final HybridData mHybridData;
private final ReadableNativeMap mReadableNativeMap;
// Need to hold a strong ref to the map so that our native references remain valid.
@DoNotStrip
private final ReadableNativeMap mMap;
public ReadableNativeMapKeySetIterator(ReadableNativeMap readableNativeMap) {
mReadableNativeMap = readableNativeMap;
initialize(mReadableNativeMap);
mMap = readableNativeMap;
mHybridData = initHybrid(readableNativeMap);
}
@Override
@ -102,6 +109,6 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
@Override
public native String nextKey();
private native void initialize(ReadableNativeMap readableNativeMap);
private static native HybridData initHybrid(ReadableNativeMap readableNativeMap);
}
}

View File

@ -57,7 +57,7 @@ public class WritableNativeArray extends ReadableNativeArray implements Writable
pushNativeMap((WritableNativeMap) map);
}
private native static HybridData initHybrid();
private static native HybridData initHybrid();
private native void pushNativeArray(WritableNativeArray array);
private native void pushNativeMap(WritableNativeMap map);
}

View File

@ -9,6 +9,7 @@
package com.facebook.react.bridge;
import com.facebook.jni.HybridData;
import com.facebook.infer.annotation.Assertions;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
@ -20,7 +21,6 @@ import com.facebook.soloader.SoLoader;
*/
@DoNotStrip
public class WritableNativeMap extends ReadableNativeMap implements WritableMap {
static {
SoLoader.loadLibrary(ReactBridge.REACT_NATIVE_LIB);
}
@ -59,6 +59,12 @@ public class WritableNativeMap extends ReadableNativeMap implements WritableMap
mergeNativeMap((ReadableNativeMap) source);
}
public WritableNativeMap() {
super(initHybrid());
}
private static native HybridData initHybrid();
private native void putNativeMap(String key, WritableNativeMap value);
private native void putNativeArray(String key, WritableNativeArray value);
private native void mergeNativeMap(ReadableNativeMap source);

View File

@ -7,13 +7,19 @@ LOCAL_MODULE := libreactnativejni
LOCAL_SRC_FILES := \
JExecutorToken.cpp \
JMessageQueueThread.cpp \
JniJSModulesUnbundle.cpp \
JSCPerfLogging.cpp \
JSLoader.cpp \
JSLogging.cpp \
JniJSModulesUnbundle.cpp \
NativeArray.cpp \
NativeCommon.cpp \
NativeMap.cpp \
OnLoad.cpp \
ProxyExecutor.cpp \
ReadableNativeArray.cpp \
ReadableNativeMap.cpp \
WritableNativeArray.cpp \
WritableNativeMap.cpp \
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../.. $(LOCAL_PATH)/..

View File

@ -31,11 +31,17 @@ jni_library(
'JMessageQueueThread.cpp',
'JSCPerfLogging.cpp',
'JSLoader.cpp',
'JSLogging.cpp',
'JniJSModulesUnbundle.cpp',
'NativeArray.cpp',
'NativeCommon.cpp',
'NativeMap.cpp',
'OnLoad.cpp',
'ProxyExecutor.cpp',
'JSLogging.cpp',
'ReadableNativeArray.cpp',
'ReadableNativeMap.cpp',
'WritableNativeArray.cpp',
'WritableNativeMap.cpp',
],
headers = [
'JSLoader.h',
@ -50,8 +56,13 @@ jni_library(
'WebWorkers.h',
],
exported_headers = [
'NativeCommon.h',
'NativeArray.h',
'NativeMap.h',
'ReadableNativeArray.h',
'ReadableNativeMap.h',
'WritableNativeArray.h',
'WritableNativeMap.h',
],
preprocessor_flags = [
'-DLOG_TAG="ReactNativeJNI"',

View File

@ -5,23 +5,24 @@
#include <fb/fbjni.h>
#include <folly/json.h>
#include "NativeCommon.h"
using namespace facebook::jni;
namespace facebook {
namespace react {
NativeArray::NativeArray(folly::dynamic a)
: array(std::move(a)) {
if (!array.isArray()) {
jni::throwNewJavaException("com/facebook/react/bridge/UnexpectedNativeTypeException",
throwNewJavaException(exceptions::gUnexpectedNativeTypeExceptionClass,
"expected Array, got a %s", array.typeName());
}
}
jstring NativeArray::toString() {
if (isConsumed) {
jni::throwNewJavaException("com/facebook/react/bridge/ObjectAlreadyConsumedException",
"Array already consumed");
}
return jni::make_jstring(folly::toJson(array).c_str()).release();
local_ref<jstring> NativeArray::toString() {
exceptions::throwIfObjectAlreadyConsumed(this, "Array already consumed");
return make_jstring(folly::toJson(array).c_str());
}
void NativeArray::registerNatives() {

View File

@ -17,7 +17,7 @@ class NativeArray : public jni::HybridClass<NativeArray> {
bool isConsumed = false;
folly::dynamic array;
jstring toString();
jni::local_ref<jstring> toString();
static void registerNatives();

View File

@ -0,0 +1,76 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "NativeCommon.h"
using namespace facebook::jni;
namespace facebook {
namespace react {
namespace exceptions {
const char *gUnexpectedNativeTypeExceptionClass =
"com/facebook/react/bridge/UnexpectedNativeTypeException";
}
namespace {
local_ref<ReadableType> getTypeField(const char* fieldName) {
static auto cls = ReadableType::javaClassStatic();
auto field = cls->getStaticField<ReadableType::javaobject>(fieldName);
return cls->getStaticFieldValue(field);
}
alias_ref<ReadableType> getNullValue() {
static alias_ref<ReadableType> val = make_global(getTypeField("Null")).release();
return val;
}
alias_ref<ReadableType> getBooleanValue() {
static alias_ref<ReadableType> val = make_global(getTypeField("Boolean")).release();
return val;
}
alias_ref<ReadableType> getNumberValue() {
static alias_ref<ReadableType> val = make_global(getTypeField("Number")).release();
return val;
}
alias_ref<ReadableType> getStringValue() {
static alias_ref<ReadableType> val = make_global(getTypeField("String")).release();
return val;
}
alias_ref<ReadableType> getMapValue() {
static alias_ref<ReadableType> val = make_global(getTypeField("Map")).release();
return val;
}
alias_ref<ReadableType> getArrayValue() {
static alias_ref<ReadableType> val = make_global(getTypeField("Array")).release();
return val;
}
} // namespace
local_ref<ReadableType> ReadableType::getType(folly::dynamic::Type type) {
switch (type) {
case folly::dynamic::Type::NULLT:
return make_local(getNullValue());
case folly::dynamic::Type::BOOL:
return make_local(getBooleanValue());
case folly::dynamic::Type::DOUBLE:
case folly::dynamic::Type::INT64:
return make_local(getNumberValue());
case folly::dynamic::Type::STRING:
return make_local(getStringValue());
case folly::dynamic::Type::OBJECT:
return make_local(getMapValue());
case folly::dynamic::Type::ARRAY:
return make_local(getArrayValue());
default:
throwNewJavaException(exceptions::gUnexpectedNativeTypeExceptionClass, "Unknown type");
}
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,31 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <fb/fbjni.h>
#include <folly/dynamic.h>
namespace facebook {
namespace react {
struct ReadableType : public jni::JavaClass<ReadableType> {
static auto constexpr kJavaDescriptor = "Lcom/facebook/react/bridge/ReadableType;";
static jni::local_ref<ReadableType> getType(folly::dynamic::Type type);
};
namespace exceptions {
extern const char *gUnexpectedNativeTypeExceptionClass;
template <typename T>
void throwIfObjectAlreadyConsumed(const T& t, const char* msg) {
if (t->isConsumed) {
jni::throwNewJavaException("com/facebook/react/bridge/ObjectAlreadyConsumedException", msg);
}
}
} // namespace exceptions
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,28 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "NativeMap.h"
#include "NativeCommon.h"
using namespace facebook::jni;
namespace facebook {
namespace react {
std::string NativeMap::toString() {
throwIfConsumed();
return ("{ NativeMap: " + folly::toJson(map_) + " }").c_str();
}
void NativeMap::registerNatives() {
registerHybrid({
makeNativeMethod("toString", NativeMap::toString),
});
}
void NativeMap::throwIfConsumed() {
exceptions::throwIfObjectAlreadyConsumed(this, "Map already consumed");
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,33 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <fb/fbjni.h>
#include <folly/dynamic.h>
#include <folly/json.h>
namespace facebook {
namespace react {
class NativeMap : public jni::HybridClass<NativeMap> {
public:
static auto constexpr kJavaDescriptor = "Lcom/facebook/react/bridge/NativeMap;";
explicit NativeMap(folly::dynamic s) : isConsumed(false), map_(s) {}
std::string toString();
bool isConsumed;
void throwIfConsumed();
static void registerNatives();
protected:
folly::dynamic map_;
friend HybridBase;
friend class ReadableNativeMapKeySetIterator;
};
} // namespace react
} // namespace facebook

View File

@ -22,6 +22,7 @@
#include "JExecutorTokenFactory.h"
#include "JNativeRunnable.h"
#include "JSLoader.h"
#include "NativeCommon.h"
#include "ReadableNativeArray.h"
#include "ProxyExecutor.h"
#include "OnLoad.h"
@ -30,6 +31,7 @@
#include "JSLogging.h"
#include "JSCPerfLogging.h"
#include "WebWorkers.h"
#include "WritableNativeMap.h"
#include <algorithm>
#ifdef WITH_FBSYSTRACE
@ -42,492 +44,6 @@ 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);
}
}
// Export getMap() so we can workaround constructing ReadableNativeMap
__attribute__((visibility("default")))
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::array()) {}
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 {
@ -758,7 +274,7 @@ static void loadScriptFromAssets(JNIEnv* env, jobject obj, jobject assetManager,
static void loadScriptFromFile(JNIEnv* env, jobject obj, jstring fileName, jstring sourceURL) {
jclass markerClass = env->FindClass("com/facebook/react/bridge/ReactMarker");
auto bridge = jni::extractRefPtr<CountableBridge>(env, obj);
auto bridge = extractRefPtr<CountableBridge>(env, obj);
auto fileNameStr = fileName == NULL ? "" : fromJString(env, fileName);
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromFile_start"));
auto script = fileName == NULL ? "" : react::loadScriptFromFile(fileNameStr);
@ -769,7 +285,7 @@ static void loadScriptFromFile(JNIEnv* env, jobject obj, jstring fileName, jstri
"sourceURL", sourceURLStr);
#endif
env->CallStaticVoidMethod(markerClass, gLogMarkerMethod, env->NewStringUTF("loadScriptFromFile_read"));
loadApplicationScript(bridge, script, jni::fromJString(env, sourceURL));
loadApplicationScript(bridge, script, fromJString(env, sourceURL));
if (env->ExceptionCheck()) {
return;
}
@ -902,17 +418,14 @@ private:
folly::dynamic m_jscConfig;
};
static void createJSCExecutor(JNIEnv *env, jobject obj, jobject jscConfig) {
auto nativeMap = extractRefPtr<NativeMap>(env, jscConfig);
exceptions::throwIfObjectAlreadyConsumed(nativeMap, "Map to push already consumed");
auto executor = createNew<CountableJSCExecutorFactory>(std::move(nativeMap->map));
nativeMap->isConsumed = true;
setCountableForJava(env, obj, std::move(executor));
static void createJSCExecutor(alias_ref<jobject> obj, WritableNativeMap* jscConfig) {
auto executor = createNew<CountableJSCExecutorFactory>(jscConfig->consume());
setCountableForJava(Environment::current(), obj.get(), std::move(executor));
}
static void createProxyExecutor(JNIEnv *env, jobject obj, jobject executorInstance) {
auto executor =
createNew<ProxyExecutorOneTimeFactory>(jni::make_global(jni::adopt_local(executorInstance)));
createNew<ProxyExecutorOneTimeFactory>(make_global(adopt_local(executorInstance)));
setCountableForJava(env, obj, std::move(executor));
}
@ -943,68 +456,19 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
PerfLogging::installNativeHooks = addNativePerfLoggingHooks;
JSLogging::nativeHook = nativeLoggingHook;
// 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),
});
NativeMap::registerNatives();
ReadableNativeMap::registerNatives();
WritableNativeMap::registerNatives();
ReadableNativeMapKeySetIterator::registerNatives();
registerNatives("com/facebook/react/bridge/JSCJavaScriptExecutor", {
makeNativeMethod("initialize", "(Lcom/facebook/react/bridge/WritableNativeMap;)V",
executors::createJSCExecutor),
makeNativeMethod("initialize", executors::createJSCExecutor),
});
registerNatives("com/facebook/react/bridge/ProxyJavaScriptExecutor", {
@ -1013,6 +477,9 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
executors::createProxyExecutor),
});
// get the current env
JNIEnv* env = Environment::current();
jclass callbackClass = env->FindClass("com/facebook/react/bridge/ReactCallback");
bridge::gCallbackMethod = env->GetMethodID(callbackClass, "call", "(Lcom/facebook/react/bridge/ExecutorToken;IILcom/facebook/react/bridge/ReadableNativeArray;)V");
bridge::gOnBatchCompleteMethod = env->GetMethodID(callbackClass, "onBatchComplete", "()V");

View File

@ -0,0 +1,102 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "ReadableNativeArray.h"
#include "ReadableNativeMap.h"
using namespace facebook::jni;
namespace facebook {
namespace react {
// 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();
}
local_ref<ReadableNativeArray::jhybridobject> ReadableNativeArray::getArray(jint index) {
auto& elem = array.at(index);
if (elem.isNull()) {
return local_ref<ReadableNativeArray::jhybridobject>(nullptr);
} else {
return ReadableNativeArray::newObjectCxxArgs(elem);
}
}
local_ref<ReadableType> ReadableNativeArray::getType(jint index) {
return ReadableType::getType(array.at(index).type());
}
// Export getMap() so we can workaround constructing ReadableNativeMap
__attribute__((visibility("default")))
local_ref<NativeMap::jhybridobject> ReadableNativeArray::getMap(jint index) {
// TODO(cjhopman): ... this moves the map?!?
return ReadableNativeMap::createWithContents(std::move(array.at(index)));
}
void ReadableNativeArray::registerNatives() {
registerHybrid({
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", jmethod_traits<ReadableNativeMap::jhybridobject(jint)>::descriptor(),
ReadableNativeArray::getMap),
makeNativeMethod("getType", ReadableNativeArray::getType),
});
}
} // namespace react
} // namespace facebook

View File

@ -4,6 +4,9 @@
#include "NativeArray.h"
#include "NativeCommon.h"
#include "NativeMap.h"
namespace facebook {
namespace react {
@ -26,8 +29,10 @@ class ReadableNativeArray : public jni::HybridClass<ReadableNativeArray, NativeA
// careful.
const char* getString(jint index);
jni::local_ref<jhybridobject> getArray(jint index);
jobject getMap(jint index);
jobject getType(jint index);
// This actually returns a ReadableNativeMap::JavaPart, but due to
// limitations of fbjni, we can't specify that here.
jni::local_ref<NativeMap::jhybridobject> getMap(jint index);
jni::local_ref<ReadableType> getType(jint index);
static void registerNatives();
};

View File

@ -0,0 +1,150 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "ReadableNativeMap.h"
using namespace facebook::jni;
namespace facebook {
namespace react {
namespace {
const char *gNoSuchKeyExceptionClass = "com/facebook/react/bridge/NoSuchKeyException";
} // namespace
void ReadableNativeMap::mapException(const std::exception& ex) {
if (dynamic_cast<const folly::TypeError*>(&ex) != nullptr) {
throwNewJavaException(exceptions::gUnexpectedNativeTypeExceptionClass, ex.what());
}
}
bool ReadableNativeMap::hasKey(const std::string& key) {
return map_.find(key) != map_.items().end();
}
const folly::dynamic& ReadableNativeMap::getMapValue(const std::string& key) {
try {
return map_.at(key);
} catch (const std::out_of_range& ex) {
throwNewJavaException(gNoSuchKeyExceptionClass, ex.what());
}
}
bool ReadableNativeMap::isNull(const std::string& key) {
return getMapValue(key).isNull();
}
bool ReadableNativeMap::getBooleanKey(const std::string& key) {
return getMapValue(key).getBool();
}
double ReadableNativeMap::getDoubleKey(const std::string& key) {
const folly::dynamic& val = getMapValue(key);
if (val.isInt()) {
return val.getInt();
}
return val.getDouble();
}
jint ReadableNativeMap::getIntKey(const std::string& key) {
auto integer = getMapValue(key).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;
}
local_ref<jstring> ReadableNativeMap::getStringKey(const std::string& key) {
const folly::dynamic& val = getMapValue(key);
if (val.isNull()) {
return local_ref<jstring>(nullptr);
}
return make_jstring(val.getString().c_str());
}
local_ref<ReadableNativeArray::jhybridobject> ReadableNativeMap::getArrayKey(const std::string& key) {
auto& value = getMapValue(key);
if (value.isNull()) {
return local_ref<ReadableNativeArray::jhybridobject>(nullptr);
} else {
return ReadableNativeArray::newObjectCxxArgs(value);
}
}
local_ref<ReadableNativeMap::jhybridobject> ReadableNativeMap::getMapKey(const std::string& key) {
auto& value = getMapValue(key);
if (value.isNull()) {
return local_ref<ReadableNativeMap::jhybridobject>(nullptr);
} else if (!value.isObject()) {
throwNewJavaException(exceptions::gUnexpectedNativeTypeExceptionClass,
"expected Map, got a %s", value.typeName());
} else {
return ReadableNativeMap::newObjectCxxArgs(value);
}
}
local_ref<ReadableType> ReadableNativeMap::getValueType(const std::string& key) {
return ReadableType::getType(getMapValue(key).type());
}
local_ref<ReadableNativeMap::jhybridobject> ReadableNativeMap::createWithContents(folly::dynamic&& map) {
if (map.isNull()) {
return local_ref<jhybridobject>(nullptr);
}
if (!map.isObject()) {
throwNewJavaException(exceptions::gUnexpectedNativeTypeExceptionClass,
"expected Map, got a %s", map.typeName());
}
return newObjectCxxArgs(std::move(map));
}
void ReadableNativeMap::registerNatives() {
registerHybrid({
makeNativeMethod("hasKey", ReadableNativeMap::hasKey),
makeNativeMethod("isNull", ReadableNativeMap::isNull),
makeNativeMethod("getBoolean", ReadableNativeMap::getBooleanKey),
makeNativeMethod("getDouble", ReadableNativeMap::getDoubleKey),
makeNativeMethod("getInt", ReadableNativeMap::getIntKey),
makeNativeMethod("getString", ReadableNativeMap::getStringKey),
makeNativeMethod("getArray", ReadableNativeMap::getArrayKey),
makeNativeMethod("getMap", ReadableNativeMap::getMapKey),
makeNativeMethod("getType", ReadableNativeMap::getValueType),
});
}
ReadableNativeMapKeySetIterator::ReadableNativeMapKeySetIterator(const folly::dynamic& map)
: iter_(map.items().begin())
, map_(map) {}
local_ref<ReadableNativeMapKeySetIterator::jhybriddata> ReadableNativeMapKeySetIterator::initHybrid(alias_ref<jclass>, ReadableNativeMap* nativeMap) {
return makeCxxInstance(nativeMap->map_);
}
bool ReadableNativeMapKeySetIterator::hasNextKey() {
return iter_ != map_.items().end();
}
local_ref<jstring> ReadableNativeMapKeySetIterator::nextKey() {
if (!hasNextKey()) {
throwNewJavaException("com/facebook/react/bridge/InvalidIteratorException",
"No such element exists");
}
auto ret = make_jstring(iter_->first.c_str());
++iter_;
return ret;
}
void ReadableNativeMapKeySetIterator::registerNatives() {
registerHybrid({
makeNativeMethod("hasNextKey", ReadableNativeMapKeySetIterator::hasNextKey),
makeNativeMethod("nextKey", ReadableNativeMapKeySetIterator::nextKey),
makeNativeMethod("initHybrid", ReadableNativeMapKeySetIterator::initHybrid),
});
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,59 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <fb/fbjni.h>
#include <folly/dynamic.h>
#include <folly/json.h>
#include "NativeCommon.h"
#include "NativeMap.h"
#include "ReadableNativeArray.h"
namespace facebook {
namespace react {
struct WritableNativeMap;
struct ReadableNativeMap : jni::HybridClass<ReadableNativeMap, NativeMap> {
static auto constexpr kJavaDescriptor = "Lcom/facebook/react/bridge/ReadableNativeMap;";
bool hasKey(const std::string& key);
const folly::dynamic& getMapValue(const std::string& key);
bool isNull(const std::string& key);
bool getBooleanKey(const std::string& key);
double getDoubleKey(const std::string& key);
jint getIntKey(const std::string& key);
jni::local_ref<jstring> getStringKey(const std::string& key);
jni::local_ref<ReadableNativeArray::jhybridobject> getArrayKey(const std::string& key);
jni::local_ref<jhybridobject> getMapKey(const std::string& key);
jni::local_ref<ReadableType> getValueType(const std::string& key);
static jni::local_ref<jhybridobject> createWithContents(folly::dynamic&& map);
static void mapException(const std::exception& ex);
static void registerNatives();
using HybridBase::HybridBase;
friend HybridBase;
friend class WritableNativeMap;
};
struct ReadableNativeMapKeySetIterator : jni::HybridClass<ReadableNativeMapKeySetIterator> {
static auto constexpr kJavaDescriptor = "Lcom/facebook/react/bridge/ReadableNativeMap$ReadableNativeMapKeySetIterator;";
ReadableNativeMapKeySetIterator(const folly::dynamic& map);
bool hasNextKey();
jni::local_ref<jstring> nextKey();
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>, ReadableNativeMap* nativeMap);
static void registerNatives();
folly::dynamic::const_item_iterator iter_;
// The Java side holds a strong ref to the Java ReadableNativeMap.
const folly::dynamic& map_;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,83 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "WritableNativeArray.h"
#include "WritableNativeMap.h"
using namespace facebook::jni;
namespace facebook {
namespace react {
WritableNativeArray::WritableNativeArray()
: HybridBase(folly::dynamic::array()) {}
local_ref<WritableNativeArray::jhybriddata> WritableNativeArray::initHybrid(alias_ref<jclass>) {
return makeCxxInstance();
}
void WritableNativeArray::pushNull() {
exceptions::throwIfObjectAlreadyConsumed(this, "Array already consumed");
array.push_back(nullptr);
}
void WritableNativeArray::pushBoolean(jboolean value) {
exceptions::throwIfObjectAlreadyConsumed(this, "Array already consumed");
array.push_back(value == JNI_TRUE);
}
void WritableNativeArray::pushDouble(jdouble value) {
exceptions::throwIfObjectAlreadyConsumed(this, "Receiving array already consumed");
array.push_back(value);
}
void WritableNativeArray::pushInt(jint value) {
exceptions::throwIfObjectAlreadyConsumed(this, "Receiving array already consumed");
array.push_back(value);
}
void WritableNativeArray::pushString(jstring value) {
if (value == NULL) {
pushNull();
return;
}
exceptions::throwIfObjectAlreadyConsumed(this, "Receiving array already consumed");
array.push_back(wrap_alias(value)->toStdString());
}
void WritableNativeArray::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 WritableNativeArray::pushNativeMap(WritableNativeMap* map) {
if (map == NULL) {
pushNull();
return;
}
exceptions::throwIfObjectAlreadyConsumed(this, "Receiving array already consumed");
map->throwIfConsumed();
array.push_back(map->consume());
}
void WritableNativeArray::registerNatives() {
registerHybrid({
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", WritableNativeArray::pushNativeMap),
});
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,35 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <fb/fbjni.h>
#include <folly/dynamic.h>
#include <folly/json.h>
#include "ReadableNativeArray.h"
namespace facebook {
namespace react {
struct WritableNativeMap;
struct WritableNativeArray
: public jni::HybridClass<WritableNativeArray, ReadableNativeArray> {
static constexpr const char* kJavaDescriptor = "Lcom/facebook/react/bridge/WritableNativeArray;";
WritableNativeArray();
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>);
void pushNull();
void pushBoolean(jboolean value);
void pushDouble(jdouble value);
void pushInt(jint value);
void pushString(jstring value);
void pushNativeArray(WritableNativeArray* otherArray);
void pushNativeMap(WritableNativeMap* map);
static void registerNatives();
};
}
}

View File

@ -0,0 +1,105 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "WritableNativeMap.h"
using namespace facebook::jni;
namespace facebook {
namespace react {
WritableNativeMap::WritableNativeMap()
: HybridBase(folly::dynamic::object()) {}
WritableNativeMap::WritableNativeMap(folly::dynamic&& val)
: HybridBase(std::move(val)) {
if (!map_.isObject()) {
throw std::runtime_error("WritableNativeMap value must be an object.");
}
}
local_ref<WritableNativeMap::jhybriddata> WritableNativeMap::initHybrid(alias_ref<jclass>) {
return makeCxxInstance();
}
folly::dynamic WritableNativeMap::consume() {
throwIfConsumed();
isConsumed = true;
return std::move(map_);
}
void WritableNativeMap::putNull(std::string key) {
throwIfConsumed();
map_.insert(std::move(key), nullptr);
}
void WritableNativeMap::putBoolean(std::string key, bool val) {
throwIfConsumed();
map_.insert(std::move(key), val);
}
void WritableNativeMap::putDouble(std::string key, double val) {
throwIfConsumed();
map_.insert(std::move(key), val);
}
void WritableNativeMap::putInt(std::string key, int val) {
throwIfConsumed();
map_.insert(std::move(key), val);
}
void WritableNativeMap::putString(std::string key, alias_ref<jstring> val) {
if (!val) {
putNull(std::move(key));
return;
}
throwIfConsumed();
map_.insert(std::move(key), val->toString());
}
void WritableNativeMap::putNativeArray(std::string key, alias_ref<WritableNativeArray::jhybridobject> val) {
if (!val) {
putNull(std::move(key));
return;
}
throwIfConsumed();
auto array = val->cthis();
exceptions::throwIfObjectAlreadyConsumed(array, "Array to put already consumed");
map_.insert(key, std::move(array->array));
array->isConsumed = true;
}
void WritableNativeMap::putNativeMap(std::string key, alias_ref<jhybridobject> val) {
if (!val) {
putNull(std::move(key));
return;
}
throwIfConsumed();
auto other = val->cthis();
map_.insert(std::move(key), other->consume());
}
void WritableNativeMap::mergeNativeMap(ReadableNativeMap* other) {
throwIfConsumed();
other->throwIfConsumed();
for (auto sourceIt : other->map_.items()) {
map_[sourceIt.first] = sourceIt.second;
}
}
void WritableNativeMap::registerNatives() {
registerHybrid({
makeNativeMethod("putNull", WritableNativeMap::putNull),
makeNativeMethod("putBoolean", WritableNativeMap::putBoolean),
makeNativeMethod("putDouble", WritableNativeMap::putDouble),
makeNativeMethod("putInt", WritableNativeMap::putInt),
makeNativeMethod("putString", WritableNativeMap::putString),
makeNativeMethod("putNativeArray", WritableNativeMap::putNativeArray),
makeNativeMethod("putNativeMap", WritableNativeMap::putNativeMap),
makeNativeMethod("mergeNativeMap", WritableNativeMap::mergeNativeMap),
makeNativeMethod("initHybrid", WritableNativeMap::initHybrid),
});
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,40 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <fb/fbjni.h>
#include <folly/dynamic.h>
#include <folly/json.h>
#include "ReadableNativeMap.h"
#include "WritableNativeArray.h"
namespace facebook {
namespace react {
struct WritableNativeMap : jni::HybridClass<WritableNativeMap, ReadableNativeMap> {
static auto constexpr kJavaDescriptor = "Lcom/facebook/react/bridge/WritableNativeMap;";
WritableNativeMap();
WritableNativeMap(folly::dynamic&& val);
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>);
folly::dynamic consume();
void putNull(std::string key);
void putBoolean(std::string key, bool val);
void putDouble(std::string key, double val);
void putInt(std::string key, int val);
void putString(std::string key, jni::alias_ref<jstring> val);
void putNativeArray(std::string key, jni::alias_ref<WritableNativeArray::jhybridobject> val);
void putNativeMap(std::string key, jni::alias_ref<jhybridobject> val);
void mergeNativeMap(ReadableNativeMap* other);
static void registerNatives();
friend HybridBase;
};
} // namespace react
} // namespace facebook

View File

@ -144,7 +144,7 @@ jvalue extract(std::weak_ptr<Instance>& instance, ExecutorToken token, char type
break;
case 'M':
// HACK: Workaround for constructing ReadableNativeMap
value.l = ExposedReadableNativeArray(folly::dynamic::array(arg)).getMap(0);
value.l = ExposedReadableNativeArray(folly::dynamic::array(arg)).getMap(0).release();
break;
case 'X':
value.l = extractCallback(instance, token, arg).release();