Skip to content

Commit ea25a4d

Browse files
committed
Add support for if. Fix instruction addresses.
1 parent d6779bb commit ea25a4d

File tree

3 files changed

+121
-11
lines changed

3 files changed

+121
-11
lines changed

functional_tests/if.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
a = 5
2+
if a == 5:
3+
print('5 == 5')
4+
5+
print('---')
6+
7+
if a == 5:
8+
print('5 == 5')
9+
else:
10+
print('5 != 5')
11+
12+
print('---')
13+
14+
if a == 6:
15+
print('5 == 6')
16+
else:
17+
print('5 != 6')
18+
19+
print('---')
20+
21+
if a == 5:
22+
print('5 == 5')
23+
elif a == 6:
24+
print('5 == 6')
25+
else:
26+
print('5 != 5 and 5 != 6')
27+
28+
print('---')
29+
30+
if a == 6:
31+
print('5 == 6')
32+
elif a == 5:
33+
print('5 == 5')
34+
else:
35+
print('5 != 5 and 5 != 6')

src/processor/instructions.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,43 @@
1+
#[derive(PartialEq)]
2+
#[derive(Debug)]
3+
pub enum CmpOperator {
4+
Lt, // Lower than
5+
Leq, // Lower or equal
6+
Eq, // Equal
7+
Neq, // Not equal
8+
Gt, // Greater than
9+
Geq, // Greater or equal
10+
In,
11+
NotIn,
12+
Is,
13+
IsNot,
14+
ExceptionMatch,
15+
}
16+
17+
impl CmpOperator {
18+
pub fn from_bytecode(n: u16) -> Self {
19+
match n {
20+
0 => CmpOperator::Lt,
21+
1 => CmpOperator::Leq,
22+
2 => CmpOperator::Eq,
23+
3 => CmpOperator::Neq,
24+
4 => CmpOperator::Gt,
25+
5 => CmpOperator::Geq,
26+
6 => CmpOperator::In,
27+
7 => CmpOperator::NotIn,
28+
8 => CmpOperator::Is,
29+
9 => CmpOperator::IsNot,
30+
10=> CmpOperator::ExceptionMatch,
31+
_ => panic!("Invalid cmp operator code.")
32+
}
33+
}
34+
}
35+
136
#[derive(PartialEq)]
237
#[derive(Debug)]
338
pub enum Instruction {
439
PopTop,
40+
Nop,
541
BinarySubscr,
642
LoadBuildClass,
743
ReturnValue,
@@ -10,6 +46,9 @@ pub enum Instruction {
1046
LoadName(usize),
1147
LoadAttr(usize),
1248
SetupLoop(usize),
49+
CompareOp(CmpOperator),
50+
JumpForward(usize),
51+
PopJumpIfFalse(usize),
1352
LoadFast(usize),
1453
LoadGlobal(usize),
1554
CallFunction(usize, usize), // nb_args, nb_kwargs
@@ -19,24 +58,29 @@ pub enum Instruction {
1958
#[derive(Debug)]
2059
pub struct InstructionDecoder<I> where I: Iterator {
2160
bytestream: I,
61+
pending_nops: u8, // Number of NOPs to be inserted after this instruction to match CPython's addresses (instructions have different sizes)
2262
}
2363

2464
impl<I> InstructionDecoder<I> where I: Iterator {
2565
pub fn new(bytes: I) -> InstructionDecoder<I> {
26-
InstructionDecoder { bytestream: bytes }
66+
InstructionDecoder { bytestream: bytes, pending_nops: 0, }
2767
}
2868
}
2969

3070
impl<'a, I> InstructionDecoder<I> where I: Iterator<Item=&'a u8> {
3171
fn read_byte(&mut self) -> u8 {
3272
match self.bytestream.next() {
33-
Some(b) => *b,
73+
Some(b) => {
74+
self.pending_nops += 1;
75+
*b
76+
},
3477
_ => panic!("End of stream in the middle of an instruction."),
3578
}
3679
}
3780
fn read_argument(&mut self) -> u16 {
3881
match (self.bytestream.next(), self.bytestream.next()) {
3982
(Some(b1), Some(b2)) => {
83+
self.pending_nops += 2;
4084
((*b2 as u16) << 8) + (*b1 as u16)},
4185
_ => panic!("End of stream in the middle of an instruction."),
4286
}
@@ -47,6 +91,10 @@ impl<'a, I> Iterator for InstructionDecoder<I> where I: Iterator<Item=&'a u8> {
4791
type Item = Instruction;
4892

4993
fn next(&mut self) -> Option<Instruction> {
94+
if self.pending_nops != 0 {
95+
self.pending_nops -= 1;
96+
return Some(Instruction::Nop)
97+
};
5098
self.bytestream.next().map(|opcode| {
5199
match *opcode {
52100
1 => Instruction::PopTop,
@@ -57,6 +105,9 @@ impl<'a, I> Iterator for InstructionDecoder<I> where I: Iterator<Item=&'a u8> {
57105
100 => Instruction::LoadConst(self.read_argument() as usize),
58106
101 => Instruction::LoadName(self.read_argument() as usize),
59107
106 => Instruction::LoadAttr(self.read_argument() as usize),
108+
107 => Instruction::CompareOp(CmpOperator::from_bytecode(self.read_argument())),
109+
110 => Instruction::JumpForward(self.read_argument() as usize + 2), // +2, because JumpForward takes 3 bytes, and the relative address is computed from the next instruction.
110+
114 => Instruction::PopJumpIfFalse(self.read_argument() as usize),
60111
116 => Instruction::LoadGlobal(self.read_argument() as usize),
61112
120 => Instruction::SetupLoop(self.read_argument() as usize),
62113
124 => Instruction::LoadFast(self.read_argument() as usize),

src/processor/mod.rs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pub mod instructions;
33
use super::objects::{Code, ObjectStore, ObjectRef, ObjectContent, PrimitiveObjects, Object};
44
use super::sandbox::EnvProxy;
55
use super::stack::{Stack, VectorStack};
6-
use self::instructions::Instruction;
6+
use self::instructions::{CmpOperator, Instruction};
77
use std::fmt;
88
use std::collections::HashMap;
99
use std::io::Read;
@@ -172,11 +172,13 @@ impl<EP: EnvProxy> Processor<EP> {
172172
let mut stacks = Stacks { var_stack: VectorStack::new(), loop_stack: VectorStack::new() };
173173
loop {
174174
let instruction = try!(instructions.get(program_counter).ok_or(ProcessorError::InvalidProgramCounter));
175+
program_counter += 1;
175176
match *instruction {
176177
Instruction::PopTop => {
177178
pop_stack!(stacks.var_stack);
178179
()
179180
},
181+
Instruction::Nop => (),
180182
Instruction::BinarySubscr => {
181183
let index_ref = pop_stack!(stacks.var_stack);
182184
let index = self.store.deref(&index_ref).content.clone();
@@ -207,13 +209,6 @@ impl<EP: EnvProxy> Processor<EP> {
207209
let name = try!(code.names.get(i).ok_or(ProcessorError::InvalidNameIndex)).clone();
208210
stacks.var_stack.push(try!(self.load_name(namespace, name)))
209211
}
210-
Instruction::SetupLoop(i) => {
211-
stacks.loop_stack.push(Loop { begin: program_counter, end: program_counter+i })
212-
}
213-
Instruction::LoadFast(i) => {
214-
let name = try!(code.varnames.get(i).ok_or(ProcessorError::InvalidVarnameIndex)).clone();
215-
stacks.var_stack.push(try!(self.load_name(namespace, name)))
216-
}
217212
Instruction::LoadAttr(i) => {
218213
let obj = {
219214
let obj_ref = try!(stacks.var_stack.pop().ok_or(ProcessorError::StackTooSmall));
@@ -222,6 +217,36 @@ impl<EP: EnvProxy> Processor<EP> {
222217
let name = try!(code.names.get(i).ok_or(ProcessorError::InvalidNameIndex)).clone();
223218
stacks.var_stack.push(try!(self.load_attr(&obj, name)))
224219
},
220+
Instruction::SetupLoop(i) => {
221+
stacks.loop_stack.push(Loop { begin: program_counter, end: program_counter+i })
222+
}
223+
Instruction::CompareOp(CmpOperator::Eq) => {
224+
// TODO: enhance this
225+
let obj1 = self.store.deref(&pop_stack!(stacks.var_stack));
226+
let obj2 = self.store.deref(&pop_stack!(stacks.var_stack));
227+
if obj1.name == obj2.name && obj1.content == obj2.content {
228+
stacks.var_stack.push(self.primitive_objects.true_obj.clone())
229+
}
230+
else {
231+
stacks.var_stack.push(self.primitive_objects.false_obj.clone())
232+
};
233+
}
234+
Instruction::JumpForward(delta) => {
235+
program_counter += delta
236+
}
237+
Instruction::LoadFast(i) => {
238+
let name = try!(code.varnames.get(i).ok_or(ProcessorError::InvalidVarnameIndex)).clone();
239+
stacks.var_stack.push(try!(self.load_name(namespace, name)))
240+
}
241+
Instruction::PopJumpIfFalse(target) => {
242+
let obj = self.store.deref(&pop_stack!(stacks.var_stack));
243+
match obj.content {
244+
ObjectContent::True => (),
245+
ObjectContent::False => program_counter = target,
246+
_ => unimplemented!(),
247+
}
248+
}
249+
225250
Instruction::CallFunction(nb_args, nb_kwargs) => {
226251
// See “Call constructs” at:
227252
// http://security.coverity.com/blog/2014/Nov/understanding-python-bytecode.html
@@ -245,7 +270,6 @@ impl<EP: EnvProxy> Processor<EP> {
245270
},
246271
_ => panic!(format!("todo: instruction {:?}", *instruction)),
247272
}
248-
program_counter += 1;
249273
};
250274
}
251275

0 commit comments

Comments
 (0)