Skip to content

Commit

Permalink
feat: Allow the hook to yield and later resume execution
Browse files Browse the repository at this point in the history
By allowing the hook to yield it is possible to release the stack and allow debug info to be queried outside of the hook
  • Loading branch information
Marwes committed Jan 22, 2017
1 parent aa47e4d commit 2aea009
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 36 deletions.
31 changes: 22 additions & 9 deletions tests/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use std::sync::{Arc, Mutex};
use std::collections::BTreeMap;

use gluon::base::pos::Line;
use gluon::{Compiler, new_vm};
use gluon::{Compiler, Error, new_vm};
use gluon::vm;
use gluon::vm::thread::{ThreadInternal, CALL_FLAG, LINE_FLAG};


Expand Down Expand Up @@ -50,19 +51,31 @@ fn line_hook() {
let _ = env_logger::init();

let thread = new_vm();
let lines = Arc::new(Mutex::new(Vec::new()));
{
let lines = lines.clone();
let mut context = thread.context();
context.set_hook(Some(Box::new(move |_, debug_context| {
lines.lock().unwrap().push(debug_context.stack_info(0).unwrap().line().unwrap());
Ok(())
})));
context.set_hook(Some(Box::new(move |_, _| Err(vm::Error::Yield))));
context.set_hook_mask(LINE_FLAG);
}
Compiler::new().implicit_prelude(false).run_expr::<i32>(&thread, "test", SIMPLE_EXPR).unwrap();
let mut result = Compiler::new()
.implicit_prelude(false)
.run_expr::<i32>(&thread, "test", SIMPLE_EXPR)
.map(|_| ());

let mut lines = Vec::new();
loop {
match result {
Ok(_) => break,
Err(Error::VM(vm::Error::Yield)) => {
let context = thread.context();
let debug_info = context.debug_info();
lines.push(debug_info.stack_info(0).unwrap().line().unwrap());
}
Err(err) => panic!("{}", err),
}
result = thread.resume().map_err(From::from);
}

assert_eq!(*lines.lock().unwrap(),
assert_eq!(lines,
vec![1, 3, 4, 3, 1].into_iter().map(Line::from).collect::<Vec<_>>());
}

Expand Down
11 changes: 8 additions & 3 deletions vm/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,11 @@ impl FunctionEnvs {
self.envs.push(FunctionEnv::new(args, id, typ, compiler.source_name.clone()));
}

fn end_function(&mut self, compiler: &mut Compiler) -> FunctionEnv {
fn end_function(&mut self, compiler: &mut Compiler, current_line: Line) -> FunctionEnv {
compiler.stack_types.exit_scope();
compiler.stack_constructors.exit_scope();
let instructions = self.function.instructions.len();
self.function.source_map.close(instructions, current_line);
self.envs.pop().expect("FunctionEnv in scope")
}
}
Expand Down Expand Up @@ -419,9 +421,11 @@ impl<'a> Compiler<'a> {
let id = self.empty_symbol.clone();
let typ = Type::function(vec![],
ArcType::from(expr.env_type_of(&self.globals).clone()));

env.start_function(self, 0, id, typ);
self.compile(expr, &mut env, true)?;
let FunctionEnv { function, .. } = env.end_function(self);
let current_line = self.source.line_number_at_byte(expr.span.end);
let FunctionEnv { function, .. } = env.end_function(self, current_line);
Ok(function)
}

Expand Down Expand Up @@ -862,7 +866,8 @@ impl<'a> Compiler<'a> {

// Insert all free variables into the above globals free variables
// if they arent in that lambdas scope
let f = function.end_function(self);
let current_line = self.source.line_number_at_byte(body.span.end);
let f = function.end_function(self, current_line);
for var in f.free_vars.iter() {
match self.find(var, function).expect("free_vars: find") {
Stack(index) => function.emit(Push(index)),
Expand Down
16 changes: 12 additions & 4 deletions vm/src/source_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,26 @@ impl SourceMap {
}
}

pub fn close(&mut self, instruction_index: usize, current_line: Line) {
// Push one final item to indicate the end of the function
self.map.push((instruction_index, current_line));
}

/// Returns the line where the instruction at `instruction_index` were defined
pub fn line(&self, instruction_index: usize) -> Line {
pub fn line(&self, instruction_index: usize) -> Option<Line> {
// The line for `instruction_index` is at the last index still larger than
// the index in `map`
let p = self.map
.iter()
.position(|&(index, _)| index > instruction_index)
.unwrap_or(self.map.len());
if p == 0 {
Line::from(0)
if p == 0 ||
(p == self.map.len() &&
instruction_index >= self.map.last().expect("Empty source_map").0) {
// instruction_index is not valid in the function
None
} else {
self.map[p - 1].1
Some(self.map[p - 1].1)
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions vm/src/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,11 @@ impl Stack {
match frame.state {
State::Closure(ref closure) => {
let line = closure.function.source_map.line(frame.instruction_index);
Some(Some(StacktraceFrame {
name: closure.function.name.clone(),
line: line,
Some(line.map(|line| {
StacktraceFrame {
name: closure.function.name.clone(),
line: line,
}
}))
}
State::Extern(ref ext) => {
Expand Down
37 changes: 20 additions & 17 deletions vm/src/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,6 @@ pub type HookFn = Box<FnMut(&Thread, DebugInfo) -> Result<()> + Send + Sync>;

pub struct DebugInfo<'a> {
stack: &'a Stack,
instruction_index: usize,
state: HookFlags,
}

Expand Down Expand Up @@ -724,7 +723,7 @@ impl<'a> StackInfo<'a> {
let frame = self.frame();
match frame.state {
State::Closure(ref closure) => {
Some(closure.function.source_map.line(frame.instruction_index))
closure.function.source_map.line(frame.instruction_index)
}
_ => None,
}
Expand All @@ -749,9 +748,10 @@ impl<'a> StackInfo<'a> {

/// Returns an iterator over all locals available at the current executing instruction
pub fn locals(&self) -> LocalIter {
match self.frame().state {
let frame = self.frame();
match frame.state {
State::Closure(ref closure) => {
closure.function.local_map.locals(self.info.instruction_index)
closure.function.local_map.locals(frame.instruction_index)
}
_ => LocalIter::empty(),
}
Expand Down Expand Up @@ -842,6 +842,13 @@ impl<'b> OwnedContext<'b> {
let Context { ref mut gc, ref stack, .. } = **self;
alloc(gc, self.thread, &stack, data)
}

pub fn debug_info(&self) -> DebugInfo {
DebugInfo {
stack: &self.stack,
state: HookFlags::empty(),
}
}
}

pub fn alloc<D>(gc: &mut Gc, thread: &Thread, stack: &Stack, def: D) -> Result<GcPtr<D::Value>>
Expand Down Expand Up @@ -889,14 +896,6 @@ impl<'b> OwnedContext<'b> {
let state = context.borrow_mut().stack.frame.state;

let instruction_index = context.borrow_mut().stack.frame.instruction_index;
if context.hook.flags.bits() != 0 {
if instruction_index == 0 {
context.hook.previous_instruction_index = 0;
} else {
// Reentering the function after returning from a call
context.hook.previous_instruction_index = instruction_index - 1;
}
}
if instruction_index == 0 && context.hook.flags.contains(CALL_FLAG) {
match state {
State::Extern(_) |
Expand All @@ -906,7 +905,6 @@ impl<'b> OwnedContext<'b> {
if let Some(ref mut hook) = context.hook.function {
let info = DebugInfo {
stack: &context.stack,
instruction_index: instruction_index,
state: CALL_FLAG,
};
hook(thread, info)?
Expand Down Expand Up @@ -1051,11 +1049,17 @@ impl<'b> ExecuteContext<'b> {

fn enter_scope(&mut self, args: VmIndex, state: State) {
self.stack.enter_scope(args, state);
self.hook.previous_instruction_index = usize::max_value();
}

fn exit_scope(&mut self) -> StdResult<(), ()> {
match self.stack.exit_scope() {
Ok(_) => Ok(()),
Ok(_) => {
if self.hook.flags.bits() != 0 {
self.hook.previous_instruction_index = self.stack.frame.instruction_index;
}
Ok(())
}
Err(_) => Err(()),
}
}
Expand Down Expand Up @@ -1170,17 +1174,16 @@ impl<'b> ExecuteContext<'b> {
let current_line = function.source_map.line(index);
let previous_line = function.source_map
.line(self.hook.previous_instruction_index);
if index == 0 || current_line != previous_line {
self.hook.previous_instruction_index = index;
if current_line != previous_line {
self.stack.frame.instruction_index = index;
self.stack.store_frame();
let info = DebugInfo {
stack: &self.stack.stack,
instruction_index: index,
state: LINE_FLAG,
};
hook(self.thread, info)?
}
self.hook.previous_instruction_index = index;
}
}

Expand Down

0 comments on commit 2aea009

Please sign in to comment.