mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
perf(lsp): collapse repeated Python scope-chain lookups
Add cbm_scope_lookup_binding so callers can read lexical presence, type, and callable identity in one traversal. Route Python expression, call, and callable-reference resolution through that complete binding and use the existing checked bind APIs for allocation-failure detection. The neutral 11-pair production matrix kept exact output parity. Python scale latency was 4.33% below merge parent07255cfeand 61.01% below upstream parent 0c64de93; incremental instructions were 3.09% and 54.07% lower. Focused ASan/UBSan scope and Python suites passed 33 and 125 tests. Signed-off-by: Andrew Hundt <ATHundt@gmail.com> (cherry picked from commit4d3a103047) Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
9d65f4cb62
commit
f339aa625f
4 files changed
+68
-70
No files matched your search
+22
-30
@@ -191,20 +191,15 @@ static void py_disable_callable_value_proof(PyLSPContext *ctx) {
|
||||
|
||||
static void py_scope_bind(PyLSPContext *ctx, const char *name, const CBMType *type) {
|
||||
ctx->type_cache_gen++;
|
||||
cbm_scope_bind(ctx->current_scope, name, type);
|
||||
if (name && !cbm_scope_contains(ctx->current_scope, name))
|
||||
if (!cbm_scope_bind_checked(ctx->current_scope, name, type))
|
||||
py_disable_callable_value_proof(ctx);
|
||||
}
|
||||
|
||||
static void py_scope_bind_callable(PyLSPContext *ctx, const char *name, const CBMType *type,
|
||||
const char *callable_qn) {
|
||||
ctx->type_cache_gen++;
|
||||
cbm_scope_bind_callable(ctx->current_scope, name, type, callable_qn);
|
||||
const char *bound = name ? cbm_scope_lookup_callable(ctx->current_scope, name) : NULL;
|
||||
if (name && (!cbm_scope_contains(ctx->current_scope, name) ||
|
||||
(callable_qn && (!bound || strcmp(bound, callable_qn) != 0)))) {
|
||||
if (!cbm_scope_bind_callable_checked(ctx->current_scope, name, type, callable_qn))
|
||||
py_disable_callable_value_proof(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static CBMScope *py_scope_push_checked(PyLSPContext *ctx) {
|
||||
@@ -938,8 +933,9 @@ static const char *py_exact_callable_target_ex(PyLSPContext *ctx, TSNode node,
|
||||
char *name = py_node_text(ctx, node);
|
||||
if (!name)
|
||||
return NULL;
|
||||
if (cbm_scope_contains(ctx->current_scope, name)) {
|
||||
const char *bound = cbm_scope_lookup_callable(ctx->current_scope, name);
|
||||
const CBMVarBinding *binding = cbm_scope_lookup_binding(ctx->current_scope, name);
|
||||
if (binding) {
|
||||
const char *bound = binding->callable_qn;
|
||||
if (bound && lexical_alias_out) {
|
||||
/* A lexical binding that does NOT simply name the module symbol
|
||||
* of the same spelling is an alias introduced in this body. Only
|
||||
@@ -1018,11 +1014,12 @@ static void py_resolve_value_references_at(PyLSPContext *ctx, TSNode call) {
|
||||
const char *candidate = strcmp(kind, "identifier") == 0
|
||||
? py_exact_imported_reference_candidate(ctx, source_name)
|
||||
: NULL;
|
||||
if (candidate && cbm_scope_contains(ctx->current_scope, source_name)) {
|
||||
const CBMType *binding =
|
||||
cbm_type_resolve_alias(cbm_scope_lookup(ctx->current_scope, source_name));
|
||||
if (binding && binding->kind == CBM_TYPE_NAMED && binding->data.named.qualified_name &&
|
||||
strcmp(binding->data.named.qualified_name, candidate) == 0) {
|
||||
const CBMVarBinding *scope_binding =
|
||||
candidate ? cbm_scope_lookup_binding(ctx->current_scope, source_name) : NULL;
|
||||
if (scope_binding) {
|
||||
const CBMType *type = cbm_type_resolve_alias(scope_binding->type);
|
||||
if (type && type->kind == CBM_TYPE_NAMED && type->data.named.qualified_name &&
|
||||
strcmp(type->data.named.qualified_name, candidate) == 0) {
|
||||
py_emit_unresolved_reference(ctx, candidate, arg);
|
||||
}
|
||||
}
|
||||
@@ -1498,9 +1495,9 @@ static const CBMType *py_eval_expr_type_uncached(PyLSPContext *ctx, TSNode node)
|
||||
char *name = py_node_text(ctx, node);
|
||||
if (!name)
|
||||
return cbm_type_unknown();
|
||||
const CBMType *t = cbm_scope_lookup(ctx->current_scope, name);
|
||||
if (cbm_scope_contains(ctx->current_scope, name))
|
||||
return t ? t : cbm_type_unknown();
|
||||
const CBMVarBinding *binding = cbm_scope_lookup_binding(ctx->current_scope, name);
|
||||
if (binding)
|
||||
return binding->type ? binding->type : cbm_type_unknown();
|
||||
// Builtin globals: True / False / None at top level.
|
||||
if (strcmp(name, "True") == 0 || strcmp(name, "False") == 0)
|
||||
return cbm_type_builtin(ctx->arena, "bool");
|
||||
@@ -1767,8 +1764,9 @@ static const CBMType *py_eval_expr_type_uncached(PyLSPContext *ctx, TSNode node)
|
||||
}
|
||||
}
|
||||
// Constructor call: ClassName() returns NAMED(ClassName).
|
||||
const CBMType *in_scope = cbm_scope_lookup(ctx->current_scope, fname);
|
||||
const char *callable_qn = cbm_scope_lookup_callable(ctx->current_scope, fname);
|
||||
const CBMVarBinding *binding = cbm_scope_lookup_binding(ctx->current_scope, fname);
|
||||
const CBMType *in_scope = binding && binding->type ? binding->type : cbm_type_unknown();
|
||||
const char *callable_qn = binding ? binding->callable_qn : NULL;
|
||||
if (callable_qn) {
|
||||
return py_func_return_type(ctx, callable_qn);
|
||||
}
|
||||
@@ -1796,7 +1794,7 @@ static const CBMType *py_eval_expr_type_uncached(PyLSPContext *ctx, TSNode node)
|
||||
}
|
||||
/* Even an UNKNOWN local/parameter is a real lexical shadow. Do
|
||||
* not borrow the return type of a same-named module function. */
|
||||
if (cbm_scope_contains(ctx->current_scope, fname))
|
||||
if (binding)
|
||||
return cbm_type_unknown();
|
||||
// Module-local function call.
|
||||
const CBMRegisteredFunc *f =
|
||||
@@ -2566,14 +2564,15 @@ static void py_emit_call_for(PyLSPContext *ctx, TSNode call_node) {
|
||||
* An ordinary local binding is also a hard shadow: if it is not a
|
||||
* proven callable alias, do not fall through and fabricate a direct
|
||||
* call to a module-level function with the same spelling. */
|
||||
if (cbm_scope_contains(ctx->current_scope, fname)) {
|
||||
const char *alias_target = cbm_scope_lookup_callable(ctx->current_scope, fname);
|
||||
const CBMVarBinding *binding = cbm_scope_lookup_binding(ctx->current_scope, fname);
|
||||
if (binding) {
|
||||
const char *alias_target = binding->callable_qn;
|
||||
if (alias_target) {
|
||||
py_emit_resolved_call_reason(ctx, alias_target, "lsp_callable_alias", 0.97f, fname,
|
||||
call_node);
|
||||
return;
|
||||
}
|
||||
const CBMType *in_scope = cbm_scope_lookup(ctx->current_scope, fname);
|
||||
const CBMType *in_scope = binding->type ? binding->type : cbm_type_unknown();
|
||||
if (!cbm_type_is_unknown(in_scope) && in_scope->kind == CBM_TYPE_NAMED) {
|
||||
const char *qn = in_scope->data.named.qualified_name;
|
||||
const char *tail = qn ? strrchr(qn, '.') : NULL;
|
||||
@@ -2604,13 +2603,6 @@ static void py_emit_call_for(PyLSPContext *ctx, TSNode call_node) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Constructor call (ClassName())
|
||||
const CBMType *in_scope = cbm_scope_lookup(ctx->current_scope, fname);
|
||||
if (!cbm_type_is_unknown(in_scope) && in_scope->kind == CBM_TYPE_NAMED) {
|
||||
const char *qn = in_scope->data.named.qualified_name;
|
||||
py_emit_resolved_call(ctx, qn, "lsp_constructor", 0.85f, call_node);
|
||||
return;
|
||||
}
|
||||
// Module-local function
|
||||
const CBMRegisteredFunc *f =
|
||||
cbm_registry_lookup_symbol(ctx->registry, ctx->module_qn, fname);
|
||||
|
||||
+22
-40
@@ -88,54 +88,36 @@ bool cbm_scope_bind_callable_checked(CBMScope *scope, const char *name, const CB
|
||||
return cbm_scope_bind_value(scope, name, type, callable_qn);
|
||||
}
|
||||
|
||||
const CBMType* cbm_scope_lookup(const CBMScope* scope, const char* name) {
|
||||
if (!name) {
|
||||
return cbm_type_unknown();
|
||||
}
|
||||
for (const CBMScope* s = scope; s != NULL; s = s->parent) {
|
||||
for (CBMScopeChunk* c = s->chunks; c != NULL; c = c->next) {
|
||||
for (int i = 0; i < c->used; i++) {
|
||||
if (c->bindings[i].name && strcmp(c->bindings[i].name, name) == 0) {
|
||||
return c->bindings[i].type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cbm_type_unknown();
|
||||
}
|
||||
|
||||
bool cbm_scope_contains(const CBMScope *scope, const char *name) {
|
||||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
for (const CBMScope *s = scope; s != NULL; s = s->parent) {
|
||||
for (const CBMScopeChunk *c = s->chunks; c != NULL; c = c->next) {
|
||||
for (int i = 0; i < c->used; i++) {
|
||||
if (c->bindings[i].name && strcmp(c->bindings[i].name, name) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *cbm_scope_lookup_callable(const CBMScope *scope, const char *name) {
|
||||
if (!name) {
|
||||
const CBMVarBinding *cbm_scope_lookup_binding(const CBMScope *scope, const char *name) {
|
||||
if (!name)
|
||||
return NULL;
|
||||
}
|
||||
for (const CBMScope *s = scope; s != NULL; s = s->parent) {
|
||||
for (const CBMScopeChunk *c = s->chunks; c != NULL; c = c->next) {
|
||||
for (const CBMScope* s = scope; s != NULL; s = s->parent) {
|
||||
for (const CBMScopeChunk* c = s->chunks; c != NULL; c = c->next) {
|
||||
for (int i = 0; i < c->used; i++) {
|
||||
if (c->bindings[i].name && strcmp(c->bindings[i].name, name) == 0) {
|
||||
return c->bindings[i].callable_qn;
|
||||
}
|
||||
if (c->bindings[i].name && strcmp(c->bindings[i].name, name) == 0)
|
||||
return &c->bindings[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const CBMType* cbm_scope_lookup(const CBMScope* scope, const char* name) {
|
||||
const CBMVarBinding *binding = cbm_scope_lookup_binding(scope, name);
|
||||
if (binding)
|
||||
return binding->type;
|
||||
return cbm_type_unknown();
|
||||
}
|
||||
|
||||
bool cbm_scope_contains(const CBMScope *scope, const char *name) {
|
||||
return cbm_scope_lookup_binding(scope, name) != NULL;
|
||||
}
|
||||
|
||||
const char *cbm_scope_lookup_callable(const CBMScope *scope, const char *name) {
|
||||
const CBMVarBinding *binding = cbm_scope_lookup_binding(scope, name);
|
||||
return binding ? binding->callable_qn : NULL;
|
||||
}
|
||||
|
||||
bool cbm_scope_update_callable(CBMScope *scope, const char *name, const char *callable_qn) {
|
||||
if (!name) {
|
||||
return false;
|
||||
|
||||
@@ -82,6 +82,10 @@ bool cbm_scope_bind_callable_checked(CBMScope *scope, const char *name, const CB
|
||||
void cbm_scope_bind_callable(CBMScope *scope, const char *name, const CBMType *type,
|
||||
const char *callable_qn);
|
||||
const CBMType* cbm_scope_lookup(const CBMScope* scope, const char* name);
|
||||
/* Return the complete nearest binding in one scope-chain walk, or NULL when
|
||||
* unbound. Callers that need presence, type, and callable identity together
|
||||
* should use this instead of repeating three linear scans. */
|
||||
const CBMVarBinding *cbm_scope_lookup_binding(const CBMScope *scope, const char *name);
|
||||
/* True when any lexical frame contains name, even when its type is UNKNOWN. */
|
||||
bool cbm_scope_contains(const CBMScope *scope, const char *name);
|
||||
/* Return the exact callable QN from the nearest binding. A nearer ordinary
|
||||
|
||||
@@ -125,6 +125,25 @@ TEST(scope_callable_identity_follows_nearest_binding) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(scope_lookup_binding_returns_nearest_complete_record) {
|
||||
CBMArena a;
|
||||
cbm_arena_init(&a);
|
||||
CBMScope *root = cbm_scope_push(&a, NULL);
|
||||
CBMScope *child = cbm_scope_push(&a, root);
|
||||
cbm_scope_bind_callable(root, "callback", named_t(&a, "Callback"), "pkg.actual");
|
||||
cbm_scope_bind(child, "callback", named_t(&a, "Shadow"));
|
||||
|
||||
const CBMVarBinding *binding = cbm_scope_lookup_binding(child, "callback");
|
||||
ASSERT_NOT_NULL(binding);
|
||||
ASSERT_STR_EQ(binding->name, "callback");
|
||||
ASSERT_STR_EQ(binding->type->data.named.qualified_name, "Shadow");
|
||||
ASSERT_NULL(binding->callable_qn);
|
||||
ASSERT_NULL(cbm_scope_lookup_binding(child, "missing"));
|
||||
|
||||
cbm_arena_destroy(&a);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(scope_assignment_updates_or_clears_nearest_callable_only) {
|
||||
CBMArena a;
|
||||
cbm_arena_init(&a);
|
||||
@@ -260,6 +279,7 @@ SUITE(scope) {
|
||||
RUN_TEST(scope_lookup_walks_parent_chain);
|
||||
RUN_TEST(scope_child_shadows_parent);
|
||||
RUN_TEST(scope_callable_identity_follows_nearest_binding);
|
||||
RUN_TEST(scope_lookup_binding_returns_nearest_complete_record);
|
||||
RUN_TEST(scope_assignment_updates_or_clears_nearest_callable_only);
|
||||
RUN_TEST(scope_checked_bind_reports_child_oom_despite_parent_name);
|
||||
RUN_TEST(scope_dynamic_growth_300_bindings);
|
||||
|
||||
Reference in new issue
Block a user