docs: 补充注释与格式化
This commit is contained in:
@@ -1,2 +1 @@
|
||||
//! 类编译核心预留模块(当前为空,为后续类相关公共状态预留位置)。
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
//! 函数:`emit_class`。
|
||||
|
||||
use crate::{CompileCtx, Emitter, FunctionBodyContext};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{Class, ClassElement, MethodDefinitionKind, PropertyKey};
|
||||
|
||||
impl Emitter {
|
||||
@@ -66,9 +66,20 @@ impl Emitter {
|
||||
let emit_instance_fields = |compiler: &Emitter, field_ctx: &mut CompileCtx| -> Result<(), String> {
|
||||
for field in &instance_fields {
|
||||
if let PropertyKey::PrivateIdentifier(private) = &field.key {
|
||||
compiler.emit_private_field_init(Operand::This, private.name.as_str(), field.value.as_ref(), field_ctx)?;
|
||||
compiler.emit_private_field_init(
|
||||
Operand::This,
|
||||
private.name.as_str(),
|
||||
field.value.as_ref(),
|
||||
field_ctx,
|
||||
)?;
|
||||
} else {
|
||||
compiler.emit_public_field_init(Operand::This, &field.key, field.computed, field.value.as_ref(), field_ctx)?;
|
||||
compiler.emit_public_field_init(
|
||||
Operand::This,
|
||||
&field.key,
|
||||
field.computed,
|
||||
field.value.as_ref(),
|
||||
field_ctx,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -108,7 +119,9 @@ impl Emitter {
|
||||
module.insts.extend(field_ctx.insts);
|
||||
module.constants = field_ctx.constants;
|
||||
module.n_registers = field_ctx.max_regs.max(1);
|
||||
module.insts.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None));
|
||||
module
|
||||
.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None));
|
||||
}
|
||||
module
|
||||
};
|
||||
|
||||
@@ -5,10 +5,10 @@ use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{ClassElement, PropertyKey};
|
||||
|
||||
impl Emitter {
|
||||
/// Emit static field initializers and static blocks in source order.
|
||||
/// JS class semantics interleave them (e.g. `static x = 1; static {
|
||||
/// this.y = 1 } static z = this.y + 1` must run in that order). Emitting
|
||||
/// all fields before all blocks would make `z` read `y` before it is set.
|
||||
/// 按源码顺序发静态字段初始化与静态块。
|
||||
/// JS 类语义要求两者交织执行(如 `static x = 1; static { this.y = 1 }
|
||||
/// static z = this.y + 1` 必须按此顺序),若全部字段先于静态块发出,
|
||||
/// `z` 会在 `y` 赋值前读到它。
|
||||
pub(crate) fn emit_class_static_elements(
|
||||
&self, elements: &[ClassElement], ctor_reg: u32, ctx: &mut CompileCtx,
|
||||
) -> Result<(), String> {
|
||||
@@ -23,9 +23,20 @@ impl Emitter {
|
||||
let prop = prop.as_ref();
|
||||
if prop.r#static {
|
||||
if let PropertyKey::PrivateIdentifier(private) = &prop.key {
|
||||
self.emit_private_field_init(Operand::Reg(ctor_reg), private.name.as_str(), prop.value.as_ref(), ctx)?;
|
||||
self.emit_private_field_init(
|
||||
Operand::Reg(ctor_reg),
|
||||
private.name.as_str(),
|
||||
prop.value.as_ref(),
|
||||
ctx,
|
||||
)?;
|
||||
} else {
|
||||
self.emit_public_field_init(Operand::Reg(ctor_reg), &prop.key, prop.computed, prop.value.as_ref(), ctx)?;
|
||||
self.emit_public_field_init(
|
||||
Operand::Reg(ctor_reg),
|
||||
&prop.key,
|
||||
prop.computed,
|
||||
prop.value.as_ref(),
|
||||
ctx,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
//! 负责单个方法函数体(含 `super` 与 home object)。
|
||||
|
||||
use crate::{CompileCtx, Emitter, FunctionBodyContext};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{ClassElement, MethodDefinitionKind, PropertyKey};
|
||||
|
||||
impl Emitter {
|
||||
@@ -36,9 +36,19 @@ impl Emitter {
|
||||
match method.kind {
|
||||
MethodDefinitionKind::Method => {
|
||||
if method.computed {
|
||||
ctx.inst(Inst::new(OpCode::SET_PROP_DYNAMIC, Operand::Reg(home_reg), Operand::Reg(key_reg), Operand::Reg(accessor_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP_DYNAMIC,
|
||||
Operand::Reg(home_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(accessor_reg),
|
||||
));
|
||||
} else {
|
||||
ctx.inst(Inst::new(OpCode::SET_PROP, Operand::Reg(home_reg), Operand::Reg(accessor_reg), Operand::Reg(key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP,
|
||||
Operand::Reg(home_reg),
|
||||
Operand::Reg(accessor_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
}
|
||||
}
|
||||
MethodDefinitionKind::Get | MethodDefinitionKind::Set => {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! 类原型对象构造:`emit_class_prototype` 建立 `Class.prototype` 与属性初始化。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
impl Emitter {
|
||||
pub(crate) fn emit_class_prototype(
|
||||
@@ -17,21 +17,46 @@ impl Emitter {
|
||||
let parent_proto_key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(parent_proto_key_reg), proto_key_idx));
|
||||
let parent_proto_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::GET_PROP, Operand::Reg(super_reg), Operand::Reg(parent_proto_reg), Operand::Reg(parent_proto_key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PROP,
|
||||
Operand::Reg(super_reg),
|
||||
Operand::Reg(parent_proto_reg),
|
||||
Operand::Reg(parent_proto_key_reg),
|
||||
));
|
||||
let proto_link_idx = ctx.add_constant(Constant::String("__proto__".to_string()));
|
||||
let proto_link_key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(proto_link_key_reg), proto_link_idx));
|
||||
ctx.inst(Inst::new(OpCode::SET_PROP, Operand::Reg(proto_reg), Operand::Reg(parent_proto_reg), Operand::Reg(proto_link_key_reg)));
|
||||
ctx.inst(Inst::new(OpCode::SET_PROP, Operand::Reg(ctor_reg), Operand::Reg(super_reg), Operand::Reg(proto_link_key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP,
|
||||
Operand::Reg(proto_reg),
|
||||
Operand::Reg(parent_proto_reg),
|
||||
Operand::Reg(proto_link_key_reg),
|
||||
));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP,
|
||||
Operand::Reg(ctor_reg),
|
||||
Operand::Reg(super_reg),
|
||||
Operand::Reg(proto_link_key_reg),
|
||||
));
|
||||
}
|
||||
let ctor_key_idx = ctx.add_constant(Constant::String("constructor".to_string()));
|
||||
let ctor_key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(ctor_key_reg), ctor_key_idx));
|
||||
ctx.inst(Inst::new(OpCode::SET_PROP, Operand::Reg(proto_reg), Operand::Reg(ctor_reg), Operand::Reg(ctor_key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP,
|
||||
Operand::Reg(proto_reg),
|
||||
Operand::Reg(ctor_reg),
|
||||
Operand::Reg(ctor_key_reg),
|
||||
));
|
||||
let proto_key_idx = ctx.add_constant(Constant::String("prototype".to_string()));
|
||||
let proto_key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(proto_key_reg), proto_key_idx));
|
||||
ctx.inst(Inst::new(OpCode::SET_PROP, Operand::Reg(ctor_reg), Operand::Reg(proto_reg), Operand::Reg(proto_key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP,
|
||||
Operand::Reg(ctor_reg),
|
||||
Operand::Reg(proto_reg),
|
||||
Operand::Reg(proto_key_reg),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ use oxide_parser::{Expression, Statement};
|
||||
use crate::Emitter;
|
||||
|
||||
impl Emitter {
|
||||
|
||||
fn collect_fn_param_names(&self, params: &oxide_parser::FormalParameters) -> HashSet<String> {
|
||||
let mut names = HashSet::new();
|
||||
for p in ¶ms.items {
|
||||
@@ -288,7 +287,9 @@ impl Emitter {
|
||||
}
|
||||
Expression::StaticMemberExpression(m) => self.collect_capture_names_expr(&m.object, ref_set, shadow, out),
|
||||
Expression::PrivateFieldExpression(m) => self.collect_capture_names_expr(&m.object, ref_set, shadow, out),
|
||||
Expression::ParenthesizedExpression(p) => self.collect_capture_names_expr(&p.expression, ref_set, shadow, out),
|
||||
Expression::ParenthesizedExpression(p) => {
|
||||
self.collect_capture_names_expr(&p.expression, ref_set, shadow, out)
|
||||
}
|
||||
Expression::TemplateLiteral(tl) => {
|
||||
for e in &tl.expressions {
|
||||
self.collect_capture_names_expr(e, ref_set, shadow, out);
|
||||
@@ -348,11 +349,7 @@ impl Emitter {
|
||||
// 名字排序分配 cell_idx(稳定跨 run,父 MAKE_CELL 与子 upvalue 统一引用)
|
||||
let mut sorted: Vec<String> = names.into_iter().collect();
|
||||
sorted.sort();
|
||||
sorted
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, n)| (n, i as u8))
|
||||
.collect()
|
||||
sorted.into_iter().enumerate().map(|(i, n)| (n, i as u8)).collect()
|
||||
}
|
||||
|
||||
/// 只从嵌套函数节点进入扫描(本函数直接引用不算捕获)。
|
||||
@@ -498,7 +495,6 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
/// 分析子函数 body:引用父级绑定的名字 → upvalue_captures(enclosing_reg 由父 emit 完成后填充)。
|
||||
/// 分析子函数 body:引用父级被捕获绑定的名字 → upvalue_captures。
|
||||
/// cell_idx 直接取父 captured_bindings 映射(父 emit 前已确定,索引一致);
|
||||
/// enclosing_reg 由父 emit 完成后填充(assemble_ir)。
|
||||
@@ -509,9 +505,8 @@ impl Emitter {
|
||||
let mut names = HashSet::new();
|
||||
self.collect_capture_names_shadowed(body_stmts, &parent_names, sub_own, &mut names);
|
||||
// HashSet 迭代序带随机种子(进程级非确定),必须排序使 upvalue_captures 的顺序与
|
||||
// 父 captured_bindings 的 cell_idx(BTreeMap 名字序)对齐——否则 uv_idx(identifier.rs
|
||||
// LOAD_UPVALUE 的 a 槽编码)与 cell_idx 错位,读错 upvalue(hoisted_function_reads_outer_var_and_function
|
||||
// 抖动根因)。
|
||||
// 父 captured_bindings 的 cell_idx(BTreeMap 名字序)对齐——否则 LOAD_UPVALUE 的
|
||||
// a 槽 uv_idx 编码与 cell_idx 错位,读错 upvalue。
|
||||
let mut names: Vec<String> = names.into_iter().collect();
|
||||
names.sort();
|
||||
names
|
||||
|
||||
@@ -124,9 +124,8 @@ pub(crate) struct FieldBuffer {
|
||||
pub(crate) labels: Vec<(LabelId, usize)>,
|
||||
}
|
||||
|
||||
/// A labeled-statement scope active during emission. `break label` targets
|
||||
/// `break_label`; `continue label` targets `continue_label` (only set when the
|
||||
/// labeled statement directly wraps an iteration statement).
|
||||
/// 标签语句作用域:编译期内登记 `break label` / `continue label` 的跳转目标。
|
||||
/// `continue_label` 仅在标签直接包裹迭代语句时存在。
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LabelScope {
|
||||
pub(crate) name: String,
|
||||
@@ -146,9 +145,8 @@ pub struct CompileCtx {
|
||||
pub(crate) labels: LabelCtx,
|
||||
pub(crate) scopes: ScopeCtx,
|
||||
pub(crate) nested: Vec<IRFunction>,
|
||||
/// Register holding `this` in the enclosing function context.
|
||||
/// Used by arrow functions to capture lexical `this`.
|
||||
/// Initialized to 254 (conventional this register) at the top level.
|
||||
/// 外层函数上下文中持有 `this` 的寄存器。
|
||||
/// 箭头函数用它捕获词法 `this`;顶层初始化为 254(约定 this 寄存器)。
|
||||
pub(crate) enclosing_this_reg: u8,
|
||||
pub(crate) in_derived_constructor: bool,
|
||||
pub(crate) in_instance_method: bool,
|
||||
@@ -247,7 +245,7 @@ impl CompileCtx {
|
||||
|
||||
pub(crate) fn alloc_reg(&mut self) -> u32 {
|
||||
let r = self.next_reg;
|
||||
// vreg 化(D-02):寄存器号无上限,RegAlloc 阶段负责压缩到物理域(≤253)。
|
||||
// vreg 化:寄存器号无上限,RegAlloc 阶段负责压缩到物理域(≤253)。
|
||||
// 254/255 是 VM 保留的 this/new.target,vreg 世界允许虚拟号越过它们,
|
||||
// 只有 RegAlloc 完成映射后 lower 的物理域检查才相关。
|
||||
self.next_reg += 1;
|
||||
@@ -406,9 +404,8 @@ impl CompileCtx {
|
||||
self.labels.label_scopes.iter().rev().find(|s| s.name == name)
|
||||
}
|
||||
|
||||
/// Queue a label name to be bound to the continue target of the next loop
|
||||
/// emitted as the labeled statement's body. Rejects duplicates in the active
|
||||
/// or pending sets.
|
||||
/// 登记一个待绑定标签名:该标签将作为下一个 emit 的循环(标签语句体)的
|
||||
/// continue 目标。活动集合与待绑定集合中出现重名报错。
|
||||
pub(crate) fn queue_loop_label(&mut self, name: &str) -> Result<(), String> {
|
||||
if self.labels.label_scopes.iter().any(|s| s.name == name)
|
||||
|| self.labels.pending_loop_labels.iter().any(|n| n == name)
|
||||
@@ -419,8 +416,8 @@ impl CompileCtx {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Drain queued loop labels into active scopes bound to this loop's break and
|
||||
/// continue targets. Returns how many scopes were pushed (to pop after).
|
||||
/// 把待绑定标签名落地为活动标签作用域,绑定到本次循环的 break/continue 目标。
|
||||
/// 返回压入的作用域个数(供事后对称弹出)。
|
||||
pub(crate) fn take_pending_loop_labels(&mut self, break_label: LabelId, continue_label: LabelId) -> usize {
|
||||
let names = std::mem::take(&mut self.labels.pending_loop_labels);
|
||||
let count = names.len();
|
||||
@@ -458,9 +455,8 @@ impl CompileCtx {
|
||||
}
|
||||
|
||||
pub(crate) fn pre_register_builtins(&mut self) {
|
||||
// Builtin globals are resolved lazily by lookup_or_builtin(). Keeping this
|
||||
// hook preserves the compile pipeline without reserving ~60 registers in
|
||||
// every module.
|
||||
// builtin 全局由 lookup_or_builtin() 惰性解析。保留此钩子维持编译管线形态,
|
||||
// 避免在每个模块预留约 60 个寄存器槽。
|
||||
}
|
||||
|
||||
/// 组装 IRFunction(两出口共用),take 走编译产物状态。
|
||||
@@ -546,13 +542,11 @@ impl Emitter {
|
||||
Ok((param_specs, body_stmts))
|
||||
}
|
||||
|
||||
/// Compile a function body (used for FD, FE, and arrow functions).
|
||||
/// This performs both counting and emitting in one pass.
|
||||
/// When `is_expression_body` is true (arrow function with expression body),
|
||||
/// the last expression's value is returned instead of undefined.
|
||||
/// `is_arrow` controls whether super flags are inherited from the parent scope
|
||||
/// (true for arrow functions, which have lexical super) or reset to false
|
||||
/// (false for regular functions, which create a new super scope).
|
||||
/// 编译函数体(函数声明/函数表达式/箭头函数共用),单 pass 完成发码。
|
||||
///
|
||||
/// `is_expression_body` 为 true(箭头表达式体)时返回最后一个表达式的值,
|
||||
/// 否则返回 undefined。`is_arrow` 控制 super 相关标志的继承:箭头函数词法
|
||||
/// 继承外层 super,普通函数重置 super 作用域。
|
||||
pub(crate) fn compile_function_body<'a>(
|
||||
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
|
||||
is_expression_body: bool, is_arrow: bool,
|
||||
@@ -588,13 +582,13 @@ impl Emitter {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/// Pre-register all builtin identifiers referenced anywhere in this body
|
||||
/// (expressions, member objects, call args, class fields, etc.) so their
|
||||
/// register slots are reserved *before* any temporary register is emitted.
|
||||
/// 预注册 builtin 引用并编译函数体(普通/箭头/类元素方法共用入口)。
|
||||
///
|
||||
/// vreg 化(D-01)后临时值不复用(独立 vreg),但 builtin 槽预注册仍
|
||||
/// 保证分配序稳定:builtin 槽先于临时值池,nested 继承边界(B012)不受扰。
|
||||
/// 本函数体内任意位置(表达式、成员对象、调用实参、类字段等)引用的内置全局
|
||||
/// 标识符,其寄存器槽都先于任何临时寄存器登记。
|
||||
///
|
||||
/// vreg 化后临时值不复用(独立 vreg),但 builtin 槽预注册仍保证分配序稳定:
|
||||
/// builtin 槽先于临时值池,嵌套函数继承边界不受扰。
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn compile_function_body_with_field_hooks<'a, E>(
|
||||
&self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
|
||||
@@ -606,16 +600,16 @@ impl Emitter {
|
||||
{
|
||||
let mut ctx = CompileCtx::new();
|
||||
|
||||
// Inherit parent's builtin_reg_map so builtin identifiers (Math, Object, etc.)
|
||||
// resolve to the correct pre-allocated registers in the sub-module's register file.
|
||||
// 继承父内置寄存器映射:子模块寄存器文件中,内置标识符(Math、Object 等)
|
||||
// 解析到父预先分配的槽位。
|
||||
ctx.scopes.builtin_reg_map = parent_ctx.scopes.builtin_reg_map.clone();
|
||||
ctx.scopes.private_name_map = parent_ctx.scopes.private_name_map.clone();
|
||||
ctx.scopes.next_private_name_id = parent_ctx.scopes.next_private_name_id;
|
||||
|
||||
// Propagate enclosing_this_reg so nested arrow functions capture the correct `this`.
|
||||
// 传递 enclosing_this_reg:嵌套箭头函数捕获正确的 `this`。
|
||||
ctx.enclosing_this_reg = parent_ctx.enclosing_this_reg;
|
||||
// Arrow functions inherit lexical super. Class method bodies also need the
|
||||
// class-provided super context for their top-level body compilation.
|
||||
|
||||
// 箭头函数词法继承 super;类方法体顶层编译也需要类提供的 super 上下文。
|
||||
if matches!(body_context, FunctionBodyContext::Arrow | FunctionBodyContext::ClassElement) {
|
||||
ctx.in_derived_constructor = parent_ctx.in_derived_constructor;
|
||||
ctx.in_instance_method = parent_ctx.in_instance_method;
|
||||
@@ -626,8 +620,7 @@ impl Emitter {
|
||||
ctx.in_static_method = false;
|
||||
}
|
||||
|
||||
// Inherit parent's global scope entries so previously-declared function names
|
||||
// are visible from within the body.
|
||||
// 继承父全局作用域条目:先前声明的函数名在函数体内可见。
|
||||
let mut inherited_reg_start = 1u32.max(ctx.builtin_reg_floor());
|
||||
for (name, binding) in &parent_ctx.scopes.symbols.scopes[0].bindings {
|
||||
ctx.scopes.symbols.scopes[0].bindings.insert(
|
||||
@@ -653,20 +646,17 @@ impl Emitter {
|
||||
}
|
||||
ctx.reserved_reg_start = inherited_reg_start.max(1);
|
||||
|
||||
// Align next_reg with builtin count so both count and emit passes start at the
|
||||
// same register offset (params go after builtin slots).
|
||||
// 让 next_reg 与 builtin 槽位对齐,参数在 builtin 槽之后分配。
|
||||
ctx.reset_regs();
|
||||
|
||||
let param_base = self.emit_params_prologue(param_specs, body_stmts, parent_ctx, &mut ctx, body_context)?;
|
||||
|
||||
self.predeclare_function_declarations(body_stmts, &mut ctx);
|
||||
|
||||
// Pre-register builtin identifier references before emitting any temporary
|
||||
// register, so builtin slots never collide with reused temporaries.
|
||||
// 预注册 builtin 引用(先于任何临时寄存器),builtin 槽不与被复用的临时值冲突。
|
||||
self.pre_register_builtin_references(body_stmts, &mut ctx);
|
||||
|
||||
// Pre-declare `var` names so hoisted function declarations (emitted in the
|
||||
// first sub-pass below) can resolve the outer vars they close over.
|
||||
// 预声明 `var` 名,使首个 sub-pass 中提升的函数声明能解析其闭包引用的外层 var。
|
||||
self.predeclare_var_declarations(body_stmts, &mut ctx);
|
||||
|
||||
if let Some(emit) = emit_fields.as_mut() {
|
||||
@@ -692,11 +682,10 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
// Emit body statements(先函数声明 hoisting,再其余)。
|
||||
// 发 body 语句(先函数声明 hoisting,再其余)。
|
||||
let last_result_reg = self.emit_body_stmts(body_stmts, &mut ctx)?;
|
||||
|
||||
// Emit implicit RETURN: expression body returns the last expression,
|
||||
// statement body returns undefined.
|
||||
// 隐式 RETURN:表达式体返回最后表达式,语句体返回 undefined。
|
||||
if is_expression_body {
|
||||
if let Some(reg) = last_result_reg {
|
||||
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(reg), Operand::None, Operand::None));
|
||||
@@ -731,7 +720,7 @@ impl Emitter {
|
||||
ctx.push_scope_with_kind(ScopeKind::FunctionScope);
|
||||
let param_base = ctx.next_reg;
|
||||
|
||||
// Emit parameters and destructuring prologue.
|
||||
// 发参数与解构 prologue。
|
||||
for spec in param_specs {
|
||||
let name = spec.register_name();
|
||||
let reg = ctx.alloc_reg();
|
||||
@@ -757,11 +746,16 @@ impl Emitter {
|
||||
let name = spec.register_name();
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||
let reg = ctx.lookup(name)?;
|
||||
ctx.inst(Inst::new(OpCode::MAKE_CELL, Operand::Reg(reg), Operand::Imm(cell_idx as u16), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::MAKE_CELL,
|
||||
Operand::Reg(reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
Operand::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Free variable analysis for upvalue capture (Ordinary + Arrow functions only)
|
||||
// 自由变量分析(仅普通/箭头函数):收集 upvalue 捕获。
|
||||
if matches!(body_context, FunctionBodyContext::Ordinary | FunctionBodyContext::Arrow) {
|
||||
ctx.current_upvalue_captures =
|
||||
self.collect_upvalue_names(body_stmts, &parent_ctx.captured_bindings, &ctx.own_bindings);
|
||||
@@ -850,35 +844,33 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a full program into an IRFunction (top-level module body).
|
||||
/// Split from the original `Compiler::compile`: this is the emit half;
|
||||
/// oxide_compiler::Compiler::compile calls this then `oxide_ir::lower::lower`.
|
||||
/// 把完整程序编译为顶层模块体的 IRFunction。
|
||||
///
|
||||
/// 调用方为 `oxide_compiler::Compiler::compile`:本函数完成 emit 半程,
|
||||
/// 随后由 `oxide_ir::lower::lower` 降为字节码。
|
||||
pub fn emit_program(&self, program: &oxide_parser::Program) -> Result<IRFunction, String> {
|
||||
let mut ctx = CompileCtx::new();
|
||||
ctx.pre_register_builtins();
|
||||
self.predeclare_function_declarations(&program.body, &mut ctx);
|
||||
|
||||
// Pre-register builtin identifier references before any temporary register
|
||||
// is emitted, keeping builtin slots clear of the temporary register pool.
|
||||
// 预注册 builtin 引用(先于任何临时寄存器),builtin 槽不进入临时寄存器池。
|
||||
self.pre_register_builtin_references(&program.body, &mut ctx);
|
||||
|
||||
// Pre-declare top-level `var` names so hoisted function declarations
|
||||
// (emitted in the first sub-pass below) can resolve the outer vars.
|
||||
// 预声明顶层 `var` 名,使首个 sub-pass 中提升的函数声明能解析外层 var。
|
||||
self.predeclare_var_declarations(&program.body, &mut ctx);
|
||||
|
||||
// 闭包捕获分析(AST 级,emit 前确定)
|
||||
ctx.own_bindings = self.collect_own_binding_names(&[], &program.body);
|
||||
ctx.captured_bindings = self.collect_captured_bindings(&program.body, &ctx.own_bindings);
|
||||
|
||||
// First sub-pass: emit FunctionDeclarations (hoisting)
|
||||
// This ensures function objects are available before any code runs.
|
||||
// 首个 sub-pass:发函数声明(hoisting),保证任何代码运行前函数对象已就绪。
|
||||
for stmt in &program.body {
|
||||
if matches!(stmt, Statement::FunctionDeclaration(_)) {
|
||||
self.emit_statement(stmt, &mut ctx)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Second sub-pass: emit all other statements
|
||||
// 第二个 sub-pass:发其余所有语句。
|
||||
let mut last_result: Option<u32> = None;
|
||||
for stmt in &program.body {
|
||||
if matches!(stmt, Statement::FunctionDeclaration(_)) {
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
//! Grouped `CompileCtx` sub-contexts.
|
||||
//! `CompileCtx` 的成组子上下文。
|
||||
//!
|
||||
//! Splits the compiler's central `CompileCtx` so syntax-domain workers borrow
|
||||
//! only the slice they need: `LabelCtx` for jump-target resolution, `ScopeCtx`
|
||||
//! for identifier binding. `PatternCtx` is reserved for destructuring work.
|
||||
//! Execution-stream fields (insts, registers, pc, …) stay flat on
|
||||
//! `CompileCtx`.
|
||||
//! 把编译器中心 `CompileCtx` 拆开,语法域 worker 只借用所需切片:
|
||||
//! `LabelCtx` 负责跳转目标解析,`ScopeCtx` 负责标识符绑定。
|
||||
//! `PatternCtx` 为解构工作预留。执行流字段(insts/registers/pc 等)仍
|
||||
//! 平铺在 `CompileCtx` 上。
|
||||
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::LabelScope;
|
||||
use oxide_ir::operand::LabelId;
|
||||
|
||||
/// Jump-target / labeled-statement resolution state.
|
||||
/// 跳转目标 / 标签语句解析状态。
|
||||
pub(crate) struct LabelCtx {
|
||||
/// label id → 指令下标。id 连续递增,Vec 索引即 id;写入前须扩容。
|
||||
pub(crate) label_pos: Vec<Option<usize>>,
|
||||
pub(crate) loop_stack: Vec<(LabelId, LabelId)>,
|
||||
pub(crate) switch_stack: Vec<LabelId>,
|
||||
/// Active labeled-statement scopes (resolves `break label` / `continue label`).
|
||||
/// 活动标签语句作用域(解析 `break label` / `continue label`)。
|
||||
pub(crate) label_scopes: Vec<LabelScope>,
|
||||
/// Label names awaiting binding to the next emitted loop's continue target.
|
||||
/// 等待绑定到下一个循环 continue 目标的标签名。
|
||||
pub(crate) pending_loop_labels: Vec<String>,
|
||||
pub(crate) label_counter: u32,
|
||||
}
|
||||
@@ -33,7 +32,7 @@ impl LabelCtx {
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifier-binding state: symbols, builtin registers, private names.
|
||||
/// 标识符绑定状态:符号表、builtin 寄存器、私有名。
|
||||
pub(crate) struct ScopeCtx {
|
||||
pub(crate) symbols: SymbolTable,
|
||||
pub(crate) builtin_reg_map: Vec<(String, u32)>,
|
||||
@@ -41,7 +40,6 @@ pub(crate) struct ScopeCtx {
|
||||
pub(crate) next_private_name_id: u32,
|
||||
}
|
||||
|
||||
/// Reserved for destructuring-pattern state (Phase 15). Intentionally empty
|
||||
/// today; named so future pattern work has a home without touching `ScopeCtx`.
|
||||
/// 预留的解构 pattern 状态。当前为空;为后续解构工作预留归属地,避免改动 `ScopeCtx`。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct PatternCtx;
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
//! 函数:`emit_assignment_expression`。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::AssignmentOperator;
|
||||
|
||||
impl Emitter {
|
||||
@@ -22,13 +22,23 @@ impl Emitter {
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx));
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(obj_reg), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.inst(Inst::ic_get(Operand::Reg(result_reg), Operand::Reg(key_reg)));
|
||||
self.emit_logical_assign_test(logical_op, result_reg, store_label, end_label, ctx)?;
|
||||
ctx.labels.set_label_pos(store_label, ctx.insts.len());
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
ctx.inst(Inst::ic_set(Operand::Reg(obj_reg), Operand::Reg(val_reg), Operand::Reg(key_reg)));
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(val_reg), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||
return Ok(result_reg);
|
||||
}
|
||||
@@ -63,19 +73,39 @@ impl Emitter {
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::GET_PROP_DYNAMIC, Operand::Reg(obj_reg), Operand::Reg(key_reg), Operand::Reg(result_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(result_reg),
|
||||
));
|
||||
self.emit_logical_assign_test(logical_op, result_reg, store_label, end_label, ctx)?;
|
||||
ctx.labels.set_label_pos(store_label, ctx.insts.len());
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
ctx.inst(Inst::new(OpCode::SET_PROP_DYNAMIC, Operand::Reg(obj_reg), Operand::Reg(key_reg), Operand::Reg(val_reg)));
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(val_reg), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(val_reg),
|
||||
));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||
return Ok(result_reg);
|
||||
}
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
ctx.inst(Inst::new(OpCode::SET_PROP_DYNAMIC, Operand::Reg(obj_reg), Operand::Reg(key_reg), Operand::Reg(val_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(val_reg),
|
||||
));
|
||||
Ok(val_reg)
|
||||
} else if let oxide_parser::AssignmentTarget::PrivateFieldExpression(member) = &assign.left {
|
||||
if assign.operator != AssignmentOperator::Assign {
|
||||
@@ -84,7 +114,12 @@ impl Emitter {
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
|
||||
ctx.inst(Inst::new(OpCode::SET_PRIVATE, Operand::Reg(obj_reg), Operand::Reg(val_reg), Operand::Reg(key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PRIVATE,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
Ok(val_reg)
|
||||
} else if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(id_ref) = &assign.left {
|
||||
if assign.operator != AssignmentOperator::Assign {
|
||||
@@ -94,14 +129,29 @@ impl Emitter {
|
||||
let name = id_ref.name.as_str();
|
||||
let var_reg = ctx.lookup_or_global(name);
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(var_reg), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(var_reg),
|
||||
Operand::None,
|
||||
));
|
||||
self.emit_logical_assign_test(logical_op, result_reg, store_label, end_label, ctx)?;
|
||||
ctx.labels.set_label_pos(store_label, ctx.insts.len());
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
let is_const = ctx.lookup_const_flag(name);
|
||||
let const_flag = if is_const { 1 } else { 0 };
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(var_reg), Operand::Reg(val_reg), Operand::Imm(const_flag)));
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(val_reg), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(var_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(const_flag),
|
||||
));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||
Ok(result_reg)
|
||||
} else if assign.operator == AssignmentOperator::Addition
|
||||
@@ -143,20 +193,35 @@ impl Emitter {
|
||||
} else {
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
let name = id_ref.name.as_str();
|
||||
// Check if target is an upvalue reference
|
||||
// 目标若是 upvalue 引用,走 STORE_UPVALUE
|
||||
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
|
||||
ctx.inst(Inst::new(OpCode::STORE_UPVALUE, Operand::None, Operand::Reg(val_reg), Operand::Imm(uv_idx as u16)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_UPVALUE,
|
||||
Operand::None,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(uv_idx as u16),
|
||||
));
|
||||
return Ok(val_reg);
|
||||
}
|
||||
// Check if target is a captured cell
|
||||
// 目标若是被捕获 cell,走 CELL_SET
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||
ctx.inst(Inst::new(OpCode::CELL_SET, Operand::None, Operand::Reg(val_reg), Operand::Imm(cell_idx as u16)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::CELL_SET,
|
||||
Operand::None,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
return Ok(val_reg);
|
||||
}
|
||||
let var_reg = ctx.lookup_or_global(name);
|
||||
let is_const = ctx.lookup_const_flag(name);
|
||||
let const_flag = if is_const { 1 } else { 0 };
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(var_reg), Operand::Reg(val_reg), Operand::Imm(const_flag)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(var_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(const_flag),
|
||||
));
|
||||
Ok(val_reg)
|
||||
}
|
||||
} else if matches!(
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
//! 函数:`emit_call_expression`、`emit_call_domain`。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::Expression;
|
||||
|
||||
impl Emitter {
|
||||
@@ -41,7 +41,12 @@ impl Emitter {
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
|
||||
let callee_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::GET_PRIVATE, Operand::Reg(callee_reg), Operand::Reg(obj_reg), Operand::Reg(key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PRIVATE,
|
||||
Operand::Reg(callee_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
(callee_reg, obj_reg)
|
||||
}
|
||||
Expression::StaticMemberExpression(member) => {
|
||||
@@ -69,7 +74,12 @@ impl Emitter {
|
||||
};
|
||||
ctx.inst(Inst::new(op, Operand::Reg(callee_reg), Operand::Reg(obj_reg), Operand::Reg(key_reg)));
|
||||
} else {
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(callee_reg), Operand::Reg(obj_reg), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(callee_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.inst(Inst::ic_get(Operand::Reg(callee_reg), Operand::Reg(key_reg)));
|
||||
}
|
||||
(callee_reg, obj_reg)
|
||||
@@ -125,9 +135,9 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
/// 把调用参数打包到连续寄存器块(VM 按 `regs[first_arg + i]` 连续读参数,D-17/调用契约)。
|
||||
/// vreg 化(05-02)后各参数由独立 vreg 承载,复杂表达式(对象/数组字面量、嵌套调用)
|
||||
/// 的临时寄存器会使参数 vreg 不连续——这里检测到不连续时用 MOV 打包到新连续块。
|
||||
/// 把调用参数打包到连续寄存器块(VM 按 `regs[first_arg + i]` 连续读参数)。
|
||||
/// 各参数由独立 vreg 承载,复杂表达式(对象/数组字面量、嵌套调用)的临时寄存器
|
||||
/// 会使参数 vreg 不连续——检测到不连续时用 MOV 打包到新连续块。
|
||||
/// 返回首参寄存器;参数为空时返回 0。
|
||||
fn pack_arg_regs(arg_regs: &mut [u32], ctx: &mut CompileCtx) -> u32 {
|
||||
let consecutive = arg_regs.windows(2).all(|w| w[1] == w[0] + 1);
|
||||
|
||||
@@ -11,12 +11,12 @@ impl Emitter {
|
||||
fn emit_arrow_function_expression(
|
||||
&self, arrow: &oxide_parser::ArrowFunctionExpression, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
// Rest params not yet supported
|
||||
// 未支持:箭头函数 rest 参数
|
||||
if let Some(_rest) = &arrow.params.rest {
|
||||
return Err("rest params in arrow functions not yet supported".into());
|
||||
}
|
||||
|
||||
// Extract param names (same pattern as FunctionExpression)
|
||||
// 提取形参名(与函数表达式相同的形态)
|
||||
let mut param_names = Vec::new();
|
||||
for (idx, param) in arrow.params.items.iter().enumerate() {
|
||||
match ¶m.pattern {
|
||||
@@ -32,8 +32,7 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
// Expression body: pass body statements directly with is_expression_body=true.
|
||||
// Statement body: pass body statements with is_expression_body=false.
|
||||
// 表达式体:以 is_expression_body=true 编译;语句体:以 false 编译。
|
||||
let body_stmts = &arrow.body.statements;
|
||||
let is_expr_body = arrow.expression;
|
||||
|
||||
@@ -41,7 +40,7 @@ impl Emitter {
|
||||
sub_module.is_arrow = true;
|
||||
|
||||
ctx.nested.push(sub_module);
|
||||
// 1-indexed: 0 = no sub_module (sentinel)
|
||||
// 子模块下标 1 起始:0 保留为无子模块哨兵
|
||||
let sub_idx = ctx.nested.len() as u16;
|
||||
|
||||
let r = ctx.alloc_reg();
|
||||
@@ -50,7 +49,7 @@ impl Emitter {
|
||||
}
|
||||
|
||||
fn emit_function_expression(&self, fe: &oxide_parser::Function, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
// FunctionExpression: compile body, emit LOAD_CONST(BytecodeFunc)
|
||||
// 函数表达式:编译函数体为子模块,create_closure 实例化闭包
|
||||
let mut param_names = Vec::new();
|
||||
for (idx, param) in fe.params.items.iter().enumerate() {
|
||||
match ¶m.pattern {
|
||||
@@ -73,7 +72,7 @@ impl Emitter {
|
||||
sub_module.function_name = Some(id.name.to_string());
|
||||
}
|
||||
ctx.nested.push(sub_module);
|
||||
// 1-indexed: 0 = no sub_module (sentinel)
|
||||
// 子模块下标 1 起始:0 保留为无子模块哨兵
|
||||
let sub_idx = ctx.nested.len() as u16;
|
||||
|
||||
let r = ctx.alloc_reg();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! 标识符表达式 emit:`emit_identifier_expression` 按绑定/builtin/全局解析寄存器。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
|
||||
impl Emitter {
|
||||
pub(crate) fn emit_identifier_expression(
|
||||
@@ -23,7 +23,12 @@ impl Emitter {
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||
let r = ctx.alloc_reg();
|
||||
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) {
|
||||
ctx.inst(Inst::new(OpCode::CELL_GET, Operand::Reg(r), Operand::Reg(binding.reg), Operand::Imm(cell_idx as u16)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::CELL_GET,
|
||||
Operand::Reg(r),
|
||||
Operand::Reg(binding.reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
} else {
|
||||
ctx.inst(Inst::new(OpCode::CELL_GET, Operand::Reg(r), Operand::None, Operand::Imm(cell_idx as u16)));
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
//! 整数走立即数编码(`is_int_literal`),其余入常量池。
|
||||
|
||||
use crate::{is_int_literal, CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::Expression;
|
||||
|
||||
impl Emitter {
|
||||
@@ -74,7 +74,12 @@ impl Emitter {
|
||||
let flags_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(flags_reg), flags_ci));
|
||||
let r = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::CREATE_REGEXP, Operand::Reg(r), Operand::Reg(pat_reg), Operand::Reg(flags_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::CREATE_REGEXP,
|
||||
Operand::Reg(r),
|
||||
Operand::Reg(pat_reg),
|
||||
Operand::Reg(flags_reg),
|
||||
));
|
||||
Ok(r)
|
||||
} else {
|
||||
Err(format!("unsupported regexp literal: {:?}", lit))
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
//! `emit_private_field_expression`、`emit_chain_expression`、`emit_member_domain`。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::Expression;
|
||||
|
||||
impl Emitter {
|
||||
@@ -47,7 +47,12 @@ impl Emitter {
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
let r = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::GET_PROP_DYNAMIC, Operand::Reg(obj_reg), Operand::Reg(key_reg), Operand::Reg(r)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(r),
|
||||
));
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
@@ -57,16 +62,28 @@ impl Emitter {
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
|
||||
let r = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::GET_PRIVATE, Operand::Reg(r), Operand::Reg(obj_reg), Operand::Reg(key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PRIVATE,
|
||||
Operand::Reg(r),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
fn emit_chain_expression(&self, chain: &oxide_parser::ChainExpression, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
fn emit_chain_expression(
|
||||
&self, chain: &oxide_parser::ChainExpression, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
let short_label = ctx.next_label_id();
|
||||
let end_label = ctx.next_label_id();
|
||||
let value_reg = self.emit_chain_element(&chain.expression, Some(short_label), ctx)?;
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(value_reg), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(value_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.inst(Inst::jmp(end_label));
|
||||
ctx.labels.set_label_pos(short_label, ctx.insts.len());
|
||||
let undefined_idx = ctx.add_constant(Constant::Undefined);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! 数组字面量 emit:`emit_array_expression` 逐元素求值并建数组(含 holes)。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
impl Emitter {
|
||||
pub(crate) fn emit_array_expression(
|
||||
&self, arr: &oxide_parser::ArrayExpression, ctx: &mut CompileCtx,
|
||||
@@ -20,7 +20,12 @@ impl Emitter {
|
||||
let idx_reg = ctx.alloc_reg();
|
||||
let idx = ctx.add_constant(Constant::Int(i as i32));
|
||||
ctx.inst(Inst::load_const(Operand::Reg(idx_reg), idx));
|
||||
ctx.inst(Inst::new(OpCode::SET_ELEM, Operand::Reg(arr_reg), Operand::Reg(idx_reg), Operand::Reg(val_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_ELEM,
|
||||
Operand::Reg(arr_reg),
|
||||
Operand::Reg(idx_reg),
|
||||
Operand::Reg(val_reg),
|
||||
));
|
||||
}
|
||||
Ok(arr_reg)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! 对象字面量 emit:`emit_object_expression` 逐属性定义(含 getter/setter/展开)。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{Expression, ObjectPropertyKind, PropertyKey, PropertyKind};
|
||||
|
||||
impl Emitter {
|
||||
@@ -52,7 +52,12 @@ impl Emitter {
|
||||
sub_mod.function_name = Some(prop_name.to_string());
|
||||
}
|
||||
}
|
||||
ctx.inst(Inst::new(OpCode::SET_PROP, Operand::Reg(obj_reg), Operand::Reg(val_reg), Operand::Reg(key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
//! 函数:`emit_operator` 及各类 `emit_*_expression`。
|
||||
|
||||
use crate::{is_side_effect_free, BinaryOperator, CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{ChainElement, Expression, LogicalOperator, SimpleAssignmentTarget, UnaryOperator, UpdateOperator};
|
||||
|
||||
impl Emitter {
|
||||
@@ -16,11 +16,18 @@ impl Emitter {
|
||||
let obj_reg = self.emit_expression(&pin.right, ctx)?;
|
||||
let key_reg = self.emit_private_id_reg(pin.left.name.as_str(), ctx)?;
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::PRIVATE_BRAND_IN, Operand::Reg(result_reg), Operand::Reg(obj_reg), Operand::Reg(key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::PRIVATE_BRAND_IN,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
Ok(result_reg)
|
||||
}
|
||||
|
||||
fn emit_binary_expression(&self, bin: &oxide_parser::BinaryExpression, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
fn emit_binary_expression(
|
||||
&self, bin: &oxide_parser::BinaryExpression, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
let left = self.emit_expression(&bin.left, ctx)?;
|
||||
let right = self.emit_expression(&bin.right, ctx)?;
|
||||
let op = match bin.operator {
|
||||
@@ -67,7 +74,12 @@ impl Emitter {
|
||||
Expression::ComputedMemberExpression(member) => {
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
ctx.inst(Inst::new(OpCode::DELETE_PROP_DYNAMIC, Operand::Reg(obj_reg), Operand::Reg(obj_reg), Operand::Reg(key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::DELETE_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
Ok(obj_reg)
|
||||
}
|
||||
Expression::ChainExpression(chain) => {
|
||||
@@ -77,7 +89,12 @@ impl Emitter {
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
if member.optional {
|
||||
let dup_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(dup_reg), Operand::Reg(obj_reg), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(dup_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.inst(Inst::jmp_if_nullish(dup_reg, short_label));
|
||||
}
|
||||
let prop_name = member.property.name.as_str();
|
||||
@@ -89,11 +106,21 @@ impl Emitter {
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
if member.optional {
|
||||
let dup_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(dup_reg), Operand::Reg(obj_reg), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(dup_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.inst(Inst::jmp_if_nullish(dup_reg, short_label));
|
||||
}
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
ctx.inst(Inst::new(OpCode::DELETE_PROP_DYNAMIC, Operand::Reg(obj_reg), Operand::Reg(obj_reg), Operand::Reg(key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::DELETE_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
obj_reg
|
||||
}
|
||||
_ => return Err("invalid delete target".into()),
|
||||
@@ -150,13 +177,23 @@ impl Emitter {
|
||||
|
||||
let cons_reg = self.emit_expression(&cond.consequent, ctx)?;
|
||||
let result_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(cons_reg), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(cons_reg),
|
||||
Operand::None,
|
||||
));
|
||||
|
||||
ctx.inst(Inst::jmp(end_label));
|
||||
|
||||
ctx.labels.set_label_pos(else_label, ctx.insts.len());
|
||||
let alt_reg = self.emit_expression(&cond.alternate, ctx)?;
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(alt_reg), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(alt_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||
|
||||
Ok(result_reg)
|
||||
@@ -220,13 +257,18 @@ impl Emitter {
|
||||
match &update.argument {
|
||||
SimpleAssignmentTarget::AssignmentTargetIdentifier(id) => {
|
||||
let name = id.name.as_str();
|
||||
// Check if this is an upvalue or captured cell reference
|
||||
// 判断目标是 upvalue 还是被捕获 cell
|
||||
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
|
||||
let captured_cell = ctx.captured_bindings.get(name).copied();
|
||||
if let Some(uv) = uv_idx {
|
||||
// Upvalue: LOAD_UPVALUE + CONST(1) + ADD/SUB + STORE_UPVALUE
|
||||
// upvalue:LOAD_UPVALUE + 常量 1 + ADD/SUB + STORE_UPVALUE
|
||||
let val_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_UPVALUE, Operand::Reg(val_reg), Operand::Imm(uv as u16), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_UPVALUE,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(uv as u16),
|
||||
Operand::None,
|
||||
));
|
||||
let one_idx = ctx.add_constant(Constant::Int(1));
|
||||
let one_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx));
|
||||
@@ -236,15 +278,30 @@ impl Emitter {
|
||||
OpCode::SUB
|
||||
};
|
||||
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(one_reg)));
|
||||
ctx.inst(Inst::new(OpCode::STORE_UPVALUE, Operand::None, Operand::Reg(val_reg), Operand::Imm(uv as u16)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_UPVALUE,
|
||||
Operand::None,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(uv as u16),
|
||||
));
|
||||
Ok(val_reg)
|
||||
} else if let Some(cell_idx) = captured_cell {
|
||||
// Captured cell: CELL_GET + CONST(1) + ADD/SUB + CELL_SET
|
||||
// 被捕获 cell:CELL_GET + 常量 1 + ADD/SUB + CELL_SET
|
||||
let val_reg = ctx.alloc_reg();
|
||||
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) {
|
||||
ctx.inst(Inst::new(OpCode::CELL_GET, Operand::Reg(val_reg), Operand::Reg(binding.reg), Operand::Imm(cell_idx as u16)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::CELL_GET,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Reg(binding.reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
} else {
|
||||
ctx.inst(Inst::new(OpCode::CELL_GET, Operand::Reg(val_reg), Operand::None, Operand::Imm(cell_idx as u16)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::CELL_GET,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::None,
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
}
|
||||
let one_idx = ctx.add_constant(Constant::Int(1));
|
||||
let one_reg = ctx.alloc_reg();
|
||||
@@ -255,7 +312,12 @@ impl Emitter {
|
||||
OpCode::SUB
|
||||
};
|
||||
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(one_reg)));
|
||||
ctx.inst(Inst::new(OpCode::CELL_SET, Operand::None, Operand::Reg(val_reg), Operand::Imm(cell_idx as u16)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::CELL_SET,
|
||||
Operand::None,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
Ok(val_reg)
|
||||
} else {
|
||||
let var_reg = ctx.lookup_or_global(name);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! 标签模板表达式 emit:`emit_tagged_template_expression` 构造 template object 并调用标签函数。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
impl Emitter {
|
||||
pub(crate) fn emit_tagged_template_expression(
|
||||
&self, tt: &oxide_parser::TaggedTemplateExpression, ctx: &mut CompileCtx,
|
||||
@@ -15,7 +15,12 @@ impl Emitter {
|
||||
let tag_reg = self.emit_expression(&tt.tag, ctx)?;
|
||||
|
||||
let cooked_temp = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::NEW_ARRAY, Operand::Reg(cooked_temp), Operand::Imm(quasis.len() as u16), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::NEW_ARRAY,
|
||||
Operand::Reg(cooked_temp),
|
||||
Operand::Imm(quasis.len() as u16),
|
||||
Operand::None,
|
||||
));
|
||||
for (i, quasi) in quasis.iter().enumerate() {
|
||||
let s = quasi.value.cooked.as_ref().map(|c| c.to_string()).unwrap_or_default();
|
||||
let const_idx = ctx.add_constant(Constant::String(s));
|
||||
@@ -24,11 +29,21 @@ impl Emitter {
|
||||
let idx_const = ctx.add_constant(Constant::Int(i as i32));
|
||||
let idx_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(idx_reg), idx_const));
|
||||
ctx.inst(Inst::new(OpCode::SET_ELEM, Operand::Reg(cooked_temp), Operand::Reg(idx_reg), Operand::Reg(str_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_ELEM,
|
||||
Operand::Reg(cooked_temp),
|
||||
Operand::Reg(idx_reg),
|
||||
Operand::Reg(str_reg),
|
||||
));
|
||||
}
|
||||
|
||||
let raw_temp = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::NEW_ARRAY, Operand::Reg(raw_temp), Operand::Imm(quasis.len() as u16), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::NEW_ARRAY,
|
||||
Operand::Reg(raw_temp),
|
||||
Operand::Imm(quasis.len() as u16),
|
||||
Operand::None,
|
||||
));
|
||||
for (i, quasi) in quasis.iter().enumerate() {
|
||||
let raw = quasi.value.raw.to_string();
|
||||
let const_idx = ctx.add_constant(Constant::String(raw));
|
||||
@@ -37,7 +52,12 @@ impl Emitter {
|
||||
let idx_const = ctx.add_constant(Constant::Int(i as i32));
|
||||
let idx_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(idx_reg), idx_const));
|
||||
ctx.inst(Inst::new(OpCode::SET_ELEM, Operand::Reg(raw_temp), Operand::Reg(idx_reg), Operand::Reg(str_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_ELEM,
|
||||
Operand::Reg(raw_temp),
|
||||
Operand::Reg(idx_reg),
|
||||
Operand::Reg(str_reg),
|
||||
));
|
||||
}
|
||||
|
||||
let mut expr_temps = Vec::new();
|
||||
@@ -52,7 +72,12 @@ impl Emitter {
|
||||
expr_slots.push(ctx.alloc_reg());
|
||||
}
|
||||
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(cooked_slot), Operand::Reg(cooked_temp), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(cooked_slot),
|
||||
Operand::Reg(cooked_temp),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(raw_slot), Operand::Reg(raw_temp), Operand::None));
|
||||
for (slot, temp) in expr_slots.iter().zip(expr_temps.iter()) {
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(*slot), Operand::Reg(*temp), Operand::None));
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
//! 拼接字面量段与内插表达式。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
|
||||
impl Emitter {
|
||||
pub(crate) fn emit_template_literal_expression(
|
||||
@@ -42,12 +42,7 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
ctx.inst(Inst::template_str(
|
||||
Operand::Reg(r),
|
||||
segment_count as u32,
|
||||
total_len_hint as u16,
|
||||
&parts,
|
||||
));
|
||||
ctx.inst(Inst::template_str(Operand::Reg(r), segment_count as u32, total_len_hint as u16, &parts));
|
||||
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! `this` 表达式 emit:`emit_this_expression` 解析当前函数的 this 寄存器。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
|
||||
impl Emitter {
|
||||
pub(crate) fn emit_this_expression(&self, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
|
||||
@@ -15,7 +15,7 @@ pub mod shared;
|
||||
pub mod stmt;
|
||||
pub mod symbol_table;
|
||||
|
||||
pub use emit::{CompileCtx, Emitter, FunctionBodyContext, LabelScope, ParamSpec};
|
||||
pub use emit::{is_int_literal, is_side_effect_free};
|
||||
pub use emit::{CompileCtx, Emitter, FunctionBodyContext, LabelScope, ParamSpec};
|
||||
pub use oxide_bytecode::module::Constant;
|
||||
pub use oxide_parser::{AssignmentOperator, BinaryOperator, UnaryOperator};
|
||||
|
||||
@@ -8,8 +8,7 @@ use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::{Expression, Statement, VariableDeclarationKind};
|
||||
|
||||
impl Emitter {
|
||||
|
||||
/// builtin slot allocation ahead of the temporary register pool.
|
||||
/// 在临时寄存器池之前分配 builtin 槽位。
|
||||
pub(crate) fn pre_register_builtin_references(&self, stmts: &[Statement], ctx: &mut CompileCtx) {
|
||||
for stmt in stmts {
|
||||
self.pre_scan_builtin_stmt(stmt, ctx);
|
||||
@@ -264,7 +263,6 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub(crate) fn predeclare_function_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) {
|
||||
for statement in statements {
|
||||
let Statement::FunctionDeclaration(function) = statement else {
|
||||
@@ -278,10 +276,8 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pre-declare all `var` bindings in this statement list so hoisted function
|
||||
/// declarations emitted earlier can resolve them. Mirrors the removed count
|
||||
/// pass behavior (see fe8bd86): top-level `var` names must be visible while
|
||||
/// compiling function bodies that close over them.
|
||||
/// 预声明语句列表中的全部 `var` 绑定,使先发的提升函数声明能解析它们。
|
||||
/// 顶层 `var` 名在编译闭包捕获它的函数体时必须可见。
|
||||
pub(crate) fn predeclare_var_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) {
|
||||
for statement in statements {
|
||||
match statement {
|
||||
@@ -315,8 +311,12 @@ impl Emitter {
|
||||
for d in &decl.declarations {
|
||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||
let reg = ctx.alloc_reg();
|
||||
let _ =
|
||||
ctx.declare_initialized(bi.name.as_str(), reg, VariableDeclarationKind::Var, false);
|
||||
let _ = ctx.declare_initialized(
|
||||
bi.name.as_str(),
|
||||
reg,
|
||||
VariableDeclarationKind::Var,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -344,5 +344,4 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
//! `emit_object_binding`、`emit_assign_target` 等。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{
|
||||
ArrayAssignmentTarget, ArrayPattern, AssignmentTarget, AssignmentTargetMaybeDefault, AssignmentTargetProperty,
|
||||
BindingPattern, Expression, ObjectAssignmentTarget, ObjectPattern, VariableDeclarationKind,
|
||||
@@ -18,12 +18,22 @@ impl Emitter {
|
||||
) -> Result<u32, String> {
|
||||
let undef_reg = self.emit_undefined(ctx);
|
||||
let eq_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::STRICT_EQ, Operand::Reg(eq_reg), Operand::Reg(val_reg), Operand::Reg(undef_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STRICT_EQ,
|
||||
Operand::Reg(eq_reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Reg(undef_reg),
|
||||
));
|
||||
// val != undefined(eq 为 false)→ 跳过默认值
|
||||
let end_label = ctx.next_label_id();
|
||||
ctx.inst(Inst::jmp_if_false(eq_reg, end_label));
|
||||
let default_reg = self.emit_expression(default_expr, ctx)?;
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(val_reg), Operand::Reg(default_reg), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Reg(default_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||
Ok(val_reg)
|
||||
}
|
||||
@@ -32,8 +42,7 @@ impl Emitter {
|
||||
&self, name: &str, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, ctx: &mut CompileCtx,
|
||||
) -> Result<(), String> {
|
||||
let target_reg = if matches!(kind, VariableDeclarationKind::Var) {
|
||||
// `var` names are pre-declared (hoisting); reuse the pre-registered slot
|
||||
// instead of allocating a new one so n_registers matches let/const.
|
||||
// `var` 名已预声明(hoisting),复用预登记槽位而非新分配,使 n_registers 与 let/const 一致。
|
||||
if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
|
||||
reg
|
||||
} else {
|
||||
@@ -47,13 +56,23 @@ impl Emitter {
|
||||
var_reg
|
||||
};
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||
ctx.inst(Inst::new(OpCode::MAKE_CELL, Operand::Reg(src_reg), Operand::Imm(cell_idx as u16), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::MAKE_CELL,
|
||||
Operand::Reg(src_reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
Operand::None,
|
||||
));
|
||||
} else {
|
||||
// B011: const 声明路径 STORE_VAR 恒 b=0,不查运行时 guard。
|
||||
// const 声明路径 STORE_VAR 恒 b=0,不查运行时 guard。
|
||||
// guard 读 regs[rd] 判断槽是否已初始化,依赖槽初始为 undefined;
|
||||
// 声明冗余检查由编译期 declare() 完成(重复声明编译报错),
|
||||
// 运行时 guard 只服务"对 const 再赋值"(赋值路径,见 emit_assign_target)。
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(target_reg), Operand::Reg(src_reg), Operand::Imm(0)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(target_reg),
|
||||
Operand::Reg(src_reg),
|
||||
Operand::Imm(0),
|
||||
));
|
||||
}
|
||||
ctx.init_var(name);
|
||||
Ok(())
|
||||
@@ -132,9 +151,19 @@ impl Emitter {
|
||||
ctx.inst(Inst::jmp_if_false(has_reg, loop_end));
|
||||
let val_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None));
|
||||
ctx.inst(Inst::new(OpCode::SET_ELEM, Operand::Reg(rest_reg), Operand::Reg(idx_reg), Operand::Reg(val_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_ELEM,
|
||||
Operand::Reg(rest_reg),
|
||||
Operand::Reg(idx_reg),
|
||||
Operand::Reg(val_reg),
|
||||
));
|
||||
let tmp_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::INC_PRE, Operand::Reg(idx_reg), Operand::Reg(tmp_reg), Operand::Reg(tmp_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::INC_PRE,
|
||||
Operand::Reg(idx_reg),
|
||||
Operand::Reg(tmp_reg),
|
||||
Operand::Reg(tmp_reg),
|
||||
));
|
||||
ctx.inst(Inst::jmp(loop_start));
|
||||
ctx.labels.set_label_pos(loop_end, ctx.insts.len());
|
||||
Ok(rest_reg)
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
//! 函数:`emit_chainable_expression`、`emit_optional_guard`、`emit_chain_call` 等。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::{LabelId, Operand};
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::{LabelId, Operand};
|
||||
use oxide_parser::{ChainElement, Expression, LogicalOperator, PropertyKey};
|
||||
|
||||
impl Emitter {
|
||||
@@ -45,7 +45,12 @@ impl Emitter {
|
||||
}
|
||||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||||
let value_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::GET_PROP_DYNAMIC, Operand::Reg(obj_reg), Operand::Reg(key_reg), Operand::Reg(value_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PROP_DYNAMIC,
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(value_reg),
|
||||
));
|
||||
Ok((value_reg, obj_reg))
|
||||
}
|
||||
|
||||
@@ -68,7 +73,12 @@ impl Emitter {
|
||||
}
|
||||
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
|
||||
let callee_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::GET_PRIVATE, Operand::Reg(callee_reg), Operand::Reg(obj_reg), Operand::Reg(key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PRIVATE,
|
||||
Operand::Reg(callee_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
(callee_reg, obj_reg)
|
||||
}
|
||||
_ => {
|
||||
@@ -152,7 +162,12 @@ impl Emitter {
|
||||
}
|
||||
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
|
||||
let value_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::GET_PRIVATE, Operand::Reg(value_reg), Operand::Reg(obj_reg), Operand::Reg(key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PRIVATE,
|
||||
Operand::Reg(value_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
Ok(value_reg)
|
||||
}
|
||||
Expression::CallExpression(call) => self.emit_chain_call(call, short_label, ctx),
|
||||
@@ -182,7 +197,12 @@ impl Emitter {
|
||||
}
|
||||
let key_reg = self.emit_private_id_reg(member.field.name.as_str(), ctx)?;
|
||||
let value_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::GET_PRIVATE, Operand::Reg(value_reg), Operand::Reg(obj_reg), Operand::Reg(key_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PRIVATE,
|
||||
Operand::Reg(value_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(key_reg),
|
||||
));
|
||||
Ok(value_reg)
|
||||
}
|
||||
ChainElement::CallExpression(call) => self.emit_chain_call(call, short_label, ctx),
|
||||
@@ -204,7 +224,7 @@ impl Emitter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn emit_object_property_read( &self, src_reg: u32, key: &str, ctx: &mut CompileCtx) -> u32 {
|
||||
pub(crate) fn emit_object_property_read(&self, src_reg: u32, key: &str, ctx: &mut CompileCtx) -> u32 {
|
||||
let prop_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(prop_reg), Operand::Reg(src_reg), Operand::None));
|
||||
let key_idx = ctx.add_constant(Constant::String(key.to_string()));
|
||||
@@ -257,7 +277,12 @@ impl Emitter {
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(prop_reg), Operand::Reg(src_reg), Operand::None));
|
||||
let key_reg = self.emit_property_key_expression(key, ctx)?;
|
||||
let val_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::GET_PROP_DYNAMIC, Operand::Reg(prop_reg), Operand::Reg(key_reg), Operand::Reg(val_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::GET_PROP_DYNAMIC,
|
||||
Operand::Reg(prop_reg),
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(val_reg),
|
||||
));
|
||||
Ok((val_reg, None))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! 类字段初始化 emit:公有/私有实例字段与私有方法的实例化初始化。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{Expression, PropertyKey};
|
||||
|
||||
impl Emitter {
|
||||
@@ -17,7 +17,12 @@ impl Emitter {
|
||||
self.emit_undefined(ctx)
|
||||
};
|
||||
if computed {
|
||||
ctx.inst(Inst::new(OpCode::SET_PROP_DYNAMIC, target, Operand::Reg(key_reg), Operand::Reg(value_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::SET_PROP_DYNAMIC,
|
||||
target,
|
||||
Operand::Reg(key_reg),
|
||||
Operand::Reg(value_reg),
|
||||
));
|
||||
} else {
|
||||
ctx.inst(Inst::new(OpCode::SET_PROP, target, Operand::Reg(value_reg), Operand::Reg(key_reg)));
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
//! 函数:`private_name_id`、`emit_private_id_reg`、`emit_class_key_reg` 等。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_parser::PropertyKey;
|
||||
|
||||
impl Emitter {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! return 语句 emit:`emit_return_statement` 求值返回值并生成返回指令。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
impl Emitter {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! 控制流语句 emit:`if` 条件分支(含 else)与域分发 `emit_control_domain`。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
impl Emitter {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! 类声明语句 emit:`emit_class_declaration_statement` 声明类名并初始化类对象。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{Statement, VariableDeclarationKind};
|
||||
|
||||
impl Emitter {
|
||||
@@ -23,7 +23,12 @@ impl Emitter {
|
||||
ctx.init_var(&name);
|
||||
let ctor_reg = self.emit_class(class, ctx)?;
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(&name) {
|
||||
ctx.inst(Inst::new(OpCode::MAKE_CELL, Operand::Reg(var_reg), Operand::Imm(cell_idx as u16), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::MAKE_CELL,
|
||||
Operand::Reg(var_reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
Operand::None,
|
||||
));
|
||||
} else {
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(var_reg), Operand::Reg(ctor_reg), Operand::None));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! 函数声明语句 emit:`emit_function_declaration_statement` 创建闭包并绑定函数名。
|
||||
|
||||
use crate::{CompileCtx, Emitter, ParamSpec};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
impl Emitter {
|
||||
@@ -38,7 +38,12 @@ impl Emitter {
|
||||
ctx.reserve_reg(var_reg);
|
||||
ctx.inst(Inst::create_closure(Operand::Reg(var_reg), ctx.nested.len() as u16));
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(&name) {
|
||||
ctx.inst(Inst::new(OpCode::MAKE_CELL, Operand::Reg(var_reg), Operand::Imm(cell_idx as u16), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::MAKE_CELL,
|
||||
Operand::Reg(var_reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
Operand::None,
|
||||
));
|
||||
} else {
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(var_reg), Operand::Reg(var_reg), Operand::None));
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ mod function_decl;
|
||||
mod var;
|
||||
|
||||
impl Emitter {
|
||||
pub(crate) fn emit_declaration_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
|
||||
pub(crate) fn emit_declaration_domain(
|
||||
&self, stmt: &Statement, ctx: &mut CompileCtx,
|
||||
) -> Result<Option<u32>, String> {
|
||||
match stmt {
|
||||
Statement::VariableDeclaration(_) => self.emit_variable_declaration_statement(stmt, ctx),
|
||||
Statement::FunctionDeclaration(_) => self.emit_function_declaration_statement(stmt, ctx),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! var/let/const 声明语句 emit:声明 + 初始化表达式,处理提升与解构 pattern。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{BindingPattern, Expression, Statement, VariableDeclarationKind};
|
||||
|
||||
impl Emitter {
|
||||
@@ -49,7 +49,12 @@ impl Emitter {
|
||||
var_reg
|
||||
};
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(bi.name.as_str()) {
|
||||
ctx.inst(Inst::new(OpCode::MAKE_CELL, Operand::Reg(tmp), Operand::Imm(cell_idx as u16), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::MAKE_CELL,
|
||||
Operand::Reg(tmp),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
Operand::None,
|
||||
));
|
||||
} else {
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(target_reg), Operand::Reg(tmp), Operand::None));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! throw 语句 emit:`emit_throw_statement` 求值异常值并生成 THROW。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
impl Emitter {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! try/catch/finally 语句 emit:`emit_try_statement` 生成 TRY_BEGIN 与处理入口。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{Statement, VariableDeclarationKind};
|
||||
|
||||
impl Emitter {
|
||||
@@ -29,7 +29,12 @@ impl Emitter {
|
||||
last_try_result = Some(r);
|
||||
}
|
||||
}
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(last_try_result.unwrap_or(result_reg)), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(last_try_result.unwrap_or(result_reg)),
|
||||
Operand::None,
|
||||
));
|
||||
if has_catch {
|
||||
ctx.inst(Inst::new(OpCode::TRY_END, Operand::None, Operand::None, Operand::None));
|
||||
}
|
||||
@@ -54,7 +59,12 @@ impl Emitter {
|
||||
last_catch_result = Some(r);
|
||||
}
|
||||
}
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(last_catch_result.unwrap_or(result_reg)), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(last_catch_result.unwrap_or(result_reg)),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.pop_scope();
|
||||
}
|
||||
if has_finally {
|
||||
@@ -65,7 +75,12 @@ impl Emitter {
|
||||
last_finally_result = Some(r);
|
||||
}
|
||||
}
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(last_finally_result.unwrap_or(result_reg)), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(result_reg),
|
||||
Operand::Reg(last_finally_result.unwrap_or(result_reg)),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.inst(Inst::new(OpCode::TRY_FINALLY_END, Operand::None, Operand::None, Operand::None));
|
||||
}
|
||||
ctx.labels.set_label_pos(try_end_label, ctx.insts.len());
|
||||
|
||||
@@ -5,7 +5,9 @@ use oxide_ir::inst::Inst;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
impl Emitter {
|
||||
pub(crate) fn emit_do_while_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
|
||||
pub(crate) fn emit_do_while_statement(
|
||||
&self, stmt: &Statement, ctx: &mut CompileCtx,
|
||||
) -> Result<Option<u32>, String> {
|
||||
let Statement::DoWhileStatement(dw) = stmt else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! for 语句 emit:`emit_for_statement` 含 init/test/update 三段与循环跳转。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{BindingPattern, ForStatementInit, Statement, VariableDeclarationKind};
|
||||
|
||||
impl Emitter {
|
||||
@@ -40,7 +40,12 @@ impl Emitter {
|
||||
ctx.declare(bi.name.as_str(), var_reg, decl.kind, is_const)?;
|
||||
var_reg
|
||||
};
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(target_reg), Operand::Reg(tmp), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(target_reg),
|
||||
Operand::Reg(tmp),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.init_var(bi.name.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! for-in 语句 emit:`emit_for_in_statement` 遍历可枚举属性名。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{ForStatementLeft, Statement, VariableDeclarationKind};
|
||||
|
||||
impl Emitter {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! for-of 语句 emit:`emit_for_of_statement` 迭代器协议遍历。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{ForStatementLeft, Statement};
|
||||
|
||||
impl Emitter {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! switch 语句 emit:case 匹配链 + 穿落(fallthrough)与 default,见 `emit_switch_statement`。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
impl Emitter {
|
||||
@@ -22,7 +22,12 @@ impl Emitter {
|
||||
if let Some(test) = &case.test {
|
||||
let test_reg = self.emit_expression(test, ctx)?;
|
||||
let eq_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::EQ, Operand::Reg(eq_reg), Operand::Reg(disc_reg), Operand::Reg(test_reg)));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::EQ,
|
||||
Operand::Reg(eq_reg),
|
||||
Operand::Reg(disc_reg),
|
||||
Operand::Reg(test_reg),
|
||||
));
|
||||
ctx.inst(Inst::jmp_if_true(eq_reg, case_label));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,16 +284,16 @@ mod tests {
|
||||
#[test]
|
||||
fn var_hoists_to_function_scope() {
|
||||
let mut st = SymbolTable::new();
|
||||
// Global scope is FunctionScope
|
||||
// 全局作用域是函数作用域
|
||||
st.declare("x", 0, l(), false).unwrap();
|
||||
st.init_var("x");
|
||||
// Push a block scope
|
||||
// 压入块作用域
|
||||
st.push_scope();
|
||||
// var declaration inside block should go to function scope
|
||||
// 块内的 var 声明应落到函数作用域
|
||||
st.declare("y", 1, v(), false).unwrap();
|
||||
st.init_var("y");
|
||||
st.pop_scope();
|
||||
// y should be accessible (it was declared in function scope, not block scope)
|
||||
// y 可见(声明在函数作用域而非块作用域)
|
||||
assert_eq!(st.lookup("y").unwrap(), 1);
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ mod tests {
|
||||
st.declare("y", 1, v(), false).unwrap();
|
||||
st.pop_scope();
|
||||
st.push_scope();
|
||||
// Same function scope, so this should be a duplicate
|
||||
// 同一函数作用域,故应为重复声明
|
||||
assert!(st.declare("y", 2, v(), false).is_err());
|
||||
st.pop_scope();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user