Skip to content

Commit ef14822

Browse files
authored
Merge pull request DeusData#949 from blankanswer/agent/fix-cpp-preproc-signature-gap
fix(cpp): remap recovered definitions to original lines
2 parents 4c60111 + 99d3919 commit ef14822

4 files changed

Lines changed: 439 additions & 37 deletions

File tree

‎internal/cbm/cbm.c‎

Lines changed: 119 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,106 @@ static bool cbm_line_contains(const char *src, int src_len, uint32_t line, const
844844
return false;
845845
}
846846

847+
static bool cbm_identifier_char(char c) {
848+
return isalnum((unsigned char)c) || c == '_';
849+
}
850+
851+
/* Verify that the mapped original span contains callable-definition syntax,
852+
* not merely the name at a macro invocation or call site. */
853+
static bool cbm_span_contains_callable_def(const char *src, int src_len, uint32_t start_line,
854+
uint32_t end_line, const char *name) {
855+
if (!src || src_len <= 0 || !name || !name[0] || start_line == 0 || end_line < start_line) {
856+
return false;
857+
}
858+
int span_start = 0;
859+
uint32_t line = 1;
860+
while (span_start < src_len && line < start_line) {
861+
if (src[span_start++] == '\n') {
862+
line++;
863+
}
864+
}
865+
if (line != start_line) {
866+
return false;
867+
}
868+
int span_end = span_start;
869+
while (span_end < src_len && line <= end_line) {
870+
if (src[span_end++] == '\n') {
871+
line++;
872+
}
873+
}
874+
size_t name_len = strlen(name);
875+
for (int pos = span_start; pos + (int)name_len <= span_end; pos++) {
876+
if (strncmp(src + pos, name, name_len) != 0 ||
877+
(pos > 0 && cbm_identifier_char(src[pos - 1])) ||
878+
(pos + (int)name_len < src_len && cbm_identifier_char(src[pos + name_len]))) {
879+
continue;
880+
}
881+
int before = pos;
882+
while (before > span_start && isspace((unsigned char)src[before - 1])) {
883+
before--;
884+
}
885+
if (before > span_start && strchr("(,=!?[.", src[before - 1])) {
886+
continue;
887+
}
888+
int open = pos + (int)name_len;
889+
while (open < span_end && isspace((unsigned char)src[open])) {
890+
open++;
891+
}
892+
if (open >= span_end || src[open] != '(') {
893+
continue;
894+
}
895+
int depth = 0;
896+
int close = -1;
897+
for (int i = open; i < span_end; i++) {
898+
if (src[i] == '(') {
899+
depth++;
900+
} else if (src[i] == ')' && --depth == 0) {
901+
close = i;
902+
break;
903+
}
904+
}
905+
if (close < 0) {
906+
continue;
907+
}
908+
for (int i = close + 1; i < span_end; i++) {
909+
if (src[i] == '{') {
910+
return true;
911+
}
912+
if (src[i] == ';') {
913+
break;
914+
}
915+
}
916+
}
917+
return false;
918+
}
919+
920+
/* Remap an expanded-source definition back to the original input file. Every
921+
* line in the definition must be attributable to the main file; generated
922+
* macro bodies, included headers, and ambiguous spans fail closed. */
923+
static bool cbm_remap_preprocessed_def(CBMDefinition *def, const CBMPreprocessedSource *pp) {
924+
if (!def || !pp || !pp->original_line_by_expanded_line || !pp->belongs_to_main_file ||
925+
def->start_line == 0 || def->end_line < def->start_line ||
926+
def->end_line > (uint32_t)pp->expanded_line_count) {
927+
return false;
928+
}
929+
930+
uint32_t original_start = pp->original_line_by_expanded_line[def->start_line];
931+
uint32_t original_end = pp->original_line_by_expanded_line[def->end_line];
932+
if (!original_start || !original_end || original_end < original_start) {
933+
return false;
934+
}
935+
for (uint32_t line = def->start_line; line <= def->end_line; line++) {
936+
if (!pp->belongs_to_main_file[line] || !pp->original_line_by_expanded_line[line]) {
937+
return false;
938+
}
939+
}
940+
941+
def->start_line = original_start;
942+
def->end_line = original_end;
943+
def->lines = (int)(original_end - original_start + 1);
944+
return true;
945+
}
946+
847947
static void cbm_subtract_recovered_regions(cbm_error_regions_t *regs, const CBMDefArray *defs) {
848948
int kept = 0;
849949
for (int i = 0; i < regs->count; i++) {
@@ -1089,9 +1189,10 @@ CBMFileResult *cbm_extract_file_ex(const char *source, int source_len, CBMLangua
10891189
// Defs keep original-source line numbers; only CALLS are extracted from expanded source.
10901190
if (language == CBM_LANG_C || language == CBM_LANG_CPP || language == CBM_LANG_CUDA) {
10911191
uint64_t pp_start = now_ns();
1092-
char *expanded = cbm_preprocess(source, source_len, rel_path, extra_defines, include_paths,
1093-
language != CBM_LANG_C);
1094-
if (expanded) {
1192+
CBMPreprocessedSource *preprocessed = cbm_preprocess_with_map(
1193+
source, source_len, rel_path, extra_defines, include_paths, language != CBM_LANG_C);
1194+
if (preprocessed && preprocessed->source) {
1195+
char *expanded = preprocessed->source;
10951196
int expanded_len = (int)strlen(expanded);
10961197
// Record calls count before second pass
10971198
int calls_before = result->calls.count;
@@ -1156,15 +1257,20 @@ CBMFileResult *cbm_extract_file_ex(const char *source, int source_len, CBMLangua
11561257
for (int i = defs_before; i < result->defs.count; i++) {
11571258
CBMDefinition *d = &result->defs.items[i];
11581259
bool adopt = false;
1159-
for (int rj = 0; rj < raw_regs.count && !adopt; rj++) {
1160-
if (d->start_line <= raw_regs.ends[rj] &&
1161-
d->end_line >= raw_regs.starts[rj]) {
1162-
adopt = true;
1260+
if (cbm_remap_preprocessed_def(d, preprocessed)) {
1261+
for (int rj = 0; rj < raw_regs.count && !adopt; rj++) {
1262+
if (d->start_line <= raw_regs.ends[rj] &&
1263+
d->end_line >= raw_regs.starts[rj]) {
1264+
adopt = true;
1265+
}
11631266
}
11641267
}
1165-
if (adopt &&
1166-
(!d->name || !cbm_line_contains(source, source_len,
1167-
d->start_line, d->name))) {
1268+
if (adopt && (!d->name ||
1269+
!cbm_line_contains(source, source_len, d->start_line,
1270+
d->name) ||
1271+
!cbm_span_contains_callable_def(
1272+
source, source_len, d->start_line, d->end_line,
1273+
d->name))) {
11681274
adopt = false;
11691275
}
11701276
for (int j = 0; j < defs_before && adopt; j++) {
@@ -1185,9 +1291,11 @@ CBMFileResult *cbm_extract_file_ex(const char *source, int source_len, CBMLangua
11851291
ts_tree_delete(pp_tree);
11861292
}
11871293
}
1188-
cbm_preprocess_free(expanded);
1294+
cbm_preprocessed_source_free(preprocessed);
11891295
atomic_fetch_add(&total_files_preprocessed, 1);
11901296
(void)calls_before; // used for future logging
1297+
} else {
1298+
cbm_preprocessed_source_free(preprocessed);
11911299
}
11921300
atomic_fetch_add(&total_preprocess_ns, now_ns() - pp_start);
11931301
}

‎internal/cbm/preprocessor.cpp‎

Lines changed: 166 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,45 +10,145 @@
1010
#include <vector>
1111
#include <cstring>
1212
#include <cstdlib>
13+
#include <cstdint>
1314

1415
extern "C" {
1516

16-
char* cbm_preprocess(
17-
const char* source, int source_len,
18-
const char* filename,
19-
const char** extra_defines,
20-
const char** include_paths,
21-
int cpp_mode
22-
) {
23-
if (!source || source_len <= 0) return NULL;
24-
25-
// Fast-path: skip if no preprocessor directives worth expanding.
26-
bool has_macros = false;
17+
static bool has_preprocessor_work(const char *source, int source_len) {
18+
if (!source || source_len <= 0) {
19+
return false;
20+
}
2721
for (int i = 0; i < source_len - 1; i++) {
2822
if (source[i] == '#') {
2923
// Skip whitespace after #
3024
int j = i + 1;
31-
while (j < source_len && (source[j] == ' ' || source[j] == '\t')) j++;
25+
while (j < source_len && (source[j] == ' ' || source[j] == '\t'))
26+
j++;
3227
int remaining = source_len - j;
3328
if (remaining >= 6 && strncmp(source + j, "define", 6) == 0) {
34-
has_macros = true;
35-
break;
29+
return true;
3630
}
3731
if (remaining >= 5 && strncmp(source + j, "ifdef", 5) == 0) {
38-
has_macros = true;
39-
break;
32+
return true;
4033
}
4134
if (remaining >= 6 && strncmp(source + j, "ifndef", 6) == 0) {
42-
has_macros = true;
43-
break;
35+
return true;
4436
}
4537
if (remaining >= 3 && strncmp(source + j, "if ", 3) == 0) {
46-
has_macros = true;
47-
break;
38+
return true;
4839
}
4940
}
5041
}
51-
if (!has_macros) return NULL; // NULL = no expansion needed, use original
42+
return false;
43+
}
44+
45+
static int count_expanded_lines(const std::string &text) {
46+
int count = 1;
47+
for (char c : text) {
48+
if (c == '\n') {
49+
count++;
50+
}
51+
}
52+
return count;
53+
}
54+
55+
static bool parse_line_directive(const char *line, size_t len, uint32_t *out_line,
56+
std::string *out_file) {
57+
size_t i = 0;
58+
while (i < len && (line[i] == ' ' || line[i] == '\t')) {
59+
i++;
60+
}
61+
if (i >= len || line[i++] != '#') {
62+
return false;
63+
}
64+
while (i < len && (line[i] == ' ' || line[i] == '\t')) {
65+
i++;
66+
}
67+
static const char prefix[] = "line";
68+
if (i + sizeof(prefix) - 1 > len || strncmp(line + i, prefix, sizeof(prefix) - 1) != 0) {
69+
return false;
70+
}
71+
i += sizeof(prefix) - 1;
72+
if (i >= len || (line[i] != ' ' && line[i] != '\t')) {
73+
return false;
74+
}
75+
while (i < len && (line[i] == ' ' || line[i] == '\t')) {
76+
i++;
77+
}
78+
if (i >= len || line[i] < '0' || line[i] > '9') {
79+
return false;
80+
}
81+
uint64_t parsed_line = 0;
82+
while (i < len && line[i] >= '0' && line[i] <= '9') {
83+
parsed_line = parsed_line * 10u + (uint64_t)(line[i] - '0');
84+
if (parsed_line > UINT32_MAX) {
85+
return false;
86+
}
87+
i++;
88+
}
89+
while (i < len && (line[i] == ' ' || line[i] == '\t')) {
90+
i++;
91+
}
92+
if (i >= len || line[i++] != '"') {
93+
return false;
94+
}
95+
size_t file_start = i;
96+
while (i < len && line[i] != '"') {
97+
i++;
98+
}
99+
if (i >= len) {
100+
return false;
101+
}
102+
*out_line = (uint32_t)parsed_line;
103+
*out_file = std::string(line + file_start, i - file_start);
104+
return true;
105+
}
106+
107+
static bool build_line_map(const std::string &expanded, const std::string &main_file,
108+
uint32_t *original_line_by_expanded_line,
109+
uint8_t *belongs_to_main_file) {
110+
std::string current_file = main_file;
111+
uint32_t current_line = 1;
112+
int expanded_line = 1;
113+
size_t line_start = 0;
114+
115+
while (line_start <= expanded.size()) {
116+
size_t line_end = expanded.find('\n', line_start);
117+
if (line_end == std::string::npos) {
118+
line_end = expanded.size();
119+
}
120+
121+
uint32_t directive_line = 0;
122+
std::string directive_file;
123+
if (parse_line_directive(expanded.c_str() + line_start, line_end - line_start,
124+
&directive_line, &directive_file)) {
125+
current_file = directive_file;
126+
current_line = directive_line;
127+
original_line_by_expanded_line[expanded_line] = 0;
128+
belongs_to_main_file[expanded_line] = 0;
129+
} else {
130+
original_line_by_expanded_line[expanded_line] = current_line;
131+
belongs_to_main_file[expanded_line] = current_file == main_file ? 1 : 0;
132+
if (current_line < UINT32_MAX) {
133+
current_line++;
134+
}
135+
}
136+
137+
if (line_end == expanded.size()) {
138+
break;
139+
}
140+
line_start = line_end + 1;
141+
expanded_line++;
142+
}
143+
return true;
144+
}
145+
146+
CBMPreprocessedSource *cbm_preprocess_with_map(const char *source, int source_len,
147+
const char *filename, const char **extra_defines,
148+
const char **include_paths, int cpp_mode) {
149+
if (!has_preprocessor_work(source, source_len)) {
150+
return NULL; // NULL = no expansion needed, use original
151+
}
52152

53153
try {
54154
simplecpp::DUI dui;
@@ -78,18 +178,58 @@ char* cbm_preprocess(
78178
// Clean up loaded file data
79179
simplecpp::cleanup(filedata);
80180

81-
char* out = (char*)malloc(result.size() + 1);
82-
if (!out) return NULL;
83-
memcpy(out, result.c_str(), result.size() + 1);
84-
return out;
181+
CBMPreprocessedSource *pp = (CBMPreprocessedSource *)calloc(1, sizeof(*pp));
182+
if (!pp) {
183+
return NULL;
184+
}
185+
int line_count = count_expanded_lines(result);
186+
pp->source = (char *)malloc(result.size() + 1);
187+
pp->original_line_by_expanded_line =
188+
(uint32_t *)calloc((size_t)line_count + 1u, sizeof(uint32_t));
189+
pp->belongs_to_main_file = (uint8_t *)calloc((size_t)line_count + 1u, sizeof(uint8_t));
190+
pp->expanded_line_count = line_count;
191+
if (!pp->source || !pp->original_line_by_expanded_line || !pp->belongs_to_main_file) {
192+
cbm_preprocessed_source_free(pp);
193+
return NULL;
194+
}
195+
memcpy(pp->source, result.c_str(), result.size() + 1);
196+
if (!build_line_map(result, files[0], pp->original_line_by_expanded_line,
197+
pp->belongs_to_main_file)) {
198+
cbm_preprocessed_source_free(pp);
199+
return NULL;
200+
}
201+
return pp;
85202
} catch (...) {
86203
// Graceful fallback: return NULL = use original source
87204
return NULL;
88205
}
89206
}
90207

91-
void cbm_preprocess_free(char* expanded) {
208+
char *cbm_preprocess(const char *source, int source_len, const char *filename,
209+
const char **extra_defines, const char **include_paths, int cpp_mode) {
210+
CBMPreprocessedSource *pp = cbm_preprocess_with_map(source, source_len, filename, extra_defines,
211+
include_paths, cpp_mode);
212+
if (!pp) {
213+
return NULL;
214+
}
215+
char *out = pp->source;
216+
pp->source = NULL;
217+
cbm_preprocessed_source_free(pp);
218+
return out;
219+
}
220+
221+
void cbm_preprocess_free(char *expanded) {
92222
free(expanded);
93223
}
94224

225+
void cbm_preprocessed_source_free(CBMPreprocessedSource *pp) {
226+
if (!pp) {
227+
return;
228+
}
229+
free(pp->source);
230+
free(pp->original_line_by_expanded_line);
231+
free(pp->belongs_to_main_file);
232+
free(pp);
233+
}
234+
95235
} // extern "C"

0 commit comments

Comments
 (0)