Files
OxideJS-New/crates/oxide_vm/src/dispatch/member.rs
T

302 lines
12 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 oxide_bytecode::opcode::OpCode;
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(
"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),
),
OpCode::COMPOUND_MEMBER_BIT_AND => self.dispatch_compound_member_bitwise(
"COMPOUND_MEMBER_BIT_AND",
rd,
a,
b,
"COMPOUND_MEMBER_BIT_AND on non-object",
|l, r| l & r,
),
OpCode::COMPOUND_MEMBER_BIT_OR => self.dispatch_compound_member_bitwise(
"COMPOUND_MEMBER_BIT_OR",
rd,
a,
b,
"COMPOUND_MEMBER_BIT_OR on non-object",
|l, r| l | r,
),
OpCode::COMPOUND_MEMBER_BIT_XOR => self.dispatch_compound_member_bitwise(
"COMPOUND_MEMBER_BIT_XOR",
rd,
a,
b,
"COMPOUND_MEMBER_BIT_XOR on non-object",
|l, r| l ^ r,
),
OpCode::COMPOUND_MEMBER_SHL => self.dispatch_compound_member_shift(
"COMPOUND_MEMBER_SHL",
rd,
a,
b,
"COMPOUND_MEMBER_SHL on non-object",
false,
false,
),
OpCode::COMPOUND_MEMBER_SHR => self.dispatch_compound_member_shift(
"COMPOUND_MEMBER_SHR",
rd,
a,
b,
"COMPOUND_MEMBER_SHR on non-object",
true,
false,
),
OpCode::COMPOUND_MEMBER_USHR => self.dispatch_compound_member_shift(
"COMPOUND_MEMBER_USHR",
rd,
a,
b,
"COMPOUND_MEMBER_USHR on non-object",
true,
true,
),
_ => 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());
};
let prop_name_si = self.property_key_si(self.regs[b])?;
Ok((obj_ptr, prop_name_si))
}
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(());
};
let receiver = self.regs[rd];
let obj = unsafe { &mut *obj_ptr };
let (prop_val, ext_pc) = self.read_member_prop(obj, prop_name_si, receiver)?;
let n = self.coerce_number_bounded(prop_val)?;
let new_val = JsValue::float(n + 1.0);
self.set_member_prop(obj, prop_name_si, new_val, receiver, ext_pc)?;
self.regs[a] = new_val;
Ok(())
}
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(());
};
let receiver = self.regs[rd];
let obj = unsafe { &mut *obj_ptr };
let (prop_val, ext_pc) = self.read_member_prop(obj, prop_name_si, receiver)?;
let n = self.coerce_number_bounded(prop_val)?;
let new_val = JsValue::float(n - 1.0);
self.set_member_prop(obj, prop_name_si, new_val, receiver, ext_pc)?;
self.regs[a] = new_val;
Ok(())
}
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(());
};
let receiver = self.regs[rd];
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[a])?;
let prop_val = self.ordinary_get(obj, prop_name_si, receiver)?;
let n = self.coerce_number_bounded(prop_val)?;
let new_val = JsValue::float(n + 1.0);
self.ordinary_set(obj, prop_name_si, new_val, receiver, self.current_strict())?;
self.regs[b] = new_val;
Ok(())
}
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(());
};
let receiver = self.regs[rd];
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[a])?;
let prop_val = self.ordinary_get(obj, prop_name_si, receiver)?;
let n = self.coerce_number_bounded(prop_val)?;
let new_val = JsValue::float(n - 1.0);
self.ordinary_set(obj, prop_name_si, new_val, receiver, self.current_strict())?;
self.regs[b] = new_val;
Ok(())
}
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(());
};
let receiver = self.regs[rd];
let (prop_raw, ext_pc) = {
let obj = unsafe { &mut *obj_ptr };
self.read_member_prop(obj, prop_name_si, receiver)?
};
let prop_val = self.coerce_primitive_bounded(prop_raw, false)?;
let rhs = self.coerce_primitive_bounded(self.regs[a], false)?;
if prop_val.is_symbol() || rhs.is_symbol() {
// 复合成员加法分流同 `+`:字符串分支走 ToString、数值分支走 ToNumber,
// 两者对 Symbol 均抛 TypeError(ECMA-262 §7.1.4 / §7.1.17)。
let msg = if prop_val.is_string() || rhs.is_string() {
"Cannot convert a Symbol value to a string"
} else {
"Cannot convert a Symbol value to a number"
};
return self.raise_type_error(msg);
}
let new_val = if prop_val.is_string() || rhs.is_string() {
let ls = coercion::to_string(prop_val);
let rs = coercion::to_string(rhs);
self.new_string(&format!("{ls}{rs}"))
} else {
let ln = coercion::to_number(prop_val);
let rn = coercion::to_number(rhs);
JsValue::float(ln + rn)
};
let obj = unsafe { &mut *obj_ptr };
self.set_member_prop(obj, prop_name_si, new_val, receiver, ext_pc)?;
self.regs[a] = new_val;
Ok(())
}
fn dispatch_compound_member_numeric<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(());
};
let receiver = self.regs[rd];
let (ln, ext_pc) = {
let obj = unsafe { &mut *obj_ptr };
let (prop_val, ext_pc) = self.read_member_prop(obj, prop_name_si, receiver)?;
(self.coerce_number_bounded(prop_val)?, ext_pc)
};
let rn = self.coerce_number_bounded(self.regs[a])?;
let new_val = JsValue::float(op(ln, rn));
self.regs[a] = new_val;
let obj = unsafe { &mut *obj_ptr };
self.set_member_prop(obj, prop_name_si, new_val, receiver, ext_pc)?;
Ok(())
}
fn dispatch_compound_member_bitwise<F>(
&mut self, op_name: &str, rd: usize, a: usize, b: usize, error_msg: &str, op: F,
) -> Result<(), String>
where
F: FnOnce(i32, i32) -> i32,
{
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(());
};
let receiver = self.regs[rd];
let (ln, ext_pc) = {
let obj = unsafe { &mut *obj_ptr };
let (prop_val, ext_pc) = self.read_member_prop(obj, prop_name_si, receiver)?;
(self.coerce_int32_bounded(prop_val)?, ext_pc)
};
let rn = self.coerce_int32_bounded(self.regs[a])?;
let new_val = JsValue::int(op(ln, rn));
self.regs[a] = new_val;
let obj = unsafe { &mut *obj_ptr };
self.set_member_prop(obj, prop_name_si, new_val, receiver, ext_pc)?;
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn dispatch_compound_member_shift(
&mut self, op_name: &str, rd: usize, a: usize, b: usize, error_msg: &str, is_right: bool, is_unsigned: bool,
) -> Result<(), String> {
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(());
};
let receiver = self.regs[rd];
let (ln, ext_pc) = {
let obj = unsafe { &mut *obj_ptr };
let (prop_val, ext_pc) = self.read_member_prop(obj, prop_name_si, receiver)?;
(self.coerce_int32_bounded(prop_val)?, ext_pc)
};
let shift = self.coerce_uint32_bounded(self.regs[a])? & 0x1F;
let new_val = if !is_right {
JsValue::int(ln.wrapping_shl(shift))
} else if is_unsigned {
let u = (ln as u32) >> shift;
if u <= i32::MAX as u32 {
JsValue::int(u as i32)
} else {
JsValue::float(u as f64)
}
} else {
JsValue::int(ln >> shift)
};
self.regs[a] = new_val;
let obj = unsafe { &mut *obj_ptr };
self.set_member_prop(obj, prop_name_si, new_val, receiver, ext_pc)?;
Ok(())
}
}