mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
refactor: 模块编译改单趟完成
This commit is contained in:
@@ -1062,11 +1062,24 @@ impl Compiler {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn predeclare_function_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) {
|
||||||
|
for statement in statements {
|
||||||
|
let Statement::FunctionDeclaration(function) = statement else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(identifier) = &function.id else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let reg = ctx.alloc_reg();
|
||||||
|
let _ = ctx.declare_initialized(identifier.name.as_str(), reg, VariableDeclarationKind::Var, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[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, C, 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,
|
||||||
mut count_fields: Option<C>, mut emit_fields: Option<E>, fields_after_super: bool,
|
_count_fields: Option<C>, mut emit_fields: Option<E>, fields_after_super: bool,
|
||||||
) -> Result<CompiledModule, String>
|
) -> Result<CompiledModule, String>
|
||||||
where
|
where
|
||||||
C: FnMut(&Compiler, &mut CompileCtx),
|
C: FnMut(&Compiler, &mut CompileCtx),
|
||||||
@@ -1148,51 +1161,7 @@ impl Compiler {
|
|||||||
|
|
||||||
let param_base = ctx.next_reg;
|
let param_base = ctx.next_reg;
|
||||||
|
|
||||||
// Register parameters as initialized.
|
// Emit parameters and destructuring prologue.
|
||||||
for spec in param_specs {
|
|
||||||
let name = spec.register_name();
|
|
||||||
let reg = ctx.alloc_reg();
|
|
||||||
ctx.declare_initialized(name, reg, VariableDeclarationKind::Var, false)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count the parameter destructuring prologue so the count pass matches the emit pass
|
|
||||||
// below (which emits it via emit_binding_pattern). Without this the projected program
|
|
||||||
// counter undercounts, drifting every jump target inside a function that has a
|
|
||||||
// destructured or default parameter.
|
|
||||||
for spec in param_specs {
|
|
||||||
if let ParamSpec::Pattern { pattern, .. } = spec {
|
|
||||||
self.count_binding_pattern(pattern, &mut ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count pass. For derived constructors the emit pass allocates/counts the instance
|
|
||||||
// fields up front but splices the field bytecode into the body right after SUPER_CALL.
|
|
||||||
// Mirror that: count the fields (allocating the same registers/constants), capture
|
|
||||||
// their instruction count, undo the position here, and re-add it at the super() call
|
|
||||||
// site during body counting so labels after super() line up with the emit pass.
|
|
||||||
if let Some(count) = count_fields.as_mut() {
|
|
||||||
if fields_after_super {
|
|
||||||
let before = ctx.projected_pc;
|
|
||||||
count(self, &mut ctx);
|
|
||||||
let field_words = ctx.projected_pc - before;
|
|
||||||
ctx.projected_pc = before;
|
|
||||||
ctx.after_super_count_words = Some(field_words);
|
|
||||||
} else {
|
|
||||||
count(self, &mut ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for stmt in body_stmts {
|
|
||||||
self.count_statement(stmt, &mut ctx);
|
|
||||||
}
|
|
||||||
ctx.max_regs = ctx.max_regs.max(1);
|
|
||||||
ctx.reg_overflow = false;
|
|
||||||
// Count-pass instruction total for this body, compared against the emit pass below
|
|
||||||
// to catch any counter/emitter drift (which would mis-target jumps). reset_regs zeroes
|
|
||||||
// projected_pc, so capture it first.
|
|
||||||
let counted_pc = ctx.projected_pc;
|
|
||||||
ctx.reset_regs();
|
|
||||||
|
|
||||||
// Emit pass - reallocate params (same order = same regs after reset)
|
|
||||||
for spec in param_specs {
|
for spec in param_specs {
|
||||||
let name = spec.register_name();
|
let name = spec.register_name();
|
||||||
let reg = ctx.alloc_reg();
|
let reg = ctx.alloc_reg();
|
||||||
@@ -1206,6 +1175,8 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.predeclare_function_declarations(body_stmts, &mut ctx);
|
||||||
|
|
||||||
// Pre-scan: run analysis for nested function expressions to mark parent captures
|
// Pre-scan: run analysis for nested function expressions to mark parent captures
|
||||||
self.pre_scan_function_expressions(body_stmts, &mut ctx)?;
|
self.pre_scan_function_expressions(body_stmts, &mut ctx)?;
|
||||||
|
|
||||||
@@ -1243,14 +1214,6 @@ impl Compiler {
|
|||||||
|
|
||||||
ctx.resolve_fixups()?;
|
ctx.resolve_fixups()?;
|
||||||
|
|
||||||
if counted_pc != ctx.bytecode.len() {
|
|
||||||
return Err(format!(
|
|
||||||
"counter/emitter instruction drift in function body (before implicit RETURN): counted {} vs emitted {}",
|
|
||||||
counted_pc,
|
|
||||||
ctx.bytecode.len()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit implicit RETURN: expression body returns the last expression,
|
// Emit implicit RETURN: expression body returns the last expression,
|
||||||
// statement body returns undefined.
|
// statement body returns undefined.
|
||||||
if is_expression_body {
|
if is_expression_body {
|
||||||
@@ -1435,28 +1398,7 @@ impl Compiler {
|
|||||||
crate::compiler_debug!("compile: starting...");
|
crate::compiler_debug!("compile: starting...");
|
||||||
let mut ctx = CompileCtx::new();
|
let mut ctx = CompileCtx::new();
|
||||||
ctx.pre_register_builtins();
|
ctx.pre_register_builtins();
|
||||||
|
self.predeclare_function_declarations(&program.body, &mut ctx);
|
||||||
// The emit pass below hoists FunctionDeclarations to the front. The count pass
|
|
||||||
// must walk in that same order: counting in source order would record jump-target
|
|
||||||
// labels at source-order PCs while emit places code at hoisted-order PCs, drifting
|
|
||||||
// every jump that sits after a hoisted function declaration (infinite loops at run).
|
|
||||||
for stmt in &program.body {
|
|
||||||
if matches!(stmt, Statement::FunctionDeclaration(_)) {
|
|
||||||
self.count_statement(stmt, &mut ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for stmt in &program.body {
|
|
||||||
if !matches!(stmt, Statement::FunctionDeclaration(_)) {
|
|
||||||
self.count_statement(stmt, &mut ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
crate::compiler_debug!("counter: {} instructions estimated", ctx.projected_pc);
|
|
||||||
ctx.max_regs = ctx.max_regs.max(1);
|
|
||||||
ctx.reg_overflow = false;
|
|
||||||
// Count-pass instruction total, compared against the emit pass below to catch
|
|
||||||
// counter/emitter drift. reset_regs zeroes projected_pc, so capture it first.
|
|
||||||
let counted_pc = ctx.projected_pc;
|
|
||||||
ctx.reset_regs();
|
|
||||||
|
|
||||||
// First sub-pass: emit FunctionDeclarations (hoisting)
|
// First sub-pass: emit FunctionDeclarations (hoisting)
|
||||||
// This ensures function objects are available before any code runs.
|
// This ensures function objects are available before any code runs.
|
||||||
@@ -1480,14 +1422,6 @@ impl Compiler {
|
|||||||
ctx.resolve_fixups()?;
|
ctx.resolve_fixups()?;
|
||||||
crate::compiler_debug!("emitter: {} bytes emitted", ctx.bytecode.len());
|
crate::compiler_debug!("emitter: {} bytes emitted", ctx.bytecode.len());
|
||||||
|
|
||||||
if counted_pc != ctx.bytecode.len() {
|
|
||||||
return Err(format!(
|
|
||||||
"counter/emitter instruction drift in top-level module (before result store + HALT): counted {} vs emitted {}",
|
|
||||||
counted_pc,
|
|
||||||
ctx.bytecode.len()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(r) = last_result {
|
if let Some(r) = last_result {
|
||||||
ctx.emit(opcode::encode(OpCode::LOAD_VAR, 0, r, 0));
|
ctx.emit(opcode::encode(OpCode::LOAD_VAR, 0, r, 0));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user