mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
91 lines
4.3 KiB
Rust
91 lines
4.3 KiB
Rust
//! 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)
|
||
}
|
||
}
|