mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
feat: class 私有成员语义(字段/方法/访问器 + brand 检查 + 契约修复)
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
¶m_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;
|
||||
|
||||
@@ -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(¶m.pattern, ref_set, shadow, out, &mut catch_shadow);
|
||||
self.collect_capture_names_binding_pattern(
|
||||
¶m.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);
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -21,17 +21,15 @@ impl Emitter {
|
||||
let mut param_names = Vec::new();
|
||||
for (idx, param) in fd.params.items.iter().enumerate() {
|
||||
match ¶m.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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user