Files
OxideJS-New/crates/oxide_vm/src/dispatch/member.rs
T
pjh123 5611d90ad0 feat: with 语句、成员位/移位复合赋值、CALL_NATIVE 无目标修复
with 语句:新增 emit 支持,with 体内自由标识符动态解析(对象属性优先,
回退外层),支持读/写/复合赋值/自增自减/delete/var 初始化动态化,
TO_OBJECT opcode 处理原始值对象,顶层 this 绑定全局对象。

复合赋值:新增 6 个 COMPOUND_MEMBER_BIT_* opcode 支持成员位/移位复合赋值,
修复 computed member 复合赋值覆盖 bug,with 内标识符复合赋值动态化。

CALL_NATIVE:实现全局 isNaN/isFinite 并绑定,用户覆盖 builtin 名时
回退通用调用路径而非报错,局部遮蔽 builtin 名走普通 CALL。

test262: with 34 pass(基线 0),compound-assignment 320 pass(基线 223)
2026-08-09 07:22:46 +08:00

291 lines
11 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);
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 = 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)?;
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 = 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)?;
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.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.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 = {
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)?;
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)?;
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 = {
let obj = unsafe { &mut *obj_ptr };
let prop_val = self.read_member_prop(obj, prop_name_si, receiver)?;
self.coerce_number_bounded(prop_val)?
};
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)?;
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 = {
let obj = unsafe { &mut *obj_ptr };
let prop_val = self.read_member_prop(obj, prop_name_si, receiver)?;
self.coerce_int32_bounded(prop_val)?
};
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)?;
Ok(())
}
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 = {
let obj = unsafe { &mut *obj_ptr };
let prop_val = self.read_member_prop(obj, prop_name_si, receiver)?;
self.coerce_int32_bounded(prop_val)?
};
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)?;
Ok(())
}
}