Big Rewrite
This commit is contained in:
+17
-106
@@ -1,131 +1,42 @@
|
||||
//! This module takes the nested QuickJS binary structure and separates the functions into a flat indexed namespace
|
||||
|
||||
use crate::parse::{
|
||||
Atom, AtomRef, ClosureKind, Function, Instr, JsString, JsValue, QuickJSBinary, atom_names,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum CaptureFrom {
|
||||
/// `parent.var_defs[i]`
|
||||
ParentArg(usize),
|
||||
/// `parent.var_defs[parent.arg_count + i]`
|
||||
ParentLocal(usize),
|
||||
/// `parent.captures[i]`, itself resolved against the grandparent.
|
||||
ParentCapture(usize),
|
||||
/// A module-level binding of the module body.
|
||||
ModuleBinding(usize),
|
||||
/// Global or module import; not frame-relative.
|
||||
NotFrameRelative(&'static str),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Capture {
|
||||
pub name: String,
|
||||
/// The name as it was on the wire. A field cannot be given a name that is
|
||||
/// not already an atom, so the operand has to be kept, not rebuilt.
|
||||
pub atom: AtomRef,
|
||||
pub from: CaptureFrom,
|
||||
}
|
||||
use crate::libqjs::parse::{Atom, AtomRef, ClosureVar, Function, Instr, JsValue, QuickJSBinary};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FlatFn {
|
||||
pub name: String,
|
||||
pub name: AtomRef,
|
||||
pub arg_count: usize,
|
||||
pub var_count: usize, // num locals
|
||||
pub value_names: Vec<AtomRef>, // adds names to args and locals, one for each arg, then one for each local, in one vec
|
||||
pub captures: Vec<Capture>,
|
||||
pub var_count: usize,
|
||||
pub cpool: Vec<JsValue>,
|
||||
pub captures: Vec<ClosureVar>,
|
||||
pub code: Vec<Instr>,
|
||||
}
|
||||
|
||||
pub fn flatten(bin: QuickJSBinary) -> (Vec<FlatFn>, Vec<Atom>) {
|
||||
let mut fns = vec![];
|
||||
walk(&mut fns, bin.root_mod.root_fn, &bin.atoms);
|
||||
walk(&mut fns, bin.root_mod.root_fn);
|
||||
(fns, bin.atoms)
|
||||
}
|
||||
|
||||
fn walk(flat_fns: &mut Vec<FlatFn>, func: Function, atoms: &[Atom]) -> usize {
|
||||
fn walk(flat_fns: &mut Vec<FlatFn>, func: Function) -> usize {
|
||||
let id = flat_fns.len();
|
||||
let name = atom_str(&func.name, atoms);
|
||||
|
||||
// Find vars func captures from its environment
|
||||
let captures = func
|
||||
.closure_vars
|
||||
.iter()
|
||||
.map(|cv| Capture {
|
||||
name: atom_str(&cv.name, atoms),
|
||||
atom: cv.name,
|
||||
from: match cv.flags.closure_kind {
|
||||
ClosureKind::Arg => CaptureFrom::ParentArg(cv.var_idx),
|
||||
ClosureKind::Local => CaptureFrom::ParentLocal(cv.var_idx),
|
||||
ClosureKind::Ref => CaptureFrom::ParentCapture(cv.var_idx),
|
||||
ClosureKind::ModuleDecl => CaptureFrom::ModuleBinding(cv.var_idx),
|
||||
ClosureKind::ModuleImport => todo!(), // CaptureFrom::NotFrameRelative("module_import"),
|
||||
ClosureKind::Global | ClosureKind::GlobalDecl | ClosureKind::GlobalRef => todo!(), // CaptureFrom::NotFrameRelative("global")
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Reserve this function's slot before recursing into nested functions,
|
||||
// so it keeps `id` while its children take the ids after it
|
||||
flat_fns.push(FlatFn {
|
||||
name,
|
||||
name: func.name,
|
||||
arg_count: func.arg_count,
|
||||
var_count: func.var_count,
|
||||
value_names: func.var_defs.iter().map(|v| v.name).collect(),
|
||||
captures,
|
||||
cpool: Vec::new(),
|
||||
cpool: vec![],
|
||||
captures: func.closure_vars,
|
||||
code: func.byte_code,
|
||||
});
|
||||
|
||||
// Nested functions live in the cpool; replace each with its flat id.
|
||||
let mut cpool = Vec::with_capacity(func.cpool.len());
|
||||
for const_entry in func.cpool {
|
||||
let entry = match const_entry {
|
||||
JsValue::Function(child) => JsValue::FnById(walk(flat_fns, child, atoms)),
|
||||
let cpool = func
|
||||
.cpool
|
||||
.into_iter()
|
||||
.map(|c| match c {
|
||||
JsValue::Function(child) => JsValue::FnById(walk(flat_fns, child)),
|
||||
other => other,
|
||||
};
|
||||
cpool.push(entry);
|
||||
}
|
||||
flat_fns[id].cpool = cpool;
|
||||
})
|
||||
.collect();
|
||||
|
||||
flat_fns[id].cpool = cpool;
|
||||
id
|
||||
}
|
||||
|
||||
// LLM GENERATED
|
||||
// ─── atom rendering ──────────────────────────────────────────────────────
|
||||
|
||||
pub fn atom_str(a: &AtomRef, atoms: &[Atom]) -> String {
|
||||
match a {
|
||||
AtomRef::TaggedInt(n) => n.to_string(),
|
||||
AtomRef::Predefined(i) => atom_names::PREDEFINED_ATOMS
|
||||
.get(*i as usize)
|
||||
.copied()
|
||||
.unwrap_or("<predef?>")
|
||||
.to_string(),
|
||||
AtomRef::FileLocal(i) => atoms
|
||||
.get(*i)
|
||||
.map(atom_text)
|
||||
.unwrap_or_else(|| format!("<file_atom {i}>")),
|
||||
}
|
||||
}
|
||||
|
||||
fn atom_text(a: &Atom) -> String {
|
||||
match a {
|
||||
Atom::String(s) | Atom::GlobalSymbol(s) | Atom::Symbol(s) => js_string(s),
|
||||
Atom::Predefined(n) => format!("<predef {n}>"),
|
||||
}
|
||||
}
|
||||
|
||||
fn js_string(s: &JsString) -> String {
|
||||
match s {
|
||||
JsString::Latin1(b) => b.iter().map(|&c| c as char).collect(),
|
||||
JsString::UTF16(b) => {
|
||||
let u: Vec<u16> = b
|
||||
.chunks_exact(2)
|
||||
.map(|c| u16::from_le_bytes([c[0], c[1]]))
|
||||
.collect();
|
||||
String::from_utf16_lossy(&u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user