mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 18:51:13 +08:00
fix: 顶层 var 读写直连全局对象属性单一真值——GDI 序言 + 声明处同步删除,类原型 session 直分防晋升克隆 identity 分裂,自增减 int 保持
This commit is contained in:
@@ -11,7 +11,14 @@ impl Emitter {
|
||||
&self, ctor_reg: u32, proto_reg: u32, super_reg: Option<u32>, sub_idx: u16, ctx: &mut CompileCtx,
|
||||
) -> Result<(), String> {
|
||||
ctx.inst(Inst::create_closure(Operand::Reg(ctor_reg), sub_idx));
|
||||
ctx.inst(Inst::new(OpCode::NEW_OBJECT, Operand::Reg(proto_reg), Operand::None, Operand::None));
|
||||
// 类原型 session 直分:实例 [[Prototype]] 与 Class.prototype 两侧须同一
|
||||
// 对象,epoch 分配在逃逸写晋升时克隆出第二份分裂 identity。
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::NEW_SESSION_OBJECT,
|
||||
Operand::Reg(proto_reg),
|
||||
Operand::None,
|
||||
Operand::None,
|
||||
));
|
||||
if let Some(super_reg) = super_reg {
|
||||
let proto_key_idx = ctx.add_constant(Constant::String("prototype".to_string()));
|
||||
let parent_proto_key_reg = ctx.alloc_reg();
|
||||
|
||||
@@ -207,6 +207,10 @@ pub struct CompileCtx {
|
||||
/// 捕获判断(MAKE_CELL / CELL_GET / CELL_SET)与子函数 upvalue cell_idx 统一查此映射,
|
||||
/// 消除符号表时序依赖与 cell 索引错位。
|
||||
pub(crate) captured_bindings: BTreeMap<String, u8>,
|
||||
/// 顶层已声明 var 名(脚本全局 GDI 提升名):裸读走全局对象属性(LOAD_GLOBAL)、
|
||||
/// 裸写走描述符感知 A 侧写,单一真值不落镜像 cell。仅脚本顶层 emit_program 计算,
|
||||
/// 逐层继承给嵌套函数(嵌套函数的局部同名遮蔽不属此集,由作用域索引判定)。
|
||||
pub(crate) global_tier_names: HashSet<String>,
|
||||
/// 本函数从父函数捕获的 const 绑定名:子 ctx 不继承父函数作用域符号表,
|
||||
/// 捕获 const 信息随 upvalue 收集一并快照,供 const 写检查(编译期拦截)使用。
|
||||
pub(crate) upvalue_const_flags: HashSet<String>,
|
||||
@@ -344,6 +348,7 @@ impl CompileCtx {
|
||||
current_upvalue_captures: Vec::new(),
|
||||
own_bindings: HashSet::new(),
|
||||
captured_bindings: BTreeMap::new(),
|
||||
global_tier_names: HashSet::new(),
|
||||
upvalue_const_flags: HashSet::new(),
|
||||
implicit_global_reads: HashSet::new(),
|
||||
implicit_global_writes: HashSet::new(),
|
||||
@@ -1140,6 +1145,9 @@ impl Emitter {
|
||||
// 一并继承——读侧登记的全局槽与写侧同属一个槽,写判定跨嵌套函数一致。
|
||||
ctx.implicit_global_writes = parent_ctx.implicit_global_writes.clone();
|
||||
ctx.implicit_global_reads = parent_ctx.implicit_global_reads.clone();
|
||||
// 顶层已声明 var 名集随作用域继承:嵌套函数裸读/写顶层 var 直连全局对象
|
||||
// (经继承的 scope-0 绑定 + 作用域索引判定,不被局部同名遮蔽误判)。
|
||||
ctx.global_tier_names = parent_ctx.global_tier_names.clone();
|
||||
for (name, reg) in extra_bindings {
|
||||
ctx.scopes.symbols.scopes[0].bindings.insert(
|
||||
(*name).to_string(),
|
||||
@@ -1578,6 +1586,45 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
/// 名字是否为顶层已声明 var(A 侧单一真值):在顶层 var 名集内,且当前解析
|
||||
/// 绑定落在全局作用域(scope 0)——嵌套函数内的局部同名遮蔽命中更高作用域,
|
||||
/// 判定为局部而非顶层,走既有 cell/寄存器路径。
|
||||
pub(crate) fn is_global_tier_name(&self, ctx: &CompileCtx, name: &str) -> bool {
|
||||
ctx.global_tier_names.contains(name) && matches!(ctx.scopes.symbols.lookup_any_binding(name), Some((_, 0)))
|
||||
}
|
||||
|
||||
/// 顶层已声明 var 的裸写落到全局对象属性(A 侧单一真值):顶层普通脚本经
|
||||
/// This=全局对象走 0x6F;顶层 eval 与嵌套函数 This≠全局对象,经 session 解析
|
||||
/// 走 0x98(c:true,不升级既有 c:false 描述符,只更值)。
|
||||
pub(crate) fn emit_tier_global_write(&self, name: &str, val_reg: u32, ctx: &mut CompileCtx) {
|
||||
let idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
if ctx.is_global_scope && !ctx.is_eval_script {
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx));
|
||||
ctx.inst(Inst::define_global_prop(Operand::This, Operand::Reg(val_reg), Operand::Reg(key_reg)));
|
||||
} else {
|
||||
ctx.inst(Inst::define_global_prop_c(Operand::Reg(val_reg), idx));
|
||||
}
|
||||
}
|
||||
|
||||
/// GDI 序言:顶层 var 全局属性 define-if-absent。既有属性(数据或 accessor)
|
||||
/// 零动作——CreateGlobalVarBinding 对既有数据描述符零修改(不更值);缺失新建。
|
||||
/// 顶层普通脚本经 This 走 0x99;eval 经 session 走 0x9A。
|
||||
pub(crate) fn emit_global_prop_write_if_absent(&self, name: &str, val_reg: u32, ctx: &mut CompileCtx) {
|
||||
let idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
if ctx.is_eval_script {
|
||||
ctx.inst(Inst::define_global_prop_c_if_absent(Operand::Reg(val_reg), idx));
|
||||
} else {
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx));
|
||||
ctx.inst(Inst::define_global_prop_if_absent(
|
||||
Operand::This,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// 把完整程序编译为顶层模块体的 IRFunction。
|
||||
///
|
||||
/// 调用方为 `oxide_compiler::Compiler::compile`:本函数完成 emit 半程,
|
||||
@@ -1610,7 +1657,12 @@ impl Emitter {
|
||||
|
||||
// 闭包捕获分析(AST 级,emit 前确定)
|
||||
ctx.own_bindings = self.collect_own_binding_names(&[], &program.body);
|
||||
// 顶层已声明 var 名(A 侧单一真值):裸读走全局对象属性、裸写走描述符感知
|
||||
// A 侧写,不落镜像 cell——从捕获集剔除,使嵌套函数经继承 scope-0 直连全局。
|
||||
// 仅在此顶层调用点过滤:嵌套函数的局部同名遮蔽是独立绑定,其调用点不过滤。
|
||||
ctx.global_tier_names = self.collect_var_binding_names(&program.body);
|
||||
ctx.captured_bindings = self.collect_captured_bindings(&program.body, &[], &ctx.own_bindings);
|
||||
ctx.captured_bindings.retain(|n, _| !ctx.global_tier_names.contains(n));
|
||||
|
||||
// 顶层 var 入口实例化:被捕获的 var 名统一 MAKE_CELL(undefined),使 var
|
||||
// 声明语句执行前创建的闭包读取到 undefined(脚本 GlobalDeclarationInstantiation
|
||||
@@ -1637,12 +1689,13 @@ impl Emitter {
|
||||
// 全局声明实例化序言:脚本求值前为顶层 var 名创建全局对象属性(值 undefined),
|
||||
// 使声明语句执行前的读取(typeof、反射、自引用)可经全局对象见绑定;同名
|
||||
// 函数声明的属性由首个 sub-pass 以函数值覆盖,声明语句保持值更新语义。
|
||||
// builtin 名的全局属性运行期预存(session 绑定):序言值写是单 opcode 值
|
||||
// 混叠,直接写 undefined 会把现存值(NaN 等)抹掉,故以 builtin 镜像槽
|
||||
// (run 起点预载全局属性值)作为写入值——CreateGlobalVarBinding 不更新
|
||||
// 既有数据描述符(0x6F 不可写分支 no-op),值幂等保留;裸读走镜像、
|
||||
// 反射见预存属性。
|
||||
let gdi_var_names = self.collect_var_binding_names(&program.body);
|
||||
// CreateGlobalVarBinding 对既有属性零动作:define-if-absent 只在属性缺失时
|
||||
// 新建,可写/不可写/可配置既有属性(含值)一律保留。builtin 名的全局属性
|
||||
// 运行期预存(session 绑定):缺失分支写入值取 builtin 镜像槽(run 起点
|
||||
// 预载全局属性值),既有属性零动作,值幂等保留。
|
||||
// 复用顶层 var 名集(global_tier_names,与捕获集剔除同源);克隆到本地
|
||||
// 避免序言循环内 &mut ctx 与名集借用冲突。
|
||||
let gdi_var_names = ctx.global_tier_names.clone();
|
||||
if !gdi_var_names.is_empty() {
|
||||
let undef_reg = self.emit_undefined(&mut ctx);
|
||||
for name in &gdi_var_names {
|
||||
@@ -1652,7 +1705,7 @@ impl Emitter {
|
||||
} else {
|
||||
undef_reg
|
||||
};
|
||||
self.emit_global_prop_write(name, value_reg, &mut ctx);
|
||||
self.emit_global_prop_write_if_absent(name, value_reg, &mut ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,15 +95,15 @@ impl Emitter {
|
||||
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None));
|
||||
return Ok(val_reg);
|
||||
}
|
||||
let is_tier = self.is_global_tier_name(ctx, name);
|
||||
let is_implicit = ctx.is_implicit_global_reg(var_reg);
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明复合写:未解析引用不可 put,值无关抛 ReferenceError。
|
||||
return self.emit_strict_undeclared_write(name, ctx);
|
||||
}
|
||||
// 未声明名槽是入口快照(读侧登记的槽从不被读刷新,同脚本后续写
|
||||
// 可使其脱节):RMW 前从全局对象属性取旧值,属性缺失按 undefined
|
||||
// (sloppy 未解析引用 GetBaseValue 语义,不抛)。
|
||||
if is_implicit {
|
||||
// RMW 前从全局对象属性取旧值:tier 名(顶层已声明 var)与未声明名同,
|
||||
// 属性缺失按 undefined(GetBaseValue 语义,不抛)。
|
||||
if is_tier || is_implicit {
|
||||
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_GLOBAL_TYPEOF,
|
||||
@@ -113,6 +113,10 @@ impl Emitter {
|
||||
));
|
||||
}
|
||||
ctx.inst(Inst::new(op, Operand::Reg(var_reg), Operand::Reg(rhs), Operand::None));
|
||||
if is_tier {
|
||||
// 顶层已声明 var 复合赋值:新值落全局对象属性(A 侧单一真值)。
|
||||
self.emit_tier_global_write(name, var_reg, ctx);
|
||||
}
|
||||
if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
}
|
||||
@@ -380,9 +384,9 @@ impl Emitter {
|
||||
));
|
||||
} else {
|
||||
let var_reg = ctx.lookup_or_global(name);
|
||||
// 未声明名槽是入口快照(读侧登记的槽从不被读刷新):
|
||||
// 旧值读前从全局对象属性取当前值,缺失按 undefined(不抛)。
|
||||
if ctx.is_implicit_global_reg(var_reg) {
|
||||
// 顶层已声明 var(tier)与未声明名同:旧值读前从全局对象
|
||||
// 属性取当前值,缺失按 undefined(不抛)。
|
||||
if self.is_global_tier_name(ctx, name) || ctx.is_implicit_global_reg(var_reg) {
|
||||
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_GLOBAL_TYPEOF,
|
||||
@@ -431,10 +435,14 @@ impl Emitter {
|
||||
} else if !readonly_builtin {
|
||||
// 隐式全局判定以寄存器集合为准:读侧(旧值解析)已登记绑定,
|
||||
// 写侧二次解析命中集合而非"新登记"。
|
||||
let is_tier = self.is_global_tier_name(ctx, name);
|
||||
let is_implicit = ctx.is_implicit_global_reg(var_reg);
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明写:抛 ReferenceError,后续 LOAD_VAR 不可达。
|
||||
let _ = self.emit_strict_undeclared_write(name, ctx)?;
|
||||
} else if is_tier {
|
||||
// 顶层已声明 var 逻辑赋值:新值落全局对象属性(A 侧单一真值)。
|
||||
self.emit_tier_global_write(name, val_reg, ctx);
|
||||
} else {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
|
||||
@@ -45,9 +45,10 @@ impl Emitter {
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let r = ctx.alloc_reg();
|
||||
if ctx.implicit_global_reads.contains(&var_reg) {
|
||||
// 未声明标识符读:运行期查 global object 属性存在性,缺失抛
|
||||
// ReferenceError(sloppy 写先登记全局槽的读不在此集合,仍走 LOAD_VAR)。
|
||||
// 顶层已声明 var 裸读与未声明标识符读同形:读全局对象属性(A 侧单一真值),
|
||||
// 缺失抛 ReferenceError(unresolvable)。tier 名属性由 GDI 序言创建(仅删除后
|
||||
// 缺失);未声明名属性缺失即 unresolvable。已声明 var 不再落镜像 cell/槽。
|
||||
if self.is_global_tier_name(ctx, name) || ctx.implicit_global_reads.contains(&var_reg) {
|
||||
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
ctx.inst(Inst::new(OpCode::LOAD_GLOBAL, Operand::Reg(r), Operand::Const(key_idx), Operand::None));
|
||||
} else {
|
||||
@@ -109,6 +110,15 @@ impl Emitter {
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
}
|
||||
} else if self.is_global_tier_name(ctx, name) {
|
||||
// 顶层已声明 var:with 对象无该属性时回退读全局对象属性(A 侧单一真值)。
|
||||
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_GLOBAL,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Const(key_idx),
|
||||
Operand::None,
|
||||
));
|
||||
} else if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(reg), Operand::None));
|
||||
} else {
|
||||
@@ -181,12 +191,18 @@ impl Emitter {
|
||||
}
|
||||
return;
|
||||
}
|
||||
let is_tier = self.is_global_tier_name(ctx, name);
|
||||
let is_implicit = ctx.is_implicit_global_reg(var_reg);
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明写:发射 ReferenceError 抛错,跳过寄存器写(值无关)。
|
||||
let _ = self.emit_strict_undeclared_write(name, ctx);
|
||||
return;
|
||||
}
|
||||
if is_tier {
|
||||
// 顶层已声明 var 裸写:落全局对象属性(A 侧单一真值),不写镜像槽。
|
||||
self.emit_tier_global_write(name, val_reg, ctx);
|
||||
return;
|
||||
}
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(var_reg),
|
||||
|
||||
@@ -267,7 +267,10 @@ impl Emitter {
|
||||
let in_with_dynamic = !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name);
|
||||
let captured =
|
||||
ctx.current_upvalue_captures.iter().any(|u| u.name == name) || ctx.captured_bindings.contains_key(name);
|
||||
if !in_with_dynamic && !captured && ctx.scopes.symbols.lookup_any_binding(name).is_none() {
|
||||
// 顶层已声明 var:typeof 读全局对象属性(A 侧单一真值),缺失 → "undefined"
|
||||
// (非抛,IsUnresolvableReference 语义)。未声明名同走此路(lookup 未命中)。
|
||||
let is_tier = self.is_global_tier_name(ctx, name);
|
||||
if !in_with_dynamic && !captured && (is_tier || ctx.scopes.symbols.lookup_any_binding(name).is_none()) {
|
||||
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
let r = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
@@ -632,15 +635,15 @@ impl Emitter {
|
||||
ctx.inst(Inst::new(op, Operand::Reg(tmp_reg), Operand::Reg(result_reg), Operand::Reg(result_reg)));
|
||||
return Ok(result_reg);
|
||||
}
|
||||
let is_tier = self.is_global_tier_name(ctx, name);
|
||||
let is_implicit = ctx.is_implicit_global_reg(var_reg);
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明更新写:值无关抛 ReferenceError。
|
||||
return self.emit_strict_undeclared_write(name, ctx);
|
||||
}
|
||||
// 未声明名槽是入口快照(读侧登记的槽从不被读刷新,同脚本后续写
|
||||
// 可使其脱节):RMW 前从全局对象属性取旧值,属性缺失按 undefined
|
||||
// (sloppy 未解析引用 GetBaseValue 语义,不抛)。
|
||||
if is_implicit {
|
||||
// RMW 前从全局对象属性取旧值:tier 名(顶层已声明 var)与未声明名同,
|
||||
// 属性缺失按 undefined(update 旧值角落,GetBaseValue 语义,不抛)。
|
||||
if is_tier || is_implicit {
|
||||
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_GLOBAL_TYPEOF,
|
||||
@@ -657,6 +660,10 @@ impl Emitter {
|
||||
(UpdateOperator::Decrement, false) => OpCode::DEC_POST,
|
||||
};
|
||||
ctx.inst(Inst::new(op, Operand::Reg(var_reg), Operand::Reg(result_reg), Operand::Reg(result_reg)));
|
||||
if is_tier {
|
||||
// 顶层已声明 var 自增/自减:新值落全局对象属性(A 侧单一真值)。
|
||||
self.emit_tier_global_write(name, var_reg, ctx);
|
||||
}
|
||||
if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
}
|
||||
|
||||
@@ -135,12 +135,18 @@ impl Emitter {
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let is_tier = self.is_global_tier_name(ctx, name);
|
||||
let is_implicit = ctx.is_implicit_global_reg(var_reg);
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。
|
||||
self.emit_strict_undeclared_write(name, ctx)?;
|
||||
return Ok(());
|
||||
}
|
||||
if is_tier {
|
||||
// 顶层已声明 var 解构赋值目标:值落全局对象属性(A 侧单一真值)。
|
||||
self.emit_tier_global_write(name, src_reg, ctx);
|
||||
return Ok(());
|
||||
}
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(var_reg),
|
||||
@@ -337,12 +343,18 @@ impl Emitter {
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let is_tier = self.is_global_tier_name(ctx, name);
|
||||
let is_implicit = ctx.is_implicit_global_reg(var_reg);
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。
|
||||
self.emit_strict_undeclared_write(name, ctx)?;
|
||||
return Ok(());
|
||||
}
|
||||
if is_tier {
|
||||
// 顶层已声明 var 解构赋值目标:值落全局对象属性(A 侧单一真值)。
|
||||
self.emit_tier_global_write(name, src_reg, ctx);
|
||||
return Ok(());
|
||||
}
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(var_reg),
|
||||
@@ -421,12 +433,18 @@ impl Emitter {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let is_tier = self.is_global_tier_name(ctx, name);
|
||||
let is_implicit = ctx.is_implicit_global_reg(var_reg);
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。
|
||||
self.emit_strict_undeclared_write(name, ctx)?;
|
||||
return Ok(());
|
||||
}
|
||||
if is_tier {
|
||||
// 顶层已声明 var 对象解构赋值:值落全局对象属性(A 侧单一真值)。
|
||||
self.emit_tier_global_write(name, prop_reg, ctx);
|
||||
continue;
|
||||
}
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(var_reg),
|
||||
|
||||
@@ -82,38 +82,6 @@ impl Emitter {
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(target_reg), Operand::Reg(tmp), Operand::None));
|
||||
}
|
||||
ctx.init_var(bi.name.as_str());
|
||||
// 脚本顶层 var:全局对象属性同步绑定当前值(首次声明即 undefined,
|
||||
// 先前已写入则保留写入值,声明不是覆盖性赋值)。被捕获绑定的值
|
||||
// 存在 cell 而非槽,从 cell 同步。builtin 名不同步:CreateGlobalVarBinding
|
||||
// 不更新既有数据描述符,GDI 序言已以镜像值同步(幂等),未初始化
|
||||
// 的槽/cell 是 undefined,再同步会把现存值(NaN 等)抹掉。
|
||||
if ctx.is_global_scope
|
||||
&& matches!(decl.kind, VariableDeclarationKind::Var)
|
||||
&& !CompileCtx::is_known_builtin(bi.name.as_str())
|
||||
{
|
||||
let src = if let Some(&cell_idx) = ctx.captured_bindings.get(bi.name.as_str()) {
|
||||
let r = ctx.alloc_reg();
|
||||
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(bi.name.as_str()) {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::CELL_GET,
|
||||
Operand::Reg(r),
|
||||
Operand::Reg(binding.reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
} else {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::CELL_GET,
|
||||
Operand::Reg(r),
|
||||
Operand::None,
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
}
|
||||
r
|
||||
} else {
|
||||
target_reg
|
||||
};
|
||||
self.emit_global_prop_write(bi.name.as_str(), src, ctx);
|
||||
}
|
||||
r = Some(var_reg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,11 @@ impl Emitter {
|
||||
Operand::Reg(key_reg),
|
||||
Operand::None,
|
||||
));
|
||||
// 顶层 for-in var 头:迭代值落全局对象属性(A 侧单一真值)。
|
||||
// builtin 名(writable 如 Math)保留既有全局属性值,不 clobber。
|
||||
if self.is_global_tier_name(ctx, name) && !CompileCtx::is_known_builtin(name) {
|
||||
self.emit_tier_global_write(name, key_reg, ctx);
|
||||
}
|
||||
}
|
||||
ctx.init_var(name);
|
||||
}
|
||||
@@ -89,10 +94,15 @@ impl Emitter {
|
||||
self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx)?;
|
||||
}
|
||||
} else {
|
||||
let is_tier = self.is_global_tier_name(ctx, name);
|
||||
let is_implicit = ctx.is_implicit_global_reg(var_reg);
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。
|
||||
self.emit_strict_undeclared_write(name, ctx)?;
|
||||
} else if is_tier && !CompileCtx::is_known_builtin(name) {
|
||||
// 顶层 for-in 赋值头:迭代值落全局对象属性(A 侧单一真值)。
|
||||
// builtin 名保留既有全局属性值,不 clobber。
|
||||
self.emit_tier_global_write(name, key_reg, ctx);
|
||||
} else {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
|
||||
Reference in New Issue
Block a user