fix scrammaps

This commit is contained in:
Toshit Chawda 2025-03-06 20:06:27 -08:00
parent e8ff38b2e5
commit f15cf568c1
No known key found for this signature in database
GPG key ID: 91480ED99E2B3D9D
4 changed files with 103 additions and 78 deletions

View file

@ -5,11 +5,11 @@ function rewriteFunction(ctx: ProxyCtx, client: ScramjetClient) {
const stringifiedFunction = ctx.call().toString(); const stringifiedFunction = ctx.call().toString();
const content = rewriteJs( const content = rewriteJs(
`return ${stringifiedFunction}`, stringifiedFunction,
"(function proxy)", "(function proxy)",
client.meta client.meta
); );
ctx.return(ctx.fn(content)()); ctx.return(ctx.fn(`return ${content}`)());
} }
export default function (client: ScramjetClient, _self: Self) { export default function (client: ScramjetClient, _self: Self) {

View file

@ -6,27 +6,28 @@ enum RewriteType {
Replace = 1, Replace = 1,
} }
type Rewrite = type Rewrite = {
start: number;
} & (
| { | {
type: RewriteType.Insert; type: RewriteType.Insert;
// offset before this rewrite
offset: number;
// start of insertion
start: number;
// size of insertion
size: number; size: number;
} }
| { | {
type: RewriteType.Replace; type: RewriteType.Replace;
// offset before this rewrite
offset: number;
// start of replacement
start: number;
// end of replacement
end: number; end: number;
// old string
str: string; str: string;
}; }
);
function getEnd(rewrite: Rewrite): number {
if (rewrite.type === RewriteType.Insert) {
return rewrite.start + rewrite.size;
} else if (rewrite.type === RewriteType.Replace) {
return rewrite.end;
}
throw "unreachable";
}
const sourcemaps: Record<string, Rewrite[]> = {}; const sourcemaps: Record<string, Rewrite[]> = {};
@ -35,49 +36,53 @@ export const enabled = (client: ScramjetClient) =>
export default function (client: ScramjetClient, self: Self) { export default function (client: ScramjetClient, self: Self) {
// every script will push a sourcemap // every script will push a sourcemap
Object.defineProperty(self, $scramjet.config.globals.pushsourcemapfn, { Object.defineProperty(
value: (buf: Array<number>, tag: string) => { self,
const sourcemap = Uint8Array.from(buf); globalThis.$scramjet.config.globals.pushsourcemapfn,
const view = new DataView(sourcemap.buffer); {
const decoder = new TextDecoder("utf-8"); value: (buf: Array<number>, tag: string) => {
const sourcemap = Uint8Array.from(buf);
const view = new DataView(sourcemap.buffer);
const decoder = new TextDecoder("utf-8");
const rewrites = []; const rewrites: Rewrite[] = [];
const rewritelen = view.getUint32(0, true); const rewritelen = view.getUint32(0, true);
let cursor = 0; let cursor = 4;
for (let i = 0; i < rewritelen; i++) { for (let i = 0; i < rewritelen; i++) {
const type = view.getUint8(cursor) as RewriteType; const type = view.getUint8(cursor) as RewriteType;
cursor += 1; cursor += 1;
if (type == RewriteType.Insert) { if (type == RewriteType.Insert) {
const offset = view.getUint32(cursor, true); const start = view.getUint32(cursor, true);
cursor += 4; cursor += 4;
const start = view.getUint32(cursor, true); const size = view.getUint32(cursor, true);
cursor += 4; cursor += 4;
const size = view.getUint32(cursor, true);
cursor += 4;
rewrites.push({ type, offset, start, size }); rewrites.push({ type, start, size });
} else if (type == RewriteType.Replace) { } else if (type == RewriteType.Replace) {
const offset = view.getUint32(cursor, true); const start = view.getUint32(cursor, true);
cursor += 4; cursor += 4;
const start = view.getUint32(cursor, true); const end = view.getUint32(cursor, true);
cursor += 4; cursor += 4;
const end = view.getUint32(cursor, true); const len = view.getUint32(cursor, true);
cursor += 4; cursor += 4;
const str = decoder.decode(sourcemap.subarray(start, end)); const str = decoder.decode(
sourcemap.subarray(cursor, cursor + len)
);
rewrites.push({ type, offset, start, end, str }); rewrites.push({ type, start, end, str });
}
} }
}
sourcemaps[tag] = rewrites; sourcemaps[tag] = rewrites;
}, },
enumerable: false, enumerable: false,
writable: false, writable: false,
configurable: false, configurable: false,
}); }
);
const scramtag_ident = "/*scramtag "; const scramtag_ident = "/*scramtag ";
@ -86,7 +91,6 @@ export default function (client: ScramjetClient, self: Self) {
client.Proxy("Function.prototype.toString", { client.Proxy("Function.prototype.toString", {
apply(ctx) { apply(ctx) {
let stringified: string = ctx.fn.call(ctx.this); let stringified: string = ctx.fn.call(ctx.this);
let newString = "";
// every function rewritten will have a scramtag comment // every function rewritten will have a scramtag comment
// it will look like this: // it will look like this:
@ -106,39 +110,50 @@ export default function (client: ScramjetClient, self: Self) {
// subtracting that from the index of the scramtag gives us the starting index of the function relative to the entire file // subtracting that from the index of the scramtag gives us the starting index of the function relative to the entire file
const absindex = abstagindex - scramtagstart; const absindex = abstagindex - scramtagstart;
const endindex = absindex + stringified.length;
const scramtagend = stringified.indexOf("*/", scramtagstart); const scramtagend = stringified.indexOf("*/", scramtagstart);
const tag = stringified.substring(firstspace + 1, scramtagend); const tag = stringified.substring(firstspace + 1, scramtagend);
// delete all scramtags inside the function (and nested ones!!) const rewrites = sourcemaps[tag];
stringified = stringified.replace(/\/\*scramtag.*?\*\//g, "");
const maps = sourcemaps[tag]; if (!rewrites) {
console.warn("failed to get rewrites for tag", tag);
let i = 0; return ctx.return(stringified);
let offset = 0;
let j = 0;
while (j < maps.length) {
/* TODO
const [str, start, end] = maps[j];
if (start < absindex) {
j++;
continue;
}
if (start - absindex + offset > stringified.length) break;
// ooh i should really document this before i forget how it works
newString += stringified.slice(i, start - absindex + offset);
newString += str;
offset += end - start - str.length;
i = start - absindex + offset + str.length;
j++;
*/
} }
newString += stringified.slice(i); let i = 0;
// skip all rewrites in the file before the fn
while (i < rewrites.length) {
if (rewrites[i].start < absindex) i++;
else break;
}
let end = i;
while (end < rewrites.length) {
if (getEnd(rewrites[end]) < endindex) end++;
else break;
}
const fnrewrites = rewrites.slice(i, end);
let newString = "";
let lastpos = absindex;
for (const rewrite of fnrewrites) {
newString += stringified.slice(lastpos, rewrite.start);
if (rewrite.type === RewriteType.Insert) {
lastpos = rewrite.start + rewrite.size;
} else if (rewrite.type === RewriteType.Replace) {
newString += rewrite.str;
lastpos = rewrite.end;
} else {
throw "unreachable";
}
}
newString += stringified.slice(lastpos);
return ctx.return(newString); return ctx.return(newString);
}, },

View file

@ -62,7 +62,14 @@ function rewriteJsWrapper(
return input; return input;
} }
const after = performance.now(); const after = performance.now();
const { js, errors, duration } = out; const { js, map, scramtag, errors, duration } = out;
if ((flagEnabled("sourcemaps", meta.base), !globalThis.clients)) {
globalThis[globalThis.$scramjet.config.globals.pushsourcemapfn](
Array.from(map),
scramtag
);
}
if (flagEnabled("rewriterLogs", meta.base)) { if (flagEnabled("rewriterLogs", meta.base)) {
for (const error of errors) { for (const error of errors) {

View file

@ -6,6 +6,9 @@ const scramjet = new ScramjetController({
shared: "/scram/scramjet.shared.js", shared: "/scram/scramjet.shared.js",
sync: "/scram/scramjet.sync.js", sync: "/scram/scramjet.sync.js",
}, },
flags: {
sourcemaps: true,
},
siteFlags: { siteFlags: {
"https://worker-playground.glitch.me/.*": { "https://worker-playground.glitch.me/.*": {
serviceworkers: true, serviceworkers: true,