mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 18:51:13 +08:00
feat: with 语句、成员位/移位复合赋值、CALL_NATIVE 无目标修复
with 语句:新增 emit 支持,with 体内自由标识符动态解析(对象属性优先, 回退外层),支持读/写/复合赋值/自增自减/delete/var 初始化动态化, TO_OBJECT opcode 处理原始值对象,顶层 this 绑定全局对象。 复合赋值:新增 6 个 COMPOUND_MEMBER_BIT_* opcode 支持成员位/移位复合赋值, 修复 computed member 复合赋值覆盖 bug,with 内标识符复合赋值动态化。 CALL_NATIVE:实现全局 isNaN/isFinite 并绑定,用户覆盖 builtin 名时 回退通用调用路径而非报错,局部遮蔽 builtin 名走普通 CALL。 test262: with 34 pass(基线 0),compound-assignment 320 pass(基线 223)
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
//! 标识符表达式 emit:`emit_identifier_expression` 按绑定/builtin/全局解析寄存器。
|
||||
//! 标识符表达式 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 {
|
||||
pub(crate) fn emit_identifier_expression(
|
||||
&self, ident: &oxide_parser::IdentifierReference, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
let name = ident.name.as_str();
|
||||
|
||||
/// 静态标识符读取: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();
|
||||
@@ -40,4 +41,166 @@ impl Emitter {
|
||||
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 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 时走 STORE_VAR 运行时 const guard(对 const 再赋值报错)。
|
||||
/// - upvalue 与 cell 写穿共享单元,不设 guard。
|
||||
pub(crate) fn emit_identifier_store(&self, name: &str, val_reg: u32, const_flag: u16, ctx: &mut CompileCtx) {
|
||||
// 目标若是 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::None,
|
||||
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);
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(var_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(const_flag),
|
||||
));
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user