diff --git a/Cargo.lock b/Cargo.lock index 308ecbb2..9d0ef521 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.13.24" +version = "0.13.25" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.13.24" +version = "0.13.25" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.13.24" +version = "0.13.25" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.13.24" +version = "0.13.25" dependencies = [ "ansi_term", "bumpalo", @@ -1226,7 +1226,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.13.24" +version = "0.13.25" dependencies = [ "lru", "oxide_bytecode", @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.13.24" +version = "0.13.25" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1255,7 +1255,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.13.24" +version = "0.13.25" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.13.24" +version = "0.13.25" dependencies = [ "num-bigint", "oxc_allocator", @@ -1284,7 +1284,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.13.24" +version = "0.13.25" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.13.24" +version = "0.13.25" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1305,7 +1305,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.13.24" +version = "0.13.25" dependencies = [ "bumpalo", "dashmap", @@ -1319,7 +1319,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.13.24" +version = "0.13.25" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1331,7 +1331,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.13.24" +version = "0.13.25" dependencies = [ "tracing", "tracing-appender", @@ -1340,7 +1340,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.13.24" +version = "0.13.25" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1355,7 +1355,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.13.24" +version = "0.13.25" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1371,7 +1371,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.13.24" +version = "0.13.25" dependencies = [ "num-bigint", "num-traits", @@ -1384,7 +1384,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.13.24" +version = "0.13.25" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1402,7 +1402,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.13.24" +version = "0.13.25" dependencies = [ "bumpalo", "dashmap", @@ -1415,7 +1415,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.13.24" +version = "0.13.25" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index b28c0c5a..72b082a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.13.24" +version = "0.13.25" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_emit/src/expr/call.rs b/crates/oxide_emit/src/expr/call.rs index 40bef76f..defda3c4 100644 --- a/crates/oxide_emit/src/expr/call.rs +++ b/crates/oxide_emit/src/expr/call.rs @@ -53,45 +53,18 @@ impl Emitter { (callee_reg, obj_reg) } Expression::StaticMemberExpression(member) => { - let is_super_member = matches!(&member.object, Expression::Super(_)); - let obj_reg = if is_super_member { - let this_reg = ctx.alloc_reg(); - ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(this_reg), Operand::This, Operand::None)); - this_reg + if matches!(&member.object, Expression::Super(_)) { + 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)); + let callee_reg = self.emit_super_get(this_reg, key_reg, ctx); + (callee_reg, this_reg) } else { - self.emit_expression(&member.object, 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)); - let callee_reg = ctx.alloc_reg(); - if is_super_member { - if !ctx.in_instance_method && !ctx.in_static_method && !ctx.in_derived_constructor { - return Err("super property only supported in class methods".into()); - } - let op = if ctx.in_static_method { - OpCode::SUPER_STATIC_GET_PROP - } else { - OpCode::SUPER_GET_PROP - }; - ctx.inst(Inst::new(op, Operand::Reg(callee_reg), Operand::Reg(obj_reg), Operand::Reg(key_reg))); - } else { - ctx.inst(Inst::new( - OpCode::LOAD_VAR, - Operand::Reg(callee_reg), - Operand::Reg(obj_reg), - Operand::None, - )); - ctx.inst(Inst::ic_get(Operand::Reg(callee_reg), Operand::Reg(key_reg))); - } - (callee_reg, obj_reg) - } - Expression::ComputedMemberExpression(member) => { - let obj_reg = self.emit_expression(&member.object, ctx)?; - // 常量字符串键折叠为 IC 静态路径:callee 寄存器独立于对象,this 保留对象。 - if let Some(key) = crate::expr::member::computed_const_key(&member.expression) { - let idx = ctx.add_constant(Constant::String(key)); + let obj_reg = self.emit_expression(&member.object, 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)); let callee_reg = ctx.alloc_reg(); @@ -103,16 +76,41 @@ impl Emitter { )); ctx.inst(Inst::ic_get(Operand::Reg(callee_reg), Operand::Reg(key_reg))); (callee_reg, obj_reg) - } else { + } + } + Expression::ComputedMemberExpression(member) => { + if matches!(&member.object, Expression::Super(_)) { + let this_reg = self.emit_super_this(ctx)?; let key_reg = self.emit_expression(&member.expression, ctx)?; - let callee_reg = ctx.alloc_reg(); - ctx.inst(Inst::new( - OpCode::GET_PROP_DYNAMIC, - Operand::Reg(obj_reg), - Operand::Reg(key_reg), - Operand::Reg(callee_reg), - )); - (callee_reg, obj_reg) + let callee_reg = self.emit_super_get(this_reg, key_reg, ctx); + (callee_reg, this_reg) + } else { + let obj_reg = self.emit_expression(&member.object, ctx)?; + // 常量字符串键折叠为 IC 静态路径:callee 寄存器独立于对象,this 保留对象。 + if let Some(key) = crate::expr::member::computed_const_key(&member.expression) { + let idx = ctx.add_constant(Constant::String(key)); + let key_reg = ctx.alloc_reg(); + ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx)); + let callee_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::LOAD_VAR, + Operand::Reg(callee_reg), + Operand::Reg(obj_reg), + Operand::None, + )); + ctx.inst(Inst::ic_get(Operand::Reg(callee_reg), Operand::Reg(key_reg))); + (callee_reg, obj_reg) + } else { + let key_reg = self.emit_expression(&member.expression, ctx)?; + let callee_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::GET_PROP_DYNAMIC, + Operand::Reg(obj_reg), + Operand::Reg(key_reg), + Operand::Reg(callee_reg), + )); + (callee_reg, obj_reg) + } } } _ => { diff --git a/crates/oxide_emit/src/expr/function/mod.rs b/crates/oxide_emit/src/expr/function/mod.rs index 2b412aca..acdff31d 100644 --- a/crates/oxide_emit/src/expr/function/mod.rs +++ b/crates/oxide_emit/src/expr/function/mod.rs @@ -61,6 +61,11 @@ impl Emitter { )? }; sub_module.is_arrow = true; + // 箭头函数词法继承 super 上下文:父带任一 super 标志时,本箭头须挂 + // home object(运行期从外层函数继承,见 CREATE_CLOSURE 分发)。 + if ctx.in_instance_method || ctx.in_static_method || ctx.in_derived_constructor { + sub_module.needs_home_object = true; + } ctx.nested.push(sub_module); // 子模块下标 1 起始:0 保留为无子模块哨兵 diff --git a/crates/oxide_emit/src/expr/member/mod.rs b/crates/oxide_emit/src/expr/member/mod.rs index dd95ea3e..4184a833 100644 --- a/crates/oxide_emit/src/expr/member/mod.rs +++ b/crates/oxide_emit/src/expr/member/mod.rs @@ -10,27 +10,43 @@ use oxide_ir::operand::Operand; use oxide_parser::Expression; impl Emitter { + /// super 属性读门判定与 this 装载(GetThisBinding):this 未初始化时 + /// 运行期抛 ReferenceError(VM 侧)。返回 this 寄存器。 + /// + /// 键装载须在本方法之后(规范序:GetThisBinding 先于键表达式求值)。 + pub(crate) fn emit_super_this(&self, ctx: &mut CompileCtx) -> Result { + if !ctx.in_instance_method && !ctx.in_static_method && !ctx.in_derived_constructor { + return Err("super property only supported in class methods".into()); + } + let this_reg = ctx.alloc_reg(); + ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(this_reg), Operand::This, Operand::None)); + Ok(this_reg) + } + + /// super 属性读:给定 this 寄存器与键寄存器,发 `SUPER_GET_PROP` / + /// `SUPER_STATIC_GET_PROP`(GetSuperBase + RequireObjectCoercible + + /// ToPropertyKey + Get 在运行期完成)。返回结果寄存器。 + pub(crate) fn emit_super_get(&self, this_reg: u32, key_reg: u32, ctx: &mut CompileCtx) -> u32 { + let result_reg = ctx.alloc_reg(); + let op = if ctx.in_static_method { + OpCode::SUPER_STATIC_GET_PROP + } else { + OpCode::SUPER_GET_PROP + }; + ctx.inst(Inst::new(op, Operand::Reg(result_reg), Operand::Reg(this_reg), Operand::Reg(key_reg))); + result_reg + } + fn emit_static_member_expression( &self, member: &oxide_parser::StaticMemberExpression, ctx: &mut CompileCtx, ) -> Result { if matches!(&member.object, Expression::Super(_)) { - if !ctx.in_instance_method && !ctx.in_static_method && !ctx.in_derived_constructor { - return Err("super property only supported in class methods".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)); - let this_reg = ctx.alloc_reg(); - ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(this_reg), Operand::This, Operand::None)); - let result_reg = ctx.alloc_reg(); - let op = if ctx.in_static_method { - OpCode::SUPER_STATIC_GET_PROP - } else { - OpCode::SUPER_GET_PROP - }; - ctx.inst(Inst::new(op, Operand::Reg(result_reg), Operand::Reg(this_reg), Operand::Reg(key_reg))); - return Ok(result_reg); + return Ok(self.emit_super_get(this_reg, key_reg, ctx)); } let obj_reg = self.emit_expression(&member.object, ctx)?; let prop_name = member.property.name.as_str(); @@ -44,6 +60,13 @@ impl Emitter { fn emit_computed_member_expression( &self, member: &oxide_parser::ComputedMemberExpression, ctx: &mut CompileCtx, ) -> Result { + // super['x']:this 先装载(GetThisBinding),键表达式后求值(规范序), + // 后走 super 读。 + if matches!(&member.object, Expression::Super(_)) { + let this_reg = self.emit_super_this(ctx)?; + let key_reg = self.emit_expression(&member.expression, ctx)?; + return Ok(self.emit_super_get(this_reg, key_reg, ctx)); + } // 常量字符串键折叠为 IC 静态路径:免去运行期键 interning 与慢路径 ordinary_get。 if let Some(key) = computed_const_key(&member.expression) { let obj_reg = self.emit_expression(&member.object, ctx)?; diff --git a/crates/oxide_emit/src/expr/object/object_literal.rs b/crates/oxide_emit/src/expr/object/object_literal.rs index b6d2152f..be51996c 100644 --- a/crates/oxide_emit/src/expr/object/object_literal.rs +++ b/crates/oxide_emit/src/expr/object/object_literal.rs @@ -8,6 +8,35 @@ use oxide_ir::operand::Operand; use oxide_parser::{ObjectPropertyKind, PropertyKind}; impl Emitter { + /// 对象方法值发射:体编译期置 `in_instance_method`(清静态/派生构造器标志, + /// 与类方法同形),闭包物化后给方法函数挂 home object(home = 对象字面量本身) + /// 并置 `needs_home_object`。 + /// + /// 仅箭头/函数表达式值挂 home(类表达式排除:类表达式方法由类自身 emit 挂 + /// home object,外层对象不作 home);非函数值只作普通值发射。 + fn emit_object_method_value( + &self, value: &oxide_parser::Expression, obj_reg: u32, ctx: &mut CompileCtx, + ) -> Result { + let saved_instance = ctx.in_instance_method; + let saved_static = ctx.in_static_method; + let saved_derived = ctx.in_derived_constructor; + ctx.in_instance_method = true; + ctx.in_static_method = false; + ctx.in_derived_constructor = false; + let val_reg = self.emit_expression(value, ctx); + ctx.in_instance_method = saved_instance; + ctx.in_static_method = saved_static; + ctx.in_derived_constructor = saved_derived; + let val_reg = val_reg?; + if is_object_method_fn(value) { + if let Some(sub_mod) = ctx.nested.last_mut() { + sub_mod.needs_home_object = true; + } + ctx.inst(Inst::new(OpCode::SET_HOME_OBJECT, Operand::Reg(val_reg), Operand::Reg(obj_reg), Operand::None)); + } + Ok(val_reg) + } + pub(crate) fn emit_object_expression( &self, obj: &oxide_parser::ObjectExpression, ctx: &mut CompileCtx, ) -> Result { @@ -37,7 +66,7 @@ impl Emitter { let ObjectPropertyKind::ObjectProperty(p) = prop else { unreachable!("批量前缀必为 ObjectProperty"); }; - let val_reg = self.emit_expression(&p.value, ctx)?; + let val_reg = self.emit_object_method_value(&p.value, obj_reg, ctx)?; if crate::is_anonymous_function_definition(&p.value) { if let Some(sub_mod) = ctx.nested.last_mut() { sub_mod.function_name = Some(self.class_property_name(&p.key).expect("批量前缀键为静态名")); @@ -112,7 +141,7 @@ impl Emitter { } else { None }; - let accessor_reg = self.emit_expression(&p.value, ctx)?; + let accessor_reg = self.emit_object_method_value(&p.value, obj_reg, ctx)?; if !computed { if let Some(sub_mod) = ctx.nested.last_mut() { // 访问器函数名带 "get "/"set " 前缀(SetFunctionName 语义)。 @@ -162,7 +191,7 @@ impl Emitter { ctx.inst(Inst::load_const(Operand::Reg(reg), idx)); reg }; - let val_reg = self.emit_expression(&p.value, ctx)?; + let val_reg = self.emit_object_method_value(&p.value, obj_reg, ctx)?; if crate::is_anonymous_function_definition(&p.value) { if let Some(sub_mod) = ctx.nested.last_mut() { sub_mod.function_name = Some(prop_name.to_string()); @@ -180,3 +209,13 @@ impl Emitter { Ok(()) } } + +/// 对象方法函数值(剥括号):箭头/函数表达式。类表达式排除——其方法由类自身 +/// emit 挂 home object,外层对象不作 home。 +fn is_object_method_fn(value: &oxide_parser::Expression) -> bool { + match value { + oxide_parser::Expression::ArrowFunctionExpression(_) | oxide_parser::Expression::FunctionExpression(_) => true, + oxide_parser::Expression::ParenthesizedExpression(p) => is_object_method_fn(&p.expression), + _ => false, + } +} diff --git a/crates/oxide_emit/src/expr/template/tagged.rs b/crates/oxide_emit/src/expr/template/tagged.rs index 9bc59d84..430bc5b1 100644 --- a/crates/oxide_emit/src/expr/template/tagged.rs +++ b/crates/oxide_emit/src/expr/template/tagged.rs @@ -88,22 +88,12 @@ impl Emitter { let pair = match tag { Expression::StaticMemberExpression(member) => { if matches!(&member.object, Expression::Super(_)) { - if !ctx.in_instance_method && !ctx.in_static_method && !ctx.in_derived_constructor { - return Err("super property only supported in class methods".into()); - } + let this_reg = self.emit_super_this(ctx)?; let prop_name = member.property.name.as_str(); let key_reg = ctx.alloc_reg(); let idx = ctx.add_constant(Constant::String(prop_name.to_string())); ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx)); - let this_reg = ctx.alloc_reg(); - ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(this_reg), Operand::This, Operand::None)); - let callee_reg = ctx.alloc_reg(); - let op = if ctx.in_static_method { - OpCode::SUPER_STATIC_GET_PROP - } else { - OpCode::SUPER_GET_PROP - }; - ctx.inst(Inst::new(op, Operand::Reg(callee_reg), Operand::Reg(this_reg), Operand::Reg(key_reg))); + let callee_reg = self.emit_super_get(this_reg, key_reg, ctx); (callee_reg, this_reg) } else { let obj_reg = self.emit_expression(&member.object, ctx)?; @@ -123,30 +113,37 @@ impl Emitter { } } Expression::ComputedMemberExpression(member) => { - let obj_reg = self.emit_expression(&member.object, ctx)?; - if let Some(key) = crate::expr::member::computed_const_key(&member.expression) { - let key_reg = ctx.alloc_reg(); - let idx = ctx.add_constant(Constant::String(key)); - ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx)); - let callee_reg = ctx.alloc_reg(); - ctx.inst(Inst::new( - OpCode::LOAD_VAR, - Operand::Reg(callee_reg), - Operand::Reg(obj_reg), - Operand::None, - )); - ctx.inst(Inst::ic_get(Operand::Reg(callee_reg), Operand::Reg(key_reg))); - (callee_reg, obj_reg) - } else { + if matches!(&member.object, Expression::Super(_)) { + let this_reg = self.emit_super_this(ctx)?; let key_reg = self.emit_expression(&member.expression, ctx)?; - let callee_reg = ctx.alloc_reg(); - ctx.inst(Inst::new( - OpCode::GET_PROP_DYNAMIC, - Operand::Reg(obj_reg), - Operand::Reg(key_reg), - Operand::Reg(callee_reg), - )); - (callee_reg, obj_reg) + let callee_reg = self.emit_super_get(this_reg, key_reg, ctx); + (callee_reg, this_reg) + } else { + let obj_reg = self.emit_expression(&member.object, ctx)?; + if let Some(key) = crate::expr::member::computed_const_key(&member.expression) { + let key_reg = ctx.alloc_reg(); + let idx = ctx.add_constant(Constant::String(key)); + ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx)); + let callee_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::LOAD_VAR, + Operand::Reg(callee_reg), + Operand::Reg(obj_reg), + Operand::None, + )); + ctx.inst(Inst::ic_get(Operand::Reg(callee_reg), Operand::Reg(key_reg))); + (callee_reg, obj_reg) + } else { + let key_reg = self.emit_expression(&member.expression, ctx)?; + let callee_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::GET_PROP_DYNAMIC, + Operand::Reg(obj_reg), + Operand::Reg(key_reg), + Operand::Reg(callee_reg), + )); + (callee_reg, obj_reg) + } } } Expression::PrivateFieldExpression(member) => { diff --git a/crates/oxide_emit/src/function_body.rs b/crates/oxide_emit/src/function_body.rs index a82977c2..0b44c272 100644 --- a/crates/oxide_emit/src/function_body.rs +++ b/crates/oxide_emit/src/function_body.rs @@ -366,7 +366,14 @@ impl Emitter { ctx.source_encoded = parent_ctx.source_encoded; // 箭头函数词法继承 super;类方法体顶层编译也需要类提供的 super 上下文。 - if matches!(body_context, FunctionBodyContext::Arrow | FunctionBodyContext::ClassElement) { + // 对象方法体(Ordinary 上下文)在父带任一 super 上下文时同样继承:对象方法 + // 发射期已置父 ctx 的 in_instance_method,其内嵌套箭头/普通函数经此继承。 + // 非法 super 位置(嵌套普通函数内等)由解析器前置拒绝,emit 侧只收到合法程序。 + if matches!(body_context, FunctionBodyContext::Arrow | FunctionBodyContext::ClassElement) + || parent_ctx.in_derived_constructor + || parent_ctx.in_instance_method + || parent_ctx.in_static_method + { ctx.in_derived_constructor = parent_ctx.in_derived_constructor; ctx.in_instance_method = parent_ctx.in_instance_method; ctx.in_static_method = parent_ctx.in_static_method; diff --git a/crates/oxide_emit/tests/emit_ir_tests.rs b/crates/oxide_emit/tests/emit_ir_tests.rs index e03017ee..8236cebc 100644 --- a/crates/oxide_emit/tests/emit_ir_tests.rs +++ b/crates/oxide_emit/tests/emit_ir_tests.rs @@ -276,6 +276,37 @@ fn class_nested_flags() { assert!(tree_count(&ir, OpCode::GET_PRIVATE) >= 1, "私有成员读面应存在"); } +/// 面 9:对象字面量方法挂 home object(模块标 needs_home_object 且树内存在 +/// SET_HOME_OBJECT),计算型 super 成员发 SUPER_GET_PROP 且 this 装载先于键表达式。 +#[test] +fn object_method_home_object_and_computed_super() { + let src = "var o = { m() { return super.x; }, g() { return super[k]; } };"; + let ir = emit_ir(src); + let (mut _base, mut _derived, mut home) = (false, false, false); + class_flags_scan(&ir, &mut _base, &mut _derived, &mut home); + assert!(home, "对象方法应标 needs_home_object"); + assert!(tree_count(&ir, OpCode::SET_HOME_OBJECT) >= 2, "对象方法应挂 SET_HOME_OBJECT"); + assert!(tree_count(&ir, OpCode::SUPER_GET_PROP) >= 2, "super 读应发 SUPER_GET_PROP"); + + // 计算型 super 成员:this 装载(LOAD_VAR This)先于键表达式求值(规范序)。 + let m_body = ir + .nested + .iter() + .find(|f| f.function_name.as_deref() == Some("g")) + .expect("方法 g 应存在"); + let this_pos = m_body + .insts + .iter() + .position(|i| i.op == OpCode::LOAD_VAR && matches!(i.a, Operand::This)) + .expect("应存在 LOAD_VAR This"); + let super_pos = m_body + .insts + .iter() + .position(|i| i.op == OpCode::SUPER_GET_PROP) + .expect("应存在 SUPER_GET_PROP"); + assert!(this_pos < super_pos, "this 装载应先于 SUPER_GET_PROP"); +} + /// 面 8:严格模式标志在函数体内与脚本顶层的继承口径。 #[test] fn strict_flag_inherited() { diff --git a/crates/oxide_vm/src/dispatch/call.rs b/crates/oxide_vm/src/dispatch/call.rs index 1fffc979..2aff1fc9 100644 --- a/crates/oxide_vm/src/dispatch/call.rs +++ b/crates/oxide_vm/src/dispatch/call.rs @@ -202,6 +202,19 @@ impl Vm { is_derived_constructor, needs_home_object, ); + // 箭头函数词法继承 super 上下文:home object 从外层函数(当前 callee) + // 继承。类方法由 SET_HOME_OBJECT 显式挂接,不经此路径。 + if is_arrow && needs_home_object { + if let Some(callee) = self.current_callee() { + if callee.is_object() { + let home = unsafe { &*callee.as_js_object_ptr() }.home_object(); + if home.is_object() { + let func_obj = unsafe { &mut *result.as_js_object_ptr() }; + func_obj.set_home_object(home); + } + } + } + } // 函数名推断:emit 端在变量声明/对象属性赋值点设置 function_name。 let func_obj = unsafe { &mut *result.as_js_object_ptr() }; let length_si = self.length_si; @@ -642,18 +655,25 @@ impl Vm { return Ok(true); } let super_obj = unsafe { &*super_ctor.as_js_object_ptr() }; - if !super_obj.is_function() { - self.raise_error_kind("TypeError", "super constructor is not a function")?; - return Ok(true); - } if super_obj.native_fn().is_some() { - // 实参先物化再走同步调用收口:native 调用协议把 253/254 槽固定为 + // 实参先物化再走 IsConstructor 检查(规范序:ArgumentListEvaluation + // 先于 IsConstructor);native 调用协议把 253/254 槽固定为 // receiver/callee,实参寄存器(颜色 ≤253)占位 253 时裸引用会被覆写 // (与 SUPER_CALL_SPREAD 路径同形)。 let args: Vec = (0..arg_count) .map(|i| self.regs[first_arg_reg.wrapping_add(i as u8) as usize]) .collect(); + if !super_obj.is_function() { + self.raise_error_kind("TypeError", "super constructor is not a function")?; + return Ok(true); + } + // native 函数仅带构造器类型标时可构造(与 NEW 路径同口径): + // 非构造器 proto 的 super 调用在实参求值后抛 TypeError。 + if super_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR { + self.raise_error_kind("TypeError", "super constructor is not a constructor")?; + return Ok(true); + } // 构造形态标记夹持(SUPER native 臂不写 reg255,new.target 继承 // 外层类构造器帧,形态判定只能靠本标记)。 let saved_constructing = self.constructing_native; @@ -742,8 +762,6 @@ impl Vm { pub(crate) fn dispatch_super_get_prop(&mut self, rd: usize, a: usize, b: usize) -> Result { vm_trace!("SUPER_GET_PROP rd={} a={} b={}", rd, a, b); - let key_val = self.regs[b]; - let prop_name_si = self.property_key_si(key_val)?; let Some(frame) = self.frames.last() else { self.raise_error_kind("ReferenceError", "super property used outside function")?; return Ok(true); @@ -759,15 +777,19 @@ impl Vm { 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.regs[rd] = JsValue::undefined(); - } else { - let super_obj = unsafe { &*super_base.as_js_object_ptr() }; - let val = self.ordinary_get_with_target(super_obj, prop_name_si, self.regs[a], rd as u8)?; - if self.accessor_frame_target_reg.take().is_none() { - self.regs[rd] = val; - } + 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 { &*super_base.as_js_object_ptr() }; + let val = self.ordinary_get_with_target(super_obj, prop_name_si, self.regs[a], rd as u8)?; + if self.accessor_frame_target_reg.take().is_none() { + self.regs[rd] = val; } Ok(false) } @@ -1174,17 +1196,26 @@ impl Vm { return Ok(true); } let super_obj = unsafe { &*super_ctor.as_js_object_ptr() }; - if !super_obj.is_function() { - self.raise_error_kind("TypeError", "super constructor is not a function")?; - return Ok(true); - } let args = match self.materialize_spread_args(&words)? { Some(args) => args, None => return Ok(true), }; + // IsConstructor 检查在实参物化之后(规范序:ArgumentListEvaluation + // 先于 IsConstructor)。 + if !super_obj.is_function() { + self.raise_error_kind("TypeError", "super constructor is not a function")?; + return Ok(true); + } + if super_obj.native_fn().is_some() { + // native 函数仅带构造器类型标时可构造(与 NEW 路径同口径): + // 非构造器 proto 的 super 调用在实参求值后抛 TypeError。 + if super_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR { + self.raise_error_kind("TypeError", "super constructor is not a constructor")?; + return Ok(true); + } // 构造形态标记夹持(SUPER native 臂不写 reg255,new.target 继承 // 外层类构造器帧,形态判定只能靠本标记)。 let saved_constructing = self.constructing_native; diff --git a/crates/oxide_vm/tests/super_property_tests.rs b/crates/oxide_vm/tests/super_property_tests.rs new file mode 100644 index 00000000..4d6aaf18 --- /dev/null +++ b/crates/oxide_vm/tests/super_property_tests.rs @@ -0,0 +1,191 @@ +//! super 属性读运行期面:对象字面量方法门、home object 挂接、嵌套箭头继承、 +//! null-proto 抛 TypeError、GetSuperBase 先于 ToPropertyKey、super 调用 IsConstructor +//! 后移、派生构造器 this 未初始化键求值面。 + +use std::sync::Arc; + +use oxide_compiler::compiler::Compiler; +use oxide_types::value::JsValue; +use oxide_vm::vm::Vm; + +fn eval(vm: &mut Vm, source: &str) -> Result { + let allocator = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; + let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?; + vm.run(&Arc::new(module)) +} + +// 对象方法 super.x 读:proto 链取值,receiver 为方法接收者。 +#[test] +fn object_method_super_read_walks_proto_chain() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var A = { fromA: 'a', fromB: 'a' }; var B = { fromB: 'b' }; \ + Object.setPrototypeOf(B, A); \ + var obj = { fromA: 'c', fromB: 'c', method() { return super.fromA + '|' + super.fromB; } }; \ + Object.setPrototypeOf(obj, B); obj.method()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "a|b"); +} + +// 对象方法内嵌套箭头继承 super 上下文:home object 从外层方法继承。 +#[test] +fn object_method_nested_arrow_inherits_super() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var A = { fromA: 'a', fromB: 'a' }; var B = { fromB: 'b' }; \ + Object.setPrototypeOf(B, A); \ + var obj = { method() { return (() => super.fromA + super.fromB)(); } }; \ + Object.setPrototypeOf(obj, B); obj.method()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "ab"); +} + +// home proto 为 null 时 super 读抛 TypeError(RequireObjectCoercible 面)。 +#[test] +fn object_method_super_read_null_proto_throws_type_error() { + let mut vm = Vm::new(); + let err = eval( + &mut vm, + "var obj = { method() { return super.x; } }; \ + Object.setPrototypeOf(obj, null); \ + try { obj.method(); } catch (e) { e.name; }", + ) + .unwrap(); + assert_eq!(vm.lookup_str(err).unwrap(), "TypeError"); +} + +// 键对象 toString 副作用换 proto:GetSuperBase 先于 ToPropertyKey,读旧 proto 值。 +#[test] +fn object_method_super_read_getsuperbase_before_topropertykey() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var proto = { p: 'ok' }; var proto2 = { p: 'bad' }; \ + var obj = { __proto__: proto, m() { return super[key]; } }; \ + var key = { toString() { Object.setPrototypeOf(obj, proto2); return 'p'; } }; \ + obj.m()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "ok"); +} + +// super 调用 IsConstructor 检查在实参物化之后:非构造器 proto 时实参副作用先执行。 +#[test] +fn super_call_evaluates_args_before_is_constructor() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var evaluatedArg = false; var caught; \ + class C extends Object { constructor() { \ + try { super(evaluatedArg = true); } catch (err) { caught = err; } \ + } } \ + Object.setPrototypeOf(C, parseInt); \ + try { new C(); } catch (_) {} \ + evaluatedArg + '|' + (caught ? caught.name : 'none')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "true|TypeError"); +} + +// 派生构造器 this 未初始化时 super[键]:GetThisBinding 先于键求值,抛 ReferenceError。 +#[test] +fn derived_ctor_super_computed_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() { return super[super()]; } } \ + try { new Derived(); } catch (e) { e.name; }", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "ReferenceError"); +} + +// 对象 getter 内 super.x(Get/Set 臂 home object 面)。 +#[test] +fn object_getter_super_read_uses_home_object() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var A = { v: 'a' }; var B = { v: 'b' }; Object.setPrototypeOf(B, A); \ + var obj = { get g() { return super.v; } }; \ + Object.setPrototypeOf(obj, B); obj.g", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "b"); +} + +// 对象 setter 内 super.x(Set 臂 home object 面)。 +#[test] +fn object_setter_super_read_uses_home_object() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var A = { v: 'a' }; var B = { v: 'b' }; Object.setPrototypeOf(B, A); \ + var seen; var obj = { set s(x) { seen = super.v; } }; \ + Object.setPrototypeOf(obj, B); obj.s = 1; seen", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "b"); +} + +// 类表达式值不挂外层对象作 home:其方法 super 读以类自身 proto 为基。 +#[test] +fn class_expression_value_not_homed_to_outer_object() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "function Base() {} Base.prototype.q = 'base'; \ + var C = class extends Base { m() { return super.q; } }; \ + var o = { c: C }; new C().m()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "base"); +} + +// 对象方法 super 调用:callee 为 proto 链上的方法,receiver 为方法接收者。 +#[test] +fn object_method_super_call_walks_proto_chain() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var A = { f() { return 'A:' + this.tag; } }; var B = { f() { return 'B:' + this.tag; } }; \ + Object.setPrototypeOf(B, A); \ + var obj = { tag: 't', m() { return super.f(); } }; \ + Object.setPrototypeOf(obj, B); obj.m()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "B:t"); +} + +// 类方法 super 计算成员读:this 未初始化(派生构造器)时抛 ReferenceError。 +#[test] +fn class_method_super_computed_uninitialized_this_throws() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class Base { constructor() { throw new Error('base'); } } \ + class Derived extends Base { constructor() { return super['x']; } } \ + try { new Derived(); } catch (e) { e.name; }", + ) + .unwrap(); + assert_eq!(vm.lookup_str(result).unwrap(), "ReferenceError"); +} + +// super 调用:native 构造器 proto 正常路径(回归面,IsConstructor 检查后移不破坏)。 +#[test] +fn super_call_native_constructor_still_works() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class C extends Object { constructor(n) { super(n); } } \ + new C(5) instanceof Object", + ) + .unwrap(); + assert!(result.is_bool() && result.as_bool(), "super() 后实例应为 Object 实例"); +}