Skip to content

Commit

Permalink
support try/finally in generators & async fns
Browse files Browse the repository at this point in the history
  • Loading branch information
y21 committed May 12, 2024
1 parent 2bb12d5 commit adddfb4
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 17 deletions.
12 changes: 0 additions & 12 deletions crates/dash_compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,6 @@ impl FunctionLocalState {
}
}

pub fn is_generator_or_async(&self) -> bool {
matches!(
self.ty,
FunctionKind::Function(Asyncness::Yes) | FunctionKind::Generator
)
}

fn enclosing_finally(&self) -> Option<Label> {
self.finally_labels.iter().copied().rev().find_map(|lbl| lbl)
}
Expand Down Expand Up @@ -1885,11 +1878,6 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
ib.current_function_mut().finally_labels.pop();

if let Some((finally_id, finally)) = finally {
if ib.current_function().is_generator_or_async() {
// TODO: is this also already an issue without finally?
unimplementedc!(span, "try-finally in generator function");
}

ib.current_function_mut()
.add_global_label(Label::Finally { finally_id });
ib.add_local_label(Label::TryEnd);
Expand Down
2 changes: 1 addition & 1 deletion crates/dash_vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::value::{ExternalValue, Unrooted};
use super::value::function::user::UserFunction;
use super::value::Value;

#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, Trace)]
pub struct TryBlock {
pub catch_ip: Option<usize>,
pub finally_ip: Option<usize>,
Expand Down
28 changes: 26 additions & 2 deletions crates/dash_vm/src/js_std/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ pub fn next(cx: CallContext) -> Result<Value, Value> {
let frame = {
let generator = as_generator(cx.scope, &cx.this)?;

let (ip, old_stack, arguments) = match &mut *generator.state().borrow_mut() {
let (ip, old_stack, arguments, try_blocks) = match &mut *generator.state().borrow_mut() {
GeneratorState::Finished => return create_generator_value(cx.scope, true, None),
GeneratorState::Running { ip, stack, arguments } => (*ip, mem::take(stack), arguments.take()),
GeneratorState::Running {
ip,
stack,
arguments,
try_blocks,
} => (*ip, mem::take(stack), arguments.take(), mem::take(try_blocks)),
};

let function = generator.function();
Expand All @@ -29,6 +34,7 @@ pub fn next(cx: CallContext) -> Result<Value, Value> {
_ => throw!(cx.scope, TypeError, "Incompatible generator function"),
};

cx.scope.try_blocks.extend(try_blocks);
let current_sp = cx.scope.stack_size();
cx.scope.try_extend_stack(old_stack).root_err(cx.scope)?;

Expand Down Expand Up @@ -68,13 +74,31 @@ pub fn next(cx: CallContext) -> Result<Value, Value> {
// Async functions are desugared to generators, so `await` is treated equivalent to `yield`, for now...
let value = value.root(cx.scope);

let fp = cx.scope.frames.len();
let frame = cx.scope.pop_frame().expect("Generator frame is missing");
let stack = cx.scope.drain_stack(frame.sp..).collect::<Vec<_>>();

// Save any try blocks part of this frame
let frame_try_blocks = cx
.scope
.try_blocks
.iter()
.rev()
.take_while(|b| b.frame_ip == fp)
.count();

let total_try_blocks = cx.scope.try_blocks.len();
let try_blocks = cx
.scope
.try_blocks
.drain(total_try_blocks - frame_try_blocks..)
.collect::<Vec<_>>();

generator.state().replace(GeneratorState::Running {
ip: frame.ip,
stack,
arguments: frame.arguments,
try_blocks,
});

create_generator_value(cx.scope, false, Some(value))
Expand Down
15 changes: 13 additions & 2 deletions crates/dash_vm/src/value/function/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::cell::RefCell;

use dash_proc_macro::Trace;

use crate::frame::TryBlock;
use crate::gc::handle::Handle;
use crate::gc::trace::{Trace, TraceCtxt};
use crate::localscope::LocalScope;
Expand Down Expand Up @@ -54,7 +55,7 @@ impl GeneratorFunction {
scope.stack.drain(sp..).collect::<Vec<_>>()
};

let iter = GeneratorIterator::new(callee, scope, args, arguments);
let iter = GeneratorIterator::new(callee, scope, args, arguments, Vec::new());
Ok(Value::Object(scope.register(iter)))
}
}
Expand All @@ -65,6 +66,7 @@ pub enum GeneratorState {
Running {
ip: usize,
stack: Vec<Value>,
try_blocks: Vec<TryBlock>,
arguments: Option<Handle>,
},
}
Expand All @@ -86,9 +88,11 @@ unsafe impl Trace for GeneratorState {
ip: _,
stack,
arguments,
try_blocks,
} => {
stack.trace(cx);
arguments.trace(cx);
try_blocks.trace(cx);
}
}
}
Expand All @@ -102,7 +106,13 @@ pub struct GeneratorIterator {
}

impl GeneratorIterator {
pub fn new(function: Handle, vm: &Vm, stack: Vec<Value>, arguments: Option<Handle>) -> Self {
pub fn new(
function: Handle,
vm: &Vm,
stack: Vec<Value>,
arguments: Option<Handle>,
try_blocks: Vec<TryBlock>,
) -> Self {
let proto = vm.statics.generator_iterator_prototype.clone();
let ctor = function.clone();

Expand All @@ -113,6 +123,7 @@ impl GeneratorIterator {
ip: 0,
stack,
arguments,
try_blocks,
}),
}
}
Expand Down

0 comments on commit adddfb4

Please sign in to comment.