@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
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
package harness.browser;
|
|
2
|
+
|
|
3
|
+
import java.nio.charset.StandardCharsets;
|
|
4
|
+
import java.nio.file.Files;
|
|
5
|
+
import java.nio.file.Path;
|
|
6
|
+
import spike.rewriter.GenericPracticeRewriter;
|
|
7
|
+
|
|
8
|
+
public final class JavaRewriteLibrary {
|
|
9
|
+
private JavaRewriteLibrary() {}
|
|
10
|
+
|
|
11
|
+
public static String rewriteSource(
|
|
12
|
+
String source,
|
|
13
|
+
String executionStyle,
|
|
14
|
+
String entryName,
|
|
15
|
+
String exportsSource,
|
|
16
|
+
String exportsClassName,
|
|
17
|
+
String packageName
|
|
18
|
+
) throws Exception {
|
|
19
|
+
Path workDir = Files.createTempDirectory("tracecode-java-rewrite-");
|
|
20
|
+
Path inputPath = workDir.resolve("Input.java");
|
|
21
|
+
Path outputPath = workDir.resolve("Output.java");
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
Files.writeString(inputPath, normalizeTopLevelPublicClasses(source), StandardCharsets.UTF_8);
|
|
25
|
+
GenericPracticeRewriter.main(
|
|
26
|
+
new String[] {
|
|
27
|
+
inputPath.toString(),
|
|
28
|
+
outputPath.toString(),
|
|
29
|
+
executionStyle,
|
|
30
|
+
entryName,
|
|
31
|
+
});
|
|
32
|
+
String rewrittenSource = Files.readString(outputPath, StandardCharsets.UTF_8);
|
|
33
|
+
String renamedExports =
|
|
34
|
+
exportsSource.replaceAll("\\bpublic class Exports\\b", "public class " + exportsClassName);
|
|
35
|
+
return "package " + packageName + ";\n\n" + rewrittenSource.trim() + "\n\n" + renamedExports.trim() + "\n";
|
|
36
|
+
} finally {
|
|
37
|
+
try (var paths = Files.walk(workDir)) {
|
|
38
|
+
paths
|
|
39
|
+
.sorted((left, right) -> right.getNameCount() - left.getNameCount())
|
|
40
|
+
.forEach(
|
|
41
|
+
path -> {
|
|
42
|
+
try {
|
|
43
|
+
Files.deleteIfExists(path);
|
|
44
|
+
} catch (Exception ignored) {
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private static String normalizeTopLevelPublicClasses(String source) {
|
|
52
|
+
return source.replaceAll("(^|\\n)\\s*public\\s+class\\s+", "$1class ");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -118,27 +118,14 @@ function createConsoleProxy(output) {
|
|
|
118
118
|
|
|
119
119
|
function isLikelyTreeNodeValue(value) {
|
|
120
120
|
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
Object.prototype.hasOwnProperty.call(value, 'value');
|
|
124
|
-
const hasTreeLinks =
|
|
125
|
-
Object.prototype.hasOwnProperty.call(value, 'left') ||
|
|
126
|
-
Object.prototype.hasOwnProperty.call(value, 'right');
|
|
127
|
-
return hasValue && hasTreeLinks;
|
|
121
|
+
if (value.__type__ === 'TreeNode') return true;
|
|
122
|
+
return value?.constructor?.name === 'TreeNode';
|
|
128
123
|
}
|
|
129
124
|
|
|
130
125
|
function isLikelyListNodeValue(value) {
|
|
131
126
|
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
Object.prototype.hasOwnProperty.call(value, 'value');
|
|
135
|
-
const hasTreeLinks =
|
|
136
|
-
Object.prototype.hasOwnProperty.call(value, 'left') ||
|
|
137
|
-
Object.prototype.hasOwnProperty.call(value, 'right');
|
|
138
|
-
const hasListLinks =
|
|
139
|
-
Object.prototype.hasOwnProperty.call(value, 'next') ||
|
|
140
|
-
Object.prototype.hasOwnProperty.call(value, 'prev');
|
|
141
|
-
return hasValue && hasListLinks && !hasTreeLinks;
|
|
127
|
+
if (value.__type__ === 'ListNode') return true;
|
|
128
|
+
return value?.constructor?.name === 'ListNode';
|
|
142
129
|
}
|
|
143
130
|
|
|
144
131
|
function getCustomClassName(value) {
|
|
@@ -199,25 +186,33 @@ function serializeValue(
|
|
|
199
186
|
const nodeId = `${nodePrefix}-${nodeRefState.nextId++}`;
|
|
200
187
|
nodeRefState.ids.set(value, nodeId);
|
|
201
188
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
189
|
+
const out =
|
|
190
|
+
nodePrefix === 'tree'
|
|
191
|
+
? {
|
|
192
|
+
__type__: 'TreeNode',
|
|
193
|
+
__id__: nodeId,
|
|
194
|
+
val: serializeValue(value.val ?? value.value ?? null, depth + 1, seen, nodeRefState),
|
|
195
|
+
left: serializeValue(value.left ?? null, depth + 1, seen, nodeRefState),
|
|
196
|
+
right: serializeValue(value.right ?? null, depth + 1, seen, nodeRefState),
|
|
197
|
+
}
|
|
198
|
+
: {
|
|
199
|
+
__type__: 'ListNode',
|
|
200
|
+
__id__: nodeId,
|
|
201
|
+
val: serializeValue(value.val ?? value.value ?? null, depth + 1, seen, nodeRefState),
|
|
202
|
+
next: serializeValue(value.next ?? null, depth + 1, seen, nodeRefState),
|
|
203
|
+
...(Object.prototype.hasOwnProperty.call(value, 'prev')
|
|
204
|
+
? { prev: serializeValue(value.prev ?? null, depth + 1, seen, nodeRefState) }
|
|
205
|
+
: {}),
|
|
206
|
+
};
|
|
207
|
+
const skipped =
|
|
208
|
+
nodePrefix === 'tree'
|
|
209
|
+
? new Set(['__id__', '__type__', '__class__', 'val', 'value', 'left', 'right'])
|
|
210
|
+
: new Set(['__id__', '__type__', '__class__', 'val', 'value', 'next', 'prev']);
|
|
211
|
+
for (const [k, v] of Object.entries(value)) {
|
|
212
|
+
if (skipped.has(k)) continue;
|
|
213
|
+
out[k] = serializeValue(v, depth + 1, seen, nodeRefState);
|
|
210
214
|
}
|
|
211
|
-
|
|
212
|
-
return {
|
|
213
|
-
__type__: 'ListNode',
|
|
214
|
-
__id__: nodeId,
|
|
215
|
-
val: serializeValue(value.val ?? value.value ?? null, depth + 1, seen, nodeRefState),
|
|
216
|
-
next: serializeValue(value.next ?? null, depth + 1, seen, nodeRefState),
|
|
217
|
-
...(Object.prototype.hasOwnProperty.call(value, 'prev')
|
|
218
|
-
? { prev: serializeValue(value.prev ?? null, depth + 1, seen, nodeRefState) }
|
|
219
|
-
: {}),
|
|
220
|
-
};
|
|
215
|
+
return out;
|
|
221
216
|
}
|
|
222
217
|
|
|
223
218
|
const customClassName = getCustomClassName(value);
|
|
@@ -273,25 +268,32 @@ function serializeTopLevelValue(value, nodeRefState) {
|
|
|
273
268
|
nodeRefState.ids.set(objectValue, nodeId);
|
|
274
269
|
}
|
|
275
270
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
271
|
+
const out =
|
|
272
|
+
isTree
|
|
273
|
+
? {
|
|
274
|
+
__type__: 'TreeNode',
|
|
275
|
+
__id__: nodeId,
|
|
276
|
+
val: serializeValue(nodeValue.val ?? nodeValue.value ?? null, 1, new WeakSet(), nodeRefState),
|
|
277
|
+
left: serializeValue(nodeValue.left ?? null, 1, new WeakSet(), nodeRefState),
|
|
278
|
+
right: serializeValue(nodeValue.right ?? null, 1, new WeakSet(), nodeRefState),
|
|
279
|
+
}
|
|
280
|
+
: {
|
|
281
|
+
__type__: 'ListNode',
|
|
282
|
+
__id__: nodeId,
|
|
283
|
+
val: serializeValue(nodeValue.val ?? nodeValue.value ?? null, 1, new WeakSet(), nodeRefState),
|
|
284
|
+
next: serializeValue(nodeValue.next ?? null, 1, new WeakSet(), nodeRefState),
|
|
285
|
+
...('prev' in nodeValue
|
|
286
|
+
? { prev: serializeValue(nodeValue.prev ?? null, 1, new WeakSet(), nodeRefState) }
|
|
287
|
+
: {}),
|
|
288
|
+
};
|
|
289
|
+
const skipped = isTree
|
|
290
|
+
? new Set(['__id__', '__type__', '__class__', 'val', 'value', 'left', 'right'])
|
|
291
|
+
: new Set(['__id__', '__type__', '__class__', 'val', 'value', 'next', 'prev']);
|
|
292
|
+
for (const [k, v] of Object.entries(nodeValue)) {
|
|
293
|
+
if (skipped.has(k)) continue;
|
|
294
|
+
out[k] = serializeValue(v, 1, new WeakSet(), nodeRefState);
|
|
284
295
|
}
|
|
285
|
-
|
|
286
|
-
return {
|
|
287
|
-
__type__: 'ListNode',
|
|
288
|
-
__id__: nodeId,
|
|
289
|
-
val: serializeValue(nodeValue.val ?? nodeValue.value ?? null, 1, new WeakSet(), nodeRefState),
|
|
290
|
-
next: serializeValue(nodeValue.next ?? null, 1, new WeakSet(), nodeRefState),
|
|
291
|
-
...('prev' in nodeValue
|
|
292
|
-
? { prev: serializeValue(nodeValue.prev ?? null, 1, new WeakSet(), nodeRefState) }
|
|
293
|
-
: {}),
|
|
294
|
-
};
|
|
296
|
+
return out;
|
|
295
297
|
}
|
|
296
298
|
|
|
297
299
|
const customClassName = getCustomClassName(value);
|
|
@@ -450,12 +452,17 @@ function materializeTreeInput(value) {
|
|
|
450
452
|
}
|
|
451
453
|
if (isLikelyTreeNodeValue(value) || value.__type__ === 'TreeNode') {
|
|
452
454
|
const nodeValue = value;
|
|
453
|
-
|
|
455
|
+
const node = {
|
|
454
456
|
val: nodeValue.val ?? nodeValue.value ?? null,
|
|
455
457
|
value: nodeValue.val ?? nodeValue.value ?? null,
|
|
456
458
|
left: materializeTreeInput(nodeValue.left ?? null),
|
|
457
459
|
right: materializeTreeInput(nodeValue.right ?? null),
|
|
458
460
|
};
|
|
461
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
462
|
+
if (key === '__id__' || key === '__type__' || key === '__class__' || key === 'val' || key === 'value' || key === 'left' || key === 'right') continue;
|
|
463
|
+
node[key] = materializeTreeInput(nested);
|
|
464
|
+
}
|
|
465
|
+
return node;
|
|
459
466
|
}
|
|
460
467
|
return value;
|
|
461
468
|
}
|
|
@@ -493,6 +500,10 @@ function materializeListInput(value, refs = new Map(), materialized = new WeakMa
|
|
|
493
500
|
refs.set(value.__id__, node);
|
|
494
501
|
}
|
|
495
502
|
node.next = materializeListInput(value.next ?? null, refs, materialized);
|
|
503
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
504
|
+
if (key === '__id__' || key === '__type__' || key === '__class__' || key === 'val' || key === 'value' || key === 'next') continue;
|
|
505
|
+
node[key] = materializeListInput(nested, refs, materialized);
|
|
506
|
+
}
|
|
496
507
|
return node;
|
|
497
508
|
}
|
|
498
509
|
return value;
|
|
@@ -571,6 +582,7 @@ function applyInputMaterializers(inputs, materializers) {
|
|
|
571
582
|
function inferFallbackInputMaterializers(inputs) {
|
|
572
583
|
if (!inputs || typeof inputs !== 'object' || Array.isArray(inputs)) return {};
|
|
573
584
|
const inferred = {};
|
|
585
|
+
const LINKED_LIST_FALLBACK_NAMES = new Set(['head', 'l1', 'l2', 'list1', 'list2', 'node']);
|
|
574
586
|
for (const [name, value] of Object.entries(inputs)) {
|
|
575
587
|
const lowerName = String(name || '').toLowerCase();
|
|
576
588
|
if (!Array.isArray(value)) continue;
|
|
@@ -578,7 +590,7 @@ function inferFallbackInputMaterializers(inputs) {
|
|
|
578
590
|
inferred[name] = 'tree';
|
|
579
591
|
continue;
|
|
580
592
|
}
|
|
581
|
-
if (lowerName === 'head' || lowerName.endsWith('head') ||
|
|
593
|
+
if (lowerName === 'head' || lowerName.endsWith('head') || LINKED_LIST_FALLBACK_NAMES.has(lowerName)) {
|
|
582
594
|
inferred[name] = 'list';
|
|
583
595
|
continue;
|
|
584
596
|
}
|
|
@@ -799,9 +811,12 @@ function createTraceRecorder(options = {}) {
|
|
|
799
811
|
const trace = [];
|
|
800
812
|
const callStack = [];
|
|
801
813
|
const pendingAccessesByFrame = new Map();
|
|
814
|
+
const deferredAccessesByFrame = new Map();
|
|
802
815
|
const lineHitCount = new Map();
|
|
803
816
|
const stableNodeRefState = { ids: new Map(), nextId: 1 };
|
|
804
817
|
const maxTraceSteps = getNumericOption(options.maxTraceSteps, 4000);
|
|
818
|
+
const maxStoredEvents = getNumericOption(options.maxStoredEvents, maxTraceSteps);
|
|
819
|
+
const effectiveMaxTraceSteps = Math.min(maxTraceSteps, maxStoredEvents);
|
|
805
820
|
const maxLineEvents = getNumericOption(options.maxLineEvents, 12000);
|
|
806
821
|
const maxSingleLineHits = getNumericOption(options.maxSingleLineHits, 1000);
|
|
807
822
|
const maxCallDepth = getNumericOption(options.maxCallDepth, 2000);
|
|
@@ -1099,11 +1114,22 @@ function createTraceRecorder(options = {}) {
|
|
|
1099
1114
|
return undefined;
|
|
1100
1115
|
}
|
|
1101
1116
|
const pending = pendingAccessesByFrame.get(frameId);
|
|
1102
|
-
|
|
1117
|
+
const deferred = deferredAccessesByFrame.get(frameId);
|
|
1118
|
+
let deferredReady = [];
|
|
1119
|
+
if (deferred && Array.isArray(deferred.accesses) && deferred.accesses.length > 0) {
|
|
1120
|
+
if (deferred.skipLineFlushes > 0) {
|
|
1121
|
+
deferred.skipLineFlushes -= 1;
|
|
1122
|
+
deferredAccessesByFrame.set(frameId, deferred);
|
|
1123
|
+
} else {
|
|
1124
|
+
deferredReady = deferred.accesses;
|
|
1125
|
+
deferredAccessesByFrame.delete(frameId);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
if ((!Array.isArray(pending) || pending.length === 0) && deferredReady.length === 0) {
|
|
1103
1129
|
return undefined;
|
|
1104
1130
|
}
|
|
1105
1131
|
pendingAccessesByFrame.delete(frameId);
|
|
1106
|
-
return pending.map((access) => ({
|
|
1132
|
+
return [...(Array.isArray(pending) ? pending : []), ...deferredReady].map((access) => ({
|
|
1107
1133
|
variable: access.variable,
|
|
1108
1134
|
kind: access.kind,
|
|
1109
1135
|
...(Array.isArray(access.indices) && access.indices.length > 0
|
|
@@ -1115,13 +1141,13 @@ function createTraceRecorder(options = {}) {
|
|
|
1115
1141
|
}
|
|
1116
1142
|
|
|
1117
1143
|
function appendTrace(step, frameId = getCurrentFrameId()) {
|
|
1118
|
-
if (trace.length >=
|
|
1144
|
+
if (trace.length >= effectiveMaxTraceSteps) {
|
|
1119
1145
|
const lineNumber = normalizeLine(step?.line, 1);
|
|
1120
1146
|
if (!traceLimitExceeded) {
|
|
1121
1147
|
traceLimitExceeded = true;
|
|
1122
1148
|
timeoutReason = 'trace-limit';
|
|
1123
1149
|
}
|
|
1124
|
-
if (!timeoutRecorded && trace.length <
|
|
1150
|
+
if (!timeoutRecorded && trace.length < effectiveMaxTraceSteps) {
|
|
1125
1151
|
trace.push({
|
|
1126
1152
|
line: lineNumber,
|
|
1127
1153
|
event: 'timeout',
|
|
@@ -1131,13 +1157,51 @@ function createTraceRecorder(options = {}) {
|
|
|
1131
1157
|
});
|
|
1132
1158
|
timeoutRecorded = true;
|
|
1133
1159
|
}
|
|
1134
|
-
throw createLimitError('trace-limit', lineNumber, `Exceeded ${
|
|
1160
|
+
throw createLimitError('trace-limit', lineNumber, `Exceeded ${effectiveMaxTraceSteps} trace steps`);
|
|
1135
1161
|
}
|
|
1136
1162
|
const accesses = flushPendingAccesses(frameId);
|
|
1137
|
-
|
|
1163
|
+
const nextStep = {
|
|
1138
1164
|
...step,
|
|
1139
1165
|
...(accesses ? { accesses } : {}),
|
|
1140
|
-
}
|
|
1166
|
+
};
|
|
1167
|
+
const previous = trace[trace.length - 1];
|
|
1168
|
+
if (canMergeConsecutiveLineSteps(previous, nextStep)) {
|
|
1169
|
+
previous.variables = {
|
|
1170
|
+
...(previous.variables || {}),
|
|
1171
|
+
...(nextStep.variables || {}),
|
|
1172
|
+
};
|
|
1173
|
+
if (nextStep.accesses?.length) {
|
|
1174
|
+
previous.accesses = [...(previous.accesses || []), ...nextStep.accesses];
|
|
1175
|
+
}
|
|
1176
|
+
if (nextStep.visualization !== undefined) {
|
|
1177
|
+
previous.visualization = nextStep.visualization;
|
|
1178
|
+
}
|
|
1179
|
+
previous.callStack = nextStep.callStack;
|
|
1180
|
+
previous.function = nextStep.function;
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
trace.push(nextStep);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
function canMergeConsecutiveLineSteps(previous, nextStep) {
|
|
1187
|
+
if (!previous || !nextStep) return false;
|
|
1188
|
+
if (previous.event !== 'line' || nextStep.event !== 'line') return false;
|
|
1189
|
+
if (previous.line !== nextStep.line) return false;
|
|
1190
|
+
if (previous.function !== nextStep.function) return false;
|
|
1191
|
+
return sameCallStackVisit(previous.callStack, nextStep.callStack);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
function sameCallStackVisit(left, right) {
|
|
1195
|
+
if (!Array.isArray(left) || !Array.isArray(right)) return false;
|
|
1196
|
+
if (left.length !== right.length) return false;
|
|
1197
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
1198
|
+
const leftFrame = left[index];
|
|
1199
|
+
const rightFrame = right[index];
|
|
1200
|
+
if (!leftFrame || !rightFrame) return false;
|
|
1201
|
+
if (leftFrame.function !== rightFrame.function) return false;
|
|
1202
|
+
if (leftFrame.line !== rightFrame.line) return false;
|
|
1203
|
+
}
|
|
1204
|
+
return true;
|
|
1141
1205
|
}
|
|
1142
1206
|
|
|
1143
1207
|
function markTimeout(reason, lineNumber, message) {
|
|
@@ -1146,7 +1210,7 @@ function createTraceRecorder(options = {}) {
|
|
|
1146
1210
|
traceLimitExceeded = true;
|
|
1147
1211
|
timeoutReason = reason;
|
|
1148
1212
|
}
|
|
1149
|
-
if (!timeoutRecorded && trace.length <
|
|
1213
|
+
if (!timeoutRecorded && trace.length < effectiveMaxTraceSteps) {
|
|
1150
1214
|
appendTrace({
|
|
1151
1215
|
line: normalizedLine,
|
|
1152
1216
|
event: 'timeout',
|
|
@@ -1209,6 +1273,7 @@ function createTraceRecorder(options = {}) {
|
|
|
1209
1273
|
const frame = callStack.pop();
|
|
1210
1274
|
if (frame?.id !== undefined) {
|
|
1211
1275
|
pendingAccessesByFrame.delete(frame.id);
|
|
1276
|
+
deferredAccessesByFrame.delete(frame.id);
|
|
1212
1277
|
}
|
|
1213
1278
|
}
|
|
1214
1279
|
}
|
|
@@ -1324,6 +1389,25 @@ function createTraceRecorder(options = {}) {
|
|
|
1324
1389
|
existing.push(normalized);
|
|
1325
1390
|
pendingAccessesByFrame.set(frameId, existing);
|
|
1326
1391
|
},
|
|
1392
|
+
deferPendingAccesses(lineFlushes = 1) {
|
|
1393
|
+
const frameId = getCurrentFrameId();
|
|
1394
|
+
if (frameId === undefined) {
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
const pending = pendingAccessesByFrame.get(frameId);
|
|
1398
|
+
if (!Array.isArray(pending) || pending.length === 0) {
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
pendingAccessesByFrame.delete(frameId);
|
|
1402
|
+
const existing = deferredAccessesByFrame.get(frameId);
|
|
1403
|
+
deferredAccessesByFrame.set(frameId, {
|
|
1404
|
+
accesses: [...(Array.isArray(existing?.accesses) ? existing.accesses : []), ...pending],
|
|
1405
|
+
skipLineFlushes: Math.max(
|
|
1406
|
+
typeof existing?.skipLineFlushes === 'number' ? existing.skipLineFlushes : 0,
|
|
1407
|
+
Math.max(0, Math.trunc(lineFlushes))
|
|
1408
|
+
),
|
|
1409
|
+
});
|
|
1410
|
+
},
|
|
1327
1411
|
line(lineNumber, snapshotFactory, functionNameOverride, functionStartLine) {
|
|
1328
1412
|
const normalizedLine = normalizeLine(lineNumber, callStack[callStack.length - 1]?.line ?? 1);
|
|
1329
1413
|
|
|
@@ -1412,6 +1496,7 @@ function createTraceRecorder(options = {}) {
|
|
|
1412
1496
|
const frame = callStack.pop();
|
|
1413
1497
|
if (frame?.id !== undefined) {
|
|
1414
1498
|
pendingAccessesByFrame.delete(frame.id);
|
|
1499
|
+
deferredAccessesByFrame.delete(frame.id);
|
|
1415
1500
|
}
|
|
1416
1501
|
}
|
|
1417
1502
|
},
|
|
@@ -1421,6 +1506,7 @@ function createTraceRecorder(options = {}) {
|
|
|
1421
1506
|
const frame = callStack.pop();
|
|
1422
1507
|
if (frame?.id !== undefined) {
|
|
1423
1508
|
pendingAccessesByFrame.delete(frame.id);
|
|
1509
|
+
deferredAccessesByFrame.delete(frame.id);
|
|
1424
1510
|
}
|
|
1425
1511
|
}
|
|
1426
1512
|
},
|
|
@@ -1724,6 +1810,8 @@ function shouldTraceStatement(ts, statement) {
|
|
|
1724
1810
|
return !(
|
|
1725
1811
|
ts.isFunctionDeclaration(statement) ||
|
|
1726
1812
|
ts.isClassDeclaration(statement) ||
|
|
1813
|
+
ts.isWhileStatement(statement) ||
|
|
1814
|
+
ts.isForStatement(statement) ||
|
|
1727
1815
|
ts.isEmptyStatement(statement) ||
|
|
1728
1816
|
ts.isBlock(statement)
|
|
1729
1817
|
);
|
|
@@ -2134,6 +2222,162 @@ function createTraceLineStatement(ts, sourceFile, statement, variableNames, line
|
|
|
2134
2222
|
);
|
|
2135
2223
|
}
|
|
2136
2224
|
|
|
2225
|
+
function ensureBlockStatement(ts, statement) {
|
|
2226
|
+
if (ts.isBlock(statement)) {
|
|
2227
|
+
return statement;
|
|
2228
|
+
}
|
|
2229
|
+
return ts.factory.createBlock([statement], true);
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
function rewriteWhileStatementForTracing(ts, sourceFile, whileStatement, variableNames, lineFunctionMap, defaultFunctionName) {
|
|
2233
|
+
const originalNode = ts.getOriginalNode(whileStatement) ?? whileStatement;
|
|
2234
|
+
const tracedLine = createTraceLineStatement(
|
|
2235
|
+
ts,
|
|
2236
|
+
sourceFile,
|
|
2237
|
+
originalNode,
|
|
2238
|
+
variableNames,
|
|
2239
|
+
lineFunctionMap,
|
|
2240
|
+
defaultFunctionName
|
|
2241
|
+
);
|
|
2242
|
+
const visitedBodyBlock = ensureBlockStatement(ts, whileStatement.statement);
|
|
2243
|
+
const guardedBreak = ts.factory.createIfStatement(
|
|
2244
|
+
ts.factory.createPrefixUnaryExpression(
|
|
2245
|
+
ts.SyntaxKind.ExclamationToken,
|
|
2246
|
+
whileStatement.expression
|
|
2247
|
+
),
|
|
2248
|
+
ts.factory.createBreakStatement(),
|
|
2249
|
+
undefined
|
|
2250
|
+
);
|
|
2251
|
+
return ts.factory.createWhileStatement(
|
|
2252
|
+
ts.factory.createTrue(),
|
|
2253
|
+
ts.factory.createBlock(
|
|
2254
|
+
[
|
|
2255
|
+
tracedLine,
|
|
2256
|
+
guardedBreak,
|
|
2257
|
+
...visitedBodyBlock.statements,
|
|
2258
|
+
],
|
|
2259
|
+
true
|
|
2260
|
+
)
|
|
2261
|
+
);
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
function rewriteForStatementForTracing(ts, sourceFile, forStatement, variableNames, lineFunctionMap, defaultFunctionName) {
|
|
2265
|
+
const originalNode = ts.getOriginalNode(forStatement) ?? forStatement;
|
|
2266
|
+
const tracedLineCall = createTraceLineStatement(
|
|
2267
|
+
ts,
|
|
2268
|
+
sourceFile,
|
|
2269
|
+
originalNode,
|
|
2270
|
+
variableNames,
|
|
2271
|
+
lineFunctionMap,
|
|
2272
|
+
defaultFunctionName
|
|
2273
|
+
).expression;
|
|
2274
|
+
const condition = forStatement.condition ?? ts.factory.createTrue();
|
|
2275
|
+
const tracedCondition = ts.factory.createParenthesizedExpression(
|
|
2276
|
+
ts.factory.createBinaryExpression(
|
|
2277
|
+
tracedLineCall,
|
|
2278
|
+
ts.SyntaxKind.CommaToken,
|
|
2279
|
+
condition
|
|
2280
|
+
)
|
|
2281
|
+
);
|
|
2282
|
+
return ts.factory.updateForStatement(
|
|
2283
|
+
forStatement,
|
|
2284
|
+
forStatement.initializer,
|
|
2285
|
+
tracedCondition,
|
|
2286
|
+
forStatement.incrementor,
|
|
2287
|
+
forStatement.statement
|
|
2288
|
+
);
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
function rewriteForOfStatementForTracing(ts, sourceFile, forOfStatement, variableNames, lineFunctionMap, defaultFunctionName) {
|
|
2292
|
+
const originalNode = ts.getOriginalNode(forOfStatement) ?? forOfStatement;
|
|
2293
|
+
const lineNumber = ts.getLineAndCharacterOfPosition(sourceFile, originalNode.getStart(sourceFile)).line + 1;
|
|
2294
|
+
const tempIndexName = `__traceForOfIndex_${lineNumber}_${originalNode.pos < 0 ? 0 : originalNode.pos}`;
|
|
2295
|
+
const tempArrayName = `__traceForOfArray_${lineNumber}_${originalNode.pos < 0 ? 0 : originalNode.pos}`;
|
|
2296
|
+
const arrayId = ts.factory.createIdentifier(tempArrayName);
|
|
2297
|
+
const indexId = ts.factory.createIdentifier(tempIndexName);
|
|
2298
|
+
const visitedBodyBlock = ensureBlockStatement(ts, forOfStatement.statement);
|
|
2299
|
+
const tracedLine = createTraceLineStatement(
|
|
2300
|
+
ts,
|
|
2301
|
+
sourceFile,
|
|
2302
|
+
originalNode,
|
|
2303
|
+
variableNames,
|
|
2304
|
+
lineFunctionMap,
|
|
2305
|
+
defaultFunctionName
|
|
2306
|
+
);
|
|
2307
|
+
const tracedLineCall = createTraceLineStatement(
|
|
2308
|
+
ts,
|
|
2309
|
+
sourceFile,
|
|
2310
|
+
originalNode,
|
|
2311
|
+
variableNames,
|
|
2312
|
+
lineFunctionMap,
|
|
2313
|
+
defaultFunctionName
|
|
2314
|
+
).expression;
|
|
2315
|
+
|
|
2316
|
+
let bindingStatement;
|
|
2317
|
+
if (ts.isVariableDeclarationList(forOfStatement.initializer)) {
|
|
2318
|
+
const declaration = forOfStatement.initializer.declarations[0];
|
|
2319
|
+
bindingStatement = ts.factory.createVariableStatement(
|
|
2320
|
+
undefined,
|
|
2321
|
+
ts.factory.updateVariableDeclarationList(forOfStatement.initializer, [
|
|
2322
|
+
ts.factory.updateVariableDeclaration(
|
|
2323
|
+
declaration,
|
|
2324
|
+
declaration.name,
|
|
2325
|
+
declaration.exclamationToken,
|
|
2326
|
+
declaration.type,
|
|
2327
|
+
ts.factory.createElementAccessExpression(arrayId, indexId)
|
|
2328
|
+
),
|
|
2329
|
+
])
|
|
2330
|
+
);
|
|
2331
|
+
} else {
|
|
2332
|
+
bindingStatement = ts.factory.createExpressionStatement(
|
|
2333
|
+
ts.factory.createAssignment(forOfStatement.initializer, ts.factory.createElementAccessExpression(arrayId, indexId))
|
|
2334
|
+
);
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
return ts.factory.createBlock(
|
|
2338
|
+
[
|
|
2339
|
+
tracedLine,
|
|
2340
|
+
ts.factory.createVariableStatement(
|
|
2341
|
+
undefined,
|
|
2342
|
+
ts.factory.createVariableDeclarationList(
|
|
2343
|
+
[ts.factory.createVariableDeclaration(arrayId, undefined, undefined, forOfStatement.expression)],
|
|
2344
|
+
ts.NodeFlags.Const
|
|
2345
|
+
)
|
|
2346
|
+
),
|
|
2347
|
+
ts.factory.createExpressionStatement(
|
|
2348
|
+
ts.factory.createCallExpression(
|
|
2349
|
+
ts.factory.createPropertyAccessExpression(
|
|
2350
|
+
ts.factory.createIdentifier('__traceRecorder'),
|
|
2351
|
+
ts.factory.createIdentifier('deferPendingAccesses')
|
|
2352
|
+
),
|
|
2353
|
+
undefined,
|
|
2354
|
+
[ts.factory.createNumericLiteral(1)]
|
|
2355
|
+
)
|
|
2356
|
+
),
|
|
2357
|
+
ts.factory.createForStatement(
|
|
2358
|
+
ts.factory.createVariableDeclarationList(
|
|
2359
|
+
[ts.factory.createVariableDeclaration(indexId, undefined, undefined, ts.factory.createNumericLiteral(0))],
|
|
2360
|
+
ts.NodeFlags.Let
|
|
2361
|
+
),
|
|
2362
|
+
ts.factory.createParenthesizedExpression(
|
|
2363
|
+
ts.factory.createBinaryExpression(
|
|
2364
|
+
tracedLineCall,
|
|
2365
|
+
ts.SyntaxKind.CommaToken,
|
|
2366
|
+
ts.factory.createBinaryExpression(
|
|
2367
|
+
indexId,
|
|
2368
|
+
ts.SyntaxKind.LessThanToken,
|
|
2369
|
+
ts.factory.createPropertyAccessExpression(arrayId, 'length')
|
|
2370
|
+
)
|
|
2371
|
+
)
|
|
2372
|
+
),
|
|
2373
|
+
ts.factory.createPostfixIncrement(indexId),
|
|
2374
|
+
ts.factory.createBlock([bindingStatement, ...visitedBodyBlock.statements], true)
|
|
2375
|
+
),
|
|
2376
|
+
],
|
|
2377
|
+
true
|
|
2378
|
+
);
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2137
2381
|
function instrumentStatementList(
|
|
2138
2382
|
ts,
|
|
2139
2383
|
sourceFile,
|
|
@@ -2530,6 +2774,42 @@ async function instrumentCodeForTracing(sourceCode, language, traceFunctionName)
|
|
|
2530
2774
|
);
|
|
2531
2775
|
}
|
|
2532
2776
|
|
|
2777
|
+
if (ts.isWhileStatement(node)) {
|
|
2778
|
+
const visited = ts.visitEachChild(node, visit, context);
|
|
2779
|
+
return rewriteWhileStatementForTracing(
|
|
2780
|
+
ts,
|
|
2781
|
+
sourceFile,
|
|
2782
|
+
visited,
|
|
2783
|
+
variableNames,
|
|
2784
|
+
lineFunctionMap,
|
|
2785
|
+
effectiveFunctionName
|
|
2786
|
+
);
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2789
|
+
if (ts.isForStatement(node)) {
|
|
2790
|
+
const visited = ts.visitEachChild(node, visit, context);
|
|
2791
|
+
return rewriteForStatementForTracing(
|
|
2792
|
+
ts,
|
|
2793
|
+
sourceFile,
|
|
2794
|
+
visited,
|
|
2795
|
+
variableNames,
|
|
2796
|
+
lineFunctionMap,
|
|
2797
|
+
effectiveFunctionName
|
|
2798
|
+
);
|
|
2799
|
+
}
|
|
2800
|
+
|
|
2801
|
+
if (ts.isForOfStatement(node)) {
|
|
2802
|
+
const visited = ts.visitEachChild(node, visit, context);
|
|
2803
|
+
return rewriteForOfStatementForTracing(
|
|
2804
|
+
ts,
|
|
2805
|
+
sourceFile,
|
|
2806
|
+
visited,
|
|
2807
|
+
variableNames,
|
|
2808
|
+
lineFunctionMap,
|
|
2809
|
+
effectiveFunctionName
|
|
2810
|
+
);
|
|
2811
|
+
}
|
|
2812
|
+
|
|
2533
2813
|
if (ts.isCaseClause(node)) {
|
|
2534
2814
|
const visited = ts.visitEachChild(node, visit, context);
|
|
2535
2815
|
return ts.factory.updateCaseClause(
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
scope.__TRACECODE_PYTHON_HARNESS__ = Object.freeze({
|
|
12
12
|
PYTHON_CLASS_DEFINITIONS: "\nclass TreeNode:\n def __init__(self, val=0, left=None, right=None):\n self.val = val\n self.value = val\n self.left = left\n self.right = right\n def __getitem__(self, key):\n if key == 'val': return getattr(self, 'val', getattr(self, 'value', None))\n if key == 'value': return getattr(self, 'value', getattr(self, 'val', None))\n if key == 'left': return self.left\n if key == 'right': return self.right\n raise KeyError(key)\n def get(self, key, default=None):\n if key == 'val': return getattr(self, 'val', getattr(self, 'value', default))\n if key == 'value': return getattr(self, 'value', getattr(self, 'val', default))\n if key == 'left': return self.left\n if key == 'right': return self.right\n return default\n def __repr__(self):\n return f\"TreeNode({getattr(self, 'val', getattr(self, 'value', None))})\"\n\nclass ListNode:\n def __init__(self, val=0, next=None):\n self.val = val\n self.value = val\n self.next = next\n def __getitem__(self, key):\n if key == 'val': return getattr(self, 'val', getattr(self, 'value', None))\n if key == 'value': return getattr(self, 'value', getattr(self, 'val', None))\n if key == 'next': return self.next\n raise KeyError(key)\n def get(self, key, default=None):\n if key == 'val': return getattr(self, 'val', getattr(self, 'value', default))\n if key == 'value': return getattr(self, 'value', getattr(self, 'val', default))\n if key == 'next': return self.next\n return default\n def __repr__(self):\n return f\"ListNode({getattr(self, 'val', getattr(self, 'value', None))})\"\n",
|
|
13
13
|
PYTHON_CONVERSION_HELPERS: "\ndef _ensure_node_value_aliases(node):\n if node is None:\n return node\n try:\n has_val = hasattr(node, 'val')\n has_value = hasattr(node, 'value')\n if has_value and not has_val:\n try:\n setattr(node, 'val', getattr(node, 'value'))\n except Exception:\n pass\n elif has_val and not has_value:\n try:\n setattr(node, 'value', getattr(node, 'val'))\n except Exception:\n pass\n except Exception:\n pass\n return node\n\ndef _dict_to_tree(d):\n if d is None:\n return None\n if not isinstance(d, dict):\n return d\n if 'val' not in d and 'value' not in d:\n return d\n node = TreeNode(d.get('val', d.get('value', 0)))\n _ensure_node_value_aliases(node)\n node.left = _dict_to_tree(d.get('left'))\n node.right = _dict_to_tree(d.get('right'))\n return node\n\ndef _dict_to_list(d, _refs=None):\n if _refs is None:\n _refs = {}\n if d is None:\n return None\n if not isinstance(d, dict):\n return d\n if '__ref__' in d:\n return _refs.get(d.get('__ref__'))\n if 'val' not in d and 'value' not in d:\n return d\n node = ListNode(d.get('val', d.get('value', 0)))\n _ensure_node_value_aliases(node)\n node_id = d.get('__id__')\n if isinstance(node_id, str) and node_id:\n _refs[node_id] = node\n node.next = _dict_to_list(d.get('next'), _refs)\n return node\n",
|
|
14
|
-
PYTHON_TRACE_SERIALIZE_FUNCTION: "\n# Sentinel to mark skipped values (functions, etc.) - distinct from None\n_SKIP_SENTINEL = \"__TRACECODE_SKIP__\"\n_MAX_SERIALIZE_DEPTH = 48\n_MAX_OBJECT_FIELDS = 32\n\ndef _serialize(obj, depth=0, node_refs=None):\n if node_refs is None:\n node_refs = {}\n if isinstance(obj, (bool, int, str, type(None))):\n return obj\n elif isinstance(obj, float):\n if not math.isfinite(obj):\n if math.isnan(obj):\n return \"NaN\"\n return \"Infinity\" if obj > 0 else \"-Infinity\"\n return obj\n if depth > _MAX_SERIALIZE_DEPTH:\n return \"<max depth>\"\n elif isinstance(obj, (list, tuple)):\n return [_serialize(x, depth + 1, node_refs) for x in obj]\n elif getattr(obj, '__class__', None) and getattr(obj.__class__, '__name__', '') == 'deque':\n return [_serialize(x, depth + 1, node_refs) for x in obj]\n elif isinstance(obj, dict):\n return {str(k): _serialize(v, depth + 1, node_refs) for k, v in obj.items()}\n elif isinstance(obj, set):\n # Use try/except for sorting to handle heterogeneous sets\n try:\n sorted_vals = sorted([_serialize(x, depth + 1, node_refs) for x in obj])\n except TypeError:\n sorted_vals = [_serialize(x, depth + 1, node_refs) for x in obj]\n return {\"__type__\": \"set\", \"values\": sorted_vals}\n elif (
|
|
15
|
-
PYTHON_EXECUTE_SERIALIZE_FUNCTION: "\n_MAX_SERIALIZE_DEPTH = 48\n\ndef _serialize(obj, depth=0):\n if isinstance(obj, (bool, int, str, type(None))):\n return obj\n elif isinstance(obj, float):\n if not math.isfinite(obj):\n if math.isnan(obj):\n return \"NaN\"\n return \"Infinity\" if obj > 0 else \"-Infinity\"\n return obj\n if depth > _MAX_SERIALIZE_DEPTH:\n return \"<max depth>\"\n elif isinstance(obj, (list, tuple)):\n return [_serialize(x, depth + 1) for x in obj]\n elif getattr(obj, '__class__', None) and getattr(obj.__class__, '__name__', '') == 'deque':\n return [_serialize(x, depth + 1) for x in obj]\n elif isinstance(obj, dict):\n return {str(k): _serialize(v, depth + 1) for k, v in obj.items()}\n elif isinstance(obj, set):\n try:\n return {\"__type__\": \"set\", \"values\": sorted([_serialize(x, depth + 1) for x in obj])}\n except TypeError:\n return {\"__type__\": \"set\", \"values\": [_serialize(x, depth + 1) for x in obj]}\n elif (
|
|
16
|
-
PYTHON_SERIALIZE_FUNCTION: "\n_MAX_SERIALIZE_DEPTH = 48\n\ndef _serialize(obj, depth=0):\n if isinstance(obj, (bool, int, str, type(None))):\n return obj\n elif isinstance(obj, float):\n if not math.isfinite(obj):\n if math.isnan(obj):\n return \"NaN\"\n return \"Infinity\" if obj > 0 else \"-Infinity\"\n return obj\n if depth > _MAX_SERIALIZE_DEPTH:\n return \"<max depth>\"\n elif isinstance(obj, (list, tuple)):\n return [_serialize(x, depth + 1) for x in obj]\n elif getattr(obj, '__class__', None) and getattr(obj.__class__, '__name__', '') == 'deque':\n return [_serialize(x, depth + 1) for x in obj]\n elif isinstance(obj, dict):\n return {str(k): _serialize(v, depth + 1) for k, v in obj.items()}\n elif isinstance(obj, set):\n try:\n return {\"__type__\": \"set\", \"values\": sorted([_serialize(x, depth + 1) for x in obj])}\n except TypeError:\n return {\"__type__\": \"set\", \"values\": [_serialize(x, depth + 1) for x in obj]}\n elif (
|
|
14
|
+
PYTHON_TRACE_SERIALIZE_FUNCTION: "\n# Sentinel to mark skipped values (functions, etc.) - distinct from None\n_SKIP_SENTINEL = \"__TRACECODE_SKIP__\"\n_MAX_SERIALIZE_DEPTH = 48\n_MAX_OBJECT_FIELDS = 32\n\ndef _serialize(obj, depth=0, node_refs=None):\n if node_refs is None:\n node_refs = {}\n if isinstance(obj, (bool, int, str, type(None))):\n return obj\n elif isinstance(obj, float):\n if not math.isfinite(obj):\n if math.isnan(obj):\n return \"NaN\"\n return \"Infinity\" if obj > 0 else \"-Infinity\"\n return obj\n if depth > _MAX_SERIALIZE_DEPTH:\n return \"<max depth>\"\n elif isinstance(obj, (list, tuple)):\n return [_serialize(x, depth + 1, node_refs) for x in obj]\n elif getattr(obj, '__class__', None) and getattr(obj.__class__, '__name__', '') == 'deque':\n return [_serialize(x, depth + 1, node_refs) for x in obj]\n elif isinstance(obj, dict):\n return {str(k): _serialize(v, depth + 1, node_refs) for k, v in obj.items()}\n elif isinstance(obj, set):\n # Use try/except for sorting to handle heterogeneous sets\n try:\n sorted_vals = sorted([_serialize(x, depth + 1, node_refs) for x in obj])\n except TypeError:\n sorted_vals = [_serialize(x, depth + 1, node_refs) for x in obj]\n return {\"__type__\": \"set\", \"values\": sorted_vals}\n elif isinstance(obj, TreeNode):\n obj_ref = id(obj)\n if obj_ref in node_refs:\n return {\"__ref__\": node_refs[obj_ref]}\n node_id = f\"tree-{obj_ref}\"\n node_refs[obj_ref] = node_id\n result = {\n \"__type__\": \"TreeNode\",\n \"__id__\": node_id,\n \"val\": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1, node_refs),\n }\n if hasattr(obj, 'left'):\n result[\"left\"] = _serialize(obj.left, depth + 1, node_refs)\n if hasattr(obj, 'right'):\n result[\"right\"] = _serialize(obj.right, depth + 1, node_refs)\n return result\n elif isinstance(obj, ListNode):\n obj_ref = id(obj)\n if obj_ref in node_refs:\n return {\"__ref__\": node_refs[obj_ref]}\n node_id = f\"list-{obj_ref}\"\n node_refs[obj_ref] = node_id\n result = {\n \"__type__\": \"ListNode\",\n \"__id__\": node_id,\n \"val\": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1, node_refs),\n }\n result[\"next\"] = _serialize(obj.next, depth + 1, node_refs)\n return result\n elif hasattr(obj, '__dict__'):\n obj_ref = id(obj)\n if obj_ref in node_refs:\n return {\"__ref__\": node_refs[obj_ref]}\n node_id = f\"object-{obj_ref}\"\n node_refs[obj_ref] = node_id\n class_name = getattr(getattr(obj, '__class__', None), '__name__', 'object')\n result = {\n \"__type__\": \"object\",\n \"__class__\": class_name,\n \"__id__\": node_id,\n }\n try:\n raw_fields = getattr(obj, '__dict__', None)\n except Exception:\n raw_fields = None\n if isinstance(raw_fields, dict):\n added = 0\n for key, value in raw_fields.items():\n key_str = str(key)\n if key_str.startswith('_'):\n continue\n if callable(value):\n continue\n result[key_str] = _serialize(value, depth + 1, node_refs)\n added += 1\n if added >= _MAX_OBJECT_FIELDS:\n result[\"__truncated__\"] = True\n break\n return result\n elif callable(obj):\n # Skip functions entirely - return sentinel\n return _SKIP_SENTINEL\n else:\n repr_str = repr(obj)\n # Filter out function-like representations (e.g., <function foo at 0x...>)\n if repr_str.startswith('<') and repr_str.endswith('>'):\n return _SKIP_SENTINEL\n return repr_str\n",
|
|
15
|
+
PYTHON_EXECUTE_SERIALIZE_FUNCTION: "\n_MAX_SERIALIZE_DEPTH = 48\n\ndef _serialize(obj, depth=0):\n if isinstance(obj, (bool, int, str, type(None))):\n return obj\n elif isinstance(obj, float):\n if not math.isfinite(obj):\n if math.isnan(obj):\n return \"NaN\"\n return \"Infinity\" if obj > 0 else \"-Infinity\"\n return obj\n if depth > _MAX_SERIALIZE_DEPTH:\n return \"<max depth>\"\n elif isinstance(obj, (list, tuple)):\n return [_serialize(x, depth + 1) for x in obj]\n elif getattr(obj, '__class__', None) and getattr(obj.__class__, '__name__', '') == 'deque':\n return [_serialize(x, depth + 1) for x in obj]\n elif isinstance(obj, dict):\n return {str(k): _serialize(v, depth + 1) for k, v in obj.items()}\n elif isinstance(obj, set):\n try:\n return {\"__type__\": \"set\", \"values\": sorted([_serialize(x, depth + 1) for x in obj])}\n except TypeError:\n return {\"__type__\": \"set\", \"values\": [_serialize(x, depth + 1) for x in obj]}\n elif isinstance(obj, TreeNode):\n result = {\"__type__\": \"TreeNode\", \"val\": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}\n if hasattr(obj, 'left'):\n result[\"left\"] = _serialize(obj.left, depth + 1)\n if hasattr(obj, 'right'):\n result[\"right\"] = _serialize(obj.right, depth + 1)\n return result\n elif isinstance(obj, ListNode):\n result = {\"__type__\": \"ListNode\", \"val\": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}\n result[\"next\"] = _serialize(obj.next, depth + 1)\n return result\n elif callable(obj):\n return None\n else:\n repr_str = repr(obj)\n if repr_str.startswith('<') and repr_str.endswith('>'):\n return None\n return repr_str\n",
|
|
16
|
+
PYTHON_SERIALIZE_FUNCTION: "\n_MAX_SERIALIZE_DEPTH = 48\n\ndef _serialize(obj, depth=0):\n if isinstance(obj, (bool, int, str, type(None))):\n return obj\n elif isinstance(obj, float):\n if not math.isfinite(obj):\n if math.isnan(obj):\n return \"NaN\"\n return \"Infinity\" if obj > 0 else \"-Infinity\"\n return obj\n if depth > _MAX_SERIALIZE_DEPTH:\n return \"<max depth>\"\n elif isinstance(obj, (list, tuple)):\n return [_serialize(x, depth + 1) for x in obj]\n elif getattr(obj, '__class__', None) and getattr(obj.__class__, '__name__', '') == 'deque':\n return [_serialize(x, depth + 1) for x in obj]\n elif isinstance(obj, dict):\n return {str(k): _serialize(v, depth + 1) for k, v in obj.items()}\n elif isinstance(obj, set):\n try:\n return {\"__type__\": \"set\", \"values\": sorted([_serialize(x, depth + 1) for x in obj])}\n except TypeError:\n return {\"__type__\": \"set\", \"values\": [_serialize(x, depth + 1) for x in obj]}\n elif isinstance(obj, TreeNode):\n result = {\"__type__\": \"TreeNode\", \"val\": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}\n if hasattr(obj, 'left'):\n result[\"left\"] = _serialize(obj.left, depth + 1)\n if hasattr(obj, 'right'):\n result[\"right\"] = _serialize(obj.right, depth + 1)\n return result\n elif isinstance(obj, ListNode):\n result = {\"__type__\": \"ListNode\", \"val\": _serialize(getattr(obj, 'val', getattr(obj, 'value', None)), depth + 1)}\n result[\"next\"] = _serialize(obj.next, depth + 1)\n return result\n elif callable(obj):\n return None\n else:\n repr_str = repr(obj)\n if repr_str.startswith('<') and repr_str.endswith('>'):\n return None\n return repr_str\n",
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
scope.__TRACECODE_toPythonLiteral = __tracecodeToPythonLiteral;
|