90 lines
2.8 KiB
Rust
90 lines
2.8 KiB
Rust
|
|
// Unique-tail function name resolution
|
||
|
|
// Extracted from execute_legacy_call to keep handlers lean
|
||
|
|
|
||
|
|
use std::collections::HashMap;
|
||
|
|
|
||
|
|
use super::MirFunction;
|
||
|
|
|
||
|
|
/// Resolve function name using unique-tail matching algorithm.
|
||
|
|
///
|
||
|
|
/// Resolution strategy:
|
||
|
|
/// 1. Fast path: exact match on raw name
|
||
|
|
/// 2. Normalize with arity: "base/N"
|
||
|
|
/// 3. Unique-tail matching: find candidates ending with ".method/N"
|
||
|
|
/// 4. Same-box preference: prioritize functions in the same box as current_function
|
||
|
|
///
|
||
|
|
/// Returns resolved function name, or None if resolution fails.
|
||
|
|
pub(super) fn resolve_function_name(
|
||
|
|
raw: &str,
|
||
|
|
arity: usize,
|
||
|
|
functions: &HashMap<String, MirFunction>,
|
||
|
|
current_function: Option<&str>,
|
||
|
|
) -> Option<String> {
|
||
|
|
// Fast path: exact match
|
||
|
|
if functions.contains_key(raw) {
|
||
|
|
return Some(raw.to_string());
|
||
|
|
}
|
||
|
|
|
||
|
|
// Robust normalization for names like "Box.method/Arity" or just "method"
|
||
|
|
let (base, ar_from_raw) = if let Some((b, a)) = raw.rsplit_once('/') {
|
||
|
|
(b.to_string(), a.parse::<usize>().ok())
|
||
|
|
} else {
|
||
|
|
(raw.to_string(), None)
|
||
|
|
};
|
||
|
|
let want_arity = ar_from_raw.unwrap_or(arity);
|
||
|
|
|
||
|
|
// Try exact canonical form: "base/arity"
|
||
|
|
let exact = format!("{}/{}", base, want_arity);
|
||
|
|
if functions.contains_key(&exact) {
|
||
|
|
return Some(exact);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Split base into optional box and method name
|
||
|
|
let (maybe_box, method) = if let Some((bx, m)) = base.split_once('.') {
|
||
|
|
(Some(bx.to_string()), m.to_string())
|
||
|
|
} else {
|
||
|
|
(None, base.clone())
|
||
|
|
};
|
||
|
|
|
||
|
|
// Collect candidates that end with ".method/arity"
|
||
|
|
let mut cands: Vec<String> = Vec::new();
|
||
|
|
let tail = format!(".{}{}", method, format!("/{}", want_arity));
|
||
|
|
for k in functions.keys() {
|
||
|
|
if k.ends_with(&tail) {
|
||
|
|
if let Some(ref bx) = maybe_box {
|
||
|
|
if k.starts_with(&format!("{}.", bx)) {
|
||
|
|
cands.push(k.clone());
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
cands.push(k.clone());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if cands.len() > 1 {
|
||
|
|
// Prefer same-box candidate based on current function's box
|
||
|
|
if let Some(cur) = current_function {
|
||
|
|
let cur_box = cur.split('.').next().unwrap_or("");
|
||
|
|
let scoped: Vec<String> = cands
|
||
|
|
.iter()
|
||
|
|
.filter(|k| k.starts_with(&format!("{}.", cur_box)))
|
||
|
|
.cloned()
|
||
|
|
.collect();
|
||
|
|
if scoped.len() == 1 {
|
||
|
|
cands = scoped;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
match cands.len() {
|
||
|
|
0 => None,
|
||
|
|
1 => Some(cands.into_iter().next().unwrap()),
|
||
|
|
_ => {
|
||
|
|
// Multiple candidates: sort and pick first (deterministic fallback)
|
||
|
|
let mut c = cands;
|
||
|
|
c.sort();
|
||
|
|
Some(c.into_iter().next().unwrap())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|