diff --git a/crates/oxide_bytecode/src/opcode.rs b/crates/oxide_bytecode/src/opcode.rs index 3af0eb7..b2df402 100644 --- a/crates/oxide_bytecode/src/opcode.rs +++ b/crates/oxide_bytecode/src/opcode.rs @@ -486,6 +486,15 @@ define_opcodes! { def = None, uses = [SlotSpec::Slot(Slot::Rd), SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], pure = false, jump = false, term = false, ic = false, + // define 数据/访问器属性并指定描述符:ext 字存放 attrs(bit0=writable, bit1=enumerable, bit2=configurable,与 PropAttributes 位一致)。 + // class 方法/constructor/prototype 需要非枚举或不可写描述符,普通 SET_PROP/DEFINE_PROP 无法表达。 + DEFINE_PROP_ATTRS = 0x90 => "DEFINE_PROP_ATTRS", + def = None, uses = [SlotSpec::Slot(Slot::Rd), SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], + pure = false, jump = false, term = false, ic = false, + DEFINE_ACCESSOR_ATTRS = 0x91 => "DEFINE_ACCESSOR_ATTRS", + def = None, uses = [SlotSpec::Slot(Slot::Rd), SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], + pure = false, jump = false, term = false, ic = false, + // ── 成员复合赋值:位/移位 (0x64-0x69) ── COMPOUND_MEMBER_BIT_AND = 0x64 => "COMPOUND_MEMBER_BIT_AND", def = Some(SlotSpec::Slot(Slot::A)), uses = [SlotSpec::Slot(Slot::Rd), SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], diff --git a/crates/oxide_emit/src/class/method.rs b/crates/oxide_emit/src/class/method.rs index dcaf178..d2106f6 100644 --- a/crates/oxide_emit/src/class/method.rs +++ b/crates/oxide_emit/src/class/method.rs @@ -8,6 +8,9 @@ use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_parser::{ClassElement, MethodDefinitionKind, PropertyKey}; +/// class 方法/访问器属性描述符:writable + configurable,enumerable = false(规范 DefineMethod)。 +const CLASS_METHOD_ATTRS: u32 = 0b101; + impl Emitter { pub(crate) fn emit_class_methods( &self, elements: &[ClassElement], ctor_reg: u32, proto_reg: u32, self_binding: &[(&str, u32)], @@ -39,21 +42,13 @@ impl Emitter { self.emit_class_method_function(method, &method_name, Operand::Reg(home_reg), ctx, self_binding)?; match method.kind { MethodDefinitionKind::Method => { - if method.computed { - ctx.inst(Inst::new( - OpCode::SET_PROP_DYNAMIC, - Operand::Reg(home_reg), - Operand::Reg(key_reg), - Operand::Reg(accessor_reg), - )); - } else { - ctx.inst(Inst::new( - OpCode::SET_PROP, - Operand::Reg(home_reg), - Operand::Reg(accessor_reg), - Operand::Reg(key_reg), - )); - } + // class 方法按规范为非枚举数据属性(DefineMethod:writable/configurable,enumerable=false)。 + ctx.inst(Inst::define_prop_attrs( + Operand::Reg(home_reg), + Operand::Reg(accessor_reg), + Operand::Reg(key_reg), + CLASS_METHOD_ATTRS, + )); } MethodDefinitionKind::Get | MethodDefinitionKind::Set => { if method.computed { @@ -67,11 +62,12 @@ impl Emitter { }; let key_name = self.class_property_name(&method.key)?; let key_idx = ctx.add_constant(Constant::String(key_name)); - ctx.inst(Inst::define_accessor( + ctx.inst(Inst::define_accessor_attrs( Operand::Reg(home_reg), Operand::Reg(get_reg), Operand::Reg(set_reg), key_idx as u32, + CLASS_METHOD_ATTRS, )); } MethodDefinitionKind::Constructor => continue, diff --git a/crates/oxide_emit/src/class/prototype.rs b/crates/oxide_emit/src/class/prototype.rs index 83c4360..a4ad498 100644 --- a/crates/oxide_emit/src/class/prototype.rs +++ b/crates/oxide_emit/src/class/prototype.rs @@ -42,20 +42,22 @@ impl Emitter { let ctor_key_idx = ctx.add_constant(Constant::String("constructor".to_string())); let ctor_key_reg = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(ctor_key_reg), ctor_key_idx)); - ctx.inst(Inst::new( - OpCode::SET_PROP, + // Class.prototype.constructor:writable/configurable,enumerable = false。 + ctx.inst(Inst::define_prop_attrs( Operand::Reg(proto_reg), Operand::Reg(ctor_reg), Operand::Reg(ctor_key_reg), + 0b101, )); let proto_key_idx = ctx.add_constant(Constant::String("prototype".to_string())); let proto_key_reg = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(proto_key_reg), proto_key_idx)); - ctx.inst(Inst::new( - OpCode::SET_PROP, + // 类构造器 .prototype 属性:writable/enumerable/configurable 全 false(MakeConstructor)。 + ctx.inst(Inst::define_prop_attrs( Operand::Reg(ctor_reg), Operand::Reg(proto_reg), Operand::Reg(proto_key_reg), + 0b000, )); Ok(()) } diff --git a/crates/oxide_ir/src/contract.rs b/crates/oxide_ir/src/contract.rs index f6dbb1a..1c5ee56 100644 --- a/crates/oxide_ir/src/contract.rs +++ b/crates/oxide_ir/src/contract.rs @@ -761,6 +761,8 @@ mod tests { assert_group(&[OpCode::NEW_OBJECT, OpCode::NEW_ARRAY], Some(1), &[], true); // define 语义属性写入 / arguments / rest assert_group(&[OpCode::DEFINE_PROP, OpCode::DEFINE_GLOBAL_PROP], None, &[1, 2, 3], false); + assert_contract(OpCode::DEFINE_PROP_ATTRS, None, &[1, 2, 3], false); + assert_contract(OpCode::DEFINE_ACCESSOR_ATTRS, None, &[1, 2, 3], false); assert_group(&[OpCode::CREATE_ARGUMENTS, OpCode::CREATE_REST_ARRAY], Some(1), &[], false); // 成员更新:val 槽原地写(a/b 槽) assert_group(&[OpCode::MEMBER_INC, OpCode::MEMBER_DEC], Some(2), &[1, 3], false); diff --git a/crates/oxide_ir/src/inst.rs b/crates/oxide_ir/src/inst.rs index c1a981d..bc42bdb 100644 --- a/crates/oxide_ir/src/inst.rs +++ b/crates/oxide_ir/src/inst.rs @@ -199,6 +199,11 @@ impl Inst { Self::with_ext(OpCode::DEFINE_ACCESSOR, home, get, set, &[key_idx]) } + + /// 定义访问器属性并指定描述符:ext = [key_idx, attrs](attrs 位同 DEFINE_PROP_ATTRS)。 + pub fn define_accessor_attrs(home: Operand, get: Operand, set: Operand, key_idx: u32, attrs: u32) -> Self { + Self::with_ext(OpCode::DEFINE_ACCESSOR_ATTRS, home, get, set, &[key_idx, attrs]) + } /// 定义访问器属性(运行时计算键):home 为宿主对象,get/set 为访问器函数寄存器, /// key_reg 为键值寄存器(编码进 ext[0],高位标记 `0x8000_0000 | key_reg`)。 pub fn define_accessor_dynamic(home: Operand, get: Operand, set: Operand, key_reg: u32) -> Self { @@ -211,6 +216,12 @@ impl Inst { Self::new(OpCode::DEFINE_PROP, target, value, key) } + + /// define 数据属性并指定描述符:ext = [attrs](bit0=writable, bit1=enumerable, bit2=configurable, + /// 与 PropAttributes 位一致);class 方法/constructor/prototype 需要非枚举或不可写描述符。 + pub fn define_prop_attrs(target: Operand, value: Operand, key: Operand, attrs: u32) -> Self { + Self::with_ext(OpCode::DEFINE_PROP_ATTRS, target, value, key, &[attrs]) + } /// 定义全局 var 绑定数据属性:target 为全局对象,value/key 为寄存器。 /// 属性可写/可枚举/不可配置(脚本顶层 var/function 声明的属性描述符)。 pub fn define_global_prop(target: Operand, value: Operand, key: Operand) -> Self { diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index e062525..61d219d 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -1459,6 +1459,20 @@ impl Vm { self.dispatch_define_global_prop(rd, a, b)?; } + + OpCode::DEFINE_PROP_ATTRS => { + let attrs = self.bytecode[self.pc] as u8; + self.pc += 1; + self.dispatch_define_prop_attrs(rd, a, b, attrs)?; + } + + OpCode::DEFINE_ACCESSOR_ATTRS => { + let key_word = self.bytecode[self.pc]; + let attrs = self.bytecode[self.pc + 1] as u8; + self.pc += 2; + self.dispatch_define_accessor_attrs(rd, a, b, key_word, attrs)?; + } + OpCode::RETURN => match self.dispatch_return(rd) { Ok(Some(result)) => return Ok(result), Ok(None) => {} diff --git a/crates/oxide_vm/src/vm_dispatch_ctrl.rs b/crates/oxide_vm/src/vm_dispatch_ctrl.rs index ff2b104..9f04644 100644 --- a/crates/oxide_vm/src/vm_dispatch_ctrl.rs +++ b/crates/oxide_vm/src/vm_dispatch_ctrl.rs @@ -195,6 +195,50 @@ impl Vm { } } + /// define 数据属性并指定描述符:ext 字为 attrs(bit0=writable, bit1=enumerable, bit2=configurable,与 PropAttributes 位一致)。 + pub(crate) fn dispatch_define_prop_attrs(&mut self, rd: usize, a: usize, b: usize, attrs: u8) -> Result<(), String> { + vm_trace!("DEFINE_PROP_ATTRS rd={} value={} key={} attrs={:#04b}", rd, a, b, attrs); + let obj_val = self.regs[rd]; + if !obj_val.is_object() { + return self.raise_type_error("DEFINE_PROP_ATTRS target is not object"); + } + let prop_name_si = self.property_key_si(self.regs[b])?; + let value = self.regs[a]; + let obj = unsafe { &mut *obj_val.as_js_object_ptr() }; + match self.define_data_property(obj, prop_name_si, value, PropAttributes(attrs)) { + Ok(()) => Ok(()), + Err(msg) => self.raise_error_kind("TypeError", &msg), + } + } + + /// 定义访问器属性并指定描述符:ext = [key 常量下标, attrs],key 编码同 DEFINE_ACCESSOR。 + pub(crate) fn dispatch_define_accessor_attrs( + &mut self, rd: usize, a: usize, b: usize, key_word: u32, attrs: u8, + ) -> Result<(), String> { + vm_trace!("DEFINE_ACCESSOR_ATTRS rd={} getter={} setter={} attrs={:#04b}", rd, a, b, attrs); + let prop_idx = key_word as usize; + if prop_idx >= self.immutables().len() { + return self.raise_type_error("DEFINE_ACCESSOR_ATTRS constant index out of bounds"); + } + let key_val = self.immutables()[prop_idx]; + let prop_name_si = if key_val.is_int() { + make_private_name_id(key_val.as_int().max(0) as u32) + } else { + self.property_key_si(key_val)? + }; + let obj_val = self.regs[rd]; + if !obj_val.is_object() { + return self.raise_type_error("DEFINE_ACCESSOR_ATTRS target is not object"); + } + let getter = self.regs[a]; + let setter = self.regs[b]; + let obj = unsafe { &mut *obj_val.as_js_object_ptr() }; + match self.define_accessor_property(obj, prop_name_si, getter, setter, PropAttributes(attrs)) { + Ok(()) => Ok(()), + Err(msg) => self.raise_error_kind("TypeError", &msg), + } + } + /// 定义全局 var 绑定数据属性:rd=全局对象,a=值,b=键。 /// 属性可写/可枚举/不可配置(CreateGlobalVarBinding 的属性描述符)。 ///