mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 对象方法 super 门加计算型 super 成员加 VM 步序对齐
This commit is contained in:
Generated
+19
-19
@@ -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",
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.13.24"
|
||||
version = "0.13.25"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
||||
@@ -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 保留为无子模块哨兵
|
||||
|
||||
@@ -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<u32, String> {
|
||||
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<u32, String> {
|
||||
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<u32, String> {
|
||||
// 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)?;
|
||||
|
||||
@@ -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<u32, String> {
|
||||
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<u32, String> {
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<JsValue> = (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<bool, String> {
|
||||
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;
|
||||
|
||||
@@ -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<JsValue, String> {
|
||||
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 实例");
|
||||
}
|
||||
Reference in New Issue
Block a user