Files
OxideJS-New/crates/oxide_emit/src/stmt/declaration/var.rs
T

91 lines
4.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! var/let/const 声明语句 emit:声明 + 初始化表达式,处理提升与解构 pattern。
use crate::{CompileCtx, Emitter};
use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand;
use oxide_parser::{BindingPattern, Statement, VariableDeclaration, VariableDeclarationKind};
impl Emitter {
pub(crate) fn emit_variable_declaration_statement(
&self, stmt: &Statement, ctx: &mut CompileCtx,
) -> Result<Option<u32>, String> {
let Statement::VariableDeclaration(decl) = stmt else {
return Ok(None);
};
self.emit_variable_declaration(decl, ctx)
}
/// 变量声明本体:供 export 声明复用(导出声明需先按普通声明 emit,再就地注册导出值)。
pub(crate) fn emit_variable_declaration(
&self, decl: &VariableDeclaration, ctx: &mut CompileCtx,
) -> Result<Option<u32>, String> {
let mut r = None;
for d in &decl.declarations {
let is_const = matches!(decl.kind, VariableDeclarationKind::Const);
if is_const && d.init.is_none() {
return Err("const declaration must have an initializer".into());
}
if let Some(init) = &d.init {
let val_reg = self.emit_expression(init, ctx)?;
self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, false, ctx)?;
if let BindingPattern::BindingIdentifier(bi) = &d.id {
if crate::is_anonymous_function_definition(init) {
if let Some(sub_mod) = ctx.nested.last_mut() {
if sub_mod.function_name.is_none() {
sub_mod.function_name = Some(bi.name.to_string());
}
}
}
}
r = Some(val_reg);
} else {
let BindingPattern::BindingIdentifier(bi) = &d.id else {
return Err("destructuring declaration requires an initializer".into());
};
let idx = ctx.add_constant(Constant::Undefined);
let tmp = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(tmp), idx));
let var_reg = ctx.alloc_reg();
// 无初始化 var 是纯声明而非赋值:绑定已预先存在(提升引用或先前写入)
// 时保留槽值,仅首次声明把 undefined 物化进槽。
let (target_reg, already_bound) = if matches!(decl.kind, VariableDeclarationKind::Var) {
match ctx.declare(bi.name.as_str(), var_reg, decl.kind, is_const) {
Ok(()) => (var_reg, false),
Err(_) => (ctx.lookup(bi.name.as_str()).unwrap_or(var_reg), true),
}
} else {
// let/const 无初始化器:复用本块预声明槽位,否则新声明
// (for 头等未预声明路径)。
let target = if let Some(reg) = ctx.scopes.symbols.consume_predeclared_slot(bi.name.as_str()) {
reg
} else {
let r = ctx.alloc_reg();
ctx.declare(bi.name.as_str(), r, decl.kind, is_const)?;
r
};
(target, false)
};
if let Some(&cell_idx) = ctx.captured_bindings.get(bi.name.as_str()) {
// 被捕获绑定的 cell 在函数入口已实例化(undefined);声明语句
// 不赋值,仅首次声明刷新初值,已绑定的 cell 保留现值。
if !already_bound {
ctx.inst(Inst::new(
OpCode::MAKE_CELL,
Operand::Reg(tmp),
Operand::Imm(cell_idx as u16),
Operand::None,
));
}
} else if !already_bound {
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(target_reg), Operand::Reg(tmp), Operand::None));
}
ctx.init_var(bi.name.as_str());
r = Some(var_reg);
}
}
Ok(r)
}
}