New macro based emit
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
//! The C of calls
|
||||
//!
|
||||
//! A call's operands are the callee's params: the callee itself, `this`, then the args (see
|
||||
//! regify). Each closure that the callee can be is a case of its own (see `Program::split`),
|
||||
//! and a callee that is not a function throws.
|
||||
|
||||
use super::classes::{ClassKind, Member};
|
||||
use super::js::{JsFunction, UNDEFINED, indent};
|
||||
use super::program::throw;
|
||||
use crate::{
|
||||
infer::{InstrRef, Type},
|
||||
libqjs::parse::opcodes::OpCode,
|
||||
native::NATIVES,
|
||||
regify::{Operand, RegInstr},
|
||||
transform::{FIRST_ARG, THIS_ARG},
|
||||
};
|
||||
|
||||
impl JsFunction<'_> {
|
||||
pub(super) fn call(&self, idx: usize, instr: &RegInstr) -> String {
|
||||
self.split(instr.args[0], |callee, member| {
|
||||
match (member, self.program.class_kind(member)) {
|
||||
(Member::Native(native), _) => self.native_call(native, instr),
|
||||
(Member::Obj(class), ClassKind::Closure(_)) => {
|
||||
self.closure_call(idx, instr, callee, class)
|
||||
}
|
||||
_ => throw("not a function"),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// A call of a closure of `class`, which is `callee`. Infer walked the closure's JS
|
||||
/// function for the types of this call's args, and that walk is the C function to call.
|
||||
/// An arg that the call does not pass is undefined. `new` gives what the constructor
|
||||
/// returns if that is an object, else the new object itself, which is `this`
|
||||
fn closure_call(&self, idx: usize, instr: &RegInstr, callee: &str, class: usize) -> String {
|
||||
let program = self.program;
|
||||
let ClassKind::Closure(fn_idx) = program.classes.classes[class].kind else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let mut types: Vec<Type> = instr.args.iter().map(|&slot| self.ty(slot)).collect();
|
||||
types[0] = Type::Ref(program.classes.classes[class].objs[0]);
|
||||
let call_site = Some(InstrRef {
|
||||
fn_idx: self.context.fn_idx,
|
||||
instr_idx: idx,
|
||||
});
|
||||
let walk = program.cache.find(fn_idx, call_site, &types, program.fns);
|
||||
|
||||
let args: Vec<String> = (0..program.fns[fn_idx].arg_count)
|
||||
.map(|i| {
|
||||
let param = program.slot_ctype(walk, Operand::Arg(i)).c_name();
|
||||
let arg = match instr.args.get(i) {
|
||||
_ if i == 0 => callee.to_string(),
|
||||
Some(&slot) => self.get(slot),
|
||||
None => UNDEFINED.into(),
|
||||
};
|
||||
|
||||
format!("CAST({param}, {arg})")
|
||||
})
|
||||
.collect();
|
||||
|
||||
let call = format!("js{walk}({})", args.join(", "));
|
||||
let ret = program.cache.ret(walk);
|
||||
let dst = instr.dsts[0];
|
||||
|
||||
// A function that never returns gives no value
|
||||
if *ret == Type::Unknown {
|
||||
return format!("{call};\nabort(); // does not return\n");
|
||||
}
|
||||
|
||||
if instr.op != OpCode::CallConstructor {
|
||||
return self.set(dst, &call);
|
||||
}
|
||||
|
||||
let this = self.get(instr.args[THIS_ARG as usize]);
|
||||
let result = program.split("result", ret, |result, member| match member {
|
||||
Member::Obj(_) => self.set(dst, result),
|
||||
_ => self.set(dst, &this),
|
||||
});
|
||||
|
||||
let ctype = program.ret_ctype(walk).c_name();
|
||||
format!("{{\n {ctype} result = {call};\n{}}}\n", indent(&result))
|
||||
}
|
||||
|
||||
/// A call of a native: a C call, with only the args. Each arg is converted to its
|
||||
/// param's C type, and an arg of the wrong kind throws (see `ARG_INT`). An arg that
|
||||
/// the call does not pass is undefined
|
||||
fn native_call(&self, native: usize, instr: &RegInstr) -> String {
|
||||
let native = &NATIVES[native];
|
||||
|
||||
let args: Vec<String> = (native.arg_types.iter().enumerate())
|
||||
.map(|(i, param)| {
|
||||
let arg = instr.args.get(FIRST_ARG as usize + i);
|
||||
let arg = arg.map_or(UNDEFINED.into(), |&slot| self.get(slot));
|
||||
|
||||
match param {
|
||||
Type::Int => format!("ARG_INT({arg})"),
|
||||
Type::Float => format!("ARG_NUMBER({arg})"),
|
||||
Type::Str(_) => format!("ARG_STRING({arg})"),
|
||||
_ => format!("ARG_UNDEFINED({arg})"),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.set(
|
||||
instr.dsts[0],
|
||||
&format!("{}({})", native.name, args.join(", ")),
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user