feat: 数组字面量 spread([...a] 展开为 for-of 追加)
This commit is contained in:
@@ -132,26 +132,33 @@ fn get_iterator<H: VmHost>(vm: &mut H, value: JsValue) -> Result<JsValue, JsValu
|
|||||||
let obj = unsafe { &*value.as_js_object_ptr() };
|
let obj = unsafe { &*value.as_js_object_ptr() };
|
||||||
// 迭代协议:GetIterator 先取 value[Symbol.iterator] 并调用。
|
// 迭代协议:GetIterator 先取 value[Symbol.iterator] 并调用。
|
||||||
let sym_iter_si = vm.kernel_core().perm_interner().intern("@@iterator").0;
|
let sym_iter_si = vm.kernel_core().perm_interner().intern("@@iterator").0;
|
||||||
let method = vm.ordinary_get(obj, sym_iter_si, value);
|
let method = match vm.ordinary_get(obj, sym_iter_si, value) {
|
||||||
if let Ok(method) = method {
|
Ok(m) => m,
|
||||||
if is_callable(method) {
|
Err(err) => {
|
||||||
let iterator = match vm.call_function_sync(method, value, &[]) {
|
// GetMethod 取 @@iterator 时 getter 抛出:透传原值,不落入鸭子回退。
|
||||||
Ok(it) => it,
|
let exc = vm
|
||||||
Err(err) => {
|
.take_uncaught_value()
|
||||||
let exc = vm
|
.unwrap_or_else(|| crate::error::create_type_error(vm, &err));
|
||||||
.take_uncaught_value()
|
return Err(exc);
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
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 迭代器包装等既有用法)。
|
// 鸭子回退:对象自身有可调用 next(Map/Set 迭代器包装等既有用法)。
|
||||||
let next_si = vm.kernel_core().perm_interner().intern("next").0;
|
let next_si = vm.kernel_core().perm_interner().intern("next").0;
|
||||||
@@ -178,9 +185,7 @@ fn next_array_like<H: VmHost>(
|
|||||||
let value = match vm.ordinary_get(arr, key_si, inner) {
|
let value = match vm.ordinary_get(arr, key_si, inner) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let exc = vm
|
let exc = vm.take_uncaught_value().unwrap_or_else(|| crate::error::create_error(vm, &err));
|
||||||
.take_uncaught_value()
|
|
||||||
.unwrap_or_else(|| crate::error::create_error(vm, &err));
|
|
||||||
return Err(exc);
|
return Err(exc);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -357,8 +357,9 @@ pub fn object_define_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
|||||||
if desc_ptr.is_null() {
|
if desc_ptr.is_null() {
|
||||||
return NativeResult::Err(crate::error::create_type_error(vm, "Property description must be an object"));
|
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]));
|
// well-known symbol 等特殊键统一走 property_key_si(映射到 @@iterator 等别名),
|
||||||
let si = vm.kernel_core().perm_interner().intern(&prop_name_str).0;
|
// 保证与计算属性访问、Reflect.defineProperty 等读键路径一致。
|
||||||
|
let si = vm.property_key_si(vm.reg(args[2]));
|
||||||
|
|
||||||
let value_si = vm.kernel_core().perm_interner().intern("value").0;
|
let value_si = vm.kernel_core().perm_interner().intern("value").0;
|
||||||
let get_si = vm.kernel_core().perm_interner().intern("get").0;
|
let get_si = vm.kernel_core().perm_interner().intern("get").0;
|
||||||
|
|||||||
@@ -169,7 +169,6 @@ impl Emitter {
|
|||||||
{
|
{
|
||||||
let rhs = self.emit_expression(&assign.right, ctx)?;
|
let rhs = self.emit_expression(&assign.right, ctx)?;
|
||||||
let name = id_ref.name.as_str();
|
let name = id_ref.name.as_str();
|
||||||
let var_reg = ctx.lookup_or_global(name);
|
|
||||||
let op = match assign.operator {
|
let op = match assign.operator {
|
||||||
AssignmentOperator::Addition => OpCode::COMPOUND_ADD,
|
AssignmentOperator::Addition => OpCode::COMPOUND_ADD,
|
||||||
AssignmentOperator::Subtraction => OpCode::COMPOUND_SUB,
|
AssignmentOperator::Subtraction => OpCode::COMPOUND_SUB,
|
||||||
@@ -185,8 +184,51 @@ impl Emitter {
|
|||||||
AssignmentOperator::ShiftRightZeroFill => OpCode::COMPOUND_USHR,
|
AssignmentOperator::ShiftRightZeroFill => OpCode::COMPOUND_USHR,
|
||||||
_ => return Err(format!("compound assignment operator {:?} not supported", assign.operator)),
|
_ => return Err(format!("compound assignment operator {:?} not supported", assign.operator)),
|
||||||
};
|
};
|
||||||
ctx.inst(Inst::new(op, Operand::Reg(var_reg), Operand::Reg(rhs), Operand::None));
|
// 目标为 upvalue / 被捕获 cell 时走显式读取-运算-写回,保证写穿透共享单元;
|
||||||
Ok(var_reg)
|
// 只有普通槽位才用 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 {
|
} else {
|
||||||
Err(format!("compound assignment operator {:?} not supported", assign.operator))
|
Err(format!("compound assignment operator {:?} not supported", assign.operator))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 crate::{CompileCtx, Emitter};
|
||||||
use oxide_bytecode::module::Constant;
|
use oxide_bytecode::module::Constant;
|
||||||
use oxide_bytecode::opcode::OpCode;
|
use oxide_bytecode::opcode::OpCode;
|
||||||
use oxide_ir::inst::Inst;
|
use oxide_ir::inst::Inst;
|
||||||
use oxide_ir::operand::Operand;
|
use oxide_ir::operand::Operand;
|
||||||
|
use oxide_parser::ArrayExpressionElement;
|
||||||
impl Emitter {
|
impl Emitter {
|
||||||
pub(crate) fn emit_array_expression(
|
pub(crate) fn emit_array_expression(
|
||||||
&self, arr: &oxide_parser::ArrayExpression, ctx: &mut CompileCtx,
|
&self, arr: &oxide_parser::ArrayExpression, ctx: &mut CompileCtx,
|
||||||
) -> Result<u32, String> {
|
) -> Result<u32, String> {
|
||||||
let arr_reg = ctx.alloc_reg();
|
let arr_reg = ctx.alloc_reg();
|
||||||
let n = arr.elements.len() as u16;
|
let has_spread = arr.elements.iter().any(|e| e.is_spread());
|
||||||
ctx.inst(Inst::new(OpCode::NEW_ARRAY, Operand::Reg(arr_reg), Operand::Imm(n), Operand::None));
|
|
||||||
for (i, elem) in arr.elements.iter().enumerate() {
|
if !has_spread {
|
||||||
let Some(e) = elem.as_expression() else {
|
// 纯静态数组:NEW_ARRAY 预置元素个数,holes 跳过索引不写,length 由 n 覆盖。
|
||||||
return Err("spread not supported".into());
|
let n = arr.elements.len() as u16;
|
||||||
};
|
ctx.inst(Inst::new(OpCode::NEW_ARRAY, Operand::Reg(arr_reg), Operand::Imm(n), Operand::None));
|
||||||
let val_reg = self.emit_expression(e, ctx)?;
|
for (i, elem) in arr.elements.iter().enumerate() {
|
||||||
let idx_reg = ctx.alloc_reg();
|
let Some(e) = elem.as_expression() else {
|
||||||
let idx = ctx.add_constant(Constant::Int(i as i32));
|
continue;
|
||||||
ctx.inst(Inst::load_const(Operand::Reg(idx_reg), idx));
|
};
|
||||||
ctx.inst(Inst::new(
|
let val_reg = self.emit_expression(e, ctx)?;
|
||||||
OpCode::SET_ELEM,
|
let idx_reg = ctx.alloc_reg();
|
||||||
Operand::Reg(arr_reg),
|
let idx = ctx.add_constant(Constant::Int(i as i32));
|
||||||
Operand::Reg(idx_reg),
|
ctx.inst(Inst::load_const(Operand::Reg(idx_reg), idx));
|
||||||
Operand::Reg(val_reg),
|
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)
|
Ok(arr_reg)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user