@tracecode/harness 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +30 -3
  3. package/dist/browser.cjs +974 -19
  4. package/dist/browser.cjs.map +1 -1
  5. package/dist/browser.d.cts +3 -2
  6. package/dist/browser.d.ts +3 -2
  7. package/dist/browser.js +974 -19
  8. package/dist/browser.js.map +1 -1
  9. package/dist/cli.cjs +24 -0
  10. package/dist/cli.cjs.map +1 -1
  11. package/dist/cli.js +24 -0
  12. package/dist/cli.js.map +1 -1
  13. package/dist/core.cjs +611 -4
  14. package/dist/core.cjs.map +1 -1
  15. package/dist/core.d.cts +20 -6
  16. package/dist/core.d.ts +20 -6
  17. package/dist/core.js +605 -4
  18. package/dist/core.js.map +1 -1
  19. package/dist/index.cjs +1004 -19
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +3 -3
  22. package/dist/index.d.ts +3 -3
  23. package/dist/index.js +998 -19
  24. package/dist/index.js.map +1 -1
  25. package/dist/internal/browser.cjs +211 -0
  26. package/dist/internal/browser.cjs.map +1 -1
  27. package/dist/internal/browser.d.cts +62 -6
  28. package/dist/internal/browser.d.ts +62 -6
  29. package/dist/internal/browser.js +210 -0
  30. package/dist/internal/browser.js.map +1 -1
  31. package/dist/javascript.d.cts +2 -2
  32. package/dist/javascript.d.ts +2 -2
  33. package/dist/{runtime-types-DtaaAhHL.d.ts → runtime-types-89nchXlY.d.cts} +8 -4
  34. package/dist/{runtime-types--lBQ6rYu.d.cts → runtime-types-CCQ-ZLc9.d.ts} +8 -4
  35. package/dist/{types-DwIYM3Ku.d.cts → types-zyvpJKCi.d.cts} +1 -0
  36. package/dist/{types-DwIYM3Ku.d.ts → types-zyvpJKCi.d.ts} +1 -0
  37. package/package.json +17 -5
  38. package/workers/java/.build/classes/harness/browser/JavaRewriteLibrary.class +0 -0
  39. package/workers/java/java-worker.js +712 -0
  40. package/workers/java/src/harness/browser/JavaRewriteLibrary.java +54 -0
  41. package/workers/javascript/javascript-worker.js +343 -63
  42. package/workers/python/generated-python-harness-snippets.js +3 -3
  43. package/workers/python/pyodide-worker.js +419 -68
  44. package/workers/python/runtime-core.js +52 -3
  45. package/workers/vendor/java-browser-spike-helper.jar +0 -0
  46. package/workers/vendor/java-practice-rewriter.jar +0 -0
  47. package/workers/vendor/java-rewrite-bridge.jar +0 -0
  48. package/workers/vendor/javaparser-core-3.25.10.jar +0 -0
  49. 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
- const hasValue =
122
- Object.prototype.hasOwnProperty.call(value, 'val') ||
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
- const hasValue =
133
- Object.prototype.hasOwnProperty.call(value, 'val') ||
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
- if (nodePrefix === 'tree') {
203
- return {
204
- __type__: 'TreeNode',
205
- __id__: nodeId,
206
- val: serializeValue(value.val ?? value.value ?? null, depth + 1, seen, nodeRefState),
207
- left: serializeValue(value.left ?? null, depth + 1, seen, nodeRefState),
208
- right: serializeValue(value.right ?? null, depth + 1, seen, nodeRefState),
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
- if (isTree) {
277
- return {
278
- __type__: 'TreeNode',
279
- __id__: nodeId,
280
- val: serializeValue(nodeValue.val ?? nodeValue.value ?? null, 1, new WeakSet(), nodeRefState),
281
- left: serializeValue(nodeValue.left ?? null, 1, new WeakSet(), nodeRefState),
282
- right: serializeValue(nodeValue.right ?? null, 1, new WeakSet(), nodeRefState),
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
- return {
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') || lowerName.includes('list')) {
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
- if (!Array.isArray(pending) || pending.length === 0) {
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 >= maxTraceSteps) {
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 < maxTraceSteps) {
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 ${maxTraceSteps} trace steps`);
1160
+ throw createLimitError('trace-limit', lineNumber, `Exceeded ${effectiveMaxTraceSteps} trace steps`);
1135
1161
  }
1136
1162
  const accesses = flushPendingAccesses(frameId);
1137
- trace.push({
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 < maxTraceSteps) {
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 (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):\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 (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):\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 (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):\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 (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):\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 (hasattr(obj, 'val') or hasattr(obj, 'value')) and (hasattr(obj, 'left') or hasattr(obj, 'right')):\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 (hasattr(obj, 'val') or hasattr(obj, 'value')) and hasattr(obj, 'next'):\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",
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;