mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-03 07:54:40 +08:00
Treat aliases of an accessor class as unreadable in the effect gate
Reading a class with a static accessor is refused because the accessor can run import-time effects, but `const B = A` gave the alias the kind "value", so a later read through `B` was admitted as pure and the accessor's effects could move with the deferred module. Propagate the "accessors" kind through alias chains to a fixed point and cover the alias in the refusal test. Co-Authored-By: Claude 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -79,6 +79,12 @@ const v = C.x;`,
|
||||
"a read through a class expression's static getter",
|
||||
`const C = class { static get x() { return 1; } };
|
||||
const v = C.x;`,
|
||||
],
|
||||
[
|
||||
"a read through an alias of a class with a static getter",
|
||||
`class C { static get x() { return require("fs").readFileSync("/etc/hostname"); } }
|
||||
const D = (C);
|
||||
const v = D.x;`,
|
||||
],
|
||||
[
|
||||
"a computed key that converts an object",
|
||||
|
||||
@@ -280,6 +280,28 @@ function checkFile(parser: typeof ts, file: string, load: (file: string) => void
|
||||
else if (nb) for (const el of nb.elements) locals.set(el.name.text, "module");
|
||||
}
|
||||
}
|
||||
// Aliasing a class (`const B = A`) gives another name for the same
|
||||
// static members, so a read through the alias runs the same accessor.
|
||||
// Propagate to a fixed point: an alias chain can be any length, and a
|
||||
// class binding is initialized before any alias of it (its TDZ).
|
||||
const aliases: [string, string][] = [];
|
||||
for (const stmt of sf.statements) {
|
||||
if (!parser.isVariableStatement(stmt)) continue;
|
||||
for (const d of stmt.declarationList.declarations) {
|
||||
if (!parser.isIdentifier(d.name) || d.initializer === undefined) continue;
|
||||
let init: ts.Expression = d.initializer;
|
||||
while (parser.isParenthesizedExpression(init)) init = init.expression;
|
||||
if (parser.isIdentifier(init)) aliases.push([d.name.text, init.text]);
|
||||
}
|
||||
}
|
||||
for (let changed = true; changed; ) {
|
||||
changed = false;
|
||||
for (const [name, source] of aliases)
|
||||
if (locals.get(source) === "accessors" && locals.get(name) !== "accessors") {
|
||||
locals.set(name, "accessors");
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
function bindNames(name: ts.BindingName, kind: "module" | "value"): void {
|
||||
if (parser.isIdentifier(name)) locals.set(name.text, kind);
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user