doppeldiff 0.1.0 → 0.1.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 +8 -0
- package/dist/cli.js +294 -87
- package/dist/cli.js.map +4 -4
- package/dist/index.js +307 -107
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/schema/report.schema.json +10 -0
package/dist/cli.js
CHANGED
|
@@ -64,6 +64,10 @@ function num(n3) {
|
|
|
64
64
|
if (Object.is(n3, -0)) return "-0";
|
|
65
65
|
return String(n3);
|
|
66
66
|
}
|
|
67
|
+
function list(items, f) {
|
|
68
|
+
if (!abbreviateLists || items.length <= MAX_ITEMS) return items.map(f).join(", ");
|
|
69
|
+
return `${items.slice(0, SHOWN_ITEMS).map(f).join(", ")}, \u2026 ${items.length - SHOWN_ITEMS} more`;
|
|
70
|
+
}
|
|
67
71
|
function formatValue(v, max = MAX_LEN) {
|
|
68
72
|
const seen = /* @__PURE__ */ new Set();
|
|
69
73
|
const f = (x) => {
|
|
@@ -91,12 +95,11 @@ function formatValue(v, max = MAX_LEN) {
|
|
|
91
95
|
if (seen.has(x)) return "[Circular]";
|
|
92
96
|
seen.add(x);
|
|
93
97
|
try {
|
|
94
|
-
if (Array.isArray(x)) return `[${x
|
|
98
|
+
if (Array.isArray(x)) return `[${list(x, f)}]`;
|
|
95
99
|
if (x instanceof Date)
|
|
96
100
|
return Number.isNaN(x.getTime()) ? "new Date(NaN)" : `new Date(${JSON.stringify(x.toISOString())})`;
|
|
97
|
-
if (x instanceof Map)
|
|
98
|
-
|
|
99
|
-
if (x instanceof Set) return `new Set([${[...x].map(f).join(", ")}])`;
|
|
101
|
+
if (x instanceof Map) return `new Map([${list([...x], ([k, val]) => `[${f(k)}, ${f(val)}]`)}])`;
|
|
102
|
+
if (x instanceof Set) return `new Set([${list([...x], f)}])`;
|
|
100
103
|
if (x instanceof RegExp) return String(x);
|
|
101
104
|
if (isProtoDescriptor(x)) {
|
|
102
105
|
const { proto, own } = x[PROTO_TAG];
|
|
@@ -110,7 +113,7 @@ function formatValue(v, max = MAX_LEN) {
|
|
|
110
113
|
return `new ${x.constructor.name}([${Array.from(x).join(", ")}])`;
|
|
111
114
|
const entries = Object.entries(x);
|
|
112
115
|
if (entries.length === 0) return "{}";
|
|
113
|
-
return `{ ${entries
|
|
116
|
+
return `{ ${list(entries, ([k, val]) => `${key(k)}: ${f(val)}`)} }`;
|
|
114
117
|
} finally {
|
|
115
118
|
seen.delete(x);
|
|
116
119
|
}
|
|
@@ -120,7 +123,16 @@ function formatValue(v, max = MAX_LEN) {
|
|
|
120
123
|
function formatArgs(args, max = MAX_LEN) {
|
|
121
124
|
return clip(args.map((a) => formatValue(a, max)).join(", "), max);
|
|
122
125
|
}
|
|
123
|
-
function formatEnc(e, max = MAX_LEN) {
|
|
126
|
+
function formatEnc(e, max = MAX_LEN, abbreviate = true) {
|
|
127
|
+
const previous = abbreviateLists;
|
|
128
|
+
abbreviateLists = abbreviate;
|
|
129
|
+
try {
|
|
130
|
+
return formatEncInner(e, max);
|
|
131
|
+
} finally {
|
|
132
|
+
abbreviateLists = previous;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function formatEncInner(e, max) {
|
|
124
136
|
const f = (x) => {
|
|
125
137
|
switch (x.t) {
|
|
126
138
|
case "undefined":
|
|
@@ -138,18 +150,18 @@ function formatEnc(e, max = MAX_LEN) {
|
|
|
138
150
|
case "symbol":
|
|
139
151
|
return `Symbol(${x.d})`;
|
|
140
152
|
case "array":
|
|
141
|
-
return `[${x.items
|
|
153
|
+
return `[${list(x.items, f)}]`;
|
|
142
154
|
case "object": {
|
|
143
155
|
const prefix = x.ctor && x.ctor !== "null-prototype" ? `${x.ctor} ` : "";
|
|
144
156
|
if (x.entries.length === 0) return `${prefix}{}`;
|
|
145
|
-
return `${prefix}{ ${x.entries
|
|
157
|
+
return `${prefix}{ ${list(x.entries, ([k, v]) => `${key(k)}: ${f(v)}`)} }`;
|
|
146
158
|
}
|
|
147
159
|
case "date":
|
|
148
160
|
return Number.isNaN(x.v) ? "Invalid Date" : new Date(x.v).toISOString();
|
|
149
161
|
case "map":
|
|
150
|
-
return `Map(${x.entries.length}) { ${x.entries
|
|
162
|
+
return `Map(${x.entries.length}) { ${list(x.entries, ([k, v]) => `${f(k)} => ${f(v)}`)} }`;
|
|
151
163
|
case "set":
|
|
152
|
-
return `Set(${x.items.length}) { ${x.items
|
|
164
|
+
return `Set(${x.items.length}) { ${list(x.items, f)} }`;
|
|
153
165
|
case "regexp":
|
|
154
166
|
return `/${x.source}/${x.flags}`;
|
|
155
167
|
case "function":
|
|
@@ -157,7 +169,7 @@ function formatEnc(e, max = MAX_LEN) {
|
|
|
157
169
|
case "error":
|
|
158
170
|
return `${x.ctor}: ${x.message}`;
|
|
159
171
|
case "typed":
|
|
160
|
-
return `${x.ctor} [${x.items
|
|
172
|
+
return `${x.ctor} [${list(x.items, String)}]`;
|
|
161
173
|
case "promise":
|
|
162
174
|
return "Promise";
|
|
163
175
|
case "cycle":
|
|
@@ -168,12 +180,12 @@ function formatEnc(e, max = MAX_LEN) {
|
|
|
168
180
|
};
|
|
169
181
|
return clip(f(e), max);
|
|
170
182
|
}
|
|
171
|
-
function formatOutcome(o, max = MAX_LEN) {
|
|
183
|
+
function formatOutcome(o, max = MAX_LEN, abbreviate = true) {
|
|
172
184
|
switch (o.kind) {
|
|
173
185
|
case "return":
|
|
174
|
-
return o.async ? `resolves ${formatEnc(o.value, max)}` : formatEnc(o.value, max);
|
|
186
|
+
return o.async ? `resolves ${formatEnc(o.value, max, abbreviate)}` : formatEnc(o.value, max, abbreviate);
|
|
175
187
|
case "throw": {
|
|
176
|
-
const e = o.error.t === "error" ? `${o.error.ctor}: ${o.error.message}` : formatEnc(o.error, max);
|
|
188
|
+
const e = o.error.t === "error" ? `${o.error.ctor}: ${o.error.message}` : formatEnc(o.error, max, abbreviate);
|
|
177
189
|
return clip(`${o.async ? "rejects" : "throws"} ${e}`, max);
|
|
178
190
|
}
|
|
179
191
|
case "timeout":
|
|
@@ -216,12 +228,15 @@ function outcomeCategory(o) {
|
|
|
216
228
|
return "crash";
|
|
217
229
|
}
|
|
218
230
|
}
|
|
219
|
-
var MAX_LEN, IDENT, key;
|
|
231
|
+
var MAX_LEN, MAX_ITEMS, SHOWN_ITEMS, abbreviateLists, IDENT, key;
|
|
220
232
|
var init_format = __esm({
|
|
221
233
|
"src/core/format/format.ts"() {
|
|
222
234
|
"use strict";
|
|
223
235
|
init_tags();
|
|
224
236
|
MAX_LEN = 120;
|
|
237
|
+
MAX_ITEMS = 10;
|
|
238
|
+
SHOWN_ITEMS = 6;
|
|
239
|
+
abbreviateLists = true;
|
|
225
240
|
IDENT = /^[A-Za-z_$][\w$]*$/;
|
|
226
241
|
key = (k) => IDENT.test(k) ? k : JSON.stringify(k);
|
|
227
242
|
}
|
|
@@ -955,6 +970,10 @@ var init_model = __esm({
|
|
|
955
970
|
inputsRun: z.number().int().nonnegative(),
|
|
956
971
|
stoppedBy: z.enum(["runs", "deadline", "failures", "exhausted"]).optional(),
|
|
957
972
|
failures: z.number().int().nonnegative(),
|
|
973
|
+
/** Timeouts/crashes on edge-case inputs (huge numbers, NaN, …), included in `failures`. */
|
|
974
|
+
edgeTimeouts: z.number().int().nonnegative().optional(),
|
|
975
|
+
/** Edge-case inputs skipped after repeated timeouts on edge-case inputs. */
|
|
976
|
+
skippedEdgeInputs: z.number().int().nonnegative().optional(),
|
|
958
977
|
durationMs: z.number().nonnegative(),
|
|
959
978
|
seed: z.number().int(),
|
|
960
979
|
usage: z.object({ files: z.number().int(), callSites: z.number().int(), values: z.number().int() }).optional(),
|
|
@@ -1054,14 +1073,14 @@ function applyRuleLabels(report, forceRefactor = false) {
|
|
|
1054
1073
|
report.summary = summarize(report.functions, report.skipped.length, report.summary.durationMs);
|
|
1055
1074
|
return report;
|
|
1056
1075
|
}
|
|
1057
|
-
function outcomeView(o) {
|
|
1076
|
+
function outcomeView(o, abbreviate = true) {
|
|
1058
1077
|
switch (o.kind) {
|
|
1059
1078
|
case "return": {
|
|
1060
1079
|
const code2 = encToCode(o.value);
|
|
1061
1080
|
return {
|
|
1062
1081
|
kind: "return",
|
|
1063
1082
|
async: o.async,
|
|
1064
|
-
display: formatOutcome(o, MAX_DISPLAY),
|
|
1083
|
+
display: formatOutcome(o, MAX_DISPLAY, abbreviate),
|
|
1065
1084
|
...code2 !== void 0 && code2.length < 4e3 ? { code: code2 } : {}
|
|
1066
1085
|
};
|
|
1067
1086
|
}
|
|
@@ -1070,7 +1089,7 @@ function outcomeView(o) {
|
|
|
1070
1089
|
return {
|
|
1071
1090
|
kind: "throw",
|
|
1072
1091
|
async: o.async,
|
|
1073
|
-
display: formatOutcome(o, MAX_DISPLAY),
|
|
1092
|
+
display: formatOutcome(o, MAX_DISPLAY, abbreviate),
|
|
1074
1093
|
...e.t === "error" ? { errorClass: e.ctor.slice(0, 200), message: e.message.slice(0, 4e3) } : {}
|
|
1075
1094
|
};
|
|
1076
1095
|
}
|
|
@@ -1087,16 +1106,21 @@ function exampleFrom(exportName, w) {
|
|
|
1087
1106
|
new: outcomeView(w.head),
|
|
1088
1107
|
minimal: w.minimal
|
|
1089
1108
|
};
|
|
1109
|
+
if (ex.old.display === ex.new.display) {
|
|
1110
|
+
ex.old = outcomeView(w.base, false);
|
|
1111
|
+
ex.new = outcomeView(w.head, false);
|
|
1112
|
+
}
|
|
1090
1113
|
if (w.diff.aspect === "mutation" && (w.base.kind === "return" || w.base.kind === "throw") && (w.head.kind === "return" || w.head.kind === "throw")) {
|
|
1091
1114
|
const i = Number(w.diff.path[0]);
|
|
1092
1115
|
const a = w.base.args[i];
|
|
1093
1116
|
const b = w.head.args[i];
|
|
1094
1117
|
if (a && b) {
|
|
1095
1118
|
const newCode = encToCode(b);
|
|
1119
|
+
const full = formatEnc(a, MAX_DISPLAY) === formatEnc(b, MAX_DISPLAY);
|
|
1096
1120
|
ex.mutation = {
|
|
1097
1121
|
argIndex: i,
|
|
1098
|
-
old: formatEnc(a, MAX_DISPLAY),
|
|
1099
|
-
new: formatEnc(b, MAX_DISPLAY),
|
|
1122
|
+
old: formatEnc(a, MAX_DISPLAY, !full),
|
|
1123
|
+
new: formatEnc(b, MAX_DISPLAY, !full),
|
|
1100
1124
|
...newCode !== void 0 && newCode.length < 4e3 ? { newCode } : {}
|
|
1101
1125
|
};
|
|
1102
1126
|
}
|
|
@@ -1128,6 +1152,8 @@ function functionReport(f) {
|
|
|
1128
1152
|
inputsRun: f.inputsRun,
|
|
1129
1153
|
...f.stoppedBy ? { stoppedBy: f.stoppedBy } : {},
|
|
1130
1154
|
failures: f.failures,
|
|
1155
|
+
...f.edgeFailures ? { edgeTimeouts: f.edgeFailures } : {},
|
|
1156
|
+
...f.skippedEdgeInputs ? { skippedEdgeInputs: f.skippedEdgeInputs } : {},
|
|
1131
1157
|
durationMs: f.durationMs,
|
|
1132
1158
|
seed: f.seed,
|
|
1133
1159
|
...f.usage ? { usage: f.usage } : {},
|
|
@@ -1312,6 +1338,18 @@ function render(r, maxExamples, sticky) {
|
|
|
1312
1338
|
const edge = r.functions.flatMap(
|
|
1313
1339
|
(f) => f.behaviors.filter((b) => b.realism === "edge").map((b) => ({ f, b }))
|
|
1314
1340
|
);
|
|
1341
|
+
const timedOut = r.functions.filter((f) => (f.edgeTimeouts ?? 0) > 0);
|
|
1342
|
+
const timeoutLine = timedOut.length ? `\u23F1\uFE0F Timed out on edge-case inputs (huge numbers, NaN, \u2026); similar inputs were skipped: ${timedOut.map((f) => `${code(f.exportName, 60)} (${n(f.edgeTimeouts ?? 0)})`).join(", ")}.` : "";
|
|
1343
|
+
if (edge.length === 0 && timeoutLine) {
|
|
1344
|
+
lines.push(
|
|
1345
|
+
"",
|
|
1346
|
+
`<details><summary>\u23F1\uFE0F Some edge-case inputs timed out</summary>`,
|
|
1347
|
+
"",
|
|
1348
|
+
timeoutLine,
|
|
1349
|
+
"",
|
|
1350
|
+
"</details>"
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1315
1353
|
if (edge.length > 0) {
|
|
1316
1354
|
const reasons = [...new Set(edge.map(({ b }) => b.realismNote).filter(Boolean))].slice(0, 4).join(", ");
|
|
1317
1355
|
const tag = {
|
|
@@ -1324,7 +1362,9 @@ function render(r, maxExamples, sticky) {
|
|
|
1324
1362
|
"",
|
|
1325
1363
|
`<details><summary>\u{1F9EA} ${edge.length} behavior change${edge.length === 1 ? "" : "s"} only on edge-case inputs${reasons ? ` (${escapeText(reasons)})` : ""}</summary>`,
|
|
1326
1364
|
"",
|
|
1327
|
-
|
|
1365
|
+
// Compact: one example per edge-case behavior.
|
|
1366
|
+
edge.map(({ f, b }) => `${tag[b.label] ?? ""}${behaviorBlock(f, b, 1)}`).join("\n\n"),
|
|
1367
|
+
...timeoutLine ? ["", timeoutLine] : [],
|
|
1328
1368
|
"",
|
|
1329
1369
|
"</details>"
|
|
1330
1370
|
);
|
|
@@ -1724,7 +1764,7 @@ var init_pool = __esm({
|
|
|
1724
1764
|
}
|
|
1725
1765
|
/** Execute a job; timeouts and crashes become outcomes, and the process is respawned. */
|
|
1726
1766
|
async run(job) {
|
|
1727
|
-
const
|
|
1767
|
+
const timeoutMs = Math.round(this.options.timeoutMs * (job.timeoutScale ?? 1));
|
|
1728
1768
|
const watchdogMs = timeoutMs + Math.max(150, Math.round(timeoutMs * 0.3));
|
|
1729
1769
|
const guardKey = `${job.moduleId}#${job.exportName}`;
|
|
1730
1770
|
const outcomes = new Array(job.inputs.length);
|
|
@@ -1748,6 +1788,11 @@ var init_pool = __esm({
|
|
|
1748
1788
|
if (m.t === "result" && m.reqId === reqId) {
|
|
1749
1789
|
outcomes[offset + m.index] = m.outcome;
|
|
1750
1790
|
count = m.index + 1;
|
|
1791
|
+
if (m.outcome.kind === "timeout") {
|
|
1792
|
+
this.guarded.add(guardKey);
|
|
1793
|
+
finish({ count }, true);
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1751
1796
|
arm();
|
|
1752
1797
|
} else if (m.t === "done" && m.reqId === reqId) {
|
|
1753
1798
|
finish({ count }, false);
|
|
@@ -1829,18 +1874,18 @@ var init_pool = __esm({
|
|
|
1829
1874
|
return results[0];
|
|
1830
1875
|
}
|
|
1831
1876
|
/** Run inputs, spreading them across processes. Outcomes are returned in input order. */
|
|
1832
|
-
run(moduleId, exportName, inputs) {
|
|
1833
|
-
const result = this.lock.then(() => this.runUnlocked(moduleId, exportName, inputs));
|
|
1877
|
+
run(moduleId, exportName, inputs, options = {}) {
|
|
1878
|
+
const result = this.lock.then(() => this.runUnlocked(moduleId, exportName, inputs, options.timeoutScale));
|
|
1834
1879
|
this.lock = result.catch(() => void 0);
|
|
1835
1880
|
return result;
|
|
1836
1881
|
}
|
|
1837
|
-
async runUnlocked(moduleId, exportName, inputs) {
|
|
1882
|
+
async runUnlocked(moduleId, exportName, inputs, timeoutScale) {
|
|
1838
1883
|
await this.start();
|
|
1839
1884
|
const chunkSize = Math.max(16, Math.ceil(inputs.length / this.procs.length));
|
|
1840
1885
|
const chunks = [];
|
|
1841
1886
|
for (let i = 0; i < inputs.length; i += chunkSize) chunks.push(inputs.slice(i, i + chunkSize));
|
|
1842
1887
|
const results = await Promise.all(
|
|
1843
|
-
chunks.map((c, i) => this.procs[i].run({ moduleId, exportName, inputs: c }))
|
|
1888
|
+
chunks.map((c, i) => this.procs[i].run({ moduleId, exportName, inputs: c, timeoutScale }))
|
|
1844
1889
|
);
|
|
1845
1890
|
return results.flat();
|
|
1846
1891
|
}
|
|
@@ -4200,6 +4245,18 @@ function numberArb(ctx) {
|
|
|
4200
4245
|
);
|
|
4201
4246
|
return weighted(typeDriven, ctx.mined.numbers, ctx);
|
|
4202
4247
|
}
|
|
4248
|
+
function sizeArb(ctx) {
|
|
4249
|
+
const typeDriven = fc2.oneof(
|
|
4250
|
+
{ arbitrary: fc2.integer({ min: 0, max: 10 }), weight: 40 },
|
|
4251
|
+
{ arbitrary: fc2.integer({ min: 0, max: 100 }), weight: 25 },
|
|
4252
|
+
{ arbitrary: fc2.integer({ min: 0, max: 1e3 }), weight: 8 },
|
|
4253
|
+
{ arbitrary: fc2.constantFrom(-1, -2, -10), weight: 3 },
|
|
4254
|
+
{ arbitrary: fc2.constantFrom(0.5, 1.5, 2.25), weight: 2 },
|
|
4255
|
+
{ arbitrary: fc2.constantFrom(...HUGE_SIZES), weight: 1 }
|
|
4256
|
+
);
|
|
4257
|
+
const mined = ctx.mined.numbers.filter((n3) => Number.isInteger(n3) && n3 >= -10 && n3 <= 1e3);
|
|
4258
|
+
return weighted(typeDriven, mined, ctx);
|
|
4259
|
+
}
|
|
4203
4260
|
function regexSamples(arbs) {
|
|
4204
4261
|
let out = sampleCache.get(arbs);
|
|
4205
4262
|
if (!out) {
|
|
@@ -4325,7 +4382,7 @@ function anythingArb(ctx) {
|
|
|
4325
4382
|
function compile(spec, ctx) {
|
|
4326
4383
|
switch (spec.kind) {
|
|
4327
4384
|
case "number":
|
|
4328
|
-
return numberArb(ctx);
|
|
4385
|
+
return spec.size ? sizeArb(ctx) : numberArb(ctx);
|
|
4329
4386
|
case "bigint":
|
|
4330
4387
|
return fc2.oneof(fc2.bigInt({ min: -1000n, max: 1000n }), fc2.bigInt());
|
|
4331
4388
|
case "string":
|
|
@@ -4458,7 +4515,7 @@ function compileSignature(sig, options = {}) {
|
|
|
4458
4515
|
if (!rest) return base;
|
|
4459
4516
|
return fc2.tuple(base, fc2.array(rest, { maxLength: 4 })).map(([f, r]) => [...f, ...r]);
|
|
4460
4517
|
}
|
|
4461
|
-
var UTF16_EDGES, STRUCTURAL_FRAGMENTS, INTERESTING_CHARS, SPECIAL_DATES, regexCache, sampleCache, TYPED_ARRAYS;
|
|
4518
|
+
var UTF16_EDGES, STRUCTURAL_FRAGMENTS, INTERESTING_CHARS, SPECIAL_DATES, HUGE_SIZES, regexCache, sampleCache, TYPED_ARRAYS;
|
|
4462
4519
|
var init_compile = __esm({
|
|
4463
4520
|
"src/core/arbitraries/compile.ts"() {
|
|
4464
4521
|
"use strict";
|
|
@@ -4525,6 +4582,7 @@ var init_compile = __esm({
|
|
|
4525
4582
|
/* @__PURE__ */ new Date("1999-12-31T23:59:59.000Z"),
|
|
4526
4583
|
/* @__PURE__ */ new Date(NaN)
|
|
4527
4584
|
];
|
|
4585
|
+
HUGE_SIZES = [2 ** 31 - 1, 2 ** 32, Number.MAX_SAFE_INTEGER, 1e15, Infinity, -Infinity, NaN];
|
|
4528
4586
|
regexCache = /* @__PURE__ */ new WeakMap();
|
|
4529
4587
|
sampleCache = /* @__PURE__ */ new WeakMap();
|
|
4530
4588
|
TYPED_ARRAYS = {
|
|
@@ -4543,6 +4601,91 @@ var init_compile = __esm({
|
|
|
4543
4601
|
}
|
|
4544
4602
|
});
|
|
4545
4603
|
|
|
4604
|
+
// src/core/analyze/sizeParams.ts
|
|
4605
|
+
import { Node as Node4, SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
4606
|
+
function isLoopCondition(node) {
|
|
4607
|
+
for (let p = node.getParent(); p; p = p.getParent()) {
|
|
4608
|
+
if (Node4.isForStatement(p)) return p.getCondition() !== void 0 && isWithin2(node, p.getCondition());
|
|
4609
|
+
if (Node4.isWhileStatement(p) || Node4.isDoStatement(p)) return isWithin2(node, p.getExpression());
|
|
4610
|
+
if (Node4.isBlock(p) || Node4.isFunctionLikeDeclaration(p)) return false;
|
|
4611
|
+
}
|
|
4612
|
+
return false;
|
|
4613
|
+
}
|
|
4614
|
+
function isWithin2(node, container) {
|
|
4615
|
+
return node.getPos() >= container.getPos() && node.getEnd() <= container.getEnd();
|
|
4616
|
+
}
|
|
4617
|
+
function usedAsSize(id) {
|
|
4618
|
+
const parent = id.getParent();
|
|
4619
|
+
if (!parent) return false;
|
|
4620
|
+
if (Node4.isBinaryExpression(parent)) {
|
|
4621
|
+
const op = parent.getOperatorToken().getKind();
|
|
4622
|
+
const comparison = op === SyntaxKind4.LessThanToken || op === SyntaxKind4.LessThanEqualsToken || op === SyntaxKind4.GreaterThanToken || op === SyntaxKind4.GreaterThanEqualsToken;
|
|
4623
|
+
if (comparison && isLoopCondition(parent)) return true;
|
|
4624
|
+
}
|
|
4625
|
+
if (Node4.isElementAccessExpression(parent) && parent.getArgumentExpression() === id) return true;
|
|
4626
|
+
if (Node4.isPropertyAssignment(parent) && parent.getName() === "length" && parent.getInitializer() === id)
|
|
4627
|
+
return true;
|
|
4628
|
+
if (Node4.isShorthandPropertyAssignment(parent) && parent.getName() === "length") return true;
|
|
4629
|
+
if ((Node4.isCallExpression(parent) || Node4.isNewExpression(parent)) && parent.getArguments().includes(id)) {
|
|
4630
|
+
const callee = parent.getExpression();
|
|
4631
|
+
const name = Node4.isPropertyAccessExpression(callee) ? callee.getName() : callee.getText();
|
|
4632
|
+
if (name === "Array" || LENGTH_CALLS.has(name)) return true;
|
|
4633
|
+
}
|
|
4634
|
+
return false;
|
|
4635
|
+
}
|
|
4636
|
+
function sizeLikeParams(fns) {
|
|
4637
|
+
const out = /* @__PURE__ */ new Set();
|
|
4638
|
+
for (const fn of fns) {
|
|
4639
|
+
if (!(Node4.isFunctionDeclaration(fn) || Node4.isFunctionExpression(fn) || Node4.isArrowFunction(fn)))
|
|
4640
|
+
continue;
|
|
4641
|
+
const params = fn.getParameters().map((p) => p.getNameNode()).filter((n3) => Node4.isIdentifier(n3)).map((n3) => n3.getText());
|
|
4642
|
+
for (const name of params) {
|
|
4643
|
+
if (SIZE_NAME.test(name)) {
|
|
4644
|
+
out.add(name);
|
|
4645
|
+
continue;
|
|
4646
|
+
}
|
|
4647
|
+
const body = fn.getBody();
|
|
4648
|
+
if (!body) continue;
|
|
4649
|
+
const refs = body.getDescendantsOfKind(SyntaxKind4.Identifier).filter((id) => id.getText() === name).filter((id) => {
|
|
4650
|
+
const p = id.getParent();
|
|
4651
|
+
return !(Node4.isPropertyAccessExpression(p) && p.getNameNode() === id);
|
|
4652
|
+
});
|
|
4653
|
+
if (refs.some(usedAsSize)) {
|
|
4654
|
+
out.add(name);
|
|
4655
|
+
continue;
|
|
4656
|
+
}
|
|
4657
|
+
for (const decl of body.getDescendantsOfKind(SyntaxKind4.VariableDeclaration)) {
|
|
4658
|
+
const init = decl.getInitializer();
|
|
4659
|
+
const local = decl.getNameNode();
|
|
4660
|
+
if (!init || !Node4.isIdentifier(local) || !refs.some((r) => isWithin2(r, init))) continue;
|
|
4661
|
+
const localRefs = body.getDescendantsOfKind(SyntaxKind4.Identifier).filter((id) => id.getText() === local.getText() && id !== local);
|
|
4662
|
+
if (localRefs.some(usedAsSize)) {
|
|
4663
|
+
out.add(name);
|
|
4664
|
+
break;
|
|
4665
|
+
}
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4668
|
+
}
|
|
4669
|
+
return out;
|
|
4670
|
+
}
|
|
4671
|
+
var SIZE_NAME, LENGTH_CALLS;
|
|
4672
|
+
var init_sizeParams = __esm({
|
|
4673
|
+
"src/core/analyze/sizeParams.ts"() {
|
|
4674
|
+
"use strict";
|
|
4675
|
+
SIZE_NAME = /^(n|k|count|size|length|len|times|limit|index|idx|width|height|depth|pages?|parts|chunks?|rows|cols|columns|steps|retries|attempts|digits|places|precision|decimals|people|items|level|levels)$|(Count|Size|Length|Index|Limit|Width|Height|Depth|Times)$/;
|
|
4676
|
+
LENGTH_CALLS = /* @__PURE__ */ new Set([
|
|
4677
|
+
"repeat",
|
|
4678
|
+
"padStart",
|
|
4679
|
+
"padEnd",
|
|
4680
|
+
"at",
|
|
4681
|
+
"slice",
|
|
4682
|
+
"substring",
|
|
4683
|
+
"substr",
|
|
4684
|
+
"splice"
|
|
4685
|
+
]);
|
|
4686
|
+
}
|
|
4687
|
+
});
|
|
4688
|
+
|
|
4546
4689
|
// src/core/compare/compare.ts
|
|
4547
4690
|
function diffEnc(a, b, opts = {}, path = []) {
|
|
4548
4691
|
if (a.t !== b.t) return path;
|
|
@@ -4839,6 +4982,10 @@ import { xoroshiro128plus } from "pure-rand/generator/xoroshiro128plus";
|
|
|
4839
4982
|
function biasFor(runIndex) {
|
|
4840
4983
|
return 2 + Math.floor(Math.log(runIndex + 1) / Math.log(10));
|
|
4841
4984
|
}
|
|
4985
|
+
function witnessCost(input, r) {
|
|
4986
|
+
const out = r ? formatOutcome(r.base, 2e3, false).length + formatOutcome(r.head, 2e3, false).length : 0;
|
|
4987
|
+
return inputComplexity(input) + Math.max(0, out - 40) / 20;
|
|
4988
|
+
}
|
|
4842
4989
|
async function explore(options) {
|
|
4843
4990
|
const {
|
|
4844
4991
|
arbitrary,
|
|
@@ -4851,6 +4998,8 @@ async function explore(options) {
|
|
|
4851
4998
|
shrinkBudget = 500,
|
|
4852
4999
|
maxWitnesses = 3,
|
|
4853
5000
|
maxFailures = 20,
|
|
5001
|
+
maxEdgeFailures = 2,
|
|
5002
|
+
maxTimeoutChecks = 12,
|
|
4854
5003
|
selfCheck = 50,
|
|
4855
5004
|
humanizeBudget = 150
|
|
4856
5005
|
} = options;
|
|
@@ -4868,7 +5017,13 @@ async function explore(options) {
|
|
|
4868
5017
|
return t;
|
|
4869
5018
|
};
|
|
4870
5019
|
let outOfDomain = 0;
|
|
5020
|
+
let edgeFailures = 0;
|
|
5021
|
+
let skippedEdgeInputs = 0;
|
|
4871
5022
|
const wanted = (input) => {
|
|
5023
|
+
if (options.realism && edgeFailures >= maxEdgeFailures && tierOf(input) === 0) {
|
|
5024
|
+
skippedEdgeInputs++;
|
|
5025
|
+
return false;
|
|
5026
|
+
}
|
|
4872
5027
|
if (!options.inDomain || options.inDomain(input)) return true;
|
|
4873
5028
|
if (options.strictDomain) return false;
|
|
4874
5029
|
return outOfDomain++ % 4 === 0;
|
|
@@ -4887,8 +5042,30 @@ async function explore(options) {
|
|
|
4887
5042
|
let failures = 0;
|
|
4888
5043
|
let shrinkEvaluations = 0;
|
|
4889
5044
|
let stoppedBy = "runs";
|
|
4890
|
-
const countFailures = (r) => {
|
|
4891
|
-
if ([r.base, r.head].some((o) => o.kind === "timeout" || o.kind === "crash"))
|
|
5045
|
+
const countFailures = (input, r) => {
|
|
5046
|
+
if (![r.base, r.head].some((o) => o.kind === "timeout" || o.kind === "crash")) return;
|
|
5047
|
+
failures++;
|
|
5048
|
+
if (options.realism && tierOf(input) === 0) edgeFailures++;
|
|
5049
|
+
};
|
|
5050
|
+
const realisticFailures = () => failures - edgeFailures;
|
|
5051
|
+
let timeoutChecks = 0;
|
|
5052
|
+
const timedOut = (r) => r.base.kind === "timeout" || r.head.kind === "timeout";
|
|
5053
|
+
const confirmTimeouts = async (inputs, results) => {
|
|
5054
|
+
const idx2 = results.map((r, i) => i).filter((i) => timedOut(results[i]) && !(options.realism && tierOf(inputs[i]) === 0)).slice(0, Math.max(0, maxTimeoutChecks - timeoutChecks));
|
|
5055
|
+
if (idx2.length === 0) return;
|
|
5056
|
+
timeoutChecks += idx2.length;
|
|
5057
|
+
const again = await evaluate(
|
|
5058
|
+
idx2.map((i) => inputs[i]),
|
|
5059
|
+
{ timeoutScale: 4 }
|
|
5060
|
+
);
|
|
5061
|
+
idx2.forEach((i, j) => {
|
|
5062
|
+
const first2 = results[i];
|
|
5063
|
+
const second = again[j];
|
|
5064
|
+
results[i] = {
|
|
5065
|
+
base: first2.base.kind === "timeout" ? second.base : first2.base,
|
|
5066
|
+
head: first2.head.kind === "timeout" ? second.head : first2.head
|
|
5067
|
+
};
|
|
5068
|
+
});
|
|
4892
5069
|
};
|
|
4893
5070
|
const evalCached = async (inputs) => {
|
|
4894
5071
|
const keys = inputs.map(stableKey);
|
|
@@ -4902,9 +5079,10 @@ async function explore(options) {
|
|
|
4902
5079
|
});
|
|
4903
5080
|
if (missing.length > 0) {
|
|
4904
5081
|
const results = await evaluate(missing);
|
|
5082
|
+
await confirmTimeouts(missing, results);
|
|
4905
5083
|
results.forEach((r, i) => {
|
|
4906
5084
|
cache.set(missingKeys[i], r);
|
|
4907
|
-
countFailures(r);
|
|
5085
|
+
countFailures(missing[i], r);
|
|
4908
5086
|
});
|
|
4909
5087
|
}
|
|
4910
5088
|
return keys.map((k) => cache.get(k));
|
|
@@ -4979,12 +5157,14 @@ async function explore(options) {
|
|
|
4979
5157
|
flakyClusters: 0,
|
|
4980
5158
|
stoppedBy: "runs",
|
|
4981
5159
|
shrinkEvaluations: 0,
|
|
4982
|
-
failures
|
|
5160
|
+
failures,
|
|
5161
|
+
edgeFailures,
|
|
5162
|
+
skippedEdgeInputs
|
|
4983
5163
|
};
|
|
4984
5164
|
}
|
|
4985
5165
|
}
|
|
4986
5166
|
cache.set(stableKey(inputs[i]), a[i]);
|
|
4987
|
-
countFailures(a[i]);
|
|
5167
|
+
countFailures(inputs[i], a[i]);
|
|
4988
5168
|
record(first[i], a[i]);
|
|
4989
5169
|
}
|
|
4990
5170
|
inputsRun = inputs.length;
|
|
@@ -4997,7 +5177,7 @@ async function explore(options) {
|
|
|
4997
5177
|
stoppedBy = "deadline";
|
|
4998
5178
|
break;
|
|
4999
5179
|
}
|
|
5000
|
-
if (
|
|
5180
|
+
if (realisticFailures() >= maxFailures) {
|
|
5001
5181
|
stoppedBy = "failures";
|
|
5002
5182
|
break;
|
|
5003
5183
|
}
|
|
@@ -5023,9 +5203,13 @@ async function explore(options) {
|
|
|
5023
5203
|
}
|
|
5024
5204
|
staleBatches = 0;
|
|
5025
5205
|
const results = await evaluate(batch.map((p) => p.value.value));
|
|
5206
|
+
await confirmTimeouts(
|
|
5207
|
+
batch.map((p) => p.value.value),
|
|
5208
|
+
results
|
|
5209
|
+
);
|
|
5026
5210
|
results.forEach((r, i) => {
|
|
5027
5211
|
cache.set(batch[i].key, r);
|
|
5028
|
-
countFailures(r);
|
|
5212
|
+
countFailures(batch[i].value.value, r);
|
|
5029
5213
|
record(batch[i].value, r);
|
|
5030
5214
|
});
|
|
5031
5215
|
inputsRun += batch.length;
|
|
@@ -5035,6 +5219,12 @@ async function explore(options) {
|
|
|
5035
5219
|
for (const cluster of clusters.values()) {
|
|
5036
5220
|
let cur = cluster.seed;
|
|
5037
5221
|
let evals = 0;
|
|
5222
|
+
const w0 = cluster.witnesses[0];
|
|
5223
|
+
if (w0.base.kind === "timeout" || w0.head.kind === "timeout") {
|
|
5224
|
+
cluster.witnesses.length = 1;
|
|
5225
|
+
cluster.witnesses[0].minimal = true;
|
|
5226
|
+
continue;
|
|
5227
|
+
}
|
|
5038
5228
|
if (exampleValues.has(cur) && !arbitrary.canShrinkWithoutContext(cur.value)) continue;
|
|
5039
5229
|
let improved = true;
|
|
5040
5230
|
while (improved && evals < shrinkBudget) {
|
|
@@ -5061,7 +5251,7 @@ async function explore(options) {
|
|
|
5061
5251
|
break;
|
|
5062
5252
|
}
|
|
5063
5253
|
}
|
|
5064
|
-
if (
|
|
5254
|
+
if (realisticFailures() >= maxFailures * 2) break;
|
|
5065
5255
|
}
|
|
5066
5256
|
shrinkEvaluations += evals;
|
|
5067
5257
|
const sameCluster = async (candidates) => {
|
|
@@ -5076,7 +5266,9 @@ async function explore(options) {
|
|
|
5076
5266
|
finals.set(stableKey(h.input), h.input);
|
|
5077
5267
|
finals.set(stableKey(start), start);
|
|
5078
5268
|
}
|
|
5079
|
-
const ranked = [...finals.values()].filter((input) => classify(input, cache.get(stableKey(input)))?.key === cluster.key).sort(
|
|
5269
|
+
const ranked = [...finals.values()].filter((input) => classify(input, cache.get(stableKey(input)))?.key === cluster.key).sort(
|
|
5270
|
+
(a, b) => tierOf(b) - tierOf(a) || witnessCost(a, cache.get(stableKey(a))) - witnessCost(b, cache.get(stableKey(b)))
|
|
5271
|
+
).slice(0, maxWitnesses);
|
|
5080
5272
|
cluster.witnesses = ranked.map((input, i) => {
|
|
5081
5273
|
const r = cache.get(stableKey(input));
|
|
5082
5274
|
const w = {
|
|
@@ -5094,7 +5286,7 @@ async function explore(options) {
|
|
|
5094
5286
|
for (const cluster of clusters.values()) {
|
|
5095
5287
|
for (const w of cluster.witnesses) w.realism ??= options.realism(w.input);
|
|
5096
5288
|
cluster.witnesses.sort(
|
|
5097
|
-
(a, b) => b.realism.tier - a.realism.tier ||
|
|
5289
|
+
(a, b) => b.realism.tier - a.realism.tier || witnessCost(a.input, { base: a.base, head: a.head }) - witnessCost(b.input, { base: b.base, head: b.head })
|
|
5098
5290
|
);
|
|
5099
5291
|
cluster.witnesses.forEach((w, i) => w.minimal = i === 0);
|
|
5100
5292
|
}
|
|
@@ -5126,7 +5318,9 @@ async function explore(options) {
|
|
|
5126
5318
|
flakyClusters,
|
|
5127
5319
|
stoppedBy,
|
|
5128
5320
|
shrinkEvaluations,
|
|
5129
|
-
failures
|
|
5321
|
+
failures,
|
|
5322
|
+
edgeFailures,
|
|
5323
|
+
skippedEdgeInputs
|
|
5130
5324
|
};
|
|
5131
5325
|
}
|
|
5132
5326
|
var size;
|
|
@@ -5228,6 +5422,11 @@ function printSpec(spec) {
|
|
|
5228
5422
|
return spec.kind;
|
|
5229
5423
|
}
|
|
5230
5424
|
}
|
|
5425
|
+
function markSize(spec) {
|
|
5426
|
+
if (spec.kind === "number") return { kind: "number", size: true };
|
|
5427
|
+
if (spec.kind === "union") return { ...spec, options: spec.options.map(markSize) };
|
|
5428
|
+
return spec;
|
|
5429
|
+
}
|
|
5231
5430
|
var BINARY_CTORS;
|
|
5232
5431
|
var init_spec = __esm({
|
|
5233
5432
|
"src/core/types/spec.ts"() {
|
|
@@ -5252,7 +5451,7 @@ var init_spec = __esm({
|
|
|
5252
5451
|
|
|
5253
5452
|
// src/core/types/fromType.ts
|
|
5254
5453
|
import {
|
|
5255
|
-
Node as
|
|
5454
|
+
Node as Node5,
|
|
5256
5455
|
ts as ts4
|
|
5257
5456
|
} from "ts-morph";
|
|
5258
5457
|
function typeId(type) {
|
|
@@ -5384,7 +5583,7 @@ function objectLike(type, ctx) {
|
|
|
5384
5583
|
}
|
|
5385
5584
|
function isClassInstance(type) {
|
|
5386
5585
|
const decls = type.getSymbol()?.getDeclarations() ?? [];
|
|
5387
|
-
return decls.some((d) =>
|
|
5586
|
+
return decls.some((d) => Node5.isClassDeclaration(d) || Node5.isClassExpression(d));
|
|
5388
5587
|
}
|
|
5389
5588
|
function unwrapPromise(type) {
|
|
5390
5589
|
const name = symbolName(type);
|
|
@@ -5463,7 +5662,7 @@ function paramSpec(p, location, index) {
|
|
|
5463
5662
|
if (optional && !rest) {
|
|
5464
5663
|
spec = spec.kind === "union" ? spec.options.some((o) => o.kind === "undefined") ? spec : { kind: "union", options: [...spec.options, { kind: "undefined" }] } : spec.kind === "undefined" ? spec : { kind: "union", options: [spec, { kind: "undefined" }] };
|
|
5465
5664
|
}
|
|
5466
|
-
const name =
|
|
5665
|
+
const name = Node5.isIdentifier(p.getNameNode()) ? p.getName() : `arg${index}`;
|
|
5467
5666
|
return { name, spec, optional, rest };
|
|
5468
5667
|
}
|
|
5469
5668
|
function signatureFromNode(fn) {
|
|
@@ -5477,7 +5676,7 @@ function signatureFromCallable(decl) {
|
|
|
5477
5676
|
const checker = decl.getProject().getTypeChecker();
|
|
5478
5677
|
const params = sig.getParameters().map((sym, i) => {
|
|
5479
5678
|
const pdecl = sym.getValueDeclaration();
|
|
5480
|
-
const isParam = pdecl !== void 0 &&
|
|
5679
|
+
const isParam = pdecl !== void 0 && Node5.isParameterDeclaration(pdecl);
|
|
5481
5680
|
const rest = isParam && pdecl.isRestParameter();
|
|
5482
5681
|
const optional = !rest && ((sym.getFlags() & ts4.SymbolFlags.Optional) !== 0 || isParam && (pdecl.isOptional() || pdecl.hasInitializer()));
|
|
5483
5682
|
let type = checker.getTypeOfSymbolAtLocation(sym, decl);
|
|
@@ -5492,7 +5691,7 @@ function signatureFromCallable(decl) {
|
|
|
5492
5691
|
return { params, async: unwrapPromise(sig.getReturnType()) !== void 0 };
|
|
5493
5692
|
}
|
|
5494
5693
|
function signatureOf(node) {
|
|
5495
|
-
return
|
|
5694
|
+
return Node5.isVariableDeclaration(node) ? signatureFromCallable(node) : signatureFromNode(node);
|
|
5496
5695
|
}
|
|
5497
5696
|
var MAX_UNION_OPTIONS, MAX_PROPS, HANDLED_BUILTINS;
|
|
5498
5697
|
var init_fromType = __esm({
|
|
@@ -5514,21 +5713,21 @@ var init_fromType = __esm({
|
|
|
5514
5713
|
});
|
|
5515
5714
|
|
|
5516
5715
|
// src/core/analyze/guards.ts
|
|
5517
|
-
import { Node as
|
|
5716
|
+
import { Node as Node6, SyntaxKind as SyntaxKind5 } from "ts-morph";
|
|
5518
5717
|
function compile2(node, params) {
|
|
5519
|
-
if (
|
|
5520
|
-
if (
|
|
5718
|
+
if (Node6.isParenthesizedExpression(node)) return compile2(node.getExpression(), params);
|
|
5719
|
+
if (Node6.isNumericLiteral(node)) {
|
|
5521
5720
|
const v = node.getLiteralValue();
|
|
5522
5721
|
return () => v;
|
|
5523
5722
|
}
|
|
5524
|
-
if (
|
|
5723
|
+
if (Node6.isStringLiteral(node) || Node6.isNoSubstitutionTemplateLiteral(node)) {
|
|
5525
5724
|
const v = node.getLiteralValue();
|
|
5526
5725
|
return () => v;
|
|
5527
5726
|
}
|
|
5528
|
-
if (node.getKind() ===
|
|
5529
|
-
if (node.getKind() ===
|
|
5530
|
-
if (node.getKind() ===
|
|
5531
|
-
if (
|
|
5727
|
+
if (node.getKind() === SyntaxKind5.TrueKeyword) return () => true;
|
|
5728
|
+
if (node.getKind() === SyntaxKind5.FalseKeyword) return () => false;
|
|
5729
|
+
if (node.getKind() === SyntaxKind5.NullKeyword) return () => null;
|
|
5730
|
+
if (Node6.isIdentifier(node)) {
|
|
5532
5731
|
const name = node.getText();
|
|
5533
5732
|
if (name === "undefined") return () => void 0;
|
|
5534
5733
|
if (name === "NaN") return () => NaN;
|
|
@@ -5536,7 +5735,7 @@ function compile2(node, params) {
|
|
|
5536
5735
|
if (params.has(name)) return (env) => env.get(name);
|
|
5537
5736
|
return void 0;
|
|
5538
5737
|
}
|
|
5539
|
-
if (
|
|
5738
|
+
if (Node6.isPropertyAccessExpression(node) && node.getName() === "length") {
|
|
5540
5739
|
const obj = compile2(node.getExpression(), params);
|
|
5541
5740
|
if (!obj) return void 0;
|
|
5542
5741
|
return (env) => {
|
|
@@ -5544,7 +5743,7 @@ function compile2(node, params) {
|
|
|
5544
5743
|
return typeof v === "string" || Array.isArray(v) ? v.length : UNKNOWN;
|
|
5545
5744
|
};
|
|
5546
5745
|
}
|
|
5547
|
-
if (
|
|
5746
|
+
if (Node6.isTypeOfExpression(node)) {
|
|
5548
5747
|
const e = compile2(node.getExpression(), params);
|
|
5549
5748
|
if (!e) return void 0;
|
|
5550
5749
|
return (env) => {
|
|
@@ -5552,16 +5751,16 @@ function compile2(node, params) {
|
|
|
5552
5751
|
return v === UNKNOWN ? UNKNOWN : v === null ? "object" : typeof v;
|
|
5553
5752
|
};
|
|
5554
5753
|
}
|
|
5555
|
-
if (
|
|
5754
|
+
if (Node6.isPrefixUnaryExpression(node)) {
|
|
5556
5755
|
const e = compile2(node.getOperand(), params);
|
|
5557
5756
|
if (!e) return void 0;
|
|
5558
5757
|
const op = node.getOperatorToken();
|
|
5559
|
-
if (op ===
|
|
5560
|
-
if (op ===
|
|
5758
|
+
if (op === SyntaxKind5.ExclamationToken) return (env) => e(env) === UNKNOWN ? UNKNOWN : !e(env);
|
|
5759
|
+
if (op === SyntaxKind5.MinusToken)
|
|
5561
5760
|
return (env) => typeof e(env) === "number" ? -e(env) : UNKNOWN;
|
|
5562
5761
|
return void 0;
|
|
5563
5762
|
}
|
|
5564
|
-
if (
|
|
5763
|
+
if (Node6.isCallExpression(node)) {
|
|
5565
5764
|
const callee = node.getExpression().getText().replace(/\s+/g, "");
|
|
5566
5765
|
const fns = {
|
|
5567
5766
|
"Number.isInteger": Number.isInteger,
|
|
@@ -5582,7 +5781,7 @@ function compile2(node, params) {
|
|
|
5582
5781
|
return v === UNKNOWN ? UNKNOWN : fn(v);
|
|
5583
5782
|
};
|
|
5584
5783
|
}
|
|
5585
|
-
if (
|
|
5784
|
+
if (Node6.isBinaryExpression(node)) {
|
|
5586
5785
|
const l = compile2(node.getLeft(), params);
|
|
5587
5786
|
const r = compile2(node.getRight(), params);
|
|
5588
5787
|
if (!l || !r) return void 0;
|
|
@@ -5593,29 +5792,29 @@ function compile2(node, params) {
|
|
|
5593
5792
|
return a === UNKNOWN || b === UNKNOWN ? UNKNOWN : f(a, b);
|
|
5594
5793
|
};
|
|
5595
5794
|
switch (op) {
|
|
5596
|
-
case
|
|
5795
|
+
case SyntaxKind5.LessThanToken:
|
|
5597
5796
|
return cmp((a, b) => a < b);
|
|
5598
|
-
case
|
|
5797
|
+
case SyntaxKind5.LessThanEqualsToken:
|
|
5599
5798
|
return cmp((a, b) => a <= b);
|
|
5600
|
-
case
|
|
5799
|
+
case SyntaxKind5.GreaterThanToken:
|
|
5601
5800
|
return cmp((a, b) => a > b);
|
|
5602
|
-
case
|
|
5801
|
+
case SyntaxKind5.GreaterThanEqualsToken:
|
|
5603
5802
|
return cmp((a, b) => a >= b);
|
|
5604
|
-
case
|
|
5803
|
+
case SyntaxKind5.EqualsEqualsEqualsToken:
|
|
5605
5804
|
return cmp((a, b) => a === b);
|
|
5606
|
-
case
|
|
5805
|
+
case SyntaxKind5.ExclamationEqualsEqualsToken:
|
|
5607
5806
|
return cmp((a, b) => a !== b);
|
|
5608
|
-
case
|
|
5807
|
+
case SyntaxKind5.EqualsEqualsToken:
|
|
5609
5808
|
return cmp((a, b) => a == b);
|
|
5610
|
-
case
|
|
5809
|
+
case SyntaxKind5.ExclamationEqualsToken:
|
|
5611
5810
|
return cmp((a, b) => a != b);
|
|
5612
|
-
case
|
|
5811
|
+
case SyntaxKind5.AmpersandAmpersandToken:
|
|
5613
5812
|
return (env) => {
|
|
5614
5813
|
const a = l(env);
|
|
5615
5814
|
if (a === UNKNOWN) return UNKNOWN;
|
|
5616
5815
|
return a ? r(env) : a;
|
|
5617
5816
|
};
|
|
5618
|
-
case
|
|
5817
|
+
case SyntaxKind5.BarBarToken:
|
|
5619
5818
|
return (env) => {
|
|
5620
5819
|
const a = l(env);
|
|
5621
5820
|
if (a === UNKNOWN) return UNKNOWN;
|
|
@@ -5628,30 +5827,30 @@ function compile2(node, params) {
|
|
|
5628
5827
|
return void 0;
|
|
5629
5828
|
}
|
|
5630
5829
|
function isExit(stmt) {
|
|
5631
|
-
if (
|
|
5632
|
-
if (
|
|
5830
|
+
if (Node6.isThrowStatement(stmt) || Node6.isReturnStatement(stmt)) return true;
|
|
5831
|
+
if (Node6.isBlock(stmt)) {
|
|
5633
5832
|
const s = stmt.getStatements();
|
|
5634
5833
|
return s.length >= 1 && isExit(s[s.length - 1]);
|
|
5635
5834
|
}
|
|
5636
5835
|
return false;
|
|
5637
5836
|
}
|
|
5638
5837
|
function inferGuards(fn) {
|
|
5639
|
-
if (!(
|
|
5838
|
+
if (!(Node6.isFunctionDeclaration(fn) || Node6.isFunctionExpression(fn) || Node6.isArrowFunction(fn)))
|
|
5640
5839
|
return void 0;
|
|
5641
5840
|
const body = fn.getBody();
|
|
5642
|
-
if (!body || !
|
|
5841
|
+
if (!body || !Node6.isBlock(body)) return void 0;
|
|
5643
5842
|
const paramDecls = fn.getParameters();
|
|
5644
|
-
const names = paramDecls.map((p) =>
|
|
5843
|
+
const names = paramDecls.map((p) => Node6.isIdentifier(p.getNameNode()) ? p.getName() : void 0);
|
|
5645
5844
|
const params = new Set(names.filter((n3) => !!n3));
|
|
5646
5845
|
const checks = [];
|
|
5647
5846
|
for (const stmt of body.getStatements()) {
|
|
5648
|
-
if (
|
|
5847
|
+
if (Node6.isIfStatement(stmt) && !stmt.getElseStatement() && isExit(stmt.getThenStatement())) {
|
|
5649
5848
|
const expr = compile2(stmt.getExpression(), params);
|
|
5650
5849
|
if (expr) checks.push({ text: stmt.getExpression().getText(), expr });
|
|
5651
5850
|
continue;
|
|
5652
5851
|
}
|
|
5653
|
-
if (
|
|
5654
|
-
if (
|
|
5852
|
+
if (Node6.isExpressionStatement(stmt) && Node6.isCallExpression(stmt.getExpression())) continue;
|
|
5853
|
+
if (Node6.isVariableStatement(stmt) && checks.length === 0) continue;
|
|
5655
5854
|
break;
|
|
5656
5855
|
}
|
|
5657
5856
|
if (checks.length === 0) return void 0;
|
|
@@ -5754,7 +5953,7 @@ var init_accepts = __esm({
|
|
|
5754
5953
|
});
|
|
5755
5954
|
|
|
5756
5955
|
// src/core/verify/verifyFunction.ts
|
|
5757
|
-
import { Node as
|
|
5956
|
+
import { Node as Node7 } from "ts-morph";
|
|
5758
5957
|
function locateExport(sf, exportName) {
|
|
5759
5958
|
const index = indexModule(sf);
|
|
5760
5959
|
const top = index.exports.get(exportName);
|
|
@@ -5848,11 +6047,16 @@ async function verifyFunction(session, target, options) {
|
|
|
5848
6047
|
values: usage.numbers.length + usage.strings.length
|
|
5849
6048
|
};
|
|
5850
6049
|
}
|
|
5851
|
-
const guards =
|
|
6050
|
+
const guards = Node7.isVariableDeclaration(baseLoc.node) ? void 0 : inferGuards(baseLoc.node);
|
|
5852
6051
|
if (guards) base.guards = guards.conditions;
|
|
5853
6052
|
const realismCtx = realismContext(usage);
|
|
5854
6053
|
base.mined = { numbers: mined.numbers.length, strings: mined.strings.length, thresholds: mined.thresholds };
|
|
5855
|
-
const
|
|
6054
|
+
const sizeNames = sizeLikeParams([baseLoc.node, headLoc.node]);
|
|
6055
|
+
const genSig = sizeNames.size === 0 ? headSig : {
|
|
6056
|
+
...headSig,
|
|
6057
|
+
params: headSig.params.map((p) => sizeNames.has(p.name) ? { ...p, spec: markSize(p.spec) } : p)
|
|
6058
|
+
};
|
|
6059
|
+
const arbitrary = options.arbitrary ?? compileSignature(genSig, { mined });
|
|
5856
6060
|
if (!arbitrary) return skip("could not build an input generator for the signature");
|
|
5857
6061
|
let examples = [...usage?.tuples ?? [], ...options.examples ?? []];
|
|
5858
6062
|
if (options.where) examples = examples.filter((e) => safeWhere(options.where, e));
|
|
@@ -5893,10 +6097,10 @@ async function verifyFunction(session, target, options) {
|
|
|
5893
6097
|
if (!m.load.exports.includes(target.exportName))
|
|
5894
6098
|
return skip(`export \`${target.exportName}\` not found at runtime (${label})`);
|
|
5895
6099
|
}
|
|
5896
|
-
const evaluate = async (inputs) => {
|
|
6100
|
+
const evaluate = async (inputs, opts) => {
|
|
5897
6101
|
const [b, h] = await Promise.all([
|
|
5898
|
-
session.pools.base.run(baseMod.moduleId, target.exportName, inputs),
|
|
5899
|
-
session.pools.head.run(headMod.moduleId, target.exportName, inputs)
|
|
6102
|
+
session.pools.base.run(baseMod.moduleId, target.exportName, inputs, opts),
|
|
6103
|
+
session.pools.head.run(headMod.moduleId, target.exportName, inputs, opts)
|
|
5900
6104
|
]);
|
|
5901
6105
|
return b.map((o, i) => ({ base: o, head: h[i] }));
|
|
5902
6106
|
};
|
|
@@ -5921,6 +6125,8 @@ async function verifyFunction(session, target, options) {
|
|
|
5921
6125
|
flakyClusters: result.flakyClusters,
|
|
5922
6126
|
stoppedBy: result.stoppedBy,
|
|
5923
6127
|
failures: result.failures,
|
|
6128
|
+
edgeFailures: result.edgeFailures,
|
|
6129
|
+
skippedEdgeInputs: result.skippedEdgeInputs,
|
|
5924
6130
|
shrinkEvaluations: result.shrinkEvaluations,
|
|
5925
6131
|
durationMs: Date.now() - started
|
|
5926
6132
|
};
|
|
@@ -5949,6 +6155,7 @@ var init_verifyFunction = __esm({
|
|
|
5949
6155
|
init_compile();
|
|
5950
6156
|
init_locate();
|
|
5951
6157
|
init_regex();
|
|
6158
|
+
init_sizeParams();
|
|
5952
6159
|
init_toplevel();
|
|
5953
6160
|
init_explore();
|
|
5954
6161
|
init_fromType();
|
|
@@ -6671,7 +6878,7 @@ function exitCodeFor(report, failOn) {
|
|
|
6671
6878
|
if (failOn === "any") return report.summary.behaviors > 0 ? 1 : 0;
|
|
6672
6879
|
return report.summary.unclaimedRealistic > 0 ? 1 : 0;
|
|
6673
6880
|
}
|
|
6674
|
-
var
|
|
6881
|
+
var list2 = (v) => {
|
|
6675
6882
|
if (v === void 0) return void 0;
|
|
6676
6883
|
const arr = Array.isArray(v) ? v : [v];
|
|
6677
6884
|
return arr.flatMap((x) => String(x).split(",")).map((s) => s.trim()).filter(Boolean);
|
|
@@ -6774,9 +6981,9 @@ async function runCli(flags) {
|
|
|
6774
6981
|
timeoutMs: Number(flags.timeout ?? config.timeoutMs ?? DEFAULT_TIMEOUT_MS),
|
|
6775
6982
|
...flags.budget !== void 0 ? { timeBudgetMs: Number(flags.budget) * 1e3 } : {},
|
|
6776
6983
|
freeze: flags.freeze !== false && config.freeze !== false,
|
|
6777
|
-
...
|
|
6778
|
-
...
|
|
6779
|
-
...
|
|
6984
|
+
...list2(flags.include) ?? config.include ? { include: list2(flags.include) ?? config.include } : {},
|
|
6985
|
+
...list2(flags.exclude) ?? config.exclude ? { exclude: [...list2(flags.exclude) ?? [], ...config.exclude ?? []] } : {},
|
|
6986
|
+
...list2(flags.only) ? { only: list2(flags.only) } : {},
|
|
6780
6987
|
...config.compare ? { compare: config.compare } : {},
|
|
6781
6988
|
...config.functions ? { functions: config.functions } : {},
|
|
6782
6989
|
mergeBase: flags.mergeBase !== false,
|