@tracecode/harness 0.5.0 → 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/LICENSE +67 -80
- package/README.md +31 -4
- 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 +1055 -52
- 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 +1049 -52
- 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.cjs +37 -19
- package/dist/javascript.cjs.map +1 -1
- package/dist/javascript.d.cts +2 -2
- package/dist/javascript.d.ts +2 -2
- package/dist/javascript.js +37 -19
- package/dist/javascript.js.map +1 -1
- package/dist/python.cjs +14 -14
- package/dist/python.cjs.map +1 -1
- package/dist/python.d.cts +8 -8
- package/dist/python.d.ts +8 -8
- package/dist/python.js +14 -14
- package/dist/python.js.map +1 -1
- 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 +12 -6
- 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.cjs
CHANGED
|
@@ -39,9 +39,13 @@ __export(src_exports, {
|
|
|
39
39
|
TEMPLATE_PYTHON_TRACE_SERIALIZE_FUNCTION: () => TEMPLATE_PYTHON_TRACE_SERIALIZE_FUNCTION,
|
|
40
40
|
TYPESCRIPT_RUNTIME_DECLARATIONS: () => TYPESCRIPT_RUNTIME_DECLARATIONS,
|
|
41
41
|
adaptJavaScriptTraceExecutionResult: () => adaptJavaScriptTraceExecutionResult,
|
|
42
|
+
adaptJavaSpikeTraceExecutionResult: () => adaptJavaTraceExecutionResult,
|
|
43
|
+
adaptJavaTraceExecutionResult: () => adaptJavaTraceExecutionResult,
|
|
42
44
|
adaptPythonTraceExecutionResult: () => adaptPythonTraceExecutionResult,
|
|
43
45
|
adaptTraceExecutionResult: () => adaptTraceExecutionResult,
|
|
44
46
|
assertRuntimeRequestSupported: () => assertRuntimeRequestSupported,
|
|
47
|
+
buildJavaExecutionResult: () => buildJavaExecutionResult,
|
|
48
|
+
buildJavaSpikeExecutionResult: () => buildJavaExecutionResult,
|
|
45
49
|
createBrowserHarness: () => createBrowserHarness,
|
|
46
50
|
executeJavaScriptCode: () => executeJavaScriptCode,
|
|
47
51
|
executeJavaScriptWithTracing: () => executeJavaScriptWithTracing,
|
|
@@ -53,6 +57,8 @@ __export(src_exports, {
|
|
|
53
57
|
getSupportedLanguageProfiles: () => getSupportedLanguageProfiles,
|
|
54
58
|
identifyConversions: () => identifyConversions,
|
|
55
59
|
isLanguageSupported: () => isLanguageSupported,
|
|
60
|
+
normalizeJavaSpikeTraceContract: () => normalizeJavaTraceContract,
|
|
61
|
+
normalizeJavaTraceContract: () => normalizeJavaTraceContract,
|
|
56
62
|
normalizeRuntimeTraceContract: () => normalizeRuntimeTraceContract,
|
|
57
63
|
resolveBrowserHarnessAssets: () => resolveBrowserHarnessAssets,
|
|
58
64
|
stableStringifyRuntimeTraceContract: () => stableStringifyRuntimeTraceContract,
|
|
@@ -63,7 +69,7 @@ __export(src_exports, {
|
|
|
63
69
|
module.exports = __toCommonJS(src_exports);
|
|
64
70
|
|
|
65
71
|
// packages/harness-core/src/trace-contract.ts
|
|
66
|
-
var RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION = "2026-
|
|
72
|
+
var RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION = "2026-04-21";
|
|
67
73
|
var TRACE_EVENTS = /* @__PURE__ */ new Set([
|
|
68
74
|
"line",
|
|
69
75
|
"call",
|
|
@@ -184,7 +190,39 @@ function normalizeAccesses(accesses) {
|
|
|
184
190
|
}
|
|
185
191
|
return payload;
|
|
186
192
|
}).filter((access) => access !== null);
|
|
187
|
-
|
|
193
|
+
if (normalized.length === 0) {
|
|
194
|
+
return void 0;
|
|
195
|
+
}
|
|
196
|
+
const statsByVariable = /* @__PURE__ */ new Map();
|
|
197
|
+
for (const access of normalized) {
|
|
198
|
+
const stats = statsByVariable.get(access.variable) ?? { hasCellRead: false, hasCellWrite: false };
|
|
199
|
+
if (access.kind === "cell-read") stats.hasCellRead = true;
|
|
200
|
+
if (access.kind === "cell-write") stats.hasCellWrite = true;
|
|
201
|
+
statsByVariable.set(access.variable, stats);
|
|
202
|
+
}
|
|
203
|
+
const deduped = /* @__PURE__ */ new Set();
|
|
204
|
+
const collapsed = normalized.filter((access) => {
|
|
205
|
+
const stats = statsByVariable.get(access.variable);
|
|
206
|
+
if (access.kind === "indexed-read" && (stats?.hasCellRead || stats?.hasCellWrite)) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
if (access.kind === "indexed-write" && stats?.hasCellWrite) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
const key = JSON.stringify([
|
|
213
|
+
access.variable,
|
|
214
|
+
access.kind,
|
|
215
|
+
access.indices ?? null,
|
|
216
|
+
access.pathDepth ?? null,
|
|
217
|
+
access.method ?? null
|
|
218
|
+
]);
|
|
219
|
+
if (deduped.has(key)) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
deduped.add(key);
|
|
223
|
+
return true;
|
|
224
|
+
});
|
|
225
|
+
return collapsed.length > 0 ? collapsed : void 0;
|
|
188
226
|
}
|
|
189
227
|
function normalizeRecord(value) {
|
|
190
228
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
@@ -248,8 +286,64 @@ function normalizeTraceStep(step) {
|
|
|
248
286
|
...normalizedVisualization ? { visualization: normalizedVisualization } : {}
|
|
249
287
|
};
|
|
250
288
|
}
|
|
289
|
+
function collapseTraceAccessNoise(trace) {
|
|
290
|
+
const variablesWithCellAccess = /* @__PURE__ */ new Set();
|
|
291
|
+
const accessStatsByVariable = /* @__PURE__ */ new Map();
|
|
292
|
+
for (const step of trace) {
|
|
293
|
+
for (const access of step.accesses ?? []) {
|
|
294
|
+
const stats = accessStatsByVariable.get(access.variable) ?? {
|
|
295
|
+
hasCellRead: false,
|
|
296
|
+
hasCellWrite: false,
|
|
297
|
+
hasMutatingCall: false,
|
|
298
|
+
hasIndexedWrite: false
|
|
299
|
+
};
|
|
300
|
+
if (access.kind === "cell-read" || access.kind === "cell-write") {
|
|
301
|
+
variablesWithCellAccess.add(access.variable);
|
|
302
|
+
}
|
|
303
|
+
if (access.kind === "cell-read") stats.hasCellRead = true;
|
|
304
|
+
if (access.kind === "cell-write") stats.hasCellWrite = true;
|
|
305
|
+
if (access.kind === "mutating-call") stats.hasMutatingCall = true;
|
|
306
|
+
if (access.kind === "indexed-write") stats.hasIndexedWrite = true;
|
|
307
|
+
accessStatsByVariable.set(access.variable, stats);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (variablesWithCellAccess.size === 0) {
|
|
311
|
+
return trace;
|
|
312
|
+
}
|
|
313
|
+
return trace.map((step) => {
|
|
314
|
+
if (!step.accesses?.length) {
|
|
315
|
+
return step;
|
|
316
|
+
}
|
|
317
|
+
const filtered = step.accesses.filter((access) => {
|
|
318
|
+
const stats = accessStatsByVariable.get(access.variable);
|
|
319
|
+
if (variablesWithCellAccess.has(access.variable)) {
|
|
320
|
+
return access.kind !== "indexed-read" && access.kind !== "indexed-write";
|
|
321
|
+
}
|
|
322
|
+
if (access.kind === "mutating-call" && stats?.hasIndexedWrite && !stats.hasCellRead && !stats.hasCellWrite) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
if (access.kind === "indexed-read" && stats?.hasMutatingCall && !stats.hasIndexedWrite && !stats.hasCellRead && !stats.hasCellWrite) {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
return true;
|
|
329
|
+
});
|
|
330
|
+
if (filtered.length === step.accesses.length) {
|
|
331
|
+
return step;
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
...step,
|
|
335
|
+
...filtered.length > 0 ? { accesses: filtered } : {},
|
|
336
|
+
...filtered.length === 0 ? { accesses: void 0 } : {}
|
|
337
|
+
};
|
|
338
|
+
});
|
|
339
|
+
}
|
|
251
340
|
function normalizeRuntimeTraceContract(language, result) {
|
|
252
|
-
const
|
|
341
|
+
const rawNormalizedTrace = collapseTraceAccessNoise(
|
|
342
|
+
Array.isArray(result.trace) ? result.trace.map(normalizeTraceStep) : []
|
|
343
|
+
);
|
|
344
|
+
const maxTraceSteps = typeof result.maxTraceSteps === "number" && Number.isFinite(result.maxTraceSteps) ? Math.max(1, Math.floor(result.maxTraceSteps)) : void 0;
|
|
345
|
+
const traceWasClipped = maxTraceSteps !== void 0 && rawNormalizedTrace.length > maxTraceSteps;
|
|
346
|
+
const normalizedTrace = traceWasClipped && maxTraceSteps !== void 0 ? rawNormalizedTrace.slice(0, maxTraceSteps) : rawNormalizedTrace;
|
|
253
347
|
const lineEventCount = typeof result.lineEventCount === "number" && Number.isFinite(result.lineEventCount) ? Math.floor(result.lineEventCount) : normalizedTrace.filter((step) => step.event === "line").length;
|
|
254
348
|
const normalizedConsole = Array.isArray(result.consoleOutput) ? result.consoleOutput.map((line) => String(line)) : [];
|
|
255
349
|
return {
|
|
@@ -261,7 +355,7 @@ function normalizeRuntimeTraceContract(language, result) {
|
|
|
261
355
|
...typeof result.errorLine === "number" && Number.isFinite(result.errorLine) ? { errorLine: Math.floor(result.errorLine) } : {},
|
|
262
356
|
consoleOutput: normalizedConsole,
|
|
263
357
|
trace: normalizedTrace,
|
|
264
|
-
...result.traceLimitExceeded !== void 0 ? { traceLimitExceeded: Boolean(result.traceLimitExceeded) } : {},
|
|
358
|
+
...result.traceLimitExceeded !== void 0 || traceWasClipped ? { traceLimitExceeded: Boolean(result.traceLimitExceeded) || traceWasClipped } : {},
|
|
265
359
|
...typeof result.timeoutReason === "string" && result.timeoutReason.length > 0 ? { timeoutReason: result.timeoutReason } : {},
|
|
266
360
|
lineEventCount: Math.max(0, lineEventCount),
|
|
267
361
|
traceStepCount: normalizedTrace.length
|
|
@@ -320,6 +414,513 @@ function adaptJavaScriptTraceExecutionResult(language, result) {
|
|
|
320
414
|
return adaptTraceExecutionResult(language, result);
|
|
321
415
|
}
|
|
322
416
|
|
|
417
|
+
// packages/harness-core/src/trace-adapters/java.ts
|
|
418
|
+
function parseScalar(raw) {
|
|
419
|
+
if (raw === "null") return null;
|
|
420
|
+
if (raw === "true") return true;
|
|
421
|
+
if (raw === "false") return false;
|
|
422
|
+
if (/^-?\d+$/.test(raw)) return Number.parseInt(raw, 10);
|
|
423
|
+
if (/^-?\d+\.\d+$/.test(raw)) return Number.parseFloat(raw);
|
|
424
|
+
if (raw.startsWith('"') && raw.endsWith('"') || raw.startsWith("[") && raw.endsWith("]") || raw.startsWith("{") && raw.endsWith("}")) {
|
|
425
|
+
try {
|
|
426
|
+
return JSON.parse(raw);
|
|
427
|
+
} catch {
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return raw;
|
|
431
|
+
}
|
|
432
|
+
function parseKeyValuePairs(fragment) {
|
|
433
|
+
const variables = {};
|
|
434
|
+
const matches = Array.from(fragment.matchAll(/\b([A-Za-z_][A-Za-z0-9_.]*)=/g));
|
|
435
|
+
for (let index = 0; index < matches.length; index += 1) {
|
|
436
|
+
const match = matches[index];
|
|
437
|
+
const rawKey = match[1];
|
|
438
|
+
const valueStart = (match.index ?? 0) + match[0].length;
|
|
439
|
+
const valueEnd = index + 1 < matches.length ? matches[index + 1].index ?? fragment.length : fragment.length;
|
|
440
|
+
const rawValue = fragment.slice(valueStart, valueEnd).trim();
|
|
441
|
+
if (!rawKey || rawValue === void 0) continue;
|
|
442
|
+
if (rawKey === "method") continue;
|
|
443
|
+
variables[rawKey.replaceAll(".", "_")] = parseScalar(rawValue);
|
|
444
|
+
}
|
|
445
|
+
return variables;
|
|
446
|
+
}
|
|
447
|
+
function extractLineMetadata(event) {
|
|
448
|
+
const match = event.match(/^line=(\d+)(?:\s+(.*))?$/);
|
|
449
|
+
if (!match) {
|
|
450
|
+
return { line: 1, payload: event };
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
line: Number.parseInt(match[1], 10),
|
|
454
|
+
payload: match[2] ?? ""
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
function stripInlineComments(line, inBlockComment) {
|
|
458
|
+
let result = "";
|
|
459
|
+
let index = 0;
|
|
460
|
+
let inBlock = inBlockComment;
|
|
461
|
+
while (index < line.length) {
|
|
462
|
+
const current = line[index];
|
|
463
|
+
const next = index + 1 < line.length ? line[index + 1] : "";
|
|
464
|
+
if (inBlock) {
|
|
465
|
+
if (current === "*" && next === "/") {
|
|
466
|
+
inBlock = false;
|
|
467
|
+
index += 2;
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
index += 1;
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
if (current === "/" && next === "*") {
|
|
474
|
+
inBlock = true;
|
|
475
|
+
index += 2;
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
if (current === "/" && next === "/") {
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
result += current;
|
|
482
|
+
index += 1;
|
|
483
|
+
}
|
|
484
|
+
return { text: result, inBlockComment: inBlock };
|
|
485
|
+
}
|
|
486
|
+
function isMethodDeclarationLine(line) {
|
|
487
|
+
const trimmed = line.trim();
|
|
488
|
+
if (!trimmed) return false;
|
|
489
|
+
if (trimmed.startsWith("@")) return false;
|
|
490
|
+
if (!trimmed.includes("(") || !trimmed.includes(")")) return false;
|
|
491
|
+
if (trimmed.endsWith(";")) return false;
|
|
492
|
+
if (trimmed.includes("->")) return false;
|
|
493
|
+
if (/^(?:if|for|while|switch|catch|do|try|else|return|throw|new)\b/.test(trimmed)) {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
if (!/[A-Za-z_][A-Za-z0-9_]*\s*\([^{};]*\)/.test(trimmed)) {
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
return /(?:\{\s*)?$/.test(trimmed);
|
|
500
|
+
}
|
|
501
|
+
function buildLineRemap(sourceText) {
|
|
502
|
+
if (typeof sourceText !== "string" || sourceText.length === 0) {
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
const lines = sourceText.split(/\r?\n/);
|
|
506
|
+
const executable = /* @__PURE__ */ new Set();
|
|
507
|
+
let inBlockComment = false;
|
|
508
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
509
|
+
const { text, inBlockComment: nextInBlockComment } = stripInlineComments(lines[index] ?? "", inBlockComment);
|
|
510
|
+
inBlockComment = nextInBlockComment;
|
|
511
|
+
if (text.trim().length > 0) {
|
|
512
|
+
executable.add(index + 1);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (executable.size === 0) {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
const nextExecutableAtOrAfter = new Array(lines.length + 2).fill(null);
|
|
519
|
+
let nextExecutable = null;
|
|
520
|
+
for (let line = lines.length; line >= 1; line -= 1) {
|
|
521
|
+
if (executable.has(line)) {
|
|
522
|
+
nextExecutable = line;
|
|
523
|
+
}
|
|
524
|
+
nextExecutableAtOrAfter[line] = nextExecutable;
|
|
525
|
+
}
|
|
526
|
+
const previousExecutableAtOrBefore = new Array(lines.length + 2).fill(null);
|
|
527
|
+
let previousExecutable = null;
|
|
528
|
+
for (let line = 1; line <= lines.length; line += 1) {
|
|
529
|
+
if (executable.has(line)) {
|
|
530
|
+
previousExecutable = line;
|
|
531
|
+
}
|
|
532
|
+
previousExecutableAtOrBefore[line] = previousExecutable;
|
|
533
|
+
}
|
|
534
|
+
const remap = /* @__PURE__ */ new Map();
|
|
535
|
+
for (let line = 1; line <= lines.length; line += 1) {
|
|
536
|
+
if (executable.has(line)) continue;
|
|
537
|
+
const forward = nextExecutableAtOrAfter[line];
|
|
538
|
+
const backward = previousExecutableAtOrBefore[line];
|
|
539
|
+
const target = forward ?? backward;
|
|
540
|
+
if (target !== null && target !== line) {
|
|
541
|
+
remap.set(line, target);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return remap;
|
|
545
|
+
}
|
|
546
|
+
function buildMethodDeclarationLineSet(sourceText) {
|
|
547
|
+
if (typeof sourceText !== "string" || sourceText.length === 0) {
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
const declarationLines = /* @__PURE__ */ new Set();
|
|
551
|
+
const lines = sourceText.split(/\r?\n/);
|
|
552
|
+
let inBlockComment = false;
|
|
553
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
554
|
+
const { text, inBlockComment: nextInBlockComment } = stripInlineComments(lines[index] ?? "", inBlockComment);
|
|
555
|
+
inBlockComment = nextInBlockComment;
|
|
556
|
+
if (isMethodDeclarationLine(text)) {
|
|
557
|
+
declarationLines.add(index + 1);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return declarationLines;
|
|
561
|
+
}
|
|
562
|
+
function parseAccessEvent(payload) {
|
|
563
|
+
const isEphemeralOutputArrayName = (name) => /^(output|outputs)$/i.test(name);
|
|
564
|
+
const cellRead = payload.match(/^access ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]\[(\d+)\]=(.+)$/);
|
|
565
|
+
if (cellRead) {
|
|
566
|
+
return [{
|
|
567
|
+
variable: cellRead[1],
|
|
568
|
+
kind: "cell-read",
|
|
569
|
+
indices: [Number.parseInt(cellRead[2], 10), Number.parseInt(cellRead[3], 10)],
|
|
570
|
+
pathDepth: 2
|
|
571
|
+
}];
|
|
572
|
+
}
|
|
573
|
+
const indexedRead = payload.match(/^access ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]=(.+)$/);
|
|
574
|
+
if (indexedRead) {
|
|
575
|
+
return [{
|
|
576
|
+
variable: indexedRead[1],
|
|
577
|
+
kind: "indexed-read",
|
|
578
|
+
indices: [Number.parseInt(indexedRead[2], 10)],
|
|
579
|
+
pathDepth: 1
|
|
580
|
+
}];
|
|
581
|
+
}
|
|
582
|
+
const cellWrite = payload.match(/^write-array ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]\[(\d+)\]=(.+)$/);
|
|
583
|
+
if (cellWrite) {
|
|
584
|
+
return [{
|
|
585
|
+
variable: cellWrite[1],
|
|
586
|
+
kind: "cell-write",
|
|
587
|
+
indices: [Number.parseInt(cellWrite[2], 10), Number.parseInt(cellWrite[3], 10)],
|
|
588
|
+
pathDepth: 2
|
|
589
|
+
}];
|
|
590
|
+
}
|
|
591
|
+
const indexedWrite = payload.match(/^write-array ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]=(.+)$/);
|
|
592
|
+
if (indexedWrite) {
|
|
593
|
+
if (isEphemeralOutputArrayName(indexedWrite[1])) {
|
|
594
|
+
return void 0;
|
|
595
|
+
}
|
|
596
|
+
return [{
|
|
597
|
+
variable: indexedWrite[1],
|
|
598
|
+
kind: "indexed-write",
|
|
599
|
+
indices: [Number.parseInt(indexedWrite[2], 10)],
|
|
600
|
+
pathDepth: 1
|
|
601
|
+
}];
|
|
602
|
+
}
|
|
603
|
+
const mutatingCall = payload.match(/^mutate ([A-Za-z_][A-Za-z0-9_]*) method=([A-Za-z_][A-Za-z0-9_]*)$/);
|
|
604
|
+
if (mutatingCall) {
|
|
605
|
+
return [{
|
|
606
|
+
variable: mutatingCall[1],
|
|
607
|
+
kind: "mutating-call",
|
|
608
|
+
method: mutatingCall[2],
|
|
609
|
+
pathDepth: 1
|
|
610
|
+
}];
|
|
611
|
+
}
|
|
612
|
+
return void 0;
|
|
613
|
+
}
|
|
614
|
+
function parseStructureState(payload) {
|
|
615
|
+
const match = payload.match(/^state (linked-list|tree) ([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
|
|
616
|
+
if (!match) return null;
|
|
617
|
+
return {
|
|
618
|
+
structure: match[1],
|
|
619
|
+
variable: match[2],
|
|
620
|
+
value: JSON.parse(match[3])
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
function parseObjectState(payload) {
|
|
624
|
+
const match = payload.match(/^object-state ([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
|
|
625
|
+
if (!match) return null;
|
|
626
|
+
return {
|
|
627
|
+
variable: match[1],
|
|
628
|
+
visualization: JSON.parse(match[2])
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
function parseObjectFieldEvent(payload) {
|
|
632
|
+
const match = payload.match(/^(access|write) ([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
|
|
633
|
+
if (!match) return null;
|
|
634
|
+
return {
|
|
635
|
+
variable: match[2],
|
|
636
|
+
field: match[3],
|
|
637
|
+
value: parseScalar(match[4])
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
function buildFieldVisualization(event) {
|
|
641
|
+
return {
|
|
642
|
+
objectKinds: {
|
|
643
|
+
[event.variable]: "object"
|
|
644
|
+
},
|
|
645
|
+
hashMaps: [
|
|
646
|
+
{
|
|
647
|
+
name: event.variable,
|
|
648
|
+
kind: "object",
|
|
649
|
+
objectClassName: event.field === "next" || event.field === "prev" ? "ListNode" : event.field === "left" || event.field === "right" ? "TreeNode" : void 0,
|
|
650
|
+
objectId: `${event.variable}-object`,
|
|
651
|
+
highlightedKey: event.field,
|
|
652
|
+
entries: [{ key: event.field, value: event.value, highlight: true }]
|
|
653
|
+
}
|
|
654
|
+
]
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
function callStacksEqual(left, right) {
|
|
658
|
+
return JSON.stringify(left ?? []) === JSON.stringify(right ?? []);
|
|
659
|
+
}
|
|
660
|
+
function mergeVisualizationPayloads(left, right) {
|
|
661
|
+
if (!left) return right;
|
|
662
|
+
if (!right) return left;
|
|
663
|
+
return {
|
|
664
|
+
...left,
|
|
665
|
+
...right,
|
|
666
|
+
objectKinds: {
|
|
667
|
+
...left.objectKinds ?? {},
|
|
668
|
+
...right.objectKinds ?? {}
|
|
669
|
+
},
|
|
670
|
+
hashMaps: [
|
|
671
|
+
...left.hashMaps ?? [],
|
|
672
|
+
...right.hashMaps ?? []
|
|
673
|
+
]
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
function maybeMergeConsecutiveLineStep(trace, nextStep) {
|
|
677
|
+
if (nextStep.event !== "line") {
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
const previous = trace.at(-1);
|
|
681
|
+
if (!previous || previous.event !== "line") {
|
|
682
|
+
return false;
|
|
683
|
+
}
|
|
684
|
+
if (previous.line !== nextStep.line || previous.function !== nextStep.function) {
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
if (!callStacksEqual(previous.callStack, nextStep.callStack)) {
|
|
688
|
+
return false;
|
|
689
|
+
}
|
|
690
|
+
previous.variables = { ...previous.variables ?? {}, ...nextStep.variables ?? {} };
|
|
691
|
+
if (nextStep.accesses?.length) {
|
|
692
|
+
previous.accesses = [...previous.accesses ?? [], ...nextStep.accesses];
|
|
693
|
+
}
|
|
694
|
+
previous.visualization = mergeVisualizationPayloads(previous.visualization, nextStep.visualization);
|
|
695
|
+
return true;
|
|
696
|
+
}
|
|
697
|
+
function filterStructuredVariables(variables) {
|
|
698
|
+
if (!variables) {
|
|
699
|
+
return void 0;
|
|
700
|
+
}
|
|
701
|
+
const isEphemeralOutputArrayName = (name) => /^(output|outputs)$/i.test(name);
|
|
702
|
+
const entries = Object.entries(variables).filter(([name, value]) => {
|
|
703
|
+
if (value === null || typeof value !== "object") {
|
|
704
|
+
return false;
|
|
705
|
+
}
|
|
706
|
+
if (!Array.isArray(value)) {
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
if (Array.isArray(value[0])) {
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
712
|
+
return !isEphemeralOutputArrayName(name);
|
|
713
|
+
});
|
|
714
|
+
if (entries.length === 0) {
|
|
715
|
+
return void 0;
|
|
716
|
+
}
|
|
717
|
+
return Object.fromEntries(entries);
|
|
718
|
+
}
|
|
719
|
+
function appendJavaTraceStep(trace, step, pendingAccesses) {
|
|
720
|
+
const nextStep = pendingAccesses.length > 0 ? { ...step, accesses: [...pendingAccesses] } : step;
|
|
721
|
+
pendingAccesses.length = 0;
|
|
722
|
+
if (!maybeMergeConsecutiveLineStep(trace, nextStep)) {
|
|
723
|
+
trace.push(nextStep);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
function mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, patch) {
|
|
727
|
+
const candidate = trace.at(-1);
|
|
728
|
+
if (!candidate || candidate.event !== "line") {
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
if (candidate.line !== line || candidate.function !== currentFunction) {
|
|
732
|
+
return false;
|
|
733
|
+
}
|
|
734
|
+
if (!callStacksEqual(candidate.callStack, currentCallStack)) {
|
|
735
|
+
return false;
|
|
736
|
+
}
|
|
737
|
+
candidate.variables = { ...candidate.variables ?? {}, ...patch.variables ?? {} };
|
|
738
|
+
if (patch.accesses?.length) {
|
|
739
|
+
candidate.accesses = [...candidate.accesses ?? [], ...patch.accesses];
|
|
740
|
+
}
|
|
741
|
+
candidate.visualization = mergeVisualizationPayloads(candidate.visualization, patch.visualization);
|
|
742
|
+
return true;
|
|
743
|
+
}
|
|
744
|
+
function mergeAccessesIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, accesses) {
|
|
745
|
+
return mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, {
|
|
746
|
+
variables: void 0,
|
|
747
|
+
accesses,
|
|
748
|
+
visualization: void 0
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
function eventsToRawTrace(events, sourceText) {
|
|
752
|
+
const trace = [];
|
|
753
|
+
const variables = {};
|
|
754
|
+
const objectKinds = {};
|
|
755
|
+
const stack = [];
|
|
756
|
+
const pendingAccesses = [];
|
|
757
|
+
let currentFunction = "<module>";
|
|
758
|
+
const lineRemap = buildLineRemap(sourceText);
|
|
759
|
+
const declarationLines = buildMethodDeclarationLineSet(sourceText);
|
|
760
|
+
for (const rawEvent of events) {
|
|
761
|
+
if (rawEvent === "clear" || rawEvent === "reset") continue;
|
|
762
|
+
const metadata = extractLineMetadata(rawEvent);
|
|
763
|
+
const line = lineRemap?.get(metadata.line) ?? metadata.line;
|
|
764
|
+
const payload = metadata.payload;
|
|
765
|
+
const isDeclarationLine = declarationLines?.has(line) === true;
|
|
766
|
+
if (payload.startsWith("call ")) {
|
|
767
|
+
const match = payload.match(/^call\s+(\S+)(?:\s+(.*))?$/);
|
|
768
|
+
const functionName = match?.[1] ?? currentFunction;
|
|
769
|
+
const argsFragment = match?.[2] ?? "";
|
|
770
|
+
Object.assign(variables, parseKeyValuePairs(argsFragment));
|
|
771
|
+
currentFunction = functionName || currentFunction;
|
|
772
|
+
stack.push({ function: currentFunction, line });
|
|
773
|
+
appendJavaTraceStep(trace, {
|
|
774
|
+
line,
|
|
775
|
+
event: "call",
|
|
776
|
+
function: currentFunction,
|
|
777
|
+
variables: { ...variables },
|
|
778
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} }))
|
|
779
|
+
}, pendingAccesses);
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
if (payload.startsWith("return ")) {
|
|
783
|
+
const match = payload.match(/^return\s+(\S+)$/);
|
|
784
|
+
const functionName = match?.[1] ?? currentFunction;
|
|
785
|
+
const returnVariables = functionName === "<module>" ? { ...variables } : filterStructuredVariables(variables) ?? {};
|
|
786
|
+
appendJavaTraceStep(trace, {
|
|
787
|
+
line,
|
|
788
|
+
event: "return",
|
|
789
|
+
function: functionName || currentFunction,
|
|
790
|
+
variables: returnVariables,
|
|
791
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} }))
|
|
792
|
+
}, pendingAccesses);
|
|
793
|
+
stack.pop();
|
|
794
|
+
currentFunction = stack[stack.length - 1]?.function ?? "<module>";
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
if (isDeclarationLine) {
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
if (payload.length === 0) {
|
|
801
|
+
appendJavaTraceStep(trace, {
|
|
802
|
+
line,
|
|
803
|
+
event: "line",
|
|
804
|
+
function: currentFunction,
|
|
805
|
+
variables: { ...variables },
|
|
806
|
+
...stack.length > 0 ? { callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) } : {}
|
|
807
|
+
}, pendingAccesses);
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
const structureState = parseStructureState(payload);
|
|
811
|
+
if (structureState) {
|
|
812
|
+
variables[structureState.variable] = structureState.value;
|
|
813
|
+
objectKinds[structureState.variable] = structureState.structure;
|
|
814
|
+
appendJavaTraceStep(trace, {
|
|
815
|
+
line,
|
|
816
|
+
event: "line",
|
|
817
|
+
function: currentFunction,
|
|
818
|
+
variables: { ...variables },
|
|
819
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
|
|
820
|
+
visualization: {
|
|
821
|
+
objectKinds: { ...objectKinds }
|
|
822
|
+
}
|
|
823
|
+
}, pendingAccesses);
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
const objectState = parseObjectState(payload);
|
|
827
|
+
if (objectState) {
|
|
828
|
+
variables[objectState.variable] = { __ref__: objectState.visualization.objectId ?? `${objectState.variable}-object` };
|
|
829
|
+
appendJavaTraceStep(trace, {
|
|
830
|
+
line,
|
|
831
|
+
event: "line",
|
|
832
|
+
function: currentFunction,
|
|
833
|
+
variables: { ...variables },
|
|
834
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
|
|
835
|
+
visualization: {
|
|
836
|
+
objectKinds: { [objectState.variable]: "object" },
|
|
837
|
+
hashMaps: [objectState.visualization]
|
|
838
|
+
}
|
|
839
|
+
}, pendingAccesses);
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
const objectField = parseObjectFieldEvent(payload);
|
|
843
|
+
if (objectField) {
|
|
844
|
+
variables[objectField.variable] = { __ref__: `${objectField.variable}-object` };
|
|
845
|
+
const currentCallStack = stack.length > 0 ? stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) : void 0;
|
|
846
|
+
if (mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, {
|
|
847
|
+
variables: { ...variables },
|
|
848
|
+
accesses: void 0,
|
|
849
|
+
visualization: buildFieldVisualization(objectField)
|
|
850
|
+
})) {
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
appendJavaTraceStep(trace, {
|
|
854
|
+
line,
|
|
855
|
+
event: "line",
|
|
856
|
+
function: currentFunction,
|
|
857
|
+
variables: { ...variables },
|
|
858
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
|
|
859
|
+
visualization: buildFieldVisualization(objectField)
|
|
860
|
+
}, pendingAccesses);
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
Object.assign(variables, parseKeyValuePairs(payload));
|
|
864
|
+
const accesses = parseAccessEvent(payload);
|
|
865
|
+
if (accesses) {
|
|
866
|
+
const currentCallStack = stack.length > 0 ? stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) : void 0;
|
|
867
|
+
if (mergeAccessesIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, accesses)) {
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
pendingAccesses.push(...accesses);
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
appendJavaTraceStep(trace, {
|
|
874
|
+
line,
|
|
875
|
+
event: "line",
|
|
876
|
+
function: currentFunction,
|
|
877
|
+
variables: { ...variables },
|
|
878
|
+
...stack.length > 0 ? { callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) } : {}
|
|
879
|
+
}, pendingAccesses);
|
|
880
|
+
}
|
|
881
|
+
if (pendingAccesses.length > 0 && trace.length > 0) {
|
|
882
|
+
const last = trace[trace.length - 1];
|
|
883
|
+
last.accesses = [...last.accesses ?? [], ...pendingAccesses];
|
|
884
|
+
}
|
|
885
|
+
return trace;
|
|
886
|
+
}
|
|
887
|
+
function buildJavaExecutionResult(output, events, executionTimeMs = 0, traceLimitExceeded, timeoutReason, maxTraceSteps, sourceText) {
|
|
888
|
+
const trace = eventsToRawTrace(events, sourceText);
|
|
889
|
+
return {
|
|
890
|
+
success: true,
|
|
891
|
+
output,
|
|
892
|
+
trace,
|
|
893
|
+
executionTimeMs,
|
|
894
|
+
consoleOutput: [],
|
|
895
|
+
...traceLimitExceeded !== void 0 ? { traceLimitExceeded } : {},
|
|
896
|
+
...maxTraceSteps !== void 0 ? { maxTraceSteps } : {},
|
|
897
|
+
...timeoutReason ? { timeoutReason } : {},
|
|
898
|
+
lineEventCount: trace.filter((step) => step.event === "line").length,
|
|
899
|
+
traceStepCount: trace.length
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
function normalizeJavaTraceContract(result) {
|
|
903
|
+
const normalized = normalizeRuntimeTraceContract(
|
|
904
|
+
"java",
|
|
905
|
+
buildJavaExecutionResult(
|
|
906
|
+
result.output,
|
|
907
|
+
result.events,
|
|
908
|
+
0,
|
|
909
|
+
result.traceLimitExceeded,
|
|
910
|
+
result.timeoutReason,
|
|
911
|
+
void 0,
|
|
912
|
+
result.sourceText
|
|
913
|
+
)
|
|
914
|
+
);
|
|
915
|
+
return {
|
|
916
|
+
...normalized,
|
|
917
|
+
language: "java"
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
function adaptJavaTraceExecutionResult(result) {
|
|
921
|
+
return adaptTraceExecutionResult("java", result);
|
|
922
|
+
}
|
|
923
|
+
|
|
323
924
|
// packages/harness-browser/src/javascript-worker-client.ts
|
|
324
925
|
var EXECUTION_TIMEOUT_MS = 7e3;
|
|
325
926
|
var INTERVIEW_MODE_TIMEOUT_MS = 5e3;
|
|
@@ -646,6 +1247,7 @@ var PYTHON_RUNTIME_PROFILE = {
|
|
|
646
1247
|
maxTraceSteps: true,
|
|
647
1248
|
maxLineEvents: true,
|
|
648
1249
|
maxSingleLineHits: true,
|
|
1250
|
+
maxStoredEvents: true,
|
|
649
1251
|
minimalTrace: true
|
|
650
1252
|
},
|
|
651
1253
|
fidelity: {
|
|
@@ -707,6 +1309,7 @@ var JAVASCRIPT_RUNTIME_PROFILE = {
|
|
|
707
1309
|
maxTraceSteps: true,
|
|
708
1310
|
maxLineEvents: true,
|
|
709
1311
|
maxSingleLineHits: true,
|
|
1312
|
+
maxStoredEvents: true,
|
|
710
1313
|
minimalTrace: true
|
|
711
1314
|
},
|
|
712
1315
|
fidelity: {
|
|
@@ -768,6 +1371,7 @@ var TYPESCRIPT_RUNTIME_PROFILE = {
|
|
|
768
1371
|
maxTraceSteps: true,
|
|
769
1372
|
maxLineEvents: true,
|
|
770
1373
|
maxSingleLineHits: true,
|
|
1374
|
+
maxStoredEvents: true,
|
|
771
1375
|
minimalTrace: true
|
|
772
1376
|
},
|
|
773
1377
|
fidelity: {
|
|
@@ -798,10 +1402,78 @@ var TYPESCRIPT_RUNTIME_PROFILE = {
|
|
|
798
1402
|
}
|
|
799
1403
|
}
|
|
800
1404
|
};
|
|
1405
|
+
var JAVA_RUNTIME_PROFILE = {
|
|
1406
|
+
language: "java",
|
|
1407
|
+
maturity: "experimental",
|
|
1408
|
+
capabilities: {
|
|
1409
|
+
execution: {
|
|
1410
|
+
styles: {
|
|
1411
|
+
function: true,
|
|
1412
|
+
solutionMethod: true,
|
|
1413
|
+
opsClass: true,
|
|
1414
|
+
script: false,
|
|
1415
|
+
interviewMode: true
|
|
1416
|
+
},
|
|
1417
|
+
timeouts: {
|
|
1418
|
+
clientTimeouts: true,
|
|
1419
|
+
runtimeTimeouts: true
|
|
1420
|
+
}
|
|
1421
|
+
},
|
|
1422
|
+
tracing: {
|
|
1423
|
+
supported: true,
|
|
1424
|
+
events: {
|
|
1425
|
+
line: true,
|
|
1426
|
+
call: true,
|
|
1427
|
+
return: true,
|
|
1428
|
+
exception: true,
|
|
1429
|
+
stdout: false,
|
|
1430
|
+
timeout: true
|
|
1431
|
+
},
|
|
1432
|
+
controls: {
|
|
1433
|
+
maxTraceSteps: true,
|
|
1434
|
+
maxLineEvents: false,
|
|
1435
|
+
maxSingleLineHits: false,
|
|
1436
|
+
maxStoredEvents: true,
|
|
1437
|
+
minimalTrace: false
|
|
1438
|
+
},
|
|
1439
|
+
fidelity: {
|
|
1440
|
+
preciseLineMapping: true,
|
|
1441
|
+
stableFunctionNames: true,
|
|
1442
|
+
callStack: true
|
|
1443
|
+
}
|
|
1444
|
+
},
|
|
1445
|
+
diagnostics: {
|
|
1446
|
+
compileErrors: true,
|
|
1447
|
+
runtimeErrors: true,
|
|
1448
|
+
mappedErrorLines: false,
|
|
1449
|
+
stackTraces: true
|
|
1450
|
+
},
|
|
1451
|
+
structures: {
|
|
1452
|
+
treeNodeRefs: true,
|
|
1453
|
+
listNodeRefs: true,
|
|
1454
|
+
mapSerialization: true,
|
|
1455
|
+
setSerialization: true,
|
|
1456
|
+
graphSerialization: false,
|
|
1457
|
+
cycleReferences: true
|
|
1458
|
+
},
|
|
1459
|
+
visualization: {
|
|
1460
|
+
runtimePayloads: true,
|
|
1461
|
+
objectKinds: true,
|
|
1462
|
+
hashMaps: true,
|
|
1463
|
+
stepVisualization: true
|
|
1464
|
+
}
|
|
1465
|
+
},
|
|
1466
|
+
notes: [
|
|
1467
|
+
"Java currently supports the browser-local Java 17 lane for function, solution-method, and ops-class execution.",
|
|
1468
|
+
"Interview-mode Java reuses the same browser-local execution path and remains experimental in the first harness slice.",
|
|
1469
|
+
"Script-style Java is not enabled yet."
|
|
1470
|
+
]
|
|
1471
|
+
};
|
|
801
1472
|
var LANGUAGE_RUNTIME_PROFILES = {
|
|
802
1473
|
python: PYTHON_RUNTIME_PROFILE,
|
|
803
1474
|
javascript: JAVASCRIPT_RUNTIME_PROFILE,
|
|
804
|
-
typescript: TYPESCRIPT_RUNTIME_PROFILE
|
|
1475
|
+
typescript: TYPESCRIPT_RUNTIME_PROFILE,
|
|
1476
|
+
java: JAVA_RUNTIME_PROFILE
|
|
805
1477
|
};
|
|
806
1478
|
var SUPPORTED_LANGUAGES = Object.freeze(
|
|
807
1479
|
Object.keys(LANGUAGE_RUNTIME_PROFILES)
|
|
@@ -878,13 +1550,307 @@ function createJavaScriptRuntimeClient(runtimeLanguage, workerClient) {
|
|
|
878
1550
|
return new JavaScriptRuntimeClient(runtimeLanguage, workerClient);
|
|
879
1551
|
}
|
|
880
1552
|
|
|
1553
|
+
// packages/harness-browser/src/java-worker-client.ts
|
|
1554
|
+
var TRACING_TIMEOUT_MS2 = 25e3;
|
|
1555
|
+
var INIT_TIMEOUT_MS2 = 25e3;
|
|
1556
|
+
var MESSAGE_TIMEOUT_MS2 = 3e4;
|
|
1557
|
+
var WORKER_READY_TIMEOUT_MS2 = 1e4;
|
|
1558
|
+
var JavaWorkerClient = class {
|
|
1559
|
+
constructor(options) {
|
|
1560
|
+
this.options = options;
|
|
1561
|
+
this.debug = options.debug ?? process.env.NODE_ENV === "development";
|
|
1562
|
+
}
|
|
1563
|
+
worker = null;
|
|
1564
|
+
pendingMessages = /* @__PURE__ */ new Map();
|
|
1565
|
+
messageId = 0;
|
|
1566
|
+
isInitializing = false;
|
|
1567
|
+
initPromise = null;
|
|
1568
|
+
workerReadyPromise = null;
|
|
1569
|
+
workerReadyResolve = null;
|
|
1570
|
+
workerReadyReject = null;
|
|
1571
|
+
debug;
|
|
1572
|
+
isSupported() {
|
|
1573
|
+
return typeof Worker !== "undefined";
|
|
1574
|
+
}
|
|
1575
|
+
getWorker() {
|
|
1576
|
+
if (this.worker) return this.worker;
|
|
1577
|
+
if (!this.isSupported()) {
|
|
1578
|
+
throw new Error("Web Workers are not supported in this environment");
|
|
1579
|
+
}
|
|
1580
|
+
this.workerReadyPromise = new Promise((resolve, reject) => {
|
|
1581
|
+
this.workerReadyResolve = resolve;
|
|
1582
|
+
this.workerReadyReject = (error) => reject(error);
|
|
1583
|
+
});
|
|
1584
|
+
const workerUrl = this.debug && !this.options.workerUrl.includes("?") ? `${this.options.workerUrl}?dev=${Date.now()}` : this.options.workerUrl;
|
|
1585
|
+
this.worker = new Worker(workerUrl);
|
|
1586
|
+
this.worker.onmessage = (event) => {
|
|
1587
|
+
const { id, type, payload } = event.data;
|
|
1588
|
+
if (type === "worker-ready") {
|
|
1589
|
+
this.workerReadyResolve?.();
|
|
1590
|
+
this.workerReadyResolve = null;
|
|
1591
|
+
this.workerReadyReject = null;
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
if (!id) return;
|
|
1595
|
+
const pending = this.pendingMessages.get(id);
|
|
1596
|
+
if (!pending) return;
|
|
1597
|
+
this.pendingMessages.delete(id);
|
|
1598
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
1599
|
+
if (type === "error") {
|
|
1600
|
+
pending.reject(new Error(payload.error));
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
pending.resolve(payload);
|
|
1604
|
+
};
|
|
1605
|
+
this.worker.onerror = (error) => {
|
|
1606
|
+
const workerError = new Error(error.message || "Java worker error");
|
|
1607
|
+
this.workerReadyReject?.(workerError);
|
|
1608
|
+
this.workerReadyResolve = null;
|
|
1609
|
+
this.workerReadyReject = null;
|
|
1610
|
+
for (const [, pending] of this.pendingMessages) {
|
|
1611
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
1612
|
+
pending.reject(workerError);
|
|
1613
|
+
}
|
|
1614
|
+
this.pendingMessages.clear();
|
|
1615
|
+
this.terminateAndReset(workerError);
|
|
1616
|
+
};
|
|
1617
|
+
return this.worker;
|
|
1618
|
+
}
|
|
1619
|
+
async waitForWorkerReady() {
|
|
1620
|
+
const readyPromise = this.workerReadyPromise;
|
|
1621
|
+
if (!readyPromise) return;
|
|
1622
|
+
await new Promise((resolve, reject) => {
|
|
1623
|
+
let settled = false;
|
|
1624
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
1625
|
+
if (settled) return;
|
|
1626
|
+
settled = true;
|
|
1627
|
+
const timeoutError = new Error(
|
|
1628
|
+
`Java worker failed to initialize in time (${Math.round(WORKER_READY_TIMEOUT_MS2 / 1e3)}s)`
|
|
1629
|
+
);
|
|
1630
|
+
this.terminateAndReset(timeoutError);
|
|
1631
|
+
reject(timeoutError);
|
|
1632
|
+
}, WORKER_READY_TIMEOUT_MS2);
|
|
1633
|
+
readyPromise.then(() => {
|
|
1634
|
+
if (settled) return;
|
|
1635
|
+
settled = true;
|
|
1636
|
+
globalThis.clearTimeout(timeoutId);
|
|
1637
|
+
resolve();
|
|
1638
|
+
}).catch((error) => {
|
|
1639
|
+
if (settled) return;
|
|
1640
|
+
settled = true;
|
|
1641
|
+
globalThis.clearTimeout(timeoutId);
|
|
1642
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
1643
|
+
});
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
async sendMessage(type, payload, timeoutMs = MESSAGE_TIMEOUT_MS2) {
|
|
1647
|
+
const worker = this.getWorker();
|
|
1648
|
+
await this.waitForWorkerReady();
|
|
1649
|
+
const id = String(++this.messageId);
|
|
1650
|
+
return new Promise((resolve, reject) => {
|
|
1651
|
+
this.pendingMessages.set(id, {
|
|
1652
|
+
resolve,
|
|
1653
|
+
reject
|
|
1654
|
+
});
|
|
1655
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
1656
|
+
const pending2 = this.pendingMessages.get(id);
|
|
1657
|
+
if (!pending2) return;
|
|
1658
|
+
this.pendingMessages.delete(id);
|
|
1659
|
+
pending2.reject(new Error(`Worker request timed out: ${type}`));
|
|
1660
|
+
}, timeoutMs);
|
|
1661
|
+
const pending = this.pendingMessages.get(id);
|
|
1662
|
+
if (pending) pending.timeoutId = timeoutId;
|
|
1663
|
+
worker.postMessage({ id, type, payload });
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
async executeWithTimeout(executor, timeoutMs) {
|
|
1667
|
+
return new Promise((resolve, reject) => {
|
|
1668
|
+
let settled = false;
|
|
1669
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
1670
|
+
if (settled) return;
|
|
1671
|
+
settled = true;
|
|
1672
|
+
this.terminateAndReset();
|
|
1673
|
+
reject(
|
|
1674
|
+
new Error(
|
|
1675
|
+
`Java execution timed out after ${Math.round(timeoutMs / 1e3)} seconds.`
|
|
1676
|
+
)
|
|
1677
|
+
);
|
|
1678
|
+
}, timeoutMs);
|
|
1679
|
+
executor().then((result) => {
|
|
1680
|
+
if (settled) return;
|
|
1681
|
+
settled = true;
|
|
1682
|
+
globalThis.clearTimeout(timeoutId);
|
|
1683
|
+
resolve(result);
|
|
1684
|
+
}).catch((error) => {
|
|
1685
|
+
if (settled) return;
|
|
1686
|
+
settled = true;
|
|
1687
|
+
globalThis.clearTimeout(timeoutId);
|
|
1688
|
+
reject(error);
|
|
1689
|
+
});
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
terminateAndReset(reason = new Error("Worker was terminated")) {
|
|
1693
|
+
this.workerReadyReject?.(reason);
|
|
1694
|
+
if (this.worker) {
|
|
1695
|
+
this.worker.terminate();
|
|
1696
|
+
this.worker = null;
|
|
1697
|
+
}
|
|
1698
|
+
this.initPromise = null;
|
|
1699
|
+
this.isInitializing = false;
|
|
1700
|
+
this.workerReadyPromise = null;
|
|
1701
|
+
this.workerReadyResolve = null;
|
|
1702
|
+
this.workerReadyReject = null;
|
|
1703
|
+
for (const [, pending] of this.pendingMessages) {
|
|
1704
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
1705
|
+
pending.reject(reason);
|
|
1706
|
+
}
|
|
1707
|
+
this.pendingMessages.clear();
|
|
1708
|
+
}
|
|
1709
|
+
async init() {
|
|
1710
|
+
if (this.initPromise) return this.initPromise;
|
|
1711
|
+
if (this.isInitializing) {
|
|
1712
|
+
await new Promise((resolve) => globalThis.setTimeout(resolve, 100));
|
|
1713
|
+
return this.init();
|
|
1714
|
+
}
|
|
1715
|
+
this.isInitializing = true;
|
|
1716
|
+
this.initPromise = this.sendMessage("init", void 0, INIT_TIMEOUT_MS2);
|
|
1717
|
+
try {
|
|
1718
|
+
return await this.initPromise;
|
|
1719
|
+
} catch (error) {
|
|
1720
|
+
this.initPromise = null;
|
|
1721
|
+
throw error;
|
|
1722
|
+
} finally {
|
|
1723
|
+
this.isInitializing = false;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
async executeWithTracing(code, functionName, inputs, options, executionStyle) {
|
|
1727
|
+
await this.init();
|
|
1728
|
+
return this.executeWithTimeout(
|
|
1729
|
+
() => this.sendMessage(
|
|
1730
|
+
"execute-with-tracing",
|
|
1731
|
+
{ code, functionName, inputs, options, executionStyle },
|
|
1732
|
+
TRACING_TIMEOUT_MS2 + 5e3
|
|
1733
|
+
),
|
|
1734
|
+
TRACING_TIMEOUT_MS2
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1737
|
+
async executeCode(code, functionName, inputs, options, executionStyle) {
|
|
1738
|
+
const result = await this.executeWithTracing(code, functionName, inputs, options, executionStyle);
|
|
1739
|
+
if (!result.success) {
|
|
1740
|
+
return {
|
|
1741
|
+
success: false,
|
|
1742
|
+
output: null,
|
|
1743
|
+
error: result.error ?? "Java execution failed",
|
|
1744
|
+
...result.errorLine !== void 0 ? { errorLine: result.errorLine } : {},
|
|
1745
|
+
consoleOutput: result.consoleOutput
|
|
1746
|
+
};
|
|
1747
|
+
}
|
|
1748
|
+
return {
|
|
1749
|
+
success: true,
|
|
1750
|
+
output: result.output,
|
|
1751
|
+
consoleOutput: result.consoleOutput
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
async executeCodeInterviewMode(code, functionName, inputs, options, executionStyle) {
|
|
1755
|
+
return this.executeCode(code, functionName, inputs, options, executionStyle);
|
|
1756
|
+
}
|
|
1757
|
+
terminate() {
|
|
1758
|
+
this.terminateAndReset();
|
|
1759
|
+
}
|
|
1760
|
+
};
|
|
1761
|
+
|
|
1762
|
+
// packages/harness-browser/src/java-runtime-client.ts
|
|
1763
|
+
var JavaRuntimeClient = class {
|
|
1764
|
+
constructor(workerClient) {
|
|
1765
|
+
this.workerClient = workerClient;
|
|
1766
|
+
}
|
|
1767
|
+
async init() {
|
|
1768
|
+
return this.workerClient.init();
|
|
1769
|
+
}
|
|
1770
|
+
async executeWithTracing(code, functionName, inputs, options, executionStyle = "function") {
|
|
1771
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
|
|
1772
|
+
request: "trace",
|
|
1773
|
+
executionStyle,
|
|
1774
|
+
functionName
|
|
1775
|
+
});
|
|
1776
|
+
const rawResult = await this.workerClient.executeWithTracing(
|
|
1777
|
+
code,
|
|
1778
|
+
functionName ?? "",
|
|
1779
|
+
inputs,
|
|
1780
|
+
options,
|
|
1781
|
+
executionStyle
|
|
1782
|
+
);
|
|
1783
|
+
if (!rawResult.success) {
|
|
1784
|
+
return {
|
|
1785
|
+
success: false,
|
|
1786
|
+
error: rawResult.error ?? "Java tracing failed",
|
|
1787
|
+
...rawResult.errorLine !== void 0 ? { errorLine: rawResult.errorLine } : {},
|
|
1788
|
+
trace: [],
|
|
1789
|
+
executionTimeMs: rawResult.executionTimeMs,
|
|
1790
|
+
consoleOutput: rawResult.consoleOutput,
|
|
1791
|
+
...rawResult.traceLimitExceeded !== void 0 ? { traceLimitExceeded: rawResult.traceLimitExceeded } : {},
|
|
1792
|
+
...rawResult.timeoutReason ? { timeoutReason: rawResult.timeoutReason } : {},
|
|
1793
|
+
lineEventCount: 0,
|
|
1794
|
+
traceStepCount: 0
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1797
|
+
const adapted = adaptJavaTraceExecutionResult(
|
|
1798
|
+
buildJavaExecutionResult(
|
|
1799
|
+
rawResult.output,
|
|
1800
|
+
rawResult.events,
|
|
1801
|
+
rawResult.executionTimeMs,
|
|
1802
|
+
rawResult.traceLimitExceeded,
|
|
1803
|
+
rawResult.timeoutReason,
|
|
1804
|
+
void 0,
|
|
1805
|
+
rawResult.sourceText
|
|
1806
|
+
)
|
|
1807
|
+
);
|
|
1808
|
+
return {
|
|
1809
|
+
...adapted,
|
|
1810
|
+
consoleOutput: rawResult.consoleOutput,
|
|
1811
|
+
executionTimeMs: rawResult.executionTimeMs
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
async executeCode(code, functionName, inputs, executionStyle = "function") {
|
|
1815
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
|
|
1816
|
+
request: "execute",
|
|
1817
|
+
executionStyle,
|
|
1818
|
+
functionName
|
|
1819
|
+
});
|
|
1820
|
+
return this.workerClient.executeCode(
|
|
1821
|
+
code,
|
|
1822
|
+
functionName,
|
|
1823
|
+
inputs,
|
|
1824
|
+
void 0,
|
|
1825
|
+
executionStyle
|
|
1826
|
+
);
|
|
1827
|
+
}
|
|
1828
|
+
async executeCodeInterviewMode(code, functionName, inputs, executionStyle = "function") {
|
|
1829
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
|
|
1830
|
+
request: "interview",
|
|
1831
|
+
executionStyle,
|
|
1832
|
+
functionName
|
|
1833
|
+
});
|
|
1834
|
+
return this.workerClient.executeCodeInterviewMode(
|
|
1835
|
+
code,
|
|
1836
|
+
functionName,
|
|
1837
|
+
inputs,
|
|
1838
|
+
void 0,
|
|
1839
|
+
executionStyle
|
|
1840
|
+
);
|
|
1841
|
+
}
|
|
1842
|
+
};
|
|
1843
|
+
function createJavaRuntimeClient(workerClient) {
|
|
1844
|
+
return new JavaRuntimeClient(workerClient);
|
|
1845
|
+
}
|
|
1846
|
+
|
|
881
1847
|
// packages/harness-browser/src/pyodide-worker-client.ts
|
|
882
1848
|
var EXECUTION_TIMEOUT_MS2 = 1e4;
|
|
883
1849
|
var INTERVIEW_MODE_TIMEOUT_MS2 = 5e3;
|
|
884
|
-
var
|
|
885
|
-
var
|
|
886
|
-
var
|
|
887
|
-
var
|
|
1850
|
+
var TRACING_TIMEOUT_MS3 = 3e4;
|
|
1851
|
+
var INIT_TIMEOUT_MS3 = 12e4;
|
|
1852
|
+
var MESSAGE_TIMEOUT_MS3 = 2e4;
|
|
1853
|
+
var WORKER_READY_TIMEOUT_MS3 = 1e4;
|
|
888
1854
|
var PyodideWorkerClient = class {
|
|
889
1855
|
constructor(options) {
|
|
890
1856
|
this.options = options;
|
|
@@ -974,14 +1940,14 @@ var PyodideWorkerClient = class {
|
|
|
974
1940
|
if (settled) return;
|
|
975
1941
|
settled = true;
|
|
976
1942
|
const timeoutError = new Error(
|
|
977
|
-
`Python worker failed to initialize in time (${Math.round(
|
|
1943
|
+
`Python worker failed to initialize in time (${Math.round(WORKER_READY_TIMEOUT_MS3 / 1e3)}s)`
|
|
978
1944
|
);
|
|
979
1945
|
if (this.debug) {
|
|
980
|
-
console.warn("[PyodideWorkerClient] worker-ready timeout", { timeoutMs:
|
|
1946
|
+
console.warn("[PyodideWorkerClient] worker-ready timeout", { timeoutMs: WORKER_READY_TIMEOUT_MS3 });
|
|
981
1947
|
}
|
|
982
1948
|
this.terminateAndReset(timeoutError);
|
|
983
1949
|
reject(timeoutError);
|
|
984
|
-
},
|
|
1950
|
+
}, WORKER_READY_TIMEOUT_MS3);
|
|
985
1951
|
readyPromise.then(() => {
|
|
986
1952
|
if (settled) return;
|
|
987
1953
|
settled = true;
|
|
@@ -998,7 +1964,7 @@ var PyodideWorkerClient = class {
|
|
|
998
1964
|
/**
|
|
999
1965
|
* Send a message to the worker and wait for a response
|
|
1000
1966
|
*/
|
|
1001
|
-
async sendMessage(type, payload, timeoutMs =
|
|
1967
|
+
async sendMessage(type, payload, timeoutMs = MESSAGE_TIMEOUT_MS3) {
|
|
1002
1968
|
const worker = this.getWorker();
|
|
1003
1969
|
await this.waitForWorkerReady();
|
|
1004
1970
|
const id = String(++this.messageId);
|
|
@@ -1082,7 +2048,7 @@ var PyodideWorkerClient = class {
|
|
|
1082
2048
|
this.isInitializing = true;
|
|
1083
2049
|
this.initPromise = (async () => {
|
|
1084
2050
|
try {
|
|
1085
|
-
return await this.sendMessage("init", void 0,
|
|
2051
|
+
return await this.sendMessage("init", void 0, INIT_TIMEOUT_MS3);
|
|
1086
2052
|
} catch (error) {
|
|
1087
2053
|
const message = error instanceof Error ? error.message : String(error);
|
|
1088
2054
|
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");
|
|
@@ -1093,7 +2059,7 @@ var PyodideWorkerClient = class {
|
|
|
1093
2059
|
console.warn("[PyodideWorkerClient] init failed, resetting worker and retrying once", { message });
|
|
1094
2060
|
}
|
|
1095
2061
|
this.terminateAndReset();
|
|
1096
|
-
return this.sendMessage("init", void 0,
|
|
2062
|
+
return this.sendMessage("init", void 0, INIT_TIMEOUT_MS3);
|
|
1097
2063
|
}
|
|
1098
2064
|
})();
|
|
1099
2065
|
try {
|
|
@@ -1120,9 +2086,9 @@ var PyodideWorkerClient = class {
|
|
|
1120
2086
|
inputs,
|
|
1121
2087
|
executionStyle,
|
|
1122
2088
|
options
|
|
1123
|
-
},
|
|
2089
|
+
}, TRACING_TIMEOUT_MS3 + 5e3),
|
|
1124
2090
|
// Message timeout slightly longer than execution timeout
|
|
1125
|
-
|
|
2091
|
+
TRACING_TIMEOUT_MS3
|
|
1126
2092
|
);
|
|
1127
2093
|
} catch (error) {
|
|
1128
2094
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -1132,7 +2098,7 @@ var PyodideWorkerClient = class {
|
|
|
1132
2098
|
success: false,
|
|
1133
2099
|
error: errorMessage,
|
|
1134
2100
|
trace: [],
|
|
1135
|
-
executionTimeMs:
|
|
2101
|
+
executionTimeMs: TRACING_TIMEOUT_MS3,
|
|
1136
2102
|
consoleOutput: [],
|
|
1137
2103
|
traceLimitExceeded: true,
|
|
1138
2104
|
timeoutReason: "client-timeout",
|
|
@@ -1288,6 +2254,7 @@ var DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS = Object.freeze({
|
|
|
1288
2254
|
pythonRuntimeCore: "pyodide/runtime-core.js",
|
|
1289
2255
|
pythonSnippets: "generated-python-harness-snippets.js",
|
|
1290
2256
|
javascriptWorker: "javascript-worker.js",
|
|
2257
|
+
javaWorker: "java-worker.js",
|
|
1291
2258
|
typescriptCompiler: "vendor/typescript.js"
|
|
1292
2259
|
});
|
|
1293
2260
|
function isExplicitAssetPath(pathname) {
|
|
@@ -1321,6 +2288,7 @@ function resolveBrowserHarnessAssets(options = {}) {
|
|
|
1321
2288
|
assetBaseUrl,
|
|
1322
2289
|
assets.javascriptWorker ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.javascriptWorker
|
|
1323
2290
|
),
|
|
2291
|
+
javaWorker: resolveAssetPath(assetBaseUrl, assets.javaWorker ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.javaWorker),
|
|
1324
2292
|
typescriptCompiler: resolveAssetPath(
|
|
1325
2293
|
assetBaseUrl,
|
|
1326
2294
|
assets.typescriptCompiler ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.typescriptCompiler
|
|
@@ -1334,6 +2302,7 @@ var BrowserHarnessRuntime = class {
|
|
|
1334
2302
|
supportedLanguages = SUPPORTED_LANGUAGES;
|
|
1335
2303
|
pythonWorkerClient;
|
|
1336
2304
|
javaScriptWorkerClient;
|
|
2305
|
+
javaWorkerClient;
|
|
1337
2306
|
clients;
|
|
1338
2307
|
constructor(options = {}) {
|
|
1339
2308
|
this.assets = resolveBrowserHarnessAssets(options);
|
|
@@ -1345,10 +2314,15 @@ var BrowserHarnessRuntime = class {
|
|
|
1345
2314
|
workerUrl: this.assets.javascriptWorker,
|
|
1346
2315
|
debug: options.debug
|
|
1347
2316
|
});
|
|
2317
|
+
this.javaWorkerClient = new JavaWorkerClient({
|
|
2318
|
+
workerUrl: this.assets.javaWorker,
|
|
2319
|
+
debug: options.debug
|
|
2320
|
+
});
|
|
1348
2321
|
this.clients = {
|
|
1349
2322
|
python: createPythonRuntimeClient(this.pythonWorkerClient),
|
|
1350
2323
|
javascript: createJavaScriptRuntimeClient("javascript", this.javaScriptWorkerClient),
|
|
1351
|
-
typescript: createJavaScriptRuntimeClient("typescript", this.javaScriptWorkerClient)
|
|
2324
|
+
typescript: createJavaScriptRuntimeClient("typescript", this.javaScriptWorkerClient),
|
|
2325
|
+
java: createJavaRuntimeClient(this.javaWorkerClient)
|
|
1352
2326
|
};
|
|
1353
2327
|
}
|
|
1354
2328
|
getClient(language) {
|
|
@@ -1372,11 +2346,16 @@ var BrowserHarnessRuntime = class {
|
|
|
1372
2346
|
this.pythonWorkerClient.terminate();
|
|
1373
2347
|
return;
|
|
1374
2348
|
}
|
|
2349
|
+
if (language === "java") {
|
|
2350
|
+
this.javaWorkerClient.terminate();
|
|
2351
|
+
return;
|
|
2352
|
+
}
|
|
1375
2353
|
this.javaScriptWorkerClient.terminate();
|
|
1376
2354
|
}
|
|
1377
2355
|
dispose() {
|
|
1378
2356
|
this.pythonWorkerClient.terminate();
|
|
1379
2357
|
this.javaScriptWorkerClient.terminate();
|
|
2358
|
+
this.javaWorkerClient.terminate();
|
|
1380
2359
|
}
|
|
1381
2360
|
};
|
|
1382
2361
|
function createBrowserHarness(options = {}) {
|
|
@@ -1479,7 +2458,7 @@ def _serialize(obj, depth=0, node_refs=None):
|
|
|
1479
2458
|
except TypeError:
|
|
1480
2459
|
sorted_vals = [_serialize(x, depth + 1, node_refs) for x in obj]
|
|
1481
2460
|
return {"__type__": "set", "values": sorted_vals}
|
|
1482
|
-
elif (
|
|
2461
|
+
elif isinstance(obj, TreeNode):
|
|
1483
2462
|
obj_ref = id(obj)
|
|
1484
2463
|
if obj_ref in node_refs:
|
|
1485
2464
|
return {"__ref__": node_refs[obj_ref]}
|
|
@@ -1495,7 +2474,7 @@ def _serialize(obj, depth=0, node_refs=None):
|
|
|
1495
2474
|
if hasattr(obj, 'right'):
|
|
1496
2475
|
result["right"] = _serialize(obj.right, depth + 1, node_refs)
|
|
1497
2476
|
return result
|
|
1498
|
-
elif (
|
|
2477
|
+
elif isinstance(obj, ListNode):
|
|
1499
2478
|
obj_ref = id(obj)
|
|
1500
2479
|
if obj_ref in node_refs:
|
|
1501
2480
|
return {"__ref__": node_refs[obj_ref]}
|
|
@@ -1573,14 +2552,14 @@ def _serialize(obj, depth=0):
|
|
|
1573
2552
|
return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
|
|
1574
2553
|
except TypeError:
|
|
1575
2554
|
return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
|
|
1576
|
-
elif (
|
|
2555
|
+
elif isinstance(obj, TreeNode):
|
|
1577
2556
|
result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
|
|
1578
2557
|
if hasattr(obj, 'left'):
|
|
1579
2558
|
result["left"] = _serialize(obj.left, depth + 1)
|
|
1580
2559
|
if hasattr(obj, 'right'):
|
|
1581
2560
|
result["right"] = _serialize(obj.right, depth + 1)
|
|
1582
2561
|
return result
|
|
1583
|
-
elif (
|
|
2562
|
+
elif isinstance(obj, ListNode):
|
|
1584
2563
|
result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
|
|
1585
2564
|
result["next"] = _serialize(obj.next, depth + 1)
|
|
1586
2565
|
return result
|
|
@@ -1655,14 +2634,14 @@ def _serialize(obj, depth=0):
|
|
|
1655
2634
|
return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
|
|
1656
2635
|
except TypeError:
|
|
1657
2636
|
return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
|
|
1658
|
-
elif
|
|
2637
|
+
elif isinstance(obj, TreeNode):
|
|
1659
2638
|
result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', None), depth + 1)}
|
|
1660
2639
|
if hasattr(obj, 'left'):
|
|
1661
2640
|
result["left"] = _serialize(obj.left, depth + 1)
|
|
1662
2641
|
if hasattr(obj, 'right'):
|
|
1663
2642
|
result["right"] = _serialize(obj.right, depth + 1)
|
|
1664
2643
|
return result
|
|
1665
|
-
elif
|
|
2644
|
+
elif isinstance(obj, ListNode):
|
|
1666
2645
|
result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', None), depth + 1)}
|
|
1667
2646
|
result["next"] = _serialize(obj.next, depth + 1)
|
|
1668
2647
|
return result
|
|
@@ -1694,14 +2673,14 @@ def _serialize(obj, depth=0):
|
|
|
1694
2673
|
return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
|
|
1695
2674
|
except TypeError:
|
|
1696
2675
|
return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
|
|
1697
|
-
elif (
|
|
2676
|
+
elif isinstance(obj, TreeNode):
|
|
1698
2677
|
result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
|
|
1699
2678
|
if hasattr(obj, 'left'):
|
|
1700
2679
|
result["left"] = _serialize(obj.left, depth + 1)
|
|
1701
2680
|
if hasattr(obj, 'right'):
|
|
1702
2681
|
result["right"] = _serialize(obj.right, depth + 1)
|
|
1703
2682
|
return result
|
|
1704
|
-
elif (
|
|
2683
|
+
elif isinstance(obj, ListNode):
|
|
1705
2684
|
result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
|
|
1706
2685
|
result["next"] = _serialize(obj.next, depth + 1)
|
|
1707
2686
|
return result
|
|
@@ -1929,7 +2908,7 @@ def _serialize(obj, depth=0, node_refs=None):
|
|
|
1929
2908
|
except TypeError:
|
|
1930
2909
|
sorted_vals = [_serialize(x, depth + 1, node_refs) for x in obj]
|
|
1931
2910
|
return {"__type__": "set", "values": sorted_vals}
|
|
1932
|
-
elif (
|
|
2911
|
+
elif isinstance(obj, TreeNode):
|
|
1933
2912
|
obj_ref = id(obj)
|
|
1934
2913
|
if obj_ref in node_refs:
|
|
1935
2914
|
return {"__ref__": node_refs[obj_ref]}
|
|
@@ -1945,7 +2924,7 @@ def _serialize(obj, depth=0, node_refs=None):
|
|
|
1945
2924
|
if hasattr(obj, 'right'):
|
|
1946
2925
|
result["right"] = _serialize(obj.right, depth + 1, node_refs)
|
|
1947
2926
|
return result
|
|
1948
|
-
elif (
|
|
2927
|
+
elif isinstance(obj, ListNode):
|
|
1949
2928
|
obj_ref = id(obj)
|
|
1950
2929
|
if obj_ref in node_refs:
|
|
1951
2930
|
return {"__ref__": node_refs[obj_ref]}
|
|
@@ -2023,14 +3002,14 @@ def _serialize(obj, depth=0):
|
|
|
2023
3002
|
return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
|
|
2024
3003
|
except TypeError:
|
|
2025
3004
|
return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
|
|
2026
|
-
elif (
|
|
3005
|
+
elif isinstance(obj, TreeNode):
|
|
2027
3006
|
result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
|
|
2028
3007
|
if hasattr(obj, 'left'):
|
|
2029
3008
|
result["left"] = _serialize(obj.left, depth + 1)
|
|
2030
3009
|
if hasattr(obj, 'right'):
|
|
2031
3010
|
result["right"] = _serialize(obj.right, depth + 1)
|
|
2032
3011
|
return result
|
|
2033
|
-
elif (
|
|
3012
|
+
elif isinstance(obj, ListNode):
|
|
2034
3013
|
result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}
|
|
2035
3014
|
result["next"] = _serialize(obj.next, depth + 1)
|
|
2036
3015
|
return result
|
|
@@ -2105,14 +3084,14 @@ def _serialize(obj, depth=0):
|
|
|
2105
3084
|
return {"__type__": "set", "values": sorted([_serialize(x, depth + 1) for x in obj])}
|
|
2106
3085
|
except TypeError:
|
|
2107
3086
|
return {"__type__": "set", "values": [_serialize(x, depth + 1) for x in obj]}
|
|
2108
|
-
elif
|
|
3087
|
+
elif isinstance(obj, TreeNode):
|
|
2109
3088
|
result = {"__type__": "TreeNode", "val": _serialize(getattr(obj, 'val', None), depth + 1)}
|
|
2110
3089
|
if hasattr(obj, 'left'):
|
|
2111
3090
|
result["left"] = _serialize(obj.left, depth + 1)
|
|
2112
3091
|
if hasattr(obj, 'right'):
|
|
2113
3092
|
result["right"] = _serialize(obj.right, depth + 1)
|
|
2114
3093
|
return result
|
|
2115
|
-
elif
|
|
3094
|
+
elif isinstance(obj, ListNode):
|
|
2116
3095
|
result = {"__type__": "ListNode", "val": _serialize(getattr(obj, 'val', None), depth + 1)}
|
|
2117
3096
|
result["next"] = _serialize(obj.next, depth + 1)
|
|
2118
3097
|
return result
|
|
@@ -2206,16 +3185,17 @@ function createConsoleProxy(output) {
|
|
|
2206
3185
|
}
|
|
2207
3186
|
function isLikelyTreeNodeValue(value) {
|
|
2208
3187
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2209
|
-
const
|
|
2210
|
-
|
|
2211
|
-
|
|
3188
|
+
const record = value;
|
|
3189
|
+
if (record.__type__ === "TreeNode") return true;
|
|
3190
|
+
const ctor = value.constructor;
|
|
3191
|
+
return ctor?.name === "TreeNode";
|
|
2212
3192
|
}
|
|
2213
3193
|
function isLikelyListNodeValue(value) {
|
|
2214
3194
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2215
|
-
const
|
|
2216
|
-
|
|
2217
|
-
const
|
|
2218
|
-
return
|
|
3195
|
+
const record = value;
|
|
3196
|
+
if (record.__type__ === "ListNode") return true;
|
|
3197
|
+
const ctor = value.constructor;
|
|
3198
|
+
return ctor?.name === "ListNode";
|
|
2219
3199
|
}
|
|
2220
3200
|
function getCustomClassName(value) {
|
|
2221
3201
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
@@ -2270,22 +3250,25 @@ function serializeValue(value, depth = 0, seen = /* @__PURE__ */ new WeakSet(),
|
|
|
2270
3250
|
const nodePrefix = isTree ? "tree" : "list";
|
|
2271
3251
|
const nodeId = `${nodePrefix}-${nodeRefState.nextId++}`;
|
|
2272
3252
|
nodeRefState.ids.set(objectValue, nodeId);
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
};
|
|
2281
|
-
}
|
|
2282
|
-
return {
|
|
3253
|
+
const out2 = isTree ? {
|
|
3254
|
+
__type__: "TreeNode",
|
|
3255
|
+
__id__: nodeId,
|
|
3256
|
+
val: serializeValue(nodeValue.val ?? nodeValue.value ?? null, depth + 1, seen, nodeRefState),
|
|
3257
|
+
left: serializeValue(nodeValue.left ?? null, depth + 1, seen, nodeRefState),
|
|
3258
|
+
right: serializeValue(nodeValue.right ?? null, depth + 1, seen, nodeRefState)
|
|
3259
|
+
} : {
|
|
2283
3260
|
__type__: "ListNode",
|
|
2284
3261
|
__id__: nodeId,
|
|
2285
3262
|
val: serializeValue(nodeValue.val ?? nodeValue.value ?? null, depth + 1, seen, nodeRefState),
|
|
2286
3263
|
next: serializeValue(nodeValue.next ?? null, depth + 1, seen, nodeRefState),
|
|
2287
3264
|
..."prev" in nodeValue ? { prev: serializeValue(nodeValue.prev ?? null, depth + 1, seen, nodeRefState) } : {}
|
|
2288
3265
|
};
|
|
3266
|
+
const skipped = isTree ? /* @__PURE__ */ new Set(["__id__", "__type__", "__class__", "val", "value", "left", "right"]) : /* @__PURE__ */ new Set(["__id__", "__type__", "__class__", "val", "value", "next", "prev"]);
|
|
3267
|
+
for (const [k, v] of Object.entries(nodeValue)) {
|
|
3268
|
+
if (skipped.has(k)) continue;
|
|
3269
|
+
out2[k] = serializeValue(v, depth + 1, seen, nodeRefState);
|
|
3270
|
+
}
|
|
3271
|
+
return out2;
|
|
2289
3272
|
}
|
|
2290
3273
|
const customClassName = getCustomClassName(value);
|
|
2291
3274
|
if (customClassName) {
|
|
@@ -2449,21 +3432,31 @@ function materializeTreeInput(value) {
|
|
|
2449
3432
|
}
|
|
2450
3433
|
const record = value;
|
|
2451
3434
|
if (isLikelyTreeNodeValue(record)) {
|
|
2452
|
-
|
|
3435
|
+
const node = {
|
|
2453
3436
|
val: record.val ?? record.value ?? null,
|
|
2454
3437
|
value: record.val ?? record.value ?? null,
|
|
2455
3438
|
left: materializeTreeInput(record.left ?? null),
|
|
2456
3439
|
right: materializeTreeInput(record.right ?? null)
|
|
2457
3440
|
};
|
|
3441
|
+
for (const [key, nested] of Object.entries(record)) {
|
|
3442
|
+
if (key === "__id__" || key === "__type__" || key === "val" || key === "value" || key === "left" || key === "right") continue;
|
|
3443
|
+
node[key] = materializeTreeInput(nested);
|
|
3444
|
+
}
|
|
3445
|
+
return node;
|
|
2458
3446
|
}
|
|
2459
3447
|
const taggedRecord = value;
|
|
2460
3448
|
if (taggedRecord.__type__ === "TreeNode") {
|
|
2461
|
-
|
|
3449
|
+
const node = {
|
|
2462
3450
|
val: taggedRecord.val ?? taggedRecord.value ?? null,
|
|
2463
3451
|
value: taggedRecord.val ?? taggedRecord.value ?? null,
|
|
2464
3452
|
left: materializeTreeInput(taggedRecord.left ?? null),
|
|
2465
3453
|
right: materializeTreeInput(taggedRecord.right ?? null)
|
|
2466
3454
|
};
|
|
3455
|
+
for (const [key, nested] of Object.entries(taggedRecord)) {
|
|
3456
|
+
if (key === "__id__" || key === "__type__" || key === "val" || key === "value" || key === "left" || key === "right") continue;
|
|
3457
|
+
node[key] = materializeTreeInput(nested);
|
|
3458
|
+
}
|
|
3459
|
+
return node;
|
|
2467
3460
|
}
|
|
2468
3461
|
return value;
|
|
2469
3462
|
}
|
|
@@ -2507,6 +3500,10 @@ function materializeListInput(value, refs = /* @__PURE__ */ new Map(), materiali
|
|
|
2507
3500
|
refs.set(taggedRecord.__id__, node);
|
|
2508
3501
|
}
|
|
2509
3502
|
node.next = materializeListInput(taggedRecord.next ?? null, refs, materialized);
|
|
3503
|
+
for (const [key, nested] of Object.entries(taggedRecord)) {
|
|
3504
|
+
if (key === "__id__" || key === "__type__" || key === "__class__" || key === "val" || key === "value" || key === "next") continue;
|
|
3505
|
+
node[key] = materializeListInput(nested, refs, materialized);
|
|
3506
|
+
}
|
|
2510
3507
|
return node;
|
|
2511
3508
|
}
|
|
2512
3509
|
return value;
|
|
@@ -2871,9 +3868,13 @@ async function executeTypeScriptCode(code, functionName, inputs, executionStyle
|
|
|
2871
3868
|
TEMPLATE_PYTHON_TRACE_SERIALIZE_FUNCTION,
|
|
2872
3869
|
TYPESCRIPT_RUNTIME_DECLARATIONS,
|
|
2873
3870
|
adaptJavaScriptTraceExecutionResult,
|
|
3871
|
+
adaptJavaSpikeTraceExecutionResult,
|
|
3872
|
+
adaptJavaTraceExecutionResult,
|
|
2874
3873
|
adaptPythonTraceExecutionResult,
|
|
2875
3874
|
adaptTraceExecutionResult,
|
|
2876
3875
|
assertRuntimeRequestSupported,
|
|
3876
|
+
buildJavaExecutionResult,
|
|
3877
|
+
buildJavaSpikeExecutionResult,
|
|
2877
3878
|
createBrowserHarness,
|
|
2878
3879
|
executeJavaScriptCode,
|
|
2879
3880
|
executeJavaScriptWithTracing,
|
|
@@ -2885,6 +3886,8 @@ async function executeTypeScriptCode(code, functionName, inputs, executionStyle
|
|
|
2885
3886
|
getSupportedLanguageProfiles,
|
|
2886
3887
|
identifyConversions,
|
|
2887
3888
|
isLanguageSupported,
|
|
3889
|
+
normalizeJavaSpikeTraceContract,
|
|
3890
|
+
normalizeJavaTraceContract,
|
|
2888
3891
|
normalizeRuntimeTraceContract,
|
|
2889
3892
|
resolveBrowserHarnessAssets,
|
|
2890
3893
|
stableStringifyRuntimeTraceContract,
|