@nudojs/core 0.2.0 → 0.3.1
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/README.md +26 -0
- package/dist/chunk-BU4A6XJ3.js +6856 -0
- package/dist/exec.d.ts +2 -0
- package/dist/exec.js +168 -0
- package/dist/index-CVYjXnwU.d.ts +902 -0
- package/dist/index.d.ts +953 -10
- package/dist/index.js +4489 -110
- package/package.json +33 -4
- package/CHANGELOG.md +0 -15
- package/src/__tests__/environment.test.ts +0 -48
- package/src/__tests__/ops-refined.test.ts +0 -194
- package/src/__tests__/ops.test.ts +0 -109
- package/src/__tests__/type-value-phase2.test.ts +0 -117
- package/src/__tests__/type-value-phase3.test.ts +0 -94
- package/src/__tests__/type-value-refined.test.ts +0 -210
- package/src/__tests__/type-value.test.ts +0 -213
- package/src/environment.ts +0 -69
- package/src/index.ts +0 -40
- package/src/ops.ts +0 -210
- package/src/refinements/index.ts +0 -2
- package/src/refinements/range.ts +0 -86
- package/src/refinements/template.ts +0 -178
- package/src/type-value.ts +0 -372
- package/tsconfig.json +0 -8
package/dist/index.js
CHANGED
|
@@ -1,3 +1,260 @@
|
|
|
1
|
+
import {
|
|
2
|
+
$add,
|
|
3
|
+
$arr,
|
|
4
|
+
$async,
|
|
5
|
+
$asyncReturn,
|
|
6
|
+
$await,
|
|
7
|
+
$call,
|
|
8
|
+
$callNamed,
|
|
9
|
+
$catchVal,
|
|
10
|
+
$class,
|
|
11
|
+
$concat,
|
|
12
|
+
$div,
|
|
13
|
+
$elems,
|
|
14
|
+
$eq,
|
|
15
|
+
$fnVal,
|
|
16
|
+
$for,
|
|
17
|
+
$forIter,
|
|
18
|
+
$forOf,
|
|
19
|
+
$fork,
|
|
20
|
+
$ge,
|
|
21
|
+
$gen,
|
|
22
|
+
$get,
|
|
23
|
+
$gt,
|
|
24
|
+
$idx,
|
|
25
|
+
$idxSet,
|
|
26
|
+
$invoke,
|
|
27
|
+
$invokeSuper,
|
|
28
|
+
$join,
|
|
29
|
+
$le,
|
|
30
|
+
$len,
|
|
31
|
+
$lit,
|
|
32
|
+
$lt,
|
|
33
|
+
$mod,
|
|
34
|
+
$mul,
|
|
35
|
+
$ne,
|
|
36
|
+
$neg,
|
|
37
|
+
$new,
|
|
38
|
+
$not,
|
|
39
|
+
$obj,
|
|
40
|
+
$optionalGet,
|
|
41
|
+
$optionalInvoke,
|
|
42
|
+
$orDefault,
|
|
43
|
+
$set,
|
|
44
|
+
$setKey,
|
|
45
|
+
$spread,
|
|
46
|
+
$staticInvoke,
|
|
47
|
+
$sub,
|
|
48
|
+
$super,
|
|
49
|
+
$switch,
|
|
50
|
+
$thisGet,
|
|
51
|
+
$thisSet,
|
|
52
|
+
$throw,
|
|
53
|
+
$typeof,
|
|
54
|
+
$while,
|
|
55
|
+
$whileSeq,
|
|
56
|
+
$yield,
|
|
57
|
+
DEFAULT_MAX_LOOP_ITERS,
|
|
58
|
+
MAX_CALL_DEPTH,
|
|
59
|
+
MAX_TOTAL_CALLS,
|
|
60
|
+
NudoThrow,
|
|
61
|
+
abs,
|
|
62
|
+
absFunction,
|
|
63
|
+
absToString,
|
|
64
|
+
add,
|
|
65
|
+
alphaOf,
|
|
66
|
+
analyzeFn,
|
|
67
|
+
and,
|
|
68
|
+
anyVar,
|
|
69
|
+
app,
|
|
70
|
+
applyAbsFn,
|
|
71
|
+
applyCallbackAbs,
|
|
72
|
+
asAbs,
|
|
73
|
+
asAbsVal,
|
|
74
|
+
attachFnImpl,
|
|
75
|
+
awaitAbs,
|
|
76
|
+
betaOf,
|
|
77
|
+
bindImports,
|
|
78
|
+
bool,
|
|
79
|
+
boolLit,
|
|
80
|
+
callAbsMethod,
|
|
81
|
+
callFunction,
|
|
82
|
+
callTranspiledExport,
|
|
83
|
+
callTranspiledExportFull,
|
|
84
|
+
classFromMethods,
|
|
85
|
+
clearBClasses,
|
|
86
|
+
cmp,
|
|
87
|
+
coerceAsyncReturn,
|
|
88
|
+
collapseToOptional,
|
|
89
|
+
collectAbsExports,
|
|
90
|
+
collectAbsNodeTypes,
|
|
91
|
+
concatString,
|
|
92
|
+
confJoin,
|
|
93
|
+
createHofCollectCtx,
|
|
94
|
+
createTemplateAbs,
|
|
95
|
+
currentExecPhi,
|
|
96
|
+
defaultLeakBudget,
|
|
97
|
+
defineClass,
|
|
98
|
+
definitelyNotNullishShape,
|
|
99
|
+
div,
|
|
100
|
+
emptyEnv,
|
|
101
|
+
emptyPhi,
|
|
102
|
+
eq,
|
|
103
|
+
evalArrayStatic,
|
|
104
|
+
evalBuiltinInstanceMethod,
|
|
105
|
+
evalBuiltinNew,
|
|
106
|
+
evalDateCtor,
|
|
107
|
+
evalDateMethod,
|
|
108
|
+
evalDateStatic,
|
|
109
|
+
evalGlobalFn,
|
|
110
|
+
evalJsonMethod,
|
|
111
|
+
evalMathMethod,
|
|
112
|
+
evalMethodBody,
|
|
113
|
+
evalNamespaceCall,
|
|
114
|
+
evalNode,
|
|
115
|
+
evalNumberStatic,
|
|
116
|
+
evalObjectMethod,
|
|
117
|
+
evalProgramAbs,
|
|
118
|
+
evalPromiseCtor,
|
|
119
|
+
evalPromiseStatic,
|
|
120
|
+
evalRegExpCtor,
|
|
121
|
+
evalRegExpMethod,
|
|
122
|
+
evalSource,
|
|
123
|
+
exceedsBudget,
|
|
124
|
+
falseConstraint,
|
|
125
|
+
findAbsAtPosition,
|
|
126
|
+
fnOf,
|
|
127
|
+
ge,
|
|
128
|
+
geNum,
|
|
129
|
+
getAbsOrigin,
|
|
130
|
+
getAbsProperty,
|
|
131
|
+
getBCallCollector,
|
|
132
|
+
getBClass,
|
|
133
|
+
getClass,
|
|
134
|
+
getClassChain,
|
|
135
|
+
getFnImpl,
|
|
136
|
+
getParseSourceCacheSize,
|
|
137
|
+
gt,
|
|
138
|
+
gtNum,
|
|
139
|
+
implies,
|
|
140
|
+
instanceOf,
|
|
141
|
+
instantiateClass,
|
|
142
|
+
instantiateReturn,
|
|
143
|
+
isDefinitelyFalse,
|
|
144
|
+
isDefinitelyTrue,
|
|
145
|
+
isExactLit,
|
|
146
|
+
isNudoThrow,
|
|
147
|
+
isNullishLitAbs,
|
|
148
|
+
isNumPrim,
|
|
149
|
+
isObj,
|
|
150
|
+
isRelFn,
|
|
151
|
+
isStrPrim,
|
|
152
|
+
isTemplateLike,
|
|
153
|
+
joinAbs,
|
|
154
|
+
joinFunctions,
|
|
155
|
+
joinObjects,
|
|
156
|
+
joinValues,
|
|
157
|
+
le,
|
|
158
|
+
leNum,
|
|
159
|
+
leakIfNeeded,
|
|
160
|
+
leqAbs,
|
|
161
|
+
lit,
|
|
162
|
+
litValue,
|
|
163
|
+
lookupMethod,
|
|
164
|
+
lookupMethodWithOwner,
|
|
165
|
+
lookupSuperMethod,
|
|
166
|
+
lt,
|
|
167
|
+
ltNum,
|
|
168
|
+
makeSum,
|
|
169
|
+
mapElementFallback,
|
|
170
|
+
maybeLeak,
|
|
171
|
+
mod,
|
|
172
|
+
mul,
|
|
173
|
+
ne,
|
|
174
|
+
negAbs,
|
|
175
|
+
never,
|
|
176
|
+
not,
|
|
177
|
+
notAbs,
|
|
178
|
+
noteMemberDispatchMiss,
|
|
179
|
+
notePrimMemberMissing,
|
|
180
|
+
noteUnknownMemberMissing,
|
|
181
|
+
num,
|
|
182
|
+
numLit,
|
|
183
|
+
numVar,
|
|
184
|
+
numericBounds,
|
|
185
|
+
obj,
|
|
186
|
+
objOf,
|
|
187
|
+
or,
|
|
188
|
+
pFalse,
|
|
189
|
+
pTrue,
|
|
190
|
+
parseSource,
|
|
191
|
+
phiAnd,
|
|
192
|
+
predEquals,
|
|
193
|
+
predToString,
|
|
194
|
+
predVars,
|
|
195
|
+
projectBrand,
|
|
196
|
+
projectFlatMapResult,
|
|
197
|
+
promoteParamShape,
|
|
198
|
+
ptypeof,
|
|
199
|
+
recordMemberDiag,
|
|
200
|
+
registerBClass,
|
|
201
|
+
relationFingerprint,
|
|
202
|
+
relationFn,
|
|
203
|
+
resetAbsCallBudget,
|
|
204
|
+
resetLeakCounter,
|
|
205
|
+
resetParseSourceCache,
|
|
206
|
+
runTranspiled,
|
|
207
|
+
setAbsAssignCollector,
|
|
208
|
+
setAbsCallCollector,
|
|
209
|
+
setAbsNodeCollector,
|
|
210
|
+
setAbsTruncationCollector,
|
|
211
|
+
setApplyCallbackHost,
|
|
212
|
+
setBCallCollector,
|
|
213
|
+
setMemberDiagCollector,
|
|
214
|
+
shapeOfTerm,
|
|
215
|
+
shapeOnlyFn,
|
|
216
|
+
shapeToString,
|
|
217
|
+
simplifyTerm,
|
|
218
|
+
snapshotAbs,
|
|
219
|
+
spread,
|
|
220
|
+
str,
|
|
221
|
+
strLit,
|
|
222
|
+
strictEqAbs,
|
|
223
|
+
stripTypes,
|
|
224
|
+
sub,
|
|
225
|
+
substAbs,
|
|
226
|
+
substPred,
|
|
227
|
+
substPredAbs,
|
|
228
|
+
superNameOf,
|
|
229
|
+
tagAbsOrigin,
|
|
230
|
+
templatePartsOf,
|
|
231
|
+
termDepth,
|
|
232
|
+
termEquals,
|
|
233
|
+
termNodes,
|
|
234
|
+
termToString,
|
|
235
|
+
transpile,
|
|
236
|
+
transpileExpression,
|
|
237
|
+
transpileFile,
|
|
238
|
+
transpileSource,
|
|
239
|
+
trueConstraint,
|
|
240
|
+
tryPromoteDirectCall,
|
|
241
|
+
tryPromoteForOfIteratee,
|
|
242
|
+
tryPromoteHofCallback,
|
|
243
|
+
tryPromoteReceiverAsArr,
|
|
244
|
+
typeofAbs,
|
|
245
|
+
undefAbs,
|
|
246
|
+
unknown,
|
|
247
|
+
v,
|
|
248
|
+
withExecPhi,
|
|
249
|
+
withVar,
|
|
250
|
+
wrapPromise
|
|
251
|
+
} from "./chunk-BU4A6XJ3.js";
|
|
252
|
+
|
|
253
|
+
// src/refinements/template-predicates.ts
|
|
254
|
+
function isTemplate(tv) {
|
|
255
|
+
return tv.kind === "refined" && Array.isArray(tv.refinement.meta.parts);
|
|
256
|
+
}
|
|
257
|
+
|
|
1
258
|
// src/type-value.ts
|
|
2
259
|
function createUnion(...members) {
|
|
3
260
|
return simplifyUnion(members);
|
|
@@ -37,7 +294,17 @@ var T = {
|
|
|
37
294
|
kind: "refined",
|
|
38
295
|
base,
|
|
39
296
|
refinement
|
|
40
|
-
})
|
|
297
|
+
}),
|
|
298
|
+
fnSig: (paramTypes, returnType, throwsType = { kind: "never" }, impl) => {
|
|
299
|
+
const dummy = {
|
|
300
|
+
kind: "function",
|
|
301
|
+
params: paramTypes.map((_p, i) => `_arg${i}`),
|
|
302
|
+
body: { type: "BlockStatement", body: [], directives: [] },
|
|
303
|
+
closure: null
|
|
304
|
+
};
|
|
305
|
+
dummy._signature = { paramTypes, returnType, throwsType, impl };
|
|
306
|
+
return dummy;
|
|
307
|
+
}
|
|
41
308
|
};
|
|
42
309
|
function typeValueEquals(a, b) {
|
|
43
310
|
if (a.kind !== b.kind) return false;
|
|
@@ -60,6 +327,23 @@ function typeValueEquals(a, b) {
|
|
|
60
327
|
return a === b;
|
|
61
328
|
}
|
|
62
329
|
function simplifyUnion(members) {
|
|
330
|
+
if (members.length === 0) return T.never;
|
|
331
|
+
if (members.length === 1) return members[0].kind === "never" ? T.never : members[0];
|
|
332
|
+
if (members.length === 2) {
|
|
333
|
+
const a = members[0];
|
|
334
|
+
const b = members[1];
|
|
335
|
+
if (a.kind !== "union" && b.kind !== "union") {
|
|
336
|
+
if (a.kind === "never") return b.kind === "never" ? T.never : b;
|
|
337
|
+
if (b.kind === "never") return a;
|
|
338
|
+
if (typeValueEquals(a, b)) return a;
|
|
339
|
+
if (a.kind === "unknown" || b.kind === "unknown") return T.unknown;
|
|
340
|
+
const aBase = absorbablePrimitiveBase(a);
|
|
341
|
+
if (aBase !== void 0 && typeValueEquals(aBase, b)) return b;
|
|
342
|
+
const bBase = absorbablePrimitiveBase(b);
|
|
343
|
+
if (bBase !== void 0 && typeValueEquals(bBase, a)) return a;
|
|
344
|
+
return { kind: "union", members: [a, b] };
|
|
345
|
+
}
|
|
346
|
+
}
|
|
63
347
|
const flat = [];
|
|
64
348
|
for (const m of members) {
|
|
65
349
|
if (m.kind === "never") continue;
|
|
@@ -75,30 +359,54 @@ function simplifyUnion(members) {
|
|
|
75
359
|
deduped.push(m);
|
|
76
360
|
}
|
|
77
361
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
362
|
+
const primitives = deduped.filter((m) => m.kind === "primitive");
|
|
363
|
+
const absorbed = primitives.length > 0 ? deduped.filter((m) => {
|
|
364
|
+
const base = absorbablePrimitiveBase(m);
|
|
365
|
+
return base === void 0 || !primitives.some((p) => typeValueEquals(p, base));
|
|
366
|
+
}) : deduped;
|
|
367
|
+
if (absorbed.length === 0) return T.never;
|
|
368
|
+
if (absorbed.length === 1) return absorbed[0];
|
|
369
|
+
if (absorbed.some((m) => m.kind === "unknown")) return T.unknown;
|
|
370
|
+
return { kind: "union", members: absorbed };
|
|
371
|
+
}
|
|
372
|
+
function absorbablePrimitiveBase(tv) {
|
|
373
|
+
if (tv.kind === "literal") {
|
|
374
|
+
const widened = widenLiteral(tv);
|
|
375
|
+
return widened.kind === "primitive" ? widened : void 0;
|
|
376
|
+
}
|
|
377
|
+
if (isTemplate(tv)) return T.string;
|
|
378
|
+
return void 0;
|
|
82
379
|
}
|
|
83
380
|
function widenLiteral(tv) {
|
|
84
381
|
if (tv.kind !== "literal") return tv;
|
|
85
|
-
const
|
|
86
|
-
if (typeof
|
|
87
|
-
if (typeof
|
|
88
|
-
if (typeof
|
|
89
|
-
if (
|
|
90
|
-
if (
|
|
382
|
+
const v2 = tv.value;
|
|
383
|
+
if (typeof v2 === "number") return T.number;
|
|
384
|
+
if (typeof v2 === "string") return T.string;
|
|
385
|
+
if (typeof v2 === "boolean") return T.boolean;
|
|
386
|
+
if (typeof v2 === "bigint") return T.bigint;
|
|
387
|
+
if (v2 === null) return T.null;
|
|
388
|
+
if (v2 === void 0) return T.undefined;
|
|
91
389
|
return T.unknown;
|
|
92
390
|
}
|
|
391
|
+
function collapseLiteralUnion(tv, maxLiterals) {
|
|
392
|
+
if (tv.kind !== "union") return tv;
|
|
393
|
+
if (tv.members.length <= maxLiterals) return tv;
|
|
394
|
+
const simplified = simplifyUnion(tv.members);
|
|
395
|
+
if (simplified.kind !== "union") return simplified;
|
|
396
|
+
if (!simplified.members.every((m) => m.kind === "literal")) return simplified;
|
|
397
|
+
const first = widenLiteral(simplified.members[0]);
|
|
398
|
+
if (!simplified.members.every((m) => typeValueEquals(widenLiteral(m), first))) return simplified;
|
|
399
|
+
return first;
|
|
400
|
+
}
|
|
93
401
|
function isSubtypeOf(a, b) {
|
|
94
402
|
if (b.kind === "unknown") return true;
|
|
95
403
|
if (a.kind === "never") return true;
|
|
96
404
|
if (typeValueEquals(a, b)) return true;
|
|
97
405
|
if (a.kind === "literal" && b.kind === "primitive") {
|
|
98
|
-
const
|
|
99
|
-
if (b.type === "number" && typeof
|
|
100
|
-
if (b.type === "string" && typeof
|
|
101
|
-
if (b.type === "boolean" && typeof
|
|
406
|
+
const v2 = a.value;
|
|
407
|
+
if (b.type === "number" && typeof v2 === "number") return true;
|
|
408
|
+
if (b.type === "string" && typeof v2 === "string") return true;
|
|
409
|
+
if (b.type === "boolean" && typeof v2 === "boolean") return true;
|
|
102
410
|
return false;
|
|
103
411
|
}
|
|
104
412
|
if (a.kind === "literal" && b.kind === "refined") {
|
|
@@ -161,8 +469,8 @@ function deepCloneTypeValue(tv, idMap) {
|
|
|
161
469
|
map.set(tv.id, newId);
|
|
162
470
|
}
|
|
163
471
|
const newProps = {};
|
|
164
|
-
for (const [k,
|
|
165
|
-
newProps[k] = deepCloneTypeValue(
|
|
472
|
+
for (const [k, v2] of Object.entries(tv.properties)) {
|
|
473
|
+
newProps[k] = deepCloneTypeValue(v2, map);
|
|
166
474
|
}
|
|
167
475
|
return { kind: "object", properties: newProps, id: newId };
|
|
168
476
|
}
|
|
@@ -177,8 +485,8 @@ function deepCloneTypeValue(tv, idMap) {
|
|
|
177
485
|
}
|
|
178
486
|
if (tv.kind === "instance") {
|
|
179
487
|
const newProps = {};
|
|
180
|
-
for (const [k,
|
|
181
|
-
newProps[k] = deepCloneTypeValue(
|
|
488
|
+
for (const [k, v2] of Object.entries(tv.properties)) {
|
|
489
|
+
newProps[k] = deepCloneTypeValue(v2, map);
|
|
182
490
|
}
|
|
183
491
|
return { kind: "instance", className: tv.className, properties: newProps };
|
|
184
492
|
}
|
|
@@ -205,13 +513,27 @@ function mergeObjectProperties(a, b) {
|
|
|
205
513
|
return { kind: "object", properties: merged, id: a.id };
|
|
206
514
|
}
|
|
207
515
|
function typeValueToString(tv) {
|
|
516
|
+
return typeValueToStringInner(tv, /* @__PURE__ */ new Set());
|
|
517
|
+
}
|
|
518
|
+
function typeValueToStringInner(tv, seen, depth = 0) {
|
|
519
|
+
if (depth > 48) return "\u2026";
|
|
520
|
+
const recur = (inner) => {
|
|
521
|
+
if (inner && typeof inner === "object") {
|
|
522
|
+
if (seen.has(inner)) return "\u2026";
|
|
523
|
+
seen.add(inner);
|
|
524
|
+
const out = typeValueToStringInner(inner, seen, depth + 1);
|
|
525
|
+
seen.delete(inner);
|
|
526
|
+
return out;
|
|
527
|
+
}
|
|
528
|
+
return typeValueToStringInner(inner, seen, depth + 1);
|
|
529
|
+
};
|
|
208
530
|
switch (tv.kind) {
|
|
209
531
|
case "literal": {
|
|
210
|
-
const
|
|
211
|
-
if (
|
|
212
|
-
if (
|
|
213
|
-
if (typeof
|
|
214
|
-
return String(
|
|
532
|
+
const v2 = tv.value;
|
|
533
|
+
if (v2 === null) return "null";
|
|
534
|
+
if (v2 === void 0) return "undefined";
|
|
535
|
+
if (typeof v2 === "string") return JSON.stringify(v2);
|
|
536
|
+
return String(v2);
|
|
215
537
|
}
|
|
216
538
|
case "primitive":
|
|
217
539
|
return tv.type;
|
|
@@ -220,29 +542,34 @@ function typeValueToString(tv) {
|
|
|
220
542
|
case "object": {
|
|
221
543
|
const entries = Object.entries(tv.properties);
|
|
222
544
|
if (entries.length === 0) return "{}";
|
|
223
|
-
const inner = entries.map(([k,
|
|
545
|
+
const inner = entries.map(([k, v2]) => `${k}: ${recur(v2)}`).join(", ");
|
|
224
546
|
return `{ ${inner} }`;
|
|
225
547
|
}
|
|
226
548
|
case "array":
|
|
227
|
-
return `${
|
|
549
|
+
return `${recur(tv.element)}[]`;
|
|
228
550
|
case "tuple": {
|
|
229
|
-
const inner = tv.elements.map(
|
|
551
|
+
const inner = tv.elements.map(recur).join(", ");
|
|
230
552
|
return `[${inner}]`;
|
|
231
553
|
}
|
|
232
554
|
case "function": {
|
|
555
|
+
const sig = getFnSig(tv);
|
|
556
|
+
if (sig) {
|
|
557
|
+
const params2 = sig.paramTypes.map((p, i) => `${tv.params[i]}: ${recur(p)}`).join(", ");
|
|
558
|
+
return `(${params2}) => ${recur(sig.returnType)}`;
|
|
559
|
+
}
|
|
233
560
|
const params = tv.params.join(", ");
|
|
234
561
|
return `(${params}) => ...`;
|
|
235
562
|
}
|
|
236
563
|
case "promise":
|
|
237
|
-
return `Promise<${
|
|
564
|
+
return `Promise<${recur(tv.value)}>`;
|
|
238
565
|
case "instance": {
|
|
239
566
|
const entries = Object.entries(tv.properties);
|
|
240
567
|
if (entries.length === 0) return tv.className;
|
|
241
|
-
const inner = entries.map(([k,
|
|
568
|
+
const inner = entries.map(([k, v2]) => `${k}: ${recur(v2)}`).join(", ");
|
|
242
569
|
return `${tv.className} { ${inner} }`;
|
|
243
570
|
}
|
|
244
571
|
case "union": {
|
|
245
|
-
return tv.members.map(
|
|
572
|
+
return tv.members.map(recur).join(" | ");
|
|
246
573
|
}
|
|
247
574
|
case "never":
|
|
248
575
|
return "never";
|
|
@@ -261,9 +588,9 @@ function subtractType(tv, predicate) {
|
|
|
261
588
|
}
|
|
262
589
|
function getPrimitiveTypeOf(tv) {
|
|
263
590
|
if (tv.kind === "literal") {
|
|
264
|
-
const
|
|
265
|
-
if (
|
|
266
|
-
return typeof
|
|
591
|
+
const v2 = tv.value;
|
|
592
|
+
if (v2 === null) return "object";
|
|
593
|
+
return typeof v2;
|
|
267
594
|
}
|
|
268
595
|
if (tv.kind === "primitive") return tv.type;
|
|
269
596
|
if (tv.kind === "refined") return getPrimitiveTypeOf(tv.base);
|
|
@@ -277,6 +604,15 @@ function getPrimitiveTypeOf(tv) {
|
|
|
277
604
|
function getRefinedBase(tv) {
|
|
278
605
|
return tv.kind === "refined" ? getRefinedBase(tv.base) : tv;
|
|
279
606
|
}
|
|
607
|
+
function isFnSig(tv) {
|
|
608
|
+
return tv.kind === "function" && "_signature" in tv;
|
|
609
|
+
}
|
|
610
|
+
function getFnSig(tv) {
|
|
611
|
+
if (tv.kind === "function" && "_signature" in tv) {
|
|
612
|
+
return tv._signature;
|
|
613
|
+
}
|
|
614
|
+
return void 0;
|
|
615
|
+
}
|
|
280
616
|
|
|
281
617
|
// src/refinements/range.ts
|
|
282
618
|
function formatRangeName(meta) {
|
|
@@ -491,9 +827,6 @@ function createTemplate(parts) {
|
|
|
491
827
|
}
|
|
492
828
|
return T.refine(T.string, createTemplateRefinement(normalized));
|
|
493
829
|
}
|
|
494
|
-
function isTemplate(tv) {
|
|
495
|
-
return tv.kind === "refined" && Array.isArray(tv.refinement.meta.parts);
|
|
496
|
-
}
|
|
497
830
|
function getTemplateParts(tv) {
|
|
498
831
|
if (tv.kind === "refined" && Array.isArray(tv.refinement.meta.parts)) {
|
|
499
832
|
return tv.refinement.meta.parts;
|
|
@@ -513,11 +846,34 @@ function bothLiteral(l, r) {
|
|
|
513
846
|
}
|
|
514
847
|
return null;
|
|
515
848
|
}
|
|
849
|
+
function isNullishLiteral(tv) {
|
|
850
|
+
return tv.kind === "literal" && (tv.value === null || tv.value === void 0);
|
|
851
|
+
}
|
|
852
|
+
function definitelyNotNullish(tv) {
|
|
853
|
+
switch (tv.kind) {
|
|
854
|
+
case "literal":
|
|
855
|
+
return tv.value !== null && tv.value !== void 0;
|
|
856
|
+
case "primitive":
|
|
857
|
+
case "object":
|
|
858
|
+
case "array":
|
|
859
|
+
case "tuple":
|
|
860
|
+
case "function":
|
|
861
|
+
case "instance":
|
|
862
|
+
case "promise":
|
|
863
|
+
return true;
|
|
864
|
+
case "refined":
|
|
865
|
+
return definitelyNotNullish(tv.base);
|
|
866
|
+
case "union":
|
|
867
|
+
return tv.members.every(definitelyNotNullish);
|
|
868
|
+
default:
|
|
869
|
+
return false;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
516
872
|
var Ops = {
|
|
517
873
|
add(left, right) {
|
|
518
|
-
const
|
|
519
|
-
if (
|
|
520
|
-
return T.literal(
|
|
874
|
+
const lit2 = bothLiteral(left, right);
|
|
875
|
+
if (lit2) {
|
|
876
|
+
return T.literal(lit2.lv + lit2.rv);
|
|
521
877
|
}
|
|
522
878
|
const leftIsString = isSubtypeOf(left, T.string) || isTemplate(left);
|
|
523
879
|
const rightIsString = isSubtypeOf(right, T.string) || isTemplate(right);
|
|
@@ -533,97 +889,49 @@ var Ops = {
|
|
|
533
889
|
}
|
|
534
890
|
return T.union(T.number, T.string);
|
|
535
891
|
},
|
|
536
|
-
sub(left, right) {
|
|
537
|
-
const lit = bothLiteral(left, right);
|
|
538
|
-
if (lit && typeof lit.lv === "number" && typeof lit.rv === "number") {
|
|
539
|
-
return T.literal(lit.lv - lit.rv);
|
|
540
|
-
}
|
|
541
|
-
return T.number;
|
|
542
|
-
},
|
|
543
|
-
mul(left, right) {
|
|
544
|
-
const lit = bothLiteral(left, right);
|
|
545
|
-
if (lit && typeof lit.lv === "number" && typeof lit.rv === "number") {
|
|
546
|
-
return T.literal(lit.lv * lit.rv);
|
|
547
|
-
}
|
|
548
|
-
return T.number;
|
|
549
|
-
},
|
|
550
|
-
div(left, right) {
|
|
551
|
-
const lit = bothLiteral(left, right);
|
|
552
|
-
if (lit && typeof lit.lv === "number" && typeof lit.rv === "number") {
|
|
553
|
-
return T.literal(lit.lv / lit.rv);
|
|
554
|
-
}
|
|
555
|
-
return T.number;
|
|
556
|
-
},
|
|
557
|
-
mod(left, right) {
|
|
558
|
-
const lit = bothLiteral(left, right);
|
|
559
|
-
if (lit && typeof lit.lv === "number" && typeof lit.rv === "number") {
|
|
560
|
-
return T.literal(lit.lv % lit.rv);
|
|
561
|
-
}
|
|
562
|
-
return T.number;
|
|
563
|
-
},
|
|
564
892
|
strictEq(left, right) {
|
|
565
|
-
const
|
|
566
|
-
if (
|
|
893
|
+
const lit2 = bothLiteral(left, right);
|
|
894
|
+
if (lit2) return T.literal(lit2.lv === lit2.rv);
|
|
895
|
+
if (isNullishLiteral(right) && definitelyNotNullish(left) || isNullishLiteral(left) && definitelyNotNullish(right)) {
|
|
896
|
+
return T.literal(false);
|
|
897
|
+
}
|
|
567
898
|
return T.boolean;
|
|
568
899
|
},
|
|
569
900
|
strictNeq(left, right) {
|
|
570
|
-
const
|
|
571
|
-
if (
|
|
901
|
+
const lit2 = bothLiteral(left, right);
|
|
902
|
+
if (lit2) return T.literal(lit2.lv !== lit2.rv);
|
|
903
|
+
if (isNullishLiteral(right) && definitelyNotNullish(left) || isNullishLiteral(left) && definitelyNotNullish(right)) {
|
|
904
|
+
return T.literal(true);
|
|
905
|
+
}
|
|
572
906
|
return T.boolean;
|
|
573
907
|
},
|
|
574
908
|
gt(left, right) {
|
|
575
|
-
const
|
|
576
|
-
if (
|
|
909
|
+
const lit2 = bothLiteral(left, right);
|
|
910
|
+
if (lit2) return T.literal(lit2.lv > lit2.rv);
|
|
577
911
|
return T.boolean;
|
|
578
912
|
},
|
|
579
913
|
lt(left, right) {
|
|
580
|
-
const
|
|
581
|
-
if (
|
|
914
|
+
const lit2 = bothLiteral(left, right);
|
|
915
|
+
if (lit2) return T.literal(lit2.lv < lit2.rv);
|
|
582
916
|
return T.boolean;
|
|
583
917
|
},
|
|
584
918
|
gte(left, right) {
|
|
585
|
-
const
|
|
586
|
-
if (
|
|
919
|
+
const lit2 = bothLiteral(left, right);
|
|
920
|
+
if (lit2) return T.literal(lit2.lv >= lit2.rv);
|
|
587
921
|
return T.boolean;
|
|
588
922
|
},
|
|
589
923
|
lte(left, right) {
|
|
590
|
-
const
|
|
591
|
-
if (
|
|
924
|
+
const lit2 = bothLiteral(left, right);
|
|
925
|
+
if (lit2) return T.literal(lit2.lv <= lit2.rv);
|
|
592
926
|
return T.boolean;
|
|
593
927
|
},
|
|
594
|
-
typeof_(operand) {
|
|
595
|
-
if (operand.kind === "literal") {
|
|
596
|
-
const v = operand.value;
|
|
597
|
-
if (v === null) return T.literal("object");
|
|
598
|
-
return T.literal(typeof v);
|
|
599
|
-
}
|
|
600
|
-
if (operand.kind === "primitive") return T.literal(operand.type);
|
|
601
|
-
if (operand.kind === "refined") return Ops.typeof_(getRefinedBase(operand));
|
|
602
|
-
if (operand.kind === "object") return T.literal("object");
|
|
603
|
-
if (operand.kind === "array" || operand.kind === "tuple")
|
|
604
|
-
return T.literal("object");
|
|
605
|
-
if (operand.kind === "function") return T.literal("function");
|
|
606
|
-
if (operand.kind === "promise") return T.literal("object");
|
|
607
|
-
if (operand.kind === "instance") return T.literal("object");
|
|
608
|
-
return T.string;
|
|
609
|
-
},
|
|
610
928
|
not(operand) {
|
|
611
929
|
if (operand.kind === "literal") return T.literal(!operand.value);
|
|
612
930
|
return T.boolean;
|
|
613
|
-
},
|
|
614
|
-
neg(operand) {
|
|
615
|
-
if (operand.kind === "literal" && typeof operand.value === "number") {
|
|
616
|
-
return T.literal(-operand.value);
|
|
617
|
-
}
|
|
618
|
-
return T.number;
|
|
619
931
|
}
|
|
620
932
|
};
|
|
621
933
|
var binaryOpMap = {
|
|
622
934
|
"+": Ops.add,
|
|
623
|
-
"-": Ops.sub,
|
|
624
|
-
"*": Ops.mul,
|
|
625
|
-
"/": Ops.div,
|
|
626
|
-
"%": Ops.mod,
|
|
627
935
|
"===": Ops.strictEq,
|
|
628
936
|
"!==": Ops.strictNeq,
|
|
629
937
|
">": Ops.gt,
|
|
@@ -697,11 +1005,14 @@ function createEnvironment(parent, bindings = /* @__PURE__ */ new Map()) {
|
|
|
697
1005
|
},
|
|
698
1006
|
extend(newBindings) {
|
|
699
1007
|
const childMap = /* @__PURE__ */ new Map();
|
|
700
|
-
for (const [k,
|
|
701
|
-
childMap.set(k,
|
|
1008
|
+
for (const [k, v2] of Object.entries(newBindings)) {
|
|
1009
|
+
childMap.set(k, v2);
|
|
702
1010
|
}
|
|
703
1011
|
return createEnvironment(env, childMap);
|
|
704
1012
|
},
|
|
1013
|
+
fork() {
|
|
1014
|
+
return createEnvironment(env);
|
|
1015
|
+
},
|
|
705
1016
|
has(name) {
|
|
706
1017
|
return store.has(name) || (parent?.has(name) ?? false);
|
|
707
1018
|
},
|
|
@@ -712,40 +1023,4108 @@ function createEnvironment(parent, bindings = /* @__PURE__ */ new Map()) {
|
|
|
712
1023
|
},
|
|
713
1024
|
getOwnBindings() {
|
|
714
1025
|
const result = {};
|
|
715
|
-
for (const [k,
|
|
716
|
-
result[k] =
|
|
1026
|
+
for (const [k, v2] of store) {
|
|
1027
|
+
result[k] = v2;
|
|
717
1028
|
}
|
|
718
1029
|
return result;
|
|
719
1030
|
}
|
|
720
1031
|
};
|
|
721
1032
|
return env;
|
|
722
1033
|
}
|
|
1034
|
+
|
|
1035
|
+
// src/mock-helpers.ts
|
|
1036
|
+
function stub() {
|
|
1037
|
+
return { kind: "mock-helper" };
|
|
1038
|
+
}
|
|
1039
|
+
stub.returns = function(value) {
|
|
1040
|
+
return { kind: "mock-helper", returnValue: value };
|
|
1041
|
+
};
|
|
1042
|
+
stub.resolves = function(value) {
|
|
1043
|
+
return { kind: "mock-helper", resolvedValue: value };
|
|
1044
|
+
};
|
|
1045
|
+
stub.rejects = function(value) {
|
|
1046
|
+
return { kind: "mock-helper", rejectedValue: value };
|
|
1047
|
+
};
|
|
1048
|
+
stub.onFirstCall = function(value) {
|
|
1049
|
+
return { kind: "mock-helper", onFirstCallValue: value };
|
|
1050
|
+
};
|
|
1051
|
+
stub.onSecondCall = function(value) {
|
|
1052
|
+
return { kind: "mock-helper", onSecondCallValue: value };
|
|
1053
|
+
};
|
|
1054
|
+
stub.withArgs = function(...args) {
|
|
1055
|
+
return { kind: "mock-helper", withArgsCases: [{ args, returnValue: T.unknown }] };
|
|
1056
|
+
};
|
|
1057
|
+
stub.callsFake = function(fn) {
|
|
1058
|
+
return { kind: "mock-helper", callsFakeImpl: fn };
|
|
1059
|
+
};
|
|
1060
|
+
function spy() {
|
|
1061
|
+
return { kind: "mock-helper" };
|
|
1062
|
+
}
|
|
1063
|
+
spy.returns = function(value) {
|
|
1064
|
+
return { kind: "mock-helper", returnValue: value };
|
|
1065
|
+
};
|
|
1066
|
+
function mock() {
|
|
1067
|
+
return { kind: "mock-helper" };
|
|
1068
|
+
}
|
|
1069
|
+
function mockArgMatches(declared, actual) {
|
|
1070
|
+
if (actual === void 0) return false;
|
|
1071
|
+
if (typeValueEquals(declared, actual)) return true;
|
|
1072
|
+
if (declared.kind === "primitive" && actual.kind === "literal") {
|
|
1073
|
+
const t = typeof actual.value;
|
|
1074
|
+
return declared.type === "number" && t === "number" || declared.type === "string" && t === "string" || declared.type === "boolean" && t === "boolean" || declared.type === "bigint" && t === "bigint" || declared.type === "symbol" && t === "symbol";
|
|
1075
|
+
}
|
|
1076
|
+
return false;
|
|
1077
|
+
}
|
|
1078
|
+
function mockHelperToTypeValue(helper, env) {
|
|
1079
|
+
if (helper.callsFakeImpl) {
|
|
1080
|
+
if (helper.callsFakeImpl.kind === "function") {
|
|
1081
|
+
return helper.callsFakeImpl;
|
|
1082
|
+
}
|
|
1083
|
+
const plain = T.fn(["...args"], { type: "BlockStatement", body: [] }, env);
|
|
1084
|
+
plain._directReturn = helper.callsFakeImpl;
|
|
1085
|
+
return plain;
|
|
1086
|
+
}
|
|
1087
|
+
let defaultReturn;
|
|
1088
|
+
if (helper.resolvedValue) {
|
|
1089
|
+
defaultReturn = T.promise(helper.resolvedValue);
|
|
1090
|
+
} else if (helper.rejectedValue) {
|
|
1091
|
+
defaultReturn = T.never;
|
|
1092
|
+
} else if (helper.returnValue) {
|
|
1093
|
+
defaultReturn = helper.returnValue;
|
|
1094
|
+
} else if (helper.onFirstCallValue) {
|
|
1095
|
+
defaultReturn = helper.onFirstCallValue;
|
|
1096
|
+
} else {
|
|
1097
|
+
defaultReturn = T.unknown;
|
|
1098
|
+
}
|
|
1099
|
+
if (helper.withArgsCases?.length) {
|
|
1100
|
+
const cases = helper.withArgsCases;
|
|
1101
|
+
return T.fnSig(
|
|
1102
|
+
cases[0].args.map(() => T.unknown),
|
|
1103
|
+
T.union(...cases.map((c) => c.returnValue), defaultReturn),
|
|
1104
|
+
T.never,
|
|
1105
|
+
(args) => {
|
|
1106
|
+
for (const c of cases) {
|
|
1107
|
+
if (c.args.every((a, i) => mockArgMatches(a, args[i]))) {
|
|
1108
|
+
return c.returnValue;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return defaultReturn;
|
|
1112
|
+
}
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
const body = { type: "BlockStatement", body: [] };
|
|
1116
|
+
const fn = T.fn(["...args"], body, env);
|
|
1117
|
+
fn._directReturn = defaultReturn;
|
|
1118
|
+
return fn;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// src/algebra/phi.ts
|
|
1122
|
+
var phiStack = [pTrue];
|
|
1123
|
+
function currentPhi() {
|
|
1124
|
+
return phiStack[phiStack.length - 1] ?? pTrue;
|
|
1125
|
+
}
|
|
1126
|
+
function pushPhi(p) {
|
|
1127
|
+
phiStack.push(p);
|
|
1128
|
+
}
|
|
1129
|
+
function popPhi() {
|
|
1130
|
+
if (phiStack.length > 1) phiStack.pop();
|
|
1131
|
+
}
|
|
1132
|
+
function resetPhi() {
|
|
1133
|
+
phiStack = [pTrue];
|
|
1134
|
+
}
|
|
1135
|
+
function withPhiConstraint(extra, body) {
|
|
1136
|
+
pushPhi(extra);
|
|
1137
|
+
try {
|
|
1138
|
+
body();
|
|
1139
|
+
} finally {
|
|
1140
|
+
popPhi();
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
function describePhi() {
|
|
1144
|
+
const p = currentPhi();
|
|
1145
|
+
return p.op === "true" ? "\u22A4" : "(phi)";
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// src/algebra/format.ts
|
|
1149
|
+
function formatAbs(a, opts = {}) {
|
|
1150
|
+
const showTerm = opts.showTerm !== false;
|
|
1151
|
+
const showPred = opts.showPred !== false;
|
|
1152
|
+
const parts = [];
|
|
1153
|
+
parts.push(formatShape(a));
|
|
1154
|
+
if (showTerm && a.term && a.term.op !== "lit") {
|
|
1155
|
+
parts.push(`= ${termToString(a.term)}`);
|
|
1156
|
+
}
|
|
1157
|
+
if (showPred && a.pred && a.pred.op !== "true") {
|
|
1158
|
+
parts.push(`where ${predToString(a.pred)}`);
|
|
1159
|
+
}
|
|
1160
|
+
parts.push(`#${a.conf}`);
|
|
1161
|
+
return parts.join(" ");
|
|
1162
|
+
}
|
|
1163
|
+
function formatShapeSlot(a) {
|
|
1164
|
+
const base = formatShape(a);
|
|
1165
|
+
if (!a.term || a.term.op === "lit") return base;
|
|
1166
|
+
if (a.shape.k === "fn" || a.shape.k === "arr") return base;
|
|
1167
|
+
if (a.shape.k === "any" && a.term.op === "var") return termToString(a.term);
|
|
1168
|
+
return `${base} = ${termToString(a.term)}`;
|
|
1169
|
+
}
|
|
1170
|
+
function formatShape(a) {
|
|
1171
|
+
const s = a.shape;
|
|
1172
|
+
switch (s.k) {
|
|
1173
|
+
case "never":
|
|
1174
|
+
return "never";
|
|
1175
|
+
case "any":
|
|
1176
|
+
return "any";
|
|
1177
|
+
case "unknown":
|
|
1178
|
+
return "unknown";
|
|
1179
|
+
case "prim": {
|
|
1180
|
+
const lv = litValue(a);
|
|
1181
|
+
if (lv !== void 0) return JSON.stringify(lv);
|
|
1182
|
+
return s.type;
|
|
1183
|
+
}
|
|
1184
|
+
case "obj": {
|
|
1185
|
+
const entries = Object.entries(s.slots).map(([k, slot]) => {
|
|
1186
|
+
const opt = slot.optional ? "?" : "";
|
|
1187
|
+
return `${k}${opt}: ${formatShape(slot.value)}`;
|
|
1188
|
+
});
|
|
1189
|
+
return `{ ${entries.join(", ")} }`;
|
|
1190
|
+
}
|
|
1191
|
+
case "arr": {
|
|
1192
|
+
if (s.element.shape.k === "any" && s.element.term?.op === "var") {
|
|
1193
|
+
return `arr(${formatShapeSlot(s.element)})`;
|
|
1194
|
+
}
|
|
1195
|
+
return `${formatShape(s.element)}[]`;
|
|
1196
|
+
}
|
|
1197
|
+
case "tuple":
|
|
1198
|
+
return `[${s.elements.map(formatShape).join(", ")}]`;
|
|
1199
|
+
case "fn": {
|
|
1200
|
+
if (s.paramTypes && s.paramTypes.length > 0) {
|
|
1201
|
+
const ps = s.paramTypes.map((p) => formatShapeSlot(p));
|
|
1202
|
+
const ret2 = s.returnType !== void 0 ? formatShapeSlot(s.returnType) : "?";
|
|
1203
|
+
return `(${ps.join(", ")}) => ${ret2}`;
|
|
1204
|
+
}
|
|
1205
|
+
const ret = s.returnType !== void 0 ? formatShapeSlot(s.returnType) : "?";
|
|
1206
|
+
return `(${s.params.join(", ")}) => ${ret}`;
|
|
1207
|
+
}
|
|
1208
|
+
case "brand":
|
|
1209
|
+
return `${s.name}`;
|
|
1210
|
+
case "eff":
|
|
1211
|
+
return `${s.eff}<${formatShape(s.inner)}>`;
|
|
1212
|
+
case "sum":
|
|
1213
|
+
return s.members.map(formatShape).join(" | ");
|
|
1214
|
+
default:
|
|
1215
|
+
return "\xB7";
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
function formatAbsMultiline(a, label) {
|
|
1219
|
+
const lines = [];
|
|
1220
|
+
if (label) lines.push(label);
|
|
1221
|
+
lines.push(` ${formatShape(a)}`);
|
|
1222
|
+
if (a.term && a.term.op !== "lit") {
|
|
1223
|
+
lines.push(` term: ${termToString(a.term)}`);
|
|
1224
|
+
}
|
|
1225
|
+
if (a.pred && a.pred.op !== "true") {
|
|
1226
|
+
lines.push(` pred: ${predToString(a.pred)}`);
|
|
1227
|
+
}
|
|
1228
|
+
lines.push(` conf: ${a.conf}`);
|
|
1229
|
+
return lines.join("\n");
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// src/algebra/constraint.ts
|
|
1233
|
+
var SELF = "__nudo_self__";
|
|
1234
|
+
function selfTerm() {
|
|
1235
|
+
return v(SELF);
|
|
1236
|
+
}
|
|
1237
|
+
function getTerm(obj2, key) {
|
|
1238
|
+
return app("get", [obj2, lit(key)]);
|
|
1239
|
+
}
|
|
1240
|
+
function lenTerm(t) {
|
|
1241
|
+
return app("length", [t]);
|
|
1242
|
+
}
|
|
1243
|
+
function isConstraint(x) {
|
|
1244
|
+
return !!x && typeof x === "object" && x.__nudoConstraint === true;
|
|
1245
|
+
}
|
|
1246
|
+
function isNudoConstraint(x) {
|
|
1247
|
+
return isConstraint(x);
|
|
1248
|
+
}
|
|
1249
|
+
function makeBuilder(prim, preds, extra) {
|
|
1250
|
+
const fields = extra?.fields;
|
|
1251
|
+
const element = extra?.element;
|
|
1252
|
+
const isInt = extra?.int;
|
|
1253
|
+
const optional = extra?.optional;
|
|
1254
|
+
const base = {
|
|
1255
|
+
__nudoConstraint: true,
|
|
1256
|
+
...prim ? { prim } : {},
|
|
1257
|
+
preds: [...preds],
|
|
1258
|
+
...fields ? { fields } : {},
|
|
1259
|
+
...element ? { element } : {},
|
|
1260
|
+
...isInt ? { int: true } : {},
|
|
1261
|
+
...optional ? { isOptional: true } : {}
|
|
1262
|
+
};
|
|
1263
|
+
const add2 = (p) => makeBuilder(prim, [...preds, p], extra);
|
|
1264
|
+
return Object.assign(/* @__PURE__ */ Object.create(null), base, {
|
|
1265
|
+
gt: (n) => add2(gt(selfTerm(), lit(n))),
|
|
1266
|
+
ge: (n) => add2(ge(selfTerm(), lit(n))),
|
|
1267
|
+
lt: (n) => add2(lt(selfTerm(), lit(n))),
|
|
1268
|
+
le: (n) => add2(le(selfTerm(), lit(n))),
|
|
1269
|
+
int: () => makeBuilder(prim ?? "number", preds, { ...extra, int: true }),
|
|
1270
|
+
min: (n) => add2(ge(lenTerm(selfTerm()), lit(n))),
|
|
1271
|
+
max: (n) => add2(le(lenTerm(selfTerm()), lit(n))),
|
|
1272
|
+
length: (n) => add2(and(ge(lenTerm(selfTerm()), lit(n)), le(lenTerm(selfTerm()), lit(n)))),
|
|
1273
|
+
optional: () => makeBuilder(prim, preds, { ...extra, optional: true })
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
function number() {
|
|
1277
|
+
return makeBuilder("number", []);
|
|
1278
|
+
}
|
|
1279
|
+
function string() {
|
|
1280
|
+
return makeBuilder("string", []);
|
|
1281
|
+
}
|
|
1282
|
+
function boolean() {
|
|
1283
|
+
return makeBuilder("boolean", []);
|
|
1284
|
+
}
|
|
1285
|
+
function array(item) {
|
|
1286
|
+
if (!isConstraint(item)) return makeBuilder(void 0, []);
|
|
1287
|
+
return makeBuilder(void 0, [], { element: item });
|
|
1288
|
+
}
|
|
1289
|
+
function shape(fields) {
|
|
1290
|
+
const mapped = {};
|
|
1291
|
+
for (const [k, v2] of Object.entries(fields)) {
|
|
1292
|
+
if (!isConstraint(v2)) continue;
|
|
1293
|
+
mapped[k] = {
|
|
1294
|
+
constraint: v2,
|
|
1295
|
+
...v2.isOptional ? { optional: true } : {}
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
return makeBuilder(void 0, [], { fields: mapped });
|
|
1299
|
+
}
|
|
1300
|
+
function substTerm(t, paramName) {
|
|
1301
|
+
if (t.op === "var" && t.id === SELF) return v(paramName);
|
|
1302
|
+
if (t.op === "app" && t.fn === "get" && t.args.length === 2) {
|
|
1303
|
+
return getTerm(substTerm(t.args[0], paramName), String(
|
|
1304
|
+
t.args[1].op === "lit" ? t.args[1].value : ""
|
|
1305
|
+
));
|
|
1306
|
+
}
|
|
1307
|
+
return t;
|
|
1308
|
+
}
|
|
1309
|
+
function substPred2(p, paramName) {
|
|
1310
|
+
switch (p.op) {
|
|
1311
|
+
case "gt":
|
|
1312
|
+
case "ge":
|
|
1313
|
+
case "lt":
|
|
1314
|
+
case "le":
|
|
1315
|
+
case "eq":
|
|
1316
|
+
case "ne": {
|
|
1317
|
+
return { op: p.op, a: substTerm(p.a, paramName), b: substTerm(p.b, paramName) };
|
|
1318
|
+
}
|
|
1319
|
+
case "typeof": {
|
|
1320
|
+
return { op: "typeof", t: substTerm(p.t, paramName), type: p.type };
|
|
1321
|
+
}
|
|
1322
|
+
case "and":
|
|
1323
|
+
return and(...p.args.map((x) => substPred2(x, paramName)));
|
|
1324
|
+
default:
|
|
1325
|
+
return p;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
function instantiateConstraint(c, paramName) {
|
|
1329
|
+
if (c.fields) {
|
|
1330
|
+
const parts = [];
|
|
1331
|
+
for (const [key, field] of Object.entries(c.fields)) {
|
|
1332
|
+
const fieldTerm = getTerm(v(paramName), key);
|
|
1333
|
+
parts.push(instantiateOnTerm(field.constraint, fieldTerm));
|
|
1334
|
+
}
|
|
1335
|
+
if (c.prim) parts.push(ptypeof(v(paramName), c.prim));
|
|
1336
|
+
if (parts.length === 0) return { op: "true" };
|
|
1337
|
+
return parts.length === 1 ? parts[0] : and(...parts);
|
|
1338
|
+
}
|
|
1339
|
+
const preds = c.preds.map((p) => substPred2(p, paramName));
|
|
1340
|
+
if (c.prim && c.preds.length === 0) {
|
|
1341
|
+
return ptypeof(v(paramName), c.prim);
|
|
1342
|
+
}
|
|
1343
|
+
return preds.length === 0 ? { op: "true" } : preds.length === 1 ? preds[0] : and(...preds);
|
|
1344
|
+
}
|
|
1345
|
+
function instantiateOnTerm(c, t) {
|
|
1346
|
+
if (c.fields) {
|
|
1347
|
+
const parts = [];
|
|
1348
|
+
for (const [key, field] of Object.entries(c.fields)) {
|
|
1349
|
+
parts.push(instantiateOnTerm(field.constraint, getTerm(t, key)));
|
|
1350
|
+
}
|
|
1351
|
+
if (parts.length === 0) return { op: "true" };
|
|
1352
|
+
return parts.length === 1 ? parts[0] : and(...parts);
|
|
1353
|
+
}
|
|
1354
|
+
const subst = (p) => {
|
|
1355
|
+
switch (p.op) {
|
|
1356
|
+
case "gt":
|
|
1357
|
+
case "ge":
|
|
1358
|
+
case "lt":
|
|
1359
|
+
case "le":
|
|
1360
|
+
case "eq":
|
|
1361
|
+
case "ne": {
|
|
1362
|
+
const a = p.a.op === "var" && p.a.id === SELF ? t : p.a;
|
|
1363
|
+
const b = p.b.op === "var" && p.b.id === SELF ? t : p.b;
|
|
1364
|
+
return { op: p.op, a, b };
|
|
1365
|
+
}
|
|
1366
|
+
case "typeof": {
|
|
1367
|
+
const tt = p.t.op === "var" && p.t.id === SELF ? t : p.t;
|
|
1368
|
+
return { op: "typeof", t: tt, type: p.type };
|
|
1369
|
+
}
|
|
1370
|
+
case "and":
|
|
1371
|
+
return and(...p.args.map(subst));
|
|
1372
|
+
default:
|
|
1373
|
+
return p;
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1376
|
+
const preds = c.preds.map(subst);
|
|
1377
|
+
if (c.prim && c.preds.length === 0) return ptypeof(t, c.prim);
|
|
1378
|
+
return preds.length === 0 ? { op: "true" } : preds.length === 1 ? preds[0] : and(...preds);
|
|
1379
|
+
}
|
|
1380
|
+
function constraintToEntryAbs(c, paramName) {
|
|
1381
|
+
const t = v(paramName);
|
|
1382
|
+
if (c.fields) {
|
|
1383
|
+
const slots = {};
|
|
1384
|
+
for (const [key, field] of Object.entries(c.fields)) {
|
|
1385
|
+
const fieldTerm = getTerm(t, key);
|
|
1386
|
+
slots[key] = {
|
|
1387
|
+
value: constraintOnTermAbs(field.constraint, fieldTerm),
|
|
1388
|
+
...field.optional || field.constraint.isOptional ? { optional: true } : {}
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
return abs({ k: "obj", slots }, t, void 0, "path");
|
|
1392
|
+
}
|
|
1393
|
+
return constraintOnTermAbs(c, t);
|
|
1394
|
+
}
|
|
1395
|
+
function constraintOnTermAbs(c, t) {
|
|
1396
|
+
if (c.fields) {
|
|
1397
|
+
const slots = {};
|
|
1398
|
+
for (const [key, field] of Object.entries(c.fields)) {
|
|
1399
|
+
slots[key] = {
|
|
1400
|
+
value: constraintOnTermAbs(field.constraint, getTerm(t, key)),
|
|
1401
|
+
...field.optional || field.constraint.isOptional ? { optional: true } : {}
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
return abs({ k: "obj", slots }, t, void 0, "path");
|
|
1405
|
+
}
|
|
1406
|
+
if (c.element) {
|
|
1407
|
+
const elem = constraintOnTermAbs(c.element, v("x[]"));
|
|
1408
|
+
return abs({ k: "arr", element: elem }, t, void 0, "path");
|
|
1409
|
+
}
|
|
1410
|
+
const pred = instantiateOnTerm(c, t);
|
|
1411
|
+
const predOut = pred.op === "true" ? void 0 : pred;
|
|
1412
|
+
if (c.prim) {
|
|
1413
|
+
return abs({ k: "prim", type: c.prim }, t, predOut, "path");
|
|
1414
|
+
}
|
|
1415
|
+
if (c.preds.length > 0) {
|
|
1416
|
+
return abs({ k: "prim", type: "number" }, t, predOut, "path");
|
|
1417
|
+
}
|
|
1418
|
+
return abs({ k: "any" }, t, void 0, "path");
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
// src/algebra/refine.ts
|
|
1422
|
+
function extractNudoImports(source) {
|
|
1423
|
+
const out = [];
|
|
1424
|
+
const named = /@nudo:import\s*\{([^}]+)\}\s*from\s*["']([^"']+)["']/g;
|
|
1425
|
+
let m;
|
|
1426
|
+
while (m = named.exec(source)) {
|
|
1427
|
+
const names = m[1].split(",").map((s) => s.trim()).filter(Boolean).map((s) => s.split(/\s+as\s+/).pop().trim());
|
|
1428
|
+
out.push({ names, spec: m[2] });
|
|
1429
|
+
}
|
|
1430
|
+
const ns = /@nudo:import\s+\*\s+as\s+(\w+)\s+from\s*["']([^"']+)["']/g;
|
|
1431
|
+
while (m = ns.exec(source)) {
|
|
1432
|
+
out.push({ names: [`*${m[1]}`], spec: m[2] });
|
|
1433
|
+
}
|
|
1434
|
+
return out;
|
|
1435
|
+
}
|
|
1436
|
+
function execNudoModule(src) {
|
|
1437
|
+
const names = [...src.matchAll(/export\s+const\s+(\w+)/g)].map((m) => m[1]);
|
|
1438
|
+
let body = src.replace(/export\s+const\b/g, "const");
|
|
1439
|
+
body = body.replace(/^\s*import\s*\{[^}]*\}\s*from\s*["'][^"']+["'];?\s*$/gm, "");
|
|
1440
|
+
body = body.replace(/^\s*import\s+\*\s+as\s+\w+\s+from\s*["'][^"']+["'];?\s*$/gm, "");
|
|
1441
|
+
body += `
|
|
1442
|
+
return { ${names.join(", ")} };`;
|
|
1443
|
+
const fn = new Function("number", "string", "boolean", "shape", "array", body);
|
|
1444
|
+
return fn(number, string, boolean, shape, array);
|
|
1445
|
+
}
|
|
1446
|
+
var nudoModuleExecCache = /* @__PURE__ */ new Map();
|
|
1447
|
+
var MAX_NUDO_MODULE_EXEC = 64;
|
|
1448
|
+
function execNudoModuleCached(src) {
|
|
1449
|
+
const hit = nudoModuleExecCache.get(src);
|
|
1450
|
+
if (hit !== void 0) {
|
|
1451
|
+
nudoModuleExecCache.delete(src);
|
|
1452
|
+
nudoModuleExecCache.set(src, hit);
|
|
1453
|
+
return hit;
|
|
1454
|
+
}
|
|
1455
|
+
const out = execNudoModule(src);
|
|
1456
|
+
if (nudoModuleExecCache.size >= MAX_NUDO_MODULE_EXEC) {
|
|
1457
|
+
const oldest = nudoModuleExecCache.keys().next().value;
|
|
1458
|
+
if (oldest !== void 0) nudoModuleExecCache.delete(oldest);
|
|
1459
|
+
}
|
|
1460
|
+
nudoModuleExecCache.set(src, out);
|
|
1461
|
+
return out;
|
|
1462
|
+
}
|
|
1463
|
+
function resetNudoModuleExecCache() {
|
|
1464
|
+
nudoModuleExecCache.clear();
|
|
1465
|
+
}
|
|
1466
|
+
function collectConstraints(source, opts) {
|
|
1467
|
+
const map = /* @__PURE__ */ new Map();
|
|
1468
|
+
const imports = extractNudoImports(source);
|
|
1469
|
+
for (const imp of imports) {
|
|
1470
|
+
if (!opts.loadModule || !opts.fromFile) continue;
|
|
1471
|
+
const src = opts.loadModule(imp.spec, opts.fromFile);
|
|
1472
|
+
if (!src) continue;
|
|
1473
|
+
let exports;
|
|
1474
|
+
try {
|
|
1475
|
+
exports = execNudoModuleCached(src);
|
|
1476
|
+
} catch {
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1479
|
+
for (const name of imp.names) {
|
|
1480
|
+
if (name.startsWith("*")) continue;
|
|
1481
|
+
const v2 = exports[name];
|
|
1482
|
+
if (isNudoConstraint(v2)) map.set(name, v2);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
return map;
|
|
1486
|
+
}
|
|
1487
|
+
function extractRefineLines(source, fnName) {
|
|
1488
|
+
const fnRe = new RegExp(
|
|
1489
|
+
`(?:export\\s+default\\s+)?(?:function\\s+${fnName}\\b|const\\s+${fnName}\\s*=)`
|
|
1490
|
+
);
|
|
1491
|
+
const m = source.match(fnRe);
|
|
1492
|
+
if (!m || m.index === void 0) return [];
|
|
1493
|
+
const before = source.slice(0, m.index);
|
|
1494
|
+
const lines = before.split("\n");
|
|
1495
|
+
const reqs = [];
|
|
1496
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
1497
|
+
const line = lines[i].trim();
|
|
1498
|
+
if (line === "" || line === "*/") continue;
|
|
1499
|
+
if (line.startsWith("*") || line.startsWith("/*") || line.startsWith("//")) {
|
|
1500
|
+
const rm = line.match(/@nudo:refine\s+(.+)$/);
|
|
1501
|
+
if (rm) reqs.unshift(rm[1].trim().replace(/\*\/$/, "").trim());
|
|
1502
|
+
continue;
|
|
1503
|
+
}
|
|
1504
|
+
break;
|
|
1505
|
+
}
|
|
1506
|
+
return reqs;
|
|
1507
|
+
}
|
|
1508
|
+
function extractRefinesFromSource(source, fnName, opts = {}) {
|
|
1509
|
+
if (!source.includes("@nudo:refine")) return [];
|
|
1510
|
+
const constraints = collectConstraints(source, opts);
|
|
1511
|
+
const out = [];
|
|
1512
|
+
for (const line of extractRefineLines(source, fnName)) {
|
|
1513
|
+
const parts = line.split(/&&|,/).map((s) => s.trim()).filter(Boolean);
|
|
1514
|
+
for (const part of parts) {
|
|
1515
|
+
const m = part.match(/^(\w+)\s+(\w+)$/);
|
|
1516
|
+
if (!m) continue;
|
|
1517
|
+
const [, param, cName] = m;
|
|
1518
|
+
if (param === "return") continue;
|
|
1519
|
+
const c = constraints.get(cName);
|
|
1520
|
+
if (!c) continue;
|
|
1521
|
+
out.push({
|
|
1522
|
+
param,
|
|
1523
|
+
pred: instantiateConstraint(c, param),
|
|
1524
|
+
constraint: c
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
return out;
|
|
1529
|
+
}
|
|
1530
|
+
function refineToIndexedFull(source, fnName, paramNames, opts = {}) {
|
|
1531
|
+
const raw = extractRefinesFromSource(source, fnName, opts);
|
|
1532
|
+
const out = [];
|
|
1533
|
+
for (const item of raw) {
|
|
1534
|
+
const idx = paramNames.indexOf(item.param);
|
|
1535
|
+
if (idx >= 0) out.push([idx, item]);
|
|
1536
|
+
}
|
|
1537
|
+
return out;
|
|
1538
|
+
}
|
|
1539
|
+
function extractRefineReturnFromSource(source, fnName, opts = {}) {
|
|
1540
|
+
if (!source.includes("@nudo:refine")) return void 0;
|
|
1541
|
+
const constraints = collectConstraints(source, opts);
|
|
1542
|
+
for (const line of extractRefineLines(source, fnName)) {
|
|
1543
|
+
const parts = line.split(/&&|,/).map((s) => s.trim()).filter(Boolean);
|
|
1544
|
+
for (const part of parts) {
|
|
1545
|
+
const m = part.match(/^return\s+(\w+)$/);
|
|
1546
|
+
if (!m) continue;
|
|
1547
|
+
const cName = m[1];
|
|
1548
|
+
const c = constraints.get(cName);
|
|
1549
|
+
if (!c) continue;
|
|
1550
|
+
return { name: cName, constraint: c };
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
return void 0;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// src/algebra/hash-source.ts
|
|
1557
|
+
var lastHashSource;
|
|
1558
|
+
var lastHashOut;
|
|
1559
|
+
function hashSource(s) {
|
|
1560
|
+
if (s === lastHashSource && lastHashOut !== void 0) return lastHashOut;
|
|
1561
|
+
let h1 = 2166136261;
|
|
1562
|
+
let h2 = 2654435769;
|
|
1563
|
+
for (let i = 0; i < s.length; i++) {
|
|
1564
|
+
const c = s.charCodeAt(i);
|
|
1565
|
+
h1 ^= c;
|
|
1566
|
+
h1 = Math.imul(h1, 16777619);
|
|
1567
|
+
h2 = Math.imul(h2 ^ c, 16777619);
|
|
1568
|
+
}
|
|
1569
|
+
lastHashSource = s;
|
|
1570
|
+
lastHashOut = `${(h1 >>> 0).toString(36)}-${(h2 >>> 0).toString(36)}_${s.length.toString(36)}`;
|
|
1571
|
+
return lastHashOut;
|
|
1572
|
+
}
|
|
1573
|
+
function resetHashSourceCache() {
|
|
1574
|
+
lastHashSource = void 0;
|
|
1575
|
+
lastHashOut = void 0;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
// src/algebra/fn-fp.ts
|
|
1579
|
+
var fnFpCache = /* @__PURE__ */ new Map();
|
|
1580
|
+
var MAX_FN_FP_CACHE = 24;
|
|
1581
|
+
var MAX_FN_FP_SOURCE_CHARS = 1e6;
|
|
1582
|
+
function resetFnFpCache() {
|
|
1583
|
+
fnFpCache.clear();
|
|
1584
|
+
}
|
|
1585
|
+
function getFnNameAndBodies(decl) {
|
|
1586
|
+
const out = [];
|
|
1587
|
+
if (decl.type === "FunctionDeclaration" && decl.id) {
|
|
1588
|
+
out.push({ name: decl.id.name, body: decl.body, fnNode: decl });
|
|
1589
|
+
return out;
|
|
1590
|
+
}
|
|
1591
|
+
if (decl.type === "VariableDeclaration") {
|
|
1592
|
+
for (const d of decl.declarations) {
|
|
1593
|
+
if (d.id.type === "Identifier" && d.init && (d.init.type === "ArrowFunctionExpression" || d.init.type === "FunctionExpression")) {
|
|
1594
|
+
const comments = d.leadingComments;
|
|
1595
|
+
const first = comments?.[0];
|
|
1596
|
+
out.push({
|
|
1597
|
+
name: d.id.name,
|
|
1598
|
+
body: d.init.body,
|
|
1599
|
+
fnNode: d.init,
|
|
1600
|
+
declaratorStart: d.start ?? void 0,
|
|
1601
|
+
declaratorLeadStart: typeof first?.start === "number" ? first.start : void 0
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
return out;
|
|
1607
|
+
}
|
|
1608
|
+
function leadingSliceStart(stmt, decl) {
|
|
1609
|
+
const comments = stmt.leadingComments ?? decl.leadingComments;
|
|
1610
|
+
const first = comments?.[0];
|
|
1611
|
+
if (first && typeof first.start === "number") return first.start;
|
|
1612
|
+
return stmt.start ?? decl.start ?? 0;
|
|
1613
|
+
}
|
|
1614
|
+
function collectSiblingRefs(node, known, out) {
|
|
1615
|
+
if (!node || typeof node !== "object") return;
|
|
1616
|
+
const obj2 = node;
|
|
1617
|
+
if (obj2.type === "Identifier" && typeof obj2.name === "string" && known.has(obj2.name)) {
|
|
1618
|
+
out.add(obj2.name);
|
|
1619
|
+
}
|
|
1620
|
+
for (const key of Object.keys(obj2)) {
|
|
1621
|
+
if (key === "loc" || key === "start" || key === "end" || key === "leadingComments" || key === "trailingComments" || key === "innerComments") {
|
|
1622
|
+
continue;
|
|
1623
|
+
}
|
|
1624
|
+
if ((key === "key" || key === "property") && (obj2.type === "MemberExpression" || obj2.type === "ObjectProperty" || obj2.type === "ObjectMethod" || obj2.type === "ClassMethod" || obj2.type === "ClassProperty") && obj2.computed !== true) {
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1627
|
+
if (key === "id" && (obj2.type === "FunctionDeclaration" || obj2.type === "FunctionExpression" || obj2.type === "ClassDeclaration" || obj2.type === "ClassExpression")) {
|
|
1628
|
+
continue;
|
|
1629
|
+
}
|
|
1630
|
+
const v2 = obj2[key];
|
|
1631
|
+
if (Array.isArray(v2)) {
|
|
1632
|
+
for (const x of v2) collectSiblingRefs(x, known, out);
|
|
1633
|
+
} else if (v2 && typeof v2 === "object") {
|
|
1634
|
+
collectSiblingRefs(v2, known, out);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
function computeFnFingerprints(source, file) {
|
|
1639
|
+
const top = [];
|
|
1640
|
+
const contextParts = [];
|
|
1641
|
+
for (const stmt of file.program.body) {
|
|
1642
|
+
let decl = stmt;
|
|
1643
|
+
if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
|
|
1644
|
+
decl = stmt.declaration;
|
|
1645
|
+
} else if (stmt.type === "ExportDefaultDeclaration" && stmt.declaration) {
|
|
1646
|
+
decl = stmt.declaration;
|
|
1647
|
+
}
|
|
1648
|
+
const fnInfos = getFnNameAndBodies(decl);
|
|
1649
|
+
if (fnInfos.length > 0) {
|
|
1650
|
+
const lead = leadingSliceStart(stmt, decl);
|
|
1651
|
+
for (let i = 0; i < fnInfos.length; i++) {
|
|
1652
|
+
const info = fnInfos[i];
|
|
1653
|
+
const sliceStart = i === 0 ? lead : info.declaratorLeadStart ?? info.declaratorStart ?? info.fnNode.start ?? lead;
|
|
1654
|
+
const end = info.fnNode.end ?? info.declaratorStart ?? decl.end ?? stmt.end ?? sliceStart;
|
|
1655
|
+
top.push({
|
|
1656
|
+
name: info.name,
|
|
1657
|
+
slice: source.slice(sliceStart, Math.max(end, sliceStart)),
|
|
1658
|
+
fnNode: info.fnNode
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
} else if (typeof stmt.start === "number" && typeof stmt.end === "number") {
|
|
1662
|
+
contextParts.push(source.slice(stmt.start, stmt.end));
|
|
1663
|
+
} else {
|
|
1664
|
+
contextParts.push(stmt.type);
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
const context = hashSource(contextParts.join("\n "));
|
|
1668
|
+
const names = new Set(top.map((t) => t.name));
|
|
1669
|
+
const own = /* @__PURE__ */ new Map();
|
|
1670
|
+
for (const t of top) own.set(t.name, hashSource(t.slice));
|
|
1671
|
+
const edges = /* @__PURE__ */ new Map();
|
|
1672
|
+
for (const t of top) {
|
|
1673
|
+
const refs = /* @__PURE__ */ new Set();
|
|
1674
|
+
collectSiblingRefs(t.fnNode, names, refs);
|
|
1675
|
+
edges.set(t.name, refs);
|
|
1676
|
+
}
|
|
1677
|
+
const result = /* @__PURE__ */ new Map();
|
|
1678
|
+
for (const t of top) {
|
|
1679
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1680
|
+
const stack = [...edges.get(t.name) ?? []];
|
|
1681
|
+
const depParts = [];
|
|
1682
|
+
while (stack.length > 0) {
|
|
1683
|
+
const cur = stack.pop();
|
|
1684
|
+
if (seen.has(cur)) continue;
|
|
1685
|
+
seen.add(cur);
|
|
1686
|
+
depParts.push(`${cur}=${own.get(cur) ?? "?"}`);
|
|
1687
|
+
for (const next of edges.get(cur) ?? []) {
|
|
1688
|
+
if (!seen.has(next)) stack.push(next);
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
depParts.sort();
|
|
1692
|
+
result.set(t.name, {
|
|
1693
|
+
own: own.get(t.name),
|
|
1694
|
+
deps: hashSource(`${depParts.join(",")} ${context}`),
|
|
1695
|
+
nRefs: seen.size
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
return result;
|
|
1699
|
+
}
|
|
1700
|
+
function fnFingerprints(source, file) {
|
|
1701
|
+
if (source.length > MAX_FN_FP_SOURCE_CHARS) {
|
|
1702
|
+
return computeFnFingerprints(source, file);
|
|
1703
|
+
}
|
|
1704
|
+
const hit = fnFpCache.get(source);
|
|
1705
|
+
if (hit !== void 0) {
|
|
1706
|
+
fnFpCache.delete(source);
|
|
1707
|
+
fnFpCache.set(source, hit);
|
|
1708
|
+
return hit;
|
|
1709
|
+
}
|
|
1710
|
+
const fp = computeFnFingerprints(source, file);
|
|
1711
|
+
if (fnFpCache.size >= MAX_FN_FP_CACHE) {
|
|
1712
|
+
const oldest = fnFpCache.keys().next().value;
|
|
1713
|
+
if (oldest !== void 0) fnFpCache.delete(oldest);
|
|
1714
|
+
}
|
|
1715
|
+
fnFpCache.set(source, fp);
|
|
1716
|
+
return fp;
|
|
1717
|
+
}
|
|
1718
|
+
function generalizeSourceKeyPart(source, fnName, file) {
|
|
1719
|
+
if (!file) return `w:${hashSource(source)}`;
|
|
1720
|
+
const fp = fnFingerprints(source, file).get(fnName);
|
|
1721
|
+
if (fp) return `f:${fp.own}:${fp.deps}`;
|
|
1722
|
+
return `u:${hashSource(source)}`;
|
|
1723
|
+
}
|
|
1724
|
+
function hasAnyCallLike(node, depth = 0) {
|
|
1725
|
+
if (!node || typeof node !== "object") return false;
|
|
1726
|
+
if (depth > 80) return true;
|
|
1727
|
+
const obj2 = node;
|
|
1728
|
+
if (obj2.type === "CallExpression" || obj2.type === "NewExpression" || obj2.type === "OptionalCallExpression" || obj2.type === "ImportExpression") {
|
|
1729
|
+
return true;
|
|
1730
|
+
}
|
|
1731
|
+
for (const key of Object.keys(obj2)) {
|
|
1732
|
+
if (key === "loc" || key === "start" || key === "end" || key === "leadingComments" || key === "trailingComments" || key === "innerComments") {
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
1735
|
+
const v2 = obj2[key];
|
|
1736
|
+
if (Array.isArray(v2)) {
|
|
1737
|
+
for (const x of v2) if (hasAnyCallLike(x, depth + 1)) return true;
|
|
1738
|
+
} else if (v2 && typeof v2 === "object") {
|
|
1739
|
+
if (hasAnyCallLike(v2, depth + 1)) return true;
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
return false;
|
|
1743
|
+
}
|
|
1744
|
+
function hasModuleSyntax(file) {
|
|
1745
|
+
for (const stmt of file.program.body) {
|
|
1746
|
+
if (stmt.type === "ImportDeclaration") return true;
|
|
1747
|
+
if (stmt.type === "ExportAllDeclaration") return true;
|
|
1748
|
+
if (stmt.type === "ExportNamedDeclaration" && stmt.source) return true;
|
|
1749
|
+
}
|
|
1750
|
+
return false;
|
|
1751
|
+
}
|
|
1752
|
+
function canSkipLiteralCallScan(source, file) {
|
|
1753
|
+
if (source.includes("@nudo:refine")) return false;
|
|
1754
|
+
if (hasModuleSyntax(file)) return false;
|
|
1755
|
+
if (hasAnyCallLike(file)) return false;
|
|
1756
|
+
const fps = fnFingerprints(source, file);
|
|
1757
|
+
for (const fp of fps.values()) {
|
|
1758
|
+
if (fp.nRefs > 0) return false;
|
|
1759
|
+
}
|
|
1760
|
+
return true;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
// src/algebra/load-deps-fp.ts
|
|
1764
|
+
var MAX_LOAD_DEP_NODES = 64;
|
|
1765
|
+
function normPath(p) {
|
|
1766
|
+
return p.replace(/\\/g, "/");
|
|
1767
|
+
}
|
|
1768
|
+
function resolveDepPath(fromFile, spec) {
|
|
1769
|
+
const s = normPath(spec);
|
|
1770
|
+
if (!s.startsWith(".")) return s;
|
|
1771
|
+
const from = normPath(fromFile);
|
|
1772
|
+
const i = from.lastIndexOf("/");
|
|
1773
|
+
const base = i >= 0 ? from.slice(0, i) : "";
|
|
1774
|
+
const parts = base ? base.split("/") : [];
|
|
1775
|
+
for (const seg of s.split("/")) {
|
|
1776
|
+
if (seg === "" || seg === ".") continue;
|
|
1777
|
+
if (seg === "..") parts.pop();
|
|
1778
|
+
else parts.push(seg);
|
|
1779
|
+
}
|
|
1780
|
+
return parts.join("/");
|
|
1781
|
+
}
|
|
1782
|
+
function extractAllLoadSpecs(source) {
|
|
1783
|
+
const specs = /* @__PURE__ */ new Set();
|
|
1784
|
+
for (const imp of extractNudoImports(source)) specs.add(imp.spec);
|
|
1785
|
+
const patterns = [
|
|
1786
|
+
/\bfrom\s*['"]([^'"]+)['"]/g,
|
|
1787
|
+
/\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
1788
|
+
/\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g
|
|
1789
|
+
];
|
|
1790
|
+
for (const re of patterns) {
|
|
1791
|
+
let m;
|
|
1792
|
+
while (m = re.exec(source)) specs.add(m[1]);
|
|
1793
|
+
}
|
|
1794
|
+
return [...specs];
|
|
1795
|
+
}
|
|
1796
|
+
function loadModuleDepsFingerprint(source, loadModule, fromFile) {
|
|
1797
|
+
const parts = [];
|
|
1798
|
+
const paths = [];
|
|
1799
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1800
|
+
const queue = extractAllLoadSpecs(source).map(
|
|
1801
|
+
(spec) => ({ spec, from: fromFile })
|
|
1802
|
+
);
|
|
1803
|
+
let n = 0;
|
|
1804
|
+
while (queue.length > 0) {
|
|
1805
|
+
if (n >= MAX_LOAD_DEP_NODES) {
|
|
1806
|
+
parts.sort();
|
|
1807
|
+
return { fp: `trunc:${parts.join(",")}`, paths, truncated: true };
|
|
1808
|
+
}
|
|
1809
|
+
const cur = queue.shift();
|
|
1810
|
+
const path = resolveDepPath(cur.from, cur.spec);
|
|
1811
|
+
if (seen.has(path)) continue;
|
|
1812
|
+
seen.add(path);
|
|
1813
|
+
n++;
|
|
1814
|
+
const src = loadModule(cur.spec, cur.from);
|
|
1815
|
+
parts.push(`${path}=${src === void 0 ? "miss" : hashSource(src)}`);
|
|
1816
|
+
paths.push(path);
|
|
1817
|
+
if (src === void 0) continue;
|
|
1818
|
+
for (const next of extractAllLoadSpecs(src)) {
|
|
1819
|
+
const nextPath = resolveDepPath(path, next);
|
|
1820
|
+
if (!seen.has(nextPath)) queue.push({ spec: next, from: path });
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
parts.sort();
|
|
1824
|
+
return { fp: parts.join(","), paths, truncated: false };
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
// src/algebra/generalize.ts
|
|
1828
|
+
var generalizeMemo = /* @__PURE__ */ new Map();
|
|
1829
|
+
var memoKeyDeps = /* @__PURE__ */ new Map();
|
|
1830
|
+
var memoDepIndex = /* @__PURE__ */ new Map();
|
|
1831
|
+
var loadModuleIds = /* @__PURE__ */ new WeakMap();
|
|
1832
|
+
var nextLoadModuleId = 1;
|
|
1833
|
+
var MAX_GENERALIZE_MEMO = 1024;
|
|
1834
|
+
function resetGeneralizeMemo() {
|
|
1835
|
+
generalizeMemo.clear();
|
|
1836
|
+
memoKeyDeps.clear();
|
|
1837
|
+
memoDepIndex.clear();
|
|
1838
|
+
resetFnFpCache();
|
|
1839
|
+
resetHashSourceCache();
|
|
1840
|
+
}
|
|
1841
|
+
function getGeneralizeMemoSize() {
|
|
1842
|
+
return generalizeMemo.size;
|
|
1843
|
+
}
|
|
1844
|
+
function unindexMemoKey(key) {
|
|
1845
|
+
const deps = memoKeyDeps.get(key);
|
|
1846
|
+
if (!deps) return;
|
|
1847
|
+
for (const p of deps) {
|
|
1848
|
+
const set = memoDepIndex.get(p);
|
|
1849
|
+
if (!set) continue;
|
|
1850
|
+
set.delete(key);
|
|
1851
|
+
if (set.size === 0) memoDepIndex.delete(p);
|
|
1852
|
+
}
|
|
1853
|
+
memoKeyDeps.delete(key);
|
|
1854
|
+
}
|
|
1855
|
+
function evictGeneralizeMemoForPaths(paths) {
|
|
1856
|
+
let n = 0;
|
|
1857
|
+
for (const raw of paths) {
|
|
1858
|
+
const p = normPath(raw);
|
|
1859
|
+
const keys = memoDepIndex.get(p);
|
|
1860
|
+
if (!keys) continue;
|
|
1861
|
+
for (const key of [...keys]) {
|
|
1862
|
+
generalizeMemo.delete(key);
|
|
1863
|
+
unindexMemoKey(key);
|
|
1864
|
+
n++;
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
return n;
|
|
1868
|
+
}
|
|
1869
|
+
function loadModuleId(fn) {
|
|
1870
|
+
if (!fn) return 0;
|
|
1871
|
+
let id = loadModuleIds.get(fn);
|
|
1872
|
+
if (id === void 0) {
|
|
1873
|
+
id = nextLoadModuleId++;
|
|
1874
|
+
loadModuleIds.set(fn, id);
|
|
1875
|
+
}
|
|
1876
|
+
return id;
|
|
1877
|
+
}
|
|
1878
|
+
function refineDepsFingerprint(source, refine) {
|
|
1879
|
+
if (!refine?.loadModule || !refine.fromFile) {
|
|
1880
|
+
return { fp: "-", paths: [], truncated: false };
|
|
1881
|
+
}
|
|
1882
|
+
return loadModuleDepsFingerprint(source, refine.loadModule, refine.fromFile);
|
|
1883
|
+
}
|
|
1884
|
+
function generalizeMemoKey(fnName, source, opts) {
|
|
1885
|
+
const r = opts.refine;
|
|
1886
|
+
const budget = opts.budget ?? defaultLeakBudget;
|
|
1887
|
+
const deps = opts.depsFp ?? (r ? refineDepsFingerprint(source, r) : { fp: "-", paths: [], truncated: false });
|
|
1888
|
+
const srcPart = generalizeSourceKeyPart(source, fnName, opts.file);
|
|
1889
|
+
const key = [
|
|
1890
|
+
srcPart,
|
|
1891
|
+
fnName,
|
|
1892
|
+
opts.label ?? "A",
|
|
1893
|
+
r ? `${loadModuleId(r.loadModule)}:${r.fromFile ?? ""}` : "-",
|
|
1894
|
+
deps.fp,
|
|
1895
|
+
`${budget.maxDepth}/${budget.maxNodes}`
|
|
1896
|
+
].join("|");
|
|
1897
|
+
return { key, depPaths: deps.paths, truncated: deps.truncated };
|
|
1898
|
+
}
|
|
1899
|
+
function generalizeMemoGet(key) {
|
|
1900
|
+
if (!generalizeMemo.has(key)) return null;
|
|
1901
|
+
const v2 = generalizeMemo.get(key);
|
|
1902
|
+
generalizeMemo.delete(key);
|
|
1903
|
+
generalizeMemo.set(key, v2);
|
|
1904
|
+
return v2;
|
|
1905
|
+
}
|
|
1906
|
+
function generalizeMemoSet(key, value, depPaths) {
|
|
1907
|
+
if (generalizeMemo.size >= MAX_GENERALIZE_MEMO) {
|
|
1908
|
+
const oldest = generalizeMemo.keys().next().value;
|
|
1909
|
+
if (oldest !== void 0) {
|
|
1910
|
+
generalizeMemo.delete(oldest);
|
|
1911
|
+
unindexMemoKey(oldest);
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
generalizeMemo.set(key, value);
|
|
1915
|
+
if (depPaths.length > 0) {
|
|
1916
|
+
memoKeyDeps.set(key, depPaths);
|
|
1917
|
+
for (const p of depPaths) {
|
|
1918
|
+
let set = memoDepIndex.get(p);
|
|
1919
|
+
if (!set) {
|
|
1920
|
+
set = /* @__PURE__ */ new Set();
|
|
1921
|
+
memoDepIndex.set(p, set);
|
|
1922
|
+
}
|
|
1923
|
+
set.add(key);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
function isCacheableAbs(a) {
|
|
1928
|
+
return a.conf === "exact" || a.conf === "path" || a.conf === "widened" || a.conf === "mock";
|
|
1929
|
+
}
|
|
1930
|
+
function termKey(t, rename) {
|
|
1931
|
+
switch (t.op) {
|
|
1932
|
+
case "lit":
|
|
1933
|
+
return `L:${typeof t.value}:${String(t.value)}`;
|
|
1934
|
+
case "var":
|
|
1935
|
+
return `V:${rename?.get(t.id) ?? t.id}`;
|
|
1936
|
+
case "app":
|
|
1937
|
+
return `A:${t.fn}(${t.args.map((a) => termKey(a, rename)).join(",")})`;
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
function predKey(p, rename) {
|
|
1941
|
+
switch (p.op) {
|
|
1942
|
+
case "true":
|
|
1943
|
+
return "T";
|
|
1944
|
+
case "false":
|
|
1945
|
+
return "F";
|
|
1946
|
+
case "eq":
|
|
1947
|
+
case "ne":
|
|
1948
|
+
case "lt":
|
|
1949
|
+
case "le":
|
|
1950
|
+
case "gt":
|
|
1951
|
+
case "ge":
|
|
1952
|
+
return `${p.op}(${termKey(p.a, rename)},${termKey(p.b, rename)})`;
|
|
1953
|
+
case "and":
|
|
1954
|
+
case "or": {
|
|
1955
|
+
const keys = p.args.map((a) => predKey(a, rename)).sort();
|
|
1956
|
+
return `${p.op}(${keys.join(",")})`;
|
|
1957
|
+
}
|
|
1958
|
+
case "not":
|
|
1959
|
+
return `not(${predKey(p.arg, rename)})`;
|
|
1960
|
+
case "typeof":
|
|
1961
|
+
return `typeof(${termKey(p.t, rename)},${p.type})`;
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
function shapeKey(s, seen, rename) {
|
|
1965
|
+
switch (s.k) {
|
|
1966
|
+
case "never":
|
|
1967
|
+
case "any":
|
|
1968
|
+
case "unknown":
|
|
1969
|
+
return s.k;
|
|
1970
|
+
case "prim":
|
|
1971
|
+
return `p:${s.type}`;
|
|
1972
|
+
case "brand":
|
|
1973
|
+
return `b:${s.name}(${absKeyInner(s.shape, seen, rename)})`;
|
|
1974
|
+
case "eff":
|
|
1975
|
+
return `e:${s.eff}<${absKeyInner(s.inner, seen, rename)}>`;
|
|
1976
|
+
case "arr":
|
|
1977
|
+
return `arr(${absKeyInner(s.element, seen, rename)})`;
|
|
1978
|
+
case "tuple": {
|
|
1979
|
+
const els = s.elements.map((e) => absKeyInner(e, seen, rename)).join(",");
|
|
1980
|
+
const rest = s.rest ? `...${absKeyInner(s.rest, seen, rename)}` : "";
|
|
1981
|
+
return `tup[${els}${rest}]`;
|
|
1982
|
+
}
|
|
1983
|
+
case "fn": {
|
|
1984
|
+
const pts = (s.paramTypes ?? []).map((t) => absKeyInner(t, seen, rename)).join(",");
|
|
1985
|
+
const ret = s.returnType ? absKeyInner(s.returnType, seen, rename) : "?";
|
|
1986
|
+
const name = s.name ? `#${s.name}` : "";
|
|
1987
|
+
return `fn${name}(${s.params.join(",")}|${pts})=>${ret}`;
|
|
1988
|
+
}
|
|
1989
|
+
case "sum":
|
|
1990
|
+
return `sum(${s.members.map((m) => absKeyInner(m, seen, rename)).join("|")})`;
|
|
1991
|
+
case "obj": {
|
|
1992
|
+
const slots = Object.keys(s.slots).sort().map((k) => {
|
|
1993
|
+
const slot = s.slots[k];
|
|
1994
|
+
const flags = (slot.optional ? "?" : "") + (slot.readonly ? "r" : "");
|
|
1995
|
+
return `${k}${flags}:${absKeyInner(slot.value, seen, rename)}`;
|
|
1996
|
+
}).join(",");
|
|
1997
|
+
const idx = s.index ? `idx(${absKeyInner(s.index.key, seen, rename)}\u2192${absKeyInner(s.index.value, seen, rename)})` : "";
|
|
1998
|
+
const open = s.open ? "open" : "";
|
|
1999
|
+
return `obj{${slots}}${idx}${open}`;
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
function absKeyInner(a, seen, rename) {
|
|
2004
|
+
if (seen.has(a)) return "cycle";
|
|
2005
|
+
seen.add(a);
|
|
2006
|
+
const t = a.term ? `=${termKey(a.term, rename)}` : "";
|
|
2007
|
+
const p = a.pred ? `@${predKey(a.pred, rename)}` : "";
|
|
2008
|
+
return `${shapeKey(a.shape, seen, rename)}${t}${p}`;
|
|
2009
|
+
}
|
|
2010
|
+
function collectTermVars(t, acc) {
|
|
2011
|
+
if (t.op === "var") acc.add(t.id);
|
|
2012
|
+
else if (t.op === "app") for (const a of t.args) collectTermVars(a, acc);
|
|
2013
|
+
}
|
|
2014
|
+
function collectPredVars(p, acc) {
|
|
2015
|
+
switch (p.op) {
|
|
2016
|
+
case "true":
|
|
2017
|
+
case "false":
|
|
2018
|
+
return;
|
|
2019
|
+
case "eq":
|
|
2020
|
+
case "ne":
|
|
2021
|
+
case "lt":
|
|
2022
|
+
case "le":
|
|
2023
|
+
case "gt":
|
|
2024
|
+
case "ge":
|
|
2025
|
+
collectTermVars(p.a, acc);
|
|
2026
|
+
collectTermVars(p.b, acc);
|
|
2027
|
+
return;
|
|
2028
|
+
case "and":
|
|
2029
|
+
case "or":
|
|
2030
|
+
for (const a of p.args) collectPredVars(a, acc);
|
|
2031
|
+
return;
|
|
2032
|
+
case "not":
|
|
2033
|
+
collectPredVars(p.arg, acc);
|
|
2034
|
+
return;
|
|
2035
|
+
case "typeof":
|
|
2036
|
+
collectTermVars(p.t, acc);
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
function collectAbsVars(a, acc, seen) {
|
|
2041
|
+
if (seen.has(a)) return;
|
|
2042
|
+
seen.add(a);
|
|
2043
|
+
if (a.term) collectTermVars(a.term, acc);
|
|
2044
|
+
if (a.pred) collectPredVars(a.pred, acc);
|
|
2045
|
+
const s = a.shape;
|
|
2046
|
+
switch (s.k) {
|
|
2047
|
+
case "brand":
|
|
2048
|
+
collectAbsVars(s.shape, acc, seen);
|
|
2049
|
+
return;
|
|
2050
|
+
case "eff":
|
|
2051
|
+
collectAbsVars(s.inner, acc, seen);
|
|
2052
|
+
return;
|
|
2053
|
+
case "arr":
|
|
2054
|
+
collectAbsVars(s.element, acc, seen);
|
|
2055
|
+
return;
|
|
2056
|
+
case "tuple":
|
|
2057
|
+
for (const e of s.elements) collectAbsVars(e, acc, seen);
|
|
2058
|
+
if (s.rest) collectAbsVars(s.rest, acc, seen);
|
|
2059
|
+
return;
|
|
2060
|
+
case "fn":
|
|
2061
|
+
for (const t of s.paramTypes ?? []) collectAbsVars(t, acc, seen);
|
|
2062
|
+
if (s.returnType) collectAbsVars(s.returnType, acc, seen);
|
|
2063
|
+
return;
|
|
2064
|
+
case "sum":
|
|
2065
|
+
for (const m of s.members) collectAbsVars(m, acc, seen);
|
|
2066
|
+
return;
|
|
2067
|
+
case "obj":
|
|
2068
|
+
for (const slot of Object.values(s.slots)) collectAbsVars(slot.value, acc, seen);
|
|
2069
|
+
if (s.index) {
|
|
2070
|
+
collectAbsVars(s.index.key, acc, seen);
|
|
2071
|
+
collectAbsVars(s.index.value, acc, seen);
|
|
2072
|
+
}
|
|
2073
|
+
return;
|
|
2074
|
+
default:
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
function renameTerm(t, map) {
|
|
2079
|
+
if (t.op === "var") {
|
|
2080
|
+
const to = map.get(t.id);
|
|
2081
|
+
return to === void 0 ? t : v(to);
|
|
2082
|
+
}
|
|
2083
|
+
if (t.op === "app") {
|
|
2084
|
+
return { op: "app", fn: t.fn, args: t.args.map((a) => renameTerm(a, map)) };
|
|
2085
|
+
}
|
|
2086
|
+
return t;
|
|
2087
|
+
}
|
|
2088
|
+
function renamePred(p, map) {
|
|
2089
|
+
switch (p.op) {
|
|
2090
|
+
case "true":
|
|
2091
|
+
case "false":
|
|
2092
|
+
return p;
|
|
2093
|
+
case "eq":
|
|
2094
|
+
case "ne":
|
|
2095
|
+
case "lt":
|
|
2096
|
+
case "le":
|
|
2097
|
+
case "gt":
|
|
2098
|
+
case "ge":
|
|
2099
|
+
return { op: p.op, a: renameTerm(p.a, map), b: renameTerm(p.b, map) };
|
|
2100
|
+
case "and":
|
|
2101
|
+
case "or":
|
|
2102
|
+
return { op: p.op, args: p.args.map((a) => renamePred(a, map)) };
|
|
2103
|
+
case "not":
|
|
2104
|
+
return { op: "not", arg: renamePred(p.arg, map) };
|
|
2105
|
+
case "typeof":
|
|
2106
|
+
return { op: "typeof", t: renameTerm(p.t, map), type: p.type };
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
function renameAbs(a, map) {
|
|
2110
|
+
const out = {
|
|
2111
|
+
shape: renameShape(a.shape, map),
|
|
2112
|
+
conf: a.conf
|
|
2113
|
+
};
|
|
2114
|
+
if (a.term) out.term = renameTerm(a.term, map);
|
|
2115
|
+
if (a.pred) out.pred = renamePred(a.pred, map);
|
|
2116
|
+
return out;
|
|
2117
|
+
}
|
|
2118
|
+
function renameShape(s, map) {
|
|
2119
|
+
switch (s.k) {
|
|
2120
|
+
case "never":
|
|
2121
|
+
case "any":
|
|
2122
|
+
case "unknown":
|
|
2123
|
+
case "prim":
|
|
2124
|
+
return s;
|
|
2125
|
+
case "brand":
|
|
2126
|
+
return { k: "brand", name: s.name, shape: renameAbs(s.shape, map) };
|
|
2127
|
+
case "eff":
|
|
2128
|
+
return { k: "eff", eff: s.eff, inner: renameAbs(s.inner, map) };
|
|
2129
|
+
case "arr":
|
|
2130
|
+
return { k: "arr", element: renameAbs(s.element, map) };
|
|
2131
|
+
case "tuple": {
|
|
2132
|
+
const out = {
|
|
2133
|
+
k: "tuple",
|
|
2134
|
+
elements: s.elements.map((e) => renameAbs(e, map))
|
|
2135
|
+
};
|
|
2136
|
+
if (s.rest) out.rest = renameAbs(s.rest, map);
|
|
2137
|
+
return out;
|
|
2138
|
+
}
|
|
2139
|
+
case "fn": {
|
|
2140
|
+
const out = { k: "fn", params: s.params };
|
|
2141
|
+
if (s.name !== void 0) out.name = s.name;
|
|
2142
|
+
if (s.paramTypes) out.paramTypes = s.paramTypes.map((t) => renameAbs(t, map));
|
|
2143
|
+
if (s.returnType) out.returnType = renameAbs(s.returnType, map);
|
|
2144
|
+
return out;
|
|
2145
|
+
}
|
|
2146
|
+
case "sum":
|
|
2147
|
+
return { k: "sum", members: s.members.map((m) => renameAbs(m, map)) };
|
|
2148
|
+
case "obj": {
|
|
2149
|
+
const slots = {};
|
|
2150
|
+
for (const [k, slot] of Object.entries(s.slots)) {
|
|
2151
|
+
slots[k] = {
|
|
2152
|
+
value: renameAbs(slot.value, map),
|
|
2153
|
+
...slot.optional ? { optional: true } : {},
|
|
2154
|
+
...slot.readonly ? { readonly: true } : {}
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
const out = { k: "obj", slots };
|
|
2158
|
+
if (s.index) {
|
|
2159
|
+
out.index = { key: renameAbs(s.index.key, map), value: renameAbs(s.index.value, map) };
|
|
2160
|
+
}
|
|
2161
|
+
if (s.open) out.open = true;
|
|
2162
|
+
return out;
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
function instantiateMemoKey(args, phi) {
|
|
2167
|
+
const acc = /* @__PURE__ */ new Set();
|
|
2168
|
+
for (const a of args) collectAbsVars(a, acc, /* @__PURE__ */ new Set());
|
|
2169
|
+
collectPredVars(phi, acc);
|
|
2170
|
+
const varOrder = [...acc].sort();
|
|
2171
|
+
const rename = new Map(varOrder.map((id, i) => [id, `\u03B1${i}`]));
|
|
2172
|
+
const key = `${args.map((a) => absKeyInner(a, /* @__PURE__ */ new Set(), rename)).join(";")}#${predKey(phi, rename)}`;
|
|
2173
|
+
return { key, varOrder };
|
|
2174
|
+
}
|
|
2175
|
+
function alphaRenameResult(result, fromOrder, toOrder) {
|
|
2176
|
+
if (fromOrder.length !== toOrder.length) return result;
|
|
2177
|
+
let same = true;
|
|
2178
|
+
for (let i = 0; i < fromOrder.length; i++) {
|
|
2179
|
+
if (fromOrder[i] !== toOrder[i]) {
|
|
2180
|
+
same = false;
|
|
2181
|
+
break;
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
if (same) return result;
|
|
2185
|
+
const map = /* @__PURE__ */ new Map();
|
|
2186
|
+
for (let i = 0; i < fromOrder.length; i++) {
|
|
2187
|
+
map.set(fromOrder[i], toOrder[i]);
|
|
2188
|
+
}
|
|
2189
|
+
return renameAbs(result, map);
|
|
2190
|
+
}
|
|
2191
|
+
function extractFn(source, fnName, fileAst) {
|
|
2192
|
+
const file = fileAst ?? parseSource(source);
|
|
2193
|
+
const env = emptyEnv();
|
|
2194
|
+
for (const stmt of file.program.body) {
|
|
2195
|
+
let decl = stmt;
|
|
2196
|
+
if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
|
|
2197
|
+
decl = stmt.declaration;
|
|
2198
|
+
}
|
|
2199
|
+
if (stmt.type === "ExportDefaultDeclaration" && stmt.declaration) {
|
|
2200
|
+
decl = stmt.declaration;
|
|
2201
|
+
}
|
|
2202
|
+
if (decl.type === "FunctionDeclaration" && decl.id) {
|
|
2203
|
+
env.fns.set(decl.id.name, {
|
|
2204
|
+
params: decl.params.map((p) => p.type === "Identifier" ? p.name : "_"),
|
|
2205
|
+
body: decl.body,
|
|
2206
|
+
async: decl.async === true
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
if (decl.type === "VariableDeclaration") {
|
|
2210
|
+
for (const d of decl.declarations) {
|
|
2211
|
+
if (d.id.type === "Identifier" && d.init && (d.init.type === "ArrowFunctionExpression" || d.init.type === "FunctionExpression")) {
|
|
2212
|
+
const init = d.init;
|
|
2213
|
+
env.fns.set(d.id.name, {
|
|
2214
|
+
params: init.params.map(
|
|
2215
|
+
(p) => p.type === "Identifier" ? p.name ?? "_" : "_"
|
|
2216
|
+
),
|
|
2217
|
+
body: init.body,
|
|
2218
|
+
async: init.async === true
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
const fn = env.fns.get(fnName);
|
|
2225
|
+
if (!fn) return void 0;
|
|
2226
|
+
return { params: fn.params, body: fn.body, env };
|
|
2227
|
+
}
|
|
2228
|
+
function generalizeFromAst(fnName, source, opts = {}) {
|
|
2229
|
+
const { key, depPaths, truncated } = generalizeMemoKey(fnName, source, opts);
|
|
2230
|
+
if (!truncated) {
|
|
2231
|
+
const cached = generalizeMemoGet(key);
|
|
2232
|
+
if (cached !== null) {
|
|
2233
|
+
return cached;
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
const result = generalizeFromAstUncached(fnName, source, opts);
|
|
2237
|
+
if (!truncated) {
|
|
2238
|
+
generalizeMemoSet(key, result, depPaths);
|
|
2239
|
+
}
|
|
2240
|
+
return result;
|
|
2241
|
+
}
|
|
2242
|
+
function generalizeFromAstUncached(fnName, source, opts = {}) {
|
|
2243
|
+
const extracted = extractFn(source, fnName, opts.file);
|
|
2244
|
+
if (!extracted) return void 0;
|
|
2245
|
+
const { params, body, env } = extracted;
|
|
2246
|
+
const budget = opts.budget ?? defaultLeakBudget;
|
|
2247
|
+
const label = opts.label ?? "A";
|
|
2248
|
+
const typeParams = params.map((p, i) => ({
|
|
2249
|
+
id: `${label}${i + 1}`,
|
|
2250
|
+
// 无约束参数 = any(任意 JS 值),不是 unknown(分析无信息)
|
|
2251
|
+
value: abs({ k: "any" }, v(`${label}${i + 1}`), pTrue, "path")
|
|
2252
|
+
}));
|
|
2253
|
+
let entryReqs;
|
|
2254
|
+
const refineEntryShapes = /* @__PURE__ */ new Map();
|
|
2255
|
+
if (opts.refine) {
|
|
2256
|
+
try {
|
|
2257
|
+
const reqs = extractRefinesFromSource(source, fnName, opts.refine);
|
|
2258
|
+
if (reqs.length > 0) {
|
|
2259
|
+
entryReqs = reqs.map((r) => ({ param: r.param, pred: r.pred }));
|
|
2260
|
+
for (const r of reqs) {
|
|
2261
|
+
const idx = params.indexOf(r.param);
|
|
2262
|
+
if (idx >= 0) {
|
|
2263
|
+
const entryAbs = constraintToEntryAbs(r.constraint, r.param);
|
|
2264
|
+
typeParams[idx] = {
|
|
2265
|
+
id: typeParams[idx].id,
|
|
2266
|
+
value: entryAbs
|
|
2267
|
+
};
|
|
2268
|
+
refineEntryShapes.set(r.param, {
|
|
2269
|
+
abs: snapshotAbs(entryAbs),
|
|
2270
|
+
source: "refine"
|
|
2271
|
+
});
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
} catch {
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
const instMemo = /* @__PURE__ */ new Map();
|
|
2279
|
+
const paramNames = new Set(params);
|
|
2280
|
+
const alphaIds = typeParams.map((t) => t.id);
|
|
2281
|
+
const run = (args, phi = pTrue, collector) => {
|
|
2282
|
+
const { key, varOrder } = instantiateMemoKey(args, phi);
|
|
2283
|
+
const hit = instMemo.get(key);
|
|
2284
|
+
if (hit !== void 0) {
|
|
2285
|
+
return alphaRenameResult(hit.result, hit.varOrder, varOrder);
|
|
2286
|
+
}
|
|
2287
|
+
const hc = collector ?? createHofCollectCtx(paramNames, alphaIds);
|
|
2288
|
+
const local = {
|
|
2289
|
+
vars: new Map(env.vars),
|
|
2290
|
+
fns: env.fns,
|
|
2291
|
+
hofCollect: hc
|
|
2292
|
+
};
|
|
2293
|
+
params.forEach((p, i) => {
|
|
2294
|
+
local.vars.set(p, args[i] ?? unknown);
|
|
2295
|
+
});
|
|
2296
|
+
const result = evalNode(body, local, phi, budget).value;
|
|
2297
|
+
if (isCacheableAbs(result)) {
|
|
2298
|
+
instMemo.set(key, { result, varOrder });
|
|
2299
|
+
}
|
|
2300
|
+
return result;
|
|
2301
|
+
};
|
|
2302
|
+
const hofCollector = createHofCollectCtx(paramNames, alphaIds);
|
|
2303
|
+
const symbolic = run(
|
|
2304
|
+
typeParams.map((t) => t.value),
|
|
2305
|
+
// 入口契约进 Φ,让 body 内的单调性可传播
|
|
2306
|
+
entryReqs && entryReqs.length > 0 ? entryReqs.length === 1 ? entryReqs[0].pred : { op: "and", args: entryReqs.map((r) => r.pred) } : pTrue,
|
|
2307
|
+
hofCollector
|
|
2308
|
+
);
|
|
2309
|
+
let fnRels;
|
|
2310
|
+
let entryShapes;
|
|
2311
|
+
let hofSites;
|
|
2312
|
+
if (symbolic.conf !== "opaque") {
|
|
2313
|
+
if (refineEntryShapes.size > 0) {
|
|
2314
|
+
entryShapes = new Map(refineEntryShapes);
|
|
2315
|
+
for (const [param, rec] of refineEntryShapes) {
|
|
2316
|
+
if (rec.abs.shape.k !== "fn") continue;
|
|
2317
|
+
if (!fnRels) fnRels = /* @__PURE__ */ new Map();
|
|
2318
|
+
fnRels.set(param, { abs: snapshotAbs(rec.abs), source: rec.source });
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
for (const [param, rec] of hofCollector.fnRels) {
|
|
2322
|
+
if (refineEntryShapes.has(param)) continue;
|
|
2323
|
+
if (!fnRels) fnRels = /* @__PURE__ */ new Map();
|
|
2324
|
+
fnRels.set(param, { abs: snapshotAbs(rec.abs), source: rec.source });
|
|
2325
|
+
}
|
|
2326
|
+
for (const [param, rec] of hofCollector.entryShapes) {
|
|
2327
|
+
if (refineEntryShapes.has(param)) continue;
|
|
2328
|
+
if (!entryShapes) entryShapes = /* @__PURE__ */ new Map();
|
|
2329
|
+
entryShapes.set(param, { abs: snapshotAbs(rec.abs), source: rec.source });
|
|
2330
|
+
}
|
|
2331
|
+
if (hofCollector.sites.length > 0) {
|
|
2332
|
+
hofSites = hofCollector.sites.map((s) => ({
|
|
2333
|
+
...s,
|
|
2334
|
+
argTerms: [...s.argTerms],
|
|
2335
|
+
result: snapshotAbs(s.result)
|
|
2336
|
+
}));
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
return {
|
|
2340
|
+
name: fnName,
|
|
2341
|
+
params,
|
|
2342
|
+
typeParams,
|
|
2343
|
+
symbolic,
|
|
2344
|
+
instantiate: (args, phi) => run(args, phi ?? pTrue),
|
|
2345
|
+
display: formatPoly(
|
|
2346
|
+
fnName,
|
|
2347
|
+
params,
|
|
2348
|
+
typeParams,
|
|
2349
|
+
symbolic,
|
|
2350
|
+
entryReqs,
|
|
2351
|
+
entryShapes,
|
|
2352
|
+
fnRels
|
|
2353
|
+
),
|
|
2354
|
+
...entryReqs ? { entryReqs } : {},
|
|
2355
|
+
...fnRels ? { fnRels } : {},
|
|
2356
|
+
...entryShapes ? { entryShapes } : {},
|
|
2357
|
+
...hofSites ? { hofSites } : {}
|
|
2358
|
+
};
|
|
2359
|
+
}
|
|
2360
|
+
function formatPoly(name, params, typeParams, symbolic, entryReqs, entryShapes, fnRels) {
|
|
2361
|
+
const reqByParam = new Map((entryReqs ?? []).map((r) => [r.param, r.pred]));
|
|
2362
|
+
const ps = params.map((p, i) => {
|
|
2363
|
+
const id = typeParams[i]?.id ?? "unknown";
|
|
2364
|
+
const pred = reqByParam.get(p);
|
|
2365
|
+
const promoted = entryShapes?.get(p)?.abs ?? fnRels?.get(p)?.abs;
|
|
2366
|
+
if (promoted) {
|
|
2367
|
+
const slot = formatShapeSlot(promoted);
|
|
2368
|
+
if (pred && pred.op !== "true") {
|
|
2369
|
+
return `${p}: ${slot} where ${predToString(pred)}`;
|
|
2370
|
+
}
|
|
2371
|
+
return `${p}: ${slot}`;
|
|
2372
|
+
}
|
|
2373
|
+
if (pred && pred.op !== "true") {
|
|
2374
|
+
return `${p}: ${id} where ${predToString(pred)}`;
|
|
2375
|
+
}
|
|
2376
|
+
return `${p}: ${id}`;
|
|
2377
|
+
}).join(", ");
|
|
2378
|
+
const ret = formatShapeSlot(symbolic);
|
|
2379
|
+
const termPart = symbolic.term && symbolic.term.op !== "lit" ? ` = ${termToString(symbolic.term)}` : "";
|
|
2380
|
+
const predPart = symbolic.pred && symbolic.pred.op !== "true" ? ` where ${predToString(symbolic.pred)}` : "";
|
|
2381
|
+
return `${name}: (${ps}) => ${ret}${termPart}${predPart}`;
|
|
2382
|
+
}
|
|
2383
|
+
function generalizeAll(source, opts = {}) {
|
|
2384
|
+
const file = parseSource(source);
|
|
2385
|
+
const names = [];
|
|
2386
|
+
for (const stmt of file.program.body) {
|
|
2387
|
+
if (stmt.type === "FunctionDeclaration" && stmt.id) names.push(stmt.id.name);
|
|
2388
|
+
if (stmt.type === "VariableDeclaration") {
|
|
2389
|
+
for (const d of stmt.declarations) {
|
|
2390
|
+
if (d.id.type === "Identifier" && d.init && (d.init.type === "ArrowFunctionExpression" || d.init.type === "FunctionExpression")) {
|
|
2391
|
+
names.push(d.id.name);
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
const out = [];
|
|
2397
|
+
for (const n of names) {
|
|
2398
|
+
const g = generalizeFromAst(n, source, opts);
|
|
2399
|
+
if (g) out.push(g);
|
|
2400
|
+
}
|
|
2401
|
+
return out;
|
|
2402
|
+
}
|
|
2403
|
+
function listFunctionNames(source) {
|
|
2404
|
+
const file = parseSource(source);
|
|
2405
|
+
const names = [];
|
|
2406
|
+
for (const stmt of file.program.body) {
|
|
2407
|
+
if (stmt.type === "FunctionDeclaration" && stmt.id) names.push(stmt.id.name);
|
|
2408
|
+
if (stmt.type === "VariableDeclaration") {
|
|
2409
|
+
for (const d of stmt.declarations) {
|
|
2410
|
+
if (d.id.type === "Identifier" && d.init && (d.init.type === "ArrowFunctionExpression" || d.init.type === "FunctionExpression")) {
|
|
2411
|
+
names.push(d.id.name);
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
return names;
|
|
2417
|
+
}
|
|
2418
|
+
function buildArgsFromAssume(source, fnName, assumeIds) {
|
|
2419
|
+
const extracted = extractFn(source, fnName);
|
|
2420
|
+
if (!extracted) return [];
|
|
2421
|
+
return extracted.params.map((p) => {
|
|
2422
|
+
if (assumeIds.has(p)) {
|
|
2423
|
+
return abs(
|
|
2424
|
+
{ k: "prim", type: "number" },
|
|
2425
|
+
v(p),
|
|
2426
|
+
{ op: "gt", a: v(p), b: { op: "lit", value: 0 } },
|
|
2427
|
+
"path"
|
|
2428
|
+
);
|
|
2429
|
+
}
|
|
2430
|
+
return abs({ k: "unknown" }, void 0, void 0, "partial");
|
|
2431
|
+
});
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
// src/algebra/diagnostics.ts
|
|
2435
|
+
function checkArg(arg, expect, phi = pTrue) {
|
|
2436
|
+
if (!expect || expect.op === "true") return void 0;
|
|
2437
|
+
if (arg.shape.k === "unknown" && !arg.term) {
|
|
2438
|
+
return {
|
|
2439
|
+
severity: "warning",
|
|
2440
|
+
code: "nudo:arg-opaque",
|
|
2441
|
+
message: `\u5B9E\u53C2\u7C7B\u578B unknown\uFF0C\u65E0\u6CD5\u9A8C\u8BC1\u7EA6\u675F ${predToString(expect)}`,
|
|
2442
|
+
suggestion: "\u8865\u5145\u8C03\u7528\u70B9\u3001@nudo:case\uFF0C\u6216 --assume \u63D0\u4F9B\u524D\u7F6E\u6761\u4EF6"
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
const combined = arg.pred && arg.pred.op !== "true" ? phi.op === "true" ? arg.pred : { op: "and", args: [phi, arg.pred] } : phi;
|
|
2446
|
+
if (implies(combined, expect)) return void 0;
|
|
2447
|
+
const lv = litValue(arg);
|
|
2448
|
+
if (lv !== void 0 && (expect.op === "gt" || expect.op === "ge" || expect.op === "lt" || expect.op === "le")) {
|
|
2449
|
+
const ok = decideLitCmp(lv, expect);
|
|
2450
|
+
if (ok === true) return void 0;
|
|
2451
|
+
if (ok === false) {
|
|
2452
|
+
return {
|
|
2453
|
+
severity: "error",
|
|
2454
|
+
code: "nudo:constraint-violated",
|
|
2455
|
+
message: `\u5B9E\u53C2 ${JSON.stringify(lv)} \u4E0D\u6EE1\u8DB3 ${predToString(expect)}`,
|
|
2456
|
+
suggestion: `\u6539\u7528\u6EE1\u8DB3\u7EA6\u675F\u7684\u503C\uFF0C\u6216\u653E\u5BBD\u51FD\u6570\u524D\u7F6E\u6761\u4EF6`
|
|
2457
|
+
};
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
return {
|
|
2461
|
+
severity: "warning",
|
|
2462
|
+
code: "nudo:constraint-unproven",
|
|
2463
|
+
message: `\u65E0\u6CD5\u8BC1\u660E\u5B9E\u53C2\u6EE1\u8DB3 ${predToString(expect)}`,
|
|
2464
|
+
suggestion: `\u5DF2\u77E5\u4FE1\u606F\u4E0D\u8DB3\uFF1B\u53EF --assume \u6216\u8865 case`
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
function decideLitCmp(lv, p) {
|
|
2468
|
+
if (typeof lv !== "number" || p.b.op !== "lit" || typeof p.b.value !== "number") {
|
|
2469
|
+
return void 0;
|
|
2470
|
+
}
|
|
2471
|
+
const n = p.b.value;
|
|
2472
|
+
switch (p.op) {
|
|
2473
|
+
case "gt":
|
|
2474
|
+
return lv > n;
|
|
2475
|
+
case "ge":
|
|
2476
|
+
return lv >= n;
|
|
2477
|
+
case "lt":
|
|
2478
|
+
return lv < n;
|
|
2479
|
+
case "le":
|
|
2480
|
+
return lv <= n;
|
|
2481
|
+
default:
|
|
2482
|
+
return void 0;
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
function checkCall(source, fnName, args, phi = pTrue) {
|
|
2486
|
+
const diags = [];
|
|
2487
|
+
const g = generalizeFromAst(fnName, source);
|
|
2488
|
+
if (!g) {
|
|
2489
|
+
diags.push({
|
|
2490
|
+
severity: "error",
|
|
2491
|
+
code: "nudo:fn-not-found",
|
|
2492
|
+
message: `\u672A\u627E\u5230\u51FD\u6570 ${fnName}`
|
|
2493
|
+
});
|
|
2494
|
+
return diags;
|
|
2495
|
+
}
|
|
2496
|
+
if (args.length !== g.params.length) {
|
|
2497
|
+
diags.push({
|
|
2498
|
+
severity: "error",
|
|
2499
|
+
code: "nudo:arg-count",
|
|
2500
|
+
message: `${fnName} \u671F\u671B ${g.params.length} \u4E2A\u53C2\u6570\uFF0C\u5B9E\u9645 ${args.length}`,
|
|
2501
|
+
fn: fnName
|
|
2502
|
+
});
|
|
2503
|
+
return diags;
|
|
2504
|
+
}
|
|
2505
|
+
const required = extractParamRequirements(g);
|
|
2506
|
+
args.forEach((arg, i) => {
|
|
2507
|
+
const req = required.get(i);
|
|
2508
|
+
const d = checkArg(arg, req, phi);
|
|
2509
|
+
if (d) diags.push({ ...d, fn: fnName, argIndex: i });
|
|
2510
|
+
});
|
|
2511
|
+
try {
|
|
2512
|
+
const result = analyzeFn(source, fnName, args, phi);
|
|
2513
|
+
if (result.conf === "partial" || result.conf === "opaque") {
|
|
2514
|
+
diags.push({
|
|
2515
|
+
severity: "info",
|
|
2516
|
+
code: "nudo:partial-result",
|
|
2517
|
+
message: `${fnName}(...) \u7ED3\u679C\u7F6E\u4FE1\u5EA6 ${result.conf}`,
|
|
2518
|
+
suggestion: "\u8865\u5145\u8C03\u7528\u70B9\u6216\u7EA6\u675F\u53EF\u63D0\u9AD8\u7CBE\u5EA6",
|
|
2519
|
+
fn: fnName
|
|
2520
|
+
});
|
|
2521
|
+
}
|
|
2522
|
+
} catch (e) {
|
|
2523
|
+
diags.push({
|
|
2524
|
+
severity: "error",
|
|
2525
|
+
code: "nudo:eval-error",
|
|
2526
|
+
message: `\u6C42\u503C\u5931\u8D25: ${e.message}`,
|
|
2527
|
+
fn: fnName
|
|
2528
|
+
});
|
|
2529
|
+
}
|
|
2530
|
+
return diags;
|
|
2531
|
+
}
|
|
2532
|
+
function extractParamRequirements(g) {
|
|
2533
|
+
const map = /* @__PURE__ */ new Map();
|
|
2534
|
+
const pred = g.symbolic.pred;
|
|
2535
|
+
if (!pred || pred.op === "true") return map;
|
|
2536
|
+
const paramIndex = /* @__PURE__ */ new Map();
|
|
2537
|
+
g.typeParams.forEach((t, i) => paramIndex.set(t.id, i));
|
|
2538
|
+
const consider = (p) => {
|
|
2539
|
+
if (p.op === "and") {
|
|
2540
|
+
p.args.forEach(consider);
|
|
2541
|
+
return;
|
|
2542
|
+
}
|
|
2543
|
+
if ((p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le") && p.a.op === "var" && paramIndex.has(p.a.id) && p.b.op === "lit") {
|
|
2544
|
+
map.set(paramIndex.get(p.a.id), p);
|
|
2545
|
+
}
|
|
2546
|
+
};
|
|
2547
|
+
consider(pred);
|
|
2548
|
+
return map;
|
|
2549
|
+
}
|
|
2550
|
+
function formatDiagnostics(diags) {
|
|
2551
|
+
if (diags.length === 0) return "No issues found.";
|
|
2552
|
+
return diags.map((d) => {
|
|
2553
|
+
const loc = d.fn ? `${d.fn}${d.argIndex !== void 0 ? `[${d.argIndex}]` : ""}: ` : "";
|
|
2554
|
+
let s = `[${d.severity}] ${loc}${d.message} (${d.code})`;
|
|
2555
|
+
if (d.suggestion) s += `
|
|
2556
|
+
\u2192 ${d.suggestion}`;
|
|
2557
|
+
return s;
|
|
2558
|
+
}).join("\n");
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
// src/algebra/stable-source-key.ts
|
|
2562
|
+
function stableAnalyzeKeySource(source) {
|
|
2563
|
+
let end = source.length;
|
|
2564
|
+
while (end > 0) {
|
|
2565
|
+
const ch = source.charCodeAt(end - 1);
|
|
2566
|
+
if (ch === 10 || ch === 13 || ch === 32 || ch === 9) {
|
|
2567
|
+
end--;
|
|
2568
|
+
continue;
|
|
2569
|
+
}
|
|
2570
|
+
break;
|
|
2571
|
+
}
|
|
2572
|
+
const lineStart = source.lastIndexOf("\n", end - 1) + 1;
|
|
2573
|
+
const lastLine = source.slice(lineStart, end);
|
|
2574
|
+
const trimmedLast = lastLine.trim();
|
|
2575
|
+
if (trimmedLast !== "" && !trimmedLast.startsWith("//") && !trimmedLast.startsWith("/*")) {
|
|
2576
|
+
let body2 = source.slice(0, end);
|
|
2577
|
+
if (body2.endsWith("*/")) {
|
|
2578
|
+
body2 = stripTrailingBlockComments(body2);
|
|
2579
|
+
return body2 + "\n";
|
|
2580
|
+
}
|
|
2581
|
+
const after = source.slice(end);
|
|
2582
|
+
if (after === "" || after === "\n") return source;
|
|
2583
|
+
return source.slice(0, end) + "\n";
|
|
2584
|
+
}
|
|
2585
|
+
const lines = source.split("\n");
|
|
2586
|
+
while (lines.length > 0) {
|
|
2587
|
+
const last = lines[lines.length - 1];
|
|
2588
|
+
if (last.trim() === "") {
|
|
2589
|
+
lines.pop();
|
|
2590
|
+
continue;
|
|
2591
|
+
}
|
|
2592
|
+
if (last.trimStart().startsWith("//") && !last.includes("@nudo")) {
|
|
2593
|
+
lines.pop();
|
|
2594
|
+
continue;
|
|
2595
|
+
}
|
|
2596
|
+
if (last.trim() === "*/" || last.trimStart().startsWith("/*")) {
|
|
2597
|
+
const joined = stripTrailingBlockComments(lines.join("\n"));
|
|
2598
|
+
const stripped = joined.split("\n");
|
|
2599
|
+
if (stripped.length < lines.length) {
|
|
2600
|
+
lines.length = 0;
|
|
2601
|
+
lines.push(...stripped);
|
|
2602
|
+
continue;
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
break;
|
|
2606
|
+
}
|
|
2607
|
+
let body = lines.join("\n").replace(/\s+$/, "");
|
|
2608
|
+
body = stripTrailingBlockComments(body);
|
|
2609
|
+
return body + "\n";
|
|
2610
|
+
}
|
|
2611
|
+
function stripTrailingBlockComments(body) {
|
|
2612
|
+
let t = body.replace(/\s+$/, "");
|
|
2613
|
+
while (t.endsWith("*/")) {
|
|
2614
|
+
const start = t.lastIndexOf("/*");
|
|
2615
|
+
if (start < 0) break;
|
|
2616
|
+
const block = t.slice(start);
|
|
2617
|
+
if (block.includes("@nudo")) break;
|
|
2618
|
+
const before = t.slice(0, start);
|
|
2619
|
+
const lineStart = before.lastIndexOf("\n") + 1;
|
|
2620
|
+
if (before.slice(lineStart).trim() !== "") break;
|
|
2621
|
+
t = before.replace(/\s+$/, "");
|
|
2622
|
+
}
|
|
2623
|
+
return t;
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
// src/algebra/check.ts
|
|
2627
|
+
function hofFnArgOk(src, tgt) {
|
|
2628
|
+
if (src.shape.k !== "fn") return false;
|
|
2629
|
+
const t = tgt.shape;
|
|
2630
|
+
if (t.k !== "fn") return false;
|
|
2631
|
+
if (src.shape.params.length < t.params.length) return false;
|
|
2632
|
+
return true;
|
|
2633
|
+
}
|
|
2634
|
+
var checkReportMemo = /* @__PURE__ */ new Map();
|
|
2635
|
+
var checkKeyDeps = /* @__PURE__ */ new Map();
|
|
2636
|
+
var checkDepIndex = /* @__PURE__ */ new Map();
|
|
2637
|
+
var loadModuleIds2 = /* @__PURE__ */ new WeakMap();
|
|
2638
|
+
var nextLoadModuleId2 = 1;
|
|
2639
|
+
var MAX_CHECK_MEMO = 256;
|
|
2640
|
+
function resetCheckSourceMemo() {
|
|
2641
|
+
checkReportMemo.clear();
|
|
2642
|
+
checkKeyDeps.clear();
|
|
2643
|
+
checkDepIndex.clear();
|
|
2644
|
+
resetHashSourceCache();
|
|
2645
|
+
}
|
|
2646
|
+
function getCheckSourceMemoSize() {
|
|
2647
|
+
return checkReportMemo.size;
|
|
2648
|
+
}
|
|
2649
|
+
function loadModuleId2(fn) {
|
|
2650
|
+
if (!fn) return 0;
|
|
2651
|
+
let id = loadModuleIds2.get(fn);
|
|
2652
|
+
if (id === void 0) {
|
|
2653
|
+
id = nextLoadModuleId2++;
|
|
2654
|
+
loadModuleIds2.set(fn, id);
|
|
2655
|
+
}
|
|
2656
|
+
return id;
|
|
2657
|
+
}
|
|
2658
|
+
function checkDepsFingerprint(source, opts) {
|
|
2659
|
+
if (!opts.loadModule || !opts.fromFile) {
|
|
2660
|
+
return { fp: "-", paths: [], truncated: false };
|
|
2661
|
+
}
|
|
2662
|
+
return loadModuleDepsFingerprint(source, opts.loadModule, opts.fromFile);
|
|
2663
|
+
}
|
|
2664
|
+
function unindexCheckKey(key) {
|
|
2665
|
+
const deps = checkKeyDeps.get(key);
|
|
2666
|
+
if (!deps) return;
|
|
2667
|
+
for (const p of deps) {
|
|
2668
|
+
const set = checkDepIndex.get(p);
|
|
2669
|
+
if (!set) continue;
|
|
2670
|
+
set.delete(key);
|
|
2671
|
+
if (set.size === 0) checkDepIndex.delete(p);
|
|
2672
|
+
}
|
|
2673
|
+
checkKeyDeps.delete(key);
|
|
2674
|
+
}
|
|
2675
|
+
function evictCheckSourceMemoForPaths(paths) {
|
|
2676
|
+
let n = 0;
|
|
2677
|
+
for (const raw of paths) {
|
|
2678
|
+
const p = normPath(raw);
|
|
2679
|
+
const keys = checkDepIndex.get(p);
|
|
2680
|
+
if (!keys) continue;
|
|
2681
|
+
for (const key of [...keys]) {
|
|
2682
|
+
checkReportMemo.delete(key);
|
|
2683
|
+
unindexCheckKey(key);
|
|
2684
|
+
n++;
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
return n;
|
|
2688
|
+
}
|
|
2689
|
+
function cloneCheckReport(r) {
|
|
2690
|
+
return {
|
|
2691
|
+
file: r.file,
|
|
2692
|
+
issues: r.issues.map((i) => ({ ...i })),
|
|
2693
|
+
ok: r.ok,
|
|
2694
|
+
signatures: r.signatures.map((s) => ({
|
|
2695
|
+
...s,
|
|
2696
|
+
params: [...s.params]
|
|
2697
|
+
})),
|
|
2698
|
+
summary: { ...r.summary }
|
|
2699
|
+
};
|
|
2700
|
+
}
|
|
2701
|
+
function checkMemoKey(filePath, source, identityOpts, deps) {
|
|
2702
|
+
return [
|
|
2703
|
+
hashSource(source),
|
|
2704
|
+
filePath,
|
|
2705
|
+
`${loadModuleId2(identityOpts.loadModule)}:${identityOpts.fromFile ?? ""}`,
|
|
2706
|
+
deps.fp
|
|
2707
|
+
].join("|");
|
|
2708
|
+
}
|
|
2709
|
+
function checkMemoGet(key) {
|
|
2710
|
+
if (!checkReportMemo.has(key)) return null;
|
|
2711
|
+
const v2 = checkReportMemo.get(key);
|
|
2712
|
+
checkReportMemo.delete(key);
|
|
2713
|
+
checkReportMemo.set(key, v2);
|
|
2714
|
+
return v2;
|
|
2715
|
+
}
|
|
2716
|
+
function checkMemoSet(key, value, depPaths) {
|
|
2717
|
+
if (checkReportMemo.size >= MAX_CHECK_MEMO) {
|
|
2718
|
+
const oldest = checkReportMemo.keys().next().value;
|
|
2719
|
+
if (oldest !== void 0) {
|
|
2720
|
+
checkReportMemo.delete(oldest);
|
|
2721
|
+
unindexCheckKey(oldest);
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
checkReportMemo.set(key, value);
|
|
2725
|
+
if (depPaths.length > 0) {
|
|
2726
|
+
checkKeyDeps.set(key, depPaths);
|
|
2727
|
+
for (const p of depPaths) {
|
|
2728
|
+
let set = checkDepIndex.get(p);
|
|
2729
|
+
if (!set) {
|
|
2730
|
+
set = /* @__PURE__ */ new Set();
|
|
2731
|
+
checkDepIndex.set(p, set);
|
|
2732
|
+
}
|
|
2733
|
+
set.add(key);
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
function listTopFunctions(source, file) {
|
|
2738
|
+
const f = file ?? parseSource(source);
|
|
2739
|
+
const names = [];
|
|
2740
|
+
for (const stmt of f.program.body) {
|
|
2741
|
+
let decl = stmt;
|
|
2742
|
+
if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
|
|
2743
|
+
decl = stmt.declaration;
|
|
2744
|
+
}
|
|
2745
|
+
if (stmt.type === "ExportDefaultDeclaration" && stmt.declaration) {
|
|
2746
|
+
decl = stmt.declaration;
|
|
2747
|
+
}
|
|
2748
|
+
if (decl.type === "FunctionDeclaration" && decl.id) names.push(decl.id.name);
|
|
2749
|
+
if (decl.type === "VariableDeclaration") {
|
|
2750
|
+
for (const d of decl.declarations) {
|
|
2751
|
+
if (d.id.type === "Identifier" && d.init && (d.init.type === "ArrowFunctionExpression" || d.init.type === "FunctionExpression")) {
|
|
2752
|
+
names.push(d.id.name);
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
return names;
|
|
2758
|
+
}
|
|
2759
|
+
function checkSource(filePath, source, phi = pTrue, opts = {}) {
|
|
2760
|
+
let callOpts = opts;
|
|
2761
|
+
if (opts.loadModule && opts.fromFile) {
|
|
2762
|
+
const loadCache = /* @__PURE__ */ new Map();
|
|
2763
|
+
const raw = opts.loadModule;
|
|
2764
|
+
const fromFile = opts.fromFile;
|
|
2765
|
+
callOpts = {
|
|
2766
|
+
...opts,
|
|
2767
|
+
loadModule: (spec, from) => {
|
|
2768
|
+
const k = `${from}\0${spec}`;
|
|
2769
|
+
if (!loadCache.has(k)) loadCache.set(k, raw(spec, from));
|
|
2770
|
+
return loadCache.get(k);
|
|
2771
|
+
}
|
|
2772
|
+
};
|
|
2773
|
+
}
|
|
2774
|
+
const useMemo = phi.op === "true";
|
|
2775
|
+
let memoKey;
|
|
2776
|
+
const stable = stableAnalyzeKeySource(source);
|
|
2777
|
+
const depsFp = checkDepsFingerprint(stable, callOpts);
|
|
2778
|
+
const allowMemo = useMemo && !depsFp.truncated;
|
|
2779
|
+
if (allowMemo) {
|
|
2780
|
+
memoKey = checkMemoKey(filePath, stable, opts, depsFp);
|
|
2781
|
+
const hit = checkMemoGet(memoKey);
|
|
2782
|
+
if (hit) return cloneCheckReport(hit);
|
|
2783
|
+
}
|
|
2784
|
+
const issues = [];
|
|
2785
|
+
const signatures = [];
|
|
2786
|
+
const file = parseSource(source);
|
|
2787
|
+
const names = listTopFunctions(source, file);
|
|
2788
|
+
const truncated = /* @__PURE__ */ new Set();
|
|
2789
|
+
resetAbsCallBudget();
|
|
2790
|
+
setAbsTruncationCollector((label) => truncated.add(label));
|
|
2791
|
+
try {
|
|
2792
|
+
const report = checkSourceInner(
|
|
2793
|
+
filePath,
|
|
2794
|
+
source,
|
|
2795
|
+
file,
|
|
2796
|
+
phi,
|
|
2797
|
+
callOpts,
|
|
2798
|
+
issues,
|
|
2799
|
+
signatures,
|
|
2800
|
+
names,
|
|
2801
|
+
truncated,
|
|
2802
|
+
opts,
|
|
2803
|
+
depsFp
|
|
2804
|
+
);
|
|
2805
|
+
if (memoKey) checkMemoSet(memoKey, report, depsFp.paths);
|
|
2806
|
+
return cloneCheckReport(report);
|
|
2807
|
+
} finally {
|
|
2808
|
+
setAbsTruncationCollector(null);
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
function checkSourceInner(filePath, source, file, phi, opts, issues, signatures, names, truncated, identityOpts = opts, depsFp) {
|
|
2812
|
+
const hasRefineDirective = source.includes("@nudo:refine");
|
|
2813
|
+
const refineLoad = identityOpts.loadModule ?? opts.loadModule;
|
|
2814
|
+
const refineFrom = identityOpts.fromFile ?? opts.fromFile ?? filePath;
|
|
2815
|
+
for (const name of names) {
|
|
2816
|
+
const g = generalizeFromAst(name, source, {
|
|
2817
|
+
file,
|
|
2818
|
+
refine: {
|
|
2819
|
+
loadModule: refineLoad,
|
|
2820
|
+
fromFile: refineFrom
|
|
2821
|
+
},
|
|
2822
|
+
depsFp
|
|
2823
|
+
});
|
|
2824
|
+
if (!g) {
|
|
2825
|
+
issues.push({
|
|
2826
|
+
severity: "warning",
|
|
2827
|
+
code: "nudo:no-signature",
|
|
2828
|
+
message: `${name}: \u65E0\u6CD5\u5F52\u7EB3\u7B26\u53F7 Abs`,
|
|
2829
|
+
suggestion: "\u8865 @nudo:case \u6216\u8BA9\u51FD\u6570\u4F53\u53EF\u4EE3\u6570\u6C42\u503C",
|
|
2830
|
+
fn: name
|
|
2831
|
+
});
|
|
2832
|
+
continue;
|
|
2833
|
+
}
|
|
2834
|
+
const fmt = formatSigCached(g.symbolic, name);
|
|
2835
|
+
signatures.push({
|
|
2836
|
+
name,
|
|
2837
|
+
params: g.params,
|
|
2838
|
+
abs: g.symbolic,
|
|
2839
|
+
display: fmt.display,
|
|
2840
|
+
detail: fmt.detail,
|
|
2841
|
+
conf: g.symbolic.conf
|
|
2842
|
+
});
|
|
2843
|
+
if (hasRefineDirective) {
|
|
2844
|
+
const ret = extractRefineReturnFromSource(source, name, {
|
|
2845
|
+
loadModule: refineLoad,
|
|
2846
|
+
fromFile: refineFrom
|
|
2847
|
+
});
|
|
2848
|
+
if (ret) {
|
|
2849
|
+
issues.push(
|
|
2850
|
+
...checkReturnConstraint(name, ret.name, ret.constraint, g.symbolic)
|
|
2851
|
+
);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
if (g.symbolic.conf === "opaque" || g.symbolic.conf === "partial") {
|
|
2855
|
+
const entryArgs = g.typeParams.map((t) => t.value);
|
|
2856
|
+
try {
|
|
2857
|
+
const r = analyzeFn(source, name, entryArgs, phi, void 0, file);
|
|
2858
|
+
if (r.conf === "opaque" && !truncated.has(name)) {
|
|
2859
|
+
issues.push({
|
|
2860
|
+
severity: "info",
|
|
2861
|
+
code: "nudo:opaque-result",
|
|
2862
|
+
message: `${name}(...): conf=opaque\uFF08\u8DEF\u5F84\u672A\u8986\u76D6\u6216 native\uFF09`,
|
|
2863
|
+
suggestion: "\u8865 @nudo:case \u6216\u8C03\u7528\u70B9",
|
|
2864
|
+
fn: name
|
|
2865
|
+
});
|
|
2866
|
+
}
|
|
2867
|
+
} catch (e) {
|
|
2868
|
+
issues.push({
|
|
2869
|
+
severity: "error",
|
|
2870
|
+
code: "nudo:eval-error",
|
|
2871
|
+
message: `${name}: \u6C42\u503C\u5931\u8D25 \u2014 ${e.message}`,
|
|
2872
|
+
fn: name
|
|
2873
|
+
});
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
for (const label of truncated) {
|
|
2878
|
+
issues.push({
|
|
2879
|
+
severity: "warning",
|
|
2880
|
+
code: "nudo:recursion-truncated",
|
|
2881
|
+
message: `Recursive evaluation of '${label}' was truncated (depth/size budget); result widened to unknown`,
|
|
2882
|
+
suggestion: "\u6536\u7A84\u9012\u5F52\u57FA\u4F8B\u6216\u6539\u7528\u663E\u5F0F @nudo:refine return \u5951\u7EA6",
|
|
2883
|
+
fn: label
|
|
2884
|
+
});
|
|
2885
|
+
}
|
|
2886
|
+
const records = [];
|
|
2887
|
+
const varAbs = /* @__PURE__ */ new Map();
|
|
2888
|
+
setAbsAssignCollector((r) => records.push(r));
|
|
2889
|
+
try {
|
|
2890
|
+
const { env } = evalProgramAbs(source, { file });
|
|
2891
|
+
for (const [k, v2] of env.vars) varAbs.set(k, v2);
|
|
2892
|
+
} catch {
|
|
2893
|
+
} finally {
|
|
2894
|
+
setAbsAssignCollector(null);
|
|
2895
|
+
}
|
|
2896
|
+
const callIssues = canSkipLiteralCallScan(source, file) ? [] : scanLiteralCalls(source, names, phi, {
|
|
2897
|
+
loadModule: opts.loadModule,
|
|
2898
|
+
fromFile: filePath,
|
|
2899
|
+
file,
|
|
2900
|
+
varAbs
|
|
2901
|
+
});
|
|
2902
|
+
issues.push(...callIssues);
|
|
2903
|
+
issues.push(
|
|
2904
|
+
...scanCaseInconsistency(source, names, {
|
|
2905
|
+
loadModule: opts.loadModule,
|
|
2906
|
+
fromFile: filePath,
|
|
2907
|
+
file
|
|
2908
|
+
})
|
|
2909
|
+
);
|
|
2910
|
+
issues.push(...structuralAssignIssues(records));
|
|
2911
|
+
const errors = issues.filter((i) => i.severity === "error").length;
|
|
2912
|
+
const warnings = issues.filter((i) => i.severity === "warning").length;
|
|
2913
|
+
const infos = issues.filter((i) => i.severity === "info").length;
|
|
2914
|
+
return {
|
|
2915
|
+
file: filePath,
|
|
2916
|
+
issues,
|
|
2917
|
+
ok: errors === 0,
|
|
2918
|
+
signatures,
|
|
2919
|
+
summary: { errors, warnings, infos, functions: signatures.length }
|
|
2920
|
+
};
|
|
2921
|
+
}
|
|
2922
|
+
function absUnknown() {
|
|
2923
|
+
return { shape: { k: "unknown" }, conf: "partial" };
|
|
2924
|
+
}
|
|
2925
|
+
var sigFormatCache = /* @__PURE__ */ new WeakMap();
|
|
2926
|
+
function formatSigCached(absVal, name) {
|
|
2927
|
+
const hit = sigFormatCache.get(absVal);
|
|
2928
|
+
if (hit) return hit;
|
|
2929
|
+
const out = {
|
|
2930
|
+
display: formatAbs(absVal),
|
|
2931
|
+
detail: formatAbsMultiline(absVal, name)
|
|
2932
|
+
};
|
|
2933
|
+
sigFormatCache.set(absVal, out);
|
|
2934
|
+
return out;
|
|
2935
|
+
}
|
|
2936
|
+
function checkReturnConstraint(fnName, cName, constraint, ret) {
|
|
2937
|
+
const out = [];
|
|
2938
|
+
if (ret.shape.k === "unknown" && !ret.term) return out;
|
|
2939
|
+
if (ret.shape.k === "any") return out;
|
|
2940
|
+
const push = (actual, expected, suggestion) => {
|
|
2941
|
+
out.push({
|
|
2942
|
+
severity: "error",
|
|
2943
|
+
code: "nudo:constraint-violated",
|
|
2944
|
+
message: `${fnName}: \u8FD4\u56DE\u503C \u22AD @nudo:refine return ${cName}`,
|
|
2945
|
+
actual,
|
|
2946
|
+
expected,
|
|
2947
|
+
suggestion,
|
|
2948
|
+
fn: fnName
|
|
2949
|
+
});
|
|
2950
|
+
};
|
|
2951
|
+
if (constraint.fields) {
|
|
2952
|
+
const slots = ret.shape.k === "obj" ? ret.shape.slots : void 0;
|
|
2953
|
+
if (!slots) {
|
|
2954
|
+
if (ret.shape.k !== "never") {
|
|
2955
|
+
push(formatAbs(ret), `object shape (${cName})`, `\u8FD4\u56DE\u6EE1\u8DB3 ${cName} \u5F62\u72B6\u7684 object`);
|
|
2956
|
+
}
|
|
2957
|
+
return out;
|
|
2958
|
+
}
|
|
2959
|
+
for (const [key, field] of Object.entries(constraint.fields)) {
|
|
2960
|
+
const slot = slots[key];
|
|
2961
|
+
if (!slot) {
|
|
2962
|
+
if (!field.optional && !field.constraint.isOptional) {
|
|
2963
|
+
push(formatAbs(ret), `missing field ${key}`, `\u8FD4\u56DE\u503C\u8865\u5168\u5B57\u6BB5 ${key}`);
|
|
2964
|
+
}
|
|
2965
|
+
continue;
|
|
2966
|
+
}
|
|
2967
|
+
if (field.constraint.prim && slot.value.shape.k === "prim") {
|
|
2968
|
+
const actualPrim = slot.value.shape.type;
|
|
2969
|
+
if (actualPrim !== field.constraint.prim) {
|
|
2970
|
+
push(
|
|
2971
|
+
formatAbs(slot.value),
|
|
2972
|
+
`typeof ${key} = "${field.constraint.prim}"`,
|
|
2973
|
+
`\u628A\u8FD4\u56DE\u503C\u7684 ${key} \u6539\u6210 ${field.constraint.prim}`
|
|
2974
|
+
);
|
|
2975
|
+
continue;
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
const lv2 = litValue(slot.value);
|
|
2979
|
+
if (lv2 !== void 0 && typeof lv2 === "number") {
|
|
2980
|
+
for (const p of field.constraint.preds) {
|
|
2981
|
+
const flat = p.op === "and" ? p.args : [p];
|
|
2982
|
+
for (const atom of flat) {
|
|
2983
|
+
if ((atom.op === "gt" || atom.op === "ge" || atom.op === "lt" || atom.op === "le") && atom.b.op === "lit" && typeof atom.b.value === "number") {
|
|
2984
|
+
const n = atom.b.value;
|
|
2985
|
+
let ok = true;
|
|
2986
|
+
if (atom.op === "gt") ok = lv2 > n;
|
|
2987
|
+
if (atom.op === "ge") ok = lv2 >= n;
|
|
2988
|
+
if (atom.op === "lt") ok = lv2 < n;
|
|
2989
|
+
if (atom.op === "le") ok = lv2 <= n;
|
|
2990
|
+
if (!ok) {
|
|
2991
|
+
const opSym = { gt: ">", ge: "\u2265", lt: "<", le: "\u2264" }[atom.op];
|
|
2992
|
+
push(
|
|
2993
|
+
formatAbs(slot.value),
|
|
2994
|
+
`${key} ${opSym} ${n}`,
|
|
2995
|
+
`\u8FD4\u56DE\u503C\u7684 ${key} \u5E94\u6EE1\u8DB3 ${key} ${opSym} ${n}`
|
|
2996
|
+
);
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
return out;
|
|
3004
|
+
}
|
|
3005
|
+
if (constraint.prim && ret.shape.k === "prim") {
|
|
3006
|
+
const actualPrim = ret.shape.type;
|
|
3007
|
+
if (actualPrim !== constraint.prim) {
|
|
3008
|
+
push(formatAbs(ret), `typeof return = "${constraint.prim}"`, `\u8FD4\u56DE ${constraint.prim}`);
|
|
3009
|
+
return out;
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
const lv = litValue(ret);
|
|
3013
|
+
if (lv !== void 0 && typeof lv === "number") {
|
|
3014
|
+
for (const p of constraint.preds) {
|
|
3015
|
+
const flat = p.op === "and" ? p.args : [p];
|
|
3016
|
+
for (const atom of flat) {
|
|
3017
|
+
if ((atom.op === "gt" || atom.op === "ge" || atom.op === "lt" || atom.op === "le") && atom.b.op === "lit" && typeof atom.b.value === "number") {
|
|
3018
|
+
const n = atom.b.value;
|
|
3019
|
+
let ok = true;
|
|
3020
|
+
if (atom.op === "gt") ok = lv > n;
|
|
3021
|
+
if (atom.op === "ge") ok = lv >= n;
|
|
3022
|
+
if (atom.op === "lt") ok = lv < n;
|
|
3023
|
+
if (atom.op === "le") ok = lv <= n;
|
|
3024
|
+
if (!ok) {
|
|
3025
|
+
const opSym = { gt: ">", ge: "\u2265", lt: "<", le: "\u2264" }[atom.op];
|
|
3026
|
+
push(formatAbs(ret), `return ${opSym} ${n}`, `\u8FD4\u56DE\u6EE1\u8DB3 ${opSym} ${n} \u7684\u503C`);
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
return out;
|
|
3033
|
+
}
|
|
3034
|
+
function structuralAssignIssues(records) {
|
|
3035
|
+
const out = [];
|
|
3036
|
+
for (const r of records) {
|
|
3037
|
+
if (!r.prev) continue;
|
|
3038
|
+
if (r.next.shape.k === "unknown" && !r.next.term) continue;
|
|
3039
|
+
if (r.prev.shape.k === "unknown" && !r.prev.term) continue;
|
|
3040
|
+
const leq = leqAbs(r.next, r.prev);
|
|
3041
|
+
if (!leq.ok) {
|
|
3042
|
+
out.push({
|
|
3043
|
+
severity: "error",
|
|
3044
|
+
code: "nudo:assign-mismatch",
|
|
3045
|
+
message: `${r.name}: \u8D4B\u503C \u22AD \u539F\u6709\u5F62\u72B6`,
|
|
3046
|
+
actual: formatAbs(r.next),
|
|
3047
|
+
expected: formatAbs(r.prev),
|
|
3048
|
+
suggestion: leq.reason ?? "\u6539\u7528\u517C\u5BB9\u7684\u503C\uFF0C\u6216\u653E\u5BBD\u7ED1\u5B9A\u7C7B\u578B",
|
|
3049
|
+
fn: r.name,
|
|
3050
|
+
line: r.line,
|
|
3051
|
+
column: r.column
|
|
3052
|
+
});
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
return out;
|
|
3056
|
+
}
|
|
3057
|
+
function scanCaseInconsistency(source, knownFns, opts) {
|
|
3058
|
+
const out = [];
|
|
3059
|
+
if (!source.includes("@nudo:case")) return out;
|
|
3060
|
+
if (!source.includes("@nudo:refine")) return out;
|
|
3061
|
+
const file = opts.file ?? parseSource(source);
|
|
3062
|
+
const parseLitArg = (s) => {
|
|
3063
|
+
const t = s.trim();
|
|
3064
|
+
if (t === "") return void 0;
|
|
3065
|
+
if (t === "true") return { shape: { k: "prim", type: "boolean" }, term: { op: "lit", value: true }, conf: "exact" };
|
|
3066
|
+
if (t === "false") return { shape: { k: "prim", type: "boolean" }, term: { op: "lit", value: false }, conf: "exact" };
|
|
3067
|
+
if (t === "null") return { shape: { k: "unknown" }, term: { op: "lit", value: null }, conf: "exact" };
|
|
3068
|
+
if (t === "undefined") return { shape: { k: "unknown" }, term: { op: "lit", value: void 0 }, conf: "exact" };
|
|
3069
|
+
if (/^-?\d+(\.\d+)?$/.test(t)) return numLit(Number(t));
|
|
3070
|
+
const str2 = t.match(/^(['"])([\s\S]*)\1$/);
|
|
3071
|
+
if (str2) {
|
|
3072
|
+
return {
|
|
3073
|
+
shape: { k: "prim", type: "string" },
|
|
3074
|
+
term: { op: "lit", value: str2[2] },
|
|
3075
|
+
conf: "exact"
|
|
3076
|
+
};
|
|
3077
|
+
}
|
|
3078
|
+
return void 0;
|
|
3079
|
+
};
|
|
3080
|
+
const parseCaseArgs = (raw) => {
|
|
3081
|
+
const m = raw.match(/@nudo:case\s+"[^"]+"\s*\(([\s\S]*)\)/);
|
|
3082
|
+
if (!m) return void 0;
|
|
3083
|
+
const inner = m[1].trim();
|
|
3084
|
+
if (inner === "") return [];
|
|
3085
|
+
const parts = [];
|
|
3086
|
+
let depth = 0;
|
|
3087
|
+
let cur = "";
|
|
3088
|
+
let quote = null;
|
|
3089
|
+
for (let i = 0; i < inner.length; i++) {
|
|
3090
|
+
const ch = inner[i];
|
|
3091
|
+
if (quote) {
|
|
3092
|
+
cur += ch;
|
|
3093
|
+
if (ch === quote && inner[i - 1] !== "\\") quote = null;
|
|
3094
|
+
continue;
|
|
3095
|
+
}
|
|
3096
|
+
if (ch === '"' || ch === "'") {
|
|
3097
|
+
quote = ch;
|
|
3098
|
+
cur += ch;
|
|
3099
|
+
continue;
|
|
3100
|
+
}
|
|
3101
|
+
if (ch === "(" || ch === "[" || ch === "{") depth++;
|
|
3102
|
+
if (ch === ")" || ch === "]" || ch === "}") depth--;
|
|
3103
|
+
if (ch === "," && depth === 0) {
|
|
3104
|
+
parts.push(cur);
|
|
3105
|
+
cur = "";
|
|
3106
|
+
continue;
|
|
3107
|
+
}
|
|
3108
|
+
cur += ch;
|
|
3109
|
+
}
|
|
3110
|
+
if (cur.trim()) parts.push(cur);
|
|
3111
|
+
return parts;
|
|
3112
|
+
};
|
|
3113
|
+
const checkCaseAgainstReqs = (fnName, caseName, args, line) => {
|
|
3114
|
+
const g = generalizeFromAst(fnName, source, file ? { file } : {});
|
|
3115
|
+
if (!g) return;
|
|
3116
|
+
const paramNames = g.params;
|
|
3117
|
+
const reqs = refineToIndexedFull(source, fnName, paramNames, {
|
|
3118
|
+
loadModule: opts.loadModule,
|
|
3119
|
+
fromFile: opts.fromFile ?? ""
|
|
3120
|
+
});
|
|
3121
|
+
if (reqs.length === 0) return;
|
|
3122
|
+
const absArgs = args.map((a) => parseLitArg(a) ?? absUnknown());
|
|
3123
|
+
for (const [idx, entry] of reqs) {
|
|
3124
|
+
if (entry.constraint.fields) continue;
|
|
3125
|
+
const arg = absArgs[idx];
|
|
3126
|
+
if (!arg) continue;
|
|
3127
|
+
const lv = litValue(arg);
|
|
3128
|
+
if (lv === void 0 || typeof lv !== "number") continue;
|
|
3129
|
+
const flatten = (p) => p.op === "and" ? p.args.flatMap(flatten) : p.op === "true" ? [] : [p];
|
|
3130
|
+
for (const p of flatten(entry.pred)) {
|
|
3131
|
+
if ((p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le") && p.b.op === "lit" && typeof p.b.value === "number") {
|
|
3132
|
+
const n = p.b.value;
|
|
3133
|
+
let ok = true;
|
|
3134
|
+
if (p.op === "gt") ok = lv > n;
|
|
3135
|
+
if (p.op === "ge") ok = lv >= n;
|
|
3136
|
+
if (p.op === "lt") ok = lv < n;
|
|
3137
|
+
if (p.op === "le") ok = lv <= n;
|
|
3138
|
+
if (!ok) {
|
|
3139
|
+
const paramName = entry.param || paramNames[idx] || `arg${idx}`;
|
|
3140
|
+
out.push({
|
|
3141
|
+
severity: "error",
|
|
3142
|
+
code: "nudo:case-inconsistency",
|
|
3143
|
+
message: `${fnName} case "${caseName}": \u89C1\u8BC1 \u22AD \u5951\u7EA6`,
|
|
3144
|
+
actual: formatAbs(arg),
|
|
3145
|
+
expected: predToString(p),
|
|
3146
|
+
suggestion: `\u6539 case \u5B9E\u53C2\uFF0C\u6216\u653E\u5BBD ${paramName} \u7684 refine`,
|
|
3147
|
+
fn: fnName,
|
|
3148
|
+
line
|
|
3149
|
+
});
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
};
|
|
3155
|
+
const visit = (n) => {
|
|
3156
|
+
if (!n || typeof n !== "object") return;
|
|
3157
|
+
const obj2 = n;
|
|
3158
|
+
let decl = obj2;
|
|
3159
|
+
if (obj2.type === "ExportNamedDeclaration" || obj2.type === "ExportDefaultDeclaration") {
|
|
3160
|
+
decl = obj2.declaration;
|
|
3161
|
+
}
|
|
3162
|
+
if (decl && (decl.type === "FunctionDeclaration" || decl.type === "VariableDeclaration" && (decl.declarations ?? [])[0]?.init && ["ArrowFunctionExpression", "FunctionExpression"].includes(
|
|
3163
|
+
String(
|
|
3164
|
+
decl.declarations[0].init.type
|
|
3165
|
+
)
|
|
3166
|
+
))) {
|
|
3167
|
+
const id = decl.type === "FunctionDeclaration" ? decl.id?.name : decl.declarations[0]?.id?.name;
|
|
3168
|
+
if (id && knownFns.includes(id)) {
|
|
3169
|
+
for (const c of obj2.leadingComments ?? []) {
|
|
3170
|
+
const caseArgs = parseCaseArgs(c.value);
|
|
3171
|
+
if (!caseArgs) continue;
|
|
3172
|
+
const caseName = /@nudo:case\s+"([^"]+)"/.exec(c.value)?.[1] ?? "?";
|
|
3173
|
+
checkCaseAgainstReqs(id, caseName, caseArgs, c.loc?.start.line);
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
for (const key of Object.keys(obj2)) {
|
|
3178
|
+
if (key === "loc" || key === "start" || key === "end" || key === "leadingComments") continue;
|
|
3179
|
+
const val = obj2[key];
|
|
3180
|
+
if (Array.isArray(val)) val.forEach(visit);
|
|
3181
|
+
else if (val && typeof val === "object") visit(val);
|
|
3182
|
+
}
|
|
3183
|
+
};
|
|
3184
|
+
visit(file);
|
|
3185
|
+
return out;
|
|
3186
|
+
}
|
|
3187
|
+
function collectParamStructReqs(source, fnName, fileAst) {
|
|
3188
|
+
const reqs = /* @__PURE__ */ new Map();
|
|
3189
|
+
const extracted = extractFn(source, fnName, fileAst);
|
|
3190
|
+
if (!extracted) return reqs;
|
|
3191
|
+
const params = new Set(extracted.params);
|
|
3192
|
+
const body = extracted.body;
|
|
3193
|
+
const BUILTIN_METHODS = /* @__PURE__ */ new Set([
|
|
3194
|
+
"map",
|
|
3195
|
+
"filter",
|
|
3196
|
+
"reduce",
|
|
3197
|
+
"flatMap",
|
|
3198
|
+
"forEach",
|
|
3199
|
+
"some",
|
|
3200
|
+
"every",
|
|
3201
|
+
"find",
|
|
3202
|
+
"findIndex",
|
|
3203
|
+
"includes",
|
|
3204
|
+
"indexOf",
|
|
3205
|
+
"lastIndexOf",
|
|
3206
|
+
"join",
|
|
3207
|
+
"slice",
|
|
3208
|
+
"splice",
|
|
3209
|
+
"push",
|
|
3210
|
+
"pop",
|
|
3211
|
+
"shift",
|
|
3212
|
+
"unshift",
|
|
3213
|
+
"sort",
|
|
3214
|
+
"reverse",
|
|
3215
|
+
"concat",
|
|
3216
|
+
"at",
|
|
3217
|
+
"replace",
|
|
3218
|
+
"replaceAll",
|
|
3219
|
+
"split",
|
|
3220
|
+
"trim",
|
|
3221
|
+
"toLowerCase",
|
|
3222
|
+
"toUpperCase",
|
|
3223
|
+
"startsWith",
|
|
3224
|
+
"endsWith",
|
|
3225
|
+
"charAt",
|
|
3226
|
+
"charCodeAt",
|
|
3227
|
+
"padStart",
|
|
3228
|
+
"padEnd",
|
|
3229
|
+
"repeat",
|
|
3230
|
+
"toString",
|
|
3231
|
+
"valueOf",
|
|
3232
|
+
"substring",
|
|
3233
|
+
"match",
|
|
3234
|
+
"search",
|
|
3235
|
+
"hasOwnProperty",
|
|
3236
|
+
"keys",
|
|
3237
|
+
"values",
|
|
3238
|
+
"entries",
|
|
3239
|
+
"then",
|
|
3240
|
+
"catch",
|
|
3241
|
+
"finally"
|
|
3242
|
+
]);
|
|
3243
|
+
const visit = (n, isMethodCallee = false) => {
|
|
3244
|
+
if (!n || typeof n !== "object") return;
|
|
3245
|
+
const obj2 = n;
|
|
3246
|
+
if (obj2.type === "MemberExpression" && !obj2.computed && !isMethodCallee) {
|
|
3247
|
+
const o = obj2.object;
|
|
3248
|
+
const p = obj2.property;
|
|
3249
|
+
if (o?.type === "Identifier" && o.name && params.has(o.name) && p?.type === "Identifier" && p.name) {
|
|
3250
|
+
if (BUILTIN_METHODS.has(p.name)) return;
|
|
3251
|
+
let set = reqs.get(o.name);
|
|
3252
|
+
if (!set) {
|
|
3253
|
+
set = /* @__PURE__ */ new Set();
|
|
3254
|
+
reqs.set(o.name, set);
|
|
3255
|
+
}
|
|
3256
|
+
set.add(p.name);
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
if (obj2.type === "CallExpression") {
|
|
3260
|
+
visit(obj2.callee, true);
|
|
3261
|
+
for (const key of Object.keys(obj2)) {
|
|
3262
|
+
if (key === "loc" || key === "start" || key === "end" || key === "callee") continue;
|
|
3263
|
+
const val = obj2[key];
|
|
3264
|
+
if (Array.isArray(val)) val.forEach((v2) => visit(v2, false));
|
|
3265
|
+
else if (val && typeof val === "object") visit(val, false);
|
|
3266
|
+
}
|
|
3267
|
+
return;
|
|
3268
|
+
}
|
|
3269
|
+
for (const key of Object.keys(obj2)) {
|
|
3270
|
+
if (key === "loc" || key === "start" || key === "end") continue;
|
|
3271
|
+
const val = obj2[key];
|
|
3272
|
+
if (Array.isArray(val)) val.forEach((v2) => visit(v2, isMethodCallee));
|
|
3273
|
+
else if (val && typeof val === "object") visit(val, isMethodCallee);
|
|
3274
|
+
}
|
|
3275
|
+
};
|
|
3276
|
+
visit(body);
|
|
3277
|
+
return reqs;
|
|
3278
|
+
}
|
|
3279
|
+
function evalArgAbs(node, lookupVar) {
|
|
3280
|
+
if (node.type === "Identifier" && typeof node.name === "string" && lookupVar) {
|
|
3281
|
+
return lookupVar(node.name);
|
|
3282
|
+
}
|
|
3283
|
+
try {
|
|
3284
|
+
const env = emptyEnv();
|
|
3285
|
+
if (lookupVar) {
|
|
3286
|
+
const rawGet = env.vars.get.bind(env.vars);
|
|
3287
|
+
env.vars.get = ((name) => {
|
|
3288
|
+
const hit = rawGet(name);
|
|
3289
|
+
if (hit !== void 0) return hit;
|
|
3290
|
+
return lookupVar(name);
|
|
3291
|
+
});
|
|
3292
|
+
}
|
|
3293
|
+
return evalNode(node, env, pTrue, defaultLeakBudget).value;
|
|
3294
|
+
} catch {
|
|
3295
|
+
return void 0;
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
function requireSpecOf(init) {
|
|
3299
|
+
if (init.type === "CallExpression") {
|
|
3300
|
+
const callee = init.callee;
|
|
3301
|
+
const args = init.arguments;
|
|
3302
|
+
if (callee?.type === "Identifier" && callee.name === "require" && args?.[0]?.type === "StringLiteral") {
|
|
3303
|
+
return String(args[0].value);
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
if (init.type === "MemberExpression") {
|
|
3307
|
+
const obj2 = init.object;
|
|
3308
|
+
if (obj2) return requireSpecOf(obj2);
|
|
3309
|
+
}
|
|
3310
|
+
return void 0;
|
|
3311
|
+
}
|
|
3312
|
+
function requireExportName(init) {
|
|
3313
|
+
if (init.type === "MemberExpression" && !init.computed) {
|
|
3314
|
+
const prop = init.property;
|
|
3315
|
+
if (prop?.type === "Identifier") return prop.name;
|
|
3316
|
+
}
|
|
3317
|
+
return void 0;
|
|
3318
|
+
}
|
|
3319
|
+
function dynamicImportSpec(node) {
|
|
3320
|
+
let n = node;
|
|
3321
|
+
if (n?.type === "AwaitExpression") {
|
|
3322
|
+
n = n.argument;
|
|
3323
|
+
}
|
|
3324
|
+
if (n?.type !== "CallExpression") return void 0;
|
|
3325
|
+
const callee = n.callee;
|
|
3326
|
+
if (callee?.type !== "Import") return void 0;
|
|
3327
|
+
const args = n.arguments;
|
|
3328
|
+
if (args?.[0]?.type === "StringLiteral") return String(args[0].value);
|
|
3329
|
+
return void 0;
|
|
3330
|
+
}
|
|
3331
|
+
function resolveExportSource(modSrc, fnName, loadSpec, depth = 0) {
|
|
3332
|
+
if (depth > 3) return modSrc;
|
|
3333
|
+
try {
|
|
3334
|
+
if (listTopFunctions(modSrc).includes(fnName)) return modSrc;
|
|
3335
|
+
} catch {
|
|
3336
|
+
return modSrc;
|
|
3337
|
+
}
|
|
3338
|
+
const file = parseSource(modSrc);
|
|
3339
|
+
let nextSpec;
|
|
3340
|
+
for (const stmt of file.program.body) {
|
|
3341
|
+
if (stmt.type !== "ExportNamedDeclaration" && stmt.type !== "ExportAllDeclaration") continue;
|
|
3342
|
+
const src = stmt.source;
|
|
3343
|
+
if (src?.type !== "StringLiteral" || typeof src.value !== "string") continue;
|
|
3344
|
+
if (stmt.type === "ExportAllDeclaration") {
|
|
3345
|
+
nextSpec = String(src.value);
|
|
3346
|
+
break;
|
|
3347
|
+
}
|
|
3348
|
+
const clause = stmt.specifiers ?? [];
|
|
3349
|
+
for (const sp of clause) {
|
|
3350
|
+
if (sp.type !== "ExportSpecifier") continue;
|
|
3351
|
+
const local = sp.local;
|
|
3352
|
+
if (local?.type === "Identifier" && local.name === fnName) {
|
|
3353
|
+
nextSpec = String(src.value);
|
|
3354
|
+
break;
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
if (nextSpec) break;
|
|
3358
|
+
}
|
|
3359
|
+
if (!nextSpec) return modSrc;
|
|
3360
|
+
const next = loadSpec(nextSpec);
|
|
3361
|
+
if (!next) return modSrc;
|
|
3362
|
+
return resolveExportSource(next, fnName, loadSpec, depth + 1);
|
|
3363
|
+
}
|
|
3364
|
+
var STMT_CONTAINER = /* @__PURE__ */ new Set([
|
|
3365
|
+
"File",
|
|
3366
|
+
"Program",
|
|
3367
|
+
"BlockStatement",
|
|
3368
|
+
"FunctionDeclaration",
|
|
3369
|
+
"FunctionExpression",
|
|
3370
|
+
"ArrowFunctionExpression",
|
|
3371
|
+
"IfStatement",
|
|
3372
|
+
"ForStatement",
|
|
3373
|
+
"ForInStatement",
|
|
3374
|
+
"ForOfStatement",
|
|
3375
|
+
"WhileStatement",
|
|
3376
|
+
"DoWhileStatement",
|
|
3377
|
+
"SwitchStatement",
|
|
3378
|
+
"SwitchCase",
|
|
3379
|
+
"TryStatement",
|
|
3380
|
+
"CatchClause",
|
|
3381
|
+
"LabeledStatement",
|
|
3382
|
+
"ExportNamedDeclaration",
|
|
3383
|
+
"ExportDefaultDeclaration",
|
|
3384
|
+
"ExportAllDeclaration",
|
|
3385
|
+
"ClassDeclaration",
|
|
3386
|
+
"ClassBody",
|
|
3387
|
+
"ClassMethod",
|
|
3388
|
+
"StaticBlock",
|
|
3389
|
+
"ObjectMethod"
|
|
3390
|
+
]);
|
|
3391
|
+
function walkStatements(n, onStmt) {
|
|
3392
|
+
if (!n || typeof n !== "object") return;
|
|
3393
|
+
const obj2 = n;
|
|
3394
|
+
if (typeof obj2.type === "string") onStmt(obj2);
|
|
3395
|
+
if (!STMT_CONTAINER.has(String(obj2.type))) return;
|
|
3396
|
+
for (const key of Object.keys(obj2)) {
|
|
3397
|
+
if (key === "loc" || key === "start" || key === "end" || key === "leadingComments") continue;
|
|
3398
|
+
const val = obj2[key];
|
|
3399
|
+
if (Array.isArray(val)) val.forEach((x) => walkStatements(x, onStmt));
|
|
3400
|
+
else if (val && typeof val === "object") walkStatements(val, onStmt);
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
function collectCallResolvers(source, knownFns, opts) {
|
|
3404
|
+
const knownSet = new Set(knownFns);
|
|
3405
|
+
const aliasToFn = /* @__PURE__ */ new Map();
|
|
3406
|
+
const memberToFn = /* @__PURE__ */ new Map();
|
|
3407
|
+
const externalFn = /* @__PURE__ */ new Map();
|
|
3408
|
+
const externalMember = /* @__PURE__ */ new Map();
|
|
3409
|
+
const file = opts?.file ?? parseSource(source);
|
|
3410
|
+
const load = opts?.loadModule;
|
|
3411
|
+
const fromFile = opts?.fromFile ?? "";
|
|
3412
|
+
const modCache = /* @__PURE__ */ new Map();
|
|
3413
|
+
const loadSpec = (spec) => {
|
|
3414
|
+
if (!load) return void 0;
|
|
3415
|
+
if (!modCache.has(spec)) modCache.set(spec, load(spec, fromFile));
|
|
3416
|
+
return modCache.get(spec);
|
|
3417
|
+
};
|
|
3418
|
+
const bindExternal = (local, modSrc, fnName) => {
|
|
3419
|
+
externalFn.set(local, {
|
|
3420
|
+
source: resolveExportSource(modSrc, fnName, loadSpec),
|
|
3421
|
+
fnName
|
|
3422
|
+
});
|
|
3423
|
+
};
|
|
3424
|
+
const visit = (n) => {
|
|
3425
|
+
if (!n || typeof n !== "object") return;
|
|
3426
|
+
const obj2 = n;
|
|
3427
|
+
if (obj2.type === "ImportDeclaration") {
|
|
3428
|
+
const spec = obj2.source;
|
|
3429
|
+
if (spec?.type === "StringLiteral" && typeof spec.value === "string") {
|
|
3430
|
+
const modSrc = loadSpec(String(spec.value));
|
|
3431
|
+
if (modSrc) {
|
|
3432
|
+
for (const sp of obj2.specifiers ?? []) {
|
|
3433
|
+
if (sp.type === "ImportSpecifier") {
|
|
3434
|
+
const imported = sp.imported;
|
|
3435
|
+
const local = sp.local;
|
|
3436
|
+
const exportName = imported?.type === "Identifier" ? imported.name : imported?.type === "StringLiteral" ? String(imported.value) : void 0;
|
|
3437
|
+
if (exportName && local?.name) {
|
|
3438
|
+
bindExternal(local.name, modSrc, exportName);
|
|
3439
|
+
}
|
|
3440
|
+
} else if (sp.type === "ImportNamespaceSpecifier") {
|
|
3441
|
+
const local = sp.local;
|
|
3442
|
+
if (local?.name) {
|
|
3443
|
+
externalMember.set(`${local.name}.__module__`, { source: modSrc, fnName: "" });
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
if (obj2.type === "VariableDeclaration") {
|
|
3451
|
+
for (const d of obj2.declarations ?? []) {
|
|
3452
|
+
const id = d.id;
|
|
3453
|
+
const init = d.init;
|
|
3454
|
+
if (!id || !init) continue;
|
|
3455
|
+
const dynSpec = dynamicImportSpec(init);
|
|
3456
|
+
if (dynSpec) {
|
|
3457
|
+
const modSrc = loadSpec(dynSpec);
|
|
3458
|
+
if (modSrc && id.type === "ObjectPattern") {
|
|
3459
|
+
for (const p of id.properties ?? []) {
|
|
3460
|
+
const key = p.key;
|
|
3461
|
+
const val = p.value;
|
|
3462
|
+
const exported = key?.type === "Identifier" ? key.name : key?.type === "StringLiteral" ? String(key.value) : void 0;
|
|
3463
|
+
const local = val?.type === "Identifier" ? val.name : exported;
|
|
3464
|
+
if (exported && local) bindExternal(local, modSrc, exported);
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
if (modSrc && id.type === "Identifier" && id.name) {
|
|
3468
|
+
externalMember.set(`${id.name}.__module__`, { source: modSrc, fnName: "" });
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
const spec = requireSpecOf(init);
|
|
3472
|
+
if (spec) {
|
|
3473
|
+
const modSrc = loadSpec(spec);
|
|
3474
|
+
if (id.type === "ObjectPattern" && modSrc) {
|
|
3475
|
+
for (const p of id.properties ?? []) {
|
|
3476
|
+
const key = p.key;
|
|
3477
|
+
const val = p.value;
|
|
3478
|
+
const exported = key?.type === "Identifier" ? key.name : key?.type === "StringLiteral" ? String(key.value) : void 0;
|
|
3479
|
+
const local = val?.type === "Identifier" ? val.name : exported;
|
|
3480
|
+
if (exported && local) {
|
|
3481
|
+
bindExternal(local, modSrc, exported);
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
if (id.type === "Identifier" && id.name && modSrc) {
|
|
3486
|
+
const exportName = requireExportName(init);
|
|
3487
|
+
if (exportName) {
|
|
3488
|
+
bindExternal(id.name, modSrc, exportName);
|
|
3489
|
+
} else {
|
|
3490
|
+
externalMember.set(`${id.name}.__module__`, { source: modSrc, fnName: "" });
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
if (id.type !== "Identifier" || !id.name) continue;
|
|
3495
|
+
if (init.type === "Identifier" && typeof init.name === "string" && knownSet.has(init.name)) {
|
|
3496
|
+
aliasToFn.set(id.name, init.name);
|
|
3497
|
+
}
|
|
3498
|
+
if (init.type === "ObjectExpression") {
|
|
3499
|
+
for (const p of init.properties ?? []) {
|
|
3500
|
+
if (p.type !== "ObjectProperty") continue;
|
|
3501
|
+
const key = p.key;
|
|
3502
|
+
const value = p.value;
|
|
3503
|
+
if (value?.type === "Identifier" && value.name && knownSet.has(value.name)) {
|
|
3504
|
+
const propKey = key?.type === "Identifier" ? key.name : key?.type === "StringLiteral" ? String(key.value) : void 0;
|
|
3505
|
+
if (propKey) memberToFn.set(`${id.name}.${propKey}`, value.name);
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
};
|
|
3512
|
+
walkStatements(file, visit);
|
|
3513
|
+
return { aliasToFn, memberToFn, externalFn, externalMember };
|
|
3514
|
+
}
|
|
3515
|
+
function resolveCalleeFn(callee, resolve, knownFns) {
|
|
3516
|
+
if (callee.type === "Identifier" && typeof callee.name === "string") {
|
|
3517
|
+
const ext = resolve.externalFn.get(callee.name);
|
|
3518
|
+
if (ext) return { external: ext };
|
|
3519
|
+
const aliased = resolve.aliasToFn.get(callee.name);
|
|
3520
|
+
if (aliased) return aliased;
|
|
3521
|
+
if (knownFns.has(callee.name)) return callee.name;
|
|
3522
|
+
return void 0;
|
|
3523
|
+
}
|
|
3524
|
+
if (callee.type === "MemberExpression") {
|
|
3525
|
+
const obj2 = callee.object;
|
|
3526
|
+
const prop = callee.property;
|
|
3527
|
+
if (!callee.computed && obj2?.type === "Identifier" && obj2.name && prop?.type === "Identifier" && prop.name) {
|
|
3528
|
+
const key = `${obj2.name}.${prop.name}`;
|
|
3529
|
+
const extM = resolve.externalMember.get(key);
|
|
3530
|
+
if (extM) return { external: extM };
|
|
3531
|
+
const mod2 = resolve.externalMember.get(`${obj2.name}.__module__`);
|
|
3532
|
+
if (mod2?.source) {
|
|
3533
|
+
return { external: { source: mod2.source, fnName: prop.name } };
|
|
3534
|
+
}
|
|
3535
|
+
const local = resolve.memberToFn.get(key);
|
|
3536
|
+
if (local) return local;
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
return void 0;
|
|
3540
|
+
}
|
|
3541
|
+
function collectForwarders(source, knownFns, resolve, fileAst) {
|
|
3542
|
+
const forwards = /* @__PURE__ */ new Map();
|
|
3543
|
+
const file = fileAst ?? parseSource(source);
|
|
3544
|
+
const tryFn = (name, params, body) => {
|
|
3545
|
+
if (!name || !params.length) return;
|
|
3546
|
+
const paramNames = params.map((p) => p?.type === "Identifier" ? p.name : void 0);
|
|
3547
|
+
if (paramNames.some((n) => !n)) return;
|
|
3548
|
+
let call;
|
|
3549
|
+
if (body?.type === "BlockStatement") {
|
|
3550
|
+
const stmts = body.body ?? [];
|
|
3551
|
+
if (stmts.length !== 1 || stmts[0].type !== "ReturnStatement") return;
|
|
3552
|
+
const arg = stmts[0].argument;
|
|
3553
|
+
if (arg?.type !== "CallExpression") return;
|
|
3554
|
+
call = arg;
|
|
3555
|
+
} else if (body?.type === "CallExpression") {
|
|
3556
|
+
call = body;
|
|
3557
|
+
}
|
|
3558
|
+
if (!call) return;
|
|
3559
|
+
const resolved = resolveCalleeFn(call.callee, resolve, knownFns);
|
|
3560
|
+
if (!resolved || typeof resolved !== "string") return;
|
|
3561
|
+
if (!knownFns.has(resolved) || resolved === name) return;
|
|
3562
|
+
const args = call.arguments ?? [];
|
|
3563
|
+
if (args.length === 0) return;
|
|
3564
|
+
const map = [];
|
|
3565
|
+
for (const a of args) {
|
|
3566
|
+
if (a.type !== "Identifier" || typeof a.name !== "string") return;
|
|
3567
|
+
const idx = paramNames.indexOf(a.name);
|
|
3568
|
+
if (idx < 0) return;
|
|
3569
|
+
map.push(idx);
|
|
3570
|
+
}
|
|
3571
|
+
forwards.set(name, { target: resolved, map });
|
|
3572
|
+
};
|
|
3573
|
+
walkStatements(file, (obj2) => {
|
|
3574
|
+
if (obj2.type === "FunctionDeclaration") {
|
|
3575
|
+
const id = obj2.id;
|
|
3576
|
+
tryFn(
|
|
3577
|
+
id?.name ?? "",
|
|
3578
|
+
obj2.params ?? [],
|
|
3579
|
+
obj2.body
|
|
3580
|
+
);
|
|
3581
|
+
}
|
|
3582
|
+
if (obj2.type === "VariableDeclaration") {
|
|
3583
|
+
for (const d of obj2.declarations ?? []) {
|
|
3584
|
+
const id = d.id;
|
|
3585
|
+
const init = d.init;
|
|
3586
|
+
if (id?.type === "Identifier" && id.name && init && (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression")) {
|
|
3587
|
+
tryFn(
|
|
3588
|
+
id.name,
|
|
3589
|
+
init.params ?? [],
|
|
3590
|
+
init.body
|
|
3591
|
+
);
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
});
|
|
3596
|
+
return forwards;
|
|
3597
|
+
}
|
|
3598
|
+
function scanLiteralCalls(source, knownFns, phi, opts) {
|
|
3599
|
+
const out = [];
|
|
3600
|
+
const file = opts?.file ?? parseSource(source);
|
|
3601
|
+
const knownSet = new Set(knownFns);
|
|
3602
|
+
const resolve = collectCallResolvers(source, knownFns, opts);
|
|
3603
|
+
const forwards = collectForwarders(source, knownSet, resolve, file);
|
|
3604
|
+
const varAbs = opts?.varAbs ?? /* @__PURE__ */ new Map();
|
|
3605
|
+
const flattenPred = (p) => p.op === "and" ? p.args.flatMap(flattenPred) : p.op === "true" ? [] : [p];
|
|
3606
|
+
const checkReqs = (displayName, reqs, paramNames, absArgs, argIndexOf, loc) => {
|
|
3607
|
+
for (const [idx, pred] of reqs) {
|
|
3608
|
+
const argIdx = argIndexOf(idx);
|
|
3609
|
+
if (argIdx === void 0) continue;
|
|
3610
|
+
const arg = absArgs[argIdx];
|
|
3611
|
+
if (!arg) continue;
|
|
3612
|
+
const lv = litValue(arg);
|
|
3613
|
+
const isStr = arg.shape.k === "prim" && arg.shape.type === "string";
|
|
3614
|
+
const strLen = typeof lv === "string" ? lv.length : void 0;
|
|
3615
|
+
const paramName = paramNames[idx] ?? `arg${idx}`;
|
|
3616
|
+
for (const p of flattenPred(pred)) {
|
|
3617
|
+
if (p.op === "typeof") {
|
|
3618
|
+
if (p.t.op !== "var") continue;
|
|
3619
|
+
const expected = p.type;
|
|
3620
|
+
const actualPrim = arg.shape.k === "prim" ? arg.shape.type : arg.shape.k === "obj" || arg.shape.k === "arr" || arg.shape.k === "tuple" ? "object" : arg.shape.k === "fn" ? "function" : void 0;
|
|
3621
|
+
if (actualPrim !== void 0 && actualPrim !== expected) {
|
|
3622
|
+
out.push({
|
|
3623
|
+
severity: "error",
|
|
3624
|
+
code: "nudo:constraint-violated",
|
|
3625
|
+
message: `${displayName}[${paramName}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
|
|
3626
|
+
actual: formatAbs(arg),
|
|
3627
|
+
expected: `typeof ${paramName} = "${expected}"`,
|
|
3628
|
+
suggestion: `\u6539\u7528 ${expected} \u7C7B\u578B\u7684\u503C\uFF0C\u6216\u653E\u5BBD ${paramName} \u7684 refine`,
|
|
3629
|
+
fn: displayName,
|
|
3630
|
+
line: loc?.start.line,
|
|
3631
|
+
column: loc?.start.column
|
|
3632
|
+
});
|
|
3633
|
+
}
|
|
3634
|
+
continue;
|
|
3635
|
+
}
|
|
3636
|
+
if (lv === void 0) continue;
|
|
3637
|
+
if ((p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le") && p.b.op === "lit" && typeof p.b.value === "number") {
|
|
3638
|
+
const n = p.b.value;
|
|
3639
|
+
const opSym = { gt: ">", ge: "\u2265", lt: "<", le: "\u2264" }[p.op];
|
|
3640
|
+
if (p.a.op === "app" && p.a.fn === "length" && strLen !== void 0) {
|
|
3641
|
+
let ok2 = true;
|
|
3642
|
+
if (p.op === "gt") ok2 = strLen > n;
|
|
3643
|
+
if (p.op === "ge") ok2 = strLen >= n;
|
|
3644
|
+
if (p.op === "lt") ok2 = strLen < n;
|
|
3645
|
+
if (p.op === "le") ok2 = strLen <= n;
|
|
3646
|
+
if (!ok2) {
|
|
3647
|
+
const paramName2 = paramNames[idx] ?? `arg${idx}`;
|
|
3648
|
+
out.push({
|
|
3649
|
+
severity: "error",
|
|
3650
|
+
code: "nudo:constraint-violated",
|
|
3651
|
+
message: `${displayName}[${paramName2}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
|
|
3652
|
+
actual: formatAbs(arg),
|
|
3653
|
+
expected: `length(${paramName2}) ${opSym} ${n}`,
|
|
3654
|
+
suggestion: `\u6539\u7528\u6EE1\u8DB3\u957F\u5EA6 ${opSym} ${n} \u7684\u503C\uFF0C\u6216\u653E\u5BBD ${paramName2} \u7684\u524D\u7F6E`,
|
|
3655
|
+
fn: displayName,
|
|
3656
|
+
line: loc?.start.line,
|
|
3657
|
+
column: loc?.start.column
|
|
3658
|
+
});
|
|
3659
|
+
}
|
|
3660
|
+
continue;
|
|
3661
|
+
}
|
|
3662
|
+
if (isStr || typeof lv !== "number") continue;
|
|
3663
|
+
let ok = true;
|
|
3664
|
+
if (p.op === "gt") ok = lv > n;
|
|
3665
|
+
if (p.op === "ge") ok = lv >= n;
|
|
3666
|
+
if (p.op === "lt") ok = lv < n;
|
|
3667
|
+
if (p.op === "le") ok = lv <= n;
|
|
3668
|
+
if (!ok) {
|
|
3669
|
+
const paramName2 = paramNames[idx] ?? `arg${idx}`;
|
|
3670
|
+
out.push({
|
|
3671
|
+
severity: "error",
|
|
3672
|
+
code: "nudo:constraint-violated",
|
|
3673
|
+
message: `${displayName}[${paramName2}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
|
|
3674
|
+
actual: formatAbs(arg),
|
|
3675
|
+
expected: predToString(p),
|
|
3676
|
+
suggestion: `\u6539\u7528\u6EE1\u8DB3 ${predToString(p)} \u7684\u503C\uFF0C\u6216\u653E\u5BBD ${paramName2} \u7684\u524D\u7F6E`,
|
|
3677
|
+
fn: displayName,
|
|
3678
|
+
line: loc?.start.line,
|
|
3679
|
+
column: loc?.start.column
|
|
3680
|
+
});
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
};
|
|
3686
|
+
const checkShapeAgainstAbs = (displayName, paramName, constraint, absArg, path, loc) => {
|
|
3687
|
+
if (!constraint.fields) return;
|
|
3688
|
+
if (absArg.shape.k === "unknown" && !absArg.term) return;
|
|
3689
|
+
const slots = absArg.shape.k === "obj" ? absArg.shape.slots : void 0;
|
|
3690
|
+
if (!slots) {
|
|
3691
|
+
if (absArg.shape.k !== "unknown" && absArg.shape.k !== "never") {
|
|
3692
|
+
out.push({
|
|
3693
|
+
severity: "error",
|
|
3694
|
+
code: "nudo:constraint-violated",
|
|
3695
|
+
message: `${displayName}[${paramName}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
|
|
3696
|
+
actual: formatAbs(absArg),
|
|
3697
|
+
expected: `object shape at ${path}`,
|
|
3698
|
+
suggestion: `\u6539\u7528\u6EE1\u8DB3 ${path} \u5F62\u72B6\u7EA6\u675F\u7684 object`,
|
|
3699
|
+
fn: displayName,
|
|
3700
|
+
line: loc?.start.line,
|
|
3701
|
+
column: loc?.start.column
|
|
3702
|
+
});
|
|
3703
|
+
}
|
|
3704
|
+
return;
|
|
3705
|
+
}
|
|
3706
|
+
for (const [key, field] of Object.entries(constraint.fields)) {
|
|
3707
|
+
const slot = slots[key];
|
|
3708
|
+
const fieldPath = path === paramName ? `${paramName}.${key}` : `${path}.${key}`;
|
|
3709
|
+
if (!slot) {
|
|
3710
|
+
if (!field.optional && !field.constraint.isOptional) {
|
|
3711
|
+
out.push({
|
|
3712
|
+
severity: "error",
|
|
3713
|
+
code: "nudo:constraint-violated",
|
|
3714
|
+
message: `${displayName}[${paramName}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
|
|
3715
|
+
actual: formatAbs(absArg),
|
|
3716
|
+
expected: `missing field ${fieldPath}`,
|
|
3717
|
+
suggestion: `\u8865\u5168\u5B57\u6BB5 ${fieldPath}`,
|
|
3718
|
+
fn: displayName,
|
|
3719
|
+
line: loc?.start.line,
|
|
3720
|
+
column: loc?.start.column
|
|
3721
|
+
});
|
|
3722
|
+
}
|
|
3723
|
+
continue;
|
|
3724
|
+
}
|
|
3725
|
+
checkFieldConstraint(displayName, paramName, field.constraint, slot.value, fieldPath, loc);
|
|
3726
|
+
}
|
|
3727
|
+
};
|
|
3728
|
+
const checkFieldConstraint = (displayName, paramName, constraint, fieldAbs, fieldPath, loc) => {
|
|
3729
|
+
if (fieldAbs.shape.k === "unknown" && !fieldAbs.term) return;
|
|
3730
|
+
if (constraint.prim && fieldAbs.shape.k === "prim") {
|
|
3731
|
+
const actualPrim = fieldAbs.shape.type;
|
|
3732
|
+
if (actualPrim !== constraint.prim) {
|
|
3733
|
+
out.push({
|
|
3734
|
+
severity: "error",
|
|
3735
|
+
code: "nudo:constraint-violated",
|
|
3736
|
+
message: `${displayName}[${paramName}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
|
|
3737
|
+
actual: formatAbs(fieldAbs),
|
|
3738
|
+
expected: `typeof ${fieldPath} = "${constraint.prim}"`,
|
|
3739
|
+
suggestion: `\u628A ${fieldPath} \u6539\u6210 ${constraint.prim}`,
|
|
3740
|
+
fn: displayName,
|
|
3741
|
+
line: loc?.start.line,
|
|
3742
|
+
column: loc?.start.column
|
|
3743
|
+
});
|
|
3744
|
+
return;
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
if (constraint.int) {
|
|
3748
|
+
const iv = litValue(fieldAbs);
|
|
3749
|
+
if (typeof iv === "number" && !Number.isInteger(iv)) {
|
|
3750
|
+
out.push({
|
|
3751
|
+
severity: "error",
|
|
3752
|
+
code: "nudo:constraint-violated",
|
|
3753
|
+
message: `${displayName}[${paramName}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
|
|
3754
|
+
actual: formatAbs(fieldAbs),
|
|
3755
|
+
expected: `${fieldPath} is int`,
|
|
3756
|
+
suggestion: `\u628A ${fieldPath} \u6539\u6210\u6574\u6570`,
|
|
3757
|
+
fn: displayName,
|
|
3758
|
+
line: loc?.start.line,
|
|
3759
|
+
column: loc?.start.column
|
|
3760
|
+
});
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
if (constraint.fields) {
|
|
3764
|
+
checkShapeAgainstAbs(displayName, paramName, constraint, fieldAbs, fieldPath, loc);
|
|
3765
|
+
}
|
|
3766
|
+
if (constraint.element) {
|
|
3767
|
+
if (fieldAbs.shape.k === "arr") {
|
|
3768
|
+
checkFieldConstraint(
|
|
3769
|
+
displayName,
|
|
3770
|
+
paramName,
|
|
3771
|
+
constraint.element,
|
|
3772
|
+
fieldAbs.shape.element,
|
|
3773
|
+
`${fieldPath}[]`,
|
|
3774
|
+
loc
|
|
3775
|
+
);
|
|
3776
|
+
} else if (fieldAbs.shape.k === "tuple") {
|
|
3777
|
+
const els = fieldAbs.shape.elements;
|
|
3778
|
+
els.forEach((el, i) => {
|
|
3779
|
+
checkFieldConstraint(
|
|
3780
|
+
displayName,
|
|
3781
|
+
paramName,
|
|
3782
|
+
constraint.element,
|
|
3783
|
+
el,
|
|
3784
|
+
`${fieldPath}[${i}]`,
|
|
3785
|
+
loc
|
|
3786
|
+
);
|
|
3787
|
+
});
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
const lv = litValue(fieldAbs);
|
|
3791
|
+
const sv = typeof lv === "string" ? lv.length : void 0;
|
|
3792
|
+
const isStr = fieldAbs.shape.k === "prim" && fieldAbs.shape.type === "string";
|
|
3793
|
+
for (const p of constraint.preds) {
|
|
3794
|
+
const flat = p.op === "and" ? p.args : [p];
|
|
3795
|
+
for (const atom of flat) {
|
|
3796
|
+
if ((atom.op === "gt" || atom.op === "ge" || atom.op === "lt" || atom.op === "le") && atom.b.op === "lit" && typeof atom.b.value === "number") {
|
|
3797
|
+
const n = atom.b.value;
|
|
3798
|
+
const opSym = { gt: ">", ge: "\u2265", lt: "<", le: "\u2264" }[atom.op];
|
|
3799
|
+
if (atom.a.op === "app" && atom.a.fn === "length") {
|
|
3800
|
+
if (sv === void 0) continue;
|
|
3801
|
+
let ok2 = true;
|
|
3802
|
+
if (atom.op === "gt") ok2 = sv > n;
|
|
3803
|
+
if (atom.op === "ge") ok2 = sv >= n;
|
|
3804
|
+
if (atom.op === "lt") ok2 = sv < n;
|
|
3805
|
+
if (atom.op === "le") ok2 = sv <= n;
|
|
3806
|
+
if (!ok2) {
|
|
3807
|
+
out.push({
|
|
3808
|
+
severity: "error",
|
|
3809
|
+
code: "nudo:constraint-violated",
|
|
3810
|
+
message: `${displayName}[${paramName}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
|
|
3811
|
+
actual: formatAbs(fieldAbs),
|
|
3812
|
+
expected: `length(${fieldPath}) ${opSym} ${n}`,
|
|
3813
|
+
suggestion: `\u6539\u7528\u6EE1\u8DB3\u957F\u5EA6 ${opSym} ${n} \u7684 ${fieldPath}`,
|
|
3814
|
+
fn: displayName,
|
|
3815
|
+
line: loc?.start.line,
|
|
3816
|
+
column: loc?.start.column
|
|
3817
|
+
});
|
|
3818
|
+
}
|
|
3819
|
+
continue;
|
|
3820
|
+
}
|
|
3821
|
+
if (lv === void 0 || typeof lv !== "number" || isStr) continue;
|
|
3822
|
+
let ok = true;
|
|
3823
|
+
if (atom.op === "gt") ok = lv > n;
|
|
3824
|
+
if (atom.op === "ge") ok = lv >= n;
|
|
3825
|
+
if (atom.op === "lt") ok = lv < n;
|
|
3826
|
+
if (atom.op === "le") ok = lv <= n;
|
|
3827
|
+
if (!ok) {
|
|
3828
|
+
out.push({
|
|
3829
|
+
severity: "error",
|
|
3830
|
+
code: "nudo:constraint-violated",
|
|
3831
|
+
message: `${displayName}[${paramName}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
|
|
3832
|
+
actual: formatAbs(fieldAbs),
|
|
3833
|
+
expected: `${fieldPath} ${opSym} ${n}`,
|
|
3834
|
+
suggestion: `\u6539\u7528\u6EE1\u8DB3 ${fieldPath} ${opSym} ${n} \u7684\u503C\uFF0C\u6216\u653E\u5BBD ${paramName} \u7684\u524D\u7F6E`,
|
|
3835
|
+
fn: displayName,
|
|
3836
|
+
line: loc?.start.line,
|
|
3837
|
+
column: loc?.start.column
|
|
3838
|
+
});
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
}
|
|
3843
|
+
};
|
|
3844
|
+
const checkShapeReqs = (displayName, reqs, paramNames, absArgs, argIndexOf, loc) => {
|
|
3845
|
+
for (const [idx, entry] of reqs) {
|
|
3846
|
+
const c = entry.constraint;
|
|
3847
|
+
if (!c.fields && !c.element && !c.int && !c.prim) continue;
|
|
3848
|
+
const argIdx = argIndexOf(idx);
|
|
3849
|
+
if (argIdx === void 0) continue;
|
|
3850
|
+
const arg = absArgs[argIdx];
|
|
3851
|
+
if (!arg) continue;
|
|
3852
|
+
const paramName = entry.param || paramNames[idx] || `arg${idx}`;
|
|
3853
|
+
if (c.fields) {
|
|
3854
|
+
checkShapeAgainstAbs(displayName, paramName, c, arg, paramName, loc);
|
|
3855
|
+
}
|
|
3856
|
+
if (c.element) {
|
|
3857
|
+
if (arg.shape.k === "arr") {
|
|
3858
|
+
checkFieldConstraint(
|
|
3859
|
+
displayName,
|
|
3860
|
+
paramName,
|
|
3861
|
+
c.element,
|
|
3862
|
+
arg.shape.element,
|
|
3863
|
+
`${paramName}[]`,
|
|
3864
|
+
loc
|
|
3865
|
+
);
|
|
3866
|
+
} else if (arg.shape.k === "tuple") {
|
|
3867
|
+
const els = arg.shape.elements;
|
|
3868
|
+
els.forEach((el, i) => {
|
|
3869
|
+
checkFieldConstraint(
|
|
3870
|
+
displayName,
|
|
3871
|
+
paramName,
|
|
3872
|
+
c.element,
|
|
3873
|
+
el,
|
|
3874
|
+
`${paramName}[${i}]`,
|
|
3875
|
+
loc
|
|
3876
|
+
);
|
|
3877
|
+
});
|
|
3878
|
+
} else if (arg.shape.k !== "unknown" && arg.shape.k !== "any") {
|
|
3879
|
+
out.push({
|
|
3880
|
+
severity: "error",
|
|
3881
|
+
code: "nudo:constraint-violated",
|
|
3882
|
+
message: `${displayName}[${paramName}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
|
|
3883
|
+
actual: formatAbs(arg),
|
|
3884
|
+
expected: `array at ${paramName}`,
|
|
3885
|
+
suggestion: `\u6539\u7528\u6EE1\u8DB3 array \u7EA6\u675F\u7684\u503C`,
|
|
3886
|
+
fn: displayName,
|
|
3887
|
+
line: loc?.start.line,
|
|
3888
|
+
column: loc?.start.column
|
|
3889
|
+
});
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
if (c.int) {
|
|
3893
|
+
const iv = litValue(arg);
|
|
3894
|
+
if (typeof iv === "number" && !Number.isInteger(iv)) {
|
|
3895
|
+
out.push({
|
|
3896
|
+
severity: "error",
|
|
3897
|
+
code: "nudo:constraint-violated",
|
|
3898
|
+
message: `${displayName}[${paramName}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
|
|
3899
|
+
actual: formatAbs(arg),
|
|
3900
|
+
expected: `${paramName} is int`,
|
|
3901
|
+
suggestion: `\u6539\u7528\u6574\u6570`,
|
|
3902
|
+
fn: displayName,
|
|
3903
|
+
line: loc?.start.line,
|
|
3904
|
+
column: loc?.start.column
|
|
3905
|
+
});
|
|
3906
|
+
}
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
};
|
|
3910
|
+
const parseCallArgs = (args) => {
|
|
3911
|
+
const absArgs = [];
|
|
3912
|
+
let hasInfo = false;
|
|
3913
|
+
for (const a of args ?? []) {
|
|
3914
|
+
if (a.type === "NumericLiteral" && typeof a.value === "number") {
|
|
3915
|
+
absArgs.push(numLit(a.value));
|
|
3916
|
+
hasInfo = true;
|
|
3917
|
+
} else if (a.type === "UnaryExpression" && a.operator === "-" && a.argument?.type === "NumericLiteral") {
|
|
3918
|
+
const num2 = a.argument;
|
|
3919
|
+
absArgs.push(numLit(-num2.value));
|
|
3920
|
+
hasInfo = true;
|
|
3921
|
+
} else if (a.type === "ObjectExpression") {
|
|
3922
|
+
const abs2 = evalArgAbs(a, (n) => varAbs.get(n));
|
|
3923
|
+
absArgs.push(abs2 ?? absUnknown());
|
|
3924
|
+
if (abs2 && abs2.shape.k === "obj") hasInfo = true;
|
|
3925
|
+
} else if (a.type === "ArrayExpression") {
|
|
3926
|
+
const abs2 = evalArgAbs(a, (n) => varAbs.get(n));
|
|
3927
|
+
absArgs.push(abs2 ?? absUnknown());
|
|
3928
|
+
if (abs2 && (abs2.shape.k === "arr" || abs2.shape.k === "tuple")) hasInfo = true;
|
|
3929
|
+
} else if (a.type === "StringLiteral" && typeof a.value === "string") {
|
|
3930
|
+
absArgs.push({
|
|
3931
|
+
shape: { k: "prim", type: "string" },
|
|
3932
|
+
term: { op: "lit", value: a.value },
|
|
3933
|
+
conf: "exact"
|
|
3934
|
+
});
|
|
3935
|
+
hasInfo = true;
|
|
3936
|
+
} else if (a.type === "Identifier" && typeof a.name === "string") {
|
|
3937
|
+
const abs2 = varAbs.get(a.name);
|
|
3938
|
+
absArgs.push(abs2 ?? absUnknown());
|
|
3939
|
+
if (abs2 && abs2.shape.k !== "unknown") hasInfo = true;
|
|
3940
|
+
} else {
|
|
3941
|
+
absArgs.push(absUnknown());
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
return { absArgs, hasInfo };
|
|
3945
|
+
};
|
|
3946
|
+
const checkOneCall = (fnName, args, loc) => {
|
|
3947
|
+
if (!knownFns.includes(fnName)) return;
|
|
3948
|
+
checkArgStructures(fnName, source, args, loc);
|
|
3949
|
+
const { absArgs, hasInfo } = parseCallArgs(args);
|
|
3950
|
+
if (!hasInfo || absArgs.length === 0) return;
|
|
3951
|
+
const g = generalizeFromAst(fnName, source, {
|
|
3952
|
+
file,
|
|
3953
|
+
refine: {
|
|
3954
|
+
loadModule: opts?.loadModule,
|
|
3955
|
+
fromFile: opts?.fromFile ?? ""
|
|
3956
|
+
}
|
|
3957
|
+
});
|
|
3958
|
+
const paramNames = g?.params ?? [];
|
|
3959
|
+
const optsR = {
|
|
3960
|
+
loadModule: opts?.loadModule,
|
|
3961
|
+
fromFile: opts?.fromFile ?? ""
|
|
3962
|
+
};
|
|
3963
|
+
const ownFull = refineToIndexedFull(source, fnName, paramNames, optsR);
|
|
3964
|
+
checkShapeReqs(fnName, ownFull, paramNames, absArgs, (i) => i, loc);
|
|
3965
|
+
checkReqs(
|
|
3966
|
+
fnName,
|
|
3967
|
+
ownFull.map(([i, e]) => [i, e.pred]),
|
|
3968
|
+
paramNames,
|
|
3969
|
+
absArgs,
|
|
3970
|
+
(i) => i,
|
|
3971
|
+
loc
|
|
3972
|
+
);
|
|
3973
|
+
const fwd = forwards.get(fnName);
|
|
3974
|
+
if (fwd) {
|
|
3975
|
+
const tg = generalizeFromAst(fwd.target, source, file ? { file } : {});
|
|
3976
|
+
const tParams = tg?.params ?? [];
|
|
3977
|
+
const tFull = refineToIndexedFull(source, fwd.target, tParams, optsR);
|
|
3978
|
+
if (tFull.length > 0) {
|
|
3979
|
+
const wrapperArgOfTarget = /* @__PURE__ */ new Map();
|
|
3980
|
+
fwd.map.forEach((wrapperIdx, targetIdx) => {
|
|
3981
|
+
wrapperArgOfTarget.set(targetIdx, wrapperIdx);
|
|
3982
|
+
});
|
|
3983
|
+
const mapArg = (targetIdx) => wrapperArgOfTarget.get(targetIdx);
|
|
3984
|
+
checkShapeReqs(`${fnName}\u2192${fwd.target}`, tFull, tParams, absArgs, mapArg, loc);
|
|
3985
|
+
checkReqs(
|
|
3986
|
+
`${fnName}\u2192${fwd.target}`,
|
|
3987
|
+
tFull.map(([i, e]) => [i, e.pred]),
|
|
3988
|
+
tParams,
|
|
3989
|
+
absArgs,
|
|
3990
|
+
mapArg,
|
|
3991
|
+
loc
|
|
3992
|
+
);
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
};
|
|
3996
|
+
const checkExternalCall = (ext, args, displayName, loc) => {
|
|
3997
|
+
checkArgStructures(ext.fnName, ext.source, args, loc, displayName);
|
|
3998
|
+
const { absArgs, hasInfo } = parseCallArgs(args);
|
|
3999
|
+
if (!hasInfo || absArgs.length === 0) return;
|
|
4000
|
+
let full = [];
|
|
4001
|
+
let paramNames = [];
|
|
4002
|
+
try {
|
|
4003
|
+
const g = generalizeFromAst(ext.fnName, ext.source);
|
|
4004
|
+
paramNames = g?.params ?? [];
|
|
4005
|
+
full = refineToIndexedFull(ext.source, ext.fnName, paramNames, {
|
|
4006
|
+
loadModule: opts?.loadModule,
|
|
4007
|
+
fromFile: opts?.fromFile ?? ""
|
|
4008
|
+
});
|
|
4009
|
+
} catch {
|
|
4010
|
+
return;
|
|
4011
|
+
}
|
|
4012
|
+
checkShapeReqs(displayName, full, paramNames, absArgs, (i) => i, loc);
|
|
4013
|
+
checkReqs(
|
|
4014
|
+
displayName,
|
|
4015
|
+
full.map(([i, e]) => [i, e.pred]),
|
|
4016
|
+
paramNames,
|
|
4017
|
+
absArgs,
|
|
4018
|
+
(i) => i,
|
|
4019
|
+
loc
|
|
4020
|
+
);
|
|
4021
|
+
};
|
|
4022
|
+
const checkHofFnRelArgs = (g, args, loc, displayName, evalArg) => {
|
|
4023
|
+
const fnRels = g.fnRels;
|
|
4024
|
+
if (!fnRels) return;
|
|
4025
|
+
for (let i = 0; i < g.params.length; i++) {
|
|
4026
|
+
const pname = g.params[i];
|
|
4027
|
+
const rel = fnRels.get(pname);
|
|
4028
|
+
if (!rel) continue;
|
|
4029
|
+
const expected = rel.abs;
|
|
4030
|
+
if (expected.shape.k !== "fn") continue;
|
|
4031
|
+
const argNode = args[i];
|
|
4032
|
+
if (!argNode) continue;
|
|
4033
|
+
const absArg = evalArg(argNode);
|
|
4034
|
+
if (!absArg) continue;
|
|
4035
|
+
if (absArg.shape.k === "any" || absArg.shape.k === "unknown") continue;
|
|
4036
|
+
if (getFnImpl(absArg)) continue;
|
|
4037
|
+
if (absArg.shape.k === "sum") {
|
|
4038
|
+
const okAny = absArg.shape.members.some(
|
|
4039
|
+
(m) => hofFnArgOk(m, expected)
|
|
4040
|
+
);
|
|
4041
|
+
if (okAny) continue;
|
|
4042
|
+
}
|
|
4043
|
+
if (!hofFnArgOk(absArg, expected)) {
|
|
4044
|
+
const isPromote = rel.source === "promote";
|
|
4045
|
+
out.push({
|
|
4046
|
+
severity: isPromote ? "warning" : "error",
|
|
4047
|
+
code: "nudo:arg-structure",
|
|
4048
|
+
message: `${displayName}[${pname}]: \u5B9E\u53C2\u4E0D\u662F\u53EF\u8C03\u7528\u7684 fn`,
|
|
4049
|
+
actual: formatAbs(absArg),
|
|
4050
|
+
expected: formatShape(expected),
|
|
4051
|
+
suggestion: `\u671F\u671B ${formatShape(expected)}`,
|
|
4052
|
+
fn: displayName,
|
|
4053
|
+
line: loc?.start.line,
|
|
4054
|
+
column: loc?.start.column
|
|
4055
|
+
});
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
};
|
|
4059
|
+
const checkArgStructures = (fnName, fnSource, args, loc, displayName) => {
|
|
4060
|
+
let structReqs;
|
|
4061
|
+
let paramNames;
|
|
4062
|
+
let gFn;
|
|
4063
|
+
try {
|
|
4064
|
+
const sameFile = fnSource === source;
|
|
4065
|
+
structReqs = collectParamStructReqs(fnSource, fnName, sameFile ? file : void 0);
|
|
4066
|
+
gFn = generalizeFromAst(
|
|
4067
|
+
fnName,
|
|
4068
|
+
fnSource,
|
|
4069
|
+
sameFile && file ? { file } : {}
|
|
4070
|
+
);
|
|
4071
|
+
paramNames = gFn?.params ?? [];
|
|
4072
|
+
} catch {
|
|
4073
|
+
return;
|
|
4074
|
+
}
|
|
4075
|
+
if (gFn?.fnRels && gFn.fnRels.size > 0) {
|
|
4076
|
+
checkHofFnRelArgs(
|
|
4077
|
+
gFn,
|
|
4078
|
+
args,
|
|
4079
|
+
loc,
|
|
4080
|
+
displayName ?? fnName,
|
|
4081
|
+
(n) => evalArgAbs(n, (x) => varAbs.get(x))
|
|
4082
|
+
);
|
|
4083
|
+
}
|
|
4084
|
+
if (structReqs.size === 0) return;
|
|
4085
|
+
let refinedParams;
|
|
4086
|
+
try {
|
|
4087
|
+
const sameFile = fnSource === source;
|
|
4088
|
+
const reqs = refineToIndexedFull(fnSource, fnName, paramNames, {
|
|
4089
|
+
loadModule: opts?.loadModule,
|
|
4090
|
+
fromFile: opts?.fromFile ?? ""
|
|
4091
|
+
});
|
|
4092
|
+
if (reqs.length > 0) {
|
|
4093
|
+
refinedParams = new Set(reqs.map(([, e]) => e.param));
|
|
4094
|
+
}
|
|
4095
|
+
} catch {
|
|
4096
|
+
}
|
|
4097
|
+
for (let i = 0; i < args.length; i++) {
|
|
4098
|
+
const pname = paramNames[i];
|
|
4099
|
+
if (!pname) continue;
|
|
4100
|
+
if (refinedParams?.has(pname)) continue;
|
|
4101
|
+
const keys = structReqs.get(pname);
|
|
4102
|
+
if (!keys || keys.size === 0) continue;
|
|
4103
|
+
const argNode = args[i];
|
|
4104
|
+
if (!argNode) continue;
|
|
4105
|
+
const absArg = evalArgAbs(argNode, (n) => varAbs.get(n));
|
|
4106
|
+
if (!absArg || absArg.shape.k === "unknown") continue;
|
|
4107
|
+
const isLeny = absArg.shape.k === "arr" || absArg.shape.k === "tuple" || absArg.shape.k === "prim" && absArg.shape.type === "string";
|
|
4108
|
+
const needKeys = isLeny ? [...keys].filter((k) => k !== "length") : [...keys];
|
|
4109
|
+
if (needKeys.length === 0) continue;
|
|
4110
|
+
const slots = {};
|
|
4111
|
+
for (const k of needKeys) slots[k] = { value: absUnknown() };
|
|
4112
|
+
const target = abs({ k: "obj", slots }, void 0, void 0, "exact");
|
|
4113
|
+
const leq = leqAbs(absArg, target);
|
|
4114
|
+
if (!leq.ok) {
|
|
4115
|
+
const name = displayName ?? fnName;
|
|
4116
|
+
out.push({
|
|
4117
|
+
severity: "error",
|
|
4118
|
+
code: "nudo:arg-structure",
|
|
4119
|
+
message: `${name}[${pname}]: \u5B9E\u53C2\u7ED3\u6784 \u22AD \u5F62\u53C2`,
|
|
4120
|
+
actual: formatAbs(absArg),
|
|
4121
|
+
expected: formatAbs(target),
|
|
4122
|
+
suggestion: leq.reason ?? `\u8865\u5168 ${pname} \u4E0A\u88AB\u8BBF\u95EE\u7684\u5B57\u6BB5`,
|
|
4123
|
+
fn: name,
|
|
4124
|
+
line: loc?.start.line,
|
|
4125
|
+
column: loc?.start.column
|
|
4126
|
+
});
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
};
|
|
4130
|
+
const visit = (n) => {
|
|
4131
|
+
if (!n || typeof n !== "object") return;
|
|
4132
|
+
const obj2 = n;
|
|
4133
|
+
if (obj2.type === "CallExpression") {
|
|
4134
|
+
const callee = obj2.callee;
|
|
4135
|
+
const args = obj2.arguments ?? [];
|
|
4136
|
+
const resolved = resolveCalleeFn(callee, resolve, knownSet);
|
|
4137
|
+
if (typeof resolved === "string") {
|
|
4138
|
+
checkOneCall(resolved, args, obj2.loc);
|
|
4139
|
+
} else if (resolved?.external) {
|
|
4140
|
+
const name = resolved.external.fnName || "require()";
|
|
4141
|
+
checkExternalCall(resolved.external, args, name, obj2.loc);
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
for (const key of Object.keys(obj2)) {
|
|
4145
|
+
if (key === "loc" || key === "start" || key === "end") continue;
|
|
4146
|
+
const val = obj2[key];
|
|
4147
|
+
if (Array.isArray(val)) val.forEach(visit);
|
|
4148
|
+
else if (val && typeof val === "object") visit(val);
|
|
4149
|
+
}
|
|
4150
|
+
};
|
|
4151
|
+
visit(file);
|
|
4152
|
+
return out;
|
|
4153
|
+
}
|
|
4154
|
+
|
|
4155
|
+
// src/algebra/check-report.ts
|
|
4156
|
+
function serializeCheckJson(r) {
|
|
4157
|
+
return {
|
|
4158
|
+
version: 1,
|
|
4159
|
+
file: r.file,
|
|
4160
|
+
ok: r.ok,
|
|
4161
|
+
summary: { ...r.summary },
|
|
4162
|
+
signatures: r.signatures.map((s) => ({
|
|
4163
|
+
name: s.name,
|
|
4164
|
+
params: [...s.params],
|
|
4165
|
+
display: s.display,
|
|
4166
|
+
detail: s.detail,
|
|
4167
|
+
conf: s.conf,
|
|
4168
|
+
abs: formatAbs(s.abs)
|
|
4169
|
+
})),
|
|
4170
|
+
issues: r.issues.map((i) => ({
|
|
4171
|
+
severity: i.severity,
|
|
4172
|
+
code: i.code,
|
|
4173
|
+
message: i.message,
|
|
4174
|
+
...i.fn !== void 0 ? { fn: i.fn } : {},
|
|
4175
|
+
...i.line !== void 0 ? { line: i.line } : {},
|
|
4176
|
+
...i.column !== void 0 ? { column: i.column } : {},
|
|
4177
|
+
...i.actual !== void 0 ? { actual: i.actual } : {},
|
|
4178
|
+
...i.expected !== void 0 ? { expected: i.expected } : {},
|
|
4179
|
+
...i.suggestion !== void 0 ? { suggestion: i.suggestion } : {}
|
|
4180
|
+
}))
|
|
4181
|
+
};
|
|
4182
|
+
}
|
|
4183
|
+
function formatCheckReport(r, opts = {}) {
|
|
4184
|
+
const lines = [];
|
|
4185
|
+
lines.push(`nudo check ${r.file}`);
|
|
4186
|
+
lines.push(r.ok ? "OK" : "FAILED");
|
|
4187
|
+
lines.push(
|
|
4188
|
+
` ${r.summary.errors} error \xB7 ${r.summary.warnings} warning \xB7 ${r.summary.infos} info \xB7 ${r.summary.functions} fn`
|
|
4189
|
+
);
|
|
4190
|
+
if (r.signatures.length > 0) {
|
|
4191
|
+
lines.push("");
|
|
4192
|
+
lines.push("signatures");
|
|
4193
|
+
for (const s of r.signatures) {
|
|
4194
|
+
lines.push(` ${s.name}(${s.params.join(", ")}) ${s.display}`);
|
|
4195
|
+
if (opts.verbose) {
|
|
4196
|
+
for (const ln of s.detail.split("\n").slice(1)) {
|
|
4197
|
+
lines.push(` ${ln}`);
|
|
4198
|
+
}
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
if (r.issues.length === 0) {
|
|
4203
|
+
lines.push("");
|
|
4204
|
+
lines.push("(no issues)");
|
|
4205
|
+
} else {
|
|
4206
|
+
lines.push("");
|
|
4207
|
+
lines.push("issues");
|
|
4208
|
+
for (const i of r.issues) {
|
|
4209
|
+
const loc = i.line != null ? `L${i.line}` : "";
|
|
4210
|
+
const head = [i.severity.toUpperCase(), loc, i.fn].filter(Boolean).join(" ");
|
|
4211
|
+
lines.push(` [${head}] ${i.message} (${i.code})`);
|
|
4212
|
+
if (i.actual) lines.push(` actual: ${i.actual}`);
|
|
4213
|
+
if (i.expected) lines.push(` expected: ${i.expected}`);
|
|
4214
|
+
if (i.suggestion) lines.push(` \u2192 ${i.suggestion}`);
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
return lines.join("\n");
|
|
4218
|
+
}
|
|
4219
|
+
|
|
4220
|
+
// src/algebra/inlay.ts
|
|
4221
|
+
function listFunctions(source) {
|
|
4222
|
+
const file = parseSource(source);
|
|
4223
|
+
const out = [];
|
|
4224
|
+
const visitDecl = (decl) => {
|
|
4225
|
+
if (decl.type === "FunctionDeclaration" && decl.id) {
|
|
4226
|
+
const fd = decl;
|
|
4227
|
+
out.push({ name: fd.id.name, node: fd });
|
|
4228
|
+
}
|
|
4229
|
+
if (decl.type === "VariableDeclaration") {
|
|
4230
|
+
for (const d of decl.declarations ?? []) {
|
|
4231
|
+
if (d.id?.type === "Identifier" && d.init && (d.init.type === "ArrowFunctionExpression" || d.init.type === "FunctionExpression")) {
|
|
4232
|
+
out.push({ name: d.id.name, node: d.init });
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
};
|
|
4237
|
+
for (const stmt of file.program.body) {
|
|
4238
|
+
if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
|
|
4239
|
+
visitDecl(stmt.declaration);
|
|
4240
|
+
} else if (stmt.type === "ExportDefaultDeclaration" && stmt.declaration) {
|
|
4241
|
+
visitDecl(stmt.declaration);
|
|
4242
|
+
} else {
|
|
4243
|
+
visitDecl(stmt);
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
return out;
|
|
4247
|
+
}
|
|
4248
|
+
function paramPreds(source, fnName, paramNames) {
|
|
4249
|
+
const byIndex = /* @__PURE__ */ new Map();
|
|
4250
|
+
const file = parseSource(source);
|
|
4251
|
+
const paramIndex = new Map(paramNames.map((p, i) => [p, i]));
|
|
4252
|
+
const pushCmp = (test, consequent) => {
|
|
4253
|
+
if (test?.type !== "BinaryExpression") return;
|
|
4254
|
+
const left = test.left;
|
|
4255
|
+
const right = test.right;
|
|
4256
|
+
const op = test.operator;
|
|
4257
|
+
if (left?.type !== "Identifier" || !left.name || !paramIndex.has(left.name) || right?.type !== "NumericLiteral" || typeof right.value !== "number") {
|
|
4258
|
+
return;
|
|
4259
|
+
}
|
|
4260
|
+
const isReturnParam = consequent?.type === "ReturnStatement" && consequent.argument?.type === "Identifier" && consequent.argument.name === left.name;
|
|
4261
|
+
if (!isReturnParam) return;
|
|
4262
|
+
const idx = paramIndex.get(left.name);
|
|
4263
|
+
const t = { op: "var", id: left.name };
|
|
4264
|
+
const b = { op: "lit", value: right.value };
|
|
4265
|
+
let pred;
|
|
4266
|
+
if (op === ">") pred = { op: "gt", a: t, b };
|
|
4267
|
+
else if (op === ">=") pred = { op: "ge", a: t, b };
|
|
4268
|
+
else if (op === "<") pred = { op: "lt", a: t, b };
|
|
4269
|
+
else if (op === "<=") pred = { op: "le", a: t, b };
|
|
4270
|
+
if (!pred) return;
|
|
4271
|
+
const list = byIndex.get(idx) ?? [];
|
|
4272
|
+
list.push(pred);
|
|
4273
|
+
byIndex.set(idx, list);
|
|
4274
|
+
};
|
|
4275
|
+
const visit = (n) => {
|
|
4276
|
+
if (!n || typeof n !== "object") return;
|
|
4277
|
+
const obj2 = n;
|
|
4278
|
+
if (obj2.type === "IfStatement") {
|
|
4279
|
+
const test = obj2.test;
|
|
4280
|
+
const consequent = obj2.consequent;
|
|
4281
|
+
pushCmp(test, consequent);
|
|
4282
|
+
if (test?.type === "LogicalExpression" && test.operator === "&&") {
|
|
4283
|
+
pushCmp(test.left, consequent);
|
|
4284
|
+
pushCmp(test.right, consequent);
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
for (const key of Object.keys(obj2)) {
|
|
4288
|
+
if (key === "loc" || key === "start" || key === "end") continue;
|
|
4289
|
+
const val = obj2[key];
|
|
4290
|
+
if (Array.isArray(val)) val.forEach(visit);
|
|
4291
|
+
else if (val && typeof val === "object") visit(val);
|
|
4292
|
+
}
|
|
4293
|
+
};
|
|
4294
|
+
visit(file);
|
|
4295
|
+
const byName = /* @__PURE__ */ new Map();
|
|
4296
|
+
for (const [idx, preds] of byIndex) {
|
|
4297
|
+
const name = paramNames[idx];
|
|
4298
|
+
if (name) byName.set(name, preds);
|
|
4299
|
+
}
|
|
4300
|
+
return byName;
|
|
4301
|
+
}
|
|
4302
|
+
function collectAbsInlays(source, refineOpts) {
|
|
4303
|
+
const inlays = [];
|
|
4304
|
+
for (const { name, node } of listFunctions(source)) {
|
|
4305
|
+
let g;
|
|
4306
|
+
try {
|
|
4307
|
+
g = generalizeFromAst(name, source, refineOpts ? { refine: refineOpts } : {});
|
|
4308
|
+
} catch {
|
|
4309
|
+
continue;
|
|
4310
|
+
}
|
|
4311
|
+
if (!g) continue;
|
|
4312
|
+
const params = g.params;
|
|
4313
|
+
const predsByName = /* @__PURE__ */ new Map();
|
|
4314
|
+
if (g.entryReqs) {
|
|
4315
|
+
for (const r of g.entryReqs) {
|
|
4316
|
+
predsByName.set(r.param, [r.pred]);
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
for (const [k, v2] of paramPreds(source, name, params)) {
|
|
4320
|
+
if (!predsByName.has(k)) predsByName.set(k, v2);
|
|
4321
|
+
}
|
|
4322
|
+
const fnNode = node;
|
|
4323
|
+
const paramList = fnNode.params ?? [];
|
|
4324
|
+
for (let i = 0; i < paramList.length; i++) {
|
|
4325
|
+
const p = paramList[i];
|
|
4326
|
+
const pname = params[i] ?? p?.name;
|
|
4327
|
+
if (!p?.loc || !pname) continue;
|
|
4328
|
+
const preds = predsByName.get(pname);
|
|
4329
|
+
if (preds && preds.length > 0) {
|
|
4330
|
+
const text = preds.map(predToString).join(" \u2227 ");
|
|
4331
|
+
inlays.push({
|
|
4332
|
+
line: p.loc.end.line,
|
|
4333
|
+
character: p.loc.end.column,
|
|
4334
|
+
label: ` where ${text}`,
|
|
4335
|
+
kind: "parameter"
|
|
4336
|
+
});
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
const bodyLoc = fnNode.body?.loc;
|
|
4340
|
+
if (bodyLoc?.start) {
|
|
4341
|
+
const retShape = formatShape(g.symbolic);
|
|
4342
|
+
let label = `: ${retShape}`;
|
|
4343
|
+
if (g.symbolic.term && g.symbolic.term.op !== "lit") {
|
|
4344
|
+
label += ` = ${termToString(g.symbolic.term)}`;
|
|
4345
|
+
}
|
|
4346
|
+
if (g.symbolic.pred && g.symbolic.pred.op !== "true") {
|
|
4347
|
+
label += ` where ${predToString(g.symbolic.pred)}`;
|
|
4348
|
+
}
|
|
4349
|
+
inlays.push({
|
|
4350
|
+
line: bodyLoc.start.line,
|
|
4351
|
+
character: Math.max(0, bodyLoc.start.column),
|
|
4352
|
+
label: `${label} `,
|
|
4353
|
+
kind: "type"
|
|
4354
|
+
});
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
return inlays;
|
|
4358
|
+
}
|
|
4359
|
+
|
|
4360
|
+
// src/algebra/denote.ts
|
|
4361
|
+
function denoteGuard(a, v2) {
|
|
4362
|
+
const lv = litValue(a);
|
|
4363
|
+
if (lv !== void 0 && (!a.pred || a.pred.op === "true")) {
|
|
4364
|
+
return `${v2} === ${JSON.stringify(lv)}`;
|
|
4365
|
+
}
|
|
4366
|
+
const shape2 = denoteShape(a.shape, v2);
|
|
4367
|
+
const pred = denotePred(a, v2);
|
|
4368
|
+
if (shape2 === "true") return pred;
|
|
4369
|
+
if (pred === "true") return shape2;
|
|
4370
|
+
return `(${shape2}) && (${pred})`;
|
|
4371
|
+
}
|
|
4372
|
+
function denoteShape(s, v2) {
|
|
4373
|
+
switch (s.k) {
|
|
4374
|
+
case "never":
|
|
4375
|
+
return "false";
|
|
4376
|
+
case "unknown":
|
|
4377
|
+
case "any":
|
|
4378
|
+
return "true";
|
|
4379
|
+
case "prim":
|
|
4380
|
+
switch (s.type) {
|
|
4381
|
+
case "number":
|
|
4382
|
+
return `typeof ${v2} === "number"`;
|
|
4383
|
+
case "string":
|
|
4384
|
+
return `typeof ${v2} === "string"`;
|
|
4385
|
+
case "boolean":
|
|
4386
|
+
return `typeof ${v2} === "boolean"`;
|
|
4387
|
+
case "bigint":
|
|
4388
|
+
return `typeof ${v2} === "bigint"`;
|
|
4389
|
+
case "symbol":
|
|
4390
|
+
return `typeof ${v2} === "symbol"`;
|
|
4391
|
+
default:
|
|
4392
|
+
return "true";
|
|
4393
|
+
}
|
|
4394
|
+
case "obj": {
|
|
4395
|
+
const checks = [`typeof ${v2} === "object"`, `${v2} !== null`];
|
|
4396
|
+
for (const [key, slot] of Object.entries(s.slots)) {
|
|
4397
|
+
const access = `${v2}.${key}`;
|
|
4398
|
+
const inner = denoteGuard(slot.value, access);
|
|
4399
|
+
if (slot.optional) {
|
|
4400
|
+
if (inner !== "true") checks.push(`(${access} === undefined || ${inner})`);
|
|
4401
|
+
} else {
|
|
4402
|
+
if (inner !== "true") checks.push(inner);
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
return checks.join(" && ");
|
|
4406
|
+
}
|
|
4407
|
+
case "arr":
|
|
4408
|
+
return `Array.isArray(${v2}) && ${v2}.every((item) => ${denoteGuard(s.element, "item")})`;
|
|
4409
|
+
case "tuple": {
|
|
4410
|
+
const checks = [`Array.isArray(${v2})`];
|
|
4411
|
+
const minLen = s.elements.length;
|
|
4412
|
+
checks.push(s.rest ? `${v2}.length >= ${minLen}` : `${v2}.length === ${minLen}`);
|
|
4413
|
+
s.elements.forEach((el, i) => {
|
|
4414
|
+
const inner = denoteGuard(el, `${v2}[${i}]`);
|
|
4415
|
+
if (inner !== "true") checks.push(inner);
|
|
4416
|
+
});
|
|
4417
|
+
if (s.rest) {
|
|
4418
|
+
const rest = denoteGuard(s.rest, "item");
|
|
4419
|
+
if (rest !== "true") {
|
|
4420
|
+
checks.push(`${v2}.slice(${minLen}).every((item) => ${rest})`);
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
return checks.join(" && ");
|
|
4424
|
+
}
|
|
4425
|
+
case "fn":
|
|
4426
|
+
return `typeof ${v2} === "function"`;
|
|
4427
|
+
case "eff":
|
|
4428
|
+
if (s.eff === "promise") return `${v2} instanceof Promise`;
|
|
4429
|
+
return "true";
|
|
4430
|
+
case "brand":
|
|
4431
|
+
return denoteShape(s.shape.shape, v2);
|
|
4432
|
+
case "sum": {
|
|
4433
|
+
if (s.members.length === 0) return "false";
|
|
4434
|
+
const parts = s.members.map((m) => denoteGuard(m, v2));
|
|
4435
|
+
return `(${parts.join(" || ")})`;
|
|
4436
|
+
}
|
|
4437
|
+
}
|
|
4438
|
+
}
|
|
4439
|
+
function denotePred(a, v2) {
|
|
4440
|
+
const p = a.pred;
|
|
4441
|
+
if (!p || p.op === "true") {
|
|
4442
|
+
const lv = litValue(a);
|
|
4443
|
+
if (lv !== void 0) return `${v2} === ${JSON.stringify(lv)}`;
|
|
4444
|
+
return "true";
|
|
4445
|
+
}
|
|
4446
|
+
return predAsJs(p, v2, a);
|
|
4447
|
+
}
|
|
4448
|
+
function predAsJs(p, v2, a) {
|
|
4449
|
+
switch (p.op) {
|
|
4450
|
+
case "true":
|
|
4451
|
+
return "true";
|
|
4452
|
+
case "false":
|
|
4453
|
+
return "false";
|
|
4454
|
+
case "and":
|
|
4455
|
+
return p.args.map((x) => predAsJs(x, v2, a)).join(" && ");
|
|
4456
|
+
case "or":
|
|
4457
|
+
return `(${p.args.map((x) => predAsJs(x, v2, a)).join(" || ")})`;
|
|
4458
|
+
case "not": {
|
|
4459
|
+
const inner = predAsJs(p.arg, v2, a);
|
|
4460
|
+
return inner === "true" ? "false" : `!(${inner})`;
|
|
4461
|
+
}
|
|
4462
|
+
case "gt":
|
|
4463
|
+
case "ge":
|
|
4464
|
+
case "lt":
|
|
4465
|
+
case "le":
|
|
4466
|
+
case "eq":
|
|
4467
|
+
case "ne": {
|
|
4468
|
+
const op = { gt: ">", ge: ">=", lt: "<", le: "<=", eq: "===", ne: "!==" }[p.op];
|
|
4469
|
+
const leftIsValue = termIsValue(p.a, a);
|
|
4470
|
+
const rightLit = litOfTerm(p.b);
|
|
4471
|
+
if (leftIsValue && rightLit !== void 0) {
|
|
4472
|
+
return `${v2} ${op} ${JSON.stringify(rightLit)}`;
|
|
4473
|
+
}
|
|
4474
|
+
const leftLit = litOfTerm(p.a);
|
|
4475
|
+
const rightIsValue = termIsValue(p.b, a);
|
|
4476
|
+
if (rightIsValue && leftLit !== void 0) {
|
|
4477
|
+
return `${JSON.stringify(leftLit)} ${op} ${v2}`;
|
|
4478
|
+
}
|
|
4479
|
+
if (leftLit !== void 0 && rightLit !== void 0) {
|
|
4480
|
+
return `${JSON.stringify(leftLit)} ${op} ${JSON.stringify(rightLit)}`;
|
|
4481
|
+
}
|
|
4482
|
+
return "true";
|
|
4483
|
+
}
|
|
4484
|
+
default:
|
|
4485
|
+
return "true";
|
|
4486
|
+
}
|
|
4487
|
+
}
|
|
4488
|
+
function litOfTerm(t) {
|
|
4489
|
+
return t.op === "lit" ? t.value : void 0;
|
|
4490
|
+
}
|
|
4491
|
+
function termIsValue(t, a) {
|
|
4492
|
+
if (t.op === "var") return true;
|
|
4493
|
+
if (t.op === "lit") {
|
|
4494
|
+
const lv = litValue(a);
|
|
4495
|
+
return lv !== void 0 && Object.is(lv, t.value);
|
|
4496
|
+
}
|
|
4497
|
+
return false;
|
|
4498
|
+
}
|
|
4499
|
+
|
|
4500
|
+
// src/algebra/bridge.ts
|
|
4501
|
+
var confByTv = /* @__PURE__ */ new WeakMap();
|
|
4502
|
+
function setTvConfidence(tv, conf) {
|
|
4503
|
+
if (tv && typeof tv === "object") confByTv.set(tv, conf);
|
|
4504
|
+
}
|
|
4505
|
+
function getTvConfidence(tv) {
|
|
4506
|
+
if (tv && typeof tv === "object") return confByTv.get(tv);
|
|
4507
|
+
return void 0;
|
|
4508
|
+
}
|
|
4509
|
+
function absToTypeValue(a) {
|
|
4510
|
+
let conf = a.conf;
|
|
4511
|
+
let result;
|
|
4512
|
+
switch (a.shape.k) {
|
|
4513
|
+
case "never":
|
|
4514
|
+
result = T.never;
|
|
4515
|
+
break;
|
|
4516
|
+
case "unknown":
|
|
4517
|
+
result = T.unknown;
|
|
4518
|
+
break;
|
|
4519
|
+
case "any":
|
|
4520
|
+
result = T.unknown;
|
|
4521
|
+
break;
|
|
4522
|
+
case "prim": {
|
|
4523
|
+
const lv = litValue(a);
|
|
4524
|
+
if (lv !== void 0) {
|
|
4525
|
+
result = T.literal(lv);
|
|
4526
|
+
break;
|
|
4527
|
+
}
|
|
4528
|
+
if (isTemplateLike(a) && a.shape.k === "prim" && a.shape.type === "string") {
|
|
4529
|
+
const parts = templatePartsOf(a).map(absToTypeValue);
|
|
4530
|
+
result = createTemplate(parts);
|
|
4531
|
+
break;
|
|
4532
|
+
}
|
|
4533
|
+
const prim = primToT(a.shape.type);
|
|
4534
|
+
if (a.pred && a.pred.op !== "true") {
|
|
4535
|
+
const refined = tryEncodeRefined(prim, a);
|
|
4536
|
+
if (refined) {
|
|
4537
|
+
result = refined;
|
|
4538
|
+
} else {
|
|
4539
|
+
result = prim;
|
|
4540
|
+
conf = confJoin(conf, "widened");
|
|
4541
|
+
}
|
|
4542
|
+
} else {
|
|
4543
|
+
result = prim;
|
|
4544
|
+
if (a.term && a.term.op !== "lit") conf = confJoin(conf, "widened");
|
|
4545
|
+
}
|
|
4546
|
+
break;
|
|
4547
|
+
}
|
|
4548
|
+
case "obj": {
|
|
4549
|
+
const props = {};
|
|
4550
|
+
for (const [k, slot] of Object.entries(a.shape.slots)) {
|
|
4551
|
+
let v2 = absToTypeValue(slot.value);
|
|
4552
|
+
if (slot.optional) {
|
|
4553
|
+
v2 = T.union(v2, T.undefined);
|
|
4554
|
+
}
|
|
4555
|
+
props[k] = v2;
|
|
4556
|
+
}
|
|
4557
|
+
result = T.object(props);
|
|
4558
|
+
if (a.shape.open) conf = confJoin(conf, "widened");
|
|
4559
|
+
break;
|
|
4560
|
+
}
|
|
4561
|
+
case "arr":
|
|
4562
|
+
result = T.array(absToTypeValue(a.shape.element));
|
|
4563
|
+
break;
|
|
4564
|
+
case "tuple":
|
|
4565
|
+
result = T.tuple(a.shape.elements.map(absToTypeValue));
|
|
4566
|
+
break;
|
|
4567
|
+
case "fn": {
|
|
4568
|
+
const body = { type: "BlockStatement", body: [], directives: [] };
|
|
4569
|
+
result = T.fn(a.shape.params, body, null);
|
|
4570
|
+
break;
|
|
4571
|
+
}
|
|
4572
|
+
case "brand":
|
|
4573
|
+
result = T.instanceOf(a.shape.name, brandProps(a.shape.shape));
|
|
4574
|
+
break;
|
|
4575
|
+
case "eff":
|
|
4576
|
+
result = T.promise(absToTypeValue(a.shape.inner));
|
|
4577
|
+
break;
|
|
4578
|
+
case "sum":
|
|
4579
|
+
result = T.union(...a.shape.members.map(absToTypeValue));
|
|
4580
|
+
break;
|
|
4581
|
+
default:
|
|
4582
|
+
result = T.unknown;
|
|
4583
|
+
conf = confJoin(conf, "partial");
|
|
4584
|
+
}
|
|
4585
|
+
setTvConfidence(result, conf);
|
|
4586
|
+
return result;
|
|
4587
|
+
}
|
|
4588
|
+
function primToT(type) {
|
|
4589
|
+
switch (type) {
|
|
4590
|
+
case "number":
|
|
4591
|
+
return T.number;
|
|
4592
|
+
case "string":
|
|
4593
|
+
return T.string;
|
|
4594
|
+
case "boolean":
|
|
4595
|
+
return T.boolean;
|
|
4596
|
+
case "bigint":
|
|
4597
|
+
return T.bigint;
|
|
4598
|
+
case "symbol":
|
|
4599
|
+
return T.symbol;
|
|
4600
|
+
default:
|
|
4601
|
+
return T.unknown;
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4604
|
+
function brandProps(shape2) {
|
|
4605
|
+
if (shape2.shape.k === "obj") {
|
|
4606
|
+
const out = {};
|
|
4607
|
+
for (const [k, slot] of Object.entries(shape2.shape.slots)) {
|
|
4608
|
+
out[k] = absToTypeValue(slot.value);
|
|
4609
|
+
}
|
|
4610
|
+
return out;
|
|
4611
|
+
}
|
|
4612
|
+
return {};
|
|
4613
|
+
}
|
|
4614
|
+
function tryEncodeRefined(base, a) {
|
|
4615
|
+
if (base.kind !== "primitive" || base.type !== "number") return void 0;
|
|
4616
|
+
const p = a.pred;
|
|
4617
|
+
if (!p) return void 0;
|
|
4618
|
+
if ((p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le") && p.b.op === "lit" && typeof p.b.value === "number") {
|
|
4619
|
+
const n = p.b.value;
|
|
4620
|
+
const op = p.op;
|
|
4621
|
+
const check = (value) => {
|
|
4622
|
+
if (typeof value !== "number") return false;
|
|
4623
|
+
switch (op) {
|
|
4624
|
+
case "gt":
|
|
4625
|
+
return value > n;
|
|
4626
|
+
case "ge":
|
|
4627
|
+
return value >= n;
|
|
4628
|
+
case "lt":
|
|
4629
|
+
return value < n;
|
|
4630
|
+
case "le":
|
|
4631
|
+
return value <= n;
|
|
4632
|
+
}
|
|
4633
|
+
};
|
|
4634
|
+
const opSym = op === "gt" ? ">" : op === "ge" ? ">=" : op === "lt" ? "<" : "<=";
|
|
4635
|
+
return T.refine(base, {
|
|
4636
|
+
name: `number (${opSym} ${n})`,
|
|
4637
|
+
meta: { op, n, pred: predToString(p) },
|
|
4638
|
+
check
|
|
4639
|
+
});
|
|
4640
|
+
}
|
|
4641
|
+
return void 0;
|
|
4642
|
+
}
|
|
4643
|
+
function typeValueToAbs(tv) {
|
|
4644
|
+
if (!tv) return abs({ k: "unknown" }, void 0, void 0, "partial");
|
|
4645
|
+
const confOr = (fallback) => getTvConfidence(tv) ?? fallback;
|
|
4646
|
+
switch (tv.kind) {
|
|
4647
|
+
case "never":
|
|
4648
|
+
return abs({ k: "never" }, void 0, void 0, confOr("exact"));
|
|
4649
|
+
case "unknown":
|
|
4650
|
+
return abs(
|
|
4651
|
+
{ k: "unknown" },
|
|
4652
|
+
void 0,
|
|
4653
|
+
void 0,
|
|
4654
|
+
confOr("partial")
|
|
4655
|
+
);
|
|
4656
|
+
case "literal":
|
|
4657
|
+
if (typeof tv.value === "bigint") {
|
|
4658
|
+
return abs(
|
|
4659
|
+
{ k: "prim", type: "bigint" },
|
|
4660
|
+
void 0,
|
|
4661
|
+
void 0,
|
|
4662
|
+
confOr("exact")
|
|
4663
|
+
);
|
|
4664
|
+
}
|
|
4665
|
+
return abs(
|
|
4666
|
+
shapeOfLit(tv.value),
|
|
4667
|
+
lit(tv.value),
|
|
4668
|
+
pTrue,
|
|
4669
|
+
confOr("exact")
|
|
4670
|
+
);
|
|
4671
|
+
case "primitive":
|
|
4672
|
+
return abs({ k: "prim", type: tv.type }, void 0, void 0, confOr("exact"));
|
|
4673
|
+
case "refined": {
|
|
4674
|
+
const tplParts = getTemplateParts(tv);
|
|
4675
|
+
if (tplParts) {
|
|
4676
|
+
return createTemplateAbs(tplParts.map(typeValueToAbs));
|
|
4677
|
+
}
|
|
4678
|
+
const base = typeValueToAbs(tv.base);
|
|
4679
|
+
const decoded = tryDecodeRefinedPred(tv, base.term);
|
|
4680
|
+
return abs(
|
|
4681
|
+
base.shape,
|
|
4682
|
+
base.term ?? (decoded ? v("_r") : void 0),
|
|
4683
|
+
decoded ?? base.pred,
|
|
4684
|
+
confOr(confJoin(base.conf, "path"))
|
|
4685
|
+
);
|
|
4686
|
+
}
|
|
4687
|
+
case "object": {
|
|
4688
|
+
const slots = {};
|
|
4689
|
+
for (const [k, v2] of Object.entries(tv.properties)) {
|
|
4690
|
+
slots[k] = { value: typeValueToAbs(v2) };
|
|
4691
|
+
}
|
|
4692
|
+
return abs({ k: "obj", slots }, void 0, void 0, confOr("exact"));
|
|
4693
|
+
}
|
|
4694
|
+
case "array":
|
|
4695
|
+
return abs(
|
|
4696
|
+
{ k: "arr", element: typeValueToAbs(tv.element) },
|
|
4697
|
+
void 0,
|
|
4698
|
+
void 0,
|
|
4699
|
+
confOr("exact")
|
|
4700
|
+
);
|
|
4701
|
+
case "tuple":
|
|
4702
|
+
return abs(
|
|
4703
|
+
{ k: "tuple", elements: tv.elements.map(typeValueToAbs) },
|
|
4704
|
+
void 0,
|
|
4705
|
+
void 0,
|
|
4706
|
+
confOr("exact")
|
|
4707
|
+
);
|
|
4708
|
+
case "function": {
|
|
4709
|
+
const params = tv.params ?? [];
|
|
4710
|
+
return abs({ k: "fn", params }, void 0, void 0, confOr("exact"));
|
|
4711
|
+
}
|
|
4712
|
+
case "promise":
|
|
4713
|
+
return abs(
|
|
4714
|
+
{ k: "eff", eff: "promise", inner: typeValueToAbs(tv.value) },
|
|
4715
|
+
void 0,
|
|
4716
|
+
void 0,
|
|
4717
|
+
confOr("exact")
|
|
4718
|
+
);
|
|
4719
|
+
case "instance": {
|
|
4720
|
+
const slots = {};
|
|
4721
|
+
for (const [k, v2] of Object.entries(tv.properties ?? {})) {
|
|
4722
|
+
slots[k] = { value: typeValueToAbs(v2) };
|
|
4723
|
+
}
|
|
4724
|
+
const inner = abs({ k: "obj", slots }, void 0, void 0, confOr("exact"));
|
|
4725
|
+
return abs(
|
|
4726
|
+
{ k: "brand", name: tv.className, shape: inner },
|
|
4727
|
+
void 0,
|
|
4728
|
+
void 0,
|
|
4729
|
+
confOr("exact")
|
|
4730
|
+
);
|
|
4731
|
+
}
|
|
4732
|
+
case "union":
|
|
4733
|
+
return abs(
|
|
4734
|
+
{ k: "sum", members: tv.members.map(typeValueToAbs) },
|
|
4735
|
+
void 0,
|
|
4736
|
+
void 0,
|
|
4737
|
+
confOr("path")
|
|
4738
|
+
);
|
|
4739
|
+
default:
|
|
4740
|
+
return abs({ k: "unknown" }, void 0, void 0, confOr("partial"));
|
|
4741
|
+
}
|
|
4742
|
+
}
|
|
4743
|
+
function tryDecodeRefinedPred(tv, term) {
|
|
4744
|
+
if (tv.kind !== "refined") return void 0;
|
|
4745
|
+
const meta = tv.refinement.meta;
|
|
4746
|
+
const op = meta?.op;
|
|
4747
|
+
const n = meta?.n;
|
|
4748
|
+
if (typeof n !== "number") return void 0;
|
|
4749
|
+
if (op !== "gt" && op !== "ge" && op !== "lt" && op !== "le") return void 0;
|
|
4750
|
+
const t = term ?? v("_r");
|
|
4751
|
+
const b = lit(n);
|
|
4752
|
+
switch (op) {
|
|
4753
|
+
case "gt":
|
|
4754
|
+
return gt(t, b);
|
|
4755
|
+
case "ge":
|
|
4756
|
+
return ge(t, b);
|
|
4757
|
+
case "lt":
|
|
4758
|
+
return lt(t, b);
|
|
4759
|
+
case "le":
|
|
4760
|
+
return le(t, b);
|
|
4761
|
+
}
|
|
4762
|
+
}
|
|
4763
|
+
function shapeOfLit(v2) {
|
|
4764
|
+
if (typeof v2 === "number") return { k: "prim", type: "number" };
|
|
4765
|
+
if (typeof v2 === "string") return { k: "prim", type: "string" };
|
|
4766
|
+
if (typeof v2 === "boolean") return { k: "prim", type: "boolean" };
|
|
4767
|
+
if (typeof v2 === "bigint") return { k: "prim", type: "bigint" };
|
|
4768
|
+
return { k: "unknown" };
|
|
4769
|
+
}
|
|
4770
|
+
function bridgeIsLossy(a) {
|
|
4771
|
+
const reasons = [];
|
|
4772
|
+
if (a.term && a.term.op !== "lit") {
|
|
4773
|
+
reasons.push(`dropped term ${termToString(a.term)}`);
|
|
4774
|
+
}
|
|
4775
|
+
if (a.pred && a.pred.op !== "true") {
|
|
4776
|
+
const tv = absToTypeValue(a);
|
|
4777
|
+
if (tv.kind !== "refined") {
|
|
4778
|
+
reasons.push(`dropped pred ${predToString(a.pred)}`);
|
|
4779
|
+
}
|
|
4780
|
+
}
|
|
4781
|
+
return { lossy: reasons.length > 0, reasons };
|
|
4782
|
+
}
|
|
723
4783
|
export {
|
|
4784
|
+
$add,
|
|
4785
|
+
$arr,
|
|
4786
|
+
$async,
|
|
4787
|
+
$asyncReturn,
|
|
4788
|
+
$await,
|
|
4789
|
+
$call,
|
|
4790
|
+
$callNamed,
|
|
4791
|
+
$catchVal,
|
|
4792
|
+
$class,
|
|
4793
|
+
$concat,
|
|
4794
|
+
$div,
|
|
4795
|
+
$elems,
|
|
4796
|
+
$eq,
|
|
4797
|
+
$fnVal,
|
|
4798
|
+
$for,
|
|
4799
|
+
$forIter,
|
|
4800
|
+
$forOf,
|
|
4801
|
+
$fork,
|
|
4802
|
+
$ge,
|
|
4803
|
+
$gen,
|
|
4804
|
+
$get,
|
|
4805
|
+
$gt,
|
|
4806
|
+
$idx,
|
|
4807
|
+
$idxSet,
|
|
4808
|
+
$invoke,
|
|
4809
|
+
$invokeSuper,
|
|
4810
|
+
$join,
|
|
4811
|
+
$le,
|
|
4812
|
+
$len,
|
|
4813
|
+
$lit,
|
|
4814
|
+
$lt,
|
|
4815
|
+
$mod,
|
|
4816
|
+
$mul,
|
|
4817
|
+
$ne,
|
|
4818
|
+
$neg,
|
|
4819
|
+
$new,
|
|
4820
|
+
$not,
|
|
4821
|
+
$obj,
|
|
4822
|
+
$optionalGet,
|
|
4823
|
+
$optionalInvoke,
|
|
4824
|
+
$orDefault,
|
|
4825
|
+
$set,
|
|
4826
|
+
$setKey,
|
|
4827
|
+
$spread,
|
|
4828
|
+
$staticInvoke,
|
|
4829
|
+
$sub,
|
|
4830
|
+
$super,
|
|
4831
|
+
$switch,
|
|
4832
|
+
$thisGet,
|
|
4833
|
+
$thisSet,
|
|
4834
|
+
$throw,
|
|
4835
|
+
$typeof,
|
|
4836
|
+
$while,
|
|
4837
|
+
$whileSeq,
|
|
4838
|
+
$yield,
|
|
4839
|
+
DEFAULT_MAX_LOOP_ITERS,
|
|
4840
|
+
MAX_CALL_DEPTH,
|
|
4841
|
+
MAX_TOTAL_CALLS,
|
|
4842
|
+
NudoThrow,
|
|
724
4843
|
Ops,
|
|
4844
|
+
SELF,
|
|
725
4845
|
T,
|
|
4846
|
+
abs,
|
|
4847
|
+
absFunction,
|
|
4848
|
+
absToString,
|
|
4849
|
+
absToTypeValue,
|
|
4850
|
+
add,
|
|
4851
|
+
alphaOf,
|
|
4852
|
+
analyzeFn,
|
|
4853
|
+
and,
|
|
4854
|
+
anyVar,
|
|
4855
|
+
app,
|
|
4856
|
+
applyAbsFn,
|
|
726
4857
|
applyBinaryOp,
|
|
4858
|
+
applyCallbackAbs,
|
|
4859
|
+
array,
|
|
4860
|
+
asAbs,
|
|
4861
|
+
asAbsVal,
|
|
4862
|
+
attachFnImpl,
|
|
4863
|
+
awaitAbs,
|
|
4864
|
+
betaOf,
|
|
4865
|
+
bindImports,
|
|
4866
|
+
bool,
|
|
4867
|
+
boolLit,
|
|
4868
|
+
boolean,
|
|
4869
|
+
bridgeIsLossy,
|
|
4870
|
+
buildArgsFromAssume,
|
|
4871
|
+
callAbsMethod,
|
|
4872
|
+
callFunction,
|
|
4873
|
+
callTranspiledExport,
|
|
4874
|
+
callTranspiledExportFull,
|
|
4875
|
+
canSkipLiteralCallScan,
|
|
4876
|
+
checkArg,
|
|
4877
|
+
checkCall,
|
|
4878
|
+
checkSource,
|
|
4879
|
+
classFromMethods,
|
|
4880
|
+
clearBClasses,
|
|
4881
|
+
cmp,
|
|
4882
|
+
coerceAsyncReturn,
|
|
4883
|
+
collapseLiteralUnion,
|
|
4884
|
+
collapseToOptional,
|
|
4885
|
+
collectAbsExports,
|
|
4886
|
+
collectAbsInlays,
|
|
4887
|
+
collectAbsNodeTypes,
|
|
4888
|
+
concatString,
|
|
727
4889
|
concatTemplates,
|
|
4890
|
+
confJoin,
|
|
4891
|
+
constraintToEntryAbs,
|
|
728
4892
|
createEnvironment,
|
|
4893
|
+
createHofCollectCtx,
|
|
729
4894
|
createRange,
|
|
730
4895
|
createTemplate,
|
|
4896
|
+
createTemplateAbs,
|
|
4897
|
+
currentExecPhi,
|
|
4898
|
+
currentPhi,
|
|
731
4899
|
deepCloneTypeValue,
|
|
4900
|
+
defaultLeakBudget,
|
|
4901
|
+
defineClass,
|
|
4902
|
+
definitelyNotNullishShape,
|
|
4903
|
+
denoteGuard,
|
|
4904
|
+
describePhi,
|
|
732
4905
|
dispatchBinaryOp,
|
|
733
4906
|
dispatchMethod,
|
|
734
4907
|
dispatchProperty,
|
|
4908
|
+
div,
|
|
4909
|
+
emptyEnv,
|
|
4910
|
+
emptyPhi,
|
|
4911
|
+
eq,
|
|
4912
|
+
evalArrayStatic,
|
|
4913
|
+
evalBuiltinInstanceMethod,
|
|
4914
|
+
evalBuiltinNew,
|
|
4915
|
+
evalDateCtor,
|
|
4916
|
+
evalDateMethod,
|
|
4917
|
+
evalDateStatic,
|
|
4918
|
+
evalGlobalFn,
|
|
4919
|
+
evalJsonMethod,
|
|
4920
|
+
evalMathMethod,
|
|
4921
|
+
evalMethodBody,
|
|
4922
|
+
evalNamespaceCall,
|
|
4923
|
+
evalNode,
|
|
4924
|
+
evalNumberStatic,
|
|
4925
|
+
evalObjectMethod,
|
|
4926
|
+
evalProgramAbs,
|
|
4927
|
+
evalPromiseCtor,
|
|
4928
|
+
evalPromiseStatic,
|
|
4929
|
+
evalRegExpCtor,
|
|
4930
|
+
evalRegExpMethod,
|
|
4931
|
+
evalSource,
|
|
4932
|
+
evictCheckSourceMemoForPaths,
|
|
4933
|
+
evictGeneralizeMemoForPaths,
|
|
4934
|
+
exceedsBudget,
|
|
4935
|
+
execNudoModule,
|
|
4936
|
+
extractAllLoadSpecs,
|
|
4937
|
+
extractFn,
|
|
4938
|
+
extractNudoImports,
|
|
4939
|
+
extractRefineReturnFromSource,
|
|
4940
|
+
extractRefinesFromSource,
|
|
4941
|
+
falseConstraint,
|
|
4942
|
+
findAbsAtPosition,
|
|
4943
|
+
fnFingerprints,
|
|
4944
|
+
fnOf,
|
|
4945
|
+
formatAbs,
|
|
4946
|
+
formatAbsMultiline,
|
|
4947
|
+
formatCheckReport,
|
|
4948
|
+
formatDiagnostics,
|
|
4949
|
+
formatShape,
|
|
4950
|
+
formatShapeSlot,
|
|
4951
|
+
ge,
|
|
4952
|
+
geNum,
|
|
4953
|
+
generalizeAll,
|
|
4954
|
+
generalizeFromAst,
|
|
4955
|
+
generalizeSourceKeyPart,
|
|
4956
|
+
getAbsOrigin,
|
|
4957
|
+
getAbsProperty,
|
|
4958
|
+
getBCallCollector,
|
|
4959
|
+
getBClass,
|
|
4960
|
+
getCheckSourceMemoSize,
|
|
4961
|
+
getClass,
|
|
4962
|
+
getClassChain,
|
|
4963
|
+
getFnImpl,
|
|
4964
|
+
getFnNameAndBodies,
|
|
4965
|
+
getFnSig,
|
|
4966
|
+
getGeneralizeMemoSize,
|
|
735
4967
|
getKnownPrefix,
|
|
736
4968
|
getKnownSuffix,
|
|
4969
|
+
getParseSourceCacheSize,
|
|
737
4970
|
getPrimitiveTypeOf,
|
|
738
4971
|
getRangeMeta,
|
|
739
4972
|
getRefinedBase,
|
|
740
4973
|
getTemplateParts,
|
|
4974
|
+
getTerm,
|
|
4975
|
+
getTvConfidence,
|
|
4976
|
+
gt,
|
|
4977
|
+
gtNum,
|
|
4978
|
+
hashSource,
|
|
4979
|
+
implies,
|
|
4980
|
+
instanceOf,
|
|
4981
|
+
instantiateClass,
|
|
4982
|
+
instantiateConstraint,
|
|
4983
|
+
instantiateReturn,
|
|
4984
|
+
isDefinitelyFalse,
|
|
4985
|
+
isDefinitelyTrue,
|
|
4986
|
+
isExactLit,
|
|
4987
|
+
isFnSig,
|
|
4988
|
+
isNudoConstraint,
|
|
4989
|
+
isNudoThrow,
|
|
4990
|
+
isNullishLitAbs,
|
|
4991
|
+
isNumPrim,
|
|
4992
|
+
isObj,
|
|
741
4993
|
isRange,
|
|
4994
|
+
isRelFn,
|
|
4995
|
+
isStrPrim,
|
|
742
4996
|
isSubtypeOf,
|
|
743
4997
|
isTemplate,
|
|
4998
|
+
isTemplateLike,
|
|
4999
|
+
joinAbs,
|
|
5000
|
+
joinFunctions,
|
|
5001
|
+
joinObjects,
|
|
5002
|
+
joinValues,
|
|
5003
|
+
le,
|
|
5004
|
+
leNum,
|
|
5005
|
+
leakIfNeeded,
|
|
5006
|
+
lenTerm,
|
|
5007
|
+
leqAbs,
|
|
5008
|
+
listFunctionNames,
|
|
5009
|
+
lit,
|
|
5010
|
+
litValue,
|
|
5011
|
+
loadModuleDepsFingerprint,
|
|
5012
|
+
lookupMethod,
|
|
5013
|
+
lookupMethodWithOwner,
|
|
5014
|
+
lookupSuperMethod,
|
|
5015
|
+
lt,
|
|
5016
|
+
ltNum,
|
|
5017
|
+
makeSum,
|
|
5018
|
+
mapElementFallback,
|
|
5019
|
+
maybeLeak,
|
|
744
5020
|
mergeObjectProperties,
|
|
5021
|
+
mock,
|
|
5022
|
+
mockHelperToTypeValue,
|
|
5023
|
+
mod,
|
|
5024
|
+
mul,
|
|
745
5025
|
narrowType,
|
|
5026
|
+
ne,
|
|
5027
|
+
negAbs,
|
|
5028
|
+
never,
|
|
5029
|
+
normPath,
|
|
5030
|
+
not,
|
|
5031
|
+
notAbs,
|
|
5032
|
+
noteMemberDispatchMiss,
|
|
5033
|
+
notePrimMemberMissing,
|
|
5034
|
+
noteUnknownMemberMissing,
|
|
5035
|
+
num,
|
|
5036
|
+
numLit,
|
|
5037
|
+
numVar,
|
|
5038
|
+
number,
|
|
5039
|
+
numericBounds,
|
|
5040
|
+
obj,
|
|
5041
|
+
objOf,
|
|
5042
|
+
or,
|
|
5043
|
+
pFalse,
|
|
5044
|
+
pTrue,
|
|
5045
|
+
parseSource,
|
|
5046
|
+
phiAnd,
|
|
5047
|
+
popPhi,
|
|
5048
|
+
predEquals,
|
|
5049
|
+
predToString,
|
|
5050
|
+
predVars,
|
|
5051
|
+
projectBrand,
|
|
5052
|
+
projectFlatMapResult,
|
|
5053
|
+
promoteParamShape,
|
|
5054
|
+
ptypeof,
|
|
5055
|
+
pushPhi,
|
|
5056
|
+
recordMemberDiag,
|
|
5057
|
+
refineToIndexedFull,
|
|
5058
|
+
registerBClass,
|
|
5059
|
+
relationFingerprint,
|
|
5060
|
+
relationFn,
|
|
5061
|
+
resetAbsCallBudget,
|
|
5062
|
+
resetCheckSourceMemo,
|
|
5063
|
+
resetFnFpCache,
|
|
5064
|
+
resetGeneralizeMemo,
|
|
5065
|
+
resetHashSourceCache,
|
|
5066
|
+
resetLeakCounter,
|
|
5067
|
+
resetNudoModuleExecCache,
|
|
5068
|
+
resetParseSourceCache,
|
|
5069
|
+
resetPhi,
|
|
5070
|
+
resolveDepPath,
|
|
5071
|
+
runTranspiled,
|
|
5072
|
+
serializeCheckJson,
|
|
5073
|
+
setAbsAssignCollector,
|
|
5074
|
+
setAbsCallCollector,
|
|
5075
|
+
setAbsNodeCollector,
|
|
5076
|
+
setAbsTruncationCollector,
|
|
5077
|
+
setApplyCallbackHost,
|
|
5078
|
+
setBCallCollector,
|
|
5079
|
+
setMemberDiagCollector,
|
|
5080
|
+
setTvConfidence,
|
|
5081
|
+
shape,
|
|
5082
|
+
shapeOfTerm,
|
|
5083
|
+
shapeOnlyFn,
|
|
5084
|
+
shapeToString,
|
|
5085
|
+
simplifyTerm,
|
|
746
5086
|
simplifyUnion,
|
|
5087
|
+
snapshotAbs,
|
|
5088
|
+
spread,
|
|
5089
|
+
spy,
|
|
5090
|
+
stableAnalyzeKeySource,
|
|
5091
|
+
str,
|
|
5092
|
+
strLit,
|
|
5093
|
+
strictEqAbs,
|
|
5094
|
+
string,
|
|
5095
|
+
stripTypes,
|
|
5096
|
+
stub,
|
|
5097
|
+
sub,
|
|
5098
|
+
substAbs,
|
|
5099
|
+
substPred,
|
|
5100
|
+
substPredAbs,
|
|
747
5101
|
subtractType,
|
|
5102
|
+
superNameOf,
|
|
5103
|
+
tagAbsOrigin,
|
|
5104
|
+
templatePartsOf,
|
|
5105
|
+
termDepth,
|
|
5106
|
+
termEquals,
|
|
5107
|
+
termNodes,
|
|
5108
|
+
termToString,
|
|
5109
|
+
transpile,
|
|
5110
|
+
transpileExpression,
|
|
5111
|
+
transpileFile,
|
|
5112
|
+
transpileSource,
|
|
5113
|
+
trueConstraint,
|
|
5114
|
+
tryPromoteDirectCall,
|
|
5115
|
+
tryPromoteForOfIteratee,
|
|
5116
|
+
tryPromoteHofCallback,
|
|
5117
|
+
tryPromoteReceiverAsArr,
|
|
748
5118
|
typeValueEquals,
|
|
5119
|
+
typeValueToAbs,
|
|
749
5120
|
typeValueToString,
|
|
750
|
-
|
|
5121
|
+
typeofAbs,
|
|
5122
|
+
undefAbs,
|
|
5123
|
+
unknown,
|
|
5124
|
+
v,
|
|
5125
|
+
widenLiteral,
|
|
5126
|
+
withExecPhi,
|
|
5127
|
+
withPhiConstraint,
|
|
5128
|
+
withVar,
|
|
5129
|
+
wrapPromise
|
|
751
5130
|
};
|