@nudojs/cli 0.1.0 → 0.2.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 +16 -0
- package/LICENSE +21 -0
- package/dist/{chunk-JW3RMCNY.js → chunk-LGWVGEMB.js} +363 -13
- package/dist/evaluator-api.d.ts +3 -1
- package/dist/evaluator-api.js +7 -3
- package/dist/index.js +12 -2
- package/package.json +13 -3
- package/src/__tests__/evaluator-loops.test.ts +163 -0
- package/src/__tests__/evaluator-refined.test.ts +121 -0
- package/src/__tests__/evaluator-string-methods.test.ts +151 -0
- package/src/evaluator-api.ts +2 -0
- package/src/evaluator.ts +380 -9
- package/src/index.ts +12 -0
- package/src/narrowing.ts +56 -0
- package/dist/chunk-RBHWWZII.js +0 -1467
- package/dist/chunk-VN5HMBGB.js +0 -1467
package/dist/chunk-RBHWWZII.js
DELETED
|
@@ -1,1467 +0,0 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
-
}) : x)(function(x) {
|
|
10
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
-
});
|
|
13
|
-
var __commonJS = (cb, mod) => function __require2() {
|
|
14
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
15
|
-
};
|
|
16
|
-
var __copyProps = (to, from, except, desc) => {
|
|
17
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
-
for (let key of __getOwnPropNames(from))
|
|
19
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
-
}
|
|
22
|
-
return to;
|
|
23
|
-
};
|
|
24
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
-
mod
|
|
31
|
-
));
|
|
32
|
-
|
|
33
|
-
// src/narrowing.ts
|
|
34
|
-
import {
|
|
35
|
-
T,
|
|
36
|
-
narrowType,
|
|
37
|
-
subtractType,
|
|
38
|
-
getPrimitiveTypeOf,
|
|
39
|
-
typeValueEquals
|
|
40
|
-
} from "@nudo/core";
|
|
41
|
-
function narrow(test, env) {
|
|
42
|
-
if (test.type === "BinaryExpression" && test.operator === "===" && isTypeofExpr(test.left) && test.right.type === "StringLiteral") {
|
|
43
|
-
const varName = getTypeofTarget(test.left);
|
|
44
|
-
if (varName) {
|
|
45
|
-
return narrowByTypeof(varName, test.right.value, env);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (test.type === "BinaryExpression" && test.operator === "===" && test.left.type === "StringLiteral" && isTypeofExpr(test.right)) {
|
|
49
|
-
const varName = getTypeofTarget(test.right);
|
|
50
|
-
if (varName) {
|
|
51
|
-
return narrowByTypeof(varName, test.left.value, env);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if (test.type === "BinaryExpression" && test.operator === "!==" && isTypeofExpr(test.left) && test.right.type === "StringLiteral") {
|
|
55
|
-
const varName = getTypeofTarget(test.left);
|
|
56
|
-
if (varName) {
|
|
57
|
-
const [trueEnv, falseEnv] = narrowByTypeof(varName, test.right.value, env);
|
|
58
|
-
return [falseEnv, trueEnv];
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (test.type === "BinaryExpression" && test.operator === "!==" && test.left.type === "StringLiteral" && isTypeofExpr(test.right)) {
|
|
62
|
-
const varName = getTypeofTarget(test.right);
|
|
63
|
-
if (varName) {
|
|
64
|
-
const [trueEnv, falseEnv] = narrowByTypeof(varName, test.left.value, env);
|
|
65
|
-
return [falseEnv, trueEnv];
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (test.type === "BinaryExpression" && test.operator === "===" && test.left.type === "Identifier" && isLiteralNode(test.right)) {
|
|
69
|
-
return narrowByStrictEqual(test.left.name, getLiteralValue(test.right), env);
|
|
70
|
-
}
|
|
71
|
-
if (test.type === "BinaryExpression" && test.operator === "===" && isLiteralNode(test.left) && test.right.type === "Identifier") {
|
|
72
|
-
return narrowByStrictEqual(test.right.name, getLiteralValue(test.left), env);
|
|
73
|
-
}
|
|
74
|
-
if (test.type === "BinaryExpression" && test.operator === "!==" && test.left.type === "Identifier" && isLiteralNode(test.right)) {
|
|
75
|
-
const [trueEnv, falseEnv] = narrowByStrictEqual(test.left.name, getLiteralValue(test.right), env);
|
|
76
|
-
return [falseEnv, trueEnv];
|
|
77
|
-
}
|
|
78
|
-
if (test.type === "BinaryExpression" && test.operator === "!==" && isLiteralNode(test.left) && test.right.type === "Identifier") {
|
|
79
|
-
const [trueEnv, falseEnv] = narrowByStrictEqual(test.right.name, getLiteralValue(test.left), env);
|
|
80
|
-
return [falseEnv, trueEnv];
|
|
81
|
-
}
|
|
82
|
-
if (test.type === "BinaryExpression" && test.operator === "instanceof" && test.left.type === "Identifier" && test.right.type === "Identifier") {
|
|
83
|
-
return narrowByInstanceof(test.left.name, test.right.name, env);
|
|
84
|
-
}
|
|
85
|
-
if (test.type === "UnaryExpression" && test.operator === "!") {
|
|
86
|
-
const [trueEnv, falseEnv] = narrow(test.argument, env);
|
|
87
|
-
return [falseEnv, trueEnv];
|
|
88
|
-
}
|
|
89
|
-
return [env, env];
|
|
90
|
-
}
|
|
91
|
-
function isTypeofExpr(node) {
|
|
92
|
-
return node.type === "UnaryExpression" && node.operator === "typeof";
|
|
93
|
-
}
|
|
94
|
-
function getTypeofTarget(node) {
|
|
95
|
-
if (node.type === "UnaryExpression" && node.operator === "typeof" && node.argument.type === "Identifier") {
|
|
96
|
-
return node.argument.name;
|
|
97
|
-
}
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
function isLiteralNode(node) {
|
|
101
|
-
return node.type === "NumericLiteral" || node.type === "StringLiteral" || node.type === "BooleanLiteral" || node.type === "NullLiteral" || node.type === "Identifier" && node.name === "undefined";
|
|
102
|
-
}
|
|
103
|
-
function getLiteralValue(node) {
|
|
104
|
-
if (node.type === "NumericLiteral") return T.literal(node.value);
|
|
105
|
-
if (node.type === "StringLiteral") return T.literal(node.value);
|
|
106
|
-
if (node.type === "BooleanLiteral") return T.literal(node.value);
|
|
107
|
-
if (node.type === "NullLiteral") return T.null;
|
|
108
|
-
if (node.type === "Identifier" && node.name === "undefined") return T.undefined;
|
|
109
|
-
return T.unknown;
|
|
110
|
-
}
|
|
111
|
-
var typeofToPrimitive = {
|
|
112
|
-
number: T.number,
|
|
113
|
-
string: T.string,
|
|
114
|
-
boolean: T.boolean,
|
|
115
|
-
bigint: T.bigint,
|
|
116
|
-
symbol: T.symbol
|
|
117
|
-
};
|
|
118
|
-
function narrowByTypeof(varName, typeStr, env) {
|
|
119
|
-
const current = env.lookup(varName);
|
|
120
|
-
const targetPrimitive = typeofToPrimitive[typeStr];
|
|
121
|
-
const matchesPrimitive = (m) => {
|
|
122
|
-
const pt = getPrimitiveTypeOf(m);
|
|
123
|
-
return pt === typeStr;
|
|
124
|
-
};
|
|
125
|
-
if (targetPrimitive) {
|
|
126
|
-
const narrowed = narrowType(current, matchesPrimitive);
|
|
127
|
-
const excluded = subtractType(current, matchesPrimitive);
|
|
128
|
-
const trueEnv = env.extend({});
|
|
129
|
-
trueEnv.bind(varName, narrowed.kind === "never" ? targetPrimitive : narrowed);
|
|
130
|
-
const falseEnv = env.extend({});
|
|
131
|
-
falseEnv.bind(varName, excluded.kind === "never" ? current : excluded);
|
|
132
|
-
return [trueEnv, falseEnv];
|
|
133
|
-
}
|
|
134
|
-
if (typeStr === "object") {
|
|
135
|
-
const narrowed = narrowType(current, (m) => {
|
|
136
|
-
const pt = getPrimitiveTypeOf(m);
|
|
137
|
-
return pt === "object";
|
|
138
|
-
});
|
|
139
|
-
const excluded = subtractType(current, (m) => {
|
|
140
|
-
const pt = getPrimitiveTypeOf(m);
|
|
141
|
-
return pt === "object";
|
|
142
|
-
});
|
|
143
|
-
const trueEnv = env.extend({});
|
|
144
|
-
trueEnv.bind(varName, narrowed.kind === "never" ? current : narrowed);
|
|
145
|
-
const falseEnv = env.extend({});
|
|
146
|
-
falseEnv.bind(varName, excluded.kind === "never" ? current : excluded);
|
|
147
|
-
return [trueEnv, falseEnv];
|
|
148
|
-
}
|
|
149
|
-
if (typeStr === "function") {
|
|
150
|
-
const narrowed = narrowType(current, (m) => m.kind === "function");
|
|
151
|
-
const excluded = subtractType(current, (m) => m.kind === "function");
|
|
152
|
-
const trueEnv = env.extend({});
|
|
153
|
-
trueEnv.bind(varName, narrowed.kind === "never" ? current : narrowed);
|
|
154
|
-
const falseEnv = env.extend({});
|
|
155
|
-
falseEnv.bind(varName, excluded.kind === "never" ? current : excluded);
|
|
156
|
-
return [trueEnv, falseEnv];
|
|
157
|
-
}
|
|
158
|
-
return [env, env];
|
|
159
|
-
}
|
|
160
|
-
function narrowByInstanceof(varName, className, env) {
|
|
161
|
-
const current = env.lookup(varName);
|
|
162
|
-
const narrowed = narrowType(
|
|
163
|
-
current,
|
|
164
|
-
(m) => m.kind === "instance" && m.className === className
|
|
165
|
-
);
|
|
166
|
-
const excluded = subtractType(
|
|
167
|
-
current,
|
|
168
|
-
(m) => m.kind === "instance" && m.className === className
|
|
169
|
-
);
|
|
170
|
-
const trueEnv = env.extend({});
|
|
171
|
-
trueEnv.bind(varName, narrowed.kind === "never" ? T.instanceOf(className) : narrowed);
|
|
172
|
-
const falseEnv = env.extend({});
|
|
173
|
-
falseEnv.bind(varName, excluded.kind === "never" ? current : excluded);
|
|
174
|
-
return [trueEnv, falseEnv];
|
|
175
|
-
}
|
|
176
|
-
function narrowByStrictEqual(varName, literalTV, env) {
|
|
177
|
-
const current = env.lookup(varName);
|
|
178
|
-
const narrowed = narrowType(current, (m) => typeValueEquals(m, literalTV));
|
|
179
|
-
const excluded = subtractType(current, (m) => typeValueEquals(m, literalTV));
|
|
180
|
-
const trueEnv = env.extend({});
|
|
181
|
-
trueEnv.bind(varName, narrowed.kind === "never" ? literalTV : narrowed);
|
|
182
|
-
const falseEnv = env.extend({});
|
|
183
|
-
falseEnv.bind(varName, excluded.kind === "never" ? current : excluded);
|
|
184
|
-
return [trueEnv, falseEnv];
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// src/evaluator.ts
|
|
188
|
-
import {
|
|
189
|
-
T as T2,
|
|
190
|
-
simplifyUnion,
|
|
191
|
-
applyBinaryOp,
|
|
192
|
-
Ops,
|
|
193
|
-
createEnvironment,
|
|
194
|
-
typeValueEquals as typeValueEquals2,
|
|
195
|
-
typeValueToString,
|
|
196
|
-
isSubtypeOf
|
|
197
|
-
} from "@nudo/core";
|
|
198
|
-
var RETURN_SIGNAL = Symbol("ReturnSignal");
|
|
199
|
-
var BRANCH_SIGNAL = Symbol("BranchSignal");
|
|
200
|
-
var THROW_SIGNAL = Symbol("ThrowSignal");
|
|
201
|
-
function makeReturn(value) {
|
|
202
|
-
return { [RETURN_SIGNAL]: true, value };
|
|
203
|
-
}
|
|
204
|
-
function makeBranch(returnedValue, fallthroughEnv) {
|
|
205
|
-
return { [BRANCH_SIGNAL]: true, returnedValue, fallthroughEnv };
|
|
206
|
-
}
|
|
207
|
-
function makeThrow(thrown, loc) {
|
|
208
|
-
return { [THROW_SIGNAL]: true, thrown, loc };
|
|
209
|
-
}
|
|
210
|
-
function isReturn(v) {
|
|
211
|
-
return typeof v === "object" && v !== null && RETURN_SIGNAL in v;
|
|
212
|
-
}
|
|
213
|
-
function isBranch(v) {
|
|
214
|
-
return typeof v === "object" && v !== null && BRANCH_SIGNAL in v;
|
|
215
|
-
}
|
|
216
|
-
function isThrow(v) {
|
|
217
|
-
return typeof v === "object" && v !== null && THROW_SIGNAL in v;
|
|
218
|
-
}
|
|
219
|
-
var MEMO_IN_PROGRESS = Symbol("MemoInProgress");
|
|
220
|
-
var callMemo = /* @__PURE__ */ new Map();
|
|
221
|
-
function buildMemoKey(fn, args) {
|
|
222
|
-
const fnName = fn._memoize;
|
|
223
|
-
if (!fnName) return null;
|
|
224
|
-
const argsKey = args.map(typeValueToString).join(",");
|
|
225
|
-
return `${fnName}(${argsKey})`;
|
|
226
|
-
}
|
|
227
|
-
var moduleCache = /* @__PURE__ */ new Map();
|
|
228
|
-
function resetMemo() {
|
|
229
|
-
callMemo.clear();
|
|
230
|
-
moduleCache.clear();
|
|
231
|
-
}
|
|
232
|
-
function setModuleResolver(resolver) {
|
|
233
|
-
currentModuleResolver = resolver;
|
|
234
|
-
}
|
|
235
|
-
var currentModuleResolver = null;
|
|
236
|
-
var currentFileDir = "";
|
|
237
|
-
var _nodeTypeCollector = null;
|
|
238
|
-
function setNodeTypeCollector(collector) {
|
|
239
|
-
_nodeTypeCollector = collector;
|
|
240
|
-
}
|
|
241
|
-
function recordNodeType(node, tv) {
|
|
242
|
-
if (_nodeTypeCollector && node.loc) {
|
|
243
|
-
_nodeTypeCollector(node, tv);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
function distributeOverUnion(tv, fn) {
|
|
247
|
-
if (tv.kind === "union") {
|
|
248
|
-
return simplifyUnion(tv.members.map(fn));
|
|
249
|
-
}
|
|
250
|
-
return fn(tv);
|
|
251
|
-
}
|
|
252
|
-
function distributeBinaryOverUnion(left, right, fn) {
|
|
253
|
-
if (left.kind === "union" && right.kind === "union") {
|
|
254
|
-
return simplifyUnion(
|
|
255
|
-
left.members.flatMap((l) => right.members.map((r) => fn(l, r)))
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
if (left.kind === "union") {
|
|
259
|
-
return simplifyUnion(left.members.map((l) => fn(l, right)));
|
|
260
|
-
}
|
|
261
|
-
if (right.kind === "union") {
|
|
262
|
-
return simplifyUnion(right.members.map((r) => fn(left, r)));
|
|
263
|
-
}
|
|
264
|
-
return fn(left, right);
|
|
265
|
-
}
|
|
266
|
-
var _unreachableRanges = [];
|
|
267
|
-
function collectUnreachable(stmts, fromIndex) {
|
|
268
|
-
for (let j = fromIndex; j < stmts.length; j++) {
|
|
269
|
-
const s = stmts[j];
|
|
270
|
-
if (s.loc) {
|
|
271
|
-
_unreachableRanges.push({
|
|
272
|
-
start: { line: s.loc.start.line, column: s.loc.start.column },
|
|
273
|
-
end: { line: s.loc.end.line, column: s.loc.end.column }
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
function evaluateStatements(stmts, env) {
|
|
279
|
-
const returnValues = [];
|
|
280
|
-
let currentEnv = env;
|
|
281
|
-
let lastValue = T2.undefined;
|
|
282
|
-
for (let i = 0; i < stmts.length; i++) {
|
|
283
|
-
const stmt = stmts[i];
|
|
284
|
-
const result = evaluate(stmt, currentEnv);
|
|
285
|
-
if (isThrow(result)) {
|
|
286
|
-
collectUnreachable(stmts, i + 1);
|
|
287
|
-
return result;
|
|
288
|
-
}
|
|
289
|
-
if (isReturn(result)) {
|
|
290
|
-
returnValues.push(result.value);
|
|
291
|
-
collectUnreachable(stmts, i + 1);
|
|
292
|
-
return makeReturn(simplifyUnion(returnValues));
|
|
293
|
-
}
|
|
294
|
-
if (isBranch(result)) {
|
|
295
|
-
returnValues.push(result.returnedValue);
|
|
296
|
-
currentEnv = result.fallthroughEnv;
|
|
297
|
-
continue;
|
|
298
|
-
}
|
|
299
|
-
lastValue = result;
|
|
300
|
-
}
|
|
301
|
-
if (returnValues.length > 0) {
|
|
302
|
-
return makeBranch(simplifyUnion(returnValues), currentEnv);
|
|
303
|
-
}
|
|
304
|
-
return lastValue;
|
|
305
|
-
}
|
|
306
|
-
function describeParam(p) {
|
|
307
|
-
if (p.type === "Identifier") return p.name;
|
|
308
|
-
if (p.type === "AssignmentPattern" && p.left.type === "Identifier") return p.left.name;
|
|
309
|
-
if (p.type === "AssignmentPattern") return describeParam(p.left);
|
|
310
|
-
if (p.type === "RestElement") return `...${describeParam(p.argument)}`;
|
|
311
|
-
if (p.type === "ObjectPattern") {
|
|
312
|
-
const keys = p.properties.map((prop) => {
|
|
313
|
-
if (prop.type === "RestElement") return `...${describeParam(prop.argument)}`;
|
|
314
|
-
const key = prop.key?.type === "Identifier" ? prop.key.name : "?";
|
|
315
|
-
return key;
|
|
316
|
-
});
|
|
317
|
-
return `{ ${keys.join(", ")} }`;
|
|
318
|
-
}
|
|
319
|
-
if (p.type === "ArrayPattern") {
|
|
320
|
-
const elems = p.elements.map((e) => e ? describeParam(e) : "");
|
|
321
|
-
return `[${elems.join(", ")}]`;
|
|
322
|
-
}
|
|
323
|
-
return "_";
|
|
324
|
-
}
|
|
325
|
-
function evaluate(node, env) {
|
|
326
|
-
const result = evaluateNode(node, env);
|
|
327
|
-
if (_nodeTypeCollector && node.loc && !isReturn(result) && !isBranch(result) && !isThrow(result)) {
|
|
328
|
-
recordNodeType(node, result);
|
|
329
|
-
}
|
|
330
|
-
return result;
|
|
331
|
-
}
|
|
332
|
-
function evaluateNode(node, env) {
|
|
333
|
-
switch (node.type) {
|
|
334
|
-
case "File":
|
|
335
|
-
return evaluate(node.program, env);
|
|
336
|
-
case "Program":
|
|
337
|
-
return evaluateStatements(node.body, env);
|
|
338
|
-
case "ExpressionStatement":
|
|
339
|
-
return evaluate(node.expression, env);
|
|
340
|
-
case "NumericLiteral":
|
|
341
|
-
return T2.literal(node.value);
|
|
342
|
-
case "StringLiteral":
|
|
343
|
-
return T2.literal(node.value);
|
|
344
|
-
case "BooleanLiteral":
|
|
345
|
-
return T2.literal(node.value);
|
|
346
|
-
case "NullLiteral":
|
|
347
|
-
return T2.null;
|
|
348
|
-
case "Identifier": {
|
|
349
|
-
if (node.name === "undefined") return T2.undefined;
|
|
350
|
-
return env.lookup(node.name);
|
|
351
|
-
}
|
|
352
|
-
case "ThisExpression":
|
|
353
|
-
return env.lookup("this");
|
|
354
|
-
case "TemplateLiteral": {
|
|
355
|
-
if (node.expressions.length === 0 && node.quasis.length === 1) {
|
|
356
|
-
return T2.literal(node.quasis[0].value.cooked ?? node.quasis[0].value.raw);
|
|
357
|
-
}
|
|
358
|
-
const parts = [];
|
|
359
|
-
for (let i = 0; i < node.quasis.length; i++) {
|
|
360
|
-
const quasi = node.quasis[i];
|
|
361
|
-
const raw = quasi.value.cooked ?? quasi.value.raw;
|
|
362
|
-
if (raw) parts.push(T2.literal(raw));
|
|
363
|
-
if (i < node.expressions.length) {
|
|
364
|
-
const exprVal = evaluate(node.expressions[i], env);
|
|
365
|
-
if (isReturn(exprVal) || isBranch(exprVal) || isThrow(exprVal)) return exprVal;
|
|
366
|
-
parts.push(exprVal);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
const allLiteral = parts.every(
|
|
370
|
-
(p) => p.kind === "literal" && (typeof p.value === "string" || typeof p.value === "number")
|
|
371
|
-
);
|
|
372
|
-
if (allLiteral) {
|
|
373
|
-
return T2.literal(
|
|
374
|
-
parts.map((p) => p.kind === "literal" ? String(p.value) : "").join("")
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
return T2.string;
|
|
378
|
-
}
|
|
379
|
-
case "BinaryExpression": {
|
|
380
|
-
const leftVal = evaluate(node.left, env);
|
|
381
|
-
if (isReturn(leftVal) || isBranch(leftVal) || isThrow(leftVal)) return leftVal;
|
|
382
|
-
const rightVal = evaluate(node.right, env);
|
|
383
|
-
if (isReturn(rightVal) || isBranch(rightVal) || isThrow(rightVal)) return rightVal;
|
|
384
|
-
if (node.operator === "instanceof") {
|
|
385
|
-
return evaluateInstanceof(leftVal, rightVal, node.right, env);
|
|
386
|
-
}
|
|
387
|
-
return distributeBinaryOverUnion(
|
|
388
|
-
leftVal,
|
|
389
|
-
rightVal,
|
|
390
|
-
(l, r) => applyBinaryOp(node.operator, l, r)
|
|
391
|
-
);
|
|
392
|
-
}
|
|
393
|
-
case "UnaryExpression": {
|
|
394
|
-
const argVal = evaluate(node.argument, env);
|
|
395
|
-
if (isReturn(argVal) || isBranch(argVal) || isThrow(argVal)) return argVal;
|
|
396
|
-
if (node.operator === "typeof") {
|
|
397
|
-
return distributeOverUnion(argVal, (v) => Ops.typeof_(v));
|
|
398
|
-
}
|
|
399
|
-
if (node.operator === "!") {
|
|
400
|
-
return distributeOverUnion(argVal, (v) => Ops.not(v));
|
|
401
|
-
}
|
|
402
|
-
if (node.operator === "-") {
|
|
403
|
-
return distributeOverUnion(argVal, (v) => Ops.neg(v));
|
|
404
|
-
}
|
|
405
|
-
return T2.unknown;
|
|
406
|
-
}
|
|
407
|
-
case "LogicalExpression": {
|
|
408
|
-
const leftVal = evaluate(node.left, env);
|
|
409
|
-
if (isReturn(leftVal) || isBranch(leftVal) || isThrow(leftVal)) return leftVal;
|
|
410
|
-
if (node.operator === "&&") {
|
|
411
|
-
if (leftVal.kind === "literal" && !leftVal.value) return leftVal;
|
|
412
|
-
if (leftVal.kind === "literal" && leftVal.value) {
|
|
413
|
-
const rv2 = evaluate(node.right, env);
|
|
414
|
-
return isReturn(rv2) || isBranch(rv2) || isThrow(rv2) ? rv2 : rv2;
|
|
415
|
-
}
|
|
416
|
-
const rv = evaluate(node.right, env);
|
|
417
|
-
const rightTV = isReturn(rv) || isBranch(rv) || isThrow(rv) ? T2.unknown : rv;
|
|
418
|
-
return simplifyUnion([leftVal, rightTV]);
|
|
419
|
-
}
|
|
420
|
-
if (node.operator === "||") {
|
|
421
|
-
if (leftVal.kind === "literal" && leftVal.value) return leftVal;
|
|
422
|
-
if (leftVal.kind === "literal" && !leftVal.value) {
|
|
423
|
-
const rv2 = evaluate(node.right, env);
|
|
424
|
-
return isReturn(rv2) || isBranch(rv2) || isThrow(rv2) ? rv2 : rv2;
|
|
425
|
-
}
|
|
426
|
-
const rv = evaluate(node.right, env);
|
|
427
|
-
const rightTV = isReturn(rv) || isBranch(rv) || isThrow(rv) ? T2.unknown : rv;
|
|
428
|
-
return simplifyUnion([leftVal, rightTV]);
|
|
429
|
-
}
|
|
430
|
-
if (node.operator === "??") {
|
|
431
|
-
if (leftVal.kind === "literal" && leftVal.value !== null && leftVal.value !== void 0) {
|
|
432
|
-
return leftVal;
|
|
433
|
-
}
|
|
434
|
-
if (leftVal.kind === "literal" && (leftVal.value === null || leftVal.value === void 0)) {
|
|
435
|
-
const rv2 = evaluate(node.right, env);
|
|
436
|
-
return isReturn(rv2) || isBranch(rv2) || isThrow(rv2) ? rv2 : rv2;
|
|
437
|
-
}
|
|
438
|
-
const rv = evaluate(node.right, env);
|
|
439
|
-
const rightTV = isReturn(rv) || isBranch(rv) || isThrow(rv) ? T2.unknown : rv;
|
|
440
|
-
return simplifyUnion([leftVal, rightTV]);
|
|
441
|
-
}
|
|
442
|
-
return T2.unknown;
|
|
443
|
-
}
|
|
444
|
-
case "ConditionalExpression": {
|
|
445
|
-
const test = node.test;
|
|
446
|
-
const [trueEnv, falseEnv] = narrow(test, env);
|
|
447
|
-
const testVal = evaluate(test, env);
|
|
448
|
-
if (isReturn(testVal) || isBranch(testVal) || isThrow(testVal)) return testVal;
|
|
449
|
-
if (testVal.kind === "literal") {
|
|
450
|
-
return testVal.value ? evaluate(node.consequent, trueEnv) : evaluate(node.alternate, falseEnv);
|
|
451
|
-
}
|
|
452
|
-
const cResult = evaluate(node.consequent, trueEnv);
|
|
453
|
-
const aResult = evaluate(node.alternate, falseEnv);
|
|
454
|
-
const cVal = isReturn(cResult) ? cResult.value : isBranch(cResult) ? cResult.returnedValue : cResult;
|
|
455
|
-
const aVal = isReturn(aResult) ? aResult.value : isBranch(aResult) ? aResult.returnedValue : aResult;
|
|
456
|
-
return simplifyUnion([cVal, aVal]);
|
|
457
|
-
}
|
|
458
|
-
case "IfStatement": {
|
|
459
|
-
const test = node.test;
|
|
460
|
-
const [trueEnv, falseEnv] = narrow(test, env);
|
|
461
|
-
const testVal = evaluate(test, env);
|
|
462
|
-
if (isReturn(testVal) || isBranch(testVal) || isThrow(testVal)) return testVal;
|
|
463
|
-
if (testVal.kind === "literal") {
|
|
464
|
-
if (testVal.value) {
|
|
465
|
-
if (node.alternate?.loc) {
|
|
466
|
-
_unreachableRanges.push({
|
|
467
|
-
start: { line: node.alternate.loc.start.line, column: node.alternate.loc.start.column },
|
|
468
|
-
end: { line: node.alternate.loc.end.line, column: node.alternate.loc.end.column }
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
return evaluate(node.consequent, trueEnv);
|
|
472
|
-
}
|
|
473
|
-
if (node.consequent.loc) {
|
|
474
|
-
_unreachableRanges.push({
|
|
475
|
-
start: { line: node.consequent.loc.start.line, column: node.consequent.loc.start.column },
|
|
476
|
-
end: { line: node.consequent.loc.end.line, column: node.consequent.loc.end.column }
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
return node.alternate ? evaluate(node.alternate, falseEnv) : T2.undefined;
|
|
480
|
-
}
|
|
481
|
-
const consequentResult = evaluate(node.consequent, trueEnv);
|
|
482
|
-
const alternateResult = node.alternate ? evaluate(node.alternate, falseEnv) : null;
|
|
483
|
-
const cReturns = isReturn(consequentResult);
|
|
484
|
-
const cBranches = isBranch(consequentResult);
|
|
485
|
-
const cThrows = isThrow(consequentResult);
|
|
486
|
-
const aReturns = alternateResult !== null && isReturn(alternateResult);
|
|
487
|
-
const aBranches = alternateResult !== null && isBranch(alternateResult);
|
|
488
|
-
const aThrows = alternateResult !== null && isThrow(alternateResult);
|
|
489
|
-
if (cThrows && aThrows) {
|
|
490
|
-
return consequentResult;
|
|
491
|
-
}
|
|
492
|
-
if (cThrows && !node.alternate) {
|
|
493
|
-
return makeBranch(T2.never, falseEnv);
|
|
494
|
-
}
|
|
495
|
-
if (cThrows) {
|
|
496
|
-
const aVal2 = aReturns ? alternateResult.value : aBranches ? alternateResult.returnedValue : alternateResult;
|
|
497
|
-
return makeBranch(aVal2, falseEnv);
|
|
498
|
-
}
|
|
499
|
-
if (aThrows) {
|
|
500
|
-
const cVal2 = cReturns ? consequentResult.value : cBranches ? consequentResult.returnedValue : consequentResult;
|
|
501
|
-
return makeBranch(cVal2, trueEnv);
|
|
502
|
-
}
|
|
503
|
-
const cVal = cReturns ? consequentResult.value : cBranches ? consequentResult.returnedValue : consequentResult;
|
|
504
|
-
const aVal = aReturns ? alternateResult.value : aBranches ? alternateResult.returnedValue : alternateResult;
|
|
505
|
-
if (cReturns && aReturns) {
|
|
506
|
-
return makeReturn(simplifyUnion([cVal, aVal]));
|
|
507
|
-
}
|
|
508
|
-
if (cReturns && !node.alternate) {
|
|
509
|
-
return makeBranch(cVal, falseEnv);
|
|
510
|
-
}
|
|
511
|
-
if (cReturns && node.alternate) {
|
|
512
|
-
if (aReturns) {
|
|
513
|
-
return makeReturn(simplifyUnion([cVal, aVal]));
|
|
514
|
-
}
|
|
515
|
-
return makeBranch(cVal, falseEnv);
|
|
516
|
-
}
|
|
517
|
-
if (aReturns) {
|
|
518
|
-
return makeBranch(aVal, trueEnv);
|
|
519
|
-
}
|
|
520
|
-
const allVals = [cVal];
|
|
521
|
-
if (aVal !== null) allVals.push(aVal);
|
|
522
|
-
else allVals.push(T2.undefined);
|
|
523
|
-
return simplifyUnion(allVals);
|
|
524
|
-
}
|
|
525
|
-
case "BlockStatement": {
|
|
526
|
-
const blockEnv = env.extend({});
|
|
527
|
-
const result = evaluateStatements(node.body, blockEnv);
|
|
528
|
-
return result;
|
|
529
|
-
}
|
|
530
|
-
case "ReturnStatement": {
|
|
531
|
-
const arg = node.argument;
|
|
532
|
-
if (!arg) return makeReturn(T2.undefined);
|
|
533
|
-
const val = evaluate(arg, env);
|
|
534
|
-
if (isReturn(val) || isBranch(val) || isThrow(val)) return val;
|
|
535
|
-
return makeReturn(val);
|
|
536
|
-
}
|
|
537
|
-
case "VariableDeclaration": {
|
|
538
|
-
for (const decl of node.declarations) {
|
|
539
|
-
const init = decl.init ? evaluate(decl.init, env) : T2.undefined;
|
|
540
|
-
if (isReturn(init) || isBranch(init) || isThrow(init)) return init;
|
|
541
|
-
bindPattern(decl.id, init, env);
|
|
542
|
-
if (decl.id.type === "Identifier") {
|
|
543
|
-
recordNodeType(decl.id, init);
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
return T2.undefined;
|
|
547
|
-
}
|
|
548
|
-
case "AssignmentExpression": {
|
|
549
|
-
if (node.left.type === "Identifier") {
|
|
550
|
-
const val = evaluate(node.right, env);
|
|
551
|
-
if (isReturn(val) || isBranch(val) || isThrow(val)) return val;
|
|
552
|
-
if (!env.update(node.left.name, val)) {
|
|
553
|
-
env.bind(node.left.name, val);
|
|
554
|
-
}
|
|
555
|
-
return val;
|
|
556
|
-
}
|
|
557
|
-
if (node.left.type === "MemberExpression") {
|
|
558
|
-
const val = evaluate(node.right, env);
|
|
559
|
-
if (isReturn(val) || isBranch(val) || isThrow(val)) return val;
|
|
560
|
-
const objVal = evaluate(node.left.object, env);
|
|
561
|
-
if (isReturn(objVal) || isBranch(objVal) || isThrow(objVal)) return val;
|
|
562
|
-
if (objVal.kind === "object") {
|
|
563
|
-
const propName = getMemberKey(node.left, env);
|
|
564
|
-
if (propName !== null) {
|
|
565
|
-
objVal.properties[propName] = val;
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
if (objVal.kind === "tuple" || objVal.kind === "array") {
|
|
569
|
-
const propVal = node.left.computed ? evaluate(node.left.property, env) : null;
|
|
570
|
-
if (objVal.kind === "tuple" && propVal && !isReturn(propVal) && !isBranch(propVal) && propVal.kind === "literal" && typeof propVal.value === "number") {
|
|
571
|
-
objVal.elements[propVal.value] = val;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
return val;
|
|
575
|
-
}
|
|
576
|
-
if (node.left.type === "ObjectPattern" || node.left.type === "ArrayPattern") {
|
|
577
|
-
const val = evaluate(node.right, env);
|
|
578
|
-
if (isReturn(val) || isBranch(val) || isThrow(val)) return val;
|
|
579
|
-
bindPattern(node.left, val, env);
|
|
580
|
-
return val;
|
|
581
|
-
}
|
|
582
|
-
return T2.unknown;
|
|
583
|
-
}
|
|
584
|
-
case "ForOfStatement": {
|
|
585
|
-
const rightVal = evaluate(node.right, env);
|
|
586
|
-
if (isReturn(rightVal) || isBranch(rightVal) || isThrow(rightVal)) return rightVal;
|
|
587
|
-
return evaluateForOf(node, rightVal, env);
|
|
588
|
-
}
|
|
589
|
-
case "ForInStatement": {
|
|
590
|
-
const rightVal = evaluate(node.right, env);
|
|
591
|
-
if (isReturn(rightVal) || isBranch(rightVal) || isThrow(rightVal)) return rightVal;
|
|
592
|
-
return evaluateForIn(node, rightVal, env);
|
|
593
|
-
}
|
|
594
|
-
case "ForStatement": {
|
|
595
|
-
if (node.init) {
|
|
596
|
-
const initResult = evaluate(node.init, env);
|
|
597
|
-
if (isReturn(initResult) || isBranch(initResult) || isThrow(initResult)) return initResult;
|
|
598
|
-
}
|
|
599
|
-
return T2.undefined;
|
|
600
|
-
}
|
|
601
|
-
case "WhileStatement":
|
|
602
|
-
return T2.undefined;
|
|
603
|
-
case "FunctionDeclaration": {
|
|
604
|
-
if (!node.id) return T2.undefined;
|
|
605
|
-
const paramNames = node.params.map(describeParam);
|
|
606
|
-
const fnType = T2.fn(paramNames, node.body, env);
|
|
607
|
-
fnType._paramPatterns = node.params;
|
|
608
|
-
if (node.async) fnType._async = true;
|
|
609
|
-
env.bind(node.id.name, fnType);
|
|
610
|
-
return T2.undefined;
|
|
611
|
-
}
|
|
612
|
-
case "FunctionExpression":
|
|
613
|
-
case "ArrowFunctionExpression": {
|
|
614
|
-
const paramNames = node.params.map(describeParam);
|
|
615
|
-
const body = node.body;
|
|
616
|
-
const fnType = T2.fn(paramNames, body, env);
|
|
617
|
-
fnType._paramPatterns = node.params;
|
|
618
|
-
if (node.async) fnType._async = true;
|
|
619
|
-
return fnType;
|
|
620
|
-
}
|
|
621
|
-
case "AwaitExpression": {
|
|
622
|
-
const argVal = evaluate(node.argument, env);
|
|
623
|
-
if (isReturn(argVal) || isBranch(argVal) || isThrow(argVal)) return argVal;
|
|
624
|
-
return distributeOverUnion(
|
|
625
|
-
argVal,
|
|
626
|
-
(v) => v.kind === "promise" ? v.value : v
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
case "ClassDeclaration": {
|
|
630
|
-
return evaluateClassDeclaration(node, env);
|
|
631
|
-
}
|
|
632
|
-
case "ImportDeclaration": {
|
|
633
|
-
return evaluateImportDeclaration(node, env);
|
|
634
|
-
}
|
|
635
|
-
case "ExportNamedDeclaration": {
|
|
636
|
-
if (node.declaration) {
|
|
637
|
-
const result = evaluate(node.declaration, env);
|
|
638
|
-
if (isReturn(result) || isBranch(result) || isThrow(result)) return result;
|
|
639
|
-
if (node.declaration.type === "VariableDeclaration") {
|
|
640
|
-
for (const decl of node.declaration.declarations) {
|
|
641
|
-
if (decl.id.type === "Identifier") {
|
|
642
|
-
const val = env.lookup(decl.id.name);
|
|
643
|
-
env.bind(`__export_${decl.id.name}`, val);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
} else if (node.declaration.type === "FunctionDeclaration" && node.declaration.id) {
|
|
647
|
-
const val = env.lookup(node.declaration.id.name);
|
|
648
|
-
env.bind(`__export_${node.declaration.id.name}`, val);
|
|
649
|
-
} else if (node.declaration.type === "ClassDeclaration" && node.declaration.id) {
|
|
650
|
-
const val = env.lookup(node.declaration.id.name);
|
|
651
|
-
env.bind(`__export_${node.declaration.id.name}`, val);
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
if (node.specifiers) {
|
|
655
|
-
for (const spec of node.specifiers) {
|
|
656
|
-
if (spec.type === "ExportSpecifier") {
|
|
657
|
-
const localName = spec.local.type === "Identifier" ? spec.local.name : null;
|
|
658
|
-
const exportedName = spec.exported.type === "Identifier" ? spec.exported.name : null;
|
|
659
|
-
if (localName && exportedName) {
|
|
660
|
-
env.bind(`__export_${exportedName}`, env.lookup(localName));
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
return T2.undefined;
|
|
666
|
-
}
|
|
667
|
-
case "ExportDefaultDeclaration": {
|
|
668
|
-
const decl = node.declaration;
|
|
669
|
-
const result = evaluate(decl, env);
|
|
670
|
-
if (isReturn(result) || isBranch(result) || isThrow(result)) return result;
|
|
671
|
-
if (decl.type === "FunctionDeclaration" && decl.id) {
|
|
672
|
-
env.bind(`__export_default`, env.lookup(decl.id.name));
|
|
673
|
-
} else if (decl.type === "ClassDeclaration" && decl.id) {
|
|
674
|
-
env.bind(`__export_default`, env.lookup(decl.id.name));
|
|
675
|
-
} else {
|
|
676
|
-
env.bind(`__export_default`, result);
|
|
677
|
-
}
|
|
678
|
-
return T2.undefined;
|
|
679
|
-
}
|
|
680
|
-
case "CallExpression": {
|
|
681
|
-
const callee = node.callee;
|
|
682
|
-
if (callee.type === "MemberExpression") {
|
|
683
|
-
const methodResult = evaluateMethodCall(callee, node.arguments, env);
|
|
684
|
-
if (methodResult !== null) return methodResult;
|
|
685
|
-
}
|
|
686
|
-
const calleeVal = evaluate(callee, env);
|
|
687
|
-
if (isReturn(calleeVal) || isBranch(calleeVal) || isThrow(calleeVal)) return calleeVal;
|
|
688
|
-
const argVals = evaluateArgs(node.arguments, env);
|
|
689
|
-
if (isReturn(argVals) || isBranch(argVals) || isThrow(argVals)) return argVals;
|
|
690
|
-
if (calleeVal.kind === "function") {
|
|
691
|
-
const full = callFunctionFull(calleeVal, argVals);
|
|
692
|
-
if (full.value.kind === "never" && full.throws.kind !== "never") {
|
|
693
|
-
const callLoc = node.loc ? {
|
|
694
|
-
start: { line: node.loc.start.line, column: node.loc.start.column },
|
|
695
|
-
end: { line: node.loc.end.line, column: node.loc.end.column }
|
|
696
|
-
} : full.throwLoc;
|
|
697
|
-
return makeThrow(full.throws, callLoc);
|
|
698
|
-
}
|
|
699
|
-
return full.value;
|
|
700
|
-
}
|
|
701
|
-
return distributeOverUnion(calleeVal, (fn) => {
|
|
702
|
-
if (fn.kind !== "function") return T2.unknown;
|
|
703
|
-
return callFunction(fn, argVals);
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
case "MemberExpression": {
|
|
707
|
-
const objVal = evaluate(node.object, env);
|
|
708
|
-
if (isReturn(objVal) || isBranch(objVal) || isThrow(objVal)) return objVal;
|
|
709
|
-
if (node.computed) {
|
|
710
|
-
const propVal = evaluate(node.property, env);
|
|
711
|
-
if (isReturn(propVal) || isBranch(propVal) || isThrow(propVal)) return propVal;
|
|
712
|
-
return distributeOverUnion(objVal, (obj) => {
|
|
713
|
-
if (obj.kind === "object" && propVal.kind === "literal" && typeof propVal.value === "string") {
|
|
714
|
-
return obj.properties[propVal.value] ?? T2.undefined;
|
|
715
|
-
}
|
|
716
|
-
if ((obj.kind === "array" || obj.kind === "tuple") && propVal.kind === "literal" && typeof propVal.value === "number") {
|
|
717
|
-
if (obj.kind === "tuple") return obj.elements[propVal.value] ?? T2.undefined;
|
|
718
|
-
return obj.element;
|
|
719
|
-
}
|
|
720
|
-
return T2.unknown;
|
|
721
|
-
});
|
|
722
|
-
}
|
|
723
|
-
if (node.property.type === "Identifier") {
|
|
724
|
-
const propName = node.property.name;
|
|
725
|
-
return distributeOverUnion(objVal, (obj) => {
|
|
726
|
-
if (obj.kind === "object") return obj.properties[propName] ?? T2.undefined;
|
|
727
|
-
if (obj.kind === "instance") return obj.properties[propName] ?? T2.undefined;
|
|
728
|
-
if (propName === "length" && (obj.kind === "array" || obj.kind === "tuple")) {
|
|
729
|
-
return obj.kind === "tuple" ? T2.literal(obj.elements.length) : T2.number;
|
|
730
|
-
}
|
|
731
|
-
return T2.unknown;
|
|
732
|
-
});
|
|
733
|
-
}
|
|
734
|
-
return T2.unknown;
|
|
735
|
-
}
|
|
736
|
-
case "ObjectExpression": {
|
|
737
|
-
const props = {};
|
|
738
|
-
for (const prop of node.properties) {
|
|
739
|
-
if (prop.type === "ObjectProperty") {
|
|
740
|
-
const key = prop.computed ? (() => {
|
|
741
|
-
const kv = evaluate(prop.key, env);
|
|
742
|
-
return !isReturn(kv) && !isBranch(kv) && !isThrow(kv) && kv.kind === "literal" && typeof kv.value === "string" ? kv.value : null;
|
|
743
|
-
})() : prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "StringLiteral" ? prop.key.value : null;
|
|
744
|
-
if (key) {
|
|
745
|
-
const val = evaluate(prop.value, env);
|
|
746
|
-
if (isReturn(val) || isBranch(val) || isThrow(val)) return val;
|
|
747
|
-
props[key] = val;
|
|
748
|
-
}
|
|
749
|
-
} else if (prop.type === "SpreadElement") {
|
|
750
|
-
const spreadVal = evaluate(prop.argument, env);
|
|
751
|
-
if (isReturn(spreadVal) || isBranch(spreadVal) || isThrow(spreadVal)) return spreadVal;
|
|
752
|
-
if (spreadVal.kind === "object") {
|
|
753
|
-
Object.assign(props, spreadVal.properties);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
return T2.object(props);
|
|
758
|
-
}
|
|
759
|
-
case "ArrayExpression": {
|
|
760
|
-
const elements = [];
|
|
761
|
-
for (const elem of node.elements) {
|
|
762
|
-
if (!elem) {
|
|
763
|
-
elements.push(T2.undefined);
|
|
764
|
-
continue;
|
|
765
|
-
}
|
|
766
|
-
if (elem.type === "SpreadElement") {
|
|
767
|
-
const spreadVal = evaluate(elem.argument, env);
|
|
768
|
-
if (isReturn(spreadVal) || isBranch(spreadVal) || isThrow(spreadVal)) return spreadVal;
|
|
769
|
-
if (spreadVal.kind === "tuple") {
|
|
770
|
-
elements.push(...spreadVal.elements);
|
|
771
|
-
} else if (spreadVal.kind === "array") {
|
|
772
|
-
return T2.array(simplifyUnion([...elements, spreadVal.element]));
|
|
773
|
-
} else {
|
|
774
|
-
elements.push(T2.unknown);
|
|
775
|
-
}
|
|
776
|
-
continue;
|
|
777
|
-
}
|
|
778
|
-
const val = evaluate(elem, env);
|
|
779
|
-
if (isReturn(val) || isBranch(val) || isThrow(val)) return val;
|
|
780
|
-
elements.push(val);
|
|
781
|
-
}
|
|
782
|
-
return T2.tuple(elements);
|
|
783
|
-
}
|
|
784
|
-
case "ThrowStatement": {
|
|
785
|
-
const argVal = node.argument ? evaluate(node.argument, env) : T2.undefined;
|
|
786
|
-
if (isReturn(argVal) || isBranch(argVal) || isThrow(argVal)) return argVal;
|
|
787
|
-
const throwLoc = node.loc ? {
|
|
788
|
-
start: { line: node.loc.start.line, column: node.loc.start.column },
|
|
789
|
-
end: { line: node.loc.end.line, column: node.loc.end.column }
|
|
790
|
-
} : void 0;
|
|
791
|
-
return makeThrow(argVal, throwLoc);
|
|
792
|
-
}
|
|
793
|
-
case "TryStatement": {
|
|
794
|
-
return evaluateTryStatement(node, env);
|
|
795
|
-
}
|
|
796
|
-
case "NewExpression": {
|
|
797
|
-
return evaluateNewExpression(node, env);
|
|
798
|
-
}
|
|
799
|
-
case "SwitchStatement": {
|
|
800
|
-
return evaluateSwitchStatement(node, env);
|
|
801
|
-
}
|
|
802
|
-
case "UpdateExpression": {
|
|
803
|
-
if (node.argument.type === "Identifier") {
|
|
804
|
-
const current = env.lookup(node.argument.name);
|
|
805
|
-
if (current.kind === "literal" && typeof current.value === "number") {
|
|
806
|
-
const newVal = node.operator === "++" ? T2.literal(current.value + 1) : T2.literal(current.value - 1);
|
|
807
|
-
if (!env.update(node.argument.name, newVal)) {
|
|
808
|
-
env.bind(node.argument.name, newVal);
|
|
809
|
-
}
|
|
810
|
-
return node.prefix ? newVal : current;
|
|
811
|
-
}
|
|
812
|
-
if (!env.update(node.argument.name, T2.number)) {
|
|
813
|
-
env.bind(node.argument.name, T2.number);
|
|
814
|
-
}
|
|
815
|
-
return T2.number;
|
|
816
|
-
}
|
|
817
|
-
return T2.number;
|
|
818
|
-
}
|
|
819
|
-
default:
|
|
820
|
-
return T2.unknown;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
function getMemberKey(node, env) {
|
|
824
|
-
if (!node.computed && node.property.type === "Identifier") {
|
|
825
|
-
return node.property.name;
|
|
826
|
-
}
|
|
827
|
-
if (node.computed) {
|
|
828
|
-
const propVal = evaluate(node.property, env);
|
|
829
|
-
if (!isReturn(propVal) && !isBranch(propVal) && !isThrow(propVal) && propVal.kind === "literal") {
|
|
830
|
-
return String(propVal.value);
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
return null;
|
|
834
|
-
}
|
|
835
|
-
function bindPattern(pattern, value, env) {
|
|
836
|
-
if (pattern.type === "Identifier") {
|
|
837
|
-
env.bind(pattern.name, value);
|
|
838
|
-
return;
|
|
839
|
-
}
|
|
840
|
-
if (pattern.type === "AssignmentPattern") {
|
|
841
|
-
const defaultVal = evaluate(pattern.right, env);
|
|
842
|
-
const resolved = value.kind === "literal" && value.value === void 0 ? !isReturn(defaultVal) && !isBranch(defaultVal) ? defaultVal : T2.unknown : value;
|
|
843
|
-
bindPattern(pattern.left, resolved, env);
|
|
844
|
-
return;
|
|
845
|
-
}
|
|
846
|
-
if (pattern.type === "ObjectPattern") {
|
|
847
|
-
const restKeys = [];
|
|
848
|
-
for (const prop of pattern.properties) {
|
|
849
|
-
if (prop.type === "RestElement") {
|
|
850
|
-
if (value.kind === "object") {
|
|
851
|
-
const remaining = {};
|
|
852
|
-
for (const [k, v] of Object.entries(value.properties)) {
|
|
853
|
-
if (!restKeys.includes(k)) remaining[k] = v;
|
|
854
|
-
}
|
|
855
|
-
bindPattern(prop.argument, T2.object(remaining), env);
|
|
856
|
-
} else {
|
|
857
|
-
bindPattern(prop.argument, T2.object({}), env);
|
|
858
|
-
}
|
|
859
|
-
continue;
|
|
860
|
-
}
|
|
861
|
-
if (prop.type !== "ObjectProperty") continue;
|
|
862
|
-
const key = prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "StringLiteral" ? prop.key.value : null;
|
|
863
|
-
if (!key) continue;
|
|
864
|
-
restKeys.push(key);
|
|
865
|
-
const propVal = value.kind === "object" ? value.properties[key] ?? T2.undefined : T2.unknown;
|
|
866
|
-
bindPattern(prop.value, propVal, env);
|
|
867
|
-
}
|
|
868
|
-
return;
|
|
869
|
-
}
|
|
870
|
-
if (pattern.type === "ArrayPattern") {
|
|
871
|
-
for (let i = 0; i < pattern.elements.length; i++) {
|
|
872
|
-
const elem = pattern.elements[i];
|
|
873
|
-
if (!elem) continue;
|
|
874
|
-
if (elem.type === "RestElement") {
|
|
875
|
-
if (value.kind === "tuple") {
|
|
876
|
-
bindPattern(elem.argument, T2.tuple(value.elements.slice(i)), env);
|
|
877
|
-
} else if (value.kind === "array") {
|
|
878
|
-
bindPattern(elem.argument, value, env);
|
|
879
|
-
} else {
|
|
880
|
-
bindPattern(elem.argument, T2.tuple([]), env);
|
|
881
|
-
}
|
|
882
|
-
continue;
|
|
883
|
-
}
|
|
884
|
-
const elemVal = value.kind === "tuple" ? value.elements[i] ?? T2.undefined : value.kind === "array" ? value.element : T2.unknown;
|
|
885
|
-
bindPattern(elem, elemVal, env);
|
|
886
|
-
}
|
|
887
|
-
return;
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
function evaluateArgs(args, env) {
|
|
891
|
-
const result = [];
|
|
892
|
-
for (const arg of args) {
|
|
893
|
-
if (arg.type === "SpreadElement") {
|
|
894
|
-
const spreadVal = evaluate(arg.argument, env);
|
|
895
|
-
if (isReturn(spreadVal) || isBranch(spreadVal) || isThrow(spreadVal)) return spreadVal;
|
|
896
|
-
if (spreadVal.kind === "tuple") {
|
|
897
|
-
result.push(...spreadVal.elements);
|
|
898
|
-
} else if (spreadVal.kind === "array") {
|
|
899
|
-
result.push(spreadVal.element);
|
|
900
|
-
} else {
|
|
901
|
-
result.push(T2.unknown);
|
|
902
|
-
}
|
|
903
|
-
continue;
|
|
904
|
-
}
|
|
905
|
-
const v = evaluate(arg, env);
|
|
906
|
-
if (isReturn(v) || isBranch(v) || isThrow(v)) return v;
|
|
907
|
-
result.push(v);
|
|
908
|
-
}
|
|
909
|
-
return result;
|
|
910
|
-
}
|
|
911
|
-
function evaluateMethodCall(callee, args, env) {
|
|
912
|
-
const objVal = evaluate(callee.object, env);
|
|
913
|
-
if (isReturn(objVal) || isBranch(objVal) || isThrow(objVal)) return objVal;
|
|
914
|
-
const methodName = !callee.computed && callee.property.type === "Identifier" ? callee.property.name : null;
|
|
915
|
-
if (!methodName) return null;
|
|
916
|
-
if (callee.object.type === "Identifier" && callee.object.name === "Object" && args.length >= 1) {
|
|
917
|
-
const argVal = evaluate(args[0], env);
|
|
918
|
-
if (isReturn(argVal) || isBranch(argVal) || isThrow(argVal)) return argVal;
|
|
919
|
-
return evaluateObjectStaticMethod(methodName, argVal);
|
|
920
|
-
}
|
|
921
|
-
if (objVal.kind === "array" || objVal.kind === "tuple") {
|
|
922
|
-
return evaluateArrayMethod(objVal, methodName, args, env);
|
|
923
|
-
}
|
|
924
|
-
return null;
|
|
925
|
-
}
|
|
926
|
-
function evaluateObjectStaticMethod(method, obj) {
|
|
927
|
-
if (obj.kind !== "object") {
|
|
928
|
-
if (method === "keys") return T2.array(T2.string);
|
|
929
|
-
if (method === "values") return T2.array(T2.unknown);
|
|
930
|
-
if (method === "entries") return T2.array(T2.tuple([T2.string, T2.unknown]));
|
|
931
|
-
return null;
|
|
932
|
-
}
|
|
933
|
-
const keys = Object.keys(obj.properties);
|
|
934
|
-
const values = Object.values(obj.properties);
|
|
935
|
-
if (method === "keys") {
|
|
936
|
-
return T2.tuple(keys.map((k) => T2.literal(k)));
|
|
937
|
-
}
|
|
938
|
-
if (method === "values") {
|
|
939
|
-
return T2.tuple(values);
|
|
940
|
-
}
|
|
941
|
-
if (method === "entries") {
|
|
942
|
-
return T2.tuple(
|
|
943
|
-
keys.map((k) => T2.tuple([T2.literal(k), obj.properties[k]]))
|
|
944
|
-
);
|
|
945
|
-
}
|
|
946
|
-
return null;
|
|
947
|
-
}
|
|
948
|
-
function evaluateArrayMethod(arr, method, args, env) {
|
|
949
|
-
const argVals = evaluateArgs(args, env);
|
|
950
|
-
if (isReturn(argVals) || isBranch(argVals) || isThrow(argVals)) return argVals;
|
|
951
|
-
const callbackFn = argVals[0];
|
|
952
|
-
if (method === "push") {
|
|
953
|
-
if (arr.kind === "tuple") {
|
|
954
|
-
arr.elements.push(...argVals);
|
|
955
|
-
return T2.literal(arr.elements.length);
|
|
956
|
-
}
|
|
957
|
-
return T2.number;
|
|
958
|
-
}
|
|
959
|
-
if (method === "length") {
|
|
960
|
-
return arr.kind === "tuple" ? T2.literal(arr.elements.length) : T2.number;
|
|
961
|
-
}
|
|
962
|
-
if (method === "indexOf" || method === "lastIndexOf") {
|
|
963
|
-
return T2.number;
|
|
964
|
-
}
|
|
965
|
-
if (method === "includes") {
|
|
966
|
-
if (arr.kind === "tuple" && argVals[0]?.kind === "literal") {
|
|
967
|
-
const searchVal = argVals[0];
|
|
968
|
-
const found = arr.elements.some((e) => typeValueEquals2(e, searchVal));
|
|
969
|
-
return T2.literal(found);
|
|
970
|
-
}
|
|
971
|
-
return T2.boolean;
|
|
972
|
-
}
|
|
973
|
-
if (method === "join") {
|
|
974
|
-
return T2.string;
|
|
975
|
-
}
|
|
976
|
-
if (method === "concat") {
|
|
977
|
-
if (arr.kind === "tuple") {
|
|
978
|
-
const otherElements = [];
|
|
979
|
-
for (const a of argVals) {
|
|
980
|
-
if (a.kind === "tuple") otherElements.push(...a.elements);
|
|
981
|
-
else if (a.kind === "array") return T2.array(simplifyUnion([...arr.elements, a.element]));
|
|
982
|
-
else otherElements.push(a);
|
|
983
|
-
}
|
|
984
|
-
return T2.tuple([...arr.elements, ...otherElements]);
|
|
985
|
-
}
|
|
986
|
-
return T2.array(arr.element);
|
|
987
|
-
}
|
|
988
|
-
if (method === "slice") {
|
|
989
|
-
if (arr.kind === "tuple") {
|
|
990
|
-
const start = argVals[0];
|
|
991
|
-
const end = argVals[1];
|
|
992
|
-
const startIdx = start?.kind === "literal" && typeof start.value === "number" ? start.value : 0;
|
|
993
|
-
const endIdx = end?.kind === "literal" && typeof end.value === "number" ? end.value : arr.elements.length;
|
|
994
|
-
return T2.tuple(arr.elements.slice(startIdx, endIdx));
|
|
995
|
-
}
|
|
996
|
-
return T2.array(arr.element);
|
|
997
|
-
}
|
|
998
|
-
if (!callbackFn || callbackFn.kind !== "function") {
|
|
999
|
-
if (method === "map") return arr.kind === "tuple" ? T2.tuple(arr.elements.map(() => T2.unknown)) : T2.array(T2.unknown);
|
|
1000
|
-
if (method === "filter") return arr.kind === "tuple" ? T2.array(simplifyUnion(arr.elements)) : arr;
|
|
1001
|
-
if (method === "find") return arr.kind === "tuple" ? simplifyUnion([...arr.elements, T2.undefined]) : simplifyUnion([arr.element, T2.undefined]);
|
|
1002
|
-
if (method === "some" || method === "every") return T2.boolean;
|
|
1003
|
-
if (method === "reduce") return argVals[1] ?? T2.unknown;
|
|
1004
|
-
if (method === "forEach") return T2.undefined;
|
|
1005
|
-
if (method === "flatMap") return T2.array(T2.unknown);
|
|
1006
|
-
return null;
|
|
1007
|
-
}
|
|
1008
|
-
const fn = callbackFn;
|
|
1009
|
-
if (method === "map") {
|
|
1010
|
-
if (arr.kind === "tuple") {
|
|
1011
|
-
const mapped = arr.elements.map(
|
|
1012
|
-
(el, i) => callFunction(fn, [el, T2.literal(i), arr])
|
|
1013
|
-
);
|
|
1014
|
-
return T2.tuple(mapped);
|
|
1015
|
-
}
|
|
1016
|
-
return T2.array(callFunction(fn, [arr.element, T2.number, arr]));
|
|
1017
|
-
}
|
|
1018
|
-
if (method === "filter") {
|
|
1019
|
-
if (arr.kind === "tuple") {
|
|
1020
|
-
const kept = [];
|
|
1021
|
-
for (let i = 0; i < arr.elements.length; i++) {
|
|
1022
|
-
const result = callFunction(fn, [arr.elements[i], T2.literal(i), arr]);
|
|
1023
|
-
if (result.kind === "literal" && !result.value) continue;
|
|
1024
|
-
kept.push(arr.elements[i]);
|
|
1025
|
-
}
|
|
1026
|
-
if (kept.length === 0) return T2.tuple([]);
|
|
1027
|
-
return T2.array(simplifyUnion(kept));
|
|
1028
|
-
}
|
|
1029
|
-
return T2.array(arr.element);
|
|
1030
|
-
}
|
|
1031
|
-
if (method === "reduce") {
|
|
1032
|
-
const init = argVals[1];
|
|
1033
|
-
if (arr.kind === "tuple") {
|
|
1034
|
-
let acc2 = init ?? arr.elements[0] ?? T2.unknown;
|
|
1035
|
-
const startIdx = init ? 0 : 1;
|
|
1036
|
-
for (let i = startIdx; i < arr.elements.length; i++) {
|
|
1037
|
-
acc2 = callFunction(fn, [acc2, arr.elements[i], T2.literal(i), arr]);
|
|
1038
|
-
}
|
|
1039
|
-
return acc2;
|
|
1040
|
-
}
|
|
1041
|
-
const acc = init ?? arr.element;
|
|
1042
|
-
return callFunction(fn, [acc, arr.element, T2.number, arr]);
|
|
1043
|
-
}
|
|
1044
|
-
if (method === "find") {
|
|
1045
|
-
const elementType = arr.kind === "tuple" ? simplifyUnion(arr.elements) : arr.element;
|
|
1046
|
-
return simplifyUnion([elementType, T2.undefined]);
|
|
1047
|
-
}
|
|
1048
|
-
if (method === "some" || method === "every") {
|
|
1049
|
-
if (arr.kind === "tuple") {
|
|
1050
|
-
const results = arr.elements.map(
|
|
1051
|
-
(el, i) => callFunction(fn, [el, T2.literal(i), arr])
|
|
1052
|
-
);
|
|
1053
|
-
const allLiteral = results.every((r) => r.kind === "literal");
|
|
1054
|
-
if (allLiteral) {
|
|
1055
|
-
const boolVals = results.map((r) => !!r.value);
|
|
1056
|
-
return T2.literal(method === "some" ? boolVals.some(Boolean) : boolVals.every(Boolean));
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
return T2.boolean;
|
|
1060
|
-
}
|
|
1061
|
-
if (method === "forEach") {
|
|
1062
|
-
if (arr.kind === "tuple") {
|
|
1063
|
-
arr.elements.forEach((el, i) => callFunction(fn, [el, T2.literal(i), arr]));
|
|
1064
|
-
} else {
|
|
1065
|
-
callFunction(fn, [arr.element, T2.number, arr]);
|
|
1066
|
-
}
|
|
1067
|
-
return T2.undefined;
|
|
1068
|
-
}
|
|
1069
|
-
if (method === "flatMap") {
|
|
1070
|
-
if (arr.kind === "tuple") {
|
|
1071
|
-
const results = [];
|
|
1072
|
-
for (let i = 0; i < arr.elements.length; i++) {
|
|
1073
|
-
const r2 = callFunction(fn, [arr.elements[i], T2.literal(i), arr]);
|
|
1074
|
-
if (r2.kind === "tuple") results.push(...r2.elements);
|
|
1075
|
-
else if (r2.kind === "array") return T2.array(r2.element);
|
|
1076
|
-
else results.push(r2);
|
|
1077
|
-
}
|
|
1078
|
-
return T2.tuple(results);
|
|
1079
|
-
}
|
|
1080
|
-
const r = callFunction(fn, [arr.element, T2.number, arr]);
|
|
1081
|
-
if (r.kind === "tuple") return T2.array(simplifyUnion(r.elements));
|
|
1082
|
-
if (r.kind === "array") return T2.array(r.element);
|
|
1083
|
-
return T2.array(r);
|
|
1084
|
-
}
|
|
1085
|
-
return null;
|
|
1086
|
-
}
|
|
1087
|
-
function evaluateForOf(node, iterable, env) {
|
|
1088
|
-
if (iterable.kind === "tuple") {
|
|
1089
|
-
const returnValues = [];
|
|
1090
|
-
let currentEnv = env;
|
|
1091
|
-
for (const element of iterable.elements) {
|
|
1092
|
-
const loopEnv = currentEnv.extend({});
|
|
1093
|
-
bindForLoopVar(node.left, element, loopEnv);
|
|
1094
|
-
const result = evaluate(node.body, loopEnv);
|
|
1095
|
-
if (isReturn(result)) {
|
|
1096
|
-
returnValues.push(result.value);
|
|
1097
|
-
return makeReturn(simplifyUnion(returnValues));
|
|
1098
|
-
}
|
|
1099
|
-
if (isBranch(result)) {
|
|
1100
|
-
returnValues.push(result.returnedValue);
|
|
1101
|
-
currentEnv = result.fallthroughEnv;
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
if (returnValues.length > 0) {
|
|
1105
|
-
return makeBranch(simplifyUnion(returnValues), currentEnv);
|
|
1106
|
-
}
|
|
1107
|
-
return T2.undefined;
|
|
1108
|
-
}
|
|
1109
|
-
if (iterable.kind === "array") {
|
|
1110
|
-
const loopEnv = env.extend({});
|
|
1111
|
-
bindForLoopVar(node.left, iterable.element, loopEnv);
|
|
1112
|
-
const result = evaluate(node.body, loopEnv);
|
|
1113
|
-
if (isReturn(result)) return makeBranch(result.value, env);
|
|
1114
|
-
return T2.undefined;
|
|
1115
|
-
}
|
|
1116
|
-
return T2.undefined;
|
|
1117
|
-
}
|
|
1118
|
-
function evaluateForIn(node, obj, env) {
|
|
1119
|
-
if (obj.kind === "object") {
|
|
1120
|
-
const keys = Object.keys(obj.properties);
|
|
1121
|
-
if (keys.length > 0) {
|
|
1122
|
-
const returnValues = [];
|
|
1123
|
-
let currentEnv = env;
|
|
1124
|
-
for (const key of keys) {
|
|
1125
|
-
const loopEnv2 = currentEnv.extend({});
|
|
1126
|
-
bindForLoopVar(node.left, T2.literal(key), loopEnv2);
|
|
1127
|
-
const result = evaluate(node.body, loopEnv2);
|
|
1128
|
-
if (isReturn(result)) {
|
|
1129
|
-
returnValues.push(result.value);
|
|
1130
|
-
return makeReturn(simplifyUnion(returnValues));
|
|
1131
|
-
}
|
|
1132
|
-
if (isBranch(result)) {
|
|
1133
|
-
returnValues.push(result.returnedValue);
|
|
1134
|
-
currentEnv = result.fallthroughEnv;
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
if (returnValues.length > 0) {
|
|
1138
|
-
return makeBranch(simplifyUnion(returnValues), currentEnv);
|
|
1139
|
-
}
|
|
1140
|
-
return T2.undefined;
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
const loopEnv = env.extend({});
|
|
1144
|
-
bindForLoopVar(node.left, T2.string, loopEnv);
|
|
1145
|
-
evaluate(node.body, loopEnv);
|
|
1146
|
-
return T2.undefined;
|
|
1147
|
-
}
|
|
1148
|
-
function bindForLoopVar(left, value, env) {
|
|
1149
|
-
if (left.type === "VariableDeclaration") {
|
|
1150
|
-
const decl = left.declarations[0];
|
|
1151
|
-
if (decl) bindPattern(decl.id, value, env);
|
|
1152
|
-
} else if (left.type === "Identifier") {
|
|
1153
|
-
env.bind(left.name, value);
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
function evaluateImportDeclaration(node, env) {
|
|
1157
|
-
const source = node.source.value;
|
|
1158
|
-
if (!currentModuleResolver) return T2.undefined;
|
|
1159
|
-
const resolved = currentModuleResolver(source, currentFileDir);
|
|
1160
|
-
if (!resolved) return T2.undefined;
|
|
1161
|
-
let moduleEnv = moduleCache.get(resolved.filePath);
|
|
1162
|
-
if (!moduleEnv) {
|
|
1163
|
-
moduleEnv = createEnvironment();
|
|
1164
|
-
moduleCache.set(resolved.filePath, moduleEnv);
|
|
1165
|
-
const savedDir = currentFileDir;
|
|
1166
|
-
currentFileDir = resolved.filePath.replace(/\/[^/]+$/, "");
|
|
1167
|
-
evaluateProgram(resolved.ast, moduleEnv);
|
|
1168
|
-
currentFileDir = savedDir;
|
|
1169
|
-
}
|
|
1170
|
-
for (const spec of node.specifiers) {
|
|
1171
|
-
if (spec.type === "ImportDefaultSpecifier") {
|
|
1172
|
-
const val = moduleEnv.has(`__export_default`) ? moduleEnv.lookup(`__export_default`) : T2.unknown;
|
|
1173
|
-
env.bind(spec.local.name, val);
|
|
1174
|
-
} else if (spec.type === "ImportSpecifier") {
|
|
1175
|
-
const importedName = spec.imported.type === "Identifier" ? spec.imported.name : null;
|
|
1176
|
-
if (importedName) {
|
|
1177
|
-
const val = moduleEnv.has(`__export_${importedName}`) ? moduleEnv.lookup(`__export_${importedName}`) : T2.unknown;
|
|
1178
|
-
env.bind(spec.local.name, val);
|
|
1179
|
-
}
|
|
1180
|
-
} else if (spec.type === "ImportNamespaceSpecifier") {
|
|
1181
|
-
const exports = {};
|
|
1182
|
-
const bindings = moduleEnv.getOwnBindings();
|
|
1183
|
-
for (const [k, v] of Object.entries(bindings)) {
|
|
1184
|
-
if (k.startsWith("__export_") && k !== "__export_default") {
|
|
1185
|
-
exports[k.slice("__export_".length)] = v;
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
env.bind(spec.local.name, T2.object(exports));
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
return T2.undefined;
|
|
1192
|
-
}
|
|
1193
|
-
function evaluateClassDeclaration(node, env) {
|
|
1194
|
-
const className = node.id?.name ?? "<anonymous>";
|
|
1195
|
-
const methods = {};
|
|
1196
|
-
let constructorFn = null;
|
|
1197
|
-
for (const member of node.body.body) {
|
|
1198
|
-
if (member.type !== "ClassMethod") continue;
|
|
1199
|
-
const methodName = member.key.type === "Identifier" ? member.key.name : null;
|
|
1200
|
-
if (!methodName) continue;
|
|
1201
|
-
const paramNames = member.params.map(
|
|
1202
|
-
(p) => p.type === "Identifier" ? p.name : `_p${Math.random().toString(36).slice(2, 6)}`
|
|
1203
|
-
);
|
|
1204
|
-
const fnType = T2.fn(paramNames, member.body, env);
|
|
1205
|
-
fnType._paramPatterns = member.params;
|
|
1206
|
-
if (member.async) fnType._async = true;
|
|
1207
|
-
if (member.kind === "constructor") {
|
|
1208
|
-
constructorFn = fnType;
|
|
1209
|
-
} else {
|
|
1210
|
-
methods[methodName] = fnType;
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
const ctorFn = constructorFn ?? T2.fn([], { type: "BlockStatement", body: [], directives: [] }, env);
|
|
1214
|
-
ctorFn._classInfo = { className, methods };
|
|
1215
|
-
if (node.id) {
|
|
1216
|
-
env.bind(className, ctorFn);
|
|
1217
|
-
}
|
|
1218
|
-
return T2.undefined;
|
|
1219
|
-
}
|
|
1220
|
-
function evaluateInstanceof(left, _right, rightNode, _env) {
|
|
1221
|
-
const className = rightNode.type === "Identifier" ? rightNode.name : null;
|
|
1222
|
-
if (!className) return T2.boolean;
|
|
1223
|
-
return distributeOverUnion(left, (lv) => {
|
|
1224
|
-
if (lv.kind === "instance") {
|
|
1225
|
-
const matches = lv.className === className || isSubtypeOf(lv, T2.instanceOf(className));
|
|
1226
|
-
return T2.literal(matches);
|
|
1227
|
-
}
|
|
1228
|
-
return T2.boolean;
|
|
1229
|
-
});
|
|
1230
|
-
}
|
|
1231
|
-
var BUILTIN_ERROR_CLASSES = /* @__PURE__ */ new Set([
|
|
1232
|
-
"Error",
|
|
1233
|
-
"TypeError",
|
|
1234
|
-
"SyntaxError",
|
|
1235
|
-
"RangeError",
|
|
1236
|
-
"ReferenceError",
|
|
1237
|
-
"URIError",
|
|
1238
|
-
"EvalError"
|
|
1239
|
-
]);
|
|
1240
|
-
function evaluateNewExpression(node, env) {
|
|
1241
|
-
const callee = node.callee;
|
|
1242
|
-
if (callee.type === "Identifier" && BUILTIN_ERROR_CLASSES.has(callee.name)) {
|
|
1243
|
-
const argVals = evaluateArgs(node.arguments, env);
|
|
1244
|
-
if (isReturn(argVals) || isBranch(argVals) || isThrow(argVals)) return argVals;
|
|
1245
|
-
const msgVal = argVals[0] ?? T2.undefined;
|
|
1246
|
-
return T2.instanceOf(callee.name, { message: msgVal });
|
|
1247
|
-
}
|
|
1248
|
-
const calleeVal = evaluate(callee, env);
|
|
1249
|
-
if (isReturn(calleeVal) || isBranch(calleeVal) || isThrow(calleeVal)) return calleeVal;
|
|
1250
|
-
if (calleeVal.kind === "function") {
|
|
1251
|
-
const argVals = evaluateArgs(node.arguments, env);
|
|
1252
|
-
if (isReturn(argVals) || isBranch(argVals) || isThrow(argVals)) return argVals;
|
|
1253
|
-
const classInfo = calleeVal._classInfo;
|
|
1254
|
-
if (classInfo) {
|
|
1255
|
-
const instanceProps = {};
|
|
1256
|
-
const constructEnv = calleeVal.closure.extend({});
|
|
1257
|
-
const thisObj = T2.object(instanceProps);
|
|
1258
|
-
constructEnv.bind("this", thisObj);
|
|
1259
|
-
const paramPatterns = calleeVal._paramPatterns;
|
|
1260
|
-
for (let i = 0; i < calleeVal.params.length; i++) {
|
|
1261
|
-
const argVal = argVals[i] ?? T2.undefined;
|
|
1262
|
-
if (paramPatterns?.[i]) {
|
|
1263
|
-
bindPattern(paramPatterns[i], argVal, constructEnv);
|
|
1264
|
-
} else {
|
|
1265
|
-
constructEnv.bind(calleeVal.params[i], argVal);
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
const result = evaluate(calleeVal.body, constructEnv);
|
|
1269
|
-
if (isThrow(result)) return result;
|
|
1270
|
-
const finalThis = constructEnv.lookup("this");
|
|
1271
|
-
const props = finalThis.kind === "object" ? { ...finalThis.properties } : instanceProps;
|
|
1272
|
-
for (const [k, v] of Object.entries(classInfo.methods)) {
|
|
1273
|
-
props[k] = v;
|
|
1274
|
-
}
|
|
1275
|
-
return T2.instanceOf(classInfo.className, props);
|
|
1276
|
-
}
|
|
1277
|
-
return callFunction(calleeVal, argVals);
|
|
1278
|
-
}
|
|
1279
|
-
return T2.unknown;
|
|
1280
|
-
}
|
|
1281
|
-
function evaluateTryStatement(node, env) {
|
|
1282
|
-
const tryResult = evaluateStatements(node.block.body, env.extend({}));
|
|
1283
|
-
const thrownType = isThrow(tryResult) ? tryResult.thrown : null;
|
|
1284
|
-
const tryValue = isThrow(tryResult) ? null : isReturn(tryResult) ? tryResult : isBranch(tryResult) ? tryResult : tryResult;
|
|
1285
|
-
let catchResult = null;
|
|
1286
|
-
if (node.handler && thrownType) {
|
|
1287
|
-
const catchEnv = env.extend({});
|
|
1288
|
-
if (node.handler.param) {
|
|
1289
|
-
bindPattern(node.handler.param, thrownType, catchEnv);
|
|
1290
|
-
}
|
|
1291
|
-
catchResult = evaluateStatements(node.handler.body.body, catchEnv);
|
|
1292
|
-
}
|
|
1293
|
-
if (node.finalizer) {
|
|
1294
|
-
const finallyResult = evaluateStatements(node.finalizer.body, env.extend({}));
|
|
1295
|
-
if (isThrow(finallyResult)) return finallyResult;
|
|
1296
|
-
if (isReturn(finallyResult)) return finallyResult;
|
|
1297
|
-
}
|
|
1298
|
-
if (catchResult !== null) {
|
|
1299
|
-
if (isThrow(catchResult)) return catchResult;
|
|
1300
|
-
if (isReturn(catchResult)) {
|
|
1301
|
-
if (tryValue !== null && isReturn(tryValue)) {
|
|
1302
|
-
return makeReturn(simplifyUnion([tryValue.value, catchResult.value]));
|
|
1303
|
-
}
|
|
1304
|
-
return catchResult;
|
|
1305
|
-
}
|
|
1306
|
-
if (tryValue !== null && isReturn(tryValue)) {
|
|
1307
|
-
return tryValue;
|
|
1308
|
-
}
|
|
1309
|
-
if (tryValue !== null && isBranch(tryValue)) {
|
|
1310
|
-
return tryValue;
|
|
1311
|
-
}
|
|
1312
|
-
return catchResult;
|
|
1313
|
-
}
|
|
1314
|
-
if (thrownType && !node.handler) {
|
|
1315
|
-
return makeThrow(thrownType);
|
|
1316
|
-
}
|
|
1317
|
-
if (tryValue !== null) return tryValue;
|
|
1318
|
-
return T2.undefined;
|
|
1319
|
-
}
|
|
1320
|
-
function evaluateSwitchStatement(node, env) {
|
|
1321
|
-
const discriminant = evaluate(node.discriminant, env);
|
|
1322
|
-
if (isReturn(discriminant) || isBranch(discriminant) || isThrow(discriminant)) return discriminant;
|
|
1323
|
-
const isConcreteDiscriminant = discriminant.kind === "literal";
|
|
1324
|
-
if (isConcreteDiscriminant) {
|
|
1325
|
-
let matched = false;
|
|
1326
|
-
const returnValues2 = [];
|
|
1327
|
-
for (const caseNode of node.cases) {
|
|
1328
|
-
if (caseNode.test) {
|
|
1329
|
-
const testVal = evaluate(caseNode.test, env);
|
|
1330
|
-
if (isReturn(testVal) || isBranch(testVal) || isThrow(testVal)) return testVal;
|
|
1331
|
-
if (testVal.kind === "literal" && discriminant.value === testVal.value) matched = true;
|
|
1332
|
-
} else {
|
|
1333
|
-
matched = true;
|
|
1334
|
-
}
|
|
1335
|
-
if (matched) {
|
|
1336
|
-
const result = evaluateStatements(caseNode.consequent, env);
|
|
1337
|
-
if (isThrow(result)) return result;
|
|
1338
|
-
if (isReturn(result)) {
|
|
1339
|
-
returnValues2.push(result.value);
|
|
1340
|
-
break;
|
|
1341
|
-
}
|
|
1342
|
-
if (isBranch(result)) {
|
|
1343
|
-
returnValues2.push(result.returnedValue);
|
|
1344
|
-
continue;
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
if (returnValues2.length > 0) {
|
|
1349
|
-
return makeBranch(simplifyUnion(returnValues2), env);
|
|
1350
|
-
}
|
|
1351
|
-
return T2.undefined;
|
|
1352
|
-
}
|
|
1353
|
-
const returnValues = [];
|
|
1354
|
-
for (const caseNode of node.cases) {
|
|
1355
|
-
const result = evaluateStatements(caseNode.consequent, env);
|
|
1356
|
-
if (isThrow(result)) continue;
|
|
1357
|
-
if (isReturn(result)) {
|
|
1358
|
-
returnValues.push(result.value);
|
|
1359
|
-
continue;
|
|
1360
|
-
}
|
|
1361
|
-
if (isBranch(result)) {
|
|
1362
|
-
returnValues.push(result.returnedValue);
|
|
1363
|
-
continue;
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
if (returnValues.length > 0) {
|
|
1367
|
-
return makeBranch(simplifyUnion(returnValues), env);
|
|
1368
|
-
}
|
|
1369
|
-
return T2.undefined;
|
|
1370
|
-
}
|
|
1371
|
-
function callFunctionFull(fn, args) {
|
|
1372
|
-
const callEnv = fn.closure.extend({});
|
|
1373
|
-
const paramPatterns = fn._paramPatterns;
|
|
1374
|
-
const isAsync = !!fn._async;
|
|
1375
|
-
for (let i = 0; i < fn.params.length; i++) {
|
|
1376
|
-
const argVal = args[i] ?? T2.undefined;
|
|
1377
|
-
if (paramPatterns && paramPatterns[i]) {
|
|
1378
|
-
bindPattern(paramPatterns[i], argVal, callEnv);
|
|
1379
|
-
} else {
|
|
1380
|
-
callEnv.bind(fn.params[i], argVal);
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
const savedUnreachable = _unreachableRanges;
|
|
1384
|
-
_unreachableRanges = [];
|
|
1385
|
-
const memoKey = buildMemoKey(fn, args);
|
|
1386
|
-
if (memoKey !== null) {
|
|
1387
|
-
const cached = callMemo.get(memoKey);
|
|
1388
|
-
if (cached !== void 0) {
|
|
1389
|
-
_unreachableRanges = savedUnreachable;
|
|
1390
|
-
if (cached === MEMO_IN_PROGRESS) {
|
|
1391
|
-
return { value: T2.unknown, throws: T2.never };
|
|
1392
|
-
}
|
|
1393
|
-
return { value: cached, throws: T2.never };
|
|
1394
|
-
}
|
|
1395
|
-
callMemo.set(memoKey, MEMO_IN_PROGRESS);
|
|
1396
|
-
const result2 = evaluate(fn.body, callEnv);
|
|
1397
|
-
_unreachableRanges = savedUnreachable;
|
|
1398
|
-
const value2 = isReturn(result2) ? result2.value : isBranch(result2) ? result2.returnedValue : isThrow(result2) ? T2.never : result2;
|
|
1399
|
-
const throws = isThrow(result2) ? result2.thrown : T2.never;
|
|
1400
|
-
const throwLoc = isThrow(result2) ? result2.loc : void 0;
|
|
1401
|
-
const wrapped2 = isAsync ? T2.promise(value2) : value2;
|
|
1402
|
-
callMemo.set(memoKey, wrapped2);
|
|
1403
|
-
return { value: wrapped2, throws, throwLoc };
|
|
1404
|
-
}
|
|
1405
|
-
const result = evaluate(fn.body, callEnv);
|
|
1406
|
-
_unreachableRanges = savedUnreachable;
|
|
1407
|
-
if (isThrow(result)) {
|
|
1408
|
-
return { value: T2.never, throws: result.thrown, throwLoc: result.loc };
|
|
1409
|
-
}
|
|
1410
|
-
const value = isReturn(result) ? result.value : isBranch(result) ? result.returnedValue : result;
|
|
1411
|
-
const wrapped = isAsync ? T2.promise(value) : value;
|
|
1412
|
-
return { value: wrapped, throws: T2.never };
|
|
1413
|
-
}
|
|
1414
|
-
function callFunction(fn, args) {
|
|
1415
|
-
return callFunctionFull(fn, args).value;
|
|
1416
|
-
}
|
|
1417
|
-
function evaluateFunction(fnNode, args, env) {
|
|
1418
|
-
return evaluateFunctionFull(fnNode, args, env).value;
|
|
1419
|
-
}
|
|
1420
|
-
function evaluateFunctionFull(fnNode, args, env) {
|
|
1421
|
-
if (fnNode.type === "FunctionDeclaration" || fnNode.type === "FunctionExpression") {
|
|
1422
|
-
const callEnv = env.extend({});
|
|
1423
|
-
const isAsync = !!fnNode.async;
|
|
1424
|
-
for (let i = 0; i < fnNode.params.length; i++) {
|
|
1425
|
-
bindPattern(fnNode.params[i], args[i] ?? T2.undefined, callEnv);
|
|
1426
|
-
}
|
|
1427
|
-
const result = evaluate(fnNode.body, callEnv);
|
|
1428
|
-
if (isThrow(result)) return { value: T2.never, throws: result.thrown, throwLoc: result.loc };
|
|
1429
|
-
const value = isReturn(result) ? result.value : isBranch(result) ? result.returnedValue : result;
|
|
1430
|
-
const wrapped = isAsync ? T2.promise(value) : value;
|
|
1431
|
-
return { value: wrapped, throws: T2.never };
|
|
1432
|
-
}
|
|
1433
|
-
return { value: T2.unknown, throws: T2.never };
|
|
1434
|
-
}
|
|
1435
|
-
function evaluateProgram(node, env) {
|
|
1436
|
-
const result = evaluate(node, env);
|
|
1437
|
-
if (isReturn(result)) return result.value;
|
|
1438
|
-
if (isBranch(result)) return result.returnedValue;
|
|
1439
|
-
if (isThrow(result)) return T2.never;
|
|
1440
|
-
return result;
|
|
1441
|
-
}
|
|
1442
|
-
function getUnreachableRanges() {
|
|
1443
|
-
return _unreachableRanges;
|
|
1444
|
-
}
|
|
1445
|
-
function resetUnreachableRanges() {
|
|
1446
|
-
_unreachableRanges = [];
|
|
1447
|
-
}
|
|
1448
|
-
function setCurrentFileDir(dir) {
|
|
1449
|
-
currentFileDir = dir;
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
export {
|
|
1453
|
-
__require,
|
|
1454
|
-
__commonJS,
|
|
1455
|
-
__toESM,
|
|
1456
|
-
narrow,
|
|
1457
|
-
resetMemo,
|
|
1458
|
-
setModuleResolver,
|
|
1459
|
-
setNodeTypeCollector,
|
|
1460
|
-
evaluate,
|
|
1461
|
-
evaluateFunction,
|
|
1462
|
-
evaluateFunctionFull,
|
|
1463
|
-
evaluateProgram,
|
|
1464
|
-
getUnreachableRanges,
|
|
1465
|
-
resetUnreachableRanges,
|
|
1466
|
-
setCurrentFileDir
|
|
1467
|
-
};
|