runmat_runtime/call/
closures.rs1use crate::call::identity::external_qualified_display_name;
2use crate::runtime_error::semantic_error;
3use crate::RuntimeError;
4use runmat_types::MemberAccess;
5use runmat_value::{Closure, Value};
6
7pub fn closure_value(function_name: String, captures: Vec<Value>) -> Value {
8 Value::Closure(Closure {
9 function_name,
10 bound_function: None,
11 captures,
12 })
13}
14
15pub fn semantic_closure_value(
16 function: runmat_types::FunctionId,
17 display_name: String,
18 captures: Vec<Value>,
19) -> Value {
20 Value::Closure(Closure {
21 function_name: display_name,
22 bound_function: Some(function.0),
23 captures,
24 })
25}
26
27pub fn caller_class_for_function(caller_function_name: Option<&str>) -> Option<String> {
28 let caller_function_name = caller_function_name?;
29 if let Some((class_name, method_name)) = caller_function_name.rsplit_once('.') {
30 if !class_name.is_empty() && !method_name.is_empty() {
31 return Some(class_name.to_string());
32 }
33 }
34 crate::class_registry::class_names()
35 .into_iter()
36 .find(|class_name| {
37 crate::class_registry::get_class(class_name).is_some_and(|class_def| {
38 class_def
39 .methods
40 .values()
41 .any(|method| method.function_name == caller_function_name)
42 })
43 })
44}
45
46pub fn method_access_permitted(
47 owner: &str,
48 access: &MemberAccess,
49 caller_function_name: Option<&str>,
50) -> bool {
51 match access {
52 MemberAccess::Public => true,
53 MemberAccess::Private => {
54 caller_class_for_function(caller_function_name).as_deref() == Some(owner)
55 }
56 MemberAccess::Protected => {
57 caller_class_for_function(caller_function_name).is_some_and(|caller_class| {
58 crate::class_registry::is_class_or_subclass(&caller_class, owner)
59 })
60 }
61 }
62}
63
64pub fn resolve_method_semantic_function_id(
65 owner: &str,
66 method_name: &str,
67 function_name: &str,
68) -> Option<usize> {
69 let trimmed = function_name.trim();
70 if !trimmed.is_empty() {
71 if let Some(function) = crate::user_functions::resolve_semantic_function_by_name(trimmed) {
72 return Some(function);
73 }
74 if !trimmed.contains('.') {
75 let owner_qualified = format!("{owner}.{trimmed}");
76 if let Some(function) =
77 crate::user_functions::resolve_semantic_function_by_name(&owner_qualified)
78 {
79 return Some(function);
80 }
81 }
82 }
83 crate::user_functions::resolve_semantic_function_by_name(&format!("{owner}.{method_name}"))
84}
85
86pub fn load_method_closure(
87 base: Value,
88 name: String,
89 caller_function_name: Option<&str>,
90) -> Result<Value, RuntimeError> {
91 match base {
92 Value::Object(object) => {
93 let function_name = external_qualified_display_name(&object.class_name, &name);
94 Ok(Value::Closure(Closure {
95 bound_function: crate::user_functions::resolve_semantic_function_by_name(
96 &function_name,
97 ),
98 function_name,
99 captures: vec![Value::Object(object)],
100 }))
101 }
102 Value::ClassRef(class_name) => {
103 if let Some((method, owner)) = crate::class_registry::lookup_method(&class_name, &name)
104 {
105 if !method.is_static {
106 return Err(semantic_error(
107 "MethodNotStatic",
108 format!("Method '{name}' is not static"),
109 ));
110 }
111 if !method_access_permitted(&owner, &method.access, caller_function_name) {
112 return Err(semantic_error(
113 "MethodPrivate",
114 format!("Method '{name}' is private"),
115 ));
116 }
117 return Ok(Value::Closure(Closure {
118 bound_function: resolve_method_semantic_function_id(
119 &owner,
120 &name,
121 &method.function_name,
122 ),
123 function_name: method.function_name,
124 captures: vec![],
125 }));
126 }
127 let qualified_name = external_qualified_display_name(&class_name, &name);
128 if runmat_builtins::builtin_name_is_known(&qualified_name) {
129 Ok(Value::Closure(Closure {
130 bound_function: crate::user_functions::resolve_semantic_function_by_name(
131 &qualified_name,
132 ),
133 function_name: qualified_name,
134 captures: vec![],
135 }))
136 } else {
137 Err(semantic_error(
138 "UnknownStaticMethod",
139 format!("Unknown static method '{name}' on class {class_name}"),
140 ))
141 }
142 }
143 _ => Err(semantic_error(
144 "LoadMethod",
145 "LoadMethod requires object or classref",
146 )),
147 }
148}