use crate::collections::SmallMap;
use crate::debug::inspect::to_scope_names_by_local_slot_id;
use crate::eval::runtime::slots::LocalSlotIdCapturedOrNot;
use crate::eval::Evaluator;
use crate::syntax::AstModule;
use crate::values::FrozenStringValue;
use crate::values::Value;
impl<'v> Evaluator<'v, '_, '_> {
pub fn eval_statements(&mut self, statements: AstModule) -> crate::Result<Value<'v>> {
self.disable_gc();
let original_module: SmallMap<FrozenStringValue, Option<Value<'v>>> = self
.module_env
.mutable_names()
.all_names_and_slots()
.into_iter()
.map(|(name, slot)| (name, self.module_env.slots().get_slot(slot)))
.collect();
if let Some(frozen) = self.top_frame_def_frozen_module(true)? {
for (name, slot) in frozen.names.symbols() {
if let Some(value) = frozen.get_slot(slot) {
self.module_env.set(&name, value.to_value())
}
}
}
let locals = self
.call_stack
.to_function_values()
.into_iter()
.rev()
.find_map(to_scope_names_by_local_slot_id);
if let Some(names) = &locals {
for (slot, name) in names.iter().enumerate() {
if let Some(value) = self
.current_frame
.get_slot_slow(LocalSlotIdCapturedOrNot(slot as u32))
{
self.module_env.set(name, value)
}
}
}
let globals = self.top_frame_def_info_for_debugger()?.globals;
let res = self.eval_module(statements, &globals);
if let Some(names) = &locals {
for (slot, name) in names.iter().enumerate() {
if let Some(value) = self.module_env.get(name) {
self.current_frame
.set_slot_slow(LocalSlotIdCapturedOrNot(slot as u32), value)
}
}
for (name, slot) in self.module_env.mutable_names().all_names_and_slots() {
match original_module.get(&name) {
None => self.module_env.mutable_names().hide_name(&name),
Some(Some(value)) => self.module_env.slots().set_slot(slot, *value),
_ => {} }
}
}
res
}
}
#[cfg(test)]
mod tests {
use itertools::Itertools;
use starlark_derive::starlark_module;
use starlark_syntax::error::StarlarkResultExt;
use super::*;
use crate as starlark;
use crate::assert;
use crate::environment::GlobalsBuilder;
use crate::syntax::Dialect;
use crate::wasm::is_wasm;
#[starlark_module]
fn debugger(builder: &mut GlobalsBuilder) {
fn debug_evaluate<'v>(
code: String,
eval: &mut Evaluator<'v, '_, '_>,
) -> anyhow::Result<Value<'v>> {
let ast = AstModule::parse("interactive", code, &Dialect::AllOptionsInternal)
.into_anyhow_result()?;
eval.eval_statements(ast).into_anyhow_result()
}
}
#[test]
fn test_debug_evaluate() {
if is_wasm() {
return;
}
let mut a = assert::Assert::new();
a.disable_static_typechecking();
a.globals_add(debugger);
let check = r#"
assert_eq(debug_evaluate("1+2"), 3)
x = 10
assert_eq(debug_evaluate("x"), 10)
assert_eq(debug_evaluate("x = 5"), None)
assert_eq(x, 5)
y = [20]
debug_evaluate("y.append(30)")
assert_eq(y, [20, 30])
"#;
a.pass(check);
a.pass(&format!(
"def local():\n{}\nlocal()",
check.lines().map(|x| format!(" {}", x)).join("\n")
));
a.pass(
r#"
def foo(x, y, z):
return bar(y)
def bar(x):
return debug_evaluate("x")
assert_eq(foo(1, 2, 3), 2)
"#,
);
a.pass(
r#"
x = 7
def bar(y):
return debug_evaluate("x + y")
assert_eq(bar(4), 4 + 7)
"#,
);
a.module(
"test",
r#"
x = 7
z = 2
def bar(y):
assert_eq(x, 7)
debug_evaluate("x = 20")
assert_eq(x, 7) # doesn't work for frozen variables
return debug_evaluate("x + y + z")
"#,
);
a.pass("load('test', 'bar'); assert_eq(bar(4), 4 + 7 + 2)");
}
}