mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
docs: 统一预扫描与模块链接注释措辞
This commit is contained in:
@@ -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<ResolvedModule, String>;
|
||||
}
|
||||
|
||||
/// 把 `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<Box<'_, WithClause<'_>>>) -> 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<String> {
|
||||
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(
|
||||
|
||||
@@ -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<String>,
|
||||
) -> HashSet<String> {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(声明实例化期拒绝,整程序
|
||||
|
||||
Reference in New Issue
Block a user