mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 03:01:12 +08:00
fix: 块级异步与生成器函数声明不再泄漏全局
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
//! emit 前置 pass,块级函数名收集:web-compat 外层 var 绑定实例化支撑——
|
||||
//! 语句树内块级函数声明名源序去重收集、词法声明名抑制集(块函数名与词法名
|
||||
//! 语句树内块级普通函数声明名源序去重收集、词法声明名抑制集(块函数名与词法名
|
||||
//! 同名时退化为纯块作用域)、形参名 ∪ 词法名抑制集构建。
|
||||
|
||||
use std::collections::HashSet;
|
||||
@@ -9,10 +9,13 @@ use crate::Emitter;
|
||||
use oxide_parser::{Declaration, Statement, VariableDeclarationKind};
|
||||
|
||||
impl Emitter {
|
||||
/// 递归收集语句树内块级函数声明名(源序去重),供 sloppy 模式浏览器兼容行为
|
||||
/// (块级函数声明建外层 var 绑定)下的外层 var 绑定实例化。
|
||||
/// 递归收集语句树内块级普通函数声明名(源序去重),供 sloppy 模式浏览器兼容
|
||||
/// 行为(Annex B.3.3 为块级普通函数声明建外层 var 绑定)下的外层 var 绑定实例化。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 只收普通 `FunctionDeclaration`:Annex B.3.3 的 web-compat 仅改写普通函数
|
||||
/// 声明,async 函数、生成器与 async 生成器是各自独立的块级词法绑定,块外
|
||||
/// 不可见,不入名集。
|
||||
/// - 调用点直接子级(函数体/程序顶层直接子句)是提升 var 声明,非块级
|
||||
/// 函数,不收集;进入嵌套语句(块/if/循环/switch case/try 各区)后的
|
||||
/// 函数声明才是块级。
|
||||
@@ -29,7 +32,7 @@ impl Emitter {
|
||||
for statement in statements {
|
||||
match statement {
|
||||
Statement::FunctionDeclaration(f) => {
|
||||
if block_level {
|
||||
if block_level && !f.generator && !f.r#async {
|
||||
if let Some(id) = &f.id {
|
||||
let name = id.name.to_string();
|
||||
if !names.contains(&name) {
|
||||
|
||||
@@ -49,17 +49,20 @@ impl Emitter {
|
||||
} else {
|
||||
self.materialize_function_declaration(fd, &name, ctx)?
|
||||
};
|
||||
// 块内函数声明求值后按三种情形处理:
|
||||
//
|
||||
// sloppy 模式下按浏览器/web 实现惯例为块级函数声明建外层 var 绑定并写回:
|
||||
// sloppy 模式下按 Annex B.3.3 为块级普通函数声明建外层 var 绑定并写回:
|
||||
// 预声明期为该名实例化的函数作用域 var 绑定在此写入块槽位里的函数对象,
|
||||
// 求值一次写回一次(循环体内每迭代覆写头绑定)。
|
||||
//
|
||||
// async 函数、生成器与 async 生成器是块级词法绑定,不在 Annex B.3.3 改写
|
||||
// 范围,块外不可见,不写回外层 var。
|
||||
//
|
||||
// 形参或函数作用域树内同名词法声明(抑制集)不写回:该名被同名声明遮蔽。
|
||||
//
|
||||
// 顶层不可写内置名不写回:sloppy 下对其赋值(put)永不成功,跳过与执行等价;
|
||||
// 顶层情形另补全局对象属性写(与 for-in 循环头的顶层写同形)。
|
||||
if !ctx.is_strict
|
||||
&& !fd.generator
|
||||
&& !fd.r#async
|
||||
&& ctx.scopes.symbols.scopes.len() > 1
|
||||
&& !ctx.block_fn_suppressed.contains(&name)
|
||||
&& !(ctx.is_global_scope && CompileCtx::is_non_writable_global_builtin(&name))
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//! try/catch/finally 语句 emit:`emit_try_statement` 生成 TRY_BEGIN 与处理入口。
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
@@ -32,13 +34,21 @@ impl Emitter {
|
||||
// try block 是独立块作用域:lexical 声明(let/const/class)限定于 try 内,
|
||||
// 与外层同名绑定互不干扰;块级预声明使声明点前读取编译为 TDZ 抛错。
|
||||
ctx.push_scope();
|
||||
// 函数预声明先于 lexical:同名 let 命中已有函数绑定报重复声明错。
|
||||
self.predeclare_block_function_declarations(&ts.block.body, ctx, false);
|
||||
// try 块 lexical 声明是局部绑定:不做受限全局名检查(重复声明错在 emit 期报)。
|
||||
let _ = self.predeclare_lexical_declarations(&ts.block.body, ctx, false);
|
||||
// 块级函数声明的块入口初始化:声明点前读命中函数对象,声明点复用入口槽。
|
||||
ctx.block_fn_entry_mats.push(HashMap::new());
|
||||
for s in &ts.block.body {
|
||||
self.emit_block_fn_entry_init_stmt(s, ctx)?;
|
||||
}
|
||||
for s in &ts.block.body {
|
||||
if let Some(r) = self.emit_statement(s, ctx)? {
|
||||
last_try_result = Some(r);
|
||||
}
|
||||
}
|
||||
ctx.block_fn_entry_mats.pop();
|
||||
ctx.pop_scope();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
@@ -64,14 +74,21 @@ impl Emitter {
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(src_reg), Operand::None, Operand::None));
|
||||
self.emit_binding_pattern(¶m.pattern, src_reg, VariableDeclarationKind::Let, false, false, ctx)?;
|
||||
}
|
||||
// catch 块函数预声明先于 lexical,块入口物化与 try/普通块同口径。
|
||||
self.predeclare_block_function_declarations(&catch.body.body, ctx, false);
|
||||
// catch 块 lexical 声明同为局部绑定:不做受限全局名检查。
|
||||
let _ = self.predeclare_lexical_declarations(&catch.body.body, ctx, false);
|
||||
ctx.block_fn_entry_mats.push(HashMap::new());
|
||||
for s in &catch.body.body {
|
||||
self.emit_block_fn_entry_init_stmt(s, ctx)?;
|
||||
}
|
||||
let mut last_catch_result: Option<u32> = None;
|
||||
for s in &catch.body.body {
|
||||
if let Some(r) = self.emit_statement(s, ctx)? {
|
||||
last_catch_result = Some(r);
|
||||
}
|
||||
}
|
||||
ctx.block_fn_entry_mats.pop();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
@@ -91,11 +108,18 @@ impl Emitter {
|
||||
// try/catch 收敛值保持 try 体或 catch 体值,finally 值不渗入。
|
||||
// finally block 同为独立块作用域,lexical 声明互不泄漏。
|
||||
ctx.push_scope();
|
||||
// finally 块函数预声明先于 lexical,块入口物化与 try/普通块同口径。
|
||||
self.predeclare_block_function_declarations(&ts.finalizer.as_ref().unwrap().body, ctx, false);
|
||||
// finally 块 lexical 声明同为局部绑定:不做受限全局名检查。
|
||||
let _ = self.predeclare_lexical_declarations(&ts.finalizer.as_ref().unwrap().body, ctx, false);
|
||||
ctx.block_fn_entry_mats.push(HashMap::new());
|
||||
for s in &ts.finalizer.as_ref().unwrap().body {
|
||||
self.emit_block_fn_entry_init_stmt(s, ctx)?;
|
||||
}
|
||||
for s in &ts.finalizer.as_ref().unwrap().body {
|
||||
self.emit_statement(s, ctx)?;
|
||||
}
|
||||
ctx.block_fn_entry_mats.pop();
|
||||
ctx.pop_scope();
|
||||
ctx.inst(Inst::new(OpCode::TRY_FINALLY_END, Operand::None, Operand::None, Operand::None));
|
||||
ctx.pop_open_try_handler();
|
||||
|
||||
Reference in New Issue
Block a user