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
| 4238 | * children under the root, plus sparse real defs so the breadth check cannot |
| 4239 | * pass vacuously). Returns elapsed milliseconds; stores the def count. */ |
| 4240 | static long extract_wide_flat_ms(int n, int *out_defs) { |
| 4241 | const size_t cap = (size_t)n * 24 + (size_t)8192; /* "// wide filler N\n" <= 24 chars */ |
| 4242 | char *src = malloc(cap); |
| 4243 | if (!src) { |
| 4244 | return -1; |
| 4245 | } |
| 4246 | size_t off = 0; |
| 4247 | /* CONSTANT def count (10), independent of n: defs that scale WITH n make |
| 4248 | * the fixture superlinear through the separate per-def sibling-scan cost |
| 4249 | * (O(defs x siblings)) — on windows-CLANG64 ASan that pushed the ratio |
| 4250 | * of LINEAR walk code to 43x. Ten spread-out defs keep the breadth check |
| 4251 | * honest while the sibling-scan term stays 10 x n = linear. */ |
| 4252 | const int def_stride = n / 10; |
| 4253 | for (int i = 0; i < n; i++) { |
| 4254 | off += (size_t)snprintf(src + off, cap - off, "// wide filler %d\n", i); |
| 4255 | if (i % def_stride == 0) { |
| 4256 | off += (size_t)snprintf(src + off, cap - off, "var wide_a%d = %d;\n", i, i); |
| 4257 | } |
| 4258 | } |
| 4259 | struct timespec a; |
| 4260 | struct timespec b; |
| 4261 | cbm_clock_gettime(CLOCK_MONOTONIC, &a); |
| 4262 | CBMFileResult *r = |
| 4263 | cbm_extract_file(src, (int)off, CBM_LANG_JAVASCRIPT, "proj", "wide.js", 0, NULL, NULL); |
| 4264 | cbm_clock_gettime(CLOCK_MONOTONIC, &b); |
| 4265 | free(src); |
| 4266 | if (!r) { |
| 4267 | return -1; |
| 4268 | } |
| 4269 | *out_defs = r->defs.count; |
| 4270 | cbm_free_result(r); |
| 4271 | return (b.tv_sec - a.tv_sec) * 1000L + (b.tv_nsec - a.tv_nsec) / 1000000L; |
| 4272 | } |
| 4273 | |
| 4274 | /* Best-of-N. Timing noise only ever ADDS time, so the minimum of a few runs is |
| 4275 | * the cheapest good estimate of the noise-free cost. A single sample at each |
no test coverage detected