feat: 调用实参 spread(f(...args) / new F(...args) / super(...args))
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user