235 lines
9.6 KiB
Rust
235 lines
9.6 KiB
Rust
//! How each type looks in C: its representation, the C type of that,
|
|
//! the struct of each class of cells, and the tagged union of each sum
|
|
|
|
use super::*;
|
|
|
|
/// A C representation
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub(super) enum Rep {
|
|
Undefined,
|
|
Null,
|
|
Bool,
|
|
Int,
|
|
Double,
|
|
Str, // a string, or in a field, the sentinel for absent
|
|
Obj(CellNum), // a pointer to an object of the cell, or in a field, the sentinel for absent
|
|
OptStr, // a sum of a string and null or undefined: `NULL` is null, and `JSRT_UNDEFINED` is undefined
|
|
OptObj(CellNum), // a sum of the objects of one cell and null or undefined: a pointer, with the same sentinels
|
|
Objs, // a sum of the objects of several cells, maybe with null or undefined: a pointer to the header
|
|
Sum(Kinds), // any other sum: a tagged union of its kinds (see `sum_defs()`)
|
|
Any, // any value: an `any_t`. The `Any` type, and the args of the runtime's ops on sums
|
|
}
|
|
|
|
/// A set of kinds of value, one bit for each `jsrt_kind` (see `lib/rt/any.h`)
|
|
pub(super) type Kinds = u8;
|
|
|
|
pub(super) const ABSENT: Kinds = 1 << 0;
|
|
|
|
/// The `jsrt_kind` of each kind, and its member in a tagged union, if it has a
|
|
/// value besides its kind
|
|
pub(super) const KINDS: [(&str, Option<(&str, &str)>); 8] = [
|
|
("ABSENT", None),
|
|
("UNDEFINED", None),
|
|
("NULL", None),
|
|
("BOOL", Some(("bool", "b"))),
|
|
("INT", Some(("int32_t", "i"))),
|
|
("FLOAT", Some(("double", "f"))),
|
|
("STRING", Some(("string_t", "s"))),
|
|
("OBJECT", Some(("void *", "o"))),
|
|
];
|
|
|
|
/// The kinds of the values of type `ty`
|
|
pub(super) fn kinds(ty: &Type) -> Kinds {
|
|
ty.members().iter().fold(0, |all, member| {
|
|
all | 1 << match member {
|
|
Type::Undefined => 1,
|
|
Type::Null => 2,
|
|
Type::Bool(_) => 3,
|
|
Type::Int => 4,
|
|
Type::Float => 5,
|
|
Type::Str(_) => 6,
|
|
_ => 7,
|
|
}
|
|
})
|
|
}
|
|
|
|
/// The C of the tagged union of each set of kinds in `sums`, and its
|
|
/// conversions to and from `any_t`. Every member of a union is at its start,
|
|
/// so a conversion copies the payload's bytes. A union has one kind of number
|
|
/// (see `join()`): the other converts. A kind that the union lacks is a
|
|
/// `TypeError`
|
|
pub(super) fn sum_defs(sums: &BTreeSet<Kinds>) -> String {
|
|
let mut out = String::new();
|
|
|
|
for &k in sums {
|
|
let has = |i: usize| k & 1 << i != 0;
|
|
let union: String = (0..8).filter(|&i| has(i)).filter_map(|i| KINDS[i].1).map(|(t, m)| format!(" {t} {m};")).collect();
|
|
let copy = |to: &str, from: &str| match union.is_empty() {
|
|
true => String::new(),
|
|
false => format!(" __builtin_memcpy(&{to}.as, &{from}.as, sizeof {to}.as);"),
|
|
};
|
|
let number = match (has(4), has(5)) {
|
|
(false, true) => " if (a.kind == JSRT_KIND_INT) { v.kind = JSRT_KIND_FLOAT; v.as.f = a.as.i; return v; }",
|
|
(true, false) => " if (a.kind == JSRT_KIND_FLOAT) { v.kind = JSRT_KIND_INT; v.as.i = (int32_t)a.as.f; return v; }",
|
|
_ => "",
|
|
};
|
|
let union = if union.is_empty() { String::new() } else { format!(" union {{{union} }} as;") };
|
|
|
|
let _ = writeln!(out, "typedef struct {{ jsrt_kind kind;{union} }} sum_{k};");
|
|
let _ = writeln!(out, "static inline any_t sum_{k}_any(sum_{k} v) {{ any_t a = {{ .kind = v.kind }};{} return a; }}", copy("a", "v"));
|
|
let _ = writeln!(
|
|
out,
|
|
"static inline sum_{k} any_sum_{k}(any_t a) {{ sum_{k} v = {{ .kind = a.kind }};{number} if (!({k}u >> a.kind & 1)) jsrt_type_error();{} return v; }}",
|
|
copy("v", "a")
|
|
);
|
|
}
|
|
|
|
out
|
|
}
|
|
|
|
impl<'a> Emitter<'a> {
|
|
/// The representation of a value of type `ty`
|
|
pub(super) fn rep(&self, ty: &Type) -> Rep {
|
|
match ty {
|
|
Type::Unknown | Type::Undefined => Rep::Undefined,
|
|
Type::Null => Rep::Null,
|
|
Type::Bool(_) => Rep::Bool,
|
|
Type::Int => Rep::Int,
|
|
Type::Float => Rep::Double,
|
|
Type::Str(_) => Rep::Str,
|
|
Type::Obj(cell) => Rep::Obj(self.num(cell)),
|
|
Type::Sum(_) => self.sum_rep(ty),
|
|
Type::Any => Rep::Any,
|
|
}
|
|
}
|
|
|
|
/// The representation of a sum. A string or the objects of one cell, with
|
|
/// null or undefined, is that pointer with sentinels. The objects of
|
|
/// several cells are a pointer to their header. Any other sum is an
|
|
/// `any_t`, which holds every kind
|
|
pub(super) fn sum_rep(&self, ty: &Type) -> Rep {
|
|
let others: Vec<&Type> = ty.members().iter().filter(|m| !m.is_nullish()).collect();
|
|
let mut classes: Vec<CellNum> = others.iter().filter_map(|m| self.cell_num(m)).collect();
|
|
classes.dedup();
|
|
|
|
match (&others[..], &classes[..]) {
|
|
([], _) => Rep::Sum(kinds(ty)),
|
|
([Type::Str(_)], _) => Rep::OptStr,
|
|
(_, [id]) if classes.len() == 1 && others.iter().all(|m| matches!(m, Type::Obj(_))) => Rep::OptObj(*id),
|
|
_ if others.iter().all(|m| matches!(m, Type::Obj(_))) => Rep::Objs,
|
|
_ => Rep::Sum(kinds(ty)),
|
|
}
|
|
}
|
|
|
|
/// The class of an `Obj` type
|
|
pub(super) fn cell_num(&self, ty: &Type) -> Option<CellNum> {
|
|
match ty {
|
|
Type::Obj(cell) => Some(self.num(cell)),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// The representation of a field of type `ty`. A field that can be absent
|
|
/// needs a sentinel for absent: a pointer has one, and any other rep is a
|
|
/// tagged union that can also be absent
|
|
pub(super) fn slot_rep(&self, ty: &Type, absent: bool) -> Rep {
|
|
match self.rep(ty) {
|
|
rep @ (Rep::Str | Rep::Obj(_) | Rep::OptStr | Rep::OptObj(_) | Rep::Objs) => rep,
|
|
_ if absent => Rep::Sum(kinds(ty) | ABSENT),
|
|
rep => rep,
|
|
}
|
|
}
|
|
|
|
/// The representation of an element of an array of cell `id`: its type,
|
|
/// or undefined for a hole
|
|
pub(super) fn elem_rep(&self, id: CellNum) -> Rep {
|
|
self.rep(&self.cells[id as usize].read_elem())
|
|
}
|
|
|
|
pub(super) fn c_type(&self, rep: Rep) -> String {
|
|
match rep {
|
|
Rep::Undefined => "undefined_t".into(),
|
|
Rep::Null => "null_t".into(),
|
|
Rep::Bool => "bool".into(),
|
|
Rep::Int => "int32_t".into(),
|
|
Rep::Double => "double".into(),
|
|
Rep::Str | Rep::OptStr => "string_t".into(),
|
|
Rep::Obj(id) | Rep::OptObj(id) => format!("{} *", struct_name(id)),
|
|
Rep::Objs => "jsrt_header *".into(),
|
|
Rep::Sum(k) => {
|
|
self.sums.borrow_mut().insert(k);
|
|
format!("sum_{k}")
|
|
}
|
|
Rep::Any => "any_t".into(),
|
|
}
|
|
}
|
|
|
|
pub(super) fn field_name(&self, name: AtomRef) -> String {
|
|
let text = atom_name(name, self.atoms);
|
|
let text = text.chars().map(|c| if c.is_ascii_alphanumeric() { c } else { '_' });
|
|
format!("f_{}", text.collect::<String>())
|
|
}
|
|
|
|
/// The struct of cell `id`, and its helpers: `init_S`, which sets the
|
|
/// header and makes each field that can be absent absent, and for an
|
|
/// array, the helpers of its elements
|
|
pub(super) fn struct_def(&mut self, id: CellNum, out: &mut String) {
|
|
let cell = self.cells[id as usize].clone();
|
|
let name = struct_name(id);
|
|
let mut members = String::from("jsrt_header h;");
|
|
let mut body = String::from(" o->h.cell = cell;\n");
|
|
|
|
// An array's elements come after its header. `items` has room for
|
|
// `cap` of them. An element holds undefined for a hole
|
|
let elem = cell.is_array().then(|| self.elem_rep(id));
|
|
|
|
if let Some(elem) = elem {
|
|
let _ = write!(members, " int32_t length; int32_t cap; {} *items;", self.c_type(elem));
|
|
}
|
|
|
|
for (field, f) in &cell.info().fields {
|
|
let absent = cell.can_be_absent(*field);
|
|
let rep = self.slot_rep(&f.ty, absent);
|
|
let member = self.field_name(*field);
|
|
let _ = write!(members, " {} {member};", self.c_type(rep));
|
|
|
|
if absent {
|
|
let _ = writeln!(body, " o->{member} = {};", self.absent(rep));
|
|
}
|
|
}
|
|
|
|
let _ = writeln!(out, "struct {name} {{ {members} }};");
|
|
self.helper(
|
|
format!("static {name} *init_{name}({name} *o, uint32_t cell)"),
|
|
format!("{body} return o;\n"),
|
|
);
|
|
|
|
if let Some(elem) = elem {
|
|
let (t, hole) = (self.c_type(elem), self.conv("(undefined_t){0}", Rep::Undefined, elem));
|
|
self.helper(
|
|
format!("static void set_length_{name}({name} *a, int32_t n)"),
|
|
format!(
|
|
" if (n < 0) jsrt_type_error();\n\
|
|
\x20 a->items = jsrt_grow(a->items, &a->cap, n, sizeof *a->items);\n\
|
|
\x20 for (int32_t i = a->length; i < n; i++) a->items[i] = {hole};\n\
|
|
\x20 a->length = n;\n"
|
|
),
|
|
);
|
|
self.helper(
|
|
format!("static {t} elem_{name}({name} *a, int32_t i)"),
|
|
format!(" return i >= 0 && i < a->length ? a->items[i] : {hole};\n"),
|
|
);
|
|
self.helper(
|
|
format!("static void set_elem_{name}({name} *a, int32_t i, {t} v)"),
|
|
" if (i < 0) jsrt_type_error();\n if (i >= a->length) set_length_{NAME}(a, i + 1);\n a->items[i] = v;\n"
|
|
.replace("{NAME}", &name),
|
|
);
|
|
}
|
|
}
|
|
|
|
pub(super) fn helper(&mut self, sig: String, body: String) {
|
|
let _ = writeln!(self.helper_protos, "{sig};");
|
|
let _ = writeln!(self.helper_defs, "{sig} {{\n{body}}}\n");
|
|
}
|
|
}
|