mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Add Free enum variant to the symbol scope to be able to distuingish between true local and free variables.
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,4 +73,4 @@ print()
|
||||
print('======== dis.dis ========')
|
||||
print()
|
||||
co = compile(source, filename, 'exec')
|
||||
print(dis.dis(co))
|
||||
dis.dis(co)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user