Modifications
This commit is contained in:
@@ -10,7 +10,8 @@
|
||||
//! This step takes the initialiser's field writes -- its body minus the
|
||||
//! this/home-object prologue and the trailing return -- and splices them into
|
||||
//! the constructor in place of that call, renumbered onto fresh value ids. The
|
||||
//! initialiser function is left unreferenced.
|
||||
//! initialiser is left with an empty body. It cannot be dropped from the
|
||||
//! function list, because a function id is an index into that list.
|
||||
//!
|
||||
//! Deficiency, left for step 3: a class with NO instance fields compiles
|
||||
//! `<class_fields_init>` to `Undefined` rather than a closure, so there is no
|
||||
@@ -19,7 +20,8 @@
|
||||
//! time -- a harmless no-op. Step 3 must strip that block when it drops the
|
||||
//! `<class_fields_init>` capture, or the `get_var_ref_check` would dangle.
|
||||
|
||||
use super::FieldInit;
|
||||
use super::step_1::ClassSequence;
|
||||
|
||||
use crate::{
|
||||
parse::{ImmOperand, opcodes::OpCode},
|
||||
regify::{RegFn, RegInstr},
|
||||
@@ -28,21 +30,27 @@ use crate::{
|
||||
/// The name quickjs gives the hoisted field initialiser.
|
||||
const FIELDS_INIT: &str = "<class_fields_init>";
|
||||
|
||||
pub(super) fn splice(fns: &mut [RegFn], field_inits: &[FieldInit]) {
|
||||
for &FieldInit { ctor, fields } in field_inits {
|
||||
splice_one(fns, ctor, fields);
|
||||
pub fn splice_ctor(fns: &mut [RegFn], class_seqs: &[Vec<ClassSequence>]) {
|
||||
// Only a class with instance fields has an initialiser to splice.
|
||||
for seq in class_seqs.iter().flatten() {
|
||||
if let Some(field_init_fn) = seq.field_init_fn {
|
||||
splice_one(fns, seq.ctor_fn, field_init_fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn splice_one(fns: &mut [RegFn], ctor: usize, fields: usize) {
|
||||
// Pull the field writes out of the initialiser (a separate function), then
|
||||
// hand them to the constructor.
|
||||
let writes = field_writes(&fns[fields]);
|
||||
|
||||
let ctor_fn = &mut fns[ctor];
|
||||
let Some((start, end)) = call_span(ctor_fn) else {
|
||||
// Find the call block first. If the constructor does not have one there is
|
||||
// nothing to splice into, and the initialiser has to keep working.
|
||||
let Some((start, end)) = call_span(&fns[ctor]) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Take the field writes out of the initialiser (a separate function), then
|
||||
// hand them to the constructor.
|
||||
let writes = take_field_writes(&mut fns[fields]);
|
||||
|
||||
let ctor_fn = &mut fns[ctor];
|
||||
let writes = renumber(writes, ctor_fn);
|
||||
ctor_fn.code.splice(start..=end, writes);
|
||||
}
|
||||
@@ -50,12 +58,13 @@ fn splice_one(fns: &mut [RegFn], ctor: usize, fields: usize) {
|
||||
/// The initialiser's field writes: its body minus the this/home-object prologue
|
||||
/// and the trailing return. They reach `this` through get_loc0, which is `this`
|
||||
/// in the constructor too, so they carry over unchanged.
|
||||
fn field_writes(init: &RegFn) -> Vec<RegInstr> {
|
||||
init.code
|
||||
.iter()
|
||||
.filter(|i| !is_prologue(i.op))
|
||||
.map(|i| RegInstr::new(i.op, i.imm, i.dsts.clone(), i.args.clone()))
|
||||
.collect()
|
||||
///
|
||||
/// This empties the initialiser, which is dead once its writes move into the
|
||||
/// constructor. The function itself has to stay: a function id is an index into
|
||||
/// the function list, so removing one would renumber every function after it.
|
||||
fn take_field_writes(init: &mut RegFn) -> Vec<RegInstr> {
|
||||
init.code.retain(|i| !is_prologue(i.op));
|
||||
std::mem::take(&mut init.code)
|
||||
}
|
||||
|
||||
/// Renumber the writes' value ids onto fresh ids in the constructor. Each write
|
||||
|
||||
Reference in New Issue
Block a user