feat(lib/modules/metro): patch all primitives to improve perf

This commit is contained in:
PalmDevs 2025-07-09 19:08:18 +07:00
parent 9560a8320b
commit 796f1cf392
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
5 changed files with 128 additions and 89 deletions

View file

@ -1,6 +1,5 @@
import {
global,
define as metroDefine,
metroImportAll,
metroImportDefault,
metroRequire,
@ -11,7 +10,7 @@ import {
executeInitializeSubscriptions,
executeRequireSubscriptions,
} from './subscriptions/_internal'
import type { Metro } from '../types'
import type { Metro, RevengeMetro } from '../types'
/** Initializing ID */
export let mInitializingId: Metro.ModuleID | null = null
@ -23,10 +22,30 @@ export const mInitialized = new Set<Metro.ModuleID>()
export const mImportedPaths = new Map<string, Metro.ModuleID>()
export const mDeps: Metro.DependencyMap[] = []
export let mList: Metro.ModuleList
export const mList: RevengeMetro.ModuleList = new Map()
const metroDefine = (
factory: Metro.FactoryFn,
id: Metro.ModuleID,
dependencyMap: Metro.DependencyMap,
) => {
mDeps[id] = dependencyMap!
mUninitialized.add(id)
const moduleObject = { exports: {} }
mList.set(id, [
0,
moduleObject,
() => {
handleFactoryCall(factory, moduleObject)
},
])
}
/**
* Patching Metro's `__d` function to handle module definitions.
* We roll our own implementation of Metro's core functions.
*
* Here's how Metro sets itself up:
* 1. `__METRO_GLOBAL_PREFIX__ = ""`
@ -36,36 +55,34 @@ export let mList: Metro.ModuleList
* - We chose to patch it in 4. because we can access all the global functions at that point.
* 3. `__r = function metroRequire(...) {}`
* 4. `__c = function clear() {}`
* - **PATCH**: Set `__d` to our own implementation.
*
* 5. `clear()`
* - Metro clears the module list via clear(), and not __c() which makes it unpatchable.
* 6. `__d(..., 0, [...])`
* - Metro clears the module list directly with `clear()` and not `__c()`.
* 6. `metroRequire.importDefault = ...`, `metroRequire.importAll = ...`
* 7. `__d(..., 0, [...])`
* - The first module is defined with ID 0, which is the index module.
* - Patch:
* - We clear the module list again, so our module list is in sync with Metro's
* - **PATCH**: Override the `importDefault` and `importAll` functions in `__r`.
* #. `__d` is called with subsequent module definitions
*/
export function patchMetroDefine() {
const defineKey = `${__METRO_GLOBAL_PREFIX__}__d` as const
const defineKey = `${__METRO_GLOBAL_PREFIX__}__d` as const
// First __d call
globalThis[defineKey] = function define(origFactory, id, deps) {
// Clear the module list so we can keep in sync with Metro's
mList = __c()
// First __d call
globalThis[defineKey] = function define(origFactory, id, deps) {
// Set own implementation of metroImportDefault and metroImportAll
metroRequire.importDefault = metroImportDefault
metroRequire.importAll = metroImportAll
// Set own implementation of metroImportDefault and metroImportAll
metroRequire.importDefault = metroImportDefault
metroRequire.importAll = metroImportAll
// Set to the our own implementation function
// And then call the function to define the first module
;(globalThis[defineKey] = metroDefine)(origFactory, id, deps)
}
// Set to the actual custom implementation
globalThis[defineKey] = metroDefine
// Call the custom implementation
metroDefine(origFactory, id, deps)
}
// Why don't we use all the arguments from Metro.FactoryFn?
// Because there's too many for Hermes to be able to its dedicated CallN function which only supports up to 4 arguments. (Call, Call1, Call2, Call3, Call4)
export function handleFactoryCall(
// Because there's too many for Hermes to be able to use its dedicated CallN operation
// which only supports up to 4 arguments. (Call, Call1, Call2, Call3, Call4)
function handleFactoryCall(
factory: Metro.FactoryFn,
moduleObject: Metro.Module,
) {

View file

@ -1,65 +1,78 @@
/**
* Custom Metro-compatible methods.
* A more performant implementation of Metro's core functions.
* Making initialization faster and use less resources.
*/
import { handleFactoryCall, mDeps, mList, mUninitialized } from './_internal'
import type { Nullish } from '@revenge-mod/utils/types'
import { mList } from './_internal'
import type { Metro } from '../types'
export const Initialized = 1 << 0
const HasError = 1 << 1
const HasImportedDefault = 1 << 2
const HasImportedAll = 1 << 3
const NotInitializedMask = ~Initialized
export const FlagKey = 0
export const ModuleObjectKey = 1
const FactoryKey = 2
const ImportedDefaultKey = 3
const ImportedAllKey = 4
const ErrorKey = 5
export const global = globalThis
export const metroRequire = global.__r
// Roll our own define function implementation that replaces Metro's
// This is much more performant than Metro's own define function, which makes initialization faster
export function define(
factory: Metro.FactoryFn,
id: Metro.ModuleID,
dependencyMap: Metro.DependencyMap | Nullish,
) {
mDeps[id] = dependencyMap!
mUninitialized.add(id)
export const metroRequire = (moduleId => {
const module = mList.get(moduleId)!
const flags = module[FlagKey]
const moduleObject = module[ModuleObjectKey]
const moduleObject = { exports: {} }
if (flags & Initialized) return moduleObject.exports
if (flags & HasError) throw module[ErrorKey]
mList.set(id, {
dependencyMap: dependencyMap!,
factory: () => {
handleFactoryCall(factory, moduleObject)
},
publicModule: moduleObject,
} satisfies Omit<
Metro.ModuleDefinition<false>,
'importedDefault' | 'importedAll' | 'isInitialized' | 'hasError'
> as unknown as Metro.ModuleDefinition<false>)
}
// Optimistically mark module as initialized before running the
// factory to keep any require cycles inside the factory from causing an
// infinite require loop.
module[FlagKey] |= Initialized
moduleObject.id = moduleId
export function metroImportDefault(
moduleId: Metro.ModuleID,
): Metro.ModuleExports {
const mod = mList.get(moduleId)!
if (mod.importedDefault) return mod.importedDefault
try {
const factory = module[FactoryKey]
module[FactoryKey] = undefined
const exports: Metro.ModuleExports = metroRequire(moduleId)
const importedDefault: Metro.ModuleExports = exports?.__esModule
? exports.default
: exports
factory!()
return (mod.importedDefault = importedDefault)
}
return moduleObject.exports
} catch (e) {
module[FlagKey] = (flags & NotInitializedMask) | HasError
module[ErrorKey] = e
// @ts-expect-error: We never access this key again
module[ModuleObjectKey] = undefined
export function metroImportAll(moduleId: Metro.ModuleID): Metro.ModuleExports {
const mod = mList.get(moduleId)!
if (mod.importedAll) return mod.importedAll
const exports: Metro.ModuleExports = metroRequire(moduleId)
let importedAll: Metro.ModuleExports
if (exports?.__esModule) importedAll = exports
else {
importedAll = {}
if (exports) for (const key in exports) importedAll[key] = exports[key]
importedAll.default = exports
// @ts-expect-error: Not documented, but used by React Native
if (global.ErrorUtils) global.ErrorUtils.reportFatalError(e)
else throw e
}
}) as Metro.Require
return (mod.importedAll = importedAll)
global.__r = metroRequire
export const metroImportDefault: Metro.RequireFn = moduleId => {
const mod = mList.get(moduleId)!
if (mod[FlagKey] & HasImportedDefault) return mod[ImportedDefaultKey]
const exports = metroRequire(moduleId)
return (mod[ImportedDefaultKey] = exports?.__esModule
? exports.default
: exports)
}
export const metroImportAll: Metro.RequireFn = moduleId => {
const mod = mList.get(moduleId)!
if (mod[FlagKey] & HasImportedAll) return mod[ImportedAllKey]
const exports = metroRequire(moduleId)
if (!exports?.__esModule) exports.default = exports
return (mod[ImportedAllKey] = exports)
}

View file

@ -1,5 +1,6 @@
import { isProxy } from '@revenge-mod/utils/proxy'
import { mDeps, mList } from './_internal'
import { FlagKey, Initialized, ModuleObjectKey } from './custom'
import type { Metro } from '../types'
/**
@ -16,10 +17,10 @@ export function getModuleDependencies(
/**
* Returns whether a module is initialized.
* @param id The module ID.
* @returns `true` if the module is initialized, `false` if it is not initialized, or `undefined` if the module does not exist.
* @returns Non-zero number if the module is initialized, `0` if it is not initialized or does not exist.
*/
export function isModuleInitialized(id: Metro.ModuleID): boolean | undefined {
return mList.get(id)?.isInitialized
export function isModuleInitialized(id: Metro.ModuleID): number | undefined {
return mList.get(id)?.[FlagKey]! & Initialized
}
/**
@ -33,7 +34,7 @@ export function isModuleInitialized(id: Metro.ModuleID): boolean | undefined {
export function getInitializedModuleExports(
id: Metro.ModuleID,
): Metro.ModuleExports | undefined {
return mList.get(id)?.publicModule?.exports
return mList.get(id)?.[ModuleObjectKey]?.exports
}
/**

View file

@ -1,4 +1,4 @@
import type { If, Nullish } from '@revenge-mod/utils/types'
import type { If } from '@revenge-mod/utils/types'
/**
* Metro is a bundler for React Native.
@ -15,7 +15,7 @@ export namespace Metro {
metroImportAll: RequireFn,
moduleObject: Module,
exports: ModuleExports,
dependencyMap: DependencyMap | Nullish,
dependencyMap: DependencyMap,
) => void
export type ModuleID = number
@ -58,11 +58,9 @@ export namespace Metro {
export type DefineFn = (
factory: FactoryFn,
moduleId: ModuleID,
dependencyMap?: DependencyMap | undefined,
dependencyMap: DependencyMap,
) => void
export type ModuleDefiner = (moduleId: ModuleID) => void
export type ClearFn = () => ModuleList
export interface Require extends RequireFn {
@ -73,6 +71,19 @@ export namespace Metro {
export type ModuleExports = any
}
export namespace RevengeMetro {
export type ModuleDefinition<Initialized = boolean> = [
flags: number,
module: Metro.Module,
factory: If<Initialized, undefined, () => void>,
importedDefault?: Metro.ModuleExports,
importedAll?: Metro.ModuleExports,
error?: If<Initialized, undefined, any>,
]
export type ModuleList = Map<Metro.ModuleID, ModuleDefinition>
}
/**
* Maybe the default export matched instead of the namespace, because you're using `options.returnNamespace`.
*/

View file

@ -3,15 +3,12 @@ import type { Metro } from '@revenge-mod/modules/types'
Object.defineProperty(globalThis, '__c', {
configurable: true,
set(clear: Metro.ClearFn) {
// @ts-expect-error
// biome-ignore lint/performance/noDelete: We need to reset the property descriptor
delete globalThis.__c
globalThis.__c = clear
Object.defineProperty(globalThis, '__c', {
value: clear,
})
const { patchMetroDefine } =
require('@revenge-mod/modules/_/metro') as typeof import('@revenge-mod/modules/_/metro')
patchMetroDefine()
// Patch Metro's core functions
require('@revenge-mod/modules/_/metro')
require('./preinit')
},