@nudojs/core 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/LICENSE +21 -0
- package/README.md +26 -0
- package/dist/index.d.ts +1633 -11
- package/dist/index.js +9965 -107
- package/package.json +28 -2
- package/src/__tests__/fn-sig-impl.test.ts +57 -0
- package/src/__tests__/ops-refined.test.ts +196 -0
- package/src/__tests__/ops.test.ts +8 -33
- package/src/__tests__/type-value-refined.test.ts +210 -0
- package/src/__tests__/type-value.test.ts +106 -0
- package/src/algebra/__tests__/abs-native.test.ts +176 -0
- package/src/algebra/__tests__/arithmetic.test.ts +170 -0
- package/src/algebra/__tests__/ast-eval.test.ts +154 -0
- package/src/algebra/__tests__/bridge.test.ts +160 -0
- package/src/algebra/__tests__/call-budget.test.ts +117 -0
- package/src/algebra/__tests__/check-case-consistency.test.ts +69 -0
- package/src/algebra/__tests__/check-gold.test.ts +290 -0
- package/src/algebra/__tests__/check-json.test.ts +57 -0
- package/src/algebra/__tests__/check-mini-repo.test.ts +49 -0
- package/src/algebra/__tests__/check-real-commander.test.ts +43 -0
- package/src/algebra/__tests__/check-real-packages.test.ts +165 -0
- package/src/algebra/__tests__/check-recall-gold.test.ts +1024 -0
- package/src/algebra/__tests__/check-require-dep-memo.test.ts +45 -0
- package/src/algebra/__tests__/check-return-contract.test.ts +90 -0
- package/src/algebra/__tests__/check-shape-gold.test.ts +303 -0
- package/src/algebra/__tests__/check-source-memo.test.ts +204 -0
- package/src/algebra/__tests__/check.test.ts +69 -0
- package/src/algebra/__tests__/denote.test.ts +77 -0
- package/src/algebra/__tests__/diagnostics.test.ts +64 -0
- package/src/algebra/__tests__/differential.test.ts +124 -0
- package/src/algebra/__tests__/div-mod.test.ts +38 -0
- package/src/algebra/__tests__/example0.test.ts +103 -0
- package/src/algebra/__tests__/exec-analyze-throws.test.ts +60 -0
- package/src/algebra/__tests__/exec-async.test.ts +46 -0
- package/src/algebra/__tests__/exec-bpath.test.ts +193 -0
- package/src/algebra/__tests__/exec-class.test.ts +56 -0
- package/src/algebra/__tests__/exec-e2e.test.ts +97 -0
- package/src/algebra/__tests__/exec-generator.test.ts +53 -0
- package/src/algebra/__tests__/exec-inherit-default.test.ts +111 -0
- package/src/algebra/__tests__/exec-optional-nested.test.ts +87 -0
- package/src/algebra/__tests__/exec-rest.test.ts +55 -0
- package/src/algebra/__tests__/exec-spread-forof.test.ts +85 -0
- package/src/algebra/__tests__/exec-static-computed.test.ts +61 -0
- package/src/algebra/__tests__/exec-switch.test.ts +74 -0
- package/src/algebra/__tests__/exec-trycatch.test.ts +71 -0
- package/src/algebra/__tests__/fn-fp-memo.test.ts +166 -0
- package/src/algebra/__tests__/fn-fp-multidecl.test.ts +49 -0
- package/src/algebra/__tests__/format.test.ts +34 -0
- package/src/algebra/__tests__/generalize-memo.test.ts +285 -0
- package/src/algebra/__tests__/generalize.test.ts +113 -0
- package/src/algebra/__tests__/hash-source.test.ts +19 -0
- package/src/algebra/__tests__/hof.test.ts +80 -0
- package/src/algebra/__tests__/inlay.test.ts +30 -0
- package/src/algebra/__tests__/language.test.ts +77 -0
- package/src/algebra/__tests__/leq.test.ts +109 -0
- package/src/algebra/__tests__/load-deps-fp-memo.test.ts +132 -0
- package/src/algebra/__tests__/methods.test.ts +37 -0
- package/src/algebra/__tests__/node-types.test.ts +26 -0
- package/src/algebra/__tests__/nudo-constraints.ts +46 -0
- package/src/algebra/__tests__/objects.test.ts +117 -0
- package/src/algebra/__tests__/parse-source-cache.test.ts +42 -0
- package/src/algebra/__tests__/refine-import.test.ts +109 -0
- package/src/algebra/__tests__/stable-source-key-block.test.ts +23 -0
- package/src/algebra/__tests__/stable-source-key.test.ts +31 -0
- package/src/algebra/__tests__/surface.test.ts +41 -0
- package/src/algebra/__tests__/this-inherit.test.ts +130 -0
- package/src/algebra/abs-fn.ts +49 -0
- package/src/algebra/abs-modules.ts +139 -0
- package/src/algebra/abs.ts +206 -0
- package/src/algebra/arithmetic.ts +691 -0
- package/src/algebra/ast-eval.ts +1763 -0
- package/src/algebra/bridge.ts +350 -0
- package/src/algebra/builtins.ts +373 -0
- package/src/algebra/check-report.ts +150 -0
- package/src/algebra/check.ts +1867 -0
- package/src/algebra/constraint.ts +304 -0
- package/src/algebra/denote.ts +166 -0
- package/src/algebra/diagnostics.ts +210 -0
- package/src/algebra/exec/call.ts +29 -0
- package/src/algebra/exec/calls.ts +96 -0
- package/src/algebra/exec/class-registry.ts +29 -0
- package/src/algebra/exec/class.ts +329 -0
- package/src/algebra/exec/index.ts +6 -0
- package/src/algebra/exec/member-diag.ts +141 -0
- package/src/algebra/exec/run.ts +341 -0
- package/src/algebra/exec/runtime.ts +583 -0
- package/src/algebra/exec/transpile.ts +993 -0
- package/src/algebra/fn-fp.ts +294 -0
- package/src/algebra/format.ts +91 -0
- package/src/algebra/generalize.ts +776 -0
- package/src/algebra/hash-source.ts +27 -0
- package/src/algebra/index.ts +34 -0
- package/src/algebra/inlay.ts +198 -0
- package/src/algebra/language.ts +289 -0
- package/src/algebra/leak.ts +68 -0
- package/src/algebra/leq.ts +301 -0
- package/src/algebra/load-deps-fp.ts +97 -0
- package/src/algebra/methods.ts +128 -0
- package/src/algebra/objects.ts +251 -0
- package/src/algebra/parse-source.ts +64 -0
- package/src/algebra/phi.ts +42 -0
- package/src/algebra/pred.ts +395 -0
- package/src/algebra/refine.ts +227 -0
- package/src/algebra/stable-source-key.ts +81 -0
- package/src/algebra/surface.ts +206 -0
- package/src/algebra/template.ts +143 -0
- package/src/algebra/term.ts +119 -0
- package/src/environment.ts +5 -0
- package/src/index.ts +52 -1
- package/src/mock-helpers.ts +166 -0
- package/src/ops.ts +106 -62
- package/src/refinements/range.ts +86 -0
- package/src/refinements/template-predicates.ts +11 -0
- package/src/refinements/template.ts +176 -0
- package/src/strip-types.ts +236 -0
- package/src/type-value.ts +193 -12
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { typeofAbs, negAbs, notAbs, strictEqAbs } from "../surface.ts";
|
|
3
|
+
import { numLit, num, abs, strLit, boolLit } from "../abs.ts";
|
|
4
|
+
import { lit, v } from "../term.ts";
|
|
5
|
+
import { gt, pTrue } from "../pred.ts";
|
|
6
|
+
import { typeValueToString } from "../../type-value.ts";
|
|
7
|
+
import { absToTypeValue } from "../bridge.ts";
|
|
8
|
+
|
|
9
|
+
describe("algebra surface ops", () => {
|
|
10
|
+
it("typeof of primitives and null", () => {
|
|
11
|
+
expect(typeValueToString(absToTypeValue(typeofAbs(numLit(1))))).toBe('"number"');
|
|
12
|
+
expect(typeValueToString(absToTypeValue(typeofAbs(strLit("x"))))).toBe('"string"');
|
|
13
|
+
expect(typeValueToString(absToTypeValue(typeofAbs(abs({ k: "unknown" }, lit(null), pTrue, "exact"))))).toBe('"object"');
|
|
14
|
+
expect(typeValueToString(absToTypeValue(typeofAbs(abs({ k: "fn", params: [] }, undefined, undefined, "exact"))))).toBe('"function"');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("typeof of unknown is abstract string (JS always returns string)", () => {
|
|
18
|
+
const r = typeofAbs(abs({ k: "unknown" }, undefined, undefined, "partial"));
|
|
19
|
+
expect(r.shape).toEqual({ k: "prim", type: "string" });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("neg folds and flips bounds", () => {
|
|
23
|
+
expect(typeValueToString(absToTypeValue(negAbs(numLit(5))))).toBe("-5");
|
|
24
|
+
const x = abs(num().shape, v("x"), gt(v("x"), lit(0)), "path");
|
|
25
|
+
const nx = negAbs(x);
|
|
26
|
+
expect(nx.term).toBeDefined();
|
|
27
|
+
expect(nx.pred).toBeDefined();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("not folds literals including undefined", () => {
|
|
31
|
+
expect(typeValueToString(absToTypeValue(notAbs(boolLit(false))))).toBe("true");
|
|
32
|
+
expect(typeValueToString(absToTypeValue(notAbs(abs({ k: "unknown" }, lit(undefined), pTrue, "exact"))))).toBe("true");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("strictEq nullish vs number is false", () => {
|
|
36
|
+
const n = abs(num().shape, undefined, undefined, "exact");
|
|
37
|
+
const nul = abs({ k: "unknown" }, lit(null), pTrue, "exact");
|
|
38
|
+
expect(strictEqAbs(n, nul)).toBe(false);
|
|
39
|
+
expect(strictEqAbs(numLit(1), numLit(1))).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { analyzeFn, numLit, litValue, formatShape } from "../index.ts";
|
|
3
|
+
|
|
4
|
+
describe("this method body + inheritance", () => {
|
|
5
|
+
it("method reads this field set in constructor", () => {
|
|
6
|
+
const src = `
|
|
7
|
+
class Counter {
|
|
8
|
+
constructor(n) { this.n = n; }
|
|
9
|
+
get() { return this.n; }
|
|
10
|
+
}
|
|
11
|
+
function main() {
|
|
12
|
+
const c = new Counter(3);
|
|
13
|
+
return c.get();
|
|
14
|
+
}
|
|
15
|
+
`;
|
|
16
|
+
const r = analyzeFn(src, "main", []);
|
|
17
|
+
expect(litValue(r)).toBe(3);
|
|
18
|
+
expect(r.conf).toBe("exact");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("method arithmetic on this", () => {
|
|
22
|
+
const src = `
|
|
23
|
+
class Acc {
|
|
24
|
+
constructor(n) { this.n = n; }
|
|
25
|
+
add(k) { return this.n + k; }
|
|
26
|
+
}
|
|
27
|
+
function main() {
|
|
28
|
+
return new Acc(10).add(5);
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
const r = analyzeFn(src, "main", []);
|
|
32
|
+
expect(litValue(r)).toBe(15);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("inherited method via super class shape", () => {
|
|
36
|
+
const src = `
|
|
37
|
+
class Base {
|
|
38
|
+
hello() { return "hi"; }
|
|
39
|
+
}
|
|
40
|
+
class Child extends Base {
|
|
41
|
+
hello() { return "child"; }
|
|
42
|
+
}
|
|
43
|
+
function main() {
|
|
44
|
+
return new Child().hello();
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
const r = analyzeFn(src, "main", []);
|
|
48
|
+
// 子类覆写
|
|
49
|
+
expect(litValue(r)).toBe("child");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("inherited method not overridden", () => {
|
|
53
|
+
const src = `
|
|
54
|
+
class Base {
|
|
55
|
+
hello() { return "hi"; }
|
|
56
|
+
n() { return 1; }
|
|
57
|
+
}
|
|
58
|
+
class Child extends Base {
|
|
59
|
+
hello() { return "child"; }
|
|
60
|
+
}
|
|
61
|
+
function main() {
|
|
62
|
+
return new Child().n();
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
const r = analyzeFn(src, "main", []);
|
|
66
|
+
expect(litValue(r)).toBe(1);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("instanceof super class name → true", () => {
|
|
70
|
+
const src = `
|
|
71
|
+
class Base { x() { return 1; } }
|
|
72
|
+
class Child extends Base { y() { return 2; } }
|
|
73
|
+
function main() {
|
|
74
|
+
return new Child() instanceof Base;
|
|
75
|
+
}
|
|
76
|
+
`;
|
|
77
|
+
const r = analyzeFn(src, "main", []);
|
|
78
|
+
expect(litValue(r)).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("super.method() dispatches to parent", () => {
|
|
82
|
+
const src = `
|
|
83
|
+
class Base {
|
|
84
|
+
hello() { return "hi"; }
|
|
85
|
+
}
|
|
86
|
+
class Child extends Base {
|
|
87
|
+
hello() { return super.hello() + "!"; }
|
|
88
|
+
}
|
|
89
|
+
function main() {
|
|
90
|
+
return new Child().hello();
|
|
91
|
+
}
|
|
92
|
+
`;
|
|
93
|
+
const r = analyzeFn(src, "main", []);
|
|
94
|
+
expect(litValue(r)).toBe("hi!");
|
|
95
|
+
expect(r.conf).toBe("exact");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("inherited constructor writes this fields", () => {
|
|
99
|
+
const src = `
|
|
100
|
+
class Base {
|
|
101
|
+
constructor(n) { this.n = n; }
|
|
102
|
+
}
|
|
103
|
+
class Child extends Base {
|
|
104
|
+
double() { return this.n * 2; }
|
|
105
|
+
}
|
|
106
|
+
function main() {
|
|
107
|
+
return new Child(4).double();
|
|
108
|
+
}
|
|
109
|
+
`;
|
|
110
|
+
const r = analyzeFn(src, "main", []);
|
|
111
|
+
expect(litValue(r)).toBe(8);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("super in multi-level chain", () => {
|
|
115
|
+
const src = `
|
|
116
|
+
class A { m() { return 1; } }
|
|
117
|
+
class B extends A {
|
|
118
|
+
m() { return super.m() + 1; }
|
|
119
|
+
}
|
|
120
|
+
class C extends B {
|
|
121
|
+
m() { return super.m() + 1; }
|
|
122
|
+
}
|
|
123
|
+
function main() {
|
|
124
|
+
return new C().m();
|
|
125
|
+
}
|
|
126
|
+
`;
|
|
127
|
+
const r = analyzeFn(src, "main", []);
|
|
128
|
+
expect(litValue(r)).toBe(3);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abs 上的一等函数:shape 是 fn,实现在旁路(不污染 Shape)。
|
|
3
|
+
* 与 term-registry 同一纪律:不给共享单例挂 impl。
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Node } from "@babel/types";
|
|
7
|
+
import type { Abs } from "./abs.ts";
|
|
8
|
+
import type { AstEnv } from "./ast-eval.ts";
|
|
9
|
+
|
|
10
|
+
export type AbsFnImpl = {
|
|
11
|
+
params: string[];
|
|
12
|
+
body: Node;
|
|
13
|
+
async?: boolean;
|
|
14
|
+
/** 声明时捕获的环境(闭包) */
|
|
15
|
+
env?: AstEnv;
|
|
16
|
+
kind?: string;
|
|
17
|
+
/** 调用时直接派发(mock withArgs 等),优先于 body */
|
|
18
|
+
apply?: (args: Abs[]) => Abs;
|
|
19
|
+
/**
|
|
20
|
+
* Optional content key for cache fingerprints. formatAbs cannot see
|
|
21
|
+
* WeakMap-side mock semantics (returns/withArgs/callsFake), so hosts that
|
|
22
|
+
* build mocks should stamp a stable fingerprint here.
|
|
23
|
+
*/
|
|
24
|
+
fingerprint?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const implByAbs = new WeakMap<object, AbsFnImpl>();
|
|
28
|
+
|
|
29
|
+
export function attachFnImpl(a: Abs, impl: AbsFnImpl): void {
|
|
30
|
+
if (a && typeof a === "object") implByAbs.set(a as object, impl);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getFnImpl(a: Abs): AbsFnImpl | undefined {
|
|
34
|
+
if (!a || typeof a !== "object") return undefined;
|
|
35
|
+
return implByAbs.get(a as object);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 造一个带实现的 Abs 函数值 */
|
|
39
|
+
export function absFunction(
|
|
40
|
+
params: string[],
|
|
41
|
+
impl: Omit<AbsFnImpl, "params">,
|
|
42
|
+
): Abs {
|
|
43
|
+
const a: Abs = {
|
|
44
|
+
shape: { k: "fn", params },
|
|
45
|
+
conf: "exact",
|
|
46
|
+
};
|
|
47
|
+
attachFnImpl(a, { params, ...impl });
|
|
48
|
+
return a;
|
|
49
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abs 模块面(无 fs):import 绑定 + export 收集。
|
|
3
|
+
* 解析路径/读文件在 host;core 只吃「specifier → 导出表」。
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { File, ImportDeclaration } from "@babel/types";
|
|
7
|
+
import type { Abs } from "./abs.ts";
|
|
8
|
+
import { unknown, obj } from "./abs.ts";
|
|
9
|
+
import type { AstEnv } from "./ast-eval.ts";
|
|
10
|
+
import { absFunction } from "./abs-fn.ts";
|
|
11
|
+
|
|
12
|
+
export type AbsModuleExports = {
|
|
13
|
+
named: Record<string, Abs>;
|
|
14
|
+
default?: Abs;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** 把 import 说明符绑定进 env(宿主已求值依赖) */
|
|
18
|
+
export function bindImports(
|
|
19
|
+
node: ImportDeclaration,
|
|
20
|
+
env: AstEnv,
|
|
21
|
+
modules: Record<string, AbsModuleExports>,
|
|
22
|
+
): void {
|
|
23
|
+
const mod = modules[node.source.value];
|
|
24
|
+
if (!mod) {
|
|
25
|
+
for (const s of node.specifiers) {
|
|
26
|
+
env.vars.set(s.local.name, unknown);
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
for (const s of node.specifiers) {
|
|
31
|
+
if (s.type === "ImportDefaultSpecifier") {
|
|
32
|
+
env.vars.set(s.local.name, mod.default ?? unknown);
|
|
33
|
+
} else if (s.type === "ImportSpecifier") {
|
|
34
|
+
const imported = s.imported.type === "Identifier" ? s.imported.name : String(s.imported);
|
|
35
|
+
env.vars.set(s.local.name, mod.named[imported] ?? unknown);
|
|
36
|
+
} else if (s.type === "ImportNamespaceSpecifier") {
|
|
37
|
+
const slots: Record<string, { value: Abs }> = {};
|
|
38
|
+
for (const [k, v] of Object.entries(mod.named)) slots[k] = { value: v };
|
|
39
|
+
if (mod.default) slots["default"] = { value: mod.default };
|
|
40
|
+
env.vars.set(s.local.name, obj(slots));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function fnToAbs(env: AstEnv, name: string): Abs | undefined {
|
|
46
|
+
const impl = env.fns.get(name);
|
|
47
|
+
if (!impl) return undefined;
|
|
48
|
+
return absFunction(impl.params, {
|
|
49
|
+
body: impl.body,
|
|
50
|
+
async: impl.async,
|
|
51
|
+
env,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function lookupExport(env: AstEnv, name: string): Abs | undefined {
|
|
56
|
+
if (env.vars.has(name)) return env.vars.get(name);
|
|
57
|
+
return fnToAbs(env, name);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 从已求值 env + AST 收集 ESM 导出。
|
|
62
|
+
* 支持:export function/const、export { a, b as c }、export default(具名)、
|
|
63
|
+
* 以及带 source 的 re-export(`export { a } from "mod"` / `export * from "mod"`——
|
|
64
|
+
* 需 host 传入已求值 modules)。
|
|
65
|
+
*/
|
|
66
|
+
export function collectAbsExports(
|
|
67
|
+
file: File,
|
|
68
|
+
env: AstEnv,
|
|
69
|
+
modules?: Record<string, AbsModuleExports>,
|
|
70
|
+
): AbsModuleExports {
|
|
71
|
+
const named: Record<string, Abs> = {};
|
|
72
|
+
let defaultExport: Abs | undefined;
|
|
73
|
+
|
|
74
|
+
for (const stmt of file.program.body) {
|
|
75
|
+
if (stmt.type === "ExportNamedDeclaration") {
|
|
76
|
+
// re-export:`export { a, b as c } from "mod"`
|
|
77
|
+
if (stmt.source && modules) {
|
|
78
|
+
const mod = modules[stmt.source.value];
|
|
79
|
+
if (mod) {
|
|
80
|
+
for (const spec of stmt.specifiers) {
|
|
81
|
+
if (spec.type !== "ExportSpecifier") continue;
|
|
82
|
+
const local = spec.local.name;
|
|
83
|
+
const exported =
|
|
84
|
+
spec.exported.type === "Identifier" ? spec.exported.name : String(spec.exported);
|
|
85
|
+
const v = local === "default" ? mod.default : mod.named[local];
|
|
86
|
+
if (v === undefined) continue;
|
|
87
|
+
if (exported === "default") defaultExport = v;
|
|
88
|
+
else named[exported] = v;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const decl = stmt.declaration;
|
|
94
|
+
if (decl) {
|
|
95
|
+
if (decl.type === "FunctionDeclaration" && decl.id) {
|
|
96
|
+
const v = lookupExport(env, decl.id.name);
|
|
97
|
+
if (v) named[decl.id.name] = v;
|
|
98
|
+
} else if (decl.type === "ClassDeclaration" && decl.id) {
|
|
99
|
+
const v = lookupExport(env, decl.id.name);
|
|
100
|
+
if (v) named[decl.id.name] = v;
|
|
101
|
+
} else if (decl.type === "VariableDeclaration") {
|
|
102
|
+
for (const d of decl.declarations) {
|
|
103
|
+
if (d.id.type === "Identifier") {
|
|
104
|
+
const v = lookupExport(env, d.id.name);
|
|
105
|
+
if (v) named[d.id.name] = v;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
for (const spec of stmt.specifiers) {
|
|
111
|
+
if (spec.type !== "ExportSpecifier") continue;
|
|
112
|
+
const local = spec.local.name;
|
|
113
|
+
const exported =
|
|
114
|
+
spec.exported.type === "Identifier" ? spec.exported.name : String(spec.exported);
|
|
115
|
+
const v = lookupExport(env, local);
|
|
116
|
+
if (v) named[exported] = v;
|
|
117
|
+
}
|
|
118
|
+
} else if (stmt.type === "ExportAllDeclaration" && stmt.source && modules) {
|
|
119
|
+
// export * from "mod":并入 named(不含 default,与 ESM 一致)
|
|
120
|
+
const mod = modules[stmt.source.value];
|
|
121
|
+
if (mod?.named) {
|
|
122
|
+
for (const [k, v] of Object.entries(mod.named)) named[k] = v;
|
|
123
|
+
}
|
|
124
|
+
} else if (stmt.type === "ExportDefaultDeclaration") {
|
|
125
|
+
const d = stmt.declaration;
|
|
126
|
+
if (d.type === "FunctionDeclaration") {
|
|
127
|
+
defaultExport = lookupExport(env, d.id ? d.id.name : "default");
|
|
128
|
+
} else if (d.type === "ClassDeclaration" && d.id) {
|
|
129
|
+
defaultExport = lookupExport(env, d.id.name);
|
|
130
|
+
} else if (d.type === "Identifier") {
|
|
131
|
+
defaultExport = lookupExport(env, d.name);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const result: AbsModuleExports = { named };
|
|
137
|
+
if (defaultExport) result.default = defaultExport;
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/** 抽象值 Abs = 形状 × 项 × 约束 × 置信度 */
|
|
2
|
+
|
|
3
|
+
import type { Term, LiteralValue } from "./term.ts";
|
|
4
|
+
import { lit, simplifyTerm, termEquals, termToString } from "./term.ts";
|
|
5
|
+
import type { Pred, PrimName } from "./pred.ts";
|
|
6
|
+
import {
|
|
7
|
+
and,
|
|
8
|
+
predToString,
|
|
9
|
+
pTrue,
|
|
10
|
+
ptypeof,
|
|
11
|
+
implies,
|
|
12
|
+
type Phi,
|
|
13
|
+
} from "./pred.ts";
|
|
14
|
+
|
|
15
|
+
export type Confidence = "exact" | "path" | "widened" | "mock" | "partial" | "opaque";
|
|
16
|
+
|
|
17
|
+
export type Shape =
|
|
18
|
+
| { k: "never" }
|
|
19
|
+
/**
|
|
20
|
+
* any:JS 里可以是任意值(无约束参数)。
|
|
21
|
+
* 运算按真实 JS 语义取并集,不是「分析失败」。
|
|
22
|
+
*/
|
|
23
|
+
| { k: "any" }
|
|
24
|
+
/** unknown:分析拿不到信息(求值失败/泄漏),不是「任意值」 */
|
|
25
|
+
| { k: "unknown" }
|
|
26
|
+
| { k: "prim"; type: PrimName }
|
|
27
|
+
| {
|
|
28
|
+
k: "obj";
|
|
29
|
+
slots: Record<string, { value: Abs; optional?: boolean; readonly?: boolean }>;
|
|
30
|
+
index?: { key: Abs; value: Abs };
|
|
31
|
+
open?: boolean;
|
|
32
|
+
}
|
|
33
|
+
| { k: "arr"; element: Abs }
|
|
34
|
+
| { k: "tuple"; elements: Abs[]; rest?: Abs }
|
|
35
|
+
| { k: "fn"; params: string[]; name?: string; paramTypes?: Abs[]; returnType?: Abs }
|
|
36
|
+
| { k: "brand"; name: string; shape: Abs }
|
|
37
|
+
| { k: "eff"; eff: "promise" | "generator"; inner: Abs }
|
|
38
|
+
| { k: "sum"; members: Abs[] };
|
|
39
|
+
|
|
40
|
+
export type Abs = {
|
|
41
|
+
shape: Shape;
|
|
42
|
+
term?: Term;
|
|
43
|
+
pred?: Pred;
|
|
44
|
+
conf: Confidence;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// --- 工厂 ---
|
|
48
|
+
|
|
49
|
+
export const never: Abs = { shape: { k: "never" }, conf: "exact" };
|
|
50
|
+
/** 分析无信息 */
|
|
51
|
+
export const unknown: Abs = { shape: { k: "unknown" }, conf: "partial" };
|
|
52
|
+
|
|
53
|
+
/** 带 term 的 any(generalize type-var) */
|
|
54
|
+
export function anyVar(id: string, conf: Confidence = "path"): Abs {
|
|
55
|
+
return { shape: { k: "any" }, term: { op: "var", id }, conf };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function num(): Abs {
|
|
59
|
+
return { shape: { k: "prim", type: "number" }, conf: "exact" };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function str(): Abs {
|
|
63
|
+
return { shape: { k: "prim", type: "string" }, conf: "exact" };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function bool(): Abs {
|
|
67
|
+
return { shape: { k: "prim", type: "boolean" }, conf: "exact" };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function numLit(value: number): Abs {
|
|
71
|
+
return {
|
|
72
|
+
shape: { k: "prim", type: "number" },
|
|
73
|
+
term: lit(value),
|
|
74
|
+
pred: pTrue,
|
|
75
|
+
conf: "exact",
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function strLit(value: string): Abs {
|
|
80
|
+
return {
|
|
81
|
+
shape: { k: "prim", type: "string" },
|
|
82
|
+
term: lit(value),
|
|
83
|
+
pred: pTrue,
|
|
84
|
+
conf: "exact",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function boolLit(value: boolean): Abs {
|
|
89
|
+
return {
|
|
90
|
+
shape: { k: "prim", type: "boolean" },
|
|
91
|
+
term: lit(value),
|
|
92
|
+
pred: pTrue,
|
|
93
|
+
conf: "exact",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** 带项的符号数,例如参数 x */
|
|
98
|
+
export function numVar(id: string, pred?: Pred, conf: Confidence = "path"): Abs {
|
|
99
|
+
return {
|
|
100
|
+
shape: { k: "prim", type: "number" },
|
|
101
|
+
term: { op: "var", id },
|
|
102
|
+
pred: pred ?? pTrue,
|
|
103
|
+
conf,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function obj(
|
|
108
|
+
slots: Record<string, { value: Abs; optional?: boolean }>,
|
|
109
|
+
): Abs {
|
|
110
|
+
return { shape: { k: "obj", slots }, conf: "exact" };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function confJoin(a: Confidence, b: Confidence): Confidence {
|
|
114
|
+
const rank: Confidence[] = ["exact", "path", "widened", "mock", "partial", "opaque"];
|
|
115
|
+
const ia = rank.indexOf(a);
|
|
116
|
+
const ib = rank.indexOf(b);
|
|
117
|
+
// 取更差的
|
|
118
|
+
return rank[Math.max(ia < 0 ? 5 : ia, ib < 0 ? 5 : ib)]!;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function absToString(a: Abs): string {
|
|
122
|
+
const parts: string[] = [shapeToString(a.shape)];
|
|
123
|
+
if (a.term) parts.push(`term=${termToString(a.term)}`);
|
|
124
|
+
if (a.pred && a.pred.op !== "true") parts.push(predToString(a.pred));
|
|
125
|
+
parts.push(`#${a.conf}`);
|
|
126
|
+
return parts.join(" ");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function shapeToString(s: Shape): string {
|
|
130
|
+
switch (s.k) {
|
|
131
|
+
case "never":
|
|
132
|
+
return "never";
|
|
133
|
+
case "any":
|
|
134
|
+
return "any";
|
|
135
|
+
case "unknown":
|
|
136
|
+
return "unknown";
|
|
137
|
+
case "prim":
|
|
138
|
+
return s.type;
|
|
139
|
+
case "obj": {
|
|
140
|
+
const entries = Object.entries(s.slots).map(
|
|
141
|
+
([k, slot]) =>
|
|
142
|
+
`${k}${slot.optional ? "?" : ""}: ${absToString(slot.value)}`,
|
|
143
|
+
);
|
|
144
|
+
return `{ ${entries.join(", ")} }`;
|
|
145
|
+
}
|
|
146
|
+
case "arr":
|
|
147
|
+
return `${shapeToString(s.element.shape)}[]`;
|
|
148
|
+
case "tuple":
|
|
149
|
+
return `[${s.elements.map((e) => shapeToString(e.shape)).join(", ")}]`;
|
|
150
|
+
case "fn":
|
|
151
|
+
return `fn(${s.params.join(", ")})${s.name ? ` ${s.name}` : ""}`;
|
|
152
|
+
case "brand":
|
|
153
|
+
return s.name;
|
|
154
|
+
case "eff":
|
|
155
|
+
return `${s.eff}<${shapeToString(s.inner.shape)}>`;
|
|
156
|
+
case "sum":
|
|
157
|
+
return s.members.map(absToString).join(" | ");
|
|
158
|
+
default:
|
|
159
|
+
return "·";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** 从 term 反推 prim shape */
|
|
164
|
+
export function shapeOfTerm(t: Term): Shape {
|
|
165
|
+
if (t.op === "lit") {
|
|
166
|
+
const v = t.value;
|
|
167
|
+
if (typeof v === "number") return { k: "prim", type: "number" };
|
|
168
|
+
if (typeof v === "string") return { k: "prim", type: "string" };
|
|
169
|
+
if (typeof v === "boolean") return { k: "prim", type: "boolean" };
|
|
170
|
+
if (typeof v === "bigint") return { k: "prim", type: "bigint" };
|
|
171
|
+
return { k: "unknown" };
|
|
172
|
+
}
|
|
173
|
+
return { k: "unknown" };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** 构造带项的结果 Abs;pred 相对 term */
|
|
177
|
+
export function abs(
|
|
178
|
+
shape: Shape,
|
|
179
|
+
term: Term | undefined,
|
|
180
|
+
pred: Pred | undefined,
|
|
181
|
+
conf: Confidence,
|
|
182
|
+
): Abs {
|
|
183
|
+
return {
|
|
184
|
+
shape,
|
|
185
|
+
term,
|
|
186
|
+
pred: pred && pred.op !== "true" ? pred : undefined,
|
|
187
|
+
conf,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function isNumPrim(a: Abs): boolean {
|
|
192
|
+
return a.shape.k === "prim" && a.shape.type === "number";
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function isStrPrim(a: Abs): boolean {
|
|
196
|
+
return a.shape.k === "prim" && a.shape.type === "string";
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** 置信度:字面量全确定 */
|
|
200
|
+
export function isExactLit(a: Abs): boolean {
|
|
201
|
+
return a.conf === "exact" && a.term?.op === "lit";
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function litValue(a: Abs): LiteralValue | undefined {
|
|
205
|
+
return a.term?.op === "lit" ? a.term.value : undefined;
|
|
206
|
+
}
|