@tracecode/harness 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +29 -3
- package/dist/browser.cjs +974 -19
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +3 -2
- package/dist/browser.d.ts +3 -2
- package/dist/browser.js +974 -19
- package/dist/browser.js.map +1 -1
- package/dist/cli.cjs +24 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +24 -0
- package/dist/cli.js.map +1 -1
- package/dist/core.cjs +611 -4
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +20 -6
- package/dist/core.d.ts +20 -6
- package/dist/core.js +605 -4
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +1004 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +998 -19
- package/dist/index.js.map +1 -1
- package/dist/internal/browser.cjs +211 -0
- package/dist/internal/browser.cjs.map +1 -1
- package/dist/internal/browser.d.cts +62 -6
- package/dist/internal/browser.d.ts +62 -6
- package/dist/internal/browser.js +210 -0
- package/dist/internal/browser.js.map +1 -1
- package/dist/javascript.d.cts +2 -2
- package/dist/javascript.d.ts +2 -2
- package/dist/{runtime-types-DtaaAhHL.d.ts → runtime-types-89nchXlY.d.cts} +8 -4
- package/dist/{runtime-types--lBQ6rYu.d.cts → runtime-types-CCQ-ZLc9.d.ts} +8 -4
- package/dist/{types-DwIYM3Ku.d.cts → types-zyvpJKCi.d.cts} +1 -0
- package/dist/{types-DwIYM3Ku.d.ts → types-zyvpJKCi.d.ts} +1 -0
- package/package.json +11 -5
- package/workers/java/.build/classes/harness/browser/JavaRewriteLibrary.class +0 -0
- package/workers/java/java-worker.js +712 -0
- package/workers/java/src/harness/browser/JavaRewriteLibrary.java +54 -0
- package/workers/javascript/javascript-worker.js +343 -63
- package/workers/python/generated-python-harness-snippets.js +3 -3
- package/workers/python/pyodide-worker.js +419 -68
- package/workers/python/runtime-core.js +52 -3
- package/workers/vendor/java-browser-spike-helper.jar +0 -0
- package/workers/vendor/java-practice-rewriter.jar +0 -0
- package/workers/vendor/java-rewrite-bridge.jar +0 -0
- package/workers/vendor/javaparser-core-3.25.10.jar +0 -0
- package/workers/vendor/jdk.compiler-17.jar +0 -0
package/dist/core.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// packages/harness-core/src/trace-contract.ts
|
|
2
|
-
var RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION = "2026-
|
|
2
|
+
var RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION = "2026-04-21";
|
|
3
3
|
var TRACE_EVENTS = /* @__PURE__ */ new Set([
|
|
4
4
|
"line",
|
|
5
5
|
"call",
|
|
@@ -120,7 +120,39 @@ function normalizeAccesses(accesses) {
|
|
|
120
120
|
}
|
|
121
121
|
return payload;
|
|
122
122
|
}).filter((access) => access !== null);
|
|
123
|
-
|
|
123
|
+
if (normalized.length === 0) {
|
|
124
|
+
return void 0;
|
|
125
|
+
}
|
|
126
|
+
const statsByVariable = /* @__PURE__ */ new Map();
|
|
127
|
+
for (const access of normalized) {
|
|
128
|
+
const stats = statsByVariable.get(access.variable) ?? { hasCellRead: false, hasCellWrite: false };
|
|
129
|
+
if (access.kind === "cell-read") stats.hasCellRead = true;
|
|
130
|
+
if (access.kind === "cell-write") stats.hasCellWrite = true;
|
|
131
|
+
statsByVariable.set(access.variable, stats);
|
|
132
|
+
}
|
|
133
|
+
const deduped = /* @__PURE__ */ new Set();
|
|
134
|
+
const collapsed = normalized.filter((access) => {
|
|
135
|
+
const stats = statsByVariable.get(access.variable);
|
|
136
|
+
if (access.kind === "indexed-read" && (stats?.hasCellRead || stats?.hasCellWrite)) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
if (access.kind === "indexed-write" && stats?.hasCellWrite) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
const key = JSON.stringify([
|
|
143
|
+
access.variable,
|
|
144
|
+
access.kind,
|
|
145
|
+
access.indices ?? null,
|
|
146
|
+
access.pathDepth ?? null,
|
|
147
|
+
access.method ?? null
|
|
148
|
+
]);
|
|
149
|
+
if (deduped.has(key)) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
deduped.add(key);
|
|
153
|
+
return true;
|
|
154
|
+
});
|
|
155
|
+
return collapsed.length > 0 ? collapsed : void 0;
|
|
124
156
|
}
|
|
125
157
|
function normalizeRecord(value) {
|
|
126
158
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
@@ -184,8 +216,64 @@ function normalizeTraceStep(step) {
|
|
|
184
216
|
...normalizedVisualization ? { visualization: normalizedVisualization } : {}
|
|
185
217
|
};
|
|
186
218
|
}
|
|
219
|
+
function collapseTraceAccessNoise(trace) {
|
|
220
|
+
const variablesWithCellAccess = /* @__PURE__ */ new Set();
|
|
221
|
+
const accessStatsByVariable = /* @__PURE__ */ new Map();
|
|
222
|
+
for (const step of trace) {
|
|
223
|
+
for (const access of step.accesses ?? []) {
|
|
224
|
+
const stats = accessStatsByVariable.get(access.variable) ?? {
|
|
225
|
+
hasCellRead: false,
|
|
226
|
+
hasCellWrite: false,
|
|
227
|
+
hasMutatingCall: false,
|
|
228
|
+
hasIndexedWrite: false
|
|
229
|
+
};
|
|
230
|
+
if (access.kind === "cell-read" || access.kind === "cell-write") {
|
|
231
|
+
variablesWithCellAccess.add(access.variable);
|
|
232
|
+
}
|
|
233
|
+
if (access.kind === "cell-read") stats.hasCellRead = true;
|
|
234
|
+
if (access.kind === "cell-write") stats.hasCellWrite = true;
|
|
235
|
+
if (access.kind === "mutating-call") stats.hasMutatingCall = true;
|
|
236
|
+
if (access.kind === "indexed-write") stats.hasIndexedWrite = true;
|
|
237
|
+
accessStatsByVariable.set(access.variable, stats);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (variablesWithCellAccess.size === 0) {
|
|
241
|
+
return trace;
|
|
242
|
+
}
|
|
243
|
+
return trace.map((step) => {
|
|
244
|
+
if (!step.accesses?.length) {
|
|
245
|
+
return step;
|
|
246
|
+
}
|
|
247
|
+
const filtered = step.accesses.filter((access) => {
|
|
248
|
+
const stats = accessStatsByVariable.get(access.variable);
|
|
249
|
+
if (variablesWithCellAccess.has(access.variable)) {
|
|
250
|
+
return access.kind !== "indexed-read" && access.kind !== "indexed-write";
|
|
251
|
+
}
|
|
252
|
+
if (access.kind === "mutating-call" && stats?.hasIndexedWrite && !stats.hasCellRead && !stats.hasCellWrite) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
if (access.kind === "indexed-read" && stats?.hasMutatingCall && !stats.hasIndexedWrite && !stats.hasCellRead && !stats.hasCellWrite) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
return true;
|
|
259
|
+
});
|
|
260
|
+
if (filtered.length === step.accesses.length) {
|
|
261
|
+
return step;
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
...step,
|
|
265
|
+
...filtered.length > 0 ? { accesses: filtered } : {},
|
|
266
|
+
...filtered.length === 0 ? { accesses: void 0 } : {}
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
}
|
|
187
270
|
function normalizeRuntimeTraceContract(language, result) {
|
|
188
|
-
const
|
|
271
|
+
const rawNormalizedTrace = collapseTraceAccessNoise(
|
|
272
|
+
Array.isArray(result.trace) ? result.trace.map(normalizeTraceStep) : []
|
|
273
|
+
);
|
|
274
|
+
const maxTraceSteps = typeof result.maxTraceSteps === "number" && Number.isFinite(result.maxTraceSteps) ? Math.max(1, Math.floor(result.maxTraceSteps)) : void 0;
|
|
275
|
+
const traceWasClipped = maxTraceSteps !== void 0 && rawNormalizedTrace.length > maxTraceSteps;
|
|
276
|
+
const normalizedTrace = traceWasClipped && maxTraceSteps !== void 0 ? rawNormalizedTrace.slice(0, maxTraceSteps) : rawNormalizedTrace;
|
|
189
277
|
const lineEventCount = typeof result.lineEventCount === "number" && Number.isFinite(result.lineEventCount) ? Math.floor(result.lineEventCount) : normalizedTrace.filter((step) => step.event === "line").length;
|
|
190
278
|
const normalizedConsole = Array.isArray(result.consoleOutput) ? result.consoleOutput.map((line) => String(line)) : [];
|
|
191
279
|
return {
|
|
@@ -197,7 +285,7 @@ function normalizeRuntimeTraceContract(language, result) {
|
|
|
197
285
|
...typeof result.errorLine === "number" && Number.isFinite(result.errorLine) ? { errorLine: Math.floor(result.errorLine) } : {},
|
|
198
286
|
consoleOutput: normalizedConsole,
|
|
199
287
|
trace: normalizedTrace,
|
|
200
|
-
...result.traceLimitExceeded !== void 0 ? { traceLimitExceeded: Boolean(result.traceLimitExceeded) } : {},
|
|
288
|
+
...result.traceLimitExceeded !== void 0 || traceWasClipped ? { traceLimitExceeded: Boolean(result.traceLimitExceeded) || traceWasClipped } : {},
|
|
201
289
|
...typeof result.timeoutReason === "string" && result.timeoutReason.length > 0 ? { timeoutReason: result.timeoutReason } : {},
|
|
202
290
|
lineEventCount: Math.max(0, lineEventCount),
|
|
203
291
|
traceStepCount: normalizedTrace.length
|
|
@@ -255,11 +343,524 @@ function adaptPythonTraceExecutionResult(result) {
|
|
|
255
343
|
function adaptJavaScriptTraceExecutionResult(language, result) {
|
|
256
344
|
return adaptTraceExecutionResult(language, result);
|
|
257
345
|
}
|
|
346
|
+
|
|
347
|
+
// packages/harness-core/src/trace-adapters/java.ts
|
|
348
|
+
function parseScalar(raw) {
|
|
349
|
+
if (raw === "null") return null;
|
|
350
|
+
if (raw === "true") return true;
|
|
351
|
+
if (raw === "false") return false;
|
|
352
|
+
if (/^-?\d+$/.test(raw)) return Number.parseInt(raw, 10);
|
|
353
|
+
if (/^-?\d+\.\d+$/.test(raw)) return Number.parseFloat(raw);
|
|
354
|
+
if (raw.startsWith('"') && raw.endsWith('"') || raw.startsWith("[") && raw.endsWith("]") || raw.startsWith("{") && raw.endsWith("}")) {
|
|
355
|
+
try {
|
|
356
|
+
return JSON.parse(raw);
|
|
357
|
+
} catch {
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return raw;
|
|
361
|
+
}
|
|
362
|
+
function parseKeyValuePairs(fragment) {
|
|
363
|
+
const variables = {};
|
|
364
|
+
const matches = Array.from(fragment.matchAll(/\b([A-Za-z_][A-Za-z0-9_.]*)=/g));
|
|
365
|
+
for (let index = 0; index < matches.length; index += 1) {
|
|
366
|
+
const match = matches[index];
|
|
367
|
+
const rawKey = match[1];
|
|
368
|
+
const valueStart = (match.index ?? 0) + match[0].length;
|
|
369
|
+
const valueEnd = index + 1 < matches.length ? matches[index + 1].index ?? fragment.length : fragment.length;
|
|
370
|
+
const rawValue = fragment.slice(valueStart, valueEnd).trim();
|
|
371
|
+
if (!rawKey || rawValue === void 0) continue;
|
|
372
|
+
if (rawKey === "method") continue;
|
|
373
|
+
variables[rawKey.replaceAll(".", "_")] = parseScalar(rawValue);
|
|
374
|
+
}
|
|
375
|
+
return variables;
|
|
376
|
+
}
|
|
377
|
+
function extractLineMetadata(event) {
|
|
378
|
+
const match = event.match(/^line=(\d+)(?:\s+(.*))?$/);
|
|
379
|
+
if (!match) {
|
|
380
|
+
return { line: 1, payload: event };
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
line: Number.parseInt(match[1], 10),
|
|
384
|
+
payload: match[2] ?? ""
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function stripInlineComments(line, inBlockComment) {
|
|
388
|
+
let result = "";
|
|
389
|
+
let index = 0;
|
|
390
|
+
let inBlock = inBlockComment;
|
|
391
|
+
while (index < line.length) {
|
|
392
|
+
const current = line[index];
|
|
393
|
+
const next = index + 1 < line.length ? line[index + 1] : "";
|
|
394
|
+
if (inBlock) {
|
|
395
|
+
if (current === "*" && next === "/") {
|
|
396
|
+
inBlock = false;
|
|
397
|
+
index += 2;
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
index += 1;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
if (current === "/" && next === "*") {
|
|
404
|
+
inBlock = true;
|
|
405
|
+
index += 2;
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
if (current === "/" && next === "/") {
|
|
409
|
+
break;
|
|
410
|
+
}
|
|
411
|
+
result += current;
|
|
412
|
+
index += 1;
|
|
413
|
+
}
|
|
414
|
+
return { text: result, inBlockComment: inBlock };
|
|
415
|
+
}
|
|
416
|
+
function isMethodDeclarationLine(line) {
|
|
417
|
+
const trimmed = line.trim();
|
|
418
|
+
if (!trimmed) return false;
|
|
419
|
+
if (trimmed.startsWith("@")) return false;
|
|
420
|
+
if (!trimmed.includes("(") || !trimmed.includes(")")) return false;
|
|
421
|
+
if (trimmed.endsWith(";")) return false;
|
|
422
|
+
if (trimmed.includes("->")) return false;
|
|
423
|
+
if (/^(?:if|for|while|switch|catch|do|try|else|return|throw|new)\b/.test(trimmed)) {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
if (!/[A-Za-z_][A-Za-z0-9_]*\s*\([^{};]*\)/.test(trimmed)) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
return /(?:\{\s*)?$/.test(trimmed);
|
|
430
|
+
}
|
|
431
|
+
function buildLineRemap(sourceText) {
|
|
432
|
+
if (typeof sourceText !== "string" || sourceText.length === 0) {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
const lines = sourceText.split(/\r?\n/);
|
|
436
|
+
const executable = /* @__PURE__ */ new Set();
|
|
437
|
+
let inBlockComment = false;
|
|
438
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
439
|
+
const { text, inBlockComment: nextInBlockComment } = stripInlineComments(lines[index] ?? "", inBlockComment);
|
|
440
|
+
inBlockComment = nextInBlockComment;
|
|
441
|
+
if (text.trim().length > 0) {
|
|
442
|
+
executable.add(index + 1);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (executable.size === 0) {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
const nextExecutableAtOrAfter = new Array(lines.length + 2).fill(null);
|
|
449
|
+
let nextExecutable = null;
|
|
450
|
+
for (let line = lines.length; line >= 1; line -= 1) {
|
|
451
|
+
if (executable.has(line)) {
|
|
452
|
+
nextExecutable = line;
|
|
453
|
+
}
|
|
454
|
+
nextExecutableAtOrAfter[line] = nextExecutable;
|
|
455
|
+
}
|
|
456
|
+
const previousExecutableAtOrBefore = new Array(lines.length + 2).fill(null);
|
|
457
|
+
let previousExecutable = null;
|
|
458
|
+
for (let line = 1; line <= lines.length; line += 1) {
|
|
459
|
+
if (executable.has(line)) {
|
|
460
|
+
previousExecutable = line;
|
|
461
|
+
}
|
|
462
|
+
previousExecutableAtOrBefore[line] = previousExecutable;
|
|
463
|
+
}
|
|
464
|
+
const remap = /* @__PURE__ */ new Map();
|
|
465
|
+
for (let line = 1; line <= lines.length; line += 1) {
|
|
466
|
+
if (executable.has(line)) continue;
|
|
467
|
+
const forward = nextExecutableAtOrAfter[line];
|
|
468
|
+
const backward = previousExecutableAtOrBefore[line];
|
|
469
|
+
const target = forward ?? backward;
|
|
470
|
+
if (target !== null && target !== line) {
|
|
471
|
+
remap.set(line, target);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return remap;
|
|
475
|
+
}
|
|
476
|
+
function buildMethodDeclarationLineSet(sourceText) {
|
|
477
|
+
if (typeof sourceText !== "string" || sourceText.length === 0) {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
const declarationLines = /* @__PURE__ */ new Set();
|
|
481
|
+
const lines = sourceText.split(/\r?\n/);
|
|
482
|
+
let inBlockComment = false;
|
|
483
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
484
|
+
const { text, inBlockComment: nextInBlockComment } = stripInlineComments(lines[index] ?? "", inBlockComment);
|
|
485
|
+
inBlockComment = nextInBlockComment;
|
|
486
|
+
if (isMethodDeclarationLine(text)) {
|
|
487
|
+
declarationLines.add(index + 1);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return declarationLines;
|
|
491
|
+
}
|
|
492
|
+
function parseAccessEvent(payload) {
|
|
493
|
+
const isEphemeralOutputArrayName = (name) => /^(output|outputs)$/i.test(name);
|
|
494
|
+
const cellRead = payload.match(/^access ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]\[(\d+)\]=(.+)$/);
|
|
495
|
+
if (cellRead) {
|
|
496
|
+
return [{
|
|
497
|
+
variable: cellRead[1],
|
|
498
|
+
kind: "cell-read",
|
|
499
|
+
indices: [Number.parseInt(cellRead[2], 10), Number.parseInt(cellRead[3], 10)],
|
|
500
|
+
pathDepth: 2
|
|
501
|
+
}];
|
|
502
|
+
}
|
|
503
|
+
const indexedRead = payload.match(/^access ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]=(.+)$/);
|
|
504
|
+
if (indexedRead) {
|
|
505
|
+
return [{
|
|
506
|
+
variable: indexedRead[1],
|
|
507
|
+
kind: "indexed-read",
|
|
508
|
+
indices: [Number.parseInt(indexedRead[2], 10)],
|
|
509
|
+
pathDepth: 1
|
|
510
|
+
}];
|
|
511
|
+
}
|
|
512
|
+
const cellWrite = payload.match(/^write-array ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]\[(\d+)\]=(.+)$/);
|
|
513
|
+
if (cellWrite) {
|
|
514
|
+
return [{
|
|
515
|
+
variable: cellWrite[1],
|
|
516
|
+
kind: "cell-write",
|
|
517
|
+
indices: [Number.parseInt(cellWrite[2], 10), Number.parseInt(cellWrite[3], 10)],
|
|
518
|
+
pathDepth: 2
|
|
519
|
+
}];
|
|
520
|
+
}
|
|
521
|
+
const indexedWrite = payload.match(/^write-array ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]=(.+)$/);
|
|
522
|
+
if (indexedWrite) {
|
|
523
|
+
if (isEphemeralOutputArrayName(indexedWrite[1])) {
|
|
524
|
+
return void 0;
|
|
525
|
+
}
|
|
526
|
+
return [{
|
|
527
|
+
variable: indexedWrite[1],
|
|
528
|
+
kind: "indexed-write",
|
|
529
|
+
indices: [Number.parseInt(indexedWrite[2], 10)],
|
|
530
|
+
pathDepth: 1
|
|
531
|
+
}];
|
|
532
|
+
}
|
|
533
|
+
const mutatingCall = payload.match(/^mutate ([A-Za-z_][A-Za-z0-9_]*) method=([A-Za-z_][A-Za-z0-9_]*)$/);
|
|
534
|
+
if (mutatingCall) {
|
|
535
|
+
return [{
|
|
536
|
+
variable: mutatingCall[1],
|
|
537
|
+
kind: "mutating-call",
|
|
538
|
+
method: mutatingCall[2],
|
|
539
|
+
pathDepth: 1
|
|
540
|
+
}];
|
|
541
|
+
}
|
|
542
|
+
return void 0;
|
|
543
|
+
}
|
|
544
|
+
function parseStructureState(payload) {
|
|
545
|
+
const match = payload.match(/^state (linked-list|tree) ([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
|
|
546
|
+
if (!match) return null;
|
|
547
|
+
return {
|
|
548
|
+
structure: match[1],
|
|
549
|
+
variable: match[2],
|
|
550
|
+
value: JSON.parse(match[3])
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
function parseObjectState(payload) {
|
|
554
|
+
const match = payload.match(/^object-state ([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
|
|
555
|
+
if (!match) return null;
|
|
556
|
+
return {
|
|
557
|
+
variable: match[1],
|
|
558
|
+
visualization: JSON.parse(match[2])
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function parseObjectFieldEvent(payload) {
|
|
562
|
+
const match = payload.match(/^(access|write) ([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
|
|
563
|
+
if (!match) return null;
|
|
564
|
+
return {
|
|
565
|
+
variable: match[2],
|
|
566
|
+
field: match[3],
|
|
567
|
+
value: parseScalar(match[4])
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function buildFieldVisualization(event) {
|
|
571
|
+
return {
|
|
572
|
+
objectKinds: {
|
|
573
|
+
[event.variable]: "object"
|
|
574
|
+
},
|
|
575
|
+
hashMaps: [
|
|
576
|
+
{
|
|
577
|
+
name: event.variable,
|
|
578
|
+
kind: "object",
|
|
579
|
+
objectClassName: event.field === "next" || event.field === "prev" ? "ListNode" : event.field === "left" || event.field === "right" ? "TreeNode" : void 0,
|
|
580
|
+
objectId: `${event.variable}-object`,
|
|
581
|
+
highlightedKey: event.field,
|
|
582
|
+
entries: [{ key: event.field, value: event.value, highlight: true }]
|
|
583
|
+
}
|
|
584
|
+
]
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
function callStacksEqual(left, right) {
|
|
588
|
+
return JSON.stringify(left ?? []) === JSON.stringify(right ?? []);
|
|
589
|
+
}
|
|
590
|
+
function mergeVisualizationPayloads(left, right) {
|
|
591
|
+
if (!left) return right;
|
|
592
|
+
if (!right) return left;
|
|
593
|
+
return {
|
|
594
|
+
...left,
|
|
595
|
+
...right,
|
|
596
|
+
objectKinds: {
|
|
597
|
+
...left.objectKinds ?? {},
|
|
598
|
+
...right.objectKinds ?? {}
|
|
599
|
+
},
|
|
600
|
+
hashMaps: [
|
|
601
|
+
...left.hashMaps ?? [],
|
|
602
|
+
...right.hashMaps ?? []
|
|
603
|
+
]
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function maybeMergeConsecutiveLineStep(trace, nextStep) {
|
|
607
|
+
if (nextStep.event !== "line") {
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
const previous = trace.at(-1);
|
|
611
|
+
if (!previous || previous.event !== "line") {
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
if (previous.line !== nextStep.line || previous.function !== nextStep.function) {
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
if (!callStacksEqual(previous.callStack, nextStep.callStack)) {
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
previous.variables = { ...previous.variables ?? {}, ...nextStep.variables ?? {} };
|
|
621
|
+
if (nextStep.accesses?.length) {
|
|
622
|
+
previous.accesses = [...previous.accesses ?? [], ...nextStep.accesses];
|
|
623
|
+
}
|
|
624
|
+
previous.visualization = mergeVisualizationPayloads(previous.visualization, nextStep.visualization);
|
|
625
|
+
return true;
|
|
626
|
+
}
|
|
627
|
+
function filterStructuredVariables(variables) {
|
|
628
|
+
if (!variables) {
|
|
629
|
+
return void 0;
|
|
630
|
+
}
|
|
631
|
+
const isEphemeralOutputArrayName = (name) => /^(output|outputs)$/i.test(name);
|
|
632
|
+
const entries = Object.entries(variables).filter(([name, value]) => {
|
|
633
|
+
if (value === null || typeof value !== "object") {
|
|
634
|
+
return false;
|
|
635
|
+
}
|
|
636
|
+
if (!Array.isArray(value)) {
|
|
637
|
+
return true;
|
|
638
|
+
}
|
|
639
|
+
if (Array.isArray(value[0])) {
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
return !isEphemeralOutputArrayName(name);
|
|
643
|
+
});
|
|
644
|
+
if (entries.length === 0) {
|
|
645
|
+
return void 0;
|
|
646
|
+
}
|
|
647
|
+
return Object.fromEntries(entries);
|
|
648
|
+
}
|
|
649
|
+
function appendJavaTraceStep(trace, step, pendingAccesses) {
|
|
650
|
+
const nextStep = pendingAccesses.length > 0 ? { ...step, accesses: [...pendingAccesses] } : step;
|
|
651
|
+
pendingAccesses.length = 0;
|
|
652
|
+
if (!maybeMergeConsecutiveLineStep(trace, nextStep)) {
|
|
653
|
+
trace.push(nextStep);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
function mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, patch) {
|
|
657
|
+
const candidate = trace.at(-1);
|
|
658
|
+
if (!candidate || candidate.event !== "line") {
|
|
659
|
+
return false;
|
|
660
|
+
}
|
|
661
|
+
if (candidate.line !== line || candidate.function !== currentFunction) {
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
if (!callStacksEqual(candidate.callStack, currentCallStack)) {
|
|
665
|
+
return false;
|
|
666
|
+
}
|
|
667
|
+
candidate.variables = { ...candidate.variables ?? {}, ...patch.variables ?? {} };
|
|
668
|
+
if (patch.accesses?.length) {
|
|
669
|
+
candidate.accesses = [...candidate.accesses ?? [], ...patch.accesses];
|
|
670
|
+
}
|
|
671
|
+
candidate.visualization = mergeVisualizationPayloads(candidate.visualization, patch.visualization);
|
|
672
|
+
return true;
|
|
673
|
+
}
|
|
674
|
+
function mergeAccessesIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, accesses) {
|
|
675
|
+
return mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, {
|
|
676
|
+
variables: void 0,
|
|
677
|
+
accesses,
|
|
678
|
+
visualization: void 0
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
function eventsToRawTrace(events, sourceText) {
|
|
682
|
+
const trace = [];
|
|
683
|
+
const variables = {};
|
|
684
|
+
const objectKinds = {};
|
|
685
|
+
const stack = [];
|
|
686
|
+
const pendingAccesses = [];
|
|
687
|
+
let currentFunction = "<module>";
|
|
688
|
+
const lineRemap = buildLineRemap(sourceText);
|
|
689
|
+
const declarationLines = buildMethodDeclarationLineSet(sourceText);
|
|
690
|
+
for (const rawEvent of events) {
|
|
691
|
+
if (rawEvent === "clear" || rawEvent === "reset") continue;
|
|
692
|
+
const metadata = extractLineMetadata(rawEvent);
|
|
693
|
+
const line = lineRemap?.get(metadata.line) ?? metadata.line;
|
|
694
|
+
const payload = metadata.payload;
|
|
695
|
+
const isDeclarationLine = declarationLines?.has(line) === true;
|
|
696
|
+
if (payload.startsWith("call ")) {
|
|
697
|
+
const match = payload.match(/^call\s+(\S+)(?:\s+(.*))?$/);
|
|
698
|
+
const functionName = match?.[1] ?? currentFunction;
|
|
699
|
+
const argsFragment = match?.[2] ?? "";
|
|
700
|
+
Object.assign(variables, parseKeyValuePairs(argsFragment));
|
|
701
|
+
currentFunction = functionName || currentFunction;
|
|
702
|
+
stack.push({ function: currentFunction, line });
|
|
703
|
+
appendJavaTraceStep(trace, {
|
|
704
|
+
line,
|
|
705
|
+
event: "call",
|
|
706
|
+
function: currentFunction,
|
|
707
|
+
variables: { ...variables },
|
|
708
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} }))
|
|
709
|
+
}, pendingAccesses);
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
if (payload.startsWith("return ")) {
|
|
713
|
+
const match = payload.match(/^return\s+(\S+)$/);
|
|
714
|
+
const functionName = match?.[1] ?? currentFunction;
|
|
715
|
+
const returnVariables = functionName === "<module>" ? { ...variables } : filterStructuredVariables(variables) ?? {};
|
|
716
|
+
appendJavaTraceStep(trace, {
|
|
717
|
+
line,
|
|
718
|
+
event: "return",
|
|
719
|
+
function: functionName || currentFunction,
|
|
720
|
+
variables: returnVariables,
|
|
721
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} }))
|
|
722
|
+
}, pendingAccesses);
|
|
723
|
+
stack.pop();
|
|
724
|
+
currentFunction = stack[stack.length - 1]?.function ?? "<module>";
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
if (isDeclarationLine) {
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
if (payload.length === 0) {
|
|
731
|
+
appendJavaTraceStep(trace, {
|
|
732
|
+
line,
|
|
733
|
+
event: "line",
|
|
734
|
+
function: currentFunction,
|
|
735
|
+
variables: { ...variables },
|
|
736
|
+
...stack.length > 0 ? { callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) } : {}
|
|
737
|
+
}, pendingAccesses);
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
const structureState = parseStructureState(payload);
|
|
741
|
+
if (structureState) {
|
|
742
|
+
variables[structureState.variable] = structureState.value;
|
|
743
|
+
objectKinds[structureState.variable] = structureState.structure;
|
|
744
|
+
appendJavaTraceStep(trace, {
|
|
745
|
+
line,
|
|
746
|
+
event: "line",
|
|
747
|
+
function: currentFunction,
|
|
748
|
+
variables: { ...variables },
|
|
749
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
|
|
750
|
+
visualization: {
|
|
751
|
+
objectKinds: { ...objectKinds }
|
|
752
|
+
}
|
|
753
|
+
}, pendingAccesses);
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
const objectState = parseObjectState(payload);
|
|
757
|
+
if (objectState) {
|
|
758
|
+
variables[objectState.variable] = { __ref__: objectState.visualization.objectId ?? `${objectState.variable}-object` };
|
|
759
|
+
appendJavaTraceStep(trace, {
|
|
760
|
+
line,
|
|
761
|
+
event: "line",
|
|
762
|
+
function: currentFunction,
|
|
763
|
+
variables: { ...variables },
|
|
764
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
|
|
765
|
+
visualization: {
|
|
766
|
+
objectKinds: { [objectState.variable]: "object" },
|
|
767
|
+
hashMaps: [objectState.visualization]
|
|
768
|
+
}
|
|
769
|
+
}, pendingAccesses);
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
const objectField = parseObjectFieldEvent(payload);
|
|
773
|
+
if (objectField) {
|
|
774
|
+
variables[objectField.variable] = { __ref__: `${objectField.variable}-object` };
|
|
775
|
+
const currentCallStack = stack.length > 0 ? stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) : void 0;
|
|
776
|
+
if (mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, {
|
|
777
|
+
variables: { ...variables },
|
|
778
|
+
accesses: void 0,
|
|
779
|
+
visualization: buildFieldVisualization(objectField)
|
|
780
|
+
})) {
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
appendJavaTraceStep(trace, {
|
|
784
|
+
line,
|
|
785
|
+
event: "line",
|
|
786
|
+
function: currentFunction,
|
|
787
|
+
variables: { ...variables },
|
|
788
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
|
|
789
|
+
visualization: buildFieldVisualization(objectField)
|
|
790
|
+
}, pendingAccesses);
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
Object.assign(variables, parseKeyValuePairs(payload));
|
|
794
|
+
const accesses = parseAccessEvent(payload);
|
|
795
|
+
if (accesses) {
|
|
796
|
+
const currentCallStack = stack.length > 0 ? stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) : void 0;
|
|
797
|
+
if (mergeAccessesIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, accesses)) {
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
pendingAccesses.push(...accesses);
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
appendJavaTraceStep(trace, {
|
|
804
|
+
line,
|
|
805
|
+
event: "line",
|
|
806
|
+
function: currentFunction,
|
|
807
|
+
variables: { ...variables },
|
|
808
|
+
...stack.length > 0 ? { callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) } : {}
|
|
809
|
+
}, pendingAccesses);
|
|
810
|
+
}
|
|
811
|
+
if (pendingAccesses.length > 0 && trace.length > 0) {
|
|
812
|
+
const last = trace[trace.length - 1];
|
|
813
|
+
last.accesses = [...last.accesses ?? [], ...pendingAccesses];
|
|
814
|
+
}
|
|
815
|
+
return trace;
|
|
816
|
+
}
|
|
817
|
+
function buildJavaExecutionResult(output, events, executionTimeMs = 0, traceLimitExceeded, timeoutReason, maxTraceSteps, sourceText) {
|
|
818
|
+
const trace = eventsToRawTrace(events, sourceText);
|
|
819
|
+
return {
|
|
820
|
+
success: true,
|
|
821
|
+
output,
|
|
822
|
+
trace,
|
|
823
|
+
executionTimeMs,
|
|
824
|
+
consoleOutput: [],
|
|
825
|
+
...traceLimitExceeded !== void 0 ? { traceLimitExceeded } : {},
|
|
826
|
+
...maxTraceSteps !== void 0 ? { maxTraceSteps } : {},
|
|
827
|
+
...timeoutReason ? { timeoutReason } : {},
|
|
828
|
+
lineEventCount: trace.filter((step) => step.event === "line").length,
|
|
829
|
+
traceStepCount: trace.length
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
function normalizeJavaTraceContract(result) {
|
|
833
|
+
const normalized = normalizeRuntimeTraceContract(
|
|
834
|
+
"java",
|
|
835
|
+
buildJavaExecutionResult(
|
|
836
|
+
result.output,
|
|
837
|
+
result.events,
|
|
838
|
+
0,
|
|
839
|
+
result.traceLimitExceeded,
|
|
840
|
+
result.timeoutReason,
|
|
841
|
+
void 0,
|
|
842
|
+
result.sourceText
|
|
843
|
+
)
|
|
844
|
+
);
|
|
845
|
+
return {
|
|
846
|
+
...normalized,
|
|
847
|
+
language: "java"
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
function adaptJavaTraceExecutionResult(result) {
|
|
851
|
+
return adaptTraceExecutionResult("java", result);
|
|
852
|
+
}
|
|
258
853
|
export {
|
|
259
854
|
RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION,
|
|
260
855
|
adaptJavaScriptTraceExecutionResult,
|
|
856
|
+
adaptJavaTraceExecutionResult as adaptJavaSpikeTraceExecutionResult,
|
|
857
|
+
adaptJavaTraceExecutionResult,
|
|
261
858
|
adaptPythonTraceExecutionResult,
|
|
262
859
|
adaptTraceExecutionResult,
|
|
860
|
+
buildJavaExecutionResult,
|
|
861
|
+
buildJavaExecutionResult as buildJavaSpikeExecutionResult,
|
|
862
|
+
normalizeJavaTraceContract as normalizeJavaSpikeTraceContract,
|
|
863
|
+
normalizeJavaTraceContract,
|
|
263
864
|
normalizeRuntimeTraceContract,
|
|
264
865
|
stableStringifyRuntimeTraceContract
|
|
265
866
|
};
|