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 @@ -15,9 +15,15 @@ import com.facebook.proguard.annotations.DoNotStrip
* to stub out creating this class in a test. TODO(5815532): Check if consumed on read
*/
@DoNotStrip
public class WritableNativeMap : ReadableNativeMap(), WritableMap {
public class WritableNativeMap @DoNotStrip private constructor(initializeNative: Boolean) :
ReadableNativeMap(), WritableMap {
/** Creates an empty writable map backed by native memory. */
public constructor() : this(true)

init {
initHybrid()
if (initializeNative) {
initHybrid()
}
}

external override fun putBoolean(key: String, value: Boolean)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <react/fabric/FabricUIManagerBinding.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/featureflags/ReactNativeFeatureFlagsDefaults.h>
#include <react/jni/WritableNativeMap.h>
#include <react/renderer/animationbackend/AnimatedProps.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/components/root/RootShadowNode.h>
Expand Down Expand Up @@ -62,6 +63,17 @@ class FabricMountingManagerTestHelper::DeleteRecorder

namespace {

jni::local_ref<WritableNativeMap::jhybridobject> createWritableNativeMapFromCxx(
jni::alias_ref<jclass> /*unused*/,
jboolean withContents) {
if (withContents == JNI_FALSE) {
return WritableNativeMap::newObjectCxxArgs();
}
folly::dynamic payload = folly::dynamic::object("value", 42)(
"nested", folly::dynamic::object("enabled", true));
return WritableNativeMap::newObjectCxxArgs(std::move(payload));
}

ShadowView makeShadowView(jint surfaceId, jint tag) {
ShadowView sv{};
sv.componentName = "View";
Expand Down Expand Up @@ -402,6 +414,8 @@ FabricMountingManagerTestHelper::pulledDeleteTags() {

void FabricMountingManagerTestHelper::registerNatives() {
registerHybrid({
makeNativeMethod(
"createWritableNativeMapFromCxx", createWritableNativeMapFromCxx),
makeNativeMethod(
"initHybrid", FabricMountingManagerTestHelper::initHybrid),
makeNativeMethod(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
#include "TouchesHelper.h"

#include <stdexcept>
#include <utility>

#include "WritableNativeArray.h"
#include "WritableNativeMap.h"

using namespace facebook::jni;
Expand Down Expand Up @@ -72,25 +72,11 @@ local_ref<WritableNativeMap::jhybridobject> createTouchPayload(
}
}

auto jPayload = WritableNativeMap::newObjectJavaArgs();
auto* payload = jPayload->cthis();
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
const double* fields = values.get() + eventPointer * kPointerFields;
payload->putDouble("pageX", fields[kPageXIndex]);
payload->putDouble("pageY", fields[kPageYIndex]);
payload->putDouble("locationX", fields[kLocationXIndex]);
payload->putDouble("locationY", fields[kLocationYIndex]);
payload->putInt(
"targetSurface", static_cast<int>(fields[kTargetSurfaceIndex]));
payload->putInt("target", static_cast<int>(fields[kTargetIndex]));
payload->putDouble("timestamp", fields[kTimestampIndex]);
payload->putDouble("identifier", fields[kIdentifierIndex]);
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
WritableNativeArray changedTouchesArray(std::move(changedTouches));
payload->putNativeArray("changedTouches", &changedTouchesArray);
WritableNativeArray touchesArray(std::move(touches));
payload->putNativeArray("touches", &touchesArray);
return jPayload;
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
auto payload = createTouch(values.get() + eventPointer * kPointerFields);
payload.insert("changedTouches", std::move(changedTouches));
payload.insert("touches", std::move(touches));
return WritableNativeMap::newObjectCxxArgs(std::move(payload));
}

} // namespace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include "WritableNativeMap.h"

#include <utility>

using namespace facebook::jni;

namespace facebook::react {
Expand All @@ -20,6 +22,13 @@ WritableNativeMap::WritableNativeMap(folly::dynamic&& val)
}
}

local_ref<WritableNativeMap::JavaPart> WritableNativeMap::newObjectCxxArgs(
folly::dynamic&& val) {
auto map = newObjectJavaArgs(false);
setCxxInstance(map, std::move(val));
return map;
}

void WritableNativeMap::initHybrid(alias_ref<jhybridobject> jobj) {
setCxxInstance(jobj);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ struct WritableNativeMap : jni::HybridClass<WritableNativeMap, ReadableNativeMap
WritableNativeMap();
WritableNativeMap(folly::dynamic &&val);

/** Creates a Java map without invoking initHybrid before installing its C++ contents. */
static jni::local_ref<JavaPart> newObjectCxxArgs(folly::dynamic &&val = folly::dynamic::object());

static void initHybrid(jni::alias_ref<jhybridobject> jobj);

void putNull(std::string key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
package com.facebook.react.fabric

import android.graphics.Rect
import android.os.ParcelFileDescriptor
import android.os.Process
import android.os.SystemClock
import android.util.Log
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
Expand Down Expand Up @@ -35,6 +39,7 @@ import com.facebook.react.uimanager.events.BatchEventDispatchedListener
import com.facebook.react.views.view.ReactViewGroup
import com.facebook.react.views.view.ReactViewManager
import com.facebook.soloader.SoLoader
import java.util.UUID
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.within
import org.junit.After
Expand Down Expand Up @@ -134,6 +139,55 @@ class FabricMountingManagerInstrumentationTest {
assertThat(smm.getView(42)).isNotNull()
}

@Test
fun writableNativeMap_createdInCxx_preservesContentsAndRemainsWritable() {
val map = createWritableNativeMapFromCxx(true)

map.putInt("added", 7)
map.putString("label", "writable")
assertThat(map.getInt("value")).isEqualTo(42)
assertThat(map.getMap("nested")?.getBoolean("enabled")).isTrue()
assertThat(map.getInt("added")).isEqualTo(7)
assertThat(map.getString("label")).isEqualTo("writable")
}

@Test
fun writableNativeMap_createdInCxxWithoutContents_isEmptyAndWritable() {
val map = createWritableNativeMapFromCxx(false)

assertThat(map.toString()).isEqualTo("{}")
map.putInt("value", 42)
assertThat(map.getInt("value")).isEqualTo(42)
}

private fun createWritableNativeMapFromCxx(withContents: Boolean): WritableNativeMap {
val token = UUID.randomUUID().toString()
val startMarker = "WritableNativeMap start $token"
val endMarker = "WritableNativeMap end $token"
Log.i("WritableNativeMapTest", startMarker)
val map = FabricMountingManagerTestHelper.createWritableNativeMapFromCxx(withContents)
Log.i("WritableNativeMapTest", endMarker)

val deadline = SystemClock.elapsedRealtime() + 2_000
do {
val descriptor =
InstrumentationRegistry.getInstrumentation()
.uiAutomation
.executeShellCommand("logcat -d -b main --pid=${Process.myPid()}")
val logs =
ParcelFileDescriptor.AutoCloseInputStream(descriptor).bufferedReader().use {
it.readText()
}
if (logs.contains(endMarker)) {
assertThat(logs).contains(startMarker)
assertThat(logs.substringAfter(startMarker).substringBefore(endMarker))
.doesNotContain("Attempt to set C++ native pointer twice")
return map
}
} while (SystemClock.elapsedRealtime() < deadline)
throw AssertionError("WritableNativeMap log markers did not arrive")
}

@Test
fun writableNativeMap_mutationAfterImportingValues_preservesCachedPointers() {
val map = WritableNativeMap()
Expand Down Expand Up @@ -252,7 +306,7 @@ class FabricMountingManagerInstrumentationTest {
}

private fun mountPaddedView(
fixTextLayoutOnPixelDensityChange: Boolean
fixTextLayoutOnPixelDensityChange: Boolean,
): Pair<FabricMountingManagerTestHelper, ReactViewGroup> {
val fabricUIManager = createFabricUIManager(listOf(PaddingViewManager()))
val helper = FabricMountingManagerTestHelper.create(fabricUIManager)
Expand Down Expand Up @@ -281,7 +335,7 @@ class FabricMountingManagerInstrumentationTest {
ReactNativeFeatureFlagsCxxInterop.dangerouslyForceOverride(
object : ReactNativeFeatureFlagsDefaults() {
override fun enableMountingCoordinatorPullModelAndroid(): Boolean = true
}
},
)
}

Expand Down Expand Up @@ -501,7 +555,7 @@ class FabricMountingManagerInstrumentationTest {
override fun enablePropsUpdateReconciliationAndroid(): Boolean = pullModel

override fun enablePreallocatedPropsDiffOnInsertAndroid(): Boolean = pullModel
}
},
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
package com.facebook.react.fabric

import com.facebook.jni.HybridClassBase
import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.bridge.NativeMap
import com.facebook.react.bridge.RuntimeExecutor
import com.facebook.react.bridge.RuntimeScheduler
import com.facebook.react.bridge.WritableNativeMap
import com.facebook.soloader.SoLoader

/**
Expand Down Expand Up @@ -90,6 +92,10 @@ private constructor(fabricUIManager: FabricUIManager) : HybridClassBase() {
SoLoader.loadLibrary("fabricjni_test_helper")
}

@DoNotStrip
@JvmStatic
external fun createWritableNativeMapFromCxx(withContents: Boolean): WritableNativeMap

@JvmStatic
fun create(fabricUIManager: FabricUIManager): FabricMountingManagerTestHelper {
return FabricMountingManagerTestHelper(fabricUIManager)
Expand Down
1 change: 1 addition & 0 deletions scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -3756,6 +3756,7 @@ struct facebook::react::WritableNativeMap : public jni::HybridClass<facebook::re
public WritableNativeMap(folly::dynamic&& val);
public friend HybridBase;
public static constexpr auto kJavaDescriptor;
public static jni::local_ref<JavaPart> newObjectCxxArgs(folly::dynamic&& val = folly::dynamic::object());
public static void initHybrid(jni::alias_ref<jhybridobject> jobj);
public static void registerNatives();
public void mergeNativeMap(facebook::react::ReadableNativeMap* other);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6806,6 +6806,7 @@ struct facebook::react::WritableNativeMap : public jni::HybridClass<facebook::re
public WritableNativeMap(folly::dynamic&& val);
public friend HybridBase;
public static constexpr auto kJavaDescriptor;
public static jni::local_ref<JavaPart> newObjectCxxArgs(folly::dynamic&& val = folly::dynamic::object());
public static void initHybrid(jni::alias_ref<jhybridobject> jobj);
public static void registerNatives();
public void mergeNativeMap(facebook::react::ReadableNativeMap* other);
Expand Down
1 change: 1 addition & 0 deletions scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -3749,6 +3749,7 @@ struct facebook::react::WritableNativeMap : public jni::HybridClass<facebook::re
public WritableNativeMap(folly::dynamic&& val);
public friend HybridBase;
public static constexpr auto kJavaDescriptor;
public static jni::local_ref<JavaPart> newObjectCxxArgs(folly::dynamic&& val = folly::dynamic::object());
public static void initHybrid(jni::alias_ref<jhybridobject> jobj);
public static void registerNatives();
public void mergeNativeMap(facebook::react::ReadableNativeMap* other);
Expand Down
1 change: 1 addition & 0 deletions scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -3749,6 +3749,7 @@ struct facebook::react::WritableNativeMap : public jni::HybridClass<facebook::re
public WritableNativeMap(folly::dynamic&& val);
public friend HybridBase;
public static constexpr auto kJavaDescriptor;
public static jni::local_ref<JavaPart> newObjectCxxArgs(folly::dynamic&& val = folly::dynamic::object());
public static void initHybrid(jni::alias_ref<jhybridobject> jobj);
public static void registerNatives();
public void mergeNativeMap(facebook::react::ReadableNativeMap* other);
Expand Down
Loading