Skip to content

Compile out RCTGetModuleClasses/RCTRegisterModule when both legacy interops are removed (#57670)#57670

Open
christophpurrer wants to merge 1 commit into
react:mainfrom
christophpurrer:export-D113555374
Open

Compile out RCTGetModuleClasses/RCTRegisterModule when both legacy interops are removed (#57670)#57670
christophpurrer wants to merge 1 commit into
react:mainfrom
christophpurrer:export-D113555374

Conversation

@christophpurrer

@christophpurrer christophpurrer commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary:

Changelog: [iOS][Changed] - Compile out RCTGetModuleClasses/RCTRegisterModule and skip static module registration when both RCT_REMOVE_LEGACY_MODULE_INTEROP and RCT_REMOVE_LEGACY_COMPONENT_INTEROP are defined

RCTGetModuleClasses() and the RCTModuleClasses registry it reads (populated by
RCTRegisterModule) exist solely to support the two legacy interop layers. Every reader
of the registry is already gated behind either RCT_REMOVE_LEGACY_MODULE_INTEROP (TurboModule
⇄ legacy NativeModule interop) or RCT_REMOVE_LEGACY_COMPONENT_INTEROP (Fabric ⇄ legacy
ViewManager interop). When an app defines both macros, the registry has no readers and the
function, its backing statics, and RCTRegisterModule are dead code.

This diff compiles them out in that configuration:

  • React/Base/RCTBridge.mm: guard RCTModuleClasses/RCTModuleClassesSyncQueue,
    RCTGetModuleClasses(), getCoreModuleClasses(), and RCTRegisterModule() behind
    #if !defined(RCT_REMOVE_LEGACY_MODULE_INTEROP) || !defined(RCT_REMOVE_LEGACY_COMPONENT_INTEROP).
    The old-arch warning helpers (getModulesLoadedWithOldArch et al.) stay compiled in — they have
    an external caller in RCTInstance.mm — and simply observe an empty list once the registry's only
    writer is gone.
  • React/Base/RCTBridge+Private.h: guard the matching RCT_EXTERN declarations.
  • React/Base/RCTBridgeModule.h: add a "both-removed" variant of RCT_EXPORT_MODULE /
    RCT_EXPORT_MODULE_NO_LOAD that emits only +moduleName and omits the RCTRegisterModule call,
    so native modules still link when the symbol is gone.
  • RCTLegacyUIManagerConstantsProvider.{h,mm}: wrap the file body in
    #ifndef RCT_REMOVE_LEGACY_COMPONENT_INTEROP, matching the sibling LegacyViewManagerInterop*
    files. It was the one caller of RCTGetModuleClasses whose file body was not already guarded.
  • fbobjc/Libraries/FBReactModuleRegistration/FBReactModuleRegistration.mm: the FIT/new-arch path
    (RCT_REMOVE_LEGACY_ARCH) hand-calls RCTRegisterModule. On cove-ios, which sets all three
    macros, that symbol is now compiled out, so the call is additionally gated to only run when
    RCTRegisterModule still exists. The registry has no readers in that config, so this is a no-op.

The default build (neither/one macro set) is behavior-unchanged: all guarded code is still compiled in.

Differential Revision: D113555374

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 24, 2026
@meta-codesync

meta-codesync Bot commented Jul 24, 2026

Copy link
Copy Markdown

@christophpurrer has exported this pull request. If you are a Meta employee, you can view the originating Diff in D113555374.

@meta-codesync meta-codesync Bot changed the title Compile out RCTGetModuleClasses/RCTRegisterModule when both legacy interops are removed Compile out RCTGetModuleClasses/RCTRegisterModule when both legacy interops are removed (#57670) Jul 25, 2026
christophpurrer added a commit to christophpurrer/react-native-macos that referenced this pull request Jul 25, 2026
…y interops are removed (react#57670)

Summary:

Changelog: [iOS][Changed] - Compile out `RCTGetModuleClasses`/`RCTRegisterModule` and skip static module registration when both `RCT_REMOVE_LEGACY_MODULE_INTEROP` and `RCT_REMOVE_LEGACY_COMPONENT_INTEROP` are defined


`RCTGetModuleClasses()` and the `RCTModuleClasses` registry it reads (populated by
`RCTRegisterModule`) exist solely to support the two legacy interop layers. Every reader
of the registry is already gated behind either `RCT_REMOVE_LEGACY_MODULE_INTEROP` (TurboModule
⇄ legacy NativeModule interop) or `RCT_REMOVE_LEGACY_COMPONENT_INTEROP` (Fabric ⇄ legacy
ViewManager interop). When an app defines **both** macros, the registry has no readers and the
function, its backing statics, and `RCTRegisterModule` are dead code.

This diff compiles them out in that configuration:

- `React/Base/RCTBridge.mm`: guard `RCTModuleClasses`/`RCTModuleClassesSyncQueue`,
  `RCTGetModuleClasses()`, `getCoreModuleClasses()`, and `RCTRegisterModule()` behind
  `#if !defined(RCT_REMOVE_LEGACY_MODULE_INTEROP) || !defined(RCT_REMOVE_LEGACY_COMPONENT_INTEROP)`.
  The old-arch warning helpers (`getModulesLoadedWithOldArch` et al.) stay compiled in — they have
  an external caller in `RCTInstance.mm` — and simply observe an empty list once the registry's only
  writer is gone.
- `React/Base/RCTBridge+Private.h`: guard the matching `RCT_EXTERN` declarations.
- `React/Base/RCTBridgeModule.h`: add a "both-removed" variant of `RCT_EXPORT_MODULE` /
  `RCT_EXPORT_MODULE_NO_LOAD` that emits only `+moduleName` and omits the `RCTRegisterModule` call,
  so native modules still link when the symbol is gone.
- `RCTLegacyUIManagerConstantsProvider.{h,mm}`: wrap the file body in
  `#ifndef RCT_REMOVE_LEGACY_COMPONENT_INTEROP`, matching the sibling `LegacyViewManagerInterop*`
  files. It was the one caller of `RCTGetModuleClasses` whose file body was not already guarded.
- `fbobjc/Libraries/FBReactModuleRegistration/FBReactModuleRegistration.mm`: the FIT/new-arch path
  (`RCT_REMOVE_LEGACY_ARCH`) hand-calls `RCTRegisterModule`. On `cove-ios`, which sets all three
  macros, that symbol is now compiled out, so the call is additionally gated to only run when
  `RCTRegisterModule` still exists. The registry has no readers in that config, so this is a no-op.

The default build (neither/one macro set) is behavior-unchanged: all guarded code is still compiled in.

Differential Revision: D113555374
christophpurrer added a commit to christophpurrer/react-native-macos that referenced this pull request Jul 25, 2026
…y interops are removed (react#57670)

Summary:

Changelog: [iOS][Changed] - Compile out `RCTGetModuleClasses`/`RCTRegisterModule` and skip static module registration when both `RCT_REMOVE_LEGACY_MODULE_INTEROP` and `RCT_REMOVE_LEGACY_COMPONENT_INTEROP` are defined


`RCTGetModuleClasses()` and the `RCTModuleClasses` registry it reads (populated by
`RCTRegisterModule`) exist solely to support the two legacy interop layers. Every reader
of the registry is already gated behind either `RCT_REMOVE_LEGACY_MODULE_INTEROP` (TurboModule
⇄ legacy NativeModule interop) or `RCT_REMOVE_LEGACY_COMPONENT_INTEROP` (Fabric ⇄ legacy
ViewManager interop). When an app defines **both** macros, the registry has no readers and the
function, its backing statics, and `RCTRegisterModule` are dead code.

This diff compiles them out in that configuration:

- `React/Base/RCTBridge.mm`: guard `RCTModuleClasses`/`RCTModuleClassesSyncQueue`,
  `RCTGetModuleClasses()`, `getCoreModuleClasses()`, and `RCTRegisterModule()` behind
  `#if !defined(RCT_REMOVE_LEGACY_MODULE_INTEROP) || !defined(RCT_REMOVE_LEGACY_COMPONENT_INTEROP)`.
  The old-arch warning helpers (`getModulesLoadedWithOldArch` et al.) stay compiled in — they have
  an external caller in `RCTInstance.mm` — and simply observe an empty list once the registry's only
  writer is gone.
- `React/Base/RCTBridge+Private.h`: guard the matching `RCT_EXTERN` declarations.
- `React/Base/RCTBridgeModule.h`: add a "both-removed" variant of `RCT_EXPORT_MODULE` /
  `RCT_EXPORT_MODULE_NO_LOAD` that emits only `+moduleName` and omits the `RCTRegisterModule` call,
  so native modules still link when the symbol is gone.
- `RCTLegacyUIManagerConstantsProvider.{h,mm}`: wrap the file body in
  `#ifndef RCT_REMOVE_LEGACY_COMPONENT_INTEROP`, matching the sibling `LegacyViewManagerInterop*`
  files. It was the one caller of `RCTGetModuleClasses` whose file body was not already guarded.
- `fbobjc/Libraries/FBReactModuleRegistration/FBReactModuleRegistration.mm`: the FIT/new-arch path
  (`RCT_REMOVE_LEGACY_ARCH`) hand-calls `RCTRegisterModule`. On `cove-ios`, which sets all three
  macros, that symbol is now compiled out, so the call is additionally gated to only run when
  `RCTRegisterModule` still exists. The registry has no readers in that config, so this is a no-op.

The default build (neither/one macro set) is behavior-unchanged: all guarded code is still compiled in.

Differential Revision: D113555374
…y interops are removed (react#57670)

Summary:

Changelog: [iOS][Changed] - Compile out `RCTGetModuleClasses`/`RCTRegisterModule` and skip static module registration when both `RCT_REMOVE_LEGACY_MODULE_INTEROP` and `RCT_REMOVE_LEGACY_COMPONENT_INTEROP` are defined


`RCTGetModuleClasses()` and the `RCTModuleClasses` registry it reads (populated by
`RCTRegisterModule`) exist solely to support the two legacy interop layers. Every reader
of the registry is already gated behind either `RCT_REMOVE_LEGACY_MODULE_INTEROP` (TurboModule
⇄ legacy NativeModule interop) or `RCT_REMOVE_LEGACY_COMPONENT_INTEROP` (Fabric ⇄ legacy
ViewManager interop). When an app defines **both** macros, the registry has no readers and the
function, its backing statics, and `RCTRegisterModule` are dead code.

This diff compiles them out in that configuration:

- `React/Base/RCTBridge.mm`: guard `RCTModuleClasses`/`RCTModuleClassesSyncQueue`,
  `RCTGetModuleClasses()`, `getCoreModuleClasses()`, and `RCTRegisterModule()` behind
  `#if !defined(RCT_REMOVE_LEGACY_MODULE_INTEROP) || !defined(RCT_REMOVE_LEGACY_COMPONENT_INTEROP)`.
  The old-arch warning helpers (`getModulesLoadedWithOldArch` et al.) stay compiled in — they have
  an external caller in `RCTInstance.mm` — and simply observe an empty list once the registry's only
  writer is gone.
- `React/Base/RCTBridge+Private.h`: guard the matching `RCT_EXTERN` declarations.
- `React/Base/RCTBridgeModule.h`: add a "both-removed" variant of `RCT_EXPORT_MODULE` /
  `RCT_EXPORT_MODULE_NO_LOAD` that emits only `+moduleName` and omits the `RCTRegisterModule` call,
  so native modules still link when the symbol is gone.
- `RCTLegacyUIManagerConstantsProvider.{h,mm}`: wrap the file body in
  `#ifndef RCT_REMOVE_LEGACY_COMPONENT_INTEROP`, matching the sibling `LegacyViewManagerInterop*`
  files. It was the one caller of `RCTGetModuleClasses` whose file body was not already guarded.
- `fbobjc/Libraries/FBReactModuleRegistration/FBReactModuleRegistration.mm`: the FIT/new-arch path
  (`RCT_REMOVE_LEGACY_ARCH`) hand-calls `RCTRegisterModule`. On `cove-ios`, which sets all three
  macros, that symbol is now compiled out, so the call is additionally gated to only run when
  `RCTRegisterModule` still exists. The registry has no readers in that config, so this is a no-op.

The default build (neither/one macro set) is behavior-unchanged: all guarded code is still compiled in.

Differential Revision: D113555374
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant