Reproduce-first (ms-typescript reallyLargeFile.ts, 2026-07-07): a file * whose root node has hundreds of thousands of FLAT SIBLINGS (580k //// * comment lines in the 3.5 MB fourslash fixture) hung extraction for over * 15 minutes: walk_defs pushed children via index-based ts_node_child(i), * which is O(i) per call in tree-sitter — O(n^2) per wide node (~1.7e11 * iterator steps on the real fil
| 4289 | * children under the root, plus sparse real defs so the breadth check cannot |
| 4290 | * pass vacuously). Returns elapsed milliseconds; stores the def count. */ |
| 4291 | static long extract_wide_flat_ms(int n, int *out_defs) { |
| 4292 | const size_t cap = (size_t)n * 24 + (size_t)8192; /* "// wide filler N\n" <= 24 chars */ |
| 4293 | char *src = malloc(cap); |
| 4294 | if (!src) { |
| 4295 | return -1; |
| 4296 | } |
| 4297 | size_t off = 0; |
| 4298 | /* CONSTANT def count (10), independent of n: defs that scale WITH n make |
| 4299 | * the fixture superlinear through the separate per-def sibling-scan cost |
| 4300 | * (O(defs x siblings)) — on windows-CLANG64 ASan that pushed the ratio |
| 4301 | * of LINEAR walk code to 43x. Ten spread-out defs keep the breadth check |
| 4302 | * honest while the sibling-scan term stays 10 x n = linear. */ |
| 4303 | const int def_stride = n / 10; |
| 4304 | for (int i = 0; i < n; i++) { |
| 4305 | off += (size_t)snprintf(src + off, cap - off, "// wide filler %d\n", i); |
| 4306 | if (i % def_stride == 0) { |
| 4307 | off += (size_t)snprintf(src + off, cap - off, "var wide_a%d = %d;\n", i, i); |
| 4308 | } |
| 4309 | } |
| 4310 | struct timespec a; |
| 4311 | struct timespec b; |
| 4312 | cbm_clock_gettime(CLOCK_MONOTONIC, &a); |
| 4313 | CBMFileResult *r = |
| 4314 | cbm_extract_file(src, (int)off, CBM_LANG_JAVASCRIPT, "proj", "wide.js", 0, NULL, NULL); |
| 4315 | cbm_clock_gettime(CLOCK_MONOTONIC, &b); |
| 4316 | free(src); |
| 4317 | if (!r) { |
| 4318 | return -1; |
| 4319 | } |
| 4320 | *out_defs = r->defs.count; |
| 4321 | cbm_free_result(r); |
| 4322 | return (b.tv_sec - a.tv_sec) * 1000L + (b.tv_nsec - a.tv_nsec) / 1000000L; |
| 4323 | } |
| 4324 | |
| 4325 | /* Best-of-N. Timing noise only ever ADDS time, so the minimum of a few runs is |
| 4326 | * the cheapest good estimate of the noise-free cost. A single sample at each |
no test coverage detected