fix: class 方法/访问器/constructor/prototype 属性描述符(非枚举/只读),新增 DEFINE_PROP_ATTRS/DEFINE_ACCESSOR_ATTRS

- opcode.rs/inst.rs/contract.rs/vm.rs/vm_dispatch_ctrl.rs:新增两个带 attrs 扩展字的 define 指令
  (attrs bit0=writable, bit1=enumerable, bit2=configurable,与 PropAttributes 一致)
- class/method.rs:方法与非计算访问器改按规范非枚举安装(DefineMethod 语义)
- class/prototype.rs:proto.constructor 非枚举;ctor.prototype 三标志全 false(MakeConstructor)
- 效果:language/statements/class 1782 pass/948 fail -> 1910 pass/716 fail(+128/-232)
This commit is contained in:
2026-08-12 14:11:02 +08:00
parent 5b884d6e75
commit f1b7a908f9
7 changed files with 98 additions and 20 deletions
+9
View File
@@ -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)],
+12 -16
View File
@@ -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,
+6 -4
View File
@@ -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(())
}
+2
View File
@@ -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);
+11
View File
@@ -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 {
+14
View File
@@ -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) => {}
+44
View File
@@ -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 的属性描述符)。
///