Rewrite with working async
This commit is contained in:
+71
-15
@@ -7,15 +7,25 @@
|
||||
//! - converts child ownership to a reference by-idx
|
||||
//! - extracts closure captures into a separate vec
|
||||
//!
|
||||
//! Also adds a synthetic entry point function that calls the top level fn generated by QuickJS
|
||||
//! Also adds a synthetic entry point function that calls the top level fn generated by QuickJS,
|
||||
//! and makes the module's bindings its locals
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::libqjs::parse::{
|
||||
Atom, AtomRef, ClosureVar, Function, ImmOperand, Instr, JsValue, QuickJSBinary, opcodes::OpCode,
|
||||
Atom, AtomRef, ClosureKind, ClosureVar, FuncKind, Function, ImmOperand, Instr, JsValue,
|
||||
QuickJSBinary, opcodes::OpCode,
|
||||
};
|
||||
|
||||
/// The module's bindings, which are locals of the entry fn, by name. The compiler reads the
|
||||
/// stdlib's by name where it needs one: the classes whose prototypes the primitives use, and
|
||||
/// what the host calls when an IO op completes
|
||||
pub type ModuleBindings = HashMap<String, usize>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FlatFn {
|
||||
pub name: AtomRef,
|
||||
pub kind: FuncKind, // a normal fn, or one that suspends: async, a generator, or both (see transform/async.rs)
|
||||
pub arg_count: usize,
|
||||
pub local_count: usize,
|
||||
pub cpool: Vec<JsValue>,
|
||||
@@ -27,14 +37,45 @@ pub struct FlatFn {
|
||||
/// Returns:
|
||||
/// - flat_fns[i] = the i'th flattened function
|
||||
/// - captures[i] = the i'th fn's captures from its parent
|
||||
pub fn flatten(bin: QuickJSBinary) -> (Vec<FlatFn>, Vec<Vec<ClosureVar>>, Vec<Atom>) {
|
||||
/// - the module's bindings, as locals of the entry fn
|
||||
pub fn flatten(
|
||||
bin: QuickJSBinary,
|
||||
) -> (Vec<FlatFn>, Vec<Vec<ClosureVar>>, Vec<Atom>, ModuleBindings) {
|
||||
let mut fns = vec![];
|
||||
let mut captures = vec![];
|
||||
|
||||
walk(bin.root_mod.root_fn, &mut fns, &mut captures);
|
||||
add_entry_point(&mut fns, &mut captures);
|
||||
let bindings = bind_module(&mut fns, &mut captures, &bin.atoms);
|
||||
|
||||
(fns, captures, bin.atoms)
|
||||
(fns, captures, bin.atoms, bindings)
|
||||
}
|
||||
|
||||
/// Make each binding of the module a local of the entry fn, after its own two. The module fn
|
||||
/// (`<eval>`, the first fn) captures them, as any fn captures its parent's locals, so they are
|
||||
/// cells (see transform/closure.rs)
|
||||
fn bind_module(
|
||||
fns: &mut [FlatFn],
|
||||
captures: &mut [Vec<ClosureVar>],
|
||||
atoms: &[Atom],
|
||||
) -> ModuleBindings {
|
||||
let entry = fns.len() - 1;
|
||||
let mut bindings = ModuleBindings::new();
|
||||
|
||||
for capture in &mut captures[0] {
|
||||
if capture.flags.closure_kind != ClosureKind::ModuleDecl {
|
||||
continue;
|
||||
}
|
||||
|
||||
let local = fns[entry].local_count;
|
||||
fns[entry].local_count += 1;
|
||||
|
||||
capture.flags.closure_kind = ClosureKind::Local;
|
||||
capture.var_idx = local;
|
||||
bindings.insert(capture.name.text(atoms), local);
|
||||
}
|
||||
|
||||
bindings
|
||||
}
|
||||
|
||||
fn walk(func: Function, flat_fns: &mut Vec<FlatFn>, captures: &mut Vec<Vec<ClosureVar>>) -> usize {
|
||||
@@ -42,6 +83,7 @@ fn walk(func: Function, flat_fns: &mut Vec<FlatFn>, captures: &mut Vec<Vec<Closu
|
||||
|
||||
flat_fns.push(FlatFn {
|
||||
name: func.name,
|
||||
kind: func.flags.func_kind,
|
||||
arg_count: func.arg_count,
|
||||
local_count: func.var_count,
|
||||
cpool: vec![],
|
||||
@@ -73,11 +115,20 @@ fn walk(func: Function, flat_fns: &mut Vec<FlatFn>, captures: &mut Vec<Vec<Closu
|
||||
/// ...
|
||||
/// }
|
||||
///
|
||||
/// <eval>.call(true); // Link phase (`this = true`)
|
||||
/// <eval>.call(false); // Run phase (`this = false`)
|
||||
/// // Link phase (`this = true`), then run phase (`this = false`)
|
||||
/// let phase = true;
|
||||
/// while (true) {
|
||||
/// <eval>.call(phase);
|
||||
/// if (!phase) break;
|
||||
/// phase = false;
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Both phases call `<eval>` from one call instr. Infer names the objects that a walk makes
|
||||
/// by its call site, so with two call instrs, each phase would make its own copy of every
|
||||
/// module-level object, and each module binding would hold one of two copies
|
||||
///
|
||||
/// This gets called by C main()
|
||||
fn add_entry_point(fns: &mut Vec<FlatFn>, captures: &mut Vec<Vec<ClosureVar>>) {
|
||||
let instr = |pc, op, imm| Instr { pc, op, imm };
|
||||
@@ -86,20 +137,25 @@ fn add_entry_point(fns: &mut Vec<FlatFn>, captures: &mut Vec<Vec<ClosureVar>>) {
|
||||
instr(0, OpCode::Fclosure8, ImmOperand::Const(0)),
|
||||
instr(1, OpCode::PutLoc, ImmOperand::Loc(0)),
|
||||
instr(2, OpCode::PushTrue, ImmOperand::None),
|
||||
instr(3, OpCode::GetLoc, ImmOperand::Loc(0)),
|
||||
instr(4, OpCode::CallMethod, ImmOperand::Npop(0)),
|
||||
instr(5, OpCode::Drop, ImmOperand::None),
|
||||
instr(6, OpCode::PushFalse, ImmOperand::None),
|
||||
instr(7, OpCode::GetLoc, ImmOperand::Loc(0)),
|
||||
instr(8, OpCode::CallMethod, ImmOperand::Npop(0)),
|
||||
instr(9, OpCode::Drop, ImmOperand::None),
|
||||
instr(10, OpCode::ReturnUndef, ImmOperand::None),
|
||||
instr(3, OpCode::PutLoc, ImmOperand::Loc(1)),
|
||||
// Loop head: one call for both phases
|
||||
instr(4, OpCode::GetLoc, ImmOperand::Loc(1)),
|
||||
instr(5, OpCode::GetLoc, ImmOperand::Loc(0)),
|
||||
instr(6, OpCode::CallMethod, ImmOperand::Npop(0)),
|
||||
instr(7, OpCode::Drop, ImmOperand::None),
|
||||
instr(8, OpCode::GetLoc, ImmOperand::Loc(1)),
|
||||
instr(9, OpCode::IfFalse8, ImmOperand::Label { target: 13 }),
|
||||
instr(10, OpCode::PushFalse, ImmOperand::None),
|
||||
instr(11, OpCode::PutLoc, ImmOperand::Loc(1)),
|
||||
instr(12, OpCode::Goto8, ImmOperand::Label { target: 4 }),
|
||||
instr(13, OpCode::ReturnUndef, ImmOperand::None),
|
||||
];
|
||||
|
||||
fns.push(FlatFn {
|
||||
name: AtomRef::Predefined(0),
|
||||
kind: FuncKind::Normal,
|
||||
arg_count: 0,
|
||||
local_count: 1,
|
||||
local_count: 2, // <eval>, and the phase
|
||||
cpool: vec![JsValue::FnByIdx(0)], // First fn is the top level <eval>
|
||||
code,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user