1use std::sync::Arc;
2
3use arc_gc::gc::GC;
4
5use crate::{
6 lambda::runnable::{Runnable, RuntimeError, StepResult},
7 types::{
8 lambda::vm_instructions::opcode::get_processed_opcode,
9 object::{OnionObject, OnionObjectCell, OnionStaticObject},
10 },
11};
12
13use super::{
14 context::{Context, Frame},
15 vm_instructions::{
16 self,
17 instruction_set::{VMInstruction, VMInstructionPackage},
18 opcode::ProcessedOpcode,
19 },
20};
21
22type InstructionHandler =
23 fn(&mut OnionLambdaRunnable, &ProcessedOpcode, &mut GC<OnionObjectCell>) -> StepResult;
24
25static INSTRUCTION_TABLE: std::sync::LazyLock<Vec<InstructionHandler>> =
27 std::sync::LazyLock::new(|| {
28 let mut instruction_table: Vec<InstructionHandler> = vec![
29 |_, opcode, _| StepResult::Error(RuntimeError::DetailedError(format!("Invalid instruction: {:?}", opcode).into())); 256 ];
32
33 instruction_table[VMInstruction::LoadNull as usize] = vm_instructions::load_null;
36 instruction_table[VMInstruction::LoadInt32 as usize] = vm_instructions::load_int;
37 instruction_table[VMInstruction::LoadInt64 as usize] = vm_instructions::load_int;
38 instruction_table[VMInstruction::LoadFloat32 as usize] = vm_instructions::load_float;
39 instruction_table[VMInstruction::LoadFloat64 as usize] = vm_instructions::load_float;
40 instruction_table[VMInstruction::LoadString as usize] = vm_instructions::load_string;
41 instruction_table[VMInstruction::LoadBytes as usize] = vm_instructions::load_bytes;
42 instruction_table[VMInstruction::LoadBool as usize] = vm_instructions::load_bool;
43 instruction_table[VMInstruction::LoadLambda as usize] = vm_instructions::load_lambda;
44 instruction_table[VMInstruction::LoadUndefined as usize] = vm_instructions::load_undefined;
45
46 instruction_table[VMInstruction::BuildTuple as usize] = vm_instructions::build_tuple;
48 instruction_table[VMInstruction::BuildKeyValue as usize] = vm_instructions::build_keyval;
49 instruction_table[VMInstruction::BuildNamed as usize] = vm_instructions::build_named;
50 instruction_table[VMInstruction::BuildRange as usize] = vm_instructions::build_range;
51 instruction_table[VMInstruction::BuildSet as usize] = vm_instructions::build_set;
52 instruction_table[VMInstruction::BinaryIn as usize] = vm_instructions::is_in;
54 instruction_table[VMInstruction::BinaryIs as usize] = vm_instructions::check_is_same_object;
55
56 instruction_table[VMInstruction::BinaryAdd as usize] = vm_instructions::binary_add;
57 instruction_table[VMInstruction::BinarySub as usize] = vm_instructions::binary_subtract;
58 instruction_table[VMInstruction::BinaryMul as usize] = vm_instructions::binary_multiply;
59 instruction_table[VMInstruction::BinaryDiv as usize] = vm_instructions::binary_divide;
60 instruction_table[VMInstruction::BinaryMod as usize] = vm_instructions::binary_modulus;
61 instruction_table[VMInstruction::BinaryPow as usize] = vm_instructions::binary_power;
62 instruction_table[VMInstruction::BinaryBitAnd as usize] =
63 vm_instructions::binary_bitwise_and;
64 instruction_table[VMInstruction::BinaryBitOr as usize] = vm_instructions::binary_bitwise_or;
65 instruction_table[VMInstruction::BinaryBitXor as usize] =
66 vm_instructions::binary_bitwise_xor;
67 instruction_table[VMInstruction::BinaryShl as usize] = vm_instructions::binary_shift_left;
68 instruction_table[VMInstruction::BinaryShr as usize] = vm_instructions::binary_shift_right;
69 instruction_table[VMInstruction::BinaryEq as usize] = vm_instructions::binary_equal;
70 instruction_table[VMInstruction::BinaryNe as usize] = vm_instructions::binary_not_equal;
71 instruction_table[VMInstruction::BinaryGt as usize] = vm_instructions::binary_greater;
72 instruction_table[VMInstruction::BinaryLt as usize] = vm_instructions::binary_less;
73 instruction_table[VMInstruction::BinaryGe as usize] = vm_instructions::binary_greater_equal;
74 instruction_table[VMInstruction::BinaryLe as usize] = vm_instructions::binary_less_equal;
75 instruction_table[VMInstruction::MapTo as usize] = vm_instructions::map_to;
76
77 instruction_table[VMInstruction::UnaryBitNot as usize] = vm_instructions::unary_bitwise_not;
79 instruction_table[VMInstruction::UnaryAbs as usize] = vm_instructions::unary_plus;
80 instruction_table[VMInstruction::UnaryNeg as usize] = vm_instructions::unary_minus;
81
82 instruction_table[VMInstruction::StoreVar as usize] = vm_instructions::let_var;
84 instruction_table[VMInstruction::LoadVar as usize] = vm_instructions::get_var;
85 instruction_table[VMInstruction::SetValue as usize] = vm_instructions::set_var;
86 instruction_table[VMInstruction::GetAttr as usize] = vm_instructions::get_attr;
87 instruction_table[VMInstruction::IndexOf as usize] = vm_instructions::index_of;
88 instruction_table[VMInstruction::KeyOf as usize] = vm_instructions::key_of;
89 instruction_table[VMInstruction::ValueOf as usize] = vm_instructions::value_of;
90 instruction_table[VMInstruction::TypeOf as usize] = vm_instructions::type_of;
91 instruction_table[VMInstruction::ShallowCopy as usize] = vm_instructions::copy;
92 instruction_table[VMInstruction::Swap as usize] = vm_instructions::swap;
93 instruction_table[VMInstruction::LengthOf as usize] = vm_instructions::get_length;
94 instruction_table[VMInstruction::Mut as usize] = vm_instructions::mutablize;
95 instruction_table[VMInstruction::Const as usize] = vm_instructions::immutablize;
96 instruction_table[VMInstruction::ForkInstruction as usize] =
97 vm_instructions::fork_instruction;
98 instruction_table[VMInstruction::Launch as usize] = vm_instructions::launch_thread;
99 instruction_table[VMInstruction::Spawn as usize] = vm_instructions::spawn_task;
100
101 instruction_table[VMInstruction::Call as usize] = vm_instructions::call_lambda;
103 instruction_table[VMInstruction::AsyncCall as usize] = vm_instructions::async_call;
104 instruction_table[VMInstruction::SyncCall as usize] = vm_instructions::sync_call;
105 instruction_table[VMInstruction::Return as usize] = vm_instructions::return_value;
106 instruction_table[VMInstruction::Raise as usize] = vm_instructions::raise;
107 instruction_table[VMInstruction::Jump as usize] = vm_instructions::jump;
108 instruction_table[VMInstruction::JumpIfFalse as usize] = vm_instructions::jump_if_false;
109
110 instruction_table[VMInstruction::NewFrame as usize] = vm_instructions::new_frame;
112 instruction_table[VMInstruction::PopFrame as usize] = vm_instructions::pop_frame;
113 instruction_table[VMInstruction::ResetStack as usize] = vm_instructions::clear_stack;
114 instruction_table[VMInstruction::Pop as usize] = vm_instructions::discard_top;
115
116 instruction_table[VMInstruction::Import as usize] = vm_instructions::import;
118
119 instruction_table[VMInstruction::Assert as usize] = vm_instructions::assert;
120
121 instruction_table
122 });
123
124pub struct OnionLambdaRunnable {
125 pub(crate) argument: OnionStaticObject,
126 pub(crate) result: OnionStaticObject,
127 pub(crate) this_lambda: OnionStaticObject,
128 pub(crate) context: Context,
129 pub(crate) ip: isize, pub(crate) instruction: Arc<VMInstructionPackage>,
131}
132
133impl OnionLambdaRunnable {
134 pub fn new(
135 argument: OnionStaticObject,
136 self_object: &OnionObject,
137 this_lambda: &OnionStaticObject,
138 instruction: Arc<VMInstructionPackage>,
139 ip: isize,
140 ) -> Result<Self, RuntimeError> {
141 let mut new_context = Context::new();
142 Context::push_frame(
143 &mut new_context,
144 Frame {
145 variables: rustc_hash::FxHashMap::default(),
146 stack: Vec::new(),
147 },
148 );
149
150 let (index_this, index_self, index_arguments) = {
151 let string_pool = instruction.get_string_pool();
152
153 let index_this = string_pool
154 .iter()
155 .position(|s| s == "this")
156 .ok_or_else(|| {
157 RuntimeError::InvalidOperation(
158 "Missing required variable 'this' in string pool"
159 .to_string()
160 .into(),
161 )
162 })?;
163
164 let index_self = string_pool
165 .iter()
166 .position(|s| s == "self")
167 .ok_or_else(|| {
168 RuntimeError::InvalidOperation(
169 "Missing required variable 'self' in string pool"
170 .to_string()
171 .into(),
172 )
173 })?;
174
175 let index_arguments = string_pool
176 .iter()
177 .position(|s| s == "arguments")
178 .ok_or_else(|| {
179 RuntimeError::InvalidOperation(
180 "Missing required variable 'arguments' in string pool"
181 .to_string()
182 .into(),
183 )
184 })?;
185
186 (index_this, index_self, index_arguments)
187 };
188
189 new_context
191 .let_variable(index_this, this_lambda.clone())
192 .map_err(|e| {
193 RuntimeError::InvalidOperation(
194 format!("Failed to initialize 'this' variable: {}", e).into(),
195 )
196 })?;
197
198 new_context
199 .let_variable(index_self, self_object.stabilize())
200 .map_err(|e| {
201 RuntimeError::InvalidOperation(
202 format!("Failed to initialize 'self' variable: {}", e).into(),
203 )
204 })?;
205
206 new_context
207 .let_variable(index_arguments, argument.clone())
208 .map_err(|e| {
209 RuntimeError::InvalidOperation(
210 format!("Failed to initialize 'arguments' variable: {}", e).into(),
211 )
212 })?;
213
214 let pool = instruction.get_string_pool();
215
216 argument.weak().with_data(|data| {
217 if let OnionObject::Tuple(tuple) = data {
218 for item in tuple.get_elements().iter() {
219 match item {
220 OnionObject::Named(named) => {
221 named.get_key().with_data(|key| match key {
222 OnionObject::String(key_str) => {
223 match pool.iter().position(|s| s.eq(key_str.as_ref())) {
224 Some(index) => new_context
225 .let_variable(index, named.get_value().stabilize()),
226 None => {
227 Ok(())
229 }
230 }
231 }
232 _ => Ok(()),
233 })?;
234 }
235 _ => {}
236 }
237 }
238 Ok(())
239 } else {
240 Err(RuntimeError::InvalidOperation(
241 "Argument must be a tuple".to_string().into(),
242 ))
243 }
244 })?;
245
246 Ok(OnionLambdaRunnable {
247 argument,
248 this_lambda: this_lambda.clone(),
249 result: OnionStaticObject::default(),
250 context: new_context,
251 ip,
252 instruction,
253 })
254 }
255}
256
257impl Runnable for OnionLambdaRunnable {
258 fn receive(
259 &mut self,
260 step_result: &StepResult,
261 _gc: &mut GC<OnionObjectCell>,
262 ) -> Result<(), RuntimeError> {
263 if let StepResult::Return(result) = step_result {
264 self.context.push_object(result.as_ref().clone())?;
265 Ok(())
266 } else {
267 Err(RuntimeError::DetailedError(
268 "receive not implemented for cases except `Return`"
269 .to_string()
270 .into(),
271 ))
272 }
273 }
274 fn step(&mut self, gc: &mut GC<OnionObjectCell>) -> StepResult {
275 const MAX_INLINE_STEPS: usize = 1024;
276
277 let mut steps = 0;
278
279 let (code_ptr, code_len) = {
281 let code = self.instruction.get_code();
282 (code.as_ptr(), code.len())
283 };
284
285 loop {
286 if steps >= MAX_INLINE_STEPS {
287 break;
288 }
289 steps += 1;
290
291 let mut ip = self.ip as usize;
292 if ip >= code_len {
293 return StepResult::Error(RuntimeError::DetailedError(
294 "Instruction pointer out of bounds".to_string().into(),
295 ));
296 }
297
298 let code = unsafe { std::slice::from_raw_parts(code_ptr, code_len) };
300 let pending_ip = ip;
301 let opcode = get_processed_opcode(code, &mut ip);
302
303 let handler = unsafe { *INSTRUCTION_TABLE.get_unchecked(opcode.instruction as usize) };
304 self.ip = ip as isize;
305
306 match handler(self, &opcode, gc) {
307 StepResult::Continue => continue,
308 StepResult::Error(RuntimeError::Pending) => {
309 self.ip = pending_ip as isize; return StepResult::Error(RuntimeError::Pending);
312 }
313 v => return v,
314 }
315 }
316
317 StepResult::Continue
318 }
319 fn copy(&self) -> Box<dyn Runnable> {
320 Box::new(OnionLambdaRunnable {
321 argument: self.argument.clone(),
322 this_lambda: self.this_lambda.clone(),
323 result: self.result.clone(),
324 context: self.context.clone(),
325 ip: self.ip,
326 instruction: self.instruction.clone(),
327 })
328 }
329
330 fn format_context(&self) -> Result<serde_json::Value, RuntimeError> {
331 let mut stack_json_array = serde_json::Value::Array(vec![]);
332 for frame in &self.context.frames {
333 let frame_json = frame.format_context();
334 stack_json_array.as_array_mut().unwrap().push(frame_json);
335 }
336 Ok(serde_json::json!({
338 "type": "lambda_runnable",
339 "frames": stack_json_array,
340 "ip": self.ip,
341 "argument": self.argument.to_string(),
342 "this_lambda": self.this_lambda.to_string(),
343 "result": self.result.to_string(),
344 }))
345 }
346}
347#[cfg(test)]
348mod size_tests {
349 use super::*;
350
351 #[test]
352 fn print_sizes() {
353 println!("StepResult size: {}", std::mem::size_of::<StepResult>());
354 println!("RuntimeError size: {}", std::mem::size_of::<RuntimeError>());
355 println!("StepResult size: {}", std::mem::size_of::<StepResult>());
356 }
357}