mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-27 04:01:11 +08:00
fix: for 头与解构赋值写点补 upvalue 与 cell 臂,捕获预扫补赋值头
This commit is contained in:
@@ -8,7 +8,10 @@ use oxide_parser::{Declaration, ExportDefaultDeclarationKind, Expression, Statem
|
|||||||
|
|
||||||
use super::collect_fn_param_names;
|
use super::collect_fn_param_names;
|
||||||
use super::names::collect_own_binding_names;
|
use super::names::collect_own_binding_names;
|
||||||
use super::scanner::{collect_capture_names_expr, collect_capture_names_shadowed, collect_class_capture_names};
|
use super::scanner::{
|
||||||
|
collect_capture_names_expr, collect_capture_names_for_left, collect_capture_names_shadowed,
|
||||||
|
collect_class_capture_names,
|
||||||
|
};
|
||||||
|
|
||||||
/// 分析本函数:哪些绑定被任意深度嵌套函数捕获 → captured_bindings。
|
/// 分析本函数:哪些绑定被任意深度嵌套函数捕获 → captured_bindings。
|
||||||
///
|
///
|
||||||
@@ -215,6 +218,10 @@ pub(crate) fn collect_captured_stmt(stmt: &Statement, own: &HashSet<String>, out
|
|||||||
for d in &vd.declarations {
|
for d in &vd.declarations {
|
||||||
collect_captured_binding_keys(&d.id, own, out);
|
collect_captured_binding_keys(&d.id, own, out);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 赋值头是既有绑定引用:头名与默认值表达式须纳入捕获判定。
|
||||||
|
let empty_shadow = HashSet::new();
|
||||||
|
collect_capture_names_for_left(&fi.left, own, &empty_shadow, out);
|
||||||
}
|
}
|
||||||
collect_captured_stmt(&fi.body, own, out);
|
collect_captured_stmt(&fi.body, own, out);
|
||||||
}
|
}
|
||||||
@@ -224,6 +231,9 @@ pub(crate) fn collect_captured_stmt(stmt: &Statement, own: &HashSet<String>, out
|
|||||||
for d in &vd.declarations {
|
for d in &vd.declarations {
|
||||||
collect_captured_binding_keys(&d.id, own, out);
|
collect_captured_binding_keys(&d.id, own, out);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let empty_shadow = HashSet::new();
|
||||||
|
collect_capture_names_for_left(&fo.left, own, &empty_shadow, out);
|
||||||
}
|
}
|
||||||
collect_captured_stmt(&fo.body, own, out);
|
collect_captured_stmt(&fo.body, own, out);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,9 @@ pub(crate) fn collect_capture_names_stmt(
|
|||||||
for d in &vd.declarations {
|
for d in &vd.declarations {
|
||||||
collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
|
collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 赋值头是既有绑定引用:头名与默认值表达式须纳入捕获判定。
|
||||||
|
collect_capture_names_for_left(&fi.left, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
collect_capture_names_shadowed(std::slice::from_ref(&fi.body), ref_set, &for_shadow, out);
|
collect_capture_names_shadowed(std::slice::from_ref(&fi.body), ref_set, &for_shadow, out);
|
||||||
}
|
}
|
||||||
@@ -115,6 +118,8 @@ pub(crate) fn collect_capture_names_stmt(
|
|||||||
for d in &vd.declarations {
|
for d in &vd.declarations {
|
||||||
collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
|
collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
collect_capture_names_for_left(&fo.left, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
collect_capture_names_shadowed(std::slice::from_ref(&fo.body), ref_set, &for_shadow, out);
|
collect_capture_names_shadowed(std::slice::from_ref(&fo.body), ref_set, &for_shadow, out);
|
||||||
}
|
}
|
||||||
@@ -335,14 +340,33 @@ pub(crate) fn collect_capture_names_assign_target(
|
|||||||
collect_capture_names_expr(&m.expression, ref_set, shadow, out);
|
collect_capture_names_expr(&m.expression, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
oxide_parser::AssignmentTarget::ArrayAssignmentTarget(a) => {
|
oxide_parser::AssignmentTarget::ArrayAssignmentTarget(a) => {
|
||||||
|
collect_capture_names_array_target(a, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
oxide_parser::AssignmentTarget::ObjectAssignmentTarget(o) => {
|
||||||
|
collect_capture_names_object_target(o, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 数组赋值目标:元素递归(含默认值),rest 目标递归。
|
||||||
|
fn collect_capture_names_array_target(
|
||||||
|
a: &oxide_parser::ArrayAssignmentTarget, ref_set: &HashSet<String>, shadow: &HashSet<String>,
|
||||||
|
out: &mut HashSet<String>,
|
||||||
|
) {
|
||||||
for e in a.elements.iter().flatten() {
|
for e in a.elements.iter().flatten() {
|
||||||
collect_capture_names_maybe_default_target(e, ref_set, shadow, out);
|
collect_capture_names_maybe_default_target(e, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
if let Some(rest) = &a.rest {
|
if let Some(rest) = &a.rest {
|
||||||
collect_capture_names_assign_target(&rest.target, ref_set, shadow, out);
|
collect_capture_names_assign_target(&rest.target, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
oxide_parser::AssignmentTarget::ObjectAssignmentTarget(o) => {
|
|
||||||
|
/// 对象赋值目标:属性标识符写名/默认值/计算键,rest 目标递归。
|
||||||
|
fn collect_capture_names_object_target(
|
||||||
|
o: &oxide_parser::ObjectAssignmentTarget, ref_set: &HashSet<String>, shadow: &HashSet<String>,
|
||||||
|
out: &mut HashSet<String>,
|
||||||
|
) {
|
||||||
for prop in &o.properties {
|
for prop in &o.properties {
|
||||||
if let oxide_parser::AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(id) = prop {
|
if let oxide_parser::AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(id) = prop {
|
||||||
if ref_set.contains(id.binding.name.as_str()) && !shadow.contains(id.binding.name.as_str()) {
|
if ref_set.contains(id.binding.name.as_str()) && !shadow.contains(id.binding.name.as_str()) {
|
||||||
@@ -361,6 +385,26 @@ pub(crate) fn collect_capture_names_assign_target(
|
|||||||
if let Some(rest) = &o.rest {
|
if let Some(rest) = &o.rest {
|
||||||
collect_capture_names_assign_target(&rest.target, ref_set, shadow, out);
|
collect_capture_names_assign_target(&rest.target, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 扫描 for-in/for-of 赋值头:标识符写名、数组/对象解构元素(含默认值)与 rest。
|
||||||
|
/// 声明头是新绑定无引用语义,不扫。
|
||||||
|
pub(crate) fn collect_capture_names_for_left(
|
||||||
|
left: &oxide_parser::ForStatementLeft, ref_set: &HashSet<String>, shadow: &HashSet<String>,
|
||||||
|
out: &mut HashSet<String>,
|
||||||
|
) {
|
||||||
|
match left {
|
||||||
|
oxide_parser::ForStatementLeft::AssignmentTargetIdentifier(id) => {
|
||||||
|
let name = id.name.as_str();
|
||||||
|
if ref_set.contains(name) && !shadow.contains(name) {
|
||||||
|
out.insert(name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oxide_parser::ForStatementLeft::ArrayAssignmentTarget(a) => {
|
||||||
|
collect_capture_names_array_target(a, ref_set, shadow, out);
|
||||||
|
}
|
||||||
|
oxide_parser::ForStatementLeft::ObjectAssignmentTarget(o) => {
|
||||||
|
collect_capture_names_object_target(o, ref_set, shadow, out);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,6 +146,31 @@ impl Emitter {
|
|||||||
// 解构赋值目标:TDZ 写抛 ReferenceError,const 写抛 TypeError(编译期拦截)。
|
// 解构赋值目标:TDZ 写抛 ReferenceError,const 写抛 TypeError(编译期拦截)。
|
||||||
self.emit_identifier_tdz_guard(name, ctx)?;
|
self.emit_identifier_tdz_guard(name, ctx)?;
|
||||||
self.emit_const_write_guard(name, ctx)?;
|
self.emit_const_write_guard(name, ctx)?;
|
||||||
|
// 目标若是 upvalue 引用,走 STORE_UPVALUE(与表达式赋值写点同臂序)。
|
||||||
|
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::STORE_UPVALUE,
|
||||||
|
Operand::Imm(0),
|
||||||
|
Operand::Reg(src_reg),
|
||||||
|
Operand::Imm(uv_idx as u16),
|
||||||
|
));
|
||||||
|
self.emit_module_write_through(name, src_reg, ctx)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
// 目标若是被捕获 cell,走 CELL_SET;仅当名字当前可解析为真实词法绑定时才写
|
||||||
|
// cell,否则落下方全局解析(未声明名不建隐式全局登记)。
|
||||||
|
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||||
|
if ctx.visible_binding_reg(name).is_some() {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::CELL_SET,
|
||||||
|
Operand::None,
|
||||||
|
Operand::Reg(src_reg),
|
||||||
|
Operand::Imm(cell_idx as u16),
|
||||||
|
));
|
||||||
|
self.emit_module_write_through(name, src_reg, ctx)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
let var_reg = ctx.lookup_or_global(name);
|
let var_reg = ctx.lookup_or_global(name);
|
||||||
if ctx.targets_readonly_builtin(name, var_reg) {
|
if ctx.targets_readonly_builtin(name, var_reg) {
|
||||||
// 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值);
|
// 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值);
|
||||||
@@ -359,6 +384,31 @@ impl Emitter {
|
|||||||
// 解构赋值目标:TDZ 写抛 ReferenceError,const 写抛 TypeError(编译期拦截)。
|
// 解构赋值目标:TDZ 写抛 ReferenceError,const 写抛 TypeError(编译期拦截)。
|
||||||
self.emit_identifier_tdz_guard(name, ctx)?;
|
self.emit_identifier_tdz_guard(name, ctx)?;
|
||||||
self.emit_const_write_guard(name, ctx)?;
|
self.emit_const_write_guard(name, ctx)?;
|
||||||
|
// 目标若是 upvalue 引用,走 STORE_UPVALUE(与表达式赋值写点同臂序)。
|
||||||
|
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::STORE_UPVALUE,
|
||||||
|
Operand::Imm(0),
|
||||||
|
Operand::Reg(src_reg),
|
||||||
|
Operand::Imm(uv_idx as u16),
|
||||||
|
));
|
||||||
|
self.emit_module_write_through(name, src_reg, ctx)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
// 目标若是被捕获 cell,走 CELL_SET;仅当名字当前可解析为真实词法绑定时才写
|
||||||
|
// cell,否则落下方全局解析(未声明名不建隐式全局登记)。
|
||||||
|
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||||
|
if ctx.visible_binding_reg(name).is_some() {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::CELL_SET,
|
||||||
|
Operand::None,
|
||||||
|
Operand::Reg(src_reg),
|
||||||
|
Operand::Imm(cell_idx as u16),
|
||||||
|
));
|
||||||
|
self.emit_module_write_through(name, src_reg, ctx)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
let var_reg = ctx.lookup_or_global(name);
|
let var_reg = ctx.lookup_or_global(name);
|
||||||
if ctx.targets_readonly_builtin(name, var_reg) {
|
if ctx.targets_readonly_builtin(name, var_reg) {
|
||||||
// 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值);
|
// 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值);
|
||||||
@@ -454,6 +504,31 @@ impl Emitter {
|
|||||||
// 解构赋值目标:TDZ 写抛 ReferenceError,const 写抛 TypeError(编译期拦截)。
|
// 解构赋值目标:TDZ 写抛 ReferenceError,const 写抛 TypeError(编译期拦截)。
|
||||||
self.emit_identifier_tdz_guard(name, ctx)?;
|
self.emit_identifier_tdz_guard(name, ctx)?;
|
||||||
self.emit_const_write_guard(name, ctx)?;
|
self.emit_const_write_guard(name, ctx)?;
|
||||||
|
// 目标若是 upvalue 引用,走 STORE_UPVALUE(与表达式赋值写点同臂序)。
|
||||||
|
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::STORE_UPVALUE,
|
||||||
|
Operand::Imm(0),
|
||||||
|
Operand::Reg(prop_reg),
|
||||||
|
Operand::Imm(uv_idx as u16),
|
||||||
|
));
|
||||||
|
self.emit_module_write_through(name, prop_reg, ctx)?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 目标若是被捕获 cell,走 CELL_SET;仅当名字当前可解析为真实词法绑定时才写
|
||||||
|
// cell,否则落下方全局解析(未声明名不建隐式全局登记)。
|
||||||
|
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||||
|
if ctx.visible_binding_reg(name).is_some() {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::CELL_SET,
|
||||||
|
Operand::None,
|
||||||
|
Operand::Reg(prop_reg),
|
||||||
|
Operand::Imm(cell_idx as u16),
|
||||||
|
));
|
||||||
|
self.emit_module_write_through(name, prop_reg, ctx)?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
let var_reg = ctx.lookup_or_global(name);
|
let var_reg = ctx.lookup_or_global(name);
|
||||||
if ctx.targets_readonly_builtin(name, var_reg) {
|
if ctx.targets_readonly_builtin(name, var_reg) {
|
||||||
// 全局不可写内置:sloppy 静默跳过该属性写(其余属性继续赋值);
|
// 全局不可写内置:sloppy 静默跳过该属性写(其余属性继续赋值);
|
||||||
|
|||||||
@@ -236,8 +236,62 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
||||||
let name = id_ref.name.as_str();
|
let name = id_ref.name.as_str();
|
||||||
let var_reg = ctx.lookup_or_global(name);
|
|
||||||
// 写仅在本迭代实际产生键后发生(for-in NEXT 之后),空集合不抛。
|
// 写仅在本迭代实际产生键后发生(for-in NEXT 之后),空集合不抛。
|
||||||
|
// 目标若是 upvalue 引用,走 STORE_UPVALUE(与表达式赋值写点同臂序);
|
||||||
|
// 目标若是被捕获 cell 且名字当前可解析为真实词法绑定,走 CELL_SET;
|
||||||
|
// 否则落全局槽写(未声明名不建隐式全局登记)。
|
||||||
|
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::STORE_UPVALUE,
|
||||||
|
Operand::Imm(0),
|
||||||
|
Operand::Reg(key_reg),
|
||||||
|
Operand::Imm(uv_idx as u16),
|
||||||
|
));
|
||||||
|
} else if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||||
|
if ctx.visible_binding_reg(name).is_some() {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::CELL_SET,
|
||||||
|
Operand::None,
|
||||||
|
Operand::Reg(key_reg),
|
||||||
|
Operand::Imm(cell_idx as u16),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
self.emit_for_in_assignment_target_write(name, key_reg, ctx)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.emit_for_in_assignment_target_write(name, key_reg, ctx)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Err("unsupported for-in left-hand side".into()),
|
||||||
|
}
|
||||||
|
let body_result = self.emit_statement(&fi.body, ctx)?;
|
||||||
|
if let Some(env) = head_env {
|
||||||
|
self.restore_for_head_env(env, ctx);
|
||||||
|
}
|
||||||
|
ctx.inst(Inst::jmp(start_label));
|
||||||
|
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||||
|
ctx.inst(Inst::new(OpCode::FOR_IN_CLEANUP, Operand::None, Operand::None, Operand::None));
|
||||||
|
ctx.pop_label_scopes(n_labeled);
|
||||||
|
ctx.pop_loop();
|
||||||
|
ctx.pop_scope();
|
||||||
|
// 循环完成值 = 循环体最后一次非空完成值;空体物化 undefined 作为带值完成
|
||||||
|
// 返回,不沿用循环前的值。
|
||||||
|
let result = match body_result {
|
||||||
|
Some(r) => r,
|
||||||
|
None => self.emit_undefined(ctx),
|
||||||
|
};
|
||||||
|
Ok(Some(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// for-in 赋值头全局槽写:readonly 内置 / strict 未声明 / 顶层 tier / 普通槽
|
||||||
|
/// 四分支(与表达式赋值写点同臂序)。
|
||||||
|
///
|
||||||
|
/// # 副作用
|
||||||
|
/// - 发射写指令;strict 未声明写抛 ReferenceError,readonly 内置 strict 抛 TypeError。
|
||||||
|
fn emit_for_in_assignment_target_write(
|
||||||
|
&self, name: &str, key_reg: u32, ctx: &mut CompileCtx,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let var_reg = ctx.lookup_or_global(name);
|
||||||
if ctx.targets_readonly_builtin(name, var_reg) {
|
if ctx.targets_readonly_builtin(name, var_reg) {
|
||||||
// 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值);
|
// 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值);
|
||||||
// strict 在本迭代抛 TypeError(put 失败)。
|
// strict 在本迭代抛 TypeError(put 失败)。
|
||||||
@@ -269,25 +323,6 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
Ok(())
|
||||||
_ => return Err("unsupported for-in left-hand side".into()),
|
|
||||||
}
|
|
||||||
let body_result = self.emit_statement(&fi.body, ctx)?;
|
|
||||||
if let Some(env) = head_env {
|
|
||||||
self.restore_for_head_env(env, ctx);
|
|
||||||
}
|
|
||||||
ctx.inst(Inst::jmp(start_label));
|
|
||||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
|
||||||
ctx.inst(Inst::new(OpCode::FOR_IN_CLEANUP, Operand::None, Operand::None, Operand::None));
|
|
||||||
ctx.pop_label_scopes(n_labeled);
|
|
||||||
ctx.pop_loop();
|
|
||||||
ctx.pop_scope();
|
|
||||||
// 循环完成值 = 循环体最后一次非空完成值;空体物化 undefined 作为带值完成
|
|
||||||
// 返回,不沿用循环前的值。
|
|
||||||
let result = match body_result {
|
|
||||||
Some(r) => r,
|
|
||||||
None => self.emit_undefined(ctx),
|
|
||||||
};
|
|
||||||
Ok(Some(result))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,8 +142,31 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
||||||
let name = id_ref.name.as_str();
|
let name = id_ref.name.as_str();
|
||||||
let var_reg = ctx.lookup_or_global(name);
|
|
||||||
// 写仅在本迭代实际产生值后发生(for-of NEXT 之后),空集合不抛。
|
// 写仅在本迭代实际产生值后发生(for-of NEXT 之后),空集合不抛。
|
||||||
|
// 目标若是 upvalue 引用,走 STORE_UPVALUE(与表达式赋值写点同臂序)。
|
||||||
|
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::STORE_UPVALUE,
|
||||||
|
Operand::Imm(0),
|
||||||
|
Operand::Reg(val_reg),
|
||||||
|
Operand::Imm(uv_idx as u16),
|
||||||
|
));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
// 目标若是被捕获 cell,走 CELL_SET;仅当名字当前可解析为真实词法绑定时才写
|
||||||
|
// cell,否则落下方全局解析(未声明名不建隐式全局登记)。
|
||||||
|
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||||
|
if ctx.visible_binding_reg(name).is_some() {
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::CELL_SET,
|
||||||
|
Operand::None,
|
||||||
|
Operand::Reg(val_reg),
|
||||||
|
Operand::Imm(cell_idx as u16),
|
||||||
|
));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let var_reg = ctx.lookup_or_global(name);
|
||||||
if ctx.targets_readonly_builtin(name, var_reg) {
|
if ctx.targets_readonly_builtin(name, var_reg) {
|
||||||
// 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值);
|
// 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值);
|
||||||
// strict 在本迭代抛 TypeError(put 失败)。
|
// strict 在本迭代抛 TypeError(put 失败)。
|
||||||
@@ -151,10 +174,14 @@ impl Emitter {
|
|||||||
self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx)?;
|
self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let is_tier = self.is_global_tier_name(ctx, name);
|
||||||
let is_implicit = ctx.is_implicit_global_reg(var_reg);
|
let is_implicit = ctx.is_implicit_global_reg(var_reg);
|
||||||
if is_implicit && ctx.is_strict {
|
if is_implicit && ctx.is_strict {
|
||||||
// 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。
|
// 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。
|
||||||
self.emit_strict_undeclared_write(name, ctx)?;
|
self.emit_strict_undeclared_write(name, ctx)?;
|
||||||
|
} else if is_tier {
|
||||||
|
// 顶层 for-of 赋值头:迭代值写入全局对象属性(顶层 var 的唯一存储)。
|
||||||
|
self.emit_tier_global_write(name, val_reg, ctx);
|
||||||
} else {
|
} else {
|
||||||
ctx.inst(Inst::new(
|
ctx.inst(Inst::new(
|
||||||
OpCode::STORE_VAR,
|
OpCode::STORE_VAR,
|
||||||
|
|||||||
Reference in New Issue
Block a user