minor improvements/fixes for autocomplete generation + cmake integration

This commit is contained in:
Marius Guggenmos 2018-12-22 21:53:58 +01:00
parent 0a823bbebc
commit 1fd0afe791
6 changed files with 86 additions and 30 deletions

View file

@ -16,6 +16,7 @@ option(INCBUILD "Increment the build number" OFF)
option(KERNEL4 "Pin will run on a Linux's kernel v4" ON)
option(PINTOOL "Build Triton with the Pin tool as tracer" OFF)
option(PYTHON_BINDINGS "Enable Python bindings into the libtriton" ON)
option(PYTHON_BINDINGS_AUTOCOMPLETE "Enable generation of triton_autocomplete module for IDE autocompletion" OFF)
option(STATICLIB "Build a static library" OFF)
option(Z3_INTERFACE "Use Z3 as SMT solver" ON)

View file

@ -17,6 +17,16 @@ add_custom_target(doc
DEPENDS gen_doc_from_spec
)
if(PYTHON_BINDINGS_AUTOCOMPLETE)
add_custom_target(python_autocomplete
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/autocomplete/generate_autocomplete.py "${CMAKE_CURRENT_BINARY_DIR}"
DEPENDS gen_doc_from_spec
)
execute_process (COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()" OUTPUT_VARIABLE PYTHON_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE)
install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/triton_autocomplete DESTINATION ${PYTHON_SITE_PACKAGES})
endif()
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile

View file

@ -0,0 +1,20 @@
from __future__ import print_function
# triton_autocomplete will raise an import error.
# IDEs should still provide autocomplete based on
# the triton_autocomplete module
from triton import *
try:
from triton_autocomplete import *
except ImportError:
pass
ctx = TritonContext()
# Set the arch
ctx.setArchitecture(ARCH.X86)
inst = Instruction()
inst.setAddress(0x40000)
inst.setOpcode("\x89\xd0") # mov eax, edx
ctx.processing(inst)
print(inst)

View file

@ -1,7 +1,14 @@
from typing import Dict, List
try:
from typing import Dict, List
except ImportError:
pass
from collections import OrderedDict
class Function():
generate_code = True
INDENT_STR = ' ' * 4
DEBUG = True
def __init__(self, name, args, ret_type, doc_str=''):
# type: (str, Dict[str, str], str, str) -> None
@ -11,16 +18,28 @@ class Function():
self.ret_type = ret_type
self.doc_str = doc_str
def gen_code(self):
# type: () -> str
code = ''
func_call = '{}return self.org.{name}({args})'.format(self.INDENT_STR * 2, name=self.name,
args=', '.join(self.args.keys()))
if self.DEBUG:
code += "{}print({})\n".format(self.INDENT_STR * 2, repr(func_call.strip()))
code += func_call
return code
def __str__(self):
# type: () -> str
func_str = '''
@staticmethod
def {fname}({arg_names}):
# type: ({arg_types}) -> {ret_type}
"""{doc_str}"""
pass
{code}
'''.format(fname=self.name, ret_type=self.ret_type,
arg_names=', '.join(self.args.keys()),
arg_names=', '.join(['self'] + list(self.args.keys())),
arg_types=', '.join('' if val == 'None' else val for val in self.args.values()),
doc_str=self.doc_str)
doc_str=self.doc_str,
code='{}pass'.format(self.INDENT_STR * 2) if not self.generate_code else self.gen_code())
return func_str

View file

@ -1,4 +1,7 @@
from typing import Optional, List, Dict, Tuple
try:
from typing import Optional, List, Dict, Tuple
except ImportError:
pass
import re
import os
@ -37,7 +40,9 @@ def sub_types(s):
replacements = [
('integer', 'int'),
('string', 'str'),
('void', 'None')
('void', 'None'),
('function', 'Callable'),
('tuple', 'Tuple'),
]
for to_repl, repl in replacements:
s = re.sub(to_repl, repl, s)
@ -53,7 +58,7 @@ def sub_types(s):
def gen_function(sig, desc):
# type: (str, str) -> Optional[Function]
dbg = False and 'getPathConstraints' in sig
dbg = False and 'land' in sig
sig = sub_types(sig)
sig_match = sig_re.search(sig)
if not sig_match:
@ -61,6 +66,11 @@ def gen_function(sig, desc):
print("pattern: {}".format(sig_re.pattern))
return None
# naming a function string... noice
func_name = sig_match.group('name')
if func_name == 'str':
func_name = 'string'
if dbg:
for i, g in enumerate(sig_match.groups()):
print('group {}: {}'.format(i, g))
@ -81,7 +91,7 @@ def gen_function(sig, desc):
arg_name = ''
# or it is a variable arg, which means we generate a generic str
else:
arg_name = '*args'
arg_name = 'args'
else:
arg_name = arg_words[1]
@ -90,7 +100,7 @@ def gen_function(sig, desc):
return None
args[arg_name] = arg_type
return Function(sig_match.group('name'), args, sig_match.group('return'), desc)
return Function(func_name, args, sig_match.group('return'), desc)
def gen_module_for_object(classname, input_str):
# type: (str, str) -> str
@ -114,9 +124,13 @@ def gen_module_for_object(classname, input_str):
# generate
autogen_str = '''
from typing import List, Union, overload
from typing import List, Union, Callable, Tuple
import triton
class {classname}:
def __init__(self, *args, **kargs):
self.org = triton.{classname}(*args, **kargs)
{functions}
'''.format(classname=classname, functions='\n'.join([str(f) for f in funcs]))
@ -134,8 +148,9 @@ def gen_module_for_namespace(classname, input_str):
if not matches:
return ""
for i, match in enumerate(matches):
member = ' {} = {}'.format(match.group('member'), i)
for match in matches:
member = ' {member} = triton.{namespace}.{member}'.format(
member = match.group('member'), namespace=classname)
members.append(member)
if not members:
@ -144,6 +159,7 @@ def gen_module_for_namespace(classname, input_str):
# generate
autogen_str = '''
import triton
class {classname}:
{members}
@ -164,7 +180,7 @@ def gen_imports(objects, names):
def gen_init_file(objects, imports):
# type: (List[Tuple[str, str]], List[str]) -> str
return '\n'.join(imports)
return '\n'.join(imports) + '\nraise ImportError\n'
def main():
@ -173,11 +189,10 @@ def main():
namespace_dir = os.path.join(src_dir, 'libtriton/bindings/python/namespaces')
object_dir = os.path.join(src_dir, 'libtriton/bindings/python/objects')
out_dir = os.path.join(this_dir, 'triton_autocomplete')
out_dir = os.path.join(this_dir if len(os.sys.argv) < 2 else os.sys.argv[1], 'triton_autocomplete')
if not os.path.exists(out_dir):
os.mkdir(out_dir)
# get names/paths for objects
obj_paths = glob(object_dir + '/*.cpp')
objs = [] # type: List[Tuple[str, str]]
@ -195,6 +210,10 @@ def main():
name_paths = glob(namespace_dir + '/*.cpp')
names = [] # type: List[Tuple[str, str]]
for name_path in name_paths:
if 'initSyscallNamespace' in name_path:
print("info: skipping {}".format(name_path))
continue
# find name of namespace from doxygen page command
with open(name_path, 'r') as f:
data = f.read()
@ -219,8 +238,8 @@ def main():
# write output
with open(os.path.join(out_dir, '{}_{}.py'.format(OBJECT_PREFIX, obj_name)), 'w') as f:
f.write('\n'.join((imp for imp in imports if obj_name not in imp)) + '\n')
f.write(mod_str)
f.write('\n' + '\n'.join((imp for imp in imports if obj_name not in imp)) + '\n')
# generate modules for namespaces
for name_path, name_name in names:

View file

@ -1,13 +0,0 @@
from __future__ import print_function
# import triton
from triton_autocomplete import *
ctx = TritonContext()
ctx.setArchitecture(ARCH.X86_64)
ctx.setAstRepresentationMode(AST_REPRESENTATION.PYTHON)
# ctx = triton_import.TritonContext()
# ctx.setConcreteMemoryValue()
print('Done')