mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 18:51:13 +08:00
fix: 不可写全局内置(undefined/NaN/Infinity)寄存器镜像写编译期拦截——sloppy 静默 no-op、strict 抛 TypeError
This commit is contained in:
@@ -15,6 +15,8 @@ impl Emitter {
|
||||
/// # 边界与前提
|
||||
/// - `op` 必须是 COMPOUND_* 族(rd 兼作 lhs 源,a 为 rhs)。
|
||||
/// - upvalue / cell 场景忽略 const 语义(共享单元由闭包机制保证)。
|
||||
/// - 不可写全局内置槽:值照算(表达式值 = 运算结果)但跳过槽写;strict 抛
|
||||
/// TypeError。局部遮蔽绑定不受影响。
|
||||
fn emit_compound_identifier_static(
|
||||
&self, name: &str, op: OpCode, rhs: u32, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
@@ -27,6 +29,16 @@ impl Emitter {
|
||||
// update 写寄存器供下一迭代 fresh 拷贝,不污染本迭代闭包捕获的 cell)。
|
||||
if ctx.register_update_names.iter().any(|n| n == name) {
|
||||
if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
|
||||
if ctx.targets_readonly_builtin(name, reg) {
|
||||
if ctx.is_strict {
|
||||
return self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx);
|
||||
}
|
||||
// 全局不可写内置:值照算(表达式值 = 运算结果),跳过槽写。
|
||||
let val_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(val_reg), Operand::Reg(reg), Operand::None));
|
||||
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None));
|
||||
return Ok(val_reg);
|
||||
}
|
||||
ctx.inst(Inst::new(op, Operand::Reg(reg), Operand::Reg(rhs), Operand::None));
|
||||
return Ok(reg);
|
||||
}
|
||||
@@ -72,6 +84,17 @@ impl Emitter {
|
||||
Ok(val_reg)
|
||||
} else {
|
||||
let var_reg = ctx.lookup_or_global(name);
|
||||
if ctx.targets_readonly_builtin(name, var_reg) {
|
||||
// 全局不可写内置槽:strict 抛 TypeError(put 失败);sloppy 值照算
|
||||
// (表达式值 = 运算结果),跳过槽写。
|
||||
if ctx.is_strict {
|
||||
return self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx);
|
||||
}
|
||||
let val_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(val_reg), Operand::Reg(var_reg), Operand::None));
|
||||
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None));
|
||||
return Ok(val_reg);
|
||||
}
|
||||
let is_implicit = ctx.implicit_global_writes.contains(&var_reg);
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明复合写:未解析引用不可 put,值无关抛 ReferenceError。
|
||||
|
||||
@@ -126,6 +126,8 @@ impl Emitter {
|
||||
/// - `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 覆盖全部循环头声明名)。
|
||||
@@ -139,6 +141,13 @@ impl Emitter {
|
||||
// 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)));
|
||||
return;
|
||||
}
|
||||
@@ -164,6 +173,14 @@ impl Emitter {
|
||||
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_implicit = ctx.implicit_global_writes.contains(&var_reg);
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明写:发射 ReferenceError 抛错,跳过寄存器写(值无关)。
|
||||
|
||||
@@ -506,6 +506,10 @@ impl Emitter {
|
||||
|
||||
/// 标识符自增/自减的静态路径:upvalue / 被捕获 cell 走显式读-增减-写回,
|
||||
/// 普通槽用 INC_PRE/POST 或 DEC_PRE/POST。返回结果寄存器。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 不可写全局内置槽:值照算(前缀返回新值、后缀返回旧值)但跳过槽写;
|
||||
/// strict 抛 TypeError。局部遮蔽绑定不受影响。
|
||||
fn emit_identifier_update_static(
|
||||
&self, name: &str, update: &oxide_parser::UpdateExpression, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
@@ -522,13 +526,24 @@ impl Emitter {
|
||||
// update 写寄存器供下一迭代 fresh 拷贝,不污染本迭代闭包捕获的 cell)。
|
||||
if ctx.register_update_names.iter().any(|n| n == name) {
|
||||
if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
|
||||
let result_reg = ctx.alloc_reg();
|
||||
let op = match (update.operator, update.prefix) {
|
||||
(UpdateOperator::Increment, true) => OpCode::INC_PRE,
|
||||
(UpdateOperator::Increment, false) => OpCode::INC_POST,
|
||||
(UpdateOperator::Decrement, true) => OpCode::DEC_PRE,
|
||||
(UpdateOperator::Decrement, false) => OpCode::DEC_POST,
|
||||
};
|
||||
if ctx.targets_readonly_builtin(name, reg) {
|
||||
if ctx.is_strict {
|
||||
return self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx);
|
||||
}
|
||||
// 全局不可写内置:值照算(前缀返回新值、后缀返回旧值),跳过槽写。
|
||||
let tmp_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(tmp_reg), Operand::Reg(reg), Operand::None));
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(op, Operand::Reg(tmp_reg), Operand::Reg(result_reg), Operand::Reg(result_reg)));
|
||||
return Ok(result_reg);
|
||||
}
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(op, Operand::Reg(reg), Operand::Reg(result_reg), Operand::Reg(result_reg)));
|
||||
return Ok(result_reg);
|
||||
}
|
||||
@@ -599,6 +614,24 @@ impl Emitter {
|
||||
Ok(if update.prefix { new_reg } else { old_reg })
|
||||
} else {
|
||||
let var_reg = ctx.lookup_or_global(name);
|
||||
if ctx.targets_readonly_builtin(name, var_reg) {
|
||||
// 全局不可写内置槽:strict 抛 TypeError(put 失败);sloppy 值照算
|
||||
// (前缀返回新值、后缀返回旧值),跳过槽写。
|
||||
if ctx.is_strict {
|
||||
return self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx);
|
||||
}
|
||||
let tmp_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(tmp_reg), Operand::Reg(var_reg), Operand::None));
|
||||
let result_reg = ctx.alloc_reg();
|
||||
let op = match (update.operator, update.prefix) {
|
||||
(UpdateOperator::Increment, true) => OpCode::INC_PRE,
|
||||
(UpdateOperator::Increment, false) => OpCode::INC_POST,
|
||||
(UpdateOperator::Decrement, true) => OpCode::DEC_PRE,
|
||||
(UpdateOperator::Decrement, false) => OpCode::DEC_POST,
|
||||
};
|
||||
ctx.inst(Inst::new(op, Operand::Reg(tmp_reg), Operand::Reg(result_reg), Operand::Reg(result_reg)));
|
||||
return Ok(result_reg);
|
||||
}
|
||||
let is_implicit = ctx.implicit_global_writes.contains(&var_reg);
|
||||
if is_implicit && ctx.is_strict {
|
||||
// 严格模式未声明更新写:值无关抛 ReferenceError。
|
||||
|
||||
Reference in New Issue
Block a user