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

Lines changed: 17 additions & 11 deletions
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

Lines changed: 21 additions & 6 deletions
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

Lines changed: 35 additions & 5 deletions
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

Lines changed: 8 additions & 0 deletions
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

Lines changed: 27 additions & 9 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy