feat: 调用实参 spread(f(...args) / new F(...args) / super(...args))
This commit is contained in:
@@ -140,6 +140,9 @@ define_opcodes! {
|
||||
CREATE_CLOSURE = 0x4A => "CREATE_CLOSURE",
|
||||
CREATE_REGEXP = 0x4B => "CREATE_REGEXP",
|
||||
STORE_UPVALUE = 0x4C => "STORE_UPVALUE",
|
||||
CALL_SPREAD = 0x4D => "CALL_SPREAD",
|
||||
NEW_EXPRESSION_SPREAD = 0x4E => "NEW_EXPRESSION_SPREAD",
|
||||
SUPER_CALL_SPREAD = 0x4F => "SUPER_CALL_SPREAD",
|
||||
|
||||
// ── Object Property (0x50-0x5F) ──
|
||||
IC_GET_PROP = 0x50 => "IC_GET_PROP",
|
||||
|
||||
@@ -528,6 +528,26 @@ fn compile_template_literal_emits_template_str() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_call_spread_emits_spread_opcodes() {
|
||||
// 普通调用 / new 表达式含 spread 实参 → 发 spread 变体 opcode
|
||||
let module = compile_source("function f(){}; f(...[1,2]); new f(...[3]);");
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::CALL_SPREAD),
|
||||
"f(...args) should emit CALL_SPREAD"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::NEW_EXPRESSION_SPREAD),
|
||||
"new f(...args) should emit NEW_EXPRESSION_SPREAD"
|
||||
);
|
||||
// 无 spread 的调用不发 spread 变体
|
||||
let plain = compile_source("function f(a){}; f(1);");
|
||||
assert!(
|
||||
!plain.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::CALL_SPREAD),
|
||||
"plain call should not emit CALL_SPREAD"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_dynamic_member_ops() {
|
||||
let module = compile_source("let obj = {}, k = 'x', arr = [1]; obj[k]; obj[k] = 1; arr[0];");
|
||||
|
||||
@@ -305,17 +305,13 @@ impl Emitter {
|
||||
Expression::CallExpression(ce) => {
|
||||
self.collect_capture_names_expr(&ce.callee, ref_set, shadow, out);
|
||||
for a in &ce.arguments {
|
||||
if let Some(e) = a.as_expression() {
|
||||
self.collect_capture_names_expr(e, ref_set, shadow, out);
|
||||
}
|
||||
self.collect_capture_names_arg(a, ref_set, shadow, out);
|
||||
}
|
||||
}
|
||||
Expression::NewExpression(ne) => {
|
||||
self.collect_capture_names_expr(&ne.callee, ref_set, shadow, out);
|
||||
for a in &ne.arguments {
|
||||
if let Some(e) = a.as_expression() {
|
||||
self.collect_capture_names_expr(e, ref_set, shadow, out);
|
||||
}
|
||||
self.collect_capture_names_arg(a, ref_set, shadow, out);
|
||||
}
|
||||
}
|
||||
Expression::SequenceExpression(se) => {
|
||||
@@ -389,15 +385,25 @@ impl Emitter {
|
||||
oxide_parser::ChainElement::CallExpression(call) => {
|
||||
self.collect_capture_names_expr(&call.callee, ref_set, shadow, out);
|
||||
for a in &call.arguments {
|
||||
if let Some(e) = a.as_expression() {
|
||||
self.collect_capture_names_expr(e, ref_set, shadow, out);
|
||||
}
|
||||
self.collect_capture_names_arg(a, ref_set, shadow, out);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// 遍历调用实参:静态实参与 spread 内部表达式都纳入捕获扫描。
|
||||
fn collect_capture_names_arg(
|
||||
&self, arg: &oxide_parser::Argument, ref_set: &HashSet<String>, shadow: &HashSet<String>,
|
||||
out: &mut HashSet<String>,
|
||||
) {
|
||||
if let Some(e) = arg.as_expression() {
|
||||
self.collect_capture_names_expr(e, ref_set, shadow, out);
|
||||
} else if let oxide_parser::Argument::SpreadElement(sp) = arg {
|
||||
self.collect_capture_names_expr(&sp.argument, ref_set, shadow, out);
|
||||
}
|
||||
}
|
||||
|
||||
/// 分析本函数:哪些绑定被任意深度嵌套函数捕获 → captured_bindings。
|
||||
pub(crate) fn collect_captured_bindings(
|
||||
&self, stmts: &[Statement], extra_exprs: &[&oxide_parser::Expression], own: &HashSet<String>,
|
||||
@@ -572,9 +578,7 @@ impl Emitter {
|
||||
Expression::CallExpression(ce) => {
|
||||
self.collect_captured_expr(&ce.callee, own, out);
|
||||
for a in &ce.arguments {
|
||||
if let Some(e) = a.as_expression() {
|
||||
self.collect_captured_expr(e, own, out);
|
||||
}
|
||||
self.collect_captured_arg(a, own, out);
|
||||
}
|
||||
}
|
||||
Expression::BinaryExpression(be) => {
|
||||
@@ -614,9 +618,7 @@ impl Emitter {
|
||||
Expression::NewExpression(ne) => {
|
||||
self.collect_captured_expr(&ne.callee, own, out);
|
||||
for a in &ne.arguments {
|
||||
if let Some(e) = a.as_expression() {
|
||||
self.collect_captured_expr(e, own, out);
|
||||
}
|
||||
self.collect_captured_arg(a, own, out);
|
||||
}
|
||||
}
|
||||
Expression::ComputedMemberExpression(m) => {
|
||||
@@ -659,22 +661,29 @@ impl Emitter {
|
||||
oxide_parser::ChainElement::CallExpression(c) => {
|
||||
self.collect_captured_expr(&c.callee, own, out);
|
||||
for a in &c.arguments {
|
||||
if let Some(e) = a.as_expression() {
|
||||
self.collect_captured_expr(e, own, out);
|
||||
}
|
||||
self.collect_captured_arg(a, own, out);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// 遍历调用实参:静态实参与 spread 内部表达式都纳入捕获判定。
|
||||
fn collect_captured_arg(&self, arg: &oxide_parser::Argument, own: &HashSet<String>, out: &mut HashSet<String>) {
|
||||
if let Some(e) = arg.as_expression() {
|
||||
self.collect_captured_expr(e, own, out);
|
||||
} else if let oxide_parser::Argument::SpreadElement(sp) = arg {
|
||||
self.collect_captured_expr(&sp.argument, own, out);
|
||||
}
|
||||
}
|
||||
|
||||
/// 分析子函数 body:引用父级被捕获绑定的名字 → upvalue_captures。
|
||||
/// 名字若在父 `captured_bindings`(父 own cell)则 `cell_idx` 索引定义方表;
|
||||
/// 若在父 `upvalue_captures`(父自身从更外层捕获)则链式标记 `parent_uv_idx`,
|
||||
/// 运行时从父闭包 upvalues 取 cell。enclosing_reg 由父 emit 完成后填充。
|
||||
pub(crate) fn collect_upvalue_names(
|
||||
&self, body_stmts: &[Statement], extra_exprs: &[&oxide_parser::Expression], parent_captured: &BTreeMap<String, u8>,
|
||||
parent_upvalues: &[UpvalueCapture], sub_own: &HashSet<String>,
|
||||
&self, body_stmts: &[Statement], extra_exprs: &[&oxide_parser::Expression],
|
||||
parent_captured: &BTreeMap<String, u8>, parent_upvalues: &[UpvalueCapture], sub_own: &HashSet<String>,
|
||||
) -> Vec<UpvalueCapture> {
|
||||
let mut parent_names: HashSet<String> = parent_captured.keys().cloned().collect();
|
||||
for u in parent_upvalues {
|
||||
|
||||
@@ -14,19 +14,19 @@ impl Emitter {
|
||||
if !ctx.in_derived_constructor {
|
||||
return Err("super() only supported in derived constructors".into());
|
||||
}
|
||||
let mut arg_regs = Vec::new();
|
||||
for arg in &call.arguments {
|
||||
if let Some(expr) = arg.as_expression() {
|
||||
arg_regs.push(self.emit_expression(expr, ctx)?);
|
||||
}
|
||||
}
|
||||
let first_arg_reg = if arg_regs.is_empty() { 0u32 } else { pack_arg_regs(&mut arg_regs, ctx) };
|
||||
let words = self.emit_call_args(&call.arguments, ctx)?;
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::super_call(
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(first_arg_reg),
|
||||
arg_regs.len() as u8,
|
||||
));
|
||||
if words.iter().any(|w| w >> 31 == 1) {
|
||||
ctx.inst(Inst::super_call_spread(Operand::Reg(result_reg), &words));
|
||||
} else {
|
||||
let mut static_regs = words;
|
||||
let first_arg_reg = if static_regs.is_empty() { 0u32 } else { pack_arg_regs(&mut static_regs, ctx) };
|
||||
ctx.inst(Inst::super_call(
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(first_arg_reg),
|
||||
static_regs.len() as u8,
|
||||
));
|
||||
}
|
||||
if let Some(mut field_buffer) = ctx.field_buffer.take() {
|
||||
let insert_inst = ctx.insts.len();
|
||||
ctx.insts.append(&mut field_buffer.insts);
|
||||
@@ -92,36 +92,37 @@ impl Emitter {
|
||||
(callee_reg, this_reg)
|
||||
}
|
||||
};
|
||||
let mut arg_regs = Vec::new();
|
||||
for arg in &call.arguments {
|
||||
if let Some(expr) = arg.as_expression() {
|
||||
arg_regs.push(self.emit_expression(expr, ctx)?);
|
||||
let words = self.emit_call_args(&call.arguments, ctx)?;
|
||||
if words.iter().any(|w| w >> 31 == 1) {
|
||||
// 任一实参是 spread → 运行期物化完整实参(有序字逐个读寄存器 / 迭代展开)。
|
||||
ctx.inst(Inst::call_spread(Operand::Reg(callee_reg), Operand::Reg(this_reg), &words));
|
||||
} else {
|
||||
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,
|
||||
_ => OpCode::CALL,
|
||||
};
|
||||
match op {
|
||||
OpCode::CALL => {
|
||||
ctx.inst(Inst::call(
|
||||
Operand::Reg(callee_reg),
|
||||
Operand::Reg(this_reg),
|
||||
Operand::Reg(first_arg_reg),
|
||||
static_regs.len() as u8,
|
||||
));
|
||||
}
|
||||
OpCode::CALL_NATIVE => {
|
||||
ctx.inst(Inst::call_native(
|
||||
Operand::Reg(callee_reg),
|
||||
Operand::Reg(this_reg),
|
||||
Operand::Reg(first_arg_reg),
|
||||
static_regs.len() as u8,
|
||||
));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
let first_arg_reg = if arg_regs.is_empty() { 0u32 } else { pack_arg_regs(&mut arg_regs, ctx) };
|
||||
let op = match &call.callee {
|
||||
Expression::Identifier(ident) if ctx.is_builtin(ident.name.as_str()) => OpCode::CALL_NATIVE,
|
||||
_ => OpCode::CALL,
|
||||
};
|
||||
match op {
|
||||
OpCode::CALL => {
|
||||
ctx.inst(Inst::call(
|
||||
Operand::Reg(callee_reg),
|
||||
Operand::Reg(this_reg),
|
||||
Operand::Reg(first_arg_reg),
|
||||
arg_regs.len() as u8,
|
||||
));
|
||||
}
|
||||
OpCode::CALL_NATIVE => {
|
||||
ctx.inst(Inst::call_native(
|
||||
Operand::Reg(callee_reg),
|
||||
Operand::Reg(this_reg),
|
||||
Operand::Reg(first_arg_reg),
|
||||
arg_regs.len() as u8,
|
||||
));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::None, Operand::None));
|
||||
Ok(result_reg)
|
||||
@@ -139,7 +140,7 @@ impl Emitter {
|
||||
/// 各参数由独立 vreg 承载,复杂表达式(对象/数组字面量、嵌套调用)的临时寄存器
|
||||
/// 会使参数 vreg 不连续——检测到不连续时用 MOV 打包到新连续块。
|
||||
/// 返回首参寄存器;参数为空时返回 0。
|
||||
fn pack_arg_regs(arg_regs: &mut [u32], ctx: &mut CompileCtx) -> u32 {
|
||||
pub(crate) fn pack_arg_regs(arg_regs: &mut [u32], ctx: &mut CompileCtx) -> u32 {
|
||||
let consecutive = arg_regs.windows(2).all(|w| w[1] == w[0] + 1);
|
||||
if consecutive {
|
||||
return arg_regs[0];
|
||||
@@ -154,3 +155,21 @@ fn pack_arg_regs(arg_regs: &mut [u32], ctx: &mut CompileCtx) -> u32 {
|
||||
}
|
||||
base
|
||||
}
|
||||
|
||||
impl Emitter {
|
||||
/// 收集调用实参为有序实参字(保持源码求值序):静态实参 = 寄存器号,
|
||||
/// spread 源 = `0x8000_0000 | 寄存器号`(高位标记区分)。
|
||||
pub(crate) fn emit_call_args(
|
||||
&self, args: &[oxide_parser::Argument], ctx: &mut CompileCtx,
|
||||
) -> Result<Vec<u32>, String> {
|
||||
let mut words = Vec::new();
|
||||
for arg in args {
|
||||
if let Some(expr) = arg.as_expression() {
|
||||
words.push(self.emit_expression(expr, ctx)?);
|
||||
} else if let oxide_parser::Argument::SpreadElement(sp) = arg {
|
||||
words.push(0x8000_0000 | self.emit_expression(&sp.argument, ctx)?);
|
||||
}
|
||||
}
|
||||
Ok(words)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//! 函数:`emit_arrow_function_expression`、`emit_function_expression`、
|
||||
//! `emit_class_expression`、`emit_new_expression`、`emit_function_domain`。
|
||||
|
||||
use crate::expr::call::pack_arg_regs;
|
||||
use crate::{CompileCtx, Emitter, ParamSpec};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
@@ -94,20 +95,20 @@ impl Emitter {
|
||||
|
||||
fn emit_new_expression(&self, ne: &oxide_parser::NewExpression, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
let constructor_reg = self.emit_expression(&ne.callee, ctx)?;
|
||||
let mut arg_regs = Vec::new();
|
||||
for arg in &ne.arguments {
|
||||
if let Some(expr) = arg.as_expression() {
|
||||
arg_regs.push(self.emit_expression(expr, ctx)?);
|
||||
}
|
||||
}
|
||||
let first_arg_reg = if arg_regs.is_empty() { 0u32 } else { arg_regs[0] };
|
||||
let words = self.emit_call_args(&ne.arguments, ctx)?;
|
||||
let r = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new_expression(
|
||||
Operand::Reg(r),
|
||||
Operand::Reg(constructor_reg),
|
||||
Operand::Reg(first_arg_reg),
|
||||
arg_regs.len() as u8,
|
||||
));
|
||||
if words.iter().any(|w| w >> 31 == 1) {
|
||||
ctx.inst(Inst::new_expression_spread(Operand::Reg(r), Operand::Reg(constructor_reg), &words));
|
||||
} else {
|
||||
let mut static_regs = words;
|
||||
let first_arg_reg = if static_regs.is_empty() { 0u32 } else { pack_arg_regs(&mut static_regs, ctx) };
|
||||
ctx.inst(Inst::new_expression(
|
||||
Operand::Reg(r),
|
||||
Operand::Reg(constructor_reg),
|
||||
Operand::Reg(first_arg_reg),
|
||||
static_regs.len() as u8,
|
||||
));
|
||||
}
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
@@ -121,4 +122,3 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -135,17 +135,13 @@ impl Emitter {
|
||||
Expression::CallExpression(call) => {
|
||||
self.pre_scan_builtin_expr(&call.callee, ctx);
|
||||
for arg in &call.arguments {
|
||||
if let Some(e) = arg.as_expression() {
|
||||
self.pre_scan_builtin_expr(e, ctx);
|
||||
}
|
||||
self.pre_scan_builtin_arg(arg, ctx);
|
||||
}
|
||||
}
|
||||
Expression::NewExpression(ne) => {
|
||||
self.pre_scan_builtin_expr(&ne.callee, ctx);
|
||||
for arg in &ne.arguments {
|
||||
if let Some(e) = arg.as_expression() {
|
||||
self.pre_scan_builtin_expr(e, ctx);
|
||||
}
|
||||
self.pre_scan_builtin_arg(arg, ctx);
|
||||
}
|
||||
}
|
||||
Expression::LogicalExpression(log) => {
|
||||
@@ -254,15 +250,22 @@ impl Emitter {
|
||||
oxide_parser::ChainElement::CallExpression(call) => {
|
||||
self.pre_scan_builtin_expr(&call.callee, ctx);
|
||||
for arg in &call.arguments {
|
||||
if let Some(e) = arg.as_expression() {
|
||||
self.pre_scan_builtin_expr(e, ctx);
|
||||
}
|
||||
self.pre_scan_builtin_arg(arg, ctx);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// 遍历调用实参:spread 内部表达式也纳入 builtin 预扫描。
|
||||
fn pre_scan_builtin_arg(&self, arg: &oxide_parser::Argument, ctx: &mut CompileCtx) {
|
||||
if let Some(e) = arg.as_expression() {
|
||||
self.pre_scan_builtin_expr(e, ctx);
|
||||
} else if let oxide_parser::Argument::SpreadElement(sp) = arg {
|
||||
self.pre_scan_builtin_expr(&sp.argument, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn predeclare_function_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) {
|
||||
for statement in statements {
|
||||
let Statement::FunctionDeclaration(function) = statement else {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! 可选链(`?.`)emit:短路守卫、链式成员访问/调用与逻辑赋值短路测试。
|
||||
//! 函数:`emit_chainable_expression`、`emit_optional_guard`、`emit_chain_call` 等。
|
||||
|
||||
use crate::expr::call::pack_arg_regs;
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
@@ -106,36 +107,36 @@ impl Emitter {
|
||||
self.emit_optional_guard(callee_reg, label, ctx)?;
|
||||
}
|
||||
}
|
||||
let mut arg_regs = Vec::new();
|
||||
for arg in &call.arguments {
|
||||
if let Some(expr) = arg.as_expression() {
|
||||
arg_regs.push(self.emit_expression(expr, ctx)?);
|
||||
let words = self.emit_call_args(&call.arguments, ctx)?;
|
||||
if words.iter().any(|w| w >> 31 == 1) {
|
||||
ctx.inst(Inst::call_spread(Operand::Reg(callee_reg), Operand::Reg(this_reg), &words));
|
||||
} else {
|
||||
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,
|
||||
_ => OpCode::CALL,
|
||||
};
|
||||
match op {
|
||||
OpCode::CALL => {
|
||||
ctx.inst(Inst::call(
|
||||
Operand::Reg(callee_reg),
|
||||
Operand::Reg(this_reg),
|
||||
Operand::Reg(first_arg_reg),
|
||||
static_regs.len() as u8,
|
||||
));
|
||||
}
|
||||
OpCode::CALL_NATIVE => {
|
||||
ctx.inst(Inst::call_native(
|
||||
Operand::Reg(callee_reg),
|
||||
Operand::Reg(this_reg),
|
||||
Operand::Reg(first_arg_reg),
|
||||
static_regs.len() as u8,
|
||||
));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
let first_arg_reg = if arg_regs.is_empty() { 0u32 } else { arg_regs[0] };
|
||||
let op = match &call.callee {
|
||||
Expression::Identifier(ident) if ctx.is_builtin(ident.name.as_str()) => OpCode::CALL_NATIVE,
|
||||
_ => OpCode::CALL,
|
||||
};
|
||||
match op {
|
||||
OpCode::CALL => {
|
||||
ctx.inst(Inst::call(
|
||||
Operand::Reg(callee_reg),
|
||||
Operand::Reg(this_reg),
|
||||
Operand::Reg(first_arg_reg),
|
||||
arg_regs.len() as u8,
|
||||
));
|
||||
}
|
||||
OpCode::CALL_NATIVE => {
|
||||
ctx.inst(Inst::call_native(
|
||||
Operand::Reg(callee_reg),
|
||||
Operand::Reg(this_reg),
|
||||
Operand::Reg(first_arg_reg),
|
||||
arg_regs.len() as u8,
|
||||
));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::None, Operand::None));
|
||||
Ok(result_reg)
|
||||
|
||||
@@ -18,7 +18,7 @@ impl Inst {
|
||||
pub fn def_reg(&self) -> Option<u32> {
|
||||
match self.op {
|
||||
// CALL 系:结果隐式写 reg 0,rd 是 callee 的 use
|
||||
OpCode::CALL | OpCode::CALL_NATIVE => Some(0),
|
||||
OpCode::CALL | OpCode::CALL_NATIVE | OpCode::CALL_SPREAD => Some(0),
|
||||
// GET_PROP 系:结果写 a/b 槽而非 rd
|
||||
OpCode::GET_PROP | OpCode::IC_GET_PROP => reg_of(&self.a),
|
||||
OpCode::GET_PROP_DYNAMIC => reg_of(&self.b),
|
||||
@@ -91,6 +91,25 @@ impl Inst {
|
||||
let nargs = self.ext.first().copied().unwrap_or(0);
|
||||
push_range(&mut uses, reg_of(&self.a), nargs);
|
||||
}
|
||||
// spread 调用系:ext 内嵌有序实参字(静态寄存器号 / `0x8000_0000|reg` spread 源)
|
||||
OpCode::CALL_SPREAD => {
|
||||
push_operand(&mut uses, &self.rd);
|
||||
push_operand(&mut uses, &self.a);
|
||||
for &w in self.ext.iter().skip(1) {
|
||||
uses.push(w & 0x7FFF_FFFF);
|
||||
}
|
||||
}
|
||||
OpCode::NEW_EXPRESSION_SPREAD => {
|
||||
push_operand(&mut uses, &self.a);
|
||||
for &w in self.ext.iter().skip(1) {
|
||||
uses.push(w & 0x7FFF_FFFF);
|
||||
}
|
||||
}
|
||||
OpCode::SUPER_CALL_SPREAD => {
|
||||
for &w in self.ext.iter().skip(1) {
|
||||
uses.push(w & 0x7FFF_FFFF);
|
||||
}
|
||||
}
|
||||
// GET_PROP 系:结果写 a/b 槽,rd=obj 是 use
|
||||
OpCode::GET_PROP => {
|
||||
push_operand(&mut uses, &self.rd);
|
||||
@@ -435,6 +454,24 @@ mod tests {
|
||||
assert_eq!(inst.use_regs().as_slice(), &[0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_call_def_and_uses() {
|
||||
// CALL_SPREAD:结果隐式写 reg 0;use = callee + this + ext 有序实参字(静态/spread 同编)
|
||||
let call = Inst::call_spread(Operand::Reg(0), Operand::Reg(1), &[5, 0x8000_0000 | 9, 300]);
|
||||
assert_eq!(call.def_reg(), Some(0));
|
||||
assert_eq!(call.use_regs().as_slice(), &[0, 1, 5, 9, 300]);
|
||||
|
||||
// NEW_EXPRESSION_SPREAD:rd 即 def;use = ctor + 实参字
|
||||
let ne = Inst::new_expression_spread(Operand::Reg(3), Operand::Reg(0), &[7, 0x8000_0000 | 12]);
|
||||
assert_eq!(ne.def_reg(), Some(3));
|
||||
assert_eq!(ne.use_regs().as_slice(), &[0, 7, 12]);
|
||||
|
||||
// SUPER_CALL_SPREAD:无 callee/this 槽
|
||||
let sc = Inst::super_call_spread(Operand::Reg(3), &[4, 0x8000_0000 | 8]);
|
||||
assert_eq!(sc.def_reg(), Some(3));
|
||||
assert_eq!(sc.use_regs().as_slice(), &[4, 8]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn void_defs_rd_no_use() {
|
||||
// VM 的 VOID handler 不读 a 槽
|
||||
@@ -630,6 +667,9 @@ mod tests {
|
||||
assert!(!Inst::call_native(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 0).is_pure(&f));
|
||||
assert!(!Inst::new_expression(Operand::Reg(3), Operand::Reg(0), Operand::Reg(1), 0).is_pure(&f));
|
||||
assert!(!Inst::super_call(Operand::Reg(3), Operand::Reg(1), 0).is_pure(&f));
|
||||
assert!(!Inst::call_spread(Operand::Reg(0), Operand::Reg(1), &[3]).is_pure(&f));
|
||||
assert!(!Inst::new_expression_spread(Operand::Reg(3), Operand::Reg(0), &[2]).is_pure(&f));
|
||||
assert!(!Inst::super_call_spread(Operand::Reg(3), &[2]).is_pure(&f));
|
||||
assert!(!Inst::new(OpCode::CREATE_REGEXP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f));
|
||||
assert!(!Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None).is_pure(&f));
|
||||
assert!(!Inst::new(OpCode::THROW, Operand::Reg(2), Operand::None, Operand::None).is_pure(&f));
|
||||
|
||||
@@ -116,6 +116,26 @@ impl Inst {
|
||||
Self::with_ext(OpCode::SUPER_CALL, result, first_arg, Operand::None, &[nargs as u32])
|
||||
}
|
||||
|
||||
/// spread 实参调用:rd=callee,a=this,b 槽未用。
|
||||
/// ext=[nstatic|(nspread<<8), 有序实参字…];每个实参字:静态实参为寄存器号,
|
||||
/// spread 源为 `0x8000_0000 | 寄存器号`(高位标记),按源码求值序排列。
|
||||
pub fn call_spread(callee: Operand, this: Operand, words: &[u32]) -> Self {
|
||||
let ext = spread_ext(words);
|
||||
Self::with_ext(OpCode::CALL_SPREAD, callee, this, Operand::None, &ext)
|
||||
}
|
||||
|
||||
/// spread 实参 `new`:结果写入 `result`,构造函数为 `constructor`。
|
||||
pub fn new_expression_spread(result: Operand, constructor: Operand, words: &[u32]) -> Self {
|
||||
let ext = spread_ext(words);
|
||||
Self::with_ext(OpCode::NEW_EXPRESSION_SPREAD, result, constructor, Operand::None, &ext)
|
||||
}
|
||||
|
||||
/// 派生类构造中的 `super(...spread)`:结果写入 `result`。
|
||||
pub fn super_call_spread(result: Operand, words: &[u32]) -> Self {
|
||||
let ext = spread_ext(words);
|
||||
Self::with_ext(OpCode::SUPER_CALL_SPREAD, result, Operand::None, Operand::None, &ext)
|
||||
}
|
||||
|
||||
// ── 其他带 ext 字 ──
|
||||
|
||||
/// 定义访问器属性:home 为宿主对象,get/set 为访问器函数寄存器,key_idx 为属性名常量下标。
|
||||
@@ -217,6 +237,17 @@ impl Inst {
|
||||
}
|
||||
}
|
||||
|
||||
/// spread 调用系 ext 构造:首字打包 `nstatic | (nspread << 8)`,后续按源码求值序排列
|
||||
/// 每个实参字(静态实参 = 寄存器号,spread 源 = `0x8000_0000 | 寄存器号`)。
|
||||
fn spread_ext(words: &[u32]) -> SmallVec<[u32; 4]> {
|
||||
let nspread = words.iter().filter(|w| *w >> 31 == 1).count() as u32;
|
||||
let nstatic = (words.len() as u32) - nspread;
|
||||
let mut ext = SmallVec::with_capacity(1 + words.len());
|
||||
ext.push(nstatic | (nspread << 8));
|
||||
ext.extend_from_slice(words);
|
||||
ext
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -282,6 +313,29 @@ mod tests {
|
||||
assert_eq!(rest.ext.as_slice(), &[7]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_call_constructors_carry_header_and_source_regs() {
|
||||
let call = Inst::call_spread(Operand::Reg(0), Operand::Reg(1), &[10, 0x8000_0000 | 300]);
|
||||
assert_eq!(call.op, OpCode::CALL_SPREAD);
|
||||
assert_eq!(call.rd, Operand::Reg(0));
|
||||
assert_eq!(call.a, Operand::Reg(1));
|
||||
assert_eq!(call.b, Operand::None);
|
||||
assert_eq!(call.ext.as_slice(), &[1 | (1 << 8), 10, 0x8000_0000 | 300]);
|
||||
|
||||
let ne = Inst::new_expression_spread(Operand::Reg(3), Operand::Reg(0), &[0x8000_0000 | 7]);
|
||||
assert_eq!(ne.op, OpCode::NEW_EXPRESSION_SPREAD);
|
||||
assert_eq!(ne.rd, Operand::Reg(3));
|
||||
assert_eq!(ne.a, Operand::Reg(0));
|
||||
assert_eq!(ne.ext.as_slice(), &[1 << 8, 0x8000_0000 | 7]);
|
||||
|
||||
let sc = Inst::super_call_spread(Operand::Reg(3), &[1, 0x8000_0000 | 5]);
|
||||
assert_eq!(sc.op, OpCode::SUPER_CALL_SPREAD);
|
||||
assert_eq!(sc.rd, Operand::Reg(3));
|
||||
assert_eq!(sc.a, Operand::None);
|
||||
assert_eq!(sc.b, Operand::None);
|
||||
assert_eq!(sc.ext.as_slice(), &[1 | (1 << 8), 1, 0x8000_0000 | 5]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regalloc_constructors_carry_expected_slots() {
|
||||
let mov = Inst::inst_mov(Operand::Reg(3), Operand::Reg(7));
|
||||
|
||||
@@ -70,6 +70,15 @@ pub(super) fn collect_real_vregs(f: &IRFunction) -> BTreeSet<u32> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// spread 调用系:ext 首字之后每个有序实参字是寄存器号(spread 源带高位标记)
|
||||
if matches!(inst.op, OpCode::CALL_SPREAD | OpCode::NEW_EXPRESSION_SPREAD | OpCode::SUPER_CALL_SPREAD) {
|
||||
for &w in inst.ext.iter().skip(1) {
|
||||
let r = w & 0x7FFF_FFFF;
|
||||
if r != 0 {
|
||||
real.insert(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
real
|
||||
}
|
||||
@@ -116,6 +125,7 @@ pub(super) fn build(
|
||||
}
|
||||
|
||||
// ── 可分配色集 ──
|
||||
// 窗口只为参数连续性 MOV 桥预留;spread 调用实参经 ext 逐个读寄存器,无连续性要求。
|
||||
let max_nargs = f
|
||||
.insts
|
||||
.iter()
|
||||
|
||||
@@ -134,7 +134,8 @@ pub(super) fn run(f: &mut IRFunction, map: &AllocMap) {
|
||||
fn rewrite_inst(
|
||||
f: &IRFunction, inst: &Inst, map: &AllocMap, slot_color: &BTreeMap<u32, u32>, i: usize,
|
||||
) -> (Inst, Vec<(u32, u32)>) {
|
||||
let nargs = inst.ext.first().copied().unwrap_or(0);
|
||||
let raw_nargs = inst.ext.first().copied().unwrap_or(0);
|
||||
let nargs = raw_nargs;
|
||||
let first_arg = match inst.op {
|
||||
OpCode::CALL | OpCode::CALL_NATIVE | OpCode::NEW_EXPRESSION => Some(inst.b),
|
||||
OpCode::SUPER_CALL => Some(inst.a),
|
||||
@@ -197,26 +198,34 @@ fn rewrite_inst(
|
||||
let mut a = rewrite(inst.a);
|
||||
let mut b = rewrite(inst.b);
|
||||
// TEMPLATE_STR 的 ext 编码表达式寄存器(seg>>31==1 时低 8 位为 expr_reg)——必须随
|
||||
// RegAlloc 重映射,否则读旧 vreg 号对应的物理槽(错值)。
|
||||
let ext = if inst.op == OpCode::TEMPLATE_STR {
|
||||
let mut ext = inst.ext.clone();
|
||||
for seg in ext.iter_mut().skip(1) {
|
||||
if *seg >> 31 == 1 {
|
||||
let r = *seg & 0xFF;
|
||||
let nr = if let Some(&c) = slot_color.get(&r) {
|
||||
c
|
||||
} else {
|
||||
match map.map.get(&r) {
|
||||
Some(Alloc::Phys(p)) => *p,
|
||||
_ => r, // 非真实 vreg / spilled 未覆盖(防御保留原号)
|
||||
}
|
||||
};
|
||||
*seg = (*seg & !0xFFu32) | (nr & 0xFF);
|
||||
// RegAlloc 重映射,否则读旧 vreg 号对应的物理槽(错值)。spread 调用系 ext[1..] 每个
|
||||
// 字是完整 spread 源 vreg,同样需重映射到物理号。
|
||||
let ext = match inst.op {
|
||||
OpCode::TEMPLATE_STR => {
|
||||
let mut ext = inst.ext.clone();
|
||||
for seg in ext.iter_mut().skip(1) {
|
||||
if *seg >> 31 == 1 {
|
||||
let r = *seg & 0xFF;
|
||||
let nr = remap_ext_reg(r, slot_color, map);
|
||||
*seg = (*seg & !0xFFu32) | (nr & 0xFF);
|
||||
}
|
||||
}
|
||||
ext
|
||||
}
|
||||
ext
|
||||
} else {
|
||||
inst.ext.clone()
|
||||
OpCode::CALL_SPREAD | OpCode::NEW_EXPRESSION_SPREAD | OpCode::SUPER_CALL_SPREAD => {
|
||||
// 有序实参字:静态字直接重映射,spread 字保留高位标记、低 31 位重映射。
|
||||
let mut ext = inst.ext.clone();
|
||||
for seg in ext.iter_mut().skip(1) {
|
||||
if *seg >> 31 == 1 {
|
||||
let nr = remap_ext_reg(*seg & 0x7FFF_FFFF, slot_color, map);
|
||||
*seg = 0x8000_0000 | nr;
|
||||
} else {
|
||||
*seg = remap_ext_reg(*seg, slot_color, map);
|
||||
}
|
||||
}
|
||||
ext
|
||||
}
|
||||
_ => inst.ext.clone(),
|
||||
};
|
||||
// 调用点首参槽改指 arg_window_base(桥接后)
|
||||
if let Some(ab) = arg_base {
|
||||
@@ -257,6 +266,19 @@ fn rewrite_inst(
|
||||
(Inst { op: inst.op, rd, a, b, ext }, arg_movs)
|
||||
}
|
||||
|
||||
/// ext 内嵌寄存器重映射:优先用本指令点的 fresh 色(spilled vreg 的 use 点 UNSPILL),
|
||||
/// 其次 AllocMap 物理色;未映射(防御)保留原号。
|
||||
fn remap_ext_reg(r: u32, slot_color: &BTreeMap<u32, u32>, map: &AllocMap) -> u32 {
|
||||
if let Some(&c) = slot_color.get(&r) {
|
||||
c
|
||||
} else {
|
||||
match map.map.get(&r) {
|
||||
Some(Alloc::Phys(p)) => *p,
|
||||
_ => r,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// terminator 判定:跳转族 + RETURN/HALT/THROW(def 结果必死,SPILL 跳过)。
|
||||
fn is_terminator(op: OpCode) -> bool {
|
||||
matches!(
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use crate::native::NativeFn;
|
||||
use crate::vm::{native_fn_ptr_to_fn, CallFrame, FrameContinuation, Vm};
|
||||
use crate::{vm_debug, vm_trace};
|
||||
use oxide_builtins::iterator::make_iterator_for_value;
|
||||
use oxide_builtins::{builtins_debug, builtins_trace};
|
||||
use oxide_bytecode::opcode;
|
||||
use oxide_runtime_api::NativeResult;
|
||||
use oxide_runtime_api::{to_boolean, NativeResult};
|
||||
use oxide_types::object::{Cell, JsObject};
|
||||
use oxide_types::value::JsValue;
|
||||
use std::sync::Arc;
|
||||
@@ -130,14 +131,15 @@ impl Vm {
|
||||
// 函数名推断:emit 端在变量声明/对象属性赋值点设置 function_name。
|
||||
let func_obj = unsafe { &mut *result.as_js_object_ptr() };
|
||||
let name_si = self.kernel_core.perm_interner().intern("name").0;
|
||||
let name_val = function_name.as_deref().map(|n| self.new_string(n)).unwrap_or_else(|| self.new_string(""));
|
||||
let name_val = function_name
|
||||
.as_deref()
|
||||
.map(|n| self.new_string(n))
|
||||
.unwrap_or_else(|| self.new_string(""));
|
||||
self.set_or_create_prop_value(func_obj, name_si, name_val);
|
||||
if !upvalue_captures.is_empty() {
|
||||
// 链式捕获(parent_uv_idx):从父闭包(创建者)的 upvalues 取 cell。
|
||||
let parent_upvalues: Vec<*mut Cell> = match self.current_callee() {
|
||||
Some(callee) if callee.is_object() => {
|
||||
unsafe { &*callee.as_js_object_ptr() }.upvalues_slice().to_vec()
|
||||
}
|
||||
Some(callee) if callee.is_object() => unsafe { &*callee.as_js_object_ptr() }.upvalues_slice().to_vec(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
if let Some(current_cells) = self.cell_stack.last_mut() {
|
||||
@@ -156,8 +158,7 @@ impl Vm {
|
||||
if current_cells[cell_idx].is_null() {
|
||||
// 占位 Cell 保持未初始化(TDZ)直到 MAKE_CELL 置位;
|
||||
// 若绑定为 var 则其初始化 MAKE_CELL 在函数序言先于任何读取执行。
|
||||
let cell =
|
||||
self.gc_state.session_epoch.alloc(Cell::new(JsValue::undefined(), false));
|
||||
let cell = self.gc_state.session_epoch.alloc(Cell::new(JsValue::undefined(), false));
|
||||
current_cells[cell_idx] = cell as *mut Cell;
|
||||
}
|
||||
current_cells[cell_idx]
|
||||
@@ -254,7 +255,8 @@ impl Vm {
|
||||
if !cell.is_null() {
|
||||
let c = unsafe { &*cell };
|
||||
if !c.is_initialized() {
|
||||
return self.raise_error_kind("ReferenceError", "Cannot access variable before initialization");
|
||||
return self
|
||||
.raise_error_kind("ReferenceError", "Cannot access variable before initialization");
|
||||
}
|
||||
self.regs[rd] = c.value;
|
||||
return Ok(());
|
||||
@@ -590,3 +592,342 @@ impl Vm {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
/// 解析 spread 调用系 ext:读走 `nstatic | (nspread<<8)` 首字与 `nstatic+nspread`
|
||||
/// 个有序实参字(静态字 = 寄存器号,spread 字高位标记)。
|
||||
fn read_spread_ext(&mut self) -> Vec<usize> {
|
||||
let header = self.bytecode[self.pc];
|
||||
self.pc += 1;
|
||||
let nstatic = (header & 0xFF) as usize;
|
||||
let nspread = (header >> 8) as usize;
|
||||
let mut words = Vec::with_capacity(nstatic + nspread);
|
||||
for _ in 0..nstatic + nspread {
|
||||
words.push(self.bytecode[self.pc] as usize);
|
||||
self.pc += 1;
|
||||
}
|
||||
words
|
||||
}
|
||||
|
||||
/// 按源码求值序物化完整实参 Vec:静态字直接读寄存器,spread 字迭代展开。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 静态实参逐个从寄存器读入;spread 源经 `make_iterator_for_value` 迭代追加。
|
||||
/// 2. 迭代中途抛错 → 当前迭代器经 return() 关闭(IteratorClose),异常恢复为 JS
|
||||
/// 异常后 unwind。
|
||||
/// 3. 展开总数超过 u16 上限(arguments_count 字段)→ RangeError。
|
||||
///
|
||||
/// # 返回值
|
||||
/// - `Ok(Some(vec))` 成功;
|
||||
/// - `Ok(None)` 异常已展开(调用方按 continue 处理);
|
||||
/// - `Err` 引擎错误直接传播。
|
||||
fn materialize_spread_args(&mut self, words: &[usize]) -> Result<Option<Vec<JsValue>>, String> {
|
||||
let mut args = Vec::with_capacity(words.len());
|
||||
let next_si = self.kernel_core.perm_interner().intern("next").0;
|
||||
let done_si = self.kernel_core.perm_interner().intern("done").0;
|
||||
let value_si = self.kernel_core.perm_interner().intern("value").0;
|
||||
for &w in words {
|
||||
if w >> 31 == 1 {
|
||||
let value = self.regs[w & 0x7FFF_FFFF];
|
||||
let iterator = match make_iterator_for_value(self, value) {
|
||||
Ok(it) => it,
|
||||
Err(err) => {
|
||||
self.exception_value = Some(err);
|
||||
self.pending_error_kind = Some(self.thrown_error_kind(err));
|
||||
return self.unwind().map(|_| None);
|
||||
}
|
||||
};
|
||||
loop {
|
||||
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
|
||||
let next_fn = match self.ordinary_get(iter_obj, next_si, iterator) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return self.spread_iter_error(iterator, e, "Error"),
|
||||
};
|
||||
let result = match self.call_function_sync(next_fn, iterator, &[]) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return self.spread_iter_error(iterator, e, "Error"),
|
||||
};
|
||||
if !result.is_object() {
|
||||
return self.spread_iter_error(
|
||||
iterator,
|
||||
self.error_message_text("TypeError", "iterator result is not an object"),
|
||||
"TypeError",
|
||||
);
|
||||
}
|
||||
let result_obj = unsafe { &*result.as_js_object_ptr() };
|
||||
let done = match self.ordinary_get(result_obj, done_si, result) {
|
||||
Ok(v) => to_boolean(v),
|
||||
Err(e) => return self.spread_iter_error(iterator, e, "Error"),
|
||||
};
|
||||
if done {
|
||||
break;
|
||||
}
|
||||
let val = match self.ordinary_get(result_obj, value_si, result) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return self.spread_iter_error(iterator, e, "Error"),
|
||||
};
|
||||
args.push(val);
|
||||
if args.len() > u16::MAX as usize {
|
||||
self.raise_error_kind("RangeError", "Too many arguments in function call")?;
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
args.push(self.regs[w]);
|
||||
if args.len() > u16::MAX as usize {
|
||||
self.raise_error_kind("RangeError", "Too many arguments in function call")?;
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(args))
|
||||
}
|
||||
|
||||
/// spread 迭代中途异常:先关闭当前迭代器(suppress return 自身错误,保留在途异常),
|
||||
/// 再把原异常(native 错误经 last_uncaught_value 恢复)重新抛出并展开。
|
||||
fn spread_iter_error(
|
||||
&mut self, iterator: JsValue, msg: String, fallback_kind: &'static str,
|
||||
) -> Result<Option<Vec<JsValue>>, String> {
|
||||
let exc = self
|
||||
.last_uncaught_value
|
||||
.take()
|
||||
.unwrap_or_else(|| oxide_builtins::error::create_kind_error(self, fallback_kind, &msg));
|
||||
self.close_for_of_iterator(iterator, true)?;
|
||||
self.exception_value = Some(exc);
|
||||
self.pending_error_kind = Some(self.thrown_error_kind(exc));
|
||||
self.unwind().map(|_| None)
|
||||
}
|
||||
|
||||
/// CALL_SPREAD:运行期物化完整实参后按目标分派(native 走值传递,字节码走帧)。
|
||||
pub(crate) fn dispatch_call_spread(&mut self, rd: usize, a: usize, _b: usize) -> Result<bool, String> {
|
||||
let callee = self.regs[rd];
|
||||
let this_value = self.regs[a];
|
||||
let words = self.read_spread_ext();
|
||||
let args = match self.materialize_spread_args(&words)? {
|
||||
Some(args) => args,
|
||||
None => return Ok(true),
|
||||
};
|
||||
if callee.is_object() {
|
||||
let obj_ptr = callee.as_js_object_ptr();
|
||||
if !obj_ptr.is_null() {
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
if obj.is_function() {
|
||||
if obj.is_class_constructor() {
|
||||
return self
|
||||
.raise_type_error("class constructor cannot be invoked without 'new'")
|
||||
.map(|_| true);
|
||||
}
|
||||
if obj.native_fn().is_some() {
|
||||
match self.call_function_sync(callee, this_value, &args) {
|
||||
Ok(v) => {
|
||||
self.regs[0] = v;
|
||||
return Ok(false);
|
||||
}
|
||||
Err(_) => {
|
||||
let exc = self
|
||||
.last_uncaught_value
|
||||
.take()
|
||||
.unwrap_or_else(|| oxide_builtins::error::create_error(self, "call failed"));
|
||||
self.exception_value = Some(exc);
|
||||
self.pending_error_kind = Some(self.thrown_error_kind(exc));
|
||||
return self.unwind().map(|_| true);
|
||||
}
|
||||
}
|
||||
} else if obj.sub_module_index() > 0 {
|
||||
self.push_bytecode_frame(
|
||||
callee,
|
||||
this_value,
|
||||
&args,
|
||||
None,
|
||||
None,
|
||||
JsValue::undefined(),
|
||||
FrameContinuation::None,
|
||||
)?;
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.raise_type_error("CALL target is not callable").map(|_| true)
|
||||
}
|
||||
|
||||
/// NEW_EXPRESSION_SPREAD:物化实参后按构造函数目标分派(native 走值传递,字节码走帧)。
|
||||
pub(crate) fn dispatch_new_expression_spread(&mut self, rd: usize, a: usize, _b: usize) -> Result<bool, String> {
|
||||
let constructor_reg = a;
|
||||
let constructor = self.regs[constructor_reg];
|
||||
|
||||
if !constructor.is_object() {
|
||||
return self
|
||||
.raise_type_error("NEW_EXPRESSION: constructor is not an object")
|
||||
.map(|_| true);
|
||||
}
|
||||
let ctor_ptr = constructor.as_js_object_ptr();
|
||||
if ctor_ptr.is_null() {
|
||||
return self.raise_type_error("NEW_EXPRESSION: constructor is null").map(|_| true);
|
||||
}
|
||||
let ctor_obj = unsafe { &*ctor_ptr };
|
||||
if !ctor_obj.is_function() {
|
||||
return self
|
||||
.raise_type_error("NEW_EXPRESSION: constructor is not a function")
|
||||
.map(|_| true);
|
||||
}
|
||||
if ctor_obj.is_arrow() {
|
||||
return self
|
||||
.raise_type_error("arrow functions cannot be used as constructors")
|
||||
.map(|_| true);
|
||||
}
|
||||
// native 方法(非构造器)不可 new。
|
||||
if ctor_obj.native_fn().is_some() && ctor_obj.type_tag != oxide_types::object::JsObject::OBJ_TYPE_CONSTRUCTOR {
|
||||
return self.raise_type_error("object is not a constructor").map(|_| true);
|
||||
}
|
||||
|
||||
let words = self.read_spread_ext();
|
||||
let args = match self.materialize_spread_args(&words)? {
|
||||
Some(args) => args,
|
||||
None => return Ok(true),
|
||||
};
|
||||
|
||||
let proto_ptr = &*self.object_prototype as *const JsObject as *mut JsObject;
|
||||
let new_obj = self.alloc_object(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
));
|
||||
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
|
||||
if let Some(proto_val) = self.resolve_property(ctor_obj, proto_si) {
|
||||
if proto_val.is_object() {
|
||||
let new_obj_mut = unsafe { &mut *new_obj };
|
||||
let proto_obj_ptr = proto_val.as_js_object_ptr();
|
||||
let _ = new_obj_mut.set_proto(JsValue::from_js_object(proto_obj_ptr));
|
||||
}
|
||||
}
|
||||
let new_obj_val = JsValue::object(new_obj as *mut u8);
|
||||
|
||||
if ctor_obj.native_fn().is_some() {
|
||||
// native 构造器:receiver 为新对象,值传递调用。
|
||||
match self.call_function_sync(constructor, new_obj_val, &args) {
|
||||
Ok(v) => {
|
||||
self.regs[rd] = if v.is_object() { v } else { new_obj_val };
|
||||
Ok(false)
|
||||
}
|
||||
Err(_) => {
|
||||
let exc = self
|
||||
.last_uncaught_value
|
||||
.take()
|
||||
.unwrap_or_else(|| oxide_builtins::error::create_error(self, "constructor call failed"));
|
||||
self.exception_value = Some(exc);
|
||||
self.pending_error_kind = Some(self.thrown_error_kind(exc));
|
||||
self.unwind().map(|_| true)
|
||||
}
|
||||
}
|
||||
} else if ctor_obj.sub_module_index() > 0 {
|
||||
if self.frames.len() >= self.kernel_core.config.max_call_depth {
|
||||
return Err(self.error_message_text("RangeError", "Maximum call stack size exceeded"));
|
||||
}
|
||||
let this_value = if ctor_obj.is_derived_constructor() {
|
||||
JsValue::undefined()
|
||||
} else {
|
||||
new_obj_val
|
||||
};
|
||||
self.push_bytecode_frame(
|
||||
constructor,
|
||||
this_value,
|
||||
&args,
|
||||
Some(rd as u8),
|
||||
Some(new_obj_val),
|
||||
constructor,
|
||||
FrameContinuation::None,
|
||||
)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
let error =
|
||||
oxide_builtins::error::create_error(self, "NEW_EXPRESSION: bytecode constructors not yet supported");
|
||||
self.exception_value = Some(error);
|
||||
self.pending_error_kind = Some(self.thrown_error_kind(error));
|
||||
self.unwind().map(|_| true)
|
||||
}
|
||||
}
|
||||
|
||||
/// SUPER_CALL_SPREAD:物化实参后按父构造器目标分派(native 走值传递,字节码走帧)。
|
||||
pub(crate) fn dispatch_super_call_spread(&mut self, rd: usize, _a: usize) -> Result<bool, String> {
|
||||
let words = self.read_spread_ext();
|
||||
|
||||
let Some(frame) = self.frames.last() else {
|
||||
self.raise_error_kind("ReferenceError", "super() used outside class constructor")?;
|
||||
return Ok(true);
|
||||
};
|
||||
if !frame.is_derived_constructor {
|
||||
self.raise_error_kind("ReferenceError", "super() used outside derived constructor")?;
|
||||
return Ok(true);
|
||||
}
|
||||
if !self.regs[254].is_undefined() {
|
||||
self.raise_error_kind("ReferenceError", "super() called more than once")?;
|
||||
return Ok(true);
|
||||
}
|
||||
let Some(derived_this) = frame.constructed_this else {
|
||||
self.raise_error_kind("ReferenceError", "super() without derived this")?;
|
||||
return Ok(true);
|
||||
};
|
||||
let new_target = self.regs[255];
|
||||
if !new_target.is_object() {
|
||||
self.raise_error_kind("TypeError", "super() new.target is not an object")?;
|
||||
return Ok(true);
|
||||
}
|
||||
let new_target_obj = unsafe { &*new_target.as_js_object_ptr() };
|
||||
let super_ctor = new_target_obj.proto();
|
||||
if !super_ctor.is_object() {
|
||||
self.raise_error_kind("TypeError", "super constructor is not an object")?;
|
||||
return Ok(true);
|
||||
}
|
||||
let super_obj = unsafe { &*super_ctor.as_js_object_ptr() };
|
||||
if !super_obj.is_function() {
|
||||
self.raise_error_kind("TypeError", "super constructor is not a function")?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let args = match self.materialize_spread_args(&words)? {
|
||||
Some(args) => args,
|
||||
None => return Ok(true),
|
||||
};
|
||||
|
||||
if super_obj.native_fn().is_some() {
|
||||
match self.call_function_sync(super_ctor, derived_this, &args) {
|
||||
Ok(val) => {
|
||||
// super() 返回实例的 [[Prototype]] 须设为 new.target.prototype
|
||||
let instance = if val.is_object() { val } else { derived_this };
|
||||
if instance.is_object() {
|
||||
self.set_constructed_proto(instance, new_target_obj)?;
|
||||
}
|
||||
self.regs[254] = instance;
|
||||
self.regs[rd] = instance;
|
||||
}
|
||||
Err(_) => {
|
||||
let exc = self
|
||||
.last_uncaught_value
|
||||
.take()
|
||||
.unwrap_or_else(|| oxide_builtins::error::create_error(self, "super() call failed"));
|
||||
self.exception_value = Some(exc);
|
||||
self.pending_error_kind = Some(self.thrown_error_kind(exc));
|
||||
self.unwind().map(|_| true)?;
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
} else if super_obj.sub_module_index() > 0 {
|
||||
if self.frames.len() >= self.kernel_core.config.max_call_depth {
|
||||
return Err(self.error_message_text("RangeError", "Maximum call stack size exceeded"));
|
||||
}
|
||||
self.push_bytecode_frame(
|
||||
super_ctor,
|
||||
derived_this,
|
||||
&args,
|
||||
Some(254),
|
||||
Some(derived_this),
|
||||
new_target,
|
||||
FrameContinuation::None,
|
||||
)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
self.raise_error_kind("TypeError", "super constructor is not callable")?;
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1110,18 +1110,36 @@ impl Vm {
|
||||
self.dispatch_call_native(rd, a, b)?;
|
||||
}
|
||||
|
||||
OpCode::CALL_SPREAD => match self.dispatch_call_spread(rd, a, b) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::NEW_EXPRESSION => match self.dispatch_new_expression(rd, a, b) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::NEW_EXPRESSION_SPREAD => match self.dispatch_new_expression_spread(rd, a, b) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::SUPER_CALL => match self.dispatch_super_call(rd, a) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::SUPER_CALL_SPREAD => match self.dispatch_super_call_spread(rd, a) {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => {}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
|
||||
OpCode::SUPER_GET_PROP | OpCode::SUPER_STATIC_GET_PROP => {
|
||||
match self.dispatch_super_get_prop(rd, a, b) {
|
||||
Ok(true) => continue,
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
//! 调用实参 spread 执行语义测试:`f(...args)`、`new F(...args)`、`super(...args)`。
|
||||
//! 覆盖静态/混合实参顺序、迭代器协议、IteratorClose、native 目标、求值顺序。
|
||||
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
fn to_str(vm: &Vm, val: JsValue) -> String {
|
||||
if val.is_string() {
|
||||
vm.lookup_str(val).unwrap_or_default()
|
||||
} else {
|
||||
format!("{}", val)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_num(val: JsValue) -> f64 {
|
||||
if val.is_int() {
|
||||
val.as_int() as f64
|
||||
} else {
|
||||
val.as_double()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_single_literal_preserves_all_args() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "function f(){return arguments.length}; f(...[1,2,3])").unwrap();
|
||||
assert_eq!(result.as_int(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_interleaved_static_and_spread_keeps_source_order() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "function f(a,b,c){return [a,b,c].join('-')}; f('x', ...['y'], 'z')").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "x-y-z");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_multiple_sources_append_in_order() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "function f(){return arguments.length}; f(...[1], 2, ...[3,4])").unwrap();
|
||||
assert_eq!(result.as_int(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_native_target() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Math.max(...[1,5,3])").unwrap();
|
||||
assert_eq!(to_num(result), 5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_new_expression_bytecode_ctor() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "new (function(a,b){this.sum=a+b})(...[2,3]).sum").unwrap();
|
||||
assert_eq!(to_num(result), 5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_new_expression_native_ctor() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "new Date(...[2020,1,1]).getFullYear()").unwrap();
|
||||
assert_eq!(to_num(result), 2020.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_super_call_passes_args() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"class A { constructor(a,b,c){ this.v=[a,b,c].join('') } }; \
|
||||
class B extends A { constructor(){ super(...[1,2,3]) } }; new B().v",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(to_str(&vm, result), "123");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_non_iterable_throws_type_error() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "try { (function(){})(...null); 'no-throw' } catch(e) { e.constructor.name }").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "TypeError");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_invokes_iterator_close_on_abrupt_completion() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var closed=0; try { (function(){}) (...{[Symbol.iterator]:function(){ return { \
|
||||
next:function(){ throw new Error('boom') }, \
|
||||
return:function(){ closed=1; return {} } } }}) } catch(e) {}; closed",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_does_not_close_iterator_on_normal_completion() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var closed=0; var i=0; (function(){}) (...{[Symbol.iterator]:function(){ return { \
|
||||
next:function(){ return ++i<=2 ? {done:false,value:i} : {done:true} }, \
|
||||
return:function(){ closed=1; return {} } } }}); closed",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result.as_int(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_closure_capture_survives() {
|
||||
let mut vm = Vm::new();
|
||||
let result =
|
||||
eval(&mut vm, "function outer(){var x=42; function g(a){return a+x}; return g(...[x])} outer()").unwrap();
|
||||
assert_eq!(to_num(result), 84.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_string_source() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "function f(){return arguments.length}; f(...'ab')").unwrap();
|
||||
assert_eq!(result.as_int(), 2);
|
||||
}
|
||||
Reference in New Issue
Block a user