Prepare for in case modules object is accessed directly in the future

This commit is contained in:
Nuckyz 2024-05-27 18:27:21 -03:00
parent 6d59a943da
commit 513eea8d14
No known key found for this signature in database
GPG key ID: 440BF8296E1C4AD9

View file

@ -72,13 +72,7 @@ Reflect.defineProperty(Function.prototype, "m", {
// Patch the pre-populated factories // Patch the pre-populated factories
for (const id in moduleFactories) { for (const id in moduleFactories) {
if (Settings.eagerPatches) { defineModulesFactoryGetter(id, Settings.eagerPatches ? patchFactory(id, moduleFactories[id]) : moduleFactories[id]);
// Patches the factory directly
moduleFactories[id] = patchFactory(id, moduleFactories[id]);
} else {
// Define a getter for the patched version
defineModulesFactoryGetter(id, moduleFactories[id]);
}
} }
Reflect.defineProperty(moduleFactories, Symbol.toStringTag, { Reflect.defineProperty(moduleFactories, Symbol.toStringTag, {
@ -90,6 +84,11 @@ Reflect.defineProperty(Function.prototype, "m", {
// 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
moduleFactories = new Proxy(moduleFactories, moduleFactoriesHandler); moduleFactories = new Proxy(moduleFactories, 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
Reflect.setPrototypeOf(moduleFactories, new Proxy(moduleFactories, moduleFactoriesHandler));
*/
} }
Reflect.defineProperty(this, "m", { Reflect.defineProperty(this, "m", {
@ -102,17 +101,17 @@ Reflect.defineProperty(Function.prototype, "m", {
}); });
/** /**
* Define the getter for returning the patched version of the module factory. This only executes and patches the factory when its accessed for the first time. * Define the getter for returning the patched version of the module factory.
* *
* It is what patches factories when eagerPatches are disabled. * If eagerPatches is enabled, the factory argument should already be the patched version, else it will be the original
* and only be patched when accessed for the first time.
* *
* The factory argument will become the patched version of the factory once it is accessed.
* @param id The id of the module * @param id The id of the module
* @param factory The original or patched module factory * @param factory The original or patched module factory
*/ */
function defineModulesFactoryGetter(id: PropertyKey, factory: PatchedModuleFactory) { function defineModulesFactoryGetter(id: PropertyKey, factory: PatchedModuleFactory) {
// Define the getter in all the module factories objects. Patches are only executed once, so make sure all module factories object // Define the getter in all the module factories objects. Patches are only executed once, so make sure all module factories object
// have the the patched version // have the patched version
for (const moduleFactories of allModuleFactories) { for (const moduleFactories of allModuleFactories) {
Reflect.defineProperty(moduleFactories, id, { Reflect.defineProperty(moduleFactories, id, {
configurable: true, configurable: true,
@ -138,6 +137,17 @@ function defineModulesFactoryGetter(id: PropertyKey, factory: PatchedModuleFacto
} }
const moduleFactoriesHandler: ProxyHandler<PatchedModuleFactories> = { const moduleFactoriesHandler: ProxyHandler<PatchedModuleFactories> = {
/*
If Discord ever decides to set module factories using the variable of the modules object directly instead of wreq.m, we need to switch the proxy to the prototype
and that requires defining additional traps for keeping the object working
// Proxies on the prototype dont intercept "get" when the property is in the object itself. But in case it isn't we need to return undefined,
// to avoid Reflect.get having no effect and causing a stack overflow
get: (target, p, receiver) => {
return undefined;
},
*/
// The set trap for patching or defining getters for the module factories when new module factories are loaded // The set trap for patching or defining getters for the module factories when new module factories are loaded
set: (target, p, newValue, receiver) => { set: (target, p, newValue, receiver) => {
// If the property is not a number, we are not dealing with a module factory // If the property is not a number, we are not dealing with a module factory
@ -145,7 +155,7 @@ const moduleFactoriesHandler: ProxyHandler<PatchedModuleFactories> = {
return Reflect.set(target, p, newValue, receiver); return Reflect.set(target, p, newValue, receiver);
} }
const existingFactory = Reflect.get(target, p, target); const existingFactory = Reflect.get(target, p, receiver);
if (!Settings.eagerPatches) { if (!Settings.eagerPatches) {
// If existingFactory exists, its either wrapped in defineModuleFactoryGetter, or it has already been required // If existingFactory exists, its either wrapped in defineModuleFactoryGetter, or it has already been required
@ -154,7 +164,7 @@ const moduleFactoriesHandler: ProxyHandler<PatchedModuleFactories> = {
return Reflect.set(target, p, newValue, receiver); return Reflect.set(target, p, newValue, receiver);
} }
// eagerPatches are disabled, so set up the getter for the patched version // eagerPatches are disabled, so the factory argument should be the original
defineModulesFactoryGetter(p, newValue); defineModulesFactoryGetter(p, newValue);
return true; return true;
} }
@ -170,14 +180,7 @@ const moduleFactoriesHandler: ProxyHandler<PatchedModuleFactories> = {
// If multiple Webpack instances exist, when new a new module is loaded, it will be set in all the module factories objects. // If multiple Webpack instances exist, when new a new module is loaded, it will be set in all the module factories objects.
// Because patches are only executed once, we need to set the patched version in all of them, to avoid the Webpack instance // Because patches are only executed once, we need to set the patched version in all of them, to avoid the Webpack instance
// that uses the factory to contain the original factory instead of the patched, in case it was set first in another instance // that uses the factory to contain the original factory instead of the patched, in case it was set first in another instance
for (const moduleFactories of allModuleFactories) { defineModulesFactoryGetter(p, patchedFactory);
Reflect.defineProperty(moduleFactories, p, {
value: patchedFactory,
configurable: true,
enumerable: true,
writable: true
});
}
return true; return true;
} }