From 3a13b1fd53f1ae2ae4f439f20778dd12a8224260 Mon Sep 17 00:00:00 2001 From: Devan Buggay Date: Mon, 28 Jul 2025 08:00:49 -0700 Subject: [PATCH 1/3] Isolate NativeDevSettings load Summary: 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 moved the NativeDevSettings load into the one function that needs it, probably not the best solution because if this function gets called in this mode we will crash still, but it works for now. 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 1e2b48df4f8ee443da024ff895c23e8ef973ff70 Mon Sep 17 00:00:00 2001 From: Devan Buggay Date: Mon, 28 Jul 2025 21:43:46 -0700 Subject: [PATCH 2/3] Refactor native/js modes (#52822) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52822 Refactors underlying modes by adding `isNativeOpt`, `isJsOp`, and `isJsBytecode` to allow for more granular control in a future diff. ### View ### | (index) | Task name | Latency average (ns) | Latency median (ns) | Throughput average (ops/s) | Throughput median (ops/s) | Samples | | ------- | --------------------------------------------------------- | ---------------------- | ------------------------- | -------------------------- | ------------------------- | ------- | | 0 | 'render 100 uncollapsable views' | '23005778.16 ± 0.53%' | '22877194.50 ± 11607.50' | '43 ± 0.50%' | '44' | 64 | | 1 | 'render 1000 uncollapsable views' | '271276451.70 ± 0.61%' | '268378201.00 ± 9925.00' | '4 ± 0.59%' | '4' | 64 | | 2 | 'render 100 views with large amount of props and styles' | '47580650.91 ± 1.21%' | '47212012.00 ± 2979.00' | '21 ± 0.89%' | '21' | 64 | | 3 | 'render 1000 views with large amount of props and styles' | '521237370.22 ± 1.09%' | '516142815.00 ± 41682.00' | '2 ± 0.84%' | '2' | 64 | | 4 | 'render 1500 views with large amount of props and styles' | '828143691.48 ± 0.94%' | '824723257.50 ± 11331.50' | '1 ± 0.73%' | '1' | 64 | ### View (mode 🚀, jsMode 🚀, bytecode) ### | (index) | Task name | Latency average (ns) | Latency median (ns) | Throughput average (ops/s) | Throughput median (ops/s) | Samples | | ------- | --------------------------------------------------------- | ---------------------- | -------------------------- | -------------------------- | ------------------------- | ------- | | 0 | 'render 100 uncollapsable views' | '4051033.45 ± 2.01%' | '3876618.00' | '251 ± 1.29%' | '258' | 247 | | 1 | 'render 1000 uncollapsable views' | '86134420.23 ± 1.38%' | '85815369.50 ± 281477.50' | '12 ± 1.38%' | '12' | 64 | | 2 | 'render 100 views with large amount of props and styles' | '13921817.92 ± 2.57%' | '13474963.50 ± 4977.50' | '72 ± 1.62%' | '74' | 72 | | 3 | 'render 1000 views with large amount of props and styles' | '182664526.31 ± 0.74%' | '181872565.00 ± 10281.00' | '5 ± 0.73%' | '5' | 64 | | 4 | 'render 1500 views with large amount of props and styles' | '313110386.45 ± 1.13%' | '307934163.50 ± 156920.50' | '3 ± 1.07%' | '3' | 64 | Changelog: [Internal] Differential Revision: D78912257 --- .../react-native-fantom/__docs__/README.md | 16 ++-- .../runner/formatFantomConfig.js | 28 +++---- .../runner/getFantomTestConfigs.js | 76 ++++++++++++------- private/react-native-fantom/runner/runner.js | 29 +++---- .../__tests__/FantomModeDevBytecode-itest.js | 16 ---- 5 files changed, 74 insertions(+), 91 deletions(-) delete mode 100644 private/react-native-fantom/src/__tests__/FantomModeDevBytecode-itest.js diff --git a/private/react-native-fantom/__docs__/README.md b/private/react-native-fantom/__docs__/README.md index e069208faefa..85210b9eb2b7 100644 --- a/private/react-native-fantom/__docs__/README.md +++ b/private/react-native-fantom/__docs__/README.md @@ -132,8 +132,6 @@ Available pragmas: - Possible values: - `dev`: development, default for tests. - `opt`: optimized and using Hermes bytecode, default for benchmarks. - - `dev-bytecode`: development but using Hermes bytecode instead of plain - text JavaScript code. - `@fantom_react_fb_flags`: used to set overrides for internal React flags set in ReactNativeInternalFeatureFlags (Meta use only) @@ -150,14 +148,12 @@ this test: Would be executed with these combinations of options: -| `jsOnlyTestFlag` | `mode` | -| ---------------- | -------------- | -| `false` | `dev` | -| `true` | `dev` | -| `false` | `dev-bytecode` | -| `true` | `dev-bytecode` | -| `false` | `opt` | -| `true` | `opt` | +| `jsOnlyTestFlag` | `mode` | +| ---------------- | ------ | +| `false` | `dev` | +| `true` | `dev` | +| `false` | `opt` | +| `true` | `opt` | With an output such as: diff --git a/private/react-native-fantom/runner/formatFantomConfig.js b/private/react-native-fantom/runner/formatFantomConfig.js index 02a243f19b55..c4c2199cae89 100644 --- a/private/react-native-fantom/runner/formatFantomConfig.js +++ b/private/react-native-fantom/runner/formatFantomConfig.js @@ -12,23 +12,9 @@ import type {FeatureFlagValue} from '../../../packages/react-native/scripts/feat import type {FantomTestConfig} from '../runner/getFantomTestConfigs'; import type {HermesVariant} from '../runner/utils'; -import { - FantomTestConfigHermesVariant, - FantomTestConfigMode, -} from '../runner/getFantomTestConfigs'; +import {FantomTestConfigHermesVariant} from '../runner/getFantomTestConfigs'; import {getOverrides} from './getFantomTestConfigs'; -function formatFantomMode(mode: FantomTestConfigMode): string { - switch (mode) { - case FantomTestConfigMode.DevelopmentWithSource: - return 'mode 🐛'; - case FantomTestConfigMode.DevelopmentWithBytecode: - return 'mode 🐛🔢'; - case FantomTestConfigMode.Optimized: - return 'mode 🚀'; - } -} - function formatFantomHermesVariant(hermesVariant: HermesVariant): string { switch (hermesVariant) { case FantomTestConfigHermesVariant.Hermes: @@ -55,8 +41,16 @@ export default function formatFantomConfig(config: FantomTestConfig): string { const overrides = getOverrides(config); const parts = []; - if (overrides.mode) { - parts.push(formatFantomMode(overrides.mode)); + if (overrides.isNativeOptimized != null) { + parts.push(overrides.isNativeOptimized ? 'native 🚀' : 'native 🐛'); + } + + if (overrides.isJsOptimized != null) { + parts.push(overrides.isJsOptimized ? 'js 🚀' : 'js 🐛'); + } + + if (overrides.isJsBytecode != null && overrides.isJsBytecode) { + parts.push('bytecode'); } if (overrides.hermesVariant) { diff --git a/private/react-native-fantom/runner/getFantomTestConfigs.js b/private/react-native-fantom/runner/getFantomTestConfigs.js index 0a92d6985986..7d95591a41ef 100644 --- a/private/react-native-fantom/runner/getFantomTestConfigs.js +++ b/private/react-native-fantom/runner/getFantomTestConfigs.js @@ -20,12 +20,6 @@ type JsOnlyFeatureFlags = (typeof ReactNativeFeatureFlags)['jsOnly']; type DocblockPragmas = {[key: string]: string | string[]}; -export enum FantomTestConfigMode { - DevelopmentWithBytecode, - DevelopmentWithSource, - Optimized, -} - export type FantomTestConfigCommonFeatureFlags = Partial<{ [key in keyof CommonFeatureFlags]: CommonFeatureFlags[key]['defaultValue'], }>; @@ -45,22 +39,27 @@ export type FantomTestConfigFeatureFlags = { }; export type FantomTestConfig = { - mode: FantomTestConfigMode, + isNativeOptimized: boolean, + isJsOptimized: boolean, + isJsBytecode: boolean, hermesVariant: HermesVariant, flags: FantomTestConfigFeatureFlags, }; export type PartialFantomTestConfig = { - mode?: FantomTestConfigMode, + isNativeOptimized?: boolean, + isJsOptimized?: boolean, + isJsBytecode?: boolean, hermesVariant?: HermesVariant, flags?: Partial, }; export const FantomTestConfigHermesVariant = HermesVariant; -export const DEFAULT_MODE: FantomTestConfigMode = - FantomTestConfigMode.DevelopmentWithSource; - +export const DEFAULT_IS_OPTIMIZED: boolean = false; +export const DEFAULT_IS_NATIVE_OPTIMIZED: boolean = false; +export const DEFAULT_IS_JS_OPTIMIZED: boolean = false; +export const DEFAULT_IS_JS_BYTECODE: boolean = false; export const DEFAULT_HERMES_VARIANT: HermesVariant = HermesVariant.Hermes; export const DEFAULT_FEATURE_FLAGS: FantomTestConfigFeatureFlags = { @@ -77,9 +76,6 @@ const FANTOM_BENCHMARK_FILENAME_RE = /[Bb]enchmark-itest\./g; const FANTOM_BENCHMARK_SUITE_RE = /\n(Fantom\.)?unstable_benchmark(\s*)\.suite\(/g; -const FANTOM_BENCHMARK_DEFAULT_MODE: FantomTestConfigMode = - FantomTestConfigMode.Optimized; - const MAX_FANTOM_CONFIGURATION_VARIATIONS = 12; const VALID_FANTOM_PRAGMAS = [ @@ -94,8 +90,16 @@ export function getOverrides( ): PartialFantomTestConfig { const overrides: PartialFantomTestConfig = {}; - if (config.mode !== DEFAULT_MODE) { - overrides.mode = config.mode; + if (config.isNativeOptimized !== DEFAULT_IS_NATIVE_OPTIMIZED) { + overrides.isNativeOptimized = config.isNativeOptimized; + } + + if (config.isJsOptimized !== DEFAULT_IS_JS_OPTIMIZED) { + overrides.isJsOptimized = config.isJsOptimized; + } + + if (config.isJsBytecode !== DEFAULT_IS_JS_BYTECODE) { + overrides.isJsBytecode = config.isJsBytecode; } if (config.hermesVariant !== DEFAULT_HERMES_VARIANT) { @@ -140,7 +144,7 @@ export function getOverrides( * * The supported options are: * - `fantom_mode`: specifies the level of optimization to compile the test - * with. Valid values are `dev`, `dev-bytecode` and `opt`. + * with. Valid values are `dev` and `opt`. * - `fantom_hermes_variant`: specifies the Hermes variant to use to run the * test. Valid values are `hermes`, `static_hermes_stable` and * `static_hermes_experimental`. @@ -162,7 +166,9 @@ export default function getFantomTestConfigs( verifyFantomPragmas(pragmas); const config: FantomTestConfig = { - mode: DEFAULT_MODE, + isNativeOptimized: DEFAULT_IS_NATIVE_OPTIMIZED, + isJsOptimized: DEFAULT_IS_JS_OPTIMIZED, + isJsBytecode: DEFAULT_IS_JS_BYTECODE, hermesVariant: DEFAULT_HERMES_VARIANT, flags: { common: { @@ -190,19 +196,27 @@ export default function getFantomTestConfigs( switch (mode) { case 'dev': - config.mode = FantomTestConfigMode.DevelopmentWithSource; - break; - case 'dev-bytecode': - config.mode = FantomTestConfigMode.DevelopmentWithBytecode; + config.isNativeOptimized = false; + config.isJsOptimized = false; + config.isJsBytecode = false; break; case 'opt': - config.mode = FantomTestConfigMode.Optimized; + config.isNativeOptimized = true; + config.isJsOptimized = true; + config.isJsBytecode = true; break; case '*': configVariations.push([ - {mode: FantomTestConfigMode.DevelopmentWithSource}, - {mode: FantomTestConfigMode.DevelopmentWithBytecode}, - {mode: FantomTestConfigMode.Optimized}, + { + isNativeOptimized: false, + isJsOptimized: false, + isJsBytecode: false, + }, + { + isNativeOptimized: true, + isJsOptimized: true, + isJsBytecode: true, + }, ]); break; default: @@ -213,7 +227,9 @@ export default function getFantomTestConfigs( FANTOM_BENCHMARK_FILENAME_RE.test(testPath) || FANTOM_BENCHMARK_SUITE_RE.test(testContents) ) { - config.mode = FANTOM_BENCHMARK_DEFAULT_MODE; + config.isNativeOptimized = true; + config.isJsOptimized = true; + config.isJsBytecode = true; } } @@ -373,7 +389,11 @@ function getConfigurationVariations( for (const currentConfigVariation of currentConfigVariations) { const currentConfigWithVariation = { - mode: currentConfigVariation.mode ?? config.mode, + isNativeOptimized: + currentConfigVariation.isNativeOptimized ?? config.isNativeOptimized, + isJsOptimized: + currentConfigVariation.isJsOptimized ?? config.isJsOptimized, + isJsBytecode: currentConfigVariation.isJsBytecode ?? config.isJsBytecode, 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 51e362cfb0fd..2633bbf80f56 100644 --- a/private/react-native-fantom/runner/runner.js +++ b/private/react-native-fantom/runner/runner.js @@ -24,7 +24,6 @@ import entrypointTemplate from './entrypoint-template'; import * as EnvironmentOptions from './EnvironmentOptions'; import formatFantomConfig from './formatFantomConfig'; import getFantomTestConfigs from './getFantomTestConfigs'; -import {FantomTestConfigMode} from './getFantomTestConfigs'; import { getInitialSnapshotData, updateSnapshotsAndGetJestSnapshotResult, @@ -249,10 +248,7 @@ module.exports = async function runTest( ]; for (const testConfig of testConfigs) { - if ( - EnvironmentOptions.isOSS && - testConfig.mode === FantomTestConfigMode.Optimized - ) { + if (EnvironmentOptions.isOSS && testConfig.isNativeOptimized) { testResultsByConfig.push( skippedTestResults({ ancestorTitles: ['"@fantom_mode opt" in docblock'], @@ -277,13 +273,10 @@ module.exports = async function runTest( continue; } - if ( - EnvironmentOptions.isOSS && - testConfig.mode !== FantomTestConfigMode.DevelopmentWithSource - ) { + if (EnvironmentOptions.isOSS && testConfig.isJsBytecode) { testResultsByConfig.push( skippedTestResults({ - ancestorTitles: ['"@fantom_mode dev-bytecode" in docblock'], + ancestorTitles: ['"@fantom_mode dev" in docblock'], title: 'Hermes bytecode is not yet supported in OSS', }), ); @@ -320,26 +313,24 @@ module.exports = async function runTest( entry: entrypointPath, out: testJSBundlePath, platform: 'android', - minify: testConfig.mode === FantomTestConfigMode.Optimized, - dev: testConfig.mode !== FantomTestConfigMode.Optimized, + minify: testConfig.isJsOptimized, + dev: !testConfig.isJsOptimized, sourceMap: true, sourceMapUrl: sourceMapPath, }); - if (testConfig.mode !== FantomTestConfigMode.DevelopmentWithSource) { + if (testConfig.isJsBytecode) { generateBytecodeBundle({ sourcePath: testJSBundlePath, bytecodePath: testBytecodeBundlePath, - isOptimizedMode: testConfig.mode === FantomTestConfigMode.Optimized, + isOptimizedMode: testConfig.isJsOptimized, hermesVariant: testConfig.hermesVariant, }); } const rnTesterCommandArgs = [ '--bundlePath', - testConfig.mode === FantomTestConfigMode.DevelopmentWithSource - ? testJSBundlePath - : testBytecodeBundlePath, + !testConfig.isJsBytecode ? testJSBundlePath : testBytecodeBundlePath, '--featureFlags', JSON.stringify(testConfig.flags.common), '--minLogLevel', @@ -354,9 +345,7 @@ module.exports = async function runTest( : runBuck2( [ 'run', - ...getBuckModesForPlatform( - testConfig.mode === FantomTestConfigMode.Optimized, - ), + ...getBuckModesForPlatform(testConfig.isNativeOptimized), ...getBuckOptionsForHermes(testConfig.hermesVariant), '//xplat/js/react-native-github/private/react-native-fantom/tester:tester', '--', diff --git a/private/react-native-fantom/src/__tests__/FantomModeDevBytecode-itest.js b/private/react-native-fantom/src/__tests__/FantomModeDevBytecode-itest.js deleted file mode 100644 index c4e2db352480..000000000000 --- a/private/react-native-fantom/src/__tests__/FantomModeDevBytecode-itest.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 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. - * - * @fantom_mode dev-bytecode - * @flow strict-local - * @format - */ - -describe('"@fantom_mode dev-bytecode" in docblock', () => { - it('should use development builds', () => { - expect(__DEV__).toBe(true); - }); -}); From f23b1a948298d9812f824a6590eef1a7c856bcf5 Mon Sep 17 00:00:00 2001 From: Devan Buggay Date: Mon, 28 Jul 2025 23:59:59 -0700 Subject: [PATCH 3/3] Simplify mode printout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Simplifies mode printout for aligned sub-modes. View-benchmark (native 🚀, js 🚀, bytecode) to View-benchmark (mode 🚀) Changelog: [Internal] Differential Revision: D79151698 --- .../runner/formatFantomConfig.js | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/private/react-native-fantom/runner/formatFantomConfig.js b/private/react-native-fantom/runner/formatFantomConfig.js index c4c2199cae89..bee8136fd61a 100644 --- a/private/react-native-fantom/runner/formatFantomConfig.js +++ b/private/react-native-fantom/runner/formatFantomConfig.js @@ -11,10 +11,43 @@ import type {FeatureFlagValue} from '../../../packages/react-native/scripts/featureflags/types'; import type {FantomTestConfig} from '../runner/getFantomTestConfigs'; import type {HermesVariant} from '../runner/utils'; +import type {PartialFantomTestConfig} from './getFantomTestConfigs'; import {FantomTestConfigHermesVariant} from '../runner/getFantomTestConfigs'; import {getOverrides} from './getFantomTestConfigs'; +function formatModes(overrides: PartialFantomTestConfig) { + const parts = []; + + if ( + overrides.isNativeOptimized === false && + overrides.isJsOptimized === false && + overrides.isJsBytecode === false + ) { + return ['mode 🐛']; + } else if ( + overrides.isNativeOptimized === true && + overrides.isJsOptimized === true && + overrides.isJsBytecode === true + ) { + return ['mode 🚀']; + } + + if (overrides.isNativeOptimized != null) { + parts.push(overrides.isNativeOptimized ? 'native 🚀' : 'native 🐛'); + } + + if (overrides.isJsOptimized != null) { + parts.push(overrides.isJsOptimized ? 'js 🚀' : 'js 🐛'); + } + + if (overrides.isJsBytecode != null && overrides.isJsBytecode) { + parts.push('bytecode'); + } + + return parts; +} + function formatFantomHermesVariant(hermesVariant: HermesVariant): string { switch (hermesVariant) { case FantomTestConfigHermesVariant.Hermes: @@ -41,17 +74,7 @@ export default function formatFantomConfig(config: FantomTestConfig): string { const overrides = getOverrides(config); const parts = []; - if (overrides.isNativeOptimized != null) { - parts.push(overrides.isNativeOptimized ? 'native 🚀' : 'native 🐛'); - } - - if (overrides.isJsOptimized != null) { - parts.push(overrides.isJsOptimized ? 'js 🚀' : 'js 🐛'); - } - - if (overrides.isJsBytecode != null && overrides.isJsBytecode) { - parts.push('bytecode'); - } + parts.push(...formatModes(overrides)); if (overrides.hermesVariant) { parts.push(formatFantomHermesVariant(overrides.hermesVariant));