Skip to content

Commit 3dfa6e7

Browse files
committed
Add support for calling built-in functions (only print for the moment).
1 parent 1fcce5c commit 3dfa6e7

File tree

5 files changed

+115
-50
lines changed

5 files changed

+115
-50
lines changed

src/lib.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod stack;
66

77
use std::fmt;
88
use std::io;
9+
use processor::Processor;
910

1011
#[derive(Debug)]
1112
pub enum InterpreterError {
@@ -24,19 +25,22 @@ impl fmt::Display for InterpreterError {
2425
}
2526
}
2627

27-
pub fn run_module<R: io::Read, EP: sandbox::EnvProxy>(reader: &mut R, envproxy: &mut EP) -> Result<objects::ObjectRef, InterpreterError> {
28+
pub fn run_module<R: io::Read, EP: sandbox::EnvProxy>(reader: &mut R, envproxy: EP) -> Result<(Processor<EP>, objects::ObjectRef), InterpreterError> {
2829
let mut buf = [0; 12];
2930
try!(reader.read_exact(&mut buf).map_err(InterpreterError::Io));
3031
// TODO: do something with the content of the buffer
3132
let mut store = objects::ObjectStore::new();
3233
let module = try!(marshal::read_object(reader, &mut store).map_err(InterpreterError::Unmarshal));
33-
processor::run_code_object(envproxy, &mut store, module).map_err(InterpreterError::Processor)
34+
let mut processor = Processor { envproxy: envproxy, store: store, builtin_functions: Processor::get_default_builtins() };
35+
let result = try!(processor.run_code_object(module).map_err(InterpreterError::Processor));
36+
Ok((processor, result))
3437
}
3538

3639
#[test]
3740
fn test_hello_world() {
3841
let mut reader: &[u8] = b"\xee\x0c\r\n\x15j\nW\x15\x00\x00\x00\xe3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00@\x00\x00\x00s\x0e\x00\x00\x00e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)\x02z\x0bHello worldN)\x01\xda\x05print\xa9\x00r\x02\x00\x00\x00r\x02\x00\x00\x00\xfa\x0b/tmp/foo.py\xda\x08<module>\x01\x00\x00\x00s\x00\x00\x00\x00";
39-
let mut envproxy = sandbox::MockEnvProxy::new();
40-
println!("{:?}", run_module(&mut reader, &mut envproxy).unwrap());
41-
assert_eq!(*envproxy.stdout_content.lock().unwrap(), b"Hello world\n");
42+
let envproxy = sandbox::MockEnvProxy::new();
43+
let (processor, result) = run_module(&mut reader, envproxy).unwrap();
44+
println!("{:?}", result);
45+
assert_eq!(*processor.envproxy.stdout_content.lock().unwrap(), b"Hello world\n");
4246
}

src/marshal/mod.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ pub mod common;
22
pub mod decode;
33

44
use std::io;
5-
use std::collections::HashSet;
6-
use super::objects::{Code, ObjectContent, Object, ObjectRef, ObjectStore};
5+
use super::objects::{Code, ObjectContent, ObjectRef, ObjectStore};
76
use self::common::Object as MarshalObject;
87
use self::common::Code as MarshalCode;
98

@@ -24,7 +23,13 @@ macro_rules! translate_code_field {
2423
fn translate_code(c: MarshalCode, translation_map: &Vec<ObjectRef>, store: &mut ObjectStore) -> Code {
2524
let code = translate_code_field!(c, Bytes, code, translation_map, store, "Code.code object must be bytes.");
2625
let consts = translate_code_field!(c, Tuple, consts, translation_map, store, "Code.consts object must be a tuple.");
27-
let names = translate_code_field!(c, Tuple, names, translation_map, store, "Code.names object must be a tuple.");
26+
let name_objs = translate_code_field!(c, Tuple, names, translation_map, store, "Code.names object must be a tuple.");
27+
let names = name_objs.iter().map(|obj| {
28+
match store.deref(obj).content {
29+
ObjectContent::String(ref name) => name.clone(),
30+
_ => panic!("At least one object in Code.names is not a string."),
31+
}
32+
}).collect();
2833
Code { code: code, consts: consts, names: names }
2934
}
3035

src/objects/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::collections::HashMap;
2-
use std::sync::Mutex;
32
use std::sync::atomic::{AtomicUsize, Ordering};
43

54
#[derive(Debug)]
@@ -12,7 +11,7 @@ pub struct Code {/*
1211
pub flags: u32,*/
1312
pub code: Vec<u8>,
1413
pub consts: Vec<ObjectRef>,
15-
pub names: Vec<ObjectRef>,/*
14+
pub names: Vec<String>,/*
1615
pub varnames: Object,
1716
pub freevars: Object,
1817
pub cellvars: Object,
@@ -23,6 +22,7 @@ pub struct Code {/*
2322
}
2423

2524
#[derive(Debug)]
25+
#[derive(Clone)]
2626
pub enum ObjectContent {
2727
None,
2828
True,
@@ -35,6 +35,7 @@ pub enum ObjectContent {
3535
Set(Vec<ObjectRef>),
3636
FrozenSet(Vec<ObjectRef>),
3737
Bytes(Vec<u8>),
38+
BuiltinFunction(String),
3839
}
3940

4041
#[derive(Debug)]

src/processor/instructions.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::str::Bytes;
2-
31
#[derive(PartialEq)]
42
#[derive(Debug)]
53
pub enum Instruction {

src/processor/mod.rs

Lines changed: 95 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
pub mod instructions;
22

3-
use super::objects::{Object, Code, ObjectStore, ObjectRef, ObjectContent};
3+
use super::objects::{Code, ObjectStore, ObjectRef, ObjectContent};
44
use super::sandbox::EnvProxy;
55
use super::stack::{Stack, VectorStack};
66
use self::instructions::Instruction;
77
use std::fmt;
8+
use std::collections::HashMap;
9+
use std::io::Write;
810

911
#[derive(Debug)]
1012
pub enum ProcessorError {
@@ -16,6 +18,8 @@ pub enum ProcessorError {
1618
StackTooSmall,
1719
InvalidConstIndex,
1820
InvalidNameIndex,
21+
UnknownBuiltin(String),
22+
Exception(String),
1923
}
2024

2125
impl fmt::Display for ProcessorError {
@@ -24,45 +28,98 @@ impl fmt::Display for ProcessorError {
2428
}
2529
}
2630

27-
fn call_function<EP: EnvProxy>(envproxy: &mut EP, store: &mut ObjectStore, func_ref: &ObjectRef, args: Vec<ObjectRef>, kwags: Vec<ObjectRef>) -> Result<ObjectRef, ProcessorError> {
28-
let code = match store.deref(func_ref).content {
29-
ObjectContent::Code(ref code) => code.clone(),
30-
ref o => return Err(ProcessorError::NotACodeObject(format!("{:?}", o))),
31-
};
32-
run_code(envproxy, store, code)
33-
}
34-
35-
fn run_code<EP: EnvProxy>(envproxy: &mut EP, store: &mut ObjectStore, code: Code) -> Result<ObjectRef, ProcessorError> {
36-
let bytecode: Vec<u8> = code.code;
37-
let instructions: Vec<Instruction> = instructions::InstructionDecoder::new(bytecode.iter()).into_iter().collect();
38-
let mut program_counter = 0 as usize;
39-
let mut stack = VectorStack::new();
40-
while true {
41-
let instruction = try!(instructions.get(program_counter).ok_or(ProcessorError::InvalidProgramCounter));
42-
program_counter += 1;
43-
match *instruction {
44-
Instruction::ReturnValue => return Ok(try!(stack.pop().ok_or(ProcessorError::StackTooSmall))),
45-
Instruction::LoadConst(i) => stack.push(try!(code.names.get(i).ok_or(ProcessorError::InvalidConstIndex)).clone()),
46-
Instruction::LoadName(i) => stack.push(try!(code.names.get(i).ok_or(ProcessorError::InvalidNameIndex)).clone()),
47-
Instruction::CallFunction(nb_args, nb_kwargs) => {
48-
// See “Call constructs” at:
49-
// http://security.coverity.com/blog/2014/Nov/understanding-python-bytecode.html
50-
let kwargs = try!(stack.pop_many(nb_kwargs*2).ok_or(ProcessorError::StackTooSmall));
51-
let args = try!(stack.pop_many(nb_args).ok_or(ProcessorError::StackTooSmall));
52-
let func = try!(stack.pop().ok_or(ProcessorError::StackTooSmall));
53-
let ret_value = call_function(envproxy, store, &func, args, kwargs);
54-
stack.push(try!(ret_value))
31+
fn builtin_print<EP: EnvProxy>(processor: &mut Processor<EP>, args: Vec<ObjectRef>, kwargs: HashMap<String, ObjectRef>) -> Result<ObjectRef, ProcessorError> {
32+
if args.len() > 1 {
33+
return Err(ProcessorError::Exception(format!("print takes exactly one argument, not {}.", args.len())))
34+
}
35+
else {
36+
let obj_ref = args.get(0).unwrap();
37+
match processor.store.deref(obj_ref).content {
38+
ObjectContent::String(ref s) => {
39+
processor.envproxy.stdout().write(s.clone().into_bytes().as_slice()).unwrap(); // TODO: check
40+
processor.envproxy.stdout().write(b"\n").unwrap(); // TODO: check
5541
}
56-
_ => panic!(format!("todo: instruction {:?}", *instruction)),
42+
ref o => return Err(ProcessorError::Exception(format!("print takes a string, not {:?}", o))),
5743
}
58-
};
59-
panic!("Unreachable")
44+
}
45+
Ok(processor.store.allocate(ObjectContent::None))
6046
}
6147

62-
pub fn run_code_object<EP: EnvProxy>(envproxy: &mut EP, store: &mut ObjectStore, module: ObjectRef) -> Result<ObjectRef, ProcessorError> {
63-
let code = match store.deref(&module).content {
64-
ObjectContent::Code(ref code) => code.clone(),
65-
ref o => return Err(ProcessorError::NotACodeObject(format!("{:?}", o))),
66-
};
67-
run_code(envproxy, store, code)
48+
pub type PyFunction<EP> = fn(&mut Processor<EP>, /*args:*/ Vec<ObjectRef>, /*kwargs:*/ HashMap<String, ObjectRef>) -> Result<ObjectRef, ProcessorError>;
49+
50+
pub struct Processor<EP: EnvProxy> {
51+
pub envproxy: EP,
52+
pub store: ObjectStore,
53+
pub builtin_functions: HashMap<String, PyFunction<EP>>,
54+
}
55+
56+
impl<EP: EnvProxy> Processor<EP> {
57+
pub fn get_default_builtins() -> HashMap<String, PyFunction<EP>> {
58+
let mut builtins: HashMap<String, PyFunction<EP>> = HashMap::new();
59+
builtins.insert("print".to_string(), builtin_print);
60+
builtins
61+
}
62+
63+
fn load_name(&mut self, name: String) -> Result<ObjectRef, ProcessorError> {
64+
Ok(self.store.allocate(ObjectContent::BuiltinFunction(name)))
65+
}
66+
67+
fn call_function(&mut self, func_ref: &ObjectRef, args: Vec<ObjectRef>, kwargs: Vec<ObjectRef>) -> Result<ObjectRef, ProcessorError> {
68+
// TODO: clone only if necessary
69+
match self.store.deref(func_ref).content.clone() {
70+
ObjectContent::Code(code) => {
71+
self.run_code(code)
72+
},
73+
ObjectContent::BuiltinFunction(name) => {
74+
let f = match self.builtin_functions.get(&name) {
75+
Some(f) => f.clone(),
76+
None => return Err(ProcessorError::UnknownBuiltin(name.clone())),
77+
};
78+
f(self, args, HashMap::new()) // TODO: use the real kwargs
79+
},
80+
ref o => return Err(ProcessorError::NotACodeObject(format!("{:?}", o))),
81+
}
82+
}
83+
84+
fn run_code(&mut self, code: Code) -> Result<ObjectRef, ProcessorError> {
85+
let bytecode: Vec<u8> = code.code;
86+
let instructions: Vec<Instruction> = instructions::InstructionDecoder::new(bytecode.iter()).into_iter().collect();
87+
let mut program_counter = 0 as usize;
88+
let mut stack = VectorStack::new();
89+
while true {
90+
let instruction = try!(instructions.get(program_counter).ok_or(ProcessorError::InvalidProgramCounter));
91+
program_counter += 1;
92+
match *instruction {
93+
Instruction::PopTop => {
94+
try!(stack.pop().ok_or(ProcessorError::StackTooSmall));
95+
()
96+
},
97+
Instruction::ReturnValue => return Ok(try!(stack.pop().ok_or(ProcessorError::StackTooSmall))),
98+
Instruction::LoadConst(i) => stack.push(try!(code.consts.get(i).ok_or(ProcessorError::InvalidConstIndex)).clone()),
99+
Instruction::LoadName(i) => {
100+
let name = try!(code.names.get(i).ok_or(ProcessorError::InvalidNameIndex)).clone();
101+
stack.push(try!(self.load_name(name)))
102+
}
103+
Instruction::CallFunction(nb_args, nb_kwargs) => {
104+
// See “Call constructs” at:
105+
// http://security.coverity.com/blog/2014/Nov/understanding-python-bytecode.html
106+
let kwargs = try!(stack.pop_many(nb_kwargs*2).ok_or(ProcessorError::StackTooSmall));
107+
let args = try!(stack.pop_many(nb_args).ok_or(ProcessorError::StackTooSmall));
108+
let func = try!(stack.pop().ok_or(ProcessorError::StackTooSmall));
109+
let ret_value = self.call_function(&func, args, kwargs);
110+
stack.push(try!(ret_value))
111+
}
112+
_ => panic!(format!("todo: instruction {:?}", *instruction)),
113+
}
114+
};
115+
panic!("Unreachable")
116+
}
117+
118+
pub fn run_code_object(&mut self, module: ObjectRef) -> Result<ObjectRef, ProcessorError> {
119+
let code = match self.store.deref(&module).content {
120+
ObjectContent::Code(ref code) => code.clone(),
121+
ref o => return Err(ProcessorError::NotACodeObject(format!("{:?}", o))),
122+
};
123+
self.run_code(code)
124+
}
68125
}

0 commit comments

Comments
 (0)