diff --git a/.flowconfig b/.flowconfig index 58ca3c06ee61..4d01ae0c0e40 100644 --- a/.flowconfig +++ b/.flowconfig @@ -64,11 +64,6 @@ module.system.haste.module_ref_prefix=m# react.runtime=automatic -experimental.deprecated_utilities.excludes=/packages/react-native/Libraries/Renderer/shims/ReactNativeTypes.js -experimental.deprecated_utilities.excludes=/packages/react-native/Libraries/Renderer/shims/ReactNativeViewConfigRegistry.js -experimental.deprecated_colon_extends.excludes=/packages/react-native/Libraries/Renderer/shims/ReactNativeTypes.js -experimental.deprecated_variance_sigils.excludes=/packages/react-native/Libraries/Renderer/shims/ReactNativeTypes.js - ban_spread_key_props=true [lints] diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js b/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js index a38c30aa751b..cb3b241f8089 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js @@ -506,6 +506,13 @@ export type TextInputAndroidProps = Readonly<{ */ textBreakStrategy?: ?('simple' | 'highQuality' | 'balanced'), + /** + * Align the input text to the top, center, or bottom of the field. + * Defaults to `'auto'`. + * @platform android + */ + textAlignVertical?: ?('auto' | 'top' | 'bottom' | 'center'), + /** * The color of the `TextInput` underline. * @platform android diff --git a/packages/react-native/Libraries/Image/ImageBackground.js b/packages/react-native/Libraries/Image/ImageBackground.js index c8e0beae02df..15e2243d98f2 100644 --- a/packages/react-native/Libraries/Image/ImageBackground.js +++ b/packages/react-native/Libraries/Image/ImageBackground.js @@ -47,7 +47,11 @@ export type {ImageBackgroundProps} from './ImageProps'; * }); * ``` * + * ImageBackground is deprecated and will be removed in a future release. + * Use a `View` with an absolutely positioned `Image` instead. + * * @see https://reactnative.dev/docs/imagebackground + * @deprecated */ class ImageBackground extends React.Component { setNativeProps(props: {...}) { diff --git a/packages/react-native/Libraries/Image/ImageProps.js b/packages/react-native/Libraries/Image/ImageProps.js index a893ac450a60..631ba635bec4 100644 --- a/packages/react-native/Libraries/Image/ImageProps.js +++ b/packages/react-native/Libraries/Image/ImageProps.js @@ -338,7 +338,13 @@ export type ImageProps = Readonly<{ style?: ?ImageStyleProp, }>; -/** @build-types emit-as-interface Uniwind compatibility */ +/** + * ImageBackground is deprecated and will be removed in a future release. + * Use a `View` with an absolutely positioned `Image` instead. + * @see https://reactnative.dev/docs/imagebackground + * @deprecated + * @build-types emit-as-interface Uniwind compatibility + */ export type ImageBackgroundProps = Readonly<{ ...ImageProps, children?: React.Node, diff --git a/packages/react-native/Package.swift b/packages/react-native/Package.swift index d2c487b8970b..00309bef7e6a 100644 --- a/packages/react-native/Package.swift +++ b/packages/react-native/Package.swift @@ -262,13 +262,13 @@ let reactRuntimeScheduler = RNTarget( dependencies: [.reactNativeDependencies, .reactFeatureFlags, .reactCxxReact, .reactPerfLogger, .reactPerformanceTimeline, .reactRendererConsistency, .reactUtils, .reactRuntimeExecutor] ) -/// ReactCommon.podspec -/// This target represent the ReactCommon/turbomodule/bridging subspec +/// React-bridging.podspec let reactTurboModuleBridging = RNTarget( name: .reactTurboModuleBridging, path: "ReactCommon/react/bridging", + searchPaths: [CallInvokerPath], excludedPaths: ["tests"], - dependencies: [.reactNativeDependencies, .reactPerfLogger, .reactCxxReact, .jsi, .logger] + dependencies: [.reactNativeDependencies, .jsi] ) /// React-jserrorhandler.podspec @@ -904,7 +904,7 @@ extension String { static let reactRCTLinking = "React-RCTLinking" static let reactCoreModules = "React-CoreModules" static let reactRCTAnimatedModuleProvider = "RCTAnimatedModuleProvider" - static let reactTurboModuleBridging = "ReactCommon/turbomodule/bridging" + static let reactTurboModuleBridging = "React-bridging" static let reactTurboModuleCore = "ReactCommon/turbomodule/core" static let reactTurboModuleCoreDefaults = "ReactCommon/turbomodule/core/defaults" static let reactTurboModuleCoreMicrotasks = "ReactCommon/turbomodule/core/microtasks" diff --git a/packages/react-native/React/React-RCTFBReactNativeSpec.podspec b/packages/react-native/React/React-RCTFBReactNativeSpec.podspec index 1abb84e7e473..f9978b78aca4 100644 --- a/packages/react-native/React/React-RCTFBReactNativeSpec.podspec +++ b/packages/react-native/React/React-RCTFBReactNativeSpec.podspec @@ -54,7 +54,7 @@ Pod::Spec.new do |s| s.dependency "React-NativeModulesApple" add_dependency(s, "ReactCommon", :subspec => "turbomodule/core", :additional_framework_paths => ["react/nativemodule/core"]) - add_dependency(s, "ReactCommon", :subspec => "turbomodule/bridging", :additional_framework_paths => ["react/nativemodule/bridging"]) + s.dependency "React-bridging" depend_on_js_engine(s) add_rn_third_party_dependencies(s) diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 699c267339a7..57ed2ec2aa73 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -1665,7 +1665,9 @@ public abstract interface class com/facebook/react/common/mapbuffer/MapBuffer : public abstract fun getBoolean (I)Z public abstract fun getCount ()I public abstract fun getDouble (I)D + public abstract fun getDoubleBuffer (I)[D public abstract fun getInt (I)I + public abstract fun getIntBuffer (I)[I public abstract fun getKeyOffset (I)I public abstract fun getLong (I)J public abstract fun getMapBuffer (I)Lcom/facebook/react/common/mapbuffer/MapBuffer; @@ -1680,9 +1682,12 @@ public final class com/facebook/react/common/mapbuffer/MapBuffer$Companion { public final class com/facebook/react/common/mapbuffer/MapBuffer$DataType : java/lang/Enum { public static final field BOOL Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; public static final field DOUBLE Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; + public static final field DOUBLE_BUFFER Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; public static final field INT Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; + public static final field INT_BUFFER Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; public static final field LONG Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; public static final field MAP Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; + public static final field MAP_BUFFER_LIST Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; public static final field STRING Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; @@ -1691,10 +1696,13 @@ public final class com/facebook/react/common/mapbuffer/MapBuffer$DataType : java public abstract interface class com/facebook/react/common/mapbuffer/MapBuffer$Entry { public abstract fun getBooleanValue ()Z + public abstract fun getDoubleBufferValue ()[D public abstract fun getDoubleValue ()D + public abstract fun getIntBufferValue ()[I public abstract fun getIntValue ()I public abstract fun getKey ()I public abstract fun getLongValue ()J + public abstract fun getMapBufferListValue ()Ljava/util/List; public abstract fun getMapBufferValue ()Lcom/facebook/react/common/mapbuffer/MapBuffer; public abstract fun getStringValue ()Ljava/lang/String; public abstract fun getType ()Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; @@ -1708,7 +1716,9 @@ public final class com/facebook/react/common/mapbuffer/ReadableMapBuffer : com/f public fun getBoolean (I)Z public fun getCount ()I public fun getDouble (I)D + public fun getDoubleBuffer (I)[D public fun getInt (I)I + public fun getIntBuffer (I)[I public fun getKeyOffset (I)I public fun getLong (I)J public synthetic fun getMapBuffer (I)Lcom/facebook/react/common/mapbuffer/MapBuffer; @@ -1940,6 +1950,11 @@ public final class com/facebook/react/devsupport/DevSupportManagerBase$Companion public abstract interface class com/facebook/react/devsupport/DevSupportManagerFactory { public abstract fun create (Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZLcom/facebook/react/devsupport/interfaces/RedBoxHandler;Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;ILjava/util/Map;Lcom/facebook/react/common/SurfaceDelegateFactory;Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;Lcom/facebook/react/devsupport/interfaces/PausedInDebuggerOverlayManager;)Lcom/facebook/react/devsupport/interfaces/DevSupportManager; public abstract fun create (Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZLcom/facebook/react/devsupport/interfaces/RedBoxHandler;Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;ILjava/util/Map;Lcom/facebook/react/common/SurfaceDelegateFactory;Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;Lcom/facebook/react/devsupport/interfaces/PausedInDebuggerOverlayManager;Z)Lcom/facebook/react/devsupport/interfaces/DevSupportManager; + public static synthetic fun create$default (Lcom/facebook/react/devsupport/DevSupportManagerFactory;Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZLcom/facebook/react/devsupport/interfaces/RedBoxHandler;Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;ILjava/util/Map;Lcom/facebook/react/common/SurfaceDelegateFactory;Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;Lcom/facebook/react/devsupport/interfaces/PausedInDebuggerOverlayManager;ZILjava/lang/Object;)Lcom/facebook/react/devsupport/interfaces/DevSupportManager; +} + +public final class com/facebook/react/devsupport/DevSupportManagerFactory$DefaultImpls { + public static synthetic fun create$default (Lcom/facebook/react/devsupport/DevSupportManagerFactory;Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZLcom/facebook/react/devsupport/interfaces/RedBoxHandler;Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;ILjava/util/Map;Lcom/facebook/react/common/SurfaceDelegateFactory;Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;Lcom/facebook/react/devsupport/interfaces/PausedInDebuggerOverlayManager;ZILjava/lang/Object;)Lcom/facebook/react/devsupport/interfaces/DevSupportManager; } public final class com/facebook/react/devsupport/DoubleTapReloadRecognizer { @@ -3007,6 +3022,8 @@ public final class com/facebook/react/runtime/ReactHostImpl : com/facebook/react public fun (Landroid/content/Context;Lcom/facebook/react/runtime/ReactHostDelegate;Lcom/facebook/react/fabric/ComponentFactory;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;ZZLcom/facebook/react/devsupport/DevSupportManagerFactory;)V public synthetic fun (Landroid/content/Context;Lcom/facebook/react/runtime/ReactHostDelegate;Lcom/facebook/react/fabric/ComponentFactory;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;ZZLcom/facebook/react/devsupport/DevSupportManagerFactory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun (Landroid/content/Context;Lcom/facebook/react/runtime/ReactHostDelegate;Lcom/facebook/react/fabric/ComponentFactory;ZZ)V + public fun (Landroid/content/Context;Lcom/facebook/react/runtime/ReactHostDelegate;Lcom/facebook/react/fabric/ComponentFactory;ZZLcom/facebook/react/devsupport/DevSupportManagerFactory;)V + public synthetic fun (Landroid/content/Context;Lcom/facebook/react/runtime/ReactHostDelegate;Lcom/facebook/react/fabric/ComponentFactory;ZZLcom/facebook/react/devsupport/DevSupportManagerFactory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun addBeforeDestroyListener (Lkotlin/jvm/functions/Function0;)V public fun addReactInstanceEventListener (Lcom/facebook/react/ReactInstanceEventListener;)V public fun createSurface (Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Lcom/facebook/react/interfaces/fabric/ReactSurface; @@ -3303,6 +3320,11 @@ public abstract class com/facebook/react/uimanager/GuardedFrameCallback : androi protected abstract fun doFrameGuarded (J)V } +public abstract interface class com/facebook/react/uimanager/HasChildPressedStateDelay { + public abstract fun getHasChildPressedStateDelay ()Ljava/lang/Boolean; + public abstract fun setHasChildPressedStateDelay (Ljava/lang/Boolean;)V +} + public abstract interface class com/facebook/react/uimanager/IViewGroupManager : com/facebook/react/uimanager/IViewManagerWithChildren { public abstract fun addView (Landroid/view/View;Landroid/view/View;I)V public abstract fun getChildAt (Landroid/view/View;I)Landroid/view/View; @@ -5146,10 +5168,13 @@ public abstract interface class com/facebook/react/viewmanagers/VirtualViewManag public abstract fun setRenderState (Landroid/view/View;I)V } -public final class com/facebook/react/views/drawer/ReactDrawerLayout : androidx/drawerlayout/widget/DrawerLayout { +public final class com/facebook/react/views/drawer/ReactDrawerLayout : androidx/drawerlayout/widget/DrawerLayout, com/facebook/react/uimanager/HasChildPressedStateDelay { public fun (Lcom/facebook/react/bridge/ReactContext;)V + public fun getHasChildPressedStateDelay ()Ljava/lang/Boolean; public fun onInterceptTouchEvent (Landroid/view/MotionEvent;)Z public fun onTouchEvent (Landroid/view/MotionEvent;)Z + public fun setHasChildPressedStateDelay (Ljava/lang/Boolean;)V + public fun shouldDelayChildPressedState ()Z } public final class com/facebook/react/views/drawer/ReactDrawerLayoutManager : com/facebook/react/uimanager/ViewGroupManager, com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerInterface { @@ -5450,7 +5475,7 @@ public final class com/facebook/react/views/scroll/ReactHorizontalScrollContaine public final class com/facebook/react/views/scroll/ReactHorizontalScrollContainerViewManager$Companion { } -public class com/facebook/react/views/scroll/ReactHorizontalScrollView : android/widget/HorizontalScrollView, android/view/View$OnLayoutChangeListener, android/view/ViewGroup$OnHierarchyChangeListener, com/facebook/react/uimanager/ReactClippingViewGroup, com/facebook/react/uimanager/ReactOverflowViewWithInset, com/facebook/react/views/scroll/ReactAccessibleScrollView, com/facebook/react/views/scroll/ReactScrollViewHelper$HasFlingAnimator, com/facebook/react/views/scroll/ReactScrollViewHelper$HasScrollEventThrottle, com/facebook/react/views/scroll/ReactScrollViewHelper$HasScrollState, com/facebook/react/views/scroll/ReactScrollViewHelper$HasSmoothScroll, com/facebook/react/views/scroll/ReactScrollViewHelper$HasStateWrapper, com/facebook/react/views/scroll/VirtualViewContainer { +public class com/facebook/react/views/scroll/ReactHorizontalScrollView : android/widget/HorizontalScrollView, android/view/View$OnLayoutChangeListener, android/view/ViewGroup$OnHierarchyChangeListener, com/facebook/react/uimanager/HasChildPressedStateDelay, com/facebook/react/uimanager/ReactClippingViewGroup, com/facebook/react/uimanager/ReactOverflowViewWithInset, com/facebook/react/views/scroll/ReactAccessibleScrollView, com/facebook/react/views/scroll/ReactScrollViewHelper$HasFlingAnimator, com/facebook/react/views/scroll/ReactScrollViewHelper$HasScrollEventThrottle, com/facebook/react/views/scroll/ReactScrollViewHelper$HasScrollState, com/facebook/react/views/scroll/ReactScrollViewHelper$HasSmoothScroll, com/facebook/react/views/scroll/ReactScrollViewHelper$HasStateWrapper, com/facebook/react/views/scroll/VirtualViewContainer { public fun (Landroid/content/Context;)V public fun (Landroid/content/Context;Lcom/facebook/react/views/scroll/FpsListener;)V public synthetic fun (Landroid/content/Context;Lcom/facebook/react/views/scroll/FpsListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -5470,6 +5495,7 @@ public class com/facebook/react/views/scroll/ReactHorizontalScrollView : android public fun getFadingEdgeLengthStart ()I public fun getFlingAnimator ()Landroid/animation/ValueAnimator; public fun getFlingExtrapolatedDistance (I)I + public fun getHasChildPressedStateDelay ()Ljava/lang/Boolean; public fun getLastScrollDispatchTime ()J protected fun getLeftFadingEdgeStrength ()F public fun getOverflow ()Ljava/lang/String; @@ -5517,6 +5543,7 @@ public class com/facebook/react/views/scroll/ReactHorizontalScrollView : android public fun setEndFillColor (I)V public fun setFadingEdgeLengthEnd (I)V public fun setFadingEdgeLengthStart (I)V + public fun setHasChildPressedStateDelay (Ljava/lang/Boolean;)V public fun setLastScrollDispatchTime (J)V public fun setOverflow (Ljava/lang/String;)V public fun setOverflowInset (IIII)V @@ -5535,6 +5562,7 @@ public class com/facebook/react/views/scroll/ReactHorizontalScrollView : android public fun setSnapToEnd (Z)V public fun setSnapToStart (Z)V public fun setStateWrapper (Lcom/facebook/react/uimanager/StateWrapper;)V + public fun shouldDelayChildPressedState ()Z public fun startFlingAnimator (II)V public fun updateClippingRect ()V public fun updateClippingRect (Ljava/util/Set;)V @@ -5597,7 +5625,7 @@ public class com/facebook/react/views/scroll/ReactHorizontalScrollViewManager : public final class com/facebook/react/views/scroll/ReactHorizontalScrollViewManager$Companion { } -public class com/facebook/react/views/scroll/ReactScrollView : android/widget/ScrollView, android/view/View$OnLayoutChangeListener, android/view/ViewGroup$OnHierarchyChangeListener, com/facebook/react/uimanager/ReactClippingViewGroup, com/facebook/react/uimanager/ReactOverflowViewWithInset, com/facebook/react/views/scroll/ReactAccessibleScrollView, com/facebook/react/views/scroll/ReactScrollViewHelper$HasFlingAnimator, com/facebook/react/views/scroll/ReactScrollViewHelper$HasScrollEventThrottle, com/facebook/react/views/scroll/ReactScrollViewHelper$HasScrollState, com/facebook/react/views/scroll/ReactScrollViewHelper$HasSmoothScroll, com/facebook/react/views/scroll/ReactScrollViewHelper$HasStateWrapper, com/facebook/react/views/scroll/VirtualViewContainer { +public class com/facebook/react/views/scroll/ReactScrollView : android/widget/ScrollView, android/view/View$OnLayoutChangeListener, android/view/ViewGroup$OnHierarchyChangeListener, com/facebook/react/uimanager/HasChildPressedStateDelay, com/facebook/react/uimanager/ReactClippingViewGroup, com/facebook/react/uimanager/ReactOverflowViewWithInset, com/facebook/react/views/scroll/ReactAccessibleScrollView, com/facebook/react/views/scroll/ReactScrollViewHelper$HasFlingAnimator, com/facebook/react/views/scroll/ReactScrollViewHelper$HasScrollEventThrottle, com/facebook/react/views/scroll/ReactScrollViewHelper$HasScrollState, com/facebook/react/views/scroll/ReactScrollViewHelper$HasSmoothScroll, com/facebook/react/views/scroll/ReactScrollViewHelper$HasStateWrapper, com/facebook/react/views/scroll/VirtualViewContainer { public fun (Landroid/content/Context;)V public fun (Landroid/content/Context;Lcom/facebook/react/views/scroll/FpsListener;)V public synthetic fun (Landroid/content/Context;Lcom/facebook/react/views/scroll/FpsListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -5615,6 +5643,7 @@ public class com/facebook/react/views/scroll/ReactScrollView : android/widget/Sc public fun getFadingEdgeLengthStart ()I public fun getFlingAnimator ()Landroid/animation/ValueAnimator; public fun getFlingExtrapolatedDistance (I)I + public fun getHasChildPressedStateDelay ()Ljava/lang/Boolean; public fun getLastScrollDispatchTime ()J protected fun getOverScrollerFromParent ()Landroid/widget/OverScroller; public fun getOverflow ()Ljava/lang/String; @@ -5661,6 +5690,7 @@ public class com/facebook/react/views/scroll/ReactScrollView : android/widget/Sc public fun setEndFillColor (I)V public fun setFadingEdgeLengthEnd (I)V public fun setFadingEdgeLengthStart (I)V + public fun setHasChildPressedStateDelay (Ljava/lang/Boolean;)V public fun setLastScrollDispatchTime (J)V public fun setOverflow (Ljava/lang/String;)V public fun setOverflowInset (IIII)V @@ -5681,6 +5711,7 @@ public class com/facebook/react/views/scroll/ReactScrollView : android/widget/Sc public fun setSnapToEnd (Z)V public fun setSnapToStart (Z)V public fun setStateWrapper (Lcom/facebook/react/uimanager/StateWrapper;)V + public fun shouldDelayChildPressedState ()Z public fun startFlingAnimator (II)V public fun updateClippingRect ()V public fun updateClippingRect (Ljava/util/Set;)V @@ -5949,15 +5980,18 @@ public final class com/facebook/react/views/scroll/VirtualViewContainerState$Com public final fun create (Landroid/view/ViewGroup;)Lcom/facebook/react/views/scroll/VirtualViewContainerState; } -public class com/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout : androidx/swiperefreshlayout/widget/SwipeRefreshLayout { +public class com/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout : androidx/swiperefreshlayout/widget/SwipeRefreshLayout, com/facebook/react/uimanager/HasChildPressedStateDelay { public fun (Lcom/facebook/react/bridge/ReactContext;)V public fun canChildScrollUp ()Z + public fun getHasChildPressedStateDelay ()Ljava/lang/Boolean; public fun onInterceptTouchEvent (Landroid/view/MotionEvent;)Z public fun onLayout (ZIIII)V public fun onTouchEvent (Landroid/view/MotionEvent;)Z public fun requestDisallowInterceptTouchEvent (Z)V + public fun setHasChildPressedStateDelay (Ljava/lang/Boolean;)V public final fun setProgressViewOffset (F)V public fun setRefreshing (Z)V + public fun shouldDelayChildPressedState ()Z } public final class com/facebook/react/views/text/DefaultStyleValuesUtil { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBuffer.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBuffer.kt index 44af587bd9f1..feba0dc97d6a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBuffer.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBuffer.kt @@ -46,6 +46,9 @@ public interface MapBuffer : Iterable { STRING, MAP, LONG, + INT_BUFFER, + DOUBLE_BUFFER, + MAP_BUFFER_LIST, } /** @@ -151,8 +154,8 @@ public interface MapBuffer : Iterable { public fun getMapBuffer(key: Int): MapBuffer /** - * Provides parsed [List] value if the entry for given key exists with [DataType.MAP] - * type + * Provides parsed [List] value if the entry for given key exists with + * [DataType.MAP_BUFFER_LIST] type * * @param key key to lookup [List] value for * @return value associated with the requested key @@ -161,6 +164,30 @@ public interface MapBuffer : Iterable { */ public fun getMapBufferList(key: Int): List + /** + * Provides parsed [IntArray] value if the entry for given key exists with [DataType.INT_BUFFER] + * type. This is a compact representation of a homogeneous list of ints with no per-element + * key/type overhead. + * + * @param key key to lookup the [IntArray] value for + * @return value associated with the requested key + * @throws IllegalArgumentException if the key doesn't exist + * @throws IllegalStateException if the data type doesn't match + */ + public fun getIntBuffer(key: Int): IntArray + + /** + * Provides parsed [DoubleArray] value if the entry for given key exists with + * [DataType.DOUBLE_BUFFER] type. This is a compact representation of a homogeneous list of + * doubles with no per-element key/type overhead. + * + * @param key key to lookup the [DoubleArray] value for + * @return value associated with the requested key + * @throws IllegalArgumentException if the key doesn't exist + * @throws IllegalStateException if the data type doesn't match + */ + public fun getDoubleBuffer(key: Int): DoubleArray + /** Iterable entry representing parsed MapBuffer values */ public interface Entry { /** @@ -213,5 +240,26 @@ public interface MapBuffer : Iterable { * @throws IllegalStateException if the data type doesn't match [DataType.MAP] */ public val mapBufferValue: MapBuffer + + /** + * Entry value represented as [IntArray] + * + * @throws IllegalStateException if the data type doesn't match [DataType.INT_BUFFER] + */ + public val intBufferValue: IntArray + + /** + * Entry value represented as [DoubleArray] + * + * @throws IllegalStateException if the data type doesn't match [DataType.DOUBLE_BUFFER] + */ + public val doubleBufferValue: DoubleArray + + /** + * Entry value represented as [List] + * + * @throws IllegalStateException if the data type doesn't match [DataType.MAP_BUFFER_LIST] + */ + public val mapBufferListValue: List } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt index c73ccb6da1dc..de9adb5d7faa 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt @@ -51,13 +51,11 @@ private constructor( ReadableMapBuffer(buffer.duplicate().apply { position(offset) }, offset) private fun readHeader() { - // byte order - val storedAlignment = buffer.short - if (storedAlignment.toInt() != ALIGNMENT) { - buffer.order(ByteOrder.LITTLE_ENDIAN) - } - // count - count = readUnsignedShort(buffer.position()).toInt() + // The C++ writer always serializes in little-endian byte order. ByteBuffer + // defaults to big-endian and duplicate() resets the order, so set it + // explicitly on every instance, including nested clones. + buffer.order(ByteOrder.LITTLE_ENDIAN) + count = readUnsignedShort(offsetToMapBuffer).toInt() } /** @@ -122,26 +120,27 @@ private constructor( return readIntValue(bufferPosition) == 1 } + // Dynamic-data entries store [offset][byteLength] in the bucket's 8-byte + // value: getInt(bufferPosition) is the offset, getInt(bufferPosition + 4) is + // the byte length. The dynamic data section itself carries no length prefix. private fun readStringValue(bufferPosition: Int): String { val offset = offsetForDynamicData + buffer.getInt(bufferPosition) - val sizeOfString = buffer.getInt(offset) + val sizeOfString = buffer.getInt(bufferPosition + Int.SIZE_BYTES) val result = ByteArray(sizeOfString) - val stringOffset = offset + Int.SIZE_BYTES - buffer.position(stringOffset) - buffer[result, 0, sizeOfString] + buffer.position(offset) + buffer.get(result, 0, sizeOfString) return String(result) } private fun readMapBufferValue(position: Int): ReadableMapBuffer { val offset = offsetForDynamicData + buffer.getInt(position) - return cloneWithOffset(offset + Int.SIZE_BYTES) + return cloneWithOffset(offset) } private fun readMapBufferListValue(position: Int): List { val readMapBufferList = arrayListOf() - var offset = offsetForDynamicData + buffer.getInt(position) - val sizeMapBufferList = buffer.getInt(offset) - offset += Int.SIZE_BYTES + val offset = offsetForDynamicData + buffer.getInt(position) + val sizeMapBufferList = buffer.getInt(position + Int.SIZE_BYTES) var curLen = 0 while (curLen < sizeMapBufferList) { val sizeMapBuffer = buffer.getInt(offset + curLen) @@ -152,6 +151,24 @@ private constructor( return readMapBufferList } + private fun readIntBufferValue(bufferPosition: Int): IntArray { + val offset = offsetForDynamicData + buffer.getInt(bufferPosition) + val byteLength = buffer.getInt(bufferPosition + Int.SIZE_BYTES) + val count = byteLength / Int.SIZE_BYTES + val result = IntArray(count) + buffer.duplicate().order(buffer.order()).apply { position(offset) }.asIntBuffer().get(result) + return result + } + + private fun readDoubleBufferValue(bufferPosition: Int): DoubleArray { + val offset = offsetForDynamicData + buffer.getInt(bufferPosition) + val byteLength = buffer.getInt(bufferPosition + Int.SIZE_BYTES) + val count = byteLength / Double.SIZE_BYTES + val result = DoubleArray(count) + buffer.duplicate().order(buffer.order()).apply { position(offset) }.asDoubleBuffer().get(result) + return result + } + private fun getKeyOffsetForBucketIndex(bucketIndex: Int): Int { return offsetToMapBuffer + HEADER_SIZE + BUCKET_SIZE * bucketIndex } @@ -191,7 +208,13 @@ private constructor( readMapBufferValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.MAP)) override fun getMapBufferList(key: Int): List = - readMapBufferListValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.MAP)) + readMapBufferListValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.MAP_BUFFER_LIST)) + + override fun getIntBuffer(key: Int): IntArray = + readIntBufferValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.INT_BUFFER)) + + override fun getDoubleBuffer(key: Int): DoubleArray = + readDoubleBufferValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.DOUBLE_BUFFER)) override fun hashCode(): Int { buffer.rewind() @@ -229,6 +252,9 @@ private constructor( append('"') } MapBuffer.DataType.MAP -> append(entry.mapBufferValue.toString()) + MapBuffer.DataType.INT_BUFFER -> append(entry.intBufferValue.contentToString()) + MapBuffer.DataType.DOUBLE_BUFFER -> append(entry.doubleBufferValue.contentToString()) + MapBuffer.DataType.MAP_BUFFER_LIST -> append(entry.mapBufferListValue.toString()) } } } @@ -311,16 +337,31 @@ private constructor( assertType(MapBuffer.DataType.MAP) return readMapBufferValue(bucketOffset + VALUE_OFFSET) } + + override val intBufferValue: IntArray + get() { + assertType(MapBuffer.DataType.INT_BUFFER) + return readIntBufferValue(bucketOffset + VALUE_OFFSET) + } + + override val doubleBufferValue: DoubleArray + get() { + assertType(MapBuffer.DataType.DOUBLE_BUFFER) + return readDoubleBufferValue(bucketOffset + VALUE_OFFSET) + } + + override val mapBufferListValue: List + get() { + assertType(MapBuffer.DataType.MAP_BUFFER_LIST) + return readMapBufferListValue(bucketOffset + VALUE_OFFSET) + } } public companion object { - // Value used to verify if the data is serialized with LittleEndian order. - private const val ALIGNMENT = 0xFE - - // 8 bytes = 2 (alignment) + 2 (count) + 4 (size) - private const val HEADER_SIZE = 8 + // 2 bytes = 2 (count) + private const val HEADER_SIZE = 2 - // 10 bytes = 2 (key) + 2 (type) + 8 (value) + // 12 bytes = 2 (key) + 2 (type) + 8 (value) private const val BUCKET_SIZE = 12 // 2 bytes = 2 (key) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/WritableMapBuffer.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/WritableMapBuffer.kt index d75e8a39a8a2..dea593ca6e7b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/WritableMapBuffer.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/WritableMapBuffer.kt @@ -84,6 +84,24 @@ internal class WritableMapBuffer : MapBuffer { */ fun put(key: Int, value: MapBuffer): WritableMapBuffer = putInternal(key, value) + /** + * Adds an [IntArray] value for given key to the current MapBuffer. + * + * @param key entry key + * @param value entry value + * @throws IllegalArgumentException if key is out of [UShort] range + */ + fun put(key: Int, value: IntArray): WritableMapBuffer = putInternal(key, value) + + /** + * Adds a [DoubleArray] value for given key to the current MapBuffer. + * + * @param key entry key + * @param value entry value + * @throws IllegalArgumentException if key is out of [UShort] range + */ + fun put(key: Int, value: DoubleArray): WritableMapBuffer = putInternal(key, value) + private fun putInternal(key: Int, value: Any): WritableMapBuffer { require(key in KEY_RANGE) { "Only integers in [${UShort.MIN_VALUE};${UShort.MAX_VALUE}] range are allowed for keys." @@ -126,6 +144,10 @@ internal class WritableMapBuffer : MapBuffer { override fun getMapBufferList(key: Int): List = verifyValue(key, values.get(key)) + override fun getIntBuffer(key: Int): IntArray = verifyValue(key, values.get(key)) + + override fun getDoubleBuffer(key: Int): DoubleArray = verifyValue(key, values.get(key)) + /** Generalizes verification of the value types based on the requested type. */ private inline fun verifyValue(key: Int, value: Any?): T { require(value != null) { "Key not found: $key" } @@ -143,6 +165,8 @@ internal class WritableMapBuffer : MapBuffer { is Double -> DataType.DOUBLE is String -> DataType.STRING is MapBuffer -> DataType.MAP + is IntArray -> DataType.INT_BUFFER + is DoubleArray -> DataType.DOUBLE_BUFFER else -> throw IllegalStateException("Key $key has value of unknown type: ${value.javaClass}") } } @@ -176,6 +200,15 @@ internal class WritableMapBuffer : MapBuffer { override val mapBufferValue: MapBuffer get() = verifyValue(key, values.valueAt(index)) + + override val intBufferValue: IntArray + get() = verifyValue(key, values.valueAt(index)) + + override val doubleBufferValue: DoubleArray + get() = verifyValue(key, values.valueAt(index)) + + override val mapBufferListValue: List + get() = verifyValue(key, values.valueAt(index)) } /* diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.kt index 7210d90a4c87..405ae476deda 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.kt @@ -48,15 +48,15 @@ public interface DevSupportManagerFactory { public fun create( applicationContext: Context, reactInstanceManagerHelper: ReactInstanceDevHelper, - packagerPathForJSBundleName: String?, - enableOnCreate: Boolean, - redBoxHandler: RedBoxHandler?, - devBundleDownloadListener: DevBundleDownloadListener?, - minNumShakes: Int, - customPackagerCommandHandlers: Map?, - surfaceDelegateFactory: SurfaceDelegateFactory?, - devLoadingViewManager: DevLoadingViewManager?, - pausedInDebuggerOverlayManager: PausedInDebuggerOverlayManager?, - useDevSupport: Boolean, + packagerPathForJSBundleName: String? = null, + enableOnCreate: Boolean = true, + redBoxHandler: RedBoxHandler? = null, + devBundleDownloadListener: DevBundleDownloadListener? = null, + minNumShakes: Int = 2, + customPackagerCommandHandlers: Map? = null, + surfaceDelegateFactory: SurfaceDelegateFactory? = null, + devLoadingViewManager: DevLoadingViewManager? = null, + pausedInDebuggerOverlayManager: PausedInDebuggerOverlayManager? = null, + useDevSupport: Boolean = true, ): DevSupportManager } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt index 66fa21ae9df3..56ffb4092686 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt @@ -113,14 +113,6 @@ public class ReactHostImpl( applicationContext = context.applicationContext, reactInstanceManagerHelper = reactHostImplDevHelper, packagerPathForJSBundleName = reactHostDelegate.jsMainModulePath, - enableOnCreate = true, - redBoxHandler = null, - devBundleDownloadListener = null, - minNumShakes = 2, - customPackagerCommandHandlers = null, - surfaceDelegateFactory = null, - devLoadingViewManager = null, - pausedInDebuggerOverlayManager = null, useDevSupport = useDevSupport, ) .also { devSupportManager -> @@ -156,12 +148,14 @@ public class ReactHostImpl( @Volatile private var hostInvalidated = false + @JvmOverloads public constructor( context: Context, delegate: ReactHostDelegate, componentFactory: ComponentFactory, allowPackagerServerAccess: Boolean, useDevSupport: Boolean, + devSupportManagerFactory: DevSupportManagerFactory? = null, ) : this( context, delegate, @@ -170,6 +164,7 @@ public class ReactHostImpl( Task.UI_THREAD_EXECUTOR, allowPackagerServerAccess, useDevSupport, + devSupportManagerFactory, ) public override val lifecycleState: LifecycleState diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/HasChildPressedStateDelay.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/HasChildPressedStateDelay.kt new file mode 100644 index 000000000000..be102ecaebaa --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/HasChildPressedStateDelay.kt @@ -0,0 +1,29 @@ +/* + * 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. + */ + +package com.facebook.react.uimanager + +/** + * Implemented by scrollable container views that allow the delaying of their children's pressed + * state (see [android.view.ViewGroup.shouldDelayChildPressedState]) to be toggled externally, + * without changing the view's default behavior. + * + * This lets a module that holds a reference to such a container disable the delay (e.g. when it + * knows the gesture will not turn into a scroll, so children should show their pressed state + * immediately) and re-enable it afterwards. + */ +public interface HasChildPressedStateDelay { + /** + * Overrides whether this view delays its children's pressed state (see + * [android.view.ViewGroup.shouldDelayChildPressedState]). + * + * When `null` (the default), the view's framework default is used. Set to `true` or `false` to + * force the behavior, and back to `null` to restore the default. Lets a module holding a + * reference to the container toggle the delay without changing the default. + */ + public var hasChildPressedStateDelay: Boolean? +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.kt index 2c87d6405507..7523bf1fe930 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.kt @@ -20,6 +20,7 @@ import com.facebook.common.logging.FLog import com.facebook.react.R import com.facebook.react.bridge.ReactContext import com.facebook.react.common.ReactConstants +import com.facebook.react.uimanager.HasChildPressedStateDelay import com.facebook.react.uimanager.ReactAccessibilityDelegate.AccessibilityRole import com.facebook.react.uimanager.events.NativeGestureUtil.notifyNativeGestureEnded import com.facebook.react.uimanager.events.NativeGestureUtil.notifyNativeGestureStarted @@ -28,11 +29,14 @@ import com.facebook.react.uimanager.events.NativeGestureUtil.notifyNativeGesture * Wrapper view for [DrawerLayout]. It manages the properties that can be set on the drawer and * contains some ReactNative-specific functionality. */ -public class ReactDrawerLayout(reactContext: ReactContext) : DrawerLayout(reactContext) { +public class ReactDrawerLayout(reactContext: ReactContext) : + DrawerLayout(reactContext), HasChildPressedStateDelay { private var drawerPosition = Gravity.START private var drawerWidth = DEFAULT_DRAWER_WIDTH private var dragging = false + override var hasChildPressedStateDelay: Boolean? = null + init { ViewCompat.setAccessibilityDelegate( this, @@ -60,6 +64,9 @@ public class ReactDrawerLayout(reactContext: ReactContext) : DrawerLayout(reactC ) } + override fun shouldDelayChildPressedState(): Boolean = + hasChildPressedStateDelay ?: super.shouldDelayChildPressedState() + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { try { if (super.onInterceptTouchEvent(ev)) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.kt index f2b308dd88fd..1e455eb2ba5c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.kt @@ -36,6 +36,7 @@ import com.facebook.react.common.ReactConstants import com.facebook.react.common.build.ReactBuildConfig import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags import com.facebook.react.uimanager.BackgroundStyleApplicator +import com.facebook.react.uimanager.HasChildPressedStateDelay import com.facebook.react.uimanager.LengthPercentage import com.facebook.react.uimanager.LengthPercentageType import com.facebook.react.uimanager.MeasureSpecAssertions @@ -84,7 +85,8 @@ constructor(context: Context, private val fpsListener: FpsListener? = null) : HasFlingAnimator, HasScrollEventThrottle, HasSmoothScroll, - VirtualViewContainer { + VirtualViewContainer, + HasChildPressedStateDelay { private companion object { private val DEBUG_MODE = false && ReactBuildConfig.DEBUG @@ -197,6 +199,7 @@ constructor(context: Context, private val fpsListener: FpsListener? = null) : override var stateWrapper: StateWrapper? = null override var scrollEventThrottle: Int = 0 override var lastScrollDispatchTime: Long = 0L + override var hasChildPressedStateDelay: Boolean? = null override val virtualViewContainerState: VirtualViewContainerState get() = @@ -631,6 +634,9 @@ constructor(context: Context, private val fpsListener: FpsListener? = null) : } } + override fun shouldDelayChildPressedState(): Boolean = + hasChildPressedStateDelay ?: super.shouldDelayChildPressedState() + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { if (!scrollEnabled) return false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactNestedScrollView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactNestedScrollView.kt index c8adcdfd082a..0a592234d2b0 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactNestedScrollView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactNestedScrollView.kt @@ -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<<2b0cbc5249ac34ae7f030a9c0fffd1f3>> + * @generated SignedSource<> */ /** @@ -38,6 +38,7 @@ import com.facebook.react.bridge.ReadableMap import com.facebook.react.common.ReactConstants import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags import com.facebook.react.uimanager.BackgroundStyleApplicator +import com.facebook.react.uimanager.HasChildPressedStateDelay import com.facebook.react.uimanager.LengthPercentage import com.facebook.react.uimanager.LengthPercentageType import com.facebook.react.uimanager.MeasureSpecAssertions @@ -93,7 +94,8 @@ constructor(context: Context, private val fpsListener: FpsListener? = null) : HasFlingAnimator, HasScrollEventThrottle, HasSmoothScroll, - VirtualViewContainer { + VirtualViewContainer, + HasChildPressedStateDelay { private companion object { private var scrollerField: java.lang.reflect.Field? = null @@ -107,6 +109,7 @@ constructor(context: Context, private val fpsListener: FpsListener? = null) : override var stateWrapper: StateWrapper? = null override var scrollEventThrottle: Int = 0 override var lastScrollDispatchTime: Long = 0L + override var hasChildPressedStateDelay: Boolean? = null public open var pointerEvents: PointerEvents = PointerEvents.AUTO @@ -552,6 +555,9 @@ constructor(context: Context, private val fpsListener: FpsListener? = null) : } } + override fun shouldDelayChildPressedState(): Boolean = + hasChildPressedStateDelay ?: super.shouldDelayChildPressedState() + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { if (!scrollEnabled) return false if (!PointerEvents.canChildrenBeTouchTarget(pointerEvents)) return true diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.kt index bd32aba8da9e..691a57cbc33c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.kt @@ -30,6 +30,7 @@ import com.facebook.react.bridge.ReadableMap import com.facebook.react.common.ReactConstants import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags import com.facebook.react.uimanager.BackgroundStyleApplicator +import com.facebook.react.uimanager.HasChildPressedStateDelay import com.facebook.react.uimanager.LengthPercentage import com.facebook.react.uimanager.LengthPercentageType import com.facebook.react.uimanager.MeasureSpecAssertions @@ -85,7 +86,8 @@ constructor(context: Context, private val fpsListener: FpsListener? = null) : HasFlingAnimator, HasScrollEventThrottle, HasSmoothScroll, - VirtualViewContainer { + VirtualViewContainer, + HasChildPressedStateDelay { private companion object { private var scrollerField: java.lang.reflect.Field? = null @@ -99,6 +101,7 @@ constructor(context: Context, private val fpsListener: FpsListener? = null) : override var stateWrapper: StateWrapper? = null override var scrollEventThrottle: Int = 0 override var lastScrollDispatchTime: Long = 0L + override var hasChildPressedStateDelay: Boolean? = null public open var pointerEvents: PointerEvents = PointerEvents.AUTO @@ -544,6 +547,9 @@ constructor(context: Context, private val fpsListener: FpsListener? = null) : } } + override fun shouldDelayChildPressedState(): Boolean = + hasChildPressedStateDelay ?: super.shouldDelayChildPressedState() + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { if (!scrollEnabled) return false if (!PointerEvents.canChildrenBeTouchTarget(pointerEvents)) return true diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout.kt index 01431e9b6853..d2c6fc8d6d90 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout.kt @@ -12,13 +12,14 @@ import android.view.ViewConfiguration import android.view.ViewGroup import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.facebook.react.bridge.ReactContext +import com.facebook.react.uimanager.HasChildPressedStateDelay import com.facebook.react.uimanager.PixelUtil import com.facebook.react.uimanager.events.NativeGestureUtil import kotlin.math.abs /** Basic extension of [SwipeRefreshLayout] with ReactNative-specific functionality. */ public open class ReactSwipeRefreshLayout(reactContext: ReactContext) : - SwipeRefreshLayout(reactContext) { + SwipeRefreshLayout(reactContext), HasChildPressedStateDelay { private var didLayout: Boolean = false private var refreshing: Boolean = false @@ -28,6 +29,8 @@ public open class ReactSwipeRefreshLayout(reactContext: ReactContext) : private var intercepted: Boolean = false private var nativeGestureStarted: Boolean = false + override var hasChildPressedStateDelay: Boolean? = null + public override fun setRefreshing(refreshing: Boolean) { this.refreshing = refreshing @@ -79,6 +82,9 @@ public open class ReactSwipeRefreshLayout(reactContext: ReactContext) : parent?.requestDisallowInterceptTouchEvent(disallowIntercept) } + override fun shouldDelayChildPressedState(): Boolean = + hasChildPressedStateDelay ?: super.shouldDelayChildPressedState() + public override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { if (shouldInterceptTouchEvent(ev) && super.onInterceptTouchEvent(ev)) { NativeGestureUtil.notifyNativeGestureStarted(this, ev) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java index ff4357278512..6b6b848385e5 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java @@ -218,28 +218,16 @@ protected void onDraw(Canvas canvas) { if (layout != null) { CanvasEffectSpan[] drawSpans = spanned.getSpans(0, spanned.length(), CanvasEffectSpan.class); - if (drawSpans.length > 0) { - canvas.save(); - canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop()); - for (CanvasEffectSpan span : drawSpans) { - int start = spanned.getSpanStart(span); - int end = spanned.getSpanEnd(span); - span.onPreDraw(start, end, canvas, layout); - } - canvas.restore(); - - super.onDraw(canvas); - - canvas.save(); - canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop()); - for (CanvasEffectSpan span : drawSpans) { - int start = spanned.getSpanStart(span); - int end = spanned.getSpanEnd(span); - span.onDraw(start, end, canvas, layout); - } - canvas.restore(); + if (shouldDrawLayoutWithoutTextViewClip()) { + drawLayoutWithoutTextViewClip(canvas, spanned, layout, drawSpans); } else { - super.onDraw(canvas); + if (drawSpans.length > 0) { + drawTextEffects(canvas, spanned, layout, drawSpans, true, false); + super.onDraw(canvas); + drawTextEffects(canvas, spanned, layout, drawSpans, false, false); + } else { + super.onDraw(canvas); + } } } else { super.onDraw(canvas); @@ -250,6 +238,74 @@ protected void onDraw(Canvas canvas) { } } + private boolean shouldDrawLayoutWithoutTextViewClip() { + return mOverflow == Overflow.VISIBLE && !mTextIsSelectable && getMovementMethod() == null; + } + + private void drawLayoutWithoutTextViewClip( + Canvas canvas, Spannable spanned, Layout layout, CanvasEffectSpan[] drawSpans) { + getPaint().setColor(getCurrentTextColor()); + getPaint().drawableState = getDrawableState(); + + drawTextEffects(canvas, spanned, layout, drawSpans, true, true); + + canvas.save(); + canvas.translate( + getCompoundPaddingLeft(), getExtendedPaddingTop() + getVerticalGravityOffset(layout)); + layout.draw(canvas); + canvas.restore(); + + drawTextEffects(canvas, spanned, layout, drawSpans, false, true); + } + + private void drawTextEffects( + Canvas canvas, + Spannable spanned, + Layout layout, + CanvasEffectSpan[] drawSpans, + boolean beforeText, + boolean includeVerticalGravityOffset) { + if (drawSpans.length == 0) { + return; + } + + canvas.save(); + canvas.translate( + getCompoundPaddingLeft(), + getExtendedPaddingTop() + + (includeVerticalGravityOffset ? getVerticalGravityOffset(layout) : 0)); + for (CanvasEffectSpan span : drawSpans) { + int start = spanned.getSpanStart(span); + int end = spanned.getSpanEnd(span); + if (beforeText) { + span.onPreDraw(start, end, canvas, layout); + } else { + span.onDraw(start, end, canvas, layout); + } + } + canvas.restore(); + } + + private int getVerticalGravityOffset(Layout layout) { + int availableVerticalSpace = getAvailableVerticalSpace(); + if (layout.getHeight() >= availableVerticalSpace) { + return 0; + } + + int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; + if (verticalGravity == Gravity.BOTTOM) { + return availableVerticalSpace - layout.getHeight(); + } else if (verticalGravity == Gravity.CENTER_VERTICAL) { + return (availableVerticalSpace - layout.getHeight()) / 2; + } + + return 0; + } + + private int getAvailableVerticalSpace() { + return getHeight() - getExtendedPaddingTop() - getExtendedPaddingBottom(); + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { try (SystraceSection s = new SystraceSection("ReactTextView.onMeasure")) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt index a0146aaa4eef..0415dca1126b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt @@ -380,11 +380,20 @@ public open class ReactEditText public constructor(context: Context) : AppCompat // Avoid refocusing to a new view on old versions of Android by default // by preventing `requestFocus()` on the rootView from moving focus to any child. // https://cs.android.com/android/_/android/platform/frameworks/base/+/bdc66cb5a0ef513f4306edf9156cc978b08e06e4 - val rootViewGroup = rootView as ViewGroup - val oldDescendantFocusability = rootViewGroup.descendantFocusability - rootViewGroup.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS - super.clearFocus() - rootViewGroup.descendantFocusability = oldDescendantFocusability + // + // getRootView() returns the view itself when it is detached from the window, so the root + // is not necessarily a ViewGroup: an IME editor action delivered over Binder can race the + // removal of this view from the hierarchy. There is no focus to move in that case, so a + // plain clearFocus() is enough. + val rootViewGroup = rootView as? ViewGroup + if (rootViewGroup != null) { + val oldDescendantFocusability = rootViewGroup.descendantFocusability + rootViewGroup.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS + super.clearFocus() + rootViewGroup.descendantFocusability = oldDescendantFocusability + } else { + super.clearFocus() + } } hideSoftKeyboard() } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/mapbuffer/react/common/mapbuffer/JWritableMapBuffer.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/mapbuffer/react/common/mapbuffer/JWritableMapBuffer.cpp index 3273c5202bcd..f243aeae4b82 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/mapbuffer/react/common/mapbuffer/JWritableMapBuffer.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/mapbuffer/react/common/mapbuffer/JWritableMapBuffer.cpp @@ -35,6 +35,8 @@ MapBuffer JWritableMapBuffer::getMapBuffer() { static const auto stringClass = jni::JString::javaClassStatic(); static const auto readableMapClass = JReadableMapBuffer::javaClassStatic(); static const auto writableMapClass = JWritableMapBuffer::javaClassStatic(); + static const auto intArrayClass = jni::JArrayInt::javaClassStatic(); + static const auto doubleArrayClass = jni::JArrayDouble::javaClassStatic(); if (value->isInstanceOf(booleanClass)) { auto element = jni::static_ref_cast(value); @@ -56,6 +58,17 @@ MapBuffer JWritableMapBuffer::getMapBuffer() { auto element = jni::static_ref_cast(value); builder.putMapBuffer(key, element->getMapBuffer()); + } else if (value->isInstanceOf(intArrayClass)) { + auto array = jni::static_ref_cast(value); + auto pinned = array->pin(); + builder.putIntBuffer( + key, + std::vector(pinned.get(), pinned.get() + pinned.size())); + } else if (value->isInstanceOf(doubleArrayClass)) { + auto array = jni::static_ref_cast(value); + auto pinned = array->pin(); + builder.putDoubleBuffer( + key, std::vector(pinned.get(), pinned.get() + pinned.size())); } } diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/ReactTextViewTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/ReactTextViewTest.kt new file mode 100644 index 000000000000..3cbb6435cf33 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/ReactTextViewTest.kt @@ -0,0 +1,154 @@ +/* + * 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. + */ + +package com.facebook.react.views.text + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.text.SpannableString +import android.text.Spanned +import android.text.style.ReplacementSpan +import android.util.TypedValue +import android.view.Gravity +import android.view.View +import androidx.core.graphics.createBitmap +import androidx.core.graphics.get +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class ReactTextViewTest { + + @Test + fun drawsGlyphInkOutsideLineHeightWhenOverflowIsVisible() { + val bitmap = drawReactTextViewWithOverflow(null) + + assertThat(hasVisiblePixelBelowViewBounds(bitmap)).isTrue() + } + + @Test + fun bottomGravityDoesNotShiftLayoutUpWhenTextIsTallerThanView() { + val lineHeight = 48 + val viewHeight = 24 + val bitmap = drawReactTextViewWithOverflow(null, lineHeight, viewHeight, Gravity.BOTTOM) + + assertThat(firstVisiblePixelY(bitmap)).isGreaterThanOrEqualTo(lineHeight) + } + + private fun drawReactTextViewWithOverflow(overflow: String?): Bitmap { + return drawReactTextViewWithOverflow(overflow, lineHeight = 24, viewHeight = 24, gravity = null) + } + + private fun drawReactTextViewWithOverflow( + overflow: String?, + lineHeight: Int, + viewHeight: Int, + gravity: Int?, + ): Bitmap { + val width = 200 + val bitmapHeight = 80 + val text = SpannableString("x") + text.setSpan( + OverflowingInkSpan(lineHeight), + 0, + text.length, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE, + ) + + val view = TestReactTextView(RuntimeEnvironment.getApplication()) + view.setTextColor(Color.BLACK) + view.setTextSize(TypedValue.COMPLEX_UNIT_PX, 24f) + view.includeFontPadding = true + view.setSpanned(text) + view.text = text + view.setOverflow(overflow) + if (gravity != null) { + view.setGravityVertical(gravity) + } + view.measure( + View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(viewHeight, View.MeasureSpec.EXACTLY), + ) + view.layout(0, 0, width, viewHeight) + + return createBitmap(width, bitmapHeight).also { + view.drawTextForTest(Canvas(it)) + } + } + + private fun hasVisiblePixelBelowViewBounds(bitmap: Bitmap): Boolean { + for (y in 24 until bitmap.height) { + for (x in 0 until bitmap.width) { + if (Color.alpha(bitmap[x, y]) != 0) { + return true + } + } + } + + return false + } + + private fun firstVisiblePixelY(bitmap: Bitmap): Int { + for (y in 0 until bitmap.height) { + for (x in 0 until bitmap.width) { + if (Color.alpha(bitmap[x, y]) != 0) { + return y + } + } + } + + return bitmap.height + } + + private class TestReactTextView(context: Context) : ReactTextView(context) { + fun drawTextForTest(canvas: Canvas) { + super.draw(canvas) + } + } + + private class OverflowingInkSpan(private val lineHeight: Int) : ReplacementSpan() { + override fun getSize( + paint: Paint, + text: CharSequence, + start: Int, + end: Int, + fm: Paint.FontMetricsInt?, + ): Int { + fm?.ascent = -lineHeight + fm?.descent = 0 + fm?.top = -lineHeight + fm?.bottom = 0 + return lineHeight + } + + override fun draw( + canvas: Canvas, + text: CharSequence, + start: Int, + end: Int, + x: Float, + top: Int, + y: Int, + bottom: Int, + paint: Paint, + ) { + canvas.drawRect( + x, + y + (lineHeight / 4f), + x + lineHeight, + y + (lineHeight / 2f), + paint, + ) + } + } +} diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/internal/span/CustomLineHeightSpanTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/internal/span/CustomLineHeightSpanTest.kt new file mode 100644 index 000000000000..a8c9f84d19e7 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/internal/span/CustomLineHeightSpanTest.kt @@ -0,0 +1,99 @@ +/* + * 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. + */ + +package com.facebook.react.views.text.internal.span + +import android.graphics.Paint +import android.text.Layout +import android.text.SpannableString +import android.text.Spanned +import android.text.StaticLayout +import android.text.TextPaint +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class CustomLineHeightSpanTest { + + @Test + fun tightLineHeightDoesNotClipFirstOrLastLineFontBounds() { + val span = CustomLineHeightSpan(16f) + val fm = + Paint.FontMetricsInt().apply { + top = -18 + ascent = -14 + descent = 6 + bottom = 8 + } + + span.chooseHeight("gjpqy", 0, 5, 0, 0, fm) + + assertThat(fm.ascent).isEqualTo(-12) + assertThat(fm.descent).isEqualTo(4) + assertThat(fm.top).isEqualTo(-12) + assertThat(fm.bottom).isEqualTo(4) + } + + @Test + fun looseLineHeightStillExpandsFirstAndLastLineBounds() { + val span = CustomLineHeightSpan(24f) + val fm = + Paint.FontMetricsInt().apply { + top = -18 + ascent = -14 + descent = 6 + bottom = 8 + } + + span.chooseHeight("gjpqy", 0, 5, 0, 0, fm) + + assertThat(fm.ascent).isEqualTo(-16) + assertThat(fm.descent).isEqualTo(8) + assertThat(fm.top).isEqualTo(-16) + assertThat(fm.bottom).isEqualTo(8) + } + + @Test + fun tightLineHeightDoesNotExpandStaticLayoutHeightWithFontPadding() { + val layout = buildStaticLayout("gjpqy\ngjpqy\ngjpqy", lineHeight = 24) + + assertThat(layout.lineCount).isEqualTo(3) + assertThat(layout.height).isEqualTo(72) + } + + @Test + fun tightLineHeightDoesNotExpandSingleLineStaticLayoutHeightWithFontPadding() { + val layout = buildStaticLayout("gjpqy", lineHeight = 24) + + assertThat(layout.lineCount).isEqualTo(1) + assertThat(layout.height).isEqualTo(24) + } + + private fun buildStaticLayout(text: String, lineHeight: Int): StaticLayout { + val spannable = SpannableString(text) + spannable.setSpan( + CustomLineHeightSpan(lineHeight.toFloat()), + 0, + text.length, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE, + ) + + return StaticLayout.Builder.obtain( + spannable, + 0, + spannable.length, + TextPaint().apply { textSize = 24f }, + 400, + ) + .setAlignment(Layout.Alignment.ALIGN_NORMAL) + .setIncludePad(true) + .setLineSpacing(0f, 1f) + .build() + } +} diff --git a/packages/react-native/ReactCommon/React-Fabric.podspec b/packages/react-native/ReactCommon/React-Fabric.podspec index a92fe2abc49d..183c039786eb 100644 --- a/packages/react-native/ReactCommon/React-Fabric.podspec +++ b/packages/react-native/ReactCommon/React-Fabric.podspec @@ -45,6 +45,7 @@ Pod::Spec.new do |s| s.dependency "React-featureflags" s.dependency "React-runtimescheduler" s.dependency "React-cxxreact" + s.dependency "React-bridging" add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) add_dependency(s, "React-rendererdebug") diff --git a/packages/react-native/ReactCommon/ReactCommon.podspec b/packages/react-native/ReactCommon/ReactCommon.podspec index 83e81864640d..a6a9ce6b5668 100644 --- a/packages/react-native/ReactCommon/ReactCommon.podspec +++ b/packages/react-native/ReactCommon/ReactCommon.podspec @@ -43,27 +43,17 @@ Pod::Spec.new do |s| s.subspec "turbomodule" do |ss| ss.dependency "React-callinvoker", version ss.dependency "React-perflogger", version - ss.dependency "React-cxxreact", version ss.dependency "React-jsi", version ss.dependency "React-logger", version if use_hermes() ss.dependency "hermes-engine" end - ss.subspec "bridging" do |sss| - sss.dependency "React-jsi", version - sss.source_files = podspec_sources("react/bridging/**/*.{cpp,h}", "react/bridging/**/*.h") - sss.exclude_files = "react/bridging/tests" - sss.header_dir = "react/bridging" - sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\"" } - if use_hermes() - sss.dependency "hermes-engine" - end - end - ss.subspec "core" do |sss| sss.source_files = podspec_sources("react/nativemodule/core/ReactCommon/**/*.{cpp,h}", "react/nativemodule/core/ReactCommon/**/*.h") sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_CONFIGURATION_BUILD_DIR)/React-debug/React_debug.framework/Headers\" \"$(PODS_CONFIGURATION_BUILD_DIR)/React-debug/React_featureflags.framework/Headers\" \"$(PODS_CONFIGURATION_BUILD_DIR)/React-utils/React_utils.framework/Headers\"" } + sss.dependency "React-bridging" + sss.dependency "React-cxxreact", version sss.dependency "React-debug", version sss.dependency "React-featureflags", version sss.dependency "React-utils", version diff --git a/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec b/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec index a5cc33ecc99a..9f6e92d59a20 100644 --- a/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec +++ b/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec @@ -38,7 +38,7 @@ Pod::Spec.new do |s| s.dependency "React-jsi" s.dependency "React-cxxreact" - s.dependency "ReactCommon/turbomodule/bridging" + s.dependency "React-bridging" add_dependency(s, "React-featureflags") add_dependency(s, "React-debug") diff --git a/packages/react-native/ReactCommon/react/bridging/React-bridging.podspec b/packages/react-native/ReactCommon/react/bridging/React-bridging.podspec new file mode 100644 index 000000000000..9e79373a2aae --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridging/React-bridging.podspec @@ -0,0 +1,44 @@ +# 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. + +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "..", "..", "..", "package.json"))) +version = package['version'] + +source = { :git => 'https://github.com/facebook/react-native.git' } +if version == '1000.0.0' + # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") +else + source[:tag] = "v#{version}" +end + +Pod::Spec.new do |s| + s.name = "React-bridging" + s.version = version + s.summary = "-" + s.homepage = "https://reactnative.dev/" + s.license = package["license"] + s.author = "Meta Platforms, Inc. and its affiliates" + s.platforms = min_supported_versions + s.source = source + s.source_files = podspec_sources("*.{cpp,h}", "*.h") + s.header_dir = "react/bridging" + s.pod_target_xcconfig = { + "USE_HEADERMAP" => "YES", + "CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(), + "DEFINES_MODULE" => "YES" + } + + resolve_use_frameworks(s, header_mappings_dir: "../..", module_name: "React_bridging") + + s.dependency "React-jsi" + s.dependency "React-callinvoker" + s.dependency "React-timing" + + add_rn_third_party_dependencies(s) + add_rncore_dependency(s) +end diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/React-NativeModulesApple.podspec b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/React-NativeModulesApple.podspec index 7fd2869614b6..92bf47ef5764 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/React-NativeModulesApple.podspec +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/React-NativeModulesApple.podspec @@ -37,7 +37,7 @@ Pod::Spec.new do |s| s.source_files = podspec_sources("ReactCommon/**/*.{mm,cpp,h}", "ReactCommon/**/*.{h}") s.dependency "ReactCommon/turbomodule/core" - s.dependency "ReactCommon/turbomodule/bridging" + s.dependency "React-bridging" s.dependency "React-callinvoker" s.dependency "React-Core" s.dependency "React-cxxreact" diff --git a/packages/react-native/ReactCommon/react/nativemodule/dom/React-domnativemodule.podspec b/packages/react-native/ReactCommon/react/nativemodule/dom/React-domnativemodule.podspec index 6119ccb41057..6cc38a3b0fa7 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/dom/React-domnativemodule.podspec +++ b/packages/react-native/ReactCommon/react/nativemodule/dom/React-domnativemodule.podspec @@ -51,6 +51,7 @@ Pod::Spec.new do |s| s.dependency "Yoga" s.dependency "ReactCommon/turbomodule/core" + s.dependency "React-bridging" s.dependency "React-Fabric" s.dependency "React-Fabric/bridging" s.dependency "React-FabricComponents" diff --git a/packages/react-native/ReactCommon/react/nativemodule/idlecallbacks/React-idlecallbacksnativemodule.podspec b/packages/react-native/ReactCommon/react/nativemodule/idlecallbacks/React-idlecallbacksnativemodule.podspec index 46327b2c74d6..654e412d5fea 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/idlecallbacks/React-idlecallbacksnativemodule.podspec +++ b/packages/react-native/ReactCommon/react/nativemodule/idlecallbacks/React-idlecallbacksnativemodule.podspec @@ -48,6 +48,7 @@ Pod::Spec.new do |s| add_rncore_dependency(s) s.dependency "ReactCommon/turbomodule/core" + s.dependency "React-bridging" s.dependency "React-runtimescheduler" add_dependency(s, "React-RCTFBReactNativeSpec") add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) diff --git a/packages/react-native/ReactCommon/react/nativemodule/intersectionobserver/React-intersectionobservernativemodule.podspec b/packages/react-native/ReactCommon/react/nativemodule/intersectionobserver/React-intersectionobservernativemodule.podspec index 089512b1cf9e..4a98cc332a52 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/intersectionobserver/React-intersectionobservernativemodule.podspec +++ b/packages/react-native/ReactCommon/react/nativemodule/intersectionobserver/React-intersectionobservernativemodule.podspec @@ -55,6 +55,7 @@ Pod::Spec.new do |s| add_rncore_dependency(s) s.dependency "ReactCommon/turbomodule/core" + s.dependency "React-bridging" s.dependency "React-Fabric" s.dependency "React-Fabric/bridging" diff --git a/packages/react-native/ReactCommon/react/nativemodule/mutationobserver/React-mutationobservernativemodule.podspec b/packages/react-native/ReactCommon/react/nativemodule/mutationobserver/React-mutationobservernativemodule.podspec index 4ce39514ae20..67b2cbd6052b 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/mutationobserver/React-mutationobservernativemodule.podspec +++ b/packages/react-native/ReactCommon/react/nativemodule/mutationobserver/React-mutationobservernativemodule.podspec @@ -55,6 +55,7 @@ Pod::Spec.new do |s| add_rncore_dependency(s) s.dependency "ReactCommon/turbomodule/core" + s.dependency "React-bridging" s.dependency "React-Fabric" s.dependency "React-Fabric/bridging" diff --git a/packages/react-native/ReactCommon/react/nativemodule/webperformance/React-webperformancenativemodule.podspec b/packages/react-native/ReactCommon/react/nativemodule/webperformance/React-webperformancenativemodule.podspec index 97b52ac93a04..e1615a6ba578 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/webperformance/React-webperformancenativemodule.podspec +++ b/packages/react-native/ReactCommon/react/nativemodule/webperformance/React-webperformancenativemodule.podspec @@ -52,6 +52,7 @@ Pod::Spec.new do |s| add_rncore_dependency(s) s.dependency "ReactCommon/turbomodule/core" + s.dependency "React-bridging" add_dependency(s, "React-RCTFBReactNativeSpec") add_dependency(s, "React-performancetimeline") diff --git a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp index 401a6670d03b..e657cc5514fc 100644 --- a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp @@ -8,8 +8,43 @@ #include "MapBuffer.h" #include +#include +#include + namespace facebook::react { +namespace { +// Reads a value of type T from a (possibly unaligned) offset in the buffer. +// MapBuffer's packed layout places multi-byte values at offsets that are not +// naturally aligned for their type (e.g. an 8-byte value at a 2-byte boundary), +// so dereferencing a reinterpret_cast pointer there is undefined behavior and +// can fault on 32-bit ARM. memcpy compiles to a single unaligned load on +// arm64/x86 and to alignment-safe loads on armv7. +template +inline T readUnaligned(const uint8_t* data, int32_t offset) { + T value; + std::memcpy(&value, data + offset, sizeof(T)); + return value; +} + +// Debug-asserts on OOB (catches corrupt buffers early in dev) AND clamps in +// release so a corrupt bucket length can never drive an OOB memcpy read. +// react_native_assert is compiled out in release, so the runtime cost outside +// dev is the single min() call. +inline int32_t +clampToBufferBounds(int32_t offset, int32_t byteLength, size_t bufferSize) { + react_native_assert(offset >= 0 && byteLength >= 0); + react_native_assert( + static_cast(offset) + static_cast(byteLength) <= + bufferSize); + size_t maxLength = bufferSize > static_cast(offset) + ? bufferSize - static_cast(offset) + : 0; + return static_cast( + std::min(static_cast(std::max(byteLength, 0)), maxLength)); +} +} // namespace + static inline int32_t bucketOffset(int32_t index) { return sizeof(MapBuffer::Header) + sizeof(MapBuffer::Bucket) * index; } @@ -18,16 +53,20 @@ static inline int32_t valueOffset(int32_t bucketIndex) { return bucketOffset(bucketIndex) + offsetof(MapBuffer::Bucket, data); } +// Dynamic-data entries pack [offset (low 32 bits)][byteLength (high 32 bits)] +// into the bucket's 8-byte value, so the payload in the dynamic data section +// carries no in-band length prefix. This returns the position of the high +// 32 bits (the length). +static inline int32_t lengthOffset(int32_t bucketIndex) { + return valueOffset(bucketIndex) + static_cast(sizeof(int32_t)); +} + // TODO T83483191: Extend MapBuffer C++ implementation to support basic random // access MapBuffer::MapBuffer(std::vector data) : bytes_(std::move(data)) { - auto header = reinterpret_cast(bytes_.data()); - count_ = header->count; - - if (header->bufferSize != bytes_.size()) { - LOG(ERROR) << "Error: Data size does not match, expected " - << header->bufferSize << " found: " << bytes_.size(); - abort(); + if (bytes_.size() >= sizeof(Header)) { + auto header = reinterpret_cast(bytes_.data()); + count_ = header->count; } } @@ -37,8 +76,7 @@ int32_t MapBuffer::getKeyBucket(Key key) const { while (lo <= hi) { int32_t mid = (lo + hi) >> 1; - Key midVal = - *reinterpret_cast(bytes_.data() + bucketOffset(mid)); + Key midVal = readUnaligned(bytes_.data(), bucketOffset(mid)); if (midVal < key) { lo = mid + 1; @@ -53,8 +91,7 @@ int32_t MapBuffer::getKeyBucket(Key key) const { } inline int32_t MapBuffer::getIntAtBucket(int32_t bucketIndex) const { - return *reinterpret_cast( - bytes_.data() + valueOffset(bucketIndex)); + return readUnaligned(bytes_.data(), valueOffset(bucketIndex)); } int32_t MapBuffer::getInt(Key key) const { @@ -74,8 +111,7 @@ int64_t MapBuffer::getLong(Key key) const { return 0; } - return *reinterpret_cast( - bytes_.data() + valueOffset(bucketIndex)); + return readUnaligned(bytes_.data(), valueOffset(bucketIndex)); } bool MapBuffer::getBool(Key key) const { @@ -89,8 +125,7 @@ double MapBuffer::getDouble(Key key) const { return 0; } - return *reinterpret_cast( - bytes_.data() + valueOffset(bucketIndex)); + return readUnaligned(bytes_.data(), valueOffset(bucketIndex)); } int32_t MapBuffer::getDynamicDataOffset() const { @@ -107,9 +142,10 @@ std::string MapBuffer::getString(Key key) const { } int32_t offset = getDynamicDataOffset() + getIntAtBucket(bucketIndex); - int32_t stringLength = - *reinterpret_cast(bytes_.data() + offset); - const uint8_t* stringPtr = bytes_.data() + offset + sizeof(int); + auto stringLength = + readUnaligned(bytes_.data(), lengthOffset(bucketIndex)); + stringLength = clampToBufferBounds(offset, stringLength, bytes_.size()); + const uint8_t* stringPtr = bytes_.data() + offset; return {stringPtr, stringPtr + stringLength}; } @@ -122,17 +158,13 @@ MapBuffer MapBuffer::getMapBuffer(Key key) const { } int32_t offset = getDynamicDataOffset() + getIntAtBucket(bucketIndex); - int32_t mapBufferLength = - *reinterpret_cast(bytes_.data() + offset); - size_t maxLength = bytes_.size() - offset - sizeof(int32_t); - if (mapBufferLength > maxLength) { - mapBufferLength = maxLength; - } + auto mapBufferLength = + readUnaligned(bytes_.data(), lengthOffset(bucketIndex)); + mapBufferLength = clampToBufferBounds(offset, mapBufferLength, bytes_.size()); std::vector value(mapBufferLength); - memcpy( - value.data(), bytes_.data() + offset + sizeof(int32_t), mapBufferLength); + memcpy(value.data(), bytes_.data() + offset, mapBufferLength); return MapBuffer(std::move(value)); } @@ -146,23 +178,81 @@ std::vector MapBuffer::getMapBufferList(MapBuffer::Key key) const { std::vector mapBufferList; int32_t offset = getDynamicDataOffset() + getIntAtBucket(bucketIndex); - int32_t mapBufferListLength = - *reinterpret_cast(bytes_.data() + offset); - offset = offset + sizeof(uint32_t); + auto mapBufferListLength = + readUnaligned(bytes_.data(), lengthOffset(bucketIndex)); + mapBufferListLength = + clampToBufferBounds(offset, mapBufferListLength, bytes_.size()); int32_t curLen = 0; while (curLen < mapBufferListLength) { - int32_t mapBufferLength = - *reinterpret_cast(bytes_.data() + offset + curLen); - curLen = curLen + sizeof(uint32_t); + if (curLen + sizeof(int32_t) > mapBufferListLength) { + break; + } + + auto mapBufferLength = + readUnaligned(bytes_.data(), offset + curLen); + curLen += sizeof(int32_t); + + mapBufferLength = + clampToBufferBounds(offset + curLen, mapBufferLength, bytes_.size()); std::vector value(mapBufferLength); memcpy(value.data(), bytes_.data() + offset + curLen, mapBufferLength); mapBufferList.emplace_back(std::move(value)); - curLen = curLen + mapBufferLength; + curLen += mapBufferLength; } return mapBufferList; } +std::vector MapBuffer::getIntBuffer(MapBuffer::Key key) const { + auto bucketIndex = getKeyBucket(key); + react_native_assert(bucketIndex != -1 && "Key not found in MapBuffer"); + if (bucketIndex == -1) { + return {}; + } + + int32_t offset = getDynamicDataOffset() + getIntAtBucket(bucketIndex); + auto byteLength = + readUnaligned(bytes_.data(), lengthOffset(bucketIndex)); + byteLength = clampToBufferBounds(offset, byteLength, bytes_.size()); + int32_t count = byteLength / static_cast(sizeof(int32_t)); + + std::vector result(count); + if (count > 0) { + // Copy only whole elements: a clamped byteLength may not be a multiple of + // sizeof(int32_t), and result holds exactly count elements. + memcpy( + result.data(), + bytes_.data() + offset, + static_cast(count) * sizeof(int32_t)); + } + return result; +} + +std::vector MapBuffer::getDoubleBuffer(MapBuffer::Key key) const { + auto bucketIndex = getKeyBucket(key); + react_native_assert(bucketIndex != -1 && "Key not found in MapBuffer"); + if (bucketIndex == -1) { + return {}; + } + + int32_t offset = getDynamicDataOffset() + getIntAtBucket(bucketIndex); + auto byteLength = + readUnaligned(bytes_.data(), lengthOffset(bucketIndex)); + byteLength = clampToBufferBounds(offset, byteLength, bytes_.size()); + int32_t count = byteLength / static_cast(sizeof(double)); + + std::vector result(count); + if (count > 0) { + // Copy only whole elements: a clamped byteLength may not be a multiple of + // sizeof(double), and result holds exactly count elements. + memcpy( + result.data(), + bytes_.data() + offset, + static_cast(count) * sizeof(double)); + } + return result; +} + size_t MapBuffer::size() const { return bytes_.size(); } diff --git a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.h b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.h index 1ae3595368c9..07b64bdfba99 100644 --- a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.h +++ b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.h @@ -40,11 +40,11 @@ class JReadableMapBuffer; * * MapBuffer data is stored in a continuous chunk of memory (bytes_ field below) with the following layout: * - * ┌─────────────────────Header──────────────────────┐ - * │ 10 bytes │ - * ├─Alignment─┬─Item count─┬──────Buffer size───────┤ - * │ 2 bytes │ 2 bytes │ 4 bytes │ - * └───────────┴────────────┴────────────────────────┘ + * ┌──────Header──────┐ + * │ 2 bytes │ + * ├────Item count────┤ + * │ 2 bytes │ + * └──────────────────┘ * ┌────────────────────────────────────────────────────────────────────────────────────────┐ * │ Buckets (one per item in the map) │ * │ │ @@ -69,14 +69,8 @@ class MapBuffer { public: using Key = uint16_t; - // The first value in the buffer, used to check correct encoding/endianness on - // JVM side. - constexpr static uint16_t HEADER_ALIGNMENT = 0xFE; - struct Header { - uint16_t alignment = HEADER_ALIGNMENT; // alignment of serialization uint16_t count; // amount of items in the map - uint32_t bufferSize; // Amount of bytes used to store the map in memory }; #pragma pack(push, 1) @@ -89,13 +83,14 @@ class MapBuffer { }; #pragma pack(pop) - static_assert(sizeof(Header) == 8, "MapBuffer header size is incorrect."); + static_assert(sizeof(Header) == 2, "MapBuffer header size is incorrect."); static_assert(sizeof(Bucket) == 12, "MapBuffer bucket size is incorrect."); /** * Data types available for serialization in MapBuffer - * Keep in sync with `DataType` enum in `JReadableMapBuffer.java`, which - * expects the same values after reading them through JNI. + * Keep in sync with the `DataType` enum in `MapBuffer.kt` + * (packages/react-native/ReactAndroid/.../common/mapbuffer/MapBuffer.kt), + * which is ordinal-indexed on the JVM side, so the order must match exactly. */ enum DataType : uint16_t { Boolean = 0, @@ -104,6 +99,19 @@ class MapBuffer { String = 3, Map = 4, Long = 5, + // Homogeneous arrays of raw elements stored contiguously in the dynamic + // data section. Unlike Map, they carry no per-element key/type overhead, so + // a batch of N values costs ~N*elementSize bytes instead of N*12-byte + // buckets. The bucket value packs [offset][byteLength]; the element count is + // recovered as byteLength / elementSize. + IntBuffer = 6, + DoubleBuffer = 7, + // A homogeneous, ordered array of nested MapBuffers. The bucket value packs + // [offset][byteLength] for the whole list region; within it each child stays + // framed as [int32 childSize][child bytes]. Distinct from `Map` so that a + // list of MapBuffers is self-describing (a single Map and a list are + // byte-distinct in payload but previously shared the `Map` type tag). + MapBufferList = 8, }; explicit MapBuffer(std::vector data); @@ -131,6 +139,10 @@ class MapBuffer { std::vector getMapBufferList(MapBuffer::Key key) const; + std::vector getIntBuffer(MapBuffer::Key key) const; + + std::vector getDoubleBuffer(MapBuffer::Key key) const; + size_t size() const; const uint8_t *data() const; diff --git a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp index 244f1065b524..1944a01f261f 100644 --- a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp @@ -15,6 +15,14 @@ constexpr uint32_t LONG_SIZE = sizeof(uint64_t); constexpr uint32_t DOUBLE_SIZE = sizeof(double); constexpr uint32_t MAX_BUCKET_VALUE_SIZE = sizeof(uint64_t); +// Dynamic-data entries store their location in the bucket's 8-byte value as +// [offset (low 32 bits)][byteLength (high 32 bits)], so the payload in the +// dynamic data section needs no in-band length prefix. +static inline uint64_t packOffsetAndLength(int32_t offset, int32_t length) { + return static_cast(static_cast(offset)) | + (static_cast(static_cast(length)) << 32); +} + MapBuffer MapBufferBuilder::EMPTY() { return MapBufferBuilder(0).build(); } @@ -22,7 +30,6 @@ MapBuffer MapBufferBuilder::EMPTY() { MapBufferBuilder::MapBufferBuilder(uint32_t initialSize) { buckets_.reserve(initialSize); header_.count = 0; - header_.bufferSize = 0; } void MapBufferBuilder::storeKeyValue( @@ -84,81 +91,111 @@ void MapBufferBuilder::putLong(MapBuffer::Key key, int64_t value) { } void MapBufferBuilder::putString(MapBuffer::Key key, const std::string& value) { - // The wire format encodes lengths and offsets as int32_t (see - // MapBuffer::getString). Without an explicit narrowing cast, `auto` deduces - // size_t (8 bytes on 64-bit) and `memcpy(&x, ..., INT_SIZE)` then copies only - // the first 4 bytes of an 8-byte value: silent truncation on little-endian, - // wrong (high) bytes on big-endian. auto strSize = static_cast(value.size()); - const char* strData = value.data(); - - // format [length of string (int)] + [Array of Characters in the string] auto offset = static_cast(dynamicData_.size()); - dynamicData_.resize(offset + INT_SIZE + strSize, 0); - memcpy(dynamicData_.data() + offset, &strSize, INT_SIZE); - memcpy(dynamicData_.data() + offset + INT_SIZE, strData, strSize); - // Store Key and pointer to the string + // The bucket stores [offset][byteLength]; the dynamic section holds only the + // raw string bytes. + dynamicData_.resize(offset + strSize, 0); + if (strSize > 0) { + memcpy(dynamicData_.data() + offset, value.data(), strSize); + } + + uint64_t data = packOffsetAndLength(offset, strSize); storeKeyValue( key, MapBuffer::DataType::String, - reinterpret_cast(&offset), - INT_SIZE); + reinterpret_cast(&data), + sizeof(data)); } void MapBufferBuilder::putMapBuffer(MapBuffer::Key key, const MapBuffer& map) { - // Wire format encodes lengths and offsets as int32_t (see - // MapBuffer::getMapBuffer). Cast explicitly so memcpy(&x, ..., INT_SIZE) - // copies the full value, not the first 4 bytes of an 8-byte size_t. auto mapBufferSize = static_cast(map.size()); - auto offset = static_cast(dynamicData_.size()); - // format [length of buffer (int)] + [bytes of MapBuffer] - dynamicData_.resize(offset + INT_SIZE + mapBufferSize, 0); - memcpy(dynamicData_.data() + offset, &mapBufferSize, INT_SIZE); - // Copy the content of the map into dynamicData_ - memcpy(dynamicData_.data() + offset + INT_SIZE, map.data(), mapBufferSize); + // The bucket stores [offset][byteLength]; the dynamic section holds only the + // serialized child MapBuffer bytes. + dynamicData_.resize(offset + mapBufferSize, 0); + memcpy(dynamicData_.data() + offset, map.data(), mapBufferSize); - // Store Key and pointer to the string + uint64_t data = packOffsetAndLength(offset, mapBufferSize); storeKeyValue( key, MapBuffer::DataType::Map, - reinterpret_cast(&offset), - INT_SIZE); + reinterpret_cast(&data), + sizeof(data)); } void MapBufferBuilder::putMapBufferList( MapBuffer::Key key, const std::vector& mapBufferList) { auto offset = static_cast(dynamicData_.size()); - int32_t dataSize = 0; - for (const MapBuffer& mapBuffer : mapBufferList) { - dataSize = dataSize + INT_SIZE + static_cast(mapBuffer.size()); - } - - dynamicData_.resize(offset + INT_SIZE, 0); - memcpy(dynamicData_.data() + offset, &dataSize, INT_SIZE); + // The bucket stores [offset][byteLength] for the whole list region; within it + // each child stays framed as [int32 childSize][child bytes] so the children + // remain individually delimited. for (const MapBuffer& mapBuffer : mapBufferList) { auto mapBufferSize = static_cast(mapBuffer.size()); - auto dynamicDataSize = static_cast(dynamicData_.size()); - dynamicData_.resize(dynamicDataSize + INT_SIZE + mapBufferSize, 0); - // format [length of buffer (int)] + [bytes of MapBuffer] - memcpy(dynamicData_.data() + dynamicDataSize, &mapBufferSize, INT_SIZE); - // Copy the content of the map into dynamicData_ + auto pos = static_cast(dynamicData_.size()); + dynamicData_.resize(pos + INT_SIZE + mapBufferSize, 0); + memcpy(dynamicData_.data() + pos, &mapBufferSize, INT_SIZE); memcpy( - dynamicData_.data() + dynamicDataSize + INT_SIZE, - mapBuffer.data(), - mapBufferSize); + dynamicData_.data() + pos + INT_SIZE, mapBuffer.data(), mapBufferSize); } - // Store Key and pointer to the string + auto totalSize = static_cast(dynamicData_.size()) - offset; + uint64_t data = packOffsetAndLength(offset, totalSize); + // Uses the dedicated MapBufferList type so the entry is self-describing and + // distinguishable from a single Map. storeKeyValue( key, - MapBuffer::DataType::Map, - reinterpret_cast(&offset), - INT_SIZE); + MapBuffer::DataType::MapBufferList, + reinterpret_cast(&data), + sizeof(data)); +} + +void MapBufferBuilder::putIntBuffer( + MapBuffer::Key key, + const std::vector& value) { + // The bucket stores [offset][byteLength]; the dynamic section holds the raw + // int32 elements. Element count is recovered as byteLength / sizeof(int32_t). + auto payloadSize = static_cast(value.size() * sizeof(int32_t)); + auto offset = static_cast(dynamicData_.size()); + dynamicData_.resize(offset + payloadSize, 0); + if (payloadSize > 0) { + memcpy(dynamicData_.data() + offset, value.data(), payloadSize); + } + + uint64_t data = packOffsetAndLength(offset, payloadSize); + storeKeyValue( + key, + MapBuffer::DataType::IntBuffer, + reinterpret_cast(&data), + sizeof(data)); +} + +void MapBufferBuilder::putDoubleBuffer( + MapBuffer::Key key, + const std::vector& value) { + // The bucket stores [offset][byteLength]; the dynamic section holds the raw + // double elements. Element count is recovered as byteLength / sizeof(double). + // Doubles are copied byte-for-byte; the reader uses memcpy, so the payload + // needs no special alignment for correctness. A consumer that wants a + // zero-copy typed view on the JVM (ByteBuffer::asDoubleBuffer) must ensure + // 8-byte alignment itself. + auto payloadSize = static_cast(value.size() * sizeof(double)); + auto offset = static_cast(dynamicData_.size()); + dynamicData_.resize(offset + payloadSize, 0); + if (payloadSize > 0) { + memcpy(dynamicData_.data() + offset, value.data(), payloadSize); + } + + uint64_t data = packOffsetAndLength(offset, payloadSize); + storeKeyValue( + key, + MapBuffer::DataType::DoubleBuffer, + reinterpret_cast(&data), + sizeof(data)); } static inline bool compareBuckets( @@ -173,8 +210,6 @@ MapBuffer MapBufferBuilder::build() { auto headerSize = sizeof(MapBuffer::Header); auto bufferSize = headerSize + bucketSize + dynamicData_.size(); - header_.bufferSize = static_cast(bufferSize); - if (needsSort_) { std::sort(buckets_.begin(), buckets_.end(), compareBuckets); } diff --git a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h index af054da55789..2963c77a9b36 100644 --- a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h +++ b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h @@ -39,6 +39,10 @@ class MapBufferBuilder { void putMapBufferList(MapBuffer::Key key, const std::vector &mapBufferList); + void putIntBuffer(MapBuffer::Key key, const std::vector &value); + + void putDoubleBuffer(MapBuffer::Key key, const std::vector &value); + MapBuffer build(); private: diff --git a/packages/react-native/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp b/packages/react-native/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp index 3cc169563fca..05c70d203f59 100644 --- a/packages/react-native/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp @@ -49,7 +49,7 @@ TEST(MapBufferTest, testSimpleLongMap) { } TEST(MapBufferTest, testMapBufferExtension) { - // 26 = 2 buckets: 2*10 + 6 sizeof(header) + // initialSize is a reserve hint for the number of buckets int initialSize = 26; auto buffer = MapBufferBuilder(initialSize); @@ -205,6 +205,101 @@ TEST(MapBufferTest, testMapListEntries) { EXPECT_EQ(mapBufferList2[1].getDouble(3), 908.1); } +TEST(MapBufferTest, testEmptyMapBufferList) { + auto builder = MapBufferBuilder(); + + builder.putMapBufferList(0, {}); + auto map = builder.build(); + + EXPECT_EQ(map.getMapBufferList(0).size(), 0); +} + +// Place the list behind another dynamic-data entry so its offset is non-zero, +// exercising `getDynamicDataOffset() + getIntAtBucket(...)` against a non-zero +// base rather than the zero-offset path testMapListEntries covers. +TEST(MapBufferTest, testMapListEntriesAtNonZeroOffset) { + std::vector mapBufferList; + auto inner = MapBufferBuilder(); + inner.putString(0, "inner"); + inner.putInt(1, 42); + mapBufferList.push_back(inner.build()); + + auto builder = MapBufferBuilder(); + builder.putString(0, "prefix"); + builder.putMapBufferList(1, mapBufferList); + auto map = builder.build(); + + EXPECT_EQ(map.getString(0), "prefix"); + std::vector readList = map.getMapBufferList(1); + EXPECT_EQ(readList.size(), 1); + EXPECT_EQ(readList[0].getString(0), "inner"); + EXPECT_EQ(readList[0].getInt(1), 42); +} + +TEST(MapBufferTest, testIntBufferEntries) { + auto builder = MapBufferBuilder(); + + std::vector values{ + 1, + -2, + 3, + std::numeric_limits::min(), + std::numeric_limits::max()}; + builder.putIntBuffer(0, values); + auto map = builder.build(); + + EXPECT_EQ(map.count(), 1); + EXPECT_EQ(map.getIntBuffer(0), values); +} + +TEST(MapBufferTest, testEmptyIntBuffer) { + auto builder = MapBufferBuilder(); + + builder.putIntBuffer(0, {}); + auto map = builder.build(); + + EXPECT_EQ(map.getIntBuffer(0).size(), 0); +} + +TEST(MapBufferTest, testDoubleBufferEntries) { + auto builder = MapBufferBuilder(); + + std::vector values{0.0, -1.5, 3.14159, 1e300, -1e-300}; + builder.putDoubleBuffer(0, values); + auto map = builder.build(); + + EXPECT_EQ(map.count(), 1); + EXPECT_EQ(map.getDoubleBuffer(0), values); +} + +TEST(MapBufferTest, testEmptyDoubleBuffer) { + auto builder = MapBufferBuilder(); + + builder.putDoubleBuffer(0, {}); + auto map = builder.build(); + + EXPECT_EQ(map.getDoubleBuffer(0).size(), 0); +} + +// Mirrors the batched-animated-props use case: a pair of typed streams plus +// some scalar metadata, with keys inserted out of order to exercise both the +// dynamic-data section and the bucket sort path. +TEST(MapBufferTest, testIntAndDoubleBuffersAlongsideScalars) { + std::vector intStream{1, 100, 1, 2, 4, 15, 4}; + std::vector doubleStream{0.5, 12.0, 0.25}; + + auto builder = MapBufferBuilder(); + builder.putDoubleBuffer(2, doubleStream); + builder.putInt(0, 7); + builder.putIntBuffer(1, intStream); + auto map = builder.build(); + + EXPECT_EQ(map.count(), 3); + EXPECT_EQ(map.getInt(0), 7); + EXPECT_EQ(map.getIntBuffer(1), intStream); + EXPECT_EQ(map.getDoubleBuffer(2), doubleStream); +} + TEST(MapBufferTest, testMapRandomAccess) { auto builder = MapBufferBuilder(); builder.putInt(1234, 4321); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp index 93554c5b47c3..5633a99ad37d 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp @@ -428,7 +428,9 @@ CommitStatus ShadowTree::tryCommit( return CommitStatus::Failed; } - auto newRevisionNumber = currentRevision_.number + 1; + auto newRevisionNumber = isReactBranch + ? oldRevisionForStateProgression.number + 1 + : currentRevision_.number + 1; { std::scoped_lock dispatchLock(EventEmitter::DispatchMutex()); @@ -513,11 +515,12 @@ void ShadowTree::mergeReactRevision() const { } } - ShadowTreeRevision::Number lastMergedRevisionNumber; + RootShadowNode::Shared lastMergedRootShadowNode; + bool lastMergeSucceeded = false; if (isPropsUpdatesAccumulationGuaranteed()) { - lastMergedRevisionNumber = promotedRevision.number; - this->commit( + lastMergedRootShadowNode = promotedRevision.rootShadowNode; + auto status = this->commit( [revision = std::move(promotedRevision)]( const RootShadowNode& /*oldRootShadowNode*/) { return std::make_shared( @@ -528,13 +531,14 @@ void ShadowTree::mergeReactRevision() const { .mountSynchronously = true, .source = CommitSource::ReactRevisionMerge, }); + lastMergeSucceeded = status == CommitStatus::Succeeded; } else { for (size_t i = 0; i < promotedRevisions.size(); ++i) { auto& revision = promotedRevisions[i]; bool isLast = i == promotedRevisions.size() - 1; - lastMergedRevisionNumber = revision.number; + lastMergedRootShadowNode = revision.rootShadowNode; - this->commit( + auto status = this->commit( [revision = std::move(revision)]( const RootShadowNode& /*oldRootShadowNode*/) { return std::make_shared( @@ -545,6 +549,7 @@ void ShadowTree::mergeReactRevision() const { .mountSynchronously = true, .source = CommitSource::ReactRevisionMerge, }); + lastMergeSucceeded = status == CommitStatus::Succeeded; } } @@ -554,8 +559,9 @@ void ShadowTree::mergeReactRevision() const { // If the current react revision is the same as the one that was just // merged, clear it. - if (currentReactRevision_.has_value() && - lastMergedRevisionNumber == currentReactRevision_.value().number) { + if (lastMergeSucceeded && currentReactRevision_.has_value() && + currentReactRevision_.value().rootShadowNode == + lastMergedRootShadowNode) { currentReactRevision_.reset(); } } diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/tests/ShadowTreeReactBranchingTest.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/tests/ShadowTreeReactBranchingTest.cpp new file mode 100644 index 000000000000..79d76a6094fb --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/mounting/tests/ShadowTreeReactBranchingTest.cpp @@ -0,0 +1,141 @@ +/* + * 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace facebook::react; + +namespace { + +class BranchingEnabledFlags : public ReactNativeFeatureFlagsDefaults { + public: + bool enableFabricCommitBranching() override { + return true; + } +}; + +class DummyShadowTreeDelegate : public ShadowTreeDelegate { + public: + RootShadowNode::Unshared shadowTreeWillCommit( + const ShadowTree& /*shadowTree*/, + const RootShadowNode::Shared& /*oldRootShadowNode*/, + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTree::CommitOptions& /*commitOptions*/) const override { + return newRootShadowNode; + } + + void shadowTreeDidFinishTransaction( + std::shared_ptr /*mountingCoordinator*/, + bool /*mountSynchronously*/) const override {} + + void shadowTreeDidFinishReactCommit( + const ShadowTree& /*shadowTree*/) const override {} + + void shadowTreeDidPromoteReactRevision( + const ShadowTree& /*shadowTree*/) const override {} +}; + +} // namespace + +class ShadowTreeReactBranchingTest : public ::testing::Test { + protected: + void SetUp() override { + // Must override before any component construction reads a flag. + ReactNativeFeatureFlags::dangerouslyReset(); + ReactNativeFeatureFlags::override( + std::make_unique()); + } + + void TearDown() override { + ReactNativeFeatureFlags::dangerouslyReset(); + } +}; + +// Two React revisions committed before a merge share a revision number. +// Merging the first must not clear the second: the old number-based comparison +// cleared it (dropping a pending update); the fix compares root node identity. +TEST_F(ShadowTreeReactBranchingTest, mergeDoesNotDropNewerReactRevision) { + // clang-format off + auto element = + Element() + .children({ + Element() + }); + // clang-format on + + ContextContainer contextContainer{}; + auto builder = simpleComponentBuilder(); + auto initialRootShadowNode = builder.build(element); + auto shadowTreeDelegate = DummyShadowTreeDelegate{}; + + ShadowTree shadowTree{ + SurfaceId{11}, + LayoutConstraints{}, + LayoutContext{}, + shadowTreeDelegate, + contextContainer}; + + // Initial (non-React) commit establishes `currentRevision_`. + shadowTree.commit( + [&](const RootShadowNode& /*oldRootShadowNode*/) { + return std::static_pointer_cast(initialRootShadowNode); + }, + {.enableStateReconciliation = false}); + + // First React revision (R1). + auto rootR1 = std::static_pointer_cast( + initialRootShadowNode->ShadowNode::clone({})); + shadowTree.commit( + [&](const RootShadowNode& /*oldRootShadowNode*/) { return rootR1; }, + {.enableStateReconciliation = false, + .mountSynchronously = false, + .source = ShadowTreeCommitSource::React}); + + auto revisionAfterR1 = shadowTree.getCurrentReactRevision(); + ASSERT_TRUE(revisionAfterR1.has_value()); + + // Promote R1 (end-of-tick), but do not merge it yet. + shadowTree.promoteReactRevision(); + + // Second React revision (R2) lands before the merge. The main revision has + // not advanced, so R2 gets R1's revision number but is a distinct tree. + auto rootR2 = std::static_pointer_cast( + initialRootShadowNode->ShadowNode::clone({})); + shadowTree.commit( + [&](const RootShadowNode& /*oldRootShadowNode*/) { return rootR2; }, + {.enableStateReconciliation = false, + .mountSynchronously = false, + .source = ShadowTreeCommitSource::React}); + + auto revisionAfterR2 = shadowTree.getCurrentReactRevision(); + ASSERT_TRUE(revisionAfterR2.has_value()); + + // Precondition for the bug: same number, different revisions. + EXPECT_EQ(revisionAfterR1->number, revisionAfterR2->number); + EXPECT_NE(revisionAfterR2->rootShadowNode, revisionAfterR1->rootShadowNode); + + // Merging R1 must not clear R2. + shadowTree.mergeReactRevision(); + + auto revisionAfterMerge = shadowTree.getCurrentReactRevision(); + ASSERT_TRUE(revisionAfterMerge.has_value()) + << "Newer React revision (R2) was incorrectly dropped while merging R1"; + EXPECT_EQ( + revisionAfterMerge->rootShadowNode, revisionAfterR2->rootShadowNode); +} diff --git a/packages/react-native/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts index 293ae885d55d..c113c8779ced 100644 --- a/packages/react-native/ReactNativeApi.d.ts +++ b/packages/react-native/ReactNativeApi.d.ts @@ -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<<67eb674c33870c82986054f0cee48385>> + * @generated SignedSource<<82e308c1a4f2f713285c7586ff3eb618>> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -5051,6 +5051,7 @@ declare type TextInputAndroidProps = { readonly rows?: number readonly selectionHandleColor?: ColorValue readonly showSoftInputOnFocus?: boolean + readonly textAlignVertical?: "auto" | "bottom" | "center" | "top" readonly textBreakStrategy?: "balanced" | "highQuality" | "simple" readonly underlineColorAndroid?: ColorValue } @@ -6135,8 +6136,8 @@ export { TaskProvider, // 266dedf2 Text, // f792e51d TextContentType, // 239b3ecc - TextInput, // 1c32d882 - TextInputAndroidProps, // 3f09ce49 + TextInput, // 4d0a088b + TextInputAndroidProps, // 7109938a TextInputBlurEvent, // b77af40e TextInputChangeEvent, // f55eef98 TextInputContentSizeChangeEvent, // a27cd32a @@ -6145,7 +6146,7 @@ export { TextInputIOSProps, // 0d05a855 TextInputInstance, // 5a0c0e0d TextInputKeyPressEvent, // 546c5d07 - TextInputProps, // 08c36ff7 + TextInputProps, // a56c62a0 TextInputSelectionChangeEvent, // e58f2abc TextInputSubmitEditingEvent, // 6bcb2aa5 TextInstance, // 05463a96 diff --git a/packages/react-native/index.js b/packages/react-native/index.js index 533c97f038fa..c2873961a5f0 100644 --- a/packages/react-native/index.js +++ b/packages/react-native/index.js @@ -62,7 +62,18 @@ module.exports = { get Image() { return require('./Libraries/Image/Image').default; }, + /** + * @deprecated ImageBackground is deprecated and will be removed in a future release. + * Use a View with an absolutely positioned Image instead. + * See https://reactnative.dev/docs/imagebackground + */ get ImageBackground() { + warnOnce( + 'image-background-deprecated', + 'ImageBackground is deprecated and will be removed in a future release. ' + + 'Use a View with an absolutely positioned Image instead. ' + + 'See https://reactnative.dev/docs/imagebackground', + ); return require('./Libraries/Image/ImageBackground').default; }, get InputAccessoryView() { diff --git a/packages/react-native/scripts/cocoapods/__tests__/codegen_utils-test.rb b/packages/react-native/scripts/cocoapods/__tests__/codegen_utils-test.rb index ad4a5a62c3ad..3dde2a593588 100644 --- a/packages/react-native/scripts/cocoapods/__tests__/codegen_utils-test.rb +++ b/packages/react-native/scripts/cocoapods/__tests__/codegen_utils-test.rb @@ -266,7 +266,7 @@ def get_podspec_no_fabric_no_script "React-Core": [], "React-jsi": [], "React-jsiexecutor": [], - "ReactCommon/turbomodule/bridging": [], + "React-bridging": [], "ReactCommon/turbomodule/core": [], "hermes-engine": [], "React-NativeModulesApple": [], diff --git a/packages/react-native/scripts/cocoapods/__tests__/new_architecture-test.rb b/packages/react-native/scripts/cocoapods/__tests__/new_architecture-test.rb index b062c6e3f243..ba95cfc3331f 100644 --- a/packages/react-native/scripts/cocoapods/__tests__/new_architecture-test.rb +++ b/packages/react-native/scripts/cocoapods/__tests__/new_architecture-test.rb @@ -152,7 +152,7 @@ def test_installModulesDependencies_whenNewArchEnabledAndNewArchAndNoSearchPaths { :dependency_name => "ReactCodegen" }, { :dependency_name => "RCTRequired" }, { :dependency_name => "RCTTypeSafety" }, - { :dependency_name => "ReactCommon/turbomodule/bridging" }, + { :dependency_name => "React-bridging" }, { :dependency_name => "ReactCommon/turbomodule/core" }, { :dependency_name => "React-NativeModulesApple" }, { :dependency_name => "Yoga" }, @@ -203,7 +203,7 @@ def test_installModulesDependencies_whenNewArchDisabledAndSearchPathsAndCompiler { :dependency_name => "ReactCodegen" }, { :dependency_name => "RCTRequired" }, { :dependency_name => "RCTTypeSafety" }, - { :dependency_name => "ReactCommon/turbomodule/bridging" }, + { :dependency_name => "React-bridging" }, { :dependency_name => "ReactCommon/turbomodule/core" }, { :dependency_name => "React-NativeModulesApple" }, { :dependency_name => "Yoga" }, diff --git a/packages/react-native/scripts/cocoapods/new_architecture.rb b/packages/react-native/scripts/cocoapods/new_architecture.rb index 3873632a7d7b..e4353427a977 100644 --- a/packages/react-native/scripts/cocoapods/new_architecture.rb +++ b/packages/react-native/scripts/cocoapods/new_architecture.rb @@ -119,7 +119,7 @@ def self.install_modules_dependencies(spec, new_arch_enabled, folly_version = He spec.dependency "RCTRequired" spec.dependency "RCTTypeSafety" - spec.dependency "ReactCommon/turbomodule/bridging" + spec.dependency "React-bridging" spec.dependency "ReactCommon/turbomodule/core" spec.dependency "React-NativeModulesApple" spec.dependency "Yoga" diff --git a/packages/react-native/scripts/cocoapods/utils.rb b/packages/react-native/scripts/cocoapods/utils.rb index 0e388d6a7ef5..43d71ac01ba9 100644 --- a/packages/react-native/scripts/cocoapods/utils.rb +++ b/packages/react-native/scripts/cocoapods/utils.rb @@ -333,6 +333,7 @@ def self.update_search_paths(installer) .concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "ReactCommon-Samples", "ReactCommon_Samples", ["platform/ios"])) .concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-Fabric", "React_Fabric", ["react/renderer/components/view/platform/cxx"], false)) .concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-NativeModulesApple", "React_NativeModulesApple", [])) + .concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-bridging", "React_bridging", [])) .concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-graphics", "React_graphics", ["react/renderer/graphics/platform/ios"])) .concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-featureflags", "React_featureflags", [])) .concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-renderercss", "React_renderercss", [])) diff --git a/packages/react-native/scripts/codegen/__tests__/__snapshots__/generate-artifacts-executor-test.js.snap b/packages/react-native/scripts/codegen/__tests__/__snapshots__/generate-artifacts-executor-test.js.snap index 40e82c15eff7..9ce1c901040e 100644 --- a/packages/react-native/scripts/codegen/__tests__/__snapshots__/generate-artifacts-executor-test.js.snap +++ b/packages/react-native/scripts/codegen/__tests__/__snapshots__/generate-artifacts-executor-test.js.snap @@ -465,7 +465,7 @@ Pod::Spec.new do |s| s.dependency \\"RCTTypeSafety\\" s.dependency \\"React-Core\\" s.dependency \\"React-jsi\\" - s.dependency \\"ReactCommon/turbomodule/bridging\\" + s.dependency \\"React-bridging\\" s.dependency \\"ReactCommon/turbomodule/core\\" s.dependency \\"React-NativeModulesApple\\" s.dependency 'React-graphics' @@ -948,7 +948,7 @@ Pod::Spec.new do |s| s.dependency \\"RCTTypeSafety\\" s.dependency \\"React-Core\\" s.dependency \\"React-jsi\\" - s.dependency \\"ReactCommon/turbomodule/bridging\\" + s.dependency \\"React-bridging\\" s.dependency \\"ReactCommon/turbomodule/core\\" s.dependency \\"React-NativeModulesApple\\" s.dependency 'React-graphics' diff --git a/packages/react-native/scripts/codegen/templates/ReactCodegen.podspec.template b/packages/react-native/scripts/codegen/templates/ReactCodegen.podspec.template index de434d4b91d8..5909a466ee82 100644 --- a/packages/react-native/scripts/codegen/templates/ReactCodegen.podspec.template +++ b/packages/react-native/scripts/codegen/templates/ReactCodegen.podspec.template @@ -69,7 +69,7 @@ Pod::Spec.new do |s| s.dependency "RCTTypeSafety" s.dependency "React-Core" s.dependency "React-jsi" - s.dependency "ReactCommon/turbomodule/bridging" + s.dependency "React-bridging" s.dependency "ReactCommon/turbomodule/core" s.dependency "React-NativeModulesApple" s.dependency 'React-graphics' diff --git a/packages/react-native/scripts/react_native_pods.rb b/packages/react-native/scripts/react_native_pods.rb index 0ac0bcc78e04..78ddc33661b9 100644 --- a/packages/react-native/scripts/react_native_pods.rb +++ b/packages/react-native/scripts/react_native_pods.rb @@ -162,6 +162,7 @@ def use_react_native! ( pod 'React-domnativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/dom" pod 'React-defaultsnativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/defaults" pod 'React-Mapbuffer', :path => "#{prefix}/ReactCommon" + pod 'React-bridging', :path => "#{prefix}/ReactCommon/react/bridging", :modular_headers => true pod 'React-jserrorhandler', :path => "#{prefix}/ReactCommon/jserrorhandler" pod 'RCTDeprecation', :path => "#{prefix}/ReactApple/Libraries/RCTFoundation/RCTDeprecation" pod 'React-RCTFBReactNativeSpec', :path => "#{prefix}/React" diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api index d484218abb9d..ef70f42b27ef 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api @@ -3247,9 +3247,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -3257,9 +3258,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -3271,9 +3275,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -3282,7 +3284,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api index ce3256b5f8be..a7b27cd3aa8a 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api @@ -3156,9 +3156,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -3166,9 +3167,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -3180,9 +3184,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -3191,7 +3193,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api index 04c7b0691b18..37597a3834e0 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api @@ -3244,9 +3244,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -3254,9 +3255,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -3268,9 +3272,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -3279,7 +3281,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api index 6e8bf9f249d1..947870b8c66e 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api @@ -5482,9 +5482,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -5492,9 +5493,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -5506,9 +5510,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -5517,7 +5519,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api index 8c903a172ef6..d60d97b5ac7f 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api @@ -5406,9 +5406,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -5416,9 +5417,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -5430,9 +5434,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -5441,7 +5443,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api index 6f48057c356d..325e0e4dd4cc 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api @@ -5479,9 +5479,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -5489,9 +5490,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -5503,9 +5507,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -5514,7 +5516,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api index 66d75beaa700..144dbea1f8ef 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api @@ -2168,9 +2168,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -2178,9 +2179,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -2192,9 +2196,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -2203,7 +2205,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api index cd680589a109..c4253f9a715e 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api @@ -2104,9 +2104,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -2114,9 +2115,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -2128,9 +2132,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -2139,7 +2141,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api index b6573d00004b..1cab650e5a96 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api @@ -2165,9 +2165,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -2175,9 +2176,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -2189,9 +2193,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -2200,7 +2202,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList);