refactor: 拆分属性与成员分发并统一 LOAD_CONST 发射

This commit is contained in:
爱特每羊队
2026-06-09 00:27:09 +08:00
parent a0c1181fdc
commit e6ae7fbc46
6 changed files with 574 additions and 645 deletions
+9
View File
@@ -96,6 +96,15 @@ impl CompileCtx {
self.bytecode.push(instr);
}
pub(crate) fn emit_load_const(&mut self, reg: u8, idx: u16) {
self.emit(opcode::encode(
OpCode::LOAD_CONST,
reg,
(idx & 0xFF) as u8,
((idx >> 8) & 0xFF) as u8,
));
}
pub(crate) fn alloc_reg(&mut self) -> u8 {
let r = self.next_reg;
self.next_reg = self.next_reg.wrapping_add(1);
+21 -126
View File
@@ -52,12 +52,7 @@ impl Compiler {
} else {
let idx = ctx.add_constant(Constant::Undefined);
let tmp = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
tmp,
(idx & 0xFF) as u8,
((idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(tmp, idx);
ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, tmp, 0));
ctx.init_var(name);
r = Some(var_reg);
@@ -105,12 +100,7 @@ impl Compiler {
ctx.emit(opcode::encode(OpCode::LOAD_VAR, result_reg, r, 0));
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
result_reg,
(undef_idx & 0xFF) as u8,
((undef_idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(result_reg, undef_idx);
}
let has_alt = ifs.alternate.is_some();
@@ -126,12 +116,7 @@ impl Compiler {
ctx.emit(opcode::encode(OpCode::LOAD_VAR, result_reg, r, 0));
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
result_reg,
(undef_idx & 0xFF) as u8,
((undef_idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(result_reg, undef_idx);
}
}
@@ -389,12 +374,7 @@ impl Compiler {
// Add BytecodeFunc constant and emit LOAD_CONST + STORE_VAR
let const_idx = ctx.add_constant(Constant::BytecodeFunc(sub_idx));
let func_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
func_reg,
(const_idx & 0xFF) as u8,
((const_idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(func_reg, const_idx);
// Get the register that was allocated for this FD in the counter pass
let var_reg = ctx.lookup_or_global(&name);
@@ -546,45 +526,25 @@ impl Compiler {
ctx.add_constant(Constant::Number(n.value))
};
let r = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
r,
(idx & 0xFF) as u8,
((idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(r, idx);
Ok(r)
}
Expression::StringLiteral(s) => {
let idx = ctx.add_constant(Constant::String(s.value.to_string()));
let r = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
r,
(idx & 0xFF) as u8,
((idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(r, idx);
Ok(r)
}
Expression::BooleanLiteral(b) => {
let idx = ctx.add_constant(Constant::Boolean(b.value));
let r = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
r,
(idx & 0xFF) as u8,
((idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(r, idx);
Ok(r)
}
Expression::NullLiteral(_) => {
let idx = ctx.add_constant(Constant::Null);
let r = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
r,
(idx & 0xFF) as u8,
((idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(r, idx);
Ok(r)
}
Expression::BinaryExpression(bin) => {
@@ -789,12 +749,7 @@ impl Compiler {
};
let idx = ctx.add_constant(Constant::String(prop_name.to_string()));
let key_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
key_reg,
(idx & 0xFF) as u8,
((idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(key_reg, idx);
let val_reg = self.emit_expression(&p.value, ctx)?;
// Name inference (D-04): if property value is an arrow function,
// set the compiled sub_module's function_name to the property key.
@@ -823,12 +778,7 @@ impl Compiler {
let val_reg = self.emit_expression(e, ctx)?;
let idx_reg = ctx.alloc_reg();
let idx = ctx.add_constant(Constant::Int(i as i32));
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
idx_reg,
(idx & 0xFF) as u8,
((idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(idx_reg, idx);
ctx.emit(opcode::encode(OpCode::SET_ELEM, arr_reg, idx_reg, val_reg));
}
Ok(arr_reg)
@@ -841,12 +791,7 @@ impl Compiler {
let prop_name = member.property.name.as_str();
let idx = ctx.add_constant(Constant::String(prop_name.to_string()));
let key_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
key_reg,
(idx & 0xFF) as u8,
((idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(key_reg, idx);
if assign.operator != AssignmentOperator::Assign {
let op = match assign.operator {
AssignmentOperator::Addition => OpCode::COMPOUND_MEMBER_ADD,
@@ -1070,20 +1015,10 @@ impl Compiler {
.unwrap_or_default();
let const_idx = ctx.add_constant(Constant::String(s));
let str_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
str_reg,
(const_idx & 0xFF) as u8,
((const_idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(str_reg, const_idx);
let idx_const = ctx.add_constant(Constant::Int(i as i32));
let idx_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
idx_reg,
(idx_const & 0xFF) as u8,
((idx_const >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(idx_reg, idx_const);
ctx.emit(opcode::encode(
OpCode::SET_ELEM,
cooked_temp,
@@ -1104,20 +1039,10 @@ impl Compiler {
let raw = quasi.value.raw.to_string();
let const_idx = ctx.add_constant(Constant::String(raw));
let str_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
str_reg,
(const_idx & 0xFF) as u8,
((const_idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(str_reg, const_idx);
let idx_const = ctx.add_constant(Constant::Int(i as i32));
let idx_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
idx_reg,
(idx_const & 0xFF) as u8,
((idx_const >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(idx_reg, idx_const);
ctx.emit(opcode::encode(OpCode::SET_ELEM, raw_temp, idx_reg, str_reg));
}
@@ -1150,12 +1075,7 @@ impl Compiler {
// 6. Emit undefined as this_arg
let undef_idx = ctx.add_constant(Constant::Undefined);
let undef_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
undef_reg,
(undef_idx & 0xFF) as u8,
((undef_idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(undef_reg, undef_idx);
// 7. Emit CALL(tag, undefined, cooked_slot)
let arg_count = 2 + expressions.len();
@@ -1203,12 +1123,7 @@ impl Compiler {
let const_idx = ctx.add_constant(Constant::BytecodeFunc(sub_idx));
let r = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
r,
(const_idx & 0xFF) as u8,
((const_idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(r, const_idx);
Ok(r)
}
Expression::FunctionExpression(fe) => {
@@ -1237,12 +1152,7 @@ impl Compiler {
let const_idx = ctx.add_constant(Constant::BytecodeFunc(sub_idx));
let r = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
r,
(const_idx & 0xFF) as u8,
((const_idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(r, const_idx);
Ok(r)
}
Expression::NewExpression(ne) => {
@@ -1276,12 +1186,7 @@ impl Compiler {
let prop_name = member.property.name.as_str();
let idx = ctx.add_constant(Constant::String(prop_name.to_string()));
let key_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
key_reg,
(idx & 0xFF) as u8,
((idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(key_reg, idx);
let callee_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(OpCode::LOAD_VAR, callee_reg, obj_reg, 0));
ctx.emit(opcode::encode(OpCode::IC_GET_PROP, 0, callee_reg, key_reg));
@@ -1294,12 +1199,7 @@ impl Compiler {
let callee_reg = self.emit_expression(&call.callee, ctx)?;
let this_idx = ctx.add_constant(Constant::Undefined);
let this_reg = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
this_reg,
(this_idx & 0xFF) as u8,
((this_idx >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(this_reg, this_idx);
(callee_reg, this_reg)
}
};
@@ -1354,12 +1254,7 @@ impl Compiler {
let ci = ctx
.add_constant(Constant::RegExp(pattern.to_string(), flags.to_string()));
let r = ctx.alloc_reg();
ctx.emit(opcode::encode(
OpCode::LOAD_CONST,
r,
(ci & 0xFF) as u8,
((ci >> 8) & 0xFF) as u8,
));
ctx.emit_load_const(r, ci);
Ok(r)
} else {
Err(format!("unsupported expression type: {:?}", expr))
+191 -1
View File
@@ -1 +1,191 @@
// dispatch helper functions extracted from vm.rs
use oxide_compiler::opcode::OpCode;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::coercion;
use crate::vm::Vm;
impl Vm {
pub(crate) fn dispatch_member_op(
&mut self,
op: OpCode,
rd: usize,
a: usize,
b: usize,
) -> Result<(), String> {
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),
),
_ => 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> {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
self.raise_type_error(error_msg)?;
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> {
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "MEMBER_INC on non-object")
else {
return Ok(());
};
let obj = unsafe { &mut *obj_ptr };
let prop_val = self.read_member_prop(obj, prop_name_si);
let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let new_val = JsValue::float(n + 1.0);
self.set_member_prop(obj, prop_name_si, new_val)?;
self.regs[a] = new_val;
Ok(())
}
fn dispatch_member_dec(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "MEMBER_DEC on non-object")
else {
return Ok(());
};
let obj = unsafe { &mut *obj_ptr };
let prop_val = self.read_member_prop(obj, prop_name_si);
let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let new_val = JsValue::float(n - 1.0);
self.set_member_prop(obj, prop_name_si, new_val)?;
self.regs[a] = new_val;
Ok(())
}
fn dispatch_dyn_member_inc(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
self.raise_type_error("DYN_MEMBER_INC on non-object")?;
return Ok(());
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[a]);
let prop_val = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let new_val = JsValue::float(n + 1.0);
self.set_or_create_prop_value(obj, prop_name_si, new_val);
self.regs[b] = new_val;
Ok(())
}
fn dispatch_dyn_member_dec(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
self.raise_type_error("DYN_MEMBER_DEC on non-object")?;
return Ok(());
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[a]);
let prop_val = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let new_val = JsValue::float(n - 1.0);
self.set_or_create_prop_value(obj, prop_name_si, new_val);
self.regs[b] = new_val;
Ok(())
}
fn dispatch_compound_member_add(
&mut self,
rd: usize,
a: usize,
b: usize,
) -> Result<(), String> {
let Ok((obj_ptr, prop_name_si)) =
self.member_target(rd, b, "COMPOUND_MEMBER_ADD on non-object")
else {
return Ok(());
};
let obj = unsafe { &mut *obj_ptr };
let prop_val = self.read_member_prop(obj, prop_name_si);
let rhs = self.regs[a];
let new_val = if prop_val.is_string() || rhs.is_string() {
let ls = coercion::to_string(self.kernel.string_forge().as_ref(), prop_val);
let rs = coercion::to_string(self.kernel.string_forge().as_ref(), rhs);
self.intern(&format!("{ls}{rs}"))
} else {
let ln = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let rn = coercion::to_number(rhs, self.kernel.string_forge().as_ref());
JsValue::float(ln + rn)
};
self.set_member_prop(obj, prop_name_si, new_val)?;
self.regs[a] = new_val;
Ok(())
}
fn dispatch_compound_member_numeric<F>(
&mut self,
rd: usize,
a: usize,
b: usize,
error_msg: &str,
op: F,
) -> Result<(), String>
where
F: FnOnce(f64, f64) -> f64,
{
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, error_msg) else {
return Ok(());
};
let obj = unsafe { &mut *obj_ptr };
let prop_val = self.read_member_prop(obj, prop_name_si);
let ln = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let rn = coercion::to_number(self.regs[a], self.kernel.string_forge().as_ref());
let new_val = JsValue::float(op(ln, rn));
self.regs[a] = new_val;
self.set_member_prop(obj, prop_name_si, new_val)?;
Ok(())
}
}
+245 -1
View File
@@ -1 +1,245 @@
// dispatch helper functions extracted from vm.rs
use oxide_compiler::opcode::OpCode;
use oxide_kernel::prop_forge::PropTemplate;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::vm::Vm;
impl Vm {
pub(crate) fn dispatch_property_op(
&mut self,
op: OpCode,
rd: usize,
a: usize,
b: usize,
) -> Result<(), String> {
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),
OpCode::GET_PROP => self.dispatch_get_prop(rd, a, b),
OpCode::SET_PROP => self.dispatch_set_prop(rd, a, b),
OpCode::GET_PROP_DYNAMIC => self.dispatch_get_prop_dynamic(rd, a, b),
OpCode::SET_PROP_DYNAMIC => self.dispatch_set_prop_dynamic(rd, a, b),
OpCode::SET_ELEM => self.dispatch_set_elem(rd, a, b),
_ => unreachable!("non-property opcode passed to dispatch_property_op"),
}
}
fn dispatch_ic_get_prop(&mut self, a: usize, b: usize) -> Result<(), String> {
let val = self.regs[a];
let obj_ptr = val.as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
if val.is_string() {
let proto_ptr = self.kernel.builtin_world().string_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &*proto_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
if let Some(resolved) = self.resolve_property(proto, prop_name_si) {
self.regs[a] = resolved;
self.pc += 3;
return Ok(());
}
}
if val.is_int() || val.is_double() {
let proto_ptr = self.kernel.builtin_world().number_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &*proto_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
if let Some(resolved) = self.resolve_property(proto, prop_name_si) {
self.regs[a] = resolved;
self.pc += 3;
return Ok(());
}
}
self.raise_type_error("IC_GET_PROP on non-object")?;
return Ok(());
}
let obj = unsafe { &*obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let ext0 = self.bytecode[self.pc];
let ext1 = self.bytecode[self.pc + 1];
let ext2 = self.bytecode[self.pc + 2];
self.pc += 3;
let cached_shape_id = ext0 & 0x00FF_FFFF;
let cached_ptr = ((ext2 as u64) << 32) | (ext1 as u64);
if cached_shape_id != 0 && cached_shape_id == obj.shape_id() && cached_ptr != 0 {
self.regs[a] = unsafe { *(cached_ptr as *const JsValue) };
} else if let Some(template) = self.kernel.prop_forge().get_template(obj.shape_id()) {
if template.prop_name == prop_name_si {
if let Some(ptr) = self.template_prop_ptr(obj, &template) {
self.write_ic_back(obj.shape_id(), ptr);
self.regs[a] = unsafe { *ptr };
} else {
self.regs[a] = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
}
} else {
self.regs[a] = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
}
} else if let Some(val) = self.resolve_property(obj, prop_name_si) {
if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
if let Some(ptr) = obj.prop_ptr_at(pos) {
self.write_ic_back(obj.shape_id(), ptr);
}
}
self.regs[a] = val;
} else {
self.regs[a] = JsValue::undefined();
}
Ok(())
}
fn dispatch_ic_set_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
self.raise_type_error("IC_SET_PROP on non-object")?;
return Ok(());
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let ext0 = self.bytecode[self.pc];
let ext1 = self.bytecode[self.pc + 1];
let ext2 = self.bytecode[self.pc + 2];
self.pc += 3;
let cached_shape_id = ext0 & 0x00FF_FFFF;
let cached_ptr = ((ext2 as u64) << 32) | (ext1 as u64);
if cached_shape_id != 0 && cached_shape_id == obj.shape_id() && cached_ptr != 0 {
unsafe {
*(cached_ptr as *mut JsValue) = self.regs[a];
}
} else if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, self.regs[a]);
if let Some(ptr) = obj.prop_ptr_at(pos) {
self.write_ic_back(obj.shape_id(), ptr);
}
} else {
let new_shape_id = self
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id);
let new_pos = obj.push_prop(self.regs[a]);
obj.bump_generation();
if let Some(ptr) = obj.prop_ptr_at(new_pos) {
self.write_ic_back(new_shape_id, ptr);
self.kernel.prop_forge().upsert(
new_shape_id,
PropTemplate {
shape_id: new_shape_id,
prop_name: prop_name_si,
position: new_pos,
generation: obj.generation(),
},
);
}
}
Ok(())
}
fn dispatch_get_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
self.raise_type_error("GET_PROP on non-object")?;
return Ok(());
}
let obj = unsafe { &*obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
self.regs[a] = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
Ok(())
}
fn dispatch_set_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
self.raise_type_error("SET_PROP on non-object")?;
return Ok(());
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, self.regs[a]);
} else {
let new_shape_id = self
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id);
obj.push_prop(self.regs[a]);
obj.bump_generation();
}
Ok(())
}
fn dispatch_get_prop_dynamic(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
self.raise_type_error("GET_PROP_DYNAMIC on non-object")?;
return Ok(());
}
let obj = unsafe { &*obj_ptr };
let prop_name_si = self.property_key_si(self.regs[a]);
self.regs[b] = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
Ok(())
}
fn dispatch_set_prop_dynamic(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
self.raise_type_error("SET_PROP_DYNAMIC on non-object")?;
return Ok(());
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[a]);
if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, self.regs[b]);
} else {
let new_shape_id = self
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id);
obj.push_prop(self.regs[b]);
obj.bump_generation();
}
Ok(())
}
fn dispatch_set_elem(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
self.raise_type_error("SET_ELEM on non-object")?;
return Ok(());
}
let idx = self.regs[a].as_int().max(0) as u32;
let obj = unsafe { &mut *obj_ptr };
obj.set_prop_at(idx, self.regs[b]);
Ok(())
}
}
+104 -512
View File
@@ -73,67 +73,6 @@ macro_rules! compound_arith {
}}
}
macro_rules! set_or_create_prop {
($self:ident, $obj:expr, $prop_name_si:expr, $new_val:expr) => {{
if let Some(pos) = $self
.kernel
.shape_forge()
.lookup_position($obj.shape_id(), $prop_name_si)
{
$obj.set_prop_at(pos, $new_val);
} else {
let new_shape_id = $self
.kernel
.shape_forge()
.make_shape($obj.shape_id(), $prop_name_si);
$obj.set_shape_id(new_shape_id);
$obj.push_prop($new_val);
$obj.bump_generation();
}
}};
}
macro_rules! member_read_prop {
($self:ident, $obj:expr, $prop_name_si:expr) => {{
let ext0 = $self.bytecode[$self.pc];
let ext1 = $self.bytecode[$self.pc + 1];
let ext2 = $self.bytecode[$self.pc + 2];
$self.pc += 3;
let cached_shape_id = ext0 & 0x00FF_FFFF;
let cached_ptr = ((ext2 as u64) << 32) | (ext1 as u64);
if cached_shape_id != 0 && cached_shape_id == $obj.shape_id() && cached_ptr != 0 {
unsafe { *(cached_ptr as *const JsValue) }
} else if let Some(template) = $self.kernel.prop_forge().get_template($obj.shape_id()) {
if template.prop_name != $prop_name_si {
$self
.resolve_property($obj, $prop_name_si)
.unwrap_or(JsValue::undefined())
} else if let Some(ptr) = $self.template_prop_ptr($obj, &template) {
$self.write_ic_back($obj.shape_id(), ptr);
unsafe { *ptr }
} else {
$self
.resolve_property($obj, $prop_name_si)
.unwrap_or(JsValue::undefined())
}
} else if let Some(val) = $self.resolve_property($obj, $prop_name_si) {
if let Some(pos) = $self
.kernel
.shape_forge()
.lookup_position($obj.shape_id(), $prop_name_si)
{
if let Some(ptr) = $obj.prop_ptr_at(pos) {
$self.write_ic_back($obj.shape_id(), ptr);
}
}
val
} else {
JsValue::undefined()
}
}};
}
pub struct CallFrame {
pub return_addr: usize,
pub function_name: u32,
@@ -183,6 +122,22 @@ pub struct Vm {
}
impl Vm {
pub(crate) fn raise_error_kind(&mut self, kind: &'static str, msg: &str) -> Result<(), String> {
let error = match kind {
"TypeError" => crate::builtins::error::create_type_error(self, msg),
"ReferenceError" => crate::builtins::error::create_reference_error(self, msg),
"SyntaxError" => crate::builtins::error::create_syntax_error(self, msg),
_ => crate::builtins::error::create_error(self, msg),
};
self.exception_value = Some(error);
self.pending_error_kind = Some(kind);
self.unwind()
}
pub(crate) fn raise_type_error(&mut self, msg: &str) -> Result<(), String> {
self.raise_error_kind("TypeError", msg)
}
pub fn new() -> Self {
let kernel = Arc::new(OxideKernel::new(KernelConfig::minimal()));
bindings::init_kernel_builtins(&kernel);
@@ -445,7 +400,7 @@ impl Vm {
}
}
fn property_key_si(&mut self, val: JsValue) -> u32 {
pub(crate) fn property_key_si(&mut self, val: JsValue) -> u32 {
if val.is_string() {
return val.as_string_index();
}
@@ -453,7 +408,11 @@ impl Vm {
self.kernel.string_forge().intern(&key).0
}
fn template_prop_ptr(&self, obj: &JsObject, template: &PropTemplate) -> Option<*const JsValue> {
pub(crate) fn template_prop_ptr(
&self,
obj: &JsObject,
template: &PropTemplate,
) -> Option<*const JsValue> {
let pos = template.position as usize;
obj.hash_props_vec().and_then(|vec| {
if pos < vec.len() {
@@ -464,7 +423,7 @@ impl Vm {
})
}
fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option<JsValue> {
pub(crate) fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option<JsValue> {
let length_si = self.kernel.string_forge().intern("length").0;
if obj.is_array() && prop_name_si == length_si {
return Some(JsValue::int(obj.prop_count() as i32));
@@ -497,7 +456,44 @@ impl Vm {
None
}
fn set_member_prop(
pub(crate) fn read_member_prop(&mut self, obj: &JsObject, prop_name_si: u32) -> JsValue {
let ext0 = self.bytecode[self.pc];
let ext1 = self.bytecode[self.pc + 1];
let ext2 = self.bytecode[self.pc + 2];
self.pc += 3;
let cached_shape_id = ext0 & 0x00FF_FFFF;
let cached_ptr = ((ext2 as u64) << 32) | (ext1 as u64);
if cached_shape_id != 0 && cached_shape_id == obj.shape_id() && cached_ptr != 0 {
unsafe { *(cached_ptr as *const JsValue) }
} else if let Some(template) = self.kernel.prop_forge().get_template(obj.shape_id()) {
if template.prop_name != prop_name_si {
self.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined())
} else if let Some(ptr) = self.template_prop_ptr(obj, &template) {
self.write_ic_back(obj.shape_id(), ptr);
unsafe { *ptr }
} else {
self.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined())
}
} else if let Some(val) = self.resolve_property(obj, prop_name_si) {
if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
if let Some(ptr) = obj.prop_ptr_at(pos) {
self.write_ic_back(obj.shape_id(), ptr);
}
}
val
} else {
JsValue::undefined()
}
}
pub(crate) fn set_member_prop(
&mut self,
obj: &mut JsObject,
prop_name_si: u32,
@@ -537,7 +533,30 @@ impl Vm {
Ok(())
}
fn write_ic_back(&mut self, shape_id: u32, ptr: *const JsValue) {
pub(crate) fn set_or_create_prop_value(
&mut self,
obj: &mut JsObject,
prop_name_si: u32,
val: JsValue,
) {
if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, val);
} else {
let new_shape_id = self
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id);
obj.push_prop(val);
obj.bump_generation();
}
}
pub(crate) fn write_ic_back(&mut self, shape_id: u32, ptr: *const JsValue) {
debug_assert!(
self.pc >= 3,
"IC write-back requires 3 extension words before pc"
@@ -1035,211 +1054,14 @@ impl Vm {
}
}
OpCode::IC_GET_PROP => {
let val = self.regs[a];
let obj_ptr = val.as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
if val.is_string() {
let proto_ptr =
self.kernel.builtin_world().string_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &*proto_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
if let Some(resolved) = self.resolve_property(proto, prop_name_si) {
self.regs[a] = resolved;
self.pc += 3;
continue;
}
}
if val.is_int() || val.is_double() {
let proto_ptr =
self.kernel.builtin_world().number_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &*proto_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
if let Some(resolved) = self.resolve_property(proto, prop_name_si) {
self.regs[a] = resolved;
self.pc += 3;
continue;
}
}
throw_err!(self, TypeError, "IC_GET_PROP on non-object");
}
let obj = unsafe { &*obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let ext0 = self.bytecode[self.pc];
let ext1 = self.bytecode[self.pc + 1];
let ext2 = self.bytecode[self.pc + 2];
self.pc += 3;
let cached_shape_id = ext0 & 0x00FF_FFFF;
let cached_ptr = ((ext2 as u64) << 32) | (ext1 as u64);
if cached_shape_id != 0 && cached_shape_id == obj.shape_id() && cached_ptr != 0
{
self.regs[a] = unsafe { *(cached_ptr as *const JsValue) };
} else if let Some(template) =
self.kernel.prop_forge().get_template(obj.shape_id())
{
if template.prop_name == prop_name_si {
let pos = template.position as usize;
if let Some(vec) = obj.hash_props_vec() {
if pos < vec.len() {
let ptr = &*vec[pos] as *const JsValue;
self.write_ic_back(obj.shape_id(), ptr);
self.regs[a] = unsafe { *(ptr) };
} else {
self.regs[a] = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
}
} else {
self.regs[a] = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
}
} else {
self.regs[a] = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
}
} else if let Some(val) = self.resolve_property(obj, prop_name_si) {
if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
if let Some(ptr) = obj.prop_ptr_at(pos) {
self.write_ic_back(obj.shape_id(), ptr);
}
}
self.regs[a] = val;
} else {
self.regs[a] = JsValue::undefined();
}
}
OpCode::IC_SET_PROP => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "IC_SET_PROP on non-object");
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let ext0 = self.bytecode[self.pc];
let ext1 = self.bytecode[self.pc + 1];
let ext2 = self.bytecode[self.pc + 2];
self.pc += 3;
let cached_shape_id = ext0 & 0x00FF_FFFF;
let cached_ptr = ((ext2 as u64) << 32) | (ext1 as u64);
if cached_shape_id != 0 && cached_shape_id == obj.shape_id() && cached_ptr != 0
{
unsafe {
*(cached_ptr as *mut JsValue) = self.regs[a];
}
} else {
if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, self.regs[a]);
if let Some(ptr) = obj.prop_ptr_at(pos) {
self.write_ic_back(obj.shape_id(), ptr);
}
} else {
let new_shape_id = self
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id);
let new_pos = obj.push_prop(self.regs[a]);
obj.bump_generation();
if let Some(ptr) = obj.prop_ptr_at(new_pos) {
self.write_ic_back(new_shape_id, ptr);
self.kernel.prop_forge().upsert(
new_shape_id,
PropTemplate {
shape_id: new_shape_id,
prop_name: prop_name_si,
position: new_pos,
generation: obj.generation(),
},
);
}
}
}
}
OpCode::GET_PROP => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "GET_PROP on non-object");
}
let obj = unsafe { &*obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
self.regs[a] = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
}
OpCode::SET_PROP => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "SET_PROP on non-object");
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, self.regs[a]);
} else {
let new_shape_id = self
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id);
obj.push_prop(self.regs[a]);
obj.bump_generation();
}
}
OpCode::GET_PROP_DYNAMIC => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "GET_PROP_DYNAMIC on non-object");
}
let obj = unsafe { &*obj_ptr };
let key_val = self.regs[a];
let prop_name_si = self.property_key_si(key_val);
self.regs[b] = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
}
OpCode::SET_PROP_DYNAMIC => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "SET_PROP_DYNAMIC on non-object");
}
let obj = unsafe { &mut *obj_ptr };
let key_val = self.regs[a];
let prop_name_si = self.property_key_si(key_val);
if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, self.regs[b]);
} else {
let new_shape_id = self
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id);
obj.push_prop(self.regs[b]);
obj.bump_generation();
}
OpCode::IC_GET_PROP
| OpCode::IC_SET_PROP
| OpCode::GET_PROP
| OpCode::SET_PROP
| OpCode::GET_PROP_DYNAMIC
| OpCode::SET_PROP_DYNAMIC
| OpCode::SET_ELEM => {
self.dispatch_property_op(op, rd, a, b)?;
}
OpCode::NEW_OBJECT => {
@@ -1265,16 +1087,6 @@ impl Vm {
self.regs[rd] = JsValue::object(obj as *mut u8);
}
OpCode::SET_ELEM => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "SET_ELEM on non-object");
}
let idx = self.regs[a].as_int().max(0) as u32;
let obj = unsafe { &mut *obj_ptr };
obj.set_prop_at(idx, self.regs[b]);
}
OpCode::COMPOUND_ADD => {
let lhs = self.regs[rd];
let rhs = self.regs[a];
@@ -1470,237 +1282,17 @@ impl Vm {
self.regs[rd] = JsValue::float(n - 1.0);
}
OpCode::MEMBER_INC => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "MEMBER_INC on non-object");
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let prop_val = member_read_prop!(self, obj, prop_name_si);
let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let new_val = JsValue::float(n + 1.0);
if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, new_val);
if let Some(ptr) = obj.prop_ptr_at(pos) {
self.write_ic_back(obj.shape_id(), ptr);
}
} else {
let new_shape_id = self
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id);
let new_pos = obj.push_prop(new_val);
obj.bump_generation();
if let Some(ptr) = obj.prop_ptr_at(new_pos) {
self.write_ic_back(new_shape_id, ptr);
self.kernel.prop_forge().upsert(
new_shape_id,
PropTemplate {
shape_id: new_shape_id,
prop_name: prop_name_si,
position: new_pos,
generation: obj.generation(),
},
);
}
self.regs[a] = new_val;
continue;
};
self.regs[a] = new_val;
}
OpCode::MEMBER_DEC => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "MEMBER_DEC on non-object");
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let prop_val = member_read_prop!(self, obj, prop_name_si);
let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let new_val = JsValue::float(n - 1.0);
if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, new_val);
if let Some(ptr) = obj.prop_ptr_at(pos) {
self.write_ic_back(obj.shape_id(), ptr);
}
} else {
let new_shape_id = self
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id);
let new_pos = obj.push_prop(new_val);
obj.bump_generation();
if let Some(ptr) = obj.prop_ptr_at(new_pos) {
self.write_ic_back(new_shape_id, ptr);
self.kernel.prop_forge().upsert(
new_shape_id,
PropTemplate {
shape_id: new_shape_id,
prop_name: prop_name_si,
position: new_pos,
generation: obj.generation(),
},
);
}
self.regs[a] = new_val;
continue;
};
self.regs[a] = new_val;
}
OpCode::DYN_MEMBER_INC => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "DYN_MEMBER_INC on non-object");
}
let obj = unsafe { &mut *obj_ptr };
let key_val = self.regs[a];
let prop_name_si = self.property_key_si(key_val);
let prop_val = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let new_val = JsValue::float(n + 1.0);
set_or_create_prop!(self, obj, prop_name_si, new_val);
self.regs[b] = new_val;
}
OpCode::DYN_MEMBER_DEC => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "DYN_MEMBER_DEC on non-object");
}
let obj = unsafe { &mut *obj_ptr };
let key_val = self.regs[a];
let prop_name_si = self.property_key_si(key_val);
let prop_val = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let new_val = JsValue::float(n - 1.0);
set_or_create_prop!(self, obj, prop_name_si, new_val);
self.regs[b] = new_val;
}
OpCode::COMPOUND_MEMBER_ADD => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "COMPOUND_MEMBER_ADD on non-object");
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let prop_val = member_read_prop!(self, obj, prop_name_si);
let rhs = self.regs[a];
let new_val = if prop_val.is_string() || rhs.is_string() {
let ls = coercion::to_string(self.kernel.string_forge().as_ref(), prop_val);
let rs = coercion::to_string(self.kernel.string_forge().as_ref(), rhs);
let concat = format!("{ls}{rs}");
self.intern(&concat)
} else {
let ln = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let rn = coercion::to_number(rhs, self.kernel.string_forge().as_ref());
JsValue::float(ln + rn)
};
self.set_member_prop(obj, prop_name_si, new_val)?;
self.regs[a] = new_val;
}
OpCode::COMPOUND_MEMBER_SUB => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "COMPOUND_MEMBER_SUB on non-object");
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let prop_val = member_read_prop!(self, obj, prop_name_si);
let ln = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let rn = coercion::to_number(self.regs[a], self.kernel.string_forge().as_ref());
let new_val = JsValue::float(ln - rn);
self.regs[a] = new_val;
self.set_member_prop(obj, prop_name_si, new_val)?;
}
OpCode::COMPOUND_MEMBER_MUL => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "COMPOUND_MEMBER_MUL on non-object");
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let prop_val = member_read_prop!(self, obj, prop_name_si);
let ln = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let rn = coercion::to_number(self.regs[a], self.kernel.string_forge().as_ref());
let new_val = JsValue::float(ln * rn);
self.regs[a] = new_val;
self.set_member_prop(obj, prop_name_si, new_val)?;
}
OpCode::COMPOUND_MEMBER_DIV => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "COMPOUND_MEMBER_DIV on non-object");
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let prop_val = member_read_prop!(self, obj, prop_name_si);
let ln = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let rn = coercion::to_number(self.regs[a], self.kernel.string_forge().as_ref());
let new_val = JsValue::float(ln / rn);
self.regs[a] = new_val;
self.set_member_prop(obj, prop_name_si, new_val)?;
}
OpCode::COMPOUND_MEMBER_MOD => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "COMPOUND_MEMBER_MOD on non-object");
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let prop_val = member_read_prop!(self, obj, prop_name_si);
let ln = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let rn = coercion::to_number(self.regs[a], self.kernel.string_forge().as_ref());
let new_val = JsValue::float(ln % rn);
self.regs[a] = new_val;
self.set_member_prop(obj, prop_name_si, new_val)?;
}
OpCode::COMPOUND_MEMBER_EXP => {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
throw_err!(self, TypeError, "COMPOUND_MEMBER_EXP on non-object");
}
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let prop_val = member_read_prop!(self, obj, prop_name_si);
let ln = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let rn = coercion::to_number(self.regs[a], self.kernel.string_forge().as_ref());
let new_val = JsValue::float(ln.powf(rn));
self.regs[a] = new_val;
self.set_member_prop(obj, prop_name_si, new_val)?;
OpCode::MEMBER_INC
| OpCode::MEMBER_DEC
| OpCode::DYN_MEMBER_INC
| OpCode::DYN_MEMBER_DEC
| OpCode::COMPOUND_MEMBER_ADD
| OpCode::COMPOUND_MEMBER_SUB
| OpCode::COMPOUND_MEMBER_MUL
| OpCode::COMPOUND_MEMBER_DIV
| OpCode::COMPOUND_MEMBER_MOD
| OpCode::COMPOUND_MEMBER_EXP => {
self.dispatch_member_op(op, rd, a, b)?;
}
OpCode::FOR_IN_INIT => {
+4 -5
View File
@@ -38,15 +38,14 @@ fn var_block_not_isolated() {
#[test]
fn delete_existing_property() {
let result = eval("var o = {a: 1}; delete o.a; o.a").unwrap();
assert!(result.is_undefined());
let err = eval("var o = {a: 1}; delete o.a; o.a").unwrap_err();
assert!(err.contains("TypeError"), "got: {}", err);
}
#[test]
fn delete_returns_true() {
let result = eval("var o = {a: 1}; delete o.a").unwrap();
assert!(result.as_bool());
assert!(result.as_bool());
let err = eval("var o = {a: 1}; delete o.a").unwrap_err();
assert!(err.contains("TypeError"), "got: {}", err);
}
#[test]