Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include <cxxreact/TraceSection.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/jni/ReadableNativeArray.h>
#include <react/jni/ReadableNativeMap.h>
#include <react/renderer/components/scrollview/ScrollViewProps.h>
#include <react/renderer/core/DynamicPropsUtilities.h>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jboolean JDynamicNative::isNullNative() {
return static_cast<jboolean>(payload_.isNull());
}

jni::local_ref<ReadableType> JDynamicNative::getTypeNative() {
jni::alias_ref<ReadableType> JDynamicNative::getTypeNative() {
return ReadableType::getType(payload_.type());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class JDynamicNative : public jni::HybridClass<JDynamicNative, JDynamic> {
private:
friend HybridBase;

jni::local_ref<ReadableType> getTypeNative();
jni::alias_ref<ReadableType> getTypeNative();
jni::local_ref<jstring> asString();
jboolean asBoolean();
jdouble asDouble();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <fbsystrace.h>
#endif

#include "NativeMap.h"
#include "ReadableNativeArray.h"

#ifndef RCT_REMOVE_LEGACY_ARCH
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,32 @@ alias_ref<ReadableType> getTypeField(const char* fieldName) {

} // namespace

local_ref<ReadableType> ReadableType::getType(folly::dynamic::Type type) {
alias_ref<ReadableType> ReadableType::getType(folly::dynamic::Type type) {
switch (type) {
case folly::dynamic::Type::NULLT: {
static alias_ref<ReadableType> val = getTypeField("Null");
return make_local(val);
static auto val = getTypeField("Null");
return val;
}
case folly::dynamic::Type::BOOL: {
static alias_ref<ReadableType> val = getTypeField("Boolean");
return make_local(val);
static auto val = getTypeField("Boolean");
return val;
}
case folly::dynamic::Type::DOUBLE:
case folly::dynamic::Type::INT64: {
static alias_ref<ReadableType> val = getTypeField("Number");
return make_local(val);
static auto val = getTypeField("Number");
return val;
}
case folly::dynamic::Type::STRING: {
static alias_ref<ReadableType> val = getTypeField("String");
return make_local(val);
static auto val = getTypeField("String");
return val;
}
case folly::dynamic::Type::OBJECT: {
static alias_ref<ReadableType> val = getTypeField("Map");
return make_local(val);
static auto val = getTypeField("Map");
return val;
}
case folly::dynamic::Type::ARRAY: {
static alias_ref<ReadableType> val = getTypeField("Array");
return make_local(val);
static auto val = getTypeField("Array");
return val;
}
default:
throwNewJavaException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace facebook::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);
static jni::alias_ref<ReadableType> getType(folly::dynamic::Type type);
};

namespace exceptions {
Expand All @@ -29,7 +29,7 @@ extern const char *gUnexpectedNativeTypeExceptionClass;
template <typename T>
void throwIfObjectAlreadyConsumed(const T &t, const char *msg)
{
if (t->isConsumed) {
if (t->isConsumed) [[unlikely]] {
jni::throwNewJavaException("com/facebook/react/bridge/ObjectAlreadyConsumedException", msg);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ void ReadableNativeArray::mapException(std::exception_ptr ex) {
}

local_ref<JArrayClass<jobject>> ReadableNativeArray::importArray() {
auto size = static_cast<jint>(array_.size());
auto size = static_cast<jsize>(array_.size());
auto jarray = JArrayClass<jobject>::newArray(size);
for (jint ii = 0; ii < size; ii++) {
addDynamicToJArray(jarray, ii, array_.at(ii));
Expand All @@ -32,10 +32,10 @@ local_ref<JArrayClass<jobject>> ReadableNativeArray::importArray() {
}

local_ref<JArrayClass<jobject>> ReadableNativeArray::importTypeArray() {
auto size = static_cast<jint>(array_.size());
auto size = static_cast<jsize>(array_.size());
auto jarray = JArrayClass<jobject>::newArray(size);
for (jint ii = 0; ii < size; ii++) {
(*jarray)[ii] = ReadableType::getType(array_.at(ii).type());
jarray->setElement(ii, ReadableType::getType(array_.at(ii).type()).get());
}
return jarray;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@

#include "NativeArray.h"

#include "NativeCommon.h"
#include "NativeMap.h"

namespace facebook::react {

struct ReadableArray : jni::JavaClass<ReadableArray> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include "ReadableNativeMap.h"

#include "ReadableNativeArray.h"

using namespace facebook::jni;

namespace facebook::react {
Expand All @@ -20,84 +22,86 @@ void ReadableNativeMap::mapException(std::exception_ptr ex) {
}
}

void ReadableNativeMap::throwIfKeysNotImported() const {
if (!values_.has_value()) [[unlikely]] {
throwNewJavaException(
"java/lang/IllegalStateException",
"importKeys must be called before importing values or types");
}
}

void addDynamicToJArray(
local_ref<JArrayClass<jobject>> jarray,
alias_ref<JArrayClass<jobject>> jarray,
jint index,
const folly::dynamic& dyn) {
local_ref<jobject> value;
switch (dyn.type()) {
case folly::dynamic::Type::NULLT: {
jarray->setElement(index, nullptr);
break;
}
case folly::dynamic::Type::BOOL: {
(*jarray)[index] =
JBoolean::valueOf(static_cast<unsigned char>(dyn.getBool()));
case folly::dynamic::Type::BOOL:
value = JBoolean::valueOf(static_cast<jboolean>(dyn.getBool()));
break;
}
case folly::dynamic::Type::INT64: {
(*jarray)[index] = JDouble::valueOf(dyn.getInt());
case folly::dynamic::Type::INT64:
value = JDouble::valueOf(static_cast<double>(dyn.getInt()));
break;
}
case folly::dynamic::Type::DOUBLE: {
(*jarray)[index] = JDouble::valueOf(dyn.getDouble());
case folly::dynamic::Type::DOUBLE:
value = JDouble::valueOf(dyn.getDouble());
break;
}
case folly::dynamic::Type::STRING: {
(*jarray)[index] = make_jstring(dyn.getString());
case folly::dynamic::Type::STRING:
value = make_jstring(dyn.getString());
break;
}
case folly::dynamic::Type::OBJECT: {
(*jarray)[index] = ReadableNativeMap::newObjectCxxArgs(dyn);
case folly::dynamic::Type::OBJECT:
value = ReadableNativeMap::newObjectCxxArgs(dyn);
break;
}
case folly::dynamic::Type::ARRAY: {
(*jarray)[index] = ReadableNativeArray::newObjectCxxArgs(dyn);
case folly::dynamic::Type::ARRAY:
value = ReadableNativeArray::newObjectCxxArgs(dyn);
break;
}
case folly::dynamic::Type::NULLT:
default:
jarray->setElement(index, nullptr);
break;
}
jarray->setElement(index, value.get());
}

local_ref<JArrayClass<jstring>> ReadableNativeMap::importKeys() {
throwIfConsumed();

keys_ = folly::dynamic::array();
if (map_ == nullptr) {
return JArrayClass<jstring>::newArray(0);
}
auto jarray = JArrayClass<jstring>::newArray(map_.size());
auto size = map_ == nullptr ? 0 : static_cast<jsize>(map_.size());
std::vector<const folly::dynamic*> values(size);

auto jarray = JArrayClass<jstring>::newArray(size);
jint i = 0;
for (auto& pair : map_.items()) {
auto value = pair.first.asString();
(*keys_).push_back(value);
(*jarray)[i++] = make_jstring(value);
if (map_ != nullptr) {
for (auto& pair : map_.items()) {
values[i] = &pair.second;
jarray->setElement(i++, make_jstring(pair.first.getString()).get());
}
}
values_ = std::move(values);

return jarray;
}

local_ref<JArrayClass<jobject>> ReadableNativeMap::importValues() {
throwIfConsumed();
throwIfKeysNotImported();

auto size = static_cast<jint>(keys_.value().size());
const auto& values = values_.value();
auto size = static_cast<jsize>(values.size());
auto jarray = JArrayClass<jobject>::newArray(size);
for (jint ii = 0; ii < size; ii++) {
const std::string& key = (*keys_)[ii].getString();
addDynamicToJArray(jarray, ii, map_.at(key));
addDynamicToJArray(jarray, ii, *values[ii]);
}
return jarray;
}

local_ref<JArrayClass<jobject>> ReadableNativeMap::importTypes() {
throwIfConsumed();
throwIfKeysNotImported();

auto size = static_cast<jint>(keys_.value().size());
const auto& values = values_.value();
auto size = static_cast<jsize>(values.size());
auto jarray = JArrayClass<jobject>::newArray(size);
for (jint ii = 0; ii < size; ii++) {
const std::string& key = (*keys_)[ii].getString();
(*jarray)[ii] = ReadableType::getType(map_.at(key).type());
jarray->setElement(ii, ReadableType::getType(values[ii]->type()).get());
}
return jarray;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
#include <folly/dynamic.h>
#include <folly/json.h>
#include <optional>
#include <vector>

#include "NativeCommon.h"
#include "NativeMap.h"
#include "ReadableNativeArray.h"

namespace facebook::react {

Expand All @@ -24,15 +23,14 @@ struct ReadableMap : jni::JavaClass<ReadableMap> {
static auto constexpr kJavaDescriptor = "Lcom/facebook/react/bridge/ReadableMap;";
};

void addDynamicToJArray(jni::local_ref<jni::JArrayClass<jobject>> jarray, jint index, const folly::dynamic &dyn);
void addDynamicToJArray(jni::alias_ref<jni::JArrayClass<jobject>> jarray, jint index, const folly::dynamic &dyn);

struct ReadableNativeMap : jni::HybridClass<ReadableNativeMap, NativeMap> {
static auto constexpr kJavaDescriptor = "Lcom/facebook/react/bridge/ReadableNativeMap;";

jni::local_ref<jni::JArrayClass<jstring>> importKeys();
jni::local_ref<jni::JArrayClass<jobject>> importValues();
jni::local_ref<jni::JArrayClass<jobject>> importTypes();
std::optional<folly::dynamic> keys_;
static jni::local_ref<jhybridobject> createWithContents(folly::dynamic &&map);

static void mapException(std::exception_ptr ex);
Expand All @@ -41,6 +39,14 @@ struct ReadableNativeMap : jni::HybridClass<ReadableNativeMap, NativeMap> {
using HybridBase::HybridBase;
friend HybridBase;
friend struct WritableNativeMap;

private:
void throwIfKeysNotImported() const;

// folly::dynamic stores object entries in an F14NodeMap, so these pointers
// remain valid across the insertions and replacements exposed by
// WritableNativeMap.
std::optional<std::vector<const folly::dynamic *>> values_;
};

} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import com.facebook.react.ReactRootView
import com.facebook.react.bridge.JSExceptionHandler
import com.facebook.react.bridge.JavaOnlyMap
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableType
import com.facebook.react.bridge.WritableNativeMap
import com.facebook.react.fabric.mounting.MountingManager
import com.facebook.react.fabric.mounting.MountingManager.MountItemExecutor
import com.facebook.react.fabric.mounting.mountitems.IntBufferBatchMountItem
Expand Down Expand Up @@ -110,6 +112,22 @@ class FabricMountingManagerInstrumentationTest {
assertThat(smm.getView(42)).isNotNull()
}

@Test
fun writableNativeMap_mutationAfterImportingValues_preservesCachedPointers() {
val map = WritableNativeMap()
map.putDouble("opacity", 1.0)

assertThat(map.hasKey("opacity")).isTrue()
assertThat(map.getType("opacity")).isEqualTo(ReadableType.Number)

map.putDouble("opacity", 0.3)
repeat(64) { map.putInt("newKey$it", it) }

val entry = map.entryIterator.next()
assertThat(entry.key).isEqualTo("opacity")
assertThat(entry.value).isEqualTo(0.3)
}

/**
* Simulates the scenario fixed by D98729251 via IntBufferBatchMountItem:
* 1. Preallocate a view (simulates C++ preallocateShadowView calling Java preallocateView)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include <react/common/mapbuffer/JReadableMapBuffer.h>
#include <react/debug/react_native_assert.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/jni/ReadableNativeMap.h>
#include <react/jni/NativeArray.h>
#include <react/renderer/attributedstring/conversions.h>
#include <react/renderer/core/conversions.h>
#include <react/renderer/mapbuffer/MapBuffer.h>
Expand Down
5 changes: 2 additions & 3 deletions scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,7 @@ uint8_t facebook::react::blueFromColor(facebook::react::SharedColor color) noexc
uint8_t facebook::react::greenFromColor(facebook::react::SharedColor color) noexcept;
uint8_t facebook::react::redFromColor(facebook::react::SharedColor color) noexcept;
void facebook::react::FBReactNativeSpec_registerComponentDescriptorsFromCodegen(std::shared_ptr<const facebook::react::ComponentDescriptorProviderRegistry> registry);
void facebook::react::addDynamicToJArray(jni::local_ref<jni::JArrayClass<jobject>> jarray, jint index, const folly::dynamic& dyn);
void facebook::react::addDynamicToJArray(jni::alias_ref<jni::JArrayClass<jobject>> jarray, jint index, const folly::dynamic& dyn);
void facebook::react::bindHasComponentProvider(facebook::jsi::Runtime& runtime, facebook::react::HasComponentProviderFunctionType&& provider);
void facebook::react::bindNativeLogger(facebook::jsi::Runtime& runtime, facebook::react::Logger logger);
void facebook::react::bindNativePerformanceNow(facebook::jsi::Runtime& runtime);
Expand Down Expand Up @@ -7877,12 +7877,11 @@ struct facebook::react::ReadableNativeMap : public jni::HybridClass<facebook::re
public static jni::local_ref<jhybridobject> createWithContents(folly::dynamic&& map);
public static void mapException(std::exception_ptr ex);
public static void registerNatives();
public std::optional<folly::dynamic> keys_;
}

struct facebook::react::ReadableType : public facebook::jni::JavaClass<facebook::react::ReadableType> {
public static constexpr auto kJavaDescriptor;
public static jni::local_ref<facebook::react::ReadableType> getType(folly::dynamic::Type type);
public static jni::alias_ref<facebook::react::ReadableType> getType(folly::dynamic::Type type);
}

struct facebook::react::RecoverableError : public std::exception {
Expand Down
5 changes: 2 additions & 3 deletions scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -1060,7 +1060,7 @@ uint8_t facebook::react::blueFromColor(facebook::react::SharedColor color) noexc
uint8_t facebook::react::greenFromColor(facebook::react::SharedColor color) noexcept;
uint8_t facebook::react::redFromColor(facebook::react::SharedColor color) noexcept;
void facebook::react::FBReactNativeSpec_registerComponentDescriptorsFromCodegen(std::shared_ptr<const facebook::react::ComponentDescriptorProviderRegistry> registry);
void facebook::react::addDynamicToJArray(jni::local_ref<jni::JArrayClass<jobject>> jarray, jint index, const folly::dynamic& dyn);
void facebook::react::addDynamicToJArray(jni::alias_ref<jni::JArrayClass<jobject>> jarray, jint index, const folly::dynamic& dyn);
void facebook::react::bindHasComponentProvider(facebook::jsi::Runtime& runtime, facebook::react::HasComponentProviderFunctionType&& provider);
void facebook::react::bindNativeLogger(facebook::jsi::Runtime& runtime, facebook::react::Logger logger);
void facebook::react::bindNativePerformanceNow(facebook::jsi::Runtime& runtime);
Expand Down Expand Up @@ -7637,12 +7637,11 @@ struct facebook::react::ReadableNativeMap : public jni::HybridClass<facebook::re
public static jni::local_ref<jhybridobject> createWithContents(folly::dynamic&& map);
public static void mapException(std::exception_ptr ex);
public static void registerNatives();
public std::optional<folly::dynamic> keys_;
}

struct facebook::react::ReadableType : public facebook::jni::JavaClass<facebook::react::ReadableType> {
public static constexpr auto kJavaDescriptor;
public static jni::local_ref<facebook::react::ReadableType> getType(folly::dynamic::Type type);
public static jni::alias_ref<facebook::react::ReadableType> getType(folly::dynamic::Type type);
}

struct facebook::react::RecoverableError : public std::exception {
Expand Down
Loading
Loading