From 958cc1f7a276b404fde468a67186d9a1277d9364 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sat, 10 Oct 2026 01:37:39 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20super=20=E5=B1=9E=E6=80=A7=E5=86=99?= =?UTF-8?q?=E6=96=B0=E5=BB=BA=20SUPER=5FPUT=5FPROP=20=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 38 ++--- Cargo.toml | 2 +- crates/oxide_bytecode/src/opcode.rs | 6 + crates/oxide_emit/src/expr/assignment.rs | 86 +++++++++- crates/oxide_emit/src/expr/member/mod.rs | 12 ++ crates/oxide_emit/src/expr/operator/mod.rs | 67 ++++++++ crates/oxide_vm/src/dispatch/call.rs | 36 ++++ crates/oxide_vm/src/vm/dispatch_loop.rs | 6 + crates/oxide_vm/tests/super_property_tests.rs | 158 ++++++++++++++++++ 9 files changed, 390 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d0ef521..85a92dd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.13.25" +version = "0.13.26" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.13.25" +version = "0.13.26" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.13.25" +version = "0.13.26" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.13.25" +version = "0.13.26" dependencies = [ "ansi_term", "bumpalo", @@ -1226,7 +1226,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.13.25" +version = "0.13.26" dependencies = [ "lru", "oxide_bytecode", @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.13.25" +version = "0.13.26" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1255,7 +1255,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.13.25" +version = "0.13.26" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.13.25" +version = "0.13.26" dependencies = [ "num-bigint", "oxc_allocator", @@ -1284,7 +1284,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.13.25" +version = "0.13.26" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.13.25" +version = "0.13.26" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1305,7 +1305,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.13.25" +version = "0.13.26" dependencies = [ "bumpalo", "dashmap", @@ -1319,7 +1319,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.13.25" +version = "0.13.26" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1331,7 +1331,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.13.25" +version = "0.13.26" dependencies = [ "tracing", "tracing-appender", @@ -1340,7 +1340,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.13.25" +version = "0.13.26" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1355,7 +1355,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.13.25" +version = "0.13.26" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1371,7 +1371,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.13.25" +version = "0.13.26" dependencies = [ "num-bigint", "num-traits", @@ -1384,7 +1384,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.13.25" +version = "0.13.26" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1402,7 +1402,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.13.25" +version = "0.13.26" dependencies = [ "bumpalo", "dashmap", @@ -1415,7 +1415,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.13.25" +version = "0.13.26" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 72b082a7..f9a58f68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.13.25" +version = "0.13.26" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_bytecode/src/opcode.rs b/crates/oxide_bytecode/src/opcode.rs index 8e7f33c3..789dea7e 100644 --- a/crates/oxide_bytecode/src/opcode.rs +++ b/crates/oxide_bytecode/src/opcode.rs @@ -536,6 +536,12 @@ define_opcodes! { DISPOSE_POP = 0x6B => "DISPOSE_POP", def = None, uses = [], pure = false, jump = false, term = false, ic = false, + // super 属性写:rd=值、a=this(receiver)、b=键。运行期 GetSuperBase 先于 + // ToPropertyKey(与 SUPER_GET_PROP 同步序),基非对象抛 TypeError,写经 + // ordinary_set(strict 由 current_strict 判定,失败抛 TypeError)。 + SUPER_PUT_PROP = 0x6C => "SUPER_PUT_PROP", + def = None, uses = [SlotSpec::Slot(Slot::Rd), SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], + pure = false, jump = false, term = false, ic = false, // ── define 语义属性写入 (0x6D, 0x6F) ── DEFINE_PROP = 0x6D => "DEFINE_PROP", diff --git a/crates/oxide_emit/src/expr/assignment.rs b/crates/oxide_emit/src/expr/assignment.rs index bb5ea0ab..65ef5f58 100644 --- a/crates/oxide_emit/src/expr/assignment.rs +++ b/crates/oxide_emit/src/expr/assignment.rs @@ -6,7 +6,7 @@ use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; -use oxide_parser::AssignmentOperator; +use oxide_parser::{AssignmentOperator, Expression}; impl Emitter { /// 标识符复合赋值的静态路径:upvalue / 被捕获 cell 走显式读取-运算-写回 @@ -168,10 +168,63 @@ impl Emitter { } } + /// 复合赋值运算符映射到二元运算指令(super 属性写与计算成员动态路径共用)。 + fn super_compound_op(&self, op: AssignmentOperator) -> Result { + Ok(match op { + AssignmentOperator::Addition => OpCode::ADD, + AssignmentOperator::Subtraction => OpCode::SUB, + AssignmentOperator::Multiplication => OpCode::MUL, + AssignmentOperator::Division => OpCode::DIV, + AssignmentOperator::Remainder => OpCode::MOD, + AssignmentOperator::Exponential => OpCode::COMPOUND_EXP, + AssignmentOperator::BitwiseAnd => OpCode::BIT_AND, + AssignmentOperator::BitwiseOR => OpCode::BIT_OR, + AssignmentOperator::BitwiseXOR => OpCode::BIT_XOR, + AssignmentOperator::ShiftLeft => OpCode::SHL, + AssignmentOperator::ShiftRight => OpCode::SHR, + AssignmentOperator::ShiftRightZeroFill => OpCode::USHR, + _ => return Err(format!("compound assignment operator {:?} not supported", op)), + }) + } + pub(crate) fn emit_assignment_expression( &self, assign: &oxide_parser::AssignmentExpression, ctx: &mut CompileCtx, ) -> Result { if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left { + // super 属性写:this 先装载(GetThisBinding),键常量装载,后发 SUPER_PUT_PROP + // (运行期 GetSuperBase 先于 ToPropertyKey,步序与 super 读对齐)。 + if matches!(&member.object, Expression::Super(_)) { + if assign.operator.to_logical_operator().is_some() { + return Err("logical assignment to super property not supported".into()); + } + let this_reg = self.emit_super_this(ctx)?; + 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.inst(Inst::load_const(Operand::Reg(key_reg), idx)); + if assign.operator != AssignmentOperator::Assign { + // 复合赋值:先读旧值(SUPER_GET_PROP),求值 RHS,运算,后写回。 + let val_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::SUPER_GET_PROP, + Operand::Reg(val_reg), + Operand::Reg(this_reg), + Operand::Reg(key_reg), + )); + let rhs = self.emit_expression(&assign.right, ctx)?; + let op = self.super_compound_op(assign.operator)?; + if assign.operator == AssignmentOperator::Exponential { + ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None)); + } else { + ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(rhs))); + } + self.emit_super_put(this_reg, key_reg, val_reg, ctx); + return Ok(val_reg); + } + let val_reg = self.emit_expression(&assign.right, ctx)?; + self.emit_super_put(this_reg, key_reg, val_reg, ctx); + return Ok(val_reg); + } if let Some(logical_op) = assign.operator.to_logical_operator() { let store_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); @@ -232,6 +285,37 @@ impl Emitter { Ok(val_reg) } } else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = &assign.left { + // super 计算成员写:this 先装载(GetThisBinding),键表达式后求值, + // 后发 SUPER_PUT_PROP(运行期 GetSuperBase 先于 ToPropertyKey)。 + if matches!(&member.object, Expression::Super(_)) { + if assign.operator.to_logical_operator().is_some() { + return Err("logical assignment to super property not supported".into()); + } + let this_reg = self.emit_super_this(ctx)?; + let key_reg = self.emit_expression(&member.expression, ctx)?; + if assign.operator != AssignmentOperator::Assign { + // 复合赋值:先读旧值(SUPER_GET_PROP),求值 RHS,运算,后写回。 + let val_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::SUPER_GET_PROP, + Operand::Reg(val_reg), + Operand::Reg(this_reg), + Operand::Reg(key_reg), + )); + let rhs = self.emit_expression(&assign.right, ctx)?; + let op = self.super_compound_op(assign.operator)?; + if assign.operator == AssignmentOperator::Exponential { + ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None)); + } else { + ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(rhs))); + } + self.emit_super_put(this_reg, key_reg, val_reg, ctx); + return Ok(val_reg); + } + let val_reg = self.emit_expression(&assign.right, ctx)?; + self.emit_super_put(this_reg, key_reg, val_reg, ctx); + return Ok(val_reg); + } // 常量字符串键折叠为 IC 静态路径,与 StaticMemberExpression 分支同构。 if let Some(key) = crate::expr::member::computed_const_key(&member.expression) { if let Some(logical_op) = assign.operator.to_logical_operator() { diff --git a/crates/oxide_emit/src/expr/member/mod.rs b/crates/oxide_emit/src/expr/member/mod.rs index 4184a833..fe950b30 100644 --- a/crates/oxide_emit/src/expr/member/mod.rs +++ b/crates/oxide_emit/src/expr/member/mod.rs @@ -37,6 +37,18 @@ impl Emitter { result_reg } + /// super 属性写:给定 this 寄存器、键寄存器与值寄存器,发 `SUPER_PUT_PROP` + /// (GetSuperBase + RequireObjectCoercible + ToPropertyKey + Set 在运行期完成, + /// 步序与 super 读对齐)。 + pub(crate) fn emit_super_put(&self, this_reg: u32, key_reg: u32, val_reg: u32, ctx: &mut CompileCtx) { + ctx.inst(Inst::new( + OpCode::SUPER_PUT_PROP, + Operand::Reg(val_reg), + Operand::Reg(this_reg), + Operand::Reg(key_reg), + )); + } + fn emit_static_member_expression( &self, member: &oxide_parser::StaticMemberExpression, ctx: &mut CompileCtx, ) -> Result { diff --git a/crates/oxide_emit/src/expr/operator/mod.rs b/crates/oxide_emit/src/expr/operator/mod.rs index 4535fdf1..cc94c745 100644 --- a/crates/oxide_emit/src/expr/operator/mod.rs +++ b/crates/oxide_emit/src/expr/operator/mod.rs @@ -579,6 +579,41 @@ impl Emitter { self.emit_identifier_update_static(name, update, ctx) } SimpleAssignmentTarget::StaticMemberExpression(member) => { + // super 属性自增/自减:this 先装载(GetThisBinding),键常量装载, + // 读旧值(SUPER_GET_PROP),运算,写回(SUPER_PUT_PROP)。 + if matches!(&member.object, Expression::Super(_)) { + let this_reg = self.emit_super_this(ctx)?; + let prop_name = member.property.name.as_str(); + let key_idx = ctx.add_constant(Constant::String(prop_name.to_string())); + let key_reg = ctx.alloc_reg(); + ctx.inst(Inst::load_const(Operand::Reg(key_reg), key_idx)); + let old_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::SUPER_GET_PROP, + Operand::Reg(old_reg), + Operand::Reg(this_reg), + Operand::Reg(key_reg), + )); + let one_idx = ctx.add_constant(Constant::Int(1)); + let one_reg = ctx.alloc_reg(); + ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx)); + let new_reg = ctx.alloc_reg(); + let op = if update.operator == UpdateOperator::Increment { + OpCode::ADD + } else { + OpCode::SUB + }; + ctx.inst(Inst::new(op, Operand::Reg(new_reg), Operand::Reg(old_reg), Operand::Reg(one_reg))); + self.emit_super_put(this_reg, key_reg, new_reg, ctx); + let result_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::LOAD_VAR, + Operand::Reg(result_reg), + Operand::Reg(if update.prefix { new_reg } else { old_reg }), + Operand::None, + )); + return Ok(result_reg); + } let obj_reg = self.emit_expression(&member.object, ctx)?; let prop_name = member.property.name.as_str(); let key_idx = ctx.add_constant(Constant::String(prop_name.to_string())); @@ -601,6 +636,38 @@ impl Emitter { Ok(val_reg) } SimpleAssignmentTarget::ComputedMemberExpression(member) => { + // super 计算成员自增/自减:this 先装载(GetThisBinding),键表达式 + // 后求值,读旧值(SUPER_GET_PROP),运算,写回(SUPER_PUT_PROP)。 + if matches!(&member.object, Expression::Super(_)) { + let this_reg = self.emit_super_this(ctx)?; + let key_reg = self.emit_expression(&member.expression, ctx)?; + let old_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::SUPER_GET_PROP, + Operand::Reg(old_reg), + Operand::Reg(this_reg), + Operand::Reg(key_reg), + )); + let one_idx = ctx.add_constant(Constant::Int(1)); + let one_reg = ctx.alloc_reg(); + ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx)); + let new_reg = ctx.alloc_reg(); + let op = if update.operator == UpdateOperator::Increment { + OpCode::ADD + } else { + OpCode::SUB + }; + ctx.inst(Inst::new(op, Operand::Reg(new_reg), Operand::Reg(old_reg), Operand::Reg(one_reg))); + self.emit_super_put(this_reg, key_reg, new_reg, ctx); + let result_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::LOAD_VAR, + Operand::Reg(result_reg), + Operand::Reg(if update.prefix { new_reg } else { old_reg }), + Operand::None, + )); + return Ok(result_reg); + } let obj_reg = self.emit_expression(&member.object, ctx)?; // 常量字符串键折叠为 IC 静态路径:MEMBER_INC/DEC 携带 IC 扩展字。 if let Some(key) = crate::expr::member::computed_const_key(&member.expression) { diff --git a/crates/oxide_vm/src/dispatch/call.rs b/crates/oxide_vm/src/dispatch/call.rs index 2aff1fc9..220f7431 100644 --- a/crates/oxide_vm/src/dispatch/call.rs +++ b/crates/oxide_vm/src/dispatch/call.rs @@ -794,6 +794,42 @@ impl Vm { Ok(false) } + /// super 属性写:GetSuperBase 先于 ToPropertyKey(与 super 读同步序),基非对象 + /// 抛 TypeError(RequireObjectCoercible 面),写经 ordinary_set(strict 由 + /// current_strict 判定,失败抛 TypeError)。rd=值、a=this(receiver)、b=键。 + pub(crate) fn dispatch_super_put_prop(&mut self, rd: usize, a: usize, b: usize) -> Result { + vm_trace!("SUPER_PUT_PROP rd={} a={} b={}", rd, a, b); + let Some(frame) = self.frames.last() else { + self.raise_error_kind("ReferenceError", "super property used outside function")?; + return Ok(true); + }; + if !frame.callee.is_object() { + self.raise_error_kind("ReferenceError", "super property has no home object")?; + return Ok(true); + } + let callee_obj = unsafe { &*frame.callee.as_js_object_ptr() }; + let home_object = callee_obj.home_object(); + if !home_object.is_object() { + self.raise_error_kind("ReferenceError", "super property has no home object")?; + return Ok(true); + } + let home_obj = unsafe { &*home_object.as_js_object_ptr() }; + // 规范步序:GetSuperBase 先于 ToPropertyKey;基非对象可强转时抛 + // TypeError(RequireObjectCoercible 面)。 + let super_base = home_obj.proto(); + if !super_base.is_object() { + self.raise_type_error("Cannot convert null or undefined to object")?; + return Ok(true); + } + let key_val = self.regs[b]; + let prop_name_si = self.property_key_si(key_val)?; + let super_obj = unsafe { &mut *super_base.as_js_object_ptr() }; + let value = self.regs[rd]; + let receiver = self.regs[a]; + self.ordinary_set_dispatch(super_obj, prop_name_si, value, receiver, self.current_strict())?; + Ok(false) + } + pub(crate) fn dispatch_set_home_object(&mut self, rd: usize, a: usize) -> Result { vm_trace!("SET_HOME_OBJECT rd={} a={}", rd, a); let func_val = self.regs[rd]; diff --git a/crates/oxide_vm/src/vm/dispatch_loop.rs b/crates/oxide_vm/src/vm/dispatch_loop.rs index 6c428b1d..7ae48742 100644 --- a/crates/oxide_vm/src/vm/dispatch_loop.rs +++ b/crates/oxide_vm/src/vm/dispatch_loop.rs @@ -531,6 +531,12 @@ impl Vm { } } + OpCode::SUPER_PUT_PROP => match self.dispatch_super_put_prop(rd, a, b) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + OpCode::SET_HOME_OBJECT => match self.dispatch_set_home_object(rd, a) { Ok(true) => continue, Ok(false) => {} diff --git a/crates/oxide_vm/tests/super_property_tests.rs b/crates/oxide_vm/tests/super_property_tests.rs index 4d6aaf18..c317ce3f 100644 --- a/crates/oxide_vm/tests/super_property_tests.rs +++ b/crates/oxide_vm/tests/super_property_tests.rs @@ -189,3 +189,161 @@ fn super_call_native_constructor_still_works() { .unwrap(); assert!(result.is_bool() && result.as_bool(), "super() 后实例应为 Object 实例"); } + +// 对象方法 super.x 写(静态成员):写经 proto 链落基(super base),receiver 为方法接收者。 +#[test] +fn object_method_super_put_static_member() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var base = { x: 'a' }; \ + var obj = { __proto__: base, method() { super.x = 'written'; return base.x; } }; \ + obj.method()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "written"); +} + +// 对象方法 super['x'] 写(计算成员):键运行期求值,写经 proto 链落基。 +#[test] +fn object_method_super_put_computed_member() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var base = { x: 'a' }; \ + var obj = { __proto__: base, method() { super['x'] = 'written'; return base.x; } }; \ + obj.method()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "written"); +} + +// 严格模式 super 写失败(基对象冻结)抛 TypeError。 +#[test] +fn object_method_super_put_strict_frozen_throws_type_error() { + let mut vm = Vm::new(); + let err = eval( + &mut vm, + "'use strict'; \ + var base = { y: 0 }; Object.freeze(base); \ + var obj = { __proto__: base, method() { super.y = 9; } }; \ + try { obj.method(); } catch (e) { e.name; }", + ) + .unwrap(); + assert_eq!(vm.lookup_str(err).unwrap(), "TypeError"); +} + +// 非严格模式 super 写失败(基对象冻结)静默跳过,不抛错。 +#[test] +fn object_method_super_put_non_strict_frozen_silent() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var base = { y: 0 }; Object.freeze(base); \ + var obj = { __proto__: base, method() { super.y = 9; return 'done'; } }; \ + obj.method()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "done"); +} + +// 键对象 toString 副作用换 proto:GetSuperBase 先于 ToPropertyKey,写旧 proto。 +#[test] +fn object_method_super_put_getsuperbase_before_topropertykey() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var proto = { p: 1 }; var proto2 = { p: -1 }; \ + var obj = { __proto__: proto, m() { super[key] = 10; } }; \ + var key = { toString() { Object.setPrototypeOf(obj, proto2); return 'p'; } }; \ + obj.m(); proto.p + '|' + proto2.p", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "10|-1"); +} + +// 复合赋值 GetSuperBase 先于 ToPropertyKey:读旧值、运算、写回均用旧 proto 基。 +#[test] +fn object_method_super_put_compound_getsuperbase_before_topropertykey() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var proto = { p: 1 }; var proto2 = { p: -1 }; \ + var obj = { __proto__: proto, m() { return super[key] += 1; } }; \ + var key = { toString() { Object.setPrototypeOf(obj, proto2); return 'p'; } }; \ + String(obj.m())", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "2"); +} + +// 自增 GetSuperBase 先于 ToPropertyKey:前缀自增返回新值。 +#[test] +fn object_method_super_put_increment_getsuperbase_before_topropertykey() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var proto = { p: 1 }; var proto2 = { p: -1 }; \ + var obj = { __proto__: proto, m() { return ++super[key]; } }; \ + var key = { toString() { Object.setPrototypeOf(obj, proto2); return 'p'; } }; \ + String(obj.m())", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "2"); +} + +// 派生构造器 this 未初始化时 super[键] = 值:GetThisBinding 先于键求值,抛 ReferenceError。 +#[test] +fn derived_ctor_super_put_uninitialized_this_throws_reference_error() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class Base { constructor() { throw new Error('base constructor'); } } \ + class Derived extends Base { constructor() { super[super()] = 0; } } \ + try { new Derived(); } catch (e) { e.name; }", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "ReferenceError"); +} + +// 派生构造器 this 未初始化时 super[键] += 值:GetThisBinding 先于键求值,抛 ReferenceError。 +#[test] +fn derived_ctor_super_put_compound_uninitialized_this_throws_reference_error() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class Base { constructor() { throw new Error('base constructor'); } } \ + class Derived extends Base { constructor() { super[super()] += 0; } } \ + try { new Derived(); } catch (e) { e.name; }", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "ReferenceError"); +} + +// 派生构造器 this 未初始化时 ++super[键]:GetThisBinding 先于键求值,抛 ReferenceError。 +#[test] +fn derived_ctor_super_put_increment_uninitialized_this_throws_reference_error() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class Base { constructor() { throw new Error('base constructor'); } } \ + class Derived extends Base { constructor() { ++super[super()]; } } \ + try { new Derived(); } catch (e) { e.name; }", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "ReferenceError"); +} + +// home proto 为 null 时 super 写抛 TypeError(RequireObjectCoercible 面)。 +#[test] +fn object_method_super_put_null_proto_throws_type_error() { + let mut vm = Vm::new(); + let err = eval( + &mut vm, + "var obj = { method() { super.x = 1; } }; \ + Object.setPrototypeOf(obj, null); \ + try { obj.method(); } catch (e) { e.name; }", + ) + .unwrap(); + assert_eq!(vm.lookup_str(err).unwrap(), "TypeError"); +}