refactor(compiler): finalize single pass compilation
This commit is contained in:
@@ -66,7 +66,6 @@ impl Compiler {
|
|||||||
false,
|
false,
|
||||||
&self_binding,
|
&self_binding,
|
||||||
FunctionBodyContext::ClassElement,
|
FunctionBodyContext::ClassElement,
|
||||||
None::<fn(&Compiler, &mut CompileCtx)>,
|
|
||||||
Some(|compiler: &Compiler, field_ctx: &mut CompileCtx| -> Result<(), String> {
|
Some(|compiler: &Compiler, field_ctx: &mut CompileCtx| -> Result<(), String> {
|
||||||
for field in &instance_fields {
|
for field in &instance_fields {
|
||||||
if let PropertyKey::PrivateIdentifier(private) = &field.key {
|
if let PropertyKey::PrivateIdentifier(private) = &field.key {
|
||||||
@@ -98,7 +97,6 @@ impl Compiler {
|
|||||||
false,
|
false,
|
||||||
&self_binding,
|
&self_binding,
|
||||||
FunctionBodyContext::ClassElement,
|
FunctionBodyContext::ClassElement,
|
||||||
None::<fn(&Compiler, &mut CompileCtx)>,
|
|
||||||
Some(|compiler: &Compiler, field_ctx: &mut CompileCtx| -> Result<(), String> {
|
Some(|compiler: &Compiler, field_ctx: &mut CompileCtx| -> Result<(), String> {
|
||||||
for field in &instance_fields {
|
for field in &instance_fields {
|
||||||
if let PropertyKey::PrivateIdentifier(private) = &field.key {
|
if let PropertyKey::PrivateIdentifier(private) = &field.key {
|
||||||
|
|||||||
@@ -138,6 +138,12 @@ pub(crate) struct JumpFixup {
|
|||||||
pub(crate) rd: u8,
|
pub(crate) rd: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct FieldBuffer {
|
||||||
|
pub(crate) bytecode: Vec<opcode::Instr>,
|
||||||
|
pub(crate) labels: Vec<(Label, usize)>,
|
||||||
|
pub(crate) fixups: Vec<JumpFixup>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A labeled-statement scope active during emission. `break label` targets
|
/// A labeled-statement scope active during emission. `break label` targets
|
||||||
/// `break_label`; `continue label` targets `continue_label` (only set when the
|
/// `break_label`; `continue label` targets `continue_label` (only set when the
|
||||||
/// labeled statement directly wraps an iteration statement).
|
/// labeled statement directly wraps an iteration statement).
|
||||||
@@ -158,7 +164,6 @@ pub(crate) struct CompileCtx {
|
|||||||
reserved_reg_start: u8,
|
reserved_reg_start: u8,
|
||||||
pub(crate) labels: LabelCtx,
|
pub(crate) labels: LabelCtx,
|
||||||
pub(crate) scopes: ScopeCtx,
|
pub(crate) scopes: ScopeCtx,
|
||||||
pub(crate) projected_pc: usize,
|
|
||||||
pub(crate) sub_modules: Vec<CompiledModule>,
|
pub(crate) sub_modules: Vec<CompiledModule>,
|
||||||
/// Register holding `this` in the enclosing function context.
|
/// Register holding `this` in the enclosing function context.
|
||||||
/// Used by arrow functions to capture lexical `this`.
|
/// Used by arrow functions to capture lexical `this`.
|
||||||
@@ -168,8 +173,7 @@ pub(crate) struct CompileCtx {
|
|||||||
pub(crate) in_instance_method: bool,
|
pub(crate) in_instance_method: bool,
|
||||||
pub(crate) in_static_method: bool,
|
pub(crate) in_static_method: bool,
|
||||||
pub(crate) static_block_this_reg: Option<u8>,
|
pub(crate) static_block_this_reg: Option<u8>,
|
||||||
pub(crate) after_super_insert: Option<Vec<opcode::Instr>>,
|
pub(crate) field_buffer: Option<FieldBuffer>,
|
||||||
pub(crate) after_super_inserted: bool,
|
|
||||||
pub(crate) current_upvalue_captures: Vec<UpvalueCapture>,
|
pub(crate) current_upvalue_captures: Vec<UpvalueCapture>,
|
||||||
/// Set when alloc_reg() overflows into the reserved this/new.target range (≥254).
|
/// Set when alloc_reg() overflows into the reserved this/new.target range (≥254).
|
||||||
/// Checked after each emit phase to produce a compile error rather than silent corruption.
|
/// Checked after each emit phase to produce a compile error rather than silent corruption.
|
||||||
@@ -237,15 +241,13 @@ impl CompileCtx {
|
|||||||
next_private_name_id: 1,
|
next_private_name_id: 1,
|
||||||
cell_registry: Vec::new(),
|
cell_registry: Vec::new(),
|
||||||
},
|
},
|
||||||
projected_pc: 0,
|
|
||||||
sub_modules: Vec::new(),
|
sub_modules: Vec::new(),
|
||||||
enclosing_this_reg: 254, // conventional this register at top level
|
enclosing_this_reg: 254, // conventional this register at top level
|
||||||
in_derived_constructor: false,
|
in_derived_constructor: false,
|
||||||
in_instance_method: false,
|
in_instance_method: false,
|
||||||
in_static_method: false,
|
in_static_method: false,
|
||||||
static_block_this_reg: None,
|
static_block_this_reg: None,
|
||||||
after_super_insert: None,
|
field_buffer: None,
|
||||||
after_super_inserted: false,
|
|
||||||
current_upvalue_captures: Vec::new(),
|
current_upvalue_captures: Vec::new(),
|
||||||
reg_overflow: false,
|
reg_overflow: false,
|
||||||
const_overflow: false,
|
const_overflow: false,
|
||||||
@@ -356,7 +358,6 @@ impl CompileCtx {
|
|||||||
|
|
||||||
pub(crate) fn reset_regs(&mut self) {
|
pub(crate) fn reset_regs(&mut self) {
|
||||||
self.next_reg = self.builtin_reg_floor().max(self.reserved_reg_start);
|
self.next_reg = self.builtin_reg_floor().max(self.reserved_reg_start);
|
||||||
self.projected_pc = 0;
|
|
||||||
self.labels.label_counter = 0;
|
self.labels.label_counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -906,7 +907,6 @@ impl Compiler {
|
|||||||
is_expression_body,
|
is_expression_body,
|
||||||
extra_bindings,
|
extra_bindings,
|
||||||
body_context,
|
body_context,
|
||||||
None::<fn(&Compiler, &mut CompileCtx)>,
|
|
||||||
None::<fn(&Compiler, &mut CompileCtx) -> Result<(), String>>,
|
None::<fn(&Compiler, &mut CompileCtx) -> Result<(), String>>,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
@@ -1022,13 +1022,12 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) fn compile_function_body_with_field_hooks<'a, C, E>(
|
pub(crate) fn compile_function_body_with_field_hooks<'a, E>(
|
||||||
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
|
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
|
||||||
is_expression_body: bool, extra_bindings: &[(&str, u8)], body_context: FunctionBodyContext,
|
is_expression_body: bool, extra_bindings: &[(&str, u8)], body_context: FunctionBodyContext,
|
||||||
_count_fields: Option<C>, mut emit_fields: Option<E>, fields_after_super: bool,
|
mut emit_fields: Option<E>, fields_after_super: bool,
|
||||||
) -> Result<CompiledModule, String>
|
) -> Result<CompiledModule, String>
|
||||||
where
|
where
|
||||||
C: FnMut(&Compiler, &mut CompileCtx),
|
|
||||||
E: FnMut(&Compiler, &mut CompileCtx) -> Result<(), String>,
|
E: FnMut(&Compiler, &mut CompileCtx) -> Result<(), String>,
|
||||||
{
|
{
|
||||||
let mut ctx = CompileCtx::new();
|
let mut ctx = CompileCtx::new();
|
||||||
@@ -1128,11 +1127,22 @@ impl Compiler {
|
|||||||
|
|
||||||
if let Some(emit) = emit_fields.as_mut() {
|
if let Some(emit) = emit_fields.as_mut() {
|
||||||
if fields_after_super {
|
if fields_after_super {
|
||||||
let start = ctx.bytecode.len();
|
let mut parent_bytecode = Vec::new();
|
||||||
|
let mut parent_labels = HashMap::new();
|
||||||
|
let mut parent_fixups = Vec::new();
|
||||||
|
std::mem::swap(&mut ctx.bytecode, &mut parent_bytecode);
|
||||||
|
std::mem::swap(&mut ctx.labels.label_map, &mut parent_labels);
|
||||||
|
std::mem::swap(&mut ctx.fixups, &mut parent_fixups);
|
||||||
emit(self, &mut ctx)?;
|
emit(self, &mut ctx)?;
|
||||||
let field_code = ctx.bytecode.split_off(start);
|
let field_buffer = FieldBuffer {
|
||||||
ctx.after_super_insert = Some(field_code);
|
bytecode: std::mem::take(&mut ctx.bytecode),
|
||||||
ctx.after_super_inserted = false;
|
labels: std::mem::take(&mut ctx.labels.label_map).into_iter().collect(),
|
||||||
|
fixups: std::mem::take(&mut ctx.fixups),
|
||||||
|
};
|
||||||
|
ctx.bytecode = parent_bytecode;
|
||||||
|
ctx.labels.label_map = parent_labels;
|
||||||
|
ctx.fixups = parent_fixups;
|
||||||
|
ctx.field_buffer = Some(field_buffer);
|
||||||
} else {
|
} else {
|
||||||
emit(self, &mut ctx)?;
|
emit(self, &mut ctx)?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,15 @@ impl Compiler {
|
|||||||
let result_reg = ctx.alloc_reg();
|
let result_reg = ctx.alloc_reg();
|
||||||
ctx.emit(opcode::encode(OpCode::SUPER_CALL, result_reg, first_arg_reg, 0));
|
ctx.emit(opcode::encode(OpCode::SUPER_CALL, result_reg, first_arg_reg, 0));
|
||||||
ctx.emit(arg_regs.len() as u32);
|
ctx.emit(arg_regs.len() as u32);
|
||||||
if !ctx.after_super_inserted {
|
if let Some(mut field_buffer) = ctx.field_buffer.take() {
|
||||||
if let Some(field_code) = ctx.after_super_insert.clone() {
|
let insert_pc = ctx.bytecode.len();
|
||||||
ctx.bytecode.extend(field_code);
|
ctx.bytecode.append(&mut field_buffer.bytecode);
|
||||||
ctx.after_super_inserted = true;
|
for (label, relative_pc) in field_buffer.labels {
|
||||||
|
ctx.labels.label_map.insert(label, insert_pc + relative_pc);
|
||||||
|
}
|
||||||
|
for mut fixup in field_buffer.fixups {
|
||||||
|
fixup.pc += insert_pc;
|
||||||
|
ctx.fixups.push(fixup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(result_reg);
|
return Ok(result_reg);
|
||||||
|
|||||||
@@ -284,6 +284,12 @@ fn compile_class_declaration_emits_constructor_and_prototype_setup() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compile_derived_constructor_inserts_instance_fields_after_super() {
|
||||||
|
let module = compile_source("class Base {} class Derived extends Base { value = 1; constructor() { super(); } }");
|
||||||
|
assert!(!module.sub_modules.is_empty(), "derived class should compile its constructor");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compile_class_declaration_default_constructor_creates_submodule() {
|
fn compile_class_declaration_default_constructor_creates_submodule() {
|
||||||
let module = compile_source("class A {}");
|
let module = compile_source("class A {}");
|
||||||
|
|||||||
Reference in New Issue
Block a user