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::{array, 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    pub(crate) fn push_export_prefix(&mut self, crate_name: &str) {
304        let sanitized = crate_name.replace('-', "_");
305        self.export_prefix_stack.push(sanitized);
306    }
307
308    pub(crate) fn pop_export_prefix(&mut self) {
309        self.export_prefix_stack.pop();
310    }
311
312    fn current_export_prefix(&self) -> Option<&str> {
313        self.export_prefix_stack.last().map(|s| s.as_str())
314    }
315
316    pub fn export_prefix(&self) -> Option<String> {
317        self.current_export_prefix().map(|s| s.to_string())
318    }
319
320    fn canonicalize_export_name(&self, export: &mut NativeExport) {
321        if let Some(prefix) = self.current_export_prefix() {
322            let needs_prefix = match export.name.strip_prefix(prefix) {
323                Some(rest) => {
324                    if rest.is_empty() {
325                        false
326                    } else {
327                        !matches!(rest.chars().next(), Some('.') | Some(':'))
328                    }
329                }
330                None => true,
331            };
332            if needs_prefix {
333                export.name = if export.name.is_empty() {
334                    prefix.to_string()
335                } else {
336                    format!("{prefix}.{}", export.name)
337                };
338            }
339        }
340    }
341
342    fn push_export_metadata(&mut self, export: NativeExport) {
343        if self.exported_natives.iter().any(|existing| existing.name == export.name) {
344            return;
345        }
346        self.exported_natives.push(export);
347    }
348
349    pub fn record_exported_native(&mut self, mut export: NativeExport) {
350        self.canonicalize_export_name(&mut export);
351        self.push_export_metadata(export);
352    }
353
354    pub fn register_exported_native<F>(&mut self, export: NativeExport, func: F)
355    where
356        F: Fn(&[Value]) -> CoreResult<NativeCallResult, String> + 'static,
357    {
358        let mut export = export;
359        self.canonicalize_export_name(&mut export);
360        let name = export.name.clone();
361        self.push_export_metadata(export);
362        let native = Value::NativeFunction(Rc::new(func));
363        self.register_native(name, native);
364    }
365
366    #[cfg(feature = "std")]
367    pub fn register_type_stubs(&mut self, stubs: Vec<ModuleStub>) {
368        if stubs.is_empty() {
369            return;
370        }
371        self.exported_type_stubs.extend(stubs);
372    }
373
374    #[cfg(feature = "std")]
375    pub fn exported_type_stubs(&self) -> &[ModuleStub] {
376        &self.exported_type_stubs
377    }
378
379    #[cfg(feature = "std")]
380    pub fn take_type_stubs(&mut self) -> Vec<ModuleStub> {
381        mem::take(&mut self.exported_type_stubs)
382    }
383
384    pub fn exported_natives(&self) -> &[NativeExport] {
385        &self.exported_natives
386    }
387
388    pub fn take_exported_natives(&mut self) -> Vec<NativeExport> {
389        mem::take(&mut self.exported_natives)
390    }
391
392    pub fn clear_native_functions(&mut self) {
393        self.natives.clear();
394        #[cfg(feature = "std")]
395        self.exported_type_stubs.clear();
396    }
397
398    #[cfg(feature = "std")]
399    pub fn dump_externs_to_dir(
400        &self,
401        output_root: impl AsRef<std::path::Path>,
402    ) -> std::io::Result<Vec<std::path::PathBuf>> {
403        self.dump_externs_to_dir_with_options(
404            output_root,
405            &crate::externs::DumpExternsOptions::default(),
406        )
407    }
408
409    #[cfg(feature = "std")]
410    pub fn dump_externs_to_dir_with_options(
411        &self,
412        output_root: impl AsRef<std::path::Path>,
413        options: &crate::externs::DumpExternsOptions,
414    ) -> std::io::Result<Vec<std::path::PathBuf>> {
415        let files = crate::externs::extern_files_from_vm(self, options);
416        crate::externs::write_extern_files(output_root, &files)
417    }
418
419    pub fn get_global(&self, name: &str) -> Option<Value> {
420        if let Some(value) = self.globals.get(name) {
421            Some(value.clone())
422        } else {
423            self.natives.get(name).cloned()
424        }
425    }
426
427    pub fn global_names(&self) -> Vec<String> {
428        self.globals.keys().cloned().collect()
429    }
430
431    pub fn globals_snapshot(&self) -> Vec<(String, Value)> {
432        self.globals
433            .iter()
434            .map(|(name, value)| (name.clone(), value.clone()))
435            .collect()
436    }
437
438    pub fn set_global(&mut self, name: impl Into<String>, value: Value) {
439        let name = name.into();
440        self.observe_value(&value);
441        self.globals.insert(name.clone(), value);
442        self.natives.remove(&name);
443        self.maybe_collect_cycles();
444    }
445
446    pub fn call(&mut self, function_name: &str, args: Vec<Value>) -> Result<Value> {
447        let func_idx = self
448            .functions
449            .iter()
450            .position(|f| f.name == function_name)
451            .ok_or_else(|| LustError::RuntimeError {
452                message: format!("Function not found: {}", function_name),
453            })?;
454        let mut frame = CallFrame {
455            function_idx: func_idx,
456            ip: 0,
457            registers: array::from_fn(|_| Value::Nil),
458            base_register: 0,
459            return_dest: None,
460            upvalues: Vec::new(),
461        };
462        let func = &self.functions[func_idx];
463        if args.len() != func.param_count as usize {
464            return Err(LustError::RuntimeError {
465                message: format!(
466                    "Function {} expects {} arguments, got {}",
467                    function_name,
468                    func.param_count,
469                    args.len()
470                ),
471            });
472        }
473
474        for (i, arg) in args.into_iter().enumerate() {
475            frame.registers[i] = arg;
476        }
477
478        self.call_stack.push(frame);
479        match self.run() {
480            Ok(v) => Ok(v),
481            Err(e) => Err(self.annotate_runtime_error(e)),
482        }
483    }
484
485    pub fn function_value(&self, function_name: &str) -> Option<Value> {
486        let canonical = if function_name.contains("::") {
487            function_name.replace("::", ".")
488        } else {
489            function_name.to_string()
490        };
491        self.functions
492            .iter()
493            .position(|f| f.name == canonical)
494            .map(Value::Function)
495    }
496
497    pub fn function_name(&self, index: usize) -> Option<&str> {
498        self.functions.get(index).map(|f| f.name.as_str())
499    }
500
501    pub fn fail_task_handle(&mut self, handle: TaskHandle, error: LustError) -> Result<()> {
502        let task_id = self.task_id_from_handle(handle)?;
503        let mut task =
504            self.task_manager
505                .detach(task_id)
506                .ok_or_else(|| LustError::RuntimeError {
507                    message: format!("Invalid task handle {}", handle.id()),
508                })?;
509        task.state = TaskState::Failed;
510        task.error = Some(error.clone());
511        task.last_yield = None;
512        task.last_result = None;
513        task.yield_dest = None;
514        task.call_stack.clear();
515        task.pending_return_value = None;
516        task.pending_return_dest = None;
517        self.task_manager.attach(task);
518        Err(error)
519    }
520}