From 44ea2a815ccf2d0d79a2487a7efa5f383941a4ef Mon Sep 17 00:00:00 2001 From: Dmytro Rykun Date: Mon, 29 Apr 2024 16:16:58 +0100 Subject: [PATCH 1/3] Introduce addProperties fast path. --- .../src/ReactNativeAttributePayload.js | 60 ++++++++++++++++--- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/packages/react-native-renderer/src/ReactNativeAttributePayload.js b/packages/react-native-renderer/src/ReactNativeAttributePayload.js index 0b316ff1ab875..afaafa9c5de2d 100644 --- a/packages/react-native-renderer/src/ReactNativeAttributePayload.js +++ b/packages/react-native-renderer/src/ReactNativeAttributePayload.js @@ -444,16 +444,62 @@ function diffProperties( return updatePayload; } +function fastAddProperties(updatePayload, nextProps, validAttributes) { + var attributeConfig; + var nextProp; + + for (var propKey in nextProps) { + + nextProp = nextProps[propKey]; + + if (nextProp === undefined) { + continue; + } + + attributeConfig = validAttributes[propKey]; + + if (attributeConfig === undefined) { + continue; + } + + if (typeof nextProp === "function") { + nextProp = true; + } + + if (typeof attributeConfig !== "object") { + if (!updatePayload) { + updatePayload = {}; + } + updatePayload[propKey] = nextProp; + continue; + } + + if (typeof attributeConfig.process === "function") { + if (!updatePayload) { + updatePayload = {}; + } + updatePayload[propKey] = attributeConfig.process(nextProp); + continue; + } + + if (isArray(nextProp)) { + for (var i = 0; i < nextProp.length; i++) { + updatePayload = fastAddProperties(updatePayload, nextProp[i], attributeConfig); + } + continue; + } + + updatePayload = fastAddProperties(updatePayload, nextProp, attributeConfig); + } + + return updatePayload; +} + /** * addProperties adds all the valid props to the payload after being processed. */ -function addProperties( - updatePayload: null | Object, - props: Object, - validAttributes: AttributeConfiguration, -): null | Object { - // TODO: Fast path - return diffProperties(updatePayload, emptyObject, props, validAttributes); +function addProperties(updatePayload, props, validAttributes) { + return fastAddProperties(updatePayload, props, validAttributes); } /** From 8d6963232a39f2dc64b75bc2a8fa546d22c4f603 Mon Sep 17 00:00:00 2001 From: Dmytro Rykun Date: Thu, 2 May 2024 12:23:25 +0100 Subject: [PATCH 2/3] flow, lint, prettier --- .../src/ReactNativeAttributePayload.js | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/packages/react-native-renderer/src/ReactNativeAttributePayload.js b/packages/react-native-renderer/src/ReactNativeAttributePayload.js index afaafa9c5de2d..c30f8fed583f5 100644 --- a/packages/react-native-renderer/src/ReactNativeAttributePayload.js +++ b/packages/react-native-renderer/src/ReactNativeAttributePayload.js @@ -444,12 +444,15 @@ function diffProperties( return updatePayload; } -function fastAddProperties(updatePayload, nextProps, validAttributes) { - var attributeConfig; - var nextProp; - - for (var propKey in nextProps) { +function fastAddProperties( + updatePayload: null | Object, + nextProps: Object, + validAttributes: AttributeConfiguration, +): null | Object { + let attributeConfig; + let nextProp; + for (const propKey in nextProps) { nextProp = nextProps[propKey]; if (nextProp === undefined) { @@ -462,34 +465,42 @@ function fastAddProperties(updatePayload, nextProps, validAttributes) { continue; } - if (typeof nextProp === "function") { - nextProp = true; + if (typeof nextProp === 'function') { + nextProp = (true: any); } - if (typeof attributeConfig !== "object") { + if (typeof attributeConfig !== 'object') { if (!updatePayload) { - updatePayload = {}; + updatePayload = ({}: {[string]: $FlowFixMe}); } updatePayload[propKey] = nextProp; continue; } - if (typeof attributeConfig.process === "function") { + if (typeof attributeConfig.process === 'function') { if (!updatePayload) { - updatePayload = {}; + updatePayload = ({}: {[string]: $FlowFixMe}); } updatePayload[propKey] = attributeConfig.process(nextProp); continue; } if (isArray(nextProp)) { - for (var i = 0; i < nextProp.length; i++) { - updatePayload = fastAddProperties(updatePayload, nextProp[i], attributeConfig); + for (let i = 0; i < nextProp.length; i++) { + updatePayload = fastAddProperties( + updatePayload, + nextProp[i], + ((attributeConfig: any): AttributeConfiguration), + ); } continue; } - updatePayload = fastAddProperties(updatePayload, nextProp, attributeConfig); + updatePayload = fastAddProperties( + updatePayload, + nextProp, + ((attributeConfig: any): AttributeConfiguration), + ); } return updatePayload; @@ -498,7 +509,11 @@ function fastAddProperties(updatePayload, nextProps, validAttributes) { /** * addProperties adds all the valid props to the payload after being processed. */ -function addProperties(updatePayload, props, validAttributes) { +function addProperties( + updatePayload: null | Object, + props: Object, + validAttributes: AttributeConfiguration, +): null | Object { return fastAddProperties(updatePayload, props, validAttributes); } From ab21f135d5963b30cdf53ce5666af4035eb8cb32 Mon Sep 17 00:00:00 2001 From: Dmytro Rykun Date: Thu, 2 May 2024 15:50:01 +0100 Subject: [PATCH 3/3] Add enableAddPropertiesFastPath feature flag --- .../src/ReactNativeAttributePayload.js | 7 ++++++- packages/shared/ReactFeatureFlags.js | 2 ++ .../shared/forks/ReactFeatureFlags.native-fb-dynamic.js | 1 + packages/shared/forks/ReactFeatureFlags.native-fb.js | 1 + packages/shared/forks/ReactFeatureFlags.native-oss.js | 1 + packages/shared/forks/ReactFeatureFlags.test-renderer.js | 1 + .../forks/ReactFeatureFlags.test-renderer.native-fb.js | 1 + .../shared/forks/ReactFeatureFlags.test-renderer.www.js | 1 + packages/shared/forks/ReactFeatureFlags.www-dynamic.js | 1 + packages/shared/forks/ReactFeatureFlags.www.js | 1 + 10 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/react-native-renderer/src/ReactNativeAttributePayload.js b/packages/react-native-renderer/src/ReactNativeAttributePayload.js index c30f8fed583f5..f5fc4af494e27 100644 --- a/packages/react-native-renderer/src/ReactNativeAttributePayload.js +++ b/packages/react-native-renderer/src/ReactNativeAttributePayload.js @@ -15,6 +15,7 @@ import { import isArray from 'shared/isArray'; import {enableEarlyReturnForPropDiffing} from 'shared/ReactFeatureFlags'; +import {enableAddPropertiesFastPath} from 'shared/ReactFeatureFlags'; import type {AttributeConfiguration} from './ReactNativeTypes'; @@ -514,7 +515,11 @@ function addProperties( props: Object, validAttributes: AttributeConfiguration, ): null | Object { - return fastAddProperties(updatePayload, props, validAttributes); + if (enableAddPropertiesFastPath) { + return fastAddProperties(updatePayload, props, validAttributes); + } else { + return diffProperties(updatePayload, emptyObject, props, validAttributes); + } } /** diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index bfb48e65df4a1..ff611728ec1a5 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -123,6 +123,8 @@ export const enableServerComponentLogs = __EXPERIMENTAL__; export const enableEarlyReturnForPropDiffing = false; +export const enableAddPropertiesFastPath = false; + /** * Enables an expiration time for retry lanes to avoid starvation. */ diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js index 2a730a71e15cd..be853f93a9009 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js @@ -29,3 +29,4 @@ export const enableUnifiedSyncLane = __VARIANT__; export const passChildrenWhenCloningPersistedNodes = __VARIANT__; export const useModernStrictMode = __VARIANT__; export const disableDefaultPropsExceptForClasses = __VARIANT__; +export const enableAddPropertiesFastPath = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index dd046dc7a4ccb..135e7e1365cd1 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -31,6 +31,7 @@ export const { passChildrenWhenCloningPersistedNodes, useModernStrictMode, disableDefaultPropsExceptForClasses, + enableAddPropertiesFastPath, } = dynamicFlags; // The rest of the flags are static for better dead code elimination. diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 243f63b623d72..7d2ef8902465e 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -103,6 +103,7 @@ export const enableDO_NOT_USE_disableStrictPassiveEffect = false; export const passChildrenWhenCloningPersistedNodes = false; export const enableEarlyReturnForPropDiffing = false; export const enableAsyncIterableChildren = false; +export const enableAddPropertiesFastPath = false; export const renameElementSymbol = true; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index e80af511bb71b..8e362743b3cb4 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -78,6 +78,7 @@ export const enableServerComponentKeys = true; export const enableServerComponentLogs = true; export const enableInfiniteRenderLoopDetection = false; export const enableEarlyReturnForPropDiffing = false; +export const enableAddPropertiesFastPath = false; export const renameElementSymbol = true; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index 2338dfcc4d557..8aaac9bdabe37 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -89,6 +89,7 @@ export const disableDOMTestUtils = false; export const disableDefaultPropsExceptForClasses = false; export const enableEarlyReturnForPropDiffing = false; +export const enableAddPropertiesFastPath = false; export const renameElementSymbol = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index dc6fcc3cec8be..43f3d9553bab1 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -89,6 +89,7 @@ export const disableDOMTestUtils = false; export const disableDefaultPropsExceptForClasses = false; export const enableEarlyReturnForPropDiffing = false; +export const enableAddPropertiesFastPath = false; export const renameElementSymbol = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index af5071c47af3b..d0b39d5abf190 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -31,6 +31,7 @@ export const enableNoCloningMemoCache = __VARIANT__; export const retryLaneExpirationMs = 5000; export const syncLaneExpirationMs = 250; export const transitionLaneExpirationMs = 5000; +export const enableAddPropertiesFastPath = __VARIANT__; // Enable this flag to help with concurrent mode debugging. // It logs information to the console about React scheduling, rendering, and commit phases. diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 252571251f55a..dd899e5830236 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -35,6 +35,7 @@ export const { favorSafetyOverHydrationPerf, disableDefaultPropsExceptForClasses, enableNoCloningMemoCache, + enableAddPropertiesFastPath, } = dynamicFeatureFlags; // On WWW, __EXPERIMENTAL__ is used for a new modern build.