@tracecode/harness 0.6.1 → 0.6.5
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/README.md +4 -3
- package/dist/browser.cjs +125 -32
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +125 -32
- 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 +128 -32
- 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 +127 -32
- 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 +6 -3
- package/workers/java/java-source-augmentations.cjs +242 -0
- package/workers/java/java-worker.js +815 -35
- 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
|
@@ -14,6 +14,11 @@ const FULL_CLASSPATH = [
|
|
|
14
14
|
const DEFAULT_COMPILER_DEBUG_PROFILE = 'full';
|
|
15
15
|
const DEFAULT_MAX_STORED_EVENTS = 50_000;
|
|
16
16
|
const IDLE_TIMEOUT_MS = 90_000;
|
|
17
|
+
const SCRIPT_METHOD_NAME = '__tracecodeScript';
|
|
18
|
+
|
|
19
|
+
if (typeof self.importScripts === 'function') {
|
|
20
|
+
self.importScripts('java-source-augmentations.cjs');
|
|
21
|
+
}
|
|
17
22
|
|
|
18
23
|
let workerReadyPromise = null;
|
|
19
24
|
let idleTimer = null;
|
|
@@ -29,6 +34,72 @@ function postMessageResponse(message) {
|
|
|
29
34
|
self.postMessage(message);
|
|
30
35
|
}
|
|
31
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 !== '[object Object]') {
|
|
72
|
+
return tag;
|
|
73
|
+
}
|
|
74
|
+
} catch {}
|
|
75
|
+
try {
|
|
76
|
+
if (typeof error.toString === 'function' && error.toString !== Object.prototype.toString) {
|
|
77
|
+
const value = error.toString();
|
|
78
|
+
if (typeof value === 'string' && value.length > 0 && value !== '[object Object]') {
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} catch {}
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const stringified = String(error);
|
|
86
|
+
if (stringified && stringified !== '[object Object]') {
|
|
87
|
+
return stringified;
|
|
88
|
+
}
|
|
89
|
+
} catch {}
|
|
90
|
+
try {
|
|
91
|
+
const json = JSON.stringify(error);
|
|
92
|
+
if (json && json !== '{}') {
|
|
93
|
+
return json;
|
|
94
|
+
}
|
|
95
|
+
} catch {}
|
|
96
|
+
return 'Unknown Java worker error';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function makeWorkerStageError(stage, error) {
|
|
100
|
+
return new Error(`Java worker ${stage} failed: ${formatWorkerErrorMessage(error)}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
32
103
|
function resetIdleTimer() {
|
|
33
104
|
idleGeneration += 1;
|
|
34
105
|
const generation = idleGeneration;
|
|
@@ -48,6 +119,10 @@ function assertSupportedExecutionStyle(executionStyle) {
|
|
|
48
119
|
}
|
|
49
120
|
}
|
|
50
121
|
|
|
122
|
+
function isScriptRequest(payload) {
|
|
123
|
+
return typeof payload?.functionName !== 'string' || payload.functionName.trim().length === 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
51
126
|
function resolveMaxStoredEvents(options = {}) {
|
|
52
127
|
const fromStored = Number(options.maxStoredEvents);
|
|
53
128
|
if (Number.isFinite(fromStored) && fromStored > 0) {
|
|
@@ -343,21 +418,415 @@ function indentBlock(source, spaces = 2) {
|
|
|
343
418
|
.join('\n');
|
|
344
419
|
}
|
|
345
420
|
|
|
346
|
-
function
|
|
347
|
-
|
|
348
|
-
|
|
421
|
+
function isJavaIdentifierPart(ch) {
|
|
422
|
+
return /[A-Za-z0-9_$]/.test(ch);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function scanJavaCode(source, start, end, onNormalChar) {
|
|
426
|
+
let state = 'normal';
|
|
427
|
+
for (let index = start; index < end; index += 1) {
|
|
428
|
+
const ch = source[index];
|
|
429
|
+
const next = index + 1 < end ? source[index + 1] : '';
|
|
430
|
+
|
|
431
|
+
if (state === 'line-comment') {
|
|
432
|
+
if (ch === '\n') state = 'normal';
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
if (state === 'block-comment') {
|
|
436
|
+
if (ch === '*' && next === '/') {
|
|
437
|
+
state = 'normal';
|
|
438
|
+
index += 1;
|
|
439
|
+
}
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (state === 'string') {
|
|
443
|
+
if (ch === '\\') {
|
|
444
|
+
index += 1;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
if (ch === '"') state = 'normal';
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (state === 'char') {
|
|
451
|
+
if (ch === '\\') {
|
|
452
|
+
index += 1;
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
if (ch === "'") state = 'normal';
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (ch === '/' && next === '/') {
|
|
459
|
+
state = 'line-comment';
|
|
460
|
+
index += 1;
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (ch === '/' && next === '*') {
|
|
464
|
+
state = 'block-comment';
|
|
465
|
+
index += 1;
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (ch === '"') {
|
|
469
|
+
state = 'string';
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (ch === "'") {
|
|
473
|
+
state = 'char';
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const result = onNormalChar(index, ch);
|
|
478
|
+
if (result === false) return index;
|
|
479
|
+
if (typeof result === 'number') index = result;
|
|
349
480
|
}
|
|
481
|
+
return end;
|
|
482
|
+
}
|
|
350
483
|
|
|
351
|
-
|
|
352
|
-
|
|
484
|
+
function findMatchingParen(source, openIndex) {
|
|
485
|
+
let depth = 0;
|
|
486
|
+
let closeIndex = -1;
|
|
487
|
+
scanJavaCode(source, openIndex, source.length, (index, ch) => {
|
|
488
|
+
if (ch === '(') depth += 1;
|
|
489
|
+
if (ch === ')') {
|
|
490
|
+
depth -= 1;
|
|
491
|
+
if (depth === 0) {
|
|
492
|
+
closeIndex = index;
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return undefined;
|
|
497
|
+
});
|
|
498
|
+
return closeIndex;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function findSingleStatementEnd(source, bodyStart) {
|
|
502
|
+
let parenDepth = 0;
|
|
503
|
+
let bracketDepth = 0;
|
|
504
|
+
let braceDepth = 0;
|
|
505
|
+
let statementEnd = -1;
|
|
506
|
+
scanJavaCode(source, bodyStart, source.length, (index, ch) => {
|
|
507
|
+
if (ch === '(') parenDepth += 1;
|
|
508
|
+
if (ch === ')') parenDepth = Math.max(0, parenDepth - 1);
|
|
509
|
+
if (ch === '[') bracketDepth += 1;
|
|
510
|
+
if (ch === ']') bracketDepth = Math.max(0, bracketDepth - 1);
|
|
511
|
+
if (ch === '{') braceDepth += 1;
|
|
512
|
+
if (ch === '}') {
|
|
513
|
+
if (braceDepth === 0) return false;
|
|
514
|
+
braceDepth -= 1;
|
|
515
|
+
}
|
|
516
|
+
if (ch === ';' && parenDepth === 0 && bracketDepth === 0 && braceDepth === 0) {
|
|
517
|
+
statementEnd = index;
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
return undefined;
|
|
521
|
+
});
|
|
522
|
+
return statementEnd;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function startsWithJavaKeyword(source, index, keyword) {
|
|
526
|
+
if (!source.startsWith(keyword, index)) return false;
|
|
527
|
+
const after = source[index + keyword.length] ?? '';
|
|
528
|
+
return !after || !isJavaIdentifierPart(after);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function wrapSingleStatementLoopBodies(source) {
|
|
532
|
+
const inserts = [];
|
|
533
|
+
scanJavaCode(source, 0, source.length, (index) => {
|
|
534
|
+
const keyword = source.startsWith('for', index)
|
|
535
|
+
? 'for'
|
|
536
|
+
: source.startsWith('while', index)
|
|
537
|
+
? 'while'
|
|
538
|
+
: null;
|
|
539
|
+
if (!keyword) return undefined;
|
|
540
|
+
|
|
541
|
+
const before = index > 0 ? source[index - 1] : '';
|
|
542
|
+
const after = source[index + keyword.length] ?? '';
|
|
543
|
+
if ((before && isJavaIdentifierPart(before)) || (after && isJavaIdentifierPart(after))) {
|
|
544
|
+
return undefined;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
let cursor = index + keyword.length;
|
|
548
|
+
while (/\s/.test(source[cursor] ?? '')) cursor += 1;
|
|
549
|
+
if (source[cursor] !== '(') return undefined;
|
|
550
|
+
|
|
551
|
+
const closeParen = findMatchingParen(source, cursor);
|
|
552
|
+
if (closeParen < 0) return undefined;
|
|
553
|
+
|
|
554
|
+
let bodyStart = closeParen + 1;
|
|
555
|
+
while (/\s/.test(source[bodyStart] ?? '')) bodyStart += 1;
|
|
556
|
+
const bodyChar = source[bodyStart];
|
|
557
|
+
if (!bodyChar || bodyChar === '{' || bodyChar === ';') return closeParen;
|
|
558
|
+
if (
|
|
559
|
+
startsWithJavaKeyword(source, bodyStart, 'if') ||
|
|
560
|
+
startsWithJavaKeyword(source, bodyStart, 'switch') ||
|
|
561
|
+
startsWithJavaKeyword(source, bodyStart, 'synchronized') ||
|
|
562
|
+
startsWithJavaKeyword(source, bodyStart, 'try')
|
|
563
|
+
) {
|
|
564
|
+
return closeParen;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const bodyEnd = findSingleStatementEnd(source, bodyStart);
|
|
568
|
+
if (bodyEnd < 0) return closeParen;
|
|
569
|
+
|
|
570
|
+
inserts.push({ index: bodyStart, text: '{ ' });
|
|
571
|
+
inserts.push({ index: bodyEnd + 1, text: ' }' });
|
|
572
|
+
return bodyEnd;
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
if (inserts.length === 0) return source;
|
|
576
|
+
|
|
577
|
+
const insertsByIndex = new Map();
|
|
578
|
+
for (const insert of inserts) {
|
|
579
|
+
insertsByIndex.set(insert.index, `${insertsByIndex.get(insert.index) ?? ''}${insert.text}`);
|
|
353
580
|
}
|
|
354
581
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
);
|
|
582
|
+
let output = '';
|
|
583
|
+
for (let index = 0; index <= source.length; index += 1) {
|
|
584
|
+
output += insertsByIndex.get(index) ?? '';
|
|
585
|
+
if (index < source.length) output += source[index];
|
|
586
|
+
}
|
|
587
|
+
return output;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function splitTopLevelJavaList(value) {
|
|
591
|
+
const parts = [];
|
|
592
|
+
let start = 0;
|
|
593
|
+
let parenDepth = 0;
|
|
594
|
+
let bracketDepth = 0;
|
|
595
|
+
let braceDepth = 0;
|
|
596
|
+
let angleDepth = 0;
|
|
597
|
+
let quote = null;
|
|
598
|
+
|
|
599
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
600
|
+
const ch = value[index];
|
|
601
|
+
const previous = index > 0 ? value[index - 1] : '';
|
|
602
|
+
if (quote) {
|
|
603
|
+
if (ch === quote && previous !== '\\') quote = null;
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (ch === '"' || ch === "'") {
|
|
607
|
+
quote = ch;
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (ch === '(') parenDepth += 1;
|
|
611
|
+
if (ch === ')') parenDepth = Math.max(0, parenDepth - 1);
|
|
612
|
+
if (ch === '[') bracketDepth += 1;
|
|
613
|
+
if (ch === ']') bracketDepth = Math.max(0, bracketDepth - 1);
|
|
614
|
+
if (ch === '{') braceDepth += 1;
|
|
615
|
+
if (ch === '}') braceDepth = Math.max(0, braceDepth - 1);
|
|
616
|
+
if (ch === '<') angleDepth += 1;
|
|
617
|
+
if (ch === '>') angleDepth = Math.max(0, angleDepth - 1);
|
|
618
|
+
if (
|
|
619
|
+
ch === ',' &&
|
|
620
|
+
parenDepth === 0 &&
|
|
621
|
+
bracketDepth === 0 &&
|
|
622
|
+
braceDepth === 0 &&
|
|
623
|
+
angleDepth === 0
|
|
624
|
+
) {
|
|
625
|
+
parts.push(value.slice(start, index).trim());
|
|
626
|
+
start = index + 1;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const tail = value.slice(start).trim();
|
|
631
|
+
if (tail.length > 0) {
|
|
632
|
+
parts.push(tail);
|
|
633
|
+
}
|
|
634
|
+
return parts;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function parseJavaParameters(parametersSource) {
|
|
638
|
+
return splitTopLevelJavaList(parametersSource)
|
|
639
|
+
.map((parameter) => parameter.replace(/@\w+(?:\([^)]*\))?/g, '').replace(/\bfinal\b/g, '').trim())
|
|
640
|
+
.map((parameter) => {
|
|
641
|
+
const match = parameter.match(/([A-Za-z_][A-Za-z0-9_]*)\s*(?:\.\.\.)?$/);
|
|
642
|
+
const name = match?.[1] ?? '';
|
|
643
|
+
return {
|
|
644
|
+
name,
|
|
645
|
+
isArray: parameter.includes('[]') || parameter.includes('...'),
|
|
646
|
+
};
|
|
647
|
+
})
|
|
648
|
+
.filter((parameter) => parameter.name.length > 0);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function parseJavaParameterNames(parametersSource) {
|
|
652
|
+
return parseJavaParameters(parametersSource).map((parameter) => parameter.name);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function collectJavaArrayDeclarations(line) {
|
|
656
|
+
const names = [];
|
|
657
|
+
const declarationPattern =
|
|
658
|
+
/\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;
|
|
659
|
+
for (const match of line.matchAll(declarationPattern)) {
|
|
660
|
+
if (match[1]) names.push(match[1]);
|
|
661
|
+
}
|
|
662
|
+
return names;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function escapeRegExp(value) {
|
|
666
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function augmentTraceCallArgumentSnapshots(source) {
|
|
670
|
+
const lines = source.split('\n');
|
|
671
|
+
const methodStack = [];
|
|
672
|
+
const methodStartPattern =
|
|
673
|
+
/^(\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*$/;
|
|
674
|
+
|
|
675
|
+
return lines.map((line) => {
|
|
676
|
+
const methodMatch = line.match(methodStartPattern);
|
|
677
|
+
if (methodMatch) {
|
|
678
|
+
methodStack.push({
|
|
679
|
+
name: methodMatch[2],
|
|
680
|
+
params: parseJavaParameterNames(methodMatch[3] ?? ''),
|
|
681
|
+
depth: 1,
|
|
682
|
+
patchedCall: false,
|
|
683
|
+
});
|
|
684
|
+
return line;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
const currentMethod = methodStack[methodStack.length - 1];
|
|
688
|
+
let nextLine = line;
|
|
689
|
+
if (currentMethod && !currentMethod.patchedCall && currentMethod.params.length > 0) {
|
|
690
|
+
const callPattern = new RegExp(
|
|
691
|
+
`^(\\s*)TraceHooks\\.emit\\((\"line=\\d+ call ${escapeRegExp(currentMethod.name)}\").*\\);\\s*$`
|
|
692
|
+
);
|
|
693
|
+
const callMatch = line.match(callPattern);
|
|
694
|
+
if (callMatch) {
|
|
695
|
+
const serializedArgs = currentMethod.params
|
|
696
|
+
.map((paramName) => ` + " ${paramName}=" + TraceHooks.serializeResult(${paramName})`)
|
|
697
|
+
.join('');
|
|
698
|
+
nextLine = `${callMatch[1]}TraceHooks.emit(${callMatch[2]}${serializedArgs});`;
|
|
699
|
+
currentMethod.patchedCall = true;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (currentMethod) {
|
|
704
|
+
currentMethod.depth += braceDelta(nextLine);
|
|
705
|
+
while (methodStack.length > 0 && methodStack[methodStack.length - 1].depth <= 0) {
|
|
706
|
+
methodStack.pop();
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
return nextLine;
|
|
711
|
+
}).join('\n');
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function augmentArrayLengthReads(source) {
|
|
715
|
+
const lines = source.split('\n');
|
|
716
|
+
const methodStack = [];
|
|
717
|
+
const methodStartPattern =
|
|
718
|
+
/^(\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*$/;
|
|
719
|
+
|
|
720
|
+
return lines.map((line) => {
|
|
721
|
+
const methodMatch = line.match(methodStartPattern);
|
|
722
|
+
if (methodMatch) {
|
|
723
|
+
const parameters = parseJavaParameters(methodMatch[3] ?? '');
|
|
724
|
+
methodStack.push({
|
|
725
|
+
depth: 1,
|
|
726
|
+
currentTraceLine: null,
|
|
727
|
+
hasTraceEmit: false,
|
|
728
|
+
arrayNames: new Set(parameters.filter((parameter) => parameter.isArray).map((parameter) => parameter.name)),
|
|
729
|
+
});
|
|
730
|
+
return line;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const currentMethod = methodStack[methodStack.length - 1];
|
|
734
|
+
let nextLine = line;
|
|
735
|
+
|
|
736
|
+
if (currentMethod) {
|
|
737
|
+
for (const name of collectJavaArrayDeclarations(line)) {
|
|
738
|
+
currentMethod.arrayNames.add(name);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const traceLineMatch = line.match(/TraceHooks\.emit\("line=(\d+)(?:\s|")/);
|
|
742
|
+
if (traceLineMatch) {
|
|
743
|
+
currentMethod.currentTraceLine = Number.parseInt(traceLineMatch[1], 10);
|
|
744
|
+
currentMethod.hasTraceEmit = true;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
if (
|
|
748
|
+
currentMethod.hasTraceEmit &&
|
|
749
|
+
currentMethod.currentTraceLine !== null &&
|
|
750
|
+
!line.includes('TraceHooks.readArrayLengthAtLine')
|
|
751
|
+
) {
|
|
752
|
+
for (const arrayName of currentMethod.arrayNames) {
|
|
753
|
+
const lengthPattern = new RegExp(`\\b${escapeRegExp(arrayName)}\\.length\\b`, 'g');
|
|
754
|
+
nextLine = nextLine.replace(
|
|
755
|
+
lengthPattern,
|
|
756
|
+
`TraceHooks.readArrayLengthAtLine(${currentMethod.currentTraceLine}, "${arrayName}", ${arrayName})`
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
currentMethod.depth += braceDelta(nextLine);
|
|
762
|
+
while (methodStack.length > 0 && methodStack[methodStack.length - 1].depth <= 0) {
|
|
763
|
+
methodStack.pop();
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
return nextLine;
|
|
768
|
+
}).join('\n');
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function augmentTraceReturnValueSnapshots(source) {
|
|
772
|
+
const lines = source.split('\n');
|
|
773
|
+
const output = [];
|
|
774
|
+
const methodStack = [];
|
|
775
|
+
let returnValueIndex = 0;
|
|
776
|
+
const methodStartPattern =
|
|
777
|
+
/^(\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*$/;
|
|
778
|
+
|
|
779
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
780
|
+
const line = lines[index];
|
|
781
|
+
const methodMatch = line.match(methodStartPattern);
|
|
782
|
+
if (methodMatch) {
|
|
783
|
+
methodStack.push({
|
|
784
|
+
name: methodMatch[3],
|
|
785
|
+
returnType: (methodMatch[2] ?? '').trim(),
|
|
786
|
+
depth: 1,
|
|
787
|
+
});
|
|
788
|
+
output.push(line);
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
const currentMethod = methodStack[methodStack.length - 1];
|
|
793
|
+
if (currentMethod && currentMethod.returnType !== 'void') {
|
|
794
|
+
const returnEmitMatch = line.match(
|
|
795
|
+
/^(\s*)TraceHooks\.emit\("line=(\d+) return ([A-Za-z_][A-Za-z0-9_]*)"\);\s*$/
|
|
796
|
+
);
|
|
797
|
+
const nextLine = lines[index + 1] ?? '';
|
|
798
|
+
const returnMatch = nextLine.match(/^(\s*)return\s+(.+);\s*$/);
|
|
799
|
+
if (returnEmitMatch && returnMatch && returnEmitMatch[3] === currentMethod.name) {
|
|
800
|
+
const tempName = `__tracecodeReturnValue${returnValueIndex++}`;
|
|
801
|
+
const indent = returnEmitMatch[1] ?? returnMatch[1] ?? '';
|
|
802
|
+
const returnExpression = returnMatch[2].trim();
|
|
803
|
+
output.push(`${indent}${currentMethod.returnType} ${tempName} = ${returnExpression};`);
|
|
804
|
+
output.push(
|
|
805
|
+
`${indent}TraceHooks.emit("line=${returnEmitMatch[2]} return ${currentMethod.name} value=" + TraceHooks.serializeResult(${tempName}));`
|
|
806
|
+
);
|
|
807
|
+
output.push(`${returnMatch[1] ?? indent}return ${tempName};`);
|
|
808
|
+
currentMethod.depth += braceDelta(line) + braceDelta(nextLine);
|
|
809
|
+
index += 1;
|
|
810
|
+
while (methodStack.length > 0 && methodStack[methodStack.length - 1].depth <= 0) {
|
|
811
|
+
methodStack.pop();
|
|
812
|
+
}
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
output.push(line);
|
|
818
|
+
if (currentMethod) {
|
|
819
|
+
currentMethod.depth += braceDelta(line);
|
|
820
|
+
while (methodStack.length > 0 && methodStack[methodStack.length - 1].depth <= 0) {
|
|
821
|
+
methodStack.pop();
|
|
822
|
+
}
|
|
823
|
+
}
|
|
359
824
|
}
|
|
360
825
|
|
|
826
|
+
return output.join('\n');
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function splitImportPrelude(source) {
|
|
361
830
|
const lines = source.split('\n');
|
|
362
831
|
const importLines = [];
|
|
363
832
|
const bodyLines = [];
|
|
@@ -373,16 +842,195 @@ function normalizeFunctionSource(source) {
|
|
|
373
842
|
bodyLines.push(line);
|
|
374
843
|
}
|
|
375
844
|
|
|
845
|
+
return { importLines, bodyLines };
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
function splitImportPreludeEntries(source) {
|
|
849
|
+
const lines = source.split('\n');
|
|
850
|
+
const importEntries = [];
|
|
851
|
+
const bodyEntries = [];
|
|
852
|
+
let inImportPrelude = true;
|
|
853
|
+
|
|
854
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
855
|
+
const line = lines[index];
|
|
856
|
+
const entry = { line, sourceLine: index + 1 };
|
|
857
|
+
const trimmed = line.trim();
|
|
858
|
+
if (inImportPrelude && (trimmed === '' || trimmed.startsWith('import '))) {
|
|
859
|
+
importEntries.push(entry);
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
inImportPrelude = false;
|
|
863
|
+
bodyEntries.push(entry);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
return { importEntries, bodyEntries };
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
function trimBlankEntries(entries) {
|
|
870
|
+
let start = 0;
|
|
871
|
+
let end = entries.length;
|
|
872
|
+
while (start < end && entries[start].line.trim().length === 0) start += 1;
|
|
873
|
+
while (end > start && entries[end - 1].line.trim().length === 0) end -= 1;
|
|
874
|
+
return entries.slice(start, end);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function isTopLevelMethodStart(line) {
|
|
878
|
+
const trimmed = line.trim();
|
|
879
|
+
return /^(?:public\s+|private\s+|protected\s+)?(?:static\s+)?(?:[\w<>\[\], ?]+\s+)+[A-Za-z_][A-Za-z0-9_]*\s*\([^;]*\)\s*\{/.test(trimmed);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
function braceDelta(line) {
|
|
883
|
+
let delta = 0;
|
|
884
|
+
let quote = null;
|
|
885
|
+
for (let index = 0; index < line.length; index += 1) {
|
|
886
|
+
const ch = line[index];
|
|
887
|
+
const prev = index > 0 ? line[index - 1] : '';
|
|
888
|
+
if (quote) {
|
|
889
|
+
if (ch === quote && prev !== '\\') quote = null;
|
|
890
|
+
continue;
|
|
891
|
+
}
|
|
892
|
+
if (ch === '"' || ch === "'") {
|
|
893
|
+
quote = ch;
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
if (ch === '{') delta += 1;
|
|
897
|
+
if (ch === '}') delta -= 1;
|
|
898
|
+
}
|
|
899
|
+
return delta;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
function splitScriptMembersAndStatements(lines) {
|
|
903
|
+
const memberLines = [];
|
|
904
|
+
const statementLines = [];
|
|
905
|
+
let statementDepth = 0;
|
|
906
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
907
|
+
const entry = lines[index];
|
|
908
|
+
const line = typeof entry === 'string' ? entry : entry.line;
|
|
909
|
+
if (statementDepth !== 0 || !isTopLevelMethodStart(line)) {
|
|
910
|
+
statementLines.push(entry);
|
|
911
|
+
statementDepth += braceDelta(line);
|
|
912
|
+
if (statementDepth < 0) statementDepth = 0;
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
let depth = 0;
|
|
917
|
+
do {
|
|
918
|
+
const current = lines[index] ?? '';
|
|
919
|
+
const currentLine = typeof current === 'string' ? current : current.line;
|
|
920
|
+
memberLines.push(current);
|
|
921
|
+
depth += braceDelta(currentLine);
|
|
922
|
+
index += 1;
|
|
923
|
+
} while (index < lines.length && depth > 0);
|
|
924
|
+
index -= 1;
|
|
925
|
+
}
|
|
926
|
+
return {
|
|
927
|
+
memberLines,
|
|
928
|
+
statementLines,
|
|
929
|
+
memberEntries: memberLines,
|
|
930
|
+
statementEntries: statementLines,
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
function normalizeFunctionSource(source) {
|
|
935
|
+
if (/\bpackage\s+[A-Za-z_][A-Za-z0-9_.]*\s*;/.test(source)) {
|
|
936
|
+
throw new Error('Java function style should not declare a package; the harness manages package isolation.');
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
if (/\bclass\s+Solution\b/.test(source)) {
|
|
940
|
+
return wrapSingleStatementLoopBodies(source);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
if (/\b(class|interface|enum|record)\b/.test(source)) {
|
|
944
|
+
throw new Error(
|
|
945
|
+
'Java function style currently expects a bare method fragment or a class named Solution containing the target method.'
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
const { importLines, bodyLines } = splitImportPrelude(source);
|
|
950
|
+
|
|
376
951
|
const importBlock = importLines.join('\n').trim();
|
|
377
952
|
const body = bodyLines.join('\n').trim();
|
|
378
953
|
if (!body) {
|
|
379
954
|
throw new Error('Java function style requires a method fragment.');
|
|
380
955
|
}
|
|
381
956
|
|
|
382
|
-
return `${importBlock ? `${importBlock}\n\n` : ''}class Solution {\n${indentBlock(body, 2)}\n}
|
|
957
|
+
return wrapSingleStatementLoopBodies(`${importBlock ? `${importBlock}\n\n` : ''}class Solution {\n${indentBlock(body, 2)}\n}`);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
function normalizeScriptSource(source) {
|
|
961
|
+
return normalizeScriptSourceWithLineMap(source).code;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
function normalizeScriptSourceWithLineMap(source) {
|
|
965
|
+
if (/\bpackage\s+[A-Za-z_][A-Za-z0-9_.]*\s*;/.test(source)) {
|
|
966
|
+
throw new Error('Java script style should not declare a package; the harness manages package isolation.');
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
const { importEntries, bodyEntries } = splitImportPreludeEntries(source);
|
|
970
|
+
const { memberEntries, statementEntries } = splitScriptMembersAndStatements(bodyEntries);
|
|
971
|
+
const trimmedMemberEntries = trimBlankEntries(memberEntries);
|
|
972
|
+
const trimmedStatementEntries = trimBlankEntries(statementEntries);
|
|
973
|
+
if (trimmedStatementEntries.length === 0) {
|
|
974
|
+
throw new Error('Java script style requires executable statements and a result assignment.');
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
const outputLines = [];
|
|
978
|
+
const lineMap = {};
|
|
979
|
+
const firstStatementLine = trimmedStatementEntries[0]?.sourceLine;
|
|
980
|
+
const lastStatementLine = trimmedStatementEntries[trimmedStatementEntries.length - 1]?.sourceLine;
|
|
981
|
+
const declaresResult = trimmedStatementEntries.some((entry) =>
|
|
982
|
+
/^(?:final\s+)?[\w<>\[\], ?]+\s+result\s*(?:=|;)/.test(entry.line.trim())
|
|
983
|
+
);
|
|
984
|
+
const pushLine = (line, sourceLine) => {
|
|
985
|
+
outputLines.push(line);
|
|
986
|
+
if (Number.isFinite(sourceLine) && sourceLine > 0) {
|
|
987
|
+
lineMap[outputLines.length] = sourceLine;
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
|
|
991
|
+
for (const entry of importEntries) {
|
|
992
|
+
pushLine(entry.line, entry.sourceLine);
|
|
993
|
+
}
|
|
994
|
+
pushLine('class Solution {');
|
|
995
|
+
for (const entry of trimmedMemberEntries) {
|
|
996
|
+
pushLine(entry.line.trim().length === 0 ? '' : ` ${entry.line}`, entry.sourceLine);
|
|
997
|
+
}
|
|
998
|
+
pushLine(` Object ${SCRIPT_METHOD_NAME}() {`, firstStatementLine);
|
|
999
|
+
if (!declaresResult) {
|
|
1000
|
+
pushLine(' Object result = null;', firstStatementLine);
|
|
1001
|
+
}
|
|
1002
|
+
for (const entry of trimmedStatementEntries) {
|
|
1003
|
+
pushLine(entry.line.trim().length === 0 ? '' : ` ${entry.line}`, entry.sourceLine);
|
|
1004
|
+
}
|
|
1005
|
+
pushLine(' return result;', lastStatementLine);
|
|
1006
|
+
pushLine(' }');
|
|
1007
|
+
pushLine('}');
|
|
1008
|
+
|
|
1009
|
+
return {
|
|
1010
|
+
code: wrapSingleStatementLoopBodies(outputLines.join('\n')),
|
|
1011
|
+
lineMap,
|
|
1012
|
+
};
|
|
383
1013
|
}
|
|
384
1014
|
|
|
385
1015
|
function normalizeJavaRequest(payload) {
|
|
1016
|
+
if (isScriptRequest(payload)) {
|
|
1017
|
+
if (payload.executionStyle !== 'function') {
|
|
1018
|
+
throw new Error('Java script-mode execution only supports executionStyle="function".');
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
const normalizedScript = normalizeScriptSourceWithLineMap(payload.code);
|
|
1022
|
+
return {
|
|
1023
|
+
...payload,
|
|
1024
|
+
code: normalizedScript.code,
|
|
1025
|
+
executionStyle: 'solution-method',
|
|
1026
|
+
functionName: SCRIPT_METHOD_NAME,
|
|
1027
|
+
sourceText: payload.code,
|
|
1028
|
+
sourceLineMap: normalizedScript.lineMap,
|
|
1029
|
+
userCodeLineCount: payload.code.split(/\r?\n/).length,
|
|
1030
|
+
scriptMode: true,
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
|
|
386
1034
|
if (payload.executionStyle !== 'function') {
|
|
387
1035
|
return payload;
|
|
388
1036
|
}
|
|
@@ -533,35 +1181,129 @@ public class ExportsTracecodeWarmup {
|
|
|
533
1181
|
}
|
|
534
1182
|
|
|
535
1183
|
async function rewriteSource(payload, requestId) {
|
|
536
|
-
const normalizedPayload = normalizeJavaRequest(payload);
|
|
537
1184
|
const rewriteLibraryClass = await getRewriteLibraryClass();
|
|
538
1185
|
const exportsClassName = buildExportsClassName(requestId);
|
|
539
1186
|
const packageName = buildPackageName(requestId);
|
|
540
1187
|
const exportsSource = buildExportsSource(
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
1188
|
+
payload.code,
|
|
1189
|
+
payload.functionName,
|
|
1190
|
+
payload.executionStyle,
|
|
1191
|
+
payload.inputs ?? {}
|
|
545
1192
|
);
|
|
546
1193
|
return rewriteLibraryClass.rewriteSource(
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
1194
|
+
payload.code,
|
|
1195
|
+
payload.executionStyle,
|
|
1196
|
+
payload.functionName,
|
|
550
1197
|
exportsSource,
|
|
551
1198
|
exportsClassName,
|
|
552
1199
|
packageName
|
|
553
1200
|
);
|
|
554
1201
|
}
|
|
555
1202
|
|
|
1203
|
+
function normalizeScriptTraceEvents(events, scriptMode, userCodeLineCount, sourceLineMap) {
|
|
1204
|
+
if (!scriptMode || !Array.isArray(events)) return events;
|
|
1205
|
+
return events.map((event) => {
|
|
1206
|
+
let normalizedEvent = String(event)
|
|
1207
|
+
.replace(new RegExp(`\\bcall\\s+${SCRIPT_METHOD_NAME}\\b`, 'g'), 'call <module>')
|
|
1208
|
+
.replace(new RegExp(`\\breturn\\s+${SCRIPT_METHOD_NAME}\\b`, 'g'), 'return <module>');
|
|
1209
|
+
|
|
1210
|
+
const lineMatch = normalizedEvent.match(/^line=(\d+)(.*)$/);
|
|
1211
|
+
if (lineMatch && sourceLineMap && Object.prototype.hasOwnProperty.call(sourceLineMap, lineMatch[1])) {
|
|
1212
|
+
const mappedLine = Number(sourceLineMap[lineMatch[1]]);
|
|
1213
|
+
if (Number.isFinite(mappedLine) && mappedLine > 0) {
|
|
1214
|
+
normalizedEvent = `line=${mappedLine}${lineMatch[2] ?? ''}`;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
const match = normalizedEvent.match(/^line=(\d+)\s+return\s+<module>$/);
|
|
1219
|
+
if (!match || !Number.isFinite(userCodeLineCount) || userCodeLineCount <= 0) {
|
|
1220
|
+
return normalizedEvent;
|
|
1221
|
+
}
|
|
1222
|
+
const line = Number.parseInt(match[1], 10);
|
|
1223
|
+
if (line <= userCodeLineCount) return normalizedEvent;
|
|
1224
|
+
return `line=${userCodeLineCount} return <module>`;
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
function parseTraceLineNumber(event) {
|
|
1229
|
+
const match = String(event).match(/^line=(\d+)(?:\s|$)/);
|
|
1230
|
+
if (!match) return null;
|
|
1231
|
+
const line = Number.parseInt(match[1], 10);
|
|
1232
|
+
return Number.isFinite(line) && line > 0 ? line : null;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
function isBareTraceLineEvent(event) {
|
|
1236
|
+
return /^line=\d+$/.test(String(event));
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
function buildLoopBodyLineMap(sourceText) {
|
|
1240
|
+
if (typeof sourceText !== 'string' || sourceText.length === 0) return null;
|
|
1241
|
+
const lines = sourceText.split(/\r?\n/);
|
|
1242
|
+
const loopBodyLineToHeaderLine = new Map();
|
|
1243
|
+
|
|
1244
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1245
|
+
const line = lines[index];
|
|
1246
|
+
if (!/\b(?:for|while)\s*\(/.test(line) || !line.includes('{')) continue;
|
|
1247
|
+
|
|
1248
|
+
for (let bodyIndex = index + 1; bodyIndex < lines.length; bodyIndex += 1) {
|
|
1249
|
+
const trimmed = lines[bodyIndex].trim();
|
|
1250
|
+
if (trimmed.length === 0) continue;
|
|
1251
|
+
if (trimmed.startsWith('}')) break;
|
|
1252
|
+
loopBodyLineToHeaderLine.set(bodyIndex + 1, index + 1);
|
|
1253
|
+
break;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
return loopBodyLineToHeaderLine.size > 0 ? loopBodyLineToHeaderLine : null;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
function expandLoopHeaderTraceEvents(events, sourceText) {
|
|
1261
|
+
if (!Array.isArray(events) || events.length === 0) return events;
|
|
1262
|
+
const loopBodyLineToHeaderLine = buildLoopBodyLineMap(sourceText);
|
|
1263
|
+
if (!loopBodyLineToHeaderLine) return events;
|
|
1264
|
+
|
|
1265
|
+
const expanded = [];
|
|
1266
|
+
for (const event of events) {
|
|
1267
|
+
const line = parseTraceLineNumber(event);
|
|
1268
|
+
const headerLine = line === null ? undefined : loopBodyLineToHeaderLine.get(line);
|
|
1269
|
+
const previousLine = expanded.length > 0 ? parseTraceLineNumber(expanded[expanded.length - 1]) : null;
|
|
1270
|
+
if (headerLine !== undefined && isBareTraceLineEvent(event) && previousLine !== headerLine) {
|
|
1271
|
+
expanded.push(`line=${headerLine}`);
|
|
1272
|
+
}
|
|
1273
|
+
expanded.push(event);
|
|
1274
|
+
}
|
|
1275
|
+
return expanded;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
556
1278
|
async function runJavaRequest(payload, requestId) {
|
|
557
1279
|
assertSupportedExecutionStyle(payload.executionStyle);
|
|
558
|
-
if (typeof payload.
|
|
1280
|
+
if (typeof payload.code !== 'string') {
|
|
1281
|
+
throw new Error('`code` must be a string');
|
|
1282
|
+
}
|
|
1283
|
+
const scriptRequest = isScriptRequest(payload);
|
|
1284
|
+
if (!scriptRequest && (typeof payload.functionName !== 'string' || payload.functionName.trim().length === 0)) {
|
|
559
1285
|
throw new Error('Java execution requires a non-empty functionName or class entry name.');
|
|
560
1286
|
}
|
|
561
1287
|
|
|
562
1288
|
const totalStart = performance.now();
|
|
563
1289
|
const rewriteStart = performance.now();
|
|
564
|
-
|
|
1290
|
+
let normalizedPayload;
|
|
1291
|
+
try {
|
|
1292
|
+
normalizedPayload = normalizeJavaRequest(payload);
|
|
1293
|
+
} catch (error) {
|
|
1294
|
+
throw makeWorkerStageError('request normalization', error);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
let rewrittenSource;
|
|
1298
|
+
try {
|
|
1299
|
+
rewrittenSource = await rewriteSource(normalizedPayload, requestId);
|
|
1300
|
+
rewrittenSource = augmentTraceCallArgumentSnapshots(rewrittenSource);
|
|
1301
|
+
rewrittenSource = augmentArrayLengthReads(rewrittenSource);
|
|
1302
|
+
rewrittenSource = self.TraceCodeJavaSourceAugmentations.augmentJavaCollectionOperations(rewrittenSource);
|
|
1303
|
+
rewrittenSource = augmentTraceReturnValueSnapshots(rewrittenSource);
|
|
1304
|
+
} catch (error) {
|
|
1305
|
+
throw makeWorkerStageError('source rewrite', error);
|
|
1306
|
+
}
|
|
565
1307
|
const rewriteEnd = performance.now();
|
|
566
1308
|
|
|
567
1309
|
const exportsClassName = buildExportsClassName(requestId);
|
|
@@ -569,21 +1311,41 @@ async function runJavaRequest(payload, requestId) {
|
|
|
569
1311
|
const sourcePath = `/str/${exportsClassName}.java`;
|
|
570
1312
|
const classesDir = `/files/java-worker/${requestId}/classes`;
|
|
571
1313
|
|
|
572
|
-
|
|
1314
|
+
try {
|
|
1315
|
+
await self.cheerpOSAddStringFile(sourcePath, rewrittenSource);
|
|
1316
|
+
} catch (error) {
|
|
1317
|
+
throw makeWorkerStageError('source file write', error);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
let compileLibraryClass;
|
|
1321
|
+
try {
|
|
1322
|
+
compileLibraryClass = await getCompileLibraryClass();
|
|
1323
|
+
} catch (error) {
|
|
1324
|
+
throw makeWorkerStageError('compiler bridge load', error);
|
|
1325
|
+
}
|
|
573
1326
|
|
|
574
|
-
const compileLibraryClass = await getCompileLibraryClass();
|
|
575
1327
|
const libraryCallStart = performance.now();
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
1328
|
+
let reportText;
|
|
1329
|
+
try {
|
|
1330
|
+
reportText = await compileLibraryClass.compileAndTrace(
|
|
1331
|
+
sourcePath,
|
|
1332
|
+
classesDir,
|
|
1333
|
+
`${packageName}.${exportsClassName}`,
|
|
1334
|
+
HELPER_JAR_PATH,
|
|
1335
|
+
DEFAULT_COMPILER_DEBUG_PROFILE,
|
|
1336
|
+
String(resolveMaxStoredEvents(payload.options))
|
|
1337
|
+
);
|
|
1338
|
+
} catch (error) {
|
|
1339
|
+
throw makeWorkerStageError('compile and trace', error);
|
|
1340
|
+
}
|
|
584
1341
|
const libraryCallEnd = performance.now();
|
|
585
1342
|
|
|
586
|
-
|
|
1343
|
+
let report;
|
|
1344
|
+
try {
|
|
1345
|
+
report = JSON.parse(reportText);
|
|
1346
|
+
} catch (error) {
|
|
1347
|
+
throw makeWorkerStageError('trace report parse', error);
|
|
1348
|
+
}
|
|
587
1349
|
const totalEnd = performance.now();
|
|
588
1350
|
const consoleOutput = [report.compilerStdout, report.compilerStderr].filter(
|
|
589
1351
|
(entry) => typeof entry === 'string' && entry.trim().length > 0
|
|
@@ -592,7 +1354,16 @@ async function runJavaRequest(payload, requestId) {
|
|
|
592
1354
|
if (report.success !== true) {
|
|
593
1355
|
return {
|
|
594
1356
|
success: false,
|
|
595
|
-
events:
|
|
1357
|
+
events: expandLoopHeaderTraceEvents(
|
|
1358
|
+
normalizeScriptTraceEvents(
|
|
1359
|
+
Array.isArray(report.events) ? report.events : [],
|
|
1360
|
+
normalizedPayload.scriptMode,
|
|
1361
|
+
normalizedPayload.userCodeLineCount,
|
|
1362
|
+
normalizedPayload.sourceLineMap
|
|
1363
|
+
),
|
|
1364
|
+
normalizedPayload.sourceText
|
|
1365
|
+
),
|
|
1366
|
+
...(normalizedPayload.sourceText ? { sourceText: normalizedPayload.sourceText } : {}),
|
|
596
1367
|
executionTimeMs: totalEnd - totalStart,
|
|
597
1368
|
consoleOutput,
|
|
598
1369
|
error:
|
|
@@ -618,7 +1389,16 @@ async function runJavaRequest(payload, requestId) {
|
|
|
618
1389
|
return {
|
|
619
1390
|
success: true,
|
|
620
1391
|
output: report.output ? JSON.parse(report.output) : undefined,
|
|
621
|
-
events:
|
|
1392
|
+
events: expandLoopHeaderTraceEvents(
|
|
1393
|
+
normalizeScriptTraceEvents(
|
|
1394
|
+
Array.isArray(report.events) ? report.events : [],
|
|
1395
|
+
normalizedPayload.scriptMode,
|
|
1396
|
+
normalizedPayload.userCodeLineCount,
|
|
1397
|
+
normalizedPayload.sourceLineMap
|
|
1398
|
+
),
|
|
1399
|
+
normalizedPayload.sourceText
|
|
1400
|
+
),
|
|
1401
|
+
...(normalizedPayload.sourceText ? { sourceText: normalizedPayload.sourceText } : {}),
|
|
622
1402
|
executionTimeMs: totalEnd - totalStart,
|
|
623
1403
|
consoleOutput,
|
|
624
1404
|
...(report.traceLimitExceeded !== undefined
|
|
@@ -674,7 +1454,7 @@ self.onmessage = (event) => {
|
|
|
674
1454
|
postMessageResponse({
|
|
675
1455
|
id: message.id,
|
|
676
1456
|
type: 'error',
|
|
677
|
-
payload: { error:
|
|
1457
|
+
payload: { error: formatWorkerErrorMessage(error) },
|
|
678
1458
|
});
|
|
679
1459
|
}
|
|
680
1460
|
});
|
|
@@ -699,7 +1479,7 @@ self.onmessage = (event) => {
|
|
|
699
1479
|
postMessageResponse({
|
|
700
1480
|
id: message.id,
|
|
701
1481
|
type: 'error',
|
|
702
|
-
payload: { error:
|
|
1482
|
+
payload: { error: formatWorkerErrorMessage(error) },
|
|
703
1483
|
});
|
|
704
1484
|
}
|
|
705
1485
|
});
|