diff --git a/packages/react-native/Libraries/Animated/shouldUseTurboAnimatedModule.js b/packages/react-native/Libraries/Animated/shouldUseTurboAnimatedModule.js index 2758cc734699..2373291131e2 100644 --- a/packages/react-native/Libraries/Animated/shouldUseTurboAnimatedModule.js +++ b/packages/react-native/Libraries/Animated/shouldUseTurboAnimatedModule.js @@ -9,13 +9,14 @@ */ import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags'; +import {isBridgeless} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import Platform from '../Utilities/Platform'; function shouldUseTurboAnimatedModule(): boolean { if (ReactNativeFeatureFlags.cxxNativeAnimatedEnabled()) { return false; } else { - return Platform.OS === 'ios' && global.RN$Bridgeless === true; + return Platform.OS === 'ios' && isBridgeless; } } diff --git a/packages/react-native/Libraries/BatchedBridge/MessageQueue.js b/packages/react-native/Libraries/BatchedBridge/MessageQueue.js index b177b30c1c1d..e5c5bde5ccdc 100644 --- a/packages/react-native/Libraries/BatchedBridge/MessageQueue.js +++ b/packages/react-native/Libraries/BatchedBridge/MessageQueue.js @@ -11,6 +11,12 @@ 'use strict'; +const { + isBridgeless, + nativeCallSyncHook, + nativeFlushQueueImmediate, + nativeTraceBeginAsyncFlow, +} = require('../../src/private/runtime/ReactNativeRuntimeGlobals'); const Systrace = require('../Performance/Systrace'); const deepFreezeAndThrowOnMutationInDev = require('../Utilities/deepFreezeAndThrowOnMutationInDev').default; @@ -179,7 +185,7 @@ class MessageQueue { ): unknown { if (__DEV__) { invariant( - global.nativeCallSyncHook, + nativeCallSyncHook, 'Calling synchronous methods on native ' + 'modules is not supported in Chrome.\n\n Consider providing alternative ' + 'methods to expose this method in debug mode, e.g. by exposing constants ' + @@ -187,7 +193,7 @@ class MessageQueue { ); } this.processCallbacks(moduleID, methodID, params, onFail, onSucc); - return global.nativeCallSyncHook(moduleID, methodID, params); + return nativeCallSyncHook?.(moduleID, methodID, params); } processCallbacks( @@ -231,12 +237,7 @@ class MessageQueue { this._failureCallbacks.set(this._callID, onFail); } if (__DEV__) { - global.nativeTraceBeginAsyncFlow && - global.nativeTraceBeginAsyncFlow( - TRACE_TAG_REACT, - 'native', - this._callID, - ); + nativeTraceBeginAsyncFlow?.(TRACE_TAG_REACT, 'native', this._callID); } this._callID++; } @@ -317,13 +318,13 @@ class MessageQueue { const now = Date.now(); if ( - global.nativeFlushQueueImmediate && + nativeFlushQueueImmediate != null && now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS ) { const queue = this._queue; this._queue = [[], [], [], this._callID]; this._lastFlush = now; - global.nativeFlushQueueImmediate(queue); + nativeFlushQueueImmediate(queue); } Systrace.counterEvent('pending_js_to_native_queue', this._queue[0].length); if (__DEV__ && this.__spy && isFinite(moduleID)) { @@ -422,8 +423,7 @@ class MessageQueue { const callableModuleNameList = callableModuleNames.join(', '); // TODO(T122225939): Remove after investigation: Why are we getting to this line in bridgeless mode? - const isBridgelessMode = - global.RN$Bridgeless === true ? 'true' : 'false'; + const isBridgelessMode = isBridgeless ? 'true' : 'false'; invariant( false, `Failed to call into JavaScript module method ${module}.${method}(). Module has not been registered as callable. Bridgeless Mode: ${isBridgelessMode}. Registered callable JavaScript modules (n = ${n}): ${callableModuleNameList}. diff --git a/packages/react-native/Libraries/BatchedBridge/NativeModules.js b/packages/react-native/Libraries/BatchedBridge/NativeModules.js index 95f1339d8ce9..47d170502b09 100644 --- a/packages/react-native/Libraries/BatchedBridge/NativeModules.js +++ b/packages/react-native/Libraries/BatchedBridge/NativeModules.js @@ -10,18 +10,18 @@ 'use strict'; +import type {ModuleConfig} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import type {ExtendedError} from '../Core/ExtendedError'; +const { + batchedBridgeConfig, + nativeModuleProxy, + nativeRequireModuleConfig, +} = require('../../src/private/runtime/ReactNativeRuntimeGlobals'); const BatchedBridge = require('./BatchedBridge').default; const invariant = require('invariant'); -export type ModuleConfig = [ - string /* name */, - ?{...} /* constants */, - ?ReadonlyArray /* functions */, - ?ReadonlyArray /* promise method IDs */, - ?ReadonlyArray /* sync method IDs */, -]; +export type {ModuleConfig} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; export type MethodType = 'async' | 'promise' | 'sync'; @@ -88,10 +88,10 @@ global.__fbGenNativeModule = genModule; function loadModule(name: string, moduleID: number): ?{...} { invariant( - global.nativeRequireModuleConfig, + nativeRequireModuleConfig, "Can't lazily create module without nativeRequireModuleConfig", ); - const config = global.nativeRequireModuleConfig(name); + const config = nativeRequireModuleConfig(name); const info = genModule(config, moduleID); return info && info.module; } @@ -182,18 +182,17 @@ function updateErrorWithErrorData( /* $FlowFixMe[unclear-type] unclear type of NativeModules */ let NativeModules: {[moduleName: string]: any, ...} = {}; -if (global.nativeModuleProxy) { - NativeModules = global.nativeModuleProxy; +if (nativeModuleProxy) { + NativeModules = nativeModuleProxy; } else { - const bridgeConfig = global.__fbBatchedBridgeConfig; invariant( - bridgeConfig, + batchedBridgeConfig, '__fbBatchedBridgeConfig is not set, cannot invoke native modules', ); const defineLazyObjectProperty = require('../Utilities/defineLazyObjectProperty').default; - (bridgeConfig.remoteModuleConfig || []).forEach( + (batchedBridgeConfig.remoteModuleConfig || []).forEach( (config: ModuleConfig, moduleID: number) => { // Initially this config will only contain the module name when running in JSC. The actual // configuration of the module will be lazily loaded. diff --git a/packages/react-native/Libraries/BatchedBridge/__tests__/NativeModules-test.js b/packages/react-native/Libraries/BatchedBridge/__tests__/NativeModules-test.js index ce4f651655c5..5359af48d5f6 100644 --- a/packages/react-native/Libraries/BatchedBridge/__tests__/NativeModules-test.js +++ b/packages/react-native/Libraries/BatchedBridge/__tests__/NativeModules-test.js @@ -205,24 +205,32 @@ describe('MessageQueue', function () { }); describe('sync methods', () => { - afterEach(function () { - delete global.nativeCallSyncHook; - }); - it('throwing an exception', function () { - global.nativeCallSyncHook = jest.fn(() => { + const mockNativeCallSyncHook = jest.fn(() => { throw new Error('firstFailure'); }); + jest.resetModules(); + jest.doMock( + '../../../src/private/runtime/ReactNativeRuntimeGlobals', + () => ({ + ...jest.requireActual( + '../../../src/private/runtime/ReactNativeRuntimeGlobals', + ), + nativeCallSyncHook: mockNativeCallSyncHook, + }), + ); + NativeModules = require('../NativeModules').default; + let error; try { NativeModules.RemoteModule1.syncMethod('paloAlto', 'menloPark'); - } catch (e) { + } catch (e: mixed) { error = e; } - expect(global.nativeCallSyncHook).toBeCalledTimes(1); - expect(global.nativeCallSyncHook).toBeCalledWith( + expect(mockNativeCallSyncHook).toHaveBeenCalledTimes(1); + expect(mockNativeCallSyncHook).toHaveBeenCalledWith( 0, // `RemoteModule1` 3, // `syncMethod` ['paloAlto', 'menloPark'], @@ -234,14 +242,26 @@ describe('MessageQueue', function () { }); it('returning a value', function () { - global.nativeCallSyncHook = jest.fn(() => { + const mockNativeCallSyncHook = jest.fn(() => { return 'secondSucc'; }); + jest.resetModules(); + jest.doMock( + '../../../src/private/runtime/ReactNativeRuntimeGlobals', + () => ({ + ...jest.requireActual( + '../../../src/private/runtime/ReactNativeRuntimeGlobals', + ), + nativeCallSyncHook: mockNativeCallSyncHook, + }), + ); + NativeModules = require('../NativeModules').default; + const result = NativeModules.RemoteModule2.syncMethod('mac', 'windows'); - expect(global.nativeCallSyncHook).toBeCalledTimes(1); - expect(global.nativeCallSyncHook).toBeCalledWith( + expect(mockNativeCallSyncHook).toHaveBeenCalledTimes(1); + expect(mockNativeCallSyncHook).toHaveBeenCalledWith( 1, // `RemoteModule2` 3, // `syncMethod` ['mac', 'windows'], diff --git a/packages/react-native/Libraries/Blob/BlobManager.js b/packages/react-native/Libraries/Blob/BlobManager.js index f6b2b7bec7a1..8b8285651b97 100644 --- a/packages/react-native/Libraries/Blob/BlobManager.js +++ b/packages/react-native/Libraries/Blob/BlobManager.js @@ -11,6 +11,7 @@ import typeof BlobT from './Blob'; import type {BlobCollector, BlobData, BlobOptions} from './BlobTypes'; +import {blobCollectorProvider} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import NativeBlobModule from './NativeBlobModule'; import invariant from 'invariant'; @@ -40,10 +41,10 @@ function uuidv4(): string { // that the current bridge infra doesn't allow to track js objects // deallocation. Ideally the whole Blob object should be a jsi::HostObject. function createBlobCollector(blobId: string): BlobCollector | null { - if (global.__blobCollectorProvider == null) { + if (blobCollectorProvider == null) { return null; } else { - return global.__blobCollectorProvider(blobId); + return blobCollectorProvider(blobId) ?? null; } } diff --git a/packages/react-native/Libraries/Core/ExceptionsManager.js b/packages/react-native/Libraries/Core/ExceptionsManager.js index 3efa552449ed..5c55736d07ad 100644 --- a/packages/react-native/Libraries/Core/ExceptionsManager.js +++ b/packages/react-native/Libraries/Core/ExceptionsManager.js @@ -13,6 +13,13 @@ import type {ExtendedError} from './ExtendedError'; import type {ExceptionData} from './NativeExceptionsManager'; +import { + handleException as nativeHandleException, + hasHandledFatalException as nativeHasHandledFatalException, + inExceptionHandler as nativeInExceptionHandler, + notifyOfFatalException as nativeNotifyOfFatalException, +} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; + export class SyntheticError extends Error { name: string = ''; } @@ -124,10 +131,10 @@ function reportException( require('./NativeExceptionsManager').default; if (NativeExceptionsManager) { if (isFatal) { - if (global.RN$hasHandledFatalException?.()) { + if (nativeHasHandledFatalException?.()) { return; } - global.RN$notifyOfFatalException?.(); + nativeNotifyOfFatalException?.(); } NativeExceptionsManager.reportException(data); } @@ -152,8 +159,8 @@ function handleException(e: unknown, isFatal: boolean) { // TODO(T196834299): We should really use a c++ turbomodule for this const reportToConsole = true; if ( - !global.RN$handleException || - !global.RN$handleException(e, isFatal, reportToConsole) + !nativeHandleException || + !nativeHandleException(e, isFatal, reportToConsole) ) { let error: Error; if (e instanceof Error) { @@ -185,7 +192,7 @@ function reactConsoleErrorHandler(...args) { if (!console.reportErrorsAsExceptions) { return; } - if (inExceptionHandler || global.RN$inExceptionHandler?.()) { + if (inExceptionHandler || nativeInExceptionHandler?.()) { // The fundamental trick here is that are multiple entry point to logging errors: // (see D19743075 for more background) // @@ -236,8 +243,8 @@ function reactConsoleErrorHandler(...args) { const isFatal = false; const reportToConsole = false; if ( - !global.RN$handleException || - !global.RN$handleException(error, isFatal, reportToConsole) + !nativeHandleException || + !nativeHandleException(error, isFatal, reportToConsole) ) { if (__DEV__) { // If we're not reporting to the console in reportException, diff --git a/packages/react-native/Libraries/Core/registerCallableModule.js b/packages/react-native/Libraries/Core/registerCallableModule.js index 0a0951c40f07..71e538fbfcbc 100644 --- a/packages/react-native/Libraries/Core/registerCallableModule.js +++ b/packages/react-native/Libraries/Core/registerCallableModule.js @@ -10,6 +10,11 @@ 'use strict'; +import { + isBridgeless, + registerCallableModule as nativeRegisterCallableModule, +} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; + type Module = {...}; type RegisterCallableModule = ( name: string, @@ -17,14 +22,14 @@ type RegisterCallableModule = ( ) => void; const registerCallableModule: RegisterCallableModule = (function () { - if (global.RN$Bridgeless === true) { + if (isBridgeless) { return (name, moduleOrFactory) => { if (typeof moduleOrFactory === 'function') { - global.RN$registerCallableModule(name, moduleOrFactory); + nativeRegisterCallableModule?.(name, moduleOrFactory); return; } - global.RN$registerCallableModule(name, () => moduleOrFactory); + nativeRegisterCallableModule?.(name, () => moduleOrFactory); }; } diff --git a/packages/react-native/Libraries/Core/setUpBatchedBridge.js b/packages/react-native/Libraries/Core/setUpBatchedBridge.js index 0d36a70786d4..37c0ea6c36f3 100644 --- a/packages/react-native/Libraries/Core/setUpBatchedBridge.js +++ b/packages/react-native/Libraries/Core/setUpBatchedBridge.js @@ -10,10 +10,11 @@ 'use strict'; +import {isBridgeless} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import registerModule from './registerCallableModule'; registerModule('Systrace', () => require('../Performance/Systrace')); -if (!(global.RN$Bridgeless === true)) { +if (!isBridgeless) { registerModule('JSTimers', () => require('./Timers/JSTimers').default); } registerModule('RCTLog', () => require('../Utilities/RCTLog').default); diff --git a/packages/react-native/Libraries/Core/setUpErrorHandling.js b/packages/react-native/Libraries/Core/setUpErrorHandling.js index 357c12e3bf79..5ec53a905a26 100644 --- a/packages/react-native/Libraries/Core/setUpErrorHandling.js +++ b/packages/react-native/Libraries/Core/setUpErrorHandling.js @@ -10,7 +10,12 @@ 'use strict'; -if (global.RN$useAlwaysAvailableJSErrorHandling !== true) { +import { + disableExceptionsManager, + useAlwaysAvailableJSErrorHandling, +} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; + +if (!useAlwaysAvailableJSErrorHandling) { /** * Sets up the console and exception handling (redbox) for React Native. * You can use this module directly, or just require InitializeCore. @@ -20,7 +25,7 @@ if (global.RN$useAlwaysAvailableJSErrorHandling !== true) { ExceptionsManager.installConsoleErrorReporter(); // Set up error handler - if (!global.__fbDisableExceptionsManager) { + if (!disableExceptionsManager) { const handleError = (e: unknown, isFatal: boolean) => { try { ExceptionsManager.handleException(e, isFatal); diff --git a/packages/react-native/Libraries/Core/setUpPerformance.js b/packages/react-native/Libraries/Core/setUpPerformance.js index 3f91689eabbf..b2c5ae5e8de3 100644 --- a/packages/react-native/Libraries/Core/setUpPerformance.js +++ b/packages/react-native/Libraries/Core/setUpPerformance.js @@ -8,6 +8,7 @@ * @format */ +import {nativePerformanceNow} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import setUpPerformanceModern from '../../src/private/setup/setUpPerformanceModern'; import NativePerformance from '../../src/private/webapis/performance/specs/NativePerformance'; @@ -24,7 +25,7 @@ if (NativePerformance) { measure: () => {}, clearMeasures: () => {}, now: () => { - const performanceNow = global.nativePerformanceNow || Date.now; + const performanceNow = nativePerformanceNow || Date.now; return performanceNow(); }, }; diff --git a/packages/react-native/Libraries/Core/setUpReactDevTools.js b/packages/react-native/Libraries/Core/setUpReactDevTools.js index 9bdf66b30184..d63946cc7cde 100644 --- a/packages/react-native/Libraries/Core/setUpReactDevTools.js +++ b/packages/react-native/Libraries/Core/setUpReactDevTools.js @@ -14,7 +14,7 @@ import type {Domain} from '../../src/private/devsupport/rndevtools/setUpFuseboxR import type {Spec as NativeReactDevToolsRuntimeSettingsModuleSpec} from '../../src/private/devsupport/rndevtools/specs/NativeReactDevToolsRuntimeSettingsModule'; if (__DEV__) { - if (typeof global.queueMicrotask !== 'function') { + if (typeof queueMicrotask !== 'function') { console.error( 'queueMicrotask should exist before setting up React DevTools.', ); diff --git a/packages/react-native/Libraries/Core/setUpTimers.js b/packages/react-native/Libraries/Core/setUpTimers.js index db8baa3e0587..22fb36f6fc56 100644 --- a/packages/react-native/Libraries/Core/setUpTimers.js +++ b/packages/react-native/Libraries/Core/setUpTimers.js @@ -10,16 +10,18 @@ 'use strict'; +import {isBridgeless} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; + const {polyfillGlobal} = require('../Utilities/PolyfillFunctions'); if (__DEV__) { - if (typeof global.Promise !== 'function') { + if (typeof Promise === 'undefined') { console.error('Promise should exist before setting up timers.'); } } // In bridgeless mode, timers are host functions installed from cpp. -if (global.RN$Bridgeless === true) { +if (isBridgeless) { // This is the flag that tells React to use `queueMicrotask` to batch state // updates, instead of using the scheduler to schedule a regular task. // We use a global variable because we don't currently have any other diff --git a/packages/react-native/Libraries/LogBox/LogBox.js b/packages/react-native/Libraries/LogBox/LogBox.js index 1109e48b2ff5..8c79423fde2e 100644 --- a/packages/react-native/Libraries/LogBox/LogBox.js +++ b/packages/react-native/Libraries/LogBox/LogBox.js @@ -11,6 +11,10 @@ import type {IgnorePattern, LogData} from './Data/LogBoxData'; import type {ExtendedExceptionData} from './Data/parseLogBoxLog'; +import { + isRuntimeReady, + registerExceptionListener, +} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import toExtendedError from '../../src/private/utilities/toExtendedError'; import Platform from '../Utilities/Platform'; import RCTLog from '../Utilities/RCTLog'; @@ -55,10 +59,10 @@ if (__DEV__) { isLogBoxInstalled = true; - if (global.RN$registerExceptionListener != null) { - global.RN$registerExceptionListener( + if (registerExceptionListener != null) { + registerExceptionListener( (error: ExtendedExceptionData & {preventDefault: () => unknown}) => { - if (global.RN$isRuntimeReady?.() || !error.isFatal) { + if (isRuntimeReady?.() || !error.isFatal) { error.preventDefault(); addException(error); } diff --git a/packages/react-native/Libraries/NativeComponent/NativeComponentRegistry.js b/packages/react-native/Libraries/NativeComponent/NativeComponentRegistry.js index ba81528445bf..b9040f78a8c6 100644 --- a/packages/react-native/Libraries/NativeComponent/NativeComponentRegistry.js +++ b/packages/react-native/Libraries/NativeComponent/NativeComponentRegistry.js @@ -14,6 +14,7 @@ import type { ViewConfig, } from '../Renderer/shims/ReactNativeTypes'; +import {isBridgeless} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import getNativeComponentAttributes from '../ReactNative/getNativeComponentAttributes'; import UIManager from '../ReactNative/UIManager'; import * as ReactNativeViewConfigRegistry from '../Renderer/shims/ReactNativeViewConfigRegistry'; @@ -54,7 +55,7 @@ export function get( ): HostComponent { ReactNativeViewConfigRegistry.register(name, () => { const {native, verify} = getRuntimeConfig?.(name) ?? { - native: !global.RN$Bridgeless, + native: !isBridgeless, verify: false, }; diff --git a/packages/react-native/Libraries/NativeComponent/NativeComponentRegistryUnstable.js b/packages/react-native/Libraries/NativeComponent/NativeComponentRegistryUnstable.js index 183076b80dde..69b757e367e1 100644 --- a/packages/react-native/Libraries/NativeComponent/NativeComponentRegistryUnstable.js +++ b/packages/react-native/Libraries/NativeComponent/NativeComponentRegistryUnstable.js @@ -8,6 +8,8 @@ * @format */ +import {nativeComponentRegistryHasComponent} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; + let componentNameToExists: Map = new Map(); /** @@ -17,16 +19,16 @@ let componentNameToExists: Map = new Map(); * is registered in the native platform. */ export function unstable_hasComponent(name: string): boolean { - let hasNativeComponent = componentNameToExists.get(name); - if (hasNativeComponent == null) { - if (global.__nativeComponentRegistry__hasComponent) { - hasNativeComponent = global.__nativeComponentRegistry__hasComponent(name); - componentNameToExists.set(name, hasNativeComponent); + let hasComponent = componentNameToExists.get(name); + if (hasComponent == null) { + if (nativeComponentRegistryHasComponent != null) { + hasComponent = nativeComponentRegistryHasComponent(name); + componentNameToExists.set(name, hasComponent); } else { throw new Error( `unstable_hasComponent('${name}'): Global function is not registered`, ); } } - return hasNativeComponent; + return hasComponent; } diff --git a/packages/react-native/Libraries/Performance/Systrace.js b/packages/react-native/Libraries/Performance/Systrace.js index 4e1be11ce6f5..b30818e8771b 100644 --- a/packages/react-native/Libraries/Performance/Systrace.js +++ b/packages/react-native/Libraries/Performance/Systrace.js @@ -10,6 +10,16 @@ import typeof * as SystraceModule from './Systrace'; +import { + isProfilingEnabled, + nativeTraceBeginAsyncSection, + nativeTraceBeginSection, + nativeTraceCounter, + nativeTraceEndAsyncSection, + nativeTraceEndSection, + nativeTraceIsTracing, +} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; + const TRACE_TAG_REACT = 1 << 13; // eslint-disable-line no-bitwise let _asyncCookie = 0; @@ -31,9 +41,9 @@ type EventArgs = ?{[string]: string}; * } */ export function isEnabled(): boolean { - return global.nativeTraceIsTracing - ? global.nativeTraceIsTracing(TRACE_TAG_REACT) - : Boolean(global.__RCTProfileIsProfiling); + return nativeTraceIsTracing != null + ? nativeTraceIsTracing(TRACE_TAG_REACT) + : isProfilingEnabled; } /** @@ -52,7 +62,7 @@ export function beginEvent(eventName: EventName, args?: EventArgs): void { if (isEnabled()) { const eventNameString = typeof eventName === 'function' ? eventName() : eventName; - global.nativeTraceBeginSection(TRACE_TAG_REACT, eventNameString, args); + nativeTraceBeginSection?.(TRACE_TAG_REACT, eventNameString, args); } } @@ -61,7 +71,7 @@ export function beginEvent(eventName: EventName, args?: EventArgs): void { */ export function endEvent(args?: EventArgs): void { if (isEnabled()) { - global.nativeTraceEndSection(TRACE_TAG_REACT, args); + nativeTraceEndSection?.(TRACE_TAG_REACT, args); } } @@ -79,7 +89,7 @@ export function beginAsyncEvent( _asyncCookie++; const eventNameString = typeof eventName === 'function' ? eventName() : eventName; - global.nativeTraceBeginAsyncSection( + nativeTraceBeginAsyncSection?.( TRACE_TAG_REACT, eventNameString, cookie, @@ -101,7 +111,7 @@ export function endAsyncEvent( if (isEnabled()) { const eventNameString = typeof eventName === 'function' ? eventName() : eventName; - global.nativeTraceEndAsyncSection( + nativeTraceEndAsyncSection?.( TRACE_TAG_REACT, eventNameString, cookie, @@ -117,8 +127,7 @@ export function counterEvent(eventName: EventName, value: number): void { if (isEnabled()) { const eventNameString = typeof eventName === 'function' ? eventName() : eventName; - global.nativeTraceCounter && - global.nativeTraceCounter(TRACE_TAG_REACT, eventNameString, value); + nativeTraceCounter?.(TRACE_TAG_REACT, eventNameString, value); } } diff --git a/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js b/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js index 0db6d3415496..e96654faed51 100644 --- a/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js +++ b/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js @@ -13,6 +13,11 @@ import type {RootTag} from '../Types/RootTagTypes'; import type {UIManagerJSInterface} from '../Types/UIManagerJSInterface'; +import { + UIManager_getConstants, + UIManager_getConstantsForViewManager, + UIManager_getDefaultEventTypes, +} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import {unstable_hasComponent} from '../NativeComponent/NativeComponentRegistryUnstable'; import defineLazyObjectProperty from '../Utilities/defineLazyObjectProperty'; import Platform from '../Utilities/Platform'; @@ -26,33 +31,24 @@ function raiseSoftError(methodName: string, details?: string): void { ); } -const getUIManagerConstants: ?() => {[viewManagerName: string]: Object} = - global.RN$LegacyInterop_UIManager_getConstants; - const getUIManagerConstantsCached = (function () { let wasCalledOnce = false; let result: {[viewManagerName: string]: Object} = {}; return (): {[viewManagerName: string]: Object} => { if (!wasCalledOnce) { - result = nullthrows(getUIManagerConstants)(); + result = nullthrows(UIManager_getConstants)(); wasCalledOnce = true; } return result; }; })(); -const getConstantsForViewManager: ?(viewManagerName: string) => ?Object = - global.RN$LegacyInterop_UIManager_getConstantsForViewManager; - -const getDefaultEventTypes: ?() => Object = - global.RN$LegacyInterop_UIManager_getDefaultEventTypes; - const getDefaultEventTypesCached = (function () { let wasCalledOnce = false; let result = null; return (): Object => { if (!wasCalledOnce) { - result = nullthrows(getDefaultEventTypes)(); + result = nullthrows(UIManager_getDefaultEventTypes)(); wasCalledOnce = true; } return result; @@ -166,15 +162,15 @@ const UIManagerJSDeprecatedPlatformAPIs = Platform.select({ const UIManagerJSPlatformAPIs = Platform.select({ android: { getConstantsForViewManager: (viewManagerName: string): ?Object => { - if (getConstantsForViewManager) { - return getConstantsForViewManager(viewManagerName); + if (UIManager_getConstantsForViewManager) { + return UIManager_getConstantsForViewManager(viewManagerName); } raiseSoftError('getConstantsForViewManager'); return {}; }, getDefaultEventTypes: (): Array => { - if (getDefaultEventTypes) { + if (UIManager_getDefaultEventTypes) { return getDefaultEventTypesCached(); } @@ -266,7 +262,7 @@ const UIManagerJS: UIManagerJSInterface & {[string]: any} = { ...UIManagerJSPlatformAPIs, ...UIManagerJSUnusedInNewArchAPIs, getViewManagerConfig: (viewManagerName: string): unknown => { - if (getUIManagerConstants) { + if (UIManager_getConstants) { const constants = getUIManagerConstantsCached(); if ( !constants[viewManagerName] && @@ -288,7 +284,7 @@ const UIManagerJS: UIManagerJSInterface & {[string]: any} = { return unstable_hasComponent(viewManagerName); }, getConstants: (): Object => { - if (getUIManagerConstants) { + if (UIManager_getConstants) { return getUIManagerConstantsCached(); } else { raiseSoftError('getConstants'); @@ -396,7 +392,7 @@ const UIManagerJS: UIManagerJSInterface & {[string]: any} = { }, }; -if (getUIManagerConstants) { +if (UIManager_getConstants) { Object.keys(getUIManagerConstantsCached()).forEach(viewConfigName => { UIManagerJS[viewConfigName] = getUIManagerConstantsCached()[viewConfigName]; }); diff --git a/packages/react-native/Libraries/ReactNative/PaperUIManager.js b/packages/react-native/Libraries/ReactNative/PaperUIManager.js index fadf460022fd..bd2df8cdc019 100644 --- a/packages/react-native/Libraries/ReactNative/PaperUIManager.js +++ b/packages/react-native/Libraries/ReactNative/PaperUIManager.js @@ -11,6 +11,7 @@ import type {RootTag} from '../Types/RootTagTypes'; import type {UIManagerJSInterface} from '../Types/UIManagerJSInterface'; +import {nativeCallSyncHook} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import NativeUIManager from './NativeUIManager'; import nullthrows from 'nullthrows'; @@ -60,7 +61,7 @@ function getViewManagerConfig(viewManagerName: string): any { // If we're in the Chrome Debugger, let's not even try calling the sync // method. - if (!global.nativeCallSyncHook) { + if (!nativeCallSyncHook) { return config; } @@ -168,7 +169,7 @@ if (Platform.OS === 'ios') { }); } -if (!global.nativeCallSyncHook) { +if (!nativeCallSyncHook) { Object.keys(getConstants()).forEach(viewManagerName => { if (!UIManagerProperties.includes(viewManagerName)) { if (!viewManagerConfigs[viewManagerName]) { diff --git a/packages/react-native/Libraries/ReactNative/ReactNativeRuntimeDiagnostics.js b/packages/react-native/Libraries/ReactNative/ReactNativeRuntimeDiagnostics.js index f28305e2e819..11336a3f5e73 100644 --- a/packages/react-native/Libraries/ReactNative/ReactNativeRuntimeDiagnostics.js +++ b/packages/react-native/Libraries/ReactNative/ReactNativeRuntimeDiagnostics.js @@ -10,6 +10,8 @@ 'use strict'; +import {diagnosticFlags} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; + /** * Perform a set of runtime diagnostics, usually useful for test purpose. * This is only meaningful for __DEV__ mode. @@ -33,8 +35,8 @@ let isEnabled = false; let shouldEnableAll = false; if (__DEV__) { - if (typeof global.RN$DiagnosticFlags === 'string') { - global.RN$DiagnosticFlags.split(',').forEach(flag => { + if (typeof diagnosticFlags === 'string') { + diagnosticFlags.split(',').forEach(flag => { switch (flag) { case 'early_js_errors': case 'all': diff --git a/packages/react-native/Libraries/ReactNative/RendererImplementation.js b/packages/react-native/Libraries/ReactNative/RendererImplementation.js index 5874de821cf1..bcc9b3945d49 100644 --- a/packages/react-native/Libraries/ReactNative/RendererImplementation.js +++ b/packages/react-native/Libraries/ReactNative/RendererImplementation.js @@ -18,6 +18,7 @@ import { onRecoverableError, onUncaughtError, } from '../../src/private/renderer/errorhandling/ErrorHandlers'; +import {isBridgeless} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import * as React from 'react'; let cachedFabricRenderer; @@ -116,7 +117,7 @@ export function dispatchCommand( command: string, args: Array, ): void { - if (global.RN$Bridgeless === true) { + if (isBridgeless) { // Note: this function has the same implementation in the legacy and new renderer. // However, evaluating the old renderer comes with some side effects. if (cachedFabricDispatchCommand == null) { diff --git a/packages/react-native/Libraries/ReactNative/UIManager.js b/packages/react-native/Libraries/ReactNative/UIManager.js index 3d2729530286..a8707d1b801d 100644 --- a/packages/react-native/Libraries/ReactNative/UIManager.js +++ b/packages/react-native/Libraries/ReactNative/UIManager.js @@ -10,6 +10,7 @@ import type {UIManagerJSInterface} from '../Types/UIManagerJSInterface'; +import {isBridgeless} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import {getFabricUIManager} from './FabricUIManager'; import nullthrows from 'nullthrows'; @@ -18,10 +19,9 @@ function isFabricReactTag(reactTag: number): boolean { return reactTag % 2 === 0; } -const UIManagerImpl: UIManagerJSInterface = - global.RN$Bridgeless === true - ? require('./BridgelessUIManager').default - : require('./PaperUIManager').default; +const UIManagerImpl: UIManagerJSInterface = isBridgeless + ? require('./BridgelessUIManager').default + : require('./PaperUIManager').default; // $FlowFixMe[cannot-spread-interface] const UIManager: UIManagerJSInterface = { diff --git a/packages/react-native/Libraries/Text/TextNativeComponent.js b/packages/react-native/Libraries/Text/TextNativeComponent.js index 020d5ceea8b6..ff34f3a59755 100644 --- a/packages/react-native/Libraries/Text/TextNativeComponent.js +++ b/packages/react-native/Libraries/Text/TextNativeComponent.js @@ -13,6 +13,7 @@ import type {ProcessedColorValue} from '../StyleSheet/processColor'; import type {GestureResponderEvent} from '../Types/CoreEventTypes'; import type {TextProps} from './TextProps'; +import {isBridgeless} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import {createViewConfig} from '../NativeComponent/ViewConfig'; import UIManager from '../ReactNative/UIManager'; import createReactNativeComponentClass from '../Renderer/shims/createReactNativeComponentClass'; @@ -83,7 +84,7 @@ export const NativeText: HostComponent = ): any); export const NativeVirtualText: HostComponent = - !global.RN$Bridgeless && !UIManager.hasViewManagerConfig('RCTVirtualText') + !isBridgeless && !UIManager.hasViewManagerConfig('RCTVirtualText') ? NativeText : (createReactNativeComponentClass('RCTVirtualText', () => /* $FlowFixMe[incompatible-type] Natural Inference rollout. See diff --git a/packages/react-native/Libraries/TurboModule/TurboModuleRegistry.js b/packages/react-native/Libraries/TurboModule/TurboModuleRegistry.js index 719603df82dc..b06161696b84 100644 --- a/packages/react-native/Libraries/TurboModule/TurboModuleRegistry.js +++ b/packages/react-native/Libraries/TurboModule/TurboModuleRegistry.js @@ -10,12 +10,11 @@ import type {TurboModule} from './RCTExport'; +import {turboModuleProxy} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import invariant from 'invariant'; const NativeModules = require('../BatchedBridge/NativeModules').default; -const turboModuleProxy = global.__turboModuleProxy; - function requireModule(name: string): ?T { if (turboModuleProxy != null) { const module: ?T = turboModuleProxy(name); diff --git a/packages/react-native/Libraries/Utilities/RCTLog.js b/packages/react-native/Libraries/Utilities/RCTLog.js index 8692aa979184..9e58f5525cd4 100644 --- a/packages/react-native/Libraries/Utilities/RCTLog.js +++ b/packages/react-native/Libraries/Utilities/RCTLog.js @@ -10,7 +10,8 @@ 'use strict'; -const invariant = require('invariant'); +import {nativeLoggingHook} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; +import invariant from 'invariant'; const levelsMap = { log: 'log', @@ -26,7 +27,7 @@ const RCTLog = { // level one of log, info, warn, error, mustfix logIfNoNativeHook(level: string, ...args: Array): void { // We already printed in the native console, so only log here if using a js debugger - if (typeof global.nativeLoggingHook === 'undefined') { + if (nativeLoggingHook == null) { RCTLog.logToConsole(level, ...args); } else { // Report native warnings to LogBox diff --git a/packages/react-native/Libraries/Utilities/__tests__/codegenNativeComponent-test.js b/packages/react-native/Libraries/Utilities/__tests__/codegenNativeComponent-test.js index da098b463c6e..d48e51e16d5b 100644 --- a/packages/react-native/Libraries/Utilities/__tests__/codegenNativeComponent-test.js +++ b/packages/react-native/Libraries/Utilities/__tests__/codegenNativeComponent-test.js @@ -10,9 +10,6 @@ 'use strict'; -const UIManager = require('../../ReactNative/UIManager').default; -const codegenNativeComponent = require('../codegenNativeComponent').default; - // We need to unmock requireNativeComponent since it's under test. // Instead, we mock the function it calls, createReactNativeComponentClass, // so that we don't run into issues populating the registry with the same @@ -22,72 +19,108 @@ jest.mock('../../Renderer/shims/createReactNativeComponentClass', () => ({ __esModule: true, default: componentName => componentName, })); -jest - .spyOn(UIManager, 'hasViewManagerConfig') - .mockImplementation(componentName => - componentName.includes('ComponentNameDoesNotExist') ? false : true, - ); + +let codegenNativeComponent; describe('codegenNativeComponent', () => { - beforeEach(() => { - jest.restoreAllMocks(); - // $FlowExpectedError[cannot-write] - global.RN$Bridgeless = false; - jest.spyOn(console, 'warn').mockImplementation(() => {}); - }); + describe('bridge mode', () => { + beforeEach(() => { + jest.resetModules(); + jest.doMock( + '../../../src/private/runtime/ReactNativeRuntimeGlobals', + () => ({ + ...jest.requireActual( + '../../../src/private/runtime/ReactNativeRuntimeGlobals', + ), + isBridgeless: false, + }), + ); - it('should require component as is ', () => { - const component = codegenNativeComponent<$FlowFixMe>('ComponentName'); - expect(component).toBe('ComponentName'); - }); + const UIManager = require('../../ReactNative/UIManager').default; + jest + .spyOn(UIManager, 'hasViewManagerConfig') + .mockImplementation(componentName => + componentName.includes('ComponentNameDoesNotExist') ? false : true, + ); - it('should require paperComponentName', () => { - const component = codegenNativeComponent<$FlowFixMe>('ComponentName', { - paperComponentName: 'PaperComponentName', + codegenNativeComponent = require('../codegenNativeComponent').default; + jest.spyOn(console, 'warn').mockImplementation(() => {}); }); - expect(component).toBe('PaperComponentName'); - }); - it('should fall back to requiring the deprecated paper component name', () => { - const component = codegenNativeComponent<$FlowFixMe>( - 'ComponentNameDoesNotExist', - { - paperComponentNameDeprecated: 'ComponentName', - }, - ); - expect(component).toBe('ComponentName'); - }); + it('should require component as is ', () => { + const component = codegenNativeComponent<$FlowFixMe>('ComponentName'); + expect(component).toBe('ComponentName'); + }); - it('should require the new component name', () => { - const component = codegenNativeComponent<$FlowFixMe>('ComponentName', { - paperComponentNameDeprecated: 'ComponentNameDoesNotExist', + it('should require paperComponentName', () => { + const component = codegenNativeComponent<$FlowFixMe>('ComponentName', { + paperComponentName: 'PaperComponentName', + }); + expect(component).toBe('PaperComponentName'); }); - expect(component).toBe('ComponentName'); - }); - it('should throw if neither component names exist', () => { - expect(() => - codegenNativeComponent<$FlowFixMe>('ComponentNameDoesNotExistOne', { - paperComponentNameDeprecated: 'ComponentNameDoesNotExistTwo', - }), - ).toThrow( - 'Failed to find native component for either ComponentNameDoesNotExistOne or ComponentNameDoesNotExistTwo', - ); - }); + it('should fall back to requiring the deprecated paper component name', () => { + const component = codegenNativeComponent<$FlowFixMe>( + 'ComponentNameDoesNotExist', + { + paperComponentNameDeprecated: 'ComponentName', + }, + ); + expect(component).toBe('ComponentName'); + }); - it('should NOT warn if called directly in BRIDGE mode', () => { - // $FlowExpectedError[cannot-write] - global.RN$Bridgeless = false; - codegenNativeComponent<$FlowFixMe>('ComponentName'); - expect(console.warn).not.toHaveBeenCalled(); + it('should require the new component name', () => { + const component = codegenNativeComponent<$FlowFixMe>('ComponentName', { + paperComponentNameDeprecated: 'ComponentNameDoesNotExist', + }); + expect(component).toBe('ComponentName'); + }); + + it('should throw if neither component names exist', () => { + expect(() => + codegenNativeComponent<$FlowFixMe>('ComponentNameDoesNotExistOne', { + paperComponentNameDeprecated: 'ComponentNameDoesNotExistTwo', + }), + ).toThrow( + 'Failed to find native component for either ComponentNameDoesNotExistOne or ComponentNameDoesNotExistTwo', + ); + }); + + it('should NOT warn if called directly in BRIDGE mode', () => { + codegenNativeComponent<$FlowFixMe>('ComponentName'); + expect(console.warn).not.toHaveBeenCalled(); + }); }); - it('should warn if called directly in BRIDGELESS mode', () => { - // $FlowExpectedError[cannot-write] - global.RN$Bridgeless = true; - codegenNativeComponent<$FlowFixMe>('ComponentName'); - expect(console.warn).toHaveBeenCalledWith( - `Codegen didn't run for ComponentName. This will be an error in the future. Make sure you are using @react-native/babel-preset when building your JavaScript code.`, - ); + describe('bridgeless mode', () => { + beforeEach(() => { + jest.resetModules(); + jest.doMock( + '../../../src/private/runtime/ReactNativeRuntimeGlobals', + () => ({ + ...jest.requireActual( + '../../../src/private/runtime/ReactNativeRuntimeGlobals', + ), + isBridgeless: true, + }), + ); + + const UIManager = require('../../ReactNative/UIManager').default; + jest + .spyOn(UIManager, 'hasViewManagerConfig') + .mockImplementation(componentName => + componentName.includes('ComponentNameDoesNotExist') ? false : true, + ); + + codegenNativeComponent = require('../codegenNativeComponent').default; + jest.spyOn(console, 'warn').mockImplementation(() => {}); + }); + + it('should warn if called directly in BRIDGELESS mode', () => { + codegenNativeComponent<$FlowFixMe>('ComponentName'); + expect(console.warn).toHaveBeenCalledWith( + `Codegen didn't run for ComponentName. This will be an error in the future. Make sure you are using @react-native/babel-preset when building your JavaScript code.`, + ); + }); }); }); diff --git a/packages/react-native/Libraries/Utilities/codegenNativeComponent.js b/packages/react-native/Libraries/Utilities/codegenNativeComponent.js index cf9b2eccbcf6..97903a8a1bee 100644 --- a/packages/react-native/Libraries/Utilities/codegenNativeComponent.js +++ b/packages/react-native/Libraries/Utilities/codegenNativeComponent.js @@ -13,6 +13,7 @@ import type {HostComponent} from '../../src/private/types/HostComponent'; import requireNativeComponent from '../../Libraries/ReactNative/requireNativeComponent'; +import {isBridgeless} from '../../src/private/runtime/ReactNativeRuntimeGlobals'; import UIManager from '../ReactNative/UIManager'; // TODO: import from CodegenSchema once workspaces are enabled @@ -35,7 +36,7 @@ function codegenNativeComponent( componentName: string, options?: NativeComponentOptions, ): NativeComponentType { - if (global.RN$Bridgeless === true && __DEV__) { + if (isBridgeless && __DEV__) { console.warn( `Codegen didn't run for ${componentName}. This will be an error in the future. Make sure you are using @react-native/babel-preset when building your JavaScript code.`, ); diff --git a/packages/react-native/flow/global.js b/packages/react-native/flow/global.js index dc6de666ec02..953632ae5836 100644 --- a/packages/react-native/flow/global.js +++ b/packages/react-native/flow/global.js @@ -77,7 +77,6 @@ declare var global: { // Internal-specific +__DEV__?: boolean, - +RN$Bridgeless?: boolean, // setupDOM +DOMRect: typeof DOMRect, diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlagsBase.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlagsBase.js index dcc7e6cc5a86..244ef89e3a82 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlagsBase.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlagsBase.js @@ -13,6 +13,10 @@ import type { ReactNativeFeatureFlagsJsOnlyOverrides, } from './ReactNativeFeatureFlags'; +import { + isBridgeless, + turboModuleProxy, +} from '../runtime/ReactNativeRuntimeGlobals'; import NativeReactNativeFeatureFlags from './specs/NativeReactNativeFeatureFlags'; const accessedFeatureFlags: Set = new Set(); @@ -106,8 +110,7 @@ export function setOverrides( } const reportedConfigNames: Set = new Set(); -const hasTurboModules = - global.RN$Bridgeless === true || global.__turboModuleProxy != null; +const hasTurboModules = isBridgeless || turboModuleProxy != null; function maybeLogUnavailableNativeModuleError(configName: string): void { if ( diff --git a/packages/react-native/src/private/runtime/ReactNativeRuntimeGlobals.js b/packages/react-native/src/private/runtime/ReactNativeRuntimeGlobals.js new file mode 100644 index 000000000000..00b2b21224be --- /dev/null +++ b/packages/react-native/src/private/runtime/ReactNativeRuntimeGlobals.js @@ -0,0 +1,356 @@ +/** + * 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. + * + * @flow strict + * @format + */ + +/** + * This module exports global variables that are defined by the native runtime. + * These are NOT assigned in JS files but are set directly by the native code. + */ + +'use strict'; + +import type {BlobCollector} from '../../../Libraries/Blob/BlobTypes'; +import type {ExtendedExceptionData} from '../../../Libraries/LogBox/Data/parseLogBoxLog'; + +// ============================================================================= +// Bridgeless Mode +// ============================================================================= + +/** + * Indicates if the app is running in bridgeless mode (new architecture). + * When true, communication with native happens through JSI directly + * rather than through the bridge. + * Set by the native runtime. + */ +export const isBridgeless: boolean = global.RN$Bridgeless === true; + +// ============================================================================= +// Runtime Diagnostics +// ============================================================================= + +/** + * Diagnostic flags for the React Native runtime. + * Can include flags like 'early_js_errors', 'all', etc. + * Set by the native runtime. + */ +export const diagnosticFlags: ?string = global.RN$DiagnosticFlags; + +// ============================================================================= +// Error Handling +// ============================================================================= + +/** + * Flag indicating whether to use always-available JS error handling. + * When true, error handling is enabled even in bridgeless mode. + * Set by the native runtime. + */ +export const useAlwaysAvailableJSErrorHandling: boolean = + global.RN$useAlwaysAvailableJSErrorHandling === true; + +/** + * Flag to disable the exceptions manager (redbox). + * Set by the native runtime. + */ +export const disableExceptionsManager: boolean = Boolean( + global.__fbDisableExceptionsManager, +); + +/** + * Handles exceptions in bridgeless mode. + * Returns true if the exception was handled and should not be propagated. + * Set by the native JSI runtime. + */ +export const handleException: ?( + error: mixed, + isFatal: boolean, + reportToConsole: boolean, +) => boolean = global.RN$handleException; + +/** + * Checks if the runtime is currently inside an exception handler. + * Only available in bridgeless mode. + * Set by the native JSI runtime. + */ +export const inExceptionHandler: ?() => boolean = global.RN$inExceptionHandler; + +/** + * Checks if a fatal exception has already been handled. + * Only available in bridgeless mode. + * Set by the native JSI runtime. + */ +export const hasHandledFatalException: ?() => boolean = + global.RN$hasHandledFatalException; + +/** + * Notifies the runtime that a fatal exception has occurred. + * Only available in bridgeless mode. + * Set by the native JSI runtime. + */ +export const notifyOfFatalException: ?() => void = + global.RN$notifyOfFatalException; + +// ============================================================================= +// Callable Modules +// ============================================================================= + +/** + * Registers a JavaScript module that can be called from native code. + * Only available in bridgeless mode. + * Set by the native JSI runtime. + */ +export const registerCallableModule: ?( + name: string, + moduleOrFactory: {...} | (() => {...}), +) => void = global.RN$registerCallableModule; + +// ============================================================================= +// UIManager (Bridgeless Mode) +// ============================================================================= + +/** + * Gets UIManager constants in bridgeless mode. + * Set by the native JSI runtime. + */ +export const UIManager_getConstants: ?() => {[viewManagerName: string]: {...}} = + global.RN$LegacyInterop_UIManager_getConstants; + +/** + * Gets constants for a specific view manager in bridgeless mode. + * Set by the native JSI runtime. + */ +export const UIManager_getConstantsForViewManager: ?( + viewManagerName: string, +) => ?{...} = global.RN$LegacyInterop_UIManager_getConstantsForViewManager; + +/** + * Gets default event types in bridgeless mode. + * Set by the native JSI runtime. + */ +export const UIManager_getDefaultEventTypes: ?() => {...} = + global.RN$LegacyInterop_UIManager_getDefaultEventTypes; + +// ============================================================================= +// Native Component Registry +// ============================================================================= + +/** + * Checks if a native component is registered. + * Set by the native JSI runtime. + */ +export const nativeComponentRegistryHasComponent: ?(name: string) => boolean = + global.__nativeComponentRegistry__hasComponent; + +// ============================================================================= +// Performance Tracing (Systrace) +// ============================================================================= + +/** + * Checks if profiling/tracing is currently enabled. + * Set by the native JSI runtime. + */ +export const nativeTraceIsTracing: ?(tag: number) => boolean = + global.nativeTraceIsTracing; + +/** + * Flag indicating if profiling is currently active. + * Set by the native runtime. + */ +export const isProfilingEnabled: boolean = Boolean( + global.__RCTProfileIsProfiling, +); + +/** + * Marks the beginning of a synchronous trace section. + * Set by the native JSI runtime. + */ +export const nativeTraceBeginSection: ?( + tag: number, + sectionName: string, + args?: ?{[string]: string}, +) => void = global.nativeTraceBeginSection; + +/** + * Marks the end of a synchronous trace section. + * Set by the native JSI runtime. + */ +export const nativeTraceEndSection: ?( + tag: number, + args?: ?{[string]: string}, +) => void = global.nativeTraceEndSection; + +/** + * Marks the beginning of an asynchronous trace section. + * Set by the native JSI runtime. + */ +export const nativeTraceBeginAsyncSection: ?( + tag: number, + sectionName: string, + cookie: number, + args?: ?{[string]: string}, +) => void = global.nativeTraceBeginAsyncSection; + +/** + * Marks the end of an asynchronous trace section. + * Set by the native JSI runtime. + */ +export const nativeTraceEndAsyncSection: ?( + tag: number, + sectionName: string, + cookie: number, + args?: ?{[string]: string}, +) => void = global.nativeTraceEndAsyncSection; + +/** + * Marks the beginning of an async flow for tracing. + * Set by the native JSI runtime. + */ +export const nativeTraceBeginAsyncFlow: ?( + tag: number, + sectionName: string, + cookie: number, +) => void = global.nativeTraceBeginAsyncFlow; + +/** + * Records a trace counter event. + * Set by the native JSI runtime. + */ +export const nativeTraceCounter: ?( + tag: number, + sectionName: string, + value: number, +) => void = global.nativeTraceCounter; + +// ============================================================================= +// Native Module Bridge (MessageQueue) +// ============================================================================= + +/** + * Synchronous hook for calling native modules. + * Not available when running in Chrome debugger. + * Set by the native JSI runtime. + */ +export const nativeCallSyncHook: ?( + moduleID: number, + methodID: number, + params: $ReadOnlyArray, +) => mixed = global.nativeCallSyncHook; + +/** + * Function to immediately flush the native call queue. + * Set by the native JSI runtime. + */ +export const nativeFlushQueueImmediate: ?( + queue: [Array, Array, Array, number], +) => void = global.nativeFlushQueueImmediate; + +// ============================================================================= +// Native Modules +// ============================================================================= + +/** + * Configuration for a native module. + * Tuple containing module name, constants, functions, promise method IDs, and sync method IDs. + */ +export type ModuleConfig = [ + string /* name */, + ?{...} /* constants */, + ?ReadonlyArray /* functions */, + ?ReadonlyArray /* promise method IDs */, + ?ReadonlyArray /* sync method IDs */, +]; + +/** + * The native module proxy for bridgeless mode. + * Provides access to native modules without the bridge. + * Set by the native JSI runtime. + */ +export const nativeModuleProxy: ?{[moduleName: string]: {...}, ...} = + global.nativeModuleProxy; + +/** + * Configuration for the batched bridge, containing module definitions. + * Used to lazily initialize native modules. + * Set by the native runtime. + */ +export const batchedBridgeConfig: ?{ + remoteModuleConfig?: $ReadOnlyArray, + ... +} = global.__fbBatchedBridgeConfig; + +/** + * Function to lazily require native module configuration. + * Set by the native JSI runtime. + */ +export const nativeRequireModuleConfig: ?(moduleName: string) => ModuleConfig = + global.nativeRequireModuleConfig; + +// ============================================================================= +// Performance +// ============================================================================= + +/** + * High-resolution performance timestamp function. + * Falls back to Date.now if not available. + * Set by the native JSI runtime. + */ +export const nativePerformanceNow: ?() => number = global.nativePerformanceNow; + +// ============================================================================= +// TurboModules +// ============================================================================= + +/** + * The TurboModule proxy function for accessing TurboModules. + * This is the main entry point for the new native modules architecture. + * Set by the native runtime. + */ +export const turboModuleProxy: ?(name: string) => ?T = + global.__turboModuleProxy; + +// ============================================================================= +// Logging +// ============================================================================= + +/** + * Native logging hook for sending console messages to native. + * Set by the native JSI runtime. + */ +export const nativeLoggingHook: ?(message: string, level: number) => void = + global.nativeLoggingHook; + +// ============================================================================= +// Blob Management +// ============================================================================= + +/** + * Provider function for creating blob collectors. + * Set by the native JSI runtime. + */ +export const blobCollectorProvider: ?(blobId: string) => ?BlobCollector = + global.__blobCollectorProvider; + +// ============================================================================= +// LogBox and Exception Handling +// ============================================================================= + +/** + * Registers an exception listener that can handle and prevent exceptions. + * Set by the native JSI runtime. + */ +export const registerExceptionListener: ?( + listener: ( + error: ExtendedExceptionData & {preventDefault: () => unknown}, + ) => void, +) => void = global.RN$registerExceptionListener; + +/** + * Checks if the runtime is ready. + * Set by the native JSI runtime. + */ +export const isRuntimeReady: ?() => boolean = global.RN$isRuntimeReady; diff --git a/packages/react-native/src/private/webapis/performance/internals/Utilities.js b/packages/react-native/src/private/webapis/performance/internals/Utilities.js index 9ee6ba1573f4..cfd52a291f14 100644 --- a/packages/react-native/src/private/webapis/performance/internals/Utilities.js +++ b/packages/react-native/src/private/webapis/performance/internals/Utilities.js @@ -9,6 +9,7 @@ */ import warnOnce from '../../../../../Libraries/Utilities/warnOnce'; +import {nativePerformanceNow} from '../../../runtime/ReactNativeRuntimeGlobals'; import NativePerformance from '../specs/NativePerformance'; export function warnNoNativePerformance() { @@ -18,10 +19,5 @@ export function warnNoNativePerformance() { ); } -declare var global: { - // This value is defined directly via JSI, if available. - +nativePerformanceNow?: ?() => number, -}; - export const getCurrentTimeStamp: () => DOMHighResTimeStamp = - NativePerformance?.now ?? global.nativePerformanceNow ?? (() => Date.now()); + NativePerformance?.now ?? nativePerformanceNow ?? (() => Date.now());