feat: 为 VM 添加结构化日志
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
use crate::vm::Vm;
|
||||
use crate::vm_trace;
|
||||
use oxide_runtime_api as coercion;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
impl Vm {
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_add(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("ADD rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let lhs = self.coerce_primitive_bounded(self.regs[a], false)?;
|
||||
let rhs = self.coerce_primitive_bounded(self.regs[b], false)?;
|
||||
if lhs.is_string() || rhs.is_string() {
|
||||
@@ -22,6 +24,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_neg(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("NEG rd={} r{}={:?}", rd, a, self.regs[a]);
|
||||
let v = self.coerce_number_bounded(self.regs[a])?;
|
||||
self.regs[rd] = JsValue::float(-v);
|
||||
Ok(())
|
||||
@@ -29,12 +32,14 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_unary_plus(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("UNARY_PLUS rd={} r{}={:?}", rd, a, self.regs[a]);
|
||||
let v = self.coerce_number_bounded(self.regs[a])?;
|
||||
self.regs[rd] = JsValue::float(v);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_compound_add(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("COMPOUND_ADD rd={} r{}={:?}", rd, a, self.regs[a]);
|
||||
let lhs = self.coerce_primitive_bounded(self.regs[rd], false)?;
|
||||
let rhs = self.coerce_primitive_bounded(self.regs[a], false)?;
|
||||
if lhs.is_string() || rhs.is_string() {
|
||||
@@ -52,6 +57,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_compound_sub(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("COMPOUND_SUB rd={} r{}={:?}", rd, a, self.regs[a]);
|
||||
let l = self.coerce_number_bounded(self.regs[rd])?;
|
||||
let r = self.coerce_number_bounded(self.regs[a])?;
|
||||
self.regs[rd] = JsValue::float(l - r);
|
||||
@@ -60,6 +66,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_compound_mul(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("COMPOUND_MUL rd={} r{}={:?}", rd, a, self.regs[a]);
|
||||
let l = self.coerce_number_bounded(self.regs[rd])?;
|
||||
let r = self.coerce_number_bounded(self.regs[a])?;
|
||||
self.regs[rd] = JsValue::float(l * r);
|
||||
@@ -68,6 +75,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_compound_div(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("COMPOUND_DIV rd={} r{}={:?}", rd, a, self.regs[a]);
|
||||
let l = self.coerce_number_bounded(self.regs[rd])?;
|
||||
let r = self.coerce_number_bounded(self.regs[a])?;
|
||||
self.regs[rd] = JsValue::float(l / r);
|
||||
@@ -76,6 +84,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_compound_mod(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("COMPOUND_MOD rd={} r{}={:?}", rd, a, self.regs[a]);
|
||||
let l = self.coerce_number_bounded(self.regs[rd])?;
|
||||
let r = self.coerce_number_bounded(self.regs[a])?;
|
||||
self.regs[rd] = JsValue::float(l % r);
|
||||
@@ -84,6 +93,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_compound_exp(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("COMPOUND_EXP rd={} r{}={:?}", rd, a, self.regs[a]);
|
||||
let l = self.coerce_number_bounded(self.regs[rd])?;
|
||||
let r = self.coerce_number_bounded(self.regs[a])?;
|
||||
self.regs[rd] = JsValue::float(l.powf(r));
|
||||
@@ -92,6 +102,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_inc_pre(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("INC_PRE rd={} a={}", rd, a);
|
||||
let n = self.coerce_number_bounded(self.regs[rd])?;
|
||||
let result = JsValue::float(n + 1.0);
|
||||
self.regs[rd] = result;
|
||||
@@ -101,6 +112,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_inc_post(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("INC_POST rd={} a={}", rd, a);
|
||||
let n = self.coerce_number_bounded(self.regs[rd])?;
|
||||
self.regs[a] = JsValue::float(n);
|
||||
self.regs[rd] = JsValue::float(n + 1.0);
|
||||
@@ -109,6 +121,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_dec_pre(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("DEC_PRE rd={} a={}", rd, a);
|
||||
let n = self.coerce_number_bounded(self.regs[rd])?;
|
||||
let result = JsValue::float(n - 1.0);
|
||||
self.regs[rd] = result;
|
||||
@@ -118,6 +131,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_dec_post(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("DEC_POST rd={} a={}", rd, a);
|
||||
let n = self.coerce_number_bounded(self.regs[rd])?;
|
||||
self.regs[a] = JsValue::float(n);
|
||||
self.regs[rd] = JsValue::float(n - 1.0);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::vm::Vm;
|
||||
use crate::vm_trace;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
impl Vm {
|
||||
@@ -13,6 +14,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_bit_and(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("BIT_AND rd={} r{},r{}", rd, a, b);
|
||||
self.regs[rd] =
|
||||
JsValue::int(self.coerce_int32_bounded(self.regs[a])? & self.coerce_int32_bounded(self.regs[b])?);
|
||||
Ok(())
|
||||
@@ -20,6 +22,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_bit_or(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("BIT_OR rd={} r{},r{}", rd, a, b);
|
||||
self.regs[rd] =
|
||||
JsValue::int(self.coerce_int32_bounded(self.regs[a])? | self.coerce_int32_bounded(self.regs[b])?);
|
||||
Ok(())
|
||||
@@ -27,6 +30,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_bit_xor(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("BIT_XOR rd={} r{},r{}", rd, a, b);
|
||||
self.regs[rd] =
|
||||
JsValue::int(self.coerce_int32_bounded(self.regs[a])? ^ self.coerce_int32_bounded(self.regs[b])?);
|
||||
Ok(())
|
||||
@@ -34,6 +38,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_shl(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("SHL rd={} r{},r{}", rd, a, b);
|
||||
let lhs = self.coerce_int32_bounded(self.regs[a])?;
|
||||
let shift = self.coerce_uint32_bounded(self.regs[b])? & 0x1F;
|
||||
self.regs[rd] = JsValue::int(lhs.wrapping_shl(shift));
|
||||
@@ -42,6 +47,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_shr(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("SHR rd={} r{},r{}", rd, a, b);
|
||||
let lhs = self.coerce_int32_bounded(self.regs[a])?;
|
||||
let shift = self.coerce_uint32_bounded(self.regs[b])? & 0x1F;
|
||||
self.regs[rd] = JsValue::int(lhs >> shift);
|
||||
@@ -50,6 +56,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_ushr(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("USHR rd={} r{},r{}", rd, a, b);
|
||||
let lhs = self.coerce_uint32_bounded(self.regs[a])?;
|
||||
let shift = self.coerce_uint32_bounded(self.regs[b])? & 0x1F;
|
||||
self.write_ushr_result(rd, lhs >> shift);
|
||||
@@ -58,12 +65,14 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_bit_not(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("BIT_NOT rd={} r{}", rd, a);
|
||||
self.regs[rd] = JsValue::int(!self.coerce_int32_bounded(self.regs[a])?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_compound_bit_and(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("COMPOUND_BIT_AND rd={} r{}", rd, a);
|
||||
self.regs[rd] =
|
||||
JsValue::int(self.coerce_int32_bounded(self.regs[rd])? & self.coerce_int32_bounded(self.regs[a])?);
|
||||
Ok(())
|
||||
@@ -71,6 +80,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_compound_bit_or(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("COMPOUND_BIT_OR rd={} r{}", rd, a);
|
||||
self.regs[rd] =
|
||||
JsValue::int(self.coerce_int32_bounded(self.regs[rd])? | self.coerce_int32_bounded(self.regs[a])?);
|
||||
Ok(())
|
||||
@@ -78,6 +88,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_compound_bit_xor(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("COMPOUND_BIT_XOR rd={} r{}", rd, a);
|
||||
self.regs[rd] =
|
||||
JsValue::int(self.coerce_int32_bounded(self.regs[rd])? ^ self.coerce_int32_bounded(self.regs[a])?);
|
||||
Ok(())
|
||||
@@ -85,6 +96,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_compound_shl(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("COMPOUND_SHL rd={} r{}", rd, a);
|
||||
let lhs = self.coerce_int32_bounded(self.regs[rd])?;
|
||||
let shift = self.coerce_uint32_bounded(self.regs[a])? & 0x1F;
|
||||
self.regs[rd] = JsValue::int(lhs.wrapping_shl(shift));
|
||||
@@ -93,6 +105,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_compound_shr(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("COMPOUND_SHR rd={} r{}", rd, a);
|
||||
let lhs = self.coerce_int32_bounded(self.regs[rd])?;
|
||||
let shift = self.coerce_uint32_bounded(self.regs[a])? & 0x1F;
|
||||
self.regs[rd] = JsValue::int(lhs >> shift);
|
||||
@@ -101,6 +114,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_compound_ushr(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("COMPOUND_USHR rd={} r{}", rd, a);
|
||||
let lhs = self.coerce_uint32_bounded(self.regs[rd])?;
|
||||
let shift = self.coerce_uint32_bounded(self.regs[a])? & 0x1F;
|
||||
self.write_ushr_result(rd, lhs >> shift);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::native::NativeFn;
|
||||
use crate::vm::{native_fn_ptr_to_fn, CallFrame, FrameContinuation, Vm};
|
||||
use crate::{vm_debug, vm_trace};
|
||||
use oxide_builtins::{builtins_debug, builtins_trace};
|
||||
use oxide_bytecode::opcode;
|
||||
use oxide_runtime_api::NativeResult;
|
||||
@@ -79,6 +80,7 @@ impl Vm {
|
||||
impl Vm {
|
||||
pub(crate) fn dispatch_create_closure(&mut self, rd: usize, instr: u32) {
|
||||
let sub_idx = opcode::imm16(instr) as u32;
|
||||
vm_trace!("CREATE_CLOSURE rd={} sub_idx={}", rd, sub_idx);
|
||||
debug_assert!(sub_idx > 0 && (sub_idx as usize) <= self.sub_modules.len());
|
||||
let sub = &self.sub_modules[sub_idx as usize - 1];
|
||||
let result = self.create_function_object(
|
||||
@@ -92,6 +94,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_create_regexp(&mut self, rd: usize, a: usize, b: usize) -> Result<Option<JsValue>, String> {
|
||||
vm_trace!("CREATE_REGEXP rd={}", rd);
|
||||
let pat_val = self.regs[a];
|
||||
let flags_val = self.regs[b];
|
||||
let ctor_ptr = self.session.builtin_world().regexp_constructor.as_ptr() as *mut JsObject;
|
||||
@@ -124,6 +127,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_super_call(&mut self, rd: usize, a: usize) -> Result<bool, String> {
|
||||
vm_debug!("SUPER_CALL depth={}", self.frames.len());
|
||||
let first_arg_reg = a as u8;
|
||||
let ext = self.bytecode[self.pc];
|
||||
self.pc += 1;
|
||||
@@ -266,6 +270,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_super_get_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<bool, String> {
|
||||
vm_trace!("SUPER_GET_PROP rd={} a={} b={}", rd, a, b);
|
||||
let key_val = self.regs[b];
|
||||
let prop_name_si = self.property_key_si(key_val);
|
||||
let Some(frame) = self.frames.last() else {
|
||||
@@ -297,6 +302,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_set_home_object(&mut self, rd: usize, a: usize) -> Result<bool, String> {
|
||||
vm_trace!("SET_HOME_OBJECT rd={} a={}", rd, a);
|
||||
let func_val = self.regs[rd];
|
||||
let home_val = self.regs[a];
|
||||
if !func_val.is_object() || !home_val.is_object() {
|
||||
|
||||
@@ -1,52 +1,85 @@
|
||||
use crate::vm::Vm;
|
||||
use crate::vm_trace;
|
||||
use oxide_runtime_api as coercion;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
impl Vm {
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_eq(&mut self, rd: usize, a: usize, b: usize) {
|
||||
vm_trace!("EQ rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let eq = coercion::abstract_eq(self.regs[a], self.regs[b]);
|
||||
self.regs[rd] = JsValue::bool(eq);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_neq(&mut self, rd: usize, a: usize, b: usize) {
|
||||
vm_trace!("NEQ rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let ne = !coercion::abstract_eq(self.regs[a], self.regs[b]);
|
||||
self.regs[rd] = JsValue::bool(ne);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_lt(&mut self, rd: usize, a: usize, b: usize) {
|
||||
vm_trace!("LT rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let rel = coercion::relational_compare(self.regs[a], self.regs[b]);
|
||||
self.regs[rd] = JsValue::bool(rel.unwrap_or(false));
|
||||
match rel {
|
||||
Some(r) => self.regs[rd] = JsValue::bool(r),
|
||||
None => {
|
||||
vm_trace!("LT incomparable r{}={:?} r{}={:?}", a, self.regs[a], b, self.regs[b]);
|
||||
self.regs[rd] = JsValue::bool(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_gt(&mut self, rd: usize, a: usize, b: usize) {
|
||||
vm_trace!("GT rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let rel = coercion::relational_compare(self.regs[b], self.regs[a]);
|
||||
self.regs[rd] = JsValue::bool(rel.unwrap_or(false));
|
||||
match rel {
|
||||
Some(r) => self.regs[rd] = JsValue::bool(r),
|
||||
None => {
|
||||
vm_trace!("GT incomparable r{}={:?} r{}={:?}", a, self.regs[a], b, self.regs[b]);
|
||||
self.regs[rd] = JsValue::bool(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_lte(&mut self, rd: usize, a: usize, b: usize) {
|
||||
vm_trace!("LTE rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let rel = coercion::relational_compare(self.regs[b], self.regs[a]);
|
||||
self.regs[rd] = JsValue::bool(!rel.unwrap_or(true));
|
||||
match rel {
|
||||
Some(r) => self.regs[rd] = JsValue::bool(!r),
|
||||
None => {
|
||||
vm_trace!("LTE incomparable r{}={:?} r{}={:?}", a, self.regs[a], b, self.regs[b]);
|
||||
self.regs[rd] = JsValue::bool(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_gte(&mut self, rd: usize, a: usize, b: usize) {
|
||||
vm_trace!("GTE rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let rel = coercion::relational_compare(self.regs[a], self.regs[b]);
|
||||
self.regs[rd] = JsValue::bool(!rel.unwrap_or(true));
|
||||
match rel {
|
||||
Some(r) => self.regs[rd] = JsValue::bool(!r),
|
||||
None => {
|
||||
vm_trace!("GTE incomparable r{}={:?} r{}={:?}", a, self.regs[a], b, self.regs[b]);
|
||||
self.regs[rd] = JsValue::bool(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_strict_eq(&mut self, rd: usize, a: usize, b: usize) {
|
||||
vm_trace!("STRICT_EQ rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let eq = coercion::strict_equality(self.regs[a], self.regs[b]);
|
||||
self.regs[rd] = JsValue::bool(eq);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_strict_neq(&mut self, rd: usize, a: usize, b: usize) {
|
||||
vm_trace!("STRICT_NEQ rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let ne = !coercion::strict_equality(self.regs[a], self.regs[b]);
|
||||
self.regs[rd] = JsValue::bool(ne);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::vm::Vm;
|
||||
use crate::vm_trace;
|
||||
use oxide_bytecode::opcode::{self, Instr};
|
||||
use oxide_runtime_api as coercion;
|
||||
use oxide_types::value::JsValue;
|
||||
@@ -7,11 +8,13 @@ impl Vm {
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_jmp(&mut self, instr: Instr) {
|
||||
let offset = opcode::offset16(instr) as isize;
|
||||
vm_trace!("JMP offset={}", offset);
|
||||
self.pc = ((self.pc as isize) + offset - 1) as usize;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_jmp_if_false(&mut self, rd: usize, instr: Instr) {
|
||||
vm_trace!("JMP_IF_FALSE r{}={:?}", rd, self.regs[rd]);
|
||||
let cond = coercion::to_boolean(self.regs[rd]);
|
||||
if !cond {
|
||||
let offset = opcode::offset16(instr) as isize;
|
||||
@@ -21,6 +24,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_jmp_if_true(&mut self, rd: usize, instr: Instr) {
|
||||
vm_trace!("JMP_IF_TRUE r{}={:?}", rd, self.regs[rd]);
|
||||
let cond = coercion::to_boolean(self.regs[rd]);
|
||||
if cond {
|
||||
let offset = opcode::offset16(instr) as isize;
|
||||
@@ -30,6 +34,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_jmp_if_nullish(&mut self, rd: usize, instr: Instr) {
|
||||
vm_trace!("JMP_IF_NULLISH r{}={:?}", rd, self.regs[rd]);
|
||||
if self.regs[rd].is_nullish() {
|
||||
let offset = opcode::offset16(instr) as isize;
|
||||
self.pc = ((self.pc as isize) + offset - 1) as usize;
|
||||
@@ -38,6 +43,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_and(&mut self, rd: usize, a: usize, b: usize) {
|
||||
vm_trace!("LOGICAL_AND rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let val = self.regs[a];
|
||||
if coercion::to_boolean(val) {
|
||||
self.regs[rd] = self.regs[b];
|
||||
@@ -48,6 +54,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_or(&mut self, rd: usize, a: usize, b: usize) {
|
||||
vm_trace!("LOGICAL_OR rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let val = self.regs[a];
|
||||
if coercion::to_boolean(val) {
|
||||
self.regs[rd] = val;
|
||||
@@ -58,12 +65,14 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_nullish(&mut self, rd: usize, a: usize, b: usize) {
|
||||
vm_trace!("NULLISH rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let val = self.regs[a];
|
||||
self.regs[rd] = if val.is_nullish() { self.regs[b] } else { val };
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_not(&mut self, rd: usize, a: usize) {
|
||||
vm_trace!("NOT rd={} r{}={:?}", rd, a, self.regs[a]);
|
||||
let b = !coercion::to_boolean(self.regs[a]);
|
||||
self.regs[rd] = JsValue::bool(b);
|
||||
}
|
||||
|
||||
@@ -3,36 +3,64 @@ use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use crate::vm::Vm;
|
||||
use crate::vm_trace;
|
||||
use oxide_runtime_api as coercion;
|
||||
|
||||
impl Vm {
|
||||
pub(crate) fn dispatch_member_op(&mut self, op: OpCode, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("MEMBER_OP {:?} rd={} a={} b={}", op, rd, a, b);
|
||||
match op {
|
||||
OpCode::MEMBER_INC => self.dispatch_member_inc(rd, a, b),
|
||||
OpCode::MEMBER_DEC => self.dispatch_member_dec(rd, a, b),
|
||||
OpCode::DYN_MEMBER_INC => self.dispatch_dyn_member_inc(rd, a, b),
|
||||
OpCode::DYN_MEMBER_DEC => self.dispatch_dyn_member_dec(rd, a, b),
|
||||
OpCode::COMPOUND_MEMBER_ADD => self.dispatch_compound_member_add(rd, a, b),
|
||||
OpCode::COMPOUND_MEMBER_SUB => {
|
||||
self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_SUB on non-object", |l, r| l - r)
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_MUL => {
|
||||
self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_MUL on non-object", |l, r| l * r)
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_DIV => {
|
||||
self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_DIV on non-object", |l, r| l / r)
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_MOD => {
|
||||
self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_MOD on non-object", |l, r| l % r)
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_EXP => {
|
||||
self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_EXP on non-object", |l, r| l.powf(r))
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_SUB => self.dispatch_compound_member_numeric(
|
||||
"COMPOUND_MEMBER_SUB",
|
||||
rd,
|
||||
a,
|
||||
b,
|
||||
"COMPOUND_MEMBER_SUB on non-object",
|
||||
|l, r| l - r,
|
||||
),
|
||||
OpCode::COMPOUND_MEMBER_MUL => self.dispatch_compound_member_numeric(
|
||||
"COMPOUND_MEMBER_MUL",
|
||||
rd,
|
||||
a,
|
||||
b,
|
||||
"COMPOUND_MEMBER_MUL on non-object",
|
||||
|l, r| l * r,
|
||||
),
|
||||
OpCode::COMPOUND_MEMBER_DIV => self.dispatch_compound_member_numeric(
|
||||
"COMPOUND_MEMBER_DIV",
|
||||
rd,
|
||||
a,
|
||||
b,
|
||||
"COMPOUND_MEMBER_DIV on non-object",
|
||||
|l, r| l / r,
|
||||
),
|
||||
OpCode::COMPOUND_MEMBER_MOD => self.dispatch_compound_member_numeric(
|
||||
"COMPOUND_MEMBER_MOD",
|
||||
rd,
|
||||
a,
|
||||
b,
|
||||
"COMPOUND_MEMBER_MOD on non-object",
|
||||
|l, r| l % r,
|
||||
),
|
||||
OpCode::COMPOUND_MEMBER_EXP => self.dispatch_compound_member_numeric(
|
||||
"COMPOUND_MEMBER_EXP",
|
||||
rd,
|
||||
a,
|
||||
b,
|
||||
"COMPOUND_MEMBER_EXP on non-object",
|
||||
|l, r| l.powf(r),
|
||||
),
|
||||
_ => unreachable!("non-member opcode passed to dispatch_member_op"),
|
||||
}
|
||||
}
|
||||
|
||||
fn member_target(&mut self, rd: usize, b: usize, error_msg: &str) -> Result<(*mut JsObject, u32), String> {
|
||||
vm_trace!("MEMBER_TARGET rd={} b={}", rd, b);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], error_msg)? else {
|
||||
return Err(String::new());
|
||||
};
|
||||
@@ -41,6 +69,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_member_inc(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("MEMBER_INC rd={} a={} b={}", rd, a, b);
|
||||
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "MEMBER_INC on non-object") else {
|
||||
return Ok(());
|
||||
};
|
||||
@@ -55,6 +84,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_member_dec(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("MEMBER_DEC rd={} a={} b={}", rd, a, b);
|
||||
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "MEMBER_DEC on non-object") else {
|
||||
return Ok(());
|
||||
};
|
||||
@@ -69,6 +99,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_dyn_member_inc(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("DYN_MEMBER_INC rd={} a={} b={}", rd, a, b);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "DYN_MEMBER_INC on non-object")? else {
|
||||
return Ok(());
|
||||
};
|
||||
@@ -84,6 +115,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_dyn_member_dec(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("DYN_MEMBER_DEC rd={} a={} b={}", rd, a, b);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "DYN_MEMBER_DEC on non-object")? else {
|
||||
return Ok(());
|
||||
};
|
||||
@@ -99,6 +131,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_compound_member_add(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("COMPOUND_MEMBER_ADD rd={} a={} b={}", rd, a, b);
|
||||
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "COMPOUND_MEMBER_ADD on non-object") else {
|
||||
return Ok(());
|
||||
};
|
||||
@@ -125,11 +158,12 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_compound_member_numeric<F>(
|
||||
&mut self, rd: usize, a: usize, b: usize, error_msg: &str, op: F,
|
||||
&mut self, op_name: &str, rd: usize, a: usize, b: usize, error_msg: &str, op: F,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
F: FnOnce(f64, f64) -> f64,
|
||||
{
|
||||
vm_trace!("{} rd={} a={} b={}", op_name, rd, a, b);
|
||||
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, error_msg) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
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;
|
||||
@@ -8,17 +9,20 @@ 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"
|
||||
@@ -46,6 +50,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -58,6 +63,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
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: check if already initialized
|
||||
if !self.regs[rd].is_undefined() {
|
||||
@@ -71,6 +77,7 @@ impl Vm {
|
||||
|
||||
#[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
|
||||
@@ -80,8 +87,9 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_new_array(&mut self, rd: usize, instr: u32) {
|
||||
let proto_ptr = self.session.builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
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);
|
||||
@@ -89,6 +97,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_void(&mut self, rd: usize) {
|
||||
vm_trace!("VOID rd={}", rd);
|
||||
self.regs[rd] = JsValue::undefined();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{ic_debug, ic_trace};
|
||||
use crate::{ic_debug, ic_trace, vm_trace};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_kernel::prop_forge::PropTemplate;
|
||||
use oxide_types::object::JsObject;
|
||||
@@ -13,6 +13,7 @@ fn prop_cache_miss() {}
|
||||
|
||||
impl Vm {
|
||||
pub(crate) fn dispatch_property_op(&mut self, op: OpCode, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("PROPERTY_OP {:?} rd={} a={} b={}", op, rd, a, b);
|
||||
match op {
|
||||
OpCode::IC_GET_PROP => self.dispatch_ic_get_prop(a, b),
|
||||
OpCode::IC_SET_PROP => self.dispatch_ic_set_prop(rd, a, b),
|
||||
@@ -88,6 +89,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_get_private(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("GET_PRIVATE rd={} a={} b={}", rd, a, b);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[a], "private field access on non-object")? else {
|
||||
return Ok(());
|
||||
};
|
||||
@@ -101,6 +103,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_set_private(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("SET_PRIVATE rd={} a={} b={}", rd, a, b);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "private field assignment on non-object")? else {
|
||||
return Ok(());
|
||||
};
|
||||
@@ -116,6 +119,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_init_private(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("INIT_PRIVATE rd={} a={} b={}", rd, a, b);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "private field initialization on non-object")?
|
||||
else {
|
||||
return Ok(());
|
||||
@@ -132,6 +136,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_private_brand_in(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("PRIVATE_BRAND_IN rd={} a={} b={}", rd, a, b);
|
||||
let obj_val = self.regs[a];
|
||||
if !obj_val.is_object() {
|
||||
self.regs[rd] = JsValue::bool(false);
|
||||
@@ -274,6 +279,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_get_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("GET_PROP rd={} a={} b={}", rd, a, b);
|
||||
let prop_name_si = self.property_key_si(self.regs[b]);
|
||||
if let Some(value) = self.primitive_property_get(self.regs[rd], prop_name_si)? {
|
||||
self.regs[a] = value;
|
||||
@@ -291,6 +297,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_set_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("SET_PROP rd={} a={} b={}", rd, a, b);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "Cannot create property on non-object")? else {
|
||||
return Ok(());
|
||||
};
|
||||
@@ -312,6 +319,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_get_prop_dynamic(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("GET_PROP_DYNAMIC rd={} a={} b={}", rd, a, b);
|
||||
let prop_name_si = self.property_key_si(self.regs[a]);
|
||||
if let Some(value) = self.primitive_property_get(self.regs[rd], prop_name_si)? {
|
||||
self.regs[b] = value;
|
||||
@@ -329,6 +337,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_set_prop_dynamic(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("SET_PROP_DYNAMIC rd={} a={} b={}", rd, a, b);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "Cannot create property on non-object")? else {
|
||||
return Ok(());
|
||||
};
|
||||
@@ -350,6 +359,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_set_elem(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("SET_ELEM rd={} a={} b={}", rd, a, b);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "Cannot create property on non-object")? else {
|
||||
return Ok(());
|
||||
};
|
||||
@@ -369,6 +379,7 @@ impl Vm {
|
||||
|
||||
impl Vm {
|
||||
pub(crate) fn dispatch_delete_prop_static(&mut self, rd: usize) -> Result<bool, String> {
|
||||
vm_trace!("DELETE_PROP_STATIC rd={}", rd);
|
||||
let prop_idx = self.bytecode[self.pc] as usize;
|
||||
self.pc += 1;
|
||||
let key_val = self.immutables().get(prop_idx).copied().unwrap_or_else(JsValue::undefined);
|
||||
@@ -385,6 +396,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_delete_prop_dynamic(&mut self, rd: usize, b: usize) -> Result<bool, String> {
|
||||
vm_trace!("DELETE_PROP_DYNAMIC rd={}", rd);
|
||||
let prop_name_si = self.property_key_si(self.regs[b]);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "delete on non-object")? else {
|
||||
return Ok(true);
|
||||
|
||||
Reference in New Issue
Block a user