pub mod code_objects;
pub mod ext_instructions;
pub mod instructions;
pub mod opcodes;
#[cfg(feature = "sir")]
pub mod sir_passes;
#[cfg(test)]
mod tests {
use python_marshal::Kind::{ShortAscii, ShortAsciiInterned};
use python_marshal::{CodeFlags, PyString};
use crate::v310;
use crate::v310::code_objects::CompareOperation::Equal;
use crate::v310::code_objects::{AbsoluteJump, Constant, FrozenConstant, LinetableEntry};
use crate::v310::ext_instructions::{ExtInstruction, ExtInstructions};
use crate::v310::instructions::{
Instruction, Instructions, get_line_number, starts_line_number,
};
use crate::v310::opcodes::Opcode;
use crate::{load_pyc, prelude::*};
use std::fs::File;
use std::io::BufReader;
#[test]
fn test_load_pyc() {
let file = File::open("tests/test.pyc").unwrap();
let reader = BufReader::new(file);
let original_pyc = python_marshal::load_pyc(reader).unwrap();
let original_pyc = python_marshal::resolver::resolve_all_refs(
&original_pyc.object,
&original_pyc.references,
)
.0;
let file = File::open("tests/test.pyc").unwrap();
let reader = BufReader::new(file);
let pyc_file = load_pyc(reader).unwrap();
let pyc: python_marshal::PycFile = pyc_file.into();
assert_eq!(original_pyc, pyc.object);
}
#[test]
fn test_extended_arg() {
let mut instructions: v310::ext_instructions::ExtInstructions = ([
(Opcode::LOAD_NAME, 0).try_into().unwrap(),
(Opcode::LOAD_CONST, 0).try_into().unwrap(),
ExtInstruction::CompareOp(Equal),
(Opcode::POP_JUMP_IF_FALSE, 4).try_into().unwrap(),
(Opcode::LOAD_NAME, 1).try_into().unwrap(),
(Opcode::LOAD_NAME, 2).try_into().unwrap(),
(Opcode::CALL_FUNCTION, 1).try_into().unwrap(),
(Opcode::POP_TOP, 0).try_into().unwrap(),
(Opcode::LOAD_CONST, 1).try_into().unwrap(),
(Opcode::RETURN_VALUE, 0).try_into().unwrap(),
(Opcode::LOAD_CONST, 1).try_into().unwrap(),
(Opcode::RETURN_VALUE, 0).try_into().unwrap(),
]
.as_slice())
.into();
instructions.insert_instructions(3, &vec![(Opcode::NOP, 0).try_into().unwrap(); 300]);
let og_target = instructions
.get_absolute_jump_target(AbsoluteJump::new(304))
.expect("Must be to the load name after the jump");
dbg!(og_target);
let resolved = instructions.to_instructions().to_resolved().unwrap();
match resolved
.iter()
.find(|i| i.get_opcode() == Opcode::POP_JUMP_IF_FALSE)
.expect("There must be a jump")
{
ExtInstruction::PopJumpIfFalse(jump) => {
dbg!(jump);
let target = resolved
.get_absolute_jump_target(*jump)
.expect("Should never fail");
dbg!(target);
assert_eq!(og_target, target);
}
_ => panic!(),
}
assert_eq!(instructions, resolved);
}
#[test]
fn test_absolute_jump() {
let mut instructions = Instructions::new(vec![
Instruction::ExtendedArg(1),
Instruction::JumpAbsolute(4), ]);
for _ in instructions.len()..150 {
instructions.append_instruction(Instruction::Nop(0));
}
assert_eq!(instructions.len(), 150);
instructions.append_instruction(Instruction::ExtendedArg(1));
for _ in instructions.len()..260 {
instructions.append_instruction(Instruction::Nop(0));
}
assert_eq!(instructions.len(), 260);
instructions.append_instruction(Instruction::ReturnValue(0));
let resolved = instructions.to_resolved().unwrap();
let resolved_jump: AbsoluteJump = resolved.first().unwrap().get_raw_value().into();
let jump: AbsoluteJump = instructions.get_full_arg(1).unwrap().into();
assert_eq!(
resolved.get_absolute_jump_target(resolved_jump).unwrap().1,
ExtInstruction::ReturnValue(0.into())
);
assert_eq!(
instructions.get_absolute_jump_target(jump).unwrap().1,
Instruction::ReturnValue(0)
);
assert_eq!(resolved.len(), 259);
assert_eq!(instructions, resolved.to_instructions());
}
#[test]
fn test_relative_jump() {
let mut instructions = Instructions::with_capacity(17);
for _ in instructions.len()..11 {
instructions.append_instruction(Instruction::Nop(0));
}
assert_eq!(instructions.len(), 11);
instructions.append_instructions(
vec![
Instruction::JumpForward(5), Instruction::ExtendedArg(1),
]
.as_slice(),
);
for _ in instructions.len()..17 {
instructions.append_instruction(Instruction::Nop(0));
}
assert_eq!(instructions.len(), 17);
instructions.append_instruction(Instruction::ReturnValue(0));
let resolved = instructions.to_resolved().unwrap();
assert_eq!(resolved.len(), 17);
assert_eq!(
resolved.get_jump_target(11).unwrap().1,
ExtInstruction::ReturnValue(0.into())
);
assert_eq!(
instructions.get_jump_target(11).unwrap().1,
Instruction::ReturnValue(0)
);
assert_eq!(instructions, resolved.to_instructions());
}
#[test]
fn test_extra_extended_arg() {
let ext_instructions = ExtInstructions::new(vec![
ExtInstruction::JumpAbsolute(255.into()),
ExtInstruction::JumpAbsolute(300.into()), ]);
let instructions = ext_instructions.to_instructions();
assert_eq!(
instructions,
Instructions::new(vec![
Instruction::ExtendedArg(1),
Instruction::JumpAbsolute(1),
Instruction::ExtendedArg(1),
Instruction::JumpAbsolute(46)
])
)
}
#[test]
fn test_full_arg() {
let ext_instructions = ExtInstructions::new(vec![
ExtInstruction::Nop(300.into()), ]);
let instructions = ext_instructions.to_instructions();
assert_eq!(
ext_instructions.first().unwrap().get_raw_value(),
instructions.get_full_arg(1).unwrap()
);
}
#[test]
fn test_line_number() {
let code_object = v310::code_objects::Code {
argcount: 0,
posonlyargcount: 0,
kwonlyargcount: 0,
nlocals: 0,
stacksize: 3,
flags: CodeFlags::from_bits_truncate(CodeFlags::NOFREE.bits()),
code: Instructions::new(vec![
Instruction::LoadName(0),
Instruction::LoadConst(0),
Instruction::CallFunction(1),
Instruction::PopTop(0),
Instruction::LoadConst(1),
Instruction::StoreName(1),
Instruction::LoadName(0),
Instruction::LoadConst(2),
Instruction::LoadName(1),
Instruction::FormatValue(2),
Instruction::BuildString(2),
Instruction::CallFunction(1),
Instruction::PopTop(0),
Instruction::LoadConst(3),
Instruction::ReturnValue(0),
]),
consts: vec![
Constant::FrozenConstant(FrozenConstant::String(PyString {
value: "line 1".into(),
kind: ShortAscii,
})),
Constant::FrozenConstant(FrozenConstant::Long(2.into())),
Constant::FrozenConstant(FrozenConstant::String(PyString {
value: "line 4, a=".into(),
kind: ShortAscii,
})),
Constant::FrozenConstant(FrozenConstant::None),
],
names: vec![
PyString {
value: "print".into(),
kind: ShortAsciiInterned,
},
PyString {
value: "a".into(),
kind: ShortAsciiInterned,
},
],
varnames: vec![],
freevars: vec![],
cellvars: vec![],
filename: PyString {
value: "test.py".into(),
kind: ShortAscii,
},
name: PyString {
value: "<module>".into(),
kind: ShortAsciiInterned,
},
firstlineno: 1,
linetable: vec![8, 0, 4, 1, 18, 2],
};
assert_eq!(
code_object.co_lines().unwrap(),
vec![
LinetableEntry {
start: 0,
end: 8,
line_number: Some(1)
},
LinetableEntry {
start: 8,
end: 12,
line_number: Some(2)
},
LinetableEntry {
start: 12,
end: 30,
line_number: Some(4)
},
]
);
assert_eq!(
starts_line_number(&code_object.co_lines().unwrap(), 0).unwrap(),
1
);
assert_eq!(
starts_line_number(&code_object.co_lines().unwrap(), 1),
None
);
assert_eq!(
get_line_number(&code_object.co_lines().unwrap(), 10).unwrap(),
4
)
}
#[test]
fn test_trait_api() {
let ext_instructions = ExtInstructions::new(vec![
ExtInstruction::JumpAbsolute(255.into()),
ExtInstruction::JumpAbsolute(300.into()), ]);
ext_instructions.get_instructions();
let instructions = Instructions::new(vec![
Instruction::ExtendedArg(1),
Instruction::JumpAbsolute(4), ]);
instructions.get_instructions();
}
}