From 035ee800451068bedc10b5345418fc98eb77831a Mon Sep 17 00:00:00 2001 From: amsyarasyiq <82711525+amsyarasyiq@users.noreply.github.com> Date: Tue, 9 Jul 2024 17:19:29 +0800 Subject: [PATCH] refactor: lazification --- shims/jsxRuntime.tsx | 4 +- src/core/polyfills/vendettaObject.tsx | 4 +- src/lib/utils/lazy.ts | 89 ++++++++++++++++++++------- src/lib/utils/logger.ts | 4 +- src/metro/proxy.ts | 19 +++--- 5 files changed, 85 insertions(+), 35 deletions(-) diff --git a/shims/jsxRuntime.tsx b/shims/jsxRuntime.tsx index 0ab19a4..41de49f 100644 --- a/shims/jsxRuntime.tsx +++ b/shims/jsxRuntime.tsx @@ -1,10 +1,10 @@ -import { getFactoryOfProxy } from "@lib/utils/lazy"; +import { getProxyFactory } from "@lib/utils/lazy"; import { findByPropsProxy } from "@metro/utils"; const jsxRuntime = findByPropsProxy("jsx", "jsxs", "Fragment"); function unproxyFirstArg(args: T[]) { - const factory = getFactoryOfProxy(args[0]); + const factory = getProxyFactory(args[0]); if (factory) args[0] = factory(); return args; } diff --git a/src/core/polyfills/vendettaObject.tsx b/src/core/polyfills/vendettaObject.tsx index ca32d0a..4e7950e 100644 --- a/src/core/polyfills/vendettaObject.tsx +++ b/src/core/polyfills/vendettaObject.tsx @@ -9,7 +9,7 @@ import * as plugins from "@lib/managers/plugins"; import * as themes from "@lib/managers/themes"; import { loaderConfig, settings } from "@lib/settings"; import * as utils from "@lib/utils"; -import { logModule } from "@lib/utils/logger"; +import { DiscordLogger } from "@lib/utils/logger"; import * as metro from "@metro"; import * as common from "@metro/common"; import { Forms } from "@metro/common/components"; @@ -32,7 +32,7 @@ export async function createVdPluginObject(plugin: plugins.BunnyPlugin) { // Wrapping this with wrapSync is NOT an option. storage: await createStorage>(storage.createMMKVBackend(plugin.id)), }, - logger: new logModule(`Bunny » ${plugin.manifest.name}`), + logger: new DiscordLogger(`Bunny » ${plugin.manifest.name}`), }; } diff --git a/src/lib/utils/lazy.ts b/src/lib/utils/lazy.ts index c24cd47..cc611f1 100644 --- a/src/lib/utils/lazy.ts +++ b/src/lib/utils/lazy.ts @@ -1,34 +1,76 @@ /* eslint-disable func-call-spacing */ -const unconfigurable = ["arguments", "caller", "prototype"]; -const isUnconfigurable = (key: PropertyKey) => typeof key === "string" && unconfigurable.includes(key); -const proxyToFactoryMap = new WeakMap any>(); -const dummyToFactoryMap = new WeakMap any>(); +interface IsolatedEntries { + [keys: symbol | string]: any; +} + +interface LazyOptions { + hint?: "function" | "object"; + isolatedEntries?: IsolatedEntries +} + +interface ContextHolder { + options: LazyOptions; + factory: any +} + +const unconfigurable = new Set(["arguments", "caller", "prototype"]); +const isUnconfigurable = (key: PropertyKey) => typeof key === "string" && unconfigurable.has(key); + +const factories = new WeakMap any>(); +const proxyContextHolder = new WeakMap(); const lazyHandler: ProxyHandler = { ...Object.fromEntries(Object.getOwnPropertyNames(Reflect).map(fnName => { return [fnName, (target: any, ...args: any[]) => { - const resolved = dummyToFactoryMap.get(target)!(); - if (!resolved) throw new Error(`Trying to ${fnName} of ${typeof resolved}`); + const contextHolder = proxyContextHolder.get(target); + const resolved = contextHolder?.factory(); + if (!resolved) throw new Error(`Trying to Reflect.${fnName} of ${typeof resolved}`); // @ts-expect-error return Reflect[fnName](resolved, ...args); }]; })), - ownKeys: target => { - const resolved = dummyToFactoryMap.get(target)!(); - if (!resolved) throw new Error(`Trying to ownKeys of ${typeof resolved}`); + has(target, p) { + const contextHolder = proxyContextHolder.get(target); - const cacheKeys = Reflect.ownKeys(dummyToFactoryMap.get(target)!()); + if (contextHolder?.options) { + const { isolatedEntries } = contextHolder.options; + if (isolatedEntries && p in isolatedEntries) return true; + } + + const resolved = contextHolder?.factory(); + if (!resolved) throw new Error(`Trying to Reflect.has of ${typeof resolved}`); + return Reflect.has(resolved, p); + }, + get(target, p, receiver) { + const contextHolder = proxyContextHolder.get(target); + + if (contextHolder?.options) { + const { isolatedEntries } = contextHolder.options; + if (isolatedEntries?.[p]) return isolatedEntries[p]; + } + + const resolved = contextHolder?.factory(); + if (!resolved) throw new Error(`Trying to Reflect.get of ${typeof resolved}`); + return Reflect.get(resolved, p, receiver); + }, + ownKeys: target => { + const contextHolder = proxyContextHolder.get(target); + const resolved = contextHolder?.factory(); + if (!resolved) throw new Error(`Trying to Reflect.ownKeys of ${typeof resolved}`); + + const cacheKeys = Reflect.ownKeys(resolved); unconfigurable.forEach(key => !cacheKeys.includes(key) && cacheKeys.push(key)); return cacheKeys; }, getOwnPropertyDescriptor: (target, p) => { - const resolved = dummyToFactoryMap.get(target)!(); + const contextHolder = proxyContextHolder.get(target); + const resolved = contextHolder?.factory(); if (!resolved) throw new Error(`Trying to getOwnPropertyDescriptor of ${typeof resolved}`); if (isUnconfigurable(p)) return Reflect.getOwnPropertyDescriptor(target, p); - const descriptor = Reflect.getOwnPropertyDescriptor(dummyToFactoryMap.get(target)!(), p); + const descriptor = Reflect.getOwnPropertyDescriptor(resolved, p); if (descriptor) Object.defineProperty(target, p, descriptor); return descriptor; }, @@ -41,15 +83,18 @@ const lazyHandler: ProxyHandler = { * @returns A proxy that will call the factory function only when needed * @example const ChannelStore = proxyLazy(() => findByProps("getChannelId")); */ -export function proxyLazy(factory: () => T, asFunction = true): T { +export function proxyLazy(factory: () => T, opts: LazyOptions = {}): T { let cache: T; - const dummy = asFunction ? function () { } as any : {}; + const dummy = opts.hint !== "object" ? function () { } as any : {}; const proxyFactory = () => cache ??= factory(); const proxy = new Proxy(dummy, lazyHandler) as T; - proxyToFactoryMap.set(proxy, proxyFactory); - dummyToFactoryMap.set(dummy, proxyFactory); + factories.set(proxy, proxyFactory); + proxyContextHolder.set(dummy, { + factory, + options: opts, + }); return proxy; } @@ -63,8 +108,8 @@ export function proxyLazy(factory: () => T, asFunction = true): T { * const { uuid4 } = lazyDestructure(() => findByProps("uuid4")) * uuid4; // <- is a lazy proxy! */ -export function lazyDestructure>(factory: () => T, asFunction = false): T { - const proxiedObject = proxyLazy(factory, asFunction); +export function lazyDestructure>(factory: () => T, opts: LazyOptions = {}): T { + const proxiedObject = proxyLazy(factory); return new Proxy({}, { get(_, property) { @@ -72,16 +117,16 @@ export function lazyDestructure>(factory: return function* () { yield proxiedObject; yield new Proxy({}, { - get: (_, p) => proxyLazy(() => proxiedObject[p]) + get: (_, p) => proxyLazy(() => proxiedObject[p], opts) }); throw new Error("This is not a real iterator, this is likely used incorrectly"); }; } - return proxyLazy(() => proxiedObject[property]); + return proxyLazy(() => proxiedObject[property], opts); } }) as T; } -export function getFactoryOfProxy(obj: T): (() => T) | void { - return proxyToFactoryMap.get(obj) as (() => T) | void; +export function getProxyFactory(obj: T): (() => T) | void { + return factories.get(obj) as (() => T) | void; } diff --git a/src/lib/utils/logger.ts b/src/lib/utils/logger.ts index 387058c..5b3451c 100644 --- a/src/lib/utils/logger.ts +++ b/src/lib/utils/logger.ts @@ -11,5 +11,5 @@ export interface Logger { verbose: LoggerFunction; } -export const logModule = findByNameProxy("Logger"); -export const logger: Logger = new logModule("Bunny"); +export const DiscordLogger = findByNameProxy("Logger"); +export const logger: Logger = new DiscordLogger("Bunny"); diff --git a/src/metro/proxy.ts b/src/metro/proxy.ts index fe98204..c679aac 100644 --- a/src/metro/proxy.ts +++ b/src/metro/proxy.ts @@ -5,9 +5,10 @@ import { findExports } from "./finders"; import { metroModules, subscribeModule } from "./modules"; import type { FilterFn } from "./types"; -const proxyContextMap = new WeakMap>(); +const moduleContextSymbol = Symbol.for("bunny.metro.modules"); +const proxyContexts = new WeakMap>(); -interface FindProxyContext { +interface ModuleProxyContext { filter: FilterFn; indexed: boolean; moduleId: number | undefined; @@ -34,15 +35,15 @@ function subscribeModuleOfFind(proxy: any, callback: (exports: any) => void) { }); } -export function getFindContext(proxy: any): FindProxyContext | void { - return proxyContextMap.get(proxy) as unknown as FindProxyContext; +export function getFindContext(proxy: any): ModuleProxyContext | void { + return proxyContexts.get(proxy) as unknown as ModuleProxyContext; } export function createFindProxy(filter: FilterFn) { let cache: any = undefined; const moduleId = getIndexedSingleFind(filter); - const context: FindProxyContext = { + const context: ModuleProxyContext = { filter, indexed: !!moduleId, moduleId, @@ -65,8 +66,12 @@ export function createFindProxy(filter: FilterFn) { } }; - const proxy = proxyLazy(() => context.unproxy()); - proxyContextMap.set(proxy, context as FindProxyContext); + const proxy = proxyLazy(() => context.unproxy(), { + isolatedEntries: { + [moduleContextSymbol]: context + } + }); + proxyContexts.set(proxy, context as ModuleProxyContext); return proxy; }