def test_conditional_a_la_pypy(): from xotl.ql import qst # >>> dis.dis(compile('x and a or y', '', 'eval')) # 1 0 LOAD_NAME 0 (x) # 3 JUMP_IF_FALSE_OR_POP 9 # 6 LOAD_NAME 1 (a) # >> 9 JUMP_IF_TRUE_OR_POP 15 # 12 LOAD_NAME 2 (y) # >> 15 RETURN_VALUE import types from xotl.ql.revenge import Uncompyled from xotl.ql.revenge.scanners import InstructionSetBuilder, label builder = InstructionSetBuilder() with builder() as Instruction: Instruction(opname='LOAD_NAME', arg=0, argval='x', starts_line=1) Instruction(opname='JUMP_IF_FALSE_OR_POP', arg=label('else')) Instruction(opname='LOAD_NAME', arg=1, argval='a') Instruction(label='else', opname='JUMP_IF_TRUE_OR_POP', arg=label('out')) Instruction(opname='LOAD_NAME', arg=2, argval='y') Instruction(label='out', opname='RETURN_VALUE') code = builder.code code = types.CodeType(0, 0, 0, 3, 0, code, (), ('x', 'a', 'y'), (), '', '<module>', 1, b'') u = Uncompyled(code) assert u.safe_ast expected = qst.parse('x and a or y') assert u.qst == expected
def test_real_pypy_normalization(): from xotl.ql.revenge.scanners import InstructionSetBuilder, label from xotl.ql.revenge.scanners import getscanner, without_nops from xotl.ql.revenge.scanners import normalize_pypy_conditional # The PyPy byte code for `a if x else y`: builder = InstructionSetBuilder() with builder() as Instruction: Instruction(opname='LOAD_NAME', arg=0, argval='x', argrepr='x', starts_line=1, is_jump_target=False), Instruction(opname='POP_JUMP_IF_FALSE', arg=label('else y'), starts_line=None) Instruction(opname='LOAD_NAME', arg=1, argval='a', argrepr='a', starts_line=None) Instruction(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', starts_line=None) Instruction(label='else y', opname='LOAD_NAME', arg=2, argval='y', argrepr='y', starts_line=None), Instruction(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', starts_line=None) expected_program = list(builder) scanner = getscanner() tokens, customize = scanner.disassemble( compile('a if x else y', '', 'eval'), normalize=(normalize_pypy_conditional, without_nops) ) instructions = [token.instruction for token in tokens if token.instruction] assert instructions == expected_program
def test_scanner_normalization_single_return(): from xotl.ql.revenge.scanners import InstructionSetBuilder, label from xotl.ql.revenge.scanners import keep_single_return builder = InstructionSetBuilder() with builder() as Instruction: Instruction(label='start', opname='LOAD_NAME', arg=0, argval='x', argrepr='x', starts_line=1), Instruction(opname='POP_JUMP_IF_FALSE', arg=label('back'), starts_line=None), Instruction(opname='LOAD_NAME', arg=1, argval='a', argrepr='a', starts_line=None), Instruction(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', starts_line=None), Instruction(label='back', opname='LOAD_NAME', arg=2, argval='y', argrepr='y', starts_line=None), # Let's make some jumps here but to see other jumps are not affected. # Since the return value above takes a single byte but the # JUMP_FORWARD takes 3 bytes, all offsets below that point are to be # shifted whereas those above will remain. Instruction(opname='JUMP_ABSOLUTE', arg=label('back'), starts_line=None) Instruction(opname='JUMP_FORWARD', arg=label('next'), starts_line=None), Instruction(label='next', opname='FOR_ITER', arg=label('start'), starts_line=None) Instruction(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', starts_line=None) original = list(builder) builder = InstructionSetBuilder() with builder() as Instruction: Instruction(label='start', opname='LOAD_NAME', arg=0, argval='x', argrepr='x', starts_line=1), Instruction(opname='POP_JUMP_IF_FALSE', arg=label('back'), starts_line=None), Instruction(opname='LOAD_NAME', arg=1, argval='a', argrepr='a', starts_line=None), Instruction(opname='JUMP_FORWARD', arg=label('retval'), starts_line=None), Instruction(label='back', opname='LOAD_NAME', arg=2, argval='y', argrepr='y', starts_line=None), # Let's make loop here to the instruction just above but to see other # jumps are not affected. Instruction(opname='JUMP_ABSOLUTE', arg=label('back'), starts_line=None) Instruction(opname='JUMP_FORWARD', arg=label('next'), starts_line=None), Instruction(label='next', opname='FOR_ITER', arg=label('start'), starts_line=None) Instruction(label='retval', opname='RETURN_VALUE', arg=None, argval=None, argrepr='', starts_line=None) expected = list(builder) assert list(keep_single_return(original)) == expected