Skip to main content

lust/vm/
api.rs

1use super::*;
2use crate::ast::Type;
3use crate::bytecode::{LustMap, ValueKey};
4use crate::config::LustConfig;
5use alloc::rc::Rc;
6use alloc::string::String;
7use alloc::vec::Vec;
8use core::result::Result as CoreResult;
9use core::mem;
10use hashbrown::HashMap;
11
12#[derive(Debug, Clone)]
13pub struct NativeExportParam {
14    name: String,
15    ty: String,
16}
17
18impl NativeExportParam {
19    pub fn new(name: impl Into<String>, ty: impl Into<String>) -> Self {
20        Self {
21            name: name.into(),
22            ty: ty.into(),
23        }
24    }
25
26    pub fn name(&self) -> &str {
27        &self.name
28    }
29
30    pub fn ty(&self) -> &str {
31        &self.ty
32    }
33}
34
35#[derive(Debug, Clone)]
36pub struct NativeExport {
37    name: String,
38    params: Vec<NativeExportParam>,
39    return_type: String,
40    doc: Option<String>,
41}
42
43impl NativeExport {
44    pub fn new(
45        name: impl Into<String>,
46        params: Vec<NativeExportParam>,
47        return_type: impl Into<String>,
48    ) -> Self {
49        Self {
50            name: name.into(),
51            params,
52            return_type: return_type.into(),
53            doc: None,
54        }
55    }
56
57    pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
58        self.doc = Some(doc.into());
59        self
60    }
61
62    pub fn name(&self) -> &str {
63        &self.name
64    }
65
66    pub fn params(&self) -> &[NativeExportParam] {
67        &self.params
68    }
69
70    pub fn return_type(&self) -> &str {
71        &self.return_type
72    }
73
74    pub fn doc(&self) -> Option<&str> {
75        self.doc.as_deref()
76    }
77}
78impl VM {
79    pub fn new() -> Self {
80        Self::with_config(&LustConfig::default())
81    }
82
83    pub fn with_config(config: &LustConfig) -> Self {
84        let mut vm = Self {
85            jit: JitState::new(),
86            budgets: BudgetState::default(),
87            functions: Vec::new(),
88            natives: HashMap::new(),
89            globals: HashMap::new(),
90            map_hasher: DefaultHashBuilder::default(),
91            call_stack: Vec::new(),
92            max_stack_depth: 1000,
93            pending_return_value: None,
94            pending_return_dest: None,
95            trace_recorder: None,
96            side_trace_context: None,
97            skip_next_trace_record: false,
98            trait_impls: HashMap::new(),
99            struct_tostring_cache: HashMap::new(),
100            struct_metadata: HashMap::new(),
101            call_until_depth: None,
102            task_manager: TaskManager::new(),
103            current_task: None,
104            pending_task_signal: None,
105            last_task_signal: None,
106            cycle_collector: cycle::CycleCollector::new(),
107            exported_natives: Vec::new(),
108            export_prefix_stack: Vec::new(),
109            #[cfg(feature = "std")]
110            exported_type_stubs: Vec::new(),
111        };
112        vm.jit.enabled = vm.jit.enabled && config.jit_enabled();
113        vm.trait_impls
114            .insert(("int".to_string(), "ToString".to_string()), true);
115        vm.trait_impls
116            .insert(("float".to_string(), "ToString".to_string()), true);
117        vm.trait_impls
118            .insert(("string".to_string(), "ToString".to_string()), true);
119        vm.trait_impls
120            .insert(("bool".to_string(), "ToString".to_string()), true);
121        super::corelib::install_core_builtins(&mut vm);
122        #[cfg(feature = "std")]
123        for (name, func) in super::stdlib::create_stdlib(config, &vm) {
124            vm.register_native(name, func);
125        }
126
127        vm
128    }
129
130    pub(crate) fn new_map(&self) -> LustMap {
131        HashMap::with_hasher(self.map_hasher.clone())
132    }
133
134    pub(crate) fn map_with_entries(
135        &self,
136        entries: impl IntoIterator<Item = (ValueKey, Value)>,
137    ) -> Value {
138        let mut map = self.new_map();
139        map.extend(entries);
140        Value::map(map)
141    }
142
143    pub(crate) fn new_map_value(&self) -> Value {
144        Value::map(self.new_map())
145    }
146
147    pub(super) fn observe_value(&mut self, value: &Value) {
148        self.cycle_collector.register_value(value);
149    }
150
151    pub(super) fn maybe_collect_cycles(&mut self) {
152        let mut collector = mem::take(&mut self.cycle_collector);
153        collector.maybe_collect(self);
154        self.cycle_collector = collector;
155    }
156
157    pub fn with_current<F, R>(f: F) -> CoreResult<R, String>
158    where
159        F: FnOnce(&mut VM) -> CoreResult<R, String>,
160    {
161        let ptr_opt = super::with_vm_stack(|stack| stack.last().copied());
162        if let Some(ptr) = ptr_opt {
163            let vm = unsafe { &mut *ptr };
164            f(vm)
165        } else {
166            Err("task API requires a running VM".to_string())
167        }
168    }
169
170    pub fn load_functions(&mut self, functions: Vec<Function>) {
171        self.functions = functions;
172    }
173
174    pub fn register_structs(&mut self, defs: &HashMap<String, StructDef>) {
175        for (name, def) in defs {
176            let field_names: Vec<Rc<String>> = def
177                .fields
178                .iter()
179                .map(|field| Rc::new(field.name.clone()))
180                .collect();
181            let field_storage: Vec<FieldStorage> = def
182                .fields
183                .iter()
184                .map(|field| match field.ownership {
185                    FieldOwnership::Weak => FieldStorage::Weak,
186                    FieldOwnership::Strong => FieldStorage::Strong,
187                })
188                .collect();
189            let field_types: Vec<Type> = def.fields.iter().map(|field| field.ty.clone()).collect();
190            let weak_targets: Vec<Option<Type>> = def
191                .fields
192                .iter()
193                .map(|field| field.weak_target.clone())
194                .collect();
195            let layout = Rc::new(StructLayout::new(
196                def.name.clone(),
197                field_names,
198                field_storage,
199                field_types,
200                weak_targets,
201            ));
202            self.struct_metadata.insert(
203                name.clone(),
204                RuntimeStructInfo {
205                    layout: layout.clone(),
206                },
207            );
208            if let Some(simple) = name.rsplit('.').next() {
209                self.struct_metadata.insert(
210                    simple.to_string(),
211                    RuntimeStructInfo {
212                        layout: layout.clone(),
213                    },
214                );
215            }
216        }
217    }
218
219    pub fn instantiate_struct(
220        &self,
221        struct_name: &str,
222        fields: Vec<(Rc<String>, Value)>,
223    ) -> Result<Value> {
224        let info =
225            self.struct_metadata
226                .get(struct_name)
227                .ok_or_else(|| LustError::RuntimeError {
228                    message: format!("Unknown struct '{}'", struct_name),
229                })?;
230        Self::build_struct_value(struct_name, info, fields)
231    }
232
233    fn build_struct_value(
234        struct_name: &str,
235        info: &RuntimeStructInfo,
236        mut fields: Vec<(Rc<String>, Value)>,
237    ) -> Result<Value> {
238        let layout = info.layout.clone();
239        let field_count = layout.field_names().len();
240        let mut ordered = vec![Value::Nil; field_count];
241        let mut filled = vec![false; field_count];
242        for (field_name, field_value) in fields.drain(..) {
243            let index_opt = layout
244                .index_of_rc(&field_name)
245                .or_else(|| layout.index_of_str(field_name.as_str()));
246            let index = match index_opt {
247                Some(i) => i,
248                None => {
249                    return Err(LustError::RuntimeError {
250                        message: format!("Struct '{}' has no field '{}'", struct_name, field_name),
251                    })
252                }
253            };
254            let canonical = layout
255                .canonicalize_field_value(index, field_value)
256                .map_err(|msg| LustError::RuntimeError { message: msg })?;
257            ordered[index] = canonical;
258            filled[index] = true;
259        }
260
261        if filled.iter().any(|slot| !*slot) {
262            let missing: Vec<String> = layout
263                .field_names()
264                .iter()
265                .enumerate()
266                .filter_map(|(idx, name)| (!filled[idx]).then(|| (**name).clone()))
267                .collect();
268            return Err(LustError::RuntimeError {
269                message: format!(
270                    "Struct '{}' is missing required field(s): {}",
271                    struct_name,
272                    missing.join(", ")
273                ),
274            });
275        }
276
277        Ok(Value::Struct {
278            name: struct_name.to_string(),
279            layout,
280            fields: Rc::new(RefCell::new(ordered)),
281        })
282    }
283
284    pub fn register_trait_impl(&mut self, type_name: String, trait_name: String) {
285        self.trait_impls.insert((type_name, trait_name), true);
286    }
287
288    pub fn register_native(&mut self, name: impl Into<String>, value: Value) {
289        let name = name.into();
290        match value {
291            Value::NativeFunction(_) => {
292                let cloned = value.clone();
293                self.natives.insert(name.clone(), value);
294                self.globals.insert(name, cloned);
295            }
296
297            other => {
298                self.globals.insert(name, other);
299            }
300        }
301    }
302
303    #[allow(dead_code)]
304    pub(crate) fn push_export_prefix(&mut self, crate_name: &str) {
305        let sanitized = crate_name.replace('-', "_");
306        self.export_prefix_stack.push(sanitized);
307    }
308
309    #[allow(dead_code)]
310    pub(crate) fn pop_export_prefix(&mut self) {
311        self.export_prefix_stack.pop();
312    }
313
314    fn current_export_prefix(&self) -> Option<&str> {
315        self.export_prefix_stack.last().map(|s| s.as_str())
316    }
317
318    pub fn export_prefix(&self) -> Option<String> {
319        self.current_export_prefix().map(|s| s.to_string())
320    }
321
322    fn canonicalize_export_name(&self, export: &mut NativeExport) {
323        if let Some(prefix) = self.current_export_prefix() {
324            let needs_prefix = match export.name.strip_prefix(prefix) {
325                Some(rest) => {
326                    if rest.is_empty() {
327                        false
328                    } else {
329                        !matches!(rest.chars().next(), Some('.') | Some(':'))
330                    }
331                }
332                None => true,
333            };
334            if needs_prefix {
335                export.name = if export.name.is_empty() {
336                    prefix.to_string()
337                } else {
338                    format!("{prefix}.{}", export.name)
339                };
340            }
341        }
342    }
343
344    fn push_export_metadata(&mut self, export: NativeExport) {
345        if self.exported_natives.iter().any(|existing| existing.name == export.name) {
346            return;
347        }
348        self.exported_natives.push(export);
349    }
350
351    pub fn record_exported_native(&mut self, mut export: NativeExport) {
352        self.canonicalize_export_name(&mut export);
353        self.push_export_metadata(export);
354    }
355
356    pub fn register_exported_native<F>(&mut self, export: NativeExport, func: F)
357    where
358        F: Fn(&[Value]) -> CoreResult<NativeCallResult, String> + 'static,
359    {
360        let mut export = export;
361        self.canonicalize_export_name(&mut export);
362        let name = export.name.clone();
363        self.push_export_metadata(export);
364        let native = Value::NativeFunction(Rc::new(func));
365        self.register_native(name, native);
366    }
367
368    #[cfg(feature = "std")]
369    pub fn register_type_stubs(&mut self, stubs: Vec<ModuleStub>) {
370        if stubs.is_empty() {
371            return;
372        }
373        self.exported_type_stubs.extend(stubs);
374    }
375
376    #[cfg(feature = "std")]
377    pub fn exported_type_stubs(&self) -> &[ModuleStub] {
378        &self.exported_type_stubs
379    }
380
381    #[cfg(feature = "std")]
382    pub fn take_type_stubs(&mut self) -> Vec<ModuleStub> {
383        mem::take(&mut self.exported_type_stubs)
384    }
385
386    pub fn exported_natives(&self) -> &[NativeExport] {
387        &self.exported_natives
388    }
389
390    pub fn take_exported_natives(&mut self) -> Vec<NativeExport> {
391        mem::take(&mut self.exported_natives)
392    }
393
394    pub fn clear_native_functions(&mut self) {
395        self.natives.clear();
396        #[cfg(feature = "std")]
397        self.exported_type_stubs.clear();
398    }
399
400    #[cfg(feature = "std")]
401    pub fn dump_externs_to_dir(
402        &self,
403        output_root: impl AsRef<std::path::Path>,
404    ) -> std::io::Result<Vec<std::path::PathBuf>> {
405        self.dump_externs_to_dir_with_options(
406            output_root,
407            &crate::externs::DumpExternsOptions::default(),
408        )
409    }
410
411    #[cfg(feature = "std")]
412    pub fn dump_externs_to_dir_with_options(
413        &self,
414        output_root: impl AsRef<std::path::Path>,
415        options: &crate::externs::DumpExternsOptions,
416    ) -> std::io::Result<Vec<std::path::PathBuf>> {
417        let files = crate::externs::extern_files_from_vm(self, options);
418        crate::externs::write_extern_files(output_root, &files)
419    }
420
421    pub fn get_global(&self, name: &str) -> Option<Value> {
422        if let Some(value) = self.globals.get(name) {
423            Some(value.clone())
424        } else {
425            self.natives.get(name).cloned()
426        }
427    }
428
429    pub fn global_names(&self) -> Vec<String> {
430        self.globals.keys().cloned().collect()
431    }
432
433    pub fn globals_snapshot(&self) -> Vec<(String, Value)> {
434        self.globals
435            .iter()
436            .map(|(name, value)| (name.clone(), value.clone()))
437            .collect()
438    }
439
440    pub fn set_global(&mut self, name: impl Into<String>, value: Value) {
441        let name = name.into();
442        self.observe_value(&value);
443        self.globals.insert(name.clone(), value);
444        self.natives.remove(&name);
445        self.maybe_collect_cycles();
446    }
447
448    pub fn call(&mut self, function_name: &str, args: Vec<Value>) -> Result<Value> {
449        let func_idx = self
450            .functions
451            .iter()
452            .position(|f| f.name == function_name)
453            .ok_or_else(|| LustError::RuntimeError {
454                message: format!("Function not found: {}", function_name),
455            })?;
456        let func = &self.functions[func_idx];
457        if args.len() != func.param_count as usize {
458            return Err(LustError::RuntimeError {
459                message: format!(
460                    "Function {} expects {} arguments, got {}",
461                    function_name,
462                    func.param_count,
463                    args.len()
464                ),
465            });
466        }
467        let register_count = func.register_count;
468
469        let mut frame = CallFrame::new(func_idx, None, register_count);
470        for (i, arg) in args.into_iter().enumerate() {
471            frame.registers[i] = arg;
472        }
473
474        self.call_stack.push(frame);
475        match self.run() {
476            Ok(v) => Ok(v),
477            Err(e) => Err(self.annotate_runtime_error(e)),
478        }
479    }
480
481    pub fn function_value(&self, function_name: &str) -> Option<Value> {
482        let canonical = if function_name.contains("::") {
483            function_name.replace("::", ".")
484        } else {
485            function_name.to_string()
486        };
487        self.functions
488            .iter()
489            .position(|f| f.name == canonical)
490            .map(Value::Function)
491    }
492
493    pub fn function_name(&self, index: usize) -> Option<&str> {
494        self.functions.get(index).map(|f| f.name.as_str())
495    }
496
497    pub fn fail_task_handle(&mut self, handle: TaskHandle, error: LustError) -> Result<()> {
498        let task_id = self.task_id_from_handle(handle)?;
499        let mut task =
500            self.task_manager
501                .detach(task_id)
502                .ok_or_else(|| LustError::RuntimeError {
503                    message: format!("Invalid task handle {}", handle.id()),
504                })?;
505        task.state = TaskState::Failed;
506        task.error = Some(error.clone());
507        task.last_yield = None;
508        task.last_result = None;
509        task.yield_dest = None;
510        task.call_stack.clear();
511        task.pending_return_value = None;
512        task.pending_return_dest = None;
513        self.task_manager.attach(task);
514        Err(error)
515    }
516}