mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
feat: 循环 let/const 每迭代绑定(MAKE_CELL_FRESH + 解构捕获修复)
This commit is contained in:
@@ -217,6 +217,9 @@ define_opcodes! {
|
|||||||
FOR_AWAIT_OF_NEXT = 0x7B => "FOR_AWAIT_OF_NEXT",
|
FOR_AWAIT_OF_NEXT = 0x7B => "FOR_AWAIT_OF_NEXT",
|
||||||
FOR_AWAIT_OF_DONE = 0x7C => "FOR_AWAIT_OF_DONE",
|
FOR_AWAIT_OF_DONE = 0x7C => "FOR_AWAIT_OF_DONE",
|
||||||
FOR_AWAIT_OF_CLOSE = 0x7D => "FOR_AWAIT_OF_CLOSE",
|
FOR_AWAIT_OF_CLOSE = 0x7D => "FOR_AWAIT_OF_CLOSE",
|
||||||
|
// 无条件新建 cell 并替换 cell_stack[cell_idx],值为 regs[rd]——循环每迭代绑定
|
||||||
|
// 把当前值拷入新 cell,本迭代闭包捕获新 cell(旧闭包仍指向旧 cell)。
|
||||||
|
MAKE_CELL_FRESH = 0x7E => "MAKE_CELL_FRESH",
|
||||||
|
|
||||||
// ── 位运算 (0x80-0x8F) ──
|
// ── 位运算 (0x80-0x8F) ──
|
||||||
BIT_AND = 0x80 => "BIT_AND",
|
BIT_AND = 0x80 => "BIT_AND",
|
||||||
|
|||||||
@@ -28,13 +28,39 @@ impl Emitter {
|
|||||||
names
|
names
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 递归收集 binding pattern 内全部绑定标识符名(解构 `[a, b]` / `{x: y}` 嵌套)。
|
||||||
|
fn collect_binding_pattern_names(&self, pattern: &oxide_parser::BindingPattern, out: &mut HashSet<String>) {
|
||||||
|
match pattern {
|
||||||
|
oxide_parser::BindingPattern::BindingIdentifier(bi) => {
|
||||||
|
out.insert(bi.name.to_string());
|
||||||
|
}
|
||||||
|
oxide_parser::BindingPattern::ArrayPattern(ap) => {
|
||||||
|
for p in ap.elements.iter().flatten() {
|
||||||
|
self.collect_binding_pattern_names(p, out);
|
||||||
|
}
|
||||||
|
if let Some(rest) = &ap.rest {
|
||||||
|
self.collect_binding_pattern_names(&rest.argument, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oxide_parser::BindingPattern::ObjectPattern(op) => {
|
||||||
|
for prop in &op.properties {
|
||||||
|
self.collect_binding_pattern_names(&prop.value, out);
|
||||||
|
}
|
||||||
|
if let Some(rest) = &op.rest {
|
||||||
|
self.collect_binding_pattern_names(&rest.argument, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oxide_parser::BindingPattern::AssignmentPattern(ap) => {
|
||||||
|
self.collect_binding_pattern_names(&ap.left, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 收集 for-in/for-of 头部声明(`var x` / 解构 pattern)绑定的名字。
|
/// 收集 for-in/for-of 头部声明(`var x` / 解构 pattern)绑定的名字。
|
||||||
fn collect_for_left_decl_names(&self, left: &oxide_parser::ForStatementLeft, out: &mut HashSet<String>) {
|
fn collect_for_left_decl_names(&self, left: &oxide_parser::ForStatementLeft, out: &mut HashSet<String>) {
|
||||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = left {
|
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = left {
|
||||||
for d in &vd.declarations {
|
for d in &vd.declarations {
|
||||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
self.collect_binding_pattern_names(&d.id, out);
|
||||||
out.insert(bi.name.to_string());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,9 +80,7 @@ impl Emitter {
|
|||||||
match stmt {
|
match stmt {
|
||||||
Statement::VariableDeclaration(vd) => {
|
Statement::VariableDeclaration(vd) => {
|
||||||
for d in &vd.declarations {
|
for d in &vd.declarations {
|
||||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
self.collect_binding_pattern_names(&d.id, out);
|
||||||
out.insert(bi.name.to_string());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::FunctionDeclaration(fd) => {
|
Statement::FunctionDeclaration(fd) => {
|
||||||
@@ -86,22 +110,14 @@ impl Emitter {
|
|||||||
// for-in / for-of 头部的 var 声明是本函数局部绑定(遮蔽外层同名),
|
// for-in / for-of 头部的 var 声明是本函数局部绑定(遮蔽外层同名),
|
||||||
// 须计入 own_bindings,否则闭包捕获分析会误判为捕获外层变量。
|
// 须计入 own_bindings,否则闭包捕获分析会误判为捕获外层变量。
|
||||||
Statement::ForInStatement(fi) => {
|
Statement::ForInStatement(fi) => {
|
||||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fi.left {
|
if let oxide_parser::ForStatementLeft::VariableDeclaration(_) = &fi.left {
|
||||||
for d in &vd.declarations {
|
self.collect_for_left_decl_names(&fi.left, out);
|
||||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
|
||||||
out.insert(bi.name.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.collect_decl_names_stmt(std::slice::from_ref(&fi.body), out);
|
self.collect_decl_names_stmt(std::slice::from_ref(&fi.body), out);
|
||||||
}
|
}
|
||||||
Statement::ForOfStatement(fo) => {
|
Statement::ForOfStatement(fo) => {
|
||||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fo.left {
|
if let oxide_parser::ForStatementLeft::VariableDeclaration(_) = &fo.left {
|
||||||
for d in &vd.declarations {
|
self.collect_for_left_decl_names(&fo.left, out);
|
||||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
|
||||||
out.insert(bi.name.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.collect_decl_names_stmt(std::slice::from_ref(&fo.body), out);
|
self.collect_decl_names_stmt(std::slice::from_ref(&fo.body), out);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,6 +194,10 @@ pub struct CompileCtx {
|
|||||||
/// try_stack 组成。每项标记是否为纯 catch handler(TRY_BEGIN,由 TRY_END
|
/// try_stack 组成。每项标记是否为纯 catch handler(TRY_BEGIN,由 TRY_END
|
||||||
/// 弹出):return 逃出 try 域时据此弹出栈顶连续纯 catch,防 handler 泄漏。
|
/// 弹出):return 逃出 try 域时据此弹出栈顶连续纯 catch,防 handler 泄漏。
|
||||||
pub(crate) open_try_handlers: Vec<bool>,
|
pub(crate) open_try_handlers: Vec<bool>,
|
||||||
|
/// 循环 update 段中应走寄存器(而非 cell)的被捕获绑定名:C 风格 for 的
|
||||||
|
/// let/const 循环变量每迭代 fresh cell,update 写寄存器(不污染本迭代闭包
|
||||||
|
/// 捕获的 cell),下一迭代 fresh 从寄存器拷入新 cell。
|
||||||
|
pub(crate) register_update_names: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 函数体编译上下文:决定 `this`/`super` 绑定与参数前导(prologue)形态。
|
/// 函数体编译上下文:决定 `this`/`super` 绑定与参数前导(prologue)形态。
|
||||||
@@ -289,6 +293,7 @@ impl CompileCtx {
|
|||||||
const_overflow: false,
|
const_overflow: false,
|
||||||
with_stack: Vec::new(),
|
with_stack: Vec::new(),
|
||||||
open_try_handlers: Vec::new(),
|
open_try_handlers: Vec::new(),
|
||||||
|
register_update_names: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1117,7 +1122,7 @@ impl Emitter {
|
|||||||
} else {
|
} else {
|
||||||
src_reg
|
src_reg
|
||||||
};
|
};
|
||||||
self.emit_binding_pattern(pattern, src_reg, VariableDeclarationKind::Var, false, ctx)?;
|
self.emit_binding_pattern(pattern, src_reg, VariableDeclarationKind::Var, false, false, ctx)?;
|
||||||
}
|
}
|
||||||
ParamSpec::Identifier { name, initializer } => {
|
ParamSpec::Identifier { name, initializer } => {
|
||||||
if let Some(init) = initializer {
|
if let Some(init) = initializer {
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ impl Emitter {
|
|||||||
fn emit_compound_identifier_static(
|
fn emit_compound_identifier_static(
|
||||||
&self, name: &str, op: OpCode, rhs: u32, ctx: &mut CompileCtx,
|
&self, name: &str, op: OpCode, rhs: u32, ctx: &mut CompileCtx,
|
||||||
) -> Result<u32, String> {
|
) -> Result<u32, String> {
|
||||||
|
// 循环 update 段:被捕获绑定走寄存器 RMW(C 风格 for 每迭代 fresh,
|
||||||
|
// update 写寄存器供下一迭代 fresh 拷贝,不污染本迭代闭包捕获的 cell)。
|
||||||
|
if ctx.register_update_names.iter().any(|n| n == name) {
|
||||||
|
if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
|
||||||
|
ctx.inst(Inst::new(op, Operand::Reg(reg), Operand::Reg(rhs), Operand::None));
|
||||||
|
return Ok(reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
|
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
|
||||||
let captured_cell = ctx.captured_bindings.get(name).copied();
|
let captured_cell = ctx.captured_bindings.get(name).copied();
|
||||||
if let Some(uv) = uv_idx {
|
if let Some(uv) = uv_idx {
|
||||||
|
|||||||
@@ -112,6 +112,19 @@ impl Emitter {
|
|||||||
/// - `const_flag` 为 1 时走 STORE_VAR 运行时 const guard(对 const 再赋值报错)。
|
/// - `const_flag` 为 1 时走 STORE_VAR 运行时 const guard(对 const 再赋值报错)。
|
||||||
/// - upvalue 与 cell 写穿共享单元,不设 guard。
|
/// - upvalue 与 cell 写穿共享单元,不设 guard。
|
||||||
pub(crate) fn emit_identifier_store(&self, name: &str, val_reg: u32, const_flag: u16, ctx: &mut CompileCtx) {
|
pub(crate) fn emit_identifier_store(&self, name: &str, val_reg: u32, const_flag: u16, ctx: &mut CompileCtx) {
|
||||||
|
// 循环 update 段:被捕获绑定走寄存器而非 cell(C 风格 for 每迭代 fresh,
|
||||||
|
// update 写寄存器供下一迭代 fresh 拷贝,不污染本迭代闭包捕获的 cell)。
|
||||||
|
if ctx.register_update_names.iter().any(|n| n == name) {
|
||||||
|
if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::STORE_VAR,
|
||||||
|
Operand::Reg(reg),
|
||||||
|
Operand::Reg(val_reg),
|
||||||
|
Operand::Imm(const_flag),
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
// 目标若是 upvalue 引用,走 STORE_UPVALUE
|
// 目标若是 upvalue 引用,走 STORE_UPVALUE
|
||||||
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
|
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
|
||||||
ctx.inst(Inst::new(
|
ctx.inst(Inst::new(
|
||||||
|
|||||||
@@ -418,6 +418,21 @@ impl Emitter {
|
|||||||
) -> Result<u32, String> {
|
) -> Result<u32, String> {
|
||||||
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
|
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
|
||||||
let captured_cell = ctx.captured_bindings.get(name).copied();
|
let captured_cell = ctx.captured_bindings.get(name).copied();
|
||||||
|
// 循环 update 段:被捕获绑定走寄存器 INC/DEC(C 风格 for 每迭代 fresh,
|
||||||
|
// update 写寄存器供下一迭代 fresh 拷贝,不污染本迭代闭包捕获的 cell)。
|
||||||
|
if ctx.register_update_names.iter().any(|n| n == name) {
|
||||||
|
if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
|
||||||
|
let result_reg = ctx.alloc_reg();
|
||||||
|
let op = match (update.operator, update.prefix) {
|
||||||
|
(UpdateOperator::Increment, true) => OpCode::INC_PRE,
|
||||||
|
(UpdateOperator::Increment, false) => OpCode::INC_POST,
|
||||||
|
(UpdateOperator::Decrement, true) => OpCode::DEC_PRE,
|
||||||
|
(UpdateOperator::Decrement, false) => OpCode::DEC_POST,
|
||||||
|
};
|
||||||
|
ctx.inst(Inst::new(op, Operand::Reg(reg), Operand::Reg(result_reg), Operand::Reg(result_reg)));
|
||||||
|
return Ok(result_reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
if let Some(uv) = uv_idx {
|
if let Some(uv) = uv_idx {
|
||||||
// upvalue:LOAD_UPVALUE + 常量 1 + ADD/SUB + STORE_UPVALUE;
|
// upvalue:LOAD_UPVALUE + 常量 1 + ADD/SUB + STORE_UPVALUE;
|
||||||
// 后缀形式返回旧值(前缀返回新值)。
|
// 后缀形式返回旧值(前缀返回新值)。
|
||||||
|
|||||||
@@ -48,8 +48,11 @@ impl Emitter {
|
|||||||
Ok(val_reg)
|
Ok(val_reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 绑定单个标识符到槽位。`fresh_cell` 为 true 时对被捕获绑定用 MAKE_CELL_FRESH
|
||||||
|
/// (循环每迭代绑定:无条件新建 cell,本迭代闭包捕获新 cell)。
|
||||||
fn emit_bind_target(
|
fn emit_bind_target(
|
||||||
&self, name: &str, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, ctx: &mut CompileCtx,
|
&self, name: &str, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, fresh_cell: bool,
|
||||||
|
ctx: &mut CompileCtx,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let target_reg = if matches!(kind, VariableDeclarationKind::Var) {
|
let target_reg = if matches!(kind, VariableDeclarationKind::Var) {
|
||||||
// `var` 名已预声明(hoisting),复用预登记槽位而非新分配,使 n_registers 与 let/const 一致。
|
// `var` 名已预声明(hoisting),复用预登记槽位而非新分配,使 n_registers 与 let/const 一致。
|
||||||
@@ -66,8 +69,9 @@ impl Emitter {
|
|||||||
var_reg
|
var_reg
|
||||||
};
|
};
|
||||||
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||||
|
let op = if fresh_cell { OpCode::MAKE_CELL_FRESH } else { OpCode::MAKE_CELL };
|
||||||
ctx.inst(Inst::new(
|
ctx.inst(Inst::new(
|
||||||
OpCode::MAKE_CELL,
|
op,
|
||||||
Operand::Reg(src_reg),
|
Operand::Reg(src_reg),
|
||||||
Operand::Imm(cell_idx as u16),
|
Operand::Imm(cell_idx as u16),
|
||||||
Operand::None,
|
Operand::None,
|
||||||
@@ -119,14 +123,14 @@ impl Emitter {
|
|||||||
|
|
||||||
pub(crate) fn emit_binding_pattern(
|
pub(crate) fn emit_binding_pattern(
|
||||||
&self, pattern: &BindingPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool,
|
&self, pattern: &BindingPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool,
|
||||||
ctx: &mut CompileCtx,
|
fresh_cell: bool, ctx: &mut CompileCtx,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
match pattern {
|
match pattern {
|
||||||
BindingPattern::BindingIdentifier(bi) => {
|
BindingPattern::BindingIdentifier(bi) => {
|
||||||
self.emit_bind_target(bi.name.as_str(), src_reg, kind, is_const, ctx)
|
self.emit_bind_target(bi.name.as_str(), src_reg, kind, is_const, fresh_cell, ctx)
|
||||||
}
|
}
|
||||||
BindingPattern::ArrayPattern(ap) => self.emit_array_binding(ap, src_reg, kind, is_const, ctx),
|
BindingPattern::ArrayPattern(ap) => self.emit_array_binding(ap, src_reg, kind, is_const, fresh_cell, ctx),
|
||||||
BindingPattern::ObjectPattern(op) => self.emit_object_binding(op, src_reg, kind, is_const, ctx),
|
BindingPattern::ObjectPattern(op) => self.emit_object_binding(op, src_reg, kind, is_const, fresh_cell, ctx),
|
||||||
BindingPattern::AssignmentPattern(ap) => {
|
BindingPattern::AssignmentPattern(ap) => {
|
||||||
// `[x = fn]`:默认值函数按绑定名推断 name。
|
// `[x = fn]`:默认值函数按绑定名推断 name。
|
||||||
let name = match &ap.left {
|
let name = match &ap.left {
|
||||||
@@ -134,13 +138,14 @@ impl Emitter {
|
|||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
let val_reg = self.emit_default_if_undefined(src_reg, &ap.right, name, ctx)?;
|
let val_reg = self.emit_default_if_undefined(src_reg, &ap.right, name, ctx)?;
|
||||||
self.emit_binding_pattern(&ap.left, val_reg, kind, is_const, ctx)
|
self.emit_binding_pattern(&ap.left, val_reg, kind, is_const, fresh_cell, ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_array_binding(
|
fn emit_array_binding(
|
||||||
&self, ap: &ArrayPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, ctx: &mut CompileCtx,
|
&self, ap: &ArrayPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, fresh_cell: bool,
|
||||||
|
ctx: &mut CompileCtx,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
ctx.inst(Inst::new(OpCode::FOR_OF_INIT, Operand::None, Operand::Reg(src_reg), Operand::None));
|
ctx.inst(Inst::new(OpCode::FOR_OF_INIT, Operand::None, Operand::Reg(src_reg), Operand::None));
|
||||||
for elem in &ap.elements {
|
for elem in &ap.elements {
|
||||||
@@ -149,12 +154,12 @@ impl Emitter {
|
|||||||
let val_reg = ctx.alloc_reg();
|
let val_reg = ctx.alloc_reg();
|
||||||
ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None));
|
ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None));
|
||||||
if let Some(pattern) = elem {
|
if let Some(pattern) = elem {
|
||||||
self.emit_binding_pattern(pattern, val_reg, kind, is_const, ctx)?;
|
self.emit_binding_pattern(pattern, val_reg, kind, is_const, fresh_cell, ctx)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(rest) = &ap.rest {
|
if let Some(rest) = &ap.rest {
|
||||||
let rest_reg = self.emit_collect_rest_array(ctx)?;
|
let rest_reg = self.emit_collect_rest_array(ctx)?;
|
||||||
self.emit_binding_pattern(&rest.argument, rest_reg, kind, is_const, ctx)?;
|
self.emit_binding_pattern(&rest.argument, rest_reg, kind, is_const, fresh_cell, ctx)?;
|
||||||
}
|
}
|
||||||
ctx.inst(Inst::new(OpCode::FOR_OF_CLOSE, Operand::None, Operand::None, Operand::None));
|
ctx.inst(Inst::new(OpCode::FOR_OF_CLOSE, Operand::None, Operand::None, Operand::None));
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -193,7 +198,8 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emit_object_binding(
|
fn emit_object_binding(
|
||||||
&self, op: &ObjectPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, ctx: &mut CompileCtx,
|
&self, op: &ObjectPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, fresh_cell: bool,
|
||||||
|
ctx: &mut CompileCtx,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// 对象解构先 ToObject(rhs):null/undefined 抛 TypeError(含空 pattern),
|
// 对象解构先 ToObject(rhs):null/undefined 抛 TypeError(含空 pattern),
|
||||||
// 原始值包装为对应对象,保证属性读取与 rest 都以对象为源。
|
// 原始值包装为对应对象,保证属性读取与 rest 都以对象为源。
|
||||||
@@ -221,7 +227,7 @@ impl Emitter {
|
|||||||
self.emit_push_excluded_key(arr, Some(key_reg), "", excl_push, ctx);
|
self.emit_push_excluded_key(arr, Some(key_reg), "", excl_push, ctx);
|
||||||
excl_push += 1;
|
excl_push += 1;
|
||||||
}
|
}
|
||||||
self.emit_binding_pattern(&prop.value, prop_reg, kind, is_const, ctx)?;
|
self.emit_binding_pattern(&prop.value, prop_reg, kind, is_const, fresh_cell, ctx)?;
|
||||||
}
|
}
|
||||||
if let Some(rest) = &op.rest {
|
if let Some(rest) = &op.rest {
|
||||||
let rest_reg = ctx.alloc_reg();
|
let rest_reg = ctx.alloc_reg();
|
||||||
@@ -232,7 +238,7 @@ impl Emitter {
|
|||||||
excluded_idx as u32,
|
excluded_idx as u32,
|
||||||
excl_arr_reg.map(Operand::Reg),
|
excl_arr_reg.map(Operand::Reg),
|
||||||
));
|
));
|
||||||
self.emit_binding_pattern(&rest.argument, rest_reg, kind, is_const, ctx)?;
|
self.emit_binding_pattern(&rest.argument, rest_reg, kind, is_const, fresh_cell, ctx)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
if let Some(init) = &d.init {
|
if let Some(init) = &d.init {
|
||||||
let val_reg = self.emit_expression(init, ctx)?;
|
let val_reg = self.emit_expression(init, ctx)?;
|
||||||
self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, ctx)?;
|
self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, false, ctx)?;
|
||||||
if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||||
if crate::is_anonymous_function_definition(init) {
|
if crate::is_anonymous_function_definition(init) {
|
||||||
if let Some(sub_mod) = ctx.nested.last_mut() {
|
if let Some(sub_mod) = ctx.nested.last_mut() {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ impl Emitter {
|
|||||||
let src_reg = ctx.alloc_reg();
|
let src_reg = ctx.alloc_reg();
|
||||||
// 异常值在 VM 物理 regs[0](unwind 展开处写入),STORE_VAR a=None 读回。
|
// 异常值在 VM 物理 regs[0](unwind 展开处写入),STORE_VAR a=None 读回。
|
||||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(src_reg), Operand::None, Operand::None));
|
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, ctx)?;
|
self.emit_binding_pattern(¶m.pattern, src_reg, VariableDeclarationKind::Let, false, false, ctx)?;
|
||||||
}
|
}
|
||||||
let mut last_catch_result: Option<u32> = None;
|
let mut last_catch_result: Option<u32> = None;
|
||||||
for s in &catch.body.body {
|
for s in &catch.body.body {
|
||||||
|
|||||||
@@ -8,6 +8,30 @@ use oxide_ir::operand::Operand;
|
|||||||
use oxide_parser::{BindingPattern, ForStatementInit, Statement, VariableDeclarationKind};
|
use oxide_parser::{BindingPattern, ForStatementInit, Statement, VariableDeclarationKind};
|
||||||
|
|
||||||
impl Emitter {
|
impl Emitter {
|
||||||
|
/// 递归收集解构 pattern 内全部绑定标识符名(用于循环头声明被捕获时的 fresh 判定)。
|
||||||
|
fn collect_pattern_binding_names(&self, pattern: &BindingPattern, out: &mut Vec<String>) {
|
||||||
|
match pattern {
|
||||||
|
BindingPattern::BindingIdentifier(bi) => out.push(bi.name.as_str().to_string()),
|
||||||
|
BindingPattern::ArrayPattern(ap) => {
|
||||||
|
for p in ap.elements.iter().flatten() {
|
||||||
|
self.collect_pattern_binding_names(p, out);
|
||||||
|
}
|
||||||
|
if let Some(rest) = &ap.rest {
|
||||||
|
self.collect_pattern_binding_names(&rest.argument, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BindingPattern::ObjectPattern(op) => {
|
||||||
|
for prop in &op.properties {
|
||||||
|
self.collect_pattern_binding_names(&prop.value, out);
|
||||||
|
}
|
||||||
|
if let Some(rest) = &op.rest {
|
||||||
|
self.collect_pattern_binding_names(&rest.argument, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BindingPattern::AssignmentPattern(ap) => self.collect_pattern_binding_names(&ap.left, out),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn emit_for_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
|
pub(crate) fn emit_for_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
|
||||||
let Statement::ForStatement(fr) = stmt else {
|
let Statement::ForStatement(fr) = stmt else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@@ -17,6 +41,8 @@ impl Emitter {
|
|||||||
let end_label = ctx.next_label_id();
|
let end_label = ctx.next_label_id();
|
||||||
ctx.push_loop(end_label, update_label);
|
ctx.push_loop(end_label, update_label);
|
||||||
let n_labeled = ctx.take_pending_loop_labels(end_label, update_label);
|
let n_labeled = ctx.take_pending_loop_labels(end_label, update_label);
|
||||||
|
// 循环头声明中被嵌套函数捕获的 let/const 绑定:每迭代 fresh cell。
|
||||||
|
let mut fresh_bindings: Vec<(String, u8)> = Vec::new();
|
||||||
if let Some(init) = &fr.init {
|
if let Some(init) = &fr.init {
|
||||||
if let Some(expr) = init.as_expression() {
|
if let Some(expr) = init.as_expression() {
|
||||||
self.emit_expression(expr, ctx)?;
|
self.emit_expression(expr, ctx)?;
|
||||||
@@ -25,7 +51,23 @@ impl Emitter {
|
|||||||
let is_const = matches!(decl.kind, VariableDeclarationKind::Const);
|
let is_const = matches!(decl.kind, VariableDeclarationKind::Const);
|
||||||
if let Some(init_expr) = &d.init {
|
if let Some(init_expr) = &d.init {
|
||||||
let val_reg = self.emit_expression(init_expr, ctx)?;
|
let val_reg = self.emit_expression(init_expr, ctx)?;
|
||||||
self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, ctx)?;
|
self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, false, ctx)?;
|
||||||
|
// 被捕获绑定:fresh 从寄存器读当前值,须把 init 值写入寄存器槽。
|
||||||
|
// emit_binding_pattern 对捕获绑定只建 cell,不写寄存器。
|
||||||
|
let mut names = Vec::new();
|
||||||
|
self.collect_pattern_binding_names(&d.id, &mut names);
|
||||||
|
for name in &names {
|
||||||
|
if ctx.captured_bindings.contains_key(name) {
|
||||||
|
if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::STORE_VAR,
|
||||||
|
Operand::Reg(reg),
|
||||||
|
Operand::Reg(val_reg),
|
||||||
|
Operand::None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
} else if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||||
let idx = ctx.add_constant(Constant::Undefined);
|
let idx = ctx.add_constant(Constant::Undefined);
|
||||||
let tmp = ctx.alloc_reg();
|
let tmp = ctx.alloc_reg();
|
||||||
@@ -48,10 +90,32 @@ impl Emitter {
|
|||||||
));
|
));
|
||||||
ctx.init_var(bi.name.as_str());
|
ctx.init_var(bi.name.as_str());
|
||||||
}
|
}
|
||||||
|
// 记录被捕获的 let/const 声明(解构 pattern 递归收集绑定名)。
|
||||||
|
if !matches!(decl.kind, VariableDeclarationKind::Var) {
|
||||||
|
let mut names = Vec::new();
|
||||||
|
self.collect_pattern_binding_names(&d.id, &mut names);
|
||||||
|
for name in &names {
|
||||||
|
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||||
|
fresh_bindings.push((name.clone(), cell_idx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.labels.set_label_pos(start_label, ctx.insts.len());
|
ctx.labels.set_label_pos(start_label, ctx.insts.len());
|
||||||
|
// 每迭代 fresh cell:被捕获的 let/const 循环变量在迭代开始拷贝当前值到新 cell,
|
||||||
|
// 本迭代闭包捕获新 cell(规范 CreatePerIterationEnvironment)。
|
||||||
|
// 值源是循环变量的寄存器(update 段写寄存器而非 cell),首迭代即 init 值。
|
||||||
|
for (name, cell_idx) in &fresh_bindings {
|
||||||
|
let reg = ctx.scopes.symbols.lookup_any(name).expect("declared loop binding");
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::MAKE_CELL_FRESH,
|
||||||
|
Operand::Reg(reg),
|
||||||
|
Operand::Imm(*cell_idx as u16),
|
||||||
|
Operand::None,
|
||||||
|
));
|
||||||
|
}
|
||||||
if let Some(test) = &fr.test {
|
if let Some(test) = &fr.test {
|
||||||
let test_reg = self.emit_expression(test, ctx)?;
|
let test_reg = self.emit_expression(test, ctx)?;
|
||||||
ctx.inst(Inst::jmp_if_false(test_reg, end_label));
|
ctx.inst(Inst::jmp_if_false(test_reg, end_label));
|
||||||
@@ -59,7 +123,13 @@ impl Emitter {
|
|||||||
self.emit_statement(&fr.body, ctx)?;
|
self.emit_statement(&fr.body, ctx)?;
|
||||||
ctx.labels.set_label_pos(update_label, ctx.insts.len());
|
ctx.labels.set_label_pos(update_label, ctx.insts.len());
|
||||||
if let Some(update) = &fr.update {
|
if let Some(update) = &fr.update {
|
||||||
|
// 被捕获的 let/const 循环变量在 update 段写寄存器(而非 cell),
|
||||||
|
// 供下一迭代 fresh 拷贝——否则 CELL_SET 会污染本迭代闭包捕获的 cell。
|
||||||
|
let prev = std::mem::take(&mut ctx.register_update_names);
|
||||||
|
let names: Vec<String> = fresh_bindings.iter().map(|(n, _)| n.clone()).collect();
|
||||||
|
ctx.register_update_names = names;
|
||||||
self.emit_expression(update, ctx)?;
|
self.emit_expression(update, ctx)?;
|
||||||
|
ctx.register_update_names = prev;
|
||||||
}
|
}
|
||||||
ctx.inst(Inst::jmp(start_label));
|
ctx.inst(Inst::jmp(start_label));
|
||||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||||
|
|||||||
@@ -29,15 +29,30 @@ impl Emitter {
|
|||||||
ctx.inst(Inst::new(OpCode::FOR_IN_NEXT, Operand::Reg(key_reg), Operand::None, Operand::None));
|
ctx.inst(Inst::new(OpCode::FOR_IN_NEXT, Operand::Reg(key_reg), Operand::None, Operand::None));
|
||||||
match &fi.left {
|
match &fi.left {
|
||||||
ForStatementLeft::VariableDeclaration(decl) => {
|
ForStatementLeft::VariableDeclaration(decl) => {
|
||||||
|
// let/const 声明对被捕获绑定用 fresh cell(每迭代新 cell);var 单绑定。
|
||||||
|
let fresh_cell = !matches!(decl.kind, VariableDeclarationKind::Var);
|
||||||
for d in &decl.declarations {
|
for d in &decl.declarations {
|
||||||
let name = match &d.id {
|
match &d.id {
|
||||||
oxide_parser::BindingPattern::BindingIdentifier(bi) => bi.name.as_str(),
|
oxide_parser::BindingPattern::BindingIdentifier(bi) => {
|
||||||
|
let is_const = matches!(decl.kind, VariableDeclarationKind::Const);
|
||||||
|
let name = bi.name.as_str();
|
||||||
|
let var_reg = ctx.alloc_reg();
|
||||||
|
ctx.declare(name, var_reg, decl.kind, is_const)?;
|
||||||
|
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||||
|
let op = if fresh_cell { OpCode::MAKE_CELL_FRESH } else { OpCode::MAKE_CELL };
|
||||||
|
ctx.inst(Inst::new(op, Operand::Reg(key_reg), Operand::Imm(cell_idx as u16), Operand::None));
|
||||||
|
} else {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::STORE_VAR,
|
||||||
|
Operand::Reg(var_reg),
|
||||||
|
Operand::Reg(key_reg),
|
||||||
|
Operand::None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
ctx.init_var(name);
|
||||||
|
}
|
||||||
_ => return Err("destructuring not supported".into()),
|
_ => return Err("destructuring not supported".into()),
|
||||||
};
|
}
|
||||||
let var_reg = ctx.alloc_reg();
|
|
||||||
ctx.declare(name, var_reg, decl.kind, matches!(decl.kind, VariableDeclarationKind::Const))?;
|
|
||||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(var_reg), Operand::Reg(key_reg), Operand::None));
|
|
||||||
ctx.init_var(name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use crate::{CompileCtx, Emitter};
|
|||||||
use oxide_bytecode::opcode::OpCode;
|
use oxide_bytecode::opcode::OpCode;
|
||||||
use oxide_ir::inst::Inst;
|
use oxide_ir::inst::Inst;
|
||||||
use oxide_ir::operand::Operand;
|
use oxide_ir::operand::Operand;
|
||||||
use oxide_parser::{ForOfStatement, ForStatementLeft, Statement};
|
use oxide_parser::{ForOfStatement, ForStatementLeft, Statement, VariableDeclarationKind};
|
||||||
|
|
||||||
impl Emitter {
|
impl Emitter {
|
||||||
pub(crate) fn emit_for_of_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
|
pub(crate) fn emit_for_of_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
|
||||||
@@ -84,13 +84,16 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// for-of/for-await-of 左侧绑定:把当前迭代值 `val_reg` 写入声明/赋值目标。
|
/// for-of/for-await-of 左侧绑定:把当前迭代值 `val_reg` 写入声明/赋值目标。
|
||||||
|
/// let/const 声明对被捕获绑定用 fresh cell(每迭代新 cell,规范 per-iteration 绑定);
|
||||||
|
/// var 保持单绑定。
|
||||||
fn emit_for_of_left_assignment(
|
fn emit_for_of_left_assignment(
|
||||||
&self, left: &ForStatementLeft, val_reg: u32, ctx: &mut CompileCtx,
|
&self, left: &ForStatementLeft, val_reg: u32, ctx: &mut CompileCtx,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
match left {
|
match left {
|
||||||
ForStatementLeft::VariableDeclaration(decl) => {
|
ForStatementLeft::VariableDeclaration(decl) => {
|
||||||
|
let fresh_cell = !matches!(decl.kind, VariableDeclarationKind::Var);
|
||||||
for d in &decl.declarations {
|
for d in &decl.declarations {
|
||||||
self.emit_binding_pattern(&d.id, val_reg, decl.kind, false, ctx)?;
|
self.emit_binding_pattern(&d.id, val_reg, decl.kind, false, fresh_cell, ctx)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ impl Inst {
|
|||||||
| OpCode::TRY_FINALLY_BEGIN
|
| OpCode::TRY_FINALLY_BEGIN
|
||||||
| OpCode::TRY_FINALLY_END
|
| OpCode::TRY_FINALLY_END
|
||||||
| OpCode::MAKE_CELL
|
| OpCode::MAKE_CELL
|
||||||
|
| OpCode::MAKE_CELL_FRESH
|
||||||
| OpCode::CELL_SET
|
| OpCode::CELL_SET
|
||||||
| OpCode::STORE_UPVALUE
|
| OpCode::STORE_UPVALUE
|
||||||
| OpCode::SET_PROP
|
| OpCode::SET_PROP
|
||||||
@@ -254,7 +255,7 @@ impl Inst {
|
|||||||
OpCode::SPILL => push_operand(&mut uses, &self.rd),
|
OpCode::SPILL => push_operand(&mut uses, &self.rd),
|
||||||
OpCode::UNSPILL => {}
|
OpCode::UNSPILL => {}
|
||||||
// MAKE_CELL:cell 初值读 rd;CELL_SET/STORE_UPVALUE 读 a
|
// MAKE_CELL:cell 初值读 rd;CELL_SET/STORE_UPVALUE 读 a
|
||||||
OpCode::MAKE_CELL => push_operand(&mut uses, &self.rd),
|
OpCode::MAKE_CELL | OpCode::MAKE_CELL_FRESH => push_operand(&mut uses, &self.rd),
|
||||||
OpCode::CELL_SET | OpCode::STORE_UPVALUE => push_operand(&mut uses, &self.a),
|
OpCode::CELL_SET | OpCode::STORE_UPVALUE => push_operand(&mut uses, &self.a),
|
||||||
// 写对象属性:rd/a/b 全 use
|
// 写对象属性:rd/a/b 全 use
|
||||||
OpCode::SET_PROP
|
OpCode::SET_PROP
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ fn rewrite_inst(
|
|||||||
inst.op
|
inst.op
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
OpCode::MAKE_CELL | OpCode::CELL_GET | OpCode::CELL_SET | OpCode::STORE_UPVALUE => {
|
OpCode::MAKE_CELL | OpCode::MAKE_CELL_FRESH | OpCode::CELL_GET | OpCode::CELL_SET | OpCode::STORE_UPVALUE => {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
matches!(b, Operand::Imm(_) | Operand::None),
|
matches!(b, Operand::Imm(_) | Operand::None),
|
||||||
"{}: b 槽是 imm16 编码位,不得为 Reg",
|
"{}: b 槽是 imm16 编码位,不得为 Reg",
|
||||||
|
|||||||
@@ -204,6 +204,24 @@ impl Vm {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 无条件新建 cell 并替换 cell_stack[cell_idx],值为 `regs[rd]`。
|
||||||
|
///
|
||||||
|
/// 循环每迭代绑定用:把当前迭代值拷入新 cell,本迭代创建的闭包经
|
||||||
|
/// CREATE_CLOSURE 捕获新 cell(旧闭包仍指向旧 cell,值保持)。与
|
||||||
|
/// `dispatch_make_cell` 的唯一区别是恒新建、不复用占位 cell。
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn dispatch_make_cell_fresh(&mut self, rd: usize, instr: u32) -> Result<(), String> {
|
||||||
|
let cell_idx = opcode::imm16(instr) as usize;
|
||||||
|
let value = self.regs[rd];
|
||||||
|
let current = self.cell_stack.last_mut().unwrap();
|
||||||
|
while current.len() <= cell_idx {
|
||||||
|
current.push(std::ptr::null_mut());
|
||||||
|
}
|
||||||
|
let cell = self.gc_state.session_epoch.alloc(Cell::new(value, true));
|
||||||
|
current[cell_idx] = cell as *mut Cell;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn dispatch_cell_get(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
pub(crate) fn dispatch_cell_get(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||||
let cell_idx = b;
|
let cell_idx = b;
|
||||||
|
|||||||
@@ -1101,6 +1101,9 @@ impl Vm {
|
|||||||
OpCode::MAKE_CELL => {
|
OpCode::MAKE_CELL => {
|
||||||
self.dispatch_make_cell(rd, instr)?;
|
self.dispatch_make_cell(rd, instr)?;
|
||||||
}
|
}
|
||||||
|
OpCode::MAKE_CELL_FRESH => {
|
||||||
|
self.dispatch_make_cell_fresh(rd, instr)?;
|
||||||
|
}
|
||||||
OpCode::CELL_GET => {
|
OpCode::CELL_GET => {
|
||||||
self.dispatch_cell_get(rd, a, b)?;
|
self.dispatch_cell_get(rd, a, b)?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
//! 循环每迭代绑定(CreatePerIterationEnvironment):for/for-of/for-in 的 let/const
|
||||||
|
//! 闭包捕获各自迭代快照。对应 PLAN 06-01(正确性报告 P1/P2)。
|
||||||
|
|
||||||
|
use oxide_compiler::compiler::Compiler;
|
||||||
|
use oxide_types::value::JsValue;
|
||||||
|
use oxide_vm::vm::Vm;
|
||||||
|
|
||||||
|
fn eval(source: &str) -> Result<(Vm, JsValue), String> {
|
||||||
|
let allocator = oxide_parser::Allocator::default();
|
||||||
|
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||||
|
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = vm.run(&module)?;
|
||||||
|
Ok((vm, result))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_str(vm: &Vm, val: JsValue) -> String {
|
||||||
|
vm.lookup_str(val).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn c_style_for_let_captures_per_iteration() {
|
||||||
|
let (vm, result) =
|
||||||
|
eval("let fns = []; for (let i = 0; i < 3; i++) fns.push(() => i); [fns[0](), fns[1](), fns[2]()].join(',')")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(to_str(&vm, result), "0,1,2");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn c_style_for_let_captures_after_multiple_updates() {
|
||||||
|
let (vm, result) = eval("let fns = []; for (let i = 0; i < 5; i += 2) fns.push(() => i); [fns[0](), fns[1](), fns[2]()].join(',')")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(to_str(&vm, result), "0,2,4");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn c_style_for_var_remains_single_binding() {
|
||||||
|
// var 循环变量是单绑定(规范:闭包捕获同一绑定,最终值共享)。
|
||||||
|
let (vm, result) =
|
||||||
|
eval("let fns = []; for (var i = 0; i < 3; i++) fns.push(() => i); [fns[0](), fns[1](), fns[2]()].join(',')")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(to_str(&vm, result), "3,3,3");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn for_of_const_captures_per_iteration() {
|
||||||
|
let (vm, result) = eval("let fns = []; for (const a of [1, 2]) fns.push(() => a); [fns[0](), fns[1]()].join(',')").unwrap();
|
||||||
|
assert_eq!(to_str(&vm, result), "1,2");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn for_of_let_captures_per_iteration() {
|
||||||
|
let (vm, result) =
|
||||||
|
eval("let fns = []; for (let a of [10, 20, 30]) fns.push(() => a); [fns[0](), fns[1](), fns[2]()].join(',')").unwrap();
|
||||||
|
assert_eq!(to_str(&vm, result), "10,20,30");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn for_of_destructuring_captures_per_iteration() {
|
||||||
|
// P2:解构 + 闭包捕获不再产出垃圾值。
|
||||||
|
let (vm, result) =
|
||||||
|
eval("let fns = []; for (const [a, b] of [[1, 2], [3, 4]]) fns.push(() => a + b); [fns[0](), fns[1]()].join(',')")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(to_str(&vm, result), "3,7");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn for_of_object_destructuring_captures_per_iteration() {
|
||||||
|
let (vm, result) =
|
||||||
|
eval("let fns = []; for (const {x, y} of [{x:1,y:2},{x:3,y:4}]) fns.push(() => x * y); [fns[0](), fns[1]()].join(',')")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(to_str(&vm, result), "2,12");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn for_in_const_captures_per_iteration() {
|
||||||
|
let (vm, result) = eval("let fns = []; for (const k in {a:1,b:2}) fns.push(() => k); [fns[0](), fns[1]()].join(',')").unwrap();
|
||||||
|
assert_eq!(to_str(&vm, result), "a,b");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_loop_closure_captures_distinct_bindings() {
|
||||||
|
let (vm, result) = eval(
|
||||||
|
"let fns = []; for (let i = 0; i < 2; i++) for (let j = 0; j < 2; j++) fns.push(() => i + j); [fns[0](), fns[1](), fns[2](), fns[3]()].join(',')",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(to_str(&vm, result), "0,1,1,2");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn loop_variable_mutation_visible_to_same_iteration() {
|
||||||
|
// 同迭代内变量修改(非闭包)仍直接可见:fresh 每迭代一次,迭代内读写走同一 cell。
|
||||||
|
let (vm, result) = eval(
|
||||||
|
"let fns = []; for (let i = 0; i < 3; i++) { fns.push(() => i); i += 0; } [fns[0](), fns[1](), fns[2]()].join(',')",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(to_str(&vm, result), "0,1,2");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user