diff --git a/tests/debug.rs b/tests/debug.rs index 057c395a03..9aed211af0 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -189,3 +189,40 @@ fn source_name() { let name = result.lock().unwrap(); assert_eq!(*name, "test"); } + + +#[test] +fn upvars() { + let _ = env_logger::init(); + + let thread = new_vm(); + let result = Arc::new(Mutex::new(Vec::new())); + { + let result = result.clone(); + let mut context = thread.context(); + context.set_hook(Some(Box::new(move |_, debug_info| { + let stack_info = debug_info.stack_info(0).unwrap(); + if stack_info.source_name() == "test" { + result.lock().unwrap().push(stack_info.upvars().to_owned()); + } + Ok(()) + }))); + context.set_hook_mask(CALL_FLAG); + } + let expr = r#" + let x = 1 + let y = 2 + let f z = + let g w = x + g x + y + z + f 3 + "#; + Compiler::new() + .run_expr::(&thread, "test", expr) + .unwrap(); + + assert_eq!(*result.lock().unwrap(), + vec![vec![], + vec!["x".to_string(), "+".to_string(), "y".to_string()], + vec!["x".to_string()]]); +} diff --git a/vm/src/compiler.rs b/vm/src/compiler.rs index 36a90bb1d2..2fc072c639 100644 --- a/vm/src/compiler.rs +++ b/vm/src/compiler.rs @@ -48,6 +48,7 @@ pub struct CompiledFunction { /// Maps instruction indexes to the line that spawned them pub source_map: SourceMap, pub local_map: LocalMap, + pub upvar_names: Vec, pub source_name: String, } @@ -65,6 +66,7 @@ impl CompiledFunction { records: Vec::new(), source_map: SourceMap::new(), local_map: LocalMap::new(), + upvar_names: Vec::new(), source_name: source_name, } } @@ -120,6 +122,12 @@ impl FunctionEnvs { compiler.stack_constructors.exit_scope(); let instructions = self.function.instructions.len(); self.function.source_map.close(instructions, current_line); + { + let function = &mut **self; + function.function + .upvar_names + .extend(function.free_vars.iter().map(|s| s.declared_name().to_string())); + } self.envs.pop().expect("FunctionEnv in scope") } } diff --git a/vm/src/thread.rs b/vm/src/thread.rs index d64e122f8a..40d81fdb40 100644 --- a/vm/src/thread.rs +++ b/vm/src/thread.rs @@ -756,6 +756,13 @@ impl<'a> StackInfo<'a> { _ => LocalIter::empty(), } } + + pub fn upvars(&self) -> &[StdString] { + match self.frame().state { + State::Closure(ref closure) => &closure.function.upvar_names, + _ => panic!("Attempted to access upvar in non closure function"), + } + } } bitflags! { diff --git a/vm/src/value.rs b/vm/src/value.rs index 95a75f511e..fd4ae0e53c 100644 --- a/vm/src/value.rs +++ b/vm/src/value.rs @@ -119,6 +119,7 @@ pub struct BytecodeFunction { pub records: Vec>, pub source_map: SourceMap, pub local_map: LocalMap, + pub upvar_names: Vec, pub source_name: StdString, } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index b5debd8f02..856e5eef86 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -42,6 +42,7 @@ fn new_bytecode(gc: &mut Gc, records, source_map, local_map, + upvar_names, source_name, .. } = f; @@ -72,6 +73,7 @@ fn new_bytecode(gc: &mut Gc, records: records?, source_map: source_map, local_map: local_map, + upvar_names: upvar_names, source_name: source_name, })) }