Locked in type model
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
//! Calls of fns, natives and proto links
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'a> Emitter<'a> {
|
||||
/// A call from `place`. The callee's cell tells which code runs, and the
|
||||
/// callee is its own env. A callee that is not a fn throws
|
||||
pub(super) fn call(&mut self, res: &InferResult, place: Place, instr: &RegInstr, out: &mut String) {
|
||||
let arg_types: Vec<Type> = instr.args.iter().map(|&a| res.val_types[a].clone()).collect();
|
||||
let operands: Vec<(String, Rep)> = (instr.args.iter().zip(&arg_types))
|
||||
.map(|(a, ty)| (format!("v{a}"), self.rep(ty)))
|
||||
.collect();
|
||||
let dst = (instr.dsts[0], self.rep(&res.val_types[instr.dsts[0]]));
|
||||
|
||||
self.dispatch(&operands[0].0, &arg_types[0], "jsrt_type_error();", out, |e, member, callee, rep| {
|
||||
let Type::Obj(cell) = member else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let mut arg_types = arg_types.clone();
|
||||
let mut operands = operands.clone();
|
||||
arg_types[0] = member.clone();
|
||||
operands[0] = (callee.into(), rep);
|
||||
|
||||
let (call, ret) = e.call_code(cell.code()?, place, &arg_types, &operands);
|
||||
Some(e.call_result(instr.op, &call, &ret, &operands[THIS_ARG as usize], dst))
|
||||
});
|
||||
}
|
||||
|
||||
/// The statement that runs `call`, which gives a value of type `ret`, and
|
||||
/// puts the call's result in `dst` (its value, and its rep). `new` gives
|
||||
/// what the constructor returns if that is an object, else the new object
|
||||
/// itself, `this` (see infer)
|
||||
pub(super) fn call_result(&self, op: OpCode, call: &str, ret: &Type, this: &(String, Rep), dst: (usize, Rep)) -> String {
|
||||
let (dst, dst_rep) = (format!("v{}", dst.0), dst.1);
|
||||
let ret_rep = self.rep(ret);
|
||||
let members = ret.members();
|
||||
let objects = members.iter().filter(|m| matches!(m, Type::Obj(_))).count();
|
||||
let this = self.conv(&this.0, this.1, dst_rep);
|
||||
|
||||
match op {
|
||||
_ if *ret == Type::Unknown => format!("{call};"),
|
||||
OpCode::CallConstructor if objects == 0 => format!("{call}; {dst} = {this};"),
|
||||
OpCode::CallConstructor if objects < members.len() => {
|
||||
let is_object = match self.tagged("r", ret_rep) {
|
||||
Some(a) => format!("__any_is_object({a})"),
|
||||
None => format!("!{}", self.nullish("r", ret)),
|
||||
};
|
||||
let value = self.conv("r", ret_rep, dst_rep);
|
||||
format!("{{ {} r = {call}; {dst} = {is_object} ? {value} : {this}; }}", self.c_type(ret_rep))
|
||||
}
|
||||
_ => format!("{dst} = {};", self.conv(call, ret_rep, dst_rep)),
|
||||
}
|
||||
}
|
||||
|
||||
/// The C call of `code` with these operands (the callee, `this`, then the
|
||||
/// args, each with its rep) whose types are `arg_types`, and the type it
|
||||
/// returns
|
||||
pub(super) fn call_code(&mut self, code: Code, place: Place, arg_types: &[Type], operands: &[(String, Rep)]) -> (String, Type) {
|
||||
// An operand as a value of rep `to`. One that the call does not pass is
|
||||
// undefined
|
||||
let operand = |e: &Self, i: usize, to: Rep| match operands.get(i) {
|
||||
Some((value, rep)) => e.conv(value, *rep, to),
|
||||
None => e.conv("(undefined_t){0}", Rep::Undefined, to),
|
||||
};
|
||||
|
||||
match code {
|
||||
Code::Fn(fn_id) => {
|
||||
let w = self.cache.find(fn_id, place, arg_types, self.fns);
|
||||
self.use_walk(w);
|
||||
|
||||
let walk = &self.cache.walks[w];
|
||||
let args: Vec<String> = (walk.req.arg_types.iter().enumerate())
|
||||
.map(|(i, param)| operand(self, i, self.rep(param)))
|
||||
.collect();
|
||||
|
||||
let ret = walk.result.as_ref().unwrap().ret_type.clone();
|
||||
(format!("{}({})", self.fn_name(w), args.join(", ")), ret)
|
||||
}
|
||||
|
||||
// A native whose args cannot be of its params' kinds throws
|
||||
Code::Native(id) if native::ret_type(id, &arg_types[FIRST_ARG as usize..]) == Type::Unknown => {
|
||||
("jsrt_type_error()".into(), Type::Unknown)
|
||||
}
|
||||
Code::Native(id) => {
|
||||
let native = &NATIVES[id];
|
||||
|
||||
let args: Vec<String> = (native.args.iter().enumerate())
|
||||
.map(|(i, param)| operand(self, FIRST_ARG as usize + i, self.rep(param)))
|
||||
.collect();
|
||||
|
||||
(format!("{}({})", native.name, args.join(", ")), native.ret.clone())
|
||||
}
|
||||
|
||||
// Keeps the prototype where field reads on primitives find it
|
||||
Code::ProtoLink(kind) => {
|
||||
let value = operand(self, FIRST_ARG as usize, self.rep(&self.cache.protos[kind as usize]));
|
||||
(format!("({} = {value}, (undefined_t){{0}})", proto_global(kind)), Type::Undefined)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user