mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: super 属性写新建 SUPER_PUT_PROP 指令
This commit is contained in:
Generated
+19
-19
@@ -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",
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.13.25"
|
||||
version = "0.13.26"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<OpCode, String> {
|
||||
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<u32, String> {
|
||||
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() {
|
||||
|
||||
@@ -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<u32, String> {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<bool, String> {
|
||||
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<bool, String> {
|
||||
vm_trace!("SET_HOME_OBJECT rd={} a={}", rd, a);
|
||||
let func_val = self.regs[rd];
|
||||
|
||||
@@ -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) => {}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user