From e996b3f346462a394012a722ce19990cdf9c3d9a Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Mon, 2 Dec 2024 03:16:28 -0800 Subject: [PATCH] Fix Animated on JSC: Object.hasOwn -> obj.hasOwnProperty (#48035) Summary: https://github.com/facebook/react-native/pull/46385 introduced use of `Object.hasOwn` as an incidental detail of some `Animated` performance improvements. Unfortunately, `Object.hasOwn` is not present in the version of JSC shipped with Android, nor the built in iOS JSC until iOS 15.4, which is greater than React Native's minimum version (13.4). Instead: - Use `obj.hasOwnProperty(prop)` for known objects that have the `Object` prototype. - Otherwise, use `Object.hasOwn` where it is defined. - Lastly, fall back to `Object.prototype.hasOwnProperty.call(obj, prop)`, which is compatible with passed `null`-prototype objects. Fixes https://github.com/facebook/react-native/issues/47963 Intend to pick for RN 0.77. ## Changelog: [GENERAL][FIXED] Replace Object.hasOwn usages to fix Animated on JSC Pull Request resolved: https://github.com/facebook/react-native/pull/48035 Test Plan: - Run `rn-tester` on Android with Hermes disabled. - Verify the FlatList->Basic example redboxes before this change, and works after it. Reviewed By: yungsters Differential Revision: D66638379 Pulled By: robhogan fbshipit-source-id: 51ac525851b41adea3bf3cc41349225138e1f2fe --- .../Animated/NativeAnimatedAllowlist.js | 8 ++++---- .../Libraries/Animated/nodes/AnimatedProps.js | 10 +++++++++- .../Libraries/Animated/nodes/AnimatedStyle.js | 10 +++++++++- .../src/private/animated/useAnimatedPropsMemo.js | 16 ++++++++++++---- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/packages/react-native/Libraries/Animated/NativeAnimatedAllowlist.js b/packages/react-native/Libraries/Animated/NativeAnimatedAllowlist.js index 71d5ff54dd17a7..ac41c77621e14d 100644 --- a/packages/react-native/Libraries/Animated/NativeAnimatedAllowlist.js +++ b/packages/react-native/Libraries/Animated/NativeAnimatedAllowlist.js @@ -106,17 +106,17 @@ export function allowTransformProp(prop: string): void { } export function isSupportedColorStyleProp(prop: string): boolean { - return Object.hasOwn(SUPPORTED_COLOR_STYLES, prop); + return SUPPORTED_COLOR_STYLES.hasOwnProperty(prop); } export function isSupportedInterpolationParam(param: string): boolean { - return Object.hasOwn(SUPPORTED_INTERPOLATION_PARAMS, param); + return SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(param); } export function isSupportedStyleProp(prop: string): boolean { - return Object.hasOwn(SUPPORTED_STYLES, prop); + return SUPPORTED_STYLES.hasOwnProperty(prop); } export function isSupportedTransformProp(prop: string): boolean { - return Object.hasOwn(SUPPORTED_TRANSFORMS, prop); + return SUPPORTED_TRANSFORMS.hasOwnProperty(prop); } diff --git a/packages/react-native/Libraries/Animated/nodes/AnimatedProps.js b/packages/react-native/Libraries/Animated/nodes/AnimatedProps.js index 44b234f505f9f7..feeaa195550984 100644 --- a/packages/react-native/Libraries/Animated/nodes/AnimatedProps.js +++ b/packages/react-native/Libraries/Animated/nodes/AnimatedProps.js @@ -37,7 +37,7 @@ function createAnimatedProps( const key = keys[ii]; const value = inputProps[key]; - if (allowlist == null || Object.hasOwn(allowlist, key)) { + if (allowlist == null || hasOwn(allowlist, key)) { let node; if (key === 'style') { node = AnimatedStyle.from(value, allowlist?.style); @@ -271,3 +271,11 @@ export default class AnimatedProps extends AnimatedNode { }; } } + +// Supported versions of JSC do not implement the newer Object.hasOwn. Remove +// this shim when they do. +// $FlowIgnore[method-unbinding] +const _hasOwnProp = Object.prototype.hasOwnProperty; +const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean = + // $FlowIgnore[method-unbinding] + Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop)); diff --git a/packages/react-native/Libraries/Animated/nodes/AnimatedStyle.js b/packages/react-native/Libraries/Animated/nodes/AnimatedStyle.js index fa167321ec77f7..359d2e7228a10b 100644 --- a/packages/react-native/Libraries/Animated/nodes/AnimatedStyle.js +++ b/packages/react-native/Libraries/Animated/nodes/AnimatedStyle.js @@ -35,7 +35,7 @@ function createAnimatedStyle( const key = keys[ii]; const value = inputStyle[key]; - if (allowlist == null || Object.hasOwn(allowlist, key)) { + if (allowlist == null || hasOwn(allowlist, key)) { let node; if (value != null && key === 'transform') { node = ReactNativeFeatureFlags.shouldUseAnimatedObjectForTransform() @@ -241,3 +241,11 @@ export default class AnimatedStyle extends AnimatedWithChildren { }; } } + +// Supported versions of JSC do not implement the newer Object.hasOwn. Remove +// this shim when they do. +// $FlowIgnore[method-unbinding] +const _hasOwnProp = Object.prototype.hasOwnProperty; +const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean = + // $FlowIgnore[method-unbinding] + Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop)); diff --git a/packages/react-native/src/private/animated/useAnimatedPropsMemo.js b/packages/react-native/src/private/animated/useAnimatedPropsMemo.js index b0a9319cbe850e..1f093c205a94d9 100644 --- a/packages/react-native/src/private/animated/useAnimatedPropsMemo.js +++ b/packages/react-native/src/private/animated/useAnimatedPropsMemo.js @@ -113,7 +113,7 @@ export function createCompositeKeyForProps( const key = keys[ii]; const value = props[key]; - if (allowlist == null || Object.hasOwn(allowlist, key)) { + if (allowlist == null || hasOwn(allowlist, key)) { let compositeKeyComponent; if (key === 'style') { // $FlowFixMe[incompatible-call] - `style` is a valid argument. @@ -205,7 +205,7 @@ function createCompositeKeyForObject( for (let ii = 0, length = keys.length; ii < length; ii++) { const key = keys[ii]; - if (allowlist == null || Object.hasOwn(allowlist, key)) { + if (allowlist == null || hasOwn(allowlist, key)) { const value = object[key]; let compositeKeyComponent; @@ -250,7 +250,7 @@ export function areCompositeKeysEqual( } for (let ii = 0; ii < length; ii++) { const key = keys[ii]; - if (!Object.hasOwn(next, key)) { + if (!hasOwn(next, key)) { return false; } const prevComponent = prev[key]; @@ -336,7 +336,7 @@ function areCompositeKeyComponentsEqual( for (let ii = 0; ii < length; ii++) { const key = keys[ii]; if ( - !Object.hasOwn(nullthrows(next), key) || + !hasOwn(nullthrows(next), key) || !areCompositeKeyComponentsEqual(prev[key], next[key]) ) { return false; @@ -346,3 +346,11 @@ function areCompositeKeyComponentsEqual( } return false; } + +// Supported versions of JSC do not implement the newer Object.hasOwn. Remove +// this shim when they do. +// $FlowIgnore[method-unbinding] +const _hasOwnProp = Object.prototype.hasOwnProperty; +const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean = + // $FlowIgnore[method-unbinding] + Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop));