Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,16 @@ use crate::util::hash::FxHashMap as HashMap;
pub use types::{Val, HeapObj, HeapPool, VmErr, Limits};
pub use methods::{ALL_METHODS, BuiltinMethodId, MethodDesc, MethodKind, lookup_method};
pub use globals::exc_parents;
pub use scope::{FnScope, Kind};

use types::*;
use cache::CachePool;
use alloc::{string::{String, ToString}, vec::Vec};

pub(crate) use types::ExceptionFrame;

#[derive(Clone, Copy, PartialEq)]
pub(crate) enum ParamKind { Normal, Star, DoubleStar, KwOnly }
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ParamKind { Normal, Star, DoubleStar, KwOnly }

/* Side-channel state passed between opcodes in one dispatch frame, grouped for auditability. */
pub(crate) struct Pending {
Expand Down Expand Up @@ -539,6 +540,15 @@ impl<'a> VM<'a> {
}

/* What the allocator holds for the program, garbage since the last collection included. */
/* The index and scope of the function whose body is `body`, its names as this VM resolved them. */
pub fn scope_of(&self, body: &SSAChunk) -> Option<(usize, &FnScope)> {
let fi = *self.body_to_fi.get(&(body as *const SSAChunk))?;
Some((fi, &self.fn_scope[fi]))
}

/* The parameters of function `fi`, each with its kind and the slot it binds. */
pub fn params_of(&self, fi: usize) -> &[(ParamKind, usize)] { &self.param_slots[fi] }

pub fn memory(&self) -> usize { self.heap.bytes() }

/* The most the allocator held for the program at once, which a bench compares run to run. */
Expand Down
4 changes: 2 additions & 2 deletions src/vm/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ impl Globals {

/* What a frame slot holds, fixed for the chunk before it runs. */
#[derive(Clone, Copy, PartialEq, Debug)]
pub(crate) enum Kind {
pub enum Kind {
Local,
// A cell a nested function shares, read and written through it.
Cell,
Expand All @@ -89,7 +89,7 @@ pub(crate) enum Kind {

/* How a function body's names resolve, computed once from the code. */
#[derive(Default, Clone)]
pub(crate) struct FnScope {
pub struct FnScope {
pub kinds: alloc::rc::Rc<[Kind]>,
/* Slots that start as a fresh cell holding what the call bound there. */
pub cellvars: Vec<usize>,
Expand Down
20 changes: 20 additions & 0 deletions tests/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,24 @@ mod test {
assert_eq!(vm.output_text(), "True True True\n");
for (child, parent) in [("TimeoutError", "OSError"), ("KeyError", "LookupError")] { assert!(exc_parents(child).contains(&parent)); }
}

#[test]
fn a_function_scope_and_parameters_are_those_the_vm_resolved() {
use compiler::parser::Parser;
use compiler::vm::{Kind, ParamKind, VM};
let src = "g = 1\ndef f(a, *r, k=2, **m):\n x = a\n def h():\n return x\n return g\n";
let (tokens, _) = compiler::lexer::lex(src);
let (chunk, errors) = Parser::new(src, tokens.into_iter()).parse();
assert!(errors.is_empty());
let vm = VM::new(&chunk);
let body = &chunk.functions[0].1;
let (fi, scope) = vm.scope_of(body).expect("f is indexed");
let kind = |name: &str| scope.kinds[body.names.iter().position(|n| n == name).unwrap_or_else(|| panic!("{name}"))];
assert_eq!(kind("a_0"), Kind::Local);
assert!(matches!(kind("g_0"), Kind::Global(_)));
assert!(scope.cellvars.iter().any(|&s| body.names[s].starts_with('x')));
let kinds: Vec<ParamKind> = vm.params_of(fi).iter().map(|p| p.0).collect();
assert_eq!(kinds, [ParamKind::Normal, ParamKind::Star, ParamKind::KwOnly, ParamKind::DoubleStar]);
assert!(vm.scope_of(&chunk).is_none());
}
}
Loading