Given a method-invocation node and its resolved CBMRegisteredFunc, walk * each lambda argument: bind its formal_parameters to the SAM's parameter * types (with generic substitution from the receiver's template args), then * resolve calls inside the lambda body against the freshly-bound scope. * * Returns a bitmask of arg indices that were handled here so the generic * walker can skip them. *
| 2413 | * Returns a bitmask of arg indices that were handled here so the generic |
| 2414 | * walker can skip them. */ |
| 2415 | static uint32_t bind_lambda_args(JavaLSPContext *ctx, TSNode call_node, |
| 2416 | const CBMRegisteredFunc *resolved, const CBMType *recv_type) { |
| 2417 | uint32_t handled_mask = 0; |
| 2418 | TSNode args_node = ts_node_child_by_field_name(call_node, "arguments", 9); |
| 2419 | if (ts_node_is_null(args_node)) |
| 2420 | return handled_mask; |
| 2421 | const CBMType *const *param_types = NULL; |
| 2422 | if (resolved && resolved->signature && resolved->signature->kind == CBM_TYPE_FUNC) { |
| 2423 | param_types = resolved->signature->data.func.param_types; |
| 2424 | } |
| 2425 | /* param_types is NULL-terminated with the DECLARED param count — the call |
| 2426 | * site may pass MORE arguments (overload mismatch, varargs). Indexing by |
| 2427 | * the raw argument index read past the terminator and dereferenced |
| 2428 | * whatever followed in the arena (elasticsearch SIGSEGV; same OOB family |
| 2429 | * as #427). Bound every access by the array's own length. */ |
| 2430 | int param_type_count = 0; |
| 2431 | if (param_types) { |
| 2432 | while (param_types[param_type_count]) { |
| 2433 | param_type_count++; |
| 2434 | } |
| 2435 | } |
| 2436 | /* Even without registry param_types, the heuristic (recv_qn + method_name |
| 2437 | * → arg shape) often pins the lambda type — that's the path that |
| 2438 | * handles `xs.forEach(x -> ...)` and `xs.stream().filter(x -> ...)` |
| 2439 | * given that stdlib registrations don't model arg types. */ |
| 2440 | |
| 2441 | /* Gather receiver template args for substitution. */ |
| 2442 | const CBMType *const *recv_targs = NULL; |
| 2443 | int recv_targ_count = 0; |
| 2444 | const char *recv_qn = NULL; |
| 2445 | if (recv_type && recv_type->kind == CBM_TYPE_TEMPLATE) { |
| 2446 | recv_targs = recv_type->data.template_type.template_args; |
| 2447 | recv_targ_count = recv_type->data.template_type.arg_count; |
| 2448 | recv_qn = recv_type->data.template_type.template_name; |
| 2449 | } else if (recv_type && recv_type->kind == CBM_TYPE_NAMED) { |
| 2450 | recv_qn = recv_type->data.named.qualified_name; |
| 2451 | } |
| 2452 | |
| 2453 | /* Method name (for heuristic). */ |
| 2454 | TSNode mname_node = ts_node_child_by_field_name(call_node, "name", 4); |
| 2455 | char *mname = ts_node_is_null(mname_node) ? NULL : java_node_text(ctx, mname_node); |
| 2456 | |
| 2457 | uint32_t n = ts_node_named_child_count(args_node); |
| 2458 | for (uint32_t i = 0; i < n && i < 32; i++) { |
| 2459 | TSNode arg = ts_node_named_child(args_node, i); |
| 2460 | const char *kind = ts_node_type(arg); |
| 2461 | if (strcmp(kind, "lambda_expression") != 0) |
| 2462 | continue; |
| 2463 | |
| 2464 | /* First try registry-driven SAM inference. */ |
| 2465 | const CBMType *expected = |
| 2466 | (param_types && i < (uint32_t)param_type_count) ? param_types[i] : NULL; |
| 2467 | if (!expected && recv_qn && mname && recv_targ_count > 0) { |
| 2468 | /* Heuristic path: bind lambda directly using receiver template |
| 2469 | * args + recognized method name, skipping the SAM table. */ |
| 2470 | int h_arity = 0; |
| 2471 | const CBMType *h_p0 = NULL; |
| 2472 | const CBMType *h_p1 = NULL; |
no test coverage detected