@tracecode/harness 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +30 -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 +17 -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/index.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
|
|
@@ -256,6 +344,513 @@ function adaptJavaScriptTraceExecutionResult(language, result) {
|
|
|
256
344
|
return adaptTraceExecutionResult(language, result);
|
|
257
345
|
}
|
|
258
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
|
+
}
|
|
853
|
+
|
|
259
854
|
// packages/harness-browser/src/javascript-worker-client.ts
|
|
260
855
|
var EXECUTION_TIMEOUT_MS = 7e3;
|
|
261
856
|
var INTERVIEW_MODE_TIMEOUT_MS = 5e3;
|
|
@@ -582,6 +1177,7 @@ var PYTHON_RUNTIME_PROFILE = {
|
|
|
582
1177
|
maxTraceSteps: true,
|
|
583
1178
|
maxLineEvents: true,
|
|
584
1179
|
maxSingleLineHits: true,
|
|
1180
|
+
maxStoredEvents: true,
|
|
585
1181
|
minimalTrace: true
|
|
586
1182
|
},
|
|
587
1183
|
fidelity: {
|
|
@@ -643,6 +1239,7 @@ var JAVASCRIPT_RUNTIME_PROFILE = {
|
|
|
643
1239
|
maxTraceSteps: true,
|
|
644
1240
|
maxLineEvents: true,
|
|
645
1241
|
maxSingleLineHits: true,
|
|
1242
|
+
maxStoredEvents: true,
|
|
646
1243
|
minimalTrace: true
|
|
647
1244
|
},
|
|
648
1245
|
fidelity: {
|
|
@@ -704,6 +1301,7 @@ var TYPESCRIPT_RUNTIME_PROFILE = {
|
|
|
704
1301
|
maxTraceSteps: true,
|
|
705
1302
|
maxLineEvents: true,
|
|
706
1303
|
maxSingleLineHits: true,
|
|
1304
|
+
maxStoredEvents: true,
|
|
707
1305
|
minimalTrace: true
|
|
708
1306
|
},
|
|
709
1307
|
fidelity: {
|
|
@@ -734,10 +1332,78 @@ var TYPESCRIPT_RUNTIME_PROFILE = {
|
|
|
734
1332
|
}
|
|
735
1333
|
}
|
|
736
1334
|
};
|
|
1335
|
+
var JAVA_RUNTIME_PROFILE = {
|
|
1336
|
+
language: "java",
|
|
1337
|
+
maturity: "experimental",
|
|
1338
|
+
capabilities: {
|
|
1339
|
+
execution: {
|
|
1340
|
+
styles: {
|
|
1341
|
+
function: true,
|
|
1342
|
+
solutionMethod: true,
|
|
1343
|
+
opsClass: true,
|
|
1344
|
+
script: false,
|
|
1345
|
+
interviewMode: true
|
|
1346
|
+
},
|
|
1347
|
+
timeouts: {
|
|
1348
|
+
clientTimeouts: true,
|
|
1349
|
+
runtimeTimeouts: true
|
|
1350
|
+
}
|
|
1351
|
+
},
|
|
1352
|
+
tracing: {
|
|
1353
|
+
supported: true,
|
|
1354
|
+
events: {
|
|
1355
|
+
line: true,
|
|
1356
|
+
call: true,
|
|
1357
|
+
return: true,
|
|
1358
|
+
exception: true,
|
|
1359
|
+
stdout: false,
|
|
1360
|
+
timeout: true
|
|
1361
|
+
},
|
|
1362
|
+
controls: {
|
|
1363
|
+
maxTraceSteps: true,
|
|
1364
|
+
maxLineEvents: false,
|
|
1365
|
+
maxSingleLineHits: false,
|
|
1366
|
+
maxStoredEvents: true,
|
|
1367
|
+
minimalTrace: false
|
|
1368
|
+
},
|
|
1369
|
+
fidelity: {
|
|
1370
|
+
preciseLineMapping: true,
|
|
1371
|
+
stableFunctionNames: true,
|
|
1372
|
+
callStack: true
|
|
1373
|
+
}
|
|
1374
|
+
},
|
|
1375
|
+
diagnostics: {
|
|
1376
|
+
compileErrors: true,
|
|
1377
|
+
runtimeErrors: true,
|
|
1378
|
+
mappedErrorLines: false,
|
|
1379
|
+
stackTraces: true
|
|
1380
|
+
},
|
|
1381
|
+
structures: {
|
|
1382
|
+
treeNodeRefs: true,
|
|
1383
|
+
listNodeRefs: true,
|
|
1384
|
+
mapSerialization: true,
|
|
1385
|
+
setSerialization: true,
|
|
1386
|
+
graphSerialization: false,
|
|
1387
|
+
cycleReferences: true
|
|
1388
|
+
},
|
|
1389
|
+
visualization: {
|
|
1390
|
+
runtimePayloads: true,
|
|
1391
|
+
objectKinds: true,
|
|
1392
|
+
hashMaps: true,
|
|
1393
|
+
stepVisualization: true
|
|
1394
|
+
}
|
|
1395
|
+
},
|
|
1396
|
+
notes: [
|
|
1397
|
+
"Java currently supports the browser-local Java 17 lane for function, solution-method, and ops-class execution.",
|
|
1398
|
+
"Interview-mode Java reuses the same browser-local execution path and remains experimental in the first harness slice.",
|
|
1399
|
+
"Script-style Java is not enabled yet."
|
|
1400
|
+
]
|
|
1401
|
+
};
|
|
737
1402
|
var LANGUAGE_RUNTIME_PROFILES = {
|
|
738
1403
|
python: PYTHON_RUNTIME_PROFILE,
|
|
739
1404
|
javascript: JAVASCRIPT_RUNTIME_PROFILE,
|
|
740
|
-
typescript: TYPESCRIPT_RUNTIME_PROFILE
|
|
1405
|
+
typescript: TYPESCRIPT_RUNTIME_PROFILE,
|
|
1406
|
+
java: JAVA_RUNTIME_PROFILE
|
|
741
1407
|
};
|
|
742
1408
|
var SUPPORTED_LANGUAGES = Object.freeze(
|
|
743
1409
|
Object.keys(LANGUAGE_RUNTIME_PROFILES)
|
|
@@ -814,13 +1480,307 @@ function createJavaScriptRuntimeClient(runtimeLanguage, workerClient) {
|
|
|
814
1480
|
return new JavaScriptRuntimeClient(runtimeLanguage, workerClient);
|
|
815
1481
|
}
|
|
816
1482
|
|
|
1483
|
+
// packages/harness-browser/src/java-worker-client.ts
|
|
1484
|
+
var TRACING_TIMEOUT_MS2 = 25e3;
|
|
1485
|
+
var INIT_TIMEOUT_MS2 = 25e3;
|
|
1486
|
+
var MESSAGE_TIMEOUT_MS2 = 3e4;
|
|
1487
|
+
var WORKER_READY_TIMEOUT_MS2 = 1e4;
|
|
1488
|
+
var JavaWorkerClient = class {
|
|
1489
|
+
constructor(options) {
|
|
1490
|
+
this.options = options;
|
|
1491
|
+
this.debug = options.debug ?? process.env.NODE_ENV === "development";
|
|
1492
|
+
}
|
|
1493
|
+
worker = null;
|
|
1494
|
+
pendingMessages = /* @__PURE__ */ new Map();
|
|
1495
|
+
messageId = 0;
|
|
1496
|
+
isInitializing = false;
|
|
1497
|
+
initPromise = null;
|
|
1498
|
+
workerReadyPromise = null;
|
|
1499
|
+
workerReadyResolve = null;
|
|
1500
|
+
workerReadyReject = null;
|
|
1501
|
+
debug;
|
|
1502
|
+
isSupported() {
|
|
1503
|
+
return typeof Worker !== "undefined";
|
|
1504
|
+
}
|
|
1505
|
+
getWorker() {
|
|
1506
|
+
if (this.worker) return this.worker;
|
|
1507
|
+
if (!this.isSupported()) {
|
|
1508
|
+
throw new Error("Web Workers are not supported in this environment");
|
|
1509
|
+
}
|
|
1510
|
+
this.workerReadyPromise = new Promise((resolve, reject) => {
|
|
1511
|
+
this.workerReadyResolve = resolve;
|
|
1512
|
+
this.workerReadyReject = (error) => reject(error);
|
|
1513
|
+
});
|
|
1514
|
+
const workerUrl = this.debug && !this.options.workerUrl.includes("?") ? `${this.options.workerUrl}?dev=${Date.now()}` : this.options.workerUrl;
|
|
1515
|
+
this.worker = new Worker(workerUrl);
|
|
1516
|
+
this.worker.onmessage = (event) => {
|
|
1517
|
+
const { id, type, payload } = event.data;
|
|
1518
|
+
if (type === "worker-ready") {
|
|
1519
|
+
this.workerReadyResolve?.();
|
|
1520
|
+
this.workerReadyResolve = null;
|
|
1521
|
+
this.workerReadyReject = null;
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
if (!id) return;
|
|
1525
|
+
const pending = this.pendingMessages.get(id);
|
|
1526
|
+
if (!pending) return;
|
|
1527
|
+
this.pendingMessages.delete(id);
|
|
1528
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
1529
|
+
if (type === "error") {
|
|
1530
|
+
pending.reject(new Error(payload.error));
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
pending.resolve(payload);
|
|
1534
|
+
};
|
|
1535
|
+
this.worker.onerror = (error) => {
|
|
1536
|
+
const workerError = new Error(error.message || "Java worker error");
|
|
1537
|
+
this.workerReadyReject?.(workerError);
|
|
1538
|
+
this.workerReadyResolve = null;
|
|
1539
|
+
this.workerReadyReject = null;
|
|
1540
|
+
for (const [, pending] of this.pendingMessages) {
|
|
1541
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
1542
|
+
pending.reject(workerError);
|
|
1543
|
+
}
|
|
1544
|
+
this.pendingMessages.clear();
|
|
1545
|
+
this.terminateAndReset(workerError);
|
|
1546
|
+
};
|
|
1547
|
+
return this.worker;
|
|
1548
|
+
}
|
|
1549
|
+
async waitForWorkerReady() {
|
|
1550
|
+
const readyPromise = this.workerReadyPromise;
|
|
1551
|
+
if (!readyPromise) return;
|
|
1552
|
+
await new Promise((resolve, reject) => {
|
|
1553
|
+
let settled = false;
|
|
1554
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
1555
|
+
if (settled) return;
|
|
1556
|
+
settled = true;
|
|
1557
|
+
const timeoutError = new Error(
|
|
1558
|
+
`Java worker failed to initialize in time (${Math.round(WORKER_READY_TIMEOUT_MS2 / 1e3)}s)`
|
|
1559
|
+
);
|
|
1560
|
+
this.terminateAndReset(timeoutError);
|
|
1561
|
+
reject(timeoutError);
|
|
1562
|
+
}, WORKER_READY_TIMEOUT_MS2);
|
|
1563
|
+
readyPromise.then(() => {
|
|
1564
|
+
if (settled) return;
|
|
1565
|
+
settled = true;
|
|
1566
|
+
globalThis.clearTimeout(timeoutId);
|
|
1567
|
+
resolve();
|
|
1568
|
+
}).catch((error) => {
|
|
1569
|
+
if (settled) return;
|
|
1570
|
+
settled = true;
|
|
1571
|
+
globalThis.clearTimeout(timeoutId);
|
|
1572
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
1573
|
+
});
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
async sendMessage(type, payload, timeoutMs = MESSAGE_TIMEOUT_MS2) {
|
|
1577
|
+
const worker = this.getWorker();
|
|
1578
|
+
await this.waitForWorkerReady();
|
|
1579
|
+
const id = String(++this.messageId);
|
|
1580
|
+
return new Promise((resolve, reject) => {
|
|
1581
|
+
this.pendingMessages.set(id, {
|
|
1582
|
+
resolve,
|
|
1583
|
+
reject
|
|
1584
|
+
});
|
|
1585
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
1586
|
+
const pending2 = this.pendingMessages.get(id);
|
|
1587
|
+
if (!pending2) return;
|
|
1588
|
+
this.pendingMessages.delete(id);
|
|
1589
|
+
pending2.reject(new Error(`Worker request timed out: ${type}`));
|
|
1590
|
+
}, timeoutMs);
|
|
1591
|
+
const pending = this.pendingMessages.get(id);
|
|
1592
|
+
if (pending) pending.timeoutId = timeoutId;
|
|
1593
|
+
worker.postMessage({ id, type, payload });
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1596
|
+
async executeWithTimeout(executor, timeoutMs) {
|
|
1597
|
+
return new Promise((resolve, reject) => {
|
|
1598
|
+
let settled = false;
|
|
1599
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
1600
|
+
if (settled) return;
|
|
1601
|
+
settled = true;
|
|
1602
|
+
this.terminateAndReset();
|
|
1603
|
+
reject(
|
|
1604
|
+
new Error(
|
|
1605
|
+
`Java execution timed out after ${Math.round(timeoutMs / 1e3)} seconds.`
|
|
1606
|
+
)
|
|
1607
|
+
);
|
|
1608
|
+
}, timeoutMs);
|
|
1609
|
+
executor().then((result) => {
|
|
1610
|
+
if (settled) return;
|
|
1611
|
+
settled = true;
|
|
1612
|
+
globalThis.clearTimeout(timeoutId);
|
|
1613
|
+
resolve(result);
|
|
1614
|
+
}).catch((error) => {
|
|
1615
|
+
if (settled) return;
|
|
1616
|
+
settled = true;
|
|
1617
|
+
globalThis.clearTimeout(timeoutId);
|
|
1618
|
+
reject(error);
|
|
1619
|
+
});
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
terminateAndReset(reason = new Error("Worker was terminated")) {
|
|
1623
|
+
this.workerReadyReject?.(reason);
|
|
1624
|
+
if (this.worker) {
|
|
1625
|
+
this.worker.terminate();
|
|
1626
|
+
this.worker = null;
|
|
1627
|
+
}
|
|
1628
|
+
this.initPromise = null;
|
|
1629
|
+
this.isInitializing = false;
|
|
1630
|
+
this.workerReadyPromise = null;
|
|
1631
|
+
this.workerReadyResolve = null;
|
|
1632
|
+
this.workerReadyReject = null;
|
|
1633
|
+
for (const [, pending] of this.pendingMessages) {
|
|
1634
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
1635
|
+
pending.reject(reason);
|
|
1636
|
+
}
|
|
1637
|
+
this.pendingMessages.clear();
|
|
1638
|
+
}
|
|
1639
|
+
async init() {
|
|
1640
|
+
if (this.initPromise) return this.initPromise;
|
|
1641
|
+
if (this.isInitializing) {
|
|
1642
|
+
await new Promise((resolve) => globalThis.setTimeout(resolve, 100));
|
|
1643
|
+
return this.init();
|
|
1644
|
+
}
|
|
1645
|
+
this.isInitializing = true;
|
|
1646
|
+
this.initPromise = this.sendMessage("init", void 0, INIT_TIMEOUT_MS2);
|
|
1647
|
+
try {
|
|
1648
|
+
return await this.initPromise;
|
|
1649
|
+
} catch (error) {
|
|
1650
|
+
this.initPromise = null;
|
|
1651
|
+
throw error;
|
|
1652
|
+
} finally {
|
|
1653
|
+
this.isInitializing = false;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
async executeWithTracing(code, functionName, inputs, options, executionStyle) {
|
|
1657
|
+
await this.init();
|
|
1658
|
+
return this.executeWithTimeout(
|
|
1659
|
+
() => this.sendMessage(
|
|
1660
|
+
"execute-with-tracing",
|
|
1661
|
+
{ code, functionName, inputs, options, executionStyle },
|
|
1662
|
+
TRACING_TIMEOUT_MS2 + 5e3
|
|
1663
|
+
),
|
|
1664
|
+
TRACING_TIMEOUT_MS2
|
|
1665
|
+
);
|
|
1666
|
+
}
|
|
1667
|
+
async executeCode(code, functionName, inputs, options, executionStyle) {
|
|
1668
|
+
const result = await this.executeWithTracing(code, functionName, inputs, options, executionStyle);
|
|
1669
|
+
if (!result.success) {
|
|
1670
|
+
return {
|
|
1671
|
+
success: false,
|
|
1672
|
+
output: null,
|
|
1673
|
+
error: result.error ?? "Java execution failed",
|
|
1674
|
+
...result.errorLine !== void 0 ? { errorLine: result.errorLine } : {},
|
|
1675
|
+
consoleOutput: result.consoleOutput
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
return {
|
|
1679
|
+
success: true,
|
|
1680
|
+
output: result.output,
|
|
1681
|
+
consoleOutput: result.consoleOutput
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
async executeCodeInterviewMode(code, functionName, inputs, options, executionStyle) {
|
|
1685
|
+
return this.executeCode(code, functionName, inputs, options, executionStyle);
|
|
1686
|
+
}
|
|
1687
|
+
terminate() {
|
|
1688
|
+
this.terminateAndReset();
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
|
|
1692
|
+
// packages/harness-browser/src/java-runtime-client.ts
|
|
1693
|
+
var JavaRuntimeClient = class {
|
|
1694
|
+
constructor(workerClient) {
|
|
1695
|
+
this.workerClient = workerClient;
|
|
1696
|
+
}
|
|
1697
|
+
async init() {
|
|
1698
|
+
return this.workerClient.init();
|
|
1699
|
+
}
|
|
1700
|
+
async executeWithTracing(code, functionName, inputs, options, executionStyle = "function") {
|
|
1701
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
|
|
1702
|
+
request: "trace",
|
|
1703
|
+
executionStyle,
|
|
1704
|
+
functionName
|
|
1705
|
+
});
|
|
1706
|
+
const rawResult = await this.workerClient.executeWithTracing(
|
|
1707
|
+
code,
|
|
1708
|
+
functionName ?? "",
|
|
1709
|
+
inputs,
|
|
1710
|
+
options,
|
|
1711
|
+
executionStyle
|
|
1712
|
+
);
|
|
1713
|
+
if (!rawResult.success) {
|
|
1714
|
+
return {
|
|
1715
|
+
success: false,
|
|
1716
|
+
error: rawResult.error ?? "Java tracing failed",
|
|
1717
|
+
...rawResult.errorLine !== void 0 ? { errorLine: rawResult.errorLine } : {},
|
|
1718
|
+
trace: [],
|
|
1719
|
+
executionTimeMs: rawResult.executionTimeMs,
|
|
1720
|
+
consoleOutput: rawResult.consoleOutput,
|
|
1721
|
+
...rawResult.traceLimitExceeded !== void 0 ? { traceLimitExceeded: rawResult.traceLimitExceeded } : {},
|
|
1722
|
+
...rawResult.timeoutReason ? { timeoutReason: rawResult.timeoutReason } : {},
|
|
1723
|
+
lineEventCount: 0,
|
|
1724
|
+
traceStepCount: 0
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
const adapted = adaptJavaTraceExecutionResult(
|
|
1728
|
+
buildJavaExecutionResult(
|
|
1729
|
+
rawResult.output,
|
|
1730
|
+
rawResult.events,
|
|
1731
|
+
rawResult.executionTimeMs,
|
|
1732
|
+
rawResult.traceLimitExceeded,
|
|
1733
|
+
rawResult.timeoutReason,
|
|
1734
|
+
void 0,
|
|
1735
|
+
rawResult.sourceText
|
|
1736
|
+
)
|
|
1737
|
+
);
|
|
1738
|
+
return {
|
|
1739
|
+
...adapted,
|
|
1740
|
+
consoleOutput: rawResult.consoleOutput,
|
|
1741
|
+
executionTimeMs: rawResult.executionTimeMs
|
|
1742
|
+
};
|
|
1743
|
+
}
|
|
1744
|
+
async executeCode(code, functionName, inputs, executionStyle = "function") {
|
|
1745
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
|
|
1746
|
+
request: "execute",
|
|
1747
|
+
executionStyle,
|
|
1748
|
+
functionName
|
|
1749
|
+
});
|
|
1750
|
+
return this.workerClient.executeCode(
|
|
1751
|
+
code,
|
|
1752
|
+
functionName,
|
|
1753
|
+
inputs,
|
|
1754
|
+
void 0,
|
|
1755
|
+
executionStyle
|
|
1756
|
+
);
|
|
1757
|
+
}
|
|
1758
|
+
async executeCodeInterviewMode(code, functionName, inputs, executionStyle = "function") {
|
|
1759
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
|
|
1760
|
+
request: "interview",
|
|
1761
|
+
executionStyle,
|
|
1762
|
+
functionName
|
|
1763
|
+
});
|
|
1764
|
+
return this.workerClient.executeCodeInterviewMode(
|
|
1765
|
+
code,
|
|
1766
|
+
functionName,
|
|
1767
|
+
inputs,
|
|
1768
|
+
void 0,
|
|
1769
|
+
executionStyle
|
|
1770
|
+
);
|
|
1771
|
+
}
|
|
1772
|
+
};
|
|
1773
|
+
function createJavaRuntimeClient(workerClient) {
|
|
1774
|
+
return new JavaRuntimeClient(workerClient);
|
|
1775
|
+
}
|
|
1776
|
+
|
|
817
1777
|
// packages/harness-browser/src/pyodide-worker-client.ts
|
|
818
1778
|
var EXECUTION_TIMEOUT_MS2 = 1e4;
|
|
819
1779
|
var INTERVIEW_MODE_TIMEOUT_MS2 = 5e3;
|
|
820
|
-
var
|
|
821
|
-
var
|
|
822
|
-
var
|
|
823
|
-
var
|
|
1780
|
+
var TRACING_TIMEOUT_MS3 = 3e4;
|
|
1781
|
+
var INIT_TIMEOUT_MS3 = 12e4;
|
|
1782
|
+
var MESSAGE_TIMEOUT_MS3 = 2e4;
|
|
1783
|
+
var WORKER_READY_TIMEOUT_MS3 = 1e4;
|
|
824
1784
|
var PyodideWorkerClient = class {
|
|
825
1785
|
constructor(options) {
|
|
826
1786
|
this.options = options;
|
|
@@ -910,14 +1870,14 @@ var PyodideWorkerClient = class {
|
|
|
910
1870
|
if (settled) return;
|
|
911
1871
|
settled = true;
|
|
912
1872
|
const timeoutError = new Error(
|
|
913
|
-
`Python worker failed to initialize in time (${Math.round(
|
|
1873
|
+
`Python worker failed to initialize in time (${Math.round(WORKER_READY_TIMEOUT_MS3 / 1e3)}s)`
|
|
914
1874
|
);
|
|
915
1875
|
if (this.debug) {
|
|
916
|
-
console.warn("[PyodideWorkerClient] worker-ready timeout", { timeoutMs:
|
|
1876
|
+
console.warn("[PyodideWorkerClient] worker-ready timeout", { timeoutMs: WORKER_READY_TIMEOUT_MS3 });
|
|
917
1877
|
}
|
|
918
1878
|
this.terminateAndReset(timeoutError);
|
|
919
1879
|
reject(timeoutError);
|
|
920
|
-
},
|
|
1880
|
+
}, WORKER_READY_TIMEOUT_MS3);
|
|
921
1881
|
readyPromise.then(() => {
|
|
922
1882
|
if (settled) return;
|
|
923
1883
|
settled = true;
|
|
@@ -934,7 +1894,7 @@ var PyodideWorkerClient = class {
|
|
|
934
1894
|
/**
|
|
935
1895
|
* Send a message to the worker and wait for a response
|
|
936
1896
|
*/
|
|
937
|
-
async sendMessage(type, payload, timeoutMs =
|
|
1897
|
+
async sendMessage(type, payload, timeoutMs = MESSAGE_TIMEOUT_MS3) {
|
|
938
1898
|
const worker = this.getWorker();
|
|
939
1899
|
await this.waitForWorkerReady();
|
|
940
1900
|
const id = String(++this.messageId);
|
|
@@ -1018,7 +1978,7 @@ var PyodideWorkerClient = class {
|
|
|
1018
1978
|
this.isInitializing = true;
|
|
1019
1979
|
this.initPromise = (async () => {
|
|
1020
1980
|
try {
|
|
1021
|
-
return await this.sendMessage("init", void 0,
|
|
1981
|
+
return await this.sendMessage("init", void 0, INIT_TIMEOUT_MS3);
|
|
1022
1982
|
} catch (error) {
|
|
1023
1983
|
const message = error instanceof Error ? error.message : String(error);
|
|
1024
1984
|
const shouldRetry = message.includes("Worker request timed out: init") || message.includes("Worker was terminated") || message.includes("Worker error") || message.includes("failed to initialize in time");
|
|
@@ -1029,7 +1989,7 @@ var PyodideWorkerClient = class {
|
|
|
1029
1989
|
console.warn("[PyodideWorkerClient] init failed, resetting worker and retrying once", { message });
|
|
1030
1990
|
}
|
|
1031
1991
|
this.terminateAndReset();
|
|
1032
|
-
return this.sendMessage("init", void 0,
|
|
1992
|
+
return this.sendMessage("init", void 0, INIT_TIMEOUT_MS3);
|
|
1033
1993
|
}
|
|
1034
1994
|
})();
|
|
1035
1995
|
try {
|
|
@@ -1056,9 +2016,9 @@ var PyodideWorkerClient = class {
|
|
|
1056
2016
|
inputs,
|
|
1057
2017
|
executionStyle,
|
|
1058
2018
|
options
|
|
1059
|
-
},
|
|
2019
|
+
}, TRACING_TIMEOUT_MS3 + 5e3),
|
|
1060
2020
|
// Message timeout slightly longer than execution timeout
|
|
1061
|
-
|
|
2021
|
+
TRACING_TIMEOUT_MS3
|
|
1062
2022
|
);
|
|
1063
2023
|
} catch (error) {
|
|
1064
2024
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -1068,7 +2028,7 @@ var PyodideWorkerClient = class {
|
|
|
1068
2028
|
success: false,
|
|
1069
2029
|
error: errorMessage,
|
|
1070
2030
|
trace: [],
|
|
1071
|
-
executionTimeMs:
|
|
2031
|
+
executionTimeMs: TRACING_TIMEOUT_MS3,
|
|
1072
2032
|
consoleOutput: [],
|
|
1073
2033
|
traceLimitExceeded: true,
|
|
1074
2034
|
timeoutReason: "client-timeout",
|
|
@@ -1224,6 +2184,7 @@ var DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS = Object.freeze({
|
|
|
1224
2184
|
pythonRuntimeCore: "pyodide/runtime-core.js",
|
|
1225
2185
|
pythonSnippets: "generated-python-harness-snippets.js",
|
|
1226
2186
|
javascriptWorker: "javascript-worker.js",
|
|
2187
|
+
javaWorker: "java-worker.js",
|
|
1227
2188
|
typescriptCompiler: "vendor/typescript.js"
|
|
1228
2189
|
});
|
|
1229
2190
|
function isExplicitAssetPath(pathname) {
|
|
@@ -1257,6 +2218,7 @@ function resolveBrowserHarnessAssets(options = {}) {
|
|
|
1257
2218
|
assetBaseUrl,
|
|
1258
2219
|
assets.javascriptWorker ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.javascriptWorker
|
|
1259
2220
|
),
|
|
2221
|
+
javaWorker: resolveAssetPath(assetBaseUrl, assets.javaWorker ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.javaWorker),
|
|
1260
2222
|
typescriptCompiler: resolveAssetPath(
|
|
1261
2223
|
assetBaseUrl,
|
|
1262
2224
|
assets.typescriptCompiler ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.typescriptCompiler
|
|
@@ -1270,6 +2232,7 @@ var BrowserHarnessRuntime = class {
|
|
|
1270
2232
|
supportedLanguages = SUPPORTED_LANGUAGES;
|
|
1271
2233
|
pythonWorkerClient;
|
|
1272
2234
|
javaScriptWorkerClient;
|
|
2235
|
+
javaWorkerClient;
|
|
1273
2236
|
clients;
|
|
1274
2237
|
constructor(options = {}) {
|
|
1275
2238
|
this.assets = resolveBrowserHarnessAssets(options);
|
|
@@ -1281,10 +2244,15 @@ var BrowserHarnessRuntime = class {
|
|
|
1281
2244
|
workerUrl: this.assets.javascriptWorker,
|
|
1282
2245
|
debug: options.debug
|
|
1283
2246
|
});
|
|
2247
|
+
this.javaWorkerClient = new JavaWorkerClient({
|
|
2248
|
+
workerUrl: this.assets.javaWorker,
|
|
2249
|
+
debug: options.debug
|
|
2250
|
+
});
|
|
1284
2251
|
this.clients = {
|
|
1285
2252
|
python: createPythonRuntimeClient(this.pythonWorkerClient),
|
|
1286
2253
|
javascript: createJavaScriptRuntimeClient("javascript", this.javaScriptWorkerClient),
|
|
1287
|
-
typescript: createJavaScriptRuntimeClient("typescript", this.javaScriptWorkerClient)
|
|
2254
|
+
typescript: createJavaScriptRuntimeClient("typescript", this.javaScriptWorkerClient),
|
|
2255
|
+
java: createJavaRuntimeClient(this.javaWorkerClient)
|
|
1288
2256
|
};
|
|
1289
2257
|
}
|
|
1290
2258
|
getClient(language) {
|
|
@@ -1308,11 +2276,16 @@ var BrowserHarnessRuntime = class {
|
|
|
1308
2276
|
this.pythonWorkerClient.terminate();
|
|
1309
2277
|
return;
|
|
1310
2278
|
}
|
|
2279
|
+
if (language === "java") {
|
|
2280
|
+
this.javaWorkerClient.terminate();
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
1311
2283
|
this.javaScriptWorkerClient.terminate();
|
|
1312
2284
|
}
|
|
1313
2285
|
dispose() {
|
|
1314
2286
|
this.pythonWorkerClient.terminate();
|
|
1315
2287
|
this.javaScriptWorkerClient.terminate();
|
|
2288
|
+
this.javaWorkerClient.terminate();
|
|
1316
2289
|
}
|
|
1317
2290
|
};
|
|
1318
2291
|
function createBrowserHarness(options = {}) {
|
|
@@ -2824,9 +3797,13 @@ export {
|
|
|
2824
3797
|
TEMPLATE_PYTHON_TRACE_SERIALIZE_FUNCTION,
|
|
2825
3798
|
TYPESCRIPT_RUNTIME_DECLARATIONS,
|
|
2826
3799
|
adaptJavaScriptTraceExecutionResult,
|
|
3800
|
+
adaptJavaTraceExecutionResult as adaptJavaSpikeTraceExecutionResult,
|
|
3801
|
+
adaptJavaTraceExecutionResult,
|
|
2827
3802
|
adaptPythonTraceExecutionResult,
|
|
2828
3803
|
adaptTraceExecutionResult,
|
|
2829
3804
|
assertRuntimeRequestSupported,
|
|
3805
|
+
buildJavaExecutionResult,
|
|
3806
|
+
buildJavaExecutionResult as buildJavaSpikeExecutionResult,
|
|
2830
3807
|
createBrowserHarness,
|
|
2831
3808
|
executeJavaScriptCode,
|
|
2832
3809
|
executeJavaScriptWithTracing,
|
|
@@ -2838,6 +3815,8 @@ export {
|
|
|
2838
3815
|
getSupportedLanguageProfiles,
|
|
2839
3816
|
identifyConversions,
|
|
2840
3817
|
isLanguageSupported,
|
|
3818
|
+
normalizeJavaTraceContract as normalizeJavaSpikeTraceContract,
|
|
3819
|
+
normalizeJavaTraceContract,
|
|
2841
3820
|
normalizeRuntimeTraceContract,
|
|
2842
3821
|
resolveBrowserHarnessAssets,
|
|
2843
3822
|
stableStringifyRuntimeTraceContract,
|