@rasputin-ai/node 0.5.0-alpha.12 → 0.5.0-alpha.14
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 +60 -29
- package/dist/auto-instrumentation/bun-plugin.d.ts.map +1 -1
- package/dist/auto-instrumentation/format-configuration-report.d.ts +4 -0
- package/dist/auto-instrumentation/format-configuration-report.d.ts.map +1 -1
- package/dist/auto-instrumentation/instrument-compiled-esm.d.ts +1 -3
- package/dist/auto-instrumentation/instrument-compiled-esm.d.ts.map +1 -1
- package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts +10 -2
- package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts.map +1 -1
- package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts +6 -0
- package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts.map +1 -1
- package/dist/auto-instrumentation/node-hooks.d.ts.map +1 -1
- package/dist/auto-instrumentation/parameter-initializer-may-run-code.d.ts +3 -0
- package/dist/auto-instrumentation/parameter-initializer-may-run-code.d.ts.map +1 -0
- package/dist/auto-instrumentation/schedule-instrumentation-manifest-upload.d.ts +1 -1
- package/dist/auto-instrumentation/schedule-instrumentation-manifest-upload.d.ts.map +1 -1
- package/dist/auto-instrumentation/source-map-locations.d.ts +1 -0
- package/dist/auto-instrumentation/source-map-locations.d.ts.map +1 -1
- package/dist/auto-instrumentation/suspension-whitelist.d.ts +33 -0
- package/dist/auto-instrumentation/suspension-whitelist.d.ts.map +1 -0
- package/dist/auto-instrumentation/transform-disk-cache.d.ts +7 -0
- package/dist/auto-instrumentation/transform-disk-cache.d.ts.map +1 -0
- package/dist/auto-instrumentation/transform-source.d.ts +9 -6
- package/dist/auto-instrumentation/transform-source.d.ts.map +1 -1
- package/dist/auto-instrumentation/verify-instrumentation-isomorphism.d.ts +2 -0
- package/dist/auto-instrumentation/verify-instrumentation-isomorphism.d.ts.map +1 -0
- package/dist/auto-instrumentation/verify-transformed-source.d.ts +2 -0
- package/dist/auto-instrumentation/verify-transformed-source.d.ts.map +1 -0
- package/dist/execution-recorder/automatic-runtime.d.ts +17 -7
- package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
- package/dist/execution-recorder/encode-frozen.d.ts +16 -0
- package/dist/execution-recorder/encode-frozen.d.ts.map +1 -0
- package/dist/execution-recorder/execution-journal.d.ts.map +1 -1
- package/dist/execution-recorder/execution-recorder-types.d.ts +124 -36
- package/dist/execution-recorder/execution-recorder-types.d.ts.map +1 -1
- package/dist/execution-recorder/execution-recorder.d.ts +2 -2
- package/dist/execution-recorder/execution-recorder.d.ts.map +1 -1
- package/dist/execution-recorder/function-plan.d.ts +8 -1
- package/dist/execution-recorder/function-plan.d.ts.map +1 -1
- package/dist/execution-recorder/index.d.ts +1 -1
- package/dist/execution-recorder/index.d.ts.map +1 -1
- package/dist/execution-recorder/recorder-memory-budget.d.ts +1 -0
- package/dist/execution-recorder/recorder-memory-budget.d.ts.map +1 -1
- package/dist/execution-recorder/redact-keys.d.ts +4 -0
- package/dist/execution-recorder/redact-keys.d.ts.map +1 -0
- package/dist/execution-recorder/run-exactly-once.d.ts +7 -0
- package/dist/execution-recorder/run-exactly-once.d.ts.map +1 -0
- package/dist/execution-recorder/runtime-state-size-budget.d.ts +14 -0
- package/dist/execution-recorder/runtime-state-size-budget.d.ts.map +1 -0
- package/dist/execution-recorder/safe-serialize.d.ts +9 -3
- package/dist/execution-recorder/safe-serialize.d.ts.map +1 -1
- package/dist/execution-recorder/value-freeze.d.ts +43 -0
- package/dist/execution-recorder/value-freeze.d.ts.map +1 -0
- package/dist/execution-recorder/warn-missing-async-context-frame.d.ts +10 -0
- package/dist/execution-recorder/warn-missing-async-context-frame.d.ts.map +1 -0
- package/dist/index.d.ts +1 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2597 -892
- package/dist/install-global-handlers.d.ts +29 -8
- package/dist/install-global-handlers.d.ts.map +1 -1
- package/dist/instrument/build.js +947 -805
- package/dist/instrument/bun.js +1098 -778
- package/dist/instrument/node.js +1078 -780
- package/dist/internal.d.ts +7 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +4072 -0
- package/dist/rasputin-init.d.ts +25 -5
- package/dist/rasputin-init.d.ts.map +1 -1
- package/dist/sdk-meta.d.ts +1 -1
- package/package.json +7 -2
- package/dist/derivation/analyze-function.d.ts +0 -42
- package/dist/derivation/analyze-function.d.ts.map +0 -1
- package/dist/derivation/build-dependency-graph.d.ts +0 -21
- package/dist/derivation/build-dependency-graph.d.ts.map +0 -1
- package/dist/derivation/derivation-ir.d.ts +0 -33
- package/dist/derivation/derivation-ir.d.ts.map +0 -1
- package/dist/derivation/derivation-proof.d.ts +0 -14
- package/dist/derivation/derivation-proof.d.ts.map +0 -1
- package/dist/derivation/emit-capture-plan.d.ts +0 -8
- package/dist/derivation/emit-capture-plan.d.ts.map +0 -1
package/dist/instrument/build.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// src/auto-instrumentation/instrument-compiled-esm.ts
|
|
2
2
|
import { mkdirSync, readdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
3
3
|
import { dirname as dirname2, join as join2, relative as relative2, resolve as resolve5 } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
hashInstrumentationManifest
|
|
6
|
+
} from "@rasputin-ai/core";
|
|
4
7
|
|
|
5
8
|
// src/auto-instrumentation/instrumentation-manifest-registry.ts
|
|
6
9
|
import { createHash } from "node:crypto";
|
|
@@ -15,6 +18,17 @@ import {
|
|
|
15
18
|
runtimeFunctionId,
|
|
16
19
|
sourceLocatorFromFile
|
|
17
20
|
} from "@rasputin-ai/core";
|
|
21
|
+
var encodedPlanHasPreBodyParameterEvaluation = (plan) => plan[7] === 1;
|
|
22
|
+
var withPreBodyParameterEvaluation = (plan) => [
|
|
23
|
+
plan[0],
|
|
24
|
+
plan[1],
|
|
25
|
+
plan[2],
|
|
26
|
+
plan[3],
|
|
27
|
+
plan[4],
|
|
28
|
+
plan[5],
|
|
29
|
+
plan[6],
|
|
30
|
+
1
|
|
31
|
+
];
|
|
18
32
|
var encodeFunctionPlan = (source) => {
|
|
19
33
|
const locator = sourceLocatorFromFile(source.filePath);
|
|
20
34
|
const definition = {
|
|
@@ -41,7 +55,8 @@ var definitionFromEncodedPlan = (plan) => {
|
|
|
41
55
|
return {
|
|
42
56
|
functionId,
|
|
43
57
|
name,
|
|
44
|
-
...source ? { source } : {}
|
|
58
|
+
...source ? { source } : {},
|
|
59
|
+
...encodedPlanHasPreBodyParameterEvaluation(plan) ? { parentAttribution: "pre_body_parameter_evaluation" } : {}
|
|
45
60
|
};
|
|
46
61
|
};
|
|
47
62
|
|
|
@@ -51,13 +66,16 @@ var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
|
|
|
51
66
|
var getRegistry = () => {
|
|
52
67
|
const existing = Reflect.get(globalThis, registrySymbol);
|
|
53
68
|
if (existing?.__rasputinManifestRegistry && existing.files instanceof Map && typeof existing.generation === "number" && typeof existing.uploadedGeneration === "number") {
|
|
54
|
-
|
|
69
|
+
const registry2 = existing;
|
|
70
|
+
if (typeof registry2.alreadyInstrumented !== "number") registry2.alreadyInstrumented = 0;
|
|
71
|
+
return registry2;
|
|
55
72
|
}
|
|
56
73
|
const registry = {
|
|
57
74
|
__rasputinManifestRegistry: true,
|
|
58
75
|
files: /* @__PURE__ */ new Map(),
|
|
59
76
|
generation: 0,
|
|
60
|
-
uploadedGeneration: 0
|
|
77
|
+
uploadedGeneration: 0,
|
|
78
|
+
alreadyInstrumented: 0
|
|
61
79
|
};
|
|
62
80
|
Reflect.set(globalThis, registrySymbol, registry);
|
|
63
81
|
return registry;
|
|
@@ -69,16 +87,36 @@ var sourceAt = (filePath, line, column) => {
|
|
|
69
87
|
var stableId = (kind, ...parts) => `${kind}_${createHash("sha256").update(parts.join("\0")).digest("base64url").slice(0, 24)}`;
|
|
70
88
|
var recordInstrumentationManifestDelta = (filePath, delta) => {
|
|
71
89
|
const registry = getRegistry();
|
|
90
|
+
if (delta.alreadyInstrumented) {
|
|
91
|
+
registry.alreadyInstrumented += 1;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
72
94
|
registry.files.set(resolve(filePath), delta);
|
|
73
95
|
registry.generation += 1;
|
|
74
96
|
registry.onDirty?.();
|
|
75
97
|
};
|
|
98
|
+
var snapshotOptionsKey = (options) => JSON.stringify({
|
|
99
|
+
sourceRoot: options.sourceRoot ?? "",
|
|
100
|
+
sourceRoots: options.sourceRoots ?? {}
|
|
101
|
+
});
|
|
76
102
|
var snapshotInstrumentationManifest = (options = {}) => {
|
|
103
|
+
const registry = getRegistry();
|
|
104
|
+
const optionsKey = snapshotOptionsKey(options);
|
|
105
|
+
if (registry.snapshotCache && registry.snapshotCache.generation === registry.generation && registry.snapshotCache.optionsKey === optionsKey) {
|
|
106
|
+
return registry.snapshotCache.manifest;
|
|
107
|
+
}
|
|
77
108
|
const functions = [];
|
|
78
109
|
const callSites = [];
|
|
79
110
|
const destructures = [];
|
|
80
|
-
const
|
|
81
|
-
|
|
111
|
+
const remember = (manifest) => {
|
|
112
|
+
registry.snapshotCache = {
|
|
113
|
+
generation: registry.generation,
|
|
114
|
+
optionsKey,
|
|
115
|
+
manifest
|
|
116
|
+
};
|
|
117
|
+
return manifest;
|
|
118
|
+
};
|
|
119
|
+
for (const delta of registry.files.values()) {
|
|
82
120
|
for (const fn of delta.functions) {
|
|
83
121
|
const definition = definitionFromEncodedPlan(fn.plan);
|
|
84
122
|
if (!definition.source) continue;
|
|
@@ -90,7 +128,8 @@ var snapshotInstrumentationManifest = (options = {}) => {
|
|
|
90
128
|
startColumn: fn.startColumn,
|
|
91
129
|
endLine: fn.endLine,
|
|
92
130
|
endColumn: fn.endColumn,
|
|
93
|
-
params: fn.params
|
|
131
|
+
params: fn.params,
|
|
132
|
+
...fn.instrumented === false ? { instrumented: false, skipReason: fn.skipReason } : {}
|
|
94
133
|
});
|
|
95
134
|
}
|
|
96
135
|
for (const site of delta.callSites) {
|
|
@@ -129,25 +168,23 @@ var snapshotInstrumentationManifest = (options = {}) => {
|
|
|
129
168
|
binding: destructure.binding
|
|
130
169
|
});
|
|
131
170
|
}
|
|
132
|
-
derivationPlans.push(...delta.derivationPlans ?? []);
|
|
133
171
|
}
|
|
134
|
-
const registry = getRegistry();
|
|
135
172
|
if (registry.prebuilt && registry.files.size === 0) {
|
|
136
173
|
const prebuilt = registry.prebuilt;
|
|
137
|
-
if (!options.sourceRoot && !options.sourceRoots) return prebuilt;
|
|
138
|
-
return {
|
|
174
|
+
if (!options.sourceRoot && !options.sourceRoots) return remember(prebuilt);
|
|
175
|
+
return remember({
|
|
139
176
|
...prebuilt,
|
|
140
177
|
sourceRoots: {
|
|
141
178
|
...options.sourceRoot ? { default: options.sourceRoot } : {},
|
|
142
179
|
packages: { ...options.sourceRoots ?? {} }
|
|
143
180
|
}
|
|
144
|
-
};
|
|
181
|
+
});
|
|
145
182
|
}
|
|
146
183
|
if (functions.length === 0 && callSites.length === 0 && destructures.length === 0) {
|
|
147
|
-
return void 0;
|
|
184
|
+
return remember(void 0);
|
|
148
185
|
}
|
|
149
186
|
const packages = { ...options.sourceRoots ?? {} };
|
|
150
|
-
return {
|
|
187
|
+
return remember({
|
|
151
188
|
schema_version: INSTRUMENTATION_MANIFEST_SCHEMA_VERSION,
|
|
152
189
|
...options.sourceRoot || Object.keys(packages).length > 0 ? {
|
|
153
190
|
sourceRoots: {
|
|
@@ -157,9 +194,8 @@ var snapshotInstrumentationManifest = (options = {}) => {
|
|
|
157
194
|
} : {},
|
|
158
195
|
functions,
|
|
159
196
|
callSites,
|
|
160
|
-
destructures
|
|
161
|
-
|
|
162
|
-
};
|
|
197
|
+
destructures
|
|
198
|
+
});
|
|
163
199
|
};
|
|
164
200
|
var resetInstrumentationManifestRegistry = () => {
|
|
165
201
|
Reflect.deleteProperty(globalThis, registrySymbol);
|
|
@@ -253,664 +289,22 @@ var createSourceMapLocationMapper = (generatedPath, source) => {
|
|
|
253
289
|
};
|
|
254
290
|
|
|
255
291
|
// src/auto-instrumentation/transform-source.ts
|
|
256
|
-
import { resolve as resolve4 } from "node:path";
|
|
257
|
-
import
|
|
258
|
-
|
|
259
|
-
// src/derivation/analyze-function.ts
|
|
260
|
-
import ts from "typescript";
|
|
261
|
-
|
|
262
|
-
// src/derivation/build-dependency-graph.ts
|
|
263
|
-
var buildDependencyGraph = (functions, edges) => {
|
|
264
|
-
const names = new Set(functions.map((fn) => fn.name));
|
|
265
|
-
const localEdges = edges.filter((edge) => names.has(edge.calleeName));
|
|
266
|
-
const calleesByCaller = /* @__PURE__ */ new Map();
|
|
267
|
-
for (const edge of localEdges) {
|
|
268
|
-
const existing = calleesByCaller.get(edge.callerFunctionId) ?? [];
|
|
269
|
-
existing.push(edge.calleeName);
|
|
270
|
-
calleesByCaller.set(edge.callerFunctionId, existing);
|
|
271
|
-
}
|
|
272
|
-
return {
|
|
273
|
-
functions: functions.map((fn) => fn.functionId),
|
|
274
|
-
edges: localEdges,
|
|
275
|
-
calleesByCaller
|
|
276
|
-
};
|
|
277
|
-
};
|
|
278
|
-
var topologicalFunctionOrder = (functions, graph) => {
|
|
279
|
-
const idByName = new Map(functions.map((fn) => [fn.name, fn.functionId]));
|
|
280
|
-
const remainingCallees = new Map(
|
|
281
|
-
functions.map((fn) => {
|
|
282
|
-
const unique = new Set(
|
|
283
|
-
(graph.calleesByCaller.get(fn.functionId) ?? []).map((name) => idByName.get(name)).filter((id) => Boolean(id) && id !== fn.functionId)
|
|
284
|
-
);
|
|
285
|
-
return [fn.functionId, unique.size];
|
|
286
|
-
})
|
|
287
|
-
);
|
|
288
|
-
const callersOf = /* @__PURE__ */ new Map();
|
|
289
|
-
for (const fn of functions) {
|
|
290
|
-
for (const calleeName of graph.calleesByCaller.get(fn.functionId) ?? []) {
|
|
291
|
-
const calleeId = idByName.get(calleeName);
|
|
292
|
-
if (!calleeId || calleeId === fn.functionId) continue;
|
|
293
|
-
const callers = callersOf.get(calleeId) ?? [];
|
|
294
|
-
callers.push(fn.functionId);
|
|
295
|
-
callersOf.set(calleeId, callers);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
const queue = functions.filter((fn) => (remainingCallees.get(fn.functionId) ?? 0) === 0).map((fn) => fn.functionId);
|
|
299
|
-
const ordered = [];
|
|
300
|
-
const queued = new Set(queue);
|
|
301
|
-
while (queue.length > 0) {
|
|
302
|
-
const current = queue.shift();
|
|
303
|
-
if (current === void 0) break;
|
|
304
|
-
ordered.push(current);
|
|
305
|
-
for (const caller of callersOf.get(current) ?? []) {
|
|
306
|
-
const next = (remainingCallees.get(caller) ?? 0) - 1;
|
|
307
|
-
remainingCallees.set(caller, next);
|
|
308
|
-
if (next === 0 && !queued.has(caller)) {
|
|
309
|
-
queued.add(caller);
|
|
310
|
-
queue.push(caller);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
for (const fn of functions) {
|
|
315
|
-
if (!queued.has(fn.functionId)) ordered.push(fn.functionId);
|
|
316
|
-
}
|
|
317
|
-
return ordered;
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
// src/derivation/derivation-ir.ts
|
|
321
|
-
var DerivationIrBuilder = class {
|
|
322
|
-
expressions = [];
|
|
323
|
-
nextId = 0;
|
|
324
|
-
constValue(value) {
|
|
325
|
-
return this.add({ id: this.nextId++, kind: "const", value });
|
|
326
|
-
}
|
|
327
|
-
param(index) {
|
|
328
|
-
return this.add({ id: this.nextId++, kind: "param", index });
|
|
329
|
-
}
|
|
330
|
-
root(rootId) {
|
|
331
|
-
return this.add({ id: this.nextId++, kind: "root", rootId });
|
|
332
|
-
}
|
|
333
|
-
alias(from) {
|
|
334
|
-
return this.add({ id: this.nextId++, kind: "alias", from });
|
|
335
|
-
}
|
|
336
|
-
binary(op, left, right, guard) {
|
|
337
|
-
return this.add({ id: this.nextId++, kind: "binary", op, left, right, guard });
|
|
338
|
-
}
|
|
339
|
-
unary(op, operand) {
|
|
340
|
-
return this.add({ id: this.nextId++, kind: "unary", op, operand });
|
|
341
|
-
}
|
|
342
|
-
logical(op, left, right) {
|
|
343
|
-
return this.add({ id: this.nextId++, kind: "logical", op, left, right });
|
|
344
|
-
}
|
|
345
|
-
cond(test, consequent, alternate) {
|
|
346
|
-
return this.add({ id: this.nextId++, kind: "cond", test, consequent, alternate });
|
|
347
|
-
}
|
|
348
|
-
object(properties) {
|
|
349
|
-
return this.add({ id: this.nextId++, kind: "object", properties });
|
|
350
|
-
}
|
|
351
|
-
array(elements) {
|
|
352
|
-
return this.add({ id: this.nextId++, kind: "array", elements });
|
|
353
|
-
}
|
|
354
|
-
prop(object, key) {
|
|
355
|
-
return this.add({ id: this.nextId++, kind: "prop", object, key });
|
|
356
|
-
}
|
|
357
|
-
index(array, index) {
|
|
358
|
-
return this.add({ id: this.nextId++, kind: "index", array, index });
|
|
359
|
-
}
|
|
360
|
-
pureCall(calleeFunctionId, args2) {
|
|
361
|
-
return this.add({ id: this.nextId++, kind: "pure_call", calleeFunctionId, args: args2 });
|
|
362
|
-
}
|
|
363
|
-
template(parts) {
|
|
364
|
-
return this.add({ id: this.nextId++, kind: "template", parts });
|
|
365
|
-
}
|
|
366
|
-
add(expression) {
|
|
367
|
-
this.expressions.push(expression);
|
|
368
|
-
return expression.id;
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
var emptyPlan = (functionId) => ({
|
|
372
|
-
planId: `dp_${functionId}`,
|
|
373
|
-
functionId,
|
|
374
|
-
expressions: [],
|
|
375
|
-
variables: [],
|
|
376
|
-
captureBoundaries: [],
|
|
377
|
-
controlFacts: []
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
// src/derivation/analyze-function.ts
|
|
381
|
-
var loc = (node, sourceFile) => {
|
|
382
|
-
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
383
|
-
return { line: position.line + 1, column: position.character + 1 };
|
|
384
|
-
};
|
|
385
|
-
var lookup = (ctx, name) => {
|
|
386
|
-
for (let index = ctx.scopes.length - 1; index >= 0; index--) {
|
|
387
|
-
const binding = ctx.scopes[index]?.get(name);
|
|
388
|
-
if (binding) return binding;
|
|
389
|
-
}
|
|
390
|
-
return void 0;
|
|
391
|
-
};
|
|
392
|
-
var fallback = (ctx, reason) => {
|
|
393
|
-
ctx.fallback ??= reason;
|
|
394
|
-
return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "unknown" };
|
|
395
|
-
};
|
|
396
|
-
var capture = (ctx, node, reason) => {
|
|
397
|
-
const rootId = ctx.nextRootId.value++;
|
|
398
|
-
const position = loc(node, ctx.sourceFile);
|
|
399
|
-
const expressionId = ctx.ir.root(rootId);
|
|
400
|
-
ctx.captureWraps.push({
|
|
401
|
-
rootId,
|
|
402
|
-
reason,
|
|
403
|
-
start: node.getStart(ctx.sourceFile),
|
|
404
|
-
end: node.end,
|
|
405
|
-
line: position.line,
|
|
406
|
-
column: position.column,
|
|
407
|
-
expressionId
|
|
408
|
-
});
|
|
409
|
-
return { expressionId, shape: "unknown", captured: true };
|
|
410
|
-
};
|
|
411
|
-
var binaryOp = (kind) => {
|
|
412
|
-
switch (kind) {
|
|
413
|
-
case ts.SyntaxKind.PlusToken:
|
|
414
|
-
return "add";
|
|
415
|
-
case ts.SyntaxKind.MinusToken:
|
|
416
|
-
return "sub";
|
|
417
|
-
case ts.SyntaxKind.AsteriskToken:
|
|
418
|
-
return "mul";
|
|
419
|
-
case ts.SyntaxKind.SlashToken:
|
|
420
|
-
return "div";
|
|
421
|
-
case ts.SyntaxKind.PercentToken:
|
|
422
|
-
return "mod";
|
|
423
|
-
case ts.SyntaxKind.EqualsEqualsToken:
|
|
424
|
-
return "eq";
|
|
425
|
-
case ts.SyntaxKind.ExclamationEqualsToken:
|
|
426
|
-
return "neq";
|
|
427
|
-
case ts.SyntaxKind.EqualsEqualsEqualsToken:
|
|
428
|
-
return "seq";
|
|
429
|
-
case ts.SyntaxKind.ExclamationEqualsEqualsToken:
|
|
430
|
-
return "sneq";
|
|
431
|
-
case ts.SyntaxKind.LessThanToken:
|
|
432
|
-
return "lt";
|
|
433
|
-
case ts.SyntaxKind.LessThanEqualsToken:
|
|
434
|
-
return "lte";
|
|
435
|
-
case ts.SyntaxKind.GreaterThanToken:
|
|
436
|
-
return "gt";
|
|
437
|
-
case ts.SyntaxKind.GreaterThanEqualsToken:
|
|
438
|
-
return "gte";
|
|
439
|
-
default:
|
|
440
|
-
return void 0;
|
|
441
|
-
}
|
|
442
|
-
};
|
|
443
|
-
var isNumericBinaryOp = (op) => op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod";
|
|
444
|
-
var unwrapParens = (node) => {
|
|
445
|
-
while (ts.isParenthesizedExpression(node)) node = node.expression;
|
|
446
|
-
return node;
|
|
447
|
-
};
|
|
448
|
-
var numericLiteralValue = (node) => {
|
|
449
|
-
const value = Number(node.text);
|
|
450
|
-
return { type: "number", value };
|
|
451
|
-
};
|
|
452
|
-
var analyzeExpression = (ctx, node) => {
|
|
453
|
-
if (ctx.fallback) return fallback(ctx, ctx.fallback);
|
|
454
|
-
if (ts.isParenthesizedExpression(node)) return analyzeExpression(ctx, node.expression);
|
|
455
|
-
if (ts.isAsExpression(node) || ts.isNonNullExpression(node)) {
|
|
456
|
-
return analyzeExpression(ctx, node.expression);
|
|
457
|
-
}
|
|
458
|
-
if (node.kind === ts.SyntaxKind.TypeAssertionExpression) {
|
|
459
|
-
return analyzeExpression(ctx, node.expression);
|
|
460
|
-
}
|
|
461
|
-
if (ts.isSatisfiesExpression(node)) return analyzeExpression(ctx, node.expression);
|
|
462
|
-
if (node.kind === ts.SyntaxKind.NullKeyword) {
|
|
463
|
-
return { expressionId: ctx.ir.constValue({ type: "null" }), shape: "primitive" };
|
|
464
|
-
}
|
|
465
|
-
if (node.kind === ts.SyntaxKind.TrueKeyword) {
|
|
466
|
-
return {
|
|
467
|
-
expressionId: ctx.ir.constValue({ type: "boolean", value: true }),
|
|
468
|
-
shape: "primitive"
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
if (node.kind === ts.SyntaxKind.FalseKeyword) {
|
|
472
|
-
return {
|
|
473
|
-
expressionId: ctx.ir.constValue({ type: "boolean", value: false }),
|
|
474
|
-
shape: "primitive"
|
|
475
|
-
};
|
|
476
|
-
}
|
|
477
|
-
if (ts.isNumericLiteral(node)) {
|
|
478
|
-
return { expressionId: ctx.ir.constValue(numericLiteralValue(node)), shape: "primitive" };
|
|
479
|
-
}
|
|
480
|
-
if (ts.isPrefixUnaryExpression(node) && ts.isNumericLiteral(node.operand) && node.operator === ts.SyntaxKind.MinusToken) {
|
|
481
|
-
const value = -Number(node.operand.text);
|
|
482
|
-
return {
|
|
483
|
-
expressionId: ctx.ir.constValue(
|
|
484
|
-
Object.is(value, -0) ? { type: "number", value: 0, negativeZero: true } : { type: "number", value }
|
|
485
|
-
),
|
|
486
|
-
shape: "primitive"
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
490
|
-
return {
|
|
491
|
-
expressionId: ctx.ir.constValue({ type: "string", value: node.text }),
|
|
492
|
-
shape: "primitive"
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
if (ts.isBigIntLiteral(node)) {
|
|
496
|
-
return {
|
|
497
|
-
expressionId: ctx.ir.constValue({ type: "bigint", value: node.text.replace(/n$/, "") }),
|
|
498
|
-
shape: "primitive"
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
if (ts.isIdentifier(node)) {
|
|
502
|
-
if (node.text === "undefined") {
|
|
503
|
-
return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "primitive" };
|
|
504
|
-
}
|
|
505
|
-
const binding = lookup(ctx, node.text);
|
|
506
|
-
if (!binding) return capture(ctx, node, "external_state");
|
|
507
|
-
if (binding.mutable) return capture(ctx, node, "mutable_object");
|
|
508
|
-
return {
|
|
509
|
-
expressionId: ctx.ir.alias(binding.expressionId),
|
|
510
|
-
shape: binding.shape
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
if (ts.isTemplateExpression(node)) {
|
|
514
|
-
const parts = [
|
|
515
|
-
{ kind: "lit", value: node.head.text }
|
|
516
|
-
];
|
|
517
|
-
for (const span of node.templateSpans) {
|
|
518
|
-
const nested = analyzeExpression(ctx, span.expression);
|
|
519
|
-
if (nested.shape !== "primitive" && !nested.captured) {
|
|
520
|
-
return capture(ctx, node, "unsupported_expression");
|
|
521
|
-
}
|
|
522
|
-
parts.push({ kind: "expr", expressionId: nested.expressionId });
|
|
523
|
-
parts.push({ kind: "lit", value: span.literal.text });
|
|
524
|
-
}
|
|
525
|
-
return { expressionId: ctx.ir.template(parts), shape: "primitive" };
|
|
526
|
-
}
|
|
527
|
-
if (ts.isArrayLiteralExpression(node)) {
|
|
528
|
-
if (node.elements.some((element) => ts.isSpreadElement(element))) {
|
|
529
|
-
return capture(ctx, node, "unsupported_expression");
|
|
530
|
-
}
|
|
531
|
-
const elements = node.elements.map((element) => {
|
|
532
|
-
if (ts.isOmittedExpression(element)) return { kind: "hole" };
|
|
533
|
-
return analyzeExpression(ctx, element).expressionId;
|
|
534
|
-
});
|
|
535
|
-
return { expressionId: ctx.ir.array(elements), shape: "array" };
|
|
536
|
-
}
|
|
537
|
-
if (ts.isObjectLiteralExpression(node)) {
|
|
538
|
-
const properties = [];
|
|
539
|
-
for (const property of node.properties) {
|
|
540
|
-
if (ts.isShorthandPropertyAssignment(property)) {
|
|
541
|
-
const nested = analyzeExpression(ctx, property.name);
|
|
542
|
-
if (nested.captured) {
|
|
543
|
-
const wrap = ctx.captureWraps.find(
|
|
544
|
-
(candidate) => candidate.expressionId === nested.expressionId
|
|
545
|
-
);
|
|
546
|
-
if (wrap) wrap.shorthandPropertyName = property.name.text;
|
|
547
|
-
}
|
|
548
|
-
properties.push({ key: property.name.text, value: nested.expressionId });
|
|
549
|
-
continue;
|
|
550
|
-
}
|
|
551
|
-
if (!ts.isPropertyAssignment(property)) {
|
|
552
|
-
return capture(ctx, node, "unsupported_expression");
|
|
553
|
-
}
|
|
554
|
-
if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
|
|
555
|
-
return capture(ctx, node, "unsupported_expression");
|
|
556
|
-
}
|
|
557
|
-
const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
|
|
558
|
-
if (key === void 0) return capture(ctx, node, "unsupported_expression");
|
|
559
|
-
properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
|
|
560
|
-
}
|
|
561
|
-
return { expressionId: ctx.ir.object(properties), shape: "object" };
|
|
562
|
-
}
|
|
563
|
-
if (ts.isPropertyAccessExpression(node)) {
|
|
564
|
-
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
565
|
-
const object = analyzeExpression(ctx, node.expression);
|
|
566
|
-
if (object.shape === "object" && ts.isIdentifier(node.name)) {
|
|
567
|
-
return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
|
|
568
|
-
}
|
|
569
|
-
return capture(ctx, node, "unknown_property");
|
|
570
|
-
}
|
|
571
|
-
if (ts.isElementAccessExpression(node)) {
|
|
572
|
-
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
573
|
-
const array = analyzeExpression(ctx, node.expression);
|
|
574
|
-
if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
|
|
575
|
-
return {
|
|
576
|
-
expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
|
|
577
|
-
shape: "unknown"
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
return capture(ctx, node, "unknown_property");
|
|
581
|
-
}
|
|
582
|
-
if (ts.isCallExpression(node)) {
|
|
583
|
-
if (node.questionDotToken || node.typeArguments?.length) {
|
|
584
|
-
return capture(ctx, node, "unsupported_expression");
|
|
585
|
-
}
|
|
586
|
-
if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
|
|
587
|
-
const calleeName = node.expression.text;
|
|
588
|
-
if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
|
|
589
|
-
const provenId = ctx.moduleCallees.get(calleeName);
|
|
590
|
-
ctx.callEdges.push({
|
|
591
|
-
callerFunctionId: ctx.functionId,
|
|
592
|
-
calleeName,
|
|
593
|
-
...provenId ? { calleeFunctionId: provenId } : {}
|
|
594
|
-
});
|
|
595
|
-
if (!provenId) return capture(ctx, node, "opaque_call");
|
|
596
|
-
const args2 = node.arguments.map((argument) => {
|
|
597
|
-
if (ts.isSpreadElement(argument)) return void 0;
|
|
598
|
-
return analyzeExpression(ctx, argument).expressionId;
|
|
599
|
-
});
|
|
600
|
-
if (args2.some((argument) => argument === void 0))
|
|
601
|
-
return capture(ctx, node, "unsupported_expression");
|
|
602
|
-
return {
|
|
603
|
-
expressionId: ctx.ir.pureCall(
|
|
604
|
-
provenId,
|
|
605
|
-
args2.filter((id) => id !== void 0)
|
|
606
|
-
),
|
|
607
|
-
shape: "unknown"
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
|
|
611
|
-
return capture(
|
|
612
|
-
ctx,
|
|
613
|
-
node,
|
|
614
|
-
node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
|
|
615
|
-
);
|
|
616
|
-
}
|
|
617
|
-
if (ts.isConditionalExpression(node)) {
|
|
618
|
-
const test = analyzeExpression(ctx, node.condition);
|
|
619
|
-
const consequent = analyzeExpression(ctx, node.whenTrue);
|
|
620
|
-
const alternate = analyzeExpression(ctx, node.whenFalse);
|
|
621
|
-
return {
|
|
622
|
-
expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
|
|
623
|
-
shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
|
-
if (ts.isBinaryExpression(node)) {
|
|
627
|
-
if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
628
|
-
ctx.fallback ??= "mutable_state";
|
|
629
|
-
return capture(ctx, node, "mutable_object");
|
|
630
|
-
}
|
|
631
|
-
if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
632
|
-
const left2 = analyzeExpression(ctx, node.left);
|
|
633
|
-
const right2 = analyzeExpression(ctx, node.right);
|
|
634
|
-
const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
|
|
635
|
-
return {
|
|
636
|
-
expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
|
|
637
|
-
shape: "unknown"
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
const op = binaryOp(node.operatorToken.kind);
|
|
641
|
-
if (!op) return capture(ctx, node, "unsupported_expression");
|
|
642
|
-
if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
|
|
643
|
-
const left = analyzeExpression(ctx, node.left);
|
|
644
|
-
const right = analyzeExpression(ctx, node.right);
|
|
645
|
-
const numeric = isNumericBinaryOp(op);
|
|
646
|
-
const expressionId = ctx.ir.binary(
|
|
647
|
-
op,
|
|
648
|
-
left.expressionId,
|
|
649
|
-
right.expressionId,
|
|
650
|
-
numeric ? "numeric" : "primitive"
|
|
651
|
-
);
|
|
652
|
-
if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
|
|
653
|
-
ctx.hasCoercionGuard = true;
|
|
654
|
-
const fallbackRootId = ctx.nextRootId.value++;
|
|
655
|
-
ctx.guardWraps.push({
|
|
656
|
-
fallbackRootId,
|
|
657
|
-
start: node.getStart(ctx.sourceFile),
|
|
658
|
-
end: node.end,
|
|
659
|
-
op
|
|
660
|
-
});
|
|
661
|
-
}
|
|
662
|
-
return { expressionId, shape: "primitive" };
|
|
663
|
-
}
|
|
664
|
-
if (ts.isPrefixUnaryExpression(node)) {
|
|
665
|
-
const operand = analyzeExpression(ctx, node.operand);
|
|
666
|
-
if (node.operator === ts.SyntaxKind.ExclamationToken) {
|
|
667
|
-
return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
|
|
668
|
-
}
|
|
669
|
-
if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
|
|
670
|
-
if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
|
|
671
|
-
return {
|
|
672
|
-
expressionId: ctx.ir.unary(
|
|
673
|
-
node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
|
|
674
|
-
operand.expressionId
|
|
675
|
-
),
|
|
676
|
-
shape: "primitive"
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
return capture(ctx, node, "unsupported_expression");
|
|
680
|
-
}
|
|
681
|
-
if (ts.isTypeOfExpression(node)) {
|
|
682
|
-
const operand = unwrapParens(node.expression);
|
|
683
|
-
if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
|
|
684
|
-
return capture(ctx, node, "external_state");
|
|
685
|
-
}
|
|
686
|
-
const inner = analyzeExpression(ctx, node.expression);
|
|
687
|
-
return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
|
|
688
|
-
}
|
|
689
|
-
return capture(ctx, node, "unsupported_expression");
|
|
690
|
-
};
|
|
691
|
-
var bindIdentifier = (ctx, name, result2, declaration) => {
|
|
692
|
-
const current = ctx.scopes[ctx.scopes.length - 1];
|
|
693
|
-
current?.set(name.text, {
|
|
694
|
-
expressionId: result2.expressionId,
|
|
695
|
-
shape: result2.shape,
|
|
696
|
-
mutable: false
|
|
697
|
-
});
|
|
698
|
-
const position = loc(name, ctx.sourceFile);
|
|
699
|
-
const available = loc(declaration, ctx.sourceFile);
|
|
700
|
-
ctx.variables.push({
|
|
701
|
-
id: ctx.nextVariableId.value++,
|
|
702
|
-
name: name.text,
|
|
703
|
-
functionId: ctx.functionId,
|
|
704
|
-
scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
|
|
705
|
-
line: position.line,
|
|
706
|
-
column: position.column,
|
|
707
|
-
assignmentId: ctx.nextAssignmentId.value++,
|
|
708
|
-
expressionId: result2.expressionId,
|
|
709
|
-
availableAtLine: available.line,
|
|
710
|
-
availableAtColumn: available.column
|
|
711
|
-
});
|
|
712
|
-
};
|
|
713
|
-
var analyzeStatement = (ctx, node) => {
|
|
714
|
-
if (ctx.fallback) return;
|
|
715
|
-
if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
|
|
716
|
-
if (ts.isBlock(node)) {
|
|
717
|
-
ctx.scopes.push(/* @__PURE__ */ new Map());
|
|
718
|
-
for (const statement of node.statements) analyzeStatement(ctx, statement);
|
|
719
|
-
ctx.scopes.pop();
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
if (ts.isReturnStatement(node)) {
|
|
723
|
-
if (!node.expression) {
|
|
724
|
-
ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
|
|
725
|
-
return;
|
|
726
|
-
}
|
|
727
|
-
ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
|
|
728
|
-
return;
|
|
729
|
-
}
|
|
730
|
-
if (ts.isVariableStatement(node)) {
|
|
731
|
-
if (node.declarationList.flags & ts.NodeFlags.Using) {
|
|
732
|
-
fallback(ctx, "side_effect");
|
|
733
|
-
return;
|
|
734
|
-
}
|
|
735
|
-
for (const declaration of node.declarationList.declarations) {
|
|
736
|
-
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
|
|
737
|
-
fallback(ctx, "unsupported_expression");
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
|
-
const result2 = analyzeExpression(ctx, declaration.initializer);
|
|
741
|
-
bindIdentifier(ctx, declaration.name, result2, declaration);
|
|
742
|
-
}
|
|
743
|
-
return;
|
|
744
|
-
}
|
|
745
|
-
if (ts.isExpressionStatement(node)) {
|
|
746
|
-
analyzeExpression(ctx, node.expression);
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
if (ts.isThrowStatement(node)) {
|
|
750
|
-
ctx.hasThrow = true;
|
|
751
|
-
analyzeExpression(ctx, node.expression);
|
|
752
|
-
return;
|
|
753
|
-
}
|
|
754
|
-
if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
|
|
755
|
-
fallback(ctx, "unsupported_expression");
|
|
756
|
-
return;
|
|
757
|
-
}
|
|
758
|
-
if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
|
|
759
|
-
fallback(ctx, "has_loop");
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
fallback(ctx, "unsupported_expression");
|
|
763
|
-
};
|
|
764
|
-
var bindParams = (ctx, node) => {
|
|
765
|
-
let index = 0;
|
|
766
|
-
for (const parameter of node.parameters) {
|
|
767
|
-
if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
768
|
-
if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
|
|
769
|
-
fallback(ctx, "unsupported_expression");
|
|
770
|
-
index++;
|
|
771
|
-
continue;
|
|
772
|
-
}
|
|
773
|
-
const expressionId = ctx.ir.param(index);
|
|
774
|
-
ctx.identifierParams.push(parameter.name.text);
|
|
775
|
-
bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
|
|
776
|
-
index++;
|
|
777
|
-
}
|
|
778
|
-
};
|
|
779
|
-
var analyzeOne = (node, functionId, name, sourceFile, moduleCallees) => {
|
|
780
|
-
const ctx = {
|
|
781
|
-
sourceFile,
|
|
782
|
-
functionId,
|
|
783
|
-
name,
|
|
784
|
-
ir: new DerivationIrBuilder(),
|
|
785
|
-
variables: [],
|
|
786
|
-
captureWraps: [],
|
|
787
|
-
guardWraps: [],
|
|
788
|
-
callEdges: [],
|
|
789
|
-
scopes: [/* @__PURE__ */ new Map()],
|
|
790
|
-
nextRootId: { value: 0 },
|
|
791
|
-
nextVariableId: { value: 0 },
|
|
792
|
-
nextAssignmentId: { value: 0 },
|
|
793
|
-
hasThrow: false,
|
|
794
|
-
hasCoercionGuard: false,
|
|
795
|
-
moduleCallees,
|
|
796
|
-
identifierParams: []
|
|
797
|
-
};
|
|
798
|
-
if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
|
|
799
|
-
ctx.fallback = "async";
|
|
800
|
-
} else {
|
|
801
|
-
bindParams(ctx, node);
|
|
802
|
-
if (ts.isBlock(node.body)) {
|
|
803
|
-
for (const statement of node.body.statements) analyzeStatement(ctx, statement);
|
|
804
|
-
} else {
|
|
805
|
-
ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
const plan = {
|
|
809
|
-
...emptyPlan(functionId),
|
|
810
|
-
expressions: ctx.ir.expressions,
|
|
811
|
-
variables: ctx.variables,
|
|
812
|
-
captureBoundaries: ctx.captureWraps.map((wrap) => ({
|
|
813
|
-
id: wrap.rootId,
|
|
814
|
-
reason: wrap.reason,
|
|
815
|
-
expressionId: wrap.expressionId,
|
|
816
|
-
line: wrap.line,
|
|
817
|
-
column: wrap.column
|
|
818
|
-
})),
|
|
819
|
-
controlFacts: [],
|
|
820
|
-
...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
|
|
821
|
-
...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
|
|
822
|
-
};
|
|
823
|
-
return {
|
|
824
|
-
functionId,
|
|
825
|
-
name,
|
|
826
|
-
node,
|
|
827
|
-
plan,
|
|
828
|
-
captureWraps: ctx.fallback ? [] : ctx.captureWraps,
|
|
829
|
-
guardWraps: ctx.fallback ? [] : ctx.guardWraps,
|
|
830
|
-
callEdges: ctx.callEdges,
|
|
831
|
-
hasThrow: ctx.hasThrow,
|
|
832
|
-
hasCoercionGuard: ctx.hasCoercionGuard
|
|
833
|
-
};
|
|
834
|
-
};
|
|
835
|
-
var analyzeModuleFunctions = (sourceFile, targets) => {
|
|
836
|
-
const nameCounts = /* @__PURE__ */ new Map();
|
|
837
|
-
for (const target of targets) {
|
|
838
|
-
nameCounts.set(target.name, (nameCounts.get(target.name) ?? 0) + 1);
|
|
839
|
-
}
|
|
840
|
-
const firstPass = targets.map(
|
|
841
|
-
(target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
|
|
842
|
-
);
|
|
843
|
-
const graph = buildDependencyGraph(
|
|
844
|
-
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
845
|
-
firstPass.flatMap((fn) => fn.callEdges)
|
|
846
|
-
);
|
|
847
|
-
const order = topologicalFunctionOrder(
|
|
848
|
-
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
849
|
-
graph
|
|
850
|
-
);
|
|
851
|
-
const byId = new Map(targets.map((target) => [target.functionId, target]));
|
|
852
|
-
const moduleCallees = /* @__PURE__ */ new Map();
|
|
853
|
-
const analyzed = [];
|
|
854
|
-
for (const functionId of order) {
|
|
855
|
-
const target = byId.get(functionId);
|
|
856
|
-
if (!target) continue;
|
|
857
|
-
const result2 = analyzeOne(
|
|
858
|
-
target.node,
|
|
859
|
-
target.functionId,
|
|
860
|
-
target.name,
|
|
861
|
-
sourceFile,
|
|
862
|
-
moduleCallees
|
|
863
|
-
);
|
|
864
|
-
if (nameCounts.get(result2.name) === 1) {
|
|
865
|
-
moduleCallees.set(result2.name, result2.functionId);
|
|
866
|
-
}
|
|
867
|
-
analyzed.push(result2);
|
|
868
|
-
}
|
|
869
|
-
const remaining = targets.filter(
|
|
870
|
-
(target) => !analyzed.some((fn) => fn.functionId === target.functionId)
|
|
871
|
-
);
|
|
872
|
-
for (const target of remaining) {
|
|
873
|
-
analyzed.push(
|
|
874
|
-
analyzeOne(target.node, target.functionId, target.name, sourceFile, moduleCallees)
|
|
875
|
-
);
|
|
876
|
-
}
|
|
877
|
-
return analyzed;
|
|
878
|
-
};
|
|
879
|
-
|
|
880
|
-
// src/derivation/derivation-proof.ts
|
|
881
|
-
var functionIsExact = (plan) => plan.fallbackReason === void 0 && plan.captureBoundaries.length === 0 && plan.controlFacts.length === 0;
|
|
882
|
-
|
|
883
|
-
// src/derivation/emit-capture-plan.ts
|
|
884
|
-
var captureWrapEdits = (wraps, capName) => {
|
|
885
|
-
const edits = [];
|
|
886
|
-
let order = 0;
|
|
887
|
-
for (const wrap of wraps) {
|
|
888
|
-
edits.push({
|
|
889
|
-
position: wrap.start,
|
|
890
|
-
text: `${wrap.shorthandPropertyName ? `${wrap.shorthandPropertyName}:` : ""}${capName}(${wrap.rootId},`,
|
|
891
|
-
order: order++
|
|
892
|
-
});
|
|
893
|
-
edits.push({
|
|
894
|
-
position: wrap.end,
|
|
895
|
-
text: ")",
|
|
896
|
-
order: order++
|
|
897
|
-
});
|
|
898
|
-
}
|
|
899
|
-
return edits;
|
|
900
|
-
};
|
|
292
|
+
import { basename, resolve as resolve4 } from "node:path";
|
|
293
|
+
import { originalPositionFor as originalPositionFor2, TraceMap as TraceMap2 } from "@jridgewell/trace-mapping";
|
|
294
|
+
import ts6 from "typescript";
|
|
901
295
|
|
|
902
296
|
// src/execution-recorder/automatic-runtime.ts
|
|
903
|
-
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.
|
|
297
|
+
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v3";
|
|
904
298
|
var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
|
|
905
299
|
|
|
906
300
|
// src/auto-instrumentation/collect-instrumentation-delta.ts
|
|
907
|
-
import
|
|
301
|
+
import ts from "typescript";
|
|
908
302
|
var paramBindingFromName = (name, sourceFile) => {
|
|
909
|
-
if (
|
|
910
|
-
if (
|
|
303
|
+
if (ts.isIdentifier(name)) return { kind: "identifier", name: name.text };
|
|
304
|
+
if (ts.isArrayBindingPattern(name)) {
|
|
911
305
|
const elements = name.elements.map(
|
|
912
306
|
(element) => {
|
|
913
|
-
if (
|
|
307
|
+
if (ts.isOmittedExpression(element)) return { kind: "omitted" };
|
|
914
308
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
915
309
|
if (element.dotDotDotToken) return { kind: "rest", binding };
|
|
916
310
|
return binding;
|
|
@@ -920,7 +314,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
920
314
|
}
|
|
921
315
|
const properties = name.elements.map((element) => {
|
|
922
316
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
923
|
-
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") :
|
|
317
|
+
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
|
|
924
318
|
if (element.dotDotDotToken) {
|
|
925
319
|
const rest = { kind: "rest", binding };
|
|
926
320
|
return { key, binding: rest };
|
|
@@ -932,7 +326,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
932
326
|
var collectFunctionParams = (node, sourceFile) => {
|
|
933
327
|
const params = [];
|
|
934
328
|
for (const parameter of node.parameters) {
|
|
935
|
-
if (
|
|
329
|
+
if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
936
330
|
const binding = paramBindingFromName(parameter.name, sourceFile);
|
|
937
331
|
params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
|
|
938
332
|
}
|
|
@@ -973,18 +367,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
|
|
|
973
367
|
};
|
|
974
368
|
var unwrapExpression = (expression) => {
|
|
975
369
|
let current = expression;
|
|
976
|
-
while (
|
|
370
|
+
while (ts.isParenthesizedExpression(current) || ts.isNonNullExpression(current)) {
|
|
977
371
|
current = current.expression;
|
|
978
372
|
}
|
|
979
|
-
if (
|
|
373
|
+
if (ts.isAwaitExpression(current)) return unwrapExpression(current.expression);
|
|
980
374
|
return current;
|
|
981
375
|
};
|
|
982
376
|
var calleeFromExpression = (expression, sourceFile) => {
|
|
983
377
|
const current = unwrapExpression(expression);
|
|
984
|
-
if (
|
|
378
|
+
if (ts.isIdentifier(current)) {
|
|
985
379
|
return { calleeName: current.text, calleeText: current.text };
|
|
986
380
|
}
|
|
987
|
-
if (
|
|
381
|
+
if (ts.isPropertyAccessExpression(current) && ts.isIdentifier(current.name)) {
|
|
988
382
|
return {
|
|
989
383
|
calleeName: current.name.text,
|
|
990
384
|
calleeText: current.getText(sourceFile).replace(/\s+/g, "")
|
|
@@ -994,9 +388,9 @@ var calleeFromExpression = (expression, sourceFile) => {
|
|
|
994
388
|
};
|
|
995
389
|
var resultBindingForCall = (call) => {
|
|
996
390
|
let node = call.parent;
|
|
997
|
-
if (
|
|
998
|
-
if (!
|
|
999
|
-
if (!
|
|
391
|
+
if (ts.isAwaitExpression(node)) node = node.parent;
|
|
392
|
+
if (!ts.isVariableDeclaration(node)) return null;
|
|
393
|
+
if (!ts.isIdentifier(node.name)) return null;
|
|
1000
394
|
const initializer = node.initializer;
|
|
1001
395
|
if (!initializer) return null;
|
|
1002
396
|
const inner = unwrapExpression(initializer);
|
|
@@ -1006,18 +400,18 @@ var resultBindingForCall = (call) => {
|
|
|
1006
400
|
var argIdentifiersForCall = (call) => {
|
|
1007
401
|
const identifiers = [];
|
|
1008
402
|
call.arguments?.forEach((argument, index) => {
|
|
1009
|
-
if (
|
|
403
|
+
if (ts.isIdentifier(argument)) {
|
|
1010
404
|
identifiers.push({ index, kind: "identifier", name: argument.text });
|
|
1011
405
|
return;
|
|
1012
406
|
}
|
|
1013
|
-
if (!
|
|
407
|
+
if (!ts.isObjectLiteralExpression(argument)) return;
|
|
1014
408
|
const properties = [];
|
|
1015
409
|
for (const property of argument.properties) {
|
|
1016
|
-
if (
|
|
410
|
+
if (ts.isShorthandPropertyAssignment(property)) {
|
|
1017
411
|
properties.push({ key: property.name.text, name: property.name.text });
|
|
1018
412
|
continue;
|
|
1019
413
|
}
|
|
1020
|
-
if (
|
|
414
|
+
if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && ts.isIdentifier(property.initializer)) {
|
|
1021
415
|
properties.push({ key: property.name.text, name: property.initializer.text });
|
|
1022
416
|
}
|
|
1023
417
|
}
|
|
@@ -1057,8 +451,8 @@ var collectCallSite = (call, caller, context) => {
|
|
|
1057
451
|
};
|
|
1058
452
|
};
|
|
1059
453
|
var collectParamDestructure = (declaration, caller, context) => {
|
|
1060
|
-
if (!declaration.initializer || !
|
|
1061
|
-
if (!
|
|
454
|
+
if (!declaration.initializer || !ts.isIdentifier(declaration.initializer)) return null;
|
|
455
|
+
if (!ts.isObjectBindingPattern(declaration.name) && !ts.isArrayBindingPattern(declaration.name)) {
|
|
1062
456
|
return null;
|
|
1063
457
|
}
|
|
1064
458
|
const sourceParam = declaration.initializer.text;
|
|
@@ -1080,58 +474,483 @@ var collectParamDestructure = (declaration, caller, context) => {
|
|
|
1080
474
|
};
|
|
1081
475
|
};
|
|
1082
476
|
|
|
1083
|
-
// src/auto-instrumentation/
|
|
477
|
+
// src/auto-instrumentation/parameter-initializer-may-run-code.ts
|
|
478
|
+
import ts2 from "typescript";
|
|
479
|
+
var unwrap = (expression) => {
|
|
480
|
+
let current = expression;
|
|
481
|
+
while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
|
|
482
|
+
current = current.expression;
|
|
483
|
+
}
|
|
484
|
+
return current;
|
|
485
|
+
};
|
|
486
|
+
var isSafeLiteralInitializer = (expression) => {
|
|
487
|
+
const current = unwrap(expression);
|
|
488
|
+
if (ts2.isNumericLiteral(current) || ts2.isStringLiteral(current) || ts2.isNoSubstitutionTemplateLiteral(current) || ts2.isBigIntLiteral(current)) {
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
if (current.kind === ts2.SyntaxKind.TrueKeyword || current.kind === ts2.SyntaxKind.FalseKeyword || current.kind === ts2.SyntaxKind.NullKeyword) {
|
|
492
|
+
return true;
|
|
493
|
+
}
|
|
494
|
+
if (ts2.isIdentifier(current) && current.text === "undefined") return true;
|
|
495
|
+
if (ts2.isPrefixUnaryExpression(current) && (current.operator === ts2.SyntaxKind.PlusToken || current.operator === ts2.SyntaxKind.MinusToken)) {
|
|
496
|
+
return ts2.isNumericLiteral(unwrap(current.operand));
|
|
497
|
+
}
|
|
498
|
+
return false;
|
|
499
|
+
};
|
|
500
|
+
var functionHasPreBodyParameterEvaluation = (node) => {
|
|
501
|
+
for (const parameter of node.parameters) {
|
|
502
|
+
if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
503
|
+
if (!parameter.initializer) continue;
|
|
504
|
+
if (!isSafeLiteralInitializer(parameter.initializer)) return true;
|
|
505
|
+
}
|
|
506
|
+
return false;
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
// src/auto-instrumentation/suspension-whitelist.ts
|
|
510
|
+
import ts3 from "typescript";
|
|
511
|
+
var nodeFlags = ts3.NodeFlags;
|
|
512
|
+
var AWAIT_USING_FLAG = nodeFlags.AwaitUsing ?? 0;
|
|
513
|
+
var USING_FLAG = nodeFlags.Using ?? 0;
|
|
514
|
+
var KNOWN_SUSPENSION_KIND_NAMES = [
|
|
515
|
+
"AwaitExpression",
|
|
516
|
+
"AwaitKeyword",
|
|
517
|
+
"YieldExpression",
|
|
518
|
+
"YieldKeyword",
|
|
519
|
+
"UsingKeyword"
|
|
520
|
+
];
|
|
521
|
+
var isFunctionLike = (node) => ts3.isFunctionLike(node);
|
|
522
|
+
var isAsyncFunctionLike = (node) => (ts3.getCombinedModifierFlags(node) & ts3.ModifierFlags.Async) !== 0;
|
|
523
|
+
var isAsyncGeneratorLike = (node) => isFunctionLike(node) && isAsyncFunctionLike(node) && "asteriskToken" in node && Boolean(node.asteriskToken);
|
|
524
|
+
var isSyncGeneratorLike = (node) => isFunctionLike(node) && "asteriskToken" in node && Boolean(node.asteriskToken) && !isAsyncFunctionLike(node);
|
|
525
|
+
var visitOwnBody = (node, visitNode) => {
|
|
526
|
+
if (!node.body) return;
|
|
527
|
+
const visit = (candidate) => {
|
|
528
|
+
if (candidate !== node && isFunctionLike(candidate)) {
|
|
529
|
+
visitNode(candidate);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (visitNode(candidate)) return;
|
|
533
|
+
ts3.forEachChild(candidate, visit);
|
|
534
|
+
};
|
|
535
|
+
visit(node.body);
|
|
536
|
+
};
|
|
537
|
+
var nestedFunctionSpans = (node, sourceFile) => {
|
|
538
|
+
const spans = [];
|
|
539
|
+
visitOwnBody(node, (candidate) => {
|
|
540
|
+
if (candidate !== node && isFunctionLike(candidate)) {
|
|
541
|
+
spans.push([candidate.getStart(sourceFile), candidate.end]);
|
|
542
|
+
}
|
|
543
|
+
return void 0;
|
|
544
|
+
});
|
|
545
|
+
return spans.sort((left, right) => left[0] - right[0]);
|
|
546
|
+
};
|
|
547
|
+
var slicesOutside = (start, end, spans) => {
|
|
548
|
+
const slices = [];
|
|
549
|
+
let cursor = start;
|
|
550
|
+
for (const [from, to] of spans) {
|
|
551
|
+
if (to <= cursor) continue;
|
|
552
|
+
if (from >= end) break;
|
|
553
|
+
if (from > cursor) slices.push([cursor, Math.min(from, end)]);
|
|
554
|
+
cursor = Math.max(cursor, to);
|
|
555
|
+
}
|
|
556
|
+
if (cursor < end) slices.push([cursor, end]);
|
|
557
|
+
return slices;
|
|
558
|
+
};
|
|
559
|
+
var sliceHasAwaitKeyword = (source, start, end) => {
|
|
560
|
+
if (end <= start) return false;
|
|
561
|
+
const scanner = ts3.createScanner(
|
|
562
|
+
ts3.ScriptTarget.Latest,
|
|
563
|
+
true,
|
|
564
|
+
ts3.LanguageVariant.Standard,
|
|
565
|
+
source.slice(start, end)
|
|
566
|
+
);
|
|
567
|
+
while (true) {
|
|
568
|
+
const token = scanner.scan();
|
|
569
|
+
if (token === ts3.SyntaxKind.EndOfFileToken) return false;
|
|
570
|
+
if (token === ts3.SyntaxKind.AwaitKeyword) return true;
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
var ownBodyHasAwaitKeyword = (node, sourceFile) => {
|
|
574
|
+
if (!node.body) return false;
|
|
575
|
+
const spans = nestedFunctionSpans(node, sourceFile);
|
|
576
|
+
for (const [start, end] of slicesOutside(node.body.getStart(sourceFile), node.body.end, spans)) {
|
|
577
|
+
if (sliceHasAwaitKeyword(sourceFile.text, start, end)) return true;
|
|
578
|
+
}
|
|
579
|
+
return false;
|
|
580
|
+
};
|
|
581
|
+
var isAwaitUsingList = (node) => {
|
|
582
|
+
const text = node.getText().trimStart();
|
|
583
|
+
if (text.startsWith("await using")) return true;
|
|
584
|
+
if (AWAIT_USING_FLAG === 0 || USING_FLAG === 0) return false;
|
|
585
|
+
return (node.flags & AWAIT_USING_FLAG) === AWAIT_USING_FLAG && (node.flags & USING_FLAG) !== 0;
|
|
586
|
+
};
|
|
587
|
+
var unknownSuspensionKind = (node) => {
|
|
588
|
+
const name = ts3.SyntaxKind[node.kind];
|
|
589
|
+
if (typeof name !== "string" || !/Await|Yield|Using/.test(name)) return false;
|
|
590
|
+
return !KNOWN_SUSPENSION_KIND_NAMES.includes(name);
|
|
591
|
+
};
|
|
592
|
+
var ownSuspensionFacts = (node, sourceFile) => {
|
|
593
|
+
const facts = {
|
|
594
|
+
awaitExpressions: false,
|
|
595
|
+
forAwait: false,
|
|
596
|
+
awaitUsing: false,
|
|
597
|
+
unknown: false,
|
|
598
|
+
awaitKeyword: ownBodyHasAwaitKeyword(node, sourceFile)
|
|
599
|
+
};
|
|
600
|
+
visitOwnBody(node, (candidate) => {
|
|
601
|
+
if (ts3.isAwaitExpression(candidate)) facts.awaitExpressions = true;
|
|
602
|
+
if (ts3.isForOfStatement(candidate) && candidate.awaitModifier) facts.forAwait = true;
|
|
603
|
+
if (ts3.isVariableDeclarationList(candidate) && isAwaitUsingList(candidate)) {
|
|
604
|
+
facts.awaitUsing = true;
|
|
605
|
+
}
|
|
606
|
+
if (unknownSuspensionKind(candidate)) facts.unknown = true;
|
|
607
|
+
return void 0;
|
|
608
|
+
});
|
|
609
|
+
return facts;
|
|
610
|
+
};
|
|
611
|
+
var bodyHasDirectEval = (node) => {
|
|
612
|
+
let found = false;
|
|
613
|
+
visitOwnBody(node, (candidate) => {
|
|
614
|
+
if (ts3.isCallExpression(candidate) && ts3.isIdentifier(candidate.expression) && candidate.expression.text === "eval") {
|
|
615
|
+
found = true;
|
|
616
|
+
}
|
|
617
|
+
return found;
|
|
618
|
+
});
|
|
619
|
+
return found;
|
|
620
|
+
};
|
|
621
|
+
var decideAsyncInstrumentation = (node, sourceFile) => {
|
|
622
|
+
if (isAsyncGeneratorLike(node)) return { action: "skip", reason: "unsupported_async_generator" };
|
|
623
|
+
if (!isAsyncFunctionLike(node)) return { action: "inline" };
|
|
624
|
+
if (bodyHasDirectEval(node)) return { action: "skip", reason: "direct_eval" };
|
|
625
|
+
const facts = ownSuspensionFacts(node, sourceFile);
|
|
626
|
+
if (facts.unknown) return { action: "skip", reason: "unknown_suspension" };
|
|
627
|
+
if (facts.forAwait) return { action: "skip", reason: "unsupported_for_await" };
|
|
628
|
+
if (facts.awaitUsing) return { action: "skip", reason: "await_using" };
|
|
629
|
+
if (facts.awaitKeyword && !facts.awaitExpressions) {
|
|
630
|
+
return { action: "skip", reason: "unknown_suspension" };
|
|
631
|
+
}
|
|
632
|
+
if (facts.awaitExpressions) return { action: "suspend_resume" };
|
|
633
|
+
return { action: "inline" };
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
// src/auto-instrumentation/verify-instrumentation-isomorphism.ts
|
|
637
|
+
import ts4 from "typescript";
|
|
638
|
+
var RASPUTIN_IDENT = /^__rasputin/;
|
|
1084
639
|
var scriptKindFor = (filePath) => {
|
|
1085
|
-
if (/\.tsx$/i.test(filePath)) return
|
|
1086
|
-
if (/\.jsx$/i.test(filePath)) return
|
|
1087
|
-
if (/\.[cm]?ts$/i.test(filePath)) return
|
|
1088
|
-
return
|
|
640
|
+
if (/\.tsx$/i.test(filePath)) return ts4.ScriptKind.TSX;
|
|
641
|
+
if (/\.jsx$/i.test(filePath)) return ts4.ScriptKind.JSX;
|
|
642
|
+
if (/\.[cm]?ts$/i.test(filePath)) return ts4.ScriptKind.TS;
|
|
643
|
+
return ts4.ScriptKind.JS;
|
|
644
|
+
};
|
|
645
|
+
var parse = (filePath, source) => ts4.createSourceFile(filePath, source, ts4.ScriptTarget.Latest, true, scriptKindFor(filePath));
|
|
646
|
+
var isRasputinName = (name) => RASPUTIN_IDENT.test(name);
|
|
647
|
+
var identText = (node) => {
|
|
648
|
+
if (ts4.isIdentifier(node)) return node.text;
|
|
649
|
+
if (ts4.isPrivateIdentifier(node)) return node.text;
|
|
650
|
+
return void 0;
|
|
651
|
+
};
|
|
652
|
+
var calleeName = (expression) => {
|
|
653
|
+
if (ts4.isIdentifier(expression)) return expression.text;
|
|
654
|
+
if (ts4.isPropertyAccessExpression(expression)) {
|
|
655
|
+
const object = identText(expression.expression);
|
|
656
|
+
const property = identText(expression.name);
|
|
657
|
+
if (object && isRasputinName(object) && property) return `${object}.${property}`;
|
|
658
|
+
}
|
|
659
|
+
return void 0;
|
|
660
|
+
};
|
|
661
|
+
var isRasputinCall = (node, methods) => {
|
|
662
|
+
if (!ts4.isCallExpression(node)) return false;
|
|
663
|
+
const name = calleeName(node.expression);
|
|
664
|
+
if (!name) return false;
|
|
665
|
+
if (isRasputinName(name)) return !methods || methods.has(name.split(".")[1] ?? "");
|
|
666
|
+
return false;
|
|
667
|
+
};
|
|
668
|
+
var unwrapCall = (node) => {
|
|
669
|
+
let current = node;
|
|
670
|
+
while (isRasputinCall(current) && current.arguments.length === 1 && current.arguments[0]) {
|
|
671
|
+
current = current.arguments[0];
|
|
672
|
+
if (ts4.isParenthesizedExpression(current)) current = current.expression;
|
|
673
|
+
}
|
|
674
|
+
if (ts4.isParenthesizedExpression(current)) return current.expression;
|
|
675
|
+
return current;
|
|
676
|
+
};
|
|
677
|
+
var isRasputinStatement = (node) => {
|
|
678
|
+
if (ts4.isEmptyStatement(node)) return true;
|
|
679
|
+
if (ts4.isVariableStatement(node)) {
|
|
680
|
+
return node.declarationList.declarations.every((declaration) => {
|
|
681
|
+
const name = identText(declaration.name);
|
|
682
|
+
return Boolean(name && isRasputinName(name));
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
if (ts4.isExpressionStatement(node)) {
|
|
686
|
+
if (!isRasputinCall(node.expression)) return false;
|
|
687
|
+
const unwrapped = unwrapCall(node.expression);
|
|
688
|
+
return unwrapped === node.expression;
|
|
689
|
+
}
|
|
690
|
+
return false;
|
|
691
|
+
};
|
|
692
|
+
var isRasputinTryWrapper = (node) => {
|
|
693
|
+
const catchClause = node.catchClause;
|
|
694
|
+
if (!catchClause || !node.finallyBlock) return false;
|
|
695
|
+
const catchCalls = catchClause.block.statements.filter(
|
|
696
|
+
(statement) => !isRasputinStatement(statement)
|
|
697
|
+
);
|
|
698
|
+
const leftover = catchCalls[0];
|
|
699
|
+
const leftoverThrow = catchCalls.length === 1 && leftover !== void 0 && ts4.isThrowStatement(leftover) && ts4.isIdentifier(leftover.expression) && isRasputinName(leftover.expression.text);
|
|
700
|
+
if (!leftoverThrow && catchCalls.length > 0) return false;
|
|
701
|
+
return node.finallyBlock.statements.every((statement) => isRasputinStatement(statement));
|
|
702
|
+
};
|
|
703
|
+
var flattenStatements = (statements) => {
|
|
704
|
+
const flattened = [];
|
|
705
|
+
for (const statement of statements) {
|
|
706
|
+
if (isRasputinStatement(statement)) continue;
|
|
707
|
+
if (ts4.isTryStatement(statement) && isRasputinTryWrapper(statement)) {
|
|
708
|
+
flattened.push(...flattenStatements(statement.tryBlock.statements));
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
flattened.push(statement);
|
|
712
|
+
}
|
|
713
|
+
return flattened;
|
|
714
|
+
};
|
|
715
|
+
var extra = (node) => {
|
|
716
|
+
if (ts4.isIdentifier(node) || ts4.isPrivateIdentifier(node)) {
|
|
717
|
+
return isRasputinName(node.text) ? "" : node.text;
|
|
718
|
+
}
|
|
719
|
+
if (ts4.isStringLiteral(node) || ts4.isNumericLiteral(node) || ts4.isNoSubstitutionTemplateLiteral(node)) {
|
|
720
|
+
return node.text;
|
|
721
|
+
}
|
|
722
|
+
if (ts4.isToken(node) && !ts4.isIdentifier(node)) return ts4.SyntaxKind[node.kind] ?? "";
|
|
723
|
+
return "";
|
|
724
|
+
};
|
|
725
|
+
var signatureOf = (node) => {
|
|
726
|
+
const unwrapped = unwrapCall(node);
|
|
727
|
+
if (unwrapped !== node) return signatureOf(unwrapped);
|
|
728
|
+
if (ts4.isEmptyStatement(unwrapped) || isRasputinStatement(unwrapped)) return "";
|
|
729
|
+
if (ts4.isTryStatement(unwrapped) && isRasputinTryWrapper(unwrapped)) {
|
|
730
|
+
return flattenStatements(unwrapped.tryBlock.statements).map(signatureOf).filter(Boolean).join("");
|
|
731
|
+
}
|
|
732
|
+
if (ts4.isBlock(unwrapped)) {
|
|
733
|
+
const statements = flattenStatements(unwrapped.statements);
|
|
734
|
+
return `Block[${statements.map(signatureOf).filter(Boolean).join(",")}]`;
|
|
735
|
+
}
|
|
736
|
+
if (ts4.isCatchClause(unwrapped)) {
|
|
737
|
+
const statements = flattenStatements(unwrapped.block.statements);
|
|
738
|
+
const variable = unwrapped.variableDeclaration ? signatureOf(unwrapped.variableDeclaration) : "";
|
|
739
|
+
return `Catch${variable}[${statements.map(signatureOf).filter(Boolean).join(",")}]`;
|
|
740
|
+
}
|
|
741
|
+
if (ts4.isArrowFunction(unwrapped) && ts4.isBlock(unwrapped.body)) {
|
|
742
|
+
const statements = flattenStatements(unwrapped.body.statements);
|
|
743
|
+
const only = statements[0];
|
|
744
|
+
if (statements.length === 1 && only && ts4.isReturnStatement(only) && only.expression) {
|
|
745
|
+
const expression = only.expression;
|
|
746
|
+
const parts2 = [];
|
|
747
|
+
ts4.forEachChild(unwrapped, (child) => {
|
|
748
|
+
if (child === unwrapped.body) {
|
|
749
|
+
const part2 = signatureOf(expression);
|
|
750
|
+
if (part2) parts2.push(part2);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const part = signatureOf(child);
|
|
754
|
+
if (part) parts2.push(part);
|
|
755
|
+
});
|
|
756
|
+
return `ArrowFunction[${parts2.join(",")}]`;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
const parts = [];
|
|
760
|
+
ts4.forEachChild(unwrapped, (child) => {
|
|
761
|
+
const part = signatureOf(child);
|
|
762
|
+
if (part) parts.push(part);
|
|
763
|
+
});
|
|
764
|
+
const kind = ts4.SyntaxKind[unwrapped.kind] ?? "Node";
|
|
765
|
+
const label = extra(unwrapped);
|
|
766
|
+
return `${kind}${label}[${parts.join(",")}]`;
|
|
767
|
+
};
|
|
768
|
+
var instrumentationTreesAreIsomorphic = (filePath, original, transformed) => {
|
|
769
|
+
return signatureOf(parse(filePath, original)) === signatureOf(parse(filePath, transformed));
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
// src/auto-instrumentation/verify-transformed-source.ts
|
|
773
|
+
import ts5 from "typescript";
|
|
774
|
+
var transpileForVerification = (filePath, code) => ts5.transpileModule(code, {
|
|
775
|
+
compilerOptions: {
|
|
776
|
+
// ES2020 downlevel keeps native @decorators out of `new Function`.
|
|
777
|
+
target: ts5.ScriptTarget.ES2020,
|
|
778
|
+
module: ts5.ModuleKind.ESNext,
|
|
779
|
+
allowJs: true,
|
|
780
|
+
jsx: ts5.JsxEmit.React,
|
|
781
|
+
experimentalDecorators: true
|
|
782
|
+
},
|
|
783
|
+
reportDiagnostics: true,
|
|
784
|
+
fileName: filePath
|
|
785
|
+
});
|
|
786
|
+
var hasScanOrParseError = (diagnostics) => Boolean(
|
|
787
|
+
diagnostics?.some(
|
|
788
|
+
(diagnostic) => diagnostic.category === ts5.DiagnosticCategory.Error && diagnostic.code < 2e3
|
|
789
|
+
)
|
|
790
|
+
);
|
|
791
|
+
var scriptBodyForEngine = (emitted) => (
|
|
792
|
+
// File loaders strip a leading shebang; `new Function` does not.
|
|
793
|
+
emitted.replace(/^#![^\n]*\n/, "").replaceAll("import.meta", "({})")
|
|
794
|
+
);
|
|
795
|
+
var looksTyped = (filePath) => /\.[cm]?tsx?$/i.test(filePath);
|
|
796
|
+
var scriptKindFor2 = (filePath) => {
|
|
797
|
+
if (/\.tsx$/i.test(filePath)) return ts5.ScriptKind.TSX;
|
|
798
|
+
if (/\.jsx$/i.test(filePath)) return ts5.ScriptKind.JSX;
|
|
799
|
+
if (/\.[cm]?ts$/i.test(filePath)) return ts5.ScriptKind.TS;
|
|
800
|
+
return ts5.ScriptKind.JS;
|
|
801
|
+
};
|
|
802
|
+
var isIterationStatement = (node) => ts5.isForStatement(node) || ts5.isForOfStatement(node) || ts5.isForInStatement(node) || ts5.isWhileStatement(node) || ts5.isDoStatement(node);
|
|
803
|
+
var unwrapLabeled = (node) => {
|
|
804
|
+
let current = node;
|
|
805
|
+
while (ts5.isLabeledStatement(current)) current = current.statement;
|
|
806
|
+
return current;
|
|
807
|
+
};
|
|
808
|
+
var hasInvalidLabelJump = (sourceFile) => {
|
|
809
|
+
const visit = (node, labels) => {
|
|
810
|
+
if (ts5.isFunctionLike(node)) {
|
|
811
|
+
let nestedInvalid = false;
|
|
812
|
+
ts5.forEachChild(node, (child) => {
|
|
813
|
+
if (visit(child, /* @__PURE__ */ new Map())) nestedInvalid = true;
|
|
814
|
+
});
|
|
815
|
+
return nestedInvalid;
|
|
816
|
+
}
|
|
817
|
+
if (ts5.isLabeledStatement(node)) {
|
|
818
|
+
const next = new Map(labels);
|
|
819
|
+
next.set(node.label.text, isIterationStatement(unwrapLabeled(node.statement)));
|
|
820
|
+
return visit(node.statement, next);
|
|
821
|
+
}
|
|
822
|
+
if (ts5.isContinueStatement(node) && node.label) {
|
|
823
|
+
if (labels.get(node.label.text) !== true) return true;
|
|
824
|
+
}
|
|
825
|
+
if (ts5.isBreakStatement(node) && node.label && !labels.has(node.label.text)) {
|
|
826
|
+
return true;
|
|
827
|
+
}
|
|
828
|
+
let invalid = false;
|
|
829
|
+
ts5.forEachChild(node, (child) => {
|
|
830
|
+
if (visit(child, labels)) invalid = true;
|
|
831
|
+
});
|
|
832
|
+
return invalid;
|
|
833
|
+
};
|
|
834
|
+
return visit(sourceFile, /* @__PURE__ */ new Map());
|
|
835
|
+
};
|
|
836
|
+
var importSpecifier = `('[^']+'|"[^"]+")`;
|
|
837
|
+
var importAttributes = String.raw`(?:\s+with\s*\{[^}]*\})?`;
|
|
838
|
+
var stripModuleSyntax = (body) => body.replace(/\bexport\s+default\b/g, "var __rasputinVerifyDefault =").replace(new RegExp(String.raw`\bexport\s*\*[\s\S]*?from\s+${importSpecifier}`, "g"), "").replace(
|
|
839
|
+
new RegExp(
|
|
840
|
+
String.raw`\bexport\s*\{[^}]*\}\s*from\s+${importSpecifier}${importAttributes}`,
|
|
841
|
+
"g"
|
|
842
|
+
),
|
|
843
|
+
""
|
|
844
|
+
).replace(/\bexport\s*\{[^}]*\}/g, "").replace(/\bexport\s+/g, "").replace(
|
|
845
|
+
new RegExp(
|
|
846
|
+
String.raw`\bimport(?:\s+type)?\s+[\s\S]*?from\s+${importSpecifier}${importAttributes}`,
|
|
847
|
+
"g"
|
|
848
|
+
),
|
|
849
|
+
""
|
|
850
|
+
).replace(/\bimport\s+type\s+[\s\S]*?(?:;|\n)/g, "").replace(new RegExp(String.raw`\bimport\s+${importSpecifier}${importAttributes}`, "g"), "");
|
|
851
|
+
var engineAccepts = (body) => {
|
|
852
|
+
for (const candidate of [body, stripModuleSyntax(body)]) {
|
|
853
|
+
try {
|
|
854
|
+
new Function(candidate);
|
|
855
|
+
return true;
|
|
856
|
+
} catch {
|
|
857
|
+
try {
|
|
858
|
+
new Function(`return (async()=>{${candidate}
|
|
859
|
+
})`);
|
|
860
|
+
return true;
|
|
861
|
+
} catch {
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
return false;
|
|
866
|
+
};
|
|
867
|
+
var GLUED_INSTRUMENTATION = /\b(?:return|await|throw|typeof|void|yield|new|delete|in|instanceof|of|from|as|case|else)__rasputin/;
|
|
868
|
+
var UNTERMINATED_BARE_LEAVE = /\breturn\s+__rasputin\w*\.leave\(\)(?!\s*;|\()/;
|
|
869
|
+
var transformedSourceIsInvalid = (filePath, code) => {
|
|
870
|
+
if (GLUED_INSTRUMENTATION.test(code)) return true;
|
|
871
|
+
if (UNTERMINATED_BARE_LEAVE.test(code)) return true;
|
|
872
|
+
const sourceFile = ts5.createSourceFile(
|
|
873
|
+
filePath,
|
|
874
|
+
code,
|
|
875
|
+
ts5.ScriptTarget.Latest,
|
|
876
|
+
true,
|
|
877
|
+
scriptKindFor2(filePath)
|
|
878
|
+
);
|
|
879
|
+
if (hasInvalidLabelJump(sourceFile)) return true;
|
|
880
|
+
const { diagnostics, outputText } = transpileForVerification(filePath, code);
|
|
881
|
+
if (hasScanOrParseError(diagnostics)) return true;
|
|
882
|
+
if (looksTyped(filePath)) return !engineAccepts(scriptBodyForEngine(outputText));
|
|
883
|
+
if (engineAccepts(scriptBodyForEngine(code))) return false;
|
|
884
|
+
return !engineAccepts(scriptBodyForEngine(outputText));
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
// src/auto-instrumentation/transform-source.ts
|
|
888
|
+
var isIdentContinue = (code) => code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 36 || code === 95;
|
|
889
|
+
var isIdentStart = (code) => code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 36 || code === 95;
|
|
890
|
+
var separateFromPreviousToken = (source, position, text) => {
|
|
891
|
+
if (position <= 0 || text.length === 0) return text;
|
|
892
|
+
if (isIdentContinue(source.charCodeAt(position - 1)) && isIdentStart(text.charCodeAt(0))) {
|
|
893
|
+
return ` ${text}`;
|
|
894
|
+
}
|
|
895
|
+
return text;
|
|
896
|
+
};
|
|
897
|
+
var RASPUTIN_INSTRUMENTATION_MARKER = "rasputin.instrumented.v3";
|
|
898
|
+
var RASPUTIN_INSTRUMENTATION_PREAMBLE = `/*${RASPUTIN_INSTRUMENTATION_MARKER}*/`;
|
|
899
|
+
var sourceIsRasputinInstrumented = (source) => source.includes(RASPUTIN_INSTRUMENTATION_PREAMBLE);
|
|
900
|
+
var alreadyInstrumentedResult = (source) => ({
|
|
901
|
+
code: source,
|
|
902
|
+
delta: { functions: [], callSites: [], destructures: [], alreadyInstrumented: true }
|
|
903
|
+
});
|
|
904
|
+
var scriptKindFor3 = (filePath) => {
|
|
905
|
+
if (/\.tsx$/i.test(filePath)) return ts6.ScriptKind.TSX;
|
|
906
|
+
if (/\.jsx$/i.test(filePath)) return ts6.ScriptKind.JSX;
|
|
907
|
+
if (/\.[cm]?ts$/i.test(filePath)) return ts6.ScriptKind.TS;
|
|
908
|
+
return ts6.ScriptKind.JS;
|
|
1089
909
|
};
|
|
1090
910
|
var functionName = (node, sourceFile) => {
|
|
911
|
+
if (ts6.isConstructorDeclaration(node)) return "constructor";
|
|
1091
912
|
if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
|
|
1092
913
|
const parent = node.parent;
|
|
1093
|
-
if (
|
|
1094
|
-
if (
|
|
1095
|
-
if (
|
|
914
|
+
if (ts6.isVariableDeclaration(parent) && ts6.isIdentifier(parent.name)) return parent.name.text;
|
|
915
|
+
if (ts6.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
|
|
916
|
+
if (ts6.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
|
|
1096
917
|
const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
1097
918
|
return `anonymous@${location.line + 1}:${location.character + 1}`;
|
|
1098
919
|
};
|
|
1099
920
|
var functionLocationPosition = (node, sourceFile) => {
|
|
1100
921
|
if (node.name) return node.name.getStart(sourceFile);
|
|
1101
922
|
const parent = node.parent;
|
|
1102
|
-
if ((
|
|
923
|
+
if ((ts6.isVariableDeclaration(parent) || ts6.isPropertyAssignment(parent)) && parent.name) {
|
|
1103
924
|
return parent.name.getStart(sourceFile);
|
|
1104
925
|
}
|
|
1105
926
|
return node.getStart(sourceFile);
|
|
1106
927
|
};
|
|
1107
|
-
var
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
const value = bindingValue(element.name, sourceFile);
|
|
1113
|
-
return element.dotDotDotToken ? `...${value}` : value;
|
|
1114
|
-
});
|
|
1115
|
-
return `[${values.join(",")}]`;
|
|
928
|
+
var functionBodyPrologueEnd = (body, sourceFile) => {
|
|
929
|
+
let end = body.getStart(sourceFile) + 1;
|
|
930
|
+
for (const statement of body.statements) {
|
|
931
|
+
if (!isDirectivePrologue(statement)) break;
|
|
932
|
+
end = statement.end;
|
|
1116
933
|
}
|
|
1117
|
-
|
|
1118
|
-
const value = bindingValue(element.name, sourceFile);
|
|
1119
|
-
if (element.dotDotDotToken) return `...${value}`;
|
|
1120
|
-
if (element.propertyName) return `${element.propertyName.getText(sourceFile)}:${value}`;
|
|
1121
|
-
return value;
|
|
1122
|
-
});
|
|
1123
|
-
return `{${properties.join(",")}}`;
|
|
934
|
+
return end;
|
|
1124
935
|
};
|
|
1125
|
-
var
|
|
936
|
+
var arrowArgsCapture = (node) => {
|
|
937
|
+
if (!node.parameters.every((parameter) => ts6.isIdentifier(parameter.name))) {
|
|
938
|
+
const count = node.parameters.filter(
|
|
939
|
+
(parameter) => !(ts6.isIdentifier(parameter.name) && parameter.name.text === "this")
|
|
940
|
+
).length;
|
|
941
|
+
return {
|
|
942
|
+
args: `[${Array.from({ length: count }, () => "void 0").join(",")}]`,
|
|
943
|
+
omitValues: true
|
|
944
|
+
};
|
|
945
|
+
}
|
|
1126
946
|
const values = node.parameters.flatMap((parameter) => {
|
|
1127
|
-
if (
|
|
1128
|
-
|
|
1129
|
-
return [parameter.dotDotDotToken ? `...${value}` : value];
|
|
947
|
+
if (!ts6.isIdentifier(parameter.name) || parameter.name.text === "this") return [];
|
|
948
|
+
return [parameter.dotDotDotToken ? `...${parameter.name.text}` : parameter.name.text];
|
|
1130
949
|
});
|
|
1131
|
-
return `[${values.join(",")}]
|
|
950
|
+
return { args: `[${values.join(",")}]`, omitValues: false };
|
|
1132
951
|
};
|
|
1133
952
|
var isSupportedFunction = (node) => {
|
|
1134
|
-
if (!
|
|
953
|
+
if (!ts6.isFunctionDeclaration(node) && !ts6.isFunctionExpression(node) && !ts6.isArrowFunction(node) && !ts6.isMethodDeclaration(node) && !ts6.isGetAccessorDeclaration(node) && !ts6.isSetAccessorDeclaration(node)) {
|
|
1135
954
|
return false;
|
|
1136
955
|
}
|
|
1137
956
|
if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
|
|
@@ -1142,7 +961,7 @@ var uniqueName = (source, base) => {
|
|
|
1142
961
|
while (source.includes(name)) name += "_";
|
|
1143
962
|
return name;
|
|
1144
963
|
};
|
|
1145
|
-
var isDirectivePrologue = (statement) =>
|
|
964
|
+
var isDirectivePrologue = (statement) => ts6.isExpressionStatement(statement) && ts6.isStringLiteral(statement.expression);
|
|
1146
965
|
var insertPositionAfterPrologue = (source, sourceFile) => {
|
|
1147
966
|
const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
|
|
1148
967
|
let end = shebangEnd;
|
|
@@ -1157,7 +976,7 @@ var preambleInsert = (source, position, preamble) => {
|
|
|
1157
976
|
const previous = source[position - 1];
|
|
1158
977
|
return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
|
|
1159
978
|
};
|
|
1160
|
-
var modulePreamble = (runName,
|
|
979
|
+
var modulePreamble = (runName, plansName, plansJson) => `${RASPUTIN_INSTRUMENTATION_PREAMBLE}const ${plansName}=${plansJson};const ${runName}=((g,k)=>{const r=g[k]??(g[k]={runtimes:[],modules:[],register(p){const d={run:(i,a,c)=>c(),enter(){return{leave(v){return v},fail(){},end(){},suspend(v){return v},resume(v){return v},run(c){return c()}}}};const m={plans:p,cell:d};this.modules.push(m);const n=this.runtimes.at(-1);if(n)n.bind(m);const f=(i,a,c,l)=>d.run(i,a,c,l);f.enter=(i,a,l)=>d.enter(i,a,l);return f}});return r.register(${plansName})})(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));`;
|
|
1161
980
|
var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
1162
981
|
const start = mappedLocation(
|
|
1163
982
|
sourceFile,
|
|
@@ -1185,40 +1004,276 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
|
1185
1004
|
endLine: end.line,
|
|
1186
1005
|
endColumn: end.column,
|
|
1187
1006
|
params,
|
|
1188
|
-
paramNames
|
|
1007
|
+
paramNames,
|
|
1008
|
+
instrumented: true
|
|
1189
1009
|
};
|
|
1190
1010
|
};
|
|
1191
|
-
var
|
|
1192
|
-
|
|
1193
|
-
|
|
1011
|
+
var wrap = (edits, start, end, open, close, depth) => {
|
|
1012
|
+
const width = Math.max(0, end - start);
|
|
1013
|
+
edits.push({ position: start, text: open, deleteCount: 0, depth, width, kind: "open" });
|
|
1014
|
+
edits.push({ position: end, text: close, deleteCount: 0, depth, width, kind: "close" });
|
|
1015
|
+
};
|
|
1016
|
+
var compareEdits = (left, right) => {
|
|
1017
|
+
if (left.position !== right.position) return right.position - left.position;
|
|
1018
|
+
if (left.kind !== right.kind) {
|
|
1019
|
+
const rank = { replace: 0, close: 1, open: 2 };
|
|
1020
|
+
return rank[left.kind] - rank[right.kind];
|
|
1194
1021
|
}
|
|
1195
|
-
|
|
1196
|
-
(
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1022
|
+
if (left.kind === "close") {
|
|
1023
|
+
if (left.width !== right.width) return right.width - left.width;
|
|
1024
|
+
return left.depth - right.depth;
|
|
1025
|
+
}
|
|
1026
|
+
if (left.kind === "open") {
|
|
1027
|
+
if (left.width !== right.width) return left.width - right.width;
|
|
1028
|
+
return right.depth - left.depth;
|
|
1029
|
+
}
|
|
1030
|
+
return 0;
|
|
1031
|
+
};
|
|
1032
|
+
var VLQ_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
1033
|
+
var encodeVlq = (value) => {
|
|
1034
|
+
let vlq = value < 0 ? -value << 1 | 1 : value << 1;
|
|
1035
|
+
let encoded = "";
|
|
1036
|
+
do {
|
|
1037
|
+
let digit = vlq & 31;
|
|
1038
|
+
vlq >>>= 5;
|
|
1039
|
+
if (vlq > 0) digit |= 32;
|
|
1040
|
+
encoded += VLQ_ALPHABET[digit];
|
|
1041
|
+
} while (vlq > 0);
|
|
1042
|
+
return encoded;
|
|
1043
|
+
};
|
|
1044
|
+
var SOURCEMAP_COMMENT = /(?:\/\/[#@][ \t]*sourceMappingURL=([^\s'"]+)|\/\*[#@][ \t]*sourceMappingURL=([^\s*'"]+)[ \t]*\*\/)\s*$/;
|
|
1045
|
+
var decodeInlineSourceMap = (url) => {
|
|
1046
|
+
const base64 = /^data:application\/json(?:;charset=[^;,]+)?;base64,(.+)$/i.exec(url);
|
|
1047
|
+
if (base64?.[1]) {
|
|
1048
|
+
try {
|
|
1049
|
+
return Buffer.from(base64[1], "base64").toString("utf8");
|
|
1050
|
+
} catch {
|
|
1051
|
+
return void 0;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
const raw = /^data:application\/json(?:;charset=[^;,]+)?,(.+)$/i.exec(url);
|
|
1055
|
+
if (!raw?.[1]) return void 0;
|
|
1056
|
+
try {
|
|
1057
|
+
return decodeURIComponent(raw[1]);
|
|
1058
|
+
} catch {
|
|
1059
|
+
return void 0;
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
var upstreamTraceMap = (original) => {
|
|
1063
|
+
const match = SOURCEMAP_COMMENT.exec(original);
|
|
1064
|
+
const url = match?.[1] ?? match?.[2];
|
|
1065
|
+
if (!url) return void 0;
|
|
1066
|
+
const payload = url.startsWith("data:") ? decodeInlineSourceMap(url) : void 0;
|
|
1067
|
+
if (!payload) return void 0;
|
|
1068
|
+
try {
|
|
1069
|
+
return new TraceMap2(payload);
|
|
1070
|
+
} catch {
|
|
1071
|
+
return void 0;
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
var lineStartsOf = (text) => {
|
|
1075
|
+
const starts = [0];
|
|
1076
|
+
for (let index = 0; index < text.length; index++) {
|
|
1077
|
+
if (text[index] === "\n") starts.push(index + 1);
|
|
1078
|
+
}
|
|
1079
|
+
return starts;
|
|
1080
|
+
};
|
|
1081
|
+
var positionAt = (starts, offset) => {
|
|
1082
|
+
let low = 0;
|
|
1083
|
+
let high = starts.length - 1;
|
|
1084
|
+
while (low < high) {
|
|
1085
|
+
const mid = low + high + 1 >> 1;
|
|
1086
|
+
if ((starts[mid] ?? 0) <= offset) low = mid;
|
|
1087
|
+
else high = mid - 1;
|
|
1088
|
+
}
|
|
1089
|
+
return { line: low, column: offset - (starts[low] ?? 0) };
|
|
1090
|
+
};
|
|
1091
|
+
var sortedEdits = (edits) => [...edits].sort(compareEdits);
|
|
1092
|
+
var originalOffsetToGenerated = (originalOffset, edits) => {
|
|
1093
|
+
let generated = originalOffset;
|
|
1094
|
+
for (const edit of edits) {
|
|
1095
|
+
const delta = edit.text.length - edit.deleteCount;
|
|
1096
|
+
if (originalOffset >= edit.position + edit.deleteCount) generated += delta;
|
|
1097
|
+
else if (originalOffset >= edit.position) generated = edit.position + edit.text.length;
|
|
1098
|
+
}
|
|
1099
|
+
return generated;
|
|
1100
|
+
};
|
|
1101
|
+
var mappingOffsets = (original, edits) => {
|
|
1102
|
+
const offsets = /* @__PURE__ */ new Set([0, original.length]);
|
|
1103
|
+
for (const start of lineStartsOf(original)) offsets.add(start);
|
|
1104
|
+
for (const edit of edits) {
|
|
1105
|
+
offsets.add(edit.position);
|
|
1106
|
+
offsets.add(edit.position + edit.deleteCount);
|
|
1107
|
+
}
|
|
1108
|
+
return [...offsets].filter((offset) => offset >= 0 && offset <= original.length).sort((left, right) => left - right);
|
|
1109
|
+
};
|
|
1110
|
+
var encodeColumnMappings = (original, generated, file, edits) => {
|
|
1111
|
+
const upstream = upstreamTraceMap(original);
|
|
1112
|
+
const sources = (upstream?.sources.filter((source) => Boolean(source)) ?? [file]).filter((source, index, all) => all.indexOf(source) === index);
|
|
1113
|
+
if (sources.length === 0) sources.push(file);
|
|
1114
|
+
const applied = sortedEdits(edits);
|
|
1115
|
+
const originalStarts = lineStartsOf(original);
|
|
1116
|
+
const generatedStarts = lineStartsOf(generated);
|
|
1117
|
+
const pointsByLine = Array.from({ length: generatedStarts.length }, () => []);
|
|
1118
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1119
|
+
for (const originalOffset of mappingOffsets(original, edits)) {
|
|
1120
|
+
const generatedOffset = Math.min(
|
|
1121
|
+
originalOffsetToGenerated(originalOffset, applied),
|
|
1122
|
+
generated.length
|
|
1123
|
+
);
|
|
1124
|
+
const originalPosition = positionAt(originalStarts, originalOffset);
|
|
1125
|
+
const generatedPosition = positionAt(generatedStarts, generatedOffset);
|
|
1126
|
+
let sourceIndex = 0;
|
|
1127
|
+
let mappedLine = originalPosition.line;
|
|
1128
|
+
let mappedColumn = originalPosition.column;
|
|
1129
|
+
if (upstream) {
|
|
1130
|
+
const mapped = originalPositionFor2(upstream, {
|
|
1131
|
+
line: originalPosition.line + 1,
|
|
1132
|
+
column: originalPosition.column
|
|
1133
|
+
});
|
|
1134
|
+
if (mapped.source && mapped.line != null && mapped.column != null) {
|
|
1135
|
+
const index = sources.indexOf(mapped.source);
|
|
1136
|
+
sourceIndex = index >= 0 ? index : 0;
|
|
1137
|
+
mappedLine = mapped.line - 1;
|
|
1138
|
+
mappedColumn = mapped.column;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
const key = `${generatedPosition.line}:${generatedPosition.column}`;
|
|
1142
|
+
if (seen.has(key)) continue;
|
|
1143
|
+
seen.add(key);
|
|
1144
|
+
pointsByLine[generatedPosition.line]?.push({
|
|
1145
|
+
generatedColumn: generatedPosition.column,
|
|
1146
|
+
sourceIndex,
|
|
1147
|
+
originalLine: mappedLine,
|
|
1148
|
+
originalColumn: mappedColumn
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
const lines = pointsByLine.map((segments) => {
|
|
1152
|
+
segments.sort((left, right) => left.generatedColumn - right.generatedColumn);
|
|
1153
|
+
let lastGeneratedColumn = 0;
|
|
1154
|
+
let lastSourceIndex = 0;
|
|
1155
|
+
let lastOriginalLine = 0;
|
|
1156
|
+
let lastOriginalColumn = 0;
|
|
1157
|
+
return segments.map((point) => {
|
|
1158
|
+
const encoded = `${encodeVlq(point.generatedColumn - lastGeneratedColumn)}${encodeVlq(point.sourceIndex - lastSourceIndex)}${encodeVlq(point.originalLine - lastOriginalLine)}${encodeVlq(point.originalColumn - lastOriginalColumn)}`;
|
|
1159
|
+
lastGeneratedColumn = point.generatedColumn;
|
|
1160
|
+
lastSourceIndex = point.sourceIndex;
|
|
1161
|
+
lastOriginalLine = point.originalLine;
|
|
1162
|
+
lastOriginalColumn = point.originalColumn;
|
|
1163
|
+
return encoded;
|
|
1164
|
+
}).join(",");
|
|
1165
|
+
});
|
|
1166
|
+
return { sources, mappings: lines.join(";") };
|
|
1167
|
+
};
|
|
1168
|
+
var attachInlineSourceMap = (original, generated, filePath, edits) => {
|
|
1169
|
+
const file = basename(filePath);
|
|
1170
|
+
const withoutUpstream = generated.replace(SOURCEMAP_COMMENT, "");
|
|
1171
|
+
const { sources, mappings } = encodeColumnMappings(original, withoutUpstream, file, edits);
|
|
1172
|
+
const comment = `/*# sourceMappingURL=data:application/json;base64,${Buffer.from(
|
|
1173
|
+
JSON.stringify({ version: 3, file, sources, mappings })
|
|
1174
|
+
).toString("base64")} */`;
|
|
1175
|
+
return withoutUpstream.endsWith("\n") ? `${withoutUpstream.slice(0, -1)}${comment}
|
|
1176
|
+
` : `${withoutUpstream}${comment}`;
|
|
1177
|
+
};
|
|
1178
|
+
var insertAfterSuperOrPrologue = (body, sourceFile) => {
|
|
1179
|
+
const end = functionBodyPrologueEnd(body, sourceFile);
|
|
1180
|
+
for (const statement of body.statements) {
|
|
1181
|
+
if (isDirectivePrologue(statement)) continue;
|
|
1182
|
+
if (ts6.isExpressionStatement(statement) && ts6.isCallExpression(statement.expression) && statement.expression.expression.kind === ts6.SyntaxKind.SuperKeyword) {
|
|
1183
|
+
return statement.end;
|
|
1184
|
+
}
|
|
1185
|
+
break;
|
|
1186
|
+
}
|
|
1187
|
+
return end;
|
|
1188
|
+
};
|
|
1189
|
+
var failEpilogue = (handleName, errorName, args2, restoreOnCatch) => `}catch(${errorName}){${restoreOnCatch ? `${handleName}.resume();` : ""}${handleName}.fail(${errorName},${args2});throw ${errorName};}finally{${handleName}.end();}`;
|
|
1190
|
+
var walkOwnBody = (node, visitNode) => {
|
|
1191
|
+
if (!node.body) return;
|
|
1192
|
+
const visit = (candidate) => {
|
|
1193
|
+
if (candidate !== node && ts6.isFunctionLike(candidate)) return;
|
|
1194
|
+
visitNode(candidate);
|
|
1195
|
+
ts6.forEachChild(candidate, visit);
|
|
1196
|
+
};
|
|
1197
|
+
visit(node.body);
|
|
1198
|
+
};
|
|
1199
|
+
var collectAwaitWraps = (node, sourceFile, handleName, edits, depth) => {
|
|
1200
|
+
walkOwnBody(node, (candidate) => {
|
|
1201
|
+
if (!ts6.isAwaitExpression(candidate)) return;
|
|
1202
|
+
wrap(
|
|
1203
|
+
edits,
|
|
1204
|
+
candidate.expression.getStart(sourceFile),
|
|
1205
|
+
candidate.expression.end,
|
|
1206
|
+
`${handleName}.suspend(`,
|
|
1207
|
+
")",
|
|
1208
|
+
depth + 3
|
|
1209
|
+
);
|
|
1210
|
+
wrap(
|
|
1211
|
+
edits,
|
|
1212
|
+
candidate.getStart(sourceFile),
|
|
1213
|
+
candidate.end,
|
|
1214
|
+
`${handleName}.resume(`,
|
|
1215
|
+
")",
|
|
1216
|
+
depth + 2
|
|
1217
|
+
);
|
|
1218
|
+
});
|
|
1219
|
+
};
|
|
1220
|
+
var collectCatchFinallyResumes = (node, sourceFile, handleName, edits, depth) => {
|
|
1221
|
+
walkOwnBody(node, (candidate) => {
|
|
1222
|
+
if (!ts6.isTryStatement(candidate)) return;
|
|
1223
|
+
const blocks = [candidate.catchClause?.block, candidate.finallyBlock];
|
|
1224
|
+
for (const block of blocks) {
|
|
1225
|
+
if (!block) continue;
|
|
1226
|
+
edits.push({
|
|
1227
|
+
position: block.getStart(sourceFile) + 1,
|
|
1228
|
+
text: `${handleName}.resume();`,
|
|
1229
|
+
deleteCount: 0,
|
|
1230
|
+
depth,
|
|
1231
|
+
width: 0,
|
|
1232
|
+
kind: "replace"
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
};
|
|
1237
|
+
var collectReturnWraps = (node, sourceFile, handleName, edits, depth) => {
|
|
1238
|
+
if (!node.body || !ts6.isBlock(node.body)) return;
|
|
1239
|
+
const visit = (candidate, at) => {
|
|
1240
|
+
if (candidate !== node && ts6.isFunctionLike(candidate)) return;
|
|
1241
|
+
if (ts6.isReturnStatement(candidate) && candidate.expression) {
|
|
1242
|
+
const expression = candidate.expression;
|
|
1243
|
+
wrap(
|
|
1244
|
+
edits,
|
|
1245
|
+
expression.getStart(sourceFile),
|
|
1246
|
+
expression.end,
|
|
1247
|
+
`(${handleName}.leave((`,
|
|
1248
|
+
")))",
|
|
1249
|
+
at
|
|
1250
|
+
);
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
ts6.forEachChild(candidate, (child) => visit(child, at + 1));
|
|
1254
|
+
};
|
|
1255
|
+
visit(node.body, depth + 1);
|
|
1200
1256
|
};
|
|
1201
1257
|
var transformSource = (source, options) => {
|
|
1202
1258
|
if (source.includes("@rasputin-ignore-file")) return void 0;
|
|
1203
|
-
|
|
1259
|
+
if (sourceIsRasputinInstrumented(source)) return alreadyInstrumentedResult(source);
|
|
1260
|
+
const sourceFile = ts6.createSourceFile(
|
|
1204
1261
|
options.filePath,
|
|
1205
1262
|
source,
|
|
1206
|
-
|
|
1263
|
+
ts6.ScriptTarget.Latest,
|
|
1207
1264
|
true,
|
|
1208
|
-
|
|
1265
|
+
scriptKindFor3(options.filePath)
|
|
1209
1266
|
);
|
|
1210
1267
|
const runName = uniqueName(source, "__rasputinRun__");
|
|
1211
|
-
const capName = uniqueName(source, "__rasputinCap__");
|
|
1212
1268
|
const plansName = uniqueName(source, "__rasputinPlans__");
|
|
1269
|
+
const handleName = uniqueName(source, "__rasputinH__");
|
|
1270
|
+
const errorName = uniqueName(source, "__rasputinE__");
|
|
1213
1271
|
const absolutePath = resolve4(options.filePath);
|
|
1214
|
-
const derivationMode = options.derivationMode ?? "analysis";
|
|
1215
1272
|
const edits = [];
|
|
1216
|
-
let order = 0;
|
|
1217
1273
|
const delta = {
|
|
1218
1274
|
functions: [],
|
|
1219
1275
|
callSites: [],
|
|
1220
|
-
destructures: []
|
|
1221
|
-
derivationPlans: []
|
|
1276
|
+
destructures: []
|
|
1222
1277
|
};
|
|
1223
1278
|
const collectContext = {
|
|
1224
1279
|
sourceFile,
|
|
@@ -1226,75 +1281,134 @@ var transformSource = (source, options) => {
|
|
|
1226
1281
|
absolutePath
|
|
1227
1282
|
};
|
|
1228
1283
|
const pending = [];
|
|
1229
|
-
const
|
|
1284
|
+
const skipFunction = (collected, reason) => {
|
|
1285
|
+
collected.instrumented = false;
|
|
1286
|
+
collected.skipReason = reason;
|
|
1287
|
+
delta.functions.push(collected);
|
|
1288
|
+
if (process.env.RASPUTIN_DEBUG === "1") {
|
|
1289
|
+
console.info(
|
|
1290
|
+
`[rasputin] skipped instrumentation for ${collected.name} in ${options.filePath}: ${reason}`
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
const visit = (node, caller, depth) => {
|
|
1295
|
+
if (isAsyncGeneratorLike(node) && node.body) {
|
|
1296
|
+
const collected = collectWrappedFunction(
|
|
1297
|
+
node,
|
|
1298
|
+
sourceFile,
|
|
1299
|
+
absolutePath,
|
|
1300
|
+
options.mapLocation
|
|
1301
|
+
);
|
|
1302
|
+
if (collected) {
|
|
1303
|
+
collected.asyncFunction = true;
|
|
1304
|
+
skipFunction(collected, "unsupported_async_generator");
|
|
1305
|
+
}
|
|
1306
|
+
ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
if (isSyncGeneratorLike(node) && node.body) {
|
|
1310
|
+
const collected = collectWrappedFunction(
|
|
1311
|
+
node,
|
|
1312
|
+
sourceFile,
|
|
1313
|
+
absolutePath,
|
|
1314
|
+
options.mapLocation
|
|
1315
|
+
);
|
|
1316
|
+
if (collected) skipFunction(collected, "unsupported_sync_generator");
|
|
1317
|
+
ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
if (ts6.isConstructorDeclaration(node) && node.body) {
|
|
1321
|
+
const collected = collectWrappedFunction(
|
|
1322
|
+
node,
|
|
1323
|
+
sourceFile,
|
|
1324
|
+
absolutePath,
|
|
1325
|
+
options.mapLocation
|
|
1326
|
+
);
|
|
1327
|
+
if (collected) skipFunction(collected, "unsupported_constructor");
|
|
1328
|
+
ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1230
1331
|
if (isSupportedFunction(node)) {
|
|
1231
1332
|
const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
|
|
1232
1333
|
if (!collected) {
|
|
1233
|
-
|
|
1334
|
+
ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
|
|
1234
1335
|
return;
|
|
1235
1336
|
}
|
|
1236
|
-
|
|
1337
|
+
const asyncFunction = isAsyncFunctionLike(node);
|
|
1338
|
+
collected.asyncFunction = asyncFunction;
|
|
1339
|
+
const decision = asyncFunction ? decideAsyncInstrumentation(node, sourceFile) : { action: "inline" };
|
|
1340
|
+
if (decision.action === "skip") {
|
|
1341
|
+
skipFunction(collected, decision.reason);
|
|
1342
|
+
ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
if (functionHasPreBodyParameterEvaluation(node)) {
|
|
1346
|
+
collected.plan = withPreBodyParameterEvaluation(collected.plan);
|
|
1347
|
+
}
|
|
1348
|
+
pending.push({
|
|
1349
|
+
node,
|
|
1350
|
+
collected,
|
|
1351
|
+
depth,
|
|
1352
|
+
suspendResume: decision.action === "suspend_resume"
|
|
1353
|
+
});
|
|
1237
1354
|
delta.functions.push(collected);
|
|
1238
|
-
|
|
1355
|
+
ts6.forEachChild(node, (child) => visit(child, collected, depth + 1));
|
|
1239
1356
|
return;
|
|
1240
1357
|
}
|
|
1241
|
-
if (caller && (
|
|
1358
|
+
if (caller && (ts6.isCallExpression(node) || ts6.isNewExpression(node))) {
|
|
1242
1359
|
const site = collectCallSite(node, caller, collectContext);
|
|
1243
1360
|
if (site) delta.callSites.push(site);
|
|
1244
1361
|
}
|
|
1245
|
-
if (caller &&
|
|
1362
|
+
if (caller && ts6.isVariableDeclaration(node)) {
|
|
1246
1363
|
const destructure = collectParamDestructure(node, caller, collectContext);
|
|
1247
1364
|
if (destructure) delta.destructures.push(destructure);
|
|
1248
1365
|
}
|
|
1249
|
-
|
|
1366
|
+
ts6.forEachChild(node, (child) => visit(child, caller, depth + 1));
|
|
1250
1367
|
};
|
|
1251
|
-
visit(sourceFile);
|
|
1252
|
-
if (pending.length === 0)
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
pending.map((entry) => ({
|
|
1256
|
-
functionId: entry.collected.functionId,
|
|
1257
|
-
name: entry.collected.name,
|
|
1258
|
-
node: entry.node
|
|
1259
|
-
}))
|
|
1260
|
-
);
|
|
1261
|
-
delta.derivationPlans = analyzed.map((fn) => fn.plan);
|
|
1262
|
-
const analyzedById = new Map(analyzed.map((fn) => [fn.functionId, fn]));
|
|
1368
|
+
visit(sourceFile, void 0, 0);
|
|
1369
|
+
if (pending.length === 0) {
|
|
1370
|
+
return delta.functions.length > 0 ? { code: source, delta } : void 0;
|
|
1371
|
+
}
|
|
1263
1372
|
for (const [planIndex, entry] of pending.entries()) {
|
|
1264
|
-
const
|
|
1265
|
-
const
|
|
1266
|
-
const
|
|
1267
|
-
const
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
edits.push({
|
|
1284
|
-
position: entry.node.body.getStart(sourceFile),
|
|
1285
|
-
text: `${runName}(${planIndex},${args2},${asyncCallback}()=>(`,
|
|
1286
|
-
order: order++
|
|
1287
|
-
});
|
|
1288
|
-
edits.push({
|
|
1289
|
-
position: entry.node.body.end,
|
|
1290
|
-
text: `)${valueFlag})`,
|
|
1291
|
-
order: order++
|
|
1292
|
-
});
|
|
1293
|
-
}
|
|
1294
|
-
if (analysis) {
|
|
1295
|
-
for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
|
|
1296
|
-
edits.push({ ...edit, order: order++ });
|
|
1373
|
+
const captured = ts6.isArrowFunction(entry.node) ? arrowArgsCapture(entry.node) : { args: "arguments", omitValues: false };
|
|
1374
|
+
const omitArg = captured.omitValues ? ",true" : "";
|
|
1375
|
+
const enterCall = `${runName}.enter(${planIndex},${captured.args}${omitArg})`;
|
|
1376
|
+
const epilogue = failEpilogue(handleName, errorName, captured.args, entry.suspendResume);
|
|
1377
|
+
if (ts6.isBlock(entry.node.body)) {
|
|
1378
|
+
const prologueAt = insertAfterSuperOrPrologue(entry.node.body, sourceFile);
|
|
1379
|
+
const closeAt = entry.node.body.end - 1;
|
|
1380
|
+
wrap(
|
|
1381
|
+
edits,
|
|
1382
|
+
prologueAt,
|
|
1383
|
+
closeAt,
|
|
1384
|
+
`;const ${handleName}=${enterCall};try{`,
|
|
1385
|
+
epilogue,
|
|
1386
|
+
entry.depth
|
|
1387
|
+
);
|
|
1388
|
+
collectReturnWraps(entry.node, sourceFile, handleName, edits, entry.depth);
|
|
1389
|
+
if (entry.suspendResume) {
|
|
1390
|
+
collectAwaitWraps(entry.node, sourceFile, handleName, edits, entry.depth);
|
|
1391
|
+
collectCatchFinallyResumes(entry.node, sourceFile, handleName, edits, entry.depth);
|
|
1297
1392
|
}
|
|
1393
|
+
} else if (entry.suspendResume) {
|
|
1394
|
+
wrap(
|
|
1395
|
+
edits,
|
|
1396
|
+
entry.node.body.getStart(sourceFile),
|
|
1397
|
+
entry.node.body.end,
|
|
1398
|
+
`{;const ${handleName}=${enterCall};try{return (${handleName}.leave((`,
|
|
1399
|
+
`)));${epilogue}}`,
|
|
1400
|
+
entry.depth
|
|
1401
|
+
);
|
|
1402
|
+
collectAwaitWraps(entry.node, sourceFile, handleName, edits, entry.depth);
|
|
1403
|
+
} else {
|
|
1404
|
+
wrap(
|
|
1405
|
+
edits,
|
|
1406
|
+
entry.node.body.getStart(sourceFile),
|
|
1407
|
+
entry.node.body.end,
|
|
1408
|
+
`{;const ${handleName}=${enterCall};try{return (${handleName}.leave((`,
|
|
1409
|
+
`)));${epilogue}}`,
|
|
1410
|
+
entry.depth
|
|
1411
|
+
);
|
|
1298
1412
|
}
|
|
1299
1413
|
}
|
|
1300
1414
|
const insertAt = insertPositionAfterPrologue(source, sourceFile);
|
|
@@ -1305,19 +1419,49 @@ var transformSource = (source, options) => {
|
|
|
1305
1419
|
insertAt,
|
|
1306
1420
|
modulePreamble(
|
|
1307
1421
|
runName,
|
|
1308
|
-
capName,
|
|
1309
1422
|
plansName,
|
|
1310
|
-
JSON.stringify(
|
|
1423
|
+
JSON.stringify(pending.map((entry) => entry.collected.plan))
|
|
1311
1424
|
)
|
|
1312
1425
|
),
|
|
1313
|
-
|
|
1426
|
+
deleteCount: 0,
|
|
1427
|
+
depth: -1,
|
|
1428
|
+
width: 0,
|
|
1429
|
+
kind: "replace"
|
|
1314
1430
|
});
|
|
1315
|
-
edits.sort(
|
|
1431
|
+
edits.sort(compareEdits);
|
|
1316
1432
|
let transformed = source;
|
|
1317
1433
|
for (const edit of edits) {
|
|
1318
|
-
|
|
1434
|
+
const text = separateFromPreviousToken(transformed, edit.position, edit.text);
|
|
1435
|
+
transformed = `${transformed.slice(0, edit.position)}${text}${transformed.slice(edit.position + edit.deleteCount)}`;
|
|
1436
|
+
}
|
|
1437
|
+
const verify = options.verifyTransform ?? true;
|
|
1438
|
+
const refuseTransformed = () => {
|
|
1439
|
+
for (const entry of pending) {
|
|
1440
|
+
entry.collected.instrumented = false;
|
|
1441
|
+
entry.collected.skipReason = "verification_failed";
|
|
1442
|
+
}
|
|
1443
|
+
if (process.env.RASPUTIN_DEBUG === "1") {
|
|
1444
|
+
console.info(
|
|
1445
|
+
`[rasputin] skipped instrumentation for ${options.filePath}: verification_failed`
|
|
1446
|
+
);
|
|
1447
|
+
}
|
|
1448
|
+
return { code: source, delta };
|
|
1449
|
+
};
|
|
1450
|
+
if (verify && transformedSourceIsInvalid(options.filePath, transformed)) {
|
|
1451
|
+
if (transformedSourceIsInvalid(options.filePath, source)) {
|
|
1452
|
+
if (process.env.RASPUTIN_DEBUG === "1") {
|
|
1453
|
+
console.info(
|
|
1454
|
+
`[rasputin] skipped instrumentation for ${options.filePath}: transformed source failed verification`
|
|
1455
|
+
);
|
|
1456
|
+
}
|
|
1457
|
+
return void 0;
|
|
1458
|
+
}
|
|
1459
|
+
return refuseTransformed();
|
|
1460
|
+
}
|
|
1461
|
+
if (verify && !instrumentationTreesAreIsomorphic(options.filePath, source, transformed)) {
|
|
1462
|
+
return refuseTransformed();
|
|
1319
1463
|
}
|
|
1320
|
-
return { code: transformed, delta };
|
|
1464
|
+
return { code: attachInlineSourceMap(source, transformed, options.filePath, edits), delta };
|
|
1321
1465
|
};
|
|
1322
1466
|
|
|
1323
1467
|
// src/auto-instrumentation/instrument-compiled-esm.ts
|
|
@@ -1357,11 +1501,14 @@ var instrumentCompiledDirectory = (options) => {
|
|
|
1357
1501
|
continue;
|
|
1358
1502
|
}
|
|
1359
1503
|
const source = readFileSync2(filePath, "utf8");
|
|
1360
|
-
if (
|
|
1504
|
+
if (sourceIsRasputinInstrumented(source)) {
|
|
1505
|
+
const outputPath2 = join2(outputDir, relative2(inputDir, filePath));
|
|
1506
|
+
if (outputPath2 !== filePath) writeText(outputPath2, source);
|
|
1507
|
+
continue;
|
|
1508
|
+
}
|
|
1361
1509
|
const transformed = transformSource(source, {
|
|
1362
1510
|
filePath,
|
|
1363
|
-
mapLocation: createSourceMapLocationMapper(filePath, source)
|
|
1364
|
-
...options.derivationMode ? { derivationMode: options.derivationMode } : {}
|
|
1511
|
+
mapLocation: createSourceMapLocationMapper(filePath, source)
|
|
1365
1512
|
});
|
|
1366
1513
|
if (!transformed) continue;
|
|
1367
1514
|
const outputPath = join2(outputDir, relative2(inputDir, filePath));
|
|
@@ -1376,6 +1523,11 @@ var instrumentCompiledDirectory = (options) => {
|
|
|
1376
1523
|
if (manifest) {
|
|
1377
1524
|
writeText(manifestPath, `${JSON.stringify(manifest, null, " ")}
|
|
1378
1525
|
`);
|
|
1526
|
+
writeText(
|
|
1527
|
+
`${manifestPath.replace(/\.json$/i, "")}.hash`,
|
|
1528
|
+
`${hashInstrumentationManifest(manifest)}
|
|
1529
|
+
`
|
|
1530
|
+
);
|
|
1379
1531
|
}
|
|
1380
1532
|
return { transformedFiles, manifest, manifestPath };
|
|
1381
1533
|
};
|
|
@@ -1387,15 +1539,7 @@ var sdkPackageScopeRootFrom = (instrumentEntryUrl) => resolve6(dirname3(fileURLT
|
|
|
1387
1539
|
|
|
1388
1540
|
// src/instrument/build.ts
|
|
1389
1541
|
var printUsage = () => {
|
|
1390
|
-
console.error(
|
|
1391
|
-
"Usage: rasputin-instrument <input-dir> [--out <output-dir>] [--manifest <path>] [--derivation-mode analysis|selective]"
|
|
1392
|
-
);
|
|
1393
|
-
};
|
|
1394
|
-
var parseDerivationMode = (value) => {
|
|
1395
|
-
if (value === void 0) return void 0;
|
|
1396
|
-
if (value === "analysis" || value === "selective") return value;
|
|
1397
|
-
console.error(`[rasputin] unknown --derivation-mode ${value} (expected analysis or selective)`);
|
|
1398
|
-
process.exit(1);
|
|
1542
|
+
console.error("Usage: rasputin-instrument <input-dir> [--out <output-dir>] [--manifest <path>]");
|
|
1399
1543
|
};
|
|
1400
1544
|
var parseArgs = (argv) => {
|
|
1401
1545
|
const inputDir = argv.find((argument) => !argument.startsWith("--"));
|
|
@@ -1406,8 +1550,7 @@ var parseArgs = (argv) => {
|
|
|
1406
1550
|
return {
|
|
1407
1551
|
inputDir,
|
|
1408
1552
|
outputDir: option("--out"),
|
|
1409
|
-
manifestPath: option("--manifest")
|
|
1410
|
-
derivationMode: parseDerivationMode(option("--derivation-mode"))
|
|
1553
|
+
manifestPath: option("--manifest")
|
|
1411
1554
|
};
|
|
1412
1555
|
};
|
|
1413
1556
|
var args = parseArgs(process.argv.slice(2));
|
|
@@ -1419,8 +1562,7 @@ var result = instrumentCompiledDirectory({
|
|
|
1419
1562
|
inputDir: args.inputDir,
|
|
1420
1563
|
outputDir: args.outputDir,
|
|
1421
1564
|
manifestPath: args.manifestPath,
|
|
1422
|
-
excludedRoots: [sdkPackageScopeRootFrom(import.meta.url)]
|
|
1423
|
-
...args.derivationMode ? { derivationMode: args.derivationMode } : {}
|
|
1565
|
+
excludedRoots: [sdkPackageScopeRootFrom(import.meta.url)]
|
|
1424
1566
|
});
|
|
1425
1567
|
console.info(
|
|
1426
1568
|
`[rasputin] instrumented ${result.transformedFiles.length} file${result.transformedFiles.length === 1 ? "" : "s"}`
|