fix(javascript): honor effective symbolic descriptor values (#7222)

Resolve ordered descriptor values at the shared JavaScript fact owner and preserve uncertain intrinsic writes instead of stale exports. Normalize ordinary method values and reuse canonical primitive, prototype, ambient identity and ordering facts across consumers.

384 selected tests, format/static/docs checks, all final CI jobs and reviews pass. Actual Node controls cover 35 bundles; one possibly repeated factory explicitly stays unknown. Shipped Obsidian selected-source complete Evidence matches base/fixed CLI and MCP, with coverage and resource measurements in the PR.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
This commit is contained in:
SII-YDDandgensokyo-dev authored and GitHub committed 2026-10-27 03:08:00 +08:00
1 parent d54f5b07b4
commit d4c511ae6e
12 files changed
+1117 -102

No files matched your search

@@ -5,11 +5,95 @@ import {
semanticMemberChain,
semanticStaticPropertyKey,
staticObjectProperty,
staticObjectPropertyReader,
staticObjectPropertyNames,
} from "./javascriptAstValues.js";
import { SEMANTIC_EXPRESSION_DEPTH_LIMIT } from "./javascriptSemanticResourceLimits.js";
it("copies ordinary members without proving requested methods or getter effects", () => {
it("keeps string spread indices separate from named descriptor fields", () => {
for (const text of ["", "x", "😀", "x".repeat(1_000_000)]) {
const original = t.objectProperty(
t.identifier("value"),
t.identifier("sender"),
);
const object = t.objectExpression([
original,
t.spreadElement(t.stringLiteral(text)),
]);
const read = staticObjectPropertyReader(object);
expect(read("value")).toEqual({
status: "explicit",
property: original,
value: original.value,
});
expect(read("get")).toEqual({ status: "missing" });
expect(read("set")).toEqual({ status: "missing" });
expect(read("0").status).toBe(text.length > 0 ? "dynamic" : "missing");
expect(read("1").status).toBe(text.length > 1 ? "dynamic" : "missing");
for (const key of ["00", "-0", "1.0", "length"])
expect(read(key)).toEqual({ status: "missing" });
// Nonempty strings keep their indexed inventory partial without expanding
// it into character-sized AST nodes or a potentially huge key-name array.
expect(staticObjectPropertyNames(object)).toEqual({
status: text.length > 0 ? "partial" : "complete",
names: ["value"],
});
}
});
it("keeps numeric overwrite order and constant template string spread keys", () => {
const property = t.objectProperty(
t.stringLiteral("0"),
t.identifier("original"),
);
const spread = t.spreadElement(
t.templateLiteral([t.templateElement({ raw: "x", cooked: "x" }, true)], []),
);
expect(
staticObjectProperty(t.objectExpression([property, spread]), "0").status,
).toBe("dynamic");
expect(
staticObjectProperty(t.objectExpression([spread, property]), "0"),
).toEqual({ status: "explicit", property, value: property.value });
});
it("keeps literal array indices, holes and unknown iterable suffixes separate from names", () => {
const value = t.objectProperty(t.identifier("value"), t.identifier("sender"));
for (const elements of [
[],
[null, null],
[t.numericLiteral(1)],
[null, t.numericLiteral(1)],
[null, t.spreadElement(t.identifier("input"))],
]) {
const object = t.objectExpression([
value,
t.spreadElement(t.arrayExpression(elements)),
]);
const read = staticObjectPropertyReader(object);
expect(read("value")).toEqual({
status: "explicit",
property: value,
value: value.value,
});
expect(read("get")).toEqual({ status: "missing" });
expect(read("set")).toEqual({ status: "missing" });
expect(read("0").status).toBe(elements[0] == null ? "missing" : "dynamic");
expect(read("1").status).toBe(elements[1] == null ? "missing" : "dynamic");
expect(read("100").status).toBe(
elements.some((entry) => t.isSpreadElement(entry))
? "dynamic"
: "missing",
);
expect(read("length")).toEqual({ status: "missing" });
expect(staticObjectPropertyNames(object)).toEqual({
status: elements.some((entry) => entry != null) ? "partial" : "complete",
names: ["value"],
});
}
});
it("preserves ordinary method data values without executing getter effects", () => {
const method = (name: string, computed = false) =>
t.objectMethod(
"method",
@@ -33,7 +117,7 @@ it("copies ordinary members without proving requested methods or getter effects"
for (const [member, status] of [
[method("helper"), "explicit"],
[setter, "explicit"],
[method("stdio"), "dynamic"],
[method("stdio"), "explicit"],
[getter, "dynamic"],
[method("key", true), "dynamic"],
] as const) {
@@ -281,3 +365,62 @@ it("shares static member paths and stops before unadmitted path expansion", () =
expect(semanticMemberChain(selected, () => ++admitted < 2)).toBeUndefined();
expect(admitted).toBe(2);
});
it("retains exact ordinary method data nodes while keeping accessors unknown", () => {
const method = t.objectMethod(
"method",
t.identifier("value"),
[],
t.blockStatement([]),
);
const getter = t.objectMethod(
"get",
t.identifier("value"),
[],
t.blockStatement([]),
);
expect(staticObjectProperty(t.objectExpression([method]), "value")).toEqual({
status: "explicit",
property: method,
value: method,
});
expect(staticObjectProperty(t.objectExpression([getter]), "value")).toEqual({
status: "dynamic",
});
});
it("keeps primitive unary results propertyless and typeof results index-only", () => {
const value = t.objectProperty(t.identifier("value"), t.identifier("sender"));
const index = t.objectProperty(
t.stringLiteral("0"),
t.stringLiteral("prior"),
);
for (const operator of ["+", "-", "~", "!", "void", "delete"] as const) {
const object = t.objectExpression([
value,
index,
t.spreadElement(t.unaryExpression(operator, t.numericLiteral(1))),
]);
expect(staticObjectProperty(object, "value")).toEqual({
status: "explicit",
property: value,
value: value.value,
});
expect(staticObjectProperty(object, "0")).toEqual({
status: "explicit",
property: index,
value: index.value,
});
}
const object = t.objectExpression([
value,
index,
t.spreadElement(t.unaryExpression("typeof", t.identifier("input"))),
]);
expect(staticObjectProperty(object, "value")).toEqual({
status: "explicit",
property: value,
value: value.value,
});
expect(staticObjectProperty(object, "0")).toEqual({ status: "dynamic" });
});