[0.81] Backport useNativeEqualsInNativeReadableArrayAndroid and useNativeTransformHelperAndroid in the experimental channel (#53567)

* Use native implementation of equals in ReadableNativeArray (#52611)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52611

We compare the current transform (represented as a ReadableArray) with the incoming one to know whether to invalidate. This can be expensive as it requires to materialize the entire transform data structure over JNI. Instead, we can delegate this comparison to native code, which can compare the underlying folly::dynamic directly.

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D78340288

fbshipit-source-id: f44a054e234694c316fb080fe2dbc2017780123a

* Use native helpers to accelerate transform processing (#52603)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52603

Processing transforms is expensive in Java, as it requires bridging the entire ReadableNativeArray/Map. Instead, we can use the existing parser logic `resolveTransform` logic to perform this operation in C++.

Ideally, we actually re-use the existing parsed transform from Props, that could be something we revisit after Props 2.0.

As a follow-up, we should consider also moving the matrix decomposition logic from MatrixMathHelper here, and make that the only information we send back to Java.

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D78298588

fbshipit-source-id: a698ac8587ccfb2be04665747082398ccdde9294

* Add TransformHelper.cpp to `reactnativejni_common` (#52640)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52640

Not having `TransformHelper.cpp` included in CMake is causing the C++ code to fail compiling.
This diff fixes it.

Changelog:
[Internal] [Changed] -

Reviewed By: cipolleschi, javache

Differential Revision: D78414015

fbshipit-source-id: 4900427a86eb38bfec10e5e385296d89c73e9051

* [LOCAL] Unbreak compilation due to CMake dependencies

---------

Co-authored-by: Pieter De Baets <pieterdb@meta.com>
This commit is contained in:
Nicola Corti
2025-09-09 15:25:02 +02:00
committed by GitHub
co-authored by Pieter De Baets
parent ed92bd67f4
commit a346096da8
33 changed files with 412 additions and 33 deletions
@@ -8,6 +8,7 @@
package com.facebook.react.bridge
import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import java.util.ArrayList
import java.util.Arrays
import kotlin.jvm.JvmStatic
@@ -65,9 +66,16 @@ public open class ReadableNativeArray protected constructor() : NativeArray(), R
if (other !is ReadableNativeArray) {
return false
}
return localArray.contentDeepEquals(other.localArray)
return if (ReactNativeFeatureFlags.useNativeEqualsInNativeReadableArrayAndroid()) {
nativeEquals(other)
} else {
localArray.contentDeepEquals(other.localArray)
}
}
private external fun nativeEquals(other: ReadableNativeArray): Boolean
override fun toArrayList(): ArrayList<Any?> {
val arrayList = ArrayList<Any?>()
repeat(size()) { i ->
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<3e4d74a17c15742d35db9e4247f3e1c1>>
* @generated SignedSource<<c52f3977ea07f976e36177f13c1ec684>>
*/
/**
@@ -342,6 +342,18 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun useFabricInterop(): Boolean = accessor.useFabricInterop()
/**
* Use a native implementation of equals in NativeReadableArray.
*/
@JvmStatic
public fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean = accessor.useNativeEqualsInNativeReadableArrayAndroid()
/**
* Use a native implementation of TransformHelper
*/
@JvmStatic
public fun useNativeTransformHelperAndroid(): Boolean = accessor.useNativeTransformHelperAndroid()
/**
* When enabled, the native view configs are used in bridgeless mode.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<e7c1c6d184681d98320aac2a23c06288>>
* @generated SignedSource<<8e0125e82b359e6a175ffc49a4df5537>>
*/
/**
@@ -72,6 +72,8 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
private var useFabricInteropCache: Boolean? = null
private var useNativeEqualsInNativeReadableArrayAndroidCache: Boolean? = null
private var useNativeTransformHelperAndroidCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
private var useRawPropsJsiValueCache: Boolean? = null
@@ -548,6 +550,24 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean {
var cached = useNativeEqualsInNativeReadableArrayAndroidCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.useNativeEqualsInNativeReadableArrayAndroid()
useNativeEqualsInNativeReadableArrayAndroidCache = cached
}
return cached
}
override fun useNativeTransformHelperAndroid(): Boolean {
var cached = useNativeTransformHelperAndroidCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.useNativeTransformHelperAndroid()
useNativeTransformHelperAndroidCache = cached
}
return cached
}
override fun useNativeViewConfigsInBridgelessMode(): Boolean {
var cached = useNativeViewConfigsInBridgelessModeCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<ba62d616188ed439c85c66cfd055810d>>
* @generated SignedSource<<a5d9d11cc2a6529641243dc47a61f201>>
*/
/**
@@ -132,6 +132,10 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun useFabricInterop(): Boolean
@DoNotStrip @JvmStatic public external fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun useNativeTransformHelperAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun useNativeViewConfigsInBridgelessMode(): Boolean
@DoNotStrip @JvmStatic public external fun useOptimizedEventBatchingOnAndroid(): Boolean
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<12c2727291b635ef7c3163d153669c2c>>
* @generated SignedSource<<10d708ce4449eede46d750a1ed48d02e>>
*/
/**
@@ -127,6 +127,10 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun useFabricInterop(): Boolean = true
override fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean = false
override fun useNativeTransformHelperAndroid(): Boolean = false
override fun useNativeViewConfigsInBridgelessMode(): Boolean = false
override fun useOptimizedEventBatchingOnAndroid(): Boolean = false
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<3ea9946ef21c8ac8bb9bb63712636e89>>
* @generated SignedSource<<b04948c792c5db63decf1df80d3a867e>>
*/
/**
@@ -76,6 +76,8 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
private var useFabricInteropCache: Boolean? = null
private var useNativeEqualsInNativeReadableArrayAndroidCache: Boolean? = null
private var useNativeTransformHelperAndroidCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
private var useRawPropsJsiValueCache: Boolean? = null
@@ -604,6 +606,26 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean {
var cached = useNativeEqualsInNativeReadableArrayAndroidCache
if (cached == null) {
cached = currentProvider.useNativeEqualsInNativeReadableArrayAndroid()
accessedFeatureFlags.add("useNativeEqualsInNativeReadableArrayAndroid")
useNativeEqualsInNativeReadableArrayAndroidCache = cached
}
return cached
}
override fun useNativeTransformHelperAndroid(): Boolean {
var cached = useNativeTransformHelperAndroidCache
if (cached == null) {
cached = currentProvider.useNativeTransformHelperAndroid()
accessedFeatureFlags.add("useNativeTransformHelperAndroid")
useNativeTransformHelperAndroidCache = cached
}
return cached
}
override fun useNativeViewConfigsInBridgelessMode(): Boolean {
var cached = useNativeViewConfigsInBridgelessModeCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<58da46268043f086730132430735b720>>
* @generated SignedSource<<0bafb0a2fb79c4220d21f1736894af14>>
*/
/**
@@ -24,4 +24,8 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android :
// but that is more expensive than just duplicating the defaults here.
override fun preventShadowTreeCommitExhaustion(): Boolean = true
override fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean = true
override fun useNativeTransformHelperAndroid(): Boolean = true
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<1e81de36735c6c9286b228c75c9a0228>>
* @generated SignedSource<<21704207ce520def05b05f89dfba1048>>
*/
/**
@@ -127,6 +127,10 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun useFabricInterop(): Boolean
@DoNotStrip public fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean
@DoNotStrip public fun useNativeTransformHelperAndroid(): Boolean
@DoNotStrip public fun useNativeViewConfigsInBridgelessMode(): Boolean
@DoNotStrip public fun useOptimizedEventBatchingOnAndroid(): Boolean
@@ -8,10 +8,12 @@
package com.facebook.react.uimanager
import com.facebook.common.logging.FLog
import com.facebook.react.bridge.NativeArray
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.ReadableType
import com.facebook.react.common.ReactConstants
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
public object TransformHelper {
@@ -69,6 +71,14 @@ public object TransformHelper {
transformOrigin: ReadableArray?,
allowPercentageResolution: Boolean
) {
if (allowPercentageResolution &&
ReactNativeFeatureFlags.useNativeTransformHelperAndroid() &&
transforms is NativeArray &&
transformOrigin is NativeArray?) {
nativeProcessTransform(transforms, result, viewWidth, viewHeight, transformOrigin)
return
}
val helperMatrix = helperMatrix.get()!!
MatrixMathHelper.resetIdentityMatrix(result)
val offsets =
@@ -220,4 +230,13 @@ public object TransformHelper {
return doubleArrayOf(newTranslateX, newTranslateY, newTranslateZ)
}
@JvmStatic
private external fun nativeProcessTransform(
transforms: NativeArray,
result: DoubleArray,
viewWidth: Float,
viewHeight: Float,
transformOrigin: NativeArray?
)
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<cf7b6ff66c614ca2acc6667a80c5590d>>
* @generated SignedSource<<bbad4ee8cacd33099874d0c3078ea716>>
*/
/**
@@ -351,6 +351,18 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool useNativeEqualsInNativeReadableArrayAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useNativeEqualsInNativeReadableArrayAndroid");
return method(javaProvider_);
}
bool useNativeTransformHelperAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useNativeTransformHelperAndroid");
return method(javaProvider_);
}
bool useNativeViewConfigsInBridgelessMode() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useNativeViewConfigsInBridgelessMode");
@@ -657,6 +669,16 @@ bool JReactNativeFeatureFlagsCxxInterop::useFabricInterop(
return ReactNativeFeatureFlags::useFabricInterop();
}
bool JReactNativeFeatureFlagsCxxInterop::useNativeEqualsInNativeReadableArrayAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useNativeEqualsInNativeReadableArrayAndroid();
}
bool JReactNativeFeatureFlagsCxxInterop::useNativeTransformHelperAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useNativeTransformHelperAndroid();
}
bool JReactNativeFeatureFlagsCxxInterop::useNativeViewConfigsInBridgelessMode(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode();
@@ -879,6 +901,12 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"useFabricInterop",
JReactNativeFeatureFlagsCxxInterop::useFabricInterop),
makeNativeMethod(
"useNativeEqualsInNativeReadableArrayAndroid",
JReactNativeFeatureFlagsCxxInterop::useNativeEqualsInNativeReadableArrayAndroid),
makeNativeMethod(
"useNativeTransformHelperAndroid",
JReactNativeFeatureFlagsCxxInterop::useNativeTransformHelperAndroid),
makeNativeMethod(
"useNativeViewConfigsInBridgelessMode",
JReactNativeFeatureFlagsCxxInterop::useNativeViewConfigsInBridgelessMode),
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<dae981c66bf0751fd2863937ecf255d8>>
* @generated SignedSource<<57f2dcf4b71512c6b15e8021258d6036>>
*/
/**
@@ -186,6 +186,12 @@ class JReactNativeFeatureFlagsCxxInterop
static bool useFabricInterop(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool useNativeEqualsInNativeReadableArrayAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool useNativeTransformHelperAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool useNativeViewConfigsInBridgelessMode(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -31,13 +31,14 @@ add_library(
OnLoad-common.cpp
ReadableNativeArray.cpp
ReadableNativeMap.cpp
TransformHelper.cpp
WritableNativeArray.cpp
WritableNativeMap.cpp
)
target_merge_so(reactnativejni_common)
target_include_directories(reactnativejni_common PUBLIC ../../)
target_link_libraries(reactnativejni_common fbjni folly_runtime react_cxxreact)
target_link_libraries(reactnativejni_common fbjni folly_runtime react_cxxreact yoga react_renderer_graphics)
target_compile_reactnative_options(reactnativejni_common PRIVATE)
target_compile_options(reactnativejni_common PRIVATE -Wno-unused-lambda-capture)
@@ -21,6 +21,10 @@ class NativeArray : public jni::HybridClass<NativeArray> {
jni::local_ref<jstring> toString();
const folly::dynamic& getArray() const {
return array_;
}
RN_EXPORT folly::dynamic consume();
// Whether this array has been added to another array or map and no longer
@@ -11,6 +11,7 @@
#include "JReactMarker.h"
#include "NativeArray.h"
#include "NativeMap.h"
#include "TransformHelper.h"
#include "WritableNativeArray.h"
#include "WritableNativeMap.h"
@@ -27,6 +28,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
ReadableNativeMap::registerNatives();
WritableNativeArray::registerNatives();
WritableNativeMap::registerNatives();
TransformHelper::registerNatives();
});
}
@@ -40,10 +40,16 @@ local_ref<JArrayClass<jobject>> ReadableNativeArray::importTypeArray() {
return jarray;
}
bool ReadableNativeArray::equals(
jni::alias_ref<ReadableNativeArray::javaobject> other) {
return array_ == other->cthis()->array_;
}
void ReadableNativeArray::registerNatives() {
registerHybrid({
makeNativeMethod("importArray", ReadableNativeArray::importArray),
makeNativeMethod("importTypeArray", ReadableNativeArray::importTypeArray),
makeNativeMethod("nativeEquals", ReadableNativeArray::equals),
});
}
@@ -35,6 +35,7 @@ class ReadableNativeArray
static void mapException(std::exception_ptr ex);
static void registerNatives();
bool equals(jni::alias_ref<ReadableNativeArray::javaobject> other);
jni::local_ref<jni::JArrayClass<jobject>> importArray();
jni::local_ref<jni::JArrayClass<jobject>> importTypeArray();
};
@@ -0,0 +1,60 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "TransformHelper.h"
#include <react/renderer/components/view/BaseViewProps.h>
#include <react/renderer/components/view/conversions.h>
#include "NativeArray.h"
using namespace facebook::jni;
namespace facebook::react {
namespace {
void processTransform(
jni::alias_ref<jclass> /*unused*/,
NativeArray* jTransforms,
jni::alias_ref<jni::JArrayDouble> jResult,
float viewWidth,
float viewHeight,
NativeArray* jTransformOrigin) {
// Assuming parsing transforms doesn't require a real PropsParserContext
static ContextContainer contextContainer;
static PropsParserContext context(0, contextContainer);
RawValue transformValue(jTransforms->getArray());
Transform transform;
fromRawValue(context, transformValue, transform);
TransformOrigin transformOrigin;
if (jTransformOrigin != nullptr) {
RawValue transformOriginValue(jTransformOrigin->getArray());
fromRawValue(context, transformOriginValue, transformOrigin);
}
auto result = BaseViewProps::resolveTransform(
Size(viewWidth, viewHeight), transform, transformOrigin);
// Convert from matrix of floats to double matrix
constexpr size_t MatrixSize = std::tuple_size_v<decltype(result.matrix)>;
std::array<double, MatrixSize> doubleTransform{};
std::copy(
result.matrix.begin(), result.matrix.end(), doubleTransform.begin());
jResult->setRegion(0, MatrixSize, doubleTransform.data());
}
} // namespace
void TransformHelper::registerNatives() {
javaClassLocal()->registerNatives({
makeNativeMethod("nativeProcessTransform", processTransform),
});
}
} // namespace facebook::react
@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <fbjni/fbjni.h>
namespace facebook::react {
class TransformHelper : public jni::JavaClass<TransformHelper> {
public:
static auto constexpr* kJavaDescriptor =
"Lcom/facebook/react/uimanager/TransformHelper;";
static void registerNatives();
};
} // namespace facebook::react
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<f73bbcd926a835c09b70d814c6662dbb>>
* @generated SignedSource<<2cabd888b74b84201ff027457efc6007>>
*/
/**
@@ -234,6 +234,14 @@ bool ReactNativeFeatureFlags::useFabricInterop() {
return getAccessor().useFabricInterop();
}
bool ReactNativeFeatureFlags::useNativeEqualsInNativeReadableArrayAndroid() {
return getAccessor().useNativeEqualsInNativeReadableArrayAndroid();
}
bool ReactNativeFeatureFlags::useNativeTransformHelperAndroid() {
return getAccessor().useNativeTransformHelperAndroid();
}
bool ReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode() {
return getAccessor().useNativeViewConfigsInBridgelessMode();
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<da14545268455bfd4cd35e5c2ecf81ee>>
* @generated SignedSource<<e26a0c35f1499abf24e46275bbcbe06d>>
*/
/**
@@ -299,6 +299,16 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool useFabricInterop();
/**
* Use a native implementation of equals in NativeReadableArray.
*/
RN_EXPORT static bool useNativeEqualsInNativeReadableArrayAndroid();
/**
* Use a native implementation of TransformHelper
*/
RN_EXPORT static bool useNativeTransformHelperAndroid();
/**
* When enabled, the native view configs are used in bridgeless mode.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<681bff71eb87886a108f67b3162b030c>>
* @generated SignedSource<<7ae9a203a94e3a22197bc9eda69b741c>>
*/
/**
@@ -965,6 +965,42 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::useNativeEqualsInNativeReadableArrayAndroid() {
auto flagValue = useNativeEqualsInNativeReadableArrayAndroid_.load();
if (!flagValue.has_value()) {
// This block is not exclusive but it is not necessary.
// If multiple threads try to initialize the feature flag, we would only
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(52, "useNativeEqualsInNativeReadableArrayAndroid");
flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid();
useNativeEqualsInNativeReadableArrayAndroid_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::useNativeTransformHelperAndroid() {
auto flagValue = useNativeTransformHelperAndroid_.load();
if (!flagValue.has_value()) {
// This block is not exclusive but it is not necessary.
// If multiple threads try to initialize the feature flag, we would only
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(53, "useNativeTransformHelperAndroid");
flagValue = currentProvider_->useNativeTransformHelperAndroid();
useNativeTransformHelperAndroid_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() {
auto flagValue = useNativeViewConfigsInBridgelessMode_.load();
@@ -974,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(52, "useNativeViewConfigsInBridgelessMode");
markFlagAsAccessed(54, "useNativeViewConfigsInBridgelessMode");
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
useNativeViewConfigsInBridgelessMode_ = flagValue;
@@ -992,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(53, "useOptimizedEventBatchingOnAndroid");
markFlagAsAccessed(55, "useOptimizedEventBatchingOnAndroid");
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
useOptimizedEventBatchingOnAndroid_ = flagValue;
@@ -1010,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(54, "useRawPropsJsiValue");
markFlagAsAccessed(56, "useRawPropsJsiValue");
flagValue = currentProvider_->useRawPropsJsiValue();
useRawPropsJsiValue_ = flagValue;
@@ -1028,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(55, "useShadowNodeStateOnClone");
markFlagAsAccessed(57, "useShadowNodeStateOnClone");
flagValue = currentProvider_->useShadowNodeStateOnClone();
useShadowNodeStateOnClone_ = flagValue;
@@ -1046,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(56, "useTurboModuleInterop");
markFlagAsAccessed(58, "useTurboModuleInterop");
flagValue = currentProvider_->useTurboModuleInterop();
useTurboModuleInterop_ = flagValue;
@@ -1064,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(57, "useTurboModules");
markFlagAsAccessed(59, "useTurboModules");
flagValue = currentProvider_->useTurboModules();
useTurboModules_ = flagValue;
@@ -1082,7 +1118,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(58, "virtualViewPrerenderRatio");
markFlagAsAccessed(60, "virtualViewPrerenderRatio");
flagValue = currentProvider_->virtualViewPrerenderRatio();
virtualViewPrerenderRatio_ = flagValue;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<e5a8a196b35c010d92d3f616979891a9>>
* @generated SignedSource<<74560113d0b23c05d7822eeba1c0dee4>>
*/
/**
@@ -84,6 +84,8 @@ class ReactNativeFeatureFlagsAccessor {
bool updateRuntimeShadowNodeReferencesOnCommit();
bool useAlwaysAvailableJSErrorHandling();
bool useFabricInterop();
bool useNativeEqualsInNativeReadableArrayAndroid();
bool useNativeTransformHelperAndroid();
bool useNativeViewConfigsInBridgelessMode();
bool useOptimizedEventBatchingOnAndroid();
bool useRawPropsJsiValue();
@@ -102,7 +104,7 @@ class ReactNativeFeatureFlagsAccessor {
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
bool wasOverridden_;
std::array<std::atomic<const char*>, 59> accessedFeatureFlags_;
std::array<std::atomic<const char*>, 61> accessedFeatureFlags_;
std::atomic<std::optional<bool>> commonTestFlag_;
std::atomic<std::optional<bool>> animatedShouldSignalBatch_;
@@ -156,6 +158,8 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> updateRuntimeShadowNodeReferencesOnCommit_;
std::atomic<std::optional<bool>> useAlwaysAvailableJSErrorHandling_;
std::atomic<std::optional<bool>> useFabricInterop_;
std::atomic<std::optional<bool>> useNativeEqualsInNativeReadableArrayAndroid_;
std::atomic<std::optional<bool>> useNativeTransformHelperAndroid_;
std::atomic<std::optional<bool>> useNativeViewConfigsInBridgelessMode_;
std::atomic<std::optional<bool>> useOptimizedEventBatchingOnAndroid_;
std::atomic<std::optional<bool>> useRawPropsJsiValue_;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<9832c18e4c7ccf232b7222e2356f99d9>>
* @generated SignedSource<<1a54000b8eb51cb91304902c7f722d45>>
*/
/**
@@ -235,6 +235,14 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return true;
}
bool useNativeEqualsInNativeReadableArrayAndroid() override {
return false;
}
bool useNativeTransformHelperAndroid() override {
return false;
}
bool useNativeViewConfigsInBridgelessMode() override {
return false;
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<19f0a48bcfa8f8ffaf634e85301adc7e>>
* @generated SignedSource<<244db790cc754f31402981967cd902b5>>
*/
/**
@@ -513,6 +513,24 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::useFabricInterop();
}
bool useNativeEqualsInNativeReadableArrayAndroid() override {
auto value = values_["useNativeEqualsInNativeReadableArrayAndroid"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::useNativeEqualsInNativeReadableArrayAndroid();
}
bool useNativeTransformHelperAndroid() override {
auto value = values_["useNativeTransformHelperAndroid"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::useNativeTransformHelperAndroid();
}
bool useNativeViewConfigsInBridgelessMode() override {
auto value = values_["useNativeViewConfigsInBridgelessMode"];
if (!value.isNull()) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<f33ea0f19a27ec6124d8dbf1a043b4ce>>
* @generated SignedSource<<a045579d42e45fa80831856734a063aa>>
*/
/**
@@ -30,6 +30,14 @@ class ReactNativeFeatureFlagsOverridesOSSExperimental : public ReactNativeFeatur
bool preventShadowTreeCommitExhaustion() override {
return true;
}
bool useNativeEqualsInNativeReadableArrayAndroid() override {
return true;
}
bool useNativeTransformHelperAndroid() override {
return true;
}
};
} // namespace facebook::react
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<815769cc8d08e19b2598dd1862ed5060>>
* @generated SignedSource<<2387ed12fe46fb5b606ad13a17511f03>>
*/
/**
@@ -77,6 +77,8 @@ class ReactNativeFeatureFlagsProvider {
virtual bool updateRuntimeShadowNodeReferencesOnCommit() = 0;
virtual bool useAlwaysAvailableJSErrorHandling() = 0;
virtual bool useFabricInterop() = 0;
virtual bool useNativeEqualsInNativeReadableArrayAndroid() = 0;
virtual bool useNativeTransformHelperAndroid() = 0;
virtual bool useNativeViewConfigsInBridgelessMode() = 0;
virtual bool useOptimizedEventBatchingOnAndroid() = 0;
virtual bool useRawPropsJsiValue() = 0;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<474a64af739969acebb4bb9bb1005168>>
* @generated SignedSource<<45e1b08fd2438b27af82591b5cfa5744>>
*/
/**
@@ -304,6 +304,16 @@ bool NativeReactNativeFeatureFlags::useFabricInterop(
return ReactNativeFeatureFlags::useFabricInterop();
}
bool NativeReactNativeFeatureFlags::useNativeEqualsInNativeReadableArrayAndroid(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::useNativeEqualsInNativeReadableArrayAndroid();
}
bool NativeReactNativeFeatureFlags::useNativeTransformHelperAndroid(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::useNativeTransformHelperAndroid();
}
bool NativeReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode();
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<bfaa3cc7ab3eeff306337b06b7ae978a>>
* @generated SignedSource<<d1862a9ce3cfeda5d140941fcd3b25bc>>
*/
/**
@@ -140,6 +140,10 @@ class NativeReactNativeFeatureFlags
bool useFabricInterop(jsi::Runtime& runtime);
bool useNativeEqualsInNativeReadableArrayAndroid(jsi::Runtime& runtime);
bool useNativeTransformHelperAndroid(jsi::Runtime& runtime);
bool useNativeViewConfigsInBridgelessMode(jsi::Runtime& runtime);
bool useOptimizedEventBatchingOnAndroid(jsi::Runtime& runtime);
@@ -550,6 +550,13 @@ BorderMetrics BaseViewProps::resolveBorderMetrics(
Transform BaseViewProps::resolveTransform(
const LayoutMetrics& layoutMetrics) const {
const auto& frameSize = layoutMetrics.frame.size;
return resolveTransform(frameSize, transform, transformOrigin);
}
Transform BaseViewProps::resolveTransform(
const Size& frameSize,
const Transform& transform,
const TransformOrigin& transformOrigin) {
auto transformMatrix = Transform{};
if (frameSize.width == 0 && frameSize.height == 0) {
return transformMatrix;
@@ -562,8 +569,7 @@ Transform BaseViewProps::resolveTransform(
} else {
for (const auto& operation : transform.operations) {
transformMatrix = transformMatrix *
Transform::FromTransformOperation(
operation, layoutMetrics.frame.size, transform);
Transform::FromTransformOperation(operation, frameSize, transform);
}
}
@@ -115,6 +115,11 @@ class BaseViewProps : public YogaStylableProps, public AccessibilityProps {
Transform resolveTransform(const LayoutMetrics& layoutMetrics) const;
bool getClipsContentToBounds() const;
static Transform resolveTransform(
const Size& frameSize,
const Transform& transform,
const TransformOrigin& transformOrigin);
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps() const override;
#endif
@@ -595,6 +595,27 @@ const definitions: FeatureFlagDefinitions = {
},
ossReleaseStage: 'none',
},
useNativeEqualsInNativeReadableArrayAndroid: {
defaultValue: false,
metadata: {
dateAdded: '2025-07-15',
description:
'Use a native implementation of equals in NativeReadableArray.',
expectedReleaseValue: true,
purpose: 'experimentation',
},
ossReleaseStage: 'experimental',
},
useNativeTransformHelperAndroid: {
defaultValue: false,
metadata: {
dateAdded: '2025-07-15',
description: 'Use a native implementation of TransformHelper',
expectedReleaseValue: true,
purpose: 'experimentation',
},
ossReleaseStage: 'experimental',
},
useNativeViewConfigsInBridgelessMode: {
defaultValue: false,
metadata: {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<999a8d329cdab258ac64c03b24f1a516>>
* @generated SignedSource<<f17b8e4e33228e19b346837e7a33a2dd>>
* @flow strict
* @noformat
*/
@@ -103,6 +103,8 @@ export type ReactNativeFeatureFlags = $ReadOnly<{
updateRuntimeShadowNodeReferencesOnCommit: Getter<boolean>,
useAlwaysAvailableJSErrorHandling: Getter<boolean>,
useFabricInterop: Getter<boolean>,
useNativeEqualsInNativeReadableArrayAndroid: Getter<boolean>,
useNativeTransformHelperAndroid: Getter<boolean>,
useNativeViewConfigsInBridgelessMode: Getter<boolean>,
useOptimizedEventBatchingOnAndroid: Getter<boolean>,
useRawPropsJsiValue: Getter<boolean>,
@@ -404,6 +406,14 @@ export const useAlwaysAvailableJSErrorHandling: Getter<boolean> = createNativeFl
* Should this application enable the Fabric Interop Layer for Android? If yes, the application will behave so that it can accept non-Fabric components and render them on Fabric. This toggle is controlling extra logic such as custom event dispatching that are needed for the Fabric Interop Layer to work correctly.
*/
export const useFabricInterop: Getter<boolean> = createNativeFlagGetter('useFabricInterop', true);
/**
* Use a native implementation of equals in NativeReadableArray.
*/
export const useNativeEqualsInNativeReadableArrayAndroid: Getter<boolean> = createNativeFlagGetter('useNativeEqualsInNativeReadableArrayAndroid', false);
/**
* Use a native implementation of TransformHelper
*/
export const useNativeTransformHelperAndroid: Getter<boolean> = createNativeFlagGetter('useNativeTransformHelperAndroid', false);
/**
* When enabled, the native view configs are used in bridgeless mode.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<1b84b6e04c214f6c2798010372937990>>
* @generated SignedSource<<8b4f1275a16d5b83f5594da1eb89c6c1>>
* @flow strict
* @noformat
*/
@@ -77,6 +77,8 @@ export interface Spec extends TurboModule {
+updateRuntimeShadowNodeReferencesOnCommit?: () => boolean;
+useAlwaysAvailableJSErrorHandling?: () => boolean;
+useFabricInterop?: () => boolean;
+useNativeEqualsInNativeReadableArrayAndroid?: () => boolean;
+useNativeTransformHelperAndroid?: () => boolean;
+useNativeViewConfigsInBridgelessMode?: () => boolean;
+useOptimizedEventBatchingOnAndroid?: () => boolean;
+useRawPropsJsiValue?: () => boolean;