From 5fc898906a08dfde0665498792110a0fbe1821dd Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sat, 10 Oct 2026 03:15:38 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=A7=81=E6=9C=89=E5=90=8D=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=90=8D=E5=8A=A0=E4=BA=95=E5=8F=B7=E5=89=8D=E7=BC=80?= =?UTF-8?q?=E5=8A=A0=E5=B5=8C=E5=A5=97=E7=B1=BB=E7=8B=AC=E7=AB=8B=E4=BD=9C?= =?UTF-8?q?=E7=94=A8=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 38 ++++----- Cargo.toml | 2 +- crates/oxide_emit/src/class/emit.rs | 50 ++++++++---- crates/oxide_emit/src/class/method.rs | 19 ++--- crates/oxide_emit/src/compile_ctx.rs | 7 ++ crates/oxide_emit/src/emit_ctx.rs | 8 +- crates/oxide_emit/src/function_body.rs | 38 +++++++-- crates/oxide_emit/src/shared/class_field.rs | 5 +- crates/oxide_emit/src/shared/class_key.rs | 15 ++-- crates/oxide_vm/tests/class_tests.rs | 87 +++++++++++++++++++++ 10 files changed, 210 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85a92dd6..d9f19de0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index f9a58f68..6c11185b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.13.26" +version = "0.13.27" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_emit/src/class/emit.rs b/crates/oxide_emit/src/class/emit.rs index 9d983176..d8305f21 100644 --- a/crates/oxide_emit/src/class/emit.rs +++ b/crates/oxide_emit/src/class/emit.rs @@ -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 = private_brand_id.map(|_| { + // 捕获名按本类 brand id 编号:嵌套类各自登记 brand,内类方法体沿 brand 链 + // 保持外类 brand 条目可见,跨类 brand 检查据此定位声明类的 brand 对象。 + let brand_cell: Option = 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) diff --git a/crates/oxide_emit/src/class/method.rs b/crates/oxide_emit/src/class/method.rs index 991db92a..7eab2fd0 100644 --- a/crates/oxide_emit/src/class/method.rs +++ b/crates/oxide_emit/src/class/method.rs @@ -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_ 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, diff --git a/crates/oxide_emit/src/compile_ctx.rs b/crates/oxide_emit/src/compile_ctx.rs index ec286fe4..dce7ce0e 100644 --- a/crates/oxide_emit/src/compile_ctx.rs +++ b/crates/oxide_emit/src/compile_ctx.rs @@ -74,6 +74,12 @@ pub struct CompileCtx { /// 类定义期全部 computed key 数组寄存器(方法/静态字段阶段按 slot 读取)。 pub(crate) class_keys_reg: Option, pub(crate) current_upvalue_captures: Vec, + /// 嵌套类私有 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)>, /// 本函数作用域声明的绑定名(参数 + 变量/函数声明,AST 收集,emit 前确定)。 pub(crate) own_bindings: HashSet, /// 本函数形参名集(含解构形参叶子与 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(), diff --git a/crates/oxide_emit/src/emit_ctx.rs b/crates/oxide_emit/src/emit_ctx.rs index 0394a787..318a3e29 100644 --- a/crates/oxide_emit/src/emit_ctx.rs +++ b/crates/oxide_emit/src/emit_ctx.rs @@ -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, 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, bool, u32)>, /// 当前类的私有 brand 私有名 id:私有方法/访问器访问时对实例做 brand 检查。 pub(crate) private_brand_id: Option, pub(crate) next_private_name_id: u32, diff --git a/crates/oxide_emit/src/function_body.rs b/crates/oxide_emit/src/function_body.rs index 0b44c272..f802f391 100644 --- a/crates/oxide_emit/src/function_body.rs +++ b/crates/oxide_emit/src/function_body.rs @@ -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, 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 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, 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 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 { 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)。 diff --git a/crates/oxide_emit/src/shared/class_field.rs b/crates/oxide_emit/src/shared/class_field.rs index 16424833..4ef48ee5 100644 --- a/crates/oxide_emit/src/shared/class_field.rs +++ b/crates/oxide_emit/src/shared/class_field.rs @@ -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)?; diff --git a/crates/oxide_emit/src/shared/class_key.rs b/crates/oxide_emit/src/shared/class_key.rs index 37728836..121c8b35 100644 --- a/crates/oxide_emit/src/shared/class_key.rs +++ b/crates/oxide_emit/src/shared/class_key.rs @@ -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_` 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(); diff --git a/crates/oxide_vm/tests/class_tests.rs b/crates/oxide_vm/tests/class_tests.rs index a4cd3189..22d12116 100644 --- a/crates/oxide_vm/tests/class_tests.rs +++ b/crates/oxide_vm/tests/class_tests.rs @@ -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); +}