From 154b17772d1b4c0ef7fc89add0ae920cc81afc17 Mon Sep 17 00:00:00 2001 From: Devan Buggay Date: Mon, 28 Jul 2025 00:45:06 -0700 Subject: [PATCH 1/3] Isolate NativeDevSettings load (#52821) Summary: Hardens RN to running in mismatched opt/dev modes. While adding an opt native/dev js mode to fantom, LogBox was trying to load NativeDevSettings when it didn't exist because of mode mismatch. This is the only location it's happening, so this just moves the NativeDevSettings load into the one function that needs it. Changelog: [Internal] Differential Revision: D78952284 --- packages/react-native/Libraries/LogBox/Data/LogBoxData.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/LogBox/Data/LogBoxData.js b/packages/react-native/Libraries/LogBox/Data/LogBoxData.js index 37fb35b9f5d3..3ba76ae2cdfe 100644 --- a/packages/react-native/Libraries/LogBox/Data/LogBoxData.js +++ b/packages/react-native/Libraries/LogBox/Data/LogBoxData.js @@ -20,7 +20,6 @@ import type { import DebuggerSessionObserver from '../../../src/private/devsupport/rndevtools/FuseboxSessionObserver'; import parseErrorStack from '../../Core/Devtools/parseErrorStack'; -import NativeDevSettings from '../../NativeModules/specs/NativeDevSettings'; import NativeLogBox from '../../NativeModules/specs/NativeLogBox'; import LogBoxLog from './LogBoxLog'; import {parseLogBoxException} from './parseLogBoxLog'; @@ -493,6 +492,10 @@ function showFuseboxWarningsMigrationMessageOnce() { return; } hasShownFuseboxWarningsMigrationMessage = true; + + const NativeDevSettings = + require('../../NativeModules/specs/NativeDevSettings').default; + appendNewLog( new LogBoxLog({ level: 'warn', From b3870e644784d2978cd5816575e64b836edb91de Mon Sep 17 00:00:00 2001 From: Devan Buggay Date: Mon, 28 Jul 2025 00:45:06 -0700 Subject: [PATCH 2/3] Add JSMode pragma option (#52822) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Adds an additional pragma allowing tests to specify which mode JS should run in. By default this new JS mode will match the regular selected mode, and only shows up in the title if it differs. ### View ### | (index) | Task name | Latency average (ns) | Latency median (ns) | Throughput average (ops/s) | Throughput median (ops/s) | Samples | | ------- | --------------------------------------------------------- | ---------------------- | -------------------------- | -------------------------- | ------------------------- | ------- | | 0 | 'render 100 uncollapsable views' | '22196658.83 ± 0.55%' | '22105506.50 ± 14452.50' | '45 ± 0.52%' | '45' | 64 | | 1 | 'render 1000 uncollapsable views' | '261264120.72 ± 0.51%' | '260129906.50 ± 4932.50' | '4 ± 0.47%' | '4' | 64 | | 2 | 'render 100 views with large amount of props and styles' | '46221879.73 ± 1.32%' | '45766943.00 ± 175.00' | '22 ± 0.96%' | '22' | 64 | | 3 | 'render 1000 views with large amount of props and styles' | '511129295.53 ± 1.11%' | '507136026.50 ± 110.50' | '2 ± 0.85%' | '2' | 64 | | 4 | 'render 1500 views with large amount of props and styles' | '809121466.03 ± 0.98%' | '805250386.00 ± 124858.00' | '1 ± 0.75%' | '1' | 64 | ### View (mode 🚀, jsMode 🐛) ### | (index) | Task name | Latency average (ns) | Latency median (ns) | Throughput average (ops/s) | Throughput median (ops/s) | Samples | | ------- | --------------------------------------------------------- | ---------------------- | -------------------------- | -------------------------- | ------------------------- | ------- | | 0 | 'render 100 uncollapsable views' | '10159906.01 ± 1.31%' | '9852036.00' | '99 ± 1.17%' | '102' | 99 | | 1 | 'render 1000 uncollapsable views' | '140887334.83 ± 1.46%' | '139692094.50 ± 95652.50' | '7 ± 1.14%' | '7' | 64 | | 2 | 'render 100 views with large amount of props and styles' | '23331150.55 ± 1.60%' | '22600244.50 ± 24962.50' | '43 ± 1.34%' | '44' | 64 | | 3 | 'render 1000 views with large amount of props and styles' | '273211225.98 ± 1.41%' | '272005374.00 ± 115394.00' | '4 ± 1.20%' | '4' | 64 | | 4 | 'render 1500 views with large amount of props and styles' | '442607142.55 ± 1.20%' | '434820116.00 ± 455811.00' | '2 ± 1.04%' | '2' | 64 | ### View (mode 🚀) ### | (index) | Task name | Latency average (ns) | Latency median (ns) | Throughput average (ops/s) | Throughput median (ops/s) | Samples | | ------- | --------------------------------------------------------- | ---------------------- | -------------------------- | -------------------------- | ------------------------- | ------- | | 0 | 'render 100 uncollapsable views' | '4104493.33 ± 2.08%' | '3923198.00 ± 110.00' | '248 ± 1.32%' | '255' | 244 | | 1 | 'render 1000 uncollapsable views' | '87679050.92 ± 1.28%' | '87123912.50 ± 5743.50' | '11 ± 1.26%' | '11' | 64 | | 2 | 'render 100 views with large amount of props and styles' | '14064648.78 ± 1.90%' | '13587137.00 ± 6355.00' | '71 ± 1.50%' | '74' | 72 | | 3 | 'render 1000 views with large amount of props and styles' | '184107829.77 ± 0.77%' | '183228851.00 ± 53144.00' | '5 ± 0.77%' | '5' | 64 | | 4 | 'render 1500 views with large amount of props and styles' | '317639710.20 ± 1.17%' | '310442132.00 ± 366431.00' | '3 ± 1.11%' | '3' | 64 | Changelog: [Internal] Differential Revision: D78912257 --- .../runner/formatFantomConfig.js | 20 ++++++++ .../runner/getFantomTestConfigs.js | 47 +++++++++++++++++++ private/react-native-fantom/runner/runner.js | 11 +++-- 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/private/react-native-fantom/runner/formatFantomConfig.js b/private/react-native-fantom/runner/formatFantomConfig.js index 642a8f647db3..702a49db475f 100644 --- a/private/react-native-fantom/runner/formatFantomConfig.js +++ b/private/react-native-fantom/runner/formatFantomConfig.js @@ -14,6 +14,7 @@ import type {HermesVariant} from '../runner/utils'; import { FantomTestConfigHermesVariant, + FantomTestConfigJsMode, FantomTestConfigMode, } from '../runner/getFantomTestConfigs'; import {getOverrides} from './getFantomTestConfigs'; @@ -29,6 +30,15 @@ function formatFantomMode(mode: FantomTestConfigMode): string { } } +function formatFantomJsMode(mode: FantomTestConfigJsMode): string { + switch (mode) { + case FantomTestConfigJsMode.Development: + return 'jsMode 🐛'; + case FantomTestConfigJsMode.Optimized: + return 'jsMode 🚀'; + } +} + function formatFantomHermesVariant(hermesVariant: HermesVariant): string { switch (hermesVariant) { case FantomTestConfigHermesVariant.Hermes: @@ -61,6 +71,16 @@ export default function formatFantomConfig(config: FantomTestConfig): string { parts.push(formatFantomMode(overrides.mode)); } + // Show if the jsMode is different from the mode + if ( + (config.jsMode === FantomTestConfigJsMode.Optimized && + config.mode !== FantomTestConfigMode.Optimized) || + (config.jsMode === FantomTestConfigJsMode.Development && + config.mode === FantomTestConfigMode.Optimized) + ) { + parts.push(formatFantomJsMode(config.jsMode)); + } + if (overrides.hermesVariant) { parts.push(formatFantomHermesVariant(overrides.hermesVariant)); } diff --git a/private/react-native-fantom/runner/getFantomTestConfigs.js b/private/react-native-fantom/runner/getFantomTestConfigs.js index 2b9ba1e3028e..1db14343ed96 100644 --- a/private/react-native-fantom/runner/getFantomTestConfigs.js +++ b/private/react-native-fantom/runner/getFantomTestConfigs.js @@ -26,6 +26,11 @@ export enum FantomTestConfigMode { Optimized, } +export enum FantomTestConfigJsMode { + Development, + Optimized, +} + export type FantomTestConfigCommonFeatureFlags = Partial<{ [key in keyof CommonFeatureFlags]: CommonFeatureFlags[key]['defaultValue'], }>; @@ -46,12 +51,14 @@ export type FantomTestConfigFeatureFlags = { export type FantomTestConfig = { mode: FantomTestConfigMode, + jsMode: FantomTestConfigJsMode, hermesVariant: HermesVariant, flags: FantomTestConfigFeatureFlags, }; export type PartialFantomTestConfig = { mode?: FantomTestConfigMode, + jsMode?: FantomTestConfigJsMode, hermesVariant?: HermesVariant, flags?: Partial, }; @@ -61,6 +68,9 @@ export const FantomTestConfigHermesVariant = HermesVariant; export const DEFAULT_MODE: FantomTestConfigMode = FantomTestConfigMode.DevelopmentWithSource; +export const DEFAULT_JS_MODE: FantomTestConfigJsMode = + FantomTestConfigJsMode.Development; + export const DEFAULT_HERMES_VARIANT: HermesVariant = HermesVariant.Hermes; export const DEFAULT_FEATURE_FLAGS: FantomTestConfigFeatureFlags = { @@ -84,6 +94,7 @@ const MAX_FANTOM_CONFIGURATION_VARIATIONS = 12; const VALID_FANTOM_PRAGMAS = [ 'fantom_mode', + 'fantom_js_mode', 'fantom_flags', 'fantom_hermes_variant', 'fantom_react_fb_flags', @@ -131,6 +142,7 @@ export function getOverrides( * /** * * @flow strict-local * * @fantom_mode opt + * * @fantom_js_mode dev * * @fantom_hermes_variant static_hermes_stable * * @fantom_flags commonTestFlag:true * * @fantom_flags jsOnlyTestFlag:true @@ -163,6 +175,7 @@ export default function getFantomTestConfigs( const config: FantomTestConfig = { mode: DEFAULT_MODE, + jsMode: DEFAULT_JS_MODE, hermesVariant: DEFAULT_HERMES_VARIANT, flags: { common: { @@ -217,6 +230,39 @@ export default function getFantomTestConfigs( } } + const maybeJsMode = pragmas.fantom_js_mode; + + if (maybeJsMode != null) { + if (Array.isArray(maybeJsMode)) { + throw new Error('Expected a single value for @fantom_js_mode'); + } + + const jsMode = maybeJsMode; + + switch (jsMode) { + case 'dev': + config.jsMode = FantomTestConfigJsMode.Development; + break; + case 'opt': + config.jsMode = FantomTestConfigJsMode.Optimized; + break; + case '*': + configVariations.push([ + {jsMode: FantomTestConfigJsMode.Development}, + {jsMode: FantomTestConfigJsMode.Optimized}, + ]); + break; + default: + throw new Error(`Invalid Fantom jsMode: ${jsMode}`); + } + } else { + // If not set, match the native mode + config.jsMode = + config.mode === FantomTestConfigMode.Optimized + ? FantomTestConfigJsMode.Optimized + : FantomTestConfigJsMode.Development; + } + const maybeHermesVariant = pragmas.fantom_hermes_variant; if (maybeHermesVariant != null) { @@ -378,6 +424,7 @@ function getConfigurationVariations( for (const currentConfigVariation of currentConfigVariations) { const currentConfigWithVariation = { mode: currentConfigVariation.mode ?? config.mode, + jsMode: currentConfigVariation.jsMode ?? config.jsMode, hermesVariant: currentConfigVariation.hermesVariant ?? config.hermesVariant, flags: { diff --git a/private/react-native-fantom/runner/runner.js b/private/react-native-fantom/runner/runner.js index 453beb282023..edb4f202b295 100644 --- a/private/react-native-fantom/runner/runner.js +++ b/private/react-native-fantom/runner/runner.js @@ -24,7 +24,10 @@ import entrypointTemplate from './entrypoint-template'; import * as EnvironmentOptions from './EnvironmentOptions'; import formatFantomConfig from './formatFantomConfig'; import getFantomTestConfigs from './getFantomTestConfigs'; -import {FantomTestConfigMode} from './getFantomTestConfigs'; +import { + FantomTestConfigJsMode, + FantomTestConfigMode, +} from './getFantomTestConfigs'; import { getInitialSnapshotData, updateSnapshotsAndGetJestSnapshotResult, @@ -317,12 +320,14 @@ module.exports = async function runTest( path.basename(testJSBundlePath, '.js') + '.map', ); + const devMode = testConfig.jsMode !== FantomTestConfigJsMode.Optimized; + await Metro.runBuild(metroConfig, { entry: entrypointPath, out: testJSBundlePath, platform: 'android', - minify: testConfig.mode === FantomTestConfigMode.Optimized, - dev: testConfig.mode !== FantomTestConfigMode.Optimized, + minify: !devMode, + dev: devMode, sourceMap: true, sourceMapUrl: sourceMapPath, }); From 999d924e63a67f188e053d33ba27c6c823b60716 Mon Sep 17 00:00:00 2001 From: Devan Buggay Date: Mon, 28 Jul 2025 00:45:06 -0700 Subject: [PATCH 3/3] Add console.timeStamp benchmark MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Add a console.timeStamp benchmark. ### Performance API ### | (index) | Task name | Latency average (ns) | Latency median (ns) | Throughput average (ops/s) | Throughput median (ops/s) | Samples | | ------- | --------------------------------------------------------- | -------------------- | ------------------- | -------------------------- | ------------------------- | ------- | | 0 | 'mark (default)' | '4461.54 ± 0.46%' | '4376.00' | '227428 ± 0.02%' | '228519' | 224138 | | 1 | 'mark (with custom startTime)' | '3908.82 ± 0.68%' | '3796.00' | '261599 ± 0.02%' | '263435' | 255832 | | 2 | 'measure (default)' | '4461.29 ± 0.51%' | '4367.00' | '228027 ± 0.02%' | '228990' | 224151 | | 3 | 'measure (with start and end timestamps)' | '4458.98 ± 0.59%' | '4356.00' | '227829 ± 0.02%' | '229568' | 224267 | | 4 | 'measure (with mark names)' | '4473.34 ± 1.03%' | '4357.00' | '227993 ± 0.02%' | '229516' | 223547 | | 5 | 'clearMarks' | '1023.77 ± 0.66%' | '991.00' | '1001044 ± 0.01%' | '1009082' | 976779 | | 6 | 'clearMeasures' | '1061.67 ± 1.13%' | '1012.00' | '978152 ± 0.01%' | '988142' | 941913 | | 7 | 'mark + clearMarks' | '5362.40 ± 0.71%' | '5198.00' | '191130 ± 0.03%' | '192382' | 186484 | | 8 | 'measure + clearMeasures (with start and end timestamps)' | '5336.53 ± 1.25%' | '5078.00' | '195237 ± 0.03%' | '196928' | 187388 | | 9 | 'measure + clearMeasures (with mark names)' | '5056.59 ± 0.70%' | '4877.00' | '203040 ± 0.03%' | '205044' | 197762 | | 10 | 'console.timeStamp' | '737.65 ± 0.87%' | '711.00' | '1397203 ± 0.01%' | '1406470' | 1355662 | ### Performance API (mode 🚀, jsMode 🐛) ### | (index) | Task name | Latency average (ns) | Latency median (ns) | Throughput average (ops/s) | Throughput median (ops/s) | Samples | | ------- | --------------------------------------------------------- | -------------------- | ------------------- | -------------------------- | ------------------------- | ------- | | 0 | 'mark (default)' | '2360.29 ± 0.86%' | '2313.00' | '430172 ± 0.01%' | '432339' | 423677 | | 1 | 'mark (with custom startTime)' | '2109.46 ± 0.88%' | '2063.00' | '482090 ± 0.01%' | '484731' | 474054 | | 2 | 'measure (default)' | '2465.00 ± 1.46%' | '2403.00' | '415030 ± 0.01%' | '416146' | 405680 | | 3 | 'measure (with start and end timestamps)' | '2285.83 ± 0.92%' | '2233.00' | '445739 ± 0.01%' | '447828' | 437477 | | 4 | 'measure (with mark names)' | '2424.85 ± 0.37%' | '2393.00' | '415565 ± 0.01%' | '417885' | 412397 | | 5 | 'clearMarks' | '737.11 ± 0.03%' | '721.00' | '1366303 ± 0.01%' | '1386963' | 1356647 | | 6 | 'clearMeasures' | '720.52 ± 0.07%' | '701.00' | '1407253 ± 0.01%' | '1426534' | 1387882 | | 7 | 'mark + clearMarks' | '2857.63 ± 1.14%' | '2754.00' | '361062 ± 0.02%' | '363108' | 349941 | | 8 | 'measure + clearMeasures (with start and end timestamps)' | '2754.35 ± 1.01%' | '2654.00' | '373865 ± 0.02%' | '376790' | 363063 | | 9 | 'measure + clearMeasures (with mark names)' | '2751.14 ± 0.55%' | '2694.00' | '368710 ± 0.02%' | '371195' | 363486 | | 10 | 'console.timeStamp' | '443.95 ± 0.07%' | '431.00' | '2284563 ± 0.01%' | '2320186' | 2252482 | ### Performance API (mode 🚀) ### | (index) | Task name | Latency average (ns) | Latency median (ns) | Throughput average (ops/s) | Throughput median (ops/s) | Samples | | ------- | --------------------------------------------------------- | -------------------- | ------------------- | -------------------------- | ------------------------- | ------- | | 0 | 'mark (default)' | '1912.09 ± 1.02%' | '1863.00' | '532939 ± 0.01%' | '536769' | 522989 | | 1 | 'mark (with custom startTime)' | '1847.81 ± 2.48%' | '1773.00' | '561174 ± 0.01%' | '564016' | 541182 | | 2 | 'measure (default)' | '1990.39 ± 1.41%' | '1933.00' | '514455 ± 0.01%' | '517331' | 502415 | | 3 | 'measure (with start and end timestamps)' | '2063.03 ± 1.17%' | '2003.00' | '495817 ± 0.01%' | '499251' | 484724 | | 4 | 'measure (with mark names)' | '2184.36 ± 1.14%' | '2134.00' | '465101 ± 0.01%' | '468604' | 457801 | | 5 | 'clearMarks' | '703.02 ± 0.03%' | '691.00' | '1432610 ± 0.01%' | '1447178' | 1422431 | | 6 | 'clearMeasures' | '682.09 ± 0.03%' | '671.00' | '1477044 ± 0.01%' | '1490313' | 1466091 | | 7 | 'mark + clearMarks' | '2420.30 ± 1.32%' | '2313.00' | '428224 ± 0.02%' | '432339' | 413173 | | 8 | 'measure + clearMeasures (with start and end timestamps)' | '2423.89 ± 1.30%' | '2343.00' | '424852 ± 0.01%' | '426803' | 412560 | | 9 | 'measure + clearMeasures (with mark names)' | '2432.48 ± 0.89%' | '2384.00' | '416522 ± 0.01%' | '419463' | 411858 | | 10 | 'console.timeStamp' | '407.00 ± 0.03%' | '401.00' | '2473111 ± 0.01%' | '2493766' | 2456981 | Changelog: [Internal] Differential Revision: D78914518 --- .../performance/__tests__/Performance-benchmark-itest.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/react-native/src/private/webapis/performance/__tests__/Performance-benchmark-itest.js b/packages/react-native/src/private/webapis/performance/__tests__/Performance-benchmark-itest.js index 2076f696cf06..b35b952b40e8 100644 --- a/packages/react-native/src/private/webapis/performance/__tests__/Performance-benchmark-itest.js +++ b/packages/react-native/src/private/webapis/performance/__tests__/Performance-benchmark-itest.js @@ -135,4 +135,7 @@ Fantom.unstable_benchmark }, afterEach: clearMarksAndMeasures, }, - ); + ) + .test('console.timeStamp', () => { + console.timeStamp('label'); + });