fix: 顶层 var 读写直连全局对象属性单一真值——GDI 序言 + 声明处同步删除,类原型 session 直分防晋升克隆 identity 分裂,自增减 int 保持

This commit is contained in:
2026-09-12 15:48:32 +00:00
parent 4b807b5b90
commit 44c16cab8a
21 changed files with 447 additions and 74 deletions
+8 -1
View File
@@ -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();
+60 -7
View File
@@ -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);
}
}
+15 -7
View File
@@ -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,
+19 -3
View File
@@ -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),
+12 -5
View File
@@ -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);
}
+18
View File
@@ -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,