mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
fix: 不可写全局内置(undefined/NaN/Infinity)寄存器镜像写编译期拦截——sloppy 静默 no-op、strict 抛 TypeError
This commit is contained in:
@@ -64,12 +64,15 @@ impl Emitter {
|
||||
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,
|
||||
));
|
||||
// 全局不可写内置的捕获写被声明路径拦截,同步写一并跳过。
|
||||
if !ctx.targets_readonly_builtin(name, reg) {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,21 +81,25 @@ impl Emitter {
|
||||
let tmp = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(tmp), idx));
|
||||
let var_reg = ctx.alloc_reg();
|
||||
let target_reg = if matches!(decl.kind, VariableDeclarationKind::Var) {
|
||||
// 无初始化 var 是纯声明而非赋值:绑定已预先存在(提升引用或
|
||||
// 先前写入)时保留槽值,仅首次声明把 undefined 物化进槽。
|
||||
let (target_reg, already_bound) = if matches!(decl.kind, VariableDeclarationKind::Var) {
|
||||
match ctx.declare(bi.name.as_str(), var_reg, decl.kind, is_const) {
|
||||
Ok(()) => var_reg,
|
||||
Err(_) => ctx.lookup(bi.name.as_str()).unwrap_or(var_reg),
|
||||
Ok(()) => (var_reg, false),
|
||||
Err(_) => (ctx.lookup(bi.name.as_str()).unwrap_or(var_reg), true),
|
||||
}
|
||||
} else {
|
||||
ctx.declare(bi.name.as_str(), var_reg, decl.kind, is_const)?;
|
||||
var_reg
|
||||
(var_reg, false)
|
||||
};
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(target_reg),
|
||||
Operand::Reg(tmp),
|
||||
Operand::None,
|
||||
));
|
||||
if !already_bound {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(target_reg),
|
||||
Operand::Reg(tmp),
|
||||
Operand::None,
|
||||
));
|
||||
}
|
||||
ctx.init_var(bi.name.as_str());
|
||||
}
|
||||
// 记录 let/const 循环头声明名(update 段写豁免所需)与被捕获的绑定
|
||||
|
||||
@@ -64,15 +64,28 @@ impl Emitter {
|
||||
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
||||
let name = id_ref.name.as_str();
|
||||
let var_reg = ctx.lookup_or_global(name);
|
||||
let is_implicit = ctx.implicit_global_writes.contains(&var_reg);
|
||||
// 未声明写仅在本迭代实际产生键后才发生(for-in NEXT 之后),空集合不抛。
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。
|
||||
self.emit_strict_undeclared_write(name, ctx)?;
|
||||
// 写仅在本迭代实际产生键后发生(for-in NEXT 之后),空集合不抛。
|
||||
if ctx.targets_readonly_builtin(name, var_reg) {
|
||||
// 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值);
|
||||
// strict 在本迭代抛 TypeError(put 失败)。
|
||||
if ctx.is_strict {
|
||||
self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx)?;
|
||||
}
|
||||
} else {
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(var_reg), Operand::Reg(key_reg), Operand::None));
|
||||
if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
let is_implicit = ctx.implicit_global_writes.contains(&var_reg);
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。
|
||||
self.emit_strict_undeclared_write(name, ctx)?;
|
||||
} else {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(var_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::None,
|
||||
));
|
||||
if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,15 +108,28 @@ impl Emitter {
|
||||
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
||||
let name = id_ref.name.as_str();
|
||||
let var_reg = ctx.lookup_or_global(name);
|
||||
let is_implicit = ctx.implicit_global_writes.contains(&var_reg);
|
||||
// 未声明写仅在本迭代实际产生值后才发生(for-of NEXT 之后),空集合不抛。
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。
|
||||
self.emit_strict_undeclared_write(name, ctx)?;
|
||||
// 写仅在本迭代实际产生值后发生(for-of NEXT 之后),空集合不抛。
|
||||
if ctx.targets_readonly_builtin(name, var_reg) {
|
||||
// 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值);
|
||||
// strict 在本迭代抛 TypeError(put 失败)。
|
||||
if ctx.is_strict {
|
||||
self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx)?;
|
||||
}
|
||||
} else {
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(var_reg), Operand::Reg(val_reg), Operand::None));
|
||||
if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
let is_implicit = ctx.implicit_global_writes.contains(&var_reg);
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。
|
||||
self.emit_strict_undeclared_write(name, ctx)?;
|
||||
} else {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(var_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::None,
|
||||
));
|
||||
if is_implicit {
|
||||
self.emit_implicit_global_write(name, var_reg, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user