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

This commit is contained in:
Nuckyz 2024-05-24 00:00:36 -03:00
commit aa9be97f83
No known key found for this signature in database
GPG key ID: 440BF8296E1C4AD9
6 changed files with 42 additions and 34 deletions

View file

@ -386,7 +386,7 @@ async function runtime(token: string) {
await Promise.all( await Promise.all(
Array.from(validChunkGroups) Array.from(validChunkGroups)
.map(([chunkIds]) => .map(([chunkIds]) =>
Promise.all(chunkIds.map(id => wreq.e(id).catch(() => { }))) Promise.all(chunkIds.map(id => wreq.e(id)))
) )
); );
@ -440,10 +440,13 @@ async function runtime(token: string) {
wreq = webpackRequire; wreq = webpackRequire;
Vencord.Webpack.factoryListeners.add(factory => { Vencord.Webpack.factoryListeners.add(factory => {
// setImmediate to avoid blocking the factory patching execution while checking for lazy chunks
setTimeout(() => {
let isResolved = false; let isResolved = false;
searchAndLoadLazyChunks(factory.toString()).then(() => isResolved = true); searchAndLoadLazyChunks(String(factory)).then(() => isResolved = true);
chunksSearchPromises.push(() => isResolved); chunksSearchPromises.push(() => isResolved);
}, 0);
}); });
// setImmediate to only search the initial factories after Discord initialized the app // setImmediate to only search the initial factories after Discord initialized the app
@ -451,7 +454,7 @@ async function runtime(token: string) {
setTimeout(() => { setTimeout(() => {
for (const factoryId in wreq.m) { for (const factoryId in wreq.m) {
let isResolved = false; let isResolved = false;
searchAndLoadLazyChunks(wreq.m[factoryId].toString()).then(() => isResolved = true); searchAndLoadLazyChunks(String(wreq.m[factoryId])).then(() => isResolved = true);
chunksSearchPromises.push(() => isResolved); chunksSearchPromises.push(() => isResolved);
} }
@ -470,7 +473,7 @@ async function runtime(token: string) {
const allChunks = [] as string[]; const allChunks = [] as string[];
// Matches "id" or id: // Matches "id" or id:
for (const currentMatch of wreq.u.toString().matchAll(/(?:"(\d+?)")|(?:(\d+?):)/g)) { for (const currentMatch of String(wreq.u).matchAll(/(?:"(\d+?)")|(?:(\d+?):)/g)) {
const id = currentMatch[1] ?? currentMatch[2]; const id = currentMatch[1] ?? currentMatch[2];
if (id == null) continue; if (id == null) continue;
@ -523,7 +526,7 @@ async function runtime(token: string) {
const module = Vencord.Webpack.findModuleFactory(...code); const module = Vencord.Webpack.findModuleFactory(...code);
if (module) { if (module) {
result = module.toString().match(Vencord.Util.canonicalizeMatch(matcher)); result = String(module).match(Vencord.Util.canonicalizeMatch(matcher));
} }
break; break;
@ -572,7 +575,7 @@ async function runtime(token: string) {
parsedArgs === args && parsedArgs === args &&
["waitFor", "find", "findComponent", "webpackDependantLazy", "webpackDependantLazyComponent"].includes(searchType) ["waitFor", "find", "findComponent", "webpackDependantLazy", "webpackDependantLazyComponent"].includes(searchType)
) { ) {
let filter = parsedArgs[0].toString(); let filter = String(parsedArgs[0]);
if (filter.length > 150) { if (filter.length > 150) {
filter = filter.slice(0, 147) + "..."; filter = filter.slice(0, 147) + "...";
} }
@ -583,7 +586,7 @@ async function runtime(token: string) {
if (parsedArgs[1] === Vencord.Webpack.DefaultExtractAndLoadChunksRegex) { if (parsedArgs[1] === Vencord.Webpack.DefaultExtractAndLoadChunksRegex) {
regexStr = "DefaultExtractAndLoadChunksRegex"; regexStr = "DefaultExtractAndLoadChunksRegex";
} else { } else {
regexStr = parsedArgs[1].toString(); regexStr = String(parsedArgs[1]);
} }
logMessage += `([${parsedArgs[0].map((arg: any) => `"${arg}"`).join(", ")}], ${regexStr})`; logMessage += `([${parsedArgs[0].map((arg: any) => `"${arg}"`).join(", ")}], ${regexStr})`;

View file

@ -56,7 +56,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError
const [compileResult, setCompileResult] = React.useState<[boolean, string]>(); const [compileResult, setCompileResult] = React.useState<[boolean, string]>();
const [patchedCode, matchResult, diff] = React.useMemo(() => { const [patchedCode, matchResult, diff] = React.useMemo(() => {
const src: string = fact.toString().replaceAll("\n", ""); const src: string = String(fact).replaceAll("\n", "");
try { try {
new RegExp(match); new RegExp(match);

View file

@ -66,11 +66,6 @@ function VencordSettings() {
title: "Enable React Developer Tools", title: "Enable React Developer Tools",
note: "Requires a full restart" note: "Requires a full restart"
}, },
{
key: "eagerPatches",
title: "Apply Vencord patches before they are needed",
note: "Increases startup timing, but may make app usage more fluid. Note that the difference of having this on or off is minimal."
},
!IS_WEB && (!IS_DISCORD_DESKTOP || !isWindows ? { !IS_WEB && (!IS_DISCORD_DESKTOP || !isWindows ? {
key: "frameless", key: "frameless",
title: "Disable the window frame", title: "Disable the window frame",

View file

@ -160,7 +160,7 @@ function initWs(isManual = false) {
return reply("Expected exactly one 'find' matches, found " + keys.length); return reply("Expected exactly one 'find' matches, found " + keys.length);
const mod = candidates[keys[0]]; const mod = candidates[keys[0]];
let src = mod.toString().replaceAll("\n", ""); let src = String(mod).replaceAll("\n", "");
if (src.startsWith("function(")) { if (src.startsWith("function(")) {
src = "0," + src; src = "0," + src;

View file

@ -24,7 +24,7 @@ const modulesProxyHandler: ProxyHandler<WebpackRequire["m"]> = {
[propName, (...args: any[]) => Reflect[propName](...args)] [propName, (...args: any[]) => Reflect[propName](...args)]
)), )),
get: (target, p) => { get: (target, p) => {
const propValue = Reflect.get(target, p); const propValue = Reflect.get(target, p, target);
// 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
// $$vencordOriginal means the factory is already patched, $$vencordRequired means it has already been required // $$vencordOriginal means the factory is already patched, $$vencordRequired means it has already been required
@ -36,7 +36,7 @@ const modulesProxyHandler: ProxyHandler<WebpackRequire["m"]> = {
// This patches factories if eagerPatches are disabled // This patches factories if eagerPatches are disabled
const patchedFactory = patchFactory(p, propValue); const patchedFactory = patchFactory(p, propValue);
Reflect.set(target, p, patchedFactory); Reflect.set(target, p, patchedFactory, target);
return patchedFactory; return patchedFactory;
}, },
@ -44,10 +44,10 @@ const modulesProxyHandler: ProxyHandler<WebpackRequire["m"]> = {
// $$vencordRequired means we are resetting the factory to its original after being required // $$vencordRequired means we are resetting the factory to its original after being required
// 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
if (!Settings.eagerPatches || newValue?.$$vencordRequired === true || Number.isNaN(Number(p))) { if (!Settings.eagerPatches || newValue?.$$vencordRequired === true || Number.isNaN(Number(p))) {
return Reflect.set(target, p, newValue); return Reflect.set(target, p, newValue, target);
} }
const existingFactory = Reflect.get(target, p); const existingFactory = Reflect.get(target, p, target);
// Check if this factory is already patched // Check if this factory is already patched
// @ts-ignore // @ts-ignore
@ -59,7 +59,7 @@ const modulesProxyHandler: ProxyHandler<WebpackRequire["m"]> = {
// Modules are only patched once, so we need to set the patched factory on all the modules // Modules are only patched once, so we need to set the patched factory on all the modules
for (const proxiedModules of allProxiedModules) { for (const proxiedModules of allProxiedModules) {
Reflect.set(proxiedModules, p, patchedFactory); Reflect.set(proxiedModules, p, patchedFactory, proxiedModules);
} }
return true; return true;
@ -91,7 +91,7 @@ Object.defineProperty(Function.prototype, "O", {
const originalOnChunksLoaded = onChunksLoaded; const originalOnChunksLoaded = onChunksLoaded;
onChunksLoaded = function (result, chunkIds, callback, priority) { onChunksLoaded = function (result, chunkIds, callback, priority) {
if (callback != null && initCallbackRegex.test(callback.toString())) { if (callback != null && initCallbackRegex.test(String(callback))) {
Object.defineProperty(this, "O", { Object.defineProperty(this, "O", {
value: originalOnChunksLoaded, value: originalOnChunksLoaded,
configurable: true, configurable: true,
@ -127,10 +127,17 @@ Object.defineProperty(Function.prototype, "O", {
configurable: true, configurable: true,
set(v: OnChunksLoaded["j"]) { set(v: OnChunksLoaded["j"]) {
// @ts-ignore function setValue(target: any) {
delete onChunksLoaded.j; Object.defineProperty(target, "j", {
onChunksLoaded.j = v; value: v,
originalOnChunksLoaded.j = v; configurable: true,
enumerable: true,
writable: true
});
}
setValue(onChunksLoaded);
setValue(originalOnChunksLoaded);
} }
}); });
} }
@ -160,6 +167,8 @@ Object.defineProperty(Function.prototype, "m", {
// The new object which will contain the factories // The new object which will contain the factories
const proxiedModules: WebpackRequire["m"] = {}; const proxiedModules: WebpackRequire["m"] = {};
// @ts-ignore
proxiedModules[Symbol.toStringTag] = "ProxiedModules";
for (const id in originalModules) { for (const id in originalModules) {
// If we have eagerPatches enabled we have to patch the pre-populated factories // If we have eagerPatches enabled we have to patch the pre-populated factories
@ -173,9 +182,10 @@ Object.defineProperty(Function.prototype, "m", {
delete originalModules[id]; delete originalModules[id];
} }
// @ts-ignore
originalModules.$$proxiedModules = proxiedModules;
allProxiedModules.add(proxiedModules); allProxiedModules.add(proxiedModules);
// @ts-ignore
originalModules[Symbol.toStringTag] = "OriginalModules";
Object.setPrototypeOf(originalModules, new Proxy(proxiedModules, modulesProxyHandler)); Object.setPrototypeOf(originalModules, new Proxy(proxiedModules, modulesProxyHandler));
} }
@ -211,7 +221,7 @@ function patchFactory(id: PropertyKey, factory: ModuleFactory) {
// cause issues. // cause issues.
// //
// 0, prefix is to turn it into an expression: 0,function(){} would be invalid syntax without the 0, // 0, prefix is to turn it into an expression: 0,function(){} would be invalid syntax without the 0,
let code: string = "0," + factory.toString().replaceAll("\n", ""); let code: string = "0," + String(factory).replaceAll("\n", "");
for (let i = 0; i < patches.length; i++) { for (let i = 0; i < patches.length; i++) {
const patch = patches[i]; const patch = patches[i];
@ -318,7 +328,7 @@ function patchFactory(id: PropertyKey, factory: ModuleFactory) {
// @ts-ignore // @ts-ignore
originalFactory.$$vencordRequired = true; originalFactory.$$vencordRequired = true;
for (const proxiedModules of allProxiedModules) { for (const proxiedModules of allProxiedModules) {
proxiedModules[id] = originalFactory; Reflect.set(proxiedModules, id, originalFactory, proxiedModules);
} }
if (wreq == null && IS_DEV) { if (wreq == null && IS_DEV) {

View file

@ -480,7 +480,7 @@ export const cacheFindBulk = traceFunction("cacheFindBulk", function cacheFindBu
export const findModuleId = traceFunction("findModuleId", function findModuleId(...code: string[]) { export const findModuleId = traceFunction("findModuleId", function findModuleId(...code: string[]) {
outer: outer:
for (const id in wreq.m) { for (const id in wreq.m) {
const str = wreq.m[id].toString(); const str = String(wreq.m[id]);
for (const c of code) { for (const c of code) {
if (!str.includes(c)) continue outer; if (!str.includes(c)) continue outer;
@ -672,7 +672,7 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def
return; return;
} }
const match = module.toString().match(canonicalizeMatch(matcher)); const match = String(module).match(canonicalizeMatch(matcher));
if (!match) { if (!match) {
const err = new Error("extractAndLoadChunks: Couldn't find chunk loading in module factory code"); const err = new Error("extractAndLoadChunks: Couldn't find chunk loading in module factory code");
logger.warn(err, "Code:", code, "Matcher:", matcher); logger.warn(err, "Code:", code, "Matcher:", matcher);
@ -731,7 +731,7 @@ export function search(...filters: Array<string | RegExp>) {
outer: outer:
for (const id in factories) { for (const id in factories) {
const factory = factories[id]; const factory = factories[id];
const str: string = factory.toString(); const str: string = String(factory);
for (const filter of filters) { for (const filter of filters) {
if (typeof filter === "string" && !str.includes(filter)) continue outer; if (typeof filter === "string" && !str.includes(filter)) continue outer;
if (filter instanceof RegExp && !filter.test(str)) continue outer; if (filter instanceof RegExp && !filter.test(str)) continue outer;
@ -759,7 +759,7 @@ export function extract(id: PropertyKey) {
// WARNING: This module was extracted to be more easily readable. // WARNING: This module was extracted to be more easily readable.
// This module is NOT ACTUALLY USED! This means putting breakpoints will have NO EFFECT!! // This module is NOT ACTUALLY USED! This means putting breakpoints will have NO EFFECT!!
0,${mod.toString()} 0,${String(mod)}
//# sourceURL=ExtractedWebpackModule${String(id)} //# sourceURL=ExtractedWebpackModule${String(id)}
`; `;
const extracted: ModuleFactory = (0, eval)(code); const extracted: ModuleFactory = (0, eval)(code);