fix: 对齐 counter/emitter 指令条数,修解构参数、数组赋值元素、解构catch、for-init 无初始化的跳转漂移
This commit is contained in:
@@ -721,6 +721,16 @@ impl Compiler {
|
||||
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
|
||||
if !fields_after_super {
|
||||
if let Some(count) = count_fields.as_mut() {
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use super::super::*;
|
||||
|
||||
impl Compiler {
|
||||
pub(in crate::counter) fn count_binding_pattern(
|
||||
&self, pattern: &oxide_parser::BindingPattern, ctx: &mut CompileCtx,
|
||||
) {
|
||||
pub(crate) fn count_binding_pattern(&self, pattern: &oxide_parser::BindingPattern, ctx: &mut CompileCtx) {
|
||||
match pattern {
|
||||
oxide_parser::BindingPattern::BindingIdentifier(_) => {
|
||||
ctx.alloc_reg();
|
||||
@@ -72,9 +70,11 @@ impl Compiler {
|
||||
ctx.count_instr(); // FOR_OF_DONE
|
||||
ctx.alloc_reg();
|
||||
ctx.count_instr(); // FOR_OF_NEXT
|
||||
if elem.is_some() {
|
||||
ctx.alloc_reg();
|
||||
ctx.count_instr(); // STORE_VAR
|
||||
if let Some(elem) = elem {
|
||||
// Mirror emit_array_assignment: an element may be a default (`[a = 1]`) or a
|
||||
// nested pattern (`[[a], b]`), not just an identifier — recurse through the
|
||||
// shared dispatch instead of hard-coding one STORE_VAR.
|
||||
self.count_assignment_maybe_default(elem, ctx);
|
||||
}
|
||||
}
|
||||
if let Some(rest) = &ap.rest {
|
||||
|
||||
@@ -40,9 +40,14 @@ impl Compiler {
|
||||
ctx.labels.label_map.insert(catch_label, ctx.projected_pc);
|
||||
if let Some(catch) = &stmt.handler {
|
||||
ctx.push_scope();
|
||||
if let Some(_param) = &catch.param {
|
||||
if let Some(param) = &catch.param {
|
||||
// Mirror the emitter: the catch register is always allocated, but STORE_VAR is
|
||||
// emitted only for an identifier binding — a destructuring catch param emits no
|
||||
// store (that binding form is not implemented yet).
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // STORE_VAR
|
||||
if let oxide_parser::BindingPattern::BindingIdentifier(_) = ¶m.pattern {
|
||||
ctx.projected_pc += 1; // STORE_VAR
|
||||
}
|
||||
}
|
||||
for cs in &catch.body.body {
|
||||
self.count_statement(cs, ctx);
|
||||
|
||||
@@ -41,7 +41,8 @@ impl Compiler {
|
||||
self.count_expression(init_expr, ctx);
|
||||
self.count_binding_pattern(&d.id, ctx);
|
||||
} else {
|
||||
ctx.alloc_reg();
|
||||
ctx.alloc_reg(); // tmp holding undefined
|
||||
ctx.alloc_reg(); // var register
|
||||
ctx.count_words(2); // LOAD_CONST(undefined) + STORE_VAR
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +75,17 @@ impl Compiler {
|
||||
let val_reg = self.emit_expression(init_expr, ctx)?;
|
||||
self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, ctx)?;
|
||||
} else if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||
// `for (var i; ...)` initializes `i` to undefined, mirroring a
|
||||
// statement-level `var i;` — emit LOAD_CONST undefined + STORE_VAR.
|
||||
// The count pass already counts these two instructions; emitting
|
||||
// nothing here drifts every jump target after the for-init.
|
||||
let idx = ctx.add_constant(Constant::Undefined);
|
||||
let tmp = ctx.alloc_reg();
|
||||
ctx.emit_load_const(tmp, idx);
|
||||
let var_reg = ctx.alloc_reg();
|
||||
ctx.declare(bi.name.as_str(), var_reg, decl.kind, is_const)?;
|
||||
ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, tmp, 0));
|
||||
ctx.init_var(bi.name.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user