@tracecode/harness 0.6.2 → 0.6.6
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/dist/browser.cjs +121 -28
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +121 -28
- package/dist/browser.js.map +1 -1
- package/dist/cli.cjs +4 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/core.cjs +121 -27
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +5 -2
- package/dist/core.d.ts +5 -2
- package/dist/core.js +120 -27
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +124 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +123 -28
- package/dist/index.js.map +1 -1
- package/dist/python.cjs +1 -0
- package/dist/python.cjs.map +1 -1
- package/dist/python.js +1 -0
- package/dist/python.js.map +1 -1
- package/package.json +5 -3
- package/workers/java/java-source-augmentations.cjs +242 -0
- package/workers/java/java-worker.js +856 -34
- package/workers/java/src/spike/user/TraceHooks.java +619 -0
- package/workers/python/runtime-core.js +78 -16
- package/workers/vendor/java-browser-spike-helper.jar +0 -0
|
@@ -16,6 +16,10 @@ const DEFAULT_MAX_STORED_EVENTS = 50_000;
|
|
|
16
16
|
const IDLE_TIMEOUT_MS = 90_000;
|
|
17
17
|
const SCRIPT_METHOD_NAME = '__tracecodeScript';
|
|
18
18
|
|
|
19
|
+
if (typeof self.importScripts === 'function') {
|
|
20
|
+
self.importScripts('java-source-augmentations.cjs');
|
|
21
|
+
}
|
|
22
|
+
|
|
19
23
|
let workerReadyPromise = null;
|
|
20
24
|
let idleTimer = null;
|
|
21
25
|
let queue = Promise.resolve();
|
|
@@ -30,6 +34,81 @@ function postMessageResponse(message) {
|
|
|
30
34
|
self.postMessage(message);
|
|
31
35
|
}
|
|
32
36
|
|
|
37
|
+
function formatWorkerErrorMessage(error) {
|
|
38
|
+
if (error instanceof Error && typeof error.message === 'string' && error.message.length > 0) {
|
|
39
|
+
return error.message;
|
|
40
|
+
}
|
|
41
|
+
if (typeof error === 'string' && error.length > 0) {
|
|
42
|
+
return error;
|
|
43
|
+
}
|
|
44
|
+
if (error && typeof error === 'object') {
|
|
45
|
+
const directKeys = ['message', 'detail', 'reason', 'cause', 'stack', 'name', 'className'];
|
|
46
|
+
for (const key of directKeys) {
|
|
47
|
+
try {
|
|
48
|
+
const value = error[key];
|
|
49
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
53
|
+
return String(value);
|
|
54
|
+
}
|
|
55
|
+
} catch {}
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const propertyNames = Object.getOwnPropertyNames(error);
|
|
59
|
+
for (const key of propertyNames) {
|
|
60
|
+
const value = error[key];
|
|
61
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
62
|
+
return `${key}: ${value}`;
|
|
63
|
+
}
|
|
64
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
65
|
+
return `${key}: ${String(value)}`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch {}
|
|
69
|
+
try {
|
|
70
|
+
const tag = Object.prototype.toString.call(error);
|
|
71
|
+
if (tag && tag.includes('ParseProblemException')) {
|
|
72
|
+
return 'Java syntax error.';
|
|
73
|
+
}
|
|
74
|
+
if (tag && tag !== '[object Object]' && !tag.startsWith('[object ')) {
|
|
75
|
+
return tag;
|
|
76
|
+
}
|
|
77
|
+
} catch {}
|
|
78
|
+
try {
|
|
79
|
+
if (typeof error.toString === 'function' && error.toString !== Object.prototype.toString) {
|
|
80
|
+
const value = error.toString();
|
|
81
|
+
if (value.includes('ParseProblemException')) {
|
|
82
|
+
return 'Java syntax error.';
|
|
83
|
+
}
|
|
84
|
+
if (typeof value === 'string' && value.length > 0 && value !== '[object Object]') {
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} catch {}
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const stringified = String(error);
|
|
92
|
+
if (stringified.includes('ParseProblemException')) {
|
|
93
|
+
return 'Java syntax error.';
|
|
94
|
+
}
|
|
95
|
+
if (stringified && stringified !== '[object Object]') {
|
|
96
|
+
return stringified;
|
|
97
|
+
}
|
|
98
|
+
} catch {}
|
|
99
|
+
try {
|
|
100
|
+
const json = JSON.stringify(error);
|
|
101
|
+
if (json && json !== '{}') {
|
|
102
|
+
return json;
|
|
103
|
+
}
|
|
104
|
+
} catch {}
|
|
105
|
+
return 'Unknown Java worker error';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function makeWorkerStageError(stage, error) {
|
|
109
|
+
return new Error(`Java worker ${stage} failed: ${formatWorkerErrorMessage(error)}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
33
112
|
function resetIdleTimer() {
|
|
34
113
|
idleGeneration += 1;
|
|
35
114
|
const generation = idleGeneration;
|
|
@@ -348,6 +427,414 @@ function indentBlock(source, spaces = 2) {
|
|
|
348
427
|
.join('\n');
|
|
349
428
|
}
|
|
350
429
|
|
|
430
|
+
function isJavaIdentifierPart(ch) {
|
|
431
|
+
return /[A-Za-z0-9_$]/.test(ch);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function scanJavaCode(source, start, end, onNormalChar) {
|
|
435
|
+
let state = 'normal';
|
|
436
|
+
for (let index = start; index < end; index += 1) {
|
|
437
|
+
const ch = source[index];
|
|
438
|
+
const next = index + 1 < end ? source[index + 1] : '';
|
|
439
|
+
|
|
440
|
+
if (state === 'line-comment') {
|
|
441
|
+
if (ch === '\n') state = 'normal';
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
if (state === 'block-comment') {
|
|
445
|
+
if (ch === '*' && next === '/') {
|
|
446
|
+
state = 'normal';
|
|
447
|
+
index += 1;
|
|
448
|
+
}
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
if (state === 'string') {
|
|
452
|
+
if (ch === '\\') {
|
|
453
|
+
index += 1;
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
if (ch === '"') state = 'normal';
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
if (state === 'char') {
|
|
460
|
+
if (ch === '\\') {
|
|
461
|
+
index += 1;
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (ch === "'") state = 'normal';
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (ch === '/' && next === '/') {
|
|
468
|
+
state = 'line-comment';
|
|
469
|
+
index += 1;
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (ch === '/' && next === '*') {
|
|
473
|
+
state = 'block-comment';
|
|
474
|
+
index += 1;
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
if (ch === '"') {
|
|
478
|
+
state = 'string';
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
if (ch === "'") {
|
|
482
|
+
state = 'char';
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const result = onNormalChar(index, ch);
|
|
487
|
+
if (result === false) return index;
|
|
488
|
+
if (typeof result === 'number') index = result;
|
|
489
|
+
}
|
|
490
|
+
return end;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function findMatchingParen(source, openIndex) {
|
|
494
|
+
let depth = 0;
|
|
495
|
+
let closeIndex = -1;
|
|
496
|
+
scanJavaCode(source, openIndex, source.length, (index, ch) => {
|
|
497
|
+
if (ch === '(') depth += 1;
|
|
498
|
+
if (ch === ')') {
|
|
499
|
+
depth -= 1;
|
|
500
|
+
if (depth === 0) {
|
|
501
|
+
closeIndex = index;
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return undefined;
|
|
506
|
+
});
|
|
507
|
+
return closeIndex;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function findSingleStatementEnd(source, bodyStart) {
|
|
511
|
+
let parenDepth = 0;
|
|
512
|
+
let bracketDepth = 0;
|
|
513
|
+
let braceDepth = 0;
|
|
514
|
+
let statementEnd = -1;
|
|
515
|
+
scanJavaCode(source, bodyStart, source.length, (index, ch) => {
|
|
516
|
+
if (ch === '(') parenDepth += 1;
|
|
517
|
+
if (ch === ')') parenDepth = Math.max(0, parenDepth - 1);
|
|
518
|
+
if (ch === '[') bracketDepth += 1;
|
|
519
|
+
if (ch === ']') bracketDepth = Math.max(0, bracketDepth - 1);
|
|
520
|
+
if (ch === '{') braceDepth += 1;
|
|
521
|
+
if (ch === '}') {
|
|
522
|
+
if (braceDepth === 0) return false;
|
|
523
|
+
braceDepth -= 1;
|
|
524
|
+
}
|
|
525
|
+
if (ch === ';' && parenDepth === 0 && bracketDepth === 0 && braceDepth === 0) {
|
|
526
|
+
statementEnd = index;
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
return undefined;
|
|
530
|
+
});
|
|
531
|
+
return statementEnd;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function startsWithJavaKeyword(source, index, keyword) {
|
|
535
|
+
if (!source.startsWith(keyword, index)) return false;
|
|
536
|
+
const after = source[index + keyword.length] ?? '';
|
|
537
|
+
return !after || !isJavaIdentifierPart(after);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function wrapSingleStatementLoopBodies(source) {
|
|
541
|
+
const inserts = [];
|
|
542
|
+
scanJavaCode(source, 0, source.length, (index) => {
|
|
543
|
+
const keyword = source.startsWith('for', index)
|
|
544
|
+
? 'for'
|
|
545
|
+
: source.startsWith('while', index)
|
|
546
|
+
? 'while'
|
|
547
|
+
: null;
|
|
548
|
+
if (!keyword) return undefined;
|
|
549
|
+
|
|
550
|
+
const before = index > 0 ? source[index - 1] : '';
|
|
551
|
+
const after = source[index + keyword.length] ?? '';
|
|
552
|
+
if ((before && isJavaIdentifierPart(before)) || (after && isJavaIdentifierPart(after))) {
|
|
553
|
+
return undefined;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
let cursor = index + keyword.length;
|
|
557
|
+
while (/\s/.test(source[cursor] ?? '')) cursor += 1;
|
|
558
|
+
if (source[cursor] !== '(') return undefined;
|
|
559
|
+
|
|
560
|
+
const closeParen = findMatchingParen(source, cursor);
|
|
561
|
+
if (closeParen < 0) return undefined;
|
|
562
|
+
|
|
563
|
+
let bodyStart = closeParen + 1;
|
|
564
|
+
while (/\s/.test(source[bodyStart] ?? '')) bodyStart += 1;
|
|
565
|
+
const bodyChar = source[bodyStart];
|
|
566
|
+
if (!bodyChar || bodyChar === '{' || bodyChar === ';') return closeParen;
|
|
567
|
+
if (
|
|
568
|
+
startsWithJavaKeyword(source, bodyStart, 'if') ||
|
|
569
|
+
startsWithJavaKeyword(source, bodyStart, 'switch') ||
|
|
570
|
+
startsWithJavaKeyword(source, bodyStart, 'synchronized') ||
|
|
571
|
+
startsWithJavaKeyword(source, bodyStart, 'try')
|
|
572
|
+
) {
|
|
573
|
+
return closeParen;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const bodyEnd = findSingleStatementEnd(source, bodyStart);
|
|
577
|
+
if (bodyEnd < 0) return closeParen;
|
|
578
|
+
|
|
579
|
+
inserts.push({ index: bodyStart, text: '{ ' });
|
|
580
|
+
inserts.push({ index: bodyEnd + 1, text: ' }' });
|
|
581
|
+
return bodyEnd;
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
if (inserts.length === 0) return source;
|
|
585
|
+
|
|
586
|
+
const insertsByIndex = new Map();
|
|
587
|
+
for (const insert of inserts) {
|
|
588
|
+
insertsByIndex.set(insert.index, `${insertsByIndex.get(insert.index) ?? ''}${insert.text}`);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
let output = '';
|
|
592
|
+
for (let index = 0; index <= source.length; index += 1) {
|
|
593
|
+
output += insertsByIndex.get(index) ?? '';
|
|
594
|
+
if (index < source.length) output += source[index];
|
|
595
|
+
}
|
|
596
|
+
return output;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function splitTopLevelJavaList(value) {
|
|
600
|
+
const parts = [];
|
|
601
|
+
let start = 0;
|
|
602
|
+
let parenDepth = 0;
|
|
603
|
+
let bracketDepth = 0;
|
|
604
|
+
let braceDepth = 0;
|
|
605
|
+
let angleDepth = 0;
|
|
606
|
+
let quote = null;
|
|
607
|
+
|
|
608
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
609
|
+
const ch = value[index];
|
|
610
|
+
const previous = index > 0 ? value[index - 1] : '';
|
|
611
|
+
if (quote) {
|
|
612
|
+
if (ch === quote && previous !== '\\') quote = null;
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
if (ch === '"' || ch === "'") {
|
|
616
|
+
quote = ch;
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
if (ch === '(') parenDepth += 1;
|
|
620
|
+
if (ch === ')') parenDepth = Math.max(0, parenDepth - 1);
|
|
621
|
+
if (ch === '[') bracketDepth += 1;
|
|
622
|
+
if (ch === ']') bracketDepth = Math.max(0, bracketDepth - 1);
|
|
623
|
+
if (ch === '{') braceDepth += 1;
|
|
624
|
+
if (ch === '}') braceDepth = Math.max(0, braceDepth - 1);
|
|
625
|
+
if (ch === '<') angleDepth += 1;
|
|
626
|
+
if (ch === '>') angleDepth = Math.max(0, angleDepth - 1);
|
|
627
|
+
if (
|
|
628
|
+
ch === ',' &&
|
|
629
|
+
parenDepth === 0 &&
|
|
630
|
+
bracketDepth === 0 &&
|
|
631
|
+
braceDepth === 0 &&
|
|
632
|
+
angleDepth === 0
|
|
633
|
+
) {
|
|
634
|
+
parts.push(value.slice(start, index).trim());
|
|
635
|
+
start = index + 1;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const tail = value.slice(start).trim();
|
|
640
|
+
if (tail.length > 0) {
|
|
641
|
+
parts.push(tail);
|
|
642
|
+
}
|
|
643
|
+
return parts;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
function parseJavaParameters(parametersSource) {
|
|
647
|
+
return splitTopLevelJavaList(parametersSource)
|
|
648
|
+
.map((parameter) => parameter.replace(/@\w+(?:\([^)]*\))?/g, '').replace(/\bfinal\b/g, '').trim())
|
|
649
|
+
.map((parameter) => {
|
|
650
|
+
const match = parameter.match(/([A-Za-z_][A-Za-z0-9_]*)\s*(?:\.\.\.)?$/);
|
|
651
|
+
const name = match?.[1] ?? '';
|
|
652
|
+
return {
|
|
653
|
+
name,
|
|
654
|
+
isArray: parameter.includes('[]') || parameter.includes('...'),
|
|
655
|
+
};
|
|
656
|
+
})
|
|
657
|
+
.filter((parameter) => parameter.name.length > 0);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function parseJavaParameterNames(parametersSource) {
|
|
661
|
+
return parseJavaParameters(parametersSource).map((parameter) => parameter.name);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function collectJavaArrayDeclarations(line) {
|
|
665
|
+
const names = [];
|
|
666
|
+
const declarationPattern =
|
|
667
|
+
/\b(?:boolean|byte|char|short|int|long|float|double|String|[A-Za-z_][A-Za-z0-9_<>.?]*)\s*(?:\[\s*\])+\s+([A-Za-z_][A-Za-z0-9_]*)\b/g;
|
|
668
|
+
for (const match of line.matchAll(declarationPattern)) {
|
|
669
|
+
if (match[1]) names.push(match[1]);
|
|
670
|
+
}
|
|
671
|
+
return names;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function escapeRegExp(value) {
|
|
675
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function augmentTraceCallArgumentSnapshots(source) {
|
|
679
|
+
const lines = source.split('\n');
|
|
680
|
+
const methodStack = [];
|
|
681
|
+
const methodStartPattern =
|
|
682
|
+
/^(\s*)(?:(?:public|private|protected|static|final|synchronized)\s+)*(?:[A-Za-z_][A-Za-z0-9_<>\[\], ?]*\s+)+([A-Za-z_][A-Za-z0-9_]*)\s*\(([^)]*)\)\s*\{\s*$/;
|
|
683
|
+
|
|
684
|
+
return lines.map((line) => {
|
|
685
|
+
const methodMatch = line.match(methodStartPattern);
|
|
686
|
+
if (methodMatch) {
|
|
687
|
+
methodStack.push({
|
|
688
|
+
name: methodMatch[2],
|
|
689
|
+
params: parseJavaParameterNames(methodMatch[3] ?? ''),
|
|
690
|
+
depth: 1,
|
|
691
|
+
patchedCall: false,
|
|
692
|
+
});
|
|
693
|
+
return line;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const currentMethod = methodStack[methodStack.length - 1];
|
|
697
|
+
let nextLine = line;
|
|
698
|
+
if (currentMethod && !currentMethod.patchedCall && currentMethod.params.length > 0) {
|
|
699
|
+
const callPattern = new RegExp(
|
|
700
|
+
`^(\\s*)TraceHooks\\.emit\\((\"line=\\d+ call ${escapeRegExp(currentMethod.name)}\").*\\);\\s*$`
|
|
701
|
+
);
|
|
702
|
+
const callMatch = line.match(callPattern);
|
|
703
|
+
if (callMatch) {
|
|
704
|
+
const serializedArgs = currentMethod.params
|
|
705
|
+
.map((paramName) => ` + " ${paramName}=" + TraceHooks.serializeResult(${paramName})`)
|
|
706
|
+
.join('');
|
|
707
|
+
nextLine = `${callMatch[1]}TraceHooks.emit(${callMatch[2]}${serializedArgs});`;
|
|
708
|
+
currentMethod.patchedCall = true;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (currentMethod) {
|
|
713
|
+
currentMethod.depth += braceDelta(nextLine);
|
|
714
|
+
while (methodStack.length > 0 && methodStack[methodStack.length - 1].depth <= 0) {
|
|
715
|
+
methodStack.pop();
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return nextLine;
|
|
720
|
+
}).join('\n');
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function augmentArrayLengthReads(source) {
|
|
724
|
+
const lines = source.split('\n');
|
|
725
|
+
const methodStack = [];
|
|
726
|
+
const methodStartPattern =
|
|
727
|
+
/^(\s*)(?:(?:public|private|protected|static|final|synchronized)\s+)*(?:[A-Za-z_][A-Za-z0-9_<>\[\], ?]*\s+)+([A-Za-z_][A-Za-z0-9_]*)\s*\(([^)]*)\)\s*\{\s*$/;
|
|
728
|
+
|
|
729
|
+
return lines.map((line) => {
|
|
730
|
+
const methodMatch = line.match(methodStartPattern);
|
|
731
|
+
if (methodMatch) {
|
|
732
|
+
const parameters = parseJavaParameters(methodMatch[3] ?? '');
|
|
733
|
+
methodStack.push({
|
|
734
|
+
depth: 1,
|
|
735
|
+
currentTraceLine: null,
|
|
736
|
+
hasTraceEmit: false,
|
|
737
|
+
arrayNames: new Set(parameters.filter((parameter) => parameter.isArray).map((parameter) => parameter.name)),
|
|
738
|
+
});
|
|
739
|
+
return line;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const currentMethod = methodStack[methodStack.length - 1];
|
|
743
|
+
let nextLine = line;
|
|
744
|
+
|
|
745
|
+
if (currentMethod) {
|
|
746
|
+
for (const name of collectJavaArrayDeclarations(line)) {
|
|
747
|
+
currentMethod.arrayNames.add(name);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const traceLineMatch = line.match(/TraceHooks\.emit\("line=(\d+)(?:\s|")/);
|
|
751
|
+
if (traceLineMatch) {
|
|
752
|
+
currentMethod.currentTraceLine = Number.parseInt(traceLineMatch[1], 10);
|
|
753
|
+
currentMethod.hasTraceEmit = true;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (
|
|
757
|
+
currentMethod.hasTraceEmit &&
|
|
758
|
+
currentMethod.currentTraceLine !== null &&
|
|
759
|
+
!line.includes('TraceHooks.readArrayLengthAtLine')
|
|
760
|
+
) {
|
|
761
|
+
for (const arrayName of currentMethod.arrayNames) {
|
|
762
|
+
const lengthPattern = new RegExp(`\\b${escapeRegExp(arrayName)}\\.length\\b`, 'g');
|
|
763
|
+
nextLine = nextLine.replace(
|
|
764
|
+
lengthPattern,
|
|
765
|
+
`TraceHooks.readArrayLengthAtLine(${currentMethod.currentTraceLine}, "${arrayName}", ${arrayName})`
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
currentMethod.depth += braceDelta(nextLine);
|
|
771
|
+
while (methodStack.length > 0 && methodStack[methodStack.length - 1].depth <= 0) {
|
|
772
|
+
methodStack.pop();
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
return nextLine;
|
|
777
|
+
}).join('\n');
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
function augmentTraceReturnValueSnapshots(source) {
|
|
781
|
+
const lines = source.split('\n');
|
|
782
|
+
const output = [];
|
|
783
|
+
const methodStack = [];
|
|
784
|
+
let returnValueIndex = 0;
|
|
785
|
+
const methodStartPattern =
|
|
786
|
+
/^(\s*)(?:(?:public|private|protected|static|final|synchronized)\s+)*([A-Za-z_][A-Za-z0-9_<>\[\], ?]*(?:\[\])?)\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(([^)]*)\)\s*\{\s*$/;
|
|
787
|
+
|
|
788
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
789
|
+
const line = lines[index];
|
|
790
|
+
const methodMatch = line.match(methodStartPattern);
|
|
791
|
+
if (methodMatch) {
|
|
792
|
+
methodStack.push({
|
|
793
|
+
name: methodMatch[3],
|
|
794
|
+
returnType: (methodMatch[2] ?? '').trim(),
|
|
795
|
+
depth: 1,
|
|
796
|
+
});
|
|
797
|
+
output.push(line);
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const currentMethod = methodStack[methodStack.length - 1];
|
|
802
|
+
if (currentMethod && currentMethod.returnType !== 'void') {
|
|
803
|
+
const returnEmitMatch = line.match(
|
|
804
|
+
/^(\s*)TraceHooks\.emit\("line=(\d+) return ([A-Za-z_][A-Za-z0-9_]*)"\);\s*$/
|
|
805
|
+
);
|
|
806
|
+
const nextLine = lines[index + 1] ?? '';
|
|
807
|
+
const returnMatch = nextLine.match(/^(\s*)return\s+(.+);\s*$/);
|
|
808
|
+
if (returnEmitMatch && returnMatch && returnEmitMatch[3] === currentMethod.name) {
|
|
809
|
+
const tempName = `__tracecodeReturnValue${returnValueIndex++}`;
|
|
810
|
+
const indent = returnEmitMatch[1] ?? returnMatch[1] ?? '';
|
|
811
|
+
const returnExpression = returnMatch[2].trim();
|
|
812
|
+
output.push(`${indent}${currentMethod.returnType} ${tempName} = ${returnExpression};`);
|
|
813
|
+
output.push(
|
|
814
|
+
`${indent}TraceHooks.emit("line=${returnEmitMatch[2]} return ${currentMethod.name} value=" + TraceHooks.serializeResult(${tempName}));`
|
|
815
|
+
);
|
|
816
|
+
output.push(`${returnMatch[1] ?? indent}return ${tempName};`);
|
|
817
|
+
currentMethod.depth += braceDelta(line) + braceDelta(nextLine);
|
|
818
|
+
index += 1;
|
|
819
|
+
while (methodStack.length > 0 && methodStack[methodStack.length - 1].depth <= 0) {
|
|
820
|
+
methodStack.pop();
|
|
821
|
+
}
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
output.push(line);
|
|
827
|
+
if (currentMethod) {
|
|
828
|
+
currentMethod.depth += braceDelta(line);
|
|
829
|
+
while (methodStack.length > 0 && methodStack[methodStack.length - 1].depth <= 0) {
|
|
830
|
+
methodStack.pop();
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
return output.join('\n');
|
|
836
|
+
}
|
|
837
|
+
|
|
351
838
|
function splitImportPrelude(source) {
|
|
352
839
|
const lines = source.split('\n');
|
|
353
840
|
const importLines = [];
|
|
@@ -367,13 +854,99 @@ function splitImportPrelude(source) {
|
|
|
367
854
|
return { importLines, bodyLines };
|
|
368
855
|
}
|
|
369
856
|
|
|
857
|
+
function splitImportPreludeEntries(source) {
|
|
858
|
+
const lines = source.split('\n');
|
|
859
|
+
const importEntries = [];
|
|
860
|
+
const bodyEntries = [];
|
|
861
|
+
let inImportPrelude = true;
|
|
862
|
+
|
|
863
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
864
|
+
const line = lines[index];
|
|
865
|
+
const entry = { line, sourceLine: index + 1 };
|
|
866
|
+
const trimmed = line.trim();
|
|
867
|
+
if (inImportPrelude && (trimmed === '' || trimmed.startsWith('import '))) {
|
|
868
|
+
importEntries.push(entry);
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
inImportPrelude = false;
|
|
872
|
+
bodyEntries.push(entry);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
return { importEntries, bodyEntries };
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
function trimBlankEntries(entries) {
|
|
879
|
+
let start = 0;
|
|
880
|
+
let end = entries.length;
|
|
881
|
+
while (start < end && entries[start].line.trim().length === 0) start += 1;
|
|
882
|
+
while (end > start && entries[end - 1].line.trim().length === 0) end -= 1;
|
|
883
|
+
return entries.slice(start, end);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function isTopLevelMethodStart(line) {
|
|
887
|
+
const trimmed = line.trim();
|
|
888
|
+
return /^(?:public\s+|private\s+|protected\s+)?(?:static\s+)?(?:[\w<>\[\], ?]+\s+)+[A-Za-z_][A-Za-z0-9_]*\s*\([^;]*\)\s*\{/.test(trimmed);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function braceDelta(line) {
|
|
892
|
+
let delta = 0;
|
|
893
|
+
let quote = null;
|
|
894
|
+
for (let index = 0; index < line.length; index += 1) {
|
|
895
|
+
const ch = line[index];
|
|
896
|
+
const prev = index > 0 ? line[index - 1] : '';
|
|
897
|
+
if (quote) {
|
|
898
|
+
if (ch === quote && prev !== '\\') quote = null;
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
if (ch === '"' || ch === "'") {
|
|
902
|
+
quote = ch;
|
|
903
|
+
continue;
|
|
904
|
+
}
|
|
905
|
+
if (ch === '{') delta += 1;
|
|
906
|
+
if (ch === '}') delta -= 1;
|
|
907
|
+
}
|
|
908
|
+
return delta;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
function splitScriptMembersAndStatements(lines) {
|
|
912
|
+
const memberLines = [];
|
|
913
|
+
const statementLines = [];
|
|
914
|
+
let statementDepth = 0;
|
|
915
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
916
|
+
const entry = lines[index];
|
|
917
|
+
const line = typeof entry === 'string' ? entry : entry.line;
|
|
918
|
+
if (statementDepth !== 0 || !isTopLevelMethodStart(line)) {
|
|
919
|
+
statementLines.push(entry);
|
|
920
|
+
statementDepth += braceDelta(line);
|
|
921
|
+
if (statementDepth < 0) statementDepth = 0;
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
let depth = 0;
|
|
926
|
+
do {
|
|
927
|
+
const current = lines[index] ?? '';
|
|
928
|
+
const currentLine = typeof current === 'string' ? current : current.line;
|
|
929
|
+
memberLines.push(current);
|
|
930
|
+
depth += braceDelta(currentLine);
|
|
931
|
+
index += 1;
|
|
932
|
+
} while (index < lines.length && depth > 0);
|
|
933
|
+
index -= 1;
|
|
934
|
+
}
|
|
935
|
+
return {
|
|
936
|
+
memberLines,
|
|
937
|
+
statementLines,
|
|
938
|
+
memberEntries: memberLines,
|
|
939
|
+
statementEntries: statementLines,
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
|
|
370
943
|
function normalizeFunctionSource(source) {
|
|
371
944
|
if (/\bpackage\s+[A-Za-z_][A-Za-z0-9_.]*\s*;/.test(source)) {
|
|
372
945
|
throw new Error('Java function style should not declare a package; the harness manages package isolation.');
|
|
373
946
|
}
|
|
374
947
|
|
|
375
948
|
if (/\bclass\s+Solution\b/.test(source)) {
|
|
376
|
-
return source;
|
|
949
|
+
return wrapSingleStatementLoopBodies(source);
|
|
377
950
|
}
|
|
378
951
|
|
|
379
952
|
if (/\b(class|interface|enum|record)\b/.test(source)) {
|
|
@@ -390,23 +963,62 @@ function normalizeFunctionSource(source) {
|
|
|
390
963
|
throw new Error('Java function style requires a method fragment.');
|
|
391
964
|
}
|
|
392
965
|
|
|
393
|
-
return `${importBlock ? `${importBlock}\n\n` : ''}class Solution {\n${indentBlock(body, 2)}\n}
|
|
966
|
+
return wrapSingleStatementLoopBodies(`${importBlock ? `${importBlock}\n\n` : ''}class Solution {\n${indentBlock(body, 2)}\n}`);
|
|
394
967
|
}
|
|
395
968
|
|
|
396
969
|
function normalizeScriptSource(source) {
|
|
970
|
+
return normalizeScriptSourceWithLineMap(source).code;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
function normalizeScriptSourceWithLineMap(source) {
|
|
397
974
|
if (/\bpackage\s+[A-Za-z_][A-Za-z0-9_.]*\s*;/.test(source)) {
|
|
398
975
|
throw new Error('Java script style should not declare a package; the harness manages package isolation.');
|
|
399
976
|
}
|
|
400
977
|
|
|
401
|
-
const {
|
|
402
|
-
const
|
|
403
|
-
|
|
978
|
+
const { importEntries, bodyEntries } = splitImportPreludeEntries(source);
|
|
979
|
+
const { memberEntries, statementEntries } = splitScriptMembersAndStatements(bodyEntries);
|
|
980
|
+
const trimmedMemberEntries = trimBlankEntries(memberEntries);
|
|
981
|
+
const trimmedStatementEntries = trimBlankEntries(statementEntries);
|
|
982
|
+
if (trimmedStatementEntries.length === 0) {
|
|
404
983
|
throw new Error('Java script style requires executable statements and a result assignment.');
|
|
405
984
|
}
|
|
406
985
|
|
|
407
|
-
const
|
|
408
|
-
const
|
|
409
|
-
|
|
986
|
+
const outputLines = [];
|
|
987
|
+
const lineMap = {};
|
|
988
|
+
const firstStatementLine = trimmedStatementEntries[0]?.sourceLine;
|
|
989
|
+
const lastStatementLine = trimmedStatementEntries[trimmedStatementEntries.length - 1]?.sourceLine;
|
|
990
|
+
const declaresResult = trimmedStatementEntries.some((entry) =>
|
|
991
|
+
/^(?:final\s+)?[\w<>\[\], ?]+\s+result\s*(?:=|;)/.test(entry.line.trim())
|
|
992
|
+
);
|
|
993
|
+
const pushLine = (line, sourceLine) => {
|
|
994
|
+
outputLines.push(line);
|
|
995
|
+
if (Number.isFinite(sourceLine) && sourceLine > 0) {
|
|
996
|
+
lineMap[outputLines.length] = sourceLine;
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
for (const entry of importEntries) {
|
|
1001
|
+
pushLine(entry.line, entry.sourceLine);
|
|
1002
|
+
}
|
|
1003
|
+
pushLine('class Solution {');
|
|
1004
|
+
for (const entry of trimmedMemberEntries) {
|
|
1005
|
+
pushLine(entry.line.trim().length === 0 ? '' : ` ${entry.line}`, entry.sourceLine);
|
|
1006
|
+
}
|
|
1007
|
+
pushLine(` Object ${SCRIPT_METHOD_NAME}() {`, firstStatementLine);
|
|
1008
|
+
if (!declaresResult) {
|
|
1009
|
+
pushLine(' Object result = null;', firstStatementLine);
|
|
1010
|
+
}
|
|
1011
|
+
for (const entry of trimmedStatementEntries) {
|
|
1012
|
+
pushLine(entry.line.trim().length === 0 ? '' : ` ${entry.line}`, entry.sourceLine);
|
|
1013
|
+
}
|
|
1014
|
+
pushLine(' return result;', lastStatementLine);
|
|
1015
|
+
pushLine(' }');
|
|
1016
|
+
pushLine('}');
|
|
1017
|
+
|
|
1018
|
+
return {
|
|
1019
|
+
code: wrapSingleStatementLoopBodies(outputLines.join('\n')),
|
|
1020
|
+
lineMap,
|
|
1021
|
+
};
|
|
410
1022
|
}
|
|
411
1023
|
|
|
412
1024
|
function normalizeJavaRequest(payload) {
|
|
@@ -415,12 +1027,14 @@ function normalizeJavaRequest(payload) {
|
|
|
415
1027
|
throw new Error('Java script-mode execution only supports executionStyle="function".');
|
|
416
1028
|
}
|
|
417
1029
|
|
|
1030
|
+
const normalizedScript = normalizeScriptSourceWithLineMap(payload.code);
|
|
418
1031
|
return {
|
|
419
1032
|
...payload,
|
|
420
|
-
code:
|
|
1033
|
+
code: normalizedScript.code,
|
|
421
1034
|
executionStyle: 'solution-method',
|
|
422
1035
|
functionName: SCRIPT_METHOD_NAME,
|
|
423
1036
|
sourceText: payload.code,
|
|
1037
|
+
sourceLineMap: normalizedScript.lineMap,
|
|
424
1038
|
userCodeLineCount: payload.code.split(/\r?\n/).length,
|
|
425
1039
|
scriptMode: true,
|
|
426
1040
|
};
|
|
@@ -595,12 +1209,103 @@ async function rewriteSource(payload, requestId) {
|
|
|
595
1209
|
);
|
|
596
1210
|
}
|
|
597
1211
|
|
|
598
|
-
function
|
|
1212
|
+
function normalizePublicClassDeclarations(source) {
|
|
1213
|
+
return String(source).replace(/(^|\n)\s*public\s+class\s+/g, '$1class ');
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
async function collectCompileProbeDiagnostics(source, requestId, options) {
|
|
1217
|
+
const probeClassName = buildExportsClassName(`${requestId}RewriteProbe`);
|
|
1218
|
+
const probePackageName = buildPackageName(`${requestId}RewriteProbe`);
|
|
1219
|
+
const sourcePath = `/str/${probeClassName}.java`;
|
|
1220
|
+
const classesDir = `/files/java-worker/${requestId}/rewrite-probe/classes`;
|
|
1221
|
+
|
|
1222
|
+
let compileLibraryClass;
|
|
1223
|
+
try {
|
|
1224
|
+
compileLibraryClass = await getCompileLibraryClass();
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
return {
|
|
1227
|
+
consoleOutput: [],
|
|
1228
|
+
error: null,
|
|
1229
|
+
hostCallMs: 0,
|
|
1230
|
+
diagnosticError: formatWorkerErrorMessage(error),
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
try {
|
|
1235
|
+
await self.cheerpOSAddStringFile(sourcePath, normalizePublicClassDeclarations(source));
|
|
1236
|
+
} catch (error) {
|
|
1237
|
+
return {
|
|
1238
|
+
consoleOutput: [],
|
|
1239
|
+
error: null,
|
|
1240
|
+
hostCallMs: 0,
|
|
1241
|
+
diagnosticError: formatWorkerErrorMessage(error),
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
const startedAt = performance.now();
|
|
1246
|
+
let reportText;
|
|
1247
|
+
try {
|
|
1248
|
+
reportText = await compileLibraryClass.compileAndTrace(
|
|
1249
|
+
sourcePath,
|
|
1250
|
+
classesDir,
|
|
1251
|
+
`${probePackageName}.${probeClassName}`,
|
|
1252
|
+
HELPER_JAR_PATH,
|
|
1253
|
+
DEFAULT_COMPILER_DEBUG_PROFILE,
|
|
1254
|
+
String(resolveMaxStoredEvents(options))
|
|
1255
|
+
);
|
|
1256
|
+
} catch (error) {
|
|
1257
|
+
return {
|
|
1258
|
+
consoleOutput: [],
|
|
1259
|
+
error: null,
|
|
1260
|
+
hostCallMs: performance.now() - startedAt,
|
|
1261
|
+
diagnosticError: formatWorkerErrorMessage(error),
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
let report;
|
|
1266
|
+
try {
|
|
1267
|
+
report = JSON.parse(reportText);
|
|
1268
|
+
} catch (error) {
|
|
1269
|
+
return {
|
|
1270
|
+
consoleOutput: [],
|
|
1271
|
+
error: null,
|
|
1272
|
+
hostCallMs: performance.now() - startedAt,
|
|
1273
|
+
diagnosticError: `Invalid compile probe report: ${formatWorkerErrorMessage(error)}`,
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
const consoleOutput = [report.compilerStdout, report.compilerStderr].filter(
|
|
1278
|
+
(entry) => typeof entry === 'string' && entry.trim().length > 0
|
|
1279
|
+
);
|
|
1280
|
+
const surfacedError =
|
|
1281
|
+
report.runtimeError ||
|
|
1282
|
+
report.compilerStderr ||
|
|
1283
|
+
report.compilerStdout ||
|
|
1284
|
+
null;
|
|
1285
|
+
|
|
1286
|
+
return {
|
|
1287
|
+
consoleOutput,
|
|
1288
|
+
error: surfacedError,
|
|
1289
|
+
hostCallMs: performance.now() - startedAt,
|
|
1290
|
+
diagnosticError: null,
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
function normalizeScriptTraceEvents(events, scriptMode, userCodeLineCount, sourceLineMap) {
|
|
599
1295
|
if (!scriptMode || !Array.isArray(events)) return events;
|
|
600
1296
|
return events.map((event) => {
|
|
601
|
-
|
|
1297
|
+
let normalizedEvent = String(event)
|
|
602
1298
|
.replace(new RegExp(`\\bcall\\s+${SCRIPT_METHOD_NAME}\\b`, 'g'), 'call <module>')
|
|
603
1299
|
.replace(new RegExp(`\\breturn\\s+${SCRIPT_METHOD_NAME}\\b`, 'g'), 'return <module>');
|
|
1300
|
+
|
|
1301
|
+
const lineMatch = normalizedEvent.match(/^line=(\d+)(.*)$/);
|
|
1302
|
+
if (lineMatch && sourceLineMap && Object.prototype.hasOwnProperty.call(sourceLineMap, lineMatch[1])) {
|
|
1303
|
+
const mappedLine = Number(sourceLineMap[lineMatch[1]]);
|
|
1304
|
+
if (Number.isFinite(mappedLine) && mappedLine > 0) {
|
|
1305
|
+
normalizedEvent = `line=${mappedLine}${lineMatch[2] ?? ''}`;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
604
1309
|
const match = normalizedEvent.match(/^line=(\d+)\s+return\s+<module>$/);
|
|
605
1310
|
if (!match || !Number.isFinite(userCodeLineCount) || userCodeLineCount <= 0) {
|
|
606
1311
|
return normalizedEvent;
|
|
@@ -611,6 +1316,56 @@ function normalizeScriptTraceEvents(events, scriptMode, userCodeLineCount) {
|
|
|
611
1316
|
});
|
|
612
1317
|
}
|
|
613
1318
|
|
|
1319
|
+
function parseTraceLineNumber(event) {
|
|
1320
|
+
const match = String(event).match(/^line=(\d+)(?:\s|$)/);
|
|
1321
|
+
if (!match) return null;
|
|
1322
|
+
const line = Number.parseInt(match[1], 10);
|
|
1323
|
+
return Number.isFinite(line) && line > 0 ? line : null;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
function isBareTraceLineEvent(event) {
|
|
1327
|
+
return /^line=\d+$/.test(String(event));
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
function buildLoopBodyLineMap(sourceText) {
|
|
1331
|
+
if (typeof sourceText !== 'string' || sourceText.length === 0) return null;
|
|
1332
|
+
const lines = sourceText.split(/\r?\n/);
|
|
1333
|
+
const loopBodyLineToHeaderLine = new Map();
|
|
1334
|
+
|
|
1335
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1336
|
+
const line = lines[index];
|
|
1337
|
+
if (!/\b(?:for|while)\s*\(/.test(line) || !line.includes('{')) continue;
|
|
1338
|
+
|
|
1339
|
+
for (let bodyIndex = index + 1; bodyIndex < lines.length; bodyIndex += 1) {
|
|
1340
|
+
const trimmed = lines[bodyIndex].trim();
|
|
1341
|
+
if (trimmed.length === 0) continue;
|
|
1342
|
+
if (trimmed.startsWith('}')) break;
|
|
1343
|
+
loopBodyLineToHeaderLine.set(bodyIndex + 1, index + 1);
|
|
1344
|
+
break;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
return loopBodyLineToHeaderLine.size > 0 ? loopBodyLineToHeaderLine : null;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
function expandLoopHeaderTraceEvents(events, sourceText) {
|
|
1352
|
+
if (!Array.isArray(events) || events.length === 0) return events;
|
|
1353
|
+
const loopBodyLineToHeaderLine = buildLoopBodyLineMap(sourceText);
|
|
1354
|
+
if (!loopBodyLineToHeaderLine) return events;
|
|
1355
|
+
|
|
1356
|
+
const expanded = [];
|
|
1357
|
+
for (const event of events) {
|
|
1358
|
+
const line = parseTraceLineNumber(event);
|
|
1359
|
+
const headerLine = line === null ? undefined : loopBodyLineToHeaderLine.get(line);
|
|
1360
|
+
const previousLine = expanded.length > 0 ? parseTraceLineNumber(expanded[expanded.length - 1]) : null;
|
|
1361
|
+
if (headerLine !== undefined && isBareTraceLineEvent(event) && previousLine !== headerLine) {
|
|
1362
|
+
expanded.push(`line=${headerLine}`);
|
|
1363
|
+
}
|
|
1364
|
+
expanded.push(event);
|
|
1365
|
+
}
|
|
1366
|
+
return expanded;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
614
1369
|
async function runJavaRequest(payload, requestId) {
|
|
615
1370
|
assertSupportedExecutionStyle(payload.executionStyle);
|
|
616
1371
|
if (typeof payload.code !== 'string') {
|
|
@@ -623,8 +1378,47 @@ async function runJavaRequest(payload, requestId) {
|
|
|
623
1378
|
|
|
624
1379
|
const totalStart = performance.now();
|
|
625
1380
|
const rewriteStart = performance.now();
|
|
626
|
-
|
|
627
|
-
|
|
1381
|
+
let normalizedPayload;
|
|
1382
|
+
try {
|
|
1383
|
+
normalizedPayload = normalizeJavaRequest(payload);
|
|
1384
|
+
} catch (error) {
|
|
1385
|
+
throw makeWorkerStageError('request normalization', error);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
let rewrittenSource;
|
|
1389
|
+
try {
|
|
1390
|
+
rewrittenSource = await rewriteSource(normalizedPayload, requestId);
|
|
1391
|
+
rewrittenSource = augmentTraceCallArgumentSnapshots(rewrittenSource);
|
|
1392
|
+
rewrittenSource = augmentArrayLengthReads(rewrittenSource);
|
|
1393
|
+
rewrittenSource = self.TraceCodeJavaSourceAugmentations.augmentJavaCollectionOperations(rewrittenSource);
|
|
1394
|
+
rewrittenSource = augmentTraceReturnValueSnapshots(rewrittenSource);
|
|
1395
|
+
} catch (error) {
|
|
1396
|
+
const rewriteError = formatWorkerErrorMessage(error);
|
|
1397
|
+
const diagnosticProbe = await collectCompileProbeDiagnostics(
|
|
1398
|
+
normalizedPayload.code,
|
|
1399
|
+
requestId,
|
|
1400
|
+
payload.options
|
|
1401
|
+
);
|
|
1402
|
+
const totalEnd = performance.now();
|
|
1403
|
+
const surfacedError =
|
|
1404
|
+
diagnosticProbe.error ??
|
|
1405
|
+
(rewriteError === 'Java syntax error.'
|
|
1406
|
+
? 'Java syntax error. Check Code Assist for parser details.'
|
|
1407
|
+
: `Java source rewrite failed: ${rewriteError}`);
|
|
1408
|
+
return {
|
|
1409
|
+
success: false,
|
|
1410
|
+
events: [],
|
|
1411
|
+
...(normalizedPayload.sourceText ? { sourceText: normalizedPayload.sourceText } : {}),
|
|
1412
|
+
executionTimeMs: totalEnd - totalStart,
|
|
1413
|
+
consoleOutput: diagnosticProbe.consoleOutput,
|
|
1414
|
+
error: surfacedError,
|
|
1415
|
+
timings: {
|
|
1416
|
+
rewriteMs: totalEnd - rewriteStart,
|
|
1417
|
+
hostCallMs: diagnosticProbe.hostCallMs,
|
|
1418
|
+
totalMs: totalEnd - totalStart,
|
|
1419
|
+
},
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
628
1422
|
const rewriteEnd = performance.now();
|
|
629
1423
|
|
|
630
1424
|
const exportsClassName = buildExportsClassName(requestId);
|
|
@@ -632,21 +1426,41 @@ async function runJavaRequest(payload, requestId) {
|
|
|
632
1426
|
const sourcePath = `/str/${exportsClassName}.java`;
|
|
633
1427
|
const classesDir = `/files/java-worker/${requestId}/classes`;
|
|
634
1428
|
|
|
635
|
-
|
|
1429
|
+
try {
|
|
1430
|
+
await self.cheerpOSAddStringFile(sourcePath, rewrittenSource);
|
|
1431
|
+
} catch (error) {
|
|
1432
|
+
throw makeWorkerStageError('source file write', error);
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
let compileLibraryClass;
|
|
1436
|
+
try {
|
|
1437
|
+
compileLibraryClass = await getCompileLibraryClass();
|
|
1438
|
+
} catch (error) {
|
|
1439
|
+
throw makeWorkerStageError('compiler bridge load', error);
|
|
1440
|
+
}
|
|
636
1441
|
|
|
637
|
-
const compileLibraryClass = await getCompileLibraryClass();
|
|
638
1442
|
const libraryCallStart = performance.now();
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
1443
|
+
let reportText;
|
|
1444
|
+
try {
|
|
1445
|
+
reportText = await compileLibraryClass.compileAndTrace(
|
|
1446
|
+
sourcePath,
|
|
1447
|
+
classesDir,
|
|
1448
|
+
`${packageName}.${exportsClassName}`,
|
|
1449
|
+
HELPER_JAR_PATH,
|
|
1450
|
+
DEFAULT_COMPILER_DEBUG_PROFILE,
|
|
1451
|
+
String(resolveMaxStoredEvents(payload.options))
|
|
1452
|
+
);
|
|
1453
|
+
} catch (error) {
|
|
1454
|
+
throw makeWorkerStageError('compile and trace', error);
|
|
1455
|
+
}
|
|
647
1456
|
const libraryCallEnd = performance.now();
|
|
648
1457
|
|
|
649
|
-
|
|
1458
|
+
let report;
|
|
1459
|
+
try {
|
|
1460
|
+
report = JSON.parse(reportText);
|
|
1461
|
+
} catch (error) {
|
|
1462
|
+
throw makeWorkerStageError('trace report parse', error);
|
|
1463
|
+
}
|
|
650
1464
|
const totalEnd = performance.now();
|
|
651
1465
|
const consoleOutput = [report.compilerStdout, report.compilerStderr].filter(
|
|
652
1466
|
(entry) => typeof entry === 'string' && entry.trim().length > 0
|
|
@@ -655,10 +1469,14 @@ async function runJavaRequest(payload, requestId) {
|
|
|
655
1469
|
if (report.success !== true) {
|
|
656
1470
|
return {
|
|
657
1471
|
success: false,
|
|
658
|
-
events:
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
1472
|
+
events: expandLoopHeaderTraceEvents(
|
|
1473
|
+
normalizeScriptTraceEvents(
|
|
1474
|
+
Array.isArray(report.events) ? report.events : [],
|
|
1475
|
+
normalizedPayload.scriptMode,
|
|
1476
|
+
normalizedPayload.userCodeLineCount,
|
|
1477
|
+
normalizedPayload.sourceLineMap
|
|
1478
|
+
),
|
|
1479
|
+
normalizedPayload.sourceText
|
|
662
1480
|
),
|
|
663
1481
|
...(normalizedPayload.sourceText ? { sourceText: normalizedPayload.sourceText } : {}),
|
|
664
1482
|
executionTimeMs: totalEnd - totalStart,
|
|
@@ -686,10 +1504,14 @@ async function runJavaRequest(payload, requestId) {
|
|
|
686
1504
|
return {
|
|
687
1505
|
success: true,
|
|
688
1506
|
output: report.output ? JSON.parse(report.output) : undefined,
|
|
689
|
-
events:
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
1507
|
+
events: expandLoopHeaderTraceEvents(
|
|
1508
|
+
normalizeScriptTraceEvents(
|
|
1509
|
+
Array.isArray(report.events) ? report.events : [],
|
|
1510
|
+
normalizedPayload.scriptMode,
|
|
1511
|
+
normalizedPayload.userCodeLineCount,
|
|
1512
|
+
normalizedPayload.sourceLineMap
|
|
1513
|
+
),
|
|
1514
|
+
normalizedPayload.sourceText
|
|
693
1515
|
),
|
|
694
1516
|
...(normalizedPayload.sourceText ? { sourceText: normalizedPayload.sourceText } : {}),
|
|
695
1517
|
executionTimeMs: totalEnd - totalStart,
|
|
@@ -747,7 +1569,7 @@ self.onmessage = (event) => {
|
|
|
747
1569
|
postMessageResponse({
|
|
748
1570
|
id: message.id,
|
|
749
1571
|
type: 'error',
|
|
750
|
-
payload: { error:
|
|
1572
|
+
payload: { error: formatWorkerErrorMessage(error) },
|
|
751
1573
|
});
|
|
752
1574
|
}
|
|
753
1575
|
});
|
|
@@ -772,7 +1594,7 @@ self.onmessage = (event) => {
|
|
|
772
1594
|
postMessageResponse({
|
|
773
1595
|
id: message.id,
|
|
774
1596
|
type: 'error',
|
|
775
|
-
payload: { error:
|
|
1597
|
+
payload: { error: formatWorkerErrorMessage(error) },
|
|
776
1598
|
});
|
|
777
1599
|
}
|
|
778
1600
|
});
|