155 lines
4.0 KiB
Rust
155 lines
4.0 KiB
Rust
//! This module applies a set of transforms on the bytecode to lower them to a simpler
|
|
//! form more suitable for type inference and emission
|
|
//!
|
|
//! After transformation, the following instructions are eliminated:
|
|
//! - call{0, 1, 2, 3}
|
|
//! - tail_call, tail_call_method
|
|
//! - get_field2
|
|
//! - get_array_el2
|
|
//! - get_loc0_loc1
|
|
//! - inc_loc, dec_loc, add_loc
|
|
//! - set_proto
|
|
//! - define_method
|
|
//! - check_ctor
|
|
//! - special_object
|
|
//! - get_var
|
|
//! - push_this
|
|
//! - get_arg{0, 1, 2, 3}
|
|
//! - put_arg{0, 1, 2, 3}
|
|
//! - set_arg{0, 1, 2, 3}
|
|
//! - get_var_ref{0, 1, 2, 3}, get_var_ref_check
|
|
//! - put_var_ref{0, 1, 2, 3}, put_var_ref_check, put_var_ref_check_init
|
|
//! - set_var_ref{0, 1, 2, 3}, set_var_ref
|
|
//! - set_loc_uninitialized
|
|
//! - fclosure8
|
|
//! - define_class
|
|
//!
|
|
//!
|
|
//! The following instructions take on a different meaning:
|
|
//! - call_constructor (see new.rs)
|
|
//! - close_loc (see closure.rs)
|
|
//!
|
|
//! And the following instructions are added:
|
|
//! - define_field_ref (see closure.rs)
|
|
//! - close_field (see closure.rs)
|
|
//!
|
|
//! Async fns and generators become a constructor and a step closure first (see async.rs),
|
|
//! so no instr of theirs is left: initial_yield, yield, await, return_async in them, and
|
|
//! close_var_ref, which deasync makes and closure.rs lowers
|
|
|
|
mod args;
|
|
mod r#async;
|
|
mod class;
|
|
mod closure;
|
|
mod expand;
|
|
mod new;
|
|
|
|
use crate::{
|
|
flatten::FlatFn,
|
|
libqjs::parse::{Atom, AtomRef, ClosureVar},
|
|
};
|
|
|
|
use args::try_replace_arg_access;
|
|
use r#async::deasync;
|
|
use class::declass;
|
|
use closure::try_replace_closure;
|
|
use expand::{replace_get_var, try_replace_expansion};
|
|
use new::try_replace_new;
|
|
|
|
// Object Prototype Rules:
|
|
// - The `__proto__` field is the JS spec's `[[Prototype]]`, ie the link between an object and
|
|
// its parent prototype
|
|
//
|
|
// - The `prototype` field is the normal `.prototype` field used by classes, ie it holds the
|
|
// object that `new F()` will link
|
|
//
|
|
// So objects are created by:
|
|
// ```
|
|
// Object.create = function (proto) {
|
|
// return { __proto__: proto };
|
|
// };
|
|
// ```
|
|
/// `__proto__`
|
|
pub const PROTO_FIELD: AtomRef = AtomRef::Predefined(72);
|
|
/// `prototype`
|
|
pub const PROTOTYPE_FIELD: AtomRef = AtomRef::Predefined(63);
|
|
|
|
// Arg Rules:
|
|
// After transformation, every fn takes its capture env and `this` as the first two args
|
|
// and all the declared args after them
|
|
pub const ENV_ARG: u16 = 0;
|
|
pub const THIS_ARG: u16 = 1;
|
|
pub const FIRST_ARG: u16 = 2;
|
|
|
|
pub fn transform(
|
|
mut flat_fns: Vec<FlatFn>,
|
|
mut captures: Vec<Vec<ClosureVar>>,
|
|
atoms: &[Atom],
|
|
) -> Vec<FlatFn> {
|
|
// Async fns and generators become state machines first: this adds fns, and changes the
|
|
// captures that the other transforms read
|
|
deasync(&mut flat_fns, &mut captures, atoms);
|
|
|
|
flat_fns
|
|
.into_iter()
|
|
.enumerate()
|
|
.map(|(idx, f)| transform_one(f, &captures[idx], &captures, atoms))
|
|
.collect()
|
|
}
|
|
|
|
/// `own` is the captures of `f`
|
|
fn transform_one(
|
|
mut f: FlatFn,
|
|
own: &[ClosureVar],
|
|
captures: &[Vec<ClosureVar>],
|
|
atoms: &[Atom],
|
|
) -> FlatFn {
|
|
// Transform class declarations
|
|
let code = declass(&f.code);
|
|
|
|
// Transforms may need to allocate new local vars
|
|
let mut next_local = f.local_count;
|
|
|
|
let mut new_local = || {
|
|
let n = next_local;
|
|
next_local += 1;
|
|
n
|
|
};
|
|
|
|
// Apply transformations
|
|
let mut out = Vec::with_capacity(code.len());
|
|
|
|
for instr in &code {
|
|
if try_replace_new(instr, &mut new_local, &mut out) {
|
|
continue;
|
|
}
|
|
|
|
if try_replace_arg_access(instr, &mut out) {
|
|
continue;
|
|
}
|
|
|
|
if try_replace_expansion(instr, &mut out) {
|
|
continue;
|
|
}
|
|
|
|
if replace_get_var(instr, atoms, &mut f.cpool, &mut out) {
|
|
continue;
|
|
}
|
|
|
|
if try_replace_closure(instr, own, captures, &f.cpool, &mut out) {
|
|
continue;
|
|
}
|
|
|
|
out.push(instr.clone());
|
|
}
|
|
|
|
FlatFn {
|
|
name: f.name,
|
|
kind: f.kind,
|
|
arg_count: f.arg_count + FIRST_ARG as usize,
|
|
local_count: next_local,
|
|
cpool: f.cpool,
|
|
code: out,
|
|
}
|
|
}
|