| 702 | } |
| 703 | |
| 704 | static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len, |
| 705 | CBMLanguage language, const char *project, |
| 706 | const char *rel_path, int64_t timeout_micros, |
| 707 | const char **extra_defines, |
| 708 | const char **include_paths) { |
| 709 | // Allocate result on heap (arena inside for all string data) |
| 710 | enum { SINGLE = 1 }; |
| 711 | CBMFileResult *result = (CBMFileResult *)calloc(SINGLE, sizeof(CBMFileResult)); |
| 712 | if (!result) { |
| 713 | return NULL; |
| 714 | } |
| 715 | |
| 716 | cbm_arena_init(&result->arena); |
| 717 | CBMArena *a = &result->arena; |
| 718 | |
| 719 | /* Crash-quarantine hard guard (Stage 3c): a file the supervisor pinned as a |
| 720 | * crasher must NEVER be parsed again. Return a clean empty result BEFORE the |
| 721 | * marker write and fault injector so no pass (including sequential re-extract |
| 722 | * passes that miss the result cache) can crash on it. The pipeline extract |
| 723 | * loops separately record it as a phase="crash" skip. Checked before the |
| 724 | * marker so quarantined files never overwrite it — the marker keeps pointing |
| 725 | * at the real (non-quarantined) file being processed when a crash hits. */ |
| 726 | if (cbm_index_is_quarantined(rel_path)) { |
| 727 | return result; |
| 728 | } |
| 729 | |
| 730 | cbm_index_mark_start(rel_path); |
| 731 | cbm_test_fault_inject(rel_path); |
| 732 | |
| 733 | // Get language spec |
| 734 | const CBMLangSpec *spec = cbm_lang_spec(language); |
| 735 | if (!spec) { |
| 736 | result->has_error = true; |
| 737 | result->error_msg = cbm_arena_strdup(a, "unsupported language"); |
| 738 | return result; |
| 739 | } |
| 740 | |
| 741 | // Get tree-sitter language |
| 742 | const TSLanguage *ts_lang = cbm_ts_language(language); |
| 743 | if (!ts_lang) { |
| 744 | result->has_error = true; |
| 745 | result->error_msg = cbm_arena_strdup(a, "no tree-sitter grammar"); |
| 746 | return result; |
| 747 | } |
| 748 | |
| 749 | // Get thread-local parser (reused across files on same thread) |
| 750 | TSParser *parser = get_thread_parser(ts_lang, language); |
| 751 | if (!parser) { |
| 752 | result->has_error = true; |
| 753 | result->error_msg = cbm_arena_strdup(a, "parser alloc failed"); |
| 754 | return result; |
| 755 | } |
| 756 | |
| 757 | // Reset parser state from any previous parse (cancellation flags etc.) |
| 758 | ts_parser_reset(parser); |
| 759 | |
| 760 | uint64_t t0 = now_ns(); |
| 761 |
no test coverage detected