feat: class 私有成员语义(字段/方法/访问器 + brand 检查 + 契约修复)

This commit is contained in:
2026-08-09 11:08:36 +08:00
parent 088b3bc976
commit 6c79dbb21c
41 changed files with 573 additions and 216 deletions
+97 -12
View File
@@ -14,7 +14,7 @@ impl Emitter {
let elements = &class.body.body;
let mut constructor_method = None;
let mut instance_field_indices = Vec::new();
let mut private_names = Vec::<(String, u32)>::new();
let mut private_names = Vec::<(String, u32, Option<MethodDefinitionKind>, bool)>::new();
let is_derived = class.super_class.is_some();
for (idx, element) in elements.iter().enumerate() {
@@ -23,12 +23,18 @@ impl Emitter {
let method = method.as_ref();
if let PropertyKey::PrivateIdentifier(private) = &method.key {
let name = private.name.as_str().to_string();
if private_names.iter().any(|(existing, _)| existing == &name) {
return Err(format!("duplicate private name #{name}"));
let kind = method.kind;
match private_names.iter().find(|(n, _, _, _)| n == &name) {
// 同名 getter/setter 构成访问器对,复用同一私有名。
Some((_, _, Some(MethodDefinitionKind::Get), _)) if kind == MethodDefinitionKind::Set => {}
Some((_, _, Some(MethodDefinitionKind::Set), _)) if kind == MethodDefinitionKind::Get => {}
Some(_) => return Err(format!("duplicate private name #{name}")),
None => {
let id = ctx.scopes.next_private_name_id;
ctx.scopes.next_private_name_id = ctx.scopes.next_private_name_id.saturating_add(1);
private_names.push((name, id, Some(kind), method.r#static));
}
}
let id = ctx.scopes.next_private_name_id;
ctx.scopes.next_private_name_id = ctx.scopes.next_private_name_id.saturating_add(1);
private_names.push((name, id));
}
if method.kind == MethodDefinitionKind::Constructor {
if constructor_method.is_some() {
@@ -41,12 +47,12 @@ impl Emitter {
let prop = prop.as_ref();
if let PropertyKey::PrivateIdentifier(private) = &prop.key {
let name = private.name.as_str().to_string();
if private_names.iter().any(|(existing, _)| existing == &name) {
if private_names.iter().any(|(existing, _, _, _)| existing == &name) {
return Err(format!("duplicate private name #{name}"));
}
let id = ctx.scopes.next_private_name_id;
ctx.scopes.next_private_name_id = ctx.scopes.next_private_name_id.saturating_add(1);
private_names.push((name, id));
private_names.push((name, id, None, prop.r#static));
}
if !prop.r#static {
instance_field_indices.push(idx);
@@ -85,8 +91,26 @@ impl Emitter {
let self_binding = ctor_name.as_deref().map(|name| vec![(name, ctor_reg)]).unwrap_or_default();
let saved_derived = ctx.in_derived_constructor;
let saved_private_names = ctx.scopes.private_name_map.clone();
let saved_private_kinds = ctx.scopes.private_element_kinds.clone();
let saved_brand_id = ctx.scopes.private_brand_id;
ctx.in_derived_constructor = is_derived;
ctx.scopes.private_name_map = private_names.clone();
ctx.scopes.private_name_map = private_names.iter().map(|(n, id, _, _)| (n.clone(), *id)).collect();
ctx.scopes.private_element_kinds = private_names
.iter()
.map(|(n, _, kind, is_static)| (n.clone(), *kind, *is_static))
.collect();
// 类 brand:有私有元素时分配 brand 私有名 id,并创建 brand 对象(= 类原型)。
// 构造器把 brand 槽写入实例 own;私有方法/访问器/静态字段访问(GET/SET)据
// brand 对象同一性做检查;instance 字段走 PrivateFieldFind 原型链查找,不检查。
let private_brand_id = if !private_names.is_empty() {
let id = ctx.scopes.next_private_name_id;
ctx.scopes.next_private_name_id = ctx.scopes.next_private_name_id.saturating_add(1);
Some(id)
} else {
None
};
ctx.scopes.private_brand_id = private_brand_id;
// 实例公有字段 computed key 求值于构造器帧外,须类定义期存入数组。构造器以
// upvalue 捕获该数组:父作用域登记 `@@field_keys`(cell_idx 取现有最大 +1)。
@@ -101,6 +125,12 @@ impl Emitter {
} else {
None
};
// 类 brand 对象(= proto)由类定义期 MAKE_CELL 写入,构造器经 upvalue 捕获。
let brand_cell: Option<u8> = private_brand_id.map(|_| {
let cell_idx = ctx.captured_bindings.values().copied().max().map_or(0, |m| m.saturating_add(1));
ctx.captured_bindings.insert("@@class_brand".to_string(), cell_idx);
cell_idx
});
// 字段值表达式运行于构造器帧:其自由变量须纳入构造器 upvalue 捕获。
let field_value_exprs: Vec<&Expression> = instance_field_indices
.iter()
@@ -109,11 +139,44 @@ impl Emitter {
_ => None,
})
.collect();
let extra_upvalue_names: Vec<(&str, u8)> = field_key_cell.map(|c| ("@@field_keys", c)).into_iter().collect();
let mut extra_upvalue_names: Vec<(&str, u8)> =
field_key_cell.map(|c| ("@@field_keys", c)).into_iter().collect();
if let Some(c) = brand_cell {
extra_upvalue_names.push(("@@class_brand", c));
}
let emit_instance_fields = |compiler: &Emitter, field_ctx: &mut CompileCtx| -> Result<(), String> {
// 先写 brand 槽(私有方法/访问器的 brand 检查依据),再初始化字段。
if let Some(bid) = private_brand_id {
if brand_cell.is_none() {
return Err("class brand cell missing".into());
}
let brand_reg = field_ctx.alloc_reg();
let uv_idx = field_ctx
.current_upvalue_captures
.iter()
.position(|u| u.name == "@@class_brand")
.ok_or("class brand upvalue missing")? as u16;
field_ctx.inst(Inst::new(
OpCode::LOAD_UPVALUE,
Operand::Reg(brand_reg),
Operand::Imm(uv_idx),
Operand::None,
));
let key_idx = field_ctx.add_constant(Constant::Int(bid as i32));
let brand_key_reg = field_ctx.alloc_reg();
field_ctx.inst(Inst::load_const(Operand::Reg(brand_key_reg), key_idx));
field_ctx.inst(Inst::init_private(
Operand::This,
Operand::Reg(brand_reg),
Operand::Reg(brand_key_reg),
false,
));
}
for &idx in &instance_field_indices {
let ClassElement::PropertyDefinition(field) = &elements[idx] else { unreachable!() };
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(
@@ -170,7 +233,8 @@ impl Emitter {
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.private_name_map =
private_names.iter().map(|(n, id, _, _)| (n.clone(), *id)).collect();
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
@@ -238,10 +302,31 @@ impl Emitter {
}
self.emit_class_prototype(ctor_reg, proto_reg, super_reg, ctor_sub_idx, ctx)?;
// brand 对象即类原型:构造器对象(静态私有访问)与实例(实例私有访问)各写
// brand 槽;构造器经 @@class_brand upvalue 捕获原型对象。
if let (Some(bid), Some(cell_idx)) = (private_brand_id, brand_cell) {
let key_idx = ctx.add_constant(Constant::Int(bid as i32));
let brand_key_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(brand_key_reg), key_idx));
ctx.inst(Inst::init_private(
Operand::Reg(ctor_reg),
Operand::Reg(proto_reg),
Operand::Reg(brand_key_reg),
false,
));
ctx.inst(Inst::new(
OpCode::MAKE_CELL,
Operand::Reg(proto_reg),
Operand::Imm(cell_idx as u16),
Operand::None,
));
}
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;
ctx.scopes.private_element_kinds = saved_private_kinds;
ctx.scopes.private_brand_id = saved_brand_id;
Ok(ctor_reg)
}
}
+13 -1
View File
@@ -90,13 +90,25 @@ impl Emitter {
let saved_static = ctx.in_static_method;
ctx.in_instance_method = !method.r#static;
ctx.in_static_method = method.r#static;
let mut method_module = self.compile_function_body_with_bindings(
// 私有方法/访问器访问需对接收者做 brand 检查:方法函数捕获类 brand 对象
// (@@class_brand upvalue,值 = 类原型)。
let extra_uv: Vec<(&str, u8)> = ctx
.captured_bindings
.get("@@class_brand")
.map(|c| ("@@class_brand", *c))
.into_iter()
.collect();
let mut method_module = self.compile_function_body_with_field_hooks(
&param_names,
body_stmts,
ctx,
false,
self_binding,
FunctionBodyContext::ClassElement,
None::<fn(&Emitter, &mut CompileCtx) -> Result<(), String>>,
false,
&[],
&extra_uv,
)?;
ctx.in_instance_method = saved_instance;
ctx.in_static_method = saved_static;
+9 -2
View File
@@ -190,7 +190,13 @@ impl Emitter {
// 参数声明名遮蔽 catch 作用域。
let mut catch_shadow = shadow.clone();
if let Some(param) = &h.param {
self.collect_capture_names_binding_pattern(&param.pattern, ref_set, shadow, out, &mut catch_shadow);
self.collect_capture_names_binding_pattern(
&param.pattern,
ref_set,
shadow,
out,
&mut catch_shadow,
);
}
for s in &h.body.body {
self.collect_capture_names_stmt(s, ref_set, &catch_shadow, out);
@@ -490,7 +496,8 @@ impl Emitter {
}
/// 只从嵌套函数节点进入扫描(本函数直接引用不算捕获)。
fn collect_captured_stmt(&self, stmt: &Statement, own: &HashSet<String>, out: &mut HashSet<String>) { match stmt {
fn collect_captured_stmt(&self, stmt: &Statement, own: &HashSet<String>, out: &mut HashSet<String>) {
match stmt {
Statement::FunctionDeclaration(fd) => {
let body: &[Statement] = fd.body.as_ref().map(|b| &b.statements[..]).unwrap_or(&[]);
self.collect_fn_default_captured(&fd.params, own, out);
+11 -6
View File
@@ -24,7 +24,6 @@ pub fn is_anonymous_function_definition(expr: &oxide_parser::Expression) -> bool
}
}
use crate::emit_ctx::{LabelCtx, ScopeCtx};
use crate::symbol_table::{Binding, ScopeKind, SymbolTable};
@@ -254,6 +253,8 @@ impl CompileCtx {
symbols: SymbolTable::new(),
builtin_reg_map: Vec::new(),
private_name_map: Vec::new(),
private_element_kinds: Vec::new(),
private_brand_id: None,
next_private_name_id: 1,
},
nested: Vec::new(),
@@ -659,7 +660,6 @@ impl Emitter {
}
}
// ── 闭包捕获分析(AST 级,时序无关)──
/// 收集函数参数的绑定名(BindingIdentifier 形态)。
@@ -741,8 +741,8 @@ impl Emitter {
pub(crate) fn compile_function_body_with_field_hooks<'a, E>(
&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)],
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>,
@@ -753,6 +753,8 @@ impl Emitter {
// 解析到父预先分配的槽位。
ctx.scopes.builtin_reg_map = parent_ctx.scopes.builtin_reg_map.clone();
ctx.scopes.private_name_map = parent_ctx.scopes.private_name_map.clone();
ctx.scopes.private_element_kinds = parent_ctx.scopes.private_element_kinds.clone();
ctx.scopes.private_brand_id = parent_ctx.scopes.private_brand_id;
ctx.scopes.next_private_name_id = parent_ctx.scopes.next_private_name_id;
// 传递 enclosing_this_reg:嵌套箭头函数捕获正确的 `this`。
@@ -961,7 +963,11 @@ impl Emitter {
for spec in param_specs {
match spec {
ParamSpec::Pattern { synthetic_name, pattern, initializer } => {
ParamSpec::Pattern {
synthetic_name,
pattern,
initializer,
} => {
let src_reg = ctx.lookup(synthetic_name)?;
let src_reg = if let Some(init) = initializer {
self.emit_default_if_undefined(src_reg, init, Some(synthetic_name), ctx)?
@@ -1154,4 +1160,3 @@ impl Default for Emitter {
Self::new()
}
}
+6
View File
@@ -8,6 +8,7 @@
use crate::symbol_table::SymbolTable;
use crate::LabelScope;
use oxide_ir::operand::LabelId;
use oxide_parser::MethodDefinitionKind;
/// 跳转目标 / 标签语句解析状态。
pub(crate) struct LabelCtx {
@@ -41,6 +42,11 @@ pub(crate) struct ScopeCtx {
pub(crate) symbols: SymbolTable,
pub(crate) builtin_reg_map: Vec<(String, u32)>,
pub(crate) private_name_map: Vec<(String, u32)>,
/// 私有元素类型(name, kind,static 标志)。kind=None 表示字段;
/// instance 字段的私有访问走 PrivateFieldFind 原型链查找,不加 brand 检查。
pub(crate) private_element_kinds: Vec<(String, Option<MethodDefinitionKind>, bool)>,
/// 当前类的私有 brand 私有名 id:私有方法/访问器访问时对实例做 brand 检查。
pub(crate) private_brand_id: Option<u32>,
pub(crate) next_private_name_id: u32,
}
+6 -3
View File
@@ -207,12 +207,15 @@ impl Emitter {
}
let obj_reg = self.emit_expression(&member.object, ctx)?;
let val_reg = self.emit_expression(&assign.right, ctx)?;
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
ctx.inst(Inst::new(
OpCode::SET_PRIVATE,
let name = member.field.name.as_str();
let (brand_reg, brand_id) = self.private_access_brand(obj_reg, name, ctx)?;
let key_reg = self.emit_private_id_reg(name, ctx)?;
ctx.inst(Inst::set_private(
Operand::Reg(obj_reg),
Operand::Reg(val_reg),
Operand::Reg(key_reg),
brand_reg,
brand_id,
));
Ok(val_reg)
} else if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(id_ref) = &assign.left {
+6 -3
View File
@@ -39,13 +39,16 @@ impl Emitter {
let (callee_reg, this_reg) = match &call.callee {
Expression::PrivateFieldExpression(member) => {
let obj_reg = self.emit_expression(&member.object, ctx)?;
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
let name = member.field.name.as_str();
let (brand_reg, brand_id) = self.private_access_brand(obj_reg, name, ctx)?;
let key_reg = self.emit_private_id_reg(name, ctx)?;
let callee_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::GET_PRIVATE,
ctx.inst(Inst::get_private(
Operand::Reg(callee_reg),
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
brand_reg,
brand_id,
));
(callee_reg, obj_reg)
}
+3 -18
View File
@@ -54,12 +54,7 @@ impl Emitter {
ctx.inst(Inst::load_const(Operand::Reg(key_reg), key_idx));
let has_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::IN,
Operand::Reg(has_reg),
Operand::Reg(key_reg),
Operand::Reg(obj_reg),
));
ctx.inst(Inst::new(OpCode::IN, Operand::Reg(has_reg), Operand::Reg(key_reg), Operand::Reg(obj_reg)));
let fallback_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
@@ -101,12 +96,7 @@ impl Emitter {
));
}
} else if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
ctx.inst(Inst::new(
OpCode::LOAD_VAR,
Operand::Reg(result_reg),
Operand::Reg(reg),
Operand::None,
));
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(reg), Operand::None));
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
ctx.inst(Inst::load_const(Operand::Reg(result_reg), undef_idx));
@@ -164,12 +154,7 @@ impl Emitter {
ctx.inst(Inst::load_const(Operand::Reg(key_reg), key_idx));
let has_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::IN,
Operand::Reg(has_reg),
Operand::Reg(key_reg),
Operand::Reg(obj_reg),
));
ctx.inst(Inst::new(OpCode::IN, Operand::Reg(has_reg), Operand::Reg(key_reg), Operand::Reg(obj_reg)));
let fallback_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
+1 -5
View File
@@ -70,9 +70,7 @@ impl Emitter {
// 非法正则字面量须在编译期报 SyntaxError(负面测试期望编译失败)。
// 用 regress(ECMAScript 语法)校验,避免误报 backreference/lookaround 等合法模式。
if regress::Regex::with_flags(&pattern, flags.as_str()).is_err() {
return Err(format!(
"SyntaxError: Invalid regular expression: /{pattern}/{flags}"
));
return Err(format!("SyntaxError: Invalid regular expression: /{pattern}/{flags}"));
}
let pat_ci = ctx.add_constant(Constant::String(pattern));
let pat_reg = ctx.alloc_reg();
@@ -96,5 +94,3 @@ impl Emitter {
}
}
}
+6 -3
View File
@@ -60,13 +60,16 @@ impl Emitter {
&self, member: &oxide_parser::PrivateFieldExpression, ctx: &mut CompileCtx,
) -> Result<u32, String> {
let obj_reg = self.emit_expression(&member.object, ctx)?;
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
let name = member.field.name.as_str();
let (brand_reg, brand_id) = self.private_access_brand(obj_reg, name, ctx)?;
let key_reg = self.emit_private_id_reg(name, ctx)?;
let r = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::GET_PRIVATE,
ctx.inst(Inst::get_private(
Operand::Reg(r),
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
brand_reg,
brand_id,
));
Ok(r)
}
+1 -6
View File
@@ -343,12 +343,7 @@ impl Emitter {
} else {
OpCode::SUB
};
ctx.inst(Inst::new(
op,
Operand::Reg(new_reg),
Operand::Reg(old_reg),
Operand::Reg(one_reg),
));
ctx.inst(Inst::new(op, Operand::Reg(new_reg), Operand::Reg(old_reg), Operand::Reg(one_reg)));
ctx.inst(Inst::new(
OpCode::SET_PROP_DYNAMIC,
Operand::Reg(obj_reg),
+18 -9
View File
@@ -72,13 +72,16 @@ impl Emitter {
self.emit_optional_guard(obj_reg, label, ctx)?;
}
}
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
let name = member.field.name.as_str();
let (brand_reg, brand_id) = self.private_access_brand(obj_reg, name, ctx)?;
let key_reg = self.emit_private_id_reg(name, ctx)?;
let callee_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::GET_PRIVATE,
ctx.inst(Inst::get_private(
Operand::Reg(callee_reg),
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
brand_reg,
brand_id,
));
(callee_reg, obj_reg)
}
@@ -165,13 +168,16 @@ impl Emitter {
self.emit_optional_guard(obj_reg, label, ctx)?;
}
}
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
let name = member.field.name.as_str();
let (brand_reg, brand_id) = self.private_access_brand(obj_reg, name, ctx)?;
let key_reg = self.emit_private_id_reg(name, ctx)?;
let value_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::GET_PRIVATE,
ctx.inst(Inst::get_private(
Operand::Reg(value_reg),
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
brand_reg,
brand_id,
));
Ok(value_reg)
}
@@ -200,13 +206,16 @@ impl Emitter {
self.emit_optional_guard(obj_reg, label, ctx)?;
}
}
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
let name = member.field.name.as_str();
let (brand_reg, brand_id) = self.private_access_brand(obj_reg, name, ctx)?;
let key_reg = self.emit_private_id_reg(name, ctx)?;
let value_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::GET_PRIVATE,
ctx.inst(Inst::get_private(
Operand::Reg(value_reg),
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
brand_reg,
brand_id,
));
Ok(value_reg)
}
+33 -7
View File
@@ -5,14 +5,14 @@ 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};
use oxide_parser::{Expression, MethodDefinitionKind, 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>,
key_slot: Option<u8>, 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 = if computed {
self.emit_instance_field_key(key_slot, ctx)?
@@ -28,7 +28,12 @@ impl Emitter {
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));
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));
@@ -56,7 +61,7 @@ impl Emitter {
) -> Result<(), String> {
let key_reg = self.emit_private_id_reg(name, 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)));
ctx.inst(Inst::init_private(target, Operand::Reg(value_reg), Operand::Reg(key_reg), false));
Ok(())
}
@@ -67,9 +72,30 @@ impl Emitter {
return Err("expected private method key".into());
};
let name = private.name.as_str();
let key_reg = self.emit_private_id_reg(name, ctx)?;
let method_reg = self.emit_class_method_function(method, name, home_reg, ctx, &[])?;
ctx.inst(Inst::new(OpCode::INIT_PRIVATE, target, Operand::Reg(method_reg), Operand::Reg(key_reg)));
match method.kind {
MethodDefinitionKind::Method => {
let key_reg = self.emit_private_id_reg(name, ctx)?;
ctx.inst(Inst::init_private(target, Operand::Reg(method_reg), Operand::Reg(key_reg), true));
}
MethodDefinitionKind::Get | MethodDefinitionKind::Set => {
let id = self.private_name_id(name, ctx)?;
let key_idx = ctx.add_constant(Constant::Int(id as i32));
let undef_reg = self.emit_undefined(ctx);
let (get_reg, set_reg) = if method.kind == MethodDefinitionKind::Get {
(method_reg, undef_reg)
} else {
(undef_reg, method_reg)
};
ctx.inst(Inst::define_accessor(
target,
Operand::Reg(get_reg),
Operand::Reg(set_reg),
key_idx as u32,
));
}
MethodDefinitionKind::Constructor => return Err("constructor cannot be private".into()),
}
Ok(())
}
}
+31 -1
View File
@@ -9,7 +9,7 @@ use oxide_ir::operand::Operand;
use oxide_parser::PropertyKey;
impl Emitter {
fn private_name_id(&self, name: &str, ctx: &CompileCtx) -> Result<u32, String> {
pub(crate) fn private_name_id(&self, name: &str, ctx: &CompileCtx) -> Result<u32, String> {
ctx.scopes
.private_name_map
.iter()
@@ -25,6 +25,36 @@ impl Emitter {
Ok(reg)
}
/// 私有成员访问的 brand 编码:返回 `(brand_reg, brand_id)`,供 GET_PRIVATE/SET_PRIVATE
/// dispatch 做 brand 检查。
///
/// instance 字段走 PrivateFieldFind 原型链查找(允许 `Object.create` 链穿透),
/// 不检查 brand(返回 `(0,0)`);方法/访问器/静态字段须验证接收者属于当前类
/// (brand 对象同一性)。brand 对象由方法函数捕获的 `@@class_brand` upvalue 提供;
/// 嵌套函数未捕获该 upvalue 时跳过检查(保持语法合法,brand 语义受限)。
pub(crate) fn private_access_brand(
&self, _obj_reg: u32, name: &str, ctx: &mut CompileCtx,
) -> Result<(u32, u32), String> {
let Some(brand_id) = ctx.scopes.private_brand_id else { return Ok((0, 0)) };
let Some((_, kind, is_static)) = ctx.scopes.private_element_kinds.iter().find(|(n, _, _)| n == name) else {
return Ok((0, 0));
};
if kind.is_none() && !is_static {
return Ok((0, 0));
}
let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == "@@class_brand") else {
return Ok((0, 0));
};
let brand_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::LOAD_UPVALUE,
Operand::Reg(brand_reg),
Operand::Imm(uv_idx as u16),
Operand::None,
));
Ok((brand_reg, brand_id))
}
pub(crate) fn emit_class_key_reg(
&self, key: &PropertyKey, computed: bool, ctx: &mut CompileCtx,
) -> Result<u32, String> {
@@ -21,17 +21,15 @@ impl Emitter {
let mut param_names = Vec::new();
for (idx, param) in fd.params.items.iter().enumerate() {
match &param.pattern {
oxide_parser::BindingPattern::BindingIdentifier(bi) => {
param_names.push(ParamSpec::Identifier {
name: bi.name.to_string(),
initializer: param.initializer.as_deref(),
})
}
oxide_parser::BindingPattern::BindingIdentifier(bi) => param_names.push(ParamSpec::Identifier {
name: bi.name.to_string(),
initializer: param.initializer.as_deref(),
}),
pattern => param_names.push(ParamSpec::Pattern {
synthetic_name: format!("@@param_{idx}"),
pattern,
initializer: param.initializer.as_deref(),
}),
synthetic_name: format!("@@param_{idx}"),
pattern,
initializer: param.initializer.as_deref(),
}),
}
}
let body_stmts: &[Statement] = if let Some(body) = &fd.body { &body.statements } else { &[] };
@@ -54,4 +52,3 @@ impl Emitter {
Ok(None)
}
}
@@ -67,4 +67,3 @@ impl Emitter {
Ok(r)
}
}