mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 03:01:12 +08:00
feat: 搭建闭包捕获基础设施
This commit is contained in:
@@ -3,6 +3,7 @@ use std::collections::HashMap;
|
||||
use crate::emit_ctx::{LabelCtx, ScopeCtx};
|
||||
use crate::symbol_table::{Binding, SymbolTable};
|
||||
use oxide_bytecode::module::CompiledModule;
|
||||
use oxide_bytecode::module::UpvalueCapture;
|
||||
use oxide_bytecode::opcode::{self, OpCode};
|
||||
|
||||
pub use crate::hash::{compiled_module_hash, structural_hash};
|
||||
@@ -164,6 +165,7 @@ pub(crate) struct CompileCtx {
|
||||
/// constructor's instance-field code, added at the super() call site during body
|
||||
/// counting so projected_pc matches where the emit pass splices the field bytecode.
|
||||
pub(crate) after_super_count_words: Option<usize>,
|
||||
pub(crate) current_upvalue_captures: Vec<UpvalueCapture>,
|
||||
/// Set when alloc_reg() overflows into the reserved this/new.target range (≥254).
|
||||
/// Checked after each emit phase to produce a compile error rather than silent corruption.
|
||||
pub(crate) reg_overflow: bool,
|
||||
@@ -227,6 +229,7 @@ impl CompileCtx {
|
||||
builtin_reg_map: Vec::new(),
|
||||
private_name_map: Vec::new(),
|
||||
next_private_name_id: 1,
|
||||
cell_registry: Vec::new(),
|
||||
},
|
||||
projected_pc: 0,
|
||||
sub_modules: Vec::new(),
|
||||
@@ -238,6 +241,7 @@ impl CompileCtx {
|
||||
after_super_insert: None,
|
||||
after_super_inserted: false,
|
||||
after_super_count_words: None,
|
||||
current_upvalue_captures: Vec::new(),
|
||||
reg_overflow: false,
|
||||
const_overflow: false,
|
||||
jump_overflow: false,
|
||||
@@ -587,6 +591,203 @@ impl Compiler {
|
||||
Self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn analyze_upvalue_captures(
|
||||
&self, body_stmts: &[Statement], parent_ctx: &CompileCtx, nested_symbols: &SymbolTable,
|
||||
) -> (Vec<UpvalueCapture>, u8) {
|
||||
let mut captures: Vec<UpvalueCapture> = Vec::new();
|
||||
let mut seen: HashMap<String, usize> = HashMap::new();
|
||||
|
||||
for stmt in body_stmts {
|
||||
self.collect_upvalue_stmt(stmt, parent_ctx, nested_symbols, &mut captures, &mut seen);
|
||||
}
|
||||
|
||||
let count = captures.len() as u8;
|
||||
(captures, count)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn collect_upvalue_stmt(
|
||||
&self, stmt: &Statement, parent_ctx: &CompileCtx, nested_symbols: &SymbolTable,
|
||||
captures: &mut Vec<UpvalueCapture>, seen: &mut HashMap<String, usize>,
|
||||
) {
|
||||
match stmt {
|
||||
Statement::ExpressionStatement(es) => {
|
||||
self.collect_upvalue_expr(&es.expression, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
Statement::VariableDeclaration(vd) => {
|
||||
for decl in &vd.declarations {
|
||||
if let Some(init) = &decl.init {
|
||||
self.collect_upvalue_expr(init, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::ReturnStatement(rs) => {
|
||||
if let Some(expr) = &rs.argument {
|
||||
self.collect_upvalue_expr(expr, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
}
|
||||
Statement::ForStatement(fs) => {
|
||||
if let Some(init) = &fs.init {
|
||||
if let Some(e) = init.as_expression() {
|
||||
self.collect_upvalue_expr(e, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
}
|
||||
if let Some(test) = &fs.test {
|
||||
self.collect_upvalue_expr(test, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
if let Some(update) = &fs.update {
|
||||
self.collect_upvalue_expr(update, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
self.collect_upvalue_stmt(&fs.body, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
Statement::IfStatement(is) => {
|
||||
self.collect_upvalue_expr(&is.test, parent_ctx, nested_symbols, captures, seen);
|
||||
self.collect_upvalue_stmt(&is.consequent, parent_ctx, nested_symbols, captures, seen);
|
||||
if let Some(alt) = &is.alternate {
|
||||
self.collect_upvalue_stmt(alt, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
}
|
||||
Statement::WhileStatement(ws) => {
|
||||
self.collect_upvalue_expr(&ws.test, parent_ctx, nested_symbols, captures, seen);
|
||||
self.collect_upvalue_stmt(&ws.body, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
Statement::DoWhileStatement(dw) => {
|
||||
self.collect_upvalue_stmt(&dw.body, parent_ctx, nested_symbols, captures, seen);
|
||||
self.collect_upvalue_expr(&dw.test, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
Statement::BlockStatement(bs) => {
|
||||
for s in &bs.body {
|
||||
self.collect_upvalue_stmt(s, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
}
|
||||
Statement::TryStatement(ts) => {
|
||||
for s in &ts.block.body {
|
||||
self.collect_upvalue_stmt(s, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
if let Some(handler) = &ts.handler {
|
||||
for s in &handler.body.body {
|
||||
self.collect_upvalue_stmt(s, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
}
|
||||
if let Some(finalizer) = &ts.finalizer {
|
||||
for s in &finalizer.body {
|
||||
self.collect_upvalue_stmt(s, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::ThrowStatement(ts) => {
|
||||
self.collect_upvalue_expr(&ts.argument, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
Statement::SwitchStatement(ss) => {
|
||||
self.collect_upvalue_expr(&ss.discriminant, parent_ctx, nested_symbols, captures, seen);
|
||||
for case in &ss.cases {
|
||||
for s in &case.consequent {
|
||||
self.collect_upvalue_stmt(s, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn collect_upvalue_expr(
|
||||
&self, expr: &Expression, parent_ctx: &CompileCtx, nested_symbols: &SymbolTable,
|
||||
captures: &mut Vec<UpvalueCapture>, seen: &mut HashMap<String, usize>,
|
||||
) {
|
||||
match expr {
|
||||
Expression::Identifier(ident) => {
|
||||
let name = ident.name.as_str();
|
||||
if nested_symbols.lookup_any_binding(name).is_some() {
|
||||
return;
|
||||
}
|
||||
if let Some((binding, _)) = parent_ctx.scopes.symbols.lookup_any_binding(name) {
|
||||
if seen.contains_key(name) {
|
||||
return;
|
||||
}
|
||||
let cell_idx = captures.len() as u8;
|
||||
seen.insert(name.to_string(), cell_idx as usize);
|
||||
captures.push(UpvalueCapture {
|
||||
name: name.to_string(),
|
||||
enclosing_reg: binding.reg,
|
||||
cell_idx,
|
||||
});
|
||||
}
|
||||
}
|
||||
Expression::AssignmentExpression(ae) => {
|
||||
if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(ati) = &ae.left {
|
||||
let name = ati.name.as_str();
|
||||
if nested_symbols.lookup_any_binding(name).is_none() {
|
||||
if let Some((binding, _)) = parent_ctx.scopes.symbols.lookup_any_binding(name) {
|
||||
if !seen.contains_key(name) {
|
||||
let cell_idx = captures.len() as u8;
|
||||
seen.insert(name.to_string(), cell_idx as usize);
|
||||
captures.push(UpvalueCapture {
|
||||
name: name.to_string(),
|
||||
enclosing_reg: binding.reg,
|
||||
cell_idx,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.collect_upvalue_expr(&ae.right, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
Expression::BinaryExpression(be) => {
|
||||
self.collect_upvalue_expr(&be.left, parent_ctx, nested_symbols, captures, seen);
|
||||
self.collect_upvalue_expr(&be.right, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
Expression::UnaryExpression(ue) => {
|
||||
self.collect_upvalue_expr(&ue.argument, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
Expression::UpdateExpression(ue) => {
|
||||
if let oxide_parser::SimpleAssignmentTarget::AssignmentTargetIdentifier(ati) = &ue.argument {
|
||||
let name = ati.name.as_str();
|
||||
if nested_symbols.lookup_any_binding(name).is_none() {
|
||||
if let Some((binding, _)) = parent_ctx.scopes.symbols.lookup_any_binding(name) {
|
||||
if !seen.contains_key(name) {
|
||||
let cell_idx = captures.len() as u8;
|
||||
seen.insert(name.to_string(), cell_idx as usize);
|
||||
captures.push(UpvalueCapture {
|
||||
name: name.to_string(),
|
||||
enclosing_reg: binding.reg,
|
||||
cell_idx,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Expression::CallExpression(ce) => {
|
||||
self.collect_upvalue_expr(&ce.callee, parent_ctx, nested_symbols, captures, seen);
|
||||
for arg in &ce.arguments {
|
||||
if let Some(e) = arg.as_expression() {
|
||||
self.collect_upvalue_expr(e, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expression::SequenceExpression(se) => {
|
||||
for sub_expr in &se.expressions {
|
||||
self.collect_upvalue_expr(sub_expr, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
}
|
||||
Expression::ConditionalExpression(ce) => {
|
||||
self.collect_upvalue_expr(&ce.test, parent_ctx, nested_symbols, captures, seen);
|
||||
self.collect_upvalue_expr(&ce.consequent, parent_ctx, nested_symbols, captures, seen);
|
||||
self.collect_upvalue_expr(&ce.alternate, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
Expression::ArrayExpression(ae) => {
|
||||
for elem in &ae.elements {
|
||||
if let Some(e) = elem.as_expression() {
|
||||
self.collect_upvalue_expr(e, parent_ctx, nested_symbols, captures, seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn extract_function_parts<'a>(
|
||||
&self, function: &'a oxide_parser::Function<'a>,
|
||||
) -> Result<(Vec<ParamSpec<'a>>, &'a [Statement<'a>]), String> {
|
||||
@@ -693,6 +894,7 @@ impl Compiler {
|
||||
reg: binding.reg,
|
||||
initialized: binding.initialized,
|
||||
is_const: binding.is_const,
|
||||
is_captured: binding.is_captured,
|
||||
},
|
||||
);
|
||||
inherited_reg_start = inherited_reg_start.max(binding.reg.saturating_add(1));
|
||||
@@ -704,6 +906,7 @@ impl Compiler {
|
||||
reg: *reg,
|
||||
initialized: true,
|
||||
is_const: true,
|
||||
is_captured: false,
|
||||
},
|
||||
);
|
||||
inherited_reg_start = inherited_reg_start.max(reg.saturating_add(1));
|
||||
@@ -714,6 +917,23 @@ impl Compiler {
|
||||
// same register offset (params go after builtin slots).
|
||||
ctx.reset_regs();
|
||||
|
||||
// Free variable analysis for upvalue capture (Ordinary + Arrow functions only)
|
||||
// ponytail: temporarily disabled to isolate closure bug
|
||||
/*
|
||||
if matches!(body_context, FunctionBodyContext::Ordinary | FunctionBodyContext::Arrow) {
|
||||
let (captures, _cells) = self.analyze_upvalue_captures(body_stmts, parent_ctx, &ctx.scopes.symbols);
|
||||
ctx.current_upvalue_captures = captures;
|
||||
ctx.scopes.cell_registry = ctx.current_upvalue_captures.iter()
|
||||
.map(|u| (u.name.clone(), u.cell_idx))
|
||||
.collect();
|
||||
for up in &ctx.current_upvalue_captures {
|
||||
if let Some((binding, _)) = parent_ctx.scopes.symbols.lookup_any_binding(&up.name) {
|
||||
binding.is_captured.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Function body scope - params and local vars
|
||||
ctx.push_scope_with_kind(ScopeKind::FunctionScope);
|
||||
|
||||
@@ -858,6 +1078,8 @@ impl Compiler {
|
||||
is_class_constructor: false,
|
||||
is_derived_constructor: false,
|
||||
needs_home_object: false,
|
||||
upvalue_captures: Vec::new(),
|
||||
cells_needed: 0,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -956,6 +1178,8 @@ impl Compiler {
|
||||
is_class_constructor: false,
|
||||
is_derived_constructor: false,
|
||||
needs_home_object: false,
|
||||
upvalue_captures: Vec::new(),
|
||||
cells_needed: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,13 +32,21 @@ impl Compiler {
|
||||
self.count_expression(&assign.right, ctx);
|
||||
ctx.count_load_const(); // private id
|
||||
ctx.count_instr(); // SET_PRIVATE
|
||||
} else if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(_) = &assign.left {
|
||||
} else if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(ati) = &assign.left {
|
||||
let name = ati.name.as_str();
|
||||
// Check if target is an upvalue or captured cell
|
||||
let is_upvalue = ctx.current_upvalue_captures.iter().any(|u| u.name == name);
|
||||
let is_captured = ctx.scopes.symbols.lookup_is_captured(name);
|
||||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||||
self.count_identifier_logical_assignment(logical_op, &assign.right, ctx);
|
||||
} else {
|
||||
self.count_expression(&assign.right, ctx);
|
||||
if assign.operator != AssignmentOperator::Assign {
|
||||
ctx.projected_pc += 1; // COMPOUND_* on var
|
||||
} else if is_upvalue {
|
||||
ctx.count_instr(); // STORE_UPVALUE
|
||||
} else if is_captured {
|
||||
ctx.count_instr(); // CELL_SET
|
||||
} else {
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // STORE_VAR
|
||||
|
||||
@@ -21,8 +21,26 @@ impl Compiler {
|
||||
let Expression::Identifier(ident) = expr else {
|
||||
return;
|
||||
};
|
||||
if CompileCtx::is_known_builtin(ident.name.as_str()) {
|
||||
let _ = ctx.lookup_or_builtin(ident.name.as_str());
|
||||
let name = ident.name.as_str();
|
||||
|
||||
// Check if this is an upvalue reference (nested closure)
|
||||
for up in &ctx.current_upvalue_captures {
|
||||
if up.name == name {
|
||||
ctx.alloc_reg();
|
||||
ctx.count_instr(); // LOAD_UPVALUE
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the owner's binding is captured (cell access)
|
||||
if ctx.scopes.symbols.lookup_is_captured(name) {
|
||||
ctx.alloc_reg();
|
||||
ctx.count_instr(); // CELL_GET
|
||||
return;
|
||||
}
|
||||
|
||||
if CompileCtx::is_known_builtin(name) {
|
||||
let _ = ctx.lookup_or_builtin(name);
|
||||
}
|
||||
ctx.alloc_reg();
|
||||
ctx.projected_pc += 1; // LOAD_VAR
|
||||
|
||||
@@ -30,6 +30,7 @@ pub(crate) struct ScopeCtx {
|
||||
pub(crate) builtin_reg_map: Vec<(String, u8)>,
|
||||
pub(crate) private_name_map: Vec<(String, u32)>,
|
||||
pub(crate) next_private_name_id: u32,
|
||||
pub(crate) cell_registry: Vec<(String, u8)>,
|
||||
}
|
||||
|
||||
/// Reserved for destructuring-pattern state (Phase 15). Intentionally empty
|
||||
|
||||
@@ -142,6 +142,20 @@ impl Compiler {
|
||||
} else {
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
let name = id_ref.name.as_str();
|
||||
// Check if target is an upvalue reference
|
||||
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
|
||||
ctx.emit(opcode::encode(OpCode::STORE_UPVALUE, 0, val_reg, uv_idx as u8));
|
||||
return Ok(val_reg);
|
||||
}
|
||||
// Check if target is a captured cell
|
||||
if ctx.scopes.symbols.lookup_is_captured(name) {
|
||||
let cell_idx = ctx.scopes.cell_registry.iter()
|
||||
.find(|(n, _)| n == name)
|
||||
.map(|(_, idx)| *idx)
|
||||
.unwrap_or(0);
|
||||
ctx.emit(opcode::encode(OpCode::CELL_SET, 0, val_reg, cell_idx));
|
||||
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 };
|
||||
|
||||
@@ -56,7 +56,34 @@ impl Compiler {
|
||||
pub(in crate::emitter) fn emit_identifier_expression(
|
||||
&self, ident: &oxide_parser::IdentifierReference, ctx: &mut CompileCtx,
|
||||
) -> Result<u8, String> {
|
||||
let var_reg = ctx.lookup_or_builtin(ident.name.as_str())?;
|
||||
let name = ident.name.as_str();
|
||||
|
||||
// Check if this is an upvalue reference (nested closure)
|
||||
for (uv_idx, up) in ctx.current_upvalue_captures.iter().enumerate() {
|
||||
if up.name == name {
|
||||
let r = ctx.alloc_reg();
|
||||
let idx = uv_idx as u8;
|
||||
ctx.emit(opcode::encode(OpCode::LOAD_UPVALUE, r, idx, 0));
|
||||
return Ok(r);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the owner's binding is captured (cell access)
|
||||
if ctx.scopes.symbols.lookup_is_captured(name) {
|
||||
let cell_idx = ctx.scopes.cell_registry.iter()
|
||||
.find(|(n, _)| n == name)
|
||||
.map(|(_, idx)| *idx)
|
||||
.unwrap_or(0);
|
||||
let r = ctx.alloc_reg();
|
||||
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) {
|
||||
ctx.emit(opcode::encode(OpCode::CELL_GET, r, binding.reg, cell_idx));
|
||||
} else {
|
||||
ctx.emit(opcode::encode(OpCode::CELL_GET, r, 0, cell_idx));
|
||||
}
|
||||
return Ok(r);
|
||||
}
|
||||
|
||||
let var_reg = ctx.lookup_or_builtin(name)?;
|
||||
let r = ctx.alloc_reg();
|
||||
ctx.emit(opcode::encode(OpCode::LOAD_VAR, r, var_reg, 0));
|
||||
Ok(r)
|
||||
|
||||
@@ -33,7 +33,14 @@ impl Compiler {
|
||||
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));
|
||||
let is_captured = ctx.scopes.symbols.lookup_is_captured(bi.name.as_str());
|
||||
if is_captured {
|
||||
let cell_idx = ctx.scopes.cell_registry.len() as u8;
|
||||
ctx.scopes.cell_registry.push((bi.name.to_string(), cell_idx));
|
||||
ctx.emit(opcode::encode(OpCode::MAKE_CELL, var_reg, cell_idx, 0));
|
||||
} else {
|
||||
ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, tmp, 0));
|
||||
}
|
||||
ctx.init_var(bi.name.as_str());
|
||||
r = Some(var_reg);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ pub(crate) struct Binding {
|
||||
pub(crate) reg: u8,
|
||||
pub(crate) initialized: bool,
|
||||
pub(crate) is_const: bool,
|
||||
pub(crate) is_captured: bool,
|
||||
}
|
||||
|
||||
pub struct SymbolTable {
|
||||
@@ -84,6 +85,7 @@ impl SymbolTable {
|
||||
reg,
|
||||
initialized: false,
|
||||
is_const: matches!(kind, VariableDeclarationKind::Const) || is_const,
|
||||
is_captured: false,
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
@@ -101,13 +103,22 @@ impl SymbolTable {
|
||||
Err(format!("Identifier '{name}' is not defined"))
|
||||
}
|
||||
|
||||
pub fn lookup_any(&self, name: &str) -> Option<u8> {
|
||||
pub(crate) fn lookup_any(&self, name: &str) -> Option<u8> {
|
||||
self.scopes
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|scope| scope.bindings.get(name).map(|binding| binding.reg))
|
||||
}
|
||||
|
||||
pub(crate) fn lookup_any_binding(&self, name: &str) -> Option<(&Binding, usize)> {
|
||||
for (i, scope) in self.scopes.iter().enumerate().rev() {
|
||||
if let Some(b) = scope.bindings.get(name) {
|
||||
return Some((b, i));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn lookup_or_global(&mut self, name: &str, reg_for_new: u8) -> u8 {
|
||||
for scope in self.scopes.iter().rev() {
|
||||
if let Some(b) = scope.bindings.get(name) {
|
||||
@@ -120,6 +131,7 @@ impl SymbolTable {
|
||||
reg: reg_for_new,
|
||||
initialized: true,
|
||||
is_const: false,
|
||||
is_captured: false,
|
||||
},
|
||||
);
|
||||
reg_for_new
|
||||
@@ -137,6 +149,15 @@ impl SymbolTable {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn lookup_is_captured(&self, name: &str) -> bool {
|
||||
for scope in self.scopes.iter().rev() {
|
||||
if let Some(b) = scope.bindings.get(name) {
|
||||
return b.is_captured;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn declare_initialized(
|
||||
&mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool,
|
||||
) -> Result<(), String> {
|
||||
@@ -157,6 +178,7 @@ impl SymbolTable {
|
||||
reg,
|
||||
initialized: true,
|
||||
is_const: matches!(kind, VariableDeclarationKind::Const) || is_const,
|
||||
is_captured: false,
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
@@ -167,6 +189,7 @@ impl SymbolTable {
|
||||
reg,
|
||||
initialized: true,
|
||||
is_const: false,
|
||||
is_captured: false,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user