@tracecode/harness 0.7.0-beta7 → 0.7.0-beta8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tracecode/harness",
3
- "version": "0.7.0-beta7",
3
+ "version": "0.7.0-beta8",
4
4
  "license": "AGPL-3.0-only",
5
5
  "homepage": "https://tracecode.app",
6
6
  "repository": {
@@ -1265,6 +1265,7 @@ std::string to_json(const std::unordered_map<K, V, Hash, Equal, Allocator>& valu
1265
1265
  inline void write_trace_event_json(const std::string& event_json, int line = 1);
1266
1266
  inline void write_trace_event_json_raw(const std::string& event_json);
1267
1267
  inline bool check_trace_budget(int line);
1268
+ inline void emit_line(int line, const char* function_name);
1268
1269
 
1269
1270
  inline std::string target_json(const std::string& name) {
1270
1271
  return std::string("{\"variable\":") + to_json(name) + "}";
@@ -1532,8 +1533,8 @@ class IndexedRangeReadIterator {
1532
1533
  using pointer = typename std::iterator_traits<RawIterator>::pointer;
1533
1534
  using iterator_category = std::input_iterator_tag;
1534
1535
 
1535
- IndexedRangeReadIterator(Container& container, RawIterator iterator, std::size_t index, int line, const char* binding_name)
1536
- : container_(container), iterator_(iterator), index_(index), line_(line), binding_name_(binding_name) {}
1536
+ IndexedRangeReadIterator(Container& container, RawIterator iterator, std::size_t index, int line, const char* binding_name, bool is_end = false)
1537
+ : container_(container), iterator_(iterator), index_(index), line_(line), binding_name_(binding_name), is_end_(is_end) {}
1537
1538
 
1538
1539
  reference operator*() const {
1539
1540
  if (binding_name_ && *binding_name_) {
@@ -1566,7 +1567,9 @@ class IndexedRangeReadIterator {
1566
1567
  }
1567
1568
 
1568
1569
  bool operator==(const IndexedRangeReadIterator& other) const {
1569
- return iterator_ == other.iterator_;
1570
+ const bool equal = iterator_ == other.iterator_;
1571
+ if (equal && (is_end_ || other.is_end_)) emit_line(line_, "");
1572
+ return equal;
1570
1573
  }
1571
1574
 
1572
1575
  bool operator!=(const IndexedRangeReadIterator& other) const {
@@ -1579,6 +1582,7 @@ class IndexedRangeReadIterator {
1579
1582
  std::size_t index_;
1580
1583
  int line_;
1581
1584
  const char* binding_name_;
1585
+ bool is_end_;
1582
1586
  };
1583
1587
 
1584
1588
  template <typename Container>
@@ -1588,11 +1592,11 @@ class IndexedRangeReadable {
1588
1592
  : container_(container), line_(line), binding_name_(binding_name) {}
1589
1593
 
1590
1594
  auto begin() {
1591
- return IndexedRangeReadIterator<Container>(container_, container_.raw().begin(), 0, line_, binding_name_);
1595
+ return IndexedRangeReadIterator<Container>(container_, container_.raw().begin(), 0, line_, binding_name_, false);
1592
1596
  }
1593
1597
 
1594
1598
  auto end() {
1595
- return IndexedRangeReadIterator<Container>(container_, container_.raw().end(), container_.raw().size(), line_, binding_name_);
1599
+ return IndexedRangeReadIterator<Container>(container_, container_.raw().end(), container_.raw().size(), line_, binding_name_, true);
1596
1600
  }
1597
1601
 
1598
1602
  private:
@@ -1612,8 +1616,8 @@ class IndexedNestedRangeReadIterator {
1612
1616
  using pointer = typename std::iterator_traits<RawIterator>::pointer;
1613
1617
  using iterator_category = std::input_iterator_tag;
1614
1618
 
1615
- IndexedNestedRangeReadIterator(Row& row, RawIterator iterator, const std::string& name, std::size_t outer, std::size_t inner, int line, const char* outer_source, const char* binding_name)
1616
- : row_(row), iterator_(iterator), name_(name), outer_(outer), inner_(inner), line_(line), outer_source_(outer_source), binding_name_(binding_name) {}
1619
+ IndexedNestedRangeReadIterator(Row& row, RawIterator iterator, const std::string& name, std::size_t outer, std::size_t inner, int line, const char* outer_source, const char* binding_name, bool is_end = false)
1620
+ : row_(row), iterator_(iterator), name_(name), outer_(outer), inner_(inner), line_(line), outer_source_(outer_source), binding_name_(binding_name), is_end_(is_end) {}
1617
1621
 
1618
1622
  reference operator*() const {
1619
1623
  emit_iteration_bind_read();
@@ -1638,7 +1642,9 @@ class IndexedNestedRangeReadIterator {
1638
1642
  }
1639
1643
 
1640
1644
  bool operator==(const IndexedNestedRangeReadIterator& other) const {
1641
- return iterator_ == other.iterator_;
1645
+ const bool equal = iterator_ == other.iterator_;
1646
+ if (equal && (is_end_ || other.is_end_)) emit_line(line_, "");
1647
+ return equal;
1642
1648
  }
1643
1649
 
1644
1650
  bool operator!=(const IndexedNestedRangeReadIterator& other) const {
@@ -1668,6 +1674,7 @@ class IndexedNestedRangeReadIterator {
1668
1674
  int line_;
1669
1675
  const char* outer_source_;
1670
1676
  const char* binding_name_;
1677
+ bool is_end_;
1671
1678
  };
1672
1679
 
1673
1680
  template <typename T>
@@ -1677,11 +1684,11 @@ class IndexedNestedRangeReadable {
1677
1684
  : row_(row), name_(name), outer_(outer), line_(line), outer_source_(outer_source), binding_name_(binding_name) {}
1678
1685
 
1679
1686
  auto begin() {
1680
- return IndexedNestedRangeReadIterator<T>(row_, row_.begin(), name_, outer_, 0, line_, outer_source_, binding_name_);
1687
+ return IndexedNestedRangeReadIterator<T>(row_, row_.begin(), name_, outer_, 0, line_, outer_source_, binding_name_, false);
1681
1688
  }
1682
1689
 
1683
1690
  auto end() {
1684
- return IndexedNestedRangeReadIterator<T>(row_, row_.end(), name_, outer_, row_.size(), line_, outer_source_, binding_name_);
1691
+ return IndexedNestedRangeReadIterator<T>(row_, row_.end(), name_, outer_, row_.size(), line_, outer_source_, binding_name_, true);
1685
1692
  }
1686
1693
 
1687
1694
  private:
@@ -1703,8 +1710,8 @@ class KeyedRangeReadIterator {
1703
1710
  using pointer = typename std::iterator_traits<RawIterator>::pointer;
1704
1711
  using iterator_category = std::input_iterator_tag;
1705
1712
 
1706
- KeyedRangeReadIterator(Container& container, RawIterator iterator, int line, const char* key_binding_name)
1707
- : container_(container), iterator_(iterator), line_(line), key_binding_name_(key_binding_name) {}
1713
+ KeyedRangeReadIterator(Container& container, RawIterator iterator, int line, const char* key_binding_name, bool is_end = false)
1714
+ : container_(container), iterator_(iterator), line_(line), key_binding_name_(key_binding_name), is_end_(is_end) {}
1708
1715
 
1709
1716
  reference operator*() const {
1710
1717
  emit_iteration_bind_read();
@@ -1728,7 +1735,9 @@ class KeyedRangeReadIterator {
1728
1735
  }
1729
1736
 
1730
1737
  bool operator==(const KeyedRangeReadIterator& other) const {
1731
- return iterator_ == other.iterator_;
1738
+ const bool equal = iterator_ == other.iterator_;
1739
+ if (equal && (is_end_ || other.is_end_)) emit_line(line_, "");
1740
+ return equal;
1732
1741
  }
1733
1742
 
1734
1743
  bool operator!=(const KeyedRangeReadIterator& other) const {
@@ -1748,6 +1757,7 @@ class KeyedRangeReadIterator {
1748
1757
  RawIterator iterator_;
1749
1758
  int line_;
1750
1759
  const char* key_binding_name_;
1760
+ bool is_end_;
1751
1761
  };
1752
1762
 
1753
1763
  template <typename Container>
@@ -1757,11 +1767,11 @@ class KeyedRangeReadable {
1757
1767
  : container_(container), line_(line), key_binding_name_(key_binding_name) {}
1758
1768
 
1759
1769
  auto begin() {
1760
- return KeyedRangeReadIterator<Container>(container_, container_.raw().begin(), line_, key_binding_name_);
1770
+ return KeyedRangeReadIterator<Container>(container_, container_.raw().begin(), line_, key_binding_name_, false);
1761
1771
  }
1762
1772
 
1763
1773
  auto end() {
1764
- return KeyedRangeReadIterator<Container>(container_, container_.raw().end(), line_, key_binding_name_);
1774
+ return KeyedRangeReadIterator<Container>(container_, container_.raw().end(), line_, key_binding_name_, true);
1765
1775
  }
1766
1776
 
1767
1777
  private:
@@ -1779,8 +1789,8 @@ class IndexedStringRangeReadIterator {
1779
1789
  using pointer = typename std::iterator_traits<RawIterator>::pointer;
1780
1790
  using iterator_category = std::input_iterator_tag;
1781
1791
 
1782
- IndexedStringRangeReadIterator(std::string& value, RawIterator iterator, std::size_t index, int line, const char* binding_name, const char* source_name)
1783
- : value_(value), iterator_(iterator), index_(index), line_(line), binding_name_(binding_name), source_name_(source_name) {}
1792
+ IndexedStringRangeReadIterator(std::string& value, RawIterator iterator, std::size_t index, int line, const char* binding_name, const char* source_name, bool is_end = false)
1793
+ : value_(value), iterator_(iterator), index_(index), line_(line), binding_name_(binding_name), source_name_(source_name), is_end_(is_end) {}
1784
1794
 
1785
1795
  reference operator*() const {
1786
1796
  emit_read();
@@ -1805,7 +1815,9 @@ class IndexedStringRangeReadIterator {
1805
1815
  }
1806
1816
 
1807
1817
  bool operator==(const IndexedStringRangeReadIterator& other) const {
1808
- return iterator_ == other.iterator_;
1818
+ const bool equal = iterator_ == other.iterator_;
1819
+ if (equal && (is_end_ || other.is_end_)) emit_line(line_, "");
1820
+ return equal;
1809
1821
  }
1810
1822
 
1811
1823
  bool operator!=(const IndexedStringRangeReadIterator& other) const {
@@ -1819,6 +1831,7 @@ class IndexedStringRangeReadIterator {
1819
1831
  int line_;
1820
1832
  const char* binding_name_;
1821
1833
  const char* source_name_;
1834
+ bool is_end_;
1822
1835
 
1823
1836
  void emit_read() const {
1824
1837
  const char item = *iterator_;
@@ -1841,11 +1854,11 @@ class IndexedStringRangeReadable {
1841
1854
  : value_(value), line_(line), binding_name_(binding_name), source_name_(source_name) {}
1842
1855
 
1843
1856
  auto begin() {
1844
- return IndexedStringRangeReadIterator(value_, value_.begin(), 0, line_, binding_name_, source_name_);
1857
+ return IndexedStringRangeReadIterator(value_, value_.begin(), 0, line_, binding_name_, source_name_, false);
1845
1858
  }
1846
1859
 
1847
1860
  auto end() {
1848
- return IndexedStringRangeReadIterator(value_, value_.end(), value_.size(), line_, binding_name_, source_name_);
1861
+ return IndexedStringRangeReadIterator(value_, value_.end(), value_.size(), line_, binding_name_, source_name_, true);
1849
1862
  }
1850
1863
 
1851
1864
  private:
@@ -1864,8 +1877,8 @@ class IndexedConstStringRangeReadIterator {
1864
1877
  using pointer = const char*;
1865
1878
  using iterator_category = std::input_iterator_tag;
1866
1879
 
1867
- IndexedConstStringRangeReadIterator(const std::string& value, RawIterator iterator, std::size_t index, int line, const char* binding_name, const char* source_name)
1868
- : value_(value), iterator_(iterator), index_(index), line_(line), binding_name_(binding_name), source_name_(source_name) {}
1880
+ IndexedConstStringRangeReadIterator(const std::string& value, RawIterator iterator, std::size_t index, int line, const char* binding_name, const char* source_name, bool is_end = false)
1881
+ : value_(value), iterator_(iterator), index_(index), line_(line), binding_name_(binding_name), source_name_(source_name), is_end_(is_end) {}
1869
1882
 
1870
1883
  char operator*() const {
1871
1884
  emit_read();
@@ -1885,7 +1898,9 @@ class IndexedConstStringRangeReadIterator {
1885
1898
  }
1886
1899
 
1887
1900
  bool operator==(const IndexedConstStringRangeReadIterator& other) const {
1888
- return iterator_ == other.iterator_;
1901
+ const bool equal = iterator_ == other.iterator_;
1902
+ if (equal && (is_end_ || other.is_end_)) emit_line(line_, "");
1903
+ return equal;
1889
1904
  }
1890
1905
 
1891
1906
  bool operator!=(const IndexedConstStringRangeReadIterator& other) const {
@@ -1899,6 +1914,7 @@ class IndexedConstStringRangeReadIterator {
1899
1914
  int line_;
1900
1915
  const char* binding_name_;
1901
1916
  const char* source_name_;
1917
+ bool is_end_;
1902
1918
 
1903
1919
  void emit_read() const {
1904
1920
  const char item = *iterator_;
@@ -1921,11 +1937,11 @@ class IndexedConstStringRangeReadable {
1921
1937
  : value_(value), line_(line), binding_name_(binding_name), source_name_(source_name) {}
1922
1938
 
1923
1939
  auto begin() const {
1924
- return IndexedConstStringRangeReadIterator(value_, value_.begin(), 0, line_, binding_name_, source_name_);
1940
+ return IndexedConstStringRangeReadIterator(value_, value_.begin(), 0, line_, binding_name_, source_name_, false);
1925
1941
  }
1926
1942
 
1927
1943
  auto end() const {
1928
- return IndexedConstStringRangeReadIterator(value_, value_.end(), value_.size(), line_, binding_name_, source_name_);
1944
+ return IndexedConstStringRangeReadIterator(value_, value_.end(), value_.size(), line_, binding_name_, source_name_, true);
1929
1945
  }
1930
1946
 
1931
1947
  private:
@@ -3179,6 +3195,12 @@ inline StdNestedVectorElementRef<T> trace_nested_index_ref(std::vector<std::vect
3179
3195
  );
3180
3196
  }
3181
3197
 
3198
+ template <typename Map, typename OuterIndex, typename Key>
3199
+ inline std::enable_if_t<is_std_unordered_map<Map>::value || is_std_map<Map>::value, NestedMapElementRef<Map>>
3200
+ trace_nested_index_ref(Vector<Map>& container, const std::string&, OuterIndex outer, const Key& key, const char* = nullptr, const char* = nullptr) {
3201
+ return NestedMapElementRef<Map>(container, static_cast<std::size_t>(outer), key);
3202
+ }
3203
+
3182
3204
  template <typename T, typename Index>
3183
3205
  inline VectorElementRef<T> trace_index_ref(Vector<T>& container, const std::string&, Index index, const char* source = nullptr) {
3184
3206
  return container.with_index_source(static_cast<std::size_t>(index), source);
@@ -132,18 +132,31 @@
132
132
 
133
133
  function indexSourceArgument(source) {
134
134
  const value = String(source).trim();
135
+ const tracedReadSource = tracedIndexedReadSource(value);
136
+ if (tracedReadSource) return JSON.stringify(tracedReadSource);
135
137
  const charAtIndexMatch = value.match(/^[A-Za-z_][A-Za-z0-9_]*\.charAt\(\s*([\s\S]+)\s*\)$/);
136
138
  if (charAtIndexMatch?.[1]) {
137
139
  const charAtSource = safeIndexSourceExpression(charAtIndexMatch[1]) ?? singleIdentifierIndexSource(charAtIndexMatch[1]);
138
140
  if (charAtSource) return JSON.stringify(charAtSource);
139
141
  }
140
- const tracedReadSourceMatch = value.match(/^TraceHooks\.read[A-Za-z0-9_]*AtLine\([\s\S]*,\s*"([A-Za-z_][A-Za-z0-9_]*)"\s*\)$/);
141
- if (tracedReadSourceMatch?.[1]) return `"${tracedReadSourceMatch[1]}"`;
142
142
  const expressionSource = safeIndexSourceExpression(value) ?? singleIdentifierIndexSource(value);
143
143
  if (expressionSource) return JSON.stringify(expressionSource);
144
144
  return isSimpleIdentifierExpression(value) ? JSON.stringify(value) : 'null';
145
145
  }
146
146
 
147
+ function tracedIndexedReadSource(source) {
148
+ const value = String(source).trim();
149
+ if (!value.startsWith('TraceHooks.read')) return null;
150
+ const open = value.indexOf('(');
151
+ if (open < 0 || !value.endsWith(')')) return null;
152
+ const args = splitTopLevelJavaList(value.slice(open + 1, -1));
153
+ if (args.length < 2) return null;
154
+ const name = String(args[1]).trim().match(/^"([A-Za-z_][A-Za-z0-9_]*)"$/)?.[1];
155
+ if (!name) return null;
156
+ const explicitSource = String(args[args.length - 1] ?? '').trim().match(/^"([^"]+)"$/)?.[1];
157
+ return explicitSource || name;
158
+ }
159
+
147
160
  function indexSourceArgumentSourceFirst(source) {
148
161
  const value = String(source).trim();
149
162
  const tracedIndexedRead = value.match(/^TraceHooks\.read[A-Za-z0-9_]*AtLine\(\s*\d+\s*,\s*"([A-Za-z_][A-Za-z0-9_]*)"\s*,[\s\S]*,\s*"([^"]+)"\s*\)$/);
@@ -462,10 +475,24 @@
462
475
  let currentTraceLine = null;
463
476
  let pendingScalarDeclarationWrites = null;
464
477
  let methodDepth = 0;
478
+ let generatedExportsClassDepth = null;
465
479
  const methodStartPattern =
466
480
  /^(\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*$/;
481
+ const generatedExportsClassPattern = /^\s*(?:(?:public|private|protected|static|final)\s+)*class\s+Exports[A-Za-z0-9_]*\s*\{/;
467
482
 
468
483
  for (const line of lines) {
484
+ if (generatedExportsClassDepth !== null) {
485
+ output.push(line);
486
+ generatedExportsClassDepth += braceDelta(line);
487
+ if (generatedExportsClassDepth <= 0) generatedExportsClassDepth = null;
488
+ continue;
489
+ }
490
+ if (generatedExportsClassPattern.test(line)) {
491
+ output.push(line);
492
+ generatedExportsClassDepth = Math.max(0, braceDelta(line));
493
+ if (generatedExportsClassDepth <= 0) generatedExportsClassDepth = null;
494
+ continue;
495
+ }
469
496
  if (methodDepth <= 0) {
470
497
  const methodMatch = line.match(methodStartPattern);
471
498
  if (methodMatch) {
@@ -1507,7 +1507,6 @@ function augmentTraceCallArgumentSnapshots(source) {
1507
1507
  const methodStack = [];
1508
1508
  const methodStartPattern =
1509
1509
  /^(\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*$/;
1510
-
1511
1510
  return lines.map((line) => {
1512
1511
  const methodMatch = line.match(methodStartPattern);
1513
1512
  if (methodMatch) {
@@ -1673,10 +1672,24 @@ function augmentJavaLocalSnapshots(source) {
1673
1672
  let currentTraceLine = null;
1674
1673
  let pendingScalarDeclarationWrites = null;
1675
1674
  let methodDepth = 0;
1675
+ let generatedExportsClassDepth = null;
1676
1676
  const methodStartPattern =
1677
1677
  /^(\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*$/;
1678
+ const generatedExportsClassPattern = /^\s*(?:(?:public|private|protected|static|final)\s+)*class\s+Exports[A-Za-z0-9_]*\s*\{/;
1678
1679
 
1679
1680
  for (const line of lines) {
1681
+ if (generatedExportsClassDepth !== null) {
1682
+ output.push(line);
1683
+ generatedExportsClassDepth += braceDelta(line);
1684
+ if (generatedExportsClassDepth <= 0) generatedExportsClassDepth = null;
1685
+ continue;
1686
+ }
1687
+ if (generatedExportsClassPattern.test(line)) {
1688
+ output.push(line);
1689
+ generatedExportsClassDepth = Math.max(0, braceDelta(line));
1690
+ if (generatedExportsClassDepth <= 0) generatedExportsClassDepth = null;
1691
+ continue;
1692
+ }
1680
1693
  if (methodDepth <= 0) {
1681
1694
  const methodMatch = line.match(methodStartPattern);
1682
1695
  if (methodMatch) {
@@ -74,8 +74,8 @@ public final class JavaRewriteLibrary {
74
74
  private static final Pattern OBJECT_FIELD_MAP_GET_CALL = Pattern.compile("(?<!\\.)\\b(?!this\\b)([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z_][A-Za-z0-9_]*)\\.get\\(([^()\\n;]+)\\)");
75
75
  private static final Pattern OBJECT_FIELD_MAP_GET_OR_DEFAULT_CALL = Pattern.compile("(?<!\\.)\\b(?!this\\b)([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z_][A-Za-z0-9_]*)\\.getOrDefault\\(([^()\\n;]+)\\)");
76
76
  private static final Pattern OBJECT_FIELD_MAP_CONTAINS_KEY_CALL = Pattern.compile("(?<!\\.)\\b(?!this\\b)([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z_][A-Za-z0-9_]*)\\.containsKey\\(([^()\\n;]+)\\)");
77
- private static final Pattern MATRIX_READ = Pattern.compile("(?<!\\.)\\b([A-Za-z_][A-Za-z0-9_]*)\\s*\\[([^;\\]\\[]+)\\]\\s*\\[([^;\\]\\[]+)\\]");
78
- private static final Pattern ARRAY_READ = Pattern.compile("(?<!\\.)\\b([A-Za-z_][A-Za-z0-9_]*)\\s*\\[([^;\\]\\[]+)\\]");
77
+ private static final Pattern MATRIX_READ = Pattern.compile("(?<!\\.)\\b([A-Za-z_][A-Za-z0-9_]*)\\s*\\[((?:[^\\]\\[()]|\\([^()]*\\))+)\\]\\s*\\[((?:[^\\]\\[()]|\\([^()]*\\))+)\\]");
78
+ private static final Pattern ARRAY_READ = Pattern.compile("(?<!\\.)\\b([A-Za-z_][A-Za-z0-9_]*)\\s*\\[((?:[^\\]\\[()]|\\([^()]*\\))+)\\]");
79
79
 
80
80
  private JavaRewriteLibrary() {}
81
81
 
@@ -1529,6 +1529,8 @@ public final class JavaRewriteLibrary {
1529
1529
 
1530
1530
  private static String indexSourceArgument(String value) {
1531
1531
  if (value != null) {
1532
+ String tracedSource = tracedIndexedReadSource(value.trim());
1533
+ if (tracedSource != null) return quote(tracedSource);
1532
1534
  java.util.regex.Matcher charAtIndex = java.util.regex.Pattern
1533
1535
  .compile("^[A-Za-z_][A-Za-z0-9_]*\\.charAt\\(\\s*([\\s\\S]+)\\s*\\)$")
1534
1536
  .matcher(value.trim());
@@ -1537,10 +1539,6 @@ public final class JavaRewriteLibrary {
1537
1539
  if (charAtSource == null) charAtSource = singleIdentifierIndexSource(charAtIndex.group(1));
1538
1540
  if (charAtSource != null) return quote(charAtSource);
1539
1541
  }
1540
- java.util.regex.Matcher tracedReadSource = java.util.regex.Pattern
1541
- .compile("^TraceHooks\\.read[A-Za-z0-9_]*AtLine\\([\\s\\S]*,\\s*\"([A-Za-z_][A-Za-z0-9_]*)\"\\s*\\)$")
1542
- .matcher(value.trim());
1543
- if (tracedReadSource.matches()) return quote(tracedReadSource.group(1));
1544
1542
  String expressionSource = safeIndexSourceExpression(value.trim());
1545
1543
  if (expressionSource == null) expressionSource = singleIdentifierIndexSource(value.trim());
1546
1544
  if (expressionSource != null) return quote(expressionSource);
@@ -1548,6 +1546,22 @@ public final class JavaRewriteLibrary {
1548
1546
  return isSimpleIdentifierExpression(value) ? quote(value) : "null";
1549
1547
  }
1550
1548
 
1549
+ private static String tracedIndexedReadSource(String value) {
1550
+ if (value == null || !value.startsWith("TraceHooks.read")) return null;
1551
+ int open = value.indexOf('(');
1552
+ if (open < 0 || !value.endsWith(")")) return null;
1553
+ java.util.List<String> args = splitTopLevel(value.substring(open + 1, value.length() - 1));
1554
+ if (args.size() < 2) return null;
1555
+ java.util.regex.Matcher name = java.util.regex.Pattern
1556
+ .compile("^\"([A-Za-z_][A-Za-z0-9_]*)\"$")
1557
+ .matcher(args.get(1).trim());
1558
+ if (!name.matches()) return null;
1559
+ java.util.regex.Matcher explicitSource = java.util.regex.Pattern
1560
+ .compile("^\"([^\"]+)\"$")
1561
+ .matcher(args.get(args.size() - 1).trim());
1562
+ return explicitSource.matches() ? explicitSource.group(1) : name.group(1);
1563
+ }
1564
+
1551
1565
  private static String escapedIndexSourcesTargetSegment(String... values) {
1552
1566
  StringBuilder out = new StringBuilder(",\\\"indexSources\\\":[");
1553
1567
  for (int index = 0; index < values.length; index++) {
@@ -611,7 +611,11 @@ public final class TraceHooks {
611
611
 
612
612
  @Override
613
613
  public boolean hasNext() {
614
- return iterator.hasNext();
614
+ boolean hasNext = iterator.hasNext();
615
+ if (!hasNext) {
616
+ emitLineAtLine(line);
617
+ }
618
+ return hasNext;
615
619
  }
616
620
 
617
621
  @Override
@@ -636,7 +640,11 @@ public final class TraceHooks {
636
640
 
637
641
  @Override
638
642
  public boolean hasNext() {
639
- return iterator.hasNext();
643
+ boolean hasNext = iterator.hasNext();
644
+ if (!hasNext) {
645
+ emitLineAtLine(line);
646
+ }
647
+ return hasNext;
640
648
  }
641
649
 
642
650
  @Override
@@ -3582,6 +3582,7 @@ function rewriteForOfStatementForTracing(ts, sourceFile, context, forOfStatement
3582
3582
  tracedExpression,
3583
3583
  bodyBlock
3584
3584
  ),
3585
+ createHeaderLine(),
3585
3586
  createAttachPendingAccessesStatement(ts),
3586
3587
  ],
3587
3588
  true
Binary file