mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
417 lines
18 KiB
Rust
417 lines
18 KiB
Rust
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);
|
||
// 代理接收者统一走全路径:ordinary_get/ordinary_set 已带代理守卫,
|
||
// 命中路径直读代理自身属性槽会绕过 get trap(member 读侧无入口守卫)。
|
||
let receiver = self.regs[rd];
|
||
if receiver.is_object() {
|
||
let obj_ptr = receiver.as_js_object_ptr();
|
||
if !obj_ptr.is_null() && unsafe { (*obj_ptr).is_proxy_obj() } {
|
||
return self.dispatch_member_op_full(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))
|
||
}
|
||
|
||
/// 代理接收者全路径读改写:ordinary_get/ordinary_set 已带代理守卫,
|
||
/// 经 get/set trap 路由,不接触 IC 命中路径。
|
||
fn dispatch_member_op_full(&mut self, op: OpCode, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||
vm_trace!("MEMBER_OP_FULL {:?} rd={} a={} b={}", op, rd, a, b);
|
||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "MEMBER_OP on non-object")? else {
|
||
return Ok(());
|
||
};
|
||
let receiver = self.regs[rd];
|
||
// 静态操作键在 regs[b]、结果在 regs[a];动态操作键在 regs[a]、结果在 regs[b]。
|
||
let (key_reg, result_reg) = match op {
|
||
OpCode::DYN_MEMBER_INC | OpCode::DYN_MEMBER_DEC => (a, b),
|
||
_ => (b, a),
|
||
};
|
||
let prop_name_si = self.property_key_si(self.regs[key_reg])?;
|
||
// 静态操作带 IC 扩展字:代理分支不写回 IC(槽恒零),先消费扩展字,
|
||
// 否则派发循环会错位把扩展字当指令。
|
||
if op.has_ic_ext_words() {
|
||
let _ = crate::ic_helper::read_ic_slot0(&self.bytecode, &mut self.pc);
|
||
}
|
||
let obj = unsafe { &mut *obj_ptr };
|
||
let prop_val = self.ordinary_get(obj, prop_name_si, receiver)?;
|
||
let new_val = self.member_op_new_value(op, prop_val, self.regs[a])?;
|
||
self.ordinary_set(obj, prop_name_si, new_val, receiver, self.current_strict())?;
|
||
self.regs[result_reg] = new_val;
|
||
Ok(())
|
||
}
|
||
|
||
/// 代理接收者全路径的新值计算:强制转换口径与既有 IC 路径各分臂一致。
|
||
fn member_op_new_value(&mut self, op: OpCode, prop_val: JsValue, rhs: JsValue) -> Result<JsValue, String> {
|
||
match op {
|
||
OpCode::MEMBER_INC | OpCode::DYN_MEMBER_INC => {
|
||
let n = self.coerce_number_bounded(prop_val)?;
|
||
Ok(JsValue::float(n + 1.0))
|
||
}
|
||
OpCode::MEMBER_DEC | OpCode::DYN_MEMBER_DEC => {
|
||
let n = self.coerce_number_bounded(prop_val)?;
|
||
Ok(JsValue::float(n - 1.0))
|
||
}
|
||
OpCode::COMPOUND_MEMBER_ADD => {
|
||
let prop_val = self.coerce_primitive_bounded(prop_val, coercion::ToPrimitiveHint::Number)?;
|
||
let rhs = self.coerce_primitive_bounded(rhs, coercion::ToPrimitiveHint::Number)?;
|
||
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"
|
||
};
|
||
self.raise_type_error(msg)?;
|
||
return Ok(JsValue::undefined());
|
||
}
|
||
if prop_val.is_string() || rhs.is_string() {
|
||
let ls = coercion::to_string(prop_val);
|
||
let rs = coercion::to_string(rhs);
|
||
Ok(self.new_string(&format!("{ls}{rs}")))
|
||
} else {
|
||
let ln = coercion::to_number(prop_val);
|
||
let rn = coercion::to_number(rhs);
|
||
Ok(JsValue::float(ln + rn))
|
||
}
|
||
}
|
||
OpCode::COMPOUND_MEMBER_SUB => self.member_op_f64(prop_val, rhs, |l, r| l - r),
|
||
OpCode::COMPOUND_MEMBER_MUL => self.member_op_f64(prop_val, rhs, |l, r| l * r),
|
||
OpCode::COMPOUND_MEMBER_DIV => self.member_op_f64(prop_val, rhs, |l, r| l / r),
|
||
OpCode::COMPOUND_MEMBER_MOD => self.member_op_f64(prop_val, rhs, |l, r| l % r),
|
||
OpCode::COMPOUND_MEMBER_EXP => self.member_op_f64(prop_val, rhs, |l, r| l.powf(r)),
|
||
OpCode::COMPOUND_MEMBER_BIT_AND => self.member_op_i32(prop_val, rhs, |l, r| l & r),
|
||
OpCode::COMPOUND_MEMBER_BIT_OR => self.member_op_i32(prop_val, rhs, |l, r| l | r),
|
||
OpCode::COMPOUND_MEMBER_BIT_XOR => self.member_op_i32(prop_val, rhs, |l, r| l ^ r),
|
||
OpCode::COMPOUND_MEMBER_SHL => self.member_op_shift(prop_val, rhs, false, false),
|
||
OpCode::COMPOUND_MEMBER_SHR => self.member_op_shift(prop_val, rhs, true, false),
|
||
OpCode::COMPOUND_MEMBER_USHR => self.member_op_shift(prop_val, rhs, true, true),
|
||
_ => unreachable!("non-member opcode passed to member_op_new_value"),
|
||
}
|
||
}
|
||
|
||
fn member_op_f64(&mut self, prop_val: JsValue, rhs: JsValue, op: impl FnOnce(f64, f64) -> f64) -> Result<JsValue, String> {
|
||
let ln = self.coerce_number_bounded(prop_val)?;
|
||
let rn = self.coerce_number_bounded(rhs)?;
|
||
Ok(JsValue::float(op(ln, rn)))
|
||
}
|
||
|
||
fn member_op_i32(&mut self, prop_val: JsValue, rhs: JsValue, op: impl FnOnce(i32, i32) -> i32) -> Result<JsValue, String> {
|
||
let ln = self.coerce_int32_bounded(prop_val)?;
|
||
let rn = self.coerce_int32_bounded(rhs)?;
|
||
Ok(JsValue::int(op(ln, rn)))
|
||
}
|
||
|
||
fn member_op_shift(&mut self, prop_val: JsValue, rhs: JsValue, is_right: bool, is_unsigned: bool) -> Result<JsValue, String> {
|
||
let ln = self.coerce_int32_bounded(prop_val)?;
|
||
let shift = self.coerce_uint32_bounded(rhs)? & 0x1F;
|
||
Ok(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)
|
||
})
|
||
}
|
||
|
||
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, coercion::ToPrimitiveHint::Number)?;
|
||
let rhs = self.coerce_primitive_bounded(self.regs[a], coercion::ToPrimitiveHint::Number)?;
|
||
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(())
|
||
}
|
||
}
|