Skip to content

Commit 49d50f8

Browse files
committed
Add support for class attributes.
1 parent 5e95013 commit 49d50f8

File tree

5 files changed

+108
-31
lines changed

5 files changed

+108
-31
lines changed

run_functional_tests.py

+17-11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import sys
55
import glob
6+
import traceback
67
import subprocess
78

89
SOURCE_DIR = os.path.dirname('__file__')
@@ -17,19 +18,24 @@
1718

1819
for filename in glob.glob(TESTS_DIR + os.path.sep + '*.py'):
1920
print('Running test: {}'.format(filename))
20-
vm_result = subprocess.check_output([BIN, LIB_DIR, filename + 'c'], universal_newlines=True)
2121
system_python_result = subprocess.check_output([sys.executable, filename], universal_newlines=True)
22-
if vm_result != system_python_result:
23-
print('=' * 100)
24-
print('Test {} failed.'.format(filename))
25-
print('-' * 100)
26-
print('System Python:')
27-
print(system_python_result)
28-
print('-' * 100)
29-
print('VM result:')
30-
print(vm_result)
31-
print('=' * 100)
22+
try:
23+
vm_result = subprocess.check_output([BIN, LIB_DIR, filename + 'c'], universal_newlines=True)
24+
except subprocess.CalledProcessError as e:
25+
traceback.print_exc()
3226
all_ok = False
27+
else:
28+
if vm_result != system_python_result:
29+
print('=' * 100)
30+
print('Test {} failed.'.format(filename))
31+
print('-' * 100)
32+
print('System Python:')
33+
print(system_python_result)
34+
print('-' * 100)
35+
print('VM result:')
36+
print(vm_result)
37+
print('=' * 100)
38+
all_ok = False
3339

3440
if all_ok:
3541
exit(0)

src/objects/mod.rs

+21-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ extern crate itertools;
22
use std::collections::HashMap;
33
use std::collections::HashSet;
44
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
5+
use std::borrow::Cow;
6+
use std::rc::Rc;
7+
use std::cell::RefCell;
58
use std::fmt;
69
use self::itertools::Itertools;
710
use super::state::State;
@@ -86,7 +89,7 @@ pub enum ObjectContent {
8689
Module(ObjectRef),
8790
PrimitiveNamespace, // __primitives__
8891
PrimitiveFunction(String),
89-
Class(Option<ObjectRef>),
92+
Class,
9093
RandomAccessIterator(ObjectRef, usize, u64), // container, index, container version
9194
OtherObject,
9295
}
@@ -101,6 +104,7 @@ pub struct Object {
101104
pub content: ObjectContent,
102105
pub class: ObjectRef,
103106
pub bases: Option<Vec<ObjectRef>>, // superclasses
107+
pub attributes: Option<Rc<RefCell<HashMap<String, ObjectRef>>>>,
104108
}
105109

106110
impl Object {
@@ -114,16 +118,18 @@ impl Object {
114118
content: content,
115119
class: class,
116120
bases: None,
121+
attributes: Some(Rc::new(RefCell::new(HashMap::new()))),
117122
}
118123
}
119124

120-
pub fn new_class(name: String, code: Option<ObjectRef>, metaclass: ObjectRef, bases: Vec<ObjectRef>) -> Object {
125+
pub fn new_class(name: String, attributes: Option<Rc<RefCell<HashMap<String, ObjectRef>>>>, metaclass: ObjectRef, bases: Vec<ObjectRef>) -> Object {
121126
Object {
122127
version: Object::new_version(),
123128
name: Some(name),
124-
content: ObjectContent::Class(code),
129+
content: ObjectContent::Class,
125130
class: metaclass,
126131
bases: Some(bases),
132+
attributes: attributes,
127133
}
128134
}
129135
}
@@ -177,7 +183,7 @@ impl ObjectRef {
177183
},
178184
ObjectContent::PrimitiveNamespace => "__primitives__".to_string(),
179185
ObjectContent::PrimitiveFunction(ref s) => format!("__primitives__.{}", s),
180-
ObjectContent::Class(_) => {
186+
ObjectContent::Class => {
181187
match obj.name {
182188
None => "<anonymous class>".to_string(),
183189
Some(ref s) => format!("<class {}>", s),
@@ -212,6 +218,13 @@ impl ObjectRef {
212218
let iterator = Object::new_instance(None, state.primitive_objects.iterator_type.clone(), ObjectContent::RandomAccessIterator(self.clone(), 0, obj_version));
213219
state.store.allocate(iterator)
214220
}
221+
222+
pub fn setattr(&self, store: &mut ObjectStore, name: String, value: ObjectRef) {
223+
match store.deref(self).attributes {
224+
Some(ref attributes) => attributes.borrow_mut().insert(name, value),
225+
None => panic!("{}'s attributes are not settable.", self.repr(store)),
226+
};
227+
}
215228
}
216229

217230

@@ -310,14 +323,16 @@ impl PrimitiveObjects {
310323
name: Some("object".to_string()),
311324
content: ObjectContent::OtherObject,
312325
bases: Some(vec![]),
313-
class: type_ref.clone()
326+
class: type_ref.clone(),
327+
attributes: None,
314328
};
315329
let type_ = Object {
316330
version: Object::new_version(),
317331
name: Some("type".to_string()),
318332
content: ObjectContent::OtherObject,
319333
bases: Some(vec![obj_ref.clone()]),
320-
class: type_ref.clone()
334+
class: type_ref.clone(),
335+
attributes: None,
321336
};
322337
store.allocate_at(obj_ref.clone(), obj);
323338
store.allocate_at(type_ref.clone(), type_);

src/primitives/mod.rs

+35-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
use std::collections::{HashMap, HashSet};
22
use std::io::Write;
33
use std::collections::linked_list::LinkedList;
4+
use std::rc::Rc;
5+
use std::cell::RefCell;
6+
use std::iter::IntoIterator;
47
use super::sandbox::EnvProxy;
58
use super::state::{State, PyResult, PyFunction, raise, return_value};
69
use super::objects::{ObjectRef, ObjectContent, Object, ObjectStore};
710
use super::processor::frame::Frame;
11+
use super::processor::instructions::{Instruction, InstructionDecoder};
12+
use super::varstack::{VarStack, VectorVarStack};
813

914
macro_rules! parse_first_arguments {
1015
( $funcname:expr, $store:expr, $args:ident, $args_iter:ident, $( $argname:tt $argexpected:tt : { $($argpattern:pat => $argcode:block,)* } ),* ) => {{
@@ -52,14 +57,17 @@ fn write_stdout<EP: EnvProxy>(processor: &mut State<EP>, call_stack: &mut Vec<Fr
5257
return_value(call_stack, processor.primitive_objects.none.clone())
5358
}
5459

55-
fn build_class<EP: EnvProxy>(processor: &mut State<EP>, call_stack: &mut Vec<Frame>, args: Vec<ObjectRef>) {
60+
fn build_class<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame>, args: Vec<ObjectRef>) {
5661
let name;
5762
let code;
5863
let mut args_iter = args.into_iter();
59-
parse_first_arguments!("__primitives__.build_class", processor.store, args, args_iter,
64+
parse_first_arguments!("__primitives__.build_class", state.store, args, args_iter,
6065
"func" "a function": {
6166
ObjectContent::Function(_, ref code_arg, _) => {
62-
code = code_arg.clone();
67+
match state.store.deref(code_arg).content {
68+
ObjectContent::Code(ref code_) => code = code_.clone(),
69+
_ => panic!("__build_class__'s function argument has a code that is not code.")
70+
}
6371
},
6472
},
6573
"name" "a string": {
@@ -68,12 +76,34 @@ fn build_class<EP: EnvProxy>(processor: &mut State<EP>, call_stack: &mut Vec<Fra
6876
);
6977
let bases: Vec<ObjectRef> = args_iter.collect();
7078
let bases = if bases.len() == 0 {
71-
vec![processor.primitive_objects.object.clone()]
79+
vec![state.primitive_objects.object.clone()]
7280
}
7381
else {
7482
bases
7583
};
76-
return_value(call_stack, processor.store.allocate(Object::new_class(name, Some(code), processor.primitive_objects.type_.clone(), bases)))
84+
85+
86+
let mut attributes = Rc::new(RefCell::new(HashMap::new()));
87+
let cls_ref = state.store.allocate(Object::new_class(name, Some(attributes.clone()), state.primitive_objects.type_.clone(), bases));
88+
89+
let mut instructions: Vec<Instruction> = InstructionDecoder::new(code.code.iter()).collect();
90+
91+
// Hack to made the class' code return the class instead of None
92+
assert_eq!(instructions.pop(), Some(Instruction::ReturnValue));
93+
instructions.pop(); // LoadConst None
94+
instructions.push(Instruction::PushImmediate(cls_ref.clone()));
95+
instructions.push(Instruction::ReturnValue);
96+
97+
let mut frame = Frame {
98+
object: cls_ref,
99+
var_stack: VectorVarStack::new(),
100+
block_stack: vec![],
101+
locals: attributes,
102+
instructions: instructions,
103+
code: (*code).clone(),
104+
program_counter: 0,
105+
};
106+
call_stack.push(frame);
77107
}
78108

79109
pub fn native_issubclass(store: &ObjectStore, first: &ObjectRef, second: &ObjectRef) -> bool {

src/processor/instructions.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use super::super::objects::ObjectRef;
2+
13
#[derive(PartialEq)]
24
#[derive(Debug)]
35
#[derive(Clone)]
@@ -38,6 +40,8 @@ impl CmpOperator {
3840
#[derive(Debug)]
3941
#[derive(Clone)]
4042
pub enum Instruction {
43+
PushImmediate(ObjectRef),
44+
4145
PopTop,
4246
DupTop,
4347
Nop,
@@ -50,6 +54,8 @@ pub enum Instruction {
5054
PopExcept,
5155
StoreName(usize),
5256
ForIter(usize),
57+
StoreAttr(usize),
58+
StoreGlobal(usize),
5359
LoadConst(usize),
5460
LoadName(usize),
5561
BuildTuple(usize),
@@ -130,6 +136,8 @@ impl<'a, I> Iterator for InstructionDecoder<I> where I: Iterator<Item=&'a u8> {
130136
89 => Instruction::PopExcept,
131137
90 => Instruction::StoreName(self.read_argument() as usize),
132138
93 => Instruction::ForIter(self.read_argument() as usize),
139+
95 => Instruction::StoreAttr(self.read_argument() as usize),
140+
97 => Instruction::StoreGlobal(self.read_argument() as usize),
133141
100 => Instruction::LoadConst(self.read_argument() as usize),
134142
101 => Instruction::LoadName(self.read_argument() as usize),
135143
102 => Instruction::BuildTuple(self.read_argument() as usize),

src/processor/mod.rs

+27-9
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,11 @@ fn load_attr<EP: EnvProxy>(state: &mut State<EP>, obj: &Object, name: &String) -
123123
}
124124
}
125125
else {
126-
panic!(format!("Not implemented: looking up attribute '{}' of {:?}", name, obj))
126+
// TODO: special names
127+
match obj.attributes {
128+
Some(ref attributes) => attributes.borrow().get(name).map(|r| r.clone()),
129+
None => None,
130+
}
127131
}
128132
}
129133
}
@@ -133,12 +137,7 @@ fn load_attr<EP: EnvProxy>(state: &mut State<EP>, obj: &Object, name: &String) -
133137
fn call_function<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame>, func_ref: &ObjectRef, mut args: Vec<ObjectRef>, kwargs: Vec<(ObjectRef, ObjectRef)>) {
134138
// TODO: clone only if necessary
135139
match state.store.deref(func_ref).content.clone() {
136-
ObjectContent::Class(None) => {
137-
let frame = call_stack.last_mut().unwrap();
138-
frame.var_stack.push(state.store.allocate(Object::new_instance(None, func_ref.clone(), ObjectContent::OtherObject)))
139-
},
140-
ObjectContent::Class(Some(ref code_ref)) => {
141-
// TODO: run code
140+
ObjectContent::Class => {
142141
let frame = call_stack.last_mut().unwrap();
143142
frame.var_stack.push(state.store.allocate(Object::new_instance(None, func_ref.clone(), ObjectContent::OtherObject)))
144143
},
@@ -236,7 +235,8 @@ fn run_code<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame>) ->
236235
let frame = call_stack.last_mut().unwrap();
237236
let instruction = py_unwrap!(state, frame.instructions.get(frame.program_counter), ProcessorError::InvalidProgramCounter);
238237
// Useful for debugging:
239-
/*println!("");
238+
/*
239+
println!("");
240240
for r in frame.var_stack.iter() {
241241
println!("{}", r.repr(&state.store));
242242
}
@@ -246,6 +246,10 @@ fn run_code<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame>) ->
246246
instruction.clone()
247247
};
248248
match instruction {
249+
Instruction::PushImmediate(r) => {
250+
let frame = call_stack.last_mut().unwrap();
251+
frame.var_stack.push(r);
252+
},
249253
Instruction::PopTop => {
250254
let frame = call_stack.last_mut().unwrap();
251255
pop_stack!(state, frame.var_stack);
@@ -368,6 +372,20 @@ fn run_code<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame>) ->
368372
let iter_func = state.store.allocate(Object::new_instance(None, state.primitive_objects.function_type.clone(), ObjectContent::PrimitiveFunction("iter".to_string())));
369373
call_function(state, call_stack, &iter_func, vec![iterator], vec![]);
370374
}
375+
Instruction::StoreAttr(i) => {
376+
let frame = call_stack.last_mut().unwrap();
377+
let name = py_unwrap!(state, frame.code.names.get(i), ProcessorError::InvalidNameIndex).clone();
378+
let owner = pop_stack!(state, frame.var_stack);
379+
let value = pop_stack!(state, frame.var_stack);
380+
owner.setattr(&mut state.store, name, value);
381+
println!("{:?}", state.store.deref(&owner).attributes)
382+
}
383+
Instruction::StoreGlobal(i) => {
384+
let frame = call_stack.last_mut().unwrap();
385+
let name = py_unwrap!(state, frame.code.varnames.get(i), ProcessorError::InvalidVarnameIndex).clone();
386+
let mut globals = state.modules.get(&frame.object.module(&state.store)).unwrap().borrow_mut();
387+
globals.insert(name, pop_stack!(state, frame.var_stack));
388+
}
371389
Instruction::LoadConst(i) => {
372390
let frame = call_stack.last_mut().unwrap();
373391
frame.var_stack.push(py_unwrap!(state, frame.code.consts.get(i), ProcessorError::InvalidConstIndex).clone())
@@ -408,7 +426,7 @@ fn run_code<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame>) ->
408426
match res {
409427
None => {
410428
let exc = state.primitive_objects.nameerror.clone();
411-
raise(state, call_stack, exc, format!("Unknown variable {}", name))
429+
raise(state, call_stack, exc, format!("Unknown attribute {}", name))
412430
},
413431
Some(obj_ref) => {
414432
let frame = call_stack.last_mut().unwrap();

0 commit comments

Comments
 (0)