diff --git a/crates/oxide_builtins/src/iterator.rs b/crates/oxide_builtins/src/iterator.rs index 48db4e4..7e5f8ba 100644 --- a/crates/oxide_builtins/src/iterator.rs +++ b/crates/oxide_builtins/src/iterator.rs @@ -132,26 +132,33 @@ fn get_iterator(vm: &mut H, value: JsValue) -> Result it, - Err(err) => { - let exc = vm - .take_uncaught_value() - .unwrap_or_else(|| crate::error::create_type_error(vm, &err)); - return Err(exc); - } - }; - if !iterator.is_object() { - return Err(crate::error::create_type_error( - vm, - "Result of the Symbol.iterator method is not an object", - )); - } - return Ok(iterator); + let method = match vm.ordinary_get(obj, sym_iter_si, value) { + Ok(m) => m, + Err(err) => { + // GetMethod 取 @@iterator 时 getter 抛出:透传原值,不落入鸭子回退。 + let exc = vm + .take_uncaught_value() + .unwrap_or_else(|| crate::error::create_type_error(vm, &err)); + return Err(exc); } + }; + if is_callable(method) { + let iterator = match vm.call_function_sync(method, value, &[]) { + Ok(it) => it, + Err(err) => { + let exc = vm + .take_uncaught_value() + .unwrap_or_else(|| crate::error::create_type_error(vm, &err)); + return Err(exc); + } + }; + if !iterator.is_object() { + return Err(crate::error::create_type_error( + vm, + "Result of the Symbol.iterator method is not an object", + )); + } + return Ok(iterator); } // 鸭子回退:对象自身有可调用 next(Map/Set 迭代器包装等既有用法)。 let next_si = vm.kernel_core().perm_interner().intern("next").0; @@ -178,9 +185,7 @@ fn next_array_like( let value = match vm.ordinary_get(arr, key_si, inner) { Ok(v) => v, Err(err) => { - let exc = vm - .take_uncaught_value() - .unwrap_or_else(|| crate::error::create_error(vm, &err)); + let exc = vm.take_uncaught_value().unwrap_or_else(|| crate::error::create_error(vm, &err)); return Err(exc); } }; diff --git a/crates/oxide_builtins/src/object.rs b/crates/oxide_builtins/src/object.rs index 692e51d..deafc62 100644 --- a/crates/oxide_builtins/src/object.rs +++ b/crates/oxide_builtins/src/object.rs @@ -357,8 +357,9 @@ pub fn object_define_property(vm: &mut H, args: &[u8]) -> NativeResul if desc_ptr.is_null() { return NativeResult::Err(crate::error::create_type_error(vm, "Property description must be an object")); } - let prop_name_str = oxide_runtime_api::to_string(vm.reg(args[2])); - let si = vm.kernel_core().perm_interner().intern(&prop_name_str).0; + // well-known symbol 等特殊键统一走 property_key_si(映射到 @@iterator 等别名), + // 保证与计算属性访问、Reflect.defineProperty 等读键路径一致。 + let si = vm.property_key_si(vm.reg(args[2])); let value_si = vm.kernel_core().perm_interner().intern("value").0; let get_si = vm.kernel_core().perm_interner().intern("get").0; diff --git a/crates/oxide_emit/src/expr/assignment.rs b/crates/oxide_emit/src/expr/assignment.rs index 424cf87..c267e28 100644 --- a/crates/oxide_emit/src/expr/assignment.rs +++ b/crates/oxide_emit/src/expr/assignment.rs @@ -169,7 +169,6 @@ impl Emitter { { let rhs = self.emit_expression(&assign.right, ctx)?; let name = id_ref.name.as_str(); - let var_reg = ctx.lookup_or_global(name); let op = match assign.operator { AssignmentOperator::Addition => OpCode::COMPOUND_ADD, AssignmentOperator::Subtraction => OpCode::COMPOUND_SUB, @@ -185,8 +184,51 @@ impl Emitter { AssignmentOperator::ShiftRightZeroFill => OpCode::COMPOUND_USHR, _ => return Err(format!("compound assignment operator {:?} not supported", assign.operator)), }; - ctx.inst(Inst::new(op, Operand::Reg(var_reg), Operand::Reg(rhs), Operand::None)); - Ok(var_reg) + // 目标为 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 { + 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) + } } else { Err(format!("compound assignment operator {:?} not supported", assign.operator)) } diff --git a/crates/oxide_emit/src/expr/object/array.rs b/crates/oxide_emit/src/expr/object/array.rs index a5fb64d..5cc9e65 100644 --- a/crates/oxide_emit/src/expr/object/array.rs +++ b/crates/oxide_emit/src/expr/object/array.rs @@ -1,31 +1,111 @@ -//! 数组字面量 emit:`emit_array_expression` 逐元素求值并建数组(含 holes)。 +//! 数组字面量 emit:`emit_array_expression` 逐元素求值并建数组(含 holes 与 spread)。 +//! +//! 纯静态数组按编译期索引直写;含 spread 时用运行期 index 寄存器贯穿整个数组, +//! spread 元素展开为 for-of 循环追加,后续元素从当前 index 继续,保证求值顺序正确。 use crate::{CompileCtx, Emitter}; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; +use oxide_parser::ArrayExpressionElement; impl Emitter { pub(crate) fn emit_array_expression( &self, arr: &oxide_parser::ArrayExpression, ctx: &mut CompileCtx, ) -> Result { let arr_reg = ctx.alloc_reg(); - let n = arr.elements.len() as u16; - ctx.inst(Inst::new(OpCode::NEW_ARRAY, Operand::Reg(arr_reg), Operand::Imm(n), Operand::None)); - for (i, elem) in arr.elements.iter().enumerate() { - let Some(e) = elem.as_expression() else { - return Err("spread not supported".into()); - }; - let val_reg = self.emit_expression(e, ctx)?; - let idx_reg = ctx.alloc_reg(); - let idx = ctx.add_constant(Constant::Int(i as i32)); - ctx.inst(Inst::load_const(Operand::Reg(idx_reg), idx)); - ctx.inst(Inst::new( - OpCode::SET_ELEM, - Operand::Reg(arr_reg), - Operand::Reg(idx_reg), - Operand::Reg(val_reg), - )); + let has_spread = arr.elements.iter().any(|e| e.is_spread()); + + if !has_spread { + // 纯静态数组:NEW_ARRAY 预置元素个数,holes 跳过索引不写,length 由 n 覆盖。 + let n = arr.elements.len() as u16; + ctx.inst(Inst::new(OpCode::NEW_ARRAY, Operand::Reg(arr_reg), Operand::Imm(n), Operand::None)); + for (i, elem) in arr.elements.iter().enumerate() { + let Some(e) = elem.as_expression() else { + continue; + }; + let val_reg = self.emit_expression(e, ctx)?; + let idx_reg = ctx.alloc_reg(); + let idx = ctx.add_constant(Constant::Int(i as i32)); + ctx.inst(Inst::load_const(Operand::Reg(idx_reg), idx)); + ctx.inst(Inst::new( + OpCode::SET_ELEM, + Operand::Reg(arr_reg), + Operand::Reg(idx_reg), + Operand::Reg(val_reg), + )); + } + return Ok(arr_reg); + } + + // 含 spread:NEW_ARRAY 预置静态元素个数(length 下界),运行期 index 贯穿全程。 + let static_count = arr.elements.iter().filter(|e| !e.is_spread()).count() as u16; + ctx.inst(Inst::new( + OpCode::NEW_ARRAY, + Operand::Reg(arr_reg), + Operand::Imm(static_count), + Operand::None, + )); + let index_reg = ctx.alloc_reg(); + let zero_idx = ctx.add_constant(Constant::Int(0)); + ctx.inst(Inst::load_const(Operand::Reg(index_reg), zero_idx)); + let one_reg = ctx.alloc_reg(); + let one_idx = ctx.add_constant(Constant::Int(1)); + ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx)); + + for elem in &arr.elements { + if let Some(e) = elem.as_expression() { + let val_reg = self.emit_expression(e, ctx)?; + ctx.inst(Inst::new( + OpCode::SET_ELEM, + Operand::Reg(arr_reg), + Operand::Reg(index_reg), + Operand::Reg(val_reg), + )); + ctx.inst(Inst::new( + OpCode::ADD, + Operand::Reg(index_reg), + Operand::Reg(index_reg), + Operand::Reg(one_reg), + )); + } else if elem.is_elision() { + // hole:只推进 index,不写元素,length 由后续 SET_ELEM 越界扩容或初值覆盖。 + ctx.inst(Inst::new( + OpCode::ADD, + Operand::Reg(index_reg), + Operand::Reg(index_reg), + Operand::Reg(one_reg), + )); + } else { + let ArrayExpressionElement::SpreadElement(sp) = elem else { + unreachable!("数组元素非表达式、hole 即 spread") + }; + let iter_src_reg = self.emit_expression(&sp.argument, ctx)?; + ctx.inst(Inst::new(OpCode::FOR_OF_INIT, Operand::None, Operand::Reg(iter_src_reg), Operand::None)); + let start_label = ctx.next_label_id(); + let end_label = ctx.next_label_id(); + ctx.labels.set_label_pos(start_label, ctx.insts.len()); + let has_reg = ctx.alloc_reg(); + ctx.inst(Inst::new(OpCode::FOR_OF_DONE, Operand::Reg(has_reg), Operand::None, Operand::None)); + ctx.inst(Inst::jmp_if_false(has_reg, end_label)); + let val_reg = ctx.alloc_reg(); + ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None)); + ctx.inst(Inst::new( + OpCode::SET_ELEM, + Operand::Reg(arr_reg), + Operand::Reg(index_reg), + Operand::Reg(val_reg), + )); + ctx.inst(Inst::new( + OpCode::ADD, + Operand::Reg(index_reg), + Operand::Reg(index_reg), + Operand::Reg(one_reg), + )); + ctx.inst(Inst::jmp(start_label)); + ctx.labels.set_label_pos(end_label, ctx.insts.len()); + ctx.inst(Inst::new(OpCode::FOR_OF_CLOSE, Operand::None, Operand::None, Operand::None)); + } } Ok(arr_reg) }