Skip to content

Commit 40f2722

Browse files
committed
fix(extract): AST traversal stacks no longer outlive the file they walk
Fixes DeusData#2010. Refs DeusData#1997. Indexing a 14k-file TypeScript repository holds 13.3 GB resident at post_extract on Linux aarch64, and about 3.1 GB of it is AST traversal scratch that no CBMFileResult points at. ts_nstack_init cut its stacks from ctx->arena, which is result->arena. In the parallel path that arena is stored into ec->result_cache[file_idx] (pass_parallel.c:963) and freed only after parallel_resolve and the infra passes (pipeline.c:1324), so a structure written for a one-file lifetime is in fact held for the whole index, once per file, concurrently. The header claimed otherwise and was wrong: "freed when the arena is destroyed at end of file extraction". The largest instance is channels. CHAN_STACK_CAP is 4096 and sizeof(TSNode) is 32, so scan_string_consts_js (extract_channels.c:104) and extract_channels_js (:376) take 128 KB each, per JavaScript or TypeScript file. Across 12,673 such files that is 3,168 MB, and it produced 23 Channel nodes out of 164,684. CBMExtractCtx gains a scratch arena, created and destroyed by the cbm_extract_file_ex call that builds the context. Nothing else changes about where anything lives: ctx->arena still owns every string a CBMFileResult points at, and ctx->scratch owns only traversal stacks, which nothing points at. Nothing a CBMFileResult points at moved. A context built without a scratch, and a file whose scratch arena fails to allocate, both fall back to ctx->arena, which is exactly the behaviour that shipped before this change, so no path can be handed a NULL arena and silently lose nodes. The signature change is what makes the mistake unrepresentable. ts_nstack_init takes the context rather than an arena, so handing it ctx->arena is an incompatible pointer type; push and push_children read the arena the stack recorded, so a stale argument is an arity error. Both fail under -Werror rather than silently retaining memory. The 55 init sites are NOT one uniform sed. 29 pass ctx->arena and 26 pass a local alias, and that alias is shared with result allocations: in parse_zig_imports (extract_imports.c:1093) the same `a` feeds cbm_node_text, strip_quotes, path_last and cbm_imports_push (extract_imports.c:1101, 1106, 1108, 1109). Rebinding it would have moved import names and module paths into memory that dies with the call while the result kept pointing at them. Each site was edited on its own and no alias was rebound. Re-derive the set with: grep -rn 'ts_nstack_init' internal/cbm/extract_*.c grep -rn 'ts_nstack_push' internal/cbm/extract_*.c parse_lisp_imports used its alias only for the stack, so the alias goes too. push_nested_class_nodes and push_class_body_children took an arena purely to reach the stack, so they take the context now; the one call site passes ctx. Two measurements, from two different builds, and they are not the same number. A macOS census build, which instruments every cbm_arena_alloc and sums the blocks behind every live CBMFileResult arena, reports the retained per-file arena set dropping from 11,226 MB mapped to 5,680 MB, and blocks from 48,045 to 34,186. Those are mapped bytes, not resident: that build logs mem.allocator.bound_populations_only, so untouched block tails never commit. The resident figures come from Linux aarch64, gcc 13.3, static build, where the build logs mem.allocator.owned classes=all and mapped equals resident. Five runs per side on the full 13,999-file tree: mem.collect post_extract rss_mb 13293 -> 6752 (-49.2%), mem.phase peak_mb 14840 -> 8565 (-42.3%). nodes 164684, parse_partial 280 and registry entries/defines/imports 787954/804999/100027 identical on all ten runs; edges 835869-835874 against 835867-835881, overlapping ranges, so run to run noise. Wall clock means 35651 ms against 30502 ms, but the ranges overlap and the host had other load, so the only supportable claim is no regression. ObjectScript Studio Export on a small fixture: nodes 18, edges 25, both sides. Scratch block size is 512 KB, chosen from measurement. arena_grow fires on one file in 12,000 at both 512 KB and 1 MB, and constantly at 256 KB, where the two channel stacks alone are exactly 262144 bytes. 512 KB therefore costs half the resident block per worker for the same growth behaviour, and stays under MI_LARGE_MAX_OBJ_SIZE in the vendored mimalloc, which is MI_LARGE_PAGE_SIZE/8, exactly 524288 bytes (types.h:426, with MI_ENABLE_LARGE_PAGES defaulting to 1 at :115 and not overridden by this build), so a 512 KB block is still bin-allocated from a 4 MiB large page. Growth is not free at that size for the same reason: arena_grow doubles to 1 MiB, above that bound and therefore a singleton OS allocation. One file in twelve thousand pays it, which is why it is accepted. The largest single file's scratch high water mark on this corpus is 33.5 MB, now transient rather than retained. Tests. Nothing in the suite asserted on result->arena size, block count or total_alloc after cbm_extract_file, which is why this went unnoticed for the life of extract_node_stack.h. traversal_stack_not_in_result_arena_issue2010 pins the byte budget: extracting "export const x = 1;" as TypeScript charged 365984 bytes to the result arena before and charges 87456 after, a difference of 278528, exactly the two 4096-entry channel walks plus the 512-entry ES import walk at 32 bytes per TSNode. Of the 87456 that remain, 7680 is the defs item array at GROW_ARRAY's starting capacity of 32 times sizeof(CBMDefinition) 240 and the other 79776 is everything else the extraction interns, none of it traversal scratch; the 128 KB bound is therefore above the new figure with room and a factor of four below the old one. extract_traversal_stacks_come_from_ctx_scratch_issue2010 in test_mem.c pins the lifetime, since a byte budget alone would also be satisfied by shrinking CHAN_STACK_CAP: it builds the extraction context directly over two arenas it owns, runs cbm_extract_channels, and asserts the scratch took at least the 262144 bytes of the two walks while the result arena did not. extract_c_macro_hidden_call_survives_preprocessed_pass_issue2010 is not a scratch test. pp_ctx carries ctx->scratch so every context in the file is uniform, but nothing reads it there: pp_ctx reaches only cbm_extract_unified and cbm_run_c_lsp, and neither extract_unified.c nor anything under internal/cbm/lsp/ includes extract_node_stack.h, so no traversal stack is built on that path today. The test guards the macro-expansion path itself, which had no assertion on a call that exists only after expansion. ObjectScript composite extraction is already covered by pipeline_objectscript_export_preserves_calls_sequential_parallel and iris_export_xml_multi_class, so no new test is added there; a per-call arena is correct on that path by construction, since each call owns and destroys its own. Local: scripts/test.sh, 7558 passed, 0 failed, 8 skipped, 139 suites. Alternatives considered Thread-local scratch arena, reset per file, with a depth guard for nesting. Rejected on three counts. This codebase never uses thread-local storage for owned bulk memory; every existing _Thread_local here is a small cache, a guard or a log buffer, and every per-worker resource is a field in a context struct. It would have needed cbm_arena_reset, which has no production caller and a documented subtlety with grown block sizes (arena.c:213-217). And extraction threads are created per cbm_parallel_for call and joined (worker_pool.c:71,88) with no thread-exit hook in this project's own code, so the arena would have leaked once per thread per dispatch and turned the leak lane red. Save and restore mark and release on the arena. Rejected because a per-call arena already makes nesting correct by construction, and a mark type plus two functions would have to be added to both arena headers. Per-walk arenas, one per traversal rather than one per file. Rejected on cost: 55 sites would each need an explicit span, for the same bound. Right-sizing the initial capacities alone. CHAN_STACK_CAP at 256 rather than 4096 would cut those two sites from 3,168 MB to about 198 MB, which is real, but it tunes the constant while the lifetime defect remains at all 55 sites, and growth is not free: 256 doubling to 4096 abandons 122,880 bytes, and one file in this corpus took 33.5 MB of stacks. The cap and the scratch block size are coupled, which is a further reason to change one at a time. Worth doing separately. Routing to a scratch inside extract_node_stack.h while keeping the arena parameters. Rejected because a parameter that is ignored lies about the API, and the alias hazard above would stay a review obligation instead of a compile error. Freeing the per-file result arena earlier. Rejected: the results are read by parallel_resolve and the infra passes. That is the retention half DeusData#1925 addresses, and this change is independent of it. Signed-off-by: Daniel Ochoa <daniel.ochoa@closedloop.ai>
1 parent 46ae198 commit 40f2722

12 files changed

Lines changed: 359 additions & 186 deletions

‎internal/cbm/cbm.c‎

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
/* Full declaration set for the same CBMArena, and it must precede cbm.h:
2+
* internal/cbm/arena.h declares a subset and the two share the CBM_ARENA_H
3+
* guard, so whichever is included first is the one this file sees. */
4+
#include "foundation/arena.h" // cbm_arena_init_sized
15
#include "cbm.h"
26
#include "arena.h" // CBMArena, cbm_arena_init/alloc/strdup/destroy
37
#include "helpers.h"
@@ -1162,11 +1166,27 @@ CBMFileResult *cbm_extract_file(const char *source, int source_len, CBMLanguage
11621166
return r;
11631167
}
11641168

1165-
CBMFileResult *cbm_extract_file_ex(const char *source, int source_len, CBMLanguage language,
1166-
const char *project, const char *rel_path,
1167-
int64_t timeout_micros, const char **extra_defines,
1168-
const char **include_paths, const CBMMacroTable *macro_table,
1169-
const CBMReturnTypeTable *return_type_table) {
1169+
/* Initial block for the per-file traversal scratch arena, chosen by measuring
1170+
* arena_grow on a 14k-file TypeScript tree: it fires on one file in 12,000 at
1171+
* both this size and at 1 MB, and on most files at 256 KB, where the two
1172+
* channel walks alone are exactly 262144 bytes. 512 KB therefore buys the same
1173+
* growth behaviour as 1 MB for half the resident block per worker. It is also
1174+
* exactly MI_LARGE_MAX_OBJ_SIZE in the vendored mimalloc
1175+
* (vendored/mimalloc/include/mimalloc/types.h:426, MI_LARGE_PAGE_SIZE/8 with
1176+
* MI_ENABLE_LARGE_PAGES defaulting to 1 at :115 and not overridden here), so
1177+
* the block is still bin-allocated from a large page. Growth is not free at
1178+
* this size for the same reason: arena_grow doubles to 1 MiB, which is above
1179+
* that bound and so a singleton OS allocation. One file in twelve thousand
1180+
* pays it, which is why the cost is accepted. */
1181+
enum { CBM_EXTRACT_SCRATCH_BLOCK = CBM_SZ_512 * CBM_SZ_1K };
1182+
1183+
static CBMFileResult *extract_file_ex_body(const char *source, int source_len, CBMLanguage language,
1184+
const char *project, const char *rel_path,
1185+
int64_t timeout_micros, const char **extra_defines,
1186+
const char **include_paths,
1187+
const CBMMacroTable *macro_table,
1188+
const CBMReturnTypeTable *return_type_table,
1189+
CBMArena *scratch) {
11701190
// Allocate result on heap (arena inside for all string data)
11711191
enum { SINGLE = 1 };
11721192
CBMFileResult *result = (CBMFileResult *)calloc(SINGLE, sizeof(CBMFileResult));
@@ -1277,6 +1297,7 @@ CBMFileResult *cbm_extract_file_ex(const char *source, int source_len, CBMLangua
12771297
// Build extraction context
12781298
CBMExtractCtx ctx = {
12791299
.arena = a,
1300+
.scratch = scratch,
12801301
.result = result,
12811302
.source = source,
12821303
.source_len = source_len,
@@ -1398,6 +1419,7 @@ CBMFileResult *cbm_extract_file_ex(const char *source, int source_len, CBMLangua
13981419
// Build context for expanded source — extract only calls via unified extractor
13991420
CBMExtractCtx pp_ctx = {
14001421
.arena = a,
1422+
.scratch = scratch,
14011423
.result = result,
14021424
.source = expanded,
14031425
.source_len = expanded_len,
@@ -1634,6 +1656,26 @@ CBMFileResult *cbm_extract_file_ex(const char *source, int source_len, CBMLangua
16341656
return result;
16351657
}
16361658

1659+
/* Public entry. Owns the traversal scratch arena for the whole of one file's
1660+
* extraction: created here, handed to the body as ctx->scratch, destroyed on
1661+
* the way out. The body has seven early returns, so bracketing it in a wrapper
1662+
* is what keeps that to one create and one destroy. If the arena cannot be
1663+
* created, the body is handed NULL and the traversal stacks fall back to the
1664+
* result arena, which is what shipped before #1997. */
1665+
CBMFileResult *cbm_extract_file_ex(const char *source, int source_len, CBMLanguage language,
1666+
const char *project, const char *rel_path,
1667+
int64_t timeout_micros, const char **extra_defines,
1668+
const char **include_paths, const CBMMacroTable *macro_table,
1669+
const CBMReturnTypeTable *return_type_table) {
1670+
CBMArena scratch;
1671+
cbm_arena_init_sized(&scratch, CBM_EXTRACT_SCRATCH_BLOCK);
1672+
CBMFileResult *result = extract_file_ex_body(
1673+
source, source_len, language, project, rel_path, timeout_micros, extra_defines,
1674+
include_paths, macro_table, return_type_table, scratch.nblocks > 0 ? &scratch : NULL);
1675+
cbm_arena_destroy(&scratch);
1676+
return result;
1677+
}
1678+
16371679
void cbm_free_result(CBMFileResult *result) {
16381680
if (!result) {
16391681
return;

‎internal/cbm/cbm.h‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,13 @@ typedef struct {
574574

575575
typedef struct {
576576
CBMArena *arena;
577+
/* Scratch for AST traversal, owned by the cbm_extract_file_ex call that
578+
* built this context and destroyed when it returns. Nothing a
579+
* CBMFileResult points at may be allocated here: `arena` is the result's
580+
* own, and it outlives extraction by the whole pipeline (#1997). NULL in a
581+
* context built without one, in which case the stacks fall back to
582+
* `arena`. */
583+
CBMArena *scratch;
577584
CBMFileResult *result;
578585
const char *source;
579586
int source_len;

‎internal/cbm/extract_channels.c‎

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ static const char *literal_from_first_child(CBMExtractCtx *ctx, TSNode node) {
101101
* const table per file is sufficient for the common Socket.IO pattern). */
102102
static void scan_string_consts_js(CBMExtractCtx *ctx, chan_const_table_t *tbl) {
103103
TSNodeStack stack;
104-
ts_nstack_init(&stack, ctx->arena, CHAN_STACK_CAP);
105-
ts_nstack_push(&stack, ctx->arena, ctx->root);
104+
ts_nstack_init(&stack, ctx, CHAN_STACK_CAP);
105+
ts_nstack_push(&stack, ctx->root);
106106

107107
while (stack.count > 0 && tbl->count < CHAN_CONST_CAP) {
108108
TSNode node = ts_nstack_pop(&stack);
@@ -128,15 +128,15 @@ static void scan_string_consts_js(CBMExtractCtx *ctx, chan_const_table_t *tbl) {
128128
}
129129
}
130130

131-
ts_nstack_push_children(&stack, ctx->arena, node);
131+
ts_nstack_push_children(&stack, node);
132132
}
133133
}
134134

135135
/* Python constant resolution: NAME = "value" (assignment node). */
136136
static void scan_string_consts_python(CBMExtractCtx *ctx, chan_const_table_t *tbl) {
137137
TSNodeStack stack;
138-
ts_nstack_init(&stack, ctx->arena, CHAN_STACK_CAP);
139-
ts_nstack_push(&stack, ctx->arena, ctx->root);
138+
ts_nstack_init(&stack, ctx, CHAN_STACK_CAP);
139+
ts_nstack_push(&stack, ctx->root);
140140

141141
while (stack.count > 0 && tbl->count < CHAN_CONST_CAP) {
142142
TSNode node = ts_nstack_pop(&stack);
@@ -163,7 +163,7 @@ static void scan_string_consts_python(CBMExtractCtx *ctx, chan_const_table_t *tb
163163

164164
uint32_t count = ts_node_child_count(node);
165165
for (int i = (int)count - SKIP_ONE; i >= 0; i--) {
166-
ts_nstack_push(&stack, ctx->arena, ts_node_child(node, (uint32_t)i));
166+
ts_nstack_push(&stack, ts_node_child(node, (uint32_t)i));
167167
}
168168
}
169169
}
@@ -373,15 +373,15 @@ static void extract_channels_js(CBMExtractCtx *ctx) {
373373

374374
/* Second pass: walk the tree looking for call_expression nodes. */
375375
TSNodeStack stack;
376-
ts_nstack_init(&stack, ctx->arena, CHAN_STACK_CAP);
377-
ts_nstack_push(&stack, ctx->arena, ctx->root);
376+
ts_nstack_init(&stack, ctx, CHAN_STACK_CAP);
377+
ts_nstack_push(&stack, ctx->root);
378378

379379
while (stack.count > 0) {
380380
TSNode node = ts_nstack_pop(&stack);
381381
if (strcmp(ts_node_type(node), "call_expression") == 0) {
382382
js_process_call(ctx, node, &consts);
383383
}
384-
ts_nstack_push_children(&stack, ctx->arena, node);
384+
ts_nstack_push_children(&stack, node);
385385
}
386386
}
387387

@@ -549,8 +549,8 @@ static void extract_channels_python(CBMExtractCtx *ctx) {
549549
scan_string_consts_python(ctx, &consts);
550550

551551
TSNodeStack stack;
552-
ts_nstack_init(&stack, ctx->arena, CHAN_STACK_CAP);
553-
ts_nstack_push(&stack, ctx->arena, ctx->root);
552+
ts_nstack_init(&stack, ctx, CHAN_STACK_CAP);
553+
ts_nstack_push(&stack, ctx->root);
554554

555555
while (stack.count > 0) {
556556
TSNode node = ts_nstack_pop(&stack);
@@ -562,7 +562,7 @@ static void extract_channels_python(CBMExtractCtx *ctx) {
562562
}
563563
uint32_t count = ts_node_child_count(node);
564564
for (int i = (int)count - SKIP_ONE; i >= 0; i--) {
565-
ts_nstack_push(&stack, ctx->arena, ts_node_child(node, (uint32_t)i));
565+
ts_nstack_push(&stack, ts_node_child(node, (uint32_t)i));
566566
}
567567
}
568568
}
@@ -627,8 +627,8 @@ static void go_process_call(CBMExtractCtx *ctx, TSNode call) {
627627

628628
static void extract_channels_go(CBMExtractCtx *ctx) {
629629
TSNodeStack stack;
630-
ts_nstack_init(&stack, ctx->arena, CHAN_STACK_CAP);
631-
ts_nstack_push(&stack, ctx->arena, ctx->root);
630+
ts_nstack_init(&stack, ctx, CHAN_STACK_CAP);
631+
ts_nstack_push(&stack, ctx->root);
632632

633633
while (stack.count > 0) {
634634
TSNode node = ts_nstack_pop(&stack);
@@ -637,7 +637,7 @@ static void extract_channels_go(CBMExtractCtx *ctx) {
637637
}
638638
uint32_t count = ts_node_child_count(node);
639639
for (int i = (int)count - SKIP_ONE; i >= 0; i--) {
640-
ts_nstack_push(&stack, ctx->arena, ts_node_child(node, (uint32_t)i));
640+
ts_nstack_push(&stack, ts_node_child(node, (uint32_t)i));
641641
}
642642
}
643643
}
@@ -724,8 +724,8 @@ static void java_process_annotation(CBMExtractCtx *ctx, TSNode annotation) {
724724

725725
static void extract_channels_java(CBMExtractCtx *ctx) {
726726
TSNodeStack stack;
727-
ts_nstack_init(&stack, ctx->arena, CHAN_STACK_CAP);
728-
ts_nstack_push(&stack, ctx->arena, ctx->root);
727+
ts_nstack_init(&stack, ctx, CHAN_STACK_CAP);
728+
ts_nstack_push(&stack, ctx->root);
729729

730730
while (stack.count > 0) {
731731
TSNode node = ts_nstack_pop(&stack);
@@ -737,7 +737,7 @@ static void extract_channels_java(CBMExtractCtx *ctx) {
737737
}
738738
uint32_t count = ts_node_child_count(node);
739739
for (int i = (int)count - SKIP_ONE; i >= 0; i--) {
740-
ts_nstack_push(&stack, ctx->arena, ts_node_child(node, (uint32_t)i));
740+
ts_nstack_push(&stack, ts_node_child(node, (uint32_t)i));
741741
}
742742
}
743743
}
@@ -793,8 +793,8 @@ static void csharp_process_call(CBMExtractCtx *ctx, TSNode call) {
793793

794794
static void extract_channels_csharp(CBMExtractCtx *ctx) {
795795
TSNodeStack stack;
796-
ts_nstack_init(&stack, ctx->arena, CHAN_STACK_CAP);
797-
ts_nstack_push(&stack, ctx->arena, ctx->root);
796+
ts_nstack_init(&stack, ctx, CHAN_STACK_CAP);
797+
ts_nstack_push(&stack, ctx->root);
798798

799799
while (stack.count > 0) {
800800
TSNode node = ts_nstack_pop(&stack);
@@ -803,7 +803,7 @@ static void extract_channels_csharp(CBMExtractCtx *ctx) {
803803
}
804804
uint32_t count = ts_node_child_count(node);
805805
for (int i = (int)count - SKIP_ONE; i >= 0; i--) {
806-
ts_nstack_push(&stack, ctx->arena, ts_node_child(node, (uint32_t)i));
806+
ts_nstack_push(&stack, ts_node_child(node, (uint32_t)i));
807807
}
808808
}
809809
}
@@ -856,8 +856,8 @@ static void ruby_process_call(CBMExtractCtx *ctx, TSNode call) {
856856

857857
static void extract_channels_ruby(CBMExtractCtx *ctx) {
858858
TSNodeStack stack;
859-
ts_nstack_init(&stack, ctx->arena, CHAN_STACK_CAP);
860-
ts_nstack_push(&stack, ctx->arena, ctx->root);
859+
ts_nstack_init(&stack, ctx, CHAN_STACK_CAP);
860+
ts_nstack_push(&stack, ctx->root);
861861

862862
while (stack.count > 0) {
863863
TSNode node = ts_nstack_pop(&stack);
@@ -866,7 +866,7 @@ static void extract_channels_ruby(CBMExtractCtx *ctx) {
866866
}
867867
uint32_t count = ts_node_child_count(node);
868868
for (int i = (int)count - SKIP_ONE; i >= 0; i--) {
869-
ts_nstack_push(&stack, ctx->arena, ts_node_child(node, (uint32_t)i));
869+
ts_nstack_push(&stack, ts_node_child(node, (uint32_t)i));
870870
}
871871
}
872872
}
@@ -958,8 +958,8 @@ static void elixir_process_function_def(CBMExtractCtx *ctx, TSNode func_def) {
958958

959959
static void extract_channels_elixir(CBMExtractCtx *ctx) {
960960
TSNodeStack stack;
961-
ts_nstack_init(&stack, ctx->arena, CHAN_STACK_CAP);
962-
ts_nstack_push(&stack, ctx->arena, ctx->root);
961+
ts_nstack_init(&stack, ctx, CHAN_STACK_CAP);
962+
ts_nstack_push(&stack, ctx->root);
963963

964964
while (stack.count > 0) {
965965
TSNode node = ts_nstack_pop(&stack);
@@ -971,7 +971,7 @@ static void extract_channels_elixir(CBMExtractCtx *ctx) {
971971
}
972972
uint32_t count = ts_node_child_count(node);
973973
for (int i = (int)count - SKIP_ONE; i >= 0; i--) {
974-
ts_nstack_push(&stack, ctx->arena, ts_node_child(node, (uint32_t)i));
974+
ts_nstack_push(&stack, ts_node_child(node, (uint32_t)i));
975975
}
976976
}
977977
}
@@ -1034,8 +1034,8 @@ static void rust_process_call(CBMExtractCtx *ctx, TSNode call) {
10341034

10351035
static void extract_channels_rust(CBMExtractCtx *ctx) {
10361036
TSNodeStack stack;
1037-
ts_nstack_init(&stack, ctx->arena, CHAN_STACK_CAP);
1038-
ts_nstack_push(&stack, ctx->arena, ctx->root);
1037+
ts_nstack_init(&stack, ctx, CHAN_STACK_CAP);
1038+
ts_nstack_push(&stack, ctx->root);
10391039

10401040
while (stack.count > 0) {
10411041
TSNode node = ts_nstack_pop(&stack);
@@ -1044,7 +1044,7 @@ static void extract_channels_rust(CBMExtractCtx *ctx) {
10441044
}
10451045
uint32_t count = ts_node_child_count(node);
10461046
for (int i = (int)count - SKIP_ONE; i >= 0; i--) {
1047-
ts_nstack_push(&stack, ctx->arena, ts_node_child(node, (uint32_t)i));
1047+
ts_nstack_push(&stack, ts_node_child(node, (uint32_t)i));
10481048
}
10491049
}
10501050
}

‎internal/cbm/extract_defs.c‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5054,8 +5054,8 @@ static TSNode emit_elixir_module_class(CBMExtractCtx *ctx, TSNode cur) {
50545054
static void extract_elixir_call(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec *spec) {
50555055
(void)spec;
50565056
TSNodeStack stack;
5057-
ts_nstack_init(&stack, ctx->arena, CBM_SZ_64);
5058-
ts_nstack_push(&stack, ctx->arena, node);
5057+
ts_nstack_init(&stack, ctx, CBM_SZ_64);
5058+
ts_nstack_push(&stack, node);
50595059

50605060
while (stack.count > 0) {
50615061
TSNode cur = ts_nstack_pop(&stack);
@@ -5083,7 +5083,7 @@ static void extract_elixir_call(CBMExtractCtx *ctx, TSNode node, const CBMLangSp
50835083
for (int di = (int)dbc - SKIP_CHAR; di >= 0; di--) {
50845084
TSNode dchild = ts_node_child(do_block, (uint32_t)di);
50855085
if (!ts_node_is_null(dchild) && strcmp(ts_node_type(dchild), "call") == 0) {
5086-
ts_nstack_push(&stack, ctx->arena, dchild);
5086+
ts_nstack_push(&stack, dchild);
50875087
}
50885088
}
50895089
}
@@ -6021,8 +6021,8 @@ static void extract_var_names(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec
60216021
// Used by YAML, TOML, INI, JSON.
60226022
static void walk_variables_iter(CBMExtractCtx *ctx, TSNode root, const CBMLangSpec *spec) {
60236023
TSNodeStack stack;
6024-
ts_nstack_init(&stack, ctx->arena, CBM_SZ_256);
6025-
ts_nstack_push(&stack, ctx->arena, root);
6024+
ts_nstack_init(&stack, ctx, CBM_SZ_256);
6025+
ts_nstack_push(&stack, root);
60266026

60276027
while (stack.count > 0) {
60286028
TSNode node = ts_nstack_pop(&stack);
@@ -6046,7 +6046,7 @@ static void walk_variables_iter(CBMExtractCtx *ctx, TSNode root, const CBMLangSp
60466046
strcmp(ck, "section") == 0 || strcmp(ck, "object") == 0 ||
60476047
strcmp(ck, "array") == 0 || strcmp(ck, "pair") == 0 || strcmp(ck, "element") == 0 ||
60486048
strcmp(ck, "content") == 0) {
6049-
ts_nstack_push(&stack, ctx->arena, child);
6049+
ts_nstack_push(&stack, child);
60506050
}
60516051
}
60526052
}
@@ -6749,10 +6749,10 @@ static void wd_push_children_reverse(wd_stack_t *s, TSNode node, const char *enc
67496749
// Push nested class nodes from a class body container onto the defs stack.
67506750
// Iteratively walks into wrapper nodes (field_declaration, template_declaration).
67516751
static void push_nested_class_nodes(TSNode body, const CBMLangSpec *spec, wd_stack_t *s,
6752-
const char *enclosing_qn, CBMArena *arena) {
6752+
const char *enclosing_qn, const CBMExtractCtx *ctx) {
67536753
TSNodeStack nc_stack;
6754-
ts_nstack_init(&nc_stack, arena, NESTED_CLASS_STACK_CAP);
6755-
ts_nstack_push(&nc_stack, arena, body);
6754+
ts_nstack_init(&nc_stack, ctx, NESTED_CLASS_STACK_CAP);
6755+
ts_nstack_push(&nc_stack, body);
67566756

67576757
while (nc_stack.count > 0) {
67586758
TSNode cur = ts_nstack_pop(&nc_stack);
@@ -6767,7 +6767,7 @@ static void push_nested_class_nodes(TSNode body, const CBMLangSpec *spec, wd_sta
67676767
const char *ck = ts_node_type(child);
67686768
if (strcmp(ck, "field_declaration") == 0 ||
67696769
strcmp(ck, "template_declaration") == 0 || strcmp(ck, "declaration") == 0) {
6770-
ts_nstack_push(&nc_stack, arena, child);
6770+
ts_nstack_push(&nc_stack, child);
67716771
}
67726772
}
67736773
}
@@ -6883,7 +6883,7 @@ static void extract_typescript_namespace_def(CBMExtractCtx *ctx, TSNode node,
68836883

68846884
// Push nested class children from a class body container onto the walk stack.
68856885
static void push_class_body_children(TSNode node, const CBMLangSpec *spec, wd_stack_t *s,
6886-
const char *new_enclosing, CBMArena *arena) {
6886+
const char *new_enclosing, const CBMExtractCtx *ctx) {
68876887
/* Use the same language-aware body selection as method extraction. The old
68886888
* independent spelling list omitted valid containers such as Scala's
68896889
* `template_body` and Solidity's contract body. Methods were extracted
@@ -6899,7 +6899,7 @@ static void push_class_body_children(TSNode node, const CBMLangSpec *spec, wd_st
68996899
body = find_class_member_body(node, spec->language);
69006900
}
69016901
if (!ts_node_is_null(body)) {
6902-
push_nested_class_nodes(body, spec, s, new_enclosing, arena);
6902+
push_nested_class_nodes(body, spec, s, new_enclosing, ctx);
69036903
return;
69046904
}
69056905

@@ -7423,7 +7423,7 @@ static void walk_defs(CBMExtractCtx *ctx, TSNode root, const CBMLangSpec *spec,
74237423
if (cbm_kind_in_set(node, spec->class_node_types)) {
74247424
extract_class_def(ctx, node, spec);
74257425
const char *new_enclosing = compute_class_qn(ctx, node, frame.enclosing_class_qn);
7426-
push_class_body_children(node, spec, &s, new_enclosing, ctx->arena);
7426+
push_class_body_children(node, spec, &s, new_enclosing, ctx);
74277427
continue;
74287428
}
74297429

0 commit comments

Comments
 (0)