Mid rewrite
This commit is contained in:
@@ -1,231 +0,0 @@
|
||||
//! Field reads and writes, array elements, and prototype chains
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'a> Emitter<'a> {
|
||||
/// `obj.field = value`. A primitive has no fields to write, and undefined
|
||||
/// and null throw
|
||||
pub(super) fn write_field(&mut self, res: &InferResult, instr: &RegInstr, out: &mut String) {
|
||||
let (value, obj) = (instr.args[0], instr.args[1]);
|
||||
let value_rep = self.rep(&res.val_types[value]);
|
||||
let field = instr.imm.atom();
|
||||
let value = format!("v{value}");
|
||||
|
||||
self.dispatch(&format!("v{obj}"), &res.val_types[obj], "jsrt_type_error();", out, |e, member, obj, _| {
|
||||
match member {
|
||||
Type::Obj(cell) if field == LENGTH_FIELD && cell.is_array() => {
|
||||
let name = struct_name(e.num(cell));
|
||||
Some(format!("set_length_{name}({obj}, {});", e.conv(&value, value_rep, Rep::Int)))
|
||||
}
|
||||
Type::Obj(cell) => {
|
||||
let slot = e.slot_rep(&cell.field(field).unwrap().ty, cell.can_be_absent(field));
|
||||
Some(format!("{obj}->{} = {};", e.field_name(field), e.conv(&value, value_rep, slot)))
|
||||
}
|
||||
Type::Undefined | Type::Null => None,
|
||||
_ => Some(";".into()),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// `dst = obj.field`: on an object, the read through its prototype chain;
|
||||
/// on a primitive, the read on its kind's prototype, except that a string's
|
||||
/// `length` is its own. Undefined and null throw
|
||||
pub(super) fn read_field(&mut self, res: &InferResult, instr: &RegInstr, out: &mut String) {
|
||||
let (obj, dst) = (instr.args[0], instr.dsts[0]);
|
||||
let dst_rep = self.rep(&res.val_types[dst]);
|
||||
|
||||
let field = match instr.op {
|
||||
OpCode::GetLength => LENGTH_FIELD,
|
||||
_ => instr.imm.atom(),
|
||||
};
|
||||
|
||||
self.dispatch(&format!("v{obj}"), &res.val_types[obj], "jsrt_type_error();", out, |e, member, obj, _| {
|
||||
let (read, rep) = match member {
|
||||
Type::Obj(cell) => e.read_on(cell, field, obj),
|
||||
_ => e.read_primitive(member, obj, field)?,
|
||||
};
|
||||
|
||||
Some(format!("v{dst} = {};", e.conv(&read, rep, dst_rep)))
|
||||
});
|
||||
}
|
||||
|
||||
/// `dst = a[i]` or `a[i] = value`, on an array, and `dst = s[i]` on a
|
||||
/// string. A string cannot be written, and undefined and null throw
|
||||
pub(super) fn element(&mut self, res: &InferResult, instr: &RegInstr, out: &mut String) {
|
||||
let put = instr.op == OpCode::PutArrayEl;
|
||||
let (index, obj) = (instr.args[put as usize], instr.args[put as usize + 1]);
|
||||
let index = self.conv(&format!("v{index}"), self.rep(&res.val_types[index]), Rep::Int);
|
||||
|
||||
// A put has the value, a get the dst
|
||||
let (v, v_rep) = match put {
|
||||
true => (instr.args[0], self.rep(&res.val_types[instr.args[0]])),
|
||||
false => (instr.dsts[0], self.rep(&res.val_types[instr.dsts[0]])),
|
||||
};
|
||||
|
||||
self.dispatch(&format!("v{obj}"), &res.val_types[obj], "jsrt_type_error();", out, |e, member, obj, _| {
|
||||
let id = match member {
|
||||
Type::Obj(cell) => e.num(cell),
|
||||
Type::Str(_) if !put => {
|
||||
let c = format!("jsrt_string_at({obj}, {index})");
|
||||
return Some(format!("v{v} = {};", e.conv(&c, Rep::OptStr, v_rep)));
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let (name, elem) = (struct_name(id), e.elem_rep(id));
|
||||
|
||||
Some(match put {
|
||||
true => format!("set_elem_{name}({obj}, {index}, {});", e.conv(&format!("v{v}"), v_rep, elem)),
|
||||
false => format!("v{v} = {};", e.conv(&format!("elem_{name}({obj}, {index})"), elem, v_rep)),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// A read of `field` on the object of `cell` at `ptr`, and its rep. An own
|
||||
/// field that is never absent is read in place. Any other read goes
|
||||
/// through the prototype chain
|
||||
pub(super) fn read_on(&mut self, cell: &Cell, field: AtomRef, ptr: &str) -> (String, Rep) {
|
||||
if field == LENGTH_FIELD && cell.is_array() {
|
||||
return (format!("{ptr}->length"), Rep::Int);
|
||||
}
|
||||
|
||||
match cell.field(field) {
|
||||
Some(f) if !cell.can_be_absent(field) => (format!("{ptr}->{}", self.field_name(field)), self.rep(&f.ty)),
|
||||
_ => {
|
||||
let (reader, rep) = self.field_reader(cell, field);
|
||||
(format!("{reader}({ptr})"), rep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A read of `field` on `e`, a primitive of type `ty`, and its rep: a
|
||||
/// string's length, or a read on its kind's prototype. `None` if it throws
|
||||
pub(super) fn read_primitive(&mut self, ty: &Type, e: &str, field: AtomRef) -> Option<(String, Rep)> {
|
||||
let kind = ProtoOf::of(ty)?;
|
||||
|
||||
if kind == ProtoOf::String && field == LENGTH_FIELD {
|
||||
return Some((format!("jsrt_string_length({e})"), Rep::Int));
|
||||
}
|
||||
|
||||
Some(self.read_proto(kind, field))
|
||||
}
|
||||
|
||||
/// A read of `field` on the prototype that the stdlib links for `kind`,
|
||||
/// and its rep
|
||||
pub(super) fn read_proto(&mut self, kind: ProtoOf, field: AtomRef) -> (String, Rep) {
|
||||
match &self.cache.protos[kind as usize] {
|
||||
Type::Unknown => ("(undefined_t){0}".into(), Rep::Undefined),
|
||||
Type::Obj(cell) => self.read_on(&cell.clone(), field, &proto_global(kind)),
|
||||
_ => self.proto_reader(kind, field),
|
||||
}
|
||||
}
|
||||
|
||||
/// The helper that reads `field` on the prototype that the stdlib links
|
||||
/// for `kind`, where the prototype's type is a sum, and the rep it gives.
|
||||
/// It reads on each member of the type
|
||||
pub(super) fn proto_reader(&mut self, kind: ProtoOf, field: AtomRef) -> (String, Rep) {
|
||||
let name = format!("get_{}_{}", proto_global(kind), self.field_name(field));
|
||||
let proto = &self.cache.protos[kind as usize];
|
||||
let ret = self.rep(&read_value(proto, field, &self.cache.protos));
|
||||
|
||||
if !self.helpers.insert(name.clone()) {
|
||||
return (format!("{name}()"), ret);
|
||||
}
|
||||
|
||||
let mut body = String::new();
|
||||
|
||||
self.dispatch(&proto_global(kind), proto, "jsrt_type_error();", &mut body, |e, member, proto, _| {
|
||||
let Type::Obj(cell) = member else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let (read, rep) = e.read_on(cell, field, proto);
|
||||
Some(format!("return {};", e.conv(&read, rep, ret)))
|
||||
});
|
||||
|
||||
self.helper(format!("static {} {name}(void)", self.c_type(ret)), body);
|
||||
(format!("{name}()"), ret)
|
||||
}
|
||||
|
||||
/// The helper that reads `field` on an object of `cell`, and the rep it
|
||||
/// gives. It reads the own field, and where that can be absent, goes on to
|
||||
/// the prototype. At the end of the chain, the field is undefined
|
||||
pub(super) fn field_reader(&mut self, cell: &Cell, field: AtomRef) -> (String, Rep) {
|
||||
let name = format!("get_{}_{}", struct_name(self.num(cell)), self.field_name(field));
|
||||
let read = cell.read(field, &self.cache.protos);
|
||||
let ret = self.rep(&read);
|
||||
|
||||
if !self.helpers.insert(name.clone()) {
|
||||
return (name, ret);
|
||||
}
|
||||
|
||||
let own = cell.field(field);
|
||||
let absent = cell.can_be_absent(field);
|
||||
let mut body = String::new();
|
||||
|
||||
if let Some(own) = &own {
|
||||
let slot = self.slot_rep(&own.ty, absent);
|
||||
let member = format!("o->{}", self.field_name(field));
|
||||
let value = self.conv(&member, slot, ret);
|
||||
|
||||
match absent {
|
||||
false => {
|
||||
let _ = writeln!(body, " return {value};");
|
||||
}
|
||||
true => {
|
||||
let _ = writeln!(body, " if ({}) return {value};", self.present(&member, slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// An array's prototype is the one that the stdlib links for arrays
|
||||
if absent && cell.is_array() {
|
||||
let (read, rep) = self.read_proto(ProtoOf::Array, field);
|
||||
let _ = writeln!(body, " return {};", self.conv(&read, rep, ret));
|
||||
} else if absent {
|
||||
let proto_ty = cell.field(PROTO_FIELD).map_or(Type::Unknown, |f| f.ty);
|
||||
let proto_absent = cell.can_be_absent(PROTO_FIELD);
|
||||
let slot = self.slot_rep(&proto_ty, proto_absent);
|
||||
let member = format!("o->{}", self.field_name(PROTO_FIELD));
|
||||
let mut chain = String::new();
|
||||
|
||||
// On a prototype that is not an object, the chain ends
|
||||
self.dispatch(&member, &proto_ty, "{}", &mut chain, |e, member, proto, _| {
|
||||
let Type::Obj(proto_cell) = member else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let (reader, rep) = e.field_reader(proto_cell, field);
|
||||
Some(format!("return {};", e.conv(&format!("{reader}({proto})"), rep, ret)))
|
||||
});
|
||||
|
||||
match (&proto_ty, proto_absent) {
|
||||
(Type::Unknown, _) => {}
|
||||
(_, true) => {
|
||||
let _ = writeln!(body, " if ({}) {{\n{chain} }}", self.present(&member, slot));
|
||||
}
|
||||
(_, false) => body += &chain,
|
||||
}
|
||||
|
||||
// The end of the chain. If the read cannot give undefined, the chain
|
||||
// always finds the field before it
|
||||
match read.members().contains(&Type::Undefined) {
|
||||
true => {
|
||||
let undefined = self.conv("(undefined_t){0}", Rep::Undefined, ret);
|
||||
let _ = writeln!(body, " return {undefined};");
|
||||
}
|
||||
false => body += " abort();\n",
|
||||
}
|
||||
}
|
||||
|
||||
// A field that is defined, but that no value reaches: the read cannot
|
||||
// run
|
||||
if own.is_none() && !absent {
|
||||
let _ = writeln!(body, " abort();");
|
||||
}
|
||||
|
||||
let sig = format!("static {} {name}({} *o)", self.c_type(ret), struct_name(self.num(cell)));
|
||||
self.helper(sig, body);
|
||||
(name, ret)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user