From f751b7b7bb986569f3a1486c15f85cd6650652bc Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Thu, 17 Sep 2026 15:55:20 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20=E7=BB=9F=E4=B8=80=E9=A2=84=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E4=B8=8E=E6=A8=A1=E5=9D=97=E9=93=BE=E6=8E=A5=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E6=8E=AA=E8=BE=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_emit/src/module.rs | 23 +++++++++++++------ crates/oxide_emit/src/prepass/block_fn.rs | 9 ++++---- crates/oxide_emit/src/prepass/builtin_scan.rs | 10 +++++++- crates/oxide_emit/src/prepass/declare.rs | 9 ++++---- crates/oxide_emit/src/prepass/mod.rs | 15 +++++++----- 5 files changed, 44 insertions(+), 22 deletions(-) diff --git a/crates/oxide_emit/src/module.rs b/crates/oxide_emit/src/module.rs index cb87df4d..2a30dc62 100644 --- a/crates/oxide_emit/src/module.rs +++ b/crates/oxide_emit/src/module.rs @@ -2,7 +2,8 @@ //! //! 快照式链接:依赖模块先整体求值(`__moduleEval`)并返回命名空间对象, //! 导入绑定在 prelude 一次性初始化;`export` 语句就地注册导出值。 -//! live binding / defer / source-phase 语义留待后续轮次(循环导入在编译期跳过)。 +//! 未支持:live binding(导入绑定活态跟随源模块值变化)、defer(延迟求值)、 +//! source-phase——导入绑定为链接期快照,不跟随源模块值变化;循环导入在编译期跳过。 use std::collections::HashSet; use std::path::Path; @@ -42,11 +43,14 @@ pub struct ResolvedModule { /// 模块加载器:由调用方(test262 runner / CLI)提供文件解析。 pub trait ModuleSourceLoader { + /// 解析依赖模块:按 `base_dir` 与 `specifier` 定位模块源码,`attributes` 为 + /// with 子句的导入属性键值对。 fn resolve( &mut self, base_dir: &str, specifier: &str, attributes: &[(&str, &str)], ) -> Result; } +/// 把 `ModuleExportName`(标识符名 / 标识符引用 / 字符串字面量)统一转为字符串导出名。 fn module_export_name_str(m: &ModuleExportName) -> String { match m { ModuleExportName::IdentifierName(i) => i.name.to_string(), @@ -55,6 +59,7 @@ fn module_export_name_str(m: &ModuleExportName) -> String { } } +/// 从 with 子句提取导入属性键值对(键取标识符或字符串字面量,值取字符串字面量)。 fn module_attributes(with_clause: &Option>>) -> Vec<(String, String)> { let mut out = Vec::new(); if let Some(wc) = with_clause { @@ -117,6 +122,7 @@ fn module_own_export_names(body: &[Statement]) -> HashSet { names } +/// 判断语句是否为提升的函数声明(含 export 包装的函数声明)。 fn is_hoisted_function_decl(stmt: &Statement) -> bool { match stmt { Statement::FunctionDeclaration(_) => true, @@ -241,8 +247,8 @@ impl Emitter { // —— 导入绑定初始化(const 语义;命名空间绑定直接引用 ns 对象)—— let own_export_names = module_own_export_names(body); - // export * from 'x'(无命名再导出)会把依赖的全部导出转发给自身: - // self-import 的名称可能来自 star,编译期无法静态判定,校验放宽。 + // 无命名 `export * from 'x'` 会把依赖的全部导出转发给自身;自导入名可能来自 + // star 再导出,编译期无法静态判定该名是否存在于自身导出,故链接校验放宽。 let has_unnamed_star = body .iter() .any(|s| matches!(s, Statement::ExportAllDeclaration(e) if e.exported.is_none())); @@ -346,7 +352,8 @@ impl Emitter { } // —— re-export(export { x } from 'mod')链接检查:实例化期解析,须先于 - // body 求值($DONOTEVALUATE 等断言依赖链接错误先抛)—— + // body 求值。$DONOTEVALUATE 是 test262 测试框架的断言工具,断言链接错误先于 + // body 求值抛出,故链接检查必须先于 body 发射。—— for stmt in body { if let Statement::ExportNamedDeclaration(exp) = stmt { if exp.declaration.is_some() { @@ -392,8 +399,9 @@ impl Emitter { // —— 收尾:封冻命名空间并返回(顶层 RETURN 亦终止 run)—— self.emit_module_call(ctx, "__moduleSeal", &[ns_reg])?; if top_level { - // 顶层模块求值完成值为空记录:对外表现 undefined(依赖面不得 - // 同口径——__moduleEval 以依赖返回值作命名空间,contract 不可破)。 + // 顶层模块求值完成值为空记录(对外表现 undefined);依赖模块求值必须返回 + // 命名空间对象(`__moduleEval` 以依赖返回值作命名空间)——两条路径的返回 + // 值契约不同,顶层路径不得复用依赖路径的返回值。 let undef_idx = ctx.add_constant(Constant::Undefined); let r = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(r), undef_idx)); @@ -439,7 +447,8 @@ impl Emitter { let this_reg = ctx.alloc_reg(); let undef_idx = ctx.add_constant(Constant::Undefined); ctx.inst(Inst::load_const(Operand::Reg(this_reg), undef_idx)); - // CALL_NATIVE 按 regs[first_arg + i] 连续读参数:把非连续 vreg 打包为连续块。 + // CALL_NATIVE 按 regs[first_arg + i] 连续读参数:把非连续虚拟寄存器(vreg,经寄存器 + // 分配后映射为物理号)打包为连续寄存器块。 let mut packed = arg_regs.to_vec(); let first_arg_reg = if packed.is_empty() { this_reg } else { pack_arg_regs(&mut packed, ctx) }; ctx.inst(Inst::call_native( diff --git a/crates/oxide_emit/src/prepass/block_fn.rs b/crates/oxide_emit/src/prepass/block_fn.rs index cb00a671..d604f386 100644 --- a/crates/oxide_emit/src/prepass/block_fn.rs +++ b/crates/oxide_emit/src/prepass/block_fn.rs @@ -9,8 +9,8 @@ use crate::Emitter; use oxide_parser::{Declaration, Statement, VariableDeclarationKind}; impl Emitter { - /// 递归收集语句树内块级函数声明名(源序去重),供 web-compat 外层 var 绑定 - /// 实例化。 + /// 递归收集语句树内块级函数声明名(源序去重),供 sloppy 模式浏览器兼容行为 + /// (块级函数声明建外层 var 绑定)下的外层 var 绑定实例化。 /// /// # 边界与前提 /// - 调用点直接子级(函数体/程序顶层直接子句)是提升 var 声明,非块级 @@ -189,8 +189,9 @@ impl Emitter { } } - /// 块级函数名 web-compat 外层绑定抑制集:形参名(含解构叶/rest)∪ 语句树内 - /// 词法声明名。命中抑制的名字不建外层 var 绑定、不求值写回。 + /// 块级函数名的外层 var 绑定抑制集(sloppy 模式浏览器兼容行为):形参名 + /// (含解构叶/rest)∪ 语句树内词法声明名。命中抑制的名字不建外层 var 绑定、 + /// 不求值写回。 pub(crate) fn collect_block_fn_suppressed_names( &self, statements: &[Statement], param_names: &HashSet, ) -> HashSet { diff --git a/crates/oxide_emit/src/prepass/builtin_scan.rs b/crates/oxide_emit/src/prepass/builtin_scan.rs index b66c81cd..2cd4216e 100644 --- a/crates/oxide_emit/src/prepass/builtin_scan.rs +++ b/crates/oxide_emit/src/prepass/builtin_scan.rs @@ -6,13 +6,16 @@ use crate::{CompileCtx, Emitter}; use oxide_parser::{BindingPattern, Declaration, Expression, Statement}; impl Emitter { - /// 在临时寄存器池之前分配 builtin 槽位。 + /// 递归遍历程序 AST,把内置全局标识符引用预登记到固定寄存器槽(内置槽在临时 + /// 寄存器池之前分配,两者隔离,槽位分配序稳定)。 pub(crate) fn pre_register_builtin_references(&self, stmts: &[Statement], ctx: &mut CompileCtx) { for stmt in stmts { self.pre_scan_builtin_stmt(stmt, ctx); } } + /// 递归遍历语句树,登记其中的内置标识符引用:表达式语句、变量/函数/类声明、 + /// 控制流各分支与模块导出声明。 fn pre_scan_builtin_stmt(&self, stmt: &Statement, ctx: &mut CompileCtx) { match stmt { Statement::ExpressionStatement(es) => self.pre_scan_builtin_expr(&es.expression, ctx), @@ -150,6 +153,8 @@ impl Emitter { } } + /// 递归遍历表达式树,登记其中的内置标识符引用:标识符、调用/构造、成员、可选链、 + /// 对象/数组字面量与各类运算表达式。 fn pre_scan_builtin_expr(&self, expr: &Expression, ctx: &mut CompileCtx) { match expr { Expression::Identifier(ident) => { @@ -262,6 +267,8 @@ impl Emitter { } } + /// 遍历赋值目标,登记其中的内置标识符引用:标识符目标直接登记,成员目标只扫描 + /// 对象与计算键(属性名不是标识符引用)。 fn pre_scan_builtin_target(&self, target: &oxide_parser::SimpleAssignmentTarget, ctx: &mut CompileCtx) { match target { oxide_parser::SimpleAssignmentTarget::AssignmentTargetIdentifier(id) => { @@ -286,6 +293,7 @@ impl Emitter { } } + /// 遍历可选链元素,登记链上成员访问、调用与实参中的内置标识符引用。 fn pre_scan_builtin_chain(&self, element: &oxide_parser::ChainElement, ctx: &mut CompileCtx) { match element { oxide_parser::ChainElement::StaticMemberExpression(member) => { diff --git a/crates/oxide_emit/src/prepass/declare.rs b/crates/oxide_emit/src/prepass/declare.rs index 66698341..74e543d9 100644 --- a/crates/oxide_emit/src/prepass/declare.rs +++ b/crates/oxide_emit/src/prepass/declare.rs @@ -27,10 +27,11 @@ impl Emitter { } } - /// 预声明单个 `var` 名:仅顶层(全局作用域)的 builtin 名落 builtin 镜像槽 - /// (run 起点预载全局属性值),使 GDI 序言与声明点同步都用入口原值而非 - /// fresh undefined 槽,现存值(NaN 等)不被抹;函数体内 builtin 名仍是局部 - /// var 遮蔽(fresh 槽,不命中只读内置拦截);其余名 fresh var 槽。 + /// 预声明单个 `var` 名:仅顶层(全局作用域)的内置名落内置镜像槽(预载全局 + /// 属性值的固定寄存器槽,本轮求值开始时预载),使 GlobalDeclarationInstantiation + /// (脚本顶层声明实例化)序言与声明点同步都用入口原值,而非程序求值前 + /// define-if-absent 序列新建的 undefined 槽,现存值(NaN 等)不被抹;函数体内 + /// 内置名仍是局部 var 遮蔽(新建槽,不命中只读内置拦截);其余名新建 var 槽。 pub(crate) fn predeclare_var_name(&self, name: &str, ctx: &mut CompileCtx) { if ctx.is_global_scope && CompileCtx::is_known_builtin(name) { let _ = ctx.lookup_or_builtin(name); diff --git a/crates/oxide_emit/src/prepass/mod.rs b/crates/oxide_emit/src/prepass/mod.rs index 1d4e0559..75bb95f9 100644 --- a/crates/oxide_emit/src/prepass/mod.rs +++ b/crates/oxide_emit/src/prepass/mod.rs @@ -8,12 +8,15 @@ mod block_fn; mod builtin_scan; mod declare; -/// 脚本顶层 lexical 声明(let/const/class)禁止使用的受限全局名:3 名静态集。 -/// 受限判定走规范自有属性臂:全局对象上 {configurable:false} 自有属性名(声明 -/// 实例化无法建立遮蔽绑定)——现行规范下恰为三常量;eval 与各 builtin 属性皆 -/// 可配置(合法遮蔽),不在此列。名基静态集(编译期不可查运行时描述符)。与 -/// BUILTIN_GLOBALS 语义不同(后者是 put 写拦截/双写名单),不互相派生,交叠 -/// 名由漂移守卫单测断言恒同步。 +/// 脚本顶层词法声明(`let`/`const`/`class`)禁止使用的受限全局名:`undefined`、 +/// `NaN`、`Infinity` 三名静态集。 +/// +/// 判定标准是全局对象上 `configurable:false` 的自有属性名:此类属性无法由声明 +/// 实例化建立遮蔽绑定,现行规范下恰为三常量;`eval` 与其余内置属性皆可配置, +/// 可合法遮蔽。运行时无法查询全局对象描述符,名单必须编译期确定。 +/// +/// 与 `BUILTIN_GLOBALS` 语义不同:后者是写入拦截与双写名单,两份名单互不派生, +/// 交叠名由 `restricted_lexical_names_within_builtin_globals` 单测断言恒同步。 pub(crate) const RESTRICTED_GLOBAL_LEXICAL_NAMES: &[&str] = &["undefined", "NaN", "Infinity"]; /// 脚本顶层 lexical 声明撞受限全局名 → SyntaxError(声明实例化期拒绝,整程序