diff --git a/crates/oxide_builtins/src/global.rs b/crates/oxide_builtins/src/global.rs index 5f3c71a5..4af1c332 100644 --- a/crates/oxide_builtins/src/global.rs +++ b/crates/oxide_builtins/src/global.rs @@ -1,4 +1,5 @@ -use oxide_runtime_api::VmHost; +use oxide_runtime_api::{NativeResult, VmHost}; +use oxide_types::value::JsValue; mod annex_b; mod uri; @@ -8,6 +9,26 @@ pub use annex_b::{js_escape, js_unescape}; /// URI 编解码全局函数(`encodeURI`/`decodeURI` 等)。 pub use uri::{decode_uri, decode_uri_component, encode_uri, encode_uri_component}; +/// `isNaN(x)`:ToNumber 后检查是否为 NaN(隐式类型转换)。 +pub fn global_is_nan(vm: &mut H, args: &[u8]) -> NativeResult { + let n = if args.len() < 2 { + f64::NAN + } else { + oxide_runtime_api::to_number(vm.reg(args[1])) + }; + NativeResult::Ok(JsValue::bool(n.is_nan())) +} + +/// `isFinite(x)`:ToNumber 后检查是否为有限数(隐式类型转换)。 +pub fn global_is_finite(vm: &mut H, args: &[u8]) -> NativeResult { + let n = if args.len() < 2 { + f64::NAN + } else { + oxide_runtime_api::to_number(vm.reg(args[1])) + }; + NativeResult::Ok(JsValue::bool(n.is_finite())) +} + fn string_arg(vm: &mut H, args: &[u8]) -> String { if args.len() > 1 { oxide_runtime_api::to_string(vm.reg(args[1])) diff --git a/crates/oxide_bytecode/src/opcode.rs b/crates/oxide_bytecode/src/opcode.rs index c067bb41..befd5d08 100644 --- a/crates/oxide_bytecode/src/opcode.rs +++ b/crates/oxide_bytecode/src/opcode.rs @@ -173,10 +173,18 @@ define_opcodes! { // ── 对象 (0x63) ── CREATE_ARGUMENTS = 0x63 => "CREATE_ARGUMENTS", - // ── Profiling — 占位符 (0x64-0x6F) ── - PROFILE_SHAPE = 0x64 => "PROFILE_SHAPE", - PROFILE_BRANCH = 0x65 => "PROFILE_BRANCH", - PROFILE_CALL = 0x66 => "PROFILE_CALL", + // ── 成员复合赋值:位/移位 (0x64-0x69) ── + COMPOUND_MEMBER_BIT_AND = 0x64 => "COMPOUND_MEMBER_BIT_AND", + COMPOUND_MEMBER_BIT_OR = 0x65 => "COMPOUND_MEMBER_BIT_OR", + COMPOUND_MEMBER_BIT_XOR = 0x66 => "COMPOUND_MEMBER_BIT_XOR", + COMPOUND_MEMBER_SHL = 0x67 => "COMPOUND_MEMBER_SHL", + COMPOUND_MEMBER_SHR = 0x68 => "COMPOUND_MEMBER_SHR", + COMPOUND_MEMBER_USHR = 0x69 => "COMPOUND_MEMBER_USHR", + + // ── Profiling — 占位符 (0x6A-0x6F) ── + PROFILE_SHAPE = 0x6A => "PROFILE_SHAPE", + PROFILE_BRANCH = 0x6B => "PROFILE_BRANCH", + PROFILE_CALL = 0x6C => "PROFILE_CALL", // ── 并行 — 占位符 (0x70-0x7F) ── FORK = 0x70 => "FORK", @@ -194,6 +202,7 @@ define_opcodes! { SHR = 0x84 => "SHR", USHR = 0x85 => "USHR", BIT_NOT = 0x86 => "BIT_NOT", + TO_OBJECT = 0x87 => "TO_OBJECT", COMPOUND_AND = 0x88 => "COMPOUND_AND", COMPOUND_OR = 0x89 => "COMPOUND_OR", COMPOUND_XOR = 0x8A => "COMPOUND_XOR", @@ -225,6 +234,12 @@ impl OpCode { | OpCode::COMPOUND_MEMBER_DIV | OpCode::COMPOUND_MEMBER_MOD | OpCode::COMPOUND_MEMBER_EXP + | OpCode::COMPOUND_MEMBER_BIT_AND + | OpCode::COMPOUND_MEMBER_BIT_OR + | OpCode::COMPOUND_MEMBER_BIT_XOR + | OpCode::COMPOUND_MEMBER_SHL + | OpCode::COMPOUND_MEMBER_SHR + | OpCode::COMPOUND_MEMBER_USHR ) } } diff --git a/crates/oxide_emit/src/closure.rs b/crates/oxide_emit/src/closure.rs index 9c5055ae..934e20d2 100644 --- a/crates/oxide_emit/src/closure.rs +++ b/crates/oxide_emit/src/closure.rs @@ -81,6 +81,7 @@ impl Emitter { } } Statement::LabeledStatement(ls) => self.collect_decl_names_stmt(std::slice::from_ref(&ls.body), out), + Statement::WithStatement(ws) => self.collect_decl_names_stmt(std::slice::from_ref(&ws.body), out), _ => {} } } @@ -202,6 +203,10 @@ impl Emitter { } } Statement::LabeledStatement(ls) => self.collect_capture_names_stmt(&ls.body, ref_set, shadow, out), + Statement::WithStatement(ws) => { + self.collect_capture_names_expr(&ws.object, ref_set, shadow, out); + self.collect_capture_names_stmt(&ws.body, ref_set, shadow, out); + } _ => {} } } @@ -525,6 +530,10 @@ impl Emitter { } } Statement::LabeledStatement(ls) => self.collect_captured_stmt(&ls.body, own, out), + Statement::WithStatement(ws) => { + self.collect_captured_expr(&ws.object, own, out); + self.collect_captured_stmt(&ws.body, own, out); + } _ => {} } } diff --git a/crates/oxide_emit/src/emit.rs b/crates/oxide_emit/src/emit.rs index 1a5fec97..688e4a15 100644 --- a/crates/oxide_emit/src/emit.rs +++ b/crates/oxide_emit/src/emit.rs @@ -175,6 +175,9 @@ pub struct CompileCtx { /// 消除符号表时序依赖与 cell 索引错位。 pub(crate) captured_bindings: BTreeMap, pub(crate) const_overflow: bool, + /// with 语句作用域栈:元素为 (with 对象寄存器, 打开时的作用域深度)。 + /// 非空时 with 体内的自由标识符需动态解析(先查对象属性,回退外层)。 + pub(crate) with_stack: Vec<(u32, usize)>, } /// 函数体编译上下文:决定 `this`/`super` 绑定与参数前导(prologue)形态。 @@ -255,6 +258,7 @@ impl CompileCtx { own_bindings: HashSet::new(), captured_bindings: BTreeMap::new(), const_overflow: false, + with_stack: Vec::new(), } } @@ -475,10 +479,45 @@ impl CompileCtx { self.scopes.builtin_reg_map.iter().any(|(n, _)| n == name) } + /// 内置名是否被局部声明遮蔽(`var parseInt = ...` 等)。 + /// builtin_reg_map 记录的是全局内置槽;若该名在静态作用域另有绑定,则调用应解析局部。 + pub(crate) fn is_local_shadowing_builtin(&self, name: &str) -> bool { + if let Some(reg) = self.scopes.symbols.lookup_any(name) { + let is_builtin_slot = self.scopes.builtin_reg_map.iter().any(|(n, r)| n == name && *r == reg); + return !is_builtin_slot; + } + false + } + pub(crate) fn is_known_builtin(name: &str) -> bool { BUILTIN_GLOBALS.contains(&name) } + /// 进入 with 语句体:记录对象寄存器与当前作用域深度,供动态标识符解析。 + pub(crate) fn push_with(&mut self, obj_reg: u32) { + let depth = self.scopes.symbols.scopes.len(); + self.with_stack.push((obj_reg, depth)); + } + + /// 离开 with 语句体。 + pub(crate) fn pop_with(&mut self) { + self.with_stack.pop(); + } + + /// 最内层 with 对象寄存器;无 with 时为 None。 + pub(crate) fn innermost_with_obj(&self) -> Option { + self.with_stack.last().map(|&(reg, _)| reg) + } + + /// 指定名字是否在 with 语句体**内**(with 打开之后的块作用域)声明。 + /// with 内声明优先于对象属性;with 之前的外层声明被对象遮蔽。 + pub(crate) fn is_with_internal_binding(&self, name: &str) -> bool { + let Some(&(_, depth)) = self.with_stack.last() else { + return false; + }; + matches!(self.scopes.symbols.lookup_any_binding(name), Some((_, idx)) if idx >= depth) + } + fn builtin_reg_floor(&self) -> u32 { self.scopes .builtin_reg_map @@ -945,6 +984,7 @@ impl Emitter { Statement::BreakStatement(b) => self.emit_break_statement(b, ctx), Statement::ContinueStatement(c) => self.emit_continue_statement(c, ctx), Statement::LabeledStatement(ls) => self.emit_labeled_statement(ls, ctx), + Statement::WithStatement(_) => self.emit_with_domain(stmt, ctx), _ => Ok(None), } } diff --git a/crates/oxide_emit/src/expr/assignment.rs b/crates/oxide_emit/src/expr/assignment.rs index c267e28f..7ca85ef6 100644 --- a/crates/oxide_emit/src/expr/assignment.rs +++ b/crates/oxide_emit/src/expr/assignment.rs @@ -9,6 +9,60 @@ use oxide_ir::operand::Operand; use oxide_parser::AssignmentOperator; impl Emitter { + /// 标识符复合赋值的静态路径:upvalue / 被捕获 cell 走显式读取-运算-写回 + /// (写穿透共享单元),普通槽用 COMPOUND_* 寄存器内 RMW。返回结果寄存器。 + /// + /// # 边界与前提 + /// - `op` 必须是 COMPOUND_* 族(rd 兼作 lhs 源,a 为 rhs)。 + /// - upvalue / cell 场景忽略 const 语义(共享单元由闭包机制保证)。 + fn emit_compound_identifier_static( + &self, name: &str, op: OpCode, rhs: u32, ctx: &mut CompileCtx, + ) -> Result { + let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name); + let captured_cell = ctx.captured_bindings.get(name).copied(); + if let Some(uv) = uv_idx { + let val_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::LOAD_UPVALUE, + Operand::Reg(val_reg), + Operand::Imm(uv as u16), + Operand::None, + )); + ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None)); + ctx.inst(Inst::new( + OpCode::STORE_UPVALUE, + Operand::None, + Operand::Reg(val_reg), + Operand::Imm(uv as u16), + )); + Ok(val_reg) + } else if let Some(cell_idx) = captured_cell { + let val_reg = ctx.alloc_reg(); + let a_operand = match ctx.scopes.symbols.lookup_any_binding(name) { + Some((binding, _)) => Operand::Reg(binding.reg), + None => Operand::None, + }; + ctx.inst(Inst::new( + OpCode::CELL_GET, + Operand::Reg(val_reg), + a_operand, + Operand::Imm(cell_idx as u16), + )); + ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None)); + ctx.inst(Inst::new( + OpCode::CELL_SET, + Operand::None, + Operand::Reg(val_reg), + Operand::Imm(cell_idx as u16), + )); + Ok(val_reg) + } else { + let var_reg = ctx.lookup_or_global(name); + ctx.inst(Inst::new(op, Operand::Reg(var_reg), Operand::Reg(rhs), Operand::None)); + Ok(var_reg) + } + } + pub(crate) fn emit_assignment_expression( &self, assign: &oxide_parser::AssignmentExpression, ctx: &mut CompileCtx, ) -> Result { @@ -59,6 +113,12 @@ impl Emitter { AssignmentOperator::Division => ctx.inst(Inst::compound_member_div(obj, val, key)), AssignmentOperator::Remainder => ctx.inst(Inst::compound_member_mod(obj, val, key)), AssignmentOperator::Exponential => ctx.inst(Inst::compound_member_exp(obj, val, key)), + AssignmentOperator::BitwiseAnd => ctx.inst(Inst::compound_member_bit_and(obj, val, key)), + AssignmentOperator::BitwiseOR => ctx.inst(Inst::compound_member_bit_or(obj, val, key)), + AssignmentOperator::BitwiseXOR => ctx.inst(Inst::compound_member_bit_xor(obj, val, key)), + AssignmentOperator::ShiftLeft => ctx.inst(Inst::compound_member_shl(obj, val, key)), + AssignmentOperator::ShiftRight => ctx.inst(Inst::compound_member_shr(obj, val, key)), + AssignmentOperator::ShiftRightZeroFill => ctx.inst(Inst::compound_member_ushr(obj, val, key)), _ => return Err(format!("compound assignment operator {:?} not supported", assign.operator)), } Ok(val_reg) @@ -99,6 +159,40 @@ impl Emitter { } let obj_reg = self.emit_expression(&member.object, ctx)?; let key_reg = self.emit_expression(&member.expression, ctx)?; + if assign.operator != AssignmentOperator::Assign { + // 复合赋值:读属性 → 运算 → 写回。val_reg 同时承载旧值与新值。 + let val_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::GET_PROP_DYNAMIC, + Operand::Reg(obj_reg), + Operand::Reg(key_reg), + Operand::Reg(val_reg), + )); + let rhs = self.emit_expression(&assign.right, ctx)?; + let op = match assign.operator { + AssignmentOperator::Addition => OpCode::ADD, + AssignmentOperator::Subtraction => OpCode::SUB, + AssignmentOperator::Multiplication => OpCode::MUL, + AssignmentOperator::Division => OpCode::DIV, + AssignmentOperator::Remainder => OpCode::MOD, + AssignmentOperator::Exponential => OpCode::COMPOUND_EXP, + AssignmentOperator::BitwiseAnd => OpCode::BIT_AND, + AssignmentOperator::BitwiseOR => OpCode::BIT_OR, + AssignmentOperator::BitwiseXOR => OpCode::BIT_XOR, + AssignmentOperator::ShiftLeft => OpCode::SHL, + AssignmentOperator::ShiftRight => OpCode::SHR, + AssignmentOperator::ShiftRightZeroFill => OpCode::USHR, + _ => return Err(format!("compound assignment operator {:?} not supported", assign.operator)), + }; + ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(rhs))); + ctx.inst(Inst::new( + OpCode::SET_PROP_DYNAMIC, + Operand::Reg(obj_reg), + Operand::Reg(key_reg), + Operand::Reg(val_reg), + )); + return Ok(val_reg); + } let val_reg = self.emit_expression(&assign.right, ctx)?; ctx.inst(Inst::new( OpCode::SET_PROP_DYNAMIC, @@ -184,51 +278,55 @@ impl Emitter { AssignmentOperator::ShiftRightZeroFill => OpCode::COMPOUND_USHR, _ => return Err(format!("compound assignment operator {:?} not supported", assign.operator)), }; - // 目标为 upvalue / 被捕获 cell 时走显式读取-运算-写回,保证写穿透共享单元; - // 只有普通槽位才用 COMPOUND_* 的寄存器内 RMW(rd 兼作 lhs 源)。 - let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name); - let captured_cell = ctx.captured_bindings.get(name).copied(); - if let Some(uv) = uv_idx { + // with 体内自由标识符的复合赋值走动态路径:对象有属性则读对象、 + // 运算后写回对象,否则回退外层静态复合。 + if !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name) { + 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 val_reg = ctx.alloc_reg(); ctx.inst(Inst::new( - OpCode::LOAD_UPVALUE, + OpCode::GET_PROP_DYNAMIC, + Operand::Reg(obj_reg), + Operand::Reg(key_reg), Operand::Reg(val_reg), - Operand::Imm(uv as u16), - Operand::None, )); ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None)); ctx.inst(Inst::new( - OpCode::STORE_UPVALUE, - Operand::None, + OpCode::SET_PROP_DYNAMIC, + Operand::Reg(obj_reg), + Operand::Reg(key_reg), Operand::Reg(val_reg), - Operand::Imm(uv as u16), )); - Ok(val_reg) - } else if let Some(cell_idx) = captured_cell { - let val_reg = ctx.alloc_reg(); - let a_operand = match ctx.scopes.symbols.lookup_any_binding(name) { - Some((binding, _)) => Operand::Reg(binding.reg), - None => Operand::None, - }; + ctx.inst(Inst::jmp(end_label)); + + // 对象无该属性:回退静态复合赋值,结果写入同一 val_reg。 + ctx.labels.set_label_pos(fallback_label, ctx.insts.len()); + let fallback_val = self.emit_compound_identifier_static(name, op, rhs, ctx)?; ctx.inst(Inst::new( - OpCode::CELL_GET, + OpCode::LOAD_VAR, Operand::Reg(val_reg), - a_operand, - Operand::Imm(cell_idx as u16), - )); - ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None)); - ctx.inst(Inst::new( - OpCode::CELL_SET, + Operand::Reg(fallback_val), Operand::None, - Operand::Reg(val_reg), - Operand::Imm(cell_idx as u16), )); - Ok(val_reg) - } else { - let var_reg = ctx.lookup_or_global(name); - ctx.inst(Inst::new(op, Operand::Reg(var_reg), Operand::Reg(rhs), Operand::None)); - Ok(var_reg) + ctx.labels.set_label_pos(end_label, ctx.insts.len()); + return Ok(val_reg); } + self.emit_compound_identifier_static(name, op, rhs, ctx) } else { Err(format!("compound assignment operator {:?} not supported", assign.operator)) } @@ -243,35 +341,16 @@ impl Emitter { } } } - // 目标若是 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), - )); + // with 体内的自由标识符走动态写:对象有属性则写对象,否则写外层。 + if !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name) { + let is_const = ctx.lookup_const_flag(name); + let const_flag: u16 = if is_const { 1 } else { 0 }; + self.emit_with_dynamic_write(name, val_reg, const_flag, ctx); return Ok(val_reg); } - // 目标若是被捕获 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 Ok(val_reg); - } - let var_reg = ctx.lookup_or_global(name); let is_const = ctx.lookup_const_flag(name); - let const_flag = if is_const { 1 } else { 0 }; - ctx.inst(Inst::new( - OpCode::STORE_VAR, - Operand::Reg(var_reg), - Operand::Reg(val_reg), - Operand::Imm(const_flag), - )); + let const_flag: u16 = if is_const { 1 } else { 0 }; + self.emit_identifier_store(name, val_reg, const_flag, ctx); Ok(val_reg) } } else if matches!( diff --git a/crates/oxide_emit/src/expr/call.rs b/crates/oxide_emit/src/expr/call.rs index a4c5d65c..7c847927 100644 --- a/crates/oxide_emit/src/expr/call.rs +++ b/crates/oxide_emit/src/expr/call.rs @@ -112,7 +112,12 @@ impl Emitter { let mut static_regs = words; let first_arg_reg = if static_regs.is_empty() { 0u32 } else { pack_arg_regs(&mut static_regs, ctx) }; let op = match &call.callee { - Expression::Identifier(ident) if ctx.is_builtin(ident.name.as_str()) => OpCode::CALL_NATIVE, + // 仅未遮蔽的内置名才用 CALL_NATIVE;局部声明覆盖后走通用 CALL。 + Expression::Identifier(ident) + if ctx.is_builtin(ident.name.as_str()) && !ctx.is_local_shadowing_builtin(ident.name.as_str()) => + { + OpCode::CALL_NATIVE + } _ => OpCode::CALL, }; match op { diff --git a/crates/oxide_emit/src/expr/identifier.rs b/crates/oxide_emit/src/expr/identifier.rs index f1d2d615..73a14937 100644 --- a/crates/oxide_emit/src/expr/identifier.rs +++ b/crates/oxide_emit/src/expr/identifier.rs @@ -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 { - let name = ident.name.as_str(); - + /// 静态标识符读取:upvalue / 被捕获 cell / 全局槽。 + fn emit_static_identifier_read(&self, name: &str, ctx: &mut CompileCtx) -> Result { 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 { + 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 { + 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) + } } diff --git a/crates/oxide_emit/src/expr/operator/mod.rs b/crates/oxide_emit/src/expr/operator/mod.rs index 9dc87997..8c0a4564 100644 --- a/crates/oxide_emit/src/expr/operator/mod.rs +++ b/crates/oxide_emit/src/expr/operator/mod.rs @@ -61,7 +61,54 @@ impl Emitter { fn emit_unary_expression(&self, un: &oxide_parser::UnaryExpression, ctx: &mut CompileCtx) -> Result { if matches!(un.operator, UnaryOperator::Delete) { return match &un.argument { - Expression::Identifier(_) => { + Expression::Identifier(ident) => { + let name = ident.name.as_str(); + // with 体内自由标识符:对象有该属性则删除对象属性(返回删除结果), + // 否则非严格语义返回 true。 + if !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name) { + 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)); + // 复制 obj 到临时寄存器执行删除:DELETE_PROP_DYNAMIC 把结果写回 + // rd 槽,直接用它会把 with 对象寄存器覆盖为布尔值。 + let tmp_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::LOAD_VAR, + Operand::Reg(tmp_reg), + Operand::Reg(obj_reg), + Operand::None, + )); + ctx.inst(Inst::new( + OpCode::DELETE_PROP_DYNAMIC, + Operand::Reg(tmp_reg), + Operand::Reg(tmp_reg), + Operand::Reg(key_reg), + )); + let result_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::LOAD_VAR, + Operand::Reg(result_reg), + Operand::Reg(tmp_reg), + Operand::None, + )); + ctx.inst(Inst::jmp(end_label)); + ctx.labels.set_label_pos(fallback_label, ctx.insts.len()); + let true_idx = ctx.add_constant(Constant::Boolean(true)); + ctx.inst(Inst::load_const(Operand::Reg(result_reg), true_idx)); + ctx.labels.set_label_pos(end_label, ctx.insts.len()); + return Ok(result_reg); + } // 严格模式的 delete 标识符由 oxc_semantic 提前拦截为早期错误; // 非严格语义返回 false(标识符不可删除)。 let idx = ctx.add_constant(Constant::Boolean(false)); @@ -262,54 +309,32 @@ impl Emitter { match &update.argument { SimpleAssignmentTarget::AssignmentTargetIdentifier(id) => { let name = id.name.as_str(); - // 判断目标是 upvalue 还是被捕获 cell - let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name); - let captured_cell = ctx.captured_bindings.get(name).copied(); - if let Some(uv) = uv_idx { - // upvalue:LOAD_UPVALUE + 常量 1 + ADD/SUB + STORE_UPVALUE; - // 后缀形式返回旧值(前缀返回新值)。 + // with 体内自由标识符的自增/自减:对象有属性则读写对象,否则回退外层。 + if !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name) { + 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 old_reg = ctx.alloc_reg(); ctx.inst(Inst::new( - OpCode::LOAD_UPVALUE, + OpCode::GET_PROP_DYNAMIC, + Operand::Reg(obj_reg), + Operand::Reg(key_reg), Operand::Reg(old_reg), - Operand::Imm(uv as u16), - Operand::None, )); - let one_idx = ctx.add_constant(Constant::Int(1)); - let one_reg = ctx.alloc_reg(); - ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx)); - let op = if update.operator == UpdateOperator::Increment { - OpCode::ADD - } else { - OpCode::SUB - }; let new_reg = ctx.alloc_reg(); - ctx.inst(Inst::new(op, Operand::Reg(new_reg), Operand::Reg(old_reg), Operand::Reg(one_reg))); - ctx.inst(Inst::new( - OpCode::STORE_UPVALUE, - Operand::None, - Operand::Reg(new_reg), - Operand::Imm(uv as u16), - )); - Ok(if update.prefix { new_reg } else { old_reg }) - } else if let Some(cell_idx) = captured_cell { - // 被捕获 cell:CELL_GET + 常量 1 + ADD/SUB + CELL_SET - let val_reg = ctx.alloc_reg(); - if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) { - ctx.inst(Inst::new( - OpCode::CELL_GET, - Operand::Reg(val_reg), - Operand::Reg(binding.reg), - Operand::Imm(cell_idx as u16), - )); - } else { - ctx.inst(Inst::new( - OpCode::CELL_GET, - Operand::Reg(val_reg), - Operand::None, - Operand::Imm(cell_idx as u16), - )); - } let one_idx = ctx.add_constant(Constant::Int(1)); let one_reg = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx)); @@ -318,26 +343,41 @@ impl Emitter { } else { OpCode::SUB }; - ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(one_reg))); ctx.inst(Inst::new( - OpCode::CELL_SET, - Operand::None, - Operand::Reg(val_reg), - Operand::Imm(cell_idx as u16), + op, + Operand::Reg(new_reg), + Operand::Reg(old_reg), + Operand::Reg(one_reg), + )); + ctx.inst(Inst::new( + OpCode::SET_PROP_DYNAMIC, + Operand::Reg(obj_reg), + Operand::Reg(key_reg), + Operand::Reg(new_reg), )); - Ok(val_reg) - } else { - let var_reg = ctx.lookup_or_global(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, - }; - ctx.inst(Inst::new(op, Operand::Reg(var_reg), Operand::Reg(result_reg), Operand::Reg(result_reg))); - Ok(result_reg) + ctx.inst(Inst::new( + OpCode::LOAD_VAR, + Operand::Reg(result_reg), + Operand::Reg(if update.prefix { new_reg } else { old_reg }), + Operand::None, + )); + ctx.inst(Inst::jmp(end_label)); + + // 对象无该属性:回退静态自增/自减,结果写入同一 result_reg。 + ctx.labels.set_label_pos(fallback_label, ctx.insts.len()); + let static_result = self.emit_identifier_update_static(name, update, ctx)?; + ctx.inst(Inst::new( + OpCode::LOAD_VAR, + Operand::Reg(result_reg), + Operand::Reg(static_result), + Operand::None, + )); + ctx.labels.set_label_pos(end_label, ctx.insts.len()); + return Ok(result_reg); } + // 判断目标是 upvalue 还是被捕获 cell + self.emit_identifier_update_static(name, update, ctx) } SimpleAssignmentTarget::StaticMemberExpression(member) => { let obj_reg = self.emit_expression(&member.object, ctx)?; @@ -376,6 +416,88 @@ impl Emitter { } } + /// 标识符自增/自减的静态路径:upvalue / 被捕获 cell 走显式读-增减-写回, + /// 普通槽用 INC_PRE/POST 或 DEC_PRE/POST。返回结果寄存器。 + fn emit_identifier_update_static( + &self, name: &str, update: &oxide_parser::UpdateExpression, ctx: &mut CompileCtx, + ) -> Result { + let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name); + let captured_cell = ctx.captured_bindings.get(name).copied(); + if let Some(uv) = uv_idx { + // upvalue:LOAD_UPVALUE + 常量 1 + ADD/SUB + STORE_UPVALUE; + // 后缀形式返回旧值(前缀返回新值)。 + let old_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::LOAD_UPVALUE, + Operand::Reg(old_reg), + Operand::Imm(uv as u16), + Operand::None, + )); + let one_idx = ctx.add_constant(Constant::Int(1)); + let one_reg = ctx.alloc_reg(); + ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx)); + let op = if update.operator == UpdateOperator::Increment { + OpCode::ADD + } else { + OpCode::SUB + }; + let new_reg = ctx.alloc_reg(); + ctx.inst(Inst::new(op, Operand::Reg(new_reg), Operand::Reg(old_reg), Operand::Reg(one_reg))); + ctx.inst(Inst::new( + OpCode::STORE_UPVALUE, + Operand::None, + Operand::Reg(new_reg), + Operand::Imm(uv as u16), + )); + Ok(if update.prefix { new_reg } else { old_reg }) + } else if let Some(cell_idx) = captured_cell { + // 被捕获 cell:CELL_GET + 常量 1 + ADD/SUB + CELL_SET + let val_reg = ctx.alloc_reg(); + if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) { + ctx.inst(Inst::new( + OpCode::CELL_GET, + Operand::Reg(val_reg), + Operand::Reg(binding.reg), + Operand::Imm(cell_idx as u16), + )); + } else { + ctx.inst(Inst::new( + OpCode::CELL_GET, + Operand::Reg(val_reg), + Operand::None, + Operand::Imm(cell_idx as u16), + )); + } + let one_idx = ctx.add_constant(Constant::Int(1)); + let one_reg = ctx.alloc_reg(); + ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx)); + let op = if update.operator == UpdateOperator::Increment { + OpCode::ADD + } else { + OpCode::SUB + }; + ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(one_reg))); + ctx.inst(Inst::new( + OpCode::CELL_SET, + Operand::None, + Operand::Reg(val_reg), + Operand::Imm(cell_idx as u16), + )); + Ok(val_reg) + } else { + let var_reg = ctx.lookup_or_global(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, + }; + ctx.inst(Inst::new(op, Operand::Reg(var_reg), Operand::Reg(result_reg), Operand::Reg(result_reg))); + Ok(result_reg) + } + } + pub(crate) fn emit_operator(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result { match expr { Expression::BinaryExpression(bin) => self.emit_binary_expression(bin, ctx), diff --git a/crates/oxide_emit/src/prepass.rs b/crates/oxide_emit/src/prepass.rs index 468057ba..694299f0 100644 --- a/crates/oxide_emit/src/prepass.rs +++ b/crates/oxide_emit/src/prepass.rs @@ -108,6 +108,10 @@ impl Emitter { } } Statement::LabeledStatement(ls) => self.pre_scan_builtin_stmt(&ls.body, ctx), + Statement::WithStatement(ws) => { + self.pre_scan_builtin_expr(&ws.object, ctx); + self.pre_scan_builtin_stmt(&ws.body, ctx); + } Statement::ClassDeclaration(cd) => { if let Some(super_class) = &cd.super_class { self.pre_scan_builtin_expr(super_class, ctx); @@ -343,6 +347,9 @@ impl Emitter { Statement::LabeledStatement(ls) => { self.predeclare_var_declarations(std::slice::from_ref(&ls.body), ctx); } + Statement::WithStatement(ws) => { + self.predeclare_var_declarations(std::slice::from_ref(&ws.body), ctx); + } _ => {} } } diff --git a/crates/oxide_emit/src/shared/binding.rs b/crates/oxide_emit/src/shared/binding.rs index 45d967e9..0746e895 100644 --- a/crates/oxide_emit/src/shared/binding.rs +++ b/crates/oxide_emit/src/shared/binding.rs @@ -72,6 +72,9 @@ impl Emitter { Operand::Imm(cell_idx as u16), Operand::None, )); + } else if !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name) { + // with 内 var 初始化:对象有该属性则写对象,否则写提升槽(动态解析)。 + self.emit_with_dynamic_write(name, src_reg, 0, ctx); } else { // const 声明路径 STORE_VAR 恒 b=0,不查运行时 guard。 // guard 读 regs[rd] 判断槽是否已初始化,依赖槽初始为 undefined; diff --git a/crates/oxide_emit/src/shared/chain.rs b/crates/oxide_emit/src/shared/chain.rs index f252a455..affcc77f 100644 --- a/crates/oxide_emit/src/shared/chain.rs +++ b/crates/oxide_emit/src/shared/chain.rs @@ -114,7 +114,11 @@ impl Emitter { let mut static_regs = words; let first_arg_reg = if static_regs.is_empty() { 0u32 } else { pack_arg_regs(&mut static_regs, ctx) }; let op = match &call.callee { - Expression::Identifier(ident) if ctx.is_builtin(ident.name.as_str()) => OpCode::CALL_NATIVE, + Expression::Identifier(ident) + if ctx.is_builtin(ident.name.as_str()) && !ctx.is_local_shadowing_builtin(ident.name.as_str()) => + { + OpCode::CALL_NATIVE + } _ => OpCode::CALL, }; match op { diff --git a/crates/oxide_emit/src/stmt/mod.rs b/crates/oxide_emit/src/stmt/mod.rs index 34cf24ac..d1ea1d9a 100644 --- a/crates/oxide_emit/src/stmt/mod.rs +++ b/crates/oxide_emit/src/stmt/mod.rs @@ -7,3 +7,4 @@ pub mod declaration; pub mod exception; pub mod iteration; pub mod switch; +pub mod with; diff --git a/crates/oxide_emit/src/stmt/with.rs b/crates/oxide_emit/src/stmt/with.rs new file mode 100644 index 00000000..dc768178 --- /dev/null +++ b/crates/oxide_emit/src/stmt/with.rs @@ -0,0 +1,34 @@ +//! with 语句 emit:求值对象压入 with 作用域栈,动态解析体内自由标识符。 +//! +//! with 自身不创建声明式作用域;body 为块时由块语句建立块作用域, +//! 块内 let/const/函数声明优先于 with 对象属性(对象环境在块环境外层)。 + +use crate::{CompileCtx, Emitter}; +use oxide_bytecode::opcode::OpCode; +use oxide_ir::inst::Inst; +use oxide_ir::operand::Operand; +use oxide_parser::Statement; + +impl Emitter { + fn emit_with_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { + let Statement::WithStatement(with) = stmt else { + return Ok(None); + }; + + // 求值 with 对象并 ToObject(对象环境记录要求);压入动态作用域栈, + // body 编译完成后弹出。 + let obj_reg = self.emit_expression(&with.object, ctx)?; + ctx.inst(Inst::new(OpCode::TO_OBJECT, Operand::Reg(obj_reg), Operand::None, Operand::None)); + ctx.push_with(obj_reg); + let result = self.emit_statement(&with.body, ctx)?; + ctx.pop_with(); + Ok(result) + } + + pub(crate) fn emit_with_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { + match stmt { + Statement::WithStatement(_) => self.emit_with_statement(stmt, ctx), + _ => Ok(None), + } + } +} diff --git a/crates/oxide_ir/src/contract.rs b/crates/oxide_ir/src/contract.rs index d1dd07d0..440fd3c1 100644 --- a/crates/oxide_ir/src/contract.rs +++ b/crates/oxide_ir/src/contract.rs @@ -30,7 +30,13 @@ impl Inst { | OpCode::COMPOUND_MEMBER_MUL | OpCode::COMPOUND_MEMBER_DIV | OpCode::COMPOUND_MEMBER_MOD - | OpCode::COMPOUND_MEMBER_EXP => reg_of(&self.a), + | OpCode::COMPOUND_MEMBER_EXP + | OpCode::COMPOUND_MEMBER_BIT_AND + | OpCode::COMPOUND_MEMBER_BIT_OR + | OpCode::COMPOUND_MEMBER_BIT_XOR + | OpCode::COMPOUND_MEMBER_SHL + | OpCode::COMPOUND_MEMBER_SHR + | OpCode::COMPOUND_MEMBER_USHR => reg_of(&self.a), // 无 def:控制流 / 写共享状态 / 纯写对象 OpCode::HALT | OpCode::RETURN @@ -139,7 +145,13 @@ impl Inst { | OpCode::COMPOUND_MEMBER_MUL | OpCode::COMPOUND_MEMBER_DIV | OpCode::COMPOUND_MEMBER_MOD - | OpCode::COMPOUND_MEMBER_EXP => { + | OpCode::COMPOUND_MEMBER_EXP + | OpCode::COMPOUND_MEMBER_BIT_AND + | OpCode::COMPOUND_MEMBER_BIT_OR + | OpCode::COMPOUND_MEMBER_BIT_XOR + | OpCode::COMPOUND_MEMBER_SHL + | OpCode::COMPOUND_MEMBER_SHR + | OpCode::COMPOUND_MEMBER_USHR => { push_operand(&mut uses, &self.rd); push_operand(&mut uses, &self.a); push_operand(&mut uses, &self.b); @@ -290,6 +302,10 @@ impl Inst { | OpCode::PROFILE_CALL | OpCode::FORK | OpCode::JOIN => {} + // TO_OBJECT:rd 原地转换(读且写) + OpCode::TO_OBJECT => { + push_operand(&mut uses, &self.rd); + } } uses } diff --git a/crates/oxide_ir/src/inst.rs b/crates/oxide_ir/src/inst.rs index be1b0e92..9986f05b 100644 --- a/crates/oxide_ir/src/inst.rs +++ b/crates/oxide_ir/src/inst.rs @@ -94,6 +94,36 @@ impl Inst { Self::with_ext(OpCode::COMPOUND_MEMBER_EXP, obj, val, key, &[0, 0, 0]) } + /// 成员复合赋值按位与:`obj[key] &= val`。 + pub fn compound_member_bit_and(obj: Operand, val: Operand, key: Operand) -> Self { + Self::with_ext(OpCode::COMPOUND_MEMBER_BIT_AND, obj, val, key, &[0, 0, 0]) + } + + /// 成员复合赋值按位或:`obj[key] |= val`。 + pub fn compound_member_bit_or(obj: Operand, val: Operand, key: Operand) -> Self { + Self::with_ext(OpCode::COMPOUND_MEMBER_BIT_OR, obj, val, key, &[0, 0, 0]) + } + + /// 成员复合赋值按位异或:`obj[key] ^= val`。 + pub fn compound_member_bit_xor(obj: Operand, val: Operand, key: Operand) -> Self { + Self::with_ext(OpCode::COMPOUND_MEMBER_BIT_XOR, obj, val, key, &[0, 0, 0]) + } + + /// 成员复合赋值左移:`obj[key] <<= val`。 + pub fn compound_member_shl(obj: Operand, val: Operand, key: Operand) -> Self { + Self::with_ext(OpCode::COMPOUND_MEMBER_SHL, obj, val, key, &[0, 0, 0]) + } + + /// 成员复合赋值右移:`obj[key] >>= val`。 + pub fn compound_member_shr(obj: Operand, val: Operand, key: Operand) -> Self { + Self::with_ext(OpCode::COMPOUND_MEMBER_SHR, obj, val, key, &[0, 0, 0]) + } + + /// 成员复合赋值无符号右移:`obj[key] >>>= val`。 + pub fn compound_member_ushr(obj: Operand, val: Operand, key: Operand) -> Self { + Self::with_ext(OpCode::COMPOUND_MEMBER_USHR, obj, val, key, &[0, 0, 0]) + } + // ── Call 系:ext = [nargs] ── /// 普通函数调用:rd=callee,a=this,b=首参,ext=\[nargs\]。参数从 `first_arg` 起连续占 nargs 个寄存器。 diff --git a/crates/oxide_vm/src/bindings/bind_number.rs b/crates/oxide_vm/src/bindings/bind_number.rs index d52a7d9a..1b618278 100644 --- a/crates/oxide_vm/src/bindings/bind_number.rs +++ b/crates/oxide_vm/src/bindings/bind_number.rs @@ -81,6 +81,8 @@ pub fn bind_number(core: &Arc, session: &KernelSession, global: &mut &[ ("parseInt", oxide_builtins::number::number_parse_int:: as *const (), 1), ("parseFloat", oxide_builtins::number::number_parse_float:: as *const (), 1), + ("isNaN", oxide_builtins::global::global_is_nan:: as *const (), 1), + ("isFinite", oxide_builtins::global::global_is_finite:: as *const (), 1), ], ); } diff --git a/crates/oxide_vm/src/bindings/mod.rs b/crates/oxide_vm/src/bindings/mod.rs index 65e4e5a6..07835220 100644 --- a/crates/oxide_vm/src/bindings/mod.rs +++ b/crates/oxide_vm/src/bindings/mod.rs @@ -230,6 +230,8 @@ fn bind_global_functions(core: &Arc, session: &KernelSession, global &[ ("parseInt", oxide_builtins::number::number_parse_int:: as *const (), 1), ("parseFloat", oxide_builtins::number::number_parse_float:: as *const (), 1), + ("isNaN", oxide_builtins::global::global_is_nan:: as *const (), 1), + ("isFinite", oxide_builtins::global::global_is_finite:: as *const (), 1), ("escape", oxide_builtins::global::js_escape:: as *const (), 1), ("unescape", oxide_builtins::global::js_unescape:: as *const (), 1), ("encodeURI", oxide_builtins::global::encode_uri:: as *const (), 1), diff --git a/crates/oxide_vm/src/dispatch/member.rs b/crates/oxide_vm/src/dispatch/member.rs index c1c01015..28e2ed2b 100644 --- a/crates/oxide_vm/src/dispatch/member.rs +++ b/crates/oxide_vm/src/dispatch/member.rs @@ -55,6 +55,57 @@ impl Vm { "COMPOUND_MEMBER_EXP on non-object", |l, r| l.powf(r), ), + OpCode::COMPOUND_MEMBER_BIT_AND => self.dispatch_compound_member_bitwise( + "COMPOUND_MEMBER_BIT_AND", + rd, + a, + b, + "COMPOUND_MEMBER_BIT_AND on non-object", + |l, r| l & r, + ), + OpCode::COMPOUND_MEMBER_BIT_OR => self.dispatch_compound_member_bitwise( + "COMPOUND_MEMBER_BIT_OR", + rd, + a, + b, + "COMPOUND_MEMBER_BIT_OR on non-object", + |l, r| l | r, + ), + OpCode::COMPOUND_MEMBER_BIT_XOR => self.dispatch_compound_member_bitwise( + "COMPOUND_MEMBER_BIT_XOR", + rd, + a, + b, + "COMPOUND_MEMBER_BIT_XOR on non-object", + |l, r| l ^ r, + ), + OpCode::COMPOUND_MEMBER_SHL => self.dispatch_compound_member_shift( + "COMPOUND_MEMBER_SHL", + rd, + a, + b, + "COMPOUND_MEMBER_SHL on non-object", + false, + false, + ), + OpCode::COMPOUND_MEMBER_SHR => self.dispatch_compound_member_shift( + "COMPOUND_MEMBER_SHR", + rd, + a, + b, + "COMPOUND_MEMBER_SHR on non-object", + true, + false, + ), + OpCode::COMPOUND_MEMBER_USHR => self.dispatch_compound_member_shift( + "COMPOUND_MEMBER_USHR", + rd, + a, + b, + "COMPOUND_MEMBER_USHR on non-object", + true, + true, + ), _ => unreachable!("non-member opcode passed to dispatch_member_op"), } } @@ -180,4 +231,60 @@ impl Vm { self.set_member_prop(obj, prop_name_si, new_val, receiver)?; Ok(()) } + + fn dispatch_compound_member_bitwise( + &mut self, op_name: &str, rd: usize, a: usize, b: usize, error_msg: &str, op: F, + ) -> Result<(), String> + where + F: FnOnce(i32, i32) -> i32, + { + vm_trace!("{} rd={} a={} b={}", op_name, rd, a, b); + let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, error_msg) else { + return Ok(()); + }; + let receiver = self.regs[rd]; + let ln = { + let obj = unsafe { &mut *obj_ptr }; + let prop_val = self.read_member_prop(obj, prop_name_si, receiver)?; + self.coerce_int32_bounded(prop_val)? + }; + let rn = self.coerce_int32_bounded(self.regs[a])?; + let new_val = JsValue::int(op(ln, rn)); + self.regs[a] = new_val; + let obj = unsafe { &mut *obj_ptr }; + self.set_member_prop(obj, prop_name_si, new_val, receiver)?; + Ok(()) + } + + fn dispatch_compound_member_shift( + &mut self, op_name: &str, rd: usize, a: usize, b: usize, error_msg: &str, is_right: bool, is_unsigned: bool, + ) -> Result<(), String> { + vm_trace!("{} rd={} a={} b={}", op_name, rd, a, b); + let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, error_msg) else { + return Ok(()); + }; + let receiver = self.regs[rd]; + let ln = { + let obj = unsafe { &mut *obj_ptr }; + let prop_val = self.read_member_prop(obj, prop_name_si, receiver)?; + self.coerce_int32_bounded(prop_val)? + }; + let shift = self.coerce_uint32_bounded(self.regs[a])? & 0x1F; + let new_val = if !is_right { + JsValue::int(ln.wrapping_shl(shift)) + } else if is_unsigned { + let u = (ln as u32) >> shift; + if u <= i32::MAX as u32 { + JsValue::int(u as i32) + } else { + JsValue::float(u as f64) + } + } else { + JsValue::int(ln >> shift) + }; + self.regs[a] = new_val; + let obj = unsafe { &mut *obj_ptr }; + self.set_member_prop(obj, prop_name_si, new_val, receiver)?; + Ok(()) + } } diff --git a/crates/oxide_vm/src/dispatch/misc.rs b/crates/oxide_vm/src/dispatch/misc.rs index cdf3eee9..24f7bbea 100644 --- a/crates/oxide_vm/src/dispatch/misc.rs +++ b/crates/oxide_vm/src/dispatch/misc.rs @@ -49,6 +49,20 @@ impl Vm { self.regs[rd] = self.new_string(result); } + /// ToObject:rd 原地转换。对象直接返回;null/undefined 抛 TypeError; + /// 其余原始值包装为对应包装对象(with 对象环境需要)。 + #[inline(always)] + pub(crate) fn dispatch_to_object(&mut self, rd: usize) -> Result<(), String> { + vm_trace!("TO_OBJECT rd={} r{}={:?}", rd, rd, self.regs[rd]); + let val = self.regs[rd]; + if val.is_object() { + return Ok(()); + } + let obj_val = oxide_runtime_api::to_object(val, self)?; + self.regs[rd] = obj_val; + Ok(()) + } + pub(crate) fn dispatch_load_var(&mut self, rd: usize, a: usize) -> Result { vm_trace!("LOAD_VAR rd={} r{}={:?}", rd, a, self.regs[a]); if a == 254 diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index eb6ee079..7f171b08 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -1305,6 +1305,10 @@ impl Vm { self.dispatch_typeof(rd, a); } + OpCode::TO_OBJECT => { + self.dispatch_to_object(rd)?; + } + OpCode::VOID => { self.dispatch_void(rd); } @@ -1374,7 +1378,13 @@ impl Vm { | OpCode::COMPOUND_MEMBER_MUL | OpCode::COMPOUND_MEMBER_DIV | OpCode::COMPOUND_MEMBER_MOD - | OpCode::COMPOUND_MEMBER_EXP => { + | OpCode::COMPOUND_MEMBER_EXP + | OpCode::COMPOUND_MEMBER_BIT_AND + | OpCode::COMPOUND_MEMBER_BIT_OR + | OpCode::COMPOUND_MEMBER_BIT_XOR + | OpCode::COMPOUND_MEMBER_SHL + | OpCode::COMPOUND_MEMBER_SHR + | OpCode::COMPOUND_MEMBER_USHR => { self.dispatch_member_op(op, rd, a, b)?; } diff --git a/crates/oxide_vm/src/vm_dispatch_ctrl.rs b/crates/oxide_vm/src/vm_dispatch_ctrl.rs index 8a03af33..2cd6c1c4 100644 --- a/crates/oxide_vm/src/vm_dispatch_ctrl.rs +++ b/crates/oxide_vm/src/vm_dispatch_ctrl.rs @@ -77,6 +77,12 @@ impl Vm { return self.raise_type_error("CALL_NATIVE target is null"); } let obj = unsafe { &*obj_ptr }; + // 目标为普通 JS 函数(如用户覆盖了 builtin 名):回退到通用调用路径, + // 复用 dispatch_call 的 JS 函数调用逻辑而非强制走 native。 + // pc 此刻指向 ext 字(与 CALL 指令一致),dispatch_call 直接读取。 + if obj.is_function() && obj.native_fn().is_none() { + return self.dispatch_call(rd, a, b).map(|_| ()); + } if !obj.is_function() || obj.native_fn().is_none() { return self.raise_type_error("CALL_NATIVE target is not a native function"); } diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index 512d4455..a1d6ea6d 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -217,7 +217,9 @@ impl Vm { } } - self.regs[254] = JsValue::undefined(); + // 全局代码的 this 绑定为全局对象(ECMA-262 全局执行上下文)。 + let global = self.session.global_object(); + self.regs[254] = JsValue::from_js_object(global.as_ptr() as *mut JsObject); self.dispatch() } diff --git a/crates/oxide_vm/tests/arrow_tests.rs b/crates/oxide_vm/tests/arrow_tests.rs index 94703c70..c41b4fcd 100644 --- a/crates/oxide_vm/tests/arrow_tests.rs +++ b/crates/oxide_vm/tests/arrow_tests.rs @@ -103,8 +103,8 @@ fn arrow_block_body_needs_explicit_return() { } #[test] -fn arrow_at_global_scope_this_is_undefined() { - assert_eq!(eval("(() => this)()"), "undefined"); +fn arrow_at_global_scope_this_is_global_object() { + assert_eq!(eval("(() => this === globalThis)()"), "true"); } #[test]