X Tutup
Skip to content

Commit eb3771e

Browse files
committed
Add Free enum variant to the symbol scope to be able to distuingish between true local and free variables.
1 parent edf6471 commit eb3771e

File tree

8 files changed

+51
-18
lines changed

8 files changed

+51
-18
lines changed

bytecode/src/bytecode.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,19 @@ bitflags! {
5959
pub type Label = usize;
6060

6161
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
62+
/// An indication where the name must be accessed.
6263
pub enum NameScope {
64+
/// The name will be in the local scope.
6365
Local,
66+
67+
/// The name will be located in scope surrounding the current scope.
6468
NonLocal,
69+
70+
/// The name will be in global scope.
6571
Global,
72+
73+
/// The name will be located in any scope between the current scope and the top scope.
74+
Free,
6675
}
6776

6877
/// Transforms a value prior to formatting it.

compiler/src/compile.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,8 @@ impl<O: OutputStream> Compiler<O> {
282282
match symbol.scope {
283283
SymbolScope::Global => bytecode::NameScope::Global,
284284
SymbolScope::Nonlocal => bytecode::NameScope::NonLocal,
285-
SymbolScope::Unknown => bytecode::NameScope::Local,
286-
SymbolScope::Local => bytecode::NameScope::Local,
285+
SymbolScope::Unknown => bytecode::NameScope::Free,
286+
SymbolScope::Local => bytecode::NameScope::Free,
287287
}
288288
}
289289

@@ -500,7 +500,7 @@ impl<O: OutputStream> Compiler<O> {
500500
self.compile_jump_if(test, true, end_label)?;
501501
self.emit(Instruction::LoadName {
502502
name: String::from("AssertionError"),
503-
scope: bytecode::NameScope::Local,
503+
scope: bytecode::NameScope::Global,
504504
});
505505
match msg {
506506
Some(e) => {
@@ -736,7 +736,7 @@ impl<O: OutputStream> Compiler<O> {
736736
// Check exception type:
737737
self.emit(Instruction::LoadName {
738738
name: String::from("isinstance"),
739-
scope: bytecode::NameScope::Local,
739+
scope: bytecode::NameScope::Global,
740740
});
741741
self.emit(Instruction::Rotate { amount: 2 });
742742
self.compile_expression(exc_type)?;
@@ -931,11 +931,11 @@ impl<O: OutputStream> Compiler<O> {
931931

932932
self.emit(Instruction::LoadName {
933933
name: "__name__".to_string(),
934-
scope: bytecode::NameScope::Local,
934+
scope: bytecode::NameScope::Free,
935935
});
936936
self.emit(Instruction::StoreName {
937937
name: "__module__".to_string(),
938-
scope: bytecode::NameScope::Local,
938+
scope: bytecode::NameScope::Free,
939939
});
940940
self.compile_statements(new_body)?;
941941
self.emit(Instruction::LoadConst {

compiler/src/symboltable.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -213,21 +213,21 @@ impl SymbolTableAnalyzer {
213213
if symbol.is_assigned || symbol.is_parameter {
214214
symbol.scope = SymbolScope::Local;
215215
} else {
216-
// TODO: comment this out and make it work properly:
217-
/*
218-
*/
219-
let found_in_outer_scope = self
220-
.tables
221-
.iter()
222-
.skip(1)
223-
.any(|t| t.symbols.contains_key(&symbol.name));
216+
// Interesting stuff about the __class__ variable:
217+
// https://docs.python.org/3/reference/datamodel.html?highlight=__class__#creating-the-class-object
218+
let found_in_outer_scope = (symbol.name == "__class__")
219+
|| self
220+
.tables
221+
.iter()
222+
.skip(1)
223+
.any(|t| t.symbols.contains_key(&symbol.name));
224224

225225
if found_in_outer_scope {
226226
// Symbol is in some outer scope.
227227
symbol.is_free = true;
228228
} else {
229229
// Well, it must be a global then :)
230-
// symbol.scope = SymbolScope::Global;
230+
symbol.scope = SymbolScope::Global;
231231
}
232232
}
233233
}

crawl_sourcecode.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,4 @@ def print_table(table, indent=0):
7373
print('======== dis.dis ========')
7474
print()
7575
co = compile(source, filename, 'exec')
76-
print(dis.dis(co))
76+
dis.dis(co)

vm/src/frame.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,9 @@ impl Frame {
907907
bytecode::NameScope::Local => {
908908
self.scope.store_name(vm, name, obj);
909909
}
910+
bytecode::NameScope::Free => {
911+
self.scope.store_name(vm, name, obj);
912+
}
910913
}
911914
Ok(None)
912915
}
@@ -928,7 +931,8 @@ impl Frame {
928931
let optional_value = match name_scope {
929932
bytecode::NameScope::Global => self.scope.load_global(vm, name),
930933
bytecode::NameScope::NonLocal => self.scope.load_cell(vm, name),
931-
bytecode::NameScope::Local => self.scope.load_name(&vm, name),
934+
bytecode::NameScope::Local => self.scope.load_local(&vm, name),
935+
bytecode::NameScope::Free => self.scope.load_name(&vm, name),
932936
};
933937

934938
let value = match optional_value {

vm/src/obj/objtype.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ fn type_dict_setter(_instance: PyClassRef, _value: PyObjectRef, vm: &VirtualMach
312312

313313
/// This is the internal get_attr implementation for fast lookup on a class.
314314
pub fn class_get_attr(class: &PyClassRef, attr_name: &str) -> Option<PyObjectRef> {
315+
flame_guard!(format!("class_get_attr({:?})", attr_name));
316+
315317
if let Some(item) = class.attributes.borrow().get(attr_name).cloned() {
316318
return Some(item);
317319
}

vm/src/scope.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ pub trait NameProtocol {
123123
fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
124124
fn store_name(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef);
125125
fn delete_name(&self, vm: &VirtualMachine, name: &str) -> PyResult;
126+
fn load_local(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
126127
fn load_cell(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
127128
fn store_cell(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef);
128129
fn load_global(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
@@ -142,6 +143,12 @@ impl NameProtocol for Scope {
142143
self.load_global(vm, name)
143144
}
144145

146+
#[cfg_attr(feature = "flame-it", flame("Scope"))]
147+
/// Load a local name. Only check the local dictionary for the given name.
148+
fn load_local(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
149+
self.get_locals().get_item_option(name, vm).unwrap()
150+
}
151+
145152
#[cfg_attr(feature = "flame-it", flame("Scope"))]
146153
fn load_cell(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
147154
for dict in self.locals.iter().skip(1) {
@@ -170,7 +177,17 @@ impl NameProtocol for Scope {
170177
}
171178

172179
#[cfg_attr(feature = "flame-it", flame("Scope"))]
180+
/// Load a global name.
173181
fn load_global(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
182+
// First, take a look in the outmost local scope (the scope at top level)
183+
let last_local_dict = self.locals.iter().last();
184+
if let Some(local_dict) = last_local_dict {
185+
if let Some(value) = local_dict.get_item_option(name, vm).unwrap() {
186+
return Some(value);
187+
}
188+
}
189+
190+
// Now, take a look at the globals or builtins.
174191
if let Some(value) = self.globals.get_item_option(name, vm).unwrap() {
175192
Some(value)
176193
} else {

vm/src/vm.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,8 @@ impl VirtualMachine {
527527
where
528528
T: Into<PyFuncArgs>,
529529
{
530+
flame_guard!(format!("call_method({:?})", method_name));
531+
530532
// This is only used in the vm for magic methods, which use a greatly simplified attribute lookup.
531533
let cls = obj.class();
532534
match objtype::class_get_attr(&cls, method_name) {
@@ -545,7 +547,6 @@ impl VirtualMachine {
545547
}
546548
}
547549

548-
#[cfg_attr(feature = "flame-it", flame("VirtualMachine"))]
549550
fn _invoke(&self, func_ref: &PyObjectRef, args: PyFuncArgs) -> PyResult {
550551
vm_trace!("Invoke: {:?} {:?}", func_ref, args);
551552

0 commit comments

Comments
 (0)
X Tutup