Add Free enum variant to the symbol scope to be able to distuingish between true local and free variables.

This commit is contained in:
Windel Bouwman
2019-08-18 12:12:27 +02:00
parent edf647160f
commit eb3771ebff
8 changed files with 51 additions and 18 deletions

View File

@@ -59,10 +59,19 @@ bitflags! {
pub type Label = usize;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
/// An indication where the name must be accessed.
pub enum NameScope {
/// The name will be in the local scope.
Local,
/// The name will be located in scope surrounding the current scope.
NonLocal,
/// The name will be in global scope.
Global,
/// The name will be located in any scope between the current scope and the top scope.
Free,
}
/// Transforms a value prior to formatting it.

View File

@@ -282,8 +282,8 @@ impl<O: OutputStream> Compiler<O> {
match symbol.scope {
SymbolScope::Global => bytecode::NameScope::Global,
SymbolScope::Nonlocal => bytecode::NameScope::NonLocal,
SymbolScope::Unknown => bytecode::NameScope::Local,
SymbolScope::Local => bytecode::NameScope::Local,
SymbolScope::Unknown => bytecode::NameScope::Free,
SymbolScope::Local => bytecode::NameScope::Free,
}
}
@@ -500,7 +500,7 @@ impl<O: OutputStream> Compiler<O> {
self.compile_jump_if(test, true, end_label)?;
self.emit(Instruction::LoadName {
name: String::from("AssertionError"),
scope: bytecode::NameScope::Local,
scope: bytecode::NameScope::Global,
});
match msg {
Some(e) => {
@@ -736,7 +736,7 @@ impl<O: OutputStream> Compiler<O> {
// Check exception type:
self.emit(Instruction::LoadName {
name: String::from("isinstance"),
scope: bytecode::NameScope::Local,
scope: bytecode::NameScope::Global,
});
self.emit(Instruction::Rotate { amount: 2 });
self.compile_expression(exc_type)?;
@@ -931,11 +931,11 @@ impl<O: OutputStream> Compiler<O> {
self.emit(Instruction::LoadName {
name: "__name__".to_string(),
scope: bytecode::NameScope::Local,
scope: bytecode::NameScope::Free,
});
self.emit(Instruction::StoreName {
name: "__module__".to_string(),
scope: bytecode::NameScope::Local,
scope: bytecode::NameScope::Free,
});
self.compile_statements(new_body)?;
self.emit(Instruction::LoadConst {

View File

@@ -213,21 +213,21 @@ impl SymbolTableAnalyzer {
if symbol.is_assigned || symbol.is_parameter {
symbol.scope = SymbolScope::Local;
} else {
// TODO: comment this out and make it work properly:
/*
*/
let found_in_outer_scope = self
.tables
.iter()
.skip(1)
.any(|t| t.symbols.contains_key(&symbol.name));
// Interesting stuff about the __class__ variable:
// https://docs.python.org/3/reference/datamodel.html?highlight=__class__#creating-the-class-object
let found_in_outer_scope = (symbol.name == "__class__")
|| self
.tables
.iter()
.skip(1)
.any(|t| t.symbols.contains_key(&symbol.name));
if found_in_outer_scope {
// Symbol is in some outer scope.
symbol.is_free = true;
} else {
// Well, it must be a global then :)
// symbol.scope = SymbolScope::Global;
symbol.scope = SymbolScope::Global;
}
}
}

View File

@@ -73,4 +73,4 @@ print()
print('======== dis.dis ========')
print()
co = compile(source, filename, 'exec')
print(dis.dis(co))
dis.dis(co)

View File

@@ -907,6 +907,9 @@ impl Frame {
bytecode::NameScope::Local => {
self.scope.store_name(vm, name, obj);
}
bytecode::NameScope::Free => {
self.scope.store_name(vm, name, obj);
}
}
Ok(None)
}
@@ -928,7 +931,8 @@ impl Frame {
let optional_value = match name_scope {
bytecode::NameScope::Global => self.scope.load_global(vm, name),
bytecode::NameScope::NonLocal => self.scope.load_cell(vm, name),
bytecode::NameScope::Local => self.scope.load_name(&vm, name),
bytecode::NameScope::Local => self.scope.load_local(&vm, name),
bytecode::NameScope::Free => self.scope.load_name(&vm, name),
};
let value = match optional_value {

View File

@@ -312,6 +312,8 @@ fn type_dict_setter(_instance: PyClassRef, _value: PyObjectRef, vm: &VirtualMach
/// This is the internal get_attr implementation for fast lookup on a class.
pub fn class_get_attr(class: &PyClassRef, attr_name: &str) -> Option<PyObjectRef> {
flame_guard!(format!("class_get_attr({:?})", attr_name));
if let Some(item) = class.attributes.borrow().get(attr_name).cloned() {
return Some(item);
}

View File

@@ -123,6 +123,7 @@ pub trait NameProtocol {
fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
fn store_name(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef);
fn delete_name(&self, vm: &VirtualMachine, name: &str) -> PyResult;
fn load_local(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
fn load_cell(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
fn store_cell(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef);
fn load_global(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
@@ -142,6 +143,12 @@ impl NameProtocol for Scope {
self.load_global(vm, name)
}
#[cfg_attr(feature = "flame-it", flame("Scope"))]
/// Load a local name. Only check the local dictionary for the given name.
fn load_local(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
self.get_locals().get_item_option(name, vm).unwrap()
}
#[cfg_attr(feature = "flame-it", flame("Scope"))]
fn load_cell(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
for dict in self.locals.iter().skip(1) {
@@ -170,7 +177,17 @@ impl NameProtocol for Scope {
}
#[cfg_attr(feature = "flame-it", flame("Scope"))]
/// Load a global name.
fn load_global(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
// First, take a look in the outmost local scope (the scope at top level)
let last_local_dict = self.locals.iter().last();
if let Some(local_dict) = last_local_dict {
if let Some(value) = local_dict.get_item_option(name, vm).unwrap() {
return Some(value);
}
}
// Now, take a look at the globals or builtins.
if let Some(value) = self.globals.get_item_option(name, vm).unwrap() {
Some(value)
} else {

View File

@@ -527,6 +527,8 @@ impl VirtualMachine {
where
T: Into<PyFuncArgs>,
{
flame_guard!(format!("call_method({:?})", method_name));
// This is only used in the vm for magic methods, which use a greatly simplified attribute lookup.
let cls = obj.class();
match objtype::class_get_attr(&cls, method_name) {
@@ -545,7 +547,6 @@ impl VirtualMachine {
}
}
#[cfg_attr(feature = "flame-it", flame("VirtualMachine"))]
fn _invoke(&self, func_ref: &PyObjectRef, args: PyFuncArgs) -> PyResult {
vm_trace!("Invoke: {:?} {:?}", func_ref, args);