feat: class 私有成员语义(字段/方法/访问器 + brand 检查 + 契约修复)
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user