Skip to main content

lust/vm/
mod.rs

1mod corelib;
2mod budget;
3mod cycle;
4#[cfg(feature = "std")]
5pub mod stdlib;
6mod task;
7pub(super) use self::task::{TaskId, TaskInstance, TaskManager, TaskState};
8use self::budget::BudgetState;
9pub(super) use crate::ast::{FieldOwnership, StructDef};
10pub(super) use crate::bytecode::{
11    FieldStorage, Function, Instruction, NativeCallResult, Register, StructLayout, TaskHandle,
12    Value,
13};
14#[cfg(feature = "std")]
15pub(super) use crate::embed::native_types::ModuleStub;
16pub(super) use crate::error::StackFrame;
17pub(super) use crate::jit::{
18    JitCompiler, JitState, TraceOptimizer, TraceRecorder, MAX_TRACE_LENGTH,
19};
20pub(super) use crate::number::{
21    float_abs, float_acos, float_asin, float_atan, float_atan2, float_ceil, float_clamp,
22    float_cos, float_floor, float_from_int, float_round, float_sin, float_sqrt, float_tan,
23    int_from_float, int_from_usize, LustFloat,
24};
25pub(super) use crate::{LustError, Result};
26pub(super) use alloc::{
27    format,
28    rc::Rc,
29    string::{String, ToString},
30    vec,
31    vec::Vec,
32};
33use core::cell::RefCell;
34use hashbrown::{DefaultHashBuilder, HashMap};
35mod api;
36mod execution;
37mod tasks;
38mod tracing;
39pub use self::api::{NativeExport, NativeExportParam};
40#[cfg(feature = "std")]
41thread_local! {
42    static CURRENT_VM_STACK: RefCell<Vec<*mut VM>> = RefCell::new(Vec::new());
43}
44
45#[cfg(not(feature = "std"))]
46struct VmStack {
47    inner: core::cell::UnsafeCell<Option<Vec<*mut VM>>>,
48}
49
50#[cfg(not(feature = "std"))]
51impl VmStack {
52    const fn new() -> Self {
53        Self {
54            inner: core::cell::UnsafeCell::new(None),
55        }
56    }
57
58    fn with_mut<F, R>(&self, f: F) -> R
59    where
60        F: FnOnce(&mut Vec<*mut VM>) -> R,
61    {
62        let vec = self.ensure_vec();
63        f(vec)
64    }
65
66    fn with_ref<F, R>(&self, f: F) -> R
67    where
68        F: FnOnce(&Vec<*mut VM>) -> R,
69    {
70        let vec = self.ensure_vec();
71        f(vec)
72    }
73
74    fn ensure_vec(&self) -> &mut Vec<*mut VM> {
75        unsafe {
76            let slot = &mut *self.inner.get();
77            if slot.is_none() {
78                *slot = Some(Vec::new());
79            }
80            slot.as_mut().unwrap()
81        }
82    }
83}
84
85#[cfg(not(feature = "std"))]
86unsafe impl Sync for VmStack {}
87
88#[cfg(not(feature = "std"))]
89static VM_STACK: VmStack = VmStack::new();
90
91#[cfg(feature = "std")]
92fn with_vm_stack_ref<F, R>(f: F) -> R
93where
94    F: FnOnce(&Vec<*mut VM>) -> R,
95{
96    CURRENT_VM_STACK.with(|stack| {
97        let stack = stack.borrow();
98        f(&stack)
99    })
100}
101
102#[cfg(feature = "std")]
103fn with_vm_stack_mut<F, R>(f: F) -> R
104where
105    F: FnOnce(&mut Vec<*mut VM>) -> R,
106{
107    CURRENT_VM_STACK.with(|stack| {
108        let mut stack = stack.borrow_mut();
109        f(&mut stack)
110    })
111}
112
113#[cfg(not(feature = "std"))]
114fn with_vm_stack_ref<F, R>(f: F) -> R
115where
116    F: FnOnce(&Vec<*mut VM>) -> R,
117{
118    VM_STACK.with_ref(f)
119}
120
121#[cfg(not(feature = "std"))]
122fn with_vm_stack_mut<F, R>(f: F) -> R
123where
124    F: FnOnce(&mut Vec<*mut VM>) -> R,
125{
126    VM_STACK.with_mut(f)
127}
128
129pub(crate) fn push_vm_ptr(vm: *mut VM) {
130    with_vm_stack_mut(|stack| stack.push(vm));
131}
132
133pub(crate) fn pop_vm_ptr() {
134    with_vm_stack_mut(|stack| {
135        stack.pop();
136    });
137}
138
139#[cfg(feature = "std")]
140pub(super) fn with_vm_stack<F, R>(f: F) -> R
141where
142    F: FnOnce(&Vec<*mut VM>) -> R,
143{
144    with_vm_stack_ref(f)
145}
146
147#[cfg(not(feature = "std"))]
148pub(super) fn with_vm_stack<F, R>(f: F) -> R
149where
150    F: FnOnce(&Vec<*mut VM>) -> R,
151{
152    with_vm_stack_ref(f)
153}
154
155pub(super) const TO_STRING_TRAIT: &str = "ToString";
156pub(super) const TO_STRING_METHOD: &str = "to_string";
157pub(super) const HASH_KEY_TRAIT: &str = "HashKey";
158pub(super) const HASH_KEY_METHOD: &str = "to_hashkey";
159pub struct VM {
160    pub(super) jit: JitState,
161    pub(super) budgets: BudgetState,
162    pub(super) functions: Vec<Function>,
163    pub(super) natives: HashMap<String, Value>,
164    pub(super) globals: HashMap<String, Value>,
165    pub(super) map_hasher: DefaultHashBuilder,
166    pub(super) call_stack: Vec<CallFrame>,
167    pub(super) max_stack_depth: usize,
168    pub(super) pending_return_value: Option<Value>,
169    pub(super) pending_return_dest: Option<Register>,
170    pub(super) trace_recorder: Option<TraceRecorder>,
171    pub(super) side_trace_context: Option<(crate::jit::TraceId, usize)>,
172    pub(super) skip_next_trace_record: bool,
173    pub(super) trait_impls: HashMap<(String, String), bool>,
174    pub(super) struct_tostring_cache: HashMap<usize, Rc<String>>,
175    pub(super) struct_metadata: HashMap<String, RuntimeStructInfo>,
176    pub(super) call_until_depth: Option<usize>,
177    pub(super) task_manager: TaskManager,
178    pub(super) current_task: Option<TaskId>,
179    pub(super) pending_task_signal: Option<TaskSignal>,
180    pub(super) last_task_signal: Option<TaskSignal>,
181    pub(super) cycle_collector: cycle::CycleCollector,
182    pub(super) exported_natives: Vec<NativeExport>,
183    pub(super) export_prefix_stack: Vec<String>,
184    #[cfg(feature = "std")]
185    pub(super) exported_type_stubs: Vec<ModuleStub>,
186}
187
188#[derive(Debug, Clone)]
189pub(super) struct CallFrame {
190    pub(super) function_idx: usize,
191    pub(super) ip: usize,
192    /// In std mode this is a fixed [Value; 256] stored inline in the frame (no extra
193    /// indirection, JIT-compatible raw pointer layout). In no_std mode (no JIT) it is a
194    /// Vec sized to the function's actual register_count, saving the unused slots.
195    #[cfg(feature = "std")]
196    pub(super) registers: [Value; 256],
197    #[cfg(not(feature = "std"))]
198    pub(super) registers: Vec<Value>,
199    #[allow(dead_code)]
200    pub(super) base_register: usize,
201    pub(super) return_dest: Option<Register>,
202    pub(super) upvalues: Vec<Value>,
203}
204
205impl CallFrame {
206    #[allow(unused_variables)]
207    pub(super) fn new(function_idx: usize, return_dest: Option<Register>, register_count: u8) -> Self {
208        Self {
209            function_idx,
210            ip: 0,
211            #[cfg(feature = "std")]
212            registers: core::array::from_fn(|_| Value::Nil),
213            #[cfg(not(feature = "std"))]
214            registers: alloc::vec![Value::Nil; register_count as usize],
215            base_register: 0,
216            return_dest,
217            upvalues: Vec::new(),
218        }
219    }
220}
221
222#[derive(Debug, Clone)]
223pub(super) struct RuntimeStructInfo {
224    pub layout: Rc<StructLayout>,
225}
226
227#[derive(Debug, Clone)]
228pub(super) enum TaskSignal {
229    Yield { dest: Register, value: Value },
230    Stop { value: Value },
231}
232
233impl Default for VM {
234    fn default() -> Self {
235        Self::new()
236    }
237}