From 820249b6b34c4235941009c7098d090614d17ca6 Mon Sep 17 00:00:00 2001 From: amsyarasyiq <82711525+amsyarasyiq@users.noreply.github.com> Date: Sat, 20 Jul 2024 10:46:45 +0800 Subject: [PATCH] perf+refactor(metro): improve polyfill impl --- src/lib/managers/themes.ts | 2 +- src/lib/ui/safeMode.tsx | 2 +- src/metro/caches.ts | 32 +++++++++---------- src/metro/enums.ts | 9 ++++++ src/metro/finders.ts | 4 +-- src/metro/{proxy.ts => lazy.ts} | 14 ++------- src/metro/modules.ts | 8 +++-- src/metro/polyfills/redesign.ts | 54 ++++++++++++++++----------------- src/metro/types.ts | 10 ++++++ src/metro/utils.ts | 2 +- 10 files changed, 71 insertions(+), 66 deletions(-) create mode 100644 src/metro/enums.ts rename src/metro/{proxy.ts => lazy.ts} (85%) diff --git a/src/lib/managers/themes.ts b/src/lib/managers/themes.ts index a36362a..92b32d7 100644 --- a/src/lib/managers/themes.ts +++ b/src/lib/managers/themes.ts @@ -12,7 +12,7 @@ import { lazyDestructure, proxyLazy } from "@lib/utils/lazy"; import { Author } from "@lib/utils/types"; import { chroma } from "@metro/common"; import { byMutableProp } from "@metro/filters"; -import { createLazyModule } from "@metro/proxy"; +import { createLazyModule } from "@metro/lazy"; import { findByNameProxy, findByProps, findByPropsProxy, findByStoreNameProxy } from "@metro/utils"; import { ImageBackground, Platform, processColor } from "react-native"; diff --git a/src/lib/ui/safeMode.tsx b/src/lib/ui/safeMode.tsx index 644eba5..1c4eee9 100644 --- a/src/lib/ui/safeMode.tsx +++ b/src/lib/ui/safeMode.tsx @@ -6,7 +6,7 @@ import { settings } from "@lib/settings"; import { lazyDestructure } from "@lib/utils/lazy"; import { ButtonColors } from "@lib/utils/types"; import { Button, CompatButton, SafeAreaView } from "@metro/common/components"; -import { _lazyContextSymbol, LazyModuleContext } from "@metro/proxy"; +import { _lazyContextSymbol, LazyModuleContext } from "@metro/lazy"; import { findByNameProxy, findByProps } from "@metro/utils"; import { semanticColors } from "@ui/color"; import { Codeblock, ErrorBoundary as _ErrorBoundary } from "@ui/components"; diff --git a/src/metro/caches.ts b/src/metro/caches.ts index 2b55e33..d70c112 100644 --- a/src/metro/caches.ts +++ b/src/metro/caches.ts @@ -1,21 +1,13 @@ import { ClientInfoManager, MMKVManager } from "@lib/api/native/modules"; import { debounce } from "es-toolkit"; -const CACHE_VERSION = 34; +import { ModuleFlags, ModulesMapInternal } from "./enums"; + +const CACHE_VERSION = 52; const BUNNY_METRO_CACHE_KEY = "__bunny_metro_cache_key__"; -export enum ExportsFlags { - EXISTS = 1 << 0, - BLACKLISTED = 1 << 1 -} - -export enum ModulesMapInternal { - FULL_LOOKUP, - NOT_FOUND -} - type ModulesMap = { - [flag in number | `_${ModulesMapInternal}`]?: 0 | ExportsFlags | undefined; + [flag in number | `_${ModulesMapInternal}`]?: ModuleFlags; }; let _metroCache = null as unknown as ReturnType; @@ -33,13 +25,13 @@ function buildInitCache() { assetsIndex: {} as Record } as const; - // Make sure all assets are cached. Delay by a second + // Force load all modules so useful modules are pre-cached. Delay by a second // because force loading it all will results in an unexpected crash. setTimeout(() => { for (const id in window.modules) { require("@metro/modules").requireModule(id); } - }, 1000); + }, 100); _metroCache = cache; return cache; @@ -52,6 +44,10 @@ export async function initMetroCache() { try { _metroCache = JSON.parse(rawCache); + if (_metroCache._v !== CACHE_VERSION) { + _metroCache = null!; + throw "cache invalidated; cache version outdated"; + } if (_metroCache._buildNumber !== ClientInfoManager.Build) { _metroCache = null!; throw "cache invalidated; version mismatch"; @@ -70,21 +66,21 @@ const saveCache = debounce(() => { }, 1000); function extractExportsFlags(moduleExports: any) { - if (!moduleExports) return 0; + if (!moduleExports) return undefined; - const bit = ExportsFlags.EXISTS; + const bit = ModuleFlags.EXISTS; return bit; } export function indexExportsFlags(moduleId: number, moduleExports: any) { const flags = extractExportsFlags(moduleExports); - if (flags !== ExportsFlags.EXISTS) { + if (flags && flags !== ModuleFlags.EXISTS) { _metroCache.exportsIndex[moduleId] = flags; } } export function indexBlacklistFlag(id: number) { - _metroCache.exportsIndex[id] |= ExportsFlags.BLACKLISTED; + _metroCache.exportsIndex[id] |= ModuleFlags.BLACKLISTED; } export function getCacherForUniq(uniq: string, allFind: boolean) { diff --git a/src/metro/enums.ts b/src/metro/enums.ts new file mode 100644 index 0000000..613043b --- /dev/null +++ b/src/metro/enums.ts @@ -0,0 +1,9 @@ +export enum ModuleFlags { + EXISTS = 1 << 0, + BLACKLISTED = 1 << 1 +} + +export enum ModulesMapInternal { + FULL_LOOKUP, + NOT_FOUND +} diff --git a/src/metro/finders.ts b/src/metro/finders.ts index 5816f09..ffb91dc 100644 --- a/src/metro/finders.ts +++ b/src/metro/finders.ts @@ -9,8 +9,8 @@ function filterExports( ): [any, boolean] | [void, false] { if ( moduleExports.default && - moduleExports.__esModule && - filter(moduleExports.default, moduleId, true) + moduleExports.__esModule && + filter(moduleExports.default, moduleId, true) ) { return [filter.raw ? moduleExports : moduleExports.default, !filter.raw]; } diff --git a/src/metro/proxy.ts b/src/metro/lazy.ts similarity index 85% rename from src/metro/proxy.ts rename to src/metro/lazy.ts index ec90e52..03894a7 100644 --- a/src/metro/proxy.ts +++ b/src/metro/lazy.ts @@ -4,20 +4,10 @@ import { proxyLazy } from "@lib/utils/lazy"; import { getMetroCache } from "./caches"; import { findExports } from "./finders"; import { metroModules, subscribeModule } from "./modules"; -import type { FilterFn } from "./types"; +import type { FilterFn, LazyModuleContext } from "./types"; export const _lazyContextSymbol = Symbol.for("bunny.metro.lazyContext"); -export const _lazyContexts = new WeakMap>(); - -export interface LazyModuleContext { - filter: FilterFn; - indexed: boolean; - moduleId?: number; - getExports(cb: (exports: any) => void): () => void; - subscribe(cb: (exports: any) => void): () => void; - forceLoad(): any; - get cache(): any; -} +export const _lazyContexts = new WeakMap(); function getIndexedFind(filter: FilterFn) { const modulesMap = getMetroCache().findIndex[filter.uniq]; diff --git a/src/metro/modules.ts b/src/metro/modules.ts index 92d5e6f..35db26e 100644 --- a/src/metro/modules.ts +++ b/src/metro/modules.ts @@ -1,6 +1,8 @@ -import { ExportsFlags, getMetroCache, indexBlacklistFlag, indexExportsFlags, ModulesMapInternal } from "@metro/caches"; +import { getMetroCache, indexBlacklistFlag, indexExportsFlags } from "@metro/caches"; import { Metro } from "@metro/types"; +import { ModuleFlags, ModulesMapInternal } from "./enums"; + const { before, instead } = require("spitroast"); export const metroModules: Metro.ModuleList = window.modules; @@ -21,7 +23,7 @@ for (const key in metroModules) { const metroModule = metroModules[id]; const cache = getMetroCache().exportsIndex[id]; - if (cache & ExportsFlags.BLACKLISTED) { + if (cache & ModuleFlags.BLACKLISTED) { blacklistModule(id); continue; } @@ -175,7 +177,7 @@ export function requireModule(id: Metro.ModuleID) { return metroRequire(id); } - // Disable Internal Metro error reporting logic + // Disable Internal RN error reporting logic const originalHandler = ErrorUtils.getGlobalHandler(); ErrorUtils.setGlobalHandler(noopHandler); diff --git a/src/metro/polyfills/redesign.ts b/src/metro/polyfills/redesign.ts index c119232..d7ca745 100644 --- a/src/metro/polyfills/redesign.ts +++ b/src/metro/polyfills/redesign.ts @@ -1,5 +1,4 @@ import { getPolyfillModuleCacher } from "@metro/caches"; -import { byProps } from "@metro/filters"; import { LiteralUnion } from "type-fest"; const redesignProps = new Set([ @@ -100,39 +99,38 @@ const redesignProps = new Set([ type Keys = LiteralUnion ? U : string, string>; +const _module = {} as Record; +const _source = {} as Record; + const cacher = getPolyfillModuleCacher("redesign_module"); -const _module = {} as Record; -export default _module; +for (const [id, moduleExports] of cacher.getModules()) { + for (const prop of redesignProps) { + let actualExports: any; -for (const prop of redesignProps) { - const filter = byProps(prop); - const candidates: any[] = []; + if (moduleExports[prop]) { + actualExports = moduleExports; + } else if (moduleExports.default?.[prop]) { + actualExports = moduleExports.default; + } else { + continue; + } - for (const [id, moduleExports] of cacher.getModules()) { - if (filter(moduleExports, id, false)) { - cacher.cacheId(id); - candidates.push(moduleExports); - } else if ( - moduleExports.__esModule - && moduleExports.default - && filter(moduleExports.default, id, false) - ) { - cacher.cacheId(id); - candidates.push(moduleExports.default); + const exportsKeysLength = Reflect.ownKeys(actualExports).length; + if (_source[prop] && exportsKeysLength >= _source[prop]) { + continue; + } + + _module[prop] = actualExports[prop]; + _source[prop] = Reflect.ownKeys(actualExports).length; + cacher.cacheId(id); + + if (exportsKeysLength === 1) { + redesignProps.delete(prop); } } - - if (candidates.length === 0) continue; - - const bestCandidate = candidates.reduce( - (c1, c2) => - (Object.keys(c2).length < Object.keys(c1).length) - ? c2 - : c1 - ); - - _module[prop] = bestCandidate[prop]; } cacher.finish(); + +export default _module; diff --git a/src/metro/types.ts b/src/metro/types.ts index d5e5817..d5b9dd1 100644 --- a/src/metro/types.ts +++ b/src/metro/types.ts @@ -148,6 +148,16 @@ export interface FilterDefinition { uniqMaker(args: A): string; } +export interface LazyModuleContext { + filter: FilterFn; + indexed: boolean; + moduleId?: number; + getExports(cb: (exports: any) => void): () => void; + subscribe(cb: (exports: any) => void): () => void; + forceLoad(): any; + get cache(): any; +} + export interface Dispatcher { _actionHandlers: unknown; _interceptors?: ((payload: any) => void | boolean)[]; diff --git a/src/metro/utils.ts b/src/metro/utils.ts index aad51ee..ed0e8d6 100644 --- a/src/metro/utils.ts +++ b/src/metro/utils.ts @@ -1,7 +1,7 @@ import { byDisplayName, byFilePath, byName, byProps, byStoreName, byTypeName } from "./filters"; import { findAllExports, findExports } from "./finders"; -import { createLazyModule } from "./proxy"; +import { createLazyModule } from "./lazy"; import { FilterCheckDef, FilterDefinition, ModuleExports } from "./types"; export function createFilterDefinition(