feat: class 公有字段 define 语义 + computed key 类定义期求值一次
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user