Files
OxideJS-New/crates/oxide_emit/src/expr/identifier.rs
T
pjh123 c564ec539b emit/kernel: 可写 builtin 全局标识符写双写全局属性,三常量描述符对齐规范 c:false,受限词法名集补 eval
- 谓词注册表化:targets_readonly_builtin 解析条件抽公共助手,新增
  targets_writable_builtin / is_writable_builtin_global(BUILTIN_GLOBALS
  减只读三常量派生,不复制名单)+ 只读/可写划分漂移守卫单测
- 12 个赋值族写点(简单/复合/更新/逻辑赋值、解构两臂、for-in/for-of
  标识符目标)补双写臂:STORE_VAR 后 0x98 同步全局对象属性(既有可写
  属性原地更值、保 e/c 位),裸读镜像与 globalThis 反射不失步
- for-in 头 tier 写点移除 builtin 名例外:可写 builtin 名迭代键照规范
  覆写全局属性(三常量仍由拦截臂跳过)
- 三常量(undefined/NaN/Infinity)全局描述符 configurable 翻正为
  false:delete 拒绝、属性保留,描述符读同步
- 脚本顶层受限词法名集 3→4(补 eval,静态受限臂):let/const/class
  eval 顶层声明编译期 SyntaxError
- 配套钉:7 处既有钉随 c 位翻正 + 2 枚 for-in/eval 形态钉反转,新增
  global_builtin_writable_tests(16 钉:双写六形态 + 遮蔽/strict/eval/
  宿主名面)

test262 子集:built-ins/global 29/29、三常量族 20/20、11.13.1-4 7/7、
assignmenttargettype plain 8/8 全绿;language/global-code 17 失败文件
双树单跑一致(pre-existing);annexB assignmenttargettype 7 个 parse
error 为既有解析器缺口
2026-09-13 10:46:10 +00:00

273 lines
13 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 标识符表达式 emit:按绑定/builtin/全局解析寄存器。
//!
//! with 语句体内,自由标识符(非 with 内部声明的绑定)编译为动态解析:
//! 运行时先查 with 对象是否有该属性,命中则取属性值,否则回退静态解析。
use crate::{CompileCtx, Emitter};
use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand;
impl Emitter {
/// 静态标识符读取:upvalue / 被捕获 cell / 全局槽。
fn emit_static_identifier_read(&self, name: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
for (uv_idx, up) in ctx.current_upvalue_captures.iter().enumerate() {
if up.name == name {
let r = ctx.alloc_reg();
let idx = uv_idx as u8;
ctx.inst(Inst::new(OpCode::LOAD_UPVALUE, Operand::Reg(r), Operand::Imm(idx as u16), Operand::None));
return Ok(r);
}
}
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
let r = ctx.alloc_reg();
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) {
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)));
}
return Ok(r);
}
let var_reg = match ctx.lookup_or_builtin(name) {
Ok(reg) => reg,
Err(err) if err.contains("before initialization") => {
// TDZ:块级预声明占位后,声明点前读取编译为运行时抛 ReferenceError。
return self.emit_tdz_throw(&err, ctx);
}
Err(err) => return Err(err),
};
let r = ctx.alloc_reg();
// 顶层已声明 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 {
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(r), Operand::Reg(var_reg), Operand::None));
}
Ok(r)
}
/// with 内动态读取:先查 with 对象属性,无则回退静态解析。
///
/// # 边界与前提
/// - 仅在 `with_stack` 非空且名字非 with 内部绑定时调用。
/// - 对象属性存在性用 `in` 判定(含原型链),与对象环境记录的 HasBinding 一致。
fn emit_with_dynamic_read(&self, name: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
let obj_reg = ctx.innermost_with_obj().expect("with stack non-empty");
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
let key_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(key_reg), key_idx));
let has_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::IN, Operand::Reg(has_reg), Operand::Reg(key_reg), Operand::Reg(obj_reg)));
let fallback_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
ctx.inst(Inst::jmp_if_false(has_reg, fallback_label));
let result_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::GET_PROP_DYNAMIC,
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
Operand::Reg(result_reg),
));
ctx.inst(Inst::jmp(end_label));
ctx.labels.set_label_pos(fallback_label, ctx.insts.len());
// 回退:upvalue / 被捕获 cell / 静态绑定 / 未定义四选一。
// 对象无该属性时才走此处,语义与静态解析一致。
if let Some((uv_idx, _)) = ctx.current_upvalue_captures.iter().enumerate().find(|(_, u)| u.name == name) {
ctx.inst(Inst::new(
OpCode::LOAD_UPVALUE,
Operand::Reg(result_reg),
Operand::Imm(uv_idx as u16),
Operand::None,
));
} else if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) {
ctx.inst(Inst::new(
OpCode::CELL_GET,
Operand::Reg(result_reg),
Operand::Reg(binding.reg),
Operand::Imm(cell_idx as u16),
));
} else {
ctx.inst(Inst::new(
OpCode::CELL_GET,
Operand::Reg(result_reg),
Operand::None,
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 {
let undef_idx = ctx.add_constant(Constant::Undefined);
ctx.inst(Inst::load_const(Operand::Reg(result_reg), undef_idx));
}
ctx.labels.set_label_pos(end_label, ctx.insts.len());
Ok(result_reg)
}
/// 静态标识符写入:upvalue / 被捕获 cell / 普通槽(含 const guard)。
///
/// # 边界与前提
/// - `const_flag` 为 1 时编译期直接抛 TypeError(值无关),覆盖普通/upvalue/cell 全写路径;
/// 不发射写指令(THROW 后不可达)。
/// - cell 写穿共享单元,无运行时 guard;const 拦截由本入口编译期完成。
/// - 不可写全局内置(undefined/NaN/Infinity)的全局绑定写在此拦截:sloppy 跳过
/// 寄存器写(槽保留入口预载原值),strict 抛 TypeError;局部遮蔽绑定不受影响。
pub(crate) fn emit_identifier_store(&self, name: &str, val_reg: u32, const_flag: u16, ctx: &mut CompileCtx) {
// 循环 update 段的 let/const 循环变量是 per-iteration 可变绑定(CreateMutableBinding),
// 写寄存器而非 cell,且豁免 const 检查(register_update_names 覆盖全部循环头声明名)。
let in_loop_update = ctx.register_update_names.iter().any(|n| n == name);
// const 再赋值编译期抛 TypeError:赋值路径(含闭包捕获 const 写 cell)统一拦截。
if const_flag != 0 && !in_loop_update {
let _ = self.emit_throw_error("TypeError", "Assignment to constant variable", ctx);
return;
}
// 循环 update 段:被捕获绑定走寄存器而非 cell(C 风格 for 每迭代 fresh,
// update 写寄存器供下一迭代 fresh 拷贝,不污染本迭代闭包捕获的 cell)。
if in_loop_update {
if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
if ctx.targets_readonly_builtin(name, reg) {
// 全局不可写内置:put 永不成功——sloppy 静默跳过,strict 抛错。
if ctx.is_strict {
let _ = self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx);
}
return;
}
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(reg), Operand::Reg(val_reg), Operand::Imm(0)));
// 可写内置名:值同步落全局对象属性,裸读(镜像)与反射不失步。
if ctx.targets_writable_builtin(name, reg) {
self.emit_global_put_write(name, reg, ctx);
}
return;
}
}
// 目标若是 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(const_flag),
Operand::Reg(val_reg),
Operand::Imm(uv_idx as u16),
));
return;
}
// 目标若是被捕获 cell,走 CELL_SET
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
ctx.inst(Inst::new(
OpCode::CELL_SET,
Operand::None,
Operand::Reg(val_reg),
Operand::Imm(cell_idx as u16),
));
return;
}
let var_reg = ctx.lookup_or_global(name);
if ctx.targets_readonly_builtin(name, var_reg) {
// 全局不可写内置槽:sloppy 跳过寄存器写(槽保留运行入口预载原值,
// 静默 no-op);strict 在 RHS 已求值后抛 TypeError(put 失败)。
if ctx.is_strict {
let _ = self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx);
}
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),
Operand::Reg(val_reg),
Operand::Imm(const_flag),
));
if is_implicit {
self.emit_implicit_global_write(name, var_reg, ctx);
} else if ctx.targets_writable_builtin(name, var_reg) {
// 可写内置名:值同步落全局对象属性(0x98 对既有可写属性仅更值、
// 保 e/c 位),否则镜像槽与 globalThis 反射失步。
self.emit_global_put_write(name, var_reg, ctx);
}
}
/// with 内动态写入:对象有该属性则写对象,否则回退静态写入。
///
/// # 边界与前提
/// - 仅在 `with_stack` 非空且名字非 with 内部绑定时调用。
/// - 对象属性存在性判定与动态读取一致(`in` 含原型链)。
/// - 回退目标未在静态作用域声明时不登记全局(with 外不应可见)。
pub(crate) fn emit_with_dynamic_write(&self, name: &str, val_reg: u32, const_flag: u16, ctx: &mut CompileCtx) {
let obj_reg = ctx.innermost_with_obj().expect("with stack non-empty");
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
let key_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(key_reg), key_idx));
let has_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::IN, Operand::Reg(has_reg), Operand::Reg(key_reg), Operand::Reg(obj_reg)));
let fallback_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
ctx.inst(Inst::jmp_if_false(has_reg, fallback_label));
ctx.inst(Inst::new(
OpCode::SET_PROP_DYNAMIC,
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
Operand::Reg(val_reg),
));
ctx.inst(Inst::jmp(end_label));
ctx.labels.set_label_pos(fallback_label, ctx.insts.len());
// 回退只写静态作用域已声明的绑定;未声明时丢弃值(隐式全局在 with 外不可解析)。
if ctx.scopes.symbols.lookup_any(name).is_some() {
self.emit_identifier_store(name, val_reg, const_flag, ctx);
}
ctx.labels.set_label_pos(end_label, ctx.insts.len());
}
pub(crate) fn emit_identifier_expression(
&self, ident: &oxide_parser::IdentifierReference, ctx: &mut CompileCtx,
) -> Result<u32, String> {
let name = ident.name.as_str();
// with 体内的自由标识符走动态解析(对象属性优先,回退外层)。
if !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name) {
return self.emit_with_dynamic_read(name, ctx);
}
self.emit_static_identifier_read(name, ctx)
}
}