feat: 私有名方法名加井号前缀加嵌套类独立作用域

This commit is contained in:
2026-10-10 03:15:38 +00:00
parent 958cc1f7a2
commit 5fc898906a
10 changed files with 210 additions and 59 deletions
Generated
+19 -19
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]]
name = "oxide_builtins"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"chrono",
"fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]]
name = "oxide_bytecode"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"num-bigint",
"oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]]
name = "oxide_cfg"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"oxide_bytecode",
"oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]]
name = "oxide_cli"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"ansi_term",
"bumpalo",
@@ -1226,7 +1226,7 @@ dependencies = [
[[package]]
name = "oxide_code_cache"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"lru",
"oxide_bytecode",
@@ -1235,7 +1235,7 @@ dependencies = [
[[package]]
name = "oxide_compiler"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1255,7 +1255,7 @@ dependencies = [
[[package]]
name = "oxide_dce"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1269,7 +1269,7 @@ dependencies = [
[[package]]
name = "oxide_emit"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"num-bigint",
"oxc_allocator",
@@ -1284,7 +1284,7 @@ dependencies = [
[[package]]
name = "oxide_engine"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1296,7 +1296,7 @@ dependencies = [
[[package]]
name = "oxide_ir"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"oxide_bytecode",
"oxide_log",
@@ -1305,7 +1305,7 @@ dependencies = [
[[package]]
name = "oxide_kernel"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"bumpalo",
"dashmap",
@@ -1319,7 +1319,7 @@ dependencies = [
[[package]]
name = "oxide_liveness"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1331,7 +1331,7 @@ dependencies = [
[[package]]
name = "oxide_log"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"tracing",
"tracing-appender",
@@ -1340,7 +1340,7 @@ dependencies = [
[[package]]
name = "oxide_parser"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1355,7 +1355,7 @@ dependencies = [
[[package]]
name = "oxide_regalloc"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1371,7 +1371,7 @@ dependencies = [
[[package]]
name = "oxide_runtime_api"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"num-bigint",
"num-traits",
@@ -1384,7 +1384,7 @@ dependencies = [
[[package]]
name = "oxide_test262"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1402,7 +1402,7 @@ dependencies = [
[[package]]
name = "oxide_types"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"bumpalo",
"dashmap",
@@ -1415,7 +1415,7 @@ dependencies = [
[[package]]
name = "oxide_vm"
version = "0.13.26"
version = "0.13.27"
dependencies = [
"bumpalo",
"chrono",
+1 -1
View File
@@ -23,7 +23,7 @@ members = [
]
[workspace.package]
version = "0.13.26"
version = "0.13.27"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+35 -15
View File
@@ -147,12 +147,8 @@ impl Emitter {
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;
let saved_brand_chain = ctx.brand_chain.clone();
ctx.in_derived_constructor = is_derived;
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)据
@@ -166,6 +162,19 @@ impl Emitter {
};
ctx.scopes.private_brand_id = private_brand_id;
// 私有名环境是词法环境链:内类条目遮蔽外类同名条目,外类其余名字保持
// 可解析(内类方法体可访问外类声明的私有名)。
let mut stacked_names = saved_private_names.clone();
let mut stacked_kinds = saved_private_kinds.clone();
for (n, id, kind, is_static) in &private_names {
stacked_names.retain(|(en, _)| en != n);
stacked_kinds.retain(|(en, _, _, _)| en != n);
stacked_names.push((n.clone(), *id));
stacked_kinds.push((n.clone(), *kind, *is_static, private_brand_id.unwrap_or(0)));
}
ctx.scopes.private_name_map = stacked_names;
ctx.scopes.private_element_kinds = stacked_kinds;
// 实例公有字段 computed key 求值于构造器帧外,须类定义期存入数组。构造器以
// upvalue 捕获该数组:父作用域登记 `@@field_keys`(索引经共享计数器追加,
// 与块级遮蔽绑定的追加索引不碰撞)。
@@ -181,9 +190,12 @@ impl Emitter {
None
};
// 类 brand 对象(= proto)由类定义期 MAKE_CELL 写入,构造器经 upvalue 捕获。
let brand_cell: Option<u8> = private_brand_id.map(|_| {
// 捕获名按本类 brand id 编号:嵌套类各自登记 brand,内类方法体沿 brand 链
// 保持外类 brand 条目可见,跨类 brand 检查据此定位声明类的 brand 对象。
let brand_cell: Option<u8> = private_brand_id.map(|bid| {
let cell_idx = ctx.alloc_cell_idx();
ctx.captured_bindings.insert("@@class_brand".to_string(), cell_idx);
ctx.captured_bindings.insert(format!("@@class_brand_{bid}"), cell_idx);
ctx.brand_chain.push((bid, cell_idx, None));
cell_idx
});
// 字段值表达式运行于构造器帧:其自由变量须纳入构造器 upvalue 捕获。
@@ -194,13 +206,15 @@ impl Emitter {
_ => None,
})
.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 mut extra_upvalue_names: Vec<(String, u8)> = Vec::new();
if let Some(c) = field_key_cell {
extra_upvalue_names.push(("@@field_keys".to_string(), c));
}
if let (Some(c), Some(bid)) = (brand_cell, private_brand_id) {
extra_upvalue_names.push((format!("@@class_brand_{bid}"), c));
}
if let (Some(name), Some(c)) = (ctor_name.as_deref(), class_self_cell) {
extra_upvalue_names.push((name, c));
extra_upvalue_names.push((name.to_string(), c));
}
let emit_instance_fields = |compiler: &Emitter, field_ctx: &mut CompileCtx| -> Result<(), String> {
@@ -210,10 +224,11 @@ impl Emitter {
return Err("class brand cell missing".into());
}
let brand_reg = field_ctx.alloc_reg();
let brand_name = format!("@@class_brand_{bid}");
let uv_idx = field_ctx
.current_upvalue_captures
.iter()
.position(|u| u.name == "@@class_brand")
.position(|u| u.name == brand_name)
.ok_or("class brand upvalue missing")? as u16;
field_ctx.inst(Inst::new(
OpCode::LOAD_UPVALUE,
@@ -299,9 +314,13 @@ impl Emitter {
// 字段初始化直接重发:构造器 upvalue/内置槽引用须与首轮编译产物对齐。
let mut field_ctx = CompileCtx::new();
// 字段表达式运行于构造器帧,构造器恒 strict,字段 ctx 同步置位。
// 私有名环境按堆叠口径整表继承(字段初始化器内的嵌套类需要外类
// 私有名与 brand 链保持可解析)。
field_ctx.is_strict = true;
field_ctx.scopes.private_name_map =
private_names.iter().map(|(n, id, _, _)| (n.clone(), *id)).collect();
field_ctx.scopes.private_name_map = ctx.scopes.private_name_map.clone();
field_ctx.scopes.private_element_kinds = ctx.scopes.private_element_kinds.clone();
field_ctx.scopes.private_brand_id = ctx.scopes.private_brand_id;
field_ctx.brand_chain = ctx.brand_chain.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
@@ -443,6 +462,7 @@ impl Emitter {
ctx.scopes.private_name_map = saved_private_names;
ctx.scopes.private_element_kinds = saved_private_kinds;
ctx.scopes.private_brand_id = saved_brand_id;
ctx.brand_chain = saved_brand_chain;
// 类编译结束恢复外层 strict 标志:类在 sloppy 上下文时不得泄漏 strict 到类后代码。
ctx.is_strict = saved_strict;
Ok(ctor_reg)
+10 -9
View File
@@ -109,16 +109,17 @@ impl Emitter {
let saved_static = ctx.in_static_method;
ctx.in_instance_method = !method.r#static;
ctx.in_static_method = method.r#static;
// 私有方法/访问器访问需对接收者做 brand 检查:方法函数捕获类 brand 对象
// (@@class_brand upvalue,值 = 类原型)。
let mut extra_uv: Vec<(&str, u8)> = ctx
.captured_bindings
.get("@@class_brand")
.map(|c| ("@@class_brand", *c))
.into_iter()
.collect();
// 私有方法/访问器访问需对接收者做 brand 检查:方法函数捕获本类 brand 对象
// (@@class_brand_<id> upvalue,值 = 类原型)。
let mut extra_uv: Vec<(String, u8)> = Vec::new();
if let Some(bid) = ctx.scopes.private_brand_id {
let brand_name = format!("@@class_brand_{bid}");
if let Some(&cell_idx) = ctx.captured_bindings.get(&brand_name) {
extra_uv.push((brand_name, cell_idx));
}
}
if let (Some((name, _)), Some(c)) = (self_binding.first(), class_self_cell) {
extra_uv.push((name, c));
extra_uv.push(((*name).to_string(), c));
}
let method_value = method.value.as_ref();
// 生成器/异步/异步生成器方法与普通方法统一走带标志入口:`*m` 置 is_generator,
+7
View File
@@ -74,6 +74,12 @@ pub struct CompileCtx {
/// 类定义期全部 computed key 数组寄存器(方法/静态字段阶段按 slot 读取)。
pub(crate) class_keys_reg: Option<u32>,
pub(crate) current_upvalue_captures: Vec<UpvalueCapture>,
/// 嵌套类私有 brand 链:每类一条 `(brand_id, cell_idx, parent_uv_idx)`,
/// 描述本帧如何到达该类的 brand cell——`parent_uv_idx` 为 None 时 cell 在
/// 本帧 own cell 表(cell_idx),为 Some(k) 时经本帧第 k 个 upvalue 可达。
/// 子函数体编译时按父链逐条追加为链式 upvalue 捕获,使内类方法能对
/// 外类声明的私有名做跨类 brand 检查。
pub(crate) brand_chain: Vec<(u32, u8, Option<u8>)>,
/// 本函数作用域声明的绑定名(参数 + 变量/函数声明,AST 收集,emit 前确定)。
pub(crate) own_bindings: HashSet<String>,
/// 本函数形参名集(含解构形参叶子与 rest,编译入口收集):块级函数名与
@@ -283,6 +289,7 @@ impl CompileCtx {
field_keys_uv: None,
class_keys_reg: None,
current_upvalue_captures: Vec::new(),
brand_chain: Vec::new(),
own_bindings: HashSet::new(),
param_names: HashSet::new(),
param_tdz_names: HashSet::new(),
+5 -3
View File
@@ -148,9 +148,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)>,
/// 私有元素类型(name, kind,static 标志,声明类 brand id)。kind=None 表示
/// 字段;instance 字段的私有访问走 PrivateFieldFind 原型链查找,不加 brand
/// 检查。brand_id 是声明该私有名的类的 brand id:跨类访问时 brand 检查须
/// 定位声明类(而非访问发生处类)的 brand 对象。
pub(crate) private_element_kinds: Vec<(String, Option<MethodDefinitionKind>, bool, u32)>,
/// 当前类的私有 brand 私有名 id:私有方法/访问器访问时对实例做 brand 检查。
pub(crate) private_brand_id: Option<u32>,
pub(crate) next_private_name_id: u32,
+33 -5
View File
@@ -280,7 +280,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,
emit_fields: Option<E>, fields_after_super: bool, extra_capture_exprs: &[&'a Expression<'a>],
extra_upvalue_names: &[(&str, u8)], own_strict: bool, name: Option<&str>,
extra_upvalue_names: &[(String, u8)], own_strict: bool, name: Option<&str>,
) -> Result<IRFunction, String>
where
E: FnMut(&Emitter, &mut CompileCtx) -> Result<(), String>,
@@ -308,7 +308,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)], is_generator: bool, is_async: bool, own_strict: bool, name: Option<&str>,
extra_upvalue_names: &[(String, u8)], is_generator: bool, is_async: bool, own_strict: bool, name: Option<&str>,
) -> Result<IRFunction, String>
where
E: FnMut(&Emitter, &mut CompileCtx) -> Result<(), String>,
@@ -655,7 +655,7 @@ impl Emitter {
fn emit_params_prologue<'a>(
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
ctx: &mut CompileCtx, body_context: FunctionBodyContext, extra_capture_exprs: &[&'a Expression<'a>],
extra_upvalue_names: &[(&str, u8)], name: Option<&str>,
extra_upvalue_names: &[(String, u8)], name: Option<&str>,
) -> Result<u32, String> {
ctx.push_scope_with_kind(ScopeKind::FunctionScope);
let param_base = ctx.next_reg;
@@ -852,15 +852,43 @@ impl Emitter {
.collect();
// 类字段 computed key 数组等合成捕获:直接追加 upvalue(cell_idx 由父分配)。
for (name, cell_idx) in extra_upvalue_names {
if !ctx.own_bindings.contains(*name) && !ctx.current_upvalue_captures.iter().any(|u| u.name == *name) {
if !ctx.own_bindings.contains(name) && !ctx.current_upvalue_captures.iter().any(|u| u.name == *name) {
ctx.current_upvalue_captures.push(UpvalueCapture {
name: (*name).to_string(),
name: name.clone(),
enclosing_reg: 0,
cell_idx: *cell_idx,
parent_uv_idx: None,
});
}
}
// 私有 brand 链随函数体继承:父帧每条 brand 链条目按同形态追加为
// upvalue 捕获(父条目描述父帧如何到达 brand cell,子帧 upvalue 以
// 同一方式指向父帧)。子帧 brand 链记录各条目在子捕获列表中的位置,
// 嵌套类逐层继承,跨类 brand 检查据此定位声明类的 brand 对象。
for (brand_id, cell_idx, parent_uv_idx) in &parent_ctx.brand_chain {
let name = format!("@@class_brand_{brand_id}");
if !ctx.own_bindings.contains(&name) && !ctx.current_upvalue_captures.iter().any(|u| u.name == name) {
ctx.current_upvalue_captures.push(UpvalueCapture {
name,
enclosing_reg: 0,
cell_idx: *cell_idx,
parent_uv_idx: *parent_uv_idx,
});
}
}
ctx.brand_chain = parent_ctx
.brand_chain
.iter()
.map(|(brand_id, _, _)| {
let name = format!("@@class_brand_{brand_id}");
let pos = ctx
.current_upvalue_captures
.iter()
.position(|u| u.name == name)
.expect("brand 链条目已追加或已存在捕获列表");
(*brand_id, 0, Some(pos as u8))
})
.collect();
}
// 创建 arguments 对象:指令须在默认参数求值前发出(默认参数可引用 arguments)。
+4 -1
View File
@@ -75,7 +75,10 @@ impl Emitter {
return Err("expected private method key".into());
};
let name = private.name.as_str();
let method_reg = self.emit_class_method_function(method, name, home_reg, ctx, self_binding, class_self_cell)?;
// 私有方法/访问器的函数名带 `#` 前缀(SetFunctionName 语义:私有键的
// 字符串值即 `#name`,访问器另加 `get `/`set ` 前缀)。
let method_reg =
self.emit_class_method_function(method, &format!("#{name}"), home_reg, ctx, self_binding, class_self_cell)?;
match method.kind {
MethodDefinitionKind::Method => {
let key_reg = self.emit_private_id_reg(name, ctx)?;
+9 -6
View File
@@ -29,20 +29,23 @@ impl Emitter {
/// dispatch 做 brand 检查。
///
/// instance 字段:dispatch 靠 own 槽存在性 + 不跨原型判定(PrivateFieldFind 只查 own),
/// 不需要 brand 值比较(返回 `(0,0)`);方法/访问器/静态字段须验证接收者属于当前类
/// (brand 对象同一性),返回 brand 编码。brand 对象由方法函数捕获的 `@@class_brand`
/// upvalue 提供;嵌套函数未捕获该 upvalue 时跳过检查(保持语法合法,brand 语义受限)。
/// 不需要 brand 值比较(返回 `(0,0)`);方法/访问器/静态字段须验证接收者属于声明类
/// (brand 对象同一性),返回 brand 编码。brand 对象按声明类的 brand id 定位
/// (`@@class_brand_<id>` upvalue,跨类访问时即外类 brand);当前函数未捕获该
/// 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 {
let entry = ctx.scopes.private_element_kinds.iter().find(|(n, _, _, _)| n == name);
let Some((_, kind, is_static, brand_id)) = entry 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 {
let brand_id = *brand_id;
let brand_name = format!("@@class_brand_{brand_id}");
let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == brand_name) else {
return Ok((0, 0));
};
let brand_reg = ctx.alloc_reg();
+87
View File
@@ -1044,3 +1044,90 @@ fn computed_accessor_symbol_key() {
.unwrap();
assert_num(result, 43.0);
}
// ── 嵌套类私有名作用域:内类方法可访问外类声明的私有名 ──
// 内类方法访问外类私有字段:字段走 PrivateFieldFind 原型链查找,不加 brand 检查。
#[test]
fn nested_class_method_reads_outer_private_field() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var C = class { #x = 1; B = class { method(o) { return o.#x; } } }; var c = new C(); new c.B().method(c)",
)
.unwrap();
assert_eq!(result.as_int(), 1);
}
// 内类方法访问外类私有方法:brand 检查经外类 brand upvalue(跨类定位声明类 brand 对象)。
#[test]
fn nested_class_method_calls_outer_private_method() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var C = class { #m() { return 4; } B = class { method(o) { return o.#m(); } } }; var c = new C(); new c.B().method(c)",
)
.unwrap();
assert_eq!(result.as_int(), 4);
}
// 内类自有同名私有名遮蔽外类:内类方法解析到内类 id,外类方法不受影响。
#[test]
fn nested_class_same_name_private_shadows_outer() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var C = class { #v = 1; outer() { return this.#v; } B = class { #v = 2; get() { return this.#v; } } }; var c = new C(); var b = new c.B(); b.get() * 10 + c.outer()",
)
.unwrap();
assert_num(result, 21.0);
}
// 跨类 brand 负例:内类对象访问外类私有方法抛 TypeError(brand 对象不同一)。
#[test]
fn nested_class_cross_brand_access_throws_type_error() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var C = class { #m() { return 1; } B = class { method(o) { return o.#m(); } } }; var c = new C(); var b = new c.B(); var r = 0; try { b.method(b); } catch (e) { r = (e.name === 'TypeError') ? 1 : 2; } r",
)
.unwrap();
assert_eq!(result.as_int(), 1);
}
// 私有方法函数名带 `#` 前缀(SetFunctionName 语义)。
#[test]
fn private_method_name_has_hash_prefix() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var C = class { static #m() {} static getM() { return this.#m; } }; C.getM().name",
)
.unwrap();
let name = vm.lookup_str(result).expect("name should be string");
assert_eq!(name, "#m");
}
// 私有访问器行为:getter 读、setter 写,经内类方法跨类访问外类私有访问器。
#[test]
fn nested_class_method_uses_outer_private_accessor() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var C = class { #v = 0; get #x() { return this.#v; } set #x(v) { this.#v = v; } B = class { use(o) { o.#x = 5; return o.#x; } } }; var c = new C(); new c.B().use(c)",
)
.unwrap();
assert_eq!(result.as_int(), 5);
}
// 三级类嵌套(类内类内类):brand 链经多层类编译保存/恢复后仍指向最外层声明类。
#[test]
fn triple_nested_class_reaches_outermost_private_method() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var C = class { #m() { return 7; } B = class { D = class { method(o) { return o.#m(); } } } }; var c = new C(); var b = new c.B(); var d = new b.D(); d.method(c)",
)
.unwrap();
assert_eq!(result.as_int(), 7);
}