use oxide_bytecode::module::CompiledModule; use oxide_bytecode::opcode; use crate::vm::{CallFrame, InlineSyncState, Vm}; use oxide_types::object::JsObject; use oxide_types::value::JsValue; impl Vm { pub(crate) fn call_bytecode_function_inline( &mut self, callee: JsValue, callee_obj: &JsObject, receiver: JsValue, args: &[JsValue], ) -> Result { if callee_obj.sub_module_index() == 0 { return Err(self.error_message_text("TypeError", "accessor is not callable")); } let sub_idx = callee_obj.sub_module_index() as usize - 1; if sub_idx >= self.sub_modules.len() { return Err(format!( "accessor sub_module_index {} out of bounds (max {})", sub_idx, self.sub_modules.len() )); } if self.frames.len() >= self.kernel_core.config.max_call_depth { self.raise_error_kind("RangeError", "Maximum call stack size exceeded")?; return Ok(JsValue::undefined()); } if self.native_call_depth >= self.kernel_core.config.max_call_depth { self.raise_error_kind("RangeError", "Maximum call stack size exceeded")?; return Ok(JsValue::undefined()); } self.native_call_depth += 1; let sub = self.sub_modules[sub_idx].clone(); let converted_constants = self.convert_constants(&sub.constants).map_err(String::from)?; let saved = Box::new(InlineSyncState { regs: Box::new(self.regs), pc: self.pc, bytecode: std::mem::take(&mut self.bytecode), constants: std::mem::take(&mut self.constants), active_reg_limit: self.active_reg_limit, root_reg_limit: self.root_reg_limit, try_stack: std::mem::take(&mut self.try_stack), frames: std::mem::take(&mut self.frames), exception_value: self.exception_value.take(), pending_exception: self.pending_exception.take(), pending_error_kind: self.pending_error_kind.take(), for_in_iters: std::mem::take(&mut self.for_in_iters), for_of_iters: std::mem::take(&mut self.for_of_iters), last_for_of_result: self.last_for_of_result, saved_bytecode_stack: std::mem::take(&mut self.saved_bytecode_stack), saved_constants_stack: std::mem::take(&mut self.saved_constants_stack), }); self.regs = [JsValue::undefined(); 256]; self.pc = 0; self.bytecode = sub.bytecode; self.constants = converted_constants; self.active_reg_limit = sub.n_registers.max(1); self.root_reg_limit = self.active_reg_limit; for i in 0..sub.n_args as usize { self.regs[sub.param_base as usize + i] = args.get(i).copied().unwrap_or(JsValue::undefined()); } self.regs[254] = if sub.is_arrow { callee_obj.captured_this() } else { receiver }; self.regs[255] = JsValue::undefined(); for (name, reg) in &sub.builtin_reg_map { let si = self.kernel_core.string_forge().intern(name.as_str()).0; let global = self.session.global_object(); if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) { self.regs[*reg as usize] = global.get_prop_at(pos); } } let _ = callee; let result = self.dispatch(); self.native_call_depth -= 1; self.regs = *saved.regs; self.pc = saved.pc; self.bytecode = saved.bytecode; self.constants = saved.constants; self.active_reg_limit = saved.active_reg_limit; self.root_reg_limit = saved.root_reg_limit; self.try_stack = saved.try_stack; self.frames = saved.frames; self.exception_value = saved.exception_value; self.pending_exception = saved.pending_exception; self.pending_error_kind = saved.pending_error_kind; self.for_in_iters = saved.for_in_iters; self.for_of_iters = saved.for_of_iters; self.last_for_of_result = saved.last_for_of_result; self.saved_bytecode_stack = saved.saved_bytecode_stack; self.saved_constants_stack = saved.saved_constants_stack; result } pub(crate) fn restore_frame(&mut self, frame: CallFrame) { if let Some(saved_bc) = self.saved_bytecode_stack.pop() { self.bytecode = saved_bc; } if let Some(saved_consts) = self.saved_constants_stack.pop() { self.constants = saved_consts; } let restore_len = frame.saved_regs.len(); self.regs[..restore_len].copy_from_slice(&frame.saved_regs); self.regs[254] = frame.saved_this; self.regs[255] = frame.saved_new_target; self.active_reg_limit = frame.caller_reg_limit; self.pc = frame.return_addr; } pub fn rerun(&mut self) -> Result { self.clear_execution_state(); self.active_reg_limit = self.root_reg_limit; self.clear_ic_caches(); self.dispatch() } fn clear_ic_caches(&mut self) { let mut i = 0; while i < self.bytecode.len() { let op = opcode::opcode(self.bytecode[i]); if op.has_ic_ext_words() { if i + 3 < self.bytecode.len() { self.bytecode[i + 1] = 0; self.bytecode[i + 2] = 0; self.bytecode[i + 3] = 0; } i += 4; } else { i += 1; } } } pub fn run(&mut self, module: &CompiledModule) -> Result { self.clear_execution_state(); self.sub_modules = module.sub_modules.clone(); self.constants = self.convert_constants(&module.constants).map_err(String::from)?; self.sub_module_constants = vec![Vec::new(); self.sub_modules.len()]; self.bytecode = module.bytecode.clone(); self.root_reg_limit = module.n_registers.max(1); self.active_reg_limit = self.root_reg_limit; for (name, reg) in &module.builtin_reg_map { let si = self.kernel_core.string_forge().intern(name.as_str()).0; let global = self.session.global_object(); if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) { self.regs[*reg as usize] = global.get_prop_at(pos); } } let global_ptr = self.session.global_object().as_ptr() as *mut JsObject; self.regs[254] = JsValue::from_js_object(global_ptr); self.dispatch() } #[allow(dead_code)] pub fn call_bytecode_func(&mut self, _callback_obj: &JsObject, _args_regs: &[u8]) -> Result { Err("bytecode function calls not yet supported".into()) } pub(crate) fn unwind(&mut self) -> Result<(), String> { while let Some(handler) = self.try_stack.pop() { while self.frames.len() > handler.frame_depth { if let Some(frame) = self.frames.pop() { self.restore_frame(frame); } } if let Some(finally_pc) = handler.finally_pc { if self.pending_exception.is_none() { self.pending_exception = self.exception_value.take(); } self.try_stack.push(handler); self.pc = finally_pc; return Ok(()); } if let Some(catch_pc) = handler.catch_pc { let exc = self.exception_value.take().unwrap_or(JsValue::undefined()); self.regs[0] = exc; self.pc = catch_pc; return Ok(()); } } while let Some(frame) = self.frames.pop() { self.restore_frame(frame); } let exc = self.exception_value.take().unwrap_or(JsValue::undefined()); let kind_str = self.pending_error_kind.take().unwrap_or("Error"); let exc_text = self.error_text(exc); let msg = if exc_text.starts_with(kind_str) { format!("uncaught {exc_text}") } else { format!("uncaught {kind_str}: {exc_text}") }; Err(msg) } }