Skip to content
This repository has been archived by the owner on Nov 15, 2021. It is now read-only.

Commit

Permalink
adding support for method calls
Browse files Browse the repository at this point in the history
  • Loading branch information
localhuman committed Sep 21, 2017
1 parent 370055b commit e75ce14
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 14 deletions.
6 changes: 4 additions & 2 deletions boa/boa.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ def write(self):
stream = StreamManager.GetStream()
writer = BinaryWriter(stream)

method = self.default.main
writer.WriteBytes( method.write())
module = self.default
result = module.write()
print("Result %s " % result)
writer.WriteBytes( result)

out = stream.getbuffer()
return bytes(out)
Expand Down
50 changes: 46 additions & 4 deletions boa/code/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,48 @@ def is_return(self):
return True
return False

@property
def has_unprocessed_method_calls(self):
for token in self.oplist:
if token.py_op == pyop.CALL_FUNCTION and token.func_processed == False:
return True
return False


def preprocess_method_calls(self):

while self.has_unprocessed_method_calls:
start_index_change = None
end_index_change = None
changed_items = None

for index, token in enumerate(self.oplist):
if token.py_op == pyop.CALL_FUNCTION and token.func_processed == False:

token.func_processed = True
param_count = token.args

params = self.oplist[index-param_count:index]

call_method_op = self.oplist[index-param_count-1]
call_method_type = call_method_op.py_op
call_method_name = call_method_op.args

token.func_params = params
token.func_name = call_method_name
token.func_type = call_method_type

changed_items = [token]

start_index_change = index - param_count - 1
end_index_change = index

if start_index_change is not None and end_index_change is not None:
tstart = self.oplist[0:start_index_change]
tend = self.oplist[end_index_change+1:]
self.oplist = tstart + changed_items + tend



@property
def has_unprocessed_array_sub(self):
Expand Down Expand Up @@ -66,10 +108,10 @@ def preprocess_array_subs(self):
settoken = PyToken(Opcode(pyop.SETITEM), token.line_no, args=index_to_sub_at, array_item=item_to_sub)
changed_items.append(settoken)

if start_index_change is not None and end_index_change is not None:
tstart = self.oplist[0:start_index_change]
tend = self.oplist[end_index_change + 2:]
self.oplist = tstart + changed_items + tend
if start_index_change is not None and end_index_change is not None:
tstart = self.oplist[0:start_index_change]
tend = self.oplist[end_index_change + 2:]
self.oplist = tstart + changed_items + tend


@property
Expand Down
15 changes: 15 additions & 0 deletions boa/code/line.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,24 @@ def is_definition(self):
return len(self.items) == 3 and self.items[1][0] == pyop.LOAD_CONST and self.items[2][0] == pyop.STORE_NAME
# return False


@property
def is_method(self):
for i, (op, arg) in enumerate(self.items):
if op == pyop.MAKE_FUNCTION:
return True
return False

@property
def is_class(self):
for i, (op, arg) in enumerate(self.items):
if op == pyop.LOAD_BUILD_CLASS:
return True
return False

@property
def code_object(self):
for i, (op, arg) in enumerate(self.items):
if type(arg) is Code:
return arg
return None
16 changes: 13 additions & 3 deletions boa/code/method.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,29 @@ class Method():

blocks = None

method_address = None

@property
def name(self):
return self.bp.name

@property
def args(self):
# alist = list(self.bp.args)
# if 'self' in alist:
# alist.remove('self')
# return alist
return self.bp.args

@property
def code(self):
return self.bp.code

@property
def vm_tokens(self):
return self.tokenizer.vm_tokens


@property
def firstlineno(self):
return self.bp.firstlineno
Expand Down Expand Up @@ -66,13 +76,11 @@ def module(self):

def __init__(self, code_object, parent):


self.bp = code_object

self.parent = parent

if not self in self.module.methods:
self.module.methods.append(self)
self.print()

self.read_initial_tokens()

Expand Down Expand Up @@ -166,6 +174,8 @@ def process_block_groups(self):
if block.has_unprocessed_array_sub:
block.preprocess_array_subs()

if block.has_unprocessed_method_calls:
block.preprocess_method_calls()

alltokens = []

Expand Down
93 changes: 93 additions & 0 deletions boa/code/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@


from boa.code.line import Line
from boa.code.method import Method
from boa.code.items import Definition, Klass, Import

import sys

class Module():

Expand All @@ -22,6 +24,18 @@ class Module():

methods = None # a list to keep all methods in the module


all_vm_tokens = None # dict for converting method tokens into linked method tokens for writing


@property
def main_path(self):
return sys.modules['__main__']

@property
def module_path(self):
return sys.modules['__main__'].__file__

@property
def main(self):
for m in self.methods:
Expand All @@ -31,6 +45,26 @@ def main(self):
return self.methods[0]
return None

@property
def orderered_methods(self):
om = []

if self.main:
om = [self.main]

for m in self.methods:
if m == self.main:
continue
om.append(m)

return om

def method_by_name(self, method_name):
for m in self.methods:
if m.name == method_name:
return m
return None

def __init__(self, path):

self.path = path
Expand Down Expand Up @@ -63,6 +97,8 @@ def build(self):
self.module_variables.append(Definition(lineset.items))
elif lineset.is_class:
self.classes.append(Klass(lineset.items, self))
elif lineset.is_method:
self.methods.append(Method(lineset.code_object, self))
else:
print('not sure what to do with line %s ' % lineset)

Expand Down Expand Up @@ -94,3 +130,60 @@ def validate_imports(self):
def build_classes(self):
#print('build classes %s ' % self.classes)
pass



def write(self):


self.link_methods()

return self.write_methods()


def write_methods(self):

b_array = bytearray()
for key, vm_token in self.all_vm_tokens.items():

b_array.append(vm_token.out_op)

if vm_token.data is not None:
b_array = b_array + vm_token.data

return b_array


def link_methods(self):

self.all_vm_tokens = {}

address = 0

for method in self.orderered_methods:

method.method_address = address

for key, vmtoken in method.vm_tokens.items():

self.all_vm_tokens[address] = vmtoken

address += 1

if vmtoken.data is not None:

address += len(vmtoken.data)

vmtoken.addr = vmtoken.addr + method.method_address


for key, vmtoken in self.all_vm_tokens.items():

if vmtoken.src_method is not None:

target_method = self.method_by_name( vmtoken.target_method )

jump_len = target_method.method_address - vmtoken.addr

vmtoken.data = jump_len.to_bytes(2, 'little', signed=True)

48 changes: 43 additions & 5 deletions boa/code/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@






class PyToken():

py_op = None
Expand All @@ -28,6 +31,17 @@ class PyToken():

array_item = None


#method calling things

func_processed = False

func_params = None

func_name = None
func_type = None


@property
def op_name(self):
if type(self.py_op) is int:
Expand Down Expand Up @@ -148,19 +162,19 @@ def to_vm(self, tokenizer, prev_token=None):

#unary ops

elif op == pyop.UNARY_INVERT:
token = tokenizer.convert1(OpCode.INVERT, self)
# elif op == pyop.UNARY_INVERT:
# token = tokenizer.convert1(OpCode.INVERT, self)

elif op == pyop.UNARY_NEGATIVE:
token = tokenizer.convert1(OpCode.NEGATE, self)

elif op == pyop.UNARY_NOT:
token = tokenizer.convert1(OpCode.NOT, self)

elif op == pyop.UNARY_POSITIVE:
# elif op == pyop.UNARY_POSITIVE:
#hmmm
token = tokenizer.convert1(OpCode.ABS, self)
pass
# token = tokenizer.convert1(OpCode.ABS, self)
# pass

#math
elif op == pyop.BINARY_ADD:
Expand Down Expand Up @@ -190,6 +204,9 @@ def to_vm(self, tokenizer, prev_token=None):
elif op == pyop.BINARY_AND:
token = tokenizer.convert1(OpCode.BOOLAND, self)

elif op == pyop.BINARY_XOR:
token = tokenizer.convert1(OpCode.XOR, self)



#compare
Expand Down Expand Up @@ -221,6 +238,11 @@ def to_vm(self, tokenizer, prev_token=None):
elif op == pyop.BINARY_SUBSCR:
token = tokenizer.convert1(OpCode.PICKITEM,self)


elif op == pyop.CALL_FUNCTION:

token = tokenizer.convert_method_call(self)

return token


Expand All @@ -234,6 +256,9 @@ class VMToken():

vm_op = None

src_method = None

target_method = None

@property
def out_op(self):
Expand All @@ -254,6 +279,9 @@ def __init__(self, vm_op=None, pytoken=None,addr=None, data=None):

self.data = data

self.src_method = None
self.target_method = None


class VMTokenizer():

Expand Down Expand Up @@ -544,3 +572,13 @@ def convert_load_parameter(self, arg, position):
self.insert1(OpCode.ROLL)
self.insert1(OpCode.SETITEM)


def convert_method_call(self, pytoken):

self.insert1(OpCode.NOP)
vmtoken = self.convert1(OpCode.CALL,py_token=pytoken,data=bytearray(b'\x05\x00'))

vmtoken.src_method = self.method
vmtoken.target_method = pytoken.func_name

return vmtoken
Loading

0 comments on commit e75ce14

Please sign in to comment.