Skip to content

Commit

Permalink
assign labels unique ids, even if their ident is reused
Browse files Browse the repository at this point in the history
  • Loading branch information
y21 committed Jun 10, 2024
1 parent 19576d9 commit 7e54356
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 34 deletions.
3 changes: 1 addition & 2 deletions crates/dash_compiler/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::ops::{Deref, DerefMut};

use dash_middle::compiler::instruction as inst;
use dash_middle::compiler::instruction::Instruction;
use dash_middle::interner::Symbol;

use crate::jump_container::JumpContainer;
use crate::{jump_container, FunctionCompiler};
Expand Down Expand Up @@ -41,7 +40,7 @@ pub enum Label {
InitParamWithDefaultValue,
FinishParamDefaultValueInit,
UserDefinedEnd {
sym: Symbol,
id: usize,
},
}

Expand Down
81 changes: 49 additions & 32 deletions crates/dash_compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ macro_rules! unimplementedc {
enum Breakable {
Loop { loop_id: usize },
Switch { switch_id: usize },
Named { sym: Symbol, label_id: usize },
}

/// Function-specific state, such as
Expand All @@ -70,6 +71,8 @@ struct FunctionLocalState {
/// A stack of try-catch-finally blocks and their optional `finally` label that can be jumped to
finally_labels: Vec<Option<Label>>,
finally_counter: Counter<usize>,
/// Counter for user-defined labels
user_label_counter: Counter<usize>,
/// The type of function that this FunctionCompiler compiles
ty: FunctionKind,
/// Container, used for storing global labels that can be jumped to
Expand All @@ -88,6 +91,15 @@ struct FunctionLocalState {
references_arguments: Option<Span>,
}

macro_rules! exit_breakable {
($fc:expr, $what:pat) => {
match $fc.breakables.pop() {
Some($what) => {}
_ => panic!("Tried to exit breakable, but wrong kind was on the stack"),
}
};
}

impl FunctionLocalState {
pub fn new(ty: FunctionKind, id: FuncId) -> Self {
Self {
Expand All @@ -96,6 +108,7 @@ impl FunctionLocalState {
try_depth: 0,
finally_labels: Vec::new(),
finally_counter: Counter::new(),
user_label_counter: Counter::new(),
ty,
jc: JumpContainer::new(),
breakables: Vec::new(),
Expand Down Expand Up @@ -127,22 +140,6 @@ impl FunctionLocalState {
switch_id
}

fn exit_loop(&mut self) {
let item = self.breakables.pop();
match item {
None | Some(Breakable::Switch { .. }) => panic!("Tried to exit loop, but no breakable was found"),
Some(Breakable::Loop { .. }) => {}
}
}

fn exit_switch(&mut self) {
let item = self.breakables.pop();
match item {
None | Some(Breakable::Loop { .. }) => panic!("Tried to exit switch, but no breakable was found"),
Some(Breakable::Switch { .. }) => {}
}
}

fn add_global_label(&mut self, label: Label) {
jump_container::add_label(&mut self.jc, label, &mut self.buf)
}
Expand All @@ -152,6 +149,20 @@ impl FunctionLocalState {
jump_container::add_jump(&mut self.jc, label, &mut self.buf)
}

/// Tries to find the target to jump to for a `break` (or `continue`).
fn find_breakable(&self, label: Option<Symbol>) -> Option<Breakable> {
self.breakables
.iter()
.rev()
.find(|brk| match (brk, label) {
(Breakable::Named { sym, label_id: _ }, Some(sym2)) => *sym == sym2,
(Breakable::Named { .. }, None) => false,
(Breakable::Loop { .. } | Breakable::Switch { .. }, None) => true,
(Breakable::Loop { .. } | Breakable::Switch { .. }, Some(_)) => false,
})
.copied()
}

pub fn is_async(&self) -> bool {
match self.ty {
FunctionKind::Function(a) => matches!(a, Asyncness::Yes),
Expand Down Expand Up @@ -1014,7 +1025,7 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {

ib.current_function_mut().add_global_label(Label::LoopEnd { loop_id });

ib.current_function_mut().exit_loop();
exit_breakable!(ib.current_function_mut(), Breakable::Loop { .. });

Ok(())
}
Expand All @@ -1033,7 +1044,7 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
ib.build_jmptruep(Label::LoopCondition { loop_id }, false);

ib.current_function_mut().add_global_label(Label::LoopEnd { loop_id });
ib.current_function_mut().exit_loop();
exit_breakable!(ib.current_function_mut(), Breakable::Loop { .. });

Ok(())
}
Expand Down Expand Up @@ -1945,7 +1956,7 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {

ib.current_function_mut().add_global_label(Label::LoopEnd { loop_id });
ib.current_scope_mut().exit();
ib.current_function_mut().exit_loop();
exit_breakable!(ib.current_function_mut(), Breakable::Loop { .. });

Ok(())
}
Expand Down Expand Up @@ -2067,15 +2078,9 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
unimplementedc!(span, "`break` in a try-finally block");
}

if let Some(sym) = sym {
ib.build_jmp(Label::UserDefinedEnd { sym }, false);
return Ok(());
}

let breakable = *ib
let breakable = ib
.current_function_mut()
.breakables
.last()
.find_breakable(sym)
.ok_or(Error::IllegalBreak(span))?;

match breakable {
Expand All @@ -2085,6 +2090,9 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
Breakable::Switch { switch_id } => {
ib.build_jmp(Label::SwitchEnd { switch_id }, false);
}
Breakable::Named { sym: _, label_id } => {
ib.build_jmp(Label::UserDefinedEnd { id: label_id }, false);
}
}
Ok(())
}
Expand All @@ -2096,10 +2104,9 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
unimplementedc!(span, "`continue` in a try-finally block");
}

let breakable = *ib
let breakable = ib
.current_function_mut()
.breakables
.last()
.find_breakable(None) // TODO
.ok_or(Error::IllegalBreak(span))?;

match breakable {
Expand All @@ -2110,6 +2117,9 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
// TODO: make it possible to use `continue` in loops even if its used in a switch
unimplementedc!(span, "`continue` used inside of a switch statement");
}
Breakable::Named { .. } => {
unimplementedc!(span, "`continue` cannot target a non-iteration statement");
}
}
Ok(())
}
Expand Down Expand Up @@ -2316,16 +2326,23 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {

ib.current_function_mut()
.add_global_label(Label::SwitchEnd { switch_id });
ib.current_function_mut().exit_switch();
exit_breakable!(ib.current_function_mut(), Breakable::Switch { .. });

Ok(())
}

fn visit_labelled(&mut self, _: Span, label: Symbol, stmt: Box<Statement>) -> Result<(), Error> {
let mut ib = InstructionBuilder::new(self);
let label_id = ib.current_function_mut().user_label_counter.inc();
ib.current_function_mut()
.breakables
.push(Breakable::Named { sym: label, label_id });

ib.accept(*stmt)?;

ib.current_function_mut()
.add_global_label(Label::UserDefinedEnd { sym: label });
.add_global_label(Label::UserDefinedEnd { id: label_id });
exit_breakable!(ib.current_function_mut(), Breakable::Named { .. });
Ok(())
}
}
Expand Down
15 changes: 15 additions & 0 deletions crates/dash_vm/src/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,21 @@ simple_test!(
i++;
}
assert(order == 'i0,i1,i2,i3,i4,i5,o5,i6,o6,i7,o7,i8,o8,i9,o9,i10,o10');
order = [];
b: {
order.push(1);
break b;
order.push(2);
}
b: {
order.push(3);
break b;
order.push(4);
}
order.push(5);
assert(order == '1,3,5' && order != '135');
",
Value::undefined()
);

0 comments on commit 7e54356

Please sign in to comment.