Merge branch 'modules-proxy-patches' into immediate-finds-modules-proxy

This commit is contained in:
Nuckyz 2024-06-28 00:58:26 -03:00
commit cc5d4a7b3a
No known key found for this signature in database
GPG key ID: 440BF8296E1C4AD9
2 changed files with 30 additions and 36 deletions

View file

@ -34,25 +34,19 @@ const define: Define = (target, p, attributes) => {
}); });
}; };
// wreq.O is the Webpack onChunksLoaded function. // wreq.m is the Webpack object containing module factories. It is pre-populated with module factories, and is also populated via webpackGlobal.push
// It is pretty likely that all important Discord Webpack instances will have this property set, // We use this setter to intercept when wreq.m is defined and apply the patching in its module factories.
// because Discord bundled code is chunked.
// As of the time of writing, only the main and sentry Webpack instances have this property, and they are the only ones we care about.
// We use this setter to intercept when wreq.O is defined, so we can patch the modules factories (wreq.m).
// wreq.m is pre-populated with module factories, and is also populated via webpackGlobal.push
// The sentry module also has their own Webpack with a pre-populated wreq.m, so this also patches those.
// We wrap wreq.m with our proxy, which is responsible for patching the module factories when they are set, or definining getters for the patched versions. // We wrap wreq.m with our proxy, which is responsible for patching the module factories when they are set, or definining getters for the patched versions.
// If this is the main Webpack, we also set up the internal references to WebpackRequire. // If this is the main Webpack, we also set up the internal references to WebpackRequire.
define(Function.prototype, "O", { define(Function.prototype, "m", {
enumerable: false, enumerable: false,
set(this: AnyWebpackRequire, onChunksLoaded: AnyWebpackRequire["O"]) { set(this: AnyWebpackRequire, originalModules: AnyWebpackRequire["m"]) {
define(this, "O", { value: onChunksLoaded }); define(this, "m", { value: originalModules });
const { stack } = new Error(); const { stack } = new Error();
if (this.m == null || !(stack?.includes("discord.com") || stack?.includes("discordapp.com"))) { if (!(stack?.includes("discord.com") || stack?.includes("discordapp.com")) || Array.isArray(originalModules)) {
return; return;
} }
@ -81,25 +75,25 @@ define(Function.prototype, "O", {
const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0); const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0);
// Patch the pre-populated factories // Patch the pre-populated factories
for (const id in this.m) { for (const id in originalModules) {
if (updateExistingFactory(this.m, id, this.m[id], true)) { if (updateExistingFactory(originalModules, id, originalModules[id], true)) {
continue; continue;
} }
notifyFactoryListeners(this.m[id]); notifyFactoryListeners(originalModules[id]);
defineModulesFactoryGetter(id, Settings.eagerPatches ? wrapAndPatchFactory(id, this.m[id]) : this.m[id]); defineModulesFactoryGetter(id, Settings.eagerPatches ? wrapAndPatchFactory(id, originalModules[id]) : originalModules[id]);
} }
define(this.m, Symbol.toStringTag, { define(originalModules, Symbol.toStringTag, {
value: "ModuleFactories", value: "ModuleFactories",
enumerable: false enumerable: false
}); });
// The proxy responsible for patching the module factories when they are set, or definining getters for the patched versions // The proxy responsible for patching the module factories when they are set, or definining getters for the patched versions
const proxiedModuleFactories = new Proxy(this.m, moduleFactoriesHandler); const proxiedModuleFactories = new Proxy(originalModules, moduleFactoriesHandler);
/* /*
If Discord ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype If Discord ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype
define(this, "m", { value: Reflect.setPrototypeOf(this.m, new Proxy(this.m, moduleFactoriesHandler)) }); define(this, "m", { value: Reflect.setPrototypeOf(originalModules, new Proxy(originalModules, moduleFactoriesHandler)) });
*/ */
define(this, "m", { value: proxiedModuleFactories }); define(this, "m", { value: proxiedModuleFactories });

34
src/webpack/wreq.d.ts vendored
View file

@ -48,10 +48,10 @@ export type ChunkHandlers = {
export type ScriptLoadDone = (event: Event) => void; export type ScriptLoadDone = (event: Event) => void;
export type OnChunksLoaded = ((this: WebpackRequire, result: any, chunkIds: PropertyKey[] | undefined | null, callback: () => any, priority: number) => any) & { // export type OnChunksLoaded = ((this: WebpackRequire, result: any, chunkIds: PropertyKey[] | undefined | null, callback: () => any, priority: number) => any) & {
/** Check if a chunk has been loaded */ // /** Check if a chunk has been loaded */
j: (this: OnChunksLoaded, chunkId: PropertyKey) => boolean; // j: (this: OnChunksLoaded, chunkId: PropertyKey) => boolean;
}; // };
export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & { export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
/** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */ /** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */
@ -160,18 +160,18 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
r: (this: WebpackRequire, exports: ModuleExports) => void; r: (this: WebpackRequire, exports: ModuleExports) => void;
/** Node.js module decorator. Decorates a module as a Node.js module */ /** Node.js module decorator. Decorates a module as a Node.js module */
nmd: (this: WebpackRequire, module: Module) => any; nmd: (this: WebpackRequire, module: Module) => any;
/** // /**
* Register deferred code which will be executed when the passed chunks are loaded. // * Register deferred code which will be executed when the passed chunks are loaded.
* // *
* If chunkIds is defined, it defers the execution of the callback and returns undefined. // * If chunkIds is defined, it defers the execution of the callback and returns undefined.
* // *
* If chunkIds is undefined, and no deferred code exists or can be executed, it returns the value of the result argument. // * If chunkIds is undefined, and no deferred code exists or can be executed, it returns the value of the result argument.
* // *
* If chunkIds is undefined, and some deferred code can already be executed, it returns the result of the callback function of the last deferred code. // * If chunkIds is undefined, and some deferred code can already be executed, it returns the result of the callback function of the last deferred code.
* // *
* When (priority & 1) it will wait for all other handlers with lower priority to be executed before itself is executed. // * When (priority & 1) it will wait for all other handlers with lower priority to be executed before itself is executed.
*/ // */
O: OnChunksLoaded; // O: OnChunksLoaded;
/** /**
* Instantiate a wasm instance with source using "wasmModuleHash", and importObject "importsObj", and then assign the exports of its instance to "exports". * Instantiate a wasm instance with source using "wasmModuleHash", and importObject "importsObj", and then assign the exports of its instance to "exports".
* @returns The exports argument, but now assigned with the exports of the wasm instance * @returns The exports argument, but now assigned with the exports of the wasm instance
@ -187,7 +187,7 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
// Utility section for Vencord // Utility section for Vencord
export type AnyWebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & Partial<Omit<WebpackRequire, "m" | "O">> & Pick<WebpackRequire, "O"> & { export type AnyWebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & Partial<Omit<WebpackRequire, "m">> & {
/** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */ /** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */
m: Record<PropertyKey, AnyModuleFactory>; m: Record<PropertyKey, AnyModuleFactory>;
}; };