mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: class 公有字段 define 语义 + computed key 类定义期求值一次
This commit is contained in:
@@ -173,6 +173,9 @@ define_opcodes! {
|
||||
// ── 对象 (0x63) ──
|
||||
CREATE_ARGUMENTS = 0x63 => "CREATE_ARGUMENTS",
|
||||
|
||||
// ── define 语义属性写入 (0x6D) ──
|
||||
DEFINE_PROP = 0x6D => "DEFINE_PROP",
|
||||
|
||||
// ── 成员复合赋值:位/移位 (0x64-0x69) ──
|
||||
COMPOUND_MEMBER_BIT_AND = 0x64 => "COMPOUND_MEMBER_BIT_AND",
|
||||
COMPOUND_MEMBER_BIT_OR = 0x65 => "COMPOUND_MEMBER_BIT_OR",
|
||||
|
||||
@@ -3,19 +3,21 @@
|
||||
//! 函数:`emit_class`。
|
||||
|
||||
use crate::{CompileCtx, Emitter, FunctionBodyContext};
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{Class, ClassElement, MethodDefinitionKind, PropertyKey};
|
||||
use oxide_parser::{Class, ClassElement, Expression, MethodDefinitionKind, PropertyKey};
|
||||
|
||||
impl Emitter {
|
||||
pub(crate) fn emit_class(&self, class: &Class, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
let elements = &class.body.body;
|
||||
let mut constructor_method = None;
|
||||
let mut instance_fields = Vec::new();
|
||||
let mut instance_field_indices = Vec::new();
|
||||
let mut private_names = Vec::<(String, u32)>::new();
|
||||
let is_derived = class.super_class.is_some();
|
||||
|
||||
for element in &class.body.body {
|
||||
for (idx, element) in elements.iter().enumerate() {
|
||||
match element {
|
||||
ClassElement::MethodDefinition(method) => {
|
||||
let method = method.as_ref();
|
||||
@@ -47,7 +49,7 @@ impl Emitter {
|
||||
private_names.push((name, id));
|
||||
}
|
||||
if !prop.r#static {
|
||||
instance_fields.push(prop);
|
||||
instance_field_indices.push(idx);
|
||||
}
|
||||
}
|
||||
ClassElement::AccessorProperty(_) => return Err("class accessor properties not yet supported".into()),
|
||||
@@ -55,6 +57,29 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
// 计算键 slot 分配:全部元素源码序(方法 + 公有字段),私有元素非 computed。
|
||||
let mut computed_slot = 0u8;
|
||||
let key_slots: Vec<Option<u8>> = elements
|
||||
.iter()
|
||||
.map(|e| {
|
||||
let computed = match e {
|
||||
ClassElement::MethodDefinition(m) => m.computed,
|
||||
ClassElement::PropertyDefinition(p) => {
|
||||
p.computed && !matches!(p.key, PropertyKey::PrivateIdentifier(_))
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if computed {
|
||||
let s = computed_slot;
|
||||
computed_slot += 1;
|
||||
Some(s)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let any_computed = computed_slot > 0;
|
||||
|
||||
let (ctor_reg, proto_reg, super_reg) = self.emit_class_header(class, ctx)?;
|
||||
let ctor_name = class.id.as_ref().map(|id| id.name.to_string());
|
||||
let self_binding = ctor_name.as_deref().map(|name| vec![(name, ctor_reg)]).unwrap_or_default();
|
||||
@@ -63,8 +88,33 @@ impl Emitter {
|
||||
ctx.in_derived_constructor = is_derived;
|
||||
ctx.scopes.private_name_map = private_names.clone();
|
||||
|
||||
// 实例公有字段 computed key 求值于构造器帧外,须类定义期存入数组。构造器以
|
||||
// upvalue 捕获该数组:父作用域登记 `@@field_keys`(cell_idx 取现有最大 +1)。
|
||||
let has_computed_instance = instance_field_indices.iter().any(|&i| {
|
||||
let ClassElement::PropertyDefinition(p) = &elements[i] else { return false };
|
||||
p.computed && !matches!(p.key, PropertyKey::PrivateIdentifier(_))
|
||||
});
|
||||
let field_key_cell: Option<u8> = if has_computed_instance {
|
||||
let cell_idx = ctx.captured_bindings.values().copied().max().map_or(0, |m| m.saturating_add(1));
|
||||
ctx.captured_bindings.insert("@@field_keys".to_string(), cell_idx);
|
||||
Some(cell_idx)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// 字段值表达式运行于构造器帧:其自由变量须纳入构造器 upvalue 捕获。
|
||||
let field_value_exprs: Vec<&Expression> = instance_field_indices
|
||||
.iter()
|
||||
.filter_map(|&i| match &elements[i] {
|
||||
ClassElement::PropertyDefinition(p) => p.value.as_ref(),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
let extra_upvalue_names: Vec<(&str, u8)> = field_key_cell.map(|c| ("@@field_keys", c)).into_iter().collect();
|
||||
|
||||
let emit_instance_fields = |compiler: &Emitter, field_ctx: &mut CompileCtx| -> Result<(), String> {
|
||||
for field in &instance_fields {
|
||||
for &idx in &instance_field_indices {
|
||||
let ClassElement::PropertyDefinition(field) = &elements[idx] else { unreachable!() };
|
||||
let field = field.as_ref();
|
||||
if let PropertyKey::PrivateIdentifier(private) = &field.key {
|
||||
compiler.emit_private_field_init(
|
||||
Operand::This,
|
||||
@@ -78,6 +128,7 @@ impl Emitter {
|
||||
&field.key,
|
||||
field.computed,
|
||||
field.value.as_ref(),
|
||||
key_slots[idx],
|
||||
field_ctx,
|
||||
)?;
|
||||
}
|
||||
@@ -96,6 +147,8 @@ impl Emitter {
|
||||
FunctionBodyContext::ClassElement,
|
||||
Some(&emit_instance_fields),
|
||||
is_derived,
|
||||
&field_value_exprs,
|
||||
&extra_upvalue_names,
|
||||
)?
|
||||
} else {
|
||||
let mut module = self.compile_function_body_with_field_hooks(
|
||||
@@ -107,14 +160,24 @@ impl Emitter {
|
||||
FunctionBodyContext::ClassElement,
|
||||
Some(&emit_instance_fields),
|
||||
is_derived,
|
||||
&field_value_exprs,
|
||||
&extra_upvalue_names,
|
||||
)?;
|
||||
if is_derived {
|
||||
module.insts.clear();
|
||||
module.constants.clear();
|
||||
module.n_registers = 1;
|
||||
module.insts.push(Inst::super_call(Operand::None, Operand::None, 0));
|
||||
// 字段初始化直接重发:构造器 upvalue/内置槽引用须与首轮编译产物对齐。
|
||||
let mut field_ctx = CompileCtx::new();
|
||||
field_ctx.scopes.private_name_map = private_names.clone();
|
||||
field_ctx.scopes.builtin_reg_map = module.builtin_reg_map.clone();
|
||||
field_ctx.current_upvalue_captures = module.upvalue_captures.clone();
|
||||
field_ctx.field_keys_uv = module
|
||||
.upvalue_captures
|
||||
.iter()
|
||||
.position(|u| u.name == "@@field_keys")
|
||||
.map(|i| i as u8);
|
||||
emit_instance_fields(self, &mut field_ctx)?;
|
||||
module.insts.extend(field_ctx.insts);
|
||||
module.constants = field_ctx.constants;
|
||||
@@ -130,10 +193,53 @@ impl Emitter {
|
||||
ctor_module.is_derived_constructor = is_derived;
|
||||
ctor_module.function_name = ctor_name.clone();
|
||||
ctx.nested.push(ctor_module);
|
||||
// 键数组构建会在 keys 表达式中压入嵌套模块(箭头/函数/类键),
|
||||
// 必须在压入后用固定下标引用构造器模块,避免嵌套长度偏移。
|
||||
let ctor_sub_idx = ctx.nested.len() as u16;
|
||||
|
||||
self.emit_class_prototype(ctor_reg, proto_reg, super_reg, ctx.nested.len() as u16, ctx)?;
|
||||
self.emit_class_methods(&class.body.body, ctor_reg, proto_reg, &self_binding, ctx)?;
|
||||
self.emit_class_static_elements(&class.body.body, ctor_reg, ctx)?;
|
||||
// 类定义期按源码序求值全部 computed key(extends 之后、构造器闭包创建之前),
|
||||
// 存数组供方法/静态字段/实例字段初始化阶段按 slot 取用。
|
||||
if any_computed {
|
||||
let keys_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::NEW_ARRAY,
|
||||
Operand::Reg(keys_reg),
|
||||
Operand::Imm(computed_slot as u16),
|
||||
Operand::None,
|
||||
));
|
||||
for (idx, element) in elements.iter().enumerate() {
|
||||
let Some(slot) = key_slots[idx] else { continue };
|
||||
let key_expr = match element {
|
||||
ClassElement::MethodDefinition(m) => m.key.as_expression(),
|
||||
ClassElement::PropertyDefinition(p) => p.key.as_expression(),
|
||||
_ => None,
|
||||
};
|
||||
let Some(expr) = key_expr else { continue };
|
||||
let key_reg = self.emit_expression(expr, ctx)?;
|
||||
let idx_reg = ctx.alloc_reg();
|
||||
let idx_const = ctx.add_constant(Constant::Int(slot as i32));
|
||||
ctx.inst(Inst::load_const(Operand::Reg(idx_reg), idx_const));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_ELEM,
|
||||
Operand::Reg(keys_reg),
|
||||
Operand::Reg(idx_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
}
|
||||
if let Some(cell_idx) = field_key_cell {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::MAKE_CELL,
|
||||
Operand::Reg(keys_reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
Operand::None,
|
||||
));
|
||||
}
|
||||
ctx.class_keys_reg = Some(keys_reg);
|
||||
}
|
||||
|
||||
self.emit_class_prototype(ctor_reg, proto_reg, super_reg, ctor_sub_idx, ctx)?;
|
||||
self.emit_class_methods(&class.body.body, ctor_reg, proto_reg, &self_binding, &key_slots, ctx)?;
|
||||
self.emit_class_static_elements(&class.body.body, ctor_reg, &key_slots, ctx)?;
|
||||
|
||||
ctx.scopes.private_name_map = saved_private_names;
|
||||
Ok(ctor_reg)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! 类静态字段/元素 emit:`emit_class_static_elements`。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{ClassElement, PropertyKey};
|
||||
|
||||
@@ -10,14 +11,14 @@ impl Emitter {
|
||||
/// static z = this.y + 1` 必须按此顺序),若全部字段先于静态块发出,
|
||||
/// `z` 会在 `y` 赋值前读到它。
|
||||
pub(crate) fn emit_class_static_elements(
|
||||
&self, elements: &[ClassElement], ctor_reg: u32, ctx: &mut CompileCtx,
|
||||
&self, elements: &[ClassElement], ctor_reg: u32, key_slots: &[Option<u8>], ctx: &mut CompileCtx,
|
||||
) -> Result<(), String> {
|
||||
let saved_static_this = ctx.static_block_this_reg;
|
||||
// static_block_this_reg 保留 u8(this 槽语义,254 特判),ctor_reg 超 u8 界时报错而非静默截断。
|
||||
ctx.static_block_this_reg = Some(
|
||||
u8::try_from(ctor_reg).map_err(|_| format!("class constructor register {ctor_reg} exceeds u8 limit"))?,
|
||||
);
|
||||
for element in elements {
|
||||
for (element, &key_slot) in elements.iter().zip(key_slots) {
|
||||
match element {
|
||||
ClassElement::PropertyDefinition(prop) => {
|
||||
let prop = prop.as_ref();
|
||||
@@ -30,13 +31,18 @@ impl Emitter {
|
||||
ctx,
|
||||
)?;
|
||||
} else {
|
||||
self.emit_public_field_init(
|
||||
// 静态字段在类定义期求值:computed key 从类定义期数组取,其余用常量。
|
||||
let key_reg = if prop.computed {
|
||||
self.emit_class_array_key_reg(key_slot.unwrap_or(0), ctx)?
|
||||
} else {
|
||||
self.emit_class_key_reg(&prop.key, false, ctx)?
|
||||
};
|
||||
let value_reg = self.emit_field_value(prop.value.as_ref(), ctx)?;
|
||||
ctx.inst(Inst::define_prop(
|
||||
Operand::Reg(ctor_reg),
|
||||
&prop.key,
|
||||
prop.computed,
|
||||
prop.value.as_ref(),
|
||||
ctx,
|
||||
)?;
|
||||
Operand::Reg(value_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ use oxide_parser::{ClassElement, MethodDefinitionKind, PropertyKey};
|
||||
impl Emitter {
|
||||
pub(crate) fn emit_class_methods(
|
||||
&self, elements: &[ClassElement], ctor_reg: u32, proto_reg: u32, self_binding: &[(&str, u32)],
|
||||
ctx: &mut CompileCtx,
|
||||
key_slots: &[Option<u8>], ctx: &mut CompileCtx,
|
||||
) -> Result<(), String> {
|
||||
for element in elements {
|
||||
for (element, &key_slot) in elements.iter().zip(key_slots) {
|
||||
if let ClassElement::MethodDefinition(method) = element {
|
||||
let method = method.as_ref();
|
||||
if method.kind == MethodDefinitionKind::Constructor {
|
||||
@@ -25,7 +25,11 @@ impl Emitter {
|
||||
continue;
|
||||
}
|
||||
let home_reg = if method.r#static { ctor_reg } else { proto_reg };
|
||||
let key_reg = self.emit_class_key_reg(&method.key, method.computed, ctx)?;
|
||||
let key_reg = if method.computed {
|
||||
self.emit_class_array_key_reg(key_slot.unwrap_or(0), ctx)?
|
||||
} else {
|
||||
self.emit_class_key_reg(&method.key, false, ctx)?
|
||||
};
|
||||
let method_name = if method.computed {
|
||||
"<computed>".to_string()
|
||||
} else {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use oxide_bytecode::module::UpvalueCapture;
|
||||
use oxide_parser::{Expression, Statement};
|
||||
use oxide_parser::{ClassBody, ClassElement, Expression, Statement};
|
||||
|
||||
use crate::Emitter;
|
||||
|
||||
@@ -127,6 +127,15 @@ impl Emitter {
|
||||
self.collect_fn_default_names(&fd.params, ref_set, shadow, out);
|
||||
self.collect_capture_names_shadowed(body, ref_set, &inner, out);
|
||||
}
|
||||
Statement::ClassDeclaration(cd) => {
|
||||
// 类体(构造器/方法体、字段键与值、静态块)是嵌套作用域:
|
||||
// 类名遮蔽外层绑定,方法形参与方法体局部进一步遮蔽。
|
||||
let mut class_shadow = shadow.clone();
|
||||
if let Some(id) = &cd.id {
|
||||
class_shadow.insert(id.name.as_str().to_string());
|
||||
}
|
||||
self.collect_class_capture_names(&cd.body, ref_set, &class_shadow, out);
|
||||
}
|
||||
Statement::IfStatement(is) => {
|
||||
self.collect_capture_names_expr(&is.test, ref_set, shadow, out);
|
||||
self.collect_capture_names_stmt(&is.consequent, ref_set, shadow, out);
|
||||
@@ -211,6 +220,38 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
/// 收集类体对 `ref_set` 的引用:方法/构造器体、字段键与值、静态块。
|
||||
/// 方法形参与方法体局部声明遮蔽外层绑定(捕获判定用)。
|
||||
fn collect_class_capture_names(
|
||||
&self, class_body: &ClassBody, ref_set: &HashSet<String>, class_shadow: &HashSet<String>,
|
||||
out: &mut HashSet<String>,
|
||||
) {
|
||||
for element in &class_body.body {
|
||||
match element {
|
||||
ClassElement::MethodDefinition(method) => {
|
||||
let mut inner = class_shadow.clone();
|
||||
inner.extend(self.collect_fn_param_names(&method.value.params));
|
||||
let body: &[Statement] = method.value.body.as_ref().map(|b| &b.statements[..]).unwrap_or(&[]);
|
||||
inner.extend(self.collect_own_binding_names(&[], body));
|
||||
self.collect_fn_default_names(&method.value.params, ref_set, class_shadow, out);
|
||||
self.collect_capture_names_shadowed(body, ref_set, &inner, out);
|
||||
}
|
||||
ClassElement::PropertyDefinition(prop) => {
|
||||
if let Some(expr) = prop.key.as_expression() {
|
||||
self.collect_capture_names_expr(expr, ref_set, class_shadow, out);
|
||||
}
|
||||
if let Some(value) = &prop.value {
|
||||
self.collect_capture_names_expr(value, ref_set, class_shadow, out);
|
||||
}
|
||||
}
|
||||
ClassElement::StaticBlock(block) => {
|
||||
self.collect_capture_names_shadowed(&block.body, ref_set, class_shadow, out);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 遍历 catch 参数解构模式:收集模式内表达式引用(默认值等),声明名写入
|
||||
/// `catch_shadow` 遮蔽 catch 作用域。
|
||||
fn collect_capture_names_binding_pattern(
|
||||
@@ -302,6 +343,14 @@ impl Emitter {
|
||||
self.collect_fn_default_names(&ae.params, ref_set, shadow, out);
|
||||
self.collect_capture_names_shadowed(&ae.body.statements, ref_set, &inner, out);
|
||||
}
|
||||
Expression::ClassExpression(class) => {
|
||||
// 类表达式:方法/构造器体与字段表达式是嵌套作用域,引用须捕获。
|
||||
let mut class_shadow = shadow.clone();
|
||||
if let Some(id) = &class.id {
|
||||
class_shadow.insert(id.name.as_str().to_string());
|
||||
}
|
||||
self.collect_class_capture_names(&class.body, ref_set, &class_shadow, out);
|
||||
}
|
||||
Expression::BinaryExpression(be) => {
|
||||
self.collect_capture_names_expr(&be.left, ref_set, shadow, out);
|
||||
self.collect_capture_names_expr(&be.right, ref_set, shadow, out);
|
||||
@@ -447,6 +496,14 @@ impl Emitter {
|
||||
self.collect_fn_default_captured(&fd.params, own, out);
|
||||
self.collect_capture_names(body, own, out);
|
||||
}
|
||||
Statement::ClassDeclaration(cd) => {
|
||||
// 类构造器/方法体与字段表达式引用的父级绑定须建 cell,供子模块 upvalue 捕获。
|
||||
let mut class_shadow = HashSet::new();
|
||||
if let Some(id) = &cd.id {
|
||||
class_shadow.insert(id.name.as_str().to_string());
|
||||
}
|
||||
self.collect_class_capture_names(&cd.body, own, &class_shadow, out);
|
||||
}
|
||||
Statement::ExpressionStatement(es) => self.collect_captured_expr(&es.expression, own, out),
|
||||
Statement::ReturnStatement(rs) => {
|
||||
if let Some(a) = &rs.argument {
|
||||
@@ -584,6 +641,14 @@ impl Emitter {
|
||||
self.collect_fn_default_captured(&ae.params, own, out);
|
||||
self.collect_capture_names_shadowed(&ae.body.statements, own, &inner, out);
|
||||
}
|
||||
Expression::ClassExpression(class) => {
|
||||
// 类表达式:构造器/方法体与字段表达式引用的父级绑定须建 cell。
|
||||
let mut class_shadow = HashSet::new();
|
||||
if let Some(id) = &class.id {
|
||||
class_shadow.insert(id.name.as_str().to_string());
|
||||
}
|
||||
self.collect_class_capture_names(&class.body, own, &class_shadow, out);
|
||||
}
|
||||
Expression::CallExpression(ce) => {
|
||||
self.collect_captured_expr(&ce.callee, own, out);
|
||||
for a in &ce.arguments {
|
||||
|
||||
@@ -167,6 +167,11 @@ pub struct CompileCtx {
|
||||
pub(crate) in_static_method: bool,
|
||||
pub(crate) static_block_this_reg: Option<u8>,
|
||||
pub(crate) field_buffer: Option<FieldBuffer>,
|
||||
/// 类构造器模块中 `@@field_keys` upvalue 下标(实例字段 computed key 数组)。
|
||||
/// 类定义期求值一次存入 cell,构造器经此 upvalue 读取。
|
||||
pub(crate) field_keys_uv: Option<u8>,
|
||||
/// 类定义期全部 computed key 数组寄存器(方法/静态字段阶段按 slot 读取)。
|
||||
pub(crate) class_keys_reg: Option<u32>,
|
||||
pub(crate) current_upvalue_captures: Vec<UpvalueCapture>,
|
||||
/// 本函数作用域声明的绑定名(参数 + 变量/函数声明,AST 收集,emit 前确定)。
|
||||
pub(crate) own_bindings: HashSet<String>,
|
||||
@@ -258,6 +263,8 @@ impl CompileCtx {
|
||||
in_static_method: false,
|
||||
static_block_this_reg: None,
|
||||
field_buffer: None,
|
||||
field_keys_uv: None,
|
||||
class_keys_reg: None,
|
||||
current_upvalue_captures: Vec::new(),
|
||||
own_bindings: HashSet::new(),
|
||||
captured_bindings: BTreeMap::new(),
|
||||
@@ -718,6 +725,8 @@ impl Emitter {
|
||||
body_context,
|
||||
None::<fn(&Emitter, &mut CompileCtx) -> Result<(), String>>,
|
||||
false,
|
||||
&[],
|
||||
&[],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -733,6 +742,7 @@ impl Emitter {
|
||||
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
|
||||
is_expression_body: bool, extra_bindings: &[(&str, u32)], body_context: FunctionBodyContext,
|
||||
mut emit_fields: Option<E>, fields_after_super: bool,
|
||||
extra_capture_exprs: &[&'a Expression<'a>], extra_upvalue_names: &[(&str, u8)],
|
||||
) -> Result<IRFunction, String>
|
||||
where
|
||||
E: FnMut(&Emitter, &mut CompileCtx) -> Result<(), String>,
|
||||
@@ -788,7 +798,21 @@ impl Emitter {
|
||||
// 让 next_reg 与 builtin 槽位对齐,参数在 builtin 槽之后分配。
|
||||
ctx.reset_regs();
|
||||
|
||||
let param_base = self.emit_params_prologue(param_specs, body_stmts, parent_ctx, &mut ctx, body_context)?;
|
||||
let param_base = self.emit_params_prologue(
|
||||
param_specs,
|
||||
body_stmts,
|
||||
parent_ctx,
|
||||
&mut ctx,
|
||||
body_context,
|
||||
extra_capture_exprs,
|
||||
extra_upvalue_names,
|
||||
)?;
|
||||
// 实例字段 computed key 数组所在 upvalue 下标,供字段初始化 emit 定位。
|
||||
ctx.field_keys_uv = ctx
|
||||
.current_upvalue_captures
|
||||
.iter()
|
||||
.position(|u| u.name == "@@field_keys")
|
||||
.map(|i| i as u8);
|
||||
|
||||
self.predeclare_function_declarations(body_stmts, &mut ctx);
|
||||
|
||||
@@ -854,7 +878,8 @@ impl Emitter {
|
||||
/// 参数 prologue:函数作用域 + 参数声明/解构 + 闭包捕获与 upvalue 分析。返回 param_base。
|
||||
fn emit_params_prologue<'a>(
|
||||
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
|
||||
ctx: &mut CompileCtx, body_context: FunctionBodyContext,
|
||||
ctx: &mut CompileCtx, body_context: FunctionBodyContext, extra_capture_exprs: &[&'a Expression<'a>],
|
||||
extra_upvalue_names: &[(&str, u8)],
|
||||
) -> Result<u32, String> {
|
||||
ctx.push_scope_with_kind(ScopeKind::FunctionScope);
|
||||
let param_base = ctx.next_reg;
|
||||
@@ -900,7 +925,10 @@ impl Emitter {
|
||||
arguments_reg = Some(reg);
|
||||
}
|
||||
|
||||
ctx.captured_bindings = self.collect_captured_bindings(body_stmts, ¶m_defaults, &ctx.own_bindings);
|
||||
// 字段初始化表达式(值表达式)与参数默认值一并纳入捕获分析。
|
||||
let mut capture_exprs: Vec<&oxide_parser::Expression> = param_defaults.clone();
|
||||
capture_exprs.extend_from_slice(extra_capture_exprs);
|
||||
ctx.captured_bindings = self.collect_captured_bindings(body_stmts, &capture_exprs, &ctx.own_bindings);
|
||||
// 自由变量分析:收集 upvalue 捕获(类方法也是普通函数,可捕获外层变量)。
|
||||
if matches!(
|
||||
body_context,
|
||||
@@ -908,11 +936,22 @@ impl Emitter {
|
||||
) {
|
||||
ctx.current_upvalue_captures = self.collect_upvalue_names(
|
||||
body_stmts,
|
||||
¶m_defaults,
|
||||
&capture_exprs,
|
||||
&parent_ctx.captured_bindings,
|
||||
&parent_ctx.current_upvalue_captures,
|
||||
&ctx.own_bindings,
|
||||
);
|
||||
// 类字段 computed key 数组等合成捕获:直接追加 upvalue(cell_idx 由父分配)。
|
||||
for (name, cell_idx) in extra_upvalue_names {
|
||||
if !ctx.current_upvalue_captures.iter().any(|u| u.name == *name) {
|
||||
ctx.current_upvalue_captures.push(UpvalueCapture {
|
||||
name: (*name).to_string(),
|
||||
enclosing_reg: 0,
|
||||
cell_idx: *cell_idx,
|
||||
parent_uv_idx: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建 arguments 对象:指令须在默认参数求值前发出(默认参数可引用 arguments)。
|
||||
|
||||
@@ -221,25 +221,65 @@ impl Emitter {
|
||||
let store_label = ctx.next_label_id();
|
||||
let end_label = ctx.next_label_id();
|
||||
let name = id_ref.name.as_str();
|
||||
let var_reg = ctx.lookup_or_global(name);
|
||||
// 目标判定:upvalue / 被捕获 cell / 普通槽,读与写须穿透共享单元。
|
||||
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
|
||||
let captured_cell = ctx.captured_bindings.get(name).copied();
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(var_reg),
|
||||
Operand::None,
|
||||
));
|
||||
if let Some(uv) = uv_idx {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_UPVALUE,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Imm(uv as u16),
|
||||
Operand::None,
|
||||
));
|
||||
} else if let Some(cell_idx) = captured_cell {
|
||||
let a_operand = match ctx.scopes.symbols.lookup_any_binding(name) {
|
||||
Some((binding, _)) => Operand::Reg(binding.reg),
|
||||
None => Operand::None,
|
||||
};
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::CELL_GET,
|
||||
Operand::Reg(result_reg),
|
||||
a_operand,
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
} else {
|
||||
let var_reg = ctx.lookup_or_global(name);
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(var_reg),
|
||||
Operand::None,
|
||||
));
|
||||
}
|
||||
self.emit_logical_assign_test(logical_op, result_reg, store_label, end_label, ctx)?;
|
||||
ctx.labels.set_label_pos(store_label, ctx.insts.len());
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
let is_const = ctx.lookup_const_flag(name);
|
||||
let const_flag = if is_const { 1 } else { 0 };
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(var_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(const_flag),
|
||||
));
|
||||
if let Some(uv) = uv_idx {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_UPVALUE,
|
||||
Operand::None,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(uv as u16),
|
||||
));
|
||||
} else if let Some(cell_idx) = captured_cell {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::CELL_SET,
|
||||
Operand::None,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
} else {
|
||||
let is_const = ctx.lookup_const_flag(name);
|
||||
let const_flag = if is_const { 1 } else { 0 };
|
||||
let var_reg = ctx.lookup_or_global(name);
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(var_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(const_flag),
|
||||
));
|
||||
}
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
|
||||
@@ -451,19 +451,20 @@ impl Emitter {
|
||||
));
|
||||
Ok(if update.prefix { new_reg } else { old_reg })
|
||||
} else if let Some(cell_idx) = captured_cell {
|
||||
// 被捕获 cell:CELL_GET + 常量 1 + ADD/SUB + CELL_SET
|
||||
let val_reg = ctx.alloc_reg();
|
||||
// 被捕获 cell:CELL_GET 旧值 + 常量 1 + ADD/SUB + CELL_SET。
|
||||
// 后缀形式须保留旧值(结果寄存器),前缀返回新值。
|
||||
let old_reg = ctx.alloc_reg();
|
||||
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::CELL_GET,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Reg(old_reg),
|
||||
Operand::Reg(binding.reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
} else {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::CELL_GET,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Reg(old_reg),
|
||||
Operand::None,
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
@@ -476,14 +477,15 @@ impl Emitter {
|
||||
} else {
|
||||
OpCode::SUB
|
||||
};
|
||||
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(one_reg)));
|
||||
let new_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(op, Operand::Reg(new_reg), Operand::Reg(old_reg), Operand::Reg(one_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::CELL_SET,
|
||||
Operand::None,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Reg(new_reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
Ok(val_reg)
|
||||
Ok(if update.prefix { new_reg } else { old_reg })
|
||||
} else {
|
||||
let var_reg = ctx.lookup_or_global(name);
|
||||
let result_reg = ctx.alloc_reg();
|
||||
|
||||
@@ -1,43 +1,61 @@
|
||||
//! 类字段初始化 emit:公有/私有实例字段与私有方法的实例化初始化。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{Expression, PropertyKey};
|
||||
|
||||
impl Emitter {
|
||||
/// 实例字段初始化:define 语义(不触发原型 setter)。
|
||||
/// 非计算键用字符串常量;计算键从 `@@field_keys` upvalue 数组按 `key_slot` 取(类定义期求值一次)。
|
||||
pub(crate) fn emit_public_field_init(
|
||||
&self, target: Operand, key: &PropertyKey, computed: bool, value: Option<&Expression>, ctx: &mut CompileCtx,
|
||||
&self, target: Operand, key: &PropertyKey, computed: bool, value: Option<&Expression>,
|
||||
key_slot: Option<u8>, ctx: &mut CompileCtx,
|
||||
) -> Result<(), String> {
|
||||
let key_reg = self.emit_class_key_reg(key, computed, ctx)?;
|
||||
let value_reg = if let Some(expr) = value {
|
||||
self.emit_expression(expr, ctx)?
|
||||
let key_reg = if computed {
|
||||
self.emit_instance_field_key(key_slot, ctx)?
|
||||
} else {
|
||||
self.emit_undefined(ctx)
|
||||
self.emit_class_key_reg(key, false, ctx)?
|
||||
};
|
||||
if computed {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP_DYNAMIC,
|
||||
target,
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(value_reg),
|
||||
));
|
||||
} else {
|
||||
ctx.inst(Inst::new(OpCode::SET_PROP, target, Operand::Reg(value_reg), Operand::Reg(key_reg)));
|
||||
}
|
||||
let value_reg = self.emit_field_value(value, ctx)?;
|
||||
ctx.inst(Inst::define_prop(target, Operand::Reg(value_reg), Operand::Reg(key_reg)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 类定义期求值一次后存入 upvalue 数组的计算键读取:`arr[slot]`。
|
||||
fn emit_instance_field_key(&self, key_slot: Option<u8>, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
let uv = ctx.field_keys_uv.expect("computed instance field without field keys upvalue");
|
||||
let arr_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_UPVALUE, Operand::Reg(arr_reg), Operand::Imm(uv as u16), Operand::None));
|
||||
let idx_reg = ctx.alloc_reg();
|
||||
let idx = ctx.add_constant(Constant::Int(key_slot.unwrap_or(0) as i32));
|
||||
ctx.inst(Inst::load_const(Operand::Reg(idx_reg), idx));
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PROP_DYNAMIC,
|
||||
Operand::Reg(arr_reg),
|
||||
Operand::Reg(idx_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
Ok(key_reg)
|
||||
}
|
||||
|
||||
/// 字段值求值:有值求表达式,无值 define undefined。
|
||||
pub(crate) fn emit_field_value(&self, value: Option<&Expression>, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
if let Some(expr) = value {
|
||||
self.emit_expression(expr, ctx)
|
||||
} else {
|
||||
Ok(self.emit_undefined(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn emit_private_field_init(
|
||||
&self, target: Operand, name: &str, value: Option<&Expression>, ctx: &mut CompileCtx,
|
||||
) -> Result<(), String> {
|
||||
let key_reg = self.emit_private_id_reg(name, ctx)?;
|
||||
let value_reg = if let Some(expr) = value {
|
||||
self.emit_expression(expr, ctx)?
|
||||
} else {
|
||||
self.emit_undefined(ctx)
|
||||
};
|
||||
let value_reg = self.emit_field_value(value, ctx)?;
|
||||
ctx.inst(Inst::new(OpCode::INIT_PRIVATE, target, Operand::Reg(value_reg), Operand::Reg(key_reg)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::PropertyKey;
|
||||
@@ -51,6 +52,22 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
/// 从类定义期 computed key 数组按 slot 取键(方法/静态字段在类定义期使用)。
|
||||
pub(crate) fn emit_class_array_key_reg(&self, slot: u8, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
let arr_reg = ctx.class_keys_reg.expect("class computed keys array missing");
|
||||
let idx_reg = ctx.alloc_reg();
|
||||
let idx = ctx.add_constant(Constant::Int(slot as i32));
|
||||
ctx.inst(Inst::load_const(Operand::Reg(idx_reg), idx));
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PROP_DYNAMIC,
|
||||
Operand::Reg(arr_reg),
|
||||
Operand::Reg(idx_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
Ok(key_reg)
|
||||
}
|
||||
|
||||
pub(crate) fn class_property_name(&self, key: &PropertyKey) -> Result<String, String> {
|
||||
match key {
|
||||
PropertyKey::StaticIdentifier(ident) => Ok(ident.name.as_str().to_string()),
|
||||
|
||||
@@ -65,6 +65,7 @@ impl Inst {
|
||||
| OpCode::DELETE_PROP_STATIC
|
||||
| OpCode::DELETE_PROP_DYNAMIC
|
||||
| OpCode::DEFINE_ACCESSOR
|
||||
| OpCode::DEFINE_PROP
|
||||
| OpCode::SET_HOME_OBJECT
|
||||
// SPILL 写 VM spill 栈而非寄存器(恢复由 UNSPILL 写 rd)
|
||||
| OpCode::SPILL
|
||||
@@ -256,7 +257,8 @@ impl Inst {
|
||||
| OpCode::SET_PRIVATE
|
||||
| OpCode::INIT_PRIVATE
|
||||
| OpCode::PRIVATE_BRAND_IN
|
||||
| OpCode::DEFINE_ACCESSOR => {
|
||||
| OpCode::DEFINE_ACCESSOR
|
||||
| OpCode::DEFINE_PROP => {
|
||||
push_operand(&mut uses, &self.rd);
|
||||
push_operand(&mut uses, &self.a);
|
||||
push_operand(&mut uses, &self.b);
|
||||
|
||||
@@ -173,6 +173,12 @@ impl Inst {
|
||||
Self::with_ext(OpCode::DEFINE_ACCESSOR, home, get, set, &[key_idx])
|
||||
}
|
||||
|
||||
/// define 数据属性:target 为宿主对象,value/key 为寄存器。
|
||||
/// 不触发原型链 setter,与 SET_PROP 语义不同。
|
||||
pub fn define_prop(target: Operand, value: Operand, key: Operand) -> Self {
|
||||
Self::new(OpCode::DEFINE_PROP, target, value, key)
|
||||
}
|
||||
|
||||
/// 静态删除属性:obj 同时放 rd/a 槽,const_idx 为属性名常量下标。
|
||||
pub fn delete_prop_static(obj: Operand, const_idx: u32) -> Self {
|
||||
Self::with_ext(OpCode::DELETE_PROP_STATIC, obj, obj, Operand::None, &[const_idx])
|
||||
|
||||
@@ -1221,6 +1221,10 @@ impl Vm {
|
||||
self.dispatch_define_accessor(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::DEFINE_PROP => {
|
||||
self.dispatch_define_prop(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::RETURN => match self.dispatch_return(rd) {
|
||||
Ok(Some(result)) => return Ok(result),
|
||||
Ok(None) => {}
|
||||
|
||||
@@ -130,6 +130,23 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
|
||||
/// define 数据属性:rd=目标对象,a=值,b=键。与 SET_PROP 不同,不触发原型链
|
||||
/// setter,走 `define_data_property`(默认属性 writable/enumerable/configurable)。
|
||||
pub(crate) fn dispatch_define_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("DEFINE_PROP rd={} value={} key={}", rd, a, b);
|
||||
let obj_val = self.regs[rd];
|
||||
if !obj_val.is_object() {
|
||||
return self.raise_type_error("DEFINE_PROP target is not object");
|
||||
}
|
||||
let prop_name_si = self.property_key_si(self.regs[b]);
|
||||
let value = self.regs[a];
|
||||
let obj = unsafe { &mut *obj_val.as_js_object_ptr() };
|
||||
match self.define_data_property(obj, prop_name_si, value, PropAttributes::DEFAULT_DATA) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(msg) => self.raise_error_kind("TypeError", &msg),
|
||||
}
|
||||
}
|
||||
|
||||
/// break 完成:`crossed`(rd 槽)为 emit 词法算出的逃出 finally 域数。
|
||||
/// 逐个穿越 finally 后跳转到目标;crossed 为 0 时直接跳转。
|
||||
pub(crate) fn dispatch_break(&mut self, instr: u32) {
|
||||
|
||||
@@ -352,3 +352,77 @@ fn this_survives_native_call_inside_method() {
|
||||
.unwrap();
|
||||
assert_eq!(result.as_int(), 7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn computed_field_key_evaluated_once_at_class_definition() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var n=0; class C { [++n] = 1; [++n] = 2; } n").unwrap();
|
||||
assert_num(result, 2.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn computed_field_key_once_but_value_per_instance() {
|
||||
let mut vm = Vm::new();
|
||||
let result =
|
||||
eval(&mut vm, "var n=0; class C { [++n] = ++n; } var a=new C(); var b=new C(); n*100 + a[1]*10 + b[1]")
|
||||
.unwrap();
|
||||
assert_num(result, 323.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn computed_keys_evaluate_in_source_order_across_static_and_instance() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var i=0; class C { [i++] = i++; static [i++] = i++; [i++] = i++; } var c = new C(); i*1000 + c[0]*100 + c[2]*10 + C[1]",
|
||||
)
|
||||
.unwrap();
|
||||
assert_num(result, 6453.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn field_definition_does_not_trigger_inherited_setter() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var log=[]; class B { set x(v){ log.push('setter'); } } class C extends B { x = 1; } var c = new C(); log.length*100 + (c.hasOwnProperty('x')?10:0) + c.x",
|
||||
)
|
||||
.unwrap();
|
||||
assert_num(result, 11.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn field_value_captures_outer_variable() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var n=0; class C { x = ++n; } var c = new C(); n*10 + c.x").unwrap();
|
||||
assert_num(result, 11.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn class_expression_field_value_captures_outer_variable() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var n=0; var C = class { x = ++n; }; new C(); n").unwrap();
|
||||
assert_num(result, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn field_initializers_run_once_on_second_super_call() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var n=0; class B {} var C = class extends B { field = ++n; constructor(){ super(); super(); } }; var err; try { new C(); } catch(e) { err = e.name; } (err === 'ReferenceError'?1:0)*10 + n",
|
||||
)
|
||||
.unwrap();
|
||||
assert_num(result, 11.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn postfix_increment_on_captured_cell_returns_old_value() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var n=0; var arr = (function(){ return [n++, n, n++]; })(); arr[0]*1000 + arr[1]*100 + arr[2]*10 + n",
|
||||
)
|
||||
.unwrap();
|
||||
assert_num(result, 112.0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user