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
| 4394 | * children under the root, plus sparse real defs so the breadth check cannot |
| 4395 | * pass vacuously). Returns elapsed milliseconds; stores the def count. */ |
| 4396 | static long extract_wide_flat_ms(int n, int *out_defs) { |
| 4397 | const size_t cap = (size_t)n * 24 + (size_t)8192; /* "// wide filler N\n" <= 24 chars */ |
| 4398 | char *src = malloc(cap); |
| 4399 | if (!src) { |
| 4400 | return -1; |
| 4401 | } |
| 4402 | size_t off = 0; |
| 4403 | /* CONSTANT def count (10), independent of n: defs that scale WITH n make |
| 4404 | * the fixture superlinear through the separate per-def sibling-scan cost |
| 4405 | * (O(defs x siblings)) — on windows-CLANG64 ASan that pushed the ratio |
| 4406 | * of LINEAR walk code to 43x. Ten spread-out defs keep the breadth check |
| 4407 | * honest while the sibling-scan term stays 10 x n = linear. */ |
| 4408 | const int def_stride = n / 10; |
| 4409 | for (int i = 0; i < n; i++) { |
| 4410 | off += (size_t)snprintf(src + off, cap - off, "// wide filler %d\n", i); |
| 4411 | if (i % def_stride == 0) { |
| 4412 | off += (size_t)snprintf(src + off, cap - off, "var wide_a%d = %d;\n", i, i); |
| 4413 | } |
| 4414 | } |
| 4415 | struct timespec a; |
| 4416 | struct timespec b; |
| 4417 | cbm_clock_gettime(CLOCK_MONOTONIC, &a); |
| 4418 | CBMFileResult *r = |
| 4419 | cbm_extract_file(src, (int)off, CBM_LANG_JAVASCRIPT, "proj", "wide.js", 0, NULL, NULL); |
| 4420 | cbm_clock_gettime(CLOCK_MONOTONIC, &b); |
| 4421 | free(src); |
| 4422 | if (!r) { |
| 4423 | return -1; |
| 4424 | } |
| 4425 | *out_defs = r->defs.count; |
| 4426 | cbm_free_result(r); |
| 4427 | return (b.tv_sec - a.tv_sec) * 1000L + (b.tv_nsec - a.tv_nsec) / 1000000L; |
| 4428 | } |
| 4429 | |
| 4430 | /* Best-of-N. Timing noise only ever ADDS time, so the minimum of a few runs is |
| 4431 | * the cheapest good estimate of the noise-free cost. A single sample at each |
no test coverage detected