from bytecode import Instr, UNSET, Compare, Label
from boa.interop import VMOp
from boa.code import pyop
from logzero import logger
import opcode
[docs]class PyToken():
instruction = None # type:Instr
expression = None
index = 0
jump_found = False
jump_target = None
jump_from = None
jump_from_addr = None
jump_to_addr = None
is_dynamic_appcall = False
_methodname = None
is_breakpoint = False
def __init__(self, instruction, expression, index, fallback_ln):
self.instruction = instruction
self.expression = expression
self.index = index
self._methodname = None
self.jump_from = None
self.jump_target = None
self.jump_found = False
self.jump_from_addr = None
self.jump_to_addr = None
# self.jump_to_addr_abs = None
# self.jump_from_addr_abs = None
if isinstance(instruction, Label):
self.jump_target = instruction
self.instruction = Instr("NOP", lineno=fallback_ln)
elif isinstance(instruction.arg, Label):
self.jump_from = instruction.arg
@property
def jump_to_addr_abs(self):
if self.jump_to_addr and self.expression.container_method:
return self.jump_to_addr + self.expression.container_method.address
return 0
@property
def jump_from_addr_abs(self):
if self.jump_from_addr and self.expression.container_method:
return self.jump_from_addr + self.expression.container_method.address
return 0
@property
def file(self):
try:
return self.expression.container_method.module.path
except Exception as e:
print("Could not get file %s " % e)
return None
@property
def method_lineno(self):
return self.expression.container_method.start_line_no - 1
@property
def method_name(self):
return self.expression.container_method.name
@property
def lineno(self):
return self.instruction.lineno
@property
def arg_str(self):
# print("INSTRUCTION ARG: %s %s" % (type(self.instruction.arg), self.instruction.arg))
params = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm']
if self.jump_target:
return 'from %s' % (self.jump_from_addr_abs)
elif self.jump_from:
return 'to %s' % (self.jump_to_addr_abs)
elif self._methodname:
return '%s(%s)' % (self._methodname, ','.join(params[0:self.instruction.arg]))
elif isinstance(self.instruction.arg, Compare):
return self.instruction.arg.name
elif isinstance(self.instruction.arg, bytes) or isinstance(self.instruction.arg, bytearray):
return str(self.instruction.arg)
return self.instruction.arg if self.instruction.arg != UNSET else ''
@property
def args(self):
return self.instruction.arg
@property
def pyop(self):
return self.instruction.opcode
@property
def func_name(self):
if not self._methodname:
self._methodname = self.expression.lookup_method_name(self.index)
return self._methodname
@property
def num_params(self):
return self.args
[docs] def to_vm(self, tokenizer, prev_token=None):
"""
:param tokenizer:
:param prev_token:
:return:
"""
op = self.instruction.opcode
# print("CONVERTING OP: %s %s " % (self.instruction.name, self.instruction.arg))
if op == pyop.NOP:
tokenizer.convert1(VMOp.NOP, self)
elif op == pyop.RETURN_VALUE:
tokenizer.method_end_items()
tokenizer.convert1(VMOp.RET, self)
# control flow
elif op == pyop.JUMP_FORWARD:
tokenizer.convert1(VMOp.JMP, self, data=bytearray(2))
elif op == pyop.JUMP_ABSOLUTE:
tokenizer.convert1(VMOp.JMP, self, data=bytearray(2))
elif op == pyop.POP_JUMP_IF_FALSE:
tokenizer.convert1(
VMOp.JMPIFNOT, self, data=bytearray(2))
# JUMP_IF_FALSE_OR_POP is not supported by the VM
# elif op == pyop.JUMP_IF_FALSE_OR_POP:
# tokenizer.convert_pop_jmp_if(self)
# VMOp.JMPIFNOT, self, data=bytearray(2))
elif op == pyop.POP_JUMP_IF_TRUE:
tokenizer.convert_pop_jmp_if(self)
# JUMP_IF_TRUE_OR_POP is not supported by the VM
# elif op == pyop.JUMP_IF_TRUE_OR_POP:
# tokenizer.convert_pop_jmp_if(self)
# loops
elif op == pyop.SETUP_LOOP:
tokenizer.convert1(VMOp.NOP, self)
elif op == pyop.BREAK_LOOP:
tokenizer.convert1(VMOp.JMP, self, data=bytearray(2))
elif op == pyop.POP_BLOCK:
tokenizer.convert1(VMOp.NOP, self)
elif op == pyop.FROMALTSTACK:
tokenizer.convert1(VMOp.FROMALTSTACK, self)
elif op == pyop.DROP:
tokenizer.convert1(VMOp.DROP, self)
elif op == pyop.XSWAP:
tokenizer.convert1(VMOp.XSWAP, self)
elif op == pyop.ROLL:
tokenizer.convert1(VMOp.ROLL, self)
# loading constants ( ie 1, 2 etc)
elif op == pyop.LOAD_CONST:
tokenizer.convert_load_const(self)
# storing / loading local variables
elif op in [pyop.STORE_FAST, pyop.STORE_NAME]:
tokenizer.convert_store_local(self)
elif op == pyop.LOAD_GLOBAL:
if self.instruction.arg in self.expression.container_method.scope:
tokenizer.convert_load_local(self)
else:
self.expression.add_method(self)
elif op in [pyop.LOAD_FAST, pyop.LOAD_NAME]:
tokenizer.convert_load_local(self)
# unary ops
elif op == pyop.UNARY_INVERT:
tokenizer.convert1(VMOp.INVERT, self)
elif op == pyop.UNARY_NEGATIVE:
tokenizer.convert1(VMOp.NEGATE, self)
elif op == pyop.UNARY_NOT:
tokenizer.convert1(VMOp.NOT, self)
# elif op == pyop.UNARY_POSITIVE:
# hmmm
# tokenizer.convert1(VMOp.ABS, self)
# pass
# math
elif op in [pyop.BINARY_ADD, pyop.INPLACE_ADD]:
# we can't tell by looking up the last token what type of item it was
# will need to figure out a different way of concatting strings
# if prev_token and type(prev_token.args) is str:
# tokenizer.convert1(VMOp.CAT, self)
# else:
tokenizer.convert1(VMOp.ADD, self)
elif op in [pyop.BINARY_SUBTRACT, pyop.INPLACE_SUBTRACT]:
tokenizer.convert1(VMOp.SUB, self)
elif op in [pyop.BINARY_MULTIPLY, pyop.INPLACE_MULTIPLY]:
tokenizer.convert1(VMOp.MUL, self)
elif op in [pyop.BINARY_FLOOR_DIVIDE, pyop.BINARY_TRUE_DIVIDE,
pyop.INPLACE_FLOOR_DIVIDE, pyop.INPLACE_TRUE_DIVIDE]:
tokenizer.convert1(VMOp.DIV, self)
elif op in [pyop.BINARY_MODULO, pyop.INPLACE_MODULO]:
tokenizer.convert1(VMOp.MOD, self)
elif op in [pyop.BINARY_OR, pyop.INPLACE_OR]:
tokenizer.convert1(VMOp.OR, self)
elif op in [pyop.BINARY_AND, pyop.INPLACE_AND]:
tokenizer.convert1(VMOp.AND, self)
elif op in [pyop.BINARY_XOR, pyop.INPLACE_XOR]:
tokenizer.convert1(VMOp.XOR, self)
elif op in [pyop.BINARY_LSHIFT, pyop.INPLACE_LSHIFT]:
tokenizer.convert1(VMOp.SHL, self)
elif op in [pyop.BINARY_RSHIFT, pyop.INPLACE_RSHIFT]:
tokenizer.convert1(VMOp.SHR, self)
# compare
elif op == pyop.COMPARE_OP:
# pdb.set_trace()
if self.instruction.arg == Compare.GT:
tokenizer.convert1(VMOp.GT, self)
elif self.instruction.arg == Compare.GE:
tokenizer.convert1(VMOp.GTE, self)
elif self.instruction.arg == Compare.LT:
tokenizer.convert1(VMOp.LT, self)
elif self.instruction.arg == Compare.LE:
tokenizer.convert1(VMOp.LTE, self)
elif self.instruction.arg == Compare.EQ:
tokenizer.convert1(VMOp.EQUAL, self)
elif self.instruction.arg == Compare.IS:
tokenizer.convert1(VMOp.EQUAL, self)
elif self.instruction.arg in [Compare.NE, Compare.IS_NOT]:
tokenizer.convert1(VMOp.NUMNOTEQUAL, self)
elif self.instruction.arg == Compare.IN:
tokenizer.convert1(VMOp.SWAP, self)
tokenizer.convert1(VMOp.HASKEY, self)
# arrays
elif op == pyop.BUILD_LIST:
tokenizer.convert_new_array(self)
elif op == pyop.DUP_TOP:
tokenizer.convert1(VMOp.DUP, self)
elif op == pyop.YIELD_VALUE:
tokenizer.convert1(VMOp.REVERSE, self)
elif op == pyop.STORE_SUBSCR:
tokenizer.convert_store_subscr(self)
elif op == pyop.BINARY_SUBSCR:
tokenizer.convert1(VMOp.PICKITEM, self)
# dict
elif op == pyop.BUILD_CONST_KEY_MAP:
tokenizer.convert1(VMOp.NEWMAP, self)
elif op == pyop.BUILD_MAP:
tokenizer.convert1(VMOp.NEWMAP, self)
elif op == pyop.BUILD_SLICE:
tokenizer.convert_build_slice(self)
elif op in [pyop.CALL_FUNCTION, pyop.CALL_METHOD]:
tokenizer.convert_method_call(self)
# elif op == pyop.LOAD_METHOD:
elif op == pyop.POP_TOP:
pass
# if prev_token:
# is_action = False
# for item in tokenizer.method.module.actions:
# if item.method_name == prev_token.func_name:
# is_action = True
#
# if is_action:
# tokenizer.convert1(VMOp.DROP, self)
elif op == pyop.DUP_TOP_TWO:
tokenizer.convert_dup_top_two(self)
elif op == pyop.ROT_THREE:
tokenizer.convert1(VMOp.ROT, self)
elif op == pyop.ROT_TWO:
tokenizer.convert1(VMOp.SWAP)
elif op == pyop.RAISE_VARARGS:
pass
# elif op == pyop.CALL_METHOD:
# pass
elif op == pyop.EXTENDED_ARG:
tokenizer.convert1(VMOp.NOP, self)
else:
# import pdb
# pdb.set_trace()
logger.warning("Op Not Converted %s %s %s %s %s" % (self.instruction.arg, self.instruction.name, self.method_name, self.lineno, self.method_lineno))