perf+refactor(metro): improve polyfill impl

This commit is contained in:
amsyarasyiq 2024-07-20 10:46:45 +08:00
parent 15deb757e4
commit 820249b6b3
10 changed files with 71 additions and 66 deletions

View file

@ -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";

View file

@ -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";

View file

@ -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<typeof buildInitCache>;
@ -33,13 +25,13 @@ function buildInitCache() {
assetsIndex: {} as Record<string, ModulesMap | undefined>
} 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) {

9
src/metro/enums.ts Normal file
View file

@ -0,0 +1,9 @@
export enum ModuleFlags {
EXISTS = 1 << 0,
BLACKLISTED = 1 << 1
}
export enum ModulesMapInternal {
FULL_LOOKUP,
NOT_FOUND
}

View file

@ -9,8 +9,8 @@ function filterExports<A extends unknown[]>(
): [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];
}

View file

@ -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<any, LazyModuleContext<any[]>>();
export interface LazyModuleContext<A extends unknown[] = unknown[]> {
filter: FilterFn<A>;
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<any, LazyModuleContext>();
function getIndexedFind<A extends unknown[]>(filter: FilterFn<A>) {
const modulesMap = getMetroCache().findIndex[filter.uniq];

View file

@ -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);

View file

@ -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<typeof redesignProps extends Set<infer U> ? U : string, string>;
const _module = {} as Record<Keys, any>;
const _source = {} as Record<Keys, number>;
const cacher = getPolyfillModuleCacher("redesign_module");
const _module = {} as Record<Keys, any>;
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;

View file

@ -148,6 +148,16 @@ export interface FilterDefinition<A extends unknown[]> {
uniqMaker(args: A): string;
}
export interface LazyModuleContext<A extends unknown[] = unknown[]> {
filter: FilterFn<A>;
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)[];

View file

@ -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<A extends unknown[]>(