@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/browser.js
CHANGED
|
@@ -255,7 +255,7 @@ var JavaScriptWorkerClient = class {
|
|
|
255
255
|
};
|
|
256
256
|
|
|
257
257
|
// packages/harness-core/src/trace-contract.ts
|
|
258
|
-
var RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION = "2026-
|
|
258
|
+
var RUNTIME_TRACE_CONTRACT_SCHEMA_VERSION = "2026-04-21";
|
|
259
259
|
var TRACE_EVENTS = /* @__PURE__ */ new Set([
|
|
260
260
|
"line",
|
|
261
261
|
"call",
|
|
@@ -376,7 +376,39 @@ function normalizeAccesses(accesses) {
|
|
|
376
376
|
}
|
|
377
377
|
return payload;
|
|
378
378
|
}).filter((access) => access !== null);
|
|
379
|
-
|
|
379
|
+
if (normalized.length === 0) {
|
|
380
|
+
return void 0;
|
|
381
|
+
}
|
|
382
|
+
const statsByVariable = /* @__PURE__ */ new Map();
|
|
383
|
+
for (const access of normalized) {
|
|
384
|
+
const stats = statsByVariable.get(access.variable) ?? { hasCellRead: false, hasCellWrite: false };
|
|
385
|
+
if (access.kind === "cell-read") stats.hasCellRead = true;
|
|
386
|
+
if (access.kind === "cell-write") stats.hasCellWrite = true;
|
|
387
|
+
statsByVariable.set(access.variable, stats);
|
|
388
|
+
}
|
|
389
|
+
const deduped = /* @__PURE__ */ new Set();
|
|
390
|
+
const collapsed = normalized.filter((access) => {
|
|
391
|
+
const stats = statsByVariable.get(access.variable);
|
|
392
|
+
if (access.kind === "indexed-read" && (stats?.hasCellRead || stats?.hasCellWrite)) {
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
if (access.kind === "indexed-write" && stats?.hasCellWrite) {
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
const key = JSON.stringify([
|
|
399
|
+
access.variable,
|
|
400
|
+
access.kind,
|
|
401
|
+
access.indices ?? null,
|
|
402
|
+
access.pathDepth ?? null,
|
|
403
|
+
access.method ?? null
|
|
404
|
+
]);
|
|
405
|
+
if (deduped.has(key)) {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
deduped.add(key);
|
|
409
|
+
return true;
|
|
410
|
+
});
|
|
411
|
+
return collapsed.length > 0 ? collapsed : void 0;
|
|
380
412
|
}
|
|
381
413
|
function normalizeRecord(value) {
|
|
382
414
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
@@ -440,8 +472,64 @@ function normalizeTraceStep(step) {
|
|
|
440
472
|
...normalizedVisualization ? { visualization: normalizedVisualization } : {}
|
|
441
473
|
};
|
|
442
474
|
}
|
|
475
|
+
function collapseTraceAccessNoise(trace) {
|
|
476
|
+
const variablesWithCellAccess = /* @__PURE__ */ new Set();
|
|
477
|
+
const accessStatsByVariable = /* @__PURE__ */ new Map();
|
|
478
|
+
for (const step of trace) {
|
|
479
|
+
for (const access of step.accesses ?? []) {
|
|
480
|
+
const stats = accessStatsByVariable.get(access.variable) ?? {
|
|
481
|
+
hasCellRead: false,
|
|
482
|
+
hasCellWrite: false,
|
|
483
|
+
hasMutatingCall: false,
|
|
484
|
+
hasIndexedWrite: false
|
|
485
|
+
};
|
|
486
|
+
if (access.kind === "cell-read" || access.kind === "cell-write") {
|
|
487
|
+
variablesWithCellAccess.add(access.variable);
|
|
488
|
+
}
|
|
489
|
+
if (access.kind === "cell-read") stats.hasCellRead = true;
|
|
490
|
+
if (access.kind === "cell-write") stats.hasCellWrite = true;
|
|
491
|
+
if (access.kind === "mutating-call") stats.hasMutatingCall = true;
|
|
492
|
+
if (access.kind === "indexed-write") stats.hasIndexedWrite = true;
|
|
493
|
+
accessStatsByVariable.set(access.variable, stats);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
if (variablesWithCellAccess.size === 0) {
|
|
497
|
+
return trace;
|
|
498
|
+
}
|
|
499
|
+
return trace.map((step) => {
|
|
500
|
+
if (!step.accesses?.length) {
|
|
501
|
+
return step;
|
|
502
|
+
}
|
|
503
|
+
const filtered = step.accesses.filter((access) => {
|
|
504
|
+
const stats = accessStatsByVariable.get(access.variable);
|
|
505
|
+
if (variablesWithCellAccess.has(access.variable)) {
|
|
506
|
+
return access.kind !== "indexed-read" && access.kind !== "indexed-write";
|
|
507
|
+
}
|
|
508
|
+
if (access.kind === "mutating-call" && stats?.hasIndexedWrite && !stats.hasCellRead && !stats.hasCellWrite) {
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
if (access.kind === "indexed-read" && stats?.hasMutatingCall && !stats.hasIndexedWrite && !stats.hasCellRead && !stats.hasCellWrite) {
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
return true;
|
|
515
|
+
});
|
|
516
|
+
if (filtered.length === step.accesses.length) {
|
|
517
|
+
return step;
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
...step,
|
|
521
|
+
...filtered.length > 0 ? { accesses: filtered } : {},
|
|
522
|
+
...filtered.length === 0 ? { accesses: void 0 } : {}
|
|
523
|
+
};
|
|
524
|
+
});
|
|
525
|
+
}
|
|
443
526
|
function normalizeRuntimeTraceContract(language, result) {
|
|
444
|
-
const
|
|
527
|
+
const rawNormalizedTrace = collapseTraceAccessNoise(
|
|
528
|
+
Array.isArray(result.trace) ? result.trace.map(normalizeTraceStep) : []
|
|
529
|
+
);
|
|
530
|
+
const maxTraceSteps = typeof result.maxTraceSteps === "number" && Number.isFinite(result.maxTraceSteps) ? Math.max(1, Math.floor(result.maxTraceSteps)) : void 0;
|
|
531
|
+
const traceWasClipped = maxTraceSteps !== void 0 && rawNormalizedTrace.length > maxTraceSteps;
|
|
532
|
+
const normalizedTrace = traceWasClipped && maxTraceSteps !== void 0 ? rawNormalizedTrace.slice(0, maxTraceSteps) : rawNormalizedTrace;
|
|
445
533
|
const lineEventCount = typeof result.lineEventCount === "number" && Number.isFinite(result.lineEventCount) ? Math.floor(result.lineEventCount) : normalizedTrace.filter((step) => step.event === "line").length;
|
|
446
534
|
const normalizedConsole = Array.isArray(result.consoleOutput) ? result.consoleOutput.map((line) => String(line)) : [];
|
|
447
535
|
return {
|
|
@@ -453,7 +541,7 @@ function normalizeRuntimeTraceContract(language, result) {
|
|
|
453
541
|
...typeof result.errorLine === "number" && Number.isFinite(result.errorLine) ? { errorLine: Math.floor(result.errorLine) } : {},
|
|
454
542
|
consoleOutput: normalizedConsole,
|
|
455
543
|
trace: normalizedTrace,
|
|
456
|
-
...result.traceLimitExceeded !== void 0 ? { traceLimitExceeded: Boolean(result.traceLimitExceeded) } : {},
|
|
544
|
+
...result.traceLimitExceeded !== void 0 || traceWasClipped ? { traceLimitExceeded: Boolean(result.traceLimitExceeded) || traceWasClipped } : {},
|
|
457
545
|
...typeof result.timeoutReason === "string" && result.timeoutReason.length > 0 ? { timeoutReason: result.timeoutReason } : {},
|
|
458
546
|
lineEventCount: Math.max(0, lineEventCount),
|
|
459
547
|
traceStepCount: normalizedTrace.length
|
|
@@ -574,6 +662,7 @@ var PYTHON_RUNTIME_PROFILE = {
|
|
|
574
662
|
maxTraceSteps: true,
|
|
575
663
|
maxLineEvents: true,
|
|
576
664
|
maxSingleLineHits: true,
|
|
665
|
+
maxStoredEvents: true,
|
|
577
666
|
minimalTrace: true
|
|
578
667
|
},
|
|
579
668
|
fidelity: {
|
|
@@ -635,6 +724,7 @@ var JAVASCRIPT_RUNTIME_PROFILE = {
|
|
|
635
724
|
maxTraceSteps: true,
|
|
636
725
|
maxLineEvents: true,
|
|
637
726
|
maxSingleLineHits: true,
|
|
727
|
+
maxStoredEvents: true,
|
|
638
728
|
minimalTrace: true
|
|
639
729
|
},
|
|
640
730
|
fidelity: {
|
|
@@ -696,6 +786,7 @@ var TYPESCRIPT_RUNTIME_PROFILE = {
|
|
|
696
786
|
maxTraceSteps: true,
|
|
697
787
|
maxLineEvents: true,
|
|
698
788
|
maxSingleLineHits: true,
|
|
789
|
+
maxStoredEvents: true,
|
|
699
790
|
minimalTrace: true
|
|
700
791
|
},
|
|
701
792
|
fidelity: {
|
|
@@ -726,10 +817,78 @@ var TYPESCRIPT_RUNTIME_PROFILE = {
|
|
|
726
817
|
}
|
|
727
818
|
}
|
|
728
819
|
};
|
|
820
|
+
var JAVA_RUNTIME_PROFILE = {
|
|
821
|
+
language: "java",
|
|
822
|
+
maturity: "experimental",
|
|
823
|
+
capabilities: {
|
|
824
|
+
execution: {
|
|
825
|
+
styles: {
|
|
826
|
+
function: true,
|
|
827
|
+
solutionMethod: true,
|
|
828
|
+
opsClass: true,
|
|
829
|
+
script: false,
|
|
830
|
+
interviewMode: true
|
|
831
|
+
},
|
|
832
|
+
timeouts: {
|
|
833
|
+
clientTimeouts: true,
|
|
834
|
+
runtimeTimeouts: true
|
|
835
|
+
}
|
|
836
|
+
},
|
|
837
|
+
tracing: {
|
|
838
|
+
supported: true,
|
|
839
|
+
events: {
|
|
840
|
+
line: true,
|
|
841
|
+
call: true,
|
|
842
|
+
return: true,
|
|
843
|
+
exception: true,
|
|
844
|
+
stdout: false,
|
|
845
|
+
timeout: true
|
|
846
|
+
},
|
|
847
|
+
controls: {
|
|
848
|
+
maxTraceSteps: true,
|
|
849
|
+
maxLineEvents: false,
|
|
850
|
+
maxSingleLineHits: false,
|
|
851
|
+
maxStoredEvents: true,
|
|
852
|
+
minimalTrace: false
|
|
853
|
+
},
|
|
854
|
+
fidelity: {
|
|
855
|
+
preciseLineMapping: true,
|
|
856
|
+
stableFunctionNames: true,
|
|
857
|
+
callStack: true
|
|
858
|
+
}
|
|
859
|
+
},
|
|
860
|
+
diagnostics: {
|
|
861
|
+
compileErrors: true,
|
|
862
|
+
runtimeErrors: true,
|
|
863
|
+
mappedErrorLines: false,
|
|
864
|
+
stackTraces: true
|
|
865
|
+
},
|
|
866
|
+
structures: {
|
|
867
|
+
treeNodeRefs: true,
|
|
868
|
+
listNodeRefs: true,
|
|
869
|
+
mapSerialization: true,
|
|
870
|
+
setSerialization: true,
|
|
871
|
+
graphSerialization: false,
|
|
872
|
+
cycleReferences: true
|
|
873
|
+
},
|
|
874
|
+
visualization: {
|
|
875
|
+
runtimePayloads: true,
|
|
876
|
+
objectKinds: true,
|
|
877
|
+
hashMaps: true,
|
|
878
|
+
stepVisualization: true
|
|
879
|
+
}
|
|
880
|
+
},
|
|
881
|
+
notes: [
|
|
882
|
+
"Java currently supports the browser-local Java 17 lane for function, solution-method, and ops-class execution.",
|
|
883
|
+
"Interview-mode Java reuses the same browser-local execution path and remains experimental in the first harness slice.",
|
|
884
|
+
"Script-style Java is not enabled yet."
|
|
885
|
+
]
|
|
886
|
+
};
|
|
729
887
|
var LANGUAGE_RUNTIME_PROFILES = {
|
|
730
888
|
python: PYTHON_RUNTIME_PROFILE,
|
|
731
889
|
javascript: JAVASCRIPT_RUNTIME_PROFILE,
|
|
732
|
-
typescript: TYPESCRIPT_RUNTIME_PROFILE
|
|
890
|
+
typescript: TYPESCRIPT_RUNTIME_PROFILE,
|
|
891
|
+
java: JAVA_RUNTIME_PROFILE
|
|
733
892
|
};
|
|
734
893
|
var SUPPORTED_LANGUAGES = Object.freeze(
|
|
735
894
|
Object.keys(LANGUAGE_RUNTIME_PROFILES)
|
|
@@ -806,13 +965,796 @@ function createJavaScriptRuntimeClient(runtimeLanguage, workerClient) {
|
|
|
806
965
|
return new JavaScriptRuntimeClient(runtimeLanguage, workerClient);
|
|
807
966
|
}
|
|
808
967
|
|
|
968
|
+
// packages/harness-browser/src/java-worker-client.ts
|
|
969
|
+
var TRACING_TIMEOUT_MS2 = 25e3;
|
|
970
|
+
var INIT_TIMEOUT_MS2 = 25e3;
|
|
971
|
+
var MESSAGE_TIMEOUT_MS2 = 3e4;
|
|
972
|
+
var WORKER_READY_TIMEOUT_MS2 = 1e4;
|
|
973
|
+
var JavaWorkerClient = class {
|
|
974
|
+
constructor(options) {
|
|
975
|
+
this.options = options;
|
|
976
|
+
this.debug = options.debug ?? process.env.NODE_ENV === "development";
|
|
977
|
+
}
|
|
978
|
+
worker = null;
|
|
979
|
+
pendingMessages = /* @__PURE__ */ new Map();
|
|
980
|
+
messageId = 0;
|
|
981
|
+
isInitializing = false;
|
|
982
|
+
initPromise = null;
|
|
983
|
+
workerReadyPromise = null;
|
|
984
|
+
workerReadyResolve = null;
|
|
985
|
+
workerReadyReject = null;
|
|
986
|
+
debug;
|
|
987
|
+
isSupported() {
|
|
988
|
+
return typeof Worker !== "undefined";
|
|
989
|
+
}
|
|
990
|
+
getWorker() {
|
|
991
|
+
if (this.worker) return this.worker;
|
|
992
|
+
if (!this.isSupported()) {
|
|
993
|
+
throw new Error("Web Workers are not supported in this environment");
|
|
994
|
+
}
|
|
995
|
+
this.workerReadyPromise = new Promise((resolve, reject) => {
|
|
996
|
+
this.workerReadyResolve = resolve;
|
|
997
|
+
this.workerReadyReject = (error) => reject(error);
|
|
998
|
+
});
|
|
999
|
+
const workerUrl = this.debug && !this.options.workerUrl.includes("?") ? `${this.options.workerUrl}?dev=${Date.now()}` : this.options.workerUrl;
|
|
1000
|
+
this.worker = new Worker(workerUrl);
|
|
1001
|
+
this.worker.onmessage = (event) => {
|
|
1002
|
+
const { id, type, payload } = event.data;
|
|
1003
|
+
if (type === "worker-ready") {
|
|
1004
|
+
this.workerReadyResolve?.();
|
|
1005
|
+
this.workerReadyResolve = null;
|
|
1006
|
+
this.workerReadyReject = null;
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
if (!id) return;
|
|
1010
|
+
const pending = this.pendingMessages.get(id);
|
|
1011
|
+
if (!pending) return;
|
|
1012
|
+
this.pendingMessages.delete(id);
|
|
1013
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
1014
|
+
if (type === "error") {
|
|
1015
|
+
pending.reject(new Error(payload.error));
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
pending.resolve(payload);
|
|
1019
|
+
};
|
|
1020
|
+
this.worker.onerror = (error) => {
|
|
1021
|
+
const workerError = new Error(error.message || "Java worker error");
|
|
1022
|
+
this.workerReadyReject?.(workerError);
|
|
1023
|
+
this.workerReadyResolve = null;
|
|
1024
|
+
this.workerReadyReject = null;
|
|
1025
|
+
for (const [, pending] of this.pendingMessages) {
|
|
1026
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
1027
|
+
pending.reject(workerError);
|
|
1028
|
+
}
|
|
1029
|
+
this.pendingMessages.clear();
|
|
1030
|
+
this.terminateAndReset(workerError);
|
|
1031
|
+
};
|
|
1032
|
+
return this.worker;
|
|
1033
|
+
}
|
|
1034
|
+
async waitForWorkerReady() {
|
|
1035
|
+
const readyPromise = this.workerReadyPromise;
|
|
1036
|
+
if (!readyPromise) return;
|
|
1037
|
+
await new Promise((resolve, reject) => {
|
|
1038
|
+
let settled = false;
|
|
1039
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
1040
|
+
if (settled) return;
|
|
1041
|
+
settled = true;
|
|
1042
|
+
const timeoutError = new Error(
|
|
1043
|
+
`Java worker failed to initialize in time (${Math.round(WORKER_READY_TIMEOUT_MS2 / 1e3)}s)`
|
|
1044
|
+
);
|
|
1045
|
+
this.terminateAndReset(timeoutError);
|
|
1046
|
+
reject(timeoutError);
|
|
1047
|
+
}, WORKER_READY_TIMEOUT_MS2);
|
|
1048
|
+
readyPromise.then(() => {
|
|
1049
|
+
if (settled) return;
|
|
1050
|
+
settled = true;
|
|
1051
|
+
globalThis.clearTimeout(timeoutId);
|
|
1052
|
+
resolve();
|
|
1053
|
+
}).catch((error) => {
|
|
1054
|
+
if (settled) return;
|
|
1055
|
+
settled = true;
|
|
1056
|
+
globalThis.clearTimeout(timeoutId);
|
|
1057
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
1058
|
+
});
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
async sendMessage(type, payload, timeoutMs = MESSAGE_TIMEOUT_MS2) {
|
|
1062
|
+
const worker = this.getWorker();
|
|
1063
|
+
await this.waitForWorkerReady();
|
|
1064
|
+
const id = String(++this.messageId);
|
|
1065
|
+
return new Promise((resolve, reject) => {
|
|
1066
|
+
this.pendingMessages.set(id, {
|
|
1067
|
+
resolve,
|
|
1068
|
+
reject
|
|
1069
|
+
});
|
|
1070
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
1071
|
+
const pending2 = this.pendingMessages.get(id);
|
|
1072
|
+
if (!pending2) return;
|
|
1073
|
+
this.pendingMessages.delete(id);
|
|
1074
|
+
pending2.reject(new Error(`Worker request timed out: ${type}`));
|
|
1075
|
+
}, timeoutMs);
|
|
1076
|
+
const pending = this.pendingMessages.get(id);
|
|
1077
|
+
if (pending) pending.timeoutId = timeoutId;
|
|
1078
|
+
worker.postMessage({ id, type, payload });
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
async executeWithTimeout(executor, timeoutMs) {
|
|
1082
|
+
return new Promise((resolve, reject) => {
|
|
1083
|
+
let settled = false;
|
|
1084
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
1085
|
+
if (settled) return;
|
|
1086
|
+
settled = true;
|
|
1087
|
+
this.terminateAndReset();
|
|
1088
|
+
reject(
|
|
1089
|
+
new Error(
|
|
1090
|
+
`Java execution timed out after ${Math.round(timeoutMs / 1e3)} seconds.`
|
|
1091
|
+
)
|
|
1092
|
+
);
|
|
1093
|
+
}, timeoutMs);
|
|
1094
|
+
executor().then((result) => {
|
|
1095
|
+
if (settled) return;
|
|
1096
|
+
settled = true;
|
|
1097
|
+
globalThis.clearTimeout(timeoutId);
|
|
1098
|
+
resolve(result);
|
|
1099
|
+
}).catch((error) => {
|
|
1100
|
+
if (settled) return;
|
|
1101
|
+
settled = true;
|
|
1102
|
+
globalThis.clearTimeout(timeoutId);
|
|
1103
|
+
reject(error);
|
|
1104
|
+
});
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
terminateAndReset(reason = new Error("Worker was terminated")) {
|
|
1108
|
+
this.workerReadyReject?.(reason);
|
|
1109
|
+
if (this.worker) {
|
|
1110
|
+
this.worker.terminate();
|
|
1111
|
+
this.worker = null;
|
|
1112
|
+
}
|
|
1113
|
+
this.initPromise = null;
|
|
1114
|
+
this.isInitializing = false;
|
|
1115
|
+
this.workerReadyPromise = null;
|
|
1116
|
+
this.workerReadyResolve = null;
|
|
1117
|
+
this.workerReadyReject = null;
|
|
1118
|
+
for (const [, pending] of this.pendingMessages) {
|
|
1119
|
+
if (pending.timeoutId) globalThis.clearTimeout(pending.timeoutId);
|
|
1120
|
+
pending.reject(reason);
|
|
1121
|
+
}
|
|
1122
|
+
this.pendingMessages.clear();
|
|
1123
|
+
}
|
|
1124
|
+
async init() {
|
|
1125
|
+
if (this.initPromise) return this.initPromise;
|
|
1126
|
+
if (this.isInitializing) {
|
|
1127
|
+
await new Promise((resolve) => globalThis.setTimeout(resolve, 100));
|
|
1128
|
+
return this.init();
|
|
1129
|
+
}
|
|
1130
|
+
this.isInitializing = true;
|
|
1131
|
+
this.initPromise = this.sendMessage("init", void 0, INIT_TIMEOUT_MS2);
|
|
1132
|
+
try {
|
|
1133
|
+
return await this.initPromise;
|
|
1134
|
+
} catch (error) {
|
|
1135
|
+
this.initPromise = null;
|
|
1136
|
+
throw error;
|
|
1137
|
+
} finally {
|
|
1138
|
+
this.isInitializing = false;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
async executeWithTracing(code, functionName, inputs, options, executionStyle) {
|
|
1142
|
+
await this.init();
|
|
1143
|
+
return this.executeWithTimeout(
|
|
1144
|
+
() => this.sendMessage(
|
|
1145
|
+
"execute-with-tracing",
|
|
1146
|
+
{ code, functionName, inputs, options, executionStyle },
|
|
1147
|
+
TRACING_TIMEOUT_MS2 + 5e3
|
|
1148
|
+
),
|
|
1149
|
+
TRACING_TIMEOUT_MS2
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
async executeCode(code, functionName, inputs, options, executionStyle) {
|
|
1153
|
+
const result = await this.executeWithTracing(code, functionName, inputs, options, executionStyle);
|
|
1154
|
+
if (!result.success) {
|
|
1155
|
+
return {
|
|
1156
|
+
success: false,
|
|
1157
|
+
output: null,
|
|
1158
|
+
error: result.error ?? "Java execution failed",
|
|
1159
|
+
...result.errorLine !== void 0 ? { errorLine: result.errorLine } : {},
|
|
1160
|
+
consoleOutput: result.consoleOutput
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
return {
|
|
1164
|
+
success: true,
|
|
1165
|
+
output: result.output,
|
|
1166
|
+
consoleOutput: result.consoleOutput
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
async executeCodeInterviewMode(code, functionName, inputs, options, executionStyle) {
|
|
1170
|
+
return this.executeCode(code, functionName, inputs, options, executionStyle);
|
|
1171
|
+
}
|
|
1172
|
+
terminate() {
|
|
1173
|
+
this.terminateAndReset();
|
|
1174
|
+
}
|
|
1175
|
+
};
|
|
1176
|
+
|
|
1177
|
+
// packages/harness-core/src/trace-adapters/java.ts
|
|
1178
|
+
function parseScalar(raw) {
|
|
1179
|
+
if (raw === "null") return null;
|
|
1180
|
+
if (raw === "true") return true;
|
|
1181
|
+
if (raw === "false") return false;
|
|
1182
|
+
if (/^-?\d+$/.test(raw)) return Number.parseInt(raw, 10);
|
|
1183
|
+
if (/^-?\d+\.\d+$/.test(raw)) return Number.parseFloat(raw);
|
|
1184
|
+
if (raw.startsWith('"') && raw.endsWith('"') || raw.startsWith("[") && raw.endsWith("]") || raw.startsWith("{") && raw.endsWith("}")) {
|
|
1185
|
+
try {
|
|
1186
|
+
return JSON.parse(raw);
|
|
1187
|
+
} catch {
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
return raw;
|
|
1191
|
+
}
|
|
1192
|
+
function parseKeyValuePairs(fragment) {
|
|
1193
|
+
const variables = {};
|
|
1194
|
+
const matches = Array.from(fragment.matchAll(/\b([A-Za-z_][A-Za-z0-9_.]*)=/g));
|
|
1195
|
+
for (let index = 0; index < matches.length; index += 1) {
|
|
1196
|
+
const match = matches[index];
|
|
1197
|
+
const rawKey = match[1];
|
|
1198
|
+
const valueStart = (match.index ?? 0) + match[0].length;
|
|
1199
|
+
const valueEnd = index + 1 < matches.length ? matches[index + 1].index ?? fragment.length : fragment.length;
|
|
1200
|
+
const rawValue = fragment.slice(valueStart, valueEnd).trim();
|
|
1201
|
+
if (!rawKey || rawValue === void 0) continue;
|
|
1202
|
+
if (rawKey === "method") continue;
|
|
1203
|
+
variables[rawKey.replaceAll(".", "_")] = parseScalar(rawValue);
|
|
1204
|
+
}
|
|
1205
|
+
return variables;
|
|
1206
|
+
}
|
|
1207
|
+
function extractLineMetadata(event) {
|
|
1208
|
+
const match = event.match(/^line=(\d+)(?:\s+(.*))?$/);
|
|
1209
|
+
if (!match) {
|
|
1210
|
+
return { line: 1, payload: event };
|
|
1211
|
+
}
|
|
1212
|
+
return {
|
|
1213
|
+
line: Number.parseInt(match[1], 10),
|
|
1214
|
+
payload: match[2] ?? ""
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
function stripInlineComments(line, inBlockComment) {
|
|
1218
|
+
let result = "";
|
|
1219
|
+
let index = 0;
|
|
1220
|
+
let inBlock = inBlockComment;
|
|
1221
|
+
while (index < line.length) {
|
|
1222
|
+
const current = line[index];
|
|
1223
|
+
const next = index + 1 < line.length ? line[index + 1] : "";
|
|
1224
|
+
if (inBlock) {
|
|
1225
|
+
if (current === "*" && next === "/") {
|
|
1226
|
+
inBlock = false;
|
|
1227
|
+
index += 2;
|
|
1228
|
+
continue;
|
|
1229
|
+
}
|
|
1230
|
+
index += 1;
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
if (current === "/" && next === "*") {
|
|
1234
|
+
inBlock = true;
|
|
1235
|
+
index += 2;
|
|
1236
|
+
continue;
|
|
1237
|
+
}
|
|
1238
|
+
if (current === "/" && next === "/") {
|
|
1239
|
+
break;
|
|
1240
|
+
}
|
|
1241
|
+
result += current;
|
|
1242
|
+
index += 1;
|
|
1243
|
+
}
|
|
1244
|
+
return { text: result, inBlockComment: inBlock };
|
|
1245
|
+
}
|
|
1246
|
+
function isMethodDeclarationLine(line) {
|
|
1247
|
+
const trimmed = line.trim();
|
|
1248
|
+
if (!trimmed) return false;
|
|
1249
|
+
if (trimmed.startsWith("@")) return false;
|
|
1250
|
+
if (!trimmed.includes("(") || !trimmed.includes(")")) return false;
|
|
1251
|
+
if (trimmed.endsWith(";")) return false;
|
|
1252
|
+
if (trimmed.includes("->")) return false;
|
|
1253
|
+
if (/^(?:if|for|while|switch|catch|do|try|else|return|throw|new)\b/.test(trimmed)) {
|
|
1254
|
+
return false;
|
|
1255
|
+
}
|
|
1256
|
+
if (!/[A-Za-z_][A-Za-z0-9_]*\s*\([^{};]*\)/.test(trimmed)) {
|
|
1257
|
+
return false;
|
|
1258
|
+
}
|
|
1259
|
+
return /(?:\{\s*)?$/.test(trimmed);
|
|
1260
|
+
}
|
|
1261
|
+
function buildLineRemap(sourceText) {
|
|
1262
|
+
if (typeof sourceText !== "string" || sourceText.length === 0) {
|
|
1263
|
+
return null;
|
|
1264
|
+
}
|
|
1265
|
+
const lines = sourceText.split(/\r?\n/);
|
|
1266
|
+
const executable = /* @__PURE__ */ new Set();
|
|
1267
|
+
let inBlockComment = false;
|
|
1268
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1269
|
+
const { text, inBlockComment: nextInBlockComment } = stripInlineComments(lines[index] ?? "", inBlockComment);
|
|
1270
|
+
inBlockComment = nextInBlockComment;
|
|
1271
|
+
if (text.trim().length > 0) {
|
|
1272
|
+
executable.add(index + 1);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
if (executable.size === 0) {
|
|
1276
|
+
return null;
|
|
1277
|
+
}
|
|
1278
|
+
const nextExecutableAtOrAfter = new Array(lines.length + 2).fill(null);
|
|
1279
|
+
let nextExecutable = null;
|
|
1280
|
+
for (let line = lines.length; line >= 1; line -= 1) {
|
|
1281
|
+
if (executable.has(line)) {
|
|
1282
|
+
nextExecutable = line;
|
|
1283
|
+
}
|
|
1284
|
+
nextExecutableAtOrAfter[line] = nextExecutable;
|
|
1285
|
+
}
|
|
1286
|
+
const previousExecutableAtOrBefore = new Array(lines.length + 2).fill(null);
|
|
1287
|
+
let previousExecutable = null;
|
|
1288
|
+
for (let line = 1; line <= lines.length; line += 1) {
|
|
1289
|
+
if (executable.has(line)) {
|
|
1290
|
+
previousExecutable = line;
|
|
1291
|
+
}
|
|
1292
|
+
previousExecutableAtOrBefore[line] = previousExecutable;
|
|
1293
|
+
}
|
|
1294
|
+
const remap = /* @__PURE__ */ new Map();
|
|
1295
|
+
for (let line = 1; line <= lines.length; line += 1) {
|
|
1296
|
+
if (executable.has(line)) continue;
|
|
1297
|
+
const forward = nextExecutableAtOrAfter[line];
|
|
1298
|
+
const backward = previousExecutableAtOrBefore[line];
|
|
1299
|
+
const target = forward ?? backward;
|
|
1300
|
+
if (target !== null && target !== line) {
|
|
1301
|
+
remap.set(line, target);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
return remap;
|
|
1305
|
+
}
|
|
1306
|
+
function buildMethodDeclarationLineSet(sourceText) {
|
|
1307
|
+
if (typeof sourceText !== "string" || sourceText.length === 0) {
|
|
1308
|
+
return null;
|
|
1309
|
+
}
|
|
1310
|
+
const declarationLines = /* @__PURE__ */ new Set();
|
|
1311
|
+
const lines = sourceText.split(/\r?\n/);
|
|
1312
|
+
let inBlockComment = false;
|
|
1313
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1314
|
+
const { text, inBlockComment: nextInBlockComment } = stripInlineComments(lines[index] ?? "", inBlockComment);
|
|
1315
|
+
inBlockComment = nextInBlockComment;
|
|
1316
|
+
if (isMethodDeclarationLine(text)) {
|
|
1317
|
+
declarationLines.add(index + 1);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
return declarationLines;
|
|
1321
|
+
}
|
|
1322
|
+
function parseAccessEvent(payload) {
|
|
1323
|
+
const isEphemeralOutputArrayName = (name) => /^(output|outputs)$/i.test(name);
|
|
1324
|
+
const cellRead = payload.match(/^access ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]\[(\d+)\]=(.+)$/);
|
|
1325
|
+
if (cellRead) {
|
|
1326
|
+
return [{
|
|
1327
|
+
variable: cellRead[1],
|
|
1328
|
+
kind: "cell-read",
|
|
1329
|
+
indices: [Number.parseInt(cellRead[2], 10), Number.parseInt(cellRead[3], 10)],
|
|
1330
|
+
pathDepth: 2
|
|
1331
|
+
}];
|
|
1332
|
+
}
|
|
1333
|
+
const indexedRead = payload.match(/^access ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]=(.+)$/);
|
|
1334
|
+
if (indexedRead) {
|
|
1335
|
+
return [{
|
|
1336
|
+
variable: indexedRead[1],
|
|
1337
|
+
kind: "indexed-read",
|
|
1338
|
+
indices: [Number.parseInt(indexedRead[2], 10)],
|
|
1339
|
+
pathDepth: 1
|
|
1340
|
+
}];
|
|
1341
|
+
}
|
|
1342
|
+
const cellWrite = payload.match(/^write-array ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]\[(\d+)\]=(.+)$/);
|
|
1343
|
+
if (cellWrite) {
|
|
1344
|
+
return [{
|
|
1345
|
+
variable: cellWrite[1],
|
|
1346
|
+
kind: "cell-write",
|
|
1347
|
+
indices: [Number.parseInt(cellWrite[2], 10), Number.parseInt(cellWrite[3], 10)],
|
|
1348
|
+
pathDepth: 2
|
|
1349
|
+
}];
|
|
1350
|
+
}
|
|
1351
|
+
const indexedWrite = payload.match(/^write-array ([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]=(.+)$/);
|
|
1352
|
+
if (indexedWrite) {
|
|
1353
|
+
if (isEphemeralOutputArrayName(indexedWrite[1])) {
|
|
1354
|
+
return void 0;
|
|
1355
|
+
}
|
|
1356
|
+
return [{
|
|
1357
|
+
variable: indexedWrite[1],
|
|
1358
|
+
kind: "indexed-write",
|
|
1359
|
+
indices: [Number.parseInt(indexedWrite[2], 10)],
|
|
1360
|
+
pathDepth: 1
|
|
1361
|
+
}];
|
|
1362
|
+
}
|
|
1363
|
+
const mutatingCall = payload.match(/^mutate ([A-Za-z_][A-Za-z0-9_]*) method=([A-Za-z_][A-Za-z0-9_]*)$/);
|
|
1364
|
+
if (mutatingCall) {
|
|
1365
|
+
return [{
|
|
1366
|
+
variable: mutatingCall[1],
|
|
1367
|
+
kind: "mutating-call",
|
|
1368
|
+
method: mutatingCall[2],
|
|
1369
|
+
pathDepth: 1
|
|
1370
|
+
}];
|
|
1371
|
+
}
|
|
1372
|
+
return void 0;
|
|
1373
|
+
}
|
|
1374
|
+
function parseStructureState(payload) {
|
|
1375
|
+
const match = payload.match(/^state (linked-list|tree) ([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
|
|
1376
|
+
if (!match) return null;
|
|
1377
|
+
return {
|
|
1378
|
+
structure: match[1],
|
|
1379
|
+
variable: match[2],
|
|
1380
|
+
value: JSON.parse(match[3])
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
function parseObjectState(payload) {
|
|
1384
|
+
const match = payload.match(/^object-state ([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
|
|
1385
|
+
if (!match) return null;
|
|
1386
|
+
return {
|
|
1387
|
+
variable: match[1],
|
|
1388
|
+
visualization: JSON.parse(match[2])
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
function parseObjectFieldEvent(payload) {
|
|
1392
|
+
const match = payload.match(/^(access|write) ([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)=(.+)$/);
|
|
1393
|
+
if (!match) return null;
|
|
1394
|
+
return {
|
|
1395
|
+
variable: match[2],
|
|
1396
|
+
field: match[3],
|
|
1397
|
+
value: parseScalar(match[4])
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
function buildFieldVisualization(event) {
|
|
1401
|
+
return {
|
|
1402
|
+
objectKinds: {
|
|
1403
|
+
[event.variable]: "object"
|
|
1404
|
+
},
|
|
1405
|
+
hashMaps: [
|
|
1406
|
+
{
|
|
1407
|
+
name: event.variable,
|
|
1408
|
+
kind: "object",
|
|
1409
|
+
objectClassName: event.field === "next" || event.field === "prev" ? "ListNode" : event.field === "left" || event.field === "right" ? "TreeNode" : void 0,
|
|
1410
|
+
objectId: `${event.variable}-object`,
|
|
1411
|
+
highlightedKey: event.field,
|
|
1412
|
+
entries: [{ key: event.field, value: event.value, highlight: true }]
|
|
1413
|
+
}
|
|
1414
|
+
]
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
function callStacksEqual(left, right) {
|
|
1418
|
+
return JSON.stringify(left ?? []) === JSON.stringify(right ?? []);
|
|
1419
|
+
}
|
|
1420
|
+
function mergeVisualizationPayloads(left, right) {
|
|
1421
|
+
if (!left) return right;
|
|
1422
|
+
if (!right) return left;
|
|
1423
|
+
return {
|
|
1424
|
+
...left,
|
|
1425
|
+
...right,
|
|
1426
|
+
objectKinds: {
|
|
1427
|
+
...left.objectKinds ?? {},
|
|
1428
|
+
...right.objectKinds ?? {}
|
|
1429
|
+
},
|
|
1430
|
+
hashMaps: [
|
|
1431
|
+
...left.hashMaps ?? [],
|
|
1432
|
+
...right.hashMaps ?? []
|
|
1433
|
+
]
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
function maybeMergeConsecutiveLineStep(trace, nextStep) {
|
|
1437
|
+
if (nextStep.event !== "line") {
|
|
1438
|
+
return false;
|
|
1439
|
+
}
|
|
1440
|
+
const previous = trace.at(-1);
|
|
1441
|
+
if (!previous || previous.event !== "line") {
|
|
1442
|
+
return false;
|
|
1443
|
+
}
|
|
1444
|
+
if (previous.line !== nextStep.line || previous.function !== nextStep.function) {
|
|
1445
|
+
return false;
|
|
1446
|
+
}
|
|
1447
|
+
if (!callStacksEqual(previous.callStack, nextStep.callStack)) {
|
|
1448
|
+
return false;
|
|
1449
|
+
}
|
|
1450
|
+
previous.variables = { ...previous.variables ?? {}, ...nextStep.variables ?? {} };
|
|
1451
|
+
if (nextStep.accesses?.length) {
|
|
1452
|
+
previous.accesses = [...previous.accesses ?? [], ...nextStep.accesses];
|
|
1453
|
+
}
|
|
1454
|
+
previous.visualization = mergeVisualizationPayloads(previous.visualization, nextStep.visualization);
|
|
1455
|
+
return true;
|
|
1456
|
+
}
|
|
1457
|
+
function filterStructuredVariables(variables) {
|
|
1458
|
+
if (!variables) {
|
|
1459
|
+
return void 0;
|
|
1460
|
+
}
|
|
1461
|
+
const isEphemeralOutputArrayName = (name) => /^(output|outputs)$/i.test(name);
|
|
1462
|
+
const entries = Object.entries(variables).filter(([name, value]) => {
|
|
1463
|
+
if (value === null || typeof value !== "object") {
|
|
1464
|
+
return false;
|
|
1465
|
+
}
|
|
1466
|
+
if (!Array.isArray(value)) {
|
|
1467
|
+
return true;
|
|
1468
|
+
}
|
|
1469
|
+
if (Array.isArray(value[0])) {
|
|
1470
|
+
return true;
|
|
1471
|
+
}
|
|
1472
|
+
return !isEphemeralOutputArrayName(name);
|
|
1473
|
+
});
|
|
1474
|
+
if (entries.length === 0) {
|
|
1475
|
+
return void 0;
|
|
1476
|
+
}
|
|
1477
|
+
return Object.fromEntries(entries);
|
|
1478
|
+
}
|
|
1479
|
+
function appendJavaTraceStep(trace, step, pendingAccesses) {
|
|
1480
|
+
const nextStep = pendingAccesses.length > 0 ? { ...step, accesses: [...pendingAccesses] } : step;
|
|
1481
|
+
pendingAccesses.length = 0;
|
|
1482
|
+
if (!maybeMergeConsecutiveLineStep(trace, nextStep)) {
|
|
1483
|
+
trace.push(nextStep);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
function mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, patch) {
|
|
1487
|
+
const candidate = trace.at(-1);
|
|
1488
|
+
if (!candidate || candidate.event !== "line") {
|
|
1489
|
+
return false;
|
|
1490
|
+
}
|
|
1491
|
+
if (candidate.line !== line || candidate.function !== currentFunction) {
|
|
1492
|
+
return false;
|
|
1493
|
+
}
|
|
1494
|
+
if (!callStacksEqual(candidate.callStack, currentCallStack)) {
|
|
1495
|
+
return false;
|
|
1496
|
+
}
|
|
1497
|
+
candidate.variables = { ...candidate.variables ?? {}, ...patch.variables ?? {} };
|
|
1498
|
+
if (patch.accesses?.length) {
|
|
1499
|
+
candidate.accesses = [...candidate.accesses ?? [], ...patch.accesses];
|
|
1500
|
+
}
|
|
1501
|
+
candidate.visualization = mergeVisualizationPayloads(candidate.visualization, patch.visualization);
|
|
1502
|
+
return true;
|
|
1503
|
+
}
|
|
1504
|
+
function mergeAccessesIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, accesses) {
|
|
1505
|
+
return mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, {
|
|
1506
|
+
variables: void 0,
|
|
1507
|
+
accesses,
|
|
1508
|
+
visualization: void 0
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1511
|
+
function eventsToRawTrace(events, sourceText) {
|
|
1512
|
+
const trace = [];
|
|
1513
|
+
const variables = {};
|
|
1514
|
+
const objectKinds = {};
|
|
1515
|
+
const stack = [];
|
|
1516
|
+
const pendingAccesses = [];
|
|
1517
|
+
let currentFunction = "<module>";
|
|
1518
|
+
const lineRemap = buildLineRemap(sourceText);
|
|
1519
|
+
const declarationLines = buildMethodDeclarationLineSet(sourceText);
|
|
1520
|
+
for (const rawEvent of events) {
|
|
1521
|
+
if (rawEvent === "clear" || rawEvent === "reset") continue;
|
|
1522
|
+
const metadata = extractLineMetadata(rawEvent);
|
|
1523
|
+
const line = lineRemap?.get(metadata.line) ?? metadata.line;
|
|
1524
|
+
const payload = metadata.payload;
|
|
1525
|
+
const isDeclarationLine = declarationLines?.has(line) === true;
|
|
1526
|
+
if (payload.startsWith("call ")) {
|
|
1527
|
+
const match = payload.match(/^call\s+(\S+)(?:\s+(.*))?$/);
|
|
1528
|
+
const functionName = match?.[1] ?? currentFunction;
|
|
1529
|
+
const argsFragment = match?.[2] ?? "";
|
|
1530
|
+
Object.assign(variables, parseKeyValuePairs(argsFragment));
|
|
1531
|
+
currentFunction = functionName || currentFunction;
|
|
1532
|
+
stack.push({ function: currentFunction, line });
|
|
1533
|
+
appendJavaTraceStep(trace, {
|
|
1534
|
+
line,
|
|
1535
|
+
event: "call",
|
|
1536
|
+
function: currentFunction,
|
|
1537
|
+
variables: { ...variables },
|
|
1538
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} }))
|
|
1539
|
+
}, pendingAccesses);
|
|
1540
|
+
continue;
|
|
1541
|
+
}
|
|
1542
|
+
if (payload.startsWith("return ")) {
|
|
1543
|
+
const match = payload.match(/^return\s+(\S+)$/);
|
|
1544
|
+
const functionName = match?.[1] ?? currentFunction;
|
|
1545
|
+
const returnVariables = functionName === "<module>" ? { ...variables } : filterStructuredVariables(variables) ?? {};
|
|
1546
|
+
appendJavaTraceStep(trace, {
|
|
1547
|
+
line,
|
|
1548
|
+
event: "return",
|
|
1549
|
+
function: functionName || currentFunction,
|
|
1550
|
+
variables: returnVariables,
|
|
1551
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} }))
|
|
1552
|
+
}, pendingAccesses);
|
|
1553
|
+
stack.pop();
|
|
1554
|
+
currentFunction = stack[stack.length - 1]?.function ?? "<module>";
|
|
1555
|
+
continue;
|
|
1556
|
+
}
|
|
1557
|
+
if (isDeclarationLine) {
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
if (payload.length === 0) {
|
|
1561
|
+
appendJavaTraceStep(trace, {
|
|
1562
|
+
line,
|
|
1563
|
+
event: "line",
|
|
1564
|
+
function: currentFunction,
|
|
1565
|
+
variables: { ...variables },
|
|
1566
|
+
...stack.length > 0 ? { callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) } : {}
|
|
1567
|
+
}, pendingAccesses);
|
|
1568
|
+
continue;
|
|
1569
|
+
}
|
|
1570
|
+
const structureState = parseStructureState(payload);
|
|
1571
|
+
if (structureState) {
|
|
1572
|
+
variables[structureState.variable] = structureState.value;
|
|
1573
|
+
objectKinds[structureState.variable] = structureState.structure;
|
|
1574
|
+
appendJavaTraceStep(trace, {
|
|
1575
|
+
line,
|
|
1576
|
+
event: "line",
|
|
1577
|
+
function: currentFunction,
|
|
1578
|
+
variables: { ...variables },
|
|
1579
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
|
|
1580
|
+
visualization: {
|
|
1581
|
+
objectKinds: { ...objectKinds }
|
|
1582
|
+
}
|
|
1583
|
+
}, pendingAccesses);
|
|
1584
|
+
continue;
|
|
1585
|
+
}
|
|
1586
|
+
const objectState = parseObjectState(payload);
|
|
1587
|
+
if (objectState) {
|
|
1588
|
+
variables[objectState.variable] = { __ref__: objectState.visualization.objectId ?? `${objectState.variable}-object` };
|
|
1589
|
+
appendJavaTraceStep(trace, {
|
|
1590
|
+
line,
|
|
1591
|
+
event: "line",
|
|
1592
|
+
function: currentFunction,
|
|
1593
|
+
variables: { ...variables },
|
|
1594
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
|
|
1595
|
+
visualization: {
|
|
1596
|
+
objectKinds: { [objectState.variable]: "object" },
|
|
1597
|
+
hashMaps: [objectState.visualization]
|
|
1598
|
+
}
|
|
1599
|
+
}, pendingAccesses);
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
const objectField = parseObjectFieldEvent(payload);
|
|
1603
|
+
if (objectField) {
|
|
1604
|
+
variables[objectField.variable] = { __ref__: `${objectField.variable}-object` };
|
|
1605
|
+
const currentCallStack = stack.length > 0 ? stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) : void 0;
|
|
1606
|
+
if (mergeIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, {
|
|
1607
|
+
variables: { ...variables },
|
|
1608
|
+
accesses: void 0,
|
|
1609
|
+
visualization: buildFieldVisualization(objectField)
|
|
1610
|
+
})) {
|
|
1611
|
+
continue;
|
|
1612
|
+
}
|
|
1613
|
+
appendJavaTraceStep(trace, {
|
|
1614
|
+
line,
|
|
1615
|
+
event: "line",
|
|
1616
|
+
function: currentFunction,
|
|
1617
|
+
variables: { ...variables },
|
|
1618
|
+
callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })),
|
|
1619
|
+
visualization: buildFieldVisualization(objectField)
|
|
1620
|
+
}, pendingAccesses);
|
|
1621
|
+
continue;
|
|
1622
|
+
}
|
|
1623
|
+
Object.assign(variables, parseKeyValuePairs(payload));
|
|
1624
|
+
const accesses = parseAccessEvent(payload);
|
|
1625
|
+
if (accesses) {
|
|
1626
|
+
const currentCallStack = stack.length > 0 ? stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) : void 0;
|
|
1627
|
+
if (mergeAccessesIntoPreviousMatchingLineStep(trace, line, currentFunction, currentCallStack, accesses)) {
|
|
1628
|
+
continue;
|
|
1629
|
+
}
|
|
1630
|
+
pendingAccesses.push(...accesses);
|
|
1631
|
+
continue;
|
|
1632
|
+
}
|
|
1633
|
+
appendJavaTraceStep(trace, {
|
|
1634
|
+
line,
|
|
1635
|
+
event: "line",
|
|
1636
|
+
function: currentFunction,
|
|
1637
|
+
variables: { ...variables },
|
|
1638
|
+
...stack.length > 0 ? { callStack: stack.map((frame) => ({ function: frame.function, line: frame.line, args: {} })) } : {}
|
|
1639
|
+
}, pendingAccesses);
|
|
1640
|
+
}
|
|
1641
|
+
if (pendingAccesses.length > 0 && trace.length > 0) {
|
|
1642
|
+
const last = trace[trace.length - 1];
|
|
1643
|
+
last.accesses = [...last.accesses ?? [], ...pendingAccesses];
|
|
1644
|
+
}
|
|
1645
|
+
return trace;
|
|
1646
|
+
}
|
|
1647
|
+
function buildJavaExecutionResult(output, events, executionTimeMs = 0, traceLimitExceeded, timeoutReason, maxTraceSteps, sourceText) {
|
|
1648
|
+
const trace = eventsToRawTrace(events, sourceText);
|
|
1649
|
+
return {
|
|
1650
|
+
success: true,
|
|
1651
|
+
output,
|
|
1652
|
+
trace,
|
|
1653
|
+
executionTimeMs,
|
|
1654
|
+
consoleOutput: [],
|
|
1655
|
+
...traceLimitExceeded !== void 0 ? { traceLimitExceeded } : {},
|
|
1656
|
+
...maxTraceSteps !== void 0 ? { maxTraceSteps } : {},
|
|
1657
|
+
...timeoutReason ? { timeoutReason } : {},
|
|
1658
|
+
lineEventCount: trace.filter((step) => step.event === "line").length,
|
|
1659
|
+
traceStepCount: trace.length
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
function adaptJavaTraceExecutionResult(result) {
|
|
1663
|
+
return adaptTraceExecutionResult("java", result);
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// packages/harness-browser/src/java-runtime-client.ts
|
|
1667
|
+
var JavaRuntimeClient = class {
|
|
1668
|
+
constructor(workerClient) {
|
|
1669
|
+
this.workerClient = workerClient;
|
|
1670
|
+
}
|
|
1671
|
+
async init() {
|
|
1672
|
+
return this.workerClient.init();
|
|
1673
|
+
}
|
|
1674
|
+
async executeWithTracing(code, functionName, inputs, options, executionStyle = "function") {
|
|
1675
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
|
|
1676
|
+
request: "trace",
|
|
1677
|
+
executionStyle,
|
|
1678
|
+
functionName
|
|
1679
|
+
});
|
|
1680
|
+
const rawResult = await this.workerClient.executeWithTracing(
|
|
1681
|
+
code,
|
|
1682
|
+
functionName ?? "",
|
|
1683
|
+
inputs,
|
|
1684
|
+
options,
|
|
1685
|
+
executionStyle
|
|
1686
|
+
);
|
|
1687
|
+
if (!rawResult.success) {
|
|
1688
|
+
return {
|
|
1689
|
+
success: false,
|
|
1690
|
+
error: rawResult.error ?? "Java tracing failed",
|
|
1691
|
+
...rawResult.errorLine !== void 0 ? { errorLine: rawResult.errorLine } : {},
|
|
1692
|
+
trace: [],
|
|
1693
|
+
executionTimeMs: rawResult.executionTimeMs,
|
|
1694
|
+
consoleOutput: rawResult.consoleOutput,
|
|
1695
|
+
...rawResult.traceLimitExceeded !== void 0 ? { traceLimitExceeded: rawResult.traceLimitExceeded } : {},
|
|
1696
|
+
...rawResult.timeoutReason ? { timeoutReason: rawResult.timeoutReason } : {},
|
|
1697
|
+
lineEventCount: 0,
|
|
1698
|
+
traceStepCount: 0
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
const adapted = adaptJavaTraceExecutionResult(
|
|
1702
|
+
buildJavaExecutionResult(
|
|
1703
|
+
rawResult.output,
|
|
1704
|
+
rawResult.events,
|
|
1705
|
+
rawResult.executionTimeMs,
|
|
1706
|
+
rawResult.traceLimitExceeded,
|
|
1707
|
+
rawResult.timeoutReason,
|
|
1708
|
+
void 0,
|
|
1709
|
+
rawResult.sourceText
|
|
1710
|
+
)
|
|
1711
|
+
);
|
|
1712
|
+
return {
|
|
1713
|
+
...adapted,
|
|
1714
|
+
consoleOutput: rawResult.consoleOutput,
|
|
1715
|
+
executionTimeMs: rawResult.executionTimeMs
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
async executeCode(code, functionName, inputs, executionStyle = "function") {
|
|
1719
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
|
|
1720
|
+
request: "execute",
|
|
1721
|
+
executionStyle,
|
|
1722
|
+
functionName
|
|
1723
|
+
});
|
|
1724
|
+
return this.workerClient.executeCode(
|
|
1725
|
+
code,
|
|
1726
|
+
functionName,
|
|
1727
|
+
inputs,
|
|
1728
|
+
void 0,
|
|
1729
|
+
executionStyle
|
|
1730
|
+
);
|
|
1731
|
+
}
|
|
1732
|
+
async executeCodeInterviewMode(code, functionName, inputs, executionStyle = "function") {
|
|
1733
|
+
assertRuntimeRequestSupported(getLanguageRuntimeProfile("java"), {
|
|
1734
|
+
request: "interview",
|
|
1735
|
+
executionStyle,
|
|
1736
|
+
functionName
|
|
1737
|
+
});
|
|
1738
|
+
return this.workerClient.executeCodeInterviewMode(
|
|
1739
|
+
code,
|
|
1740
|
+
functionName,
|
|
1741
|
+
inputs,
|
|
1742
|
+
void 0,
|
|
1743
|
+
executionStyle
|
|
1744
|
+
);
|
|
1745
|
+
}
|
|
1746
|
+
};
|
|
1747
|
+
function createJavaRuntimeClient(workerClient) {
|
|
1748
|
+
return new JavaRuntimeClient(workerClient);
|
|
1749
|
+
}
|
|
1750
|
+
|
|
809
1751
|
// packages/harness-browser/src/pyodide-worker-client.ts
|
|
810
1752
|
var EXECUTION_TIMEOUT_MS2 = 1e4;
|
|
811
1753
|
var INTERVIEW_MODE_TIMEOUT_MS2 = 5e3;
|
|
812
|
-
var
|
|
813
|
-
var
|
|
814
|
-
var
|
|
815
|
-
var
|
|
1754
|
+
var TRACING_TIMEOUT_MS3 = 3e4;
|
|
1755
|
+
var INIT_TIMEOUT_MS3 = 12e4;
|
|
1756
|
+
var MESSAGE_TIMEOUT_MS3 = 2e4;
|
|
1757
|
+
var WORKER_READY_TIMEOUT_MS3 = 1e4;
|
|
816
1758
|
var PyodideWorkerClient = class {
|
|
817
1759
|
constructor(options) {
|
|
818
1760
|
this.options = options;
|
|
@@ -902,14 +1844,14 @@ var PyodideWorkerClient = class {
|
|
|
902
1844
|
if (settled) return;
|
|
903
1845
|
settled = true;
|
|
904
1846
|
const timeoutError = new Error(
|
|
905
|
-
`Python worker failed to initialize in time (${Math.round(
|
|
1847
|
+
`Python worker failed to initialize in time (${Math.round(WORKER_READY_TIMEOUT_MS3 / 1e3)}s)`
|
|
906
1848
|
);
|
|
907
1849
|
if (this.debug) {
|
|
908
|
-
console.warn("[PyodideWorkerClient] worker-ready timeout", { timeoutMs:
|
|
1850
|
+
console.warn("[PyodideWorkerClient] worker-ready timeout", { timeoutMs: WORKER_READY_TIMEOUT_MS3 });
|
|
909
1851
|
}
|
|
910
1852
|
this.terminateAndReset(timeoutError);
|
|
911
1853
|
reject(timeoutError);
|
|
912
|
-
},
|
|
1854
|
+
}, WORKER_READY_TIMEOUT_MS3);
|
|
913
1855
|
readyPromise.then(() => {
|
|
914
1856
|
if (settled) return;
|
|
915
1857
|
settled = true;
|
|
@@ -926,7 +1868,7 @@ var PyodideWorkerClient = class {
|
|
|
926
1868
|
/**
|
|
927
1869
|
* Send a message to the worker and wait for a response
|
|
928
1870
|
*/
|
|
929
|
-
async sendMessage(type, payload, timeoutMs =
|
|
1871
|
+
async sendMessage(type, payload, timeoutMs = MESSAGE_TIMEOUT_MS3) {
|
|
930
1872
|
const worker = this.getWorker();
|
|
931
1873
|
await this.waitForWorkerReady();
|
|
932
1874
|
const id = String(++this.messageId);
|
|
@@ -1010,7 +1952,7 @@ var PyodideWorkerClient = class {
|
|
|
1010
1952
|
this.isInitializing = true;
|
|
1011
1953
|
this.initPromise = (async () => {
|
|
1012
1954
|
try {
|
|
1013
|
-
return await this.sendMessage("init", void 0,
|
|
1955
|
+
return await this.sendMessage("init", void 0, INIT_TIMEOUT_MS3);
|
|
1014
1956
|
} catch (error) {
|
|
1015
1957
|
const message = error instanceof Error ? error.message : String(error);
|
|
1016
1958
|
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");
|
|
@@ -1021,7 +1963,7 @@ var PyodideWorkerClient = class {
|
|
|
1021
1963
|
console.warn("[PyodideWorkerClient] init failed, resetting worker and retrying once", { message });
|
|
1022
1964
|
}
|
|
1023
1965
|
this.terminateAndReset();
|
|
1024
|
-
return this.sendMessage("init", void 0,
|
|
1966
|
+
return this.sendMessage("init", void 0, INIT_TIMEOUT_MS3);
|
|
1025
1967
|
}
|
|
1026
1968
|
})();
|
|
1027
1969
|
try {
|
|
@@ -1048,9 +1990,9 @@ var PyodideWorkerClient = class {
|
|
|
1048
1990
|
inputs,
|
|
1049
1991
|
executionStyle,
|
|
1050
1992
|
options
|
|
1051
|
-
},
|
|
1993
|
+
}, TRACING_TIMEOUT_MS3 + 5e3),
|
|
1052
1994
|
// Message timeout slightly longer than execution timeout
|
|
1053
|
-
|
|
1995
|
+
TRACING_TIMEOUT_MS3
|
|
1054
1996
|
);
|
|
1055
1997
|
} catch (error) {
|
|
1056
1998
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -1060,7 +2002,7 @@ var PyodideWorkerClient = class {
|
|
|
1060
2002
|
success: false,
|
|
1061
2003
|
error: errorMessage,
|
|
1062
2004
|
trace: [],
|
|
1063
|
-
executionTimeMs:
|
|
2005
|
+
executionTimeMs: TRACING_TIMEOUT_MS3,
|
|
1064
2006
|
consoleOutput: [],
|
|
1065
2007
|
traceLimitExceeded: true,
|
|
1066
2008
|
timeoutReason: "client-timeout",
|
|
@@ -1221,6 +2163,7 @@ var DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS = Object.freeze({
|
|
|
1221
2163
|
pythonRuntimeCore: "pyodide/runtime-core.js",
|
|
1222
2164
|
pythonSnippets: "generated-python-harness-snippets.js",
|
|
1223
2165
|
javascriptWorker: "javascript-worker.js",
|
|
2166
|
+
javaWorker: "java-worker.js",
|
|
1224
2167
|
typescriptCompiler: "vendor/typescript.js"
|
|
1225
2168
|
});
|
|
1226
2169
|
function isExplicitAssetPath(pathname) {
|
|
@@ -1254,6 +2197,7 @@ function resolveBrowserHarnessAssets(options = {}) {
|
|
|
1254
2197
|
assetBaseUrl,
|
|
1255
2198
|
assets.javascriptWorker ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.javascriptWorker
|
|
1256
2199
|
),
|
|
2200
|
+
javaWorker: resolveAssetPath(assetBaseUrl, assets.javaWorker ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.javaWorker),
|
|
1257
2201
|
typescriptCompiler: resolveAssetPath(
|
|
1258
2202
|
assetBaseUrl,
|
|
1259
2203
|
assets.typescriptCompiler ?? DEFAULT_BROWSER_HARNESS_ASSET_RELATIVE_PATHS.typescriptCompiler
|
|
@@ -1267,6 +2211,7 @@ var BrowserHarnessRuntime = class {
|
|
|
1267
2211
|
supportedLanguages = SUPPORTED_LANGUAGES;
|
|
1268
2212
|
pythonWorkerClient;
|
|
1269
2213
|
javaScriptWorkerClient;
|
|
2214
|
+
javaWorkerClient;
|
|
1270
2215
|
clients;
|
|
1271
2216
|
constructor(options = {}) {
|
|
1272
2217
|
this.assets = resolveBrowserHarnessAssets(options);
|
|
@@ -1278,10 +2223,15 @@ var BrowserHarnessRuntime = class {
|
|
|
1278
2223
|
workerUrl: this.assets.javascriptWorker,
|
|
1279
2224
|
debug: options.debug
|
|
1280
2225
|
});
|
|
2226
|
+
this.javaWorkerClient = new JavaWorkerClient({
|
|
2227
|
+
workerUrl: this.assets.javaWorker,
|
|
2228
|
+
debug: options.debug
|
|
2229
|
+
});
|
|
1281
2230
|
this.clients = {
|
|
1282
2231
|
python: createPythonRuntimeClient(this.pythonWorkerClient),
|
|
1283
2232
|
javascript: createJavaScriptRuntimeClient("javascript", this.javaScriptWorkerClient),
|
|
1284
|
-
typescript: createJavaScriptRuntimeClient("typescript", this.javaScriptWorkerClient)
|
|
2233
|
+
typescript: createJavaScriptRuntimeClient("typescript", this.javaScriptWorkerClient),
|
|
2234
|
+
java: createJavaRuntimeClient(this.javaWorkerClient)
|
|
1285
2235
|
};
|
|
1286
2236
|
}
|
|
1287
2237
|
getClient(language) {
|
|
@@ -1305,11 +2255,16 @@ var BrowserHarnessRuntime = class {
|
|
|
1305
2255
|
this.pythonWorkerClient.terminate();
|
|
1306
2256
|
return;
|
|
1307
2257
|
}
|
|
2258
|
+
if (language === "java") {
|
|
2259
|
+
this.javaWorkerClient.terminate();
|
|
2260
|
+
return;
|
|
2261
|
+
}
|
|
1308
2262
|
this.javaScriptWorkerClient.terminate();
|
|
1309
2263
|
}
|
|
1310
2264
|
dispose() {
|
|
1311
2265
|
this.pythonWorkerClient.terminate();
|
|
1312
2266
|
this.javaScriptWorkerClient.terminate();
|
|
2267
|
+
this.javaWorkerClient.terminate();
|
|
1313
2268
|
}
|
|
1314
2269
|
};
|
|
1315
2270
|
function createBrowserHarness(options = {}) {
|