This repository has been archived on 2026-09-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OxideJS/crates/oxide_vm/src/dispatch/misc.rs
T
2026-08-06 23:10:27 +08:00

151 lines
5.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use crate::vm::Vm;
use crate::{vm_error, vm_trace};
use oxide_bytecode::opcode;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
impl Vm {
#[inline(always)]
pub(crate) fn dispatch_load_const(&mut self, rd: usize, instr: u32) -> Result<(), String> {
let idx = (instr >> 16) as usize;
vm_trace!("LOAD_CONST rd={} idx={}", rd, idx);
let imm = self.immutables();
if idx < imm.len() {
self.regs[rd] = imm[idx];
Ok(())
} else {
vm_error!("LOAD_CONST index {} out of bounds (len={})", idx, imm.len());
Err(format!("constant index {idx} out of bounds"))
}
}
#[inline(always)]
pub(crate) fn dispatch_typeof(&mut self, rd: usize, a: usize) {
vm_trace!("TYPEOF rd={} r{}={:?}", rd, a, self.regs[a]);
let val = self.regs[a];
let result = if val.is_undefined() {
"undefined"
} else if val.is_null() {
"object"
} else if val.is_bool() {
"boolean"
} else if val.is_int() || val.is_double() {
"number"
} else if val.is_string() {
"string"
} else if val.is_symbol() {
"symbol"
} else if val.is_object() {
let obj = unsafe { &*val.as_js_object_ptr() };
if obj.is_function() {
"function"
} else {
"object"
}
} else {
"undefined"
};
self.regs[rd] = self.new_string(result);
}
pub(crate) fn dispatch_load_var(&mut self, rd: usize, a: usize) -> Result<bool, String> {
vm_trace!("LOAD_VAR rd={} r{}={:?}", rd, a, self.regs[a]);
if a == 254
&& self.frames.last().map(|frame| frame.is_derived_constructor).unwrap_or(false)
&& self.regs[a].is_undefined()
{
self.raise_error_kind("ReferenceError", "must call super constructor before using 'this'")?;
return Ok(true);
}
self.regs[rd] = self.regs[a];
Ok(false)
}
pub(crate) fn dispatch_store_var(&mut self, rd: usize, a: usize, b: usize) -> Result<bool, String> {
vm_trace!("STORE_VAR r{}={:?} const={}", rd, self.regs[a], b);
if b != 0 {
// const guard:检查目标槽是否已初始化。
if !self.regs[rd].is_undefined() {
self.raise_error_kind("TypeError", "Assignment to constant variable")?;
return Ok(true);
}
}
self.regs[rd] = self.regs[a];
Ok(false)
}
/// SPILL:`regs[rd]` → `spill_stack[帧基址 + slot]`。
///
/// ext 字为 slot(u16);帧基址 = 当前 CallFrame.spill_offset(顶层模块无帧为 0)。
/// # 边界与前提
/// - slot 超 u16 范围返回错误(RegAlloc 保证发射在界内)。
/// # 副作用
/// - 写 spill 栈,可能触发 resize 扩容。
#[inline(always)]
pub(crate) fn dispatch_spill(&mut self, rd: usize) -> Result<(), String> {
let slot = self.bytecode[self.pc] as usize;
self.pc += 1;
if slot > u16::MAX as usize {
return Err(format!("SPILL slot {slot} out of range (max 65535)"));
}
let base = self.frames.last().map(|f| f.spill_offset as usize).unwrap_or(0);
let idx = base + slot;
if idx >= self.spill_stack.len() {
self.spill_stack.resize(idx + 1, JsValue::undefined());
}
vm_trace!("SPILL r{} -> slot[{}] base={}", rd, slot, base);
self.spill_stack[idx] = self.regs[rd];
Ok(())
}
/// UNSPILL:`spill_stack[帧基址 + slot]` → `regs[rd]`。
///
/// # 边界与前提
/// - slot 超 u16 范围返回错误。
/// - 越界读返回 undefined(防御:slot 由 RegAlloc 保证在界内)。
#[inline(always)]
pub(crate) fn dispatch_unspill(&mut self, rd: usize) -> Result<(), String> {
let slot = self.bytecode[self.pc] as usize;
self.pc += 1;
if slot > u16::MAX as usize {
return Err(format!("UNSPILL slot {slot} out of range (max 65535)"));
}
let base = self.frames.last().map(|f| f.spill_offset as usize).unwrap_or(0);
let idx = base + slot;
vm_trace!("UNSPILL r{} <- slot[{}] base={}", rd, slot, base);
self.regs[rd] = if idx < self.spill_stack.len() {
self.spill_stack[idx]
} else {
JsValue::undefined()
};
Ok(())
}
#[inline(always)]
pub(crate) fn dispatch_new_object(&mut self, rd: usize) {
vm_trace!("NEW_OBJECT rd={}", rd);
let proto_ptr = &*self.object_prototype as *const JsObject as *mut JsObject;
let obj = self
.epoch
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr)));
self.regs[rd] = JsValue::object(obj as *mut u8);
}
#[inline(always)]
pub(crate) fn dispatch_new_array(&mut self, rd: usize, instr: u32) {
let n = opcode::imm16(instr) as usize;
vm_trace!("NEW_ARRAY rd={} n={}", rd, n);
let proto_ptr = self.session.builtin_world().array_proto.as_ptr() as *mut JsObject;
let bump = self.epoch.bump();
let obj = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), n, bump));
self.regs[rd] = JsValue::object(obj as *mut u8);
}
#[inline(always)]
pub(crate) fn dispatch_void(&mut self, rd: usize) {
vm_trace!("VOID rd={}", rd);
self.regs[rd] = JsValue::undefined();
}
}