1use std::{collections::BTreeMap, sync::Arc};
4
5use sim_kernel::{
6 Args, CORE_FUNCTION_CLASS_ID, Callable, ClassRef, Cx, Error, Object, ObjectCompat, Result,
7 Symbol, Value,
8};
9
10use crate::{GuestRuntimeKit, LanguageProfile, ProfileRegistry};
11
12pub type ProfileFunctionBody = Arc<dyn Fn(&mut Cx, Args) -> Result<Value> + Send + Sync>;
14
15#[derive(Clone)]
17pub struct ProfileFunction {
18 defining_profile: Symbol,
19 organ: Symbol,
20 function: Symbol,
21 body: ProfileFunctionBody,
22}
23
24impl ProfileFunction {
25 pub fn new<F>(defining_profile: Symbol, organ: Symbol, function: Symbol, body: F) -> Self
28 where
29 F: Fn(&mut Cx, Args) -> Result<Value> + Send + Sync + 'static,
30 {
31 Self {
32 defining_profile,
33 organ,
34 function,
35 body: Arc::new(body),
36 }
37 }
38
39 pub fn defining_profile(&self) -> &Symbol {
41 &self.defining_profile
42 }
43
44 pub fn organ(&self) -> &Symbol {
46 &self.organ
47 }
48
49 pub fn function(&self) -> &Symbol {
51 &self.function
52 }
53}
54
55impl Callable for ProfileFunction {
56 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
57 (self.body)(cx, args)
58 }
59}
60
61impl Object for ProfileFunction {
62 fn display(&self, _cx: &mut Cx) -> Result<String> {
63 Ok(format!(
64 "#<profile-function {} defined-by {}>",
65 self.function, self.defining_profile
66 ))
67 }
68
69 fn as_any(&self) -> &dyn std::any::Any {
70 self
71 }
72}
73
74impl ObjectCompat for ProfileFunction {
75 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
76 cx.factory().class_stub(
77 CORE_FUNCTION_CLASS_ID,
78 Symbol::qualified("core", "Function"),
79 )
80 }
81
82 fn as_callable(&self) -> Option<&dyn Callable> {
83 Some(self)
84 }
85}
86
87#[derive(Clone, Debug)]
90pub struct ProfileFunctionBinding {
91 pub defining_profile: Symbol,
93 pub organ: Symbol,
95 pub function: Symbol,
97 pub value: Value,
99}
100
101#[derive(Clone, Debug, Default)]
104pub struct SharedOrganRuntime {
105 registry: ProfileRegistry,
106 functions: BTreeMap<Symbol, ProfileFunctionBinding>,
107 kits: BTreeMap<Symbol, GuestRuntimeKit>,
108}
109
110impl SharedOrganRuntime {
111 pub fn new() -> Self {
113 Self::default()
114 }
115
116 pub fn register_profile(&mut self, profile: LanguageProfile) -> Result<()> {
118 self.registry.register_profile(profile)
119 }
120
121 pub fn profile(&self, symbol: &Symbol) -> Option<&LanguageProfile> {
123 self.registry.profile(symbol)
124 }
125
126 pub fn profiles(&self) -> impl Iterator<Item = &LanguageProfile> {
128 self.registry.profiles()
129 }
130
131 pub fn register_kit(&mut self, profile: &Symbol, kit: GuestRuntimeKit) -> Result<()> {
135 if self.registry.profile(profile).is_none() {
136 return Err(Error::UnknownSymbol {
137 symbol: profile.clone(),
138 });
139 }
140 if self.kits.contains_key(profile) {
141 return Err(Error::DuplicateExport {
142 kind: "standard-guest-runtime-kit",
143 symbol: profile.clone(),
144 });
145 }
146 self.kits.insert(profile.clone(), kit);
147 Ok(())
148 }
149
150 pub fn kit(&self, profile: &Symbol) -> Option<&GuestRuntimeKit> {
152 self.kits.get(profile)
153 }
154
155 pub fn define_function(
160 &mut self,
161 defining_profile: &Symbol,
162 organ: Symbol,
163 function: Symbol,
164 value: Value,
165 ) -> Result<()> {
166 self.require_profile_uses_organ(defining_profile, &organ)?;
167 if value.object().as_callable().is_none() {
168 return Err(Error::TypeMismatch {
169 expected: "callable",
170 found: "non-callable",
171 });
172 }
173 if self.functions.contains_key(&function) {
174 return Err(Error::DuplicateExport {
175 kind: "standard-profile-function",
176 symbol: function,
177 });
178 }
179 self.functions.insert(
180 function.clone(),
181 ProfileFunctionBinding {
182 defining_profile: defining_profile.clone(),
183 organ,
184 function,
185 value,
186 },
187 );
188 Ok(())
189 }
190
191 pub fn function(&self, function: &Symbol) -> Option<&ProfileFunctionBinding> {
193 self.functions.get(function)
194 }
195
196 pub fn call_function(
199 &self,
200 cx: &mut Cx,
201 calling_profile: &Symbol,
202 function: &Symbol,
203 args: Vec<Value>,
204 ) -> Result<Value> {
205 let binding = self
206 .functions
207 .get(function)
208 .ok_or_else(|| Error::UnknownFunction {
209 function: function.clone(),
210 })?;
211 self.require_profile_uses_organ(calling_profile, &binding.organ)?;
212 let callable = binding
213 .value
214 .object()
215 .as_callable()
216 .ok_or(Error::TypeMismatch {
217 expected: "callable",
218 found: "non-callable",
219 })?;
220 callable.call(cx, Args::new(args))
221 }
222
223 fn require_profile_uses_organ(&self, profile: &Symbol, organ: &Symbol) -> Result<()> {
224 let profile_record =
225 self.registry
226 .profile(profile)
227 .ok_or_else(|| Error::UnknownSymbol {
228 symbol: profile.clone(),
229 })?;
230 if profile_record
231 .organs
232 .iter()
233 .any(|used| &used.organ == organ)
234 {
235 Ok(())
236 } else {
237 Err(Error::Eval(format!(
238 "profile {profile} does not use organ {organ}"
239 )))
240 }
241 }
242}
243
244pub fn profile_function_value<F>(
246 cx: &mut Cx,
247 defining_profile: Symbol,
248 organ: Symbol,
249 function: Symbol,
250 body: F,
251) -> Result<Value>
252where
253 F: Fn(&mut Cx, Args) -> Result<Value> + Send + Sync + 'static,
254{
255 cx.factory().opaque(Arc::new(ProfileFunction::new(
256 defining_profile,
257 organ,
258 function,
259 body,
260 )))
261}