refactor: 拆分 dispatch 控制流辅助逻辑

This commit is contained in:
爱特每羊队
2026-06-14 00:28:34 +08:00
parent 18f4fe4000
commit 2518b08be1
3 changed files with 219 additions and 177 deletions
+1
View File
@@ -6,6 +6,7 @@ pub mod coercion;
mod dispatch;
pub mod native;
pub mod vm;
mod vm_dispatch_ctrl;
pub mod vm_pool;
mod vm_props;
mod vm_runtime;
+23 -174
View File
@@ -11,7 +11,7 @@ use crate::coercion;
use crate::native::{NativeFn, NativeResult};
use oxide_kernel::kernel::OxideKernel;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_kernel::{vm_debug, vm_trace};
use oxide_kernel::vm_debug;
use oxide_types::error::{JsError, JsErrorKind};
use oxide_types::mem::{Epoch, P};
use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes};
@@ -619,84 +619,14 @@ impl Vm {
self.regs[rd] = self.regs[a];
}
OpCode::CALL => {
let callee_reg = rd;
let this_reg = a as u8;
let first_arg_reg = b as u8;
let callee = self.regs[callee_reg];
if callee.is_object() {
let obj_ptr = callee.as_js_object_ptr();
if !obj_ptr.is_null() {
let obj = unsafe { &*obj_ptr };
if obj.is_function() {
if obj.is_class_constructor() {
throw_err!(self, TypeError, "class constructor cannot be invoked without 'new'");
}
let ext = self.bytecode[self.pc];
self.pc += 1;
let arg_count = (ext & 0xFF) as usize;
vm_debug!(
"CALL rd={} this={} args={} depth={}",
rd,
this_reg,
arg_count,
self.frames.len()
);
if obj.native_fn().is_some() {
match self.dispatch_native_call(obj, callee, this_reg, first_arg_reg, arg_count) {
Ok(()) => continue,
OpCode::CALL => match self.dispatch_call(rd, a, b) {
Ok(true) => continue,
Ok(false) => {}
Err(e) => return Err(e),
}
} else if obj.sub_module_index() > 0 {
let args: Vec<JsValue> = (0..arg_count)
.map(|i| self.regs[first_arg_reg.wrapping_add(i as u8) as usize])
.collect();
self.push_bytecode_frame(
callee,
self.regs[this_reg as usize],
&args,
None,
None,
JsValue::undefined(),
FrameContinuation::None,
)?;
continue;
}
}
}
}
throw_err!(self, TypeError, "CALL target is not callable");
}
},
OpCode::CALL_NATIVE => {
let callee_reg = rd;
let this_reg = a as u8;
let first_arg_reg = b as u8;
let callee = self.regs[callee_reg];
if !callee.is_object() {
throw_err!(self, TypeError, "CALL_NATIVE target is not an object");
}
let obj_ptr = callee.as_js_object_ptr();
if obj_ptr.is_null() {
throw_err!(self, TypeError, "CALL_NATIVE target is null");
}
let obj = unsafe { &*obj_ptr };
if !obj.is_function() || obj.native_fn().is_none() {
throw_err!(self, TypeError, "CALL_NATIVE target is not a native function");
}
let ext = self.bytecode[self.pc];
self.pc += 1;
let arg_count = (ext & 0xFF) as usize;
vm_debug!("CALL_NATIVE rd={} args={}", rd, arg_count);
self.dispatch_native_call(obj, callee, this_reg, first_arg_reg, arg_count)?;
self.dispatch_call_native(rd, a, b)?;
}
OpCode::NEW_EXPRESSION => {
@@ -1039,67 +969,14 @@ impl Vm {
}
OpCode::DEFINE_ACCESSOR => {
let prop_idx = self.bytecode[self.pc] as usize;
self.pc += 1;
if prop_idx >= self.constants.len() {
throw_err!(self, TypeError, "DEFINE_ACCESSOR constant index out of bounds");
}
let obj_val = self.regs[rd];
if !obj_val.is_object() {
throw_err!(self, TypeError, "DEFINE_ACCESSOR target is not object");
}
let prop_name_si = self.property_key_si(self.constants[prop_idx]);
let getter = self.regs[a];
let setter = self.regs[b];
let obj = unsafe { &mut *obj_val.as_js_object_ptr() };
let existing = self
.get_own_property_slot(obj, prop_name_si)
.and_then(|pos| obj.prop_meta_at(pos))
.filter(|meta| meta.is_accessor);
let get = if getter.is_undefined() {
existing.map(|meta| meta.get).unwrap_or(JsValue::undefined())
} else {
getter
};
let set = if setter.is_undefined() {
existing.map(|meta| meta.set).unwrap_or(JsValue::undefined())
} else {
setter
};
self.define_accessor_property(obj, prop_name_si, get, set, PropAttributes::DEFAULT_DATA)?;
self.dispatch_define_accessor(rd, a, b)?;
}
OpCode::RETURN => {
let result = self.regs[rd];
vm_debug!("RETURN depth={}", self.frames.len());
if let Some(frame) = self.frames.pop() {
let construct_result_reg = frame.construct_result_reg;
let constructed_this = frame.constructed_this;
let is_derived_constructor = frame.is_derived_constructor;
let continuation = frame.continuation;
let callee_this = self.regs[254];
self.restore_frame(frame);
if let (Some(target_reg), Some(constructed_this)) = (construct_result_reg, constructed_this) {
if is_derived_constructor && result.is_undefined() && callee_this.is_undefined() {
throw_err!(self, ReferenceError, "derived constructor must call super()");
}
self.regs[target_reg as usize] = if result.is_object() { result } else { constructed_this };
self.regs[0] = self.regs[target_reg as usize];
} else {
match continuation {
FrameContinuation::None => {
self.regs[0] = result;
}
FrameContinuation::AccessorGet { target_reg } => {
self.regs[target_reg as usize] = result;
}
FrameContinuation::AccessorSet => {}
}
}
} else {
return Ok(result);
}
}
OpCode::RETURN => match self.dispatch_return(rd) {
Ok(Some(result)) => return Ok(result),
Ok(None) => {}
Err(e) => return Err(e),
},
OpCode::IC_GET_PROP
| OpCode::IC_SET_PROP
@@ -1447,57 +1324,29 @@ impl Vm {
return Err("opcode FOR_OF_CLOSE not yet implemented".into());
}
OpCode::THROW => {
vm_debug!("THROW pc={}", self.pc);
let exc_value = self.regs[rd];
self.exception_value = Some(exc_value);
self.pending_error_kind = Some(self.thrown_error_kind(exc_value));
match self.unwind() {
Ok(()) => continue,
OpCode::THROW => match self.dispatch_throw(rd) {
Ok(true) => continue,
Ok(false) => {}
Err(e) => return Err(e),
}
}
},
OpCode::TRY_BEGIN => {
vm_trace!("TRY_BEGIN frame_depth={}", self.frames.len());
let offset = opcode::offset16(instr) as isize;
let catch_pc = if offset == 0 {
None
} else {
Some(((self.pc as isize) + offset - 1) as usize)
};
self.try_stack.push(TryHandler {
catch_pc,
finally_pc: None,
frame_depth: self.frames.len(),
});
self.dispatch_try_begin(instr);
}
OpCode::TRY_END => {
self.try_stack.pop();
self.dispatch_try_end();
}
OpCode::TRY_FINALLY_BEGIN => {
let offset = opcode::offset16(instr) as isize;
let finally_pc = ((self.pc as isize) + offset - 1) as usize;
self.try_stack.push(TryHandler {
catch_pc: None,
finally_pc: Some(finally_pc),
frame_depth: self.frames.len(),
});
self.dispatch_try_finally_begin(instr);
}
OpCode::TRY_FINALLY_END => {
self.try_stack.pop();
if self.pending_exception.is_some() && self.exception_value.is_none() {
self.exception_value = self.pending_exception.take();
match self.unwind() {
Ok(()) => continue,
OpCode::TRY_FINALLY_END => match self.dispatch_try_finally_end() {
Ok(true) => continue,
Ok(false) => {}
Err(e) => return Err(e),
}
}
self.pending_exception = None;
}
},
_ => {
return Err(format!("opcode {op} not yet implemented"));
+192
View File
@@ -0,0 +1,192 @@
use crate::vm::{FrameContinuation, TryHandler, Vm};
use oxide_compiler::opcode;
use oxide_types::object::PropAttributes;
use oxide_types::value::JsValue;
impl Vm {
pub(crate) fn dispatch_call(&mut self, rd: usize, a: usize, b: usize) -> Result<bool, String> {
let callee_reg = rd;
let this_reg = a as u8;
let first_arg_reg = b as u8;
let callee = self.regs[callee_reg];
if callee.is_object() {
let obj_ptr = callee.as_js_object_ptr();
if !obj_ptr.is_null() {
let obj = unsafe { &*obj_ptr };
if obj.is_function() {
if obj.is_class_constructor() {
return self
.raise_type_error("class constructor cannot be invoked without 'new'")
.map(|_| true);
}
let ext = self.bytecode[self.pc];
self.pc += 1;
let arg_count = (ext & 0xFF) as usize;
oxide_kernel::vm_debug!(
"CALL rd={} this={} args={} depth={}",
rd,
this_reg,
arg_count,
self.frames.len()
);
if obj.native_fn().is_some() {
self.dispatch_native_call(obj, callee, this_reg, first_arg_reg, arg_count)?;
return Ok(true);
} else if obj.sub_module_index() > 0 {
let args: Vec<JsValue> = (0..arg_count)
.map(|i| self.regs[first_arg_reg.wrapping_add(i as u8) as usize])
.collect();
self.push_bytecode_frame(
callee,
self.regs[this_reg as usize],
&args,
None,
None,
JsValue::undefined(),
FrameContinuation::None,
)?;
return Ok(true);
}
}
}
}
self.raise_type_error("CALL target is not callable").map(|_| true)
}
pub(crate) fn dispatch_call_native(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let callee_reg = rd;
let this_reg = a as u8;
let first_arg_reg = b as u8;
let callee = self.regs[callee_reg];
if !callee.is_object() {
return self.raise_type_error("CALL_NATIVE target is not an object");
}
let obj_ptr = callee.as_js_object_ptr();
if obj_ptr.is_null() {
return self.raise_type_error("CALL_NATIVE target is null");
}
let obj = unsafe { &*obj_ptr };
if !obj.is_function() || obj.native_fn().is_none() {
return self.raise_type_error("CALL_NATIVE target is not a native function");
}
let ext = self.bytecode[self.pc];
self.pc += 1;
let arg_count = (ext & 0xFF) as usize;
oxide_kernel::vm_debug!("CALL_NATIVE rd={} args={}", rd, arg_count);
self.dispatch_native_call(obj, callee, this_reg, first_arg_reg, arg_count)
}
pub(crate) fn dispatch_define_accessor(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let prop_idx = self.bytecode[self.pc] as usize;
self.pc += 1;
if prop_idx >= self.constants.len() {
return self.raise_type_error("DEFINE_ACCESSOR constant index out of bounds");
}
let obj_val = self.regs[rd];
if !obj_val.is_object() {
return self.raise_type_error("DEFINE_ACCESSOR target is not object");
}
let prop_name_si = self.property_key_si(self.constants[prop_idx]);
let getter = self.regs[a];
let setter = self.regs[b];
let obj = unsafe { &mut *obj_val.as_js_object_ptr() };
let existing = self
.get_own_property_slot(obj, prop_name_si)
.and_then(|pos| obj.prop_meta_at(pos))
.filter(|meta| meta.is_accessor);
let get = if getter.is_undefined() {
existing.map(|meta| meta.get).unwrap_or(JsValue::undefined())
} else {
getter
};
let set = if setter.is_undefined() {
existing.map(|meta| meta.set).unwrap_or(JsValue::undefined())
} else {
setter
};
self.define_accessor_property(obj, prop_name_si, get, set, PropAttributes::DEFAULT_DATA)
}
pub(crate) fn dispatch_return(&mut self, rd: usize) -> Result<Option<JsValue>, String> {
let result = self.regs[rd];
oxide_kernel::vm_debug!("RETURN depth={}", self.frames.len());
if let Some(frame) = self.frames.pop() {
let construct_result_reg = frame.construct_result_reg;
let constructed_this = frame.constructed_this;
let is_derived_constructor = frame.is_derived_constructor;
let continuation = frame.continuation;
let callee_this = self.regs[254];
self.restore_frame(frame);
if let (Some(target_reg), Some(constructed_this)) = (construct_result_reg, constructed_this) {
if is_derived_constructor && result.is_undefined() && callee_this.is_undefined() {
self.raise_error_kind("ReferenceError", "derived constructor must call super()")?;
return Ok(None);
}
self.regs[target_reg as usize] = if result.is_object() { result } else { constructed_this };
self.regs[0] = self.regs[target_reg as usize];
} else {
match continuation {
FrameContinuation::None => self.regs[0] = result,
FrameContinuation::AccessorGet { target_reg } => self.regs[target_reg as usize] = result,
FrameContinuation::AccessorSet => {}
}
}
Ok(None)
} else {
Ok(Some(result))
}
}
pub(crate) fn dispatch_throw(&mut self, rd: usize) -> Result<bool, String> {
oxide_kernel::vm_debug!("THROW pc={}", self.pc);
let exc_value = self.regs[rd];
self.exception_value = Some(exc_value);
self.pending_error_kind = Some(self.thrown_error_kind(exc_value));
self.unwind().map(|_| true)
}
pub(crate) fn dispatch_try_begin(&mut self, instr: u32) {
oxide_kernel::vm_trace!("TRY_BEGIN frame_depth={}", self.frames.len());
let offset = opcode::offset16(instr) as isize;
let catch_pc = if offset == 0 {
None
} else {
Some(((self.pc as isize) + offset - 1) as usize)
};
self.try_stack.push(TryHandler {
catch_pc,
finally_pc: None,
frame_depth: self.frames.len(),
});
}
pub(crate) fn dispatch_try_end(&mut self) {
self.try_stack.pop();
}
pub(crate) fn dispatch_try_finally_begin(&mut self, instr: u32) {
let offset = opcode::offset16(instr) as isize;
let finally_pc = ((self.pc as isize) + offset - 1) as usize;
self.try_stack.push(TryHandler {
catch_pc: None,
finally_pc: Some(finally_pc),
frame_depth: self.frames.len(),
});
}
pub(crate) fn dispatch_try_finally_end(&mut self) -> Result<bool, String> {
self.try_stack.pop();
if self.pending_exception.is_some() && self.exception_value.is_none() {
self.exception_value = self.pending_exception.take();
self.unwind().map(|_| true)
} else {
self.pending_exception = None;
Ok(false)
}
}
}