Skip to content

Commit

Permalink
Fix Animated on JSC: Object.hasOwn -> obj.hasOwnProperty (#48035)
Browse files Browse the repository at this point in the history
Summary:
#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 #47963

Intend to pick for RN 0.77.

## Changelog:

[GENERAL][FIXED] Replace Object.hasOwn usages to fix Animated on JSC

Pull Request resolved: #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
  • Loading branch information
robhogan authored and facebook-github-bot committed Dec 2, 2024
1 parent f791fb9 commit e996b3f
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
10 changes: 9 additions & 1 deletion packages/react-native/Libraries/Animated/nodes/AnimatedProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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));
10 changes: 9 additions & 1 deletion packages/react-native/Libraries/Animated/nodes/AnimatedStyle.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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));
16 changes: 12 additions & 4 deletions packages/react-native/src/private/animated/useAnimatedPropsMemo.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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;
Expand All @@ -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));

0 comments on commit e996b3f

Please sign in to comment.