Insert an edge, splicing the call-site line (,"line":N) in before the closing * brace when one was captured. Mirrors finalize_and_emit() on the parallel path * so CALLS edges carry their source line regardless of resolution path. Restricted * to CALLS: route/config edge props feed full-only predump passes * (create_route_nodes/create_data_flows), so altering them desyncs full vs * incremental
| 275 | * (< 50 file) repos that take the sequential path (#514). Mirrors the parallel |
| 276 | * path's append_args_json shape so both pipelines agree. */ |
| 277 | static void calls_append_args(char *props, size_t cap, const CBMCall *call) { |
| 278 | if (!call || call->arg_count <= 0) { |
| 279 | return; |
| 280 | } |
| 281 | size_t len = strlen(props); |
| 282 | if (len < SKIP_ONE || props[len - SKIP_ONE] != '}') { |
| 283 | return; |
| 284 | } |
| 285 | /* Overwrite the trailing '}' and rebuild it after the args array. */ |
| 286 | size_t pos = len - SKIP_ONE; |
| 287 | int n = snprintf(props + pos, cap - pos, ",\"args\":["); |
| 288 | if (n <= 0 || (size_t)n >= cap - pos) { |
| 289 | return; |
| 290 | } |
| 291 | pos += (size_t)n; |
| 292 | for (int i = 0; i < call->arg_count; i++) { |
| 293 | const CBMCallArg *a = &call->args[i]; |
| 294 | char esc_e[CBM_SZ_256]; |
| 295 | cbm_json_escape(esc_e, sizeof(esc_e), a->expr ? a->expr : ""); |
| 296 | char one[CBM_SZ_512]; |
| 297 | if (a->value) { |
| 298 | char esc_v[CBM_SZ_256]; |
| 299 | cbm_json_escape(esc_v, sizeof(esc_v), a->value); |
| 300 | n = snprintf(one, sizeof(one), "%s{\"i\":%d,\"e\":\"%s\",\"v\":\"%s\"}", |
| 301 | i > 0 ? "," : "", a->index, esc_e, esc_v); |
| 302 | } else { |
| 303 | n = snprintf(one, sizeof(one), "%s{\"i\":%d,\"e\":\"%s\"}", i > 0 ? "," : "", a->index, |
| 304 | esc_e); |
| 305 | } |
| 306 | /* Add rather than subtract: pos is unsigned, so `cap - pos - PAIR_LEN` |
| 307 | * wraps once pos reaches cap - PAIR_LEN and stops bounding the memcpy |
| 308 | * below. The closing write at the end of this function already guards |
| 309 | * additively; match it. */ |
| 310 | if (n <= 0 || pos + (size_t)n + PAIR_LEN >= cap) { |
| 311 | break; /* not enough room — close the array with what fits */ |
| 312 | } |
| 313 | memcpy(props + pos, one, (size_t)n); |
| 314 | pos += (size_t)n; |
| 315 | } |
| 316 | if (pos + PAIR_LEN < cap) { |
| 317 | props[pos++] = ']'; |
| 318 | props[pos++] = '}'; |
| 319 | props[pos] = '\0'; |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | static void calls_emit_edge(cbm_gbuf_t *gbuf, int64_t src, int64_t tgt, const char *type, |
| 324 | char *props, size_t cap, const CBMCall *call) { |
no test coverage detected