@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/browser.cjs
CHANGED
|
@@ -289,7 +289,7 @@ var JavaScriptWorkerClient = class {
|
|
|
289
289
|
};
|
|
290
290
|
|
|
291
291
|
// packages/harness-core/src/trace-contract.ts
|
|
292
|
-
var RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION = "2026-
|
|
292
|
+
var RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION = "2026-04-21";
|
|
293
293
|
var TRACE_EVENTS = /* @__PURE__ */ new Set([
|
|
294
294
|
"line",
|
|
295
295
|
"call",
|
|
@@ -410,7 +410,39 @@ function normalizeAccesses(accesses) {
|
|
|
410
410
|
}
|
|
411
411
|
return payload;
|
|
412
412
|
}).filter((access) => access !== null);
|
|
413
|
-
|
|
413
|
+
if (normalized.length === 0) {
|
|
414
|
+
return void 0;
|
|
415
|
+
}
|
|
416
|
+
const statsByVariable = /* @__PURE__ */ new Map();
|
|
417
|
+
for (const access of normalized) {
|
|
418
|
+
const stats = statsByVariable.get(access.variable) ?? { hasCellRead: false, hasCellWrite: false };
|
|
419
|
+
if (access.kind === "cell-read") stats.hasCellRead = true;
|
|
420
|
+
if (access.kind === "cell-write") stats.hasCellWrite = true;
|
|
421
|
+
statsByVariable.set(access.variable, stats);
|
|
422
|
+
}
|
|
423
|
+
const deduped = /* @__PURE__ */ new Set();
|
|
424
|
+
const collapsed = normalized.filter((access) => {
|
|
425
|
+
const stats = statsByVariable.get(access.variable);
|
|
426
|
+
if (access.kind === "indexed-read" && (stats?.hasCellRead || stats?.hasCellWrite)) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
if (access.kind === "indexed-write" && stats?.hasCellWrite) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
const key = JSON.stringify([
|
|
433
|
+
access.variable,
|
|
434
|
+
access.kind,
|
|
435
|
+
access.indices ?? null,
|
|
436
|
+
access.pathDepth ?? null,
|
|
437
|
+
access.method ?? null
|
|
438
|
+
]);
|
|
439
|
+
if (deduped.has(key)) {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
deduped.add(key);
|
|
443
|
+
return true;
|
|
444
|
+
});
|
|
445
|
+
return collapsed.length > 0 ? collapsed : void 0;
|
|
414
446
|
}
|
|
415
447
|
function normalizeRecord(value) {
|
|
416
448
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
@@ -474,8 +506,64 @@ function normalizeTraceStep(step) {
|
|
|
474
506
|
...normalizedVisualization ? { visualization: normalizedVisualization } : {}
|
|
475
507
|
};
|
|
476
508
|
}
|
|
509
|
+
function collapseTraceAccessNoise(trace) {
|
|
510
|
+
const variablesWithCellAccess = /* @__PURE__ */ new Set();
|
|
511
|
+
const accessStatsByVariable = /* @__PURE__ */ new Map();
|
|
512
|
+
for (const step of trace) {
|
|
513
|
+
for (const access of step.accesses ?? []) {
|
|
514
|
+
const stats = accessStatsByVariable.get(access.variable) ?? {
|
|
515
|
+
hasCellRead: false,
|
|
516
|
+
hasCellWrite: false,
|
|
517
|
+
hasMutatingCall: false,
|
|
518
|
+
hasIndexedWrite: false
|
|
519
|
+
};
|
|
520
|
+
if (access.kind === "cell-read" || access.kind === "cell-write") {
|
|
521
|
+
variablesWithCellAccess.add(access.variable);
|
|
522
|
+
}
|
|
523
|
+
if (access.kind === "cell-read") stats.hasCellRead = true;
|
|
524
|
+
if (access.kind === "cell-write") stats.hasCellWrite = true;
|
|
525
|
+
if (access.kind === "mutating-call") stats.hasMutatingCall = true;
|
|
526
|
+
if (access.kind === "indexed-write") stats.hasIndexedWrite = true;
|
|
527
|
+
accessStatsByVariable.set(access.variable, stats);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (variablesWithCellAccess.size === 0) {
|
|
531
|
+
return trace;
|
|
532
|
+
}
|
|
533
|
+
return trace.map((step) => {
|
|
534
|
+
if (!step.accesses?.length) {
|
|
535
|
+
return step;
|
|
536
|
+
}
|
|
537
|
+
const filtered = step.accesses.filter((access) => {
|
|
538
|
+
const stats = accessStatsByVariable.get(access.variable);
|
|
539
|
+
if (variablesWithCellAccess.has(access.variable)) {
|
|
540
|
+
return access.kind !== "indexed-read" && access.kind !== "indexed-write";
|
|
541
|
+
}
|
|
542
|
+
if (access.kind === "mutating-call" && stats?.hasIndexedWrite && !stats.hasCellRead && !stats.hasCellWrite) {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
if (access.kind === "indexed-read" && stats?.hasMutatingCall && !stats.hasIndexedWrite && !stats.hasCellRead && !stats.hasCellWrite) {
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
return true;
|
|
549
|
+
});
|
|
550
|
+
if (filtered.length === step.accesses.length) {
|
|
551
|
+
return step;
|
|
552
|
+
}
|
|
553
|
+
return {
|
|
554
|
+
...step,
|
|
555
|
+
...filtered.length > 0 ? { accesses: filtered } : {},
|
|
556
|
+
...filtered.length === 0 ? { accesses: void 0 } : {}
|
|
557
|
+
};
|
|
558
|
+
});
|
|
559
|
+
}
|
|
477
560
|
function normalizeRuntimeTraceContract(language, result) {
|
|
478
|
-
const
|
|
561
|
+
const rawNormalizedTrace = collapseTraceAccessNoise(
|
|
562
|
+
Array.isArray(result.trace) ? result.trace.map(normalizeTraceStep) : []
|
|
563
|
+
);
|
|
564
|
+
const maxTraceSteps = typeof result.maxTraceSteps === "number" && Number.isFinite(result.maxTraceSteps) ? Math.max(1, Math.floor(result.maxTraceSteps)) : void 0;
|
|
565
|
+
const traceWasClipped = maxTraceSteps !== void 0 && rawNormalizedTrace.length > maxTraceSteps;
|
|
566
|
+
const normalizedTrace = traceWasClipped && maxTraceSteps !== void 0 ? rawNormalizedTrace.slice(0, maxTraceSteps) : rawNormalizedTrace;
|
|
479
567
|
const lineEventCount = typeof result.lineEventCount === "number" && Number.isFinite(result.lineEventCount) ? Math.floor(result.lineEventCount) : normalizedTrace.filter((step) => step.event === "line").length;
|
|
480
568
|
const normalizedConsole = Array.isArray(result.consoleOutput) ? result.consoleOutput.map((line) => String(line)) : [];
|
|
481
569
|
return {
|
|
@@ -487,7 +575,7 @@ function normalizeRuntimeTraceContract(language, result) {
|
|
|
487
575
|
...typeof result.errorLine === "number" && Number.isFinite(result.errorLine) ? { errorLine: Math.floor(result.errorLine) } : {},
|
|
488
576
|
consoleOutput: normalizedConsole,
|
|
489
577
|
trace: normalizedTrace,
|
|
490
|
-
...result.traceLimitExceeded !== void 0 ? { traceLimitExceeded: Boolean(result.traceLimitExceeded) } : {},
|
|
578
|
+
...result.traceLimitExceeded !== void 0 || traceWasClipped ? { traceLimitExceeded: Boolean(result.traceLimitExceeded) || traceWasClipped } : {},
|
|
491
579
|
...typeof result.timeoutReason === "string" && result.timeoutReason.length > 0 ? { timeoutReason: result.timeoutReason } : {},
|
|
492
580
|
lineEventCount: Math.max(0, lineEventCount),
|
|
493
581
|
traceStepCount: normalizedTrace.length
|
|
@@ -608,6 +696,7 @@ var PYTHON_RUNTIME_PROFILE = {
|
|
|
608
696
|
maxTraceSteps: true,
|
|
609
697
|
maxLineEvents: true,
|
|
610
698
|
maxSingleLineHits: true,
|
|
699
|
+
maxStoredEvents: true,
|
|
611
700
|
minimalTrace: true
|
|
612
701
|
},
|
|
613
702
|
fidelity: {
|
|
@@ -669,6 +758,7 @@ var JAVASCRIPT_RUNTIME_PROFILE = {
|
|
|
669
758
|
maxTraceSteps: true,
|
|
670
759
|
maxLineEvents: true,
|
|
671
760
|
maxSingleLineHits: true,
|
|
761
|
+
maxStoredEvents: true,
|
|
672
762
|
minimalTrace: true
|
|
673
763
|
},
|
|
674
764
|
fidelity: {
|
|
@@ -730,6 +820,7 @@ var TYPESCRIPT_RUNTIME_PROFILE = {
|
|
|
730
820
|
maxTraceSteps: true,
|
|
731
821
|
maxLineEvents: true,
|
|
732
822
|
maxSingleLineHits: true,
|
|
823
|
+
maxStoredEvents: true,
|
|
733
824
|
minimalTrace: true
|
|
734
825
|
},
|
|
735
826
|
fidelity: {
|
|
@@ -760,10 +851,78 @@ var TYPESCRIPT_RUNTIME_PROFILE = {
|
|
|
760
851
|
}
|
|
761
852
|
}
|
|
762
853
|
};
|
|
854
|
+
var JAVA_RUNTIME_PROFILE = {
|
|
855
|
+
language: "java",
|
|
856
|
+
maturity: "experimental",
|
|
857
|
+
capabilities: {
|
|
858
|
+
execution: {
|
|
859
|
+
styles: {
|
|
860
|
+
function: true,
|
|
861
|
+
solutionMethod: true,
|
|
862
|
+
opsClass: true,
|
|
863
|
+
script: false,
|
|
864
|
+
interviewMode: true
|
|
865
|
+
},
|
|
866
|
+
timeouts: {
|
|
867
|
+
clientTimeouts: true,
|
|
868
|
+
runtimeTimeouts: true
|
|
869
|
+
}
|
|
870
|
+
},
|
|
871
|
+
tracing: {
|
|
872
|
+
supported: true,
|
|
873
|
+
events: {
|
|
874
|
+
line: true,
|
|
875
|
+
call: true,
|
|
876
|
+
return: true,
|
|
877
|
+
exception: true,
|
|
878
|
+
stdout: false,
|
|
879
|
+
timeout: true
|
|
880
|
+
},
|
|
881
|
+
controls: {
|
|
882
|
+
maxTraceSteps: true,
|
|
883
|
+
maxLineEvents: false,
|
|
884
|
+
maxSingleLineHits: false,
|
|
885
|
+
maxStoredEvents: true,
|
|
886
|
+
minimalTrace: false
|
|
887
|
+
},
|
|
888
|
+
fidelity: {
|
|
889
|
+
preciseLineMapping: true,
|
|
890
|
+
stableFunctionNames: true,
|
|
891
|
+
callStack: true
|
|
892
|
+
}
|
|
893
|
+
},
|
|
894
|
+
diagnostics: {
|
|
895
|
+
compileErrors: true,
|
|
896
|
+
runtimeErrors: true,
|
|
897
|
+
mappedErrorLines: false,
|
|
898
|
+
stackTraces: true
|
|
899
|
+
},
|
|
900
|
+
structures: {
|
|
901
|
+
treeNodeRefs: true,
|
|
902
|
+
listNodeRefs: true,
|
|
903
|
+
mapSerialization: true,
|
|
904
|
+
setSerialization: true,
|
|
905
|
+
graphSerialization: false,
|
|
906
|
+
cycleReferences: true
|
|
907
|
+
},
|
|
908
|
+
visualization: {
|
|
909
|
+
runtimePayloads: true,
|
|
910
|
+
objectKinds: true,
|
|
911
|
+
hashMaps: true,
|
|
912
|
+
stepVisualization: true
|
|
913
|
+
}
|
|
914
|
+
},
|
|
915
|
+
notes: [
|
|
916
|
+
"Java currently supports the browser-local Java 17 lane for function, solution-method, and ops-class execution.",
|
|
917
|
+
"Interview-mode Java reuses the same browser-local execution path and remains experimental in the first harness slice.",
|
|
918
|
+
"Script-style Java is not enabled yet."
|
|
919
|
+
]
|
|
920
|
+
};
|
|
763
921
|
var LANGUAGE_RUNTIME_PROFILES = {
|
|
764
922
|
python: PYTHON_RUNTIME_PROFILE,
|
|
765
923
|
javascript: JAVASCRIPT_RUNTIME_PROFILE,
|
|
766
|
-
typescript: TYPESCRIPT_RUNTIME_PROFILE
|
|
924
|
+
typescript: TYPESCRIPT_RUNTIME_PROFILE,
|
|
925
|
+
java: JAVA_RUNTIME_PROFILE
|
|
767
926
|
};
|
|
768
927
|
var SUPPORTED_LANGUAGES = Object.freeze(
|
|
769
928
|
Object.keys(LANGUAGE_RUNTIME_PROFILES)
|
|
@@ -840,13 +999,796 @@ function createJavaScriptRuntimeClient(runtimeLanguage, workerClient) {
|
|
|
840
999
|
return new JavaScriptRuntimeClient(runtimeLanguage, workerClient);
|
|
841
1000
|
}
|
|
842
1001
|
|
|
1002
|
+
// packages/harness-browser/src/java-worker-client.ts
|
|
1003
|
+
var TRACING_TIMEOUT_MS2 = 25e3;
|
|
1004
|
+
var INIT_TIMEOUT_MS2 = 25e3;
|
|
1005
|
+
var MESSAGE_TIMEOUT_MS2 = 3e4;
|
|
1006
|
+
var WORKER_READY_TIMEOUT_MS2 = 1e4;
|
|
1007
|
+
var JavaWorkerClient = class {
|
|
1008
|
+
constructor(options) {
|
|
1009
|
+
this.options = options;
|
|
1010
|
+
this.debug = options.debug ?? process.env.NODE_ENV === "development";
|
|
1011
|
+
}
|
|
1012
|
+
worker = null;
|
|
1013
|
+
pendingMessages = /* @__PURE__ */ new Map();
|
|
1014
|
+
messageId = 0;
|
|
1015
|
+
isInitializing = false;
|
|
1016
|
+
initPromise = null;
|
|
1017
|
+
workerReadyPromise = null;
|
|
1018
|
+
workerReadyResolve = null;
|
|
1019
|
+
workerReadyReject = null;
|
|
1020
|
+
debug;
|
|
1021
|
+
isSupported() {
|
|
1022
|
+
return typeof Worker !== "undefined";
|
|
1023
|
+
}
|
|
1024
|
+
getWorker() {
|
|
1025
|
+
if (this.worker) return this.worker;
|
|
1026
|
+
if (!this.isSupported()) {
|
|
1027
|
+
throw new Error("Web Workers are not supported in this environment");
|
|
1028
|
+
}
|
|
1029
|
+
this.workerReadyPromise = new Promise((resolve, reject) => {
|
|
1030
|
+
this.workerReadyResolve = resolve;
|
|
1031
|
+
this.workerReadyReject = (error) => reject(error);
|
|
1032
|
+
});
|
|
1033
|
+
const workerUrl = this.debug && !this.options.workerUrl.includes("?") ? `${this.options.workerUrl}?dev=${Date.now()}` : this.options.workerUrl;
|
|
1034
|
+
this.worker = new Worker(workerUrl);
|
|
1035
|
+
this.worker.onmessage = (event) => {
|
|
1036
|
+
const { id, type, payload } = event.data;
|
|
1037
|
+
if (type === "worker-ready") {
|
|
1038
|
+
this.workerReadyResolve?.();
|
|
1039
|
+
this.workerReadyResolve = null;
|
|
1040
|
+
this.workerReadyReject = null;
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
if (!id) return;
|
|
1044
|
+
const pending = this.pendingMessages.get(id);
|
|
1045
|
+
if (!pending) return;
|
|
1046
|
+
this.pendingMessages.delete(id);
|
|
1047
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
1048
|
+
if (type === "error") {
|
|
1049
|
+
pending.reject(new Error(payload.error));
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
pending.resolve(payload);
|
|
1053
|
+
};
|
|
1054
|
+
this.worker.onerror = (error) => {
|
|
1055
|
+
const workerError = new Error(error.message || "Java worker error");
|
|
1056
|
+
this.workerReadyReject?.(workerError);
|
|
1057
|
+
this.workerReadyResolve = null;
|
|
1058
|
+
this.workerReadyReject = null;
|
|
1059
|
+
for (const [, pending] of this.pendingMessages) {
|
|
1060
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
1061
|
+
pending.reject(workerError);
|
|
1062
|
+
}
|
|
1063
|
+
this.pendingMessages.clear();
|
|
1064
|
+
this.terminateAndReset(workerError);
|
|
1065
|
+
};
|
|
1066
|
+
return this.worker;
|
|
1067
|
+
}
|
|
1068
|
+
async waitForWorkerReady() {
|
|
1069
|
+
const readyPromise = this.workerReadyPromise;
|
|
1070
|
+
if (!readyPromise) return;
|
|
1071
|
+
await new Promise((resolve, reject) => {
|
|
1072
|
+
let settled = false;
|
|
1073
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
1074
|
+
if (settled) return;
|
|
1075
|
+
settled = true;
|
|
1076
|
+
const timeoutError = new Error(
|
|
1077
|
+
`Java worker failed to initialize in time (${Math.round(WORKER_READY_TIMEOUT_MS2 / 1e3)}s)`
|
|
1078
|
+
);
|
|
1079
|
+
this.terminateAndReset(timeoutError);
|
|
1080
|
+
reject(timeoutError);
|
|
1081
|
+
}, WORKER_READY_TIMEOUT_MS2);
|
|
1082
|
+
readyPromise.then(() => {
|
|
1083
|
+
if (settled) return;
|
|
1084
|
+
settled = true;
|
|
1085
|
+
globalThis.clearTimeout(timeoutId);
|
|
1086
|
+
resolve();
|
|
1087
|
+
}).catch((error) => {
|
|
1088
|
+
if (settled) return;
|
|
1089
|
+
settled = true;
|
|
1090
|
+
globalThis.clearTimeout(timeoutId);
|
|
1091
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
1092
|
+
});
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
async sendMessage(type, payload, timeoutMs = MESSAGE_TIMEOUT_MS2) {
|
|
1096
|
+
const worker = this.getWorker();
|
|
1097
|
+
await this.waitForWorkerReady();
|
|
1098
|
+
const id = String(++this.messageId);
|
|
1099
|
+
return new Promise((resolve, reject) => {
|
|
1100
|
+
this.pendingMessages.set(id, {
|
|
1101
|
+
resolve,
|
|
1102
|
+
reject
|
|
1103
|
+
});
|
|
1104
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
1105
|
+
const pending2 = this.pendingMessages.get(id);
|
|
1106
|
+
if (!pending2) return;
|
|
1107
|
+
this.pendingMessages.delete(id);
|
|
1108
|
+
pending2.reject(new Error(`Worker request timed out: ${type}`));
|
|
1109
|
+
}, timeoutMs);
|
|
1110
|
+
const pending = this.pendingMessages.get(id);
|
|
1111
|
+
if (pending) pending.timeoutId = timeoutId;
|
|
1112
|
+
worker.postMessage({ id, type, payload });
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
async executeWithTimeout(executor, timeoutMs) {
|
|
1116
|
+
return new Promise((resolve, reject) => {
|
|
1117
|
+
let settled = false;
|
|
1118
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
1119
|
+
if (settled) return;
|
|
1120
|
+
settled = true;
|
|
1121
|
+
this.terminateAndReset();
|
|
1122
|
+
reject(
|
|
1123
|
+
new Error(
|
|
1124
|
+
`Java execution timed out after ${Math.round(timeoutMs / 1e3)} seconds.`
|
|
1125
|
+
)
|
|
1126
|
+
);
|
|
1127
|
+
}, timeoutMs);
|
|
1128
|
+
executor().then((result) => {
|
|
1129
|
+
if (settled) return;
|
|
1130
|
+
settled = true;
|
|
1131
|
+
globalThis.clearTimeout(timeoutId);
|
|
1132
|
+
resolve(result);
|
|
1133
|
+
}).catch((error) => {
|
|
1134
|
+
if (settled) return;
|
|
1135
|
+
settled = true;
|
|
1136
|
+
globalThis.clearTimeout(timeoutId);
|
|
1137
|
+
reject(error);
|
|
1138
|
+
});
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
terminateAndReset(reason = new Error("Worker was terminated")) {
|
|
1142
|
+
this.workerReadyReject?.(reason);
|
|
1143
|
+
if (this.worker) {
|
|
1144
|
+
this.worker.terminate();
|
|
1145
|
+
this.worker = null;
|
|
1146
|
+
}
|
|
1147
|
+
this.initPromise = null;
|
|
1148
|
+
this.isInitializing = false;
|
|
1149
|
+
this.workerReadyPromise = null;
|
|
1150
|
+
this.workerReadyResolve = null;
|
|
1151
|
+
this.workerReadyReject = null;
|
|
1152
|
+
for (const [, pending] of this.pendingMessages) {
|
|
1153
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
1154
|
+
pending.reject(reason);
|
|
1155
|
+
}
|
|
1156
|
+
this.pendingMessages.clear();
|
|
1157
|
+
}
|
|
1158
|
+
async init() {
|
|
1159
|
+
if (this.initPromise) return this.initPromise;
|
|
1160
|
+
if (this.isInitializing) {
|
|
1161
|
+
await new Promise((resolve) => globalThis.setTimeout(resolve, 100));
|
|
1162
|
+
return this.init();
|
|
1163
|
+
}
|
|
1164
|
+
this.isInitializing = true;
|
|
1165
|
+
this.initPromise = this.sendMessage("init", void 0, INIT_TIMEOUT_MS2);
|
|
1166
|
+
try {
|
|
1167
|
+
return await this.initPromise;
|
|
1168
|
+
} catch (error) {
|
|
1169
|
+
this.initPromise = null;
|
|
1170
|
+
throw error;
|
|
1171
|
+
} finally {
|
|
1172
|
+
this.isInitializing = false;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
async executeWithTracing(code, functionName, inputs, options, executionStyle) {
|
|
1176
|
+
await this.init();
|
|
1177
|
+
return this.executeWithTimeout(
|
|
1178
|
+
() => this.sendMessage(
|
|
1179
|
+
"execute-with-tracing",
|
|
1180
|
+
{ code, functionName, inputs, options, executionStyle },
|
|
1181
|
+
TRACING_TIMEOUT_MS2 + 5e3
|
|
1182
|
+
),
|
|
1183
|
+
TRACING_TIMEOUT_MS2
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
async executeCode(code, functionName, inputs, options, executionStyle) {
|
|
1187
|
+
const result = await this.executeWithTracing(code, functionName, inputs, options, executionStyle);
|
|
1188
|
+
if (!result.success) {
|
|
1189
|
+
return {
|
|
1190
|
+
success: false,
|
|
1191
|
+
output: null,
|
|
1192
|
+
error: result.error ?? "Java execution failed",
|
|
1193
|
+
...result.errorLine !== void 0 ? { errorLine: result.errorLine } : {},
|
|
1194
|
+
consoleOutput: result.consoleOutput
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
return {
|
|
1198
|
+
success: true,
|
|
1199
|
+
output: result.output,
|
|
1200
|
+
consoleOutput: result.consoleOutput
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
async executeCodeInterviewMode(code, functionName, inputs, options, executionStyle) {
|
|
1204
|
+
return this.executeCode(code, functionName, inputs, options, executionStyle);
|
|
1205
|
+
}
|
|
1206
|
+
terminate() {
|
|
1207
|
+
this.terminateAndReset();
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
|
|
1211
|
+
// packages/harness-core/src/trace-adapters/java.ts
|
|
1212
|
+
function parseScalar(raw) {
|
|
1213
|
+
if (raw === "null") return null;
|
|
1214
|
+
if (raw === "true") return true;
|
|
1215
|
+
if (raw === "false") return false;
|
|
1216
|
+
if (/^-?\d+$/.test(raw)) return Number.parseInt(raw, 10);
|
|
1217
|
+
if (/^-?\d+\.\d+$/.test(raw)) return Number.parseFloat(raw);
|
|
1218
|
+
if (raw.startsWith('"') && raw.endsWith('"') || raw.startsWith("[") && raw.endsWith("]") || raw.startsWith("{") && raw.endsWith("}")) {
|
|
1219
|
+
try {
|
|
1220
|
+
return JSON.parse(raw);
|
|
1221
|
+
} catch {
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
return raw;
|
|
1225
|
+
}
|
|
1226
|
+
function parseKeyValuePairs(fragment) {
|
|
1227
|
+
const variables = {};
|
|
1228
|
+
const matches = Array.from(fragment.matchAll(/\b([A-Za-z_][A-Za-z0-9_.]*)=/g));
|
|
1229
|
+
for (let index = 0; index < matches.length; index += 1) {
|
|
1230
|
+
const match = matches[index];
|
|
1231
|
+
const rawKey = match[1];
|
|
1232
|
+
const valueStart = (match.index ?? 0) + match[0].length;
|
|
1233
|
+
const valueEnd = index + 1 < matches.length ? matches[index + 1].index ?? fragment.length : fragment.length;
|
|
1234
|
+
const rawValue = fragment.slice(valueStart, valueEnd).trim();
|
|
1235
|
+
if (!rawKey || rawValue === void 0) continue;
|
|
1236
|
+
if (rawKey === "method") continue;
|
|
1237
|
+
variables[rawKey.replaceAll(".", "_")] = parseScalar(rawValue);
|
|
1238
|
+
}
|
|
1239
|
+
return variables;
|
|
1240
|
+
}
|
|
1241
|
+
function extractLineMetadata(event) {
|
|
1242
|
+
const match = event.match(/^line=(\d+)(?:\s+(.*))?$/);
|
|
1243
|
+
if (!match) {
|
|
1244
|
+
return { line: 1, payload: event };
|
|
1245
|
+
}
|
|
1246
|
+
return {
|
|
1247
|
+
line: Number.parseInt(match[1], 10),
|
|
1248
|
+
payload: match[2] ?? ""
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
function stripInlineComments(line, inBlockComment) {
|
|
1252
|
+
let result = "";
|
|
1253
|
+
let index = 0;
|
|
1254
|
+
let inBlock = inBlockComment;
|
|
1255
|
+
while (index < line.length) {
|
|
1256
|
+
const current = line[index];
|
|
1257
|
+
const next = index + 1 < line.length ? line[index + 1] : "";
|
|
1258
|
+
if (inBlock) {
|
|
1259
|
+
if (current === "*" && next === "/") {
|
|
1260
|
+
inBlock = false;
|
|
1261
|
+
index += 2;
|
|
1262
|
+
continue;
|
|
1263
|
+
}
|
|
1264
|
+
index += 1;
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
if (current === "/" && next === "*") {
|
|
1268
|
+
inBlock = true;
|
|
1269
|
+
index += 2;
|
|
1270
|
+
continue;
|
|
1271
|
+
}
|
|
1272
|
+
if (current === "/" && next === "/") {
|
|
1273
|
+
break;
|
|
1274
|
+
}
|
|
1275
|
+
result += current;
|
|
1276
|
+
index += 1;
|
|
1277
|
+
}
|
|
1278
|
+
return { text: result, inBlockComment: inBlock };
|
|
1279
|
+
}
|
|
1280
|
+
function isMethodDeclarationLine(line) {
|
|
1281
|
+
const trimmed = line.trim();
|
|
1282
|
+
if (!trimmed) return false;
|
|
1283
|
+
if (trimmed.startsWith("@")) return false;
|
|
1284
|
+
if (!trimmed.includes("(") || !trimmed.includes(")")) return false;
|
|
1285
|
+
if (trimmed.endsWith(";")) return false;
|
|
1286
|
+
if (trimmed.includes("->")) return false;
|
|
1287
|
+
if (/^(?:if|for|while|switch|catch|do|try|else|return|throw|new)\b/.test(trimmed)) {
|
|
1288
|
+
return false;
|
|
1289
|
+
}
|
|
1290
|
+
if (!/[A-Za-z_][A-Za-z0-9_]*\s*\([^{};]*\)/.test(trimmed)) {
|
|
1291
|
+
return false;
|
|
1292
|
+
}
|
|
1293
|
+
return /(?:\{\s*)?$/.test(trimmed);
|
|
1294
|
+
}
|
|
1295
|
+
function buildLineRemap(sourceText) {
|
|
1296
|
+
if (typeof sourceText !== "string" || sourceText.length === 0) {
|
|
1297
|
+
return null;
|
|
1298
|
+
}
|
|
1299
|
+
const lines = sourceText.split(/\r?\n/);
|
|
1300
|
+
const executable = /* @__PURE__ */ new Set();
|
|
1301
|
+
let inBlockComment = false;
|
|
1302
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1303
|
+
const { text, inBlockComment: nextInBlockComment } = stripInlineComments(lines[index] ?? "", inBlockComment);
|
|
1304
|
+
inBlockComment = nextInBlockComment;
|
|
1305
|
+
if (text.trim().length > 0) {
|
|
1306
|
+
executable.add(index + 1);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
if (executable.size === 0) {
|
|
1310
|
+
return null;
|
|
1311
|
+
}
|
|
1312
|
+
const nextExecutableAtOrAfter = new Array(lines.length + 2).fill(null);
|
|
1313
|
+
let nextExecutable = null;
|
|
1314
|
+
for (let line = lines.length; line >= 1; line -= 1) {
|
|
1315
|
+
if (executable.has(line)) {
|
|
1316
|
+
nextExecutable = line;
|
|
1317
|
+
}
|
|
1318
|
+
nextExecutableAtOrAfter[line] = nextExecutable;
|
|
1319
|
+
}
|
|
1320
|
+
const previousExecutableAtOrBefore = new Array(lines.length + 2).fill(null);
|
|
1321
|
+
let previousExecutable = null;
|
|
1322
|
+
for (let line = 1; line <= lines.length; line += 1) {
|
|
1323
|
+
if (executable.has(line)) {
|
|
1324
|
+
previousExecutable = line;
|
|
1325
|
+
}
|
|
1326
|
+
previousExecutableAtOrBefore[line] = previousExecutable;
|
|
1327
|
+
}
|
|
1328
|
+
const remap = /* @__PURE__ */ new Map();
|
|
1329
|
+
for (let line = 1; line <= lines.length; line += 1) {
|
|
1330
|
+
if (executable.has(line)) continue;
|
|
1331
|
+
const forward = nextExecutableAtOrAfter[line];
|
|
1332
|
+
const backward = previousExecutableAtOrBefore[line];
|
|
1333
|
+
const target = forward ?? backward;
|
|
1334
|
+
if (target !== null && target !== line) {
|
|
1335
|
+
remap.set(line, target);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
return remap;
|
|
1339
|
+
}
|
|
1340
|
+
function buildMethodDeclarationLineSet(sourceText) {
|
|
1341
|
+
if (typeof sourceText !== "string" || sourceText.length === 0) {
|
|
1342
|
+
return null;
|
|
1343
|
+
}
|
|
1344
|
+
const declarationLines = /* @__PURE__ */ new Set();
|
|
1345
|
+
const lines = sourceText.split(/\r?\n/);
|
|
1346
|
+
let inBlockComment = false;
|
|
1347
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1348
|
+
const { text, inBlockComment: nextInBlockComment } = stripInlineComments(lines[index] ?? "", inBlockComment);
|
|
1349
|
+
inBlockComment = nextInBlockComment;
|
|
1350
|
+
if (isMethodDeclarationLine(text)) {
|
|
1351
|
+
declarationLines.add(index + 1);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
return declarationLines;
|
|
1355
|
+
}
|
|
1356
|
+
function parseAccessEvent(payload) {
|
|
1357
|
+
const isEphemeralOutputArrayName = (name) => /^(output|outputs)$/i.test(name);
|
|
1358
|
+
const cellRead = payload.match(/^access ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]\[(\d+)\]=(.+)$/);
|
|
1359
|
+
if (cellRead) {
|
|
1360
|
+
return [{
|
|
1361
|
+
variable: cellRead[1],
|
|
1362
|
+
kind: "cell-read",
|
|
1363
|
+
indices: [Number.parseInt(cellRead[2], 10), Number.parseInt(cellRead[3], 10)],
|
|
1364
|
+
pathDepth: 2
|
|
1365
|
+
}];
|
|
1366
|
+
}
|
|
1367
|
+
const indexedRead = payload.match(/^access ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]=(.+)$/);
|
|
1368
|
+
if (indexedRead) {
|
|
1369
|
+
return [{
|
|
1370
|
+
variable: indexedRead[1],
|
|
1371
|
+
kind: "indexed-read",
|
|
1372
|
+
indices: [Number.parseInt(indexedRead[2], 10)],
|
|
1373
|
+
pathDepth: 1
|
|
1374
|
+
}];
|
|
1375
|
+
}
|
|
1376
|
+
const cellWrite = payload.match(/^write-array ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]\[(\d+)\]=(.+)$/);
|
|
1377
|
+
if (cellWrite) {
|
|
1378
|
+
return [{
|
|
1379
|
+
variable: cellWrite[1],
|
|
1380
|
+
kind: "cell-write",
|
|
1381
|
+
indices: [Number.parseInt(cellWrite[2], 10), Number.parseInt(cellWrite[3], 10)],
|
|
1382
|
+
pathDepth: 2
|
|
1383
|
+
}];
|
|
1384
|
+
}
|
|
1385
|
+
const indexedWrite = payload.match(/^write-array ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]=(.+)$/);
|
|
1386
|
+
if (indexedWrite) {
|
|
1387
|
+
if (isEphemeralOutputArrayName(indexedWrite[1])) {
|
|
1388
|
+
return void 0;
|
|
1389
|
+
}
|
|
1390
|
+
return [{
|
|
1391
|
+
variable: indexedWrite[1],
|
|
1392
|
+
kind: "indexed-write",
|
|
1393
|
+
indices: [Number.parseInt(indexedWrite[2], 10)],
|
|
1394
|
+
pathDepth: 1
|
|
1395
|
+
}];
|
|
1396
|
+
}
|
|
1397
|
+
const mutatingCall = payload.match(/^mutate ([A-Za-z_][A-Za-z0-9_]*) method=([A-Za-z_][A-Za-z0-9_]*)$/);
|
|
1398
|
+
if (mutatingCall) {
|
|
1399
|
+
return [{
|
|
1400
|
+
variable: mutatingCall[1],
|
|
1401
|
+
kind: "mutating-call",
|
|
1402
|
+
method: mutatingCall[2],
|
|
1403
|
+
pathDepth: 1
|
|
1404
|
+
}];
|
|
1405
|
+
}
|
|
1406
|
+
return void 0;
|
|
1407
|
+
}
|
|
1408
|
+
function parseStructureState(payload) {
|
|
1409
|
+
const match = payload.match(/^state (linked-list|tree) ([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
|
|
1410
|
+
if (!match) return null;
|
|
1411
|
+
return {
|
|
1412
|
+
structure: match[1],
|
|
1413
|
+
variable: match[2],
|
|
1414
|
+
value: JSON.parse(match[3])
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
function parseObjectState(payload) {
|
|
1418
|
+
const match = payload.match(/^object-state ([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
|
|
1419
|
+
if (!match) return null;
|
|
1420
|
+
return {
|
|
1421
|
+
variable: match[1],
|
|
1422
|
+
visualization: JSON.parse(match[2])
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
function parseObjectFieldEvent(payload) {
|
|
1426
|
+
const match = payload.match(/^(access|write) ([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
|
|
1427
|
+
if (!match) return null;
|
|
1428
|
+
return {
|
|
1429
|
+
variable: match[2],
|
|
1430
|
+
field: match[3],
|
|
1431
|
+
value: parseScalar(match[4])
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
function buildFieldVisualization(event) {
|
|
1435
|
+
return {
|
|
1436
|
+
objectKinds: {
|
|
1437
|
+
[event.variable]: "object"
|
|
1438
|
+
},
|
|
1439
|
+
hashMaps: [
|
|
1440
|
+
{
|
|
1441
|
+
name: event.variable,
|
|
1442
|
+
kind: "object",
|
|
1443
|
+
objectClassName: event.field === "next" || event.field === "prev" ? "ListNode" : event.field === "left" || event.field === "right" ? "TreeNode" : void 0,
|
|
1444
|
+
objectId: `${event.variable}-object`,
|
|
1445
|
+
highlightedKey: event.field,
|
|
1446
|
+
entries: [{ key: event.field, value: event.value, highlight: true }]
|
|
1447
|
+
}
|
|
1448
|
+
]
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
function callStacksEqual(left, right) {
|
|
1452
|
+
return JSON.stringify(left ?? []) === JSON.stringify(right ?? []);
|
|
1453
|
+
}
|
|
1454
|
+
function mergeVisualizationPayloads(left, right) {
|
|
1455
|
+
if (!left) return right;
|
|
1456
|
+
if (!right) return left;
|
|
1457
|
+
return {
|
|
1458
|
+
...left,
|
|
1459
|
+
...right,
|
|
1460
|
+
objectKinds: {
|
|
1461
|
+
...left.objectKinds ?? {},
|
|
1462
|
+
...right.objectKinds ?? {}
|
|
1463
|
+
},
|
|
1464
|
+
hashMaps: [
|
|
1465
|
+
...left.hashMaps ?? [],
|
|
1466
|
+
...right.hashMaps ?? []
|
|
1467
|
+
]
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
function maybeMergeConsecutiveLineStep(trace, nextStep) {
|
|
1471
|
+
if (nextStep.event !== "line") {
|
|
1472
|
+
return false;
|
|
1473
|
+
}
|
|
1474
|
+
const previous = trace.at(-1);
|
|
1475
|
+
if (!previous || previous.event !== "line") {
|
|
1476
|
+
return false;
|
|
1477
|
+
}
|
|
1478
|
+
if (previous.line !== nextStep.line || previous.function !== nextStep.function) {
|
|
1479
|
+
return false;
|
|
1480
|
+
}
|
|
1481
|
+
if (!callStacksEqual(previous.callStack, nextStep.callStack)) {
|
|
1482
|
+
return false;
|
|
1483
|
+
}
|
|
1484
|
+
previous.variables = { ...previous.variables ?? {}, ...nextStep.variables ?? {} };
|
|
1485
|
+
if (nextStep.accesses?.length) {
|
|
1486
|
+
previous.accesses = [...previous.accesses ?? [], ...nextStep.accesses];
|
|
1487
|
+
}
|
|
1488
|
+
previous.visualization = mergeVisualizationPayloads(previous.visualization, nextStep.visualization);
|
|
1489
|
+
return true;
|
|
1490
|
+
}
|
|
1491
|
+
function filterStructuredVariables(variables) {
|
|
1492
|
+
if (!variables) {
|
|
1493
|
+
return void 0;
|
|
1494
|
+
}
|
|
1495
|
+
const isEphemeralOutputArrayName = (name) => /^(output|outputs)$/i.test(name);
|
|
1496
|
+
const entries = Object.entries(variables).filter(([name, value]) => {
|
|
1497
|
+
if (value === null || typeof value !== "object") {
|
|
1498
|
+
return false;
|
|
1499
|
+
}
|
|
1500
|
+
if (!Array.isArray(value)) {
|
|
1501
|
+
return true;
|
|
1502
|
+
}
|
|
1503
|
+
if (Array.isArray(value[0])) {
|
|
1504
|
+
return true;
|
|
1505
|
+
}
|
|
1506
|
+
return !isEphemeralOutputArrayName(name);
|
|
1507
|
+
});
|
|
1508
|
+
if (entries.length === 0) {
|
|
1509
|
+
return void 0;
|
|
1510
|
+
}
|
|
1511
|
+
return Object.fromEntries(entries);
|
|
1512
|
+
}
|
|
1513
|
+
function appendJavaTraceStep(trace, step, pendingAccesses) {
|
|
1514
|
+
const nextStep = pendingAccesses.length > 0 ? { ...step, accesses: [...pendingAccesses] } : step;
|
|
1515
|
+
pendingAccesses.length = 0;
|
|
1516
|
+
if (!maybeMergeConsecutiveLineStep(trace, nextStep)) {
|
|
1517
|
+
trace.push(nextStep);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
function mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, patch) {
|
|
1521
|
+
const candidate = trace.at(-1);
|
|
1522
|
+
if (!candidate || candidate.event !== "line") {
|
|
1523
|
+
return false;
|
|
1524
|
+
}
|
|
1525
|
+
if (candidate.line !== line || candidate.function !== currentFunction) {
|
|
1526
|
+
return false;
|
|
1527
|
+
}
|
|
1528
|
+
if (!callStacksEqual(candidate.callStack, currentCallStack)) {
|
|
1529
|
+
return false;
|
|
1530
|
+
}
|
|
1531
|
+
candidate.variables = { ...candidate.variables ?? {}, ...patch.variables ?? {} };
|
|
1532
|
+
if (patch.accesses?.length) {
|
|
1533
|
+
candidate.accesses = [...candidate.accesses ?? [], ...patch.accesses];
|
|
1534
|
+
}
|
|
1535
|
+
candidate.visualization = mergeVisualizationPayloads(candidate.visualization, patch.visualization);
|
|
1536
|
+
return true;
|
|
1537
|
+
}
|
|
1538
|
+
function mergeAccessesIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, accesses) {
|
|
1539
|
+
return mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, {
|
|
1540
|
+
variables: void 0,
|
|
1541
|
+
accesses,
|
|
1542
|
+
visualization: void 0
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1545
|
+
function eventsToRawTrace(events, sourceText) {
|
|
1546
|
+
const trace = [];
|
|
1547
|
+
const variables = {};
|
|
1548
|
+
const objectKinds = {};
|
|
1549
|
+
const stack = [];
|
|
1550
|
+
const pendingAccesses = [];
|
|
1551
|
+
let currentFunction = "<module>";
|
|
1552
|
+
const lineRemap = buildLineRemap(sourceText);
|
|
1553
|
+
const declarationLines = buildMethodDeclarationLineSet(sourceText);
|
|
1554
|
+
for (const rawEvent of events) {
|
|
1555
|
+
if (rawEvent === "clear" || rawEvent === "reset") continue;
|
|
1556
|
+
const metadata = extractLineMetadata(rawEvent);
|
|
1557
|
+
const line = lineRemap?.get(metadata.line) ?? metadata.line;
|
|
1558
|
+
const payload = metadata.payload;
|
|
1559
|
+
const isDeclarationLine = declarationLines?.has(line) === true;
|
|
1560
|
+
if (payload.startsWith("call ")) {
|
|
1561
|
+
const match = payload.match(/^call\s+(\S+)(?:\s+(.*))?$/);
|
|
1562
|
+
const functionName = match?.[1] ?? currentFunction;
|
|
1563
|
+
const argsFragment = match?.[2] ?? "";
|
|
1564
|
+
Object.assign(variables, parseKeyValuePairs(argsFragment));
|
|
1565
|
+
currentFunction = functionName || currentFunction;
|
|
1566
|
+
stack.push({ function: currentFunction, line });
|
|
1567
|
+
appendJavaTraceStep(trace, {
|
|
1568
|
+
line,
|
|
1569
|
+
event: "call",
|
|
1570
|
+
function: currentFunction,
|
|
1571
|
+
variables: { ...variables },
|
|
1572
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} }))
|
|
1573
|
+
}, pendingAccesses);
|
|
1574
|
+
continue;
|
|
1575
|
+
}
|
|
1576
|
+
if (payload.startsWith("return ")) {
|
|
1577
|
+
const match = payload.match(/^return\s+(\S+)$/);
|
|
1578
|
+
const functionName = match?.[1] ?? currentFunction;
|
|
1579
|
+
const returnVariables = functionName === "<module>" ? { ...variables } : filterStructuredVariables(variables) ?? {};
|
|
1580
|
+
appendJavaTraceStep(trace, {
|
|
1581
|
+
line,
|
|
1582
|
+
event: "return",
|
|
1583
|
+
function: functionName || currentFunction,
|
|
1584
|
+
variables: returnVariables,
|
|
1585
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} }))
|
|
1586
|
+
}, pendingAccesses);
|
|
1587
|
+
stack.pop();
|
|
1588
|
+
currentFunction = stack[stack.length - 1]?.function ?? "<module>";
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
if (isDeclarationLine) {
|
|
1592
|
+
continue;
|
|
1593
|
+
}
|
|
1594
|
+
if (payload.length === 0) {
|
|
1595
|
+
appendJavaTraceStep(trace, {
|
|
1596
|
+
line,
|
|
1597
|
+
event: "line",
|
|
1598
|
+
function: currentFunction,
|
|
1599
|
+
variables: { ...variables },
|
|
1600
|
+
...stack.length > 0 ? { callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) } : {}
|
|
1601
|
+
}, pendingAccesses);
|
|
1602
|
+
continue;
|
|
1603
|
+
}
|
|
1604
|
+
const structureState = parseStructureState(payload);
|
|
1605
|
+
if (structureState) {
|
|
1606
|
+
variables[structureState.variable] = structureState.value;
|
|
1607
|
+
objectKinds[structureState.variable] = structureState.structure;
|
|
1608
|
+
appendJavaTraceStep(trace, {
|
|
1609
|
+
line,
|
|
1610
|
+
event: "line",
|
|
1611
|
+
function: currentFunction,
|
|
1612
|
+
variables: { ...variables },
|
|
1613
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
|
|
1614
|
+
visualization: {
|
|
1615
|
+
objectKinds: { ...objectKinds }
|
|
1616
|
+
}
|
|
1617
|
+
}, pendingAccesses);
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
const objectState = parseObjectState(payload);
|
|
1621
|
+
if (objectState) {
|
|
1622
|
+
variables[objectState.variable] = { __ref__: objectState.visualization.objectId ?? `${objectState.variable}-object` };
|
|
1623
|
+
appendJavaTraceStep(trace, {
|
|
1624
|
+
line,
|
|
1625
|
+
event: "line",
|
|
1626
|
+
function: currentFunction,
|
|
1627
|
+
variables: { ...variables },
|
|
1628
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
|
|
1629
|
+
visualization: {
|
|
1630
|
+
objectKinds: { [objectState.variable]: "object" },
|
|
1631
|
+
hashMaps: [objectState.visualization]
|
|
1632
|
+
}
|
|
1633
|
+
}, pendingAccesses);
|
|
1634
|
+
continue;
|
|
1635
|
+
}
|
|
1636
|
+
const objectField = parseObjectFieldEvent(payload);
|
|
1637
|
+
if (objectField) {
|
|
1638
|
+
variables[objectField.variable] = { __ref__: `${objectField.variable}-object` };
|
|
1639
|
+
const currentCallStack = stack.length > 0 ? stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) : void 0;
|
|
1640
|
+
if (mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, {
|
|
1641
|
+
variables: { ...variables },
|
|
1642
|
+
accesses: void 0,
|
|
1643
|
+
visualization: buildFieldVisualization(objectField)
|
|
1644
|
+
})) {
|
|
1645
|
+
continue;
|
|
1646
|
+
}
|
|
1647
|
+
appendJavaTraceStep(trace, {
|
|
1648
|
+
line,
|
|
1649
|
+
event: "line",
|
|
1650
|
+
function: currentFunction,
|
|
1651
|
+
variables: { ...variables },
|
|
1652
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
|
|
1653
|
+
visualization: buildFieldVisualization(objectField)
|
|
1654
|
+
}, pendingAccesses);
|
|
1655
|
+
continue;
|
|
1656
|
+
}
|
|
1657
|
+
Object.assign(variables, parseKeyValuePairs(payload));
|
|
1658
|
+
const accesses = parseAccessEvent(payload);
|
|
1659
|
+
if (accesses) {
|
|
1660
|
+
const currentCallStack = stack.length > 0 ? stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) : void 0;
|
|
1661
|
+
if (mergeAccessesIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, accesses)) {
|
|
1662
|
+
continue;
|
|
1663
|
+
}
|
|
1664
|
+
pendingAccesses.push(...accesses);
|
|
1665
|
+
continue;
|
|
1666
|
+
}
|
|
1667
|
+
appendJavaTraceStep(trace, {
|
|
1668
|
+
line,
|
|
1669
|
+
event: "line",
|
|
1670
|
+
function: currentFunction,
|
|
1671
|
+
variables: { ...variables },
|
|
1672
|
+
...stack.length > 0 ? { callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) } : {}
|
|
1673
|
+
}, pendingAccesses);
|
|
1674
|
+
}
|
|
1675
|
+
if (pendingAccesses.length > 0 && trace.length > 0) {
|
|
1676
|
+
const last = trace[trace.length - 1];
|
|
1677
|
+
last.accesses = [...last.accesses ?? [], ...pendingAccesses];
|
|
1678
|
+
}
|
|
1679
|
+
return trace;
|
|
1680
|
+
}
|
|
1681
|
+
function buildJavaExecutionResult(output, events, executionTimeMs = 0, traceLimitExceeded, timeoutReason, maxTraceSteps, sourceText) {
|
|
1682
|
+
const trace = eventsToRawTrace(events, sourceText);
|
|
1683
|
+
return {
|
|
1684
|
+
success: true,
|
|
1685
|
+
output,
|
|
1686
|
+
trace,
|
|
1687
|
+
executionTimeMs,
|
|
1688
|
+
consoleOutput: [],
|
|
1689
|
+
...traceLimitExceeded !== void 0 ? { traceLimitExceeded } : {},
|
|
1690
|
+
...maxTraceSteps !== void 0 ? { maxTraceSteps } : {},
|
|
1691
|
+
...timeoutReason ? { timeoutReason } : {},
|
|
1692
|
+
lineEventCount: trace.filter((step) => step.event === "line").length,
|
|
1693
|
+
traceStepCount: trace.length
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
function adaptJavaTraceExecutionResult(result) {
|
|
1697
|
+
return adaptTraceExecutionResult("java", result);
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
// packages/harness-browser/src/java-runtime-client.ts
|
|
1701
|
+
var JavaRuntimeClient = class {
|
|
1702
|
+
constructor(workerClient) {
|
|
1703
|
+
this.workerClient = workerClient;
|
|
1704
|
+
}
|
|
1705
|
+
async init() {
|
|
1706
|
+
return this.workerClient.init();
|
|
1707
|
+
}
|
|
1708
|
+
async executeWithTracing(code, functionName, inputs, options, executionStyle = "function") {
|
|
1709
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
|
|
1710
|
+
request: "trace",
|
|
1711
|
+
executionStyle,
|
|
1712
|
+
functionName
|
|
1713
|
+
});
|
|
1714
|
+
const rawResult = await this.workerClient.executeWithTracing(
|
|
1715
|
+
code,
|
|
1716
|
+
functionName ?? "",
|
|
1717
|
+
inputs,
|
|
1718
|
+
options,
|
|
1719
|
+
executionStyle
|
|
1720
|
+
);
|
|
1721
|
+
if (!rawResult.success) {
|
|
1722
|
+
return {
|
|
1723
|
+
success: false,
|
|
1724
|
+
error: rawResult.error ?? "Java tracing failed",
|
|
1725
|
+
...rawResult.errorLine !== void 0 ? { errorLine: rawResult.errorLine } : {},
|
|
1726
|
+
trace: [],
|
|
1727
|
+
executionTimeMs: rawResult.executionTimeMs,
|
|
1728
|
+
consoleOutput: rawResult.consoleOutput,
|
|
1729
|
+
...rawResult.traceLimitExceeded !== void 0 ? { traceLimitExceeded: rawResult.traceLimitExceeded } : {},
|
|
1730
|
+
...rawResult.timeoutReason ? { timeoutReason: rawResult.timeoutReason } : {},
|
|
1731
|
+
lineEventCount: 0,
|
|
1732
|
+
traceStepCount: 0
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
const adapted = adaptJavaTraceExecutionResult(
|
|
1736
|
+
buildJavaExecutionResult(
|
|
1737
|
+
rawResult.output,
|
|
1738
|
+
rawResult.events,
|
|
1739
|
+
rawResult.executionTimeMs,
|
|
1740
|
+
rawResult.traceLimitExceeded,
|
|
1741
|
+
rawResult.timeoutReason,
|
|
1742
|
+
void 0,
|
|
1743
|
+
rawResult.sourceText
|
|
1744
|
+
)
|
|
1745
|
+
);
|
|
1746
|
+
return {
|
|
1747
|
+
...adapted,
|
|
1748
|
+
consoleOutput: rawResult.consoleOutput,
|
|
1749
|
+
executionTimeMs: rawResult.executionTimeMs
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
async executeCode(code, functionName, inputs, executionStyle = "function") {
|
|
1753
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
|
|
1754
|
+
request: "execute",
|
|
1755
|
+
executionStyle,
|
|
1756
|
+
functionName
|
|
1757
|
+
});
|
|
1758
|
+
return this.workerClient.executeCode(
|
|
1759
|
+
code,
|
|
1760
|
+
functionName,
|
|
1761
|
+
inputs,
|
|
1762
|
+
void 0,
|
|
1763
|
+
executionStyle
|
|
1764
|
+
);
|
|
1765
|
+
}
|
|
1766
|
+
async executeCodeInterviewMode(code, functionName, inputs, executionStyle = "function") {
|
|
1767
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
|
|
1768
|
+
request: "interview",
|
|
1769
|
+
executionStyle,
|
|
1770
|
+
functionName
|
|
1771
|
+
});
|
|
1772
|
+
return this.workerClient.executeCodeInterviewMode(
|
|
1773
|
+
code,
|
|
1774
|
+
functionName,
|
|
1775
|
+
inputs,
|
|
1776
|
+
void 0,
|
|
1777
|
+
executionStyle
|
|
1778
|
+
);
|
|
1779
|
+
}
|
|
1780
|
+
};
|
|
1781
|
+
function createJavaRuntimeClient(workerClient) {
|
|
1782
|
+
return new JavaRuntimeClient(workerClient);
|
|
1783
|
+
}
|
|
1784
|
+
|
|
843
1785
|
// packages/harness-browser/src/pyodide-worker-client.ts
|
|
844
1786
|
var EXECUTION_TIMEOUT_MS2 = 1e4;
|
|
845
1787
|
var INTERVIEW_MODE_TIMEOUT_MS2 = 5e3;
|
|
846
|
-
var
|
|
847
|
-
var
|
|
848
|
-
var
|
|
849
|
-
var
|
|
1788
|
+
var TRACING_TIMEOUT_MS3 = 3e4;
|
|
1789
|
+
var INIT_TIMEOUT_MS3 = 12e4;
|
|
1790
|
+
var MESSAGE_TIMEOUT_MS3 = 2e4;
|
|
1791
|
+
var WORKER_READY_TIMEOUT_MS3 = 1e4;
|
|
850
1792
|
var PyodideWorkerClient = class {
|
|
851
1793
|
constructor(options) {
|
|
852
1794
|
this.options = options;
|
|
@@ -936,14 +1878,14 @@ var PyodideWorkerClient = class {
|
|
|
936
1878
|
if (settled) return;
|
|
937
1879
|
settled = true;
|
|
938
1880
|
const timeoutError = new Error(
|
|
939
|
-
`Python worker failed to initialize in time (${Math.round(
|
|
1881
|
+
`Python worker failed to initialize in time (${Math.round(WORKER_READY_TIMEOUT_MS3 / 1e3)}s)`
|
|
940
1882
|
);
|
|
941
1883
|
if (this.debug) {
|
|
942
|
-
console.warn("[PyodideWorkerClient] worker-ready timeout", { timeoutMs:
|
|
1884
|
+
console.warn("[PyodideWorkerClient] worker-ready timeout", { timeoutMs: WORKER_READY_TIMEOUT_MS3 });
|
|
943
1885
|
}
|
|
944
1886
|
this.terminateAndReset(timeoutError);
|
|
945
1887
|
reject(timeoutError);
|
|
946
|
-
},
|
|
1888
|
+
}, WORKER_READY_TIMEOUT_MS3);
|
|
947
1889
|
readyPromise.then(() => {
|
|
948
1890
|
if (settled) return;
|
|
949
1891
|
settled = true;
|
|
@@ -960,7 +1902,7 @@ var PyodideWorkerClient = class {
|
|
|
960
1902
|
/**
|
|
961
1903
|
* Send a message to the worker and wait for a response
|
|
962
1904
|
*/
|
|
963
|
-
async sendMessage(type, payload, timeoutMs =
|
|
1905
|
+
async sendMessage(type, payload, timeoutMs = MESSAGE_TIMEOUT_MS3) {
|
|
964
1906
|
const worker = this.getWorker();
|
|
965
1907
|
await this.waitForWorkerReady();
|
|
966
1908
|
const id = String(++this.messageId);
|
|
@@ -1044,7 +1986,7 @@ var PyodideWorkerClient = class {
|
|
|
1044
1986
|
this.isInitializing = true;
|
|
1045
1987
|
this.initPromise = (async () => {
|
|
1046
1988
|
try {
|
|
1047
|
-
return await this.sendMessage("init", void 0,
|
|
1989
|
+
return await this.sendMessage("init", void 0, INIT_TIMEOUT_MS3);
|
|
1048
1990
|
} catch (error) {
|
|
1049
1991
|
const message = error instanceof Error ? error.message : String(error);
|
|
1050
1992
|
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");
|
|
@@ -1055,7 +1997,7 @@ var PyodideWorkerClient = class {
|
|
|
1055
1997
|
console.warn("[PyodideWorkerClient] init failed, resetting worker and retrying once", { message });
|
|
1056
1998
|
}
|
|
1057
1999
|
this.terminateAndReset();
|
|
1058
|
-
return this.sendMessage("init", void 0,
|
|
2000
|
+
return this.sendMessage("init", void 0, INIT_TIMEOUT_MS3);
|
|
1059
2001
|
}
|
|
1060
2002
|
})();
|
|
1061
2003
|
try {
|
|
@@ -1082,9 +2024,9 @@ var PyodideWorkerClient = class {
|
|
|
1082
2024
|
inputs,
|
|
1083
2025
|
executionStyle,
|
|
1084
2026
|
options
|
|
1085
|
-
},
|
|
2027
|
+
}, TRACING_TIMEOUT_MS3 + 5e3),
|
|
1086
2028
|
// Message timeout slightly longer than execution timeout
|
|
1087
|
-
|
|
2029
|
+
TRACING_TIMEOUT_MS3
|
|
1088
2030
|
);
|
|
1089
2031
|
} catch (error) {
|
|
1090
2032
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -1094,7 +2036,7 @@ var PyodideWorkerClient = class {
|
|
|
1094
2036
|
success: false,
|
|
1095
2037
|
error: errorMessage,
|
|
1096
2038
|
trace: [],
|
|
1097
|
-
executionTimeMs:
|
|
2039
|
+
executionTimeMs: TRACING_TIMEOUT_MS3,
|
|
1098
2040
|
consoleOutput: [],
|
|
1099
2041
|
traceLimitExceeded: true,
|
|
1100
2042
|
timeoutReason: "client-timeout",
|
|
@@ -1255,6 +2197,7 @@ var DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS = Object.freeze({
|
|
|
1255
2197
|
pythonRuntimeCore: "pyodide/runtime-core.js",
|
|
1256
2198
|
pythonSnippets: "generated-python-harness-snippets.js",
|
|
1257
2199
|
javascriptWorker: "javascript-worker.js",
|
|
2200
|
+
javaWorker: "java-worker.js",
|
|
1258
2201
|
typescriptCompiler: "vendor/typescript.js"
|
|
1259
2202
|
});
|
|
1260
2203
|
function isExplicitAssetPath(pathname) {
|
|
@@ -1288,6 +2231,7 @@ function resolveBrowserHarnessAssets(options = {}) {
|
|
|
1288
2231
|
assetBaseUrl,
|
|
1289
2232
|
assets.javascriptWorker ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.javascriptWorker
|
|
1290
2233
|
),
|
|
2234
|
+
javaWorker: resolveAssetPath(assetBaseUrl, assets.javaWorker ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.javaWorker),
|
|
1291
2235
|
typescriptCompiler: resolveAssetPath(
|
|
1292
2236
|
assetBaseUrl,
|
|
1293
2237
|
assets.typescriptCompiler ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.typescriptCompiler
|
|
@@ -1301,6 +2245,7 @@ var BrowserHarnessRuntime = class {
|
|
|
1301
2245
|
supportedLanguages = SUPPORTED_LANGUAGES;
|
|
1302
2246
|
pythonWorkerClient;
|
|
1303
2247
|
javaScriptWorkerClient;
|
|
2248
|
+
javaWorkerClient;
|
|
1304
2249
|
clients;
|
|
1305
2250
|
constructor(options = {}) {
|
|
1306
2251
|
this.assets = resolveBrowserHarnessAssets(options);
|
|
@@ -1312,10 +2257,15 @@ var BrowserHarnessRuntime = class {
|
|
|
1312
2257
|
workerUrl: this.assets.javascriptWorker,
|
|
1313
2258
|
debug: options.debug
|
|
1314
2259
|
});
|
|
2260
|
+
this.javaWorkerClient = new JavaWorkerClient({
|
|
2261
|
+
workerUrl: this.assets.javaWorker,
|
|
2262
|
+
debug: options.debug
|
|
2263
|
+
});
|
|
1315
2264
|
this.clients = {
|
|
1316
2265
|
python: createPythonRuntimeClient(this.pythonWorkerClient),
|
|
1317
2266
|
javascript: createJavaScriptRuntimeClient("javascript", this.javaScriptWorkerClient),
|
|
1318
|
-
typescript: createJavaScriptRuntimeClient("typescript", this.javaScriptWorkerClient)
|
|
2267
|
+
typescript: createJavaScriptRuntimeClient("typescript", this.javaScriptWorkerClient),
|
|
2268
|
+
java: createJavaRuntimeClient(this.javaWorkerClient)
|
|
1319
2269
|
};
|
|
1320
2270
|
}
|
|
1321
2271
|
getClient(language) {
|
|
@@ -1339,11 +2289,16 @@ var BrowserHarnessRuntime = class {
|
|
|
1339
2289
|
this.pythonWorkerClient.terminate();
|
|
1340
2290
|
return;
|
|
1341
2291
|
}
|
|
2292
|
+
if (language === "java") {
|
|
2293
|
+
this.javaWorkerClient.terminate();
|
|
2294
|
+
return;
|
|
2295
|
+
}
|
|
1342
2296
|
this.javaScriptWorkerClient.terminate();
|
|
1343
2297
|
}
|
|
1344
2298
|
dispose() {
|
|
1345
2299
|
this.pythonWorkerClient.terminate();
|
|
1346
2300
|
this.javaScriptWorkerClient.terminate();
|
|
2301
|
+
this.javaWorkerClient.terminate();
|
|
1347
2302
|
}
|
|
1348
2303
|
};
|
|
1349
2304
|
function createBrowserHarness(options = {}) {
|