diff --git a/AUTHORS.txt b/AUTHORS.txt index 6f8d4ee..e7bc0b6 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -8,8 +8,11 @@ The IDAPython Team: * Hex-Rays - http://www.hex-rays.com/ - Hex-Rays joined the IDAPython project in September 2009 and started contributing. - It is primarily maintained by Elias Bachaalany. + It is primarily maintained by Arnaud Diederen. +* Elias Bachaalany - http://0xeb.wordpress.com/ + + Current project owner and maintainer * Ero Carrera - http://dkbza.org/ diff --git a/Scripts/callstack_test.py b/Scripts/callstack_test.py new file mode 100644 index 0000000..7f00fba --- /dev/null +++ b/Scripts/callstack_test.py @@ -0,0 +1,91 @@ +import sys +import os + +def __sys(cmd, fmt=None, echo=True): + """Executes a string of OS commands and returns the a list of tuples (return code,command executed)""" + if not fmt: + fmt = {} + r = [] + for cmd in [x for x in (cmd % fmt).split("\n") if len(x)]: + if echo: + print ">>>", cmd + r.append((os.system(cmd), cmd)) + return r + +body = r"""/// Autogenerated file +#include +#include +#include +#include + +void want_break(int n) +{ + printf("do you want to DebugBreak in func%d()?", n); + char ch = _toupper(_getch()); + printf("\n"); + if (ch == 'Y') + DebugBreak(); + else if (ch == 'X') + ExitProcess(n); +} +%FUNCS% +int main(int /*argc*/, char * /*argv[]*/) +{ + func1(); + return 0; +} +""" + +funcs_body = [] + +func_body = r""" +void func%(n)d() +{ + printf("%(ident)senter %(n)d\n");%(pause)s + func%(n1)d(); + printf("%(ident)sleave %(n)d\n"); +} +""" + +if len(sys.argv) < 2: + print "usage: gen nb_calls pause_frequency" + sys.exit(0) + +n = int(sys.argv[1]) +if n < 1: + print "at least one call should be passed!" + sys.exit(1) + +m = int(sys.argv[2]) + +func_params = {'n': 0, 'n1': 0, 'ident': '', 'pause' : ''} + +for i in xrange(1, n + 1): + func_params['n'] = i + func_params['n1'] = i+1 + func_params['ident'] = " " * i + func_params['pause'] = ("\n want_break(%d);" % i) if (i % m) == 0 else '' + + funcs_body.append(func_body % func_params) +funcs_body.append(r""" +void func%(n)d() +{ + printf("that's it #%(n)d!\n"); +} +""" % {'n':i+1}) +funcs_body.reverse() + +# write the file +body = body.replace('%FUNCS%', ''.join(funcs_body)) +f = file('src.cpp', 'w') +f.write(body) +f.close() + + +__sys(""" +if exist src.exe del src.exe +bcc32 src +if exist src.exe move src.exe src_bcc.exe +if exist src.obj del src.obj +cl32 src.cpp /Zi /Od +""") \ No newline at end of file diff --git a/build.py b/build.py index 06e62b0..4086611 100644 --- a/build.py +++ b/build.py @@ -24,7 +24,7 @@ from distutils import sysconfig VERBOSE = True IDA_MAJOR_VERSION = 6 -IDA_MINOR_VERSION = 4 +IDA_MINOR_VERSION = 5 if 'IDA' in os.environ: IDA_SDK = os.environ['IDA'] @@ -35,8 +35,8 @@ else: # IDAPython version VERSION_MAJOR = 1 -VERSION_MINOR = 5 -VERSION_PATCH = 6 +VERSION_MINOR = 6 +VERSION_PATCH = 0 # Determine Python version PYTHON_MAJOR_VERSION = int(platform.python_version()[0]) @@ -50,7 +50,7 @@ S_WITH_HEXRAYS = 'with-hexrays' S_NO_OPT = 'no-opt' # Swig command-line parameters -SWIG_OPTIONS = '-modern -python -c++ -w451 -shadow -D__GNUC__ -DNO_OBSOLETE_FUNCS' +SWIG_OPTIONS = '-modern -python -threads -c++ -w451 -shadow -D__GNUC__' # Common macros for all compilations COMMON_MACROS = [ @@ -169,8 +169,8 @@ def parse_options(args): with_hexrays = '--' + S_WITH_HEXRAYS in sys.argv return { - S_EA64: ea64, - S_WITH_HEXRAYS: with_hexrays, + S_EA64: ea64, + S_WITH_HEXRAYS: with_hexrays, S_NO_OPT: no_opt } @@ -335,9 +335,9 @@ def build_distribution(manifest, distrootdir, ea64, nukeold): # ----------------------------------------------------------------------- def build_plugin( - platform, - idasdkdir, - plugin_name, + platform, + idasdkdir, + plugin_name, options): # Get the arguments @@ -401,7 +401,7 @@ def build_plugin( platform_macros.append("PLUGINFIX") # Turn off obsolete functions - platform_macros.append("NO_OBSOLETE_FUNCS") + #platform_macros.append("NO_OBSOLETE_FUNCS") # Build the wrapper from the interface files ea64flag = ea64 and "-D__EA64__" or "" @@ -467,17 +467,17 @@ def build_binary_package(options, nukeold): platform_string) # Build the plugin build_plugin(platform_string, IDA_SDK, plugin_name, options) - + # Build the binary distribution binmanifest = [] if nukeold: binmanifest.extend(BINDIST_MANIFEST) - + if not ea64 or nukeold: binmanifest.extend([(x, "python") for x in "python/init.py", "python/idc.py", "python/idautils.py", "idaapi.py"]) - + binmanifest.append((plugin_name, "plugins")) - + build_distribution(binmanifest, BINDISTDIR, ea64, nukeold) diff --git a/examples/ex_gdl_qflow_chart.py b/examples/ex_gdl_qflow_chart.py index 2421e61..dae5bd4 100644 --- a/examples/ex_gdl_qflow_chart.py +++ b/examples/ex_gdl_qflow_chart.py @@ -15,11 +15,11 @@ def raw_main(p=True): for ns in xrange(0, q.nsucc(n)): if p: - print " %d->%d" % (n, q.succ(n, ns)) + print "SUCC: %d->%d" % (n, q.succ(n, ns)) for ns in xrange(0, q.npred(n)): if p: - print " %d->%d" % (n, q.pred(n, ns)) + print "PRED: %d->%d" % (n, q.pred(n, ns)) # ----------------------------------------------------------------------- # Using the class diff --git a/examples/ex_idagraph.py b/examples/ex_idagraph.py new file mode 100644 index 0000000..8dc03c5 --- /dev/null +++ b/examples/ex_idagraph.py @@ -0,0 +1,114 @@ +# ----------------------------------------------------------------------- +# This is an example illustrating how to manipulate an existing IDA-provided +# view (and thus its graph), in Python. +# (c) Hex-Rays +# +from idaapi import IDAViewWrapper +from time import sleep +import threading + +class Worker(threading.Thread): + def __init__(self, w): + threading.Thread.__init__(self) + self.w = w + + def req_SetCurrentRendererType(self, switch_to): + w = self.w + def f(): + print "Switching.." + w.SetCurrentRendererType(switch_to) + idaapi.execute_sync(f, idaapi.MFF_FAST) + + def req_SetNodeInfo(self, node, info, flags): + w = self.w + def f(): + print "Setting node info.." + w.SetNodeInfo(node, info, flags) + idaapi.execute_sync(f, idaapi.MFF_FAST) + + def req_DelNodesInfos(self, *nodes): + w = self.w + def f(): + print "Deleting nodes infos.." + w.DelNodesInfos(*nodes) + idaapi.execute_sync(f, idaapi.MFF_FAST) + + def run(self): + # Note, in order to leave the UI available + # to the user, we'll perform UI operations + # in this thread. + # + # But. + # + # Qt expects that all UI operations be performed from + # the main thread. Therefore, we'll have to use + # 'idaapi.execute_sync' to send requests to the main thread. + + # Switch back & forth to & from graph view + for i in xrange(3): + self.req_SetCurrentRendererType(idaapi.TCCRT_FLAT) + sleep(1) + self.req_SetCurrentRendererType(idaapi.TCCRT_GRAPH) + sleep(1) + + # Go to graph view, and set the first node's color + self.req_SetCurrentRendererType(idaapi.TCCRT_GRAPH) + ni = idaapi.node_info_t() + ni.bg_color = 0x00ff00ff + ni.frame_color = 0x0000ff00 + self.req_SetNodeInfo(0, ni, idaapi.NIF_BG_COLOR|idaapi.NIF_FRAME_COLOR) + sleep(3) + + # This was fun. But let's revert it. + self.req_DelNodesInfos(0) + sleep(3) + + print "Done." + +class MyIDAViewWrapper(IDAViewWrapper): + # A wrapper around the standard IDA view wrapper. + # We'll react to some events and print the parameters + # that were sent to us, that's all. + def __init__(self, viewName): + IDAViewWrapper.__init__(self, viewName) + + # Helper function, to be called by "On*" event handlers. + # This will print all the arguments that were passed! + def printPrevFrame(self): + import inspect + stack = inspect.stack() + frame, _, _, _, _, _ = stack[1] + args, _, _, values = inspect.getargvalues(frame) + print "EVENT: %s: args=%s" % ( + inspect.getframeinfo(frame)[2], + [(i, values[i]) for i in args[1:]]) + + def OnViewKeydown(self, key, state): + self.printPrevFrame() + + def OnViewClick(self, x, y, state): + self.printPrevFrame() + + def OnViewDblclick(self, x, y, state): + self.printPrevFrame() + + def OnViewSwitched(self, rt): + self.printPrevFrame() + + def OnViewMouseOver(self, x, y, state, over_type, over_data): + self.printPrevFrame() + + + +viewName = "IDA View-A" +w = MyIDAViewWrapper(viewName) +if w.Bind(): + print "Succesfully bound to %s" % viewName + + # We'll launch the sequence of operations in another thread, + # so that sleep() calls don't freeze the UI + worker = Worker(w) + worker.start() + +else: + print "Couldn't bind to view %s. Is it available?" % viewName diff --git a/examples/vds1.py b/examples/vds1.py index c259c28..a1fb772 100644 --- a/examples/vds1.py +++ b/examples/vds1.py @@ -1,27 +1,27 @@ import idaapi def main(): - if not idaapi.init_hexrays_plugin(): - return False + if not idaapi.init_hexrays_plugin(): + return False - print "Hex-rays version %s has been detected" % idaapi.get_hexrays_version() + print "Hex-rays version %s has been detected" % idaapi.get_hexrays_version() + + f = idaapi.get_func(idaapi.get_screen_ea()); + if f is None: + print "Please position the cursor within a function" + return True + + cfunc = idaapi.decompile(f); + if cfunc is None: + print "Failed to decompile!" + return True + + sv = cfunc.get_pseudocode(); + for i in xrange(0, sv.size()): + line = idaapi.tag_remove(str(sv[i])); + print line - f = idaapi.get_func(idaapi.get_screen_ea()); - if f is None: - print "Please position the cursor within a function" return True - - cfunc = idaapi.decompile(f); - if cfunc is None: - print "Failed to decompile!" - return True - - sv = cfunc.get_pseudocode(); - for i in xrange(0, sv.size()): - line = idaapi.tag_remove(str(sv[i])); - print line - - return True if main(): - idaapi.term_hexrays_plugin(); + idaapi.term_hexrays_plugin(); diff --git a/examples/vds3.py b/examples/vds3.py index b6b920a..2bf89f7 100644 --- a/examples/vds3.py +++ b/examples/vds3.py @@ -5,9 +5,9 @@ Author: EiNSTeiN_ This is a rewrite in Python of the vds3 example that comes with hexrays sdk. -The main difference with the original C code is that when we create the inverted -condition object, the newly created cexpr_t instance is given to the hexrays and -must not be freed by swig. To achieve this, we have to change the 'thisown' flag +The main difference with the original C code is that when we create the inverted +condition object, the newly created cexpr_t instance is given to the hexrays and +must not be freed by swig. To achieve this, we have to change the 'thisown' flag when appropriate. See http://www.swig.org/Doc1.3/Python.html#Python_nn35 """ @@ -21,23 +21,23 @@ import traceback NETNODE_NAME = '$ hexrays-inverted-if' class hexrays_callback_info(object): - + def __init__(self): self.vu = None - + self.node = idaapi.netnode() if not self.node.create(NETNODE_NAME): # node exists self.load() else: self.stored = [] - + return - + def load(self): - + self.stored = [] - + try: data = self.node.getblob(0, 'I') if data: @@ -47,39 +47,39 @@ class hexrays_callback_info(object): print 'Failed to load invert-if locations' traceback.print_exc() return - + return - + def save(self): - + try: self.node.setblob(repr(self.stored), 0, 'I') except: print 'Failed to save invert-if locations' traceback.print_exc() return - + return - + def invert_if(self, cfunc, insn): - + if insn.opname != 'if': return False - + cif = insn.details - + if not cif.ithen or not cif.ielse: return False - + idaapi.qswap(cif.ithen, cif.ielse) cond = idaapi.cexpr_t(cif.expr) notcond = idaapi.lnot(cond) cond.thisown = 0 # the new wrapper 'notcond' now holds the reference to the cexpr_t - + cif.expr.swap(notcond) - + return True - + def add_location(self, ea): if ea in self.stored: self.stored.remove(ea) @@ -87,15 +87,15 @@ class hexrays_callback_info(object): self.stored.append(ea) self.save() return - + def find_if_statement(self, vu): - + vu.get_current_item(idaapi.USE_KEYBOARD) item = vu.item - + if item.is_citem() and item.it.op == idaapi.cit_if and item.it.to_specific_type.cif.ielse is not None: return item.it.to_specific_type - + if vu.tail.citype == idaapi.VDI_TAIL and vu.tail.loc.itp == idaapi.ITP_ELSE: # for tail marks, we know only the corresponding ea, # not the pointer to if-statement @@ -103,48 +103,48 @@ class hexrays_callback_info(object): class if_finder_t(idaapi.ctree_visitor_t): def __init__(self, ea): idaapi.ctree_visitor_t.__init__(self, idaapi.CV_FAST | idaapi.CV_INSNS) - + self.ea = ea self.found = None return - + def visit_insn(self, i): if i.op == idaapi.cit_if and i.ea == self.ea: self.found = i return 1 # stop enumeration return 0 - + iff = if_finder_t(vu.tail.loc.ea) if iff.apply_to(vu.cfunc.body, None): return iff.found - + return - + def invert_if_event(self, vu): - + cfunc = vu.cfunc.__deref__() - + i = self.find_if_statement(vu) if not i: return False - + if self.invert_if(cfunc, i): vu.refresh_ctext() - + self.add_location(i.ea) - + return True - + def restore(self, cfunc): - + class visitor(idaapi.ctree_visitor_t): - + def __init__(self, inverter, cfunc): idaapi.ctree_visitor_t.__init__(self, idaapi.CV_FAST | idaapi.CV_INSNS) self.inverter = inverter self.cfunc = cfunc return - + def visit_insn(self, i): try: if i.op == idaapi.cit_if and i.ea in self.inverter.stored: @@ -152,40 +152,40 @@ class hexrays_callback_info(object): except: traceback.print_exc() return 0 # continue enumeration - + visitor(self, cfunc).apply_to(cfunc.body, None) - + return - + def menu_callback(self): try: self.invert_if_event(self.vu) except: traceback.print_exc() return 0 - + def event_callback(self, event, *args): - + try: if event == idaapi.hxe_keyboard: vu, keycode, shift = args - + if idaapi.lookup_key_code(keycode, shift, True) == idaapi.get_key_code("I") and shift == 0: if self.invert_if_event(vu): return 1 - + elif event == idaapi.hxe_right_click: self.vu, = args idaapi.add_custom_viewer_popup_item(self.vu.ct, "Invert then/else", "I", self.menu_callback) - + elif event == idaapi.hxe_maturity: cfunc, maturity = args - + if maturity == idaapi.CMAT_FINAL: self.restore(cfunc) except: traceback.print_exc() - + return 0 if idaapi.init_hexrays_plugin(): @@ -193,3 +193,4 @@ if idaapi.init_hexrays_plugin(): idaapi.install_hexrays_callback(i.event_callback) else: print 'invert-if: hexrays is not available.' + diff --git a/python.cfg b/python.cfg index f4614c7..1ab7d58 100644 --- a/python.cfg +++ b/python.cfg @@ -3,7 +3,7 @@ ALERT_AUTO_SCRIPTS = 1 // Remove current directory from import search path -REMOVE_CWD_SYS_PATH = 0 +REMOVE_CWD_SYS_PATH = 1 // Script timeout (in seconds) // (A value of 0 disables the timeout) diff --git a/python.cpp b/python.cpp index eae5160..6e56fd8 100644 --- a/python.cpp +++ b/python.cpp @@ -32,8 +32,8 @@ #include #ifdef WITH_HEXRAYS - #include - hexdsp_t *hexdsp = NULL; +#include +hexdsp_t *hexdsp = NULL; #endif #include "pywraps.hpp" @@ -56,12 +56,6 @@ static const char S_MAIN[] = "__main__"; static const char S_IDC_RUNPYTHON_STATEMENT[] = "RunPythonStatement"; static const char S_IDAPYTHON_DATA_NODE[] = "IDAPython_Data"; -#ifdef PLUGINFIX - #define PLUGIN_FLAGS PLUGIN_FIX -#else - #define PLUGIN_FLAGS 0 -#endif - //------------------------------------------------------------------------- // Types @@ -178,6 +172,7 @@ static void begin_execution() if ( !g_ui_ready || script_timeout == 0 ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); end_execution(); reset_execution_time(); PyEval_SetTrace(break_check, NULL); @@ -198,6 +193,7 @@ static void hide_script_waitbox() static void end_execution() { hide_script_waitbox(); + PYW_GIL_CHECK_LOCKED_SCOPE(); #ifdef ENABLE_PYTHON_PROFILING PyEval_SetTrace(tracefunc, NULL); #else @@ -264,8 +260,9 @@ static void PythonEvalOrExec( const char *filename = "") { // Compile as an expression + PYW_GIL_CHECK_LOCKED_SCOPE(); PyCompilerFlags cf = {0}; - PyObject *py_code = Py_CompileStringFlags(str, filename, Py_eval_input, &cf); + newref_t py_code(Py_CompileStringFlags(str, filename, Py_eval_input, &cf)); if ( py_code == NULL || PyErr_Occurred() ) { // Not an expression? @@ -273,28 +270,27 @@ static void PythonEvalOrExec( // Run as a string PyRun_SimpleString(str); - return; } - - PyObject *py_globals = GetMainGlobals(); - PYW_GIL_ENSURE; - PyObject *py_result = PyEval_EvalCode( - (PyCodeObject *) py_code, - py_globals, - py_globals); - PYW_GIL_RELEASE; - Py_DECREF(py_code); - - if ( py_result == NULL || PyErr_Occurred() ) + else { - PyErr_Print(); - return; - } - qstring result_str; - if ( py_result != Py_None && PyW_ObjectToString(py_result, &result_str) ) - msg("%s\n", result_str.c_str()); + PyObject *py_globals = GetMainGlobals(); + newref_t py_result( + PyEval_EvalCode( + (PyCodeObject *) py_code.o, + py_globals, + py_globals)); - Py_DECREF(py_result); + if ( py_result == NULL || PyErr_Occurred() ) + { + PyErr_Print(); + } + else + { + qstring result_str; + if ( py_result.o != Py_None && PyW_ObjectToString(py_result.o, &result_str) ) + msg("%s\n", result_str.c_str()); + } + } } //------------------------------------------------------------------------ @@ -304,6 +300,7 @@ static bool idaapi IDAPython_extlang_run_statements( char *errbuf, size_t errbufsize) { + PYW_GIL_GET; PyObject *globals = GetMainGlobals(); bool ok; if ( globals == NULL ) @@ -315,18 +312,13 @@ static bool idaapi IDAPython_extlang_run_statements( errbuf[0] = '\0'; PyErr_Clear(); begin_execution(); - PYW_GIL_ENSURE; - PyObject *result = PyRun_String( - str, - Py_file_input, - globals, - globals); - PYW_GIL_RELEASE; - Py_XDECREF(result); + newref_t result(PyRun_String( + str, + Py_file_input, + globals, + globals)); end_execution(); - ok = result != NULL && !PyErr_Occurred(); - if ( !ok ) handle_python_error(errbuf, errbufsize); } @@ -433,6 +425,7 @@ static int PyRunFile(const char *FileName) } #endif + PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *file_obj = PyFile_FromString((char*)FileName, "r"); //lint !e1776 PyObject *globals = GetMainGlobals(); if ( globals == NULL || file_obj == NULL ) @@ -442,19 +435,16 @@ static int PyRunFile(const char *FileName) } PyErr_Clear(); - PYW_GIL_ENSURE; PyObject *result = PyRun_File( PyFile_AsFile(file_obj), FileName, Py_file_input, globals, globals); - PYW_GIL_RELEASE; - Py_XDECREF(file_obj); + int rc = result != NULL && !PyErr_Occurred(); Py_XDECREF(result); - - return result != NULL && !PyErr_Occurred(); + return rc; } //------------------------------------------------------------------------- @@ -484,18 +474,55 @@ void IDAPython_RunStatement(void) } } +//------------------------------------------------------------------------- +// Convert return value from Python to IDC or report about an error. +// This function also decrements the reference "result" (python variable) +static bool return_python_result( + idc_value_t *idc_result, + const ref_t &py_result, + char *errbuf, + size_t errbufsize) +{ + if ( errbufsize > 0 ) + errbuf[0] = '\0'; + + if ( py_result == NULL ) + { + handle_python_error(errbuf, errbufsize); + return false; + } + + int cvt = CIP_OK; + if ( idc_result != NULL ) + { + idc_result->clear(); + cvt = pyvar_to_idcvar(py_result, idc_result); + if ( cvt < CIP_OK ) + qsnprintf(errbuf, errbufsize, "ERROR: bad return value"); + } + + return cvt >= CIP_OK; +} + //------------------------------------------------------------------------- // This function will call the Python function 'idaapi.IDAPython_ExecFile' // It does not use 'import', thus the executed script will not yield a new module name // It returns the exception and traceback information. // We use the Python function to execute the script because it knows how to deal with // module reloading. -static bool IDAPython_ExecFile(const char *FileName, char *errbuf, size_t errbufsz) +static bool IDAPython_ExecFile( + const char *FileName, + char *errbuf, + size_t errbufsz, + const char *idaapi_script = S_IDAAPI_EXECSCRIPT, + idc_value_t *second_res = NULL, + bool want_tuple = false) { - PyObject *py_execscript = get_idaapi_attr(S_IDAAPI_EXECSCRIPT); + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_execscript(get_idaapi_attr(idaapi_script)); if ( py_execscript == NULL ) { - qstrncpy(errbuf, "Could not find idaapi." S_IDAAPI_EXECSCRIPT " ?!", errbufsz); + qsnprintf(errbuf, errbufsz, "Could not find idaapi.%s ?!", idaapi_script); return false; } @@ -503,18 +530,15 @@ static bool IDAPython_ExecFile(const char *FileName, char *errbuf, size_t errbuf qstrncpy(script, FileName, sizeof(script)); strrpl(script, '\\', '/'); - PyObject *py_script = PyString_FromString(script); - PYW_GIL_ENSURE; - PyObject *py_ret = PyObject_CallFunctionObjArgs( - py_execscript, - py_script, + newref_t py_script(PyString_FromString(script)); + newref_t py_ret(PyObject_CallFunctionObjArgs( + py_execscript.o, + py_script.o, GetMainGlobals(), - NULL); - PYW_GIL_RELEASE; - Py_DECREF(py_script); - Py_DECREF(py_execscript); + NULL)); // Failure at this point means the script was interrupted + bool interrupted = false; qstring err; if ( PyW_GetError(&err) || py_ret == NULL ) { @@ -523,23 +547,52 @@ static bool IDAPython_ExecFile(const char *FileName, char *errbuf, size_t errbuf qstrncpy(errbuf, "Script interrupted", errbufsz); else qstrncpy(errbuf, err.c_str(), errbufsz); - Py_XDECREF(py_ret); - return false; + interrupted = true; } - bool ok; - if ( py_ret == Py_None ) - ok = true; - else if ( PyString_Check(py_ret) ) + bool ok = false; + if ( !interrupted ) { - qstrncpy(errbuf, PyString_AsString(py_ret), errbufsz); - ok = false; - } - // Cannot be otherwise! - else - INTERR(30154); + PyObject *ret_o; + if ( want_tuple ) + { + if ( second_res != NULL + && PyTuple_Check(py_ret.o) + && PyTuple_Size(py_ret.o) == 2 ) + { + ret_o = PyTuple_GetItem(py_ret.o, 0); // Borrowed reference + } + else + { + INTERR(30444); + } + } + else + { + ret_o = py_ret.o; + } - Py_XDECREF(py_ret); + if ( ret_o == Py_None ) + { + if ( want_tuple ) + { + borref_t ret2_o(PyTuple_GetItem(py_ret.o, 1)); + ok = return_python_result(second_res, ret2_o, errbuf, errbufsz); + } + else + { + ok = true; + } + } + else if ( PyString_Check(ret_o) ) + { + qstrncpy(errbuf, PyString_AsString(ret_o), errbufsz); + } + else + { + INTERR(30154); + } + } return ok; } @@ -584,39 +637,6 @@ static bool parse_py_modname( return p != NULL; } -//------------------------------------------------------------------------- -// Convert return value from Python to IDC or report about an error. -// This function also decrements the reference "result" (python variable) -static bool return_python_result( - idc_value_t *idc_result, - PyObject *py_result, - char *errbuf, - size_t errbufsize) -{ - if ( errbufsize > 0 ) - errbuf[0] = '\0'; - - if ( py_result == NULL ) - { - handle_python_error(errbuf, errbufsize); - return false; - } - - int cvt = CIP_OK; - if ( idc_result != NULL ) - { - idc_result->clear(); - cvt = pyvar_to_idcvar(py_result, idc_result); - if ( cvt < CIP_OK ) - qsnprintf(errbuf, errbufsize, "ERROR: bad return value"); - } - - if ( cvt != CIP_OK_NODECREF ) - Py_XDECREF(py_result); - - return cvt >= CIP_OK; -} - //------------------------------------------------------------------------- // Compile callback for Python external language evaluator bool idaapi IDAPython_extlang_compile( @@ -626,6 +646,7 @@ bool idaapi IDAPython_extlang_compile( char *errbuf, size_t errbufsize) { + PYW_GIL_GET; PyObject *globals = GetMainGlobals(); PyCodeObject *code = (PyCodeObject *)Py_CompileString(expr, "", Py_eval_input); @@ -668,27 +689,25 @@ bool idaapi IDAPython_extlang_run( char *errbuf, size_t errbufsize) { + PYW_GIL_GET; // Try to extract module name (if any) from the funcname char modname[MAXSTR] = {0}; char funcname[MAXSTR] = {0}; bool imported_module = parse_py_modname(name, modname, funcname, MAXSTR); - ppyobject_vec_t pargs; - boolvec_t decref; bool ok = true; PyObject *module = NULL; + ref_vec_t pargs; do { // Convert arguments to python - ok = pyw_convert_idc_args(args, nargs, pargs, &decref, errbuf, errbufsize); + ok = pyw_convert_idc_args(args, nargs, pargs, false, errbuf, errbufsize); if ( !ok ) break; if ( imported_module ) { - PYW_GIL_ENSURE; module = PyImport_ImportModule(modname); - PYW_GIL_RELEASE; } else { @@ -707,22 +726,20 @@ bool idaapi IDAPython_extlang_run( break; } - PyCodeObject *code = (PyCodeObject *) PyFunction_GetCode(func); - PYW_GIL_ENSURE; - PyObject *pres = PyEval_EvalCodeEx( - code, - globals, NULL, - &pargs[0], nargs, - NULL, 0, NULL, 0, NULL); - PYW_GIL_RELEASE; - ok = return_python_result(result, pres, errbuf, errbufsize); + borref_t code(PyFunction_GetCode(func)); + qvector pargs_ptrs; + pargs.to_pyobject_pointers(&pargs_ptrs); + newref_t py_res(PyEval_EvalCodeEx( + (PyCodeObject*) code.o, + globals, NULL, + pargs_ptrs.begin(), + nargs, + NULL, 0, NULL, 0, NULL)); + ok = return_python_result(result, py_res, errbuf, errbufsize); } while ( false ); - pyw_free_idc_args(pargs, &decref); - if ( imported_module ) Py_XDECREF(module); - return ok; } @@ -733,12 +750,47 @@ bool idaapi IDAPython_extlang_compile_file( char *errbuf, size_t errbufsize) { + PYW_GIL_GET; begin_execution(); bool ok = IDAPython_ExecFile(filename, errbuf, errbufsize); end_execution(); return ok; } +//------------------------------------------------------------------------- +// Load processor module callback for Python external language evaluator +static bool idaapi IDAPython_extlang_loadprocmod( + const char *filename, + idc_value_t *procobj, + char *errbuf, + size_t errbufsize) +{ + PYW_GIL_GET; + begin_execution(); + bool ok = IDAPython_ExecFile(filename, errbuf, errbufsize, S_IDAAPI_LOADPROCMOD, procobj, true); + if ( ok && procobj->is_zero() ) + { + errbuf[0] = '\0'; + ok = false; + } + end_execution(); + return ok; +} + +//------------------------------------------------------------------------- +// Unload processor module callback for Python external language evaluator +static bool idaapi IDAPython_extlang_unloadprocmod( + const char *filename, + char *errbuf, + size_t errbufsize) +{ + PYW_GIL_GET; + begin_execution(); + bool ok = IDAPython_ExecFile(filename, errbuf, errbufsize, S_IDAAPI_UNLOADPROCMOD); + end_execution(); + return ok; +} + //------------------------------------------------------------------------- // Create an object instance bool idaapi IDAPython_extlang_create_object( @@ -749,10 +801,9 @@ bool idaapi IDAPython_extlang_create_object( char *errbuf, // out: error message if evaluation fails size_t errbufsize) // in: size of the error buffer { - PyObject *py_mod(NULL), *py_cls(NULL); - ppyobject_vec_t pargs; - + PYW_GIL_GET; bool ok = false; + ref_vec_t pargs; do { // Parse the object name (to get the module and class name) @@ -761,7 +812,7 @@ bool idaapi IDAPython_extlang_create_object( parse_py_modname(name, modname, clsname, MAXSTR); // Get a reference to the module - py_mod = PyW_TryImportModule(modname); + ref_t py_mod(PyW_TryImportModule(modname)); if ( py_mod == NULL ) { qsnprintf(errbuf, errbufsize, "Could not import module '%s'!", modname); @@ -769,7 +820,7 @@ bool idaapi IDAPython_extlang_create_object( } // Get the class reference - py_cls = PyW_TryGetAttrString(py_mod, clsname); + ref_t py_cls(PyW_TryGetAttrString(py_mod.o, clsname)); if ( py_cls == NULL ) { qsnprintf(errbuf, errbufsize, "Could not find class type '%s'!", clsname); @@ -777,22 +828,15 @@ bool idaapi IDAPython_extlang_create_object( } // Error during conversion? - ok = pyw_convert_idc_args(args, nargs, pargs, NULL, errbuf, errbufsize); + ok = pyw_convert_idc_args(args, nargs, pargs, true, errbuf, errbufsize); if ( !ok ) break; // Call the constructor - PYW_GIL_ENSURE; - PyObject *py_res = PyObject_CallObject(py_cls, pargs.empty() ? NULL : pargs[0]); - PYW_GIL_RELEASE; + newref_t py_res(PyObject_CallObject(py_cls.o, pargs.empty() ? NULL : pargs[0].o)); ok = return_python_result(result, py_res, errbuf, errbufsize); } while ( false ); - Py_XDECREF(py_mod); - Py_XDECREF(py_cls); - - // Free the arguments tuple - pyw_free_idc_args(pargs); return ok; } @@ -803,38 +847,37 @@ bool idaapi IDAPython_extlang_get_attr( const char *attr, // in: attribute name idc_value_t *result) { - PyObject *py_mod(NULL), *py_obj(NULL); - bool is_opaque_obj = false; + PYW_GIL_GET; int cvt = CIP_FAILED; do { // Get a reference to the module - py_mod = PyW_TryImportModule(S_MAIN); + ref_t py_mod(PyW_TryImportModule(S_MAIN)); if ( py_mod == NULL ) break; // Object specified: // - (1) string contain attribute name in the main module // - (2) opaque object (we use it as is) + ref_t py_obj; if ( obj != NULL ) { // (1) Get attribute from main module if ( obj->vtype == VT_STR2 ) - py_obj = PyW_TryGetAttrString(py_mod, obj->c_str()); + { + py_obj = PyW_TryGetAttrString(py_mod.o, obj->c_str()); + } // (2) see if opaque object else { // Convert object (expecting opaque object) cvt = idcvar_to_pyvar(*obj, &py_obj); - // Only opaque objects are accepted - if ( cvt != CIP_OK_NODECREF ) + if ( cvt != CIP_OK_OPAQUE ) // Only opaque objects are accepted { - Py_XDECREF(py_obj); - py_obj = NULL; + py_obj = ref_t(); cvt = CIP_FAILED; break; } - is_opaque_obj = true; } // Get the attribute reference if ( py_obj == NULL ) @@ -852,24 +895,22 @@ bool idaapi IDAPython_extlang_get_attr( { cvt = CIP_FAILED; // Get the class - PyObject *cls = PyObject_GetAttrString(py_obj, "__class__"); + newref_t cls(PyObject_GetAttrString(py_obj.o, "__class__")); if ( cls == NULL ) break; // Get its name - PyObject *name = PyObject_GetAttrString(cls, "__name__"); - Py_DECREF(cls); + newref_t name(PyObject_GetAttrString(cls.o, "__name__")); if ( name == NULL ) break; // Convert name object to string object - PyObject *string = PyObject_Str(name); - Py_DECREF(name); + newref_t string(PyObject_Str(name.o)); if ( string == NULL ) break; // Convert name python string to a C string - const char *clsname = PyString_AsString(name); + const char *clsname = PyString_AsString(string.o); if ( clsname == NULL ) break; @@ -878,7 +919,7 @@ bool idaapi IDAPython_extlang_get_attr( break; } - PyObject *py_attr = PyW_TryGetAttrString(py_obj, attr); + ref_t py_attr(PyW_TryGetAttrString(py_obj.o, attr)); // No attribute? if ( py_attr == NULL ) { @@ -890,32 +931,23 @@ bool idaapi IDAPython_extlang_get_attr( { cvt = CIP_OK; // Decrement attribute (because of GetAttrString) - Py_DECREF(py_attr); } else { cvt = pyvar_to_idcvar(py_attr, result); - // Conversion succeeded and opaque object was passed: - // Since the object will be passed to IDC, it is likely that IDC value will be - // destroyed and also destroying the opaque object with it. That is an undesired effect. - // We increment the reference of the object so that even if the IDC value dies - // the opaque object remains. So by not decrement reference after GetAttrString() call - // we are indirectly increasing the reference. If it was not opaque then we decrement the reference. - if ( cvt >= CIP_OK && cvt != CIP_OK_NODECREF ) - { - // Decrement the reference (that was incremented by GetAttrString()) - Py_DECREF(py_attr); - } + // // Conversion succeeded and opaque object was passed: + // // Since the object will be passed to IDC, it is likely that IDC value will be + // // destroyed and also destroying the opaque object with it. That is an undesired effect. + // // We increment the reference of the object so that even if the IDC value dies + // // the opaque object remains. So by not decrement reference after GetAttrString() call + // // we are indirectly increasing the reference. If it was not opaque then we decrement the reference. + // if ( cvt >= CIP_OK && cvt != CIP_OK_NODECREF ) + // { + // // Decrement the reference (that was incremented by GetAttrString()) + // py_attr.decref(); + // } } } while ( false ); - - // Free main module reference - Py_XDECREF(py_mod); - - // Wasn't working with main module? - if ( obj != NULL && !is_opaque_obj ) - Py_XDECREF(py_obj); - return cvt >= CIP_OK; } @@ -927,31 +959,27 @@ bool idaapi IDAPython_extlang_set_attr( const char *attr, // in: attribute name idc_value_t *value) { - PyObject *py_mod(NULL), *py_obj(NULL); - bool ok = false, is_opaque_obj = false; + PYW_GIL_GET; + bool ok = false; do { // Get a reference to the module - py_mod = PyW_TryImportModule(S_MAIN); + ref_t py_mod(PyW_TryImportModule(S_MAIN)); if ( py_mod == NULL ) break; - + ref_t py_obj; if ( obj != NULL ) { // Get the attribute reference (from just a name) if ( obj->vtype == VT_STR2 ) - py_obj = PyW_TryGetAttrString(py_mod, obj->c_str()); + { + py_obj = PyW_TryGetAttrString(py_mod.o, obj->c_str()); + } else { int cvt = idcvar_to_pyvar(*obj, &py_obj); - // Only opaque objects are accepted - if ( cvt != CIP_OK_NODECREF ) - { - Py_XDECREF(py_obj); - py_obj = NULL; - } - else - is_opaque_obj = true; + if ( cvt != CIP_OK_OPAQUE ) // Only opaque objects are accepted + py_obj = ref_t(); } // No object to set_attr on? if ( py_obj == NULL ) @@ -963,21 +991,15 @@ bool idaapi IDAPython_extlang_set_attr( py_obj = py_mod; } // Convert the value - PyObject *py_var(NULL); + ref_t py_var; int cvt = idcvar_to_pyvar(*value, &py_var); if ( cvt >= CIP_OK ) { - ok = PyObject_SetAttrString(py_obj, attr, py_var) != -1; - if ( cvt != CIP_OK_NODECREF ) - Py_XDECREF(py_var); + ok = PyObject_SetAttrString(py_obj.o, attr, py_var.o) != -1; + // if ( cvt != CIP_OK_NODECREF ) + // Py_XDECREF(py_var); } } while ( false ); - - Py_XDECREF(py_mod); - - if ( obj != NULL && !is_opaque_obj ) - Py_XDECREF(py_obj); - return ok; } @@ -991,17 +1013,19 @@ bool idaapi IDAPython_extlang_calcexpr( char *errbuf, size_t errbufsize) { + PYW_GIL_GET; PyObject *globals = GetMainGlobals(); - if ( globals == NULL ) - return false; - - begin_execution(); - PYW_GIL_ENSURE; - PyObject *result = PyRun_String(expr, Py_eval_input, globals, globals); - PYW_GIL_RELEASE; - end_execution(); - - return return_python_result(rv, result, errbuf, errbufsize); + bool ok = globals != NULL; + ref_t result; + if ( ok ) + { + begin_execution(); + result = newref_t(PyRun_String(expr, Py_eval_input, globals, globals)); + end_execution(); + } + if ( ok && result != NULL ) + ok = return_python_result(rv, result, errbuf, errbufsize); + return ok; } //------------------------------------------------------------------------- @@ -1014,6 +1038,7 @@ bool idaapi IDAPython_extlang_call_method( char *errbuf, size_t errbufsize) { + PYW_GIL_GET; // Check for unsupported usage of call_method. // Mainly a method call requires an object and a method. if ( (idc_obj == NULL && method_name == NULL) || (idc_obj != NULL && method_name == NULL) ) @@ -1023,16 +1048,18 @@ bool idaapi IDAPython_extlang_call_method( } // Behave like run() else if ( idc_obj == NULL && method_name != NULL ) + { return IDAPython_extlang_run(method_name, nargs, args, result, errbuf, errbufsize); + } - PyObject *py_obj(NULL), *py_method(NULL); // Holds conversion status of input object int obj_cvt; - ppyobject_vec_t pargs; bool ok = false; + ref_vec_t pargs; do { // Convert input object + ref_t py_obj; obj_cvt = idcvar_to_pyvar(*idc_obj, &py_obj); if ( obj_cvt < CIP_OK ) { @@ -1040,32 +1067,22 @@ bool idaapi IDAPython_extlang_call_method( break; } - py_method = PyW_TryGetAttrString(py_obj, method_name); - if ( py_method == NULL || !PyCallable_Check(py_method) ) + ref_t py_method(PyW_TryGetAttrString(py_obj.o, method_name)); + if ( py_method == NULL || !PyCallable_Check(py_method.o) ) { qsnprintf(errbuf, errbufsize, "The input object does not have a callable method called '%s'", method_name); break; } // Convert arguments to python objects - ok = pyw_convert_idc_args(args, nargs, pargs, NULL, errbuf, errbufsize); + ok = pyw_convert_idc_args(args, nargs, pargs, true, errbuf, errbufsize); if ( !ok ) break; - PYW_GIL_ENSURE; - PyObject *py_res = PyObject_CallObject(py_method, pargs.empty() ? NULL : pargs[0]); - PYW_GIL_RELEASE; + newref_t py_res(PyObject_CallObject(py_method.o, pargs.empty() ? NULL : pargs[0].o)); ok = return_python_result(result, py_res, errbuf, errbufsize); } while ( false ); - // Free converted args - pyw_free_idc_args(pargs); - - // Release reference of object if needed - if ( obj_cvt != CIP_OK_NODECREF ) - Py_XDECREF(py_obj); - - Py_XDECREF(py_method); return ok; } @@ -1085,6 +1102,8 @@ const extlang_t extlang_python = IDAPython_extlang_set_attr, IDAPython_extlang_call_method, IDAPython_extlang_run_statements, + IDAPython_extlang_loadprocmod, + IDAPython_extlang_unloadprocmod, }; //------------------------------------------------------------------------- @@ -1100,6 +1119,8 @@ void enable_extlang_python(bool enable) // Execute a line in the Python CLI bool idaapi IDAPython_cli_execute_line(const char *line) { + PYW_GIL_GET; + // Do not process empty lines if ( line[0] == '\0' ) return true; @@ -1152,26 +1173,20 @@ bool idaapi IDAPYthon_cli_complete_line( const char *line, int x) { - PyObject *py_complete = get_idaapi_attr(S_IDAAPI_COMPLETION); + PYW_GIL_GET; + + ref_t py_complete(get_idaapi_attr(S_IDAAPI_COMPLETION)); if ( py_complete == NULL ) return false; - PYW_GIL_ENSURE; - PyObject *py_ret = PyObject_CallFunction(py_complete, "sisi", prefix, n, line, x); //lint !e1776 - PYW_GIL_RELEASE; - - Py_DECREF(py_complete); + newref_t py_ret(PyObject_CallFunction(py_complete.o, "sisi", prefix, n, line, x)); //lint !e1776 // Swallow the error PyW_GetError(completion); - bool ok = py_ret != NULL && PyString_Check(py_ret) != 0; - + bool ok = py_ret != NULL && PyString_Check(py_ret.o) != 0; if ( ok ) - *completion = PyString_AS_STRING(py_ret); - - Py_XDECREF(py_ret); - + *completion = PyString_AS_STRING(py_ret.o); return ok; } @@ -1202,9 +1217,8 @@ void enable_python_cli(bool enable) // Prints the IDAPython copyright banner void py_print_banner() { - PYW_GIL_ENSURE; + PYW_GIL_CHECK_LOCKED_SCOPE(); PyRun_SimpleString("print_banner()"); - PYW_GIL_RELEASE; } //------------------------------------------------------------------------ @@ -1216,7 +1230,7 @@ void parse_plugin_options() // No options? if ( options == NULL ) - return; + return; // User specified 'when' parameter? const char *p = strchr(options, ';'); @@ -1238,7 +1252,8 @@ void parse_plugin_options() // The arguments will then be accessible via 'idc' module / 'ARGV' variable. void convert_idc_args() { - PyObject *py_args = PyList_New(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_args(PyList_New(0)); idc_value_t *idc_args = find_idc_gvar(S_IDC_ARGS_VARNAME); if ( idc_args != NULL ) @@ -1247,48 +1262,77 @@ void convert_idc_args() char attr_name[20] = {"0"}; for ( int i=1; VarGetAttr(idc_args, attr_name, &attr) == eOk; i++ ) { - PyList_Insert(py_args, i, PyString_FromString(attr.c_str())); + PyList_Insert(py_args.o, i, PyString_FromString(attr.c_str())); qsnprintf(attr_name, sizeof(attr_name), "%d", i); } } // Get reference to the IDC module (it is imported by init.py) - PyObject *py_mod = PyW_TryImportModule(S_IDC_MODNAME); + ref_t py_mod(PyW_TryImportModule(S_IDC_MODNAME)); if ( py_mod != NULL ) - PyObject_SetAttrString(py_mod, S_IDC_ARGS_VARNAME, py_args); - - Py_DECREF(py_args); - Py_XDECREF(py_mod); + PyObject_SetAttrString(py_mod.o, S_IDC_ARGS_VARNAME, py_args.o); } //------------------------------------------------------------------------ -// We install the menu later because the text version crashes if -// add_menu_item is called too early -static int idaapi menu_installer_cb(void *, int code, va_list) +static int idaapi script_runner_cb(void *, int code, va_list) { switch ( code ) { case ui_ready_to_run: - g_ui_ready = true; - py_print_banner(); + { + PYW_GIL_GET; // This hook gets called from the kernel. Ensure we hold the GIL. + g_ui_ready = true; + py_print_banner(); - if ( g_run_when == run_on_ui_ready ) + if ( g_run_when == run_on_ui_ready ) RunScript(g_run_script); + } break; case ui_database_inited: { + PYW_GIL_GET; // This hook gets called from the kernel. Ensure we hold the GIL. convert_idc_args(); if ( g_run_when == run_on_db_open ) RunScript(g_run_script); - break; } + break; default: break; } return 0; } +#ifdef _DEBUG +//------------------------------------------------------------------------ +// extern int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag); +extern PyThreadState *_PyThreadState_Current; +static int idaapi ui_debug_handler_cb(void *, int code, va_list) +{ + // This hook gets called from the kernel, but its very point is to + // make sure that we don't hold the GIL. Thus: No PYW_GIL_GET here! + switch ( code ) + { + case debug_assert_thread_waitready: + // We will *always* be in a non-main thread when this is called. + if ( _PyThreadState_Current != NULL ) + { + PyThreadState *tcur = PyGILState_GetThisThreadState(); + if ( tcur == _PyThreadState_Current ) + { + // This thread is the '_PyThreadState_Current'; i.e., it holds the lock. + // We're likely to end up in a deadlock. + BPT; + } + } + break; + default: + break; + } + return 0; +} +#endif + //------------------------------------------------------------------------- // remove current directory (empty entry) from the sys.path static void sanitize_path() @@ -1323,14 +1367,13 @@ static bool initsite(void) { PyErr_Print(); Py_Finalize(); - return false; } - else + else { Py_DECREF(m); - return true; } + return true; } //------------------------------------------------------------------------- @@ -1386,9 +1429,9 @@ bool IDAPython_Init(void) read_user_config_file("python.cfg", set_python_options, NULL); if ( g_alert_auto_scripts ) { - if ( pywraps_check_autoscripts(tmp, sizeof(tmp)) - && askyn_c(0, "HIDECANCEL\nTITLE IDAPython\nThe script '%s' was found in the current directory and will be automatically executed by Python.\n\n" - "Do you want to continue loading IDAPython?", tmp) <= 0 ) + if ( pywraps_check_autoscripts(tmp, sizeof(tmp)) + && askyn_c(0, "HIDECANCEL\nTITLE IDAPython\nThe script '%s' was found in the current directory and will be automatically executed by Python.\n\n" + "Do you want to continue loading IDAPython?", tmp) <= 0 ) { return false; } @@ -1432,17 +1475,16 @@ bool IDAPython_Init(void) // Set IDAPYTHON_VERSION in Python qsnprintf( - tmp, - sizeof(tmp), - "IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)\n" - "IDAPYTHON_REMOVE_CWD_SYS_PATH = %s\n", - VER_MAJOR, - VER_MINOR, - VER_PATCH, - VER_STATUS, - VER_SERIAL, - g_remove_cwd_sys_path ? "True" : "False"); - + tmp, + sizeof(tmp), + "IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)\n" + "IDAPYTHON_REMOVE_CWD_SYS_PATH = %s\n", + VER_MAJOR, + VER_MINOR, + VER_PATCH, + VER_STATUS, + VER_SERIAL, + g_remove_cwd_sys_path ? "True" : "False"); PyRun_SimpleString(tmp); // Install extlang. Needs to be done before running init.py @@ -1483,27 +1525,28 @@ bool IDAPython_Init(void) // Register a RunPythonStatement() function for IDC set_idc_func_ex( - S_IDC_RUNPYTHON_STATEMENT, - idc_runpythonstatement, - idc_runpythonstatement_args, - 0); + S_IDC_RUNPYTHON_STATEMENT, + idc_runpythonstatement, + idc_runpythonstatement_args, + 0); // A script specified on the command line is run if ( g_run_when == run_on_init ) RunScript(g_run_script); -#ifdef PLUGINFIX - hook_to_notification_point(HT_UI, menu_installer_cb, NULL); -#else - install_python_menus(); - py_print_banner(); +#ifdef _DEBUG + hook_to_notification_point(HT_UI, ui_debug_handler_cb, NULL); #endif + hook_to_notification_point(HT_UI, script_runner_cb, NULL); // Enable the CLI by default enable_python_cli(true); g_initialized = true; pywraps_nw_notify(NW_INITIDA_SLOT); + + PyEval_ReleaseThread(PyThreadState_Get()); + return true; } @@ -1511,11 +1554,20 @@ bool IDAPython_Init(void) // Cleaning up Python void IDAPython_Term(void) { -#ifdef PLUGINFIX - unhook_from_notification_point(HT_UI, menu_installer_cb, NULL); + if ( PyGILState_GetThisThreadState() ) + { + // Note: No 'PYW_GIL_GET' here, as it would try to release + // the state after 'Py_Finalize()' has been called. + // ...nor is it a good idea to try to put it in its own scope, + // as it will PyGILState_Release() the current thread & GIL, and + // Py_Finalize() itself wouldn't be happy then. + PyGILState_Ensure(); + } + + unhook_from_notification_point(HT_UI, script_runner_cb, NULL); +#ifdef _DEBUG + unhook_from_notification_point(HT_UI, ui_debug_handler_cb, NULL); #endif - /* Remove the menu items before termination */ - del_menu_item("File/Python command..."); // Notify about IDA closing pywraps_nw_notify(NW_TERMIDA_SLOT); @@ -1594,7 +1646,7 @@ void idaapi run(int arg) plugin_t PLUGIN = { IDP_INTERFACE_VERSION, - PLUGIN_FLAGS | PLUGIN_HIDE, // plugin flags + PLUGIN_FIX | PLUGIN_HIDE, // plugin flags init, // initialize term, // terminate. this pointer may be NULL. run, // invoke plugin diff --git a/python/idc.py b/python/idc.py index a2a2bfc..58e311c 100644 --- a/python/idc.py +++ b/python/idc.py @@ -260,6 +260,9 @@ NEF_FLAT = idaapi.NEF_FLAT # Autocreated FLAT group (PE) def IsString(var): raise NotImplementedError, "this function is not needed in Python" def IsLong(var): raise NotImplementedError, "this function is not needed in Python" def IsFloat(var): raise NotImplementedError, "this function is not needed in Python" +def IsFunc(var): raise NotImplementedError, "this function is not needed in Python" +def IsPvoid(var): raise NotImplementedError, "this function is not needed in Python" +def IsInt64(var): raise NotImplementedError, "this function is not needed in Python" def MK_FP(seg, off): """ @@ -1159,7 +1162,11 @@ REFINFO_NOBASE = 0x80 # offset base is a number # that base have be any value # nb: base xrefs are created only if base # points to the middle of a segment - +REFINFO_SUBTRACT = 0x0100 # the reference value is subtracted from + # the base value instead of (as usual) + # being added to it +REFINFO_SIGNEDOP = 0x0200 # the operand value is sign-extended (only + # supported for REF_OFF8/16/32/64) def OpSeg(ea, n): """ @@ -1865,24 +1872,28 @@ def Qword(ea): def GetFloat(ea): """ Get value of a floating point number (4 bytes) - + This function assumes number stored using IEEE format + and in the same endianness as integers. + @param ea: linear address @return: float """ - tmp = idaapi.get_many_bytes(ea, 4) + tmp = struct.pack("I", Dword(ea)) return struct.unpack("f", tmp)[0] def GetDouble(ea): """ Get value of a floating point number (8 bytes) + This function assumes number stored using IEEE format + and in the same endianness as integers. @param ea: linear address @return: double """ - tmp = idaapi.get_many_bytes(ea, 8) + tmp = struct.pack("Q", Qword(ea)) return struct.unpack("d", tmp)[0] @@ -2165,23 +2176,46 @@ def Demangle(name, disable_mask): return idaapi.demangle_name(name, disable_mask) +def GetDisasmEx(ea, flags): + """ + Get disassembly line + + @param ea: linear address of instruction + + @param flags: combination of the GENDSM_ flags, or 0 + + @return: "" - could not decode instruction at the specified location + + @note: this function may not return exactly the same mnemonics + as you see on the screen. + """ + text = idaapi.generate_disasm_line(ea, flags) + if text: + return idaapi.tag_remove(text) + else: + return "" + +# flags for GetDisasmEx +# generate a disassembly line as if +# there is an instruction at 'ea' +GENDSM_FORCE_CODE = idaapi.GENDSM_FORCE_CODE + +# if the instruction consists of several lines, +# produce all of them (useful for parallel instructions) +GENDSM_MULTI_LINE = idaapi.GENDSM_MULTI_LINE + def GetDisasm(ea): """ Get disassembly line @param ea: linear address of instruction - @return: "" - no instruction at the specified location + @return: "" - could not decode instruction at the specified location @note: this function may not return exactly the same mnemonics as you see on the screen. """ - text = idaapi.generate_disasm_line(ea) - if text: - return idaapi.tag_remove(text) - else: - return "" - + return GetDisasmEx(ea, 0) def GetMnem(ea): """ @@ -2475,9 +2509,9 @@ def ChangeConfig(directive): @param directive: directives to process, for example: PACK_DATABASE=2 @note: If the directives are erroneous, a fatal error will be generated. - The changes will be effective only for the current session. + The settings are permanent: effective for the current session and the next ones """ - return Eval('ChangeConfig("%s")' % directive) + return Eval('ChangeConfig("%s")' % idaapi.str2user(directive)) # The following functions allow you to set/get common parameters. @@ -3279,7 +3313,7 @@ def SegName(ea): return name -def AddSeg(startea, endea, base, use32, align, comb): +def AddSegEx(startea, endea, base, use32, align, comb, flags): """ Create a new segment @@ -3294,6 +3328,7 @@ def AddSeg(startea, endea, base, use32, align, comb): @param use32: 0: 16bit segment, 1: 32bit segment, 2: 64bit segment @param align: segment alignment. see below for alignment values @param comb: segment combination. see below for combination values. + @param flags: combination of ADDSEG_... bits @return: 0-failed, 1-ok """ @@ -3304,8 +3339,27 @@ def AddSeg(startea, endea, base, use32, align, comb): s.bitness = use32 s.align = align s.comb = comb - return idaapi.add_segm_ex(s, "", "", idaapi.ADDSEG_NOSREG) + return idaapi.add_segm_ex(s, "", "", flags) +ADDSEG_NOSREG = idaapi.ADDSEG_NOSREG # set all default segment register values + # to BADSELs + # (undefine all default segment registers) +ADDSEG_OR_DIE = idaapi. ADDSEG_OR_DIE # qexit() if can't add a segment +ADDSEG_NOTRUNC = idaapi.ADDSEG_NOTRUNC # don't truncate the new segment at the beginning + # of the next segment if they overlap. + # destroy/truncate old segments instead. +ADDSEG_QUIET = idaapi.ADDSEG_QUIET # silent mode, no "Adding segment..." in the messages window +ADDSEG_FILLGAP = idaapi.ADDSEG_FILLGAP # If there is a gap between the new segment + # and the previous one, and this gap is less + # than 64K, then fill the gap by extending the + # previous segment and adding .align directive + # to it. This way we avoid gaps between segments. + # Too many gaps lead to a virtual array failure. + # It can not hold more than ~1000 gaps. +ADDSEG_SPARSE = idaapi.ADDSEG_SPARSE # Use sparse storage method for the new segment + +def AddSeg(startea, endea, base, use32, align, comb): + return AddSegEx(startea, endea, base, use32, align, comb, ADDSEG_NOSREG) def DelSeg(ea, flags): """ @@ -3889,7 +3943,7 @@ def SaveFile(filepath, pos, ea, size): @return: 0 - error, 1 - ok """ - of = idaapi.fopenWB(filepath) + of = idaapi.fopenM(filepath) if of: retval = idaapi.base2file(of, pos, ea, ea+size) @@ -5369,10 +5423,10 @@ def AddStrucMember(sid, name, offset, flag, typeid, nbytes, target=-1, tdelta=0, """ if isOff0(flag): - return Eval('AddStrucMember(%d, "%s", %d, %d, %d, %d, %d, %d, %d);' % (sid, name, offset, flag, typeid, nbytes, + return Eval('AddStrucMember(%d, "%s", %d, %d, %d, %d, %d, %d, %d);' % (sid, idaapi.str2user(name), offset, flag, typeid, nbytes, target, tdelta, reftype)) else: - return Eval('AddStrucMember(%d, "%s", %d, %d, %d, %d);' % (sid, name, offset, flag, typeid, nbytes)) + return Eval('AddStrucMember(%d, "%s", %d, %d, %d, %d);' % (sid, idaapi.str2user(name), offset, flag, typeid, nbytes)) STRUC_ERROR_MEMBER_NAME = -1 # already has member with this name (bad name) @@ -6799,10 +6853,10 @@ def GetType(ea): def SizeOf(typestr): """ Returns the size of the type. It is equivalent to IDC's sizeof(). - Use name, tp, fld = idc.ParseType() ; Sizeof(fld) to retrieve the size + Use name, tp, fld = idc.ParseType() ; SizeOf(tp) to retrieve the size @return: -1 if typestring is not valid otherwise the size of the type """ - return idaapi.get_type_size0(idaapi.cvar.idati, typestr) + return idaapi.calc_type_size(idaapi.cvar.idati, typestr) def GetTinfo(ea): """ @@ -6813,6 +6867,15 @@ def GetTinfo(ea): """ return idaapi.idc_get_type_raw(ea) +def GetLocalTinfo(ordinal): + """ + Get local type information as 'typeinfo' object + + @param ordinal: slot number (1...NumberOfLocalTypes) + @return: None on failure, or (type, fields, name) tuple. + """ + return idaapi.idc_get_local_type_raw(ordinal) + def GuessType(ea): """ Guess type of function/variable @@ -6823,6 +6886,37 @@ def GuessType(ea): """ return idaapi.idc_guess_type(ea) +TINFO_GUESSED = 0x0000 # this is a guessed type +TINFO_DEFINITE = 0x0001 # this is a definite type +TINFO_DELAYFUNC = 0x0002 # if type is a function and no function exists at ea, + # schedule its creation and argument renaming to + # auto-analysis otherwise try to create it immediately + +def ApplyType(ea, py_type, flags = TINFO_DEFINITE): + """ + Apply the specified type to the address + + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param py_type: typeinfo tuple (type, fields) as GetTinfo() returns + or tuple (name, type, fields) as ParseType() returns + or None + if specified as None, then the + item associated with 'ea' will be deleted. + @param ea: the address of the object + @param flags: combination of TINFO_... constants or 0 + @return: Boolean + """ + + if py_type != None: + if len(py_type) == 3: + pt = py_type[1:] # skip name component + else: + pt = py_type + return idaapi.apply_type(idaapi.cvar.idati, pt[0], pt[1], ea, flags) + if idaapi.has_ti(ea): + idaapi.del_tinfo(ea) + return True + return False def SetType(ea, newtype): """ @@ -6837,11 +6931,10 @@ def SetType(ea, newtype): @return: 1-ok, 0-failed. """ if newtype is not '': - return idaapi.apply_cdecl2(idaapi.cvar.idati, ea, newtype) - if idaapi.has_ti(ea): - idaapi.del_tinfo(ea) - return True - return False + pt = ParseType(newtype, 0)[1:] + else: + pt = None + return ApplyType(ea, pt, TINFO_DEFINITE) def ParseType(inputtype, flags): """ @@ -6852,6 +6945,8 @@ def ParseType(inputtype, flags): @return: None on failure or (name, type, fields) tuple """ + if len(inputtype) != 0 and inputtype[-1] != ';': + inputtype = inputtype + ';' return idaapi.idc_parse_decl(idaapi.cvar.idati, inputtype, flags) def ParseTypes(inputtype, flags = 0): @@ -6874,6 +6969,9 @@ PT_PAK2 = 0x0020 # #pragma pack(2) PT_PAK4 = 0x0030 # #pragma pack(4) PT_PAK8 = 0x0040 # #pragma pack(8) PT_PAK16 = 0x0050 # #pragma pack(16) +PT_HIGH = 0x0080 # assume high level prototypes + # (with hidden args, etc) +PT_LOWER = 0x0100 # lower the function prototypes def GetMaxLocalType(): @@ -6903,17 +7001,14 @@ def SetLocalType(ordinal, input, flags): def GetLocalType(ordinal, flags): """ Retrieve a local type declaration - - @param ordinal: slot number (1...NumberOfLocalTypes) @param flags: any of PRTYPE_* constants - @return: local type as a C declaration or "" - - @note: This function can return types strings up to 64KiB. Use idaapi.idc_get_local_type() - for larger types. """ - res,str = idaapi.idc_get_local_type(ordinal, flags, 2**16) - return str + (type, fields) = GetLocalTinfo(ordinal) + if type: + name = GetLocalTypeName(ordinal) + return idaapi.idc_print_type(type, fields, name, flags) + return "" PRTYPE_1LINE = 0x0000 # print to one line PRTYPE_MULTI = 0x0001 # print to many lines @@ -7313,7 +7408,7 @@ def SendDbgCommand(cmd): An exception will be raised if the debugger is not running or the current debugger does not export the 'SendDbgCommand' IDC command. """ - s = Eval('SendDbgCommand("%s");' % cmd) + s = Eval('SendDbgCommand("%s");' % idaapi.str2user(cmd)) if s.startswith("IDC_FAILURE"): raise Exception, "Debugger command is available only when the debugger is active!" return s @@ -7806,10 +7901,11 @@ BPTATTR_SIZE = 2 # size of the breakpoint (undefined for software breakpoint BPTATTR_TYPE = 3 # Breakpoint types: -BPT_EXEC = 0 # Hardware: Execute instruction -BPT_WRITE = 1 # Hardware: Write access -BPT_RDWR = 3 # Hardware: Read/write access -BPT_SOFT = 4 # Software breakpoint +BPT_WRITE = 1 # Hardware: Write access +BPT_RDWR = 3 # Hardware: Read/write access +BPT_SOFT = 4 # Software breakpoint +BPT_EXEC = 8 # Hardware: Execute instruction +BPT_DEFAULT = (BPT_SOFT|BPT_EXEC); # Choose bpt type automaticaly BPTATTR_COUNT = 4 BPTATTR_FLAGS = 5 @@ -7921,7 +8017,7 @@ def AddBptEx(ea, size, bpttype): def AddBpt(ea): - return AddBptEx(ea, 0, BPT_SOFT) + return AddBptEx(ea, 0, BPT_DEFAULT) def DelBpt(ea): @@ -8019,12 +8115,13 @@ def LoadTraceFile(filename): Load a previously recorded binary trace file @param filename: trace file """ - return idaapi.load_trace_file(filename, None) + return idaapi.load_trace_file(filename) def SaveTraceFile(filename, description): """ Save current trace to a binary trace file @param filename: trace file + @param description: trace description """ return idaapi.save_trace_file(filename, description) diff --git a/pywraps.hpp b/pywraps.hpp index 815bb84..792b5c0 100644 --- a/pywraps.hpp +++ b/pywraps.hpp @@ -29,10 +29,8 @@ #define S_IDAAPI_EXECSCRIPT "IDAPython_ExecScript" #define S_IDAAPI_COMPLETION "IDAPython_Completion" #define S_IDAAPI_FORMATEXC "IDAPython_FormatExc" - -//------------------------------------------------------------------------ -// Vector of PyObject* -typedef qvector ppyobject_vec_t; +#define S_IDAAPI_LOADPROCMOD "IDAPython_LoadProcMod" +#define S_IDAAPI_UNLOADPROCMOD "IDAPython_UnLoadProcMod" //------------------------------------------------------------------------ // PyIdc conversion object IDs @@ -58,7 +56,7 @@ typedef qvector ppyobject_vec_t; #define CIP_FAILED -1 // Conversion error #define CIP_IMMUTABLE 0 // Immutable object passed. Will not update the object but no error occured #define CIP_OK 1 // Success -#define CIP_OK_NODECREF 2 // Success but do not decrement its reference +#define CIP_OK_OPAQUE 2 // Success, but the data pointed to by the PyObject* is an opaque object. //--------------------------------------------------------------------------- // Helper macro to create C counterparts of Python py_clinked_object_t object @@ -66,10 +64,12 @@ typedef qvector ppyobject_vec_t; #define DECLARE_PY_CLINKED_OBJECT(type) \ static PyObject *type##_create() \ { \ + PYW_GIL_CHECK_LOCKED_SCOPE(); \ return PyCObject_FromVoidPtr(new type(), NULL); \ } \ static bool type##_destroy(PyObject *py_obj) \ { \ + PYW_GIL_CHECK_LOCKED_SCOPE(); \ if ( !PyCObject_Check(py_obj) ) \ return false; \ delete (type *)PyCObject_AsVoidPtr(py_obj); \ @@ -77,10 +77,12 @@ typedef qvector ppyobject_vec_t; } \ static type *type##_get_clink(PyObject *self) \ { \ + PYW_GIL_CHECK_LOCKED_SCOPE(); \ return (type *)pyobj_get_clink(self); \ } \ static PyObject *type##_get_clink_ptr(PyObject *self) \ { \ + PYW_GIL_CHECK_LOCKED_SCOPE(); \ return PyLong_FromUnsignedLongLong( \ (unsigned PY_LONG_LONG)pyobj_get_clink(self)); \ } @@ -91,47 +93,183 @@ typedef qvector ppyobject_vec_t; #endif // __PYWRAPS__ //--------------------------------------------------------------------------- -class CGilStateAuto +class gil_lock_t { private: PyGILState_STATE state; public: - CGilStateAuto() + gil_lock_t() { state = PyGILState_Ensure(); } - ~CGilStateAuto() + ~gil_lock_t() { PyGILState_Release(state); } }; // Declare a variable to acquire/release the GIL -#define PYW_GIL_AUTO_ENSURE CGilStateAuto GIL_STATE_AUTO +#define PYW_GIL_GET gil_lock_t lock; -// Macros to acquire/release GIL in a given scope -#define PYW_GIL_ENSURE_N(name) PyGILState_STATE gil_state##name = PyGILState_Ensure() -#define PYW_GIL_RELEASE_N(name) PyGILState_Release(gil_state##name) +#ifdef _DEBUG +#define GIL_CHKCONDFAIL (PyGILState_GetThisThreadState() != _PyThreadState_Current) +#else +#define GIL_CHKCONDFAIL (((debug & IDA_DEBUG_PLUGIN) == IDA_DEBUG_PLUGIN) \ + && PyGILState_GetThisThreadState() != _PyThreadState_Current) +#endif + +#define PYW_GIL_CHECK_LOCKED_SCOPE() \ + do \ + { \ + if ( GIL_CHKCONDFAIL ) \ + { \ + msg("*** WARNING: Code at %s:%d should have the GIL, but apparently doesn't ***\n", \ + __FILE__, __LINE__); \ + if ( under_debugger ) \ + BPT; \ + } \ + } while ( false ) -#define PYW_GIL_ENSURE PYW_GIL_ENSURE_N(_) -#define PYW_GIL_RELEASE PYW_GIL_RELEASE_N(_) //------------------------------------------------------------------------ // All the exported functions from PyWraps are forward declared here insn_t *insn_t_get_clink(PyObject *self); op_t *op_t_get_clink(PyObject *self); -// Returns a reference to a class -PyObject *get_idaapi_attr(const char *attr); +//------------------------------------------------------------------------- +// The base for a reference. Will automatically increase the reference +// counter for the object when it is assigned from another ref_t, +// and decrease the reference counter when destroyed. +// This is meant to be used whenever possible, in order to prevent +// situations where, e.g., a given code path is taken and we return from +// a function without first decreasing the reference counter. +// +// Note: You should never, ever have to Py_[INCREF|DECREF] the 'o' object yourself. +// Note: These simple ref_t cannot be created with a PyObject* directly +// (that would be the role of 'newref_t'/'borref_t' below.) +// In other words: simple 'ref_t' instances are never created from the +// result of calling the CPython API. They are only used when in +// idapython land. +// In yet other words: the CPython API only deals in terms of +// 'New references' and 'Borrowed references'. Those are implemented, +// respectively, by the 'newref_t' and 'borref_t' classes below. +// This 'ref_t' is only used for internal handling. +struct ref_t +{ + PyObject *o; -// Returns a reference to a class by its ID -PyObject *get_idaapi_attr_by_id(const int class_id); + ref_t() : o(NULL) {} + ref_t(const ref_t &other) : o(other.o) { incref(); } + ~ref_t() { decref(); } + ref_t &operator=(const ref_t &other) + { + decref(); + o = other.o; + incref(); + return *this; + } + + void incref() const { if ( o != NULL ) Py_INCREF(o); } + void decref() const { if ( o != NULL ) Py_DECREF(o); } + + bool operator==(PyObject *other) const { return o == other; } + bool operator!=(PyObject *other) const { return ! ((*this) == other); } + + bool operator==(const ref_t &other) const { return o == other.o; } + bool operator!=(const ref_t &other) const { return ! ((*this) == other); } + + // operator PyObject *() const { return o; } + // PyObject *operator ->() const { return o; } + // PyObject &operator *() const { return *o; } + //protected: +}; + +//------------------------------------------------------------------------- +// A 'new' reference. Typically used when the CPython implementation returns +// a PyObject* whose refcnt was already increased, and that the caller is +// responsible for releasing. +// +// This implements the 'New reference' idea at http://docs.python.org/2/c-api/intro.html: +// --- +// "When a function passes ownership of a reference on to its caller, +// the caller is said to receive a new reference" +// --- +// E.g., from "PyObject_GetAttrString"'s doc: +// --- +// "Return value: New reference. +// Retrieve an attribute named attr_name from object o[...]" +// --- +struct newref_t : public ref_t +{ + newref_t(); // No. + newref_t(const newref_t &other); // No. + newref_t &operator=(const newref_t &other); // No. + newref_t(PyObject *_o) + { +#ifdef _DEBUG + QASSERT(30409, _o == NULL || _o->ob_refcnt >= 1); +#endif + o = _o; + } +}; + +//------------------------------------------------------------------------- +// A 'borrowed' reference. Typically used when the CPython implementation returns +// a PyObject* whose ownership is _not_ transferred to the caller. +// Therefore, and since the caller wants to make sure the object is not +// released while it is using it, it must first increase the reference count, +// and then decrease it. +// +// This is similar to the simpler 'ref_t' in that it first increases, and then +// decreases the reference count. The difference is that 'borref_t' instances +// can be created with a PyObject*, while 'ref_t' instances cannot (by design). +// +// This implements the 'Borrowed reference' idea at http://docs.python.org/2/c-api/intro.html: +// --- +// "When no ownership is transferred, the caller is said to borrow the reference. +// Nothing needs to be done for a borrowed reference." +// --- +struct borref_t : public ref_t +{ + borref_t(); // No. + borref_t(const newref_t &other); // No. + borref_t &operator=(const newref_t &other); // No. + borref_t(PyObject *_o) + { + o = _o; + incref(); // ~ref_t() will decref(), so we need to incref. + } +}; + + +//------------------------------------------------------------------------ +// Vector of ref_t +struct ref_vec_t : public qvector +{ + void to_pyobject_pointers(qvector *out) + { + size_t n = size(); + out->resize(n); + for ( size_t i = 0; i < n; ++i ) + out->at(i) = at(i).o; + } +}; + + +// Returns a new reference to a class +// Return value: New reference. +ref_t get_idaapi_attr(const char *attr); + +// Returns a new reference to a class by its ID +// Return value: New reference. +ref_t get_idaapi_attr_by_id(const int class_id); // Tries to import a module and swallows the exception if it fails and returns NULL -PyObject *PyW_TryImportModule(const char *name); +// Return value: New reference. +ref_t PyW_TryImportModule(const char *name); // Tries to get an attribute and swallows the exception if it fails and returns NULL -PyObject *PyW_TryGetAttrString(PyObject *py_var, const char *attr); +ref_t PyW_TryGetAttrString(PyObject *py_var, const char *attr); // Returns the linked object (void *) from a PyObject void *pyobj_get_clink(PyObject *pyobj); @@ -160,17 +298,17 @@ bool PyW_GetError(char *buf, size_t bufsz, bool clear_err = true); bool PyW_ShowCbErr(const char *cb_name); // Utility function to create a class instance whose constructor takes zero arguments -PyObject *create_idaapi_class_instance0(const char *clsname); +ref_t create_idaapi_class_instance0(const char *clsname); // Utility function to create linked class instances -PyObject *create_idaapi_linked_class_instance(const char *clsname, void *lnk); +ref_t create_idaapi_linked_class_instance(const char *clsname, void *lnk); // Returns the string representation of a PyObject bool PyW_ObjectToString(PyObject *obj, qstring *out); // Utility function to convert a python object to an IDC object // and sets a python exception on failure. -bool convert_pyobj_to_idc_exc(PyObject *py_obj, idc_value_t *idc_obj); +bool pyvar_to_idcvar_or_error(const ref_t &py_obj, idc_value_t *idc_obj); // Creates and initializes an IDC exception error_t PyW_CreateIdcException(idc_value_t *res, const char *msg); @@ -179,38 +317,38 @@ error_t PyW_CreateIdcException(idc_value_t *res, const char *msg); // Conversion functions // bool pyw_convert_idc_args( - const idc_value_t args[], - int nargs, - ppyobject_vec_t &pargs, - boolvec_t *decref, - char *errbuf = NULL, - size_t errbufsize = 0); - -void pyw_free_idc_args( - ppyobject_vec_t &pargs, - boolvec_t *decref = NULL); + const idc_value_t args[], + int nargs, + ref_vec_t &pargs, + bool as_tupple, + char *errbuf = NULL, + size_t errbufsize = 0); // Converts Python variable to IDC variable // gvar_sn is used in case the Python object was a created from a call to idcvar_to_pyvar and the IDC object was a VT_REF int pyvar_to_idcvar( - PyObject *py_var, - idc_value_t *idc_var, - int *gvar_sn = NULL); + const ref_t &py_var, + idc_value_t *idc_var, + int *gvar_sn = NULL); // Converts from IDC to Python // We support converting VT_REF IDC variable types int idcvar_to_pyvar( const idc_value_t &idc_var, - PyObject **py_var); + ref_t *py_var); // Walks a Python list or Sequence and calls the callback Py_ssize_t pyvar_walk_list( - PyObject *py_list, - int (idaapi *cb)(PyObject *py_item, Py_ssize_t index, void *ud) = NULL, - void *ud = NULL); + const ref_t &py_list, + int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud) = NULL, + void *ud = NULL); +Py_ssize_t pyvar_walk_list( + PyObject *py_list, + int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud) = NULL, + void *ud = NULL); // Converts an intvec_t to a Python list object -PyObject *PyW_IntVecToPyList(const intvec_t &intvec); +ref_t PyW_IntVecToPyList(const intvec_t &intvec); // Converts an Python list to an intvec bool PyW_PyListToIntVec(PyObject *py_list, intvec_t &intvec); @@ -233,4 +371,4 @@ bool pywraps_check_autoscripts(char *buf, size_t bufsize); bool init_pywraps(); void deinit_pywraps(); -#endif \ No newline at end of file +#endif diff --git a/pywraps/deploy.bat b/pywraps/deploy.bat index 6cf4dba..142e80b 100644 --- a/pywraps/deploy.bat +++ b/pywraps/deploy.bat @@ -1,125 +1,2 @@ @echo off - -rem Please use the same tag for the same .i file -rem That means if many insertions are going to happen in one given .i file then don't use more than code marking tag - -set PY=c:\python27\python.exe - -echo. -echo -------- DEPLOY started -------------------------------------------------- -echo. - -rem -------------------------------------------------------------------------- -echo Deploying idaapi (common functions, notifywhen) -%PY% deploy.py py_idaapi py_cvt.hpp,py_idaapi.hpp,py_idaapi.py,py_notifywhen.hpp,py_notifywhen.py ..\swig\idaapi.i - -rem -------------------------------------------------------------------------- -echo Deploying Graph -%PY% deploy.py py_graph py_graph.hpp,py_graph.py ..\swig\graph.i - -rem -------------------------------------------------------------------------- -echo Deploying custview -%PY% deploy.py py_custviewer py_custview.py,py_custview.hpp ..\swig\kernwin.i - -rem -------------------------------------------------------------------------- -echo Deploying plgform -%PY% deploy.py py_plgform py_plgform.hpp,py_plgform.py ..\swig\kernwin.i - -rem -------------------------------------------------------------------------- -echo Deploying expr -%PY% deploy.py py_expr py_expr.hpp,py_expr.py ..\swig\expr.i - -rem -------------------------------------------------------------------------- -echo Deploying cli -%PY% deploy.py py_cli py_cli.py,py_cli.hpp ..\swig\kernwin.i - -rem -------------------------------------------------------------------------- -echo Deploying Loader -%PY% deploy.py py_loader py_loader.hpp ..\swig\loader.i - -rem -------------------------------------------------------------------------- -echo Deploying kernwin, choose2, askusingform -%PY% deploy.py py_kernwin py_kernwin.hpp,py_kernwin.py,py_choose.hpp,py_choose2.hpp,py_choose2.py,py_askusingform.hpp,py_askusingform.py ..\swig\kernwin.i - -rem -------------------------------------------------------------------------- -echo Deploying idd -%PY% deploy.py py_idd py_dbg.hpp,py_appcall.py ..\swig\idd.i - -rem -------------------------------------------------------------------------- -echo Deploying nalt -%PY% deploy.py py_nalt py_nalt.hpp,py_nalt.py ..\swig\nalt.i - -rem -------------------------------------------------------------------------- -echo Deploying dbg -%PY% deploy.py py_dbg py_dbg.hpp ..\swig\dbg.i - -rem -------------------------------------------------------------------------- -echo Deploying linput/diskio -%PY% deploy.py py_diskio py_linput.hpp,py_diskio.hpp,py_diskio.py ..\swig\diskio.i - -rem -------------------------------------------------------------------------- -echo Deploying name -%PY% deploy.py py_name py_name.hpp,py_name.py ..\swig\name.i - -rem -------------------------------------------------------------------------- -echo Deploying qfile -%PY% deploy.py py_qfile py_qfile.hpp ..\swig\fpro.i - -rem -------------------------------------------------------------------------- -echo Deploying bytes -%PY% deploy.py py_bytes py_bytes.hpp,py_custdata.py,py_custdata.hpp ..\swig\bytes.i - -rem -------------------------------------------------------------------------- -echo Deploying typeinf -%PY% deploy.py py_typeinf py_typeinf.hpp ..\swig\typeinf.i - -rem -------------------------------------------------------------------------- -echo Deploying gdl -%PY% deploy.py py_gdl py_gdl.py ..\swig\gdl.i - -rem -------------------------------------------------------------------------- -echo Deploying ua -%PY% deploy.py py_ua py_ua.hpp,py_ua.py ..\swig\ua.i - -rem -------------------------------------------------------------------------- -echo Deploying idp -%PY% deploy.py py_idp py_idp.hpp ..\swig\idp.i - -rem -------------------------------------------------------------------------- -echo Deploying lines -%PY% deploy.py py_lines py_lines.hpp,py_lines.py ..\swig\lines.i - -rem -------------------------------------------------------------------------- -echo Deploying pc_win32_appcall -%PY% deploy.py appcalltest py_appcall.py ..\..\..\tests\input\pc_win32_appcall.pe.hints - -rem -------------------------------------------------------------------------- -echo Deploying ex_custdata example -%PY% deploy.py ex_custdata ..\examples\ex_custdata.py ..\..\..\tests\input\pc_win32_custdata1.pe.hints - -rem -------------------------------------------------------------------------- -echo Deploying ex_formchooser -%PY% deploy.py ex_formchooser py_askusingform.py ..\..\formchooser\formchooser.py - -rem -------------------------------------------------------------------------- -echo Deploying ex_askusingform -%PY% deploy.py ex_askusingform py_askusingform.py ..\examples\ex_askusingform.py - -rem -------------------------------------------------------------------------- -echo Deploying ex_cli example -%PY% deploy.py ex_cli_ex1 py_cli.py ..\examples\ex_cli.py - -rem -------------------------------------------------------------------------- -echo Deploying ex_expr example -%PY% deploy.py ex_expr py_expr.py ..\examples\ex_expr.py - -rem -------------------------------------------------------------------------- -echo Deploying ex_custview.py example -%PY% deploy.py py_custviewerex1 py_custview.py ..\examples\ex_custview.py - -rem -------------------------------------------------------------------------- -echo. -echo -------- DEPLOY finished ------------------------------------------------- -echo. - -:end \ No newline at end of file +c:\python27\python.exe deploy_all.py diff --git a/pywraps/deploy.py b/pywraps/deploy.py index d883ea7..ab09940 100644 --- a/pywraps/deploy.py +++ b/pywraps/deploy.py @@ -13,7 +13,13 @@ def make_re(tag, mod_name, prefix): s = '%(p)s<%(tag)s\(%(m)s\)>(.+?)%(p)s' % {'m': mod_name, 'tag': tag, 'p': prefix} return (s, re.compile(s, re.DOTALL)) +def convert_path(path_in): + parts = path_in.split('/') + return os.sep.join(parts) + def deploy(mod_name, src_files, dest_file, silent = True): + dest_file = convert_path(dest_file) + src_files = map(convert_path, src_files) # create regular expressions templates = ( ('pycode', make_re('pycode', mod_name, '#')), @@ -88,4 +94,5 @@ def main(argv = None): deploy(mod_name, src_files, dest_file) #main(['', 'py_graph', 'py_graph.hpp,py_graph.py', 'graph.i']) -main() \ No newline at end of file +if __name__ == '__main__': + main() diff --git a/pywraps/deploy_all.py b/pywraps/deploy_all.py new file mode 100644 index 0000000..c2e7dcf --- /dev/null +++ b/pywraps/deploy_all.py @@ -0,0 +1,194 @@ +# Please use the same tag for the same .i file +# That means if many insertions are going to happen in one +# given .i file then don't use more than code marking tag + +print "\n-------- DEPLOY started --------------------------------------------------\n" + +deploys = { + "idaapi (common functions, notifywhen)" : { + "tag" : "py_idaapi", + "src" : ["py_cvt.hpp", "py_idaapi.hpp", "py_idaapi.py", "py_notifywhen.hpp", "py_notifywhen.py"], + "tgt" : "../swig/idaapi.i" + }, + + "View (common)" : { + "tag" : "py_view_base", + "src" : ["py_view_base.hpp", "py_view_base.py"], + "tgt" : "../swig/view.i" + }, + + "IDAView" : { + "tag" : "py_idaview", + "src" : ["py_idaview.hpp", "py_idaview.py"], + "tgt" : "../swig/view.i" + }, + + "Graph" : { + "tag" : "py_graph", + "src" : ["py_graph.hpp", "py_graph.py"], + "tgt" : "../swig/graph.i" + }, + + "custview" : { + "tag" : "py_custviewer", + "src" : ["py_custview.py","py_custview.hpp"], + "tgt" : "../swig/kernwin.i" + }, + + "plgform" : { + "tag" : "py_plgform", + "src" : ["py_plgform.hpp","py_plgform.py"], + "tgt" : "../swig/kernwin.i" + }, + + "expr" : { + "tag" : "py_expr", + "src" : ["py_expr.hpp","py_expr.py"], + "tgt" : "../swig/expr.i" + }, + + "cli" : { + "tag" : "py_cli", + "src" : ["py_cli.py","py_cli.hpp"], + "tgt" : "../swig/kernwin.i" + }, + + "Loader" : { + "tag" : "py_loader", + "src" : ["py_loader.hpp"], + "tgt" : "../swig/loader.i" + }, + + "kernwin, choose2, askusingform" : { + "tag" : "py_kernwin", + "src" : ["py_kernwin.hpp","py_kernwin.py","py_choose.hpp","py_choose2.hpp","py_choose2.py","py_askusingform.hpp","py_askusingform.py"], + "tgt" : "../swig/kernwin.i" + }, + + "idd" : { + "tag" : "py_idd", + "src" : ["py_dbg.hpp","py_appcall.py"], + "tgt" : "../swig/idd.i" + }, + + "idd (python)" : { + "tag" : "py_idd_2", + "src" : ["py_dbg.py"], + "tgt" : "../swig/idd.i" + }, + + "nalt" : { + "tag" : "py_nalt", + "src" : ["py_nalt.hpp","py_nalt.py"], + "tgt" : "../swig/nalt.i" + }, + + "dbg" : { + "tag" : "py_dbg", + "src" : ["py_dbg.hpp"], + "tgt" : "../swig/dbg.i" + }, + + "linput/diskio" : { + "tag" : "py_diskio", + "src" : ["py_linput.hpp","py_diskio.hpp","py_diskio.py"], + "tgt" : "../swig/diskio.i" + }, + + "name" : { + "tag" : "py_name", + "src" : ["py_name.hpp","py_name.py"], + "tgt" : "../swig/name.i" + }, + + "qfile" : { + "tag" : "py_qfile", + "src" : ["py_qfile.hpp"], + "tgt" : "../swig/fpro.i" + }, + + "bytes" : { + "tag" : "py_bytes", + "src" : ["py_bytes.hpp","py_custdata.py","py_custdata.hpp"], + "tgt" : "../swig/bytes.i" + }, + + "typeinf" : { + "tag" : "py_typeinf", + "src" : ["py_typeinf.hpp","py_typeinf.py"], + "tgt" : "../swig/typeinf.i" + }, + + "gdl" : { + "tag" : "py_gdl", + "src" : ["py_gdl.py"], + "tgt" : "../swig/gdl.i" + }, + + "ua" : { + "tag" : "py_ua", + "src" : ["py_ua.hpp","py_ua.py"], + "tgt" : "../swig/ua.i" + }, + + "idp" : { + "tag" : "py_idp", + "src" : ["py_idp.hpp"], + "tgt" : "../swig/idp.i" + }, + + "lines" : { + "tag" : "py_lines", + "src" : ["py_lines.hpp","py_lines.py"], + "tgt" : "../swig/lines.i" + }, + + "pc_win32_appcall" : { + "tag" : "appcalltest", + "src" : ["py_appcall.py"], + "tgt" : "../../../tests/input/pc_win32_appcall.pe.hints" + }, + + "ex_custdata example" : { + "tag" : "ex_custdata", + "src" : ["../examples/ex_custdata.py"], + "tgt" : "../../../tests/input/pc_win32_custdata1.pe.hints" + }, + + "ex_formchooser" : { + "tag" : "ex_formchooser", + "src" : ["py_askusingform.py"], + "tgt" : "../../formchooser/formchooser.py" + }, + + "ex_askusingform" : { + "tag" : "ex_askusingform", + "src" : ["py_askusingform.py"], + "tgt" : "../examples/ex_askusingform.py" + }, + + "ex_cli example" : { + "tag" : "ex_cli_ex1", + "src" : ["py_cli.py"], + "tgt" : "../examples/ex_cli.py" + }, + + "ex_expr example" : { + "tag" : "ex_expr", + "src" : ["py_expr.py"], + "tgt" : "../examples/ex_expr.py" + }, + + "ex_custview.py example" : { + "tag" : "py_custviewerex1", + "src" : ["py_custview.py"], + "tgt" : "../examples/ex_custview.py" + } + } + +import deploy +for name in deploys: + data = deploys[name] + print "Deploying %s" % name + deploy.deploy(data["tag"], data["src"], data["tgt"]) + diff --git a/pywraps/driver_dbg.cpp b/pywraps/driver_dbg.cpp index 70704be..8fbe0de 100644 --- a/pywraps/driver_dbg.cpp +++ b/pywraps/driver_dbg.cpp @@ -58,7 +58,8 @@ static PyObject *ex_pytoidc( return NULL; idc_value_t v; int sn = 0; - if ( pyvar_to_idcvar(self, &v, &sn) < CIP_OK ) + borref_t self_ref(self); + if ( pyvar_to_idcvar(self_ref, &v, &sn) < CIP_OK ) Py_RETURN_NONE; Py_RETURN_TRUE; } @@ -75,4 +76,4 @@ static PyMethodDef py_methods_dbg[] = {"pytoidc", ex_pytoidc, METH_VARARGS, ""}, {NULL, NULL, 0, NULL} /* Sentinel */ }; -DRIVER_INIT_METHODS(dbg); \ No newline at end of file +DRIVER_INIT_METHODS(dbg); diff --git a/pywraps/py_appcall.py b/pywraps/py_appcall.py index ac6ca1a..f407964 100644 --- a/pywraps/py_appcall.py +++ b/pywraps/py_appcall.py @@ -162,7 +162,7 @@ class Appcall_callable__(object): def __get_size(self): if self.__type == None: return -1 - r = _idaapi.get_type_size0(_idaapi.cvar.idati, self.__type) + r = _idaapi.calc_type_size(_idaapi.cvar.idati, self.__type) if not r: return -1 return r @@ -658,6 +658,46 @@ def test_pck_bv(): return 1 # ----------------------------------------------------------------------- +def test_local_types(): + (type, fields) = GetLocalTinfo(1) + if not type: + return -1 + decl = GetLocalType(1, PRTYPE_MULTI) + if decl != "enum\n"\ + + "{\n"\ + + " FEATURE_OBJECT_CACHING = 0x0,\n"\ + + " FEATURE_ZONE_ELEVATION = 0x1,\n"\ + + " FEATURE_MIME_HANDLING = 0x2,\n"\ + + " FEATURE_MIME_SNIFFING = 0x3,\n"\ + + " FEATURE_WINDOW_RESTRICTIONS = 0x4,\n"\ + + " FEATURE_WEBOC_POPUPMANAGEMENT = 0x5,\n"\ + + " FEATURE_BEHAVIORS = 0x6,\n"\ + + " FEATURE_DISABLE_MK_PROTOCOL = 0x7,\n"\ + + " FEATURE_LOCALMACHINE_LOCKDOWN = 0x8,\n"\ + + " FEATURE_SECURITYBAND = 0x9,\n"\ + + " FEATURE_RESTRICT_ACTIVEXINSTALL = 0xA,\n"\ + + " FEATURE_VALIDATE_NAVIGATE_URL = 0xB,\n"\ + + " FEATURE_RESTRICT_FILEDOWNLOAD = 0xC,\n"\ + + " FEATURE_ADDON_MANAGEMENT = 0xD,\n"\ + + " FEATURE_PROTOCOL_LOCKDOWN = 0xE,\n"\ + + " FEATURE_HTTP_USERNAME_PASSWORD_DISABLE = 0xF,\n"\ + + " FEATURE_SAFE_BINDTOOBJECT = 0x10,\n"\ + + " FEATURE_UNC_SAVEDFILECHECK = 0x11,\n"\ + + " FEATURE_GET_URL_DOM_FILEPATH_UNENCODED = 0x12,\n"\ + + " FEATURE_TABBED_BROWSING = 0x13,\n"\ + + " FEATURE_SSLUX = 0x14,\n"\ + + " FEATURE_DISABLE_NAVIGATION_SOUNDS = 0x15,\n"\ + + " FEATURE_DISABLE_LEGACY_COMPRESSION = 0x16,\n"\ + + " FEATURE_FORCE_ADDR_AND_STATUS = 0x17,\n"\ + + " FEATURE_XMLHTTP = 0x18,\n"\ + + " FEATURE_DISABLE_TELNET_PROTOCOL = 0x19,\n"\ + + " FEATURE_FEEDS = 0x1A,\n"\ + + " FEATURE_BLOCK_INPUT_PROMPTS = 0x1B,\n"\ + + " FEATURE_ENTRY_COUNT = 0x1C,\n"\ + + "} _tagINTERNETFEATURELIST\n": + print "decl = " + decl + return -2 + return 1 # various tests def test1(stage): # call a method that takes a string buffer and appends a dot to its end @@ -891,7 +931,7 @@ def test_exec_throw(): # ----------------------------------------------------------------------- # all the tests that take zero parameters tests0 = (test_gpa, test_pck_idb_raw, test_pck_bv_raw, - test_unpack_raw, test_pck_idb, test_pck_bv, + test_unpack_raw, test_pck_idb, test_pck_bv, test_local_types, test_enum_files, test2, test_exec_throw, test_loaddll) test_log = None # test log file diff --git a/pywraps/py_askusingform.hpp b/pywraps/py_askusingform.hpp index 7729af3..3de1233 100644 --- a/pywraps/py_askusingform.hpp +++ b/pywraps/py_askusingform.hpp @@ -142,6 +142,7 @@ static PyObject *formchgcbfa_get_field_value( size_t sz) { DECLARE_FORM_ACTIONS; + PYW_GIL_CHECK_LOCKED_SCOPE(); switch ( ft ) { case 8: @@ -213,7 +214,9 @@ static PyObject *formchgcbfa_get_field_value( for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) (*it)--; - return PyW_IntVecToPyList(intvec); + ref_t l(PyW_IntVecToPyList(intvec)); + l.incref(); + return l.o; } break; } @@ -276,6 +279,7 @@ static bool formchgcbfa_set_field_value( PyObject *py_val) { DECLARE_FORM_ACTIONS; + PYW_GIL_CHECK_LOCKED_SCOPE(); switch ( ft ) { diff --git a/pywraps/py_bytes.hpp b/pywraps/py_bytes.hpp index ce0d486..485a8fb 100644 --- a/pywraps/py_bytes.hpp +++ b/pywraps/py_bytes.hpp @@ -5,20 +5,17 @@ //------------------------------------------------------------------------ static bool idaapi py_testf_cb(flags_t flags, void *ud) { - PyObject *py_flags = PyLong_FromUnsignedLong(flags); - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallFunctionObjArgs((PyObject *) ud, py_flags, NULL); - PYW_GIL_RELEASE; - bool ret = result != NULL && PyObject_IsTrue(result); - Py_XDECREF(result); - Py_XDECREF(py_flags); - return ret; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_flags(PyLong_FromUnsignedLong(flags)); + newref_t result(PyObject_CallFunctionObjArgs((PyObject *) ud, py_flags.o, NULL)); + return result != NULL && PyObject_IsTrue(result.o); } //------------------------------------------------------------------------ // Wraps the (next|prev)that() static ea_t py_npthat(ea_t ea, ea_t bound, PyObject *py_callable, bool next) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCallable_Check(py_callable) ) return BADADDR; else @@ -33,20 +30,17 @@ static int idaapi py_visit_patched_bytes_cb( uint32 v, void *ud) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallFunction( - (PyObject *)ud, - PY_FMT64 "iII", - pyul_t(ea), - fpos, - o, - v); - PYW_GIL_RELEASE; - + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallFunction( + (PyObject *)ud, + PY_FMT64 "iII", + pyul_t(ea), + fpos, + o, + v)); PyW_ShowCbErr("visit_patched_bytes"); - int ret = (py_result != NULL && PyInt_Check(py_result)) ? PyInt_AsLong(py_result) : 0; - Py_XDECREF(py_result); - return ret; + return (py_result != NULL && PyInt_Check(py_result.o)) ? PyInt_AsLong(py_result.o) : 0; } // //------------------------------------------------------------------------ @@ -74,6 +68,7 @@ def visit_patched_bytes(ea1, ea2, callable): */ static int py_visit_patched_bytes(ea_t ea1, ea_t ea2, PyObject *py_callable) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCallable_Check(py_callable) ) return 0; else @@ -122,29 +117,23 @@ def get_many_bytes(ea, size): */ static PyObject *py_get_many_bytes(ea_t ea, unsigned int size) { + PYW_GIL_CHECK_LOCKED_SCOPE(); do { if ( size <= 0 ) break; // Allocate memory via Python - PyObject *py_buf = PyString_FromStringAndSize(NULL, Py_ssize_t(size)); + newref_t py_buf(PyString_FromStringAndSize(NULL, Py_ssize_t(size))); if ( py_buf == NULL ) break; // Read bytes - bool ok = get_many_bytes(ea, PyString_AsString(py_buf), size); + if ( !get_many_bytes(ea, PyString_AsString(py_buf.o), size) ) + Py_RETURN_NONE; - // If failed, dispose the Python string - if ( !ok ) - { - Py_DECREF(py_buf); - - py_buf = Py_None; - Py_INCREF(py_buf); - } - - return py_buf; + py_buf.incref(); + return py_buf.o; } while ( false ); Py_RETURN_NONE; } @@ -195,9 +184,11 @@ static PyObject *py_get_ascii_contents2( } if ( type == ASCSTR_C && used_size > 0 && buf[used_size-1] == '\0' ) used_size--; - PyObject *py_buf = PyString_FromStringAndSize((const char *)buf, used_size); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_buf(PyString_FromStringAndSize((const char *)buf, used_size)); qfree(buf); - return py_buf; + py_buf.incref(); + return py_buf.o; } //--------------------------------------------------------------------------- /* diff --git a/pywraps/py_choose.hpp b/pywraps/py_choose.hpp index 6cecdc1..2ac54b6 100644 --- a/pywraps/py_choose.hpp +++ b/pywraps/py_choose.hpp @@ -6,45 +6,35 @@ //--------------------------------------------------------------------------- uint32 idaapi choose_sizer(void *self) { - PyObject *pyres; - uint32 res; - - PYW_GIL_ENSURE; - pyres = PyObject_CallMethod((PyObject *)self, "sizer", ""); - PYW_GIL_RELEASE; - - res = PyInt_AsLong(pyres); - Py_DECREF(pyres); - return res; + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod((PyObject *)self, "sizer", "")); + return PyInt_AsLong(pyres.o); } //--------------------------------------------------------------------------- char *idaapi choose_getl(void *self, uint32 n, char *buf) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - (PyObject *)self, - "getl", - "l", - n); - PYW_GIL_RELEASE; + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + (PyObject *)self, + "getl", + "l", + n)); const char *res; - if (pyres == NULL || (res = PyString_AsString(pyres)) == NULL ) + if (pyres == NULL || (res = PyString_AsString(pyres.o)) == NULL ) qstrncpy(buf, "", MAXSTR); else qstrncpy(buf, res, MAXSTR); - - Py_XDECREF(pyres); return buf; } //--------------------------------------------------------------------------- void idaapi choose_enter(void *self, uint32 n) { - PYW_GIL_ENSURE; - Py_XDECREF(PyObject_CallMethod((PyObject *)self, "enter", "l", n)); - PYW_GIL_RELEASE; + PYW_GIL_GET; + newref_t res(PyObject_CallMethod((PyObject *)self, "enter", "l", n)); } //--------------------------------------------------------------------------- @@ -57,8 +47,9 @@ uint32 choose_choose( int deflt, int icon) { - PyObject *pytitle = PyObject_GetAttrString((PyObject *)self, "title"); - const char *title = pytitle != NULL ? PyString_AsString(pytitle) : "Choose"; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t pytitle(PyObject_GetAttrString((PyObject *)self, "title")); + const char *title = pytitle != NULL ? PyString_AsString(pytitle.o) : "Choose"; int r = choose( flags, @@ -79,9 +70,9 @@ uint32 choose_choose( NULL, /* destroy */ NULL, /* popup_names */ NULL);/* get_icon */ - Py_XDECREF(pytitle); + return r; } // -#endif // __PY_CHOOSE__ \ No newline at end of file +#endif // __PY_CHOOSE__ diff --git a/pywraps/py_choose2.hpp b/pywraps/py_choose2.hpp index aa886a9..9fa8846 100644 --- a/pywraps/py_choose2.hpp +++ b/pywraps/py_choose2.hpp @@ -11,8 +11,8 @@ #define thisdecl py_choose2_t *_this = thisobj #define MENU_COMMAND_CB(id) \ static uint32 idaapi s_menu_command_##id(void *obj, uint32 n) \ - { \ - return thisobj->on_command(id, int(n)); \ + { \ + return thisobj->on_command(id, int(n)); \ } //------------------------------------------------------------------------ @@ -100,6 +100,9 @@ private: //------------------------------------------------------------------------ static int idaapi ui_cb(void *obj, int notification_code, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + // UI callback to handle chooser items with attributes if ( notification_code != ui_get_chooser_item_attrs ) return 0; @@ -209,6 +212,9 @@ private: void on_get_line(int lineno, char * const *line_arr) { + // Called from s_getl, which itself can be called from the kernel. Ensure GIL + PYW_GIL_GET; + // Get headers? if ( lineno == 0 ) { @@ -224,64 +230,51 @@ private: line_arr[i][0] = '\0'; // Call Python - PYW_GIL_ENSURE; - PyObject *list = PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1); - PYW_GIL_RELEASE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t list(PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1)); if ( list == NULL ) return; // Go over the List returned by Python and convert to C strings for ( int i=ncols-1; i>=0; i-- ) { - PyObject *item = PyList_GetItem(list, Py_ssize_t(i)); + borref_t item(PyList_GetItem(list.o, Py_ssize_t(i))); if ( item == NULL ) continue; - const char *str = PyString_AsString(item); + const char *str = PyString_AsString(item.o); if ( str != NULL ) qstrncpy(line_arr[i], str, MAXSTR); } - Py_DECREF(list); } size_t on_get_size() { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL); - PYW_GIL_RELEASE; + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL)); if ( pyres == NULL ) return 0; - size_t res = PyInt_AsLong(pyres); - Py_DECREF(pyres); - return res; + return PyInt_AsLong(pyres.o); } void on_refreshed() { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_REFRESHED, NULL); - PYW_GIL_RELEASE; - Py_XDECREF(pyres); + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_REFRESHED, NULL)); } void on_select(const intvec_t &intvec) { - PYW_GIL_ENSURE; - PyObject *py_list = PyW_IntVecToPyList(intvec); - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_SELECT, "O", py_list); - PYW_GIL_RELEASE; - Py_XDECREF(pyres); - Py_XDECREF(py_list); + PYW_GIL_GET; + ref_t py_list(PyW_IntVecToPyList(intvec)); + newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_SELECT, "O", py_list.o)); } void on_close() { - // Call Python - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL); - PYW_GIL_RELEASE; - Py_XDECREF(pyres); + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL)); // Delete this instance if none modal and not embedded if ( !is_modal() && get_embedded() == NULL ) @@ -290,123 +283,96 @@ private: int on_delete_line(int lineno) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - self, - (char *)S_ON_DELETE_LINE, - "i", - lineno - 1); - PYW_GIL_RELEASE; - - if ( pyres == NULL ) - return lineno; - - size_t res = PyInt_AsLong(pyres); - Py_DECREF(pyres); - return res + 1; + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_DELETE_LINE, + "i", + lineno - 1)); + return pyres == NULL ? lineno : PyInt_AsLong(pyres.o) + 1; } int on_refresh(int lineno) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - self, - (char *)S_ON_REFRESH, - "i", - lineno - 1); - PYW_GIL_RELEASE; - if ( pyres == NULL ) - return lineno; - - size_t res = PyInt_AsLong(pyres); - Py_DECREF(pyres); - return res + 1; + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_REFRESH, + "i", + lineno - 1)); + return pyres == NULL ? lineno : PyInt_AsLong(pyres.o) + 1; } void on_insert_line() { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_INSERT_LINE, NULL); - PYW_GIL_RELEASE; - Py_XDECREF(pyres); + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_INSERT_LINE, NULL)); } void on_enter(int lineno) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - self, - (char *)S_ON_SELECT_LINE, - "i", - lineno - 1); - PYW_GIL_RELEASE; - Py_XDECREF(pyres); + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_SELECT_LINE, + "i", + lineno - 1)); } void on_edit_line(int lineno) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - self, - (char *)S_ON_EDIT_LINE, - "i", - lineno - 1); - PYW_GIL_RELEASE; - Py_XDECREF(pyres); + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_EDIT_LINE, + "i", + lineno - 1)); } int on_command(int cmd_id, int lineno) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - self, - (char *)S_ON_COMMAND, - "ii", - lineno - 1, - cmd_id); - PYW_GIL_RELEASE; - - if ( pyres==NULL ) - return lineno; - - size_t res = PyInt_AsLong(pyres); - Py_XDECREF(pyres); - return res; + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_COMMAND, + "ii", + lineno - 1, + cmd_id)); + return pyres == NULL ? lineno : PyInt_AsLong(pyres.o); } int on_get_icon(int lineno) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - self, - (char *)S_ON_GET_ICON, - "i", - lineno - 1); - PYW_GIL_RELEASE; - - size_t res = PyInt_AsLong(pyres); - Py_XDECREF(pyres); - return res; + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_GET_ICON, + "i", + lineno - 1)); + return PyInt_AsLong(pyres.o); } void on_get_line_attr(int lineno, chooser_item_attrs_t *attr) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1); - PYW_GIL_RELEASE; - - if ( pyres == NULL ) - return; - - if ( PyList_Check(pyres) ) + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1)); + if ( pyres != NULL ) { - PyObject *item; - if ( (item = PyList_GetItem(pyres, 0)) != NULL ) - attr->color = PyInt_AsLong(item); - if ( (item = PyList_GetItem(pyres, 1)) != NULL ) - attr->flags = PyInt_AsLong(item); + if ( PyList_Check(pyres.o) ) + { + PyObject *item; + if ( (item = PyList_GetItem(pyres.o, 0)) != NULL ) + attr->color = PyInt_AsLong(item); + if ( (item = PyList_GetItem(pyres.o, 1)) != NULL ) + attr->flags = PyInt_AsLong(item); + } } - Py_XDECREF(pyres); } public: @@ -496,7 +462,7 @@ public: // Adjust the title ptitle = title_buf; - + // Adjust the caption p = strtok(NULL, delimiter); caption += (p - temp); @@ -507,12 +473,12 @@ public: } if ( !add_chooser_command( - ptitle, - caption, - menu_cbs[menu_cb_idx], - menu_index, - icon, - flags)) + ptitle, + caption, + menu_cbs[menu_cb_idx], + menu_index, + icon, + flags)) { return -1; } @@ -525,83 +491,71 @@ public: // Otherwise the chooser window is created and displayed int create(PyObject *self) { - PyObject *attr; + PYW_GIL_CHECK_LOCKED_SCOPE(); // Get flags - attr = PyW_TryGetAttrString(self, S_FLAGS); - if ( attr == NULL ) + ref_t flags_attr(PyW_TryGetAttrString(self, S_FLAGS)); + if ( flags_attr == NULL ) return -1; - - flags = PyInt_Check(attr) != 0 ? PyInt_AsLong(attr) : 0; - Py_DECREF(attr); + flags = PyInt_Check(flags_attr.o) != 0 ? PyInt_AsLong(flags_attr.o) : 0; // Get the title if ( !PyW_GetStringAttr(self, S_TITLE, &title) ) return -1; // Get columns - attr = PyW_TryGetAttrString(self, "cols"); - if ( attr == NULL ) + ref_t cols_attr(PyW_TryGetAttrString(self, "cols")); + if ( cols_attr == NULL ) return -1; // Get col count - int ncols = int(PyList_Size(attr)); + int ncols = int(PyList_Size(cols_attr.o)); // Get cols caption and widthes cols.qclear(); for ( int i=0; irefresh = NULL; } } - Py_XDECREF(attr); // Create the chooser (if not embedded) int r; @@ -873,6 +823,8 @@ void choose2_activate(PyObject *self) //------------------------------------------------------------------------ PyObject *choose2_get_embedded_selection(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_choose2_t *c2 = choose2_find_instance(self); chooser_info_t *embedded; @@ -886,13 +838,17 @@ PyObject *choose2_get_embedded_selection(PyObject *self) for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) (*it)--; - return PyW_IntVecToPyList(intvec); + ref_t ret(PyW_IntVecToPyList(intvec)); + ret.incref(); + return ret.o; } //------------------------------------------------------------------------ // Return the C instances as 64bit numbers PyObject *choose2_get_embedded(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_choose2_t *c2 = choose2_find_instance(self); chooser_info_t *embedded; @@ -945,12 +901,12 @@ PyObject *choose2_get_embedded_selection(PyObject *self); static void NT_CDECL choose2_test_embedded(chooser_info_t *embedded) { - msg("cb=%d -> looks %valid\n", - embedded->cb, + msg("cb=%d -> looks %valid\n", + embedded->cb, embedded->cb == sizeof(chooser_info_t) ? "" : "in"); } static size_t choose2_get_test_embedded() { return (size_t)choose2_test_embedded; } -#endif // __PY_CHOOSE2__ \ No newline at end of file +#endif // __PY_CHOOSE2__ diff --git a/pywraps/py_cli.hpp b/pywraps/py_cli.hpp index 1250d33..7c24192 100644 --- a/pywraps/py_cli.hpp +++ b/pywraps/py_cli.hpp @@ -37,17 +37,17 @@ private: static py_cli_t *py_clis[MAX_PY_CLI]; static const py_cli_cbs_t py_cli_cbs[MAX_PY_CLI]; //-------------------------------------------------------------------------- -#define IMPL_PY_CLI_CB(CBN) \ +#define IMPL_PY_CLI_CB(CBN) \ static bool idaapi s_keydown##CBN(qstring *line, int *p_x, int *p_sellen, int *vk_key, int shift) \ - { \ + { \ return py_clis[CBN]->on_keydown(line, p_x, p_sellen, vk_key, shift); \ - } \ - static bool idaapi s_execute_line##CBN(const char *line) \ - { \ - return py_clis[CBN]->on_execute_line(line); \ - } \ + } \ + static bool idaapi s_execute_line##CBN(const char *line) \ + { \ + return py_clis[CBN]->on_execute_line(line); \ + } \ static bool idaapi s_complete_line##CBN(qstring *completion, const char *prefix, int n, const char *line, int x) \ - { \ + { \ return py_clis[CBN]->on_complete_line(completion, prefix, n, line, x); \ } @@ -62,18 +62,15 @@ private: // Returns: true-executed line, false-ask for more lines bool on_execute_line(const char *line) { - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_EXECUTE_LINE, - "s", - line); - PYW_GIL_RELEASE; - - bool ok = result != NULL && PyObject_IsTrue(result); + PYW_GIL_GET; + newref_t result( + PyObject_CallMethod( + self, + (char *)S_ON_EXECUTE_LINE, + "s", + line)); PyW_ShowCbErr(S_ON_EXECUTE_LINE); - Py_XDECREF(result); - return ok; + return result != NULL && PyObject_IsTrue(result.o); } //-------------------------------------------------------------------------- @@ -95,41 +92,45 @@ private: int *vk_key, int shift) { - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_KEYDOWN, - "siiHi", - line->c_str(), - *p_x, - *p_sellen, - *vk_key, - shift); - PYW_GIL_RELEASE; + PYW_GIL_GET; + newref_t result( + PyObject_CallMethod( + self, + (char *)S_ON_KEYDOWN, + "siiHi", + line->c_str(), + *p_x, + *p_sellen, + *vk_key, + shift)); - bool ok = result != NULL && PyTuple_Check(result); + bool ok = result != NULL && PyTuple_Check(result.o); PyW_ShowCbErr(S_ON_KEYDOWN); if ( ok ) { - Py_ssize_t sz = PyTuple_Size(result); + Py_ssize_t sz = PyTuple_Size(result.o); PyObject *item; - - if ( sz > 0 && (item = PyTuple_GetItem(result, 0)) != NULL && PyString_Check(item) ) - *line = PyString_AsString(item); - - if ( sz > 1 && (item = PyTuple_GetItem(result, 1)) != NULL && PyInt_Check(item) ) - *p_x = PyInt_AsLong(item); - - if ( sz > 2 && (item = PyTuple_GetItem(result, 2)) != NULL && PyInt_Check(item) ) - *p_sellen = PyInt_AsLong(item); - if ( sz > 3 && (item = PyTuple_GetItem(result, 3)) != NULL && PyInt_Check(item) ) - *vk_key = PyInt_AsLong(item) & 0xffff; +#define GET_TUPLE_ENTRY(col, PyThingy, AsThingy, out) \ + do \ + { \ + if ( sz > col ) \ + { \ + borref_t _r(PyTuple_GetItem(result.o, col)); \ + if ( _r != NULL && PyThingy##_Check(_r.o) ) \ + *out = PyThingy##_##AsThingy(_r.o); \ + } \ + } while ( false ) + + GET_TUPLE_ENTRY(0, PyString, AsString, line); + GET_TUPLE_ENTRY(1, PyInt, AsLong, p_x); + GET_TUPLE_ENTRY(2, PyInt, AsLong, p_sellen); + GET_TUPLE_ENTRY(3, PyInt, AsLong, vk_key); + *vk_key &= 0xffff; +#undef GET_TUPLE_ENTRY } - - Py_XDECREF(result); return ok; } @@ -140,41 +141,41 @@ private: // Returns: true if generated a new completion // This callback is optional bool on_complete_line( - qstring *completion, - const char *prefix, - int n, - const char *line, - int x) + qstring *completion, + const char *prefix, + int n, + const char *line, + int x) { - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_COMPLETE_LINE, - "sisi", - prefix, - n, - line, - x); - PYW_GIL_RELEASE; - - bool ok = result != NULL && PyString_Check(result); + PYW_GIL_GET; + newref_t result( + PyObject_CallMethod( + self, + (char *)S_ON_COMPLETE_LINE, + "sisi", + prefix, + n, + line, + x)); + + bool ok = result != NULL && PyString_Check(result.o); PyW_ShowCbErr(S_ON_COMPLETE_LINE); if ( ok ) - *completion = PyString_AsString(result); - - Py_XDECREF(result); + *completion = PyString_AsString(result.o); return ok; } // Private ctor (use bind()) - py_cli_t() - { + py_cli_t() + { } public: //--------------------------------------------------------------------------- static int bind(PyObject *py_obj) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int cli_idx; // Find an empty slot for ( cli_idx = 0; cli_idx < MAX_PY_CLI; ++cli_idx ) @@ -183,7 +184,7 @@ public: break; } py_cli_t *py_cli = NULL; - do + do { // No free slots? if ( cli_idx >= MAX_PY_CLI ) @@ -197,14 +198,12 @@ public: py_cli->cli.size = sizeof(cli_t); // Store 'flags' - if ( (attr = PyW_TryGetAttrString(py_obj, S_FLAGS)) == NULL ) { - py_cli->cli.flags = 0; - } - else - { - py_cli->cli.flags = PyLong_AsLong(attr); - Py_DECREF(attr); + ref_t flags_attr(PyW_TryGetAttrString(py_obj, S_FLAGS)); + if ( flags_attr == NULL ) + py_cli->cli.flags = 0; + else + py_cli->cli.flags = PyLong_AsLong(flags_attr.o); } // Store 'sname' @@ -256,9 +255,12 @@ public: py_cli_t *py_cli = py_clis[cli_idx]; remove_command_interpreter(&py_cli->cli); - - Py_DECREF(py_cli->self); - delete py_cli; + + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_DECREF(py_cli->self); + delete py_cli; + } py_clis[cli_idx] = NULL; @@ -280,12 +282,12 @@ const py_cli_cbs_t py_cli_t::py_cli_cbs[MAX_PY_CLI] = // static int py_install_command_interpreter(PyObject *py_obj) -{ +{ return py_cli_t::bind(py_obj); } static void py_remove_command_interpreter(int cli_idx) -{ +{ py_cli_t::unbind(cli_idx); } // diff --git a/pywraps/py_custdata.hpp b/pywraps/py_custdata.hpp index 984256c..6f4bbec 100644 --- a/pywraps/py_custdata.hpp +++ b/pywraps/py_custdata.hpp @@ -18,20 +18,18 @@ class py_custom_data_type_t size_t nbytes) // size of the future item { py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; - - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_self, - (char *)S_MAY_CREATE_AT, - PY_FMT64 PY_FMT64, - pyul_t(ea), - pyul_t(nbytes)); - PYW_GIL_RELEASE; + + PYW_GIL_GET; + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_MAY_CREATE_AT, + PY_FMT64 PY_FMT64, + pyul_t(ea), + pyul_t(nbytes))); PyW_ShowCbErr(S_MAY_CREATE_AT); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; + return py_result != NULL && PyObject_IsTrue(py_result.o); } // !=NULL means variable size datatype @@ -42,24 +40,23 @@ class py_custom_data_type_t ea_t ea, // address of the item asize_t maxsize) // maximal size of the item { + PYW_GIL_GET; // Returns: 0-no such item can be created/displayed // this callback is required only for varsize datatypes py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_self, - (char *)S_CALC_ITEM_SIZE, - PY_FMT64 PY_FMT64, - pyul_t(ea), - pyul_t(maxsize)); - PYW_GIL_RELEASE; - + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_CALC_ITEM_SIZE, + PY_FMT64 PY_FMT64, + pyul_t(ea), + pyul_t(maxsize))); + if ( PyW_ShowCbErr(S_CALC_ITEM_SIZE) || py_result == NULL ) return 0; - + uint64 num = 0; - PyW_GetNumber(py_result, &num); - Py_XDECREF(py_result); + PyW_GetNumber(py_result.o, &num); return asize_t(num); } @@ -77,6 +74,8 @@ public: int register_dt(PyObject *py_obj) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Already registered? if ( dtid >= 0 ) return dtid; @@ -85,9 +84,10 @@ public: dt.cbsize = sizeof(dt); dt.ud = this; - PyObject *py_attr = NULL; do { + ref_t py_attr; + // name if ( !PyW_GetStringAttr(py_obj, S_NAME, &dt_name) ) break; @@ -108,30 +108,27 @@ public: // value_size py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); - if ( py_attr != NULL && PyInt_Check(py_attr) ) - dt.value_size = PyInt_AsLong(py_attr); - Py_XDECREF(py_attr); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + dt.value_size = PyInt_AsLong(py_attr.o); + py_attr = ref_t(); // props py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); - if ( py_attr != NULL && PyInt_Check(py_attr) ) - dt.props = PyInt_AsLong(py_attr); - Py_XDECREF(py_attr); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + dt.props = PyInt_AsLong(py_attr.o); + py_attr = ref_t(); // may_create_at py_attr = PyW_TryGetAttrString(py_obj, S_MAY_CREATE_AT); - if ( py_attr != NULL && PyCallable_Check(py_attr) ) + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) dt.may_create_at = s_may_create_at; - Py_XDECREF(py_attr); + py_attr = ref_t(); // calc_item_size py_attr = PyW_TryGetAttrString(py_obj, S_CALC_ITEM_SIZE); - if ( py_attr != NULL && PyCallable_Check(py_attr) ) + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) dt.calc_item_size = s_calc_item_size; - Py_XDECREF(py_attr); - - // Clear attribute - py_attr = NULL; + py_attr = ref_t(); // Now try to register dtid = register_custom_data_type(&dt); @@ -142,20 +139,16 @@ public: Py_INCREF(py_obj); py_self = py_obj; - py_attr = PyInt_FromLong(dtid); - PyObject_SetAttrString(py_obj, S_ID, py_attr); - Py_DECREF(py_attr); - - // Done with attribute - py_attr = NULL; + py_attr = newref_t(PyInt_FromLong(dtid)); + PyObject_SetAttrString(py_obj, S_ID, py_attr.o); } while ( false ); - - Py_XDECREF(py_attr); return dtid; } bool unregister_dt() { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( dtid < 0 ) return true; @@ -195,44 +188,41 @@ private: int operand_num, // current operand number int dtid) // custom data type id { + PYW_GIL_GET; + // Build a string from the buffer - PyObject *py_value = PyString_FromStringAndSize( - (const char *)value, - Py_ssize_t(size)); + newref_t py_value(PyString_FromStringAndSize( + (const char *)value, + Py_ssize_t(size))); if ( py_value == NULL ) return false; py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_self, - (char *)S_PRINTF, - "O" PY_FMT64 "ii", - py_value, - pyul_t(current_ea), - operand_num, - dtid); - PYW_GIL_RELEASE; - // Done with the string - Py_DECREF(py_value); + newref_t py_result(PyObject_CallMethod( + _this->py_self, + (char *)S_PRINTF, + "O" PY_FMT64 "ii", + py_value.o, + pyul_t(current_ea), + operand_num, + dtid)); // Error while calling the function? if ( PyW_ShowCbErr(S_PRINTF) || py_result == NULL ) return false; bool ok = false; - if ( PyString_Check(py_result) ) + if ( PyString_Check(py_result.o) ) { Py_ssize_t len; char *buf; - if ( out != NULL && PyString_AsStringAndSize(py_result, &buf, &len) != -1 ) + if ( out != NULL && PyString_AsStringAndSize(py_result.o, &buf, &len) != -1 ) { out->qclear(); out->append(buf, len); } ok = true; } - Py_DECREF(py_result); return ok; } @@ -244,16 +234,17 @@ private: int operand_num, // current operand number (-1 if unknown) qstring *errstr) // buffer for error message { + PYW_GIL_GET; + py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_self, - (char *)S_SCAN, - "s" PY_FMT64, - input, - pyul_t(current_ea), - operand_num); - PYW_GIL_RELEASE; + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_SCAN, + "s" PY_FMT64, + input, + pyul_t(current_ea), + operand_num)); // Error while calling the function? if ( PyW_ShowCbErr(S_SCAN) || py_result == NULL) @@ -263,15 +254,14 @@ private: do { // We expect a tuple(bool, string|None) - if ( !PyTuple_Check(py_result) || PyTuple_Size(py_result) != 2 ) + if ( !PyTuple_Check(py_result.o) || PyTuple_Size(py_result.o) != 2 ) break; - // Borrow references - PyObject *py_bool = PyTuple_GetItem(py_result, 0); - PyObject *py_val = PyTuple_GetItem(py_result, 1); + borref_t py_bool(PyTuple_GetItem(py_result.o, 0)); + borref_t py_val(PyTuple_GetItem(py_result.o, 1)); // Get return code from Python - ok = PyObject_IsTrue(py_bool); + ok = PyObject_IsTrue(py_bool.o); // We expect None or the value (depending on probe) if ( ok ) @@ -282,7 +272,7 @@ private: Py_ssize_t len; char *buf; - if ( PyString_AsStringAndSize(py_val, &buf, &len) != -1 ) + if ( PyString_AsStringAndSize(py_val.o, &buf, &len) != -1 ) { value->qclear(); value->append(buf, len); @@ -292,16 +282,15 @@ private: else { // Make sure the user returned (False, String) - if ( py_bool != Py_False || !PyString_Check(py_val) ) + if ( py_bool.o != Py_False || !PyString_Check(py_val.o) ) { *errstr = "Invalid return value returned from the Python callback!"; break; } // Get the error message - *errstr = PyString_AsString(py_val); + *errstr = PyString_AsString(py_val.o); } } while ( false ); - Py_DECREF(py_result); return ok; } @@ -313,19 +302,18 @@ private: // xrefs from the current item. // this callback may be missing. { + PYW_GIL_GET; + py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; - - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_self, - (char *)S_ANALYZE, - PY_FMT64 "i", - pyul_t(current_ea), - operand_num); - PYW_GIL_RELEASE; - + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_ANALYZE, + PY_FMT64 "i", + pyul_t(current_ea), + operand_num)); + PyW_ShowCbErr(S_ANALYZE); - Py_XDECREF(py_result); } public: py_custom_data_format_t() @@ -334,9 +322,9 @@ public: py_self = NULL; } - const char *get_name() const - { - return df_name.c_str(); + const char *get_name() const + { + return df_name.c_str(); } int register_df(int dtid, PyObject *py_obj) @@ -348,10 +336,12 @@ public: memset(&df, 0, sizeof(df)); df.cbsize = sizeof(df); df.ud = this; - PyObject *py_attr = NULL; + PYW_GIL_CHECK_LOCKED_SCOPE(); do { + ref_t py_attr; + // name if ( !PyW_GetStringAttr(py_obj, S_NAME, &df_name) ) break; @@ -363,9 +353,8 @@ public: // props py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); - if ( py_attr != NULL && PyInt_Check(py_attr) ) - df.props = PyInt_AsLong(py_attr); - Py_XDECREF(py_attr); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + df.props = PyInt_AsLong(py_attr.o); // hotkey if ( PyW_GetStringAttr(py_obj, S_HOTKEY, &df_hotkey) ) @@ -373,36 +362,28 @@ public: // value_size py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); - if ( py_attr != NULL && PyInt_Check(py_attr) ) - df.value_size = PyInt_AsLong(py_attr); - Py_XDECREF(py_attr); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + df.value_size = PyInt_AsLong(py_attr.o); // text_width py_attr = PyW_TryGetAttrString(py_obj, S_TEXT_WIDTH); - if ( py_attr != NULL && PyInt_Check(py_attr) ) - df.text_width = PyInt_AsLong(py_attr); - Py_XDECREF(py_attr); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + df.text_width = PyInt_AsLong(py_attr.o); // print cb py_attr = PyW_TryGetAttrString(py_obj, S_PRINTF); - if ( py_attr != NULL && PyCallable_Check(py_attr) ) + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) df.print = s_print; - Py_XDECREF(py_attr); // scan cb py_attr = PyW_TryGetAttrString(py_obj, S_SCAN); - if ( py_attr != NULL && PyCallable_Check(py_attr) ) + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) df.scan = s_scan; - Py_XDECREF(py_attr); // analyze cb py_attr = PyW_TryGetAttrString(py_obj, S_ANALYZE); - if ( py_attr != NULL && PyCallable_Check(py_attr) ) + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) df.analyze = s_analyze; - Py_XDECREF(py_attr); - - // Done with attribute - py_attr = NULL; // Now try to register dfid = register_custom_data_format(dtid, &df); @@ -414,19 +395,16 @@ public: py_self = py_obj; // Update the format ID - py_attr = PyInt_FromLong(dfid); - PyObject_SetAttrString(py_obj, S_ID, py_attr); - Py_DECREF(py_attr); - - py_attr = NULL; + py_attr = newref_t(PyInt_FromLong(dfid)); + PyObject_SetAttrString(py_obj, S_ID, py_attr.o); } while ( false ); - - Py_XDECREF(py_attr); return dfid; } bool unregister_df(int dtid) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Never registered? if ( dfid < 0 ) return true; @@ -490,6 +468,8 @@ static py_custom_data_format_list_t py_df_list; //------------------------------------------------------------------------ static PyObject *py_data_type_to_py_dict(const data_type_t *dt) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + return Py_BuildValue("{s:" PY_FMT64 ",s:i,s:i,s:s,s:s,s:s,s:s}", S_VALUE_SIZE, pyul_t(dt->value_size), S_PROPS, dt->props, @@ -503,6 +483,8 @@ static PyObject *py_data_type_to_py_dict(const data_type_t *dt) //------------------------------------------------------------------------ static PyObject *py_data_format_to_py_dict(const data_format_t *df) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + return Py_BuildValue("{s:i,s:i,s:i,s:" PY_FMT64 ",s:s,s:s,s:s}", S_PROPS, df->props, S_CBSIZE, df->cbsize, @@ -668,6 +650,7 @@ def get_custom_data_format(dtid, dfid): // Get definition of a registered custom data format and returns a dictionary static PyObject *py_get_custom_data_format(int dtid, int fid) { + PYW_GIL_CHECK_LOCKED_SCOPE(); const data_format_t *df = get_custom_data_format(dtid, fid); if ( df == NULL ) Py_RETURN_NONE; @@ -688,6 +671,7 @@ def get_custom_data_type(dtid): // Get definition of a registered custom data format and returns a dictionary static PyObject *py_get_custom_data_type(int dtid) { + PYW_GIL_CHECK_LOCKED_SCOPE(); const data_type_t *dt = get_custom_data_type(dtid); if ( dt == NULL ) Py_RETURN_NONE; diff --git a/pywraps/py_custview.hpp b/pywraps/py_custview.hpp index c77ebe4..aa181cf 100644 --- a/pywraps/py_custview.hpp +++ b/pywraps/py_custview.hpp @@ -168,6 +168,7 @@ private: static bool idaapi s_popup_cb(void *ud) { + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; return _this->on_popup(); } @@ -179,6 +180,7 @@ private: if ( it == _global_popup_map.end() ) return false; + PYW_GIL_GET; return it->second.cv->on_popup_menu(it->second.menu_id); } @@ -188,6 +190,7 @@ private: int shift, void *ud) { + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; return _this->on_keydown(vk_key, shift); } @@ -195,6 +198,7 @@ private: // The popup menu is being constructed static void idaapi s_cv_popup(TCustomControl * /*cv*/, void *ud) { + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; _this->on_popup(); } @@ -202,6 +206,7 @@ private: // The user clicked static bool idaapi s_cv_click(TCustomControl * /*cv*/, int shift, void *ud) { + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; return _this->on_click(shift); } @@ -209,6 +214,7 @@ private: // The user double clicked static bool idaapi s_cv_dblclick(TCustomControl * /*cv*/, int shift, void *ud) { + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; return _this->on_dblclick(shift); } @@ -216,6 +222,7 @@ private: // Cursor position has been changed static void idaapi s_cv_curpos(TCustomControl * /*cv*/, void *ud) { + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; _this->on_curpos_changed(); } @@ -223,6 +230,8 @@ private: //-------------------------------------------------------------------------- static int idaapi s_ui_cb(void *ud, int code, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; switch ( code ) { @@ -243,11 +252,12 @@ private: TForm *form = va_arg(va, TForm *); if ( _this->_form != form ) break; - - unhook_from_notification_point(HT_UI, s_ui_cb, _this); - _this->on_close(); - _this->on_post_close(); } + // fallthrough... + case ui_term: + unhook_from_notification_point(HT_UI, s_ui_cb, _this); + _this->on_close(); + _this->on_post_close(); break; } @@ -541,6 +551,8 @@ private: // Convert a tuple (String, [color, [bgcolor]]) to a simpleline_t static bool py_to_simpleline(PyObject *py, simpleline_t &sl) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyString_Check(py) ) { sl.line = PyString_AsString(py); @@ -570,37 +582,29 @@ private: // virtual bool on_click(int shift) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CLICK, "i", shift); - PYW_GIL_RELEASE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CLICK, "i", shift)); PyW_ShowCbErr(S_ON_CLICK); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; + return py_result != NULL && PyObject_IsTrue(py_result.o); } //-------------------------------------------------------------------------- // OnDblClick virtual bool on_dblclick(int shift) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_DBL_CLICK, "i", shift); - PYW_GIL_RELEASE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_DBL_CLICK, "i", shift)); PyW_ShowCbErr(S_ON_DBL_CLICK); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; + return py_result != NULL && PyObject_IsTrue(py_result.o); } //-------------------------------------------------------------------------- // OnCurorPositionChanged virtual void on_curpos_changed() { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CURSOR_POS_CHANGED, NULL); - PYW_GIL_RELEASE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CURSOR_POS_CHANGED, NULL)); PyW_ShowCbErr(S_ON_CURSOR_POS_CHANGED); - Py_XDECREF(py_result); } //-------------------------------------------------------------------------- @@ -610,12 +614,9 @@ private: // Call the close method if it is there and the object is still bound if ( (features & HAVE_CLOSE) != 0 && py_self != NULL ) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CLOSE, NULL); - PYW_GIL_RELEASE; - + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CLOSE, NULL)); PyW_ShowCbErr(S_ON_CLOSE); - Py_XDECREF(py_result); // Cleanup Py_DECREF(py_self); @@ -627,36 +628,31 @@ private: // OnKeyDown virtual bool on_keydown(int vk_key, int shift) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - py_self, - (char *)S_ON_KEYDOWN, - "ii", - vk_key, - shift); - PYW_GIL_RELEASE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_KEYDOWN, + "ii", + vk_key, + shift)); PyW_ShowCbErr(S_ON_KEYDOWN); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; + return py_result != NULL && PyObject_IsTrue(py_result.o); } //-------------------------------------------------------------------------- // OnPopupShow virtual bool on_popup() { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - py_self, - (char *)S_ON_POPUP, - NULL); - PYW_GIL_RELEASE; - + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_POPUP, + NULL)); PyW_ShowCbErr(S_ON_POPUP); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; + return py_result != NULL && PyObject_IsTrue(py_result.o); } //-------------------------------------------------------------------------- @@ -664,28 +660,22 @@ private: virtual bool on_hint(place_t *place, int *important_lines, qstring &hint) { size_t ln = data.to_lineno(place); - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - py_self, - (char *)S_ON_HINT, - PY_FMT64, - pyul_t(ln)); - PYW_GIL_RELEASE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_HINT, + PY_FMT64, + pyul_t(ln))); PyW_ShowCbErr(S_ON_HINT); - bool ok = py_result != NULL && PyTuple_Check(py_result) && PyTuple_Size(py_result) == 2; + bool ok = py_result != NULL && PyTuple_Check(py_result.o) && PyTuple_Size(py_result.o) == 2; if ( ok ) { - // Borrow references - PyObject *py_nlines = PyTuple_GetItem(py_result, 0); - PyObject *py_hint = PyTuple_GetItem(py_result, 1); - if ( important_lines != NULL ) - *important_lines = PyInt_AsLong(py_nlines); - - hint = PyString_AsString(py_hint); + *important_lines = PyInt_AsLong(PyTuple_GetItem(py_result.o, 0)); + hint = PyString_AsString(PyTuple_GetItem(py_result.o, 1)); } - Py_XDECREF(py_result); return ok; } @@ -693,18 +683,15 @@ private: // OnPopupMenuClick virtual bool on_popup_menu(size_t menu_id) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - py_self, - (char *)S_ON_POPUP_MENU, - PY_FMT64, - pyul_t(menu_id)); - PYW_GIL_RELEASE; - + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_POPUP_MENU, + PY_FMT64, + pyul_t(menu_id))); PyW_ShowCbErr(S_ON_POPUP_MENU); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; + return py_result != NULL && PyObject_IsTrue(py_result.o); } //-------------------------------------------------------------------------- @@ -776,6 +763,7 @@ public: place_t *pl; int x, y; pl = get_place(mouse, &x, &y); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( pl == NULL ) Py_RETURN_NONE; return Py_BuildValue("(" PY_FMT64 "ii)", pyul_t(data.to_lineno(pl)), x, y); @@ -786,6 +774,7 @@ public: PyObject *get_line(size_t nline) { simpleline_t *r = data.get_line(nline); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( r == NULL ) Py_RETURN_NONE; return Py_BuildValue("(sII)", r->line.c_str(), (unsigned int)r->color, (unsigned int)r->bgcolor); @@ -835,6 +824,8 @@ public: {S_ON_DBL_CLICK, HAVE_DBLCLICK}, {S_ON_CURSOR_POS_CHANGED, HAVE_CURPOS} }; + + PYW_GIL_CHECK_LOCKED_SCOPE(); for ( size_t i=0; iinit(py_link, title); if ( !ok ) @@ -964,6 +958,7 @@ bool pyscv_refresh_current(PyObject *py_this) PyObject *pyscv_get_current_line(PyObject *py_this, bool mouse, bool notags) { DECL_THIS; + PYW_GIL_CHECK_LOCKED_SCOPE(); const char *line; if ( _this == NULL || (line = _this->get_current_line(mouse, notags)) == NULL ) Py_RETURN_NONE; @@ -1024,7 +1019,10 @@ PyObject *pyscv_get_line(PyObject *py_this, size_t nline) { DECL_THIS; if ( _this == NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_RETURN_NONE; + } return _this->get_line(nline); } @@ -1034,7 +1032,10 @@ PyObject *pyscv_get_pos(PyObject *py_this, bool mouse) { DECL_THIS; if ( _this == NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_RETURN_NONE; + } return _this->get_pos(mouse); } @@ -1044,6 +1045,7 @@ PyObject *pyscv_clear_lines(PyObject *py_this) DECL_THIS; if ( _this != NULL ) _this->clear(); + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_RETURN_NONE; } @@ -1081,7 +1083,10 @@ PyObject *pyscv_get_selection(PyObject *py_this) { DECL_THIS; if ( _this == NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_RETURN_NONE; + } return _this->py_get_selection(); } @@ -1089,6 +1094,7 @@ PyObject *pyscv_get_selection(PyObject *py_this) PyObject *pyscv_get_current_word(PyObject *py_this, bool mouse) { DECL_THIS; + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( _this != NULL ) { qstring word; diff --git a/pywraps/py_cvt.hpp b/pywraps/py_cvt.hpp index 6b5b996..a6603e9 100644 --- a/pywraps/py_cvt.hpp +++ b/pywraps/py_cvt.hpp @@ -61,380 +61,395 @@ struct scfld_t #define FT_BAD_TYPE -2 #define FT_OK 1 -//----------------------------------------------------------------------- -class pycvt_t -{ - struct attr_t - { - qstring str; - uint64 u64; - // User is responsible to release this attribute when done - PyObject *py_obj; - }; +// //----------------------------------------------------------------------- +// class pycvt_t +// { +// struct attr_t +// { +// qstring str; +// uint64 u64; +// // User is responsible to release this attribute when done +// PyObject *py_obj; +// }; - //----------------------------------------------------------------------- - static int get_attr( - PyObject *py_obj, - const char *attrname, - int ft, - attr_t &val) - { - PyObject *py_attr; - if ( (py_attr = PyW_TryGetAttrString(py_obj, attrname)) == NULL ) - return FT_NOT_FOUND; +// //----------------------------------------------------------------------- +// static int get_attr( +// PyObject *py_obj, +// const char *attrname, +// int ft, +// attr_t &val) +// { +// ref_t py_attr(PyW_TryGetAttrString(py_obj, attrname)); +// if ( py_attr == NULL ) +// return FT_NOT_FOUND; - int cvt = FT_OK; - if ( ft == FT_STR || ft == FT_CHAR && PyString_Check(py_attr) ) - val.str = PyString_AsString(py_attr); - else if ( (ft > FT_FIRST_NUM && ft < FT_LAST_NUM) && PyW_GetNumber(py_attr, &val.u64) ) - ; // nothing to be done - // A string array? - else if ( (ft == FT_STRARR || ft == FT_NUM16ARR || ft == FT_CHRARR_STATIC ) - && (PyList_CheckExact(py_attr) || PyW_IsSequenceType(py_attr)) ) - { - // Return a reference to the attribute - val.py_obj = py_attr; - // Do not decrement the reference to this attribute - py_attr = NULL; - } - else - cvt = FT_BAD_TYPE; - Py_XDECREF(py_attr); - return cvt; - } +// int cvt = FT_OK; +// if ( ft == FT_STR || ft == FT_CHAR && PyString_Check(py_attr.o) ) +// val.str = PyString_AsString(py_attr.o); +// else if ( (ft > FT_FIRST_NUM && ft < FT_LAST_NUM) && PyW_GetNumber(py_attr.o, &val.u64) ) +// ; // nothing to be done +// // A string array? +// else if ( (ft == FT_STRARR || ft == FT_NUM16ARR || ft == FT_CHRARR_STATIC ) +// && (PyList_CheckExact(py_attr.o) || PyW_IsSequenceType(py_attr.o)) ) +// { +// // Return a reference to the attribute +// val.py_obj = py_attr.o; +// // Do not decrement the reference to this attribute +// py_attr = NULL; +// } +// else +// cvt = FT_BAD_TYPE; +// return cvt; +// } - //----------------------------------------------------------------------- - static int idaapi make_str_list_cb( - PyObject *py_item, - Py_ssize_t index, - void *ud) - { - if ( !PyString_Check(py_item) ) - return CIP_FAILED; - char **a = (char **)ud; - a[index] = qstrdup(PyString_AsString(py_item)); - return CIP_OK; - } +// //----------------------------------------------------------------------- +// static int idaapi make_str_list_cb( +// PyObject *py_item, +// Py_ssize_t index, +// void *ud) +// { +// if ( !PyString_Check(py_item) ) +// return CIP_FAILED; +// char **a = (char **)ud; +// a[index] = qstrdup(PyString_AsString(py_item)); +// return CIP_OK; +// } - //----------------------------------------------------------------------- - // Converts an IDC list of strings to a C string list - static Py_ssize_t str_list_to_str_arr( - PyObject *py_list, - char ***arr) - { - // Take the size - Py_ssize_t size = pyvar_walk_list(py_list); +// //----------------------------------------------------------------------- +// // Converts an IDC list of strings to a C string list +// static Py_ssize_t str_list_to_str_arr( +// PyObject *py_list, +// char ***arr) +// { +// // Take the size +// Py_ssize_t size = pyvar_walk_list(py_list); - // Allocate a buffer - char **a = (char **)qalloc((size + 1) * sizeof(char *)); +// // Allocate a buffer +// char **a = (char **)qalloc((size + 1) * sizeof(char *)); - // Walk and populate - size = pyvar_walk_list(py_list, make_str_list_cb, a); +// // Walk and populate +// size = pyvar_walk_list(py_list, make_str_list_cb, a); - // Make the list NULL terminated - a[size] = NULL; +// // Make the list NULL terminated +// a[size] = NULL; - // Return the list to the user - *arr = a; +// // Return the list to the user +// *arr = a; - // Return the size of items processed - return size; - } +// // Return the size of items processed +// return size; +// } - //----------------------------------------------------------------------- - typedef qvector uint64vec_t; - static int idaapi make_int_list( - PyObject *py_item, - Py_ssize_t /*index*/, - void *ud) - { - uint64 val; - if ( !PyW_GetNumber(py_item, &val) ) - return CIP_FAILED; - uint64vec_t *vec = (uint64vec_t *)ud; - vec->push_back(val); - return CIP_OK; - } +// //----------------------------------------------------------------------- +// typedef qvector uint64vec_t; +// static int idaapi make_int_list( +// PyObject *py_item, +// Py_ssize_t /*index*/, +// void *ud) +// { +// uint64 val; +// if ( !PyW_GetNumber(py_item, &val) ) +// return CIP_FAILED; +// uint64vec_t *vec = (uint64vec_t *)ud; +// vec->push_back(val); +// return CIP_OK; +// } -public: - //----------------------------------------------------------------------- - // Frees a NULL terminated list of fields - static void free_fields( - const scfld_t *fields, - void *store_area) - { - for ( int i=0; ; i++ ) - { - // End of list? - const scfld_t &fd = fields[i]; - if ( fd.field_name == NULL ) - break; +// public: +// //----------------------------------------------------------------------- +// // Frees a NULL terminated list of fields +// static void free_fields( +// const scfld_t *fields, +// void *store_area) +// { +// for ( int i=0; ; i++ ) +// { +// // End of list? +// const scfld_t &fd = fields[i]; +// if ( fd.field_name == NULL ) +// break; - void *store = (void *)((char *)store_area + fd.field_offs); - int ft = fd.field_type & ~FT_VALUE_MASK; - switch ( ft ) - { - case FT_STR: // Simple string - { - char **s = (char **)store; - if ( *s != NULL ) - { - qfree(*s); - *s = NULL; - } - } - break; +// void *store = (void *)((char *)store_area + fd.field_offs); +// int ft = fd.field_type & ~FT_VALUE_MASK; +// switch ( ft ) +// { +// case FT_STR: // Simple string +// { +// char **s = (char **)store; +// if ( *s != NULL ) +// { +// qfree(*s); +// *s = NULL; +// } +// } +// break; - case FT_STRARR: // Array of strings - { - char ***op = (char ***)store, **p = *op; - while ( *p != NULL ) - qfree((void *)*p++); - qfree(*op); - *op = NULL; - } - break; +// case FT_STRARR: // Array of strings +// { +// char ***op = (char ***)store, **p = *op; +// while ( *p != NULL ) +// qfree((void *)*p++); +// qfree(*op); +// *op = NULL; +// } +// break; - case FT_NUM16ARR: - { - uint16 **arr = (uint16 **)store; - if ( *arr != NULL ) - { - qfree(*arr); - *arr = NULL; - } - } - break; - } - } - } +// case FT_NUM16ARR: +// { +// uint16 **arr = (uint16 **)store; +// if ( *arr != NULL ) +// { +// qfree(*arr); +// *arr = NULL; +// } +// } +// break; +// } +// } +// } - //----------------------------------------------------------------------- - // Converts from a C structure to Python - static int from_c( - const scfld_t *fields, - void *read_area, - PyObject *py_obj) - { - PyObject *py_attr; - int i; - bool ok = false; - for ( i=0; ; i++ ) - { - // End of list? - const scfld_t &fd = fields[i]; - if ( fd.field_name == NULL ) - { - ok = true; - break; - } +// //----------------------------------------------------------------------- +// // Converts from a C structure to Python +// static int from_c( +// const scfld_t *fields, +// void *read_area, +// PyObject *py_obj) +// { +// PyObject *py_attr; +// int i; +// bool ok = false; +// for ( i=0; ; i++ ) +// { +// // End of list? +// const scfld_t &fd = fields[i]; +// if ( fd.field_name == NULL ) +// { +// ok = true; +// break; +// } - // Point to structure member - int ft = fd.field_type & ~FT_VALUE_MASK; - void *read = (void *)((char *)read_area + fd.field_offs); - // Create the python attribute properly - if ( ft > FT_FIRST_NUM && ft < FT_LAST_NUM ) - { - if ( ft == FT_NUM16 ) - py_attr = Py_BuildValue("H", *(uint16 *)read); - else if ( ft == FT_NUM32 ) - py_attr = Py_BuildValue("I", *(uint32 *)read); - else if ( ft == FT_INT ) - py_attr = Py_BuildValue("i", *(int *)read); - else if ( ft == FT_SIZET ) - py_attr = Py_BuildValue(PY_FMT64,*(size_t *)read); - else if ( ft == FT_SSIZET ) - py_attr = Py_BuildValue(PY_SFMT64,*(ssize_t *)read); - } - else if ( ft == FT_STR || ft == FT_CHAR ) - { - if ( ft == FT_STR ) - py_attr = PyString_FromString(*(char **)read); - else - py_attr = Py_BuildValue("c", *(char *)read); - } - else if ( ft == FT_STRARR ) - { - char **arr = *(char ***)read; - py_attr = PyList_New(0); - while ( *arr != NULL ) - PyList_Append(py_attr, PyString_FromString(*arr++)); - } - else - continue; - PyObject_SetAttrString(py_obj, fd.field_name, py_attr); - Py_XDECREF(py_attr); - } - return ok ? -1 : i; - } +// // Point to structure member +// int ft = fd.field_type & ~FT_VALUE_MASK; +// void *read = (void *)((char *)read_area + fd.field_offs); +// // Create the python attribute properly +// if ( ft > FT_FIRST_NUM && ft < FT_LAST_NUM ) +// { +// if ( ft == FT_NUM16 ) +// py_attr = Py_BuildValue("H", *(uint16 *)read); +// else if ( ft == FT_NUM32 ) +// py_attr = Py_BuildValue("I", *(uint32 *)read); +// else if ( ft == FT_INT ) +// py_attr = Py_BuildValue("i", *(int *)read); +// else if ( ft == FT_SIZET ) +// py_attr = Py_BuildValue(PY_FMT64,*(size_t *)read); +// else if ( ft == FT_SSIZET ) +// py_attr = Py_BuildValue(PY_SFMT64,*(ssize_t *)read); +// } +// else if ( ft == FT_STR || ft == FT_CHAR ) +// { +// if ( ft == FT_STR ) +// py_attr = PyString_FromString(*(char **)read); +// else +// py_attr = Py_BuildValue("c", *(char *)read); +// } +// else if ( ft == FT_STRARR ) +// { +// char **arr = *(char ***)read; +// py_attr = PyList_New(0); +// while ( *arr != NULL ) +// PyList_Append(py_attr, PyString_FromString(*arr++)); +// } +// else +// continue; +// PyObject_SetAttrString(py_obj, fd.field_name, py_attr); +// Py_XDECREF(py_attr); +// } +// return ok ? -1 : i; +// } - //----------------------------------------------------------------------- - // Converts fields from IDC and field description into a C structure - // If 'use_extlang' is specified, then the passed idc_obj is considered - // to be an opaque object and thus can be queried only through extlang - static int from_script( - const scfld_t *fields, - void *store_area, - PyObject *py_obj) - { - int i; - bool ok = false; - attr_t attr; - for ( i=0; ; i++ ) - { - // End of list? - const scfld_t &fd = fields[i]; - if ( fd.field_name == NULL ) - { - ok = true; - break; - } +// //----------------------------------------------------------------------- +// // Converts fields from IDC and field description into a C structure +// // If 'use_extlang' is specified, then the passed idc_obj is considered +// // to be an opaque object and thus can be queried only through extlang +// static int from_script( +// const scfld_t *fields, +// void *store_area, +// PyObject *py_obj) +// { +// int i; +// bool ok = false; +// attr_t attr; +// for ( i=0; ; i++ ) +// { +// // End of list? +// const scfld_t &fd = fields[i]; +// if ( fd.field_name == NULL ) +// { +// ok = true; +// break; +// } - // Get field type - int ft = fd.field_type & ~FT_VALUE_MASK; +// // Get field type +// int ft = fd.field_type & ~FT_VALUE_MASK; - // Point to structure member - void *store = (void *)((char *)store_area + fd.field_offs); +// // Point to structure member +// void *store = (void *)((char *)store_area + fd.field_offs); - // Retrieve attribute and type - int cvt = get_attr(py_obj, fd.field_name, ft, attr); +// // Retrieve attribute and type +// int cvt = get_attr(py_obj, fd.field_name, ft, attr); - // Attribute not found? - if ( cvt == FT_NOT_FOUND ) - { - // Skip optional fields - if ( fd.is_optional ) - continue; - break; - } +// // Attribute not found? +// if ( cvt == FT_NOT_FOUND ) +// { +// // Skip optional fields +// if ( fd.is_optional ) +// continue; +// break; +// } - if ( ft == FT_STR ) - *(char **)store = qstrdup(attr.str.c_str()); - else if ( ft == FT_NUM32 ) - *(uint32 *)store = uint32(attr.u64); - else if ( ft == FT_NUM16 ) - *(uint16 *)store = attr.u64 & 0xffff; - else if ( ft == FT_INT ) - *(int *)store = int(attr.u64); - else if ( ft == FT_SIZET ) - *(size_t *)store = size_t(attr.u64); - else if ( ft == FT_SSIZET ) - *(ssize_t *)store = ssize_t(attr.u64); - else if ( ft == FT_CHAR ) - *(char *)store = *attr.str.c_str(); - else if ( ft == FT_STRARR ) - { - str_list_to_str_arr(attr.py_obj, (char ***)store); - Py_DECREF(attr.py_obj); - } - else if ( ft == FT_CHRARR_STATIC ) - { - size_t sz = (fd.field_type & FT_VALUE_MASK) >> 16; - if ( sz == 0 ) - break; - uint64vec_t w; - char *a = (char *) store; - if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) ) - { - sz = qmin(w.size(), sz); - for ( size_t i=0; i < sz; i++ ) - a[i] = w[i] & 0xFF; - } - } - else if ( ft == FT_NUM16ARR ) - { - uint64vec_t w; - if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) > 0 ) - { - size_t max_sz = (fd.field_type & FT_VALUE_MASK) >> 16; - bool zero_term; - if ( max_sz == 0 ) - { - zero_term = true; - max_sz = w.size(); - } - else - { - zero_term = false; - max_sz = qmin(max_sz, w.size()); - } - // Allocate as much as we parsed elements - // Add one more element if list was zero terminated - uint16 *a = (uint16 *)qalloc(sizeof(uint16) * (max_sz + (zero_term ? 1 : 0))) ; - for ( size_t i=0; i < max_sz; i++ ) - a[i] = w[i] & 0xFF; +// if ( ft == FT_STR ) +// *(char **)store = qstrdup(attr.str.c_str()); +// else if ( ft == FT_NUM32 ) +// *(uint32 *)store = uint32(attr.u64); +// else if ( ft == FT_NUM16 ) +// *(uint16 *)store = attr.u64 & 0xffff; +// else if ( ft == FT_INT ) +// *(int *)store = int(attr.u64); +// else if ( ft == FT_SIZET ) +// *(size_t *)store = size_t(attr.u64); +// else if ( ft == FT_SSIZET ) +// *(ssize_t *)store = ssize_t(attr.u64); +// else if ( ft == FT_CHAR ) +// *(char *)store = *attr.str.c_str(); +// else if ( ft == FT_STRARR ) +// { +// str_list_to_str_arr(attr.py_obj, (char ***)store); +// Py_DECREF(attr.py_obj); +// } +// else if ( ft == FT_CHRARR_STATIC ) +// { +// size_t sz = (fd.field_type & FT_VALUE_MASK) >> 16; +// if ( sz == 0 ) +// break; +// uint64vec_t w; +// char *a = (char *) store; +// if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) ) +// { +// sz = qmin(w.size(), sz); +// for ( size_t i=0; i < sz; i++ ) +// a[i] = w[i] & 0xFF; +// } +// } +// else if ( ft == FT_NUM16ARR ) +// { +// uint64vec_t w; +// if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) > 0 ) +// { +// size_t max_sz = (fd.field_type & FT_VALUE_MASK) >> 16; +// bool zero_term; +// if ( max_sz == 0 ) +// { +// zero_term = true; +// max_sz = w.size(); +// } +// else +// { +// zero_term = false; +// max_sz = qmin(max_sz, w.size()); +// } +// // Allocate as much as we parsed elements +// // Add one more element if list was zero terminated +// uint16 *a = (uint16 *)qalloc(sizeof(uint16) * (max_sz + (zero_term ? 1 : 0))) ; +// for ( size_t i=0; i < max_sz; i++ ) +// a[i] = w[i] & 0xFF; - if ( zero_term ) - a[max_sz] = 0; - *(uint16 **)store = a; - } - } - else - { - // Unsupported field type! - break; - } - } - return ok ? -1 : i; - } -}; +// if ( zero_term ) +// a[max_sz] = 0; +// *(uint16 **)store = a; +// } +// } +// else +// { +// // Unsupported field type! +// break; +// } +// } +// return ok ? -1 : i; +// } +// }; //------------------------------------------------------------------------- Py_ssize_t pyvar_walk_list( - PyObject *py_list, - int (idaapi *cb)(PyObject *py_item, Py_ssize_t index, void *ud), - void *ud) + const ref_t &py_list, + int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud), + void *ud) { - if ( !PyList_CheckExact(py_list) && !PyW_IsSequenceType(py_list) ) - return CIP_FAILED; + PYW_GIL_CHECK_LOCKED_SCOPE(); - bool is_seq = !PyList_CheckExact(py_list); - Py_ssize_t size = is_seq ? PySequence_Size(py_list) : PyList_Size(py_list); - - if ( cb == NULL ) - return size; - - Py_ssize_t i; - for ( i=0; i= CIP_OK; + if ( !ok ) PyErr_SetString(PyExc_ValueError, "Could not convert Python object to IDC object!"); - return false; - } - return true; + return ok; } //------------------------------------------------------------------------ @@ -516,8 +526,10 @@ static idc_class_t *get_py_idc_cvt_opaque() //------------------------------------------------------------------------- // Utility function to create opaque / convertible Python <-> IDC variables // The referred Python variable will have its reference increased -static bool create_py_idc_opaque_obj(PyObject *py_var, idc_value_t *idc_var) +static bool wrap_PyObject_ptr(const ref_t &py_var, idc_value_t *idc_var) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Create an IDC object of this special helper class if ( VarObject(idc_var, get_py_idc_cvt_opaque()) != eOk ) return false; @@ -528,7 +540,8 @@ static bool create_py_idc_opaque_obj(PyObject *py_var, idc_value_t *idc_var) VarSetAttr(idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val); // Store the value as a PVOID referencing the given Python object - idc_val.set_pvoid(py_var); + py_var.incref(); + idc_val.set_pvoid(py_var.o); VarSetAttr(idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val); return true; @@ -542,6 +555,11 @@ static error_t idaapi py_idc_opaque_dtor( idc_value_t *argv, idc_value_t * /*res*/) { + // This can be called at plugin registration time, when a + // 'script_plugin_t' instance is ::free()'d. It is + // not guaranteed that we have the GIL at that point. + PYW_GIL_GET; + // Get the value from the object idc_value_t idc_val; VarGetAttr(&argv[0], S_PY_IDCCVT_VALUE_ATTR, &idc_val); @@ -559,42 +577,53 @@ static error_t idaapi py_idc_opaque_dtor( // Converts a Python variable into an IDC variable // This function returns on one CIP_XXXX int pyvar_to_idcvar( - PyObject *py_var, - idc_value_t *idc_var, - int *gvar_sn) + const ref_t &py_var, + idc_value_t *idc_var, + int *gvar_sn) { - PyObject *attr; + PYW_GIL_CHECK_LOCKED_SCOPE(); + // None / NULL - if ( py_var == NULL || py_var == Py_None ) - idc_var->set_long(0); - // Numbers? - else if ( PyW_GetNumberAsIDC(py_var, idc_var) ) - return CIP_OK; - // String - else if ( PyString_Check(py_var) ) - idc_var->_set_string(PyString_AsString(py_var), PyString_Size(py_var)); - // Float - else if ( PyBool_Check(py_var) ) - idc_var->set_long(py_var == Py_True ? 1 : 0); - // Boolean - else if ( PyFloat_Check(py_var) ) + if ( py_var == NULL || py_var.o == Py_None ) { - double dresult = PyFloat_AsDouble(py_var); + idc_var->set_long(0); + } + // Numbers? + else if ( PyW_GetNumberAsIDC(py_var.o, idc_var) ) + { + return CIP_OK; + } + // String + else if ( PyString_Check(py_var.o) ) + { + idc_var->_set_string(PyString_AsString(py_var.o), PyString_Size(py_var.o)); + } + // Boolean + else if ( PyBool_Check(py_var.o) ) + { + idc_var->set_long(py_var.o == Py_True ? 1 : 0); + } + // Float + else if ( PyFloat_Check(py_var.o) ) + { + double dresult = PyFloat_AsDouble(py_var.o); ieee_realcvt((void *)&dresult, idc_var->e, 3); idc_var->vtype = VT_FLOAT; } // void* - else if ( PyCObject_Check(py_var) ) - idc_var->set_pvoid(PyCObject_AsVoidPtr(py_var)); - // Is it a Python list? - else if ( PyList_CheckExact(py_var) || PyW_IsSequenceType(py_var) ) + else if ( PyCObject_Check(py_var.o) ) + { + idc_var->set_pvoid(PyCObject_AsVoidPtr(py_var.o)); + } + // Python list? + else if ( PyList_CheckExact(py_var.o) || PyW_IsSequenceType(py_var.o) ) { // Create the object VarObject(idc_var); // Determine list size and type - bool is_seq = !PyList_CheckExact(py_var); - Py_ssize_t size = is_seq ? PySequence_Size(py_var) : PyList_Size(py_var); + bool is_seq = !PyList_CheckExact(py_var.o); + Py_ssize_t size = is_seq ? PySequence_Size(py_var.o) : PyList_Size(py_var.o); bool ok = true; qstring attr_name; @@ -602,7 +631,11 @@ int pyvar_to_idcvar( for ( Py_ssize_t i=0; i (key, value) - PyObject *py_item = PyList_GetItem(py_items, i); + PyObject *py_item = PyList_GetItem(py_items.o, i); // Extract key/value - PyObject *key = PySequence_GetItem(py_item, 0); - PyObject *val = PySequence_GetItem(py_item, 1); + newref_t key(PySequence_GetItem(py_item, 0)); + newref_t val(PySequence_GetItem(py_item, 1)); // Get key's string representation - PyW_ObjectToString(key, &key_name); + PyW_ObjectToString(key.o, &key_name); // Convert the attribute into an IDC value idc_value_t v; @@ -659,17 +688,13 @@ int pyvar_to_idcvar( // Store the attribute VarSetAttr(idc_var, key_name.c_str(), &v); } - Py_XDECREF(key); - Py_XDECREF(val); if ( !ok ) break; } - // Decrement attribute reference - Py_DECREF(py_items); return ok ? CIP_OK : CIP_FAILED; } // Possible function? - else if ( PyCallable_Check(py_var) ) + else if ( PyCallable_Check(py_var.o) ) { idc_var->clear(); idc_var->vtype = VT_FUNC; @@ -682,20 +707,21 @@ int pyvar_to_idcvar( else { // Get the type - int cvt_id = get_pyidc_cvt_type(py_var); + int cvt_id = get_pyidc_cvt_type(py_var.o); switch ( cvt_id ) { // // INT64 // case PY_ICID_INT64: - // Get the value attribute - attr = PyW_TryGetAttrString(py_var, S_PY_IDCCVT_VALUE_ATTR); - if ( attr == NULL ) - return false; - idc_var->set_int64(PyLong_AsLongLong(attr)); - Py_DECREF(attr); - return CIP_OK; + { + // Get the value attribute + ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); + if ( attr == NULL ) + return false; + idc_var->set_int64(PyLong_AsLongLong(attr.o)); + return CIP_OK; + } // // BYREF // @@ -706,7 +732,7 @@ int pyvar_to_idcvar( return CIP_FAILED; // Get the value attribute - attr = PyW_TryGetAttrString(py_var, S_PY_IDCCVT_VALUE_ATTR); + ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); if ( attr == NULL ) return CIP_FAILED; @@ -722,7 +748,6 @@ int pyvar_to_idcvar( // Create a reference to this global variable VarRef(idc_var, gvar); } - Py_DECREF(attr); return ok ? CIP_OK : CIP_FAILED; } // @@ -730,28 +755,25 @@ int pyvar_to_idcvar( // case PY_ICID_OPAQUE: { - if ( !create_py_idc_opaque_obj(py_var, idc_var) ) + if ( !wrap_PyObject_ptr(py_var, idc_var) ) return CIP_FAILED; - return CIP_OK_NODECREF; + return CIP_OK_OPAQUE; } // // Other objects // default: // A normal object? - PyObject *py_dir = PyObject_Dir(py_var); - Py_ssize_t size = PyList_Size(py_dir); - if ( py_dir == NULL || !PyList_Check(py_dir) || size == 0 ) - { - Py_XDECREF(py_dir); + newref_t py_dir(PyObject_Dir(py_var.o)); + Py_ssize_t size = PyList_Size(py_dir.o); + if ( py_dir == NULL || !PyList_Check(py_dir.o) || size == 0 ) return CIP_FAILED; - } // Create the IDC object VarObject(idc_var); for ( Py_ssize_t i=0; io); if ( t != PY_ICID_INT64 ) return CIP_IMMUTABLE; // Cannot recycle immutable object // Update the attribute - PyObject_SetAttrString(*py_var, S_PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64)); + PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64)); return CIP_OK; } - PyObject *py_cls = get_idaapi_attr_by_id(PY_CLSID_CVT_INT64); + ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_INT64)); if ( py_cls == NULL ) return CIP_FAILED; - PYW_GIL_ENSURE; - *py_var = PyObject_CallFunctionObjArgs(py_cls, PyLong_FromLongLong(idc_var.i64), NULL); - PYW_GIL_RELEASE; - Py_DECREF(py_cls); + *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, PyLong_FromLongLong(idc_var.i64), NULL)); if ( PyW_GetError() || *py_var == NULL ) return CIP_FAILED; break; @@ -832,7 +864,7 @@ int idcvar_to_pyvar( #if !defined(NO_OBSOLETE_FUNCS) || defined(__EXPR_SRC) case VT_STR: - *py_var = PyString_FromString(idc_var.str); + *py_var = newref_t(PyString_FromString(idc_var.str)); break; #endif @@ -840,7 +872,7 @@ int idcvar_to_pyvar( if ( *py_var == NULL ) { const qstring &s = idc_var.qstr(); - *py_var = PyString_FromStringAndSize(s.begin(), s.length()); + *py_var = newref_t(PyString_FromStringAndSize(s.begin(), s.length())); break; } else @@ -849,11 +881,7 @@ int idcvar_to_pyvar( // Cannot recycle immutable objects if ( *py_var != NULL ) return CIP_IMMUTABLE; -#ifdef __EA64__ - *py_var = PyLong_FromLongLong(idc_var.num); -#else - *py_var = PyLong_FromLong(idc_var.num); -#endif + *py_var = newref_t(cvt_to_pylong(idc_var.num)); break; case VT_FLOAT: if ( *py_var == NULL ) @@ -862,7 +890,7 @@ int idcvar_to_pyvar( if ( ph.realcvt(&x, (uint16 *)idc_var.e, (sizeof(x)/2-1)|010) != 0 ) INTERR(30160); - *py_var = PyFloat_FromDouble(x); + *py_var = newref_t(PyFloat_FromDouble(x)); break; } else @@ -872,19 +900,16 @@ int idcvar_to_pyvar( { if ( *py_var == NULL ) { - PyObject *py_cls = get_idaapi_attr_by_id(PY_CLSID_CVT_BYREF); + ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_BYREF)); if ( py_cls == NULL ) return CIP_FAILED; // Create a byref object with None value. We populate it later - PYW_GIL_ENSURE; - *py_var = PyObject_CallFunctionObjArgs(py_cls, Py_None, NULL); - PYW_GIL_RELEASE; - Py_DECREF(py_cls); + *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, Py_None, NULL)); if ( PyW_GetError() || *py_var == NULL ) return CIP_FAILED; } - int t = *py_var == NULL ? -1 : get_pyidc_cvt_type(*py_var); + int t = get_pyidc_cvt_type(py_var->o); if ( t != PY_ICID_BYREF ) return CIP_FAILED; @@ -895,27 +920,25 @@ int idcvar_to_pyvar( return CIP_FAILED; // Can we recycle the object? - PyObject *new_py_val = PyW_TryGetAttrString(*py_var, S_PY_IDCCVT_VALUE_ATTR); + ref_t new_py_val(PyW_TryGetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR)); if ( new_py_val != NULL ) { // Recycle t = idcvar_to_pyvar(*dref_v, &new_py_val); - Py_XDECREF(new_py_val); // DECREF because of GetAttrStr // Success? Nothing more to be done if ( t == CIP_OK ) return CIP_OK; // Clear it so we don't recycle it - new_py_val = NULL; + new_py_val = ref_t(); } // Try to convert (not recycle) if ( idcvar_to_pyvar(*dref_v, &new_py_val) != CIP_OK ) return CIP_FAILED; // Update the attribute - PyObject_SetAttrString(*py_var, S_PY_IDCCVT_VALUE_ATTR, new_py_val); - Py_DECREF(new_py_val); + PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, new_py_val.o); break; } @@ -929,25 +952,22 @@ int idcvar_to_pyvar( && VarGetAttr(&idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val) == eOk ) { // Extract the object - *py_var = (PyObject *) idc_val.pvoid; - return CIP_OK_NODECREF; + *py_var = borref_t((PyObject *) idc_val.pvoid); + return CIP_OK_OPAQUE; } - PyObject *obj; + ref_t obj; bool is_dict = false; // Need to create a new object? if ( *py_var == NULL ) { // Get skeleton class reference - PyObject *py_cls = get_idaapi_attr_by_id(PY_CLSID_APPCALL_SKEL_OBJ); + ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_APPCALL_SKEL_OBJ)); if ( py_cls == NULL ) return CIP_FAILED; // Call constructor - PYW_GIL_ENSURE; - obj = PyObject_CallFunctionObjArgs(py_cls, NULL); - PYW_GIL_RELEASE; - Py_DECREF(py_cls); + obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, NULL)); if ( PyW_GetError() || obj == NULL ) return CIP_FAILED; } @@ -955,7 +975,7 @@ int idcvar_to_pyvar( { // Recycle existing variable obj = *py_var; - if ( PyDict_Check(obj) ) + if ( PyDict_Check(obj.o) ) is_dict = true; } @@ -969,30 +989,21 @@ int idcvar_to_pyvar( VarGetAttr(&idc_var, attr_name, &v, true); // Convert attribute to a python value (recursively) - PyObject *py_attr(NULL); + ref_t py_attr; int cvt = idcvar_to_pyvar(v, &py_attr); if ( cvt <= CIP_IMMUTABLE ) - { - // Delete the object (if we created it) - if ( *py_var == NULL ) - Py_DECREF(obj); - return CIP_FAILED; - } if ( is_dict ) - PyDict_SetItemString(obj, attr_name, py_attr); + PyDict_SetItemString(obj.o, attr_name, py_attr.o); else - PyObject_SetAttrString(obj, attr_name, py_attr); - - if ( cvt == CIP_OK ) - Py_XDECREF(py_attr); + PyObject_SetAttrString(obj.o, attr_name, py_attr.o); } *py_var = obj; break; } // Unhandled type default: - *py_var = NULL; + *py_var = ref_t(); return CIP_FAILED; } return CIP_OK; @@ -1004,36 +1015,31 @@ int idcvar_to_pyvar( bool pyw_convert_idc_args( const idc_value_t args[], int nargs, - ppyobject_vec_t &pargs, - boolvec_t *decref, + ref_vec_t &pargs, + bool as_tupple, char *errbuf, size_t errbufsize) { - bool as_tupple = decref == NULL; - PyObject *py_tuple(NULL); + PYW_GIL_CHECK_LOCKED_SCOPE(); + + ref_t py_tuple; pargs.qclear(); if ( as_tupple ) { - py_tuple = PyTuple_New(nargs); + py_tuple = newref_t(PyTuple_New(nargs)); if ( py_tuple == NULL ) { if ( errbuf != 0 && errbufsize > 0 ) qstrncpy(errbuf, "Failed to create a new tuple to store arguments!", errbufsize); return false; } - // Add the tuple - pargs.push_back(py_tuple); - } - else - { - decref->qclear(); } for ( int i=0; ipush_back(cvt == CIP_OK); } } - return true; -} -//------------------------------------------------------------------------- -// Frees arguments returned by pyw_convert_idc_args() -void pyw_free_idc_args( - ppyobject_vec_t &pargs, - boolvec_t *decref) -{ - if ( decref == NULL ) - { - if ( !pargs.empty() ) - Py_XDECREF(pargs[0]); - } - else - { - // free argument objects - for ( int i=(int)pargs.size()-1; i>=0; i-- ) - { - if ( decref->at(i) ) - Py_DECREF(pargs[i]); - } - decref->clear(); - } - pargs.clear(); + // Add the tuple to the list of args only now. Doing so earlier will + // cause the py_tuple.o->ob_refcnt to be 2 and not 1, and that will + // cause 'PyTuple_SetItem()' to fail. + if ( as_tupple ) + pargs.push_back(py_tuple); + + return true; } // diff --git a/pywraps/py_dbg.hpp b/pywraps/py_dbg.hpp index a5669a2..c0d7a13 100644 --- a/pywraps/py_dbg.hpp +++ b/pywraps/py_dbg.hpp @@ -14,6 +14,8 @@ static bool dbg_can_query() //------------------------------------------------------------------------- static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyObject *py_list = PyList_New(areas.size()); meminfo_vec_t::const_iterator it, it_end(areas.end()); Py_ssize_t i = 0; @@ -42,6 +44,8 @@ PyObject *py_appcall( PyObject *py_fields, PyObject *arg_list) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyList_Check(arg_list) ) return NULL; @@ -57,11 +61,11 @@ PyObject *py_appcall( for ( Py_ssize_t i=0; i%s\n", int(i), s.c_str()); } // Convert it @@ -81,6 +85,10 @@ PyObject *py_appcall( return NULL; } + error_t ret; + idc_value_t idc_result; + Py_BEGIN_ALLOW_THREADS; + if ( (debug & IDA_DEBUG_APPCALL) != 0 ) { msg("input variables:\n" @@ -96,8 +104,7 @@ PyObject *py_appcall( } // Do Appcall - idc_value_t idc_result; - error_t ret = appcall( + ret = appcall( func_ea, tid, (type_t *)type, @@ -106,16 +113,17 @@ PyObject *py_appcall( idc_args.begin(), &idc_result); + Py_END_ALLOW_THREADS; + if ( ret != eOk ) { // An exception was thrown? if ( ret == eExecThrow ) { // Convert the result (which is a debug_event) into a Python object - PyObject *py_appcall_exc(NULL); + ref_t py_appcall_exc; idcvar_to_pyvar(idc_result, &py_appcall_exc); - PyErr_SetObject(PyExc_OSError, py_appcall_exc); - Py_DECREF(py_appcall_exc); + PyErr_SetObject(PyExc_OSError, py_appcall_exc.o); return NULL; } // An error in the Appcall? (or an exception but AppCallOptions/DEBEV is not set) @@ -145,7 +153,7 @@ PyObject *py_appcall( for ( Py_ssize_t i=0; iob_refcnt == 1); if ( (debug & IDA_DEBUG_APPCALL) != 0 ) { msg("return var:\n" @@ -170,7 +178,8 @@ PyObject *py_appcall( VarPrint(&s, &idc_result); msg("%s\n-----------\n", s.c_str()); } - return py_result; + py_result.incref(); + return py_result.o; } // @@ -193,6 +202,8 @@ def dbg_get_registers(): */ static PyObject *dbg_get_registers() { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( dbg == NULL ) Py_RETURN_NONE; @@ -251,6 +262,8 @@ def dbg_get_thread_sreg_base(tid, sreg_value): */ static PyObject *dbg_get_thread_sreg_base(PyObject *py_tid, PyObject *py_sreg_value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !dbg_can_query() || !PyInt_Check(py_tid) || !PyInt_Check(py_sreg_value) ) Py_RETURN_NONE; ea_t answer; @@ -277,6 +290,8 @@ def dbg_read_memory(ea, sz): */ static PyObject *dbg_read_memory(PyObject *py_ea, PyObject *py_sz) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + uint64 ea, sz; if ( !dbg_can_query() || !PyW_GetNumber(py_ea, &ea) || !PyW_GetNumber(py_sz, &sz) ) Py_RETURN_NONE; @@ -314,6 +329,8 @@ def dbg_write_memory(ea, buffer): */ static PyObject *dbg_write_memory(PyObject *py_ea, PyObject *py_buf) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + uint64 ea; if ( !dbg_can_query() || !PyString_Check(py_buf) || !PyW_GetNumber(py_ea, &ea) ) Py_RETURN_NONE; @@ -338,6 +355,8 @@ def dbg_get_name(): */ static PyObject *dbg_get_name() { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( dbg == NULL ) Py_RETURN_NONE; else @@ -359,15 +378,19 @@ def dbg_get_memory_info(): */ static PyObject *dbg_get_memory_info() { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !dbg_can_query() ) Py_RETURN_NONE; // Invalidate memory + meminfo_vec_t areas; + Py_BEGIN_ALLOW_THREADS; invalidate_dbgmem_config(); invalidate_dbgmem_contents(BADADDR, BADADDR); - meminfo_vec_t areas; get_dbg_memory_info(&areas); + Py_END_ALLOW_THREADS; return meminfo_vec_t_to_py(areas); } @@ -459,6 +482,7 @@ static PyObject *refresh_debugger_memory() // Invalidate the cache isEnabled(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_RETURN_NONE; } @@ -466,74 +490,78 @@ int idaapi DBG_Callback(void *ud, int notification_code, va_list va); class DBG_Hooks { public: - virtual ~DBG_Hooks() { unhook(); }; + virtual ~DBG_Hooks() { unhook(); } - bool hook() { return hook_to_notification_point(HT_DBG, DBG_Callback, this); }; - bool unhook() { return unhook_from_notification_point(HT_DBG, DBG_Callback, this); }; + bool hook() { return hook_to_notification_point(HT_DBG, DBG_Callback, this); } + bool unhook() { return unhook_from_notification_point(HT_DBG, DBG_Callback, this); } /* Hook functions to be overridden in Python */ virtual void dbg_process_start(pid_t pid, thid_t tid, ea_t ea, char *name, ea_t base, - asize_t size) { }; + asize_t size) {} virtual void dbg_process_exit(pid_t pid, thid_t tid, ea_t ea, - int exit_code) { }; + int exit_code) {} virtual void dbg_process_attach(pid_t pid, thid_t tid, ea_t ea, char *name, ea_t base, - asize_t size) { }; + asize_t size) {} virtual void dbg_process_detach(pid_t pid, thid_t tid, - ea_t ea) { }; + ea_t ea) {} virtual void dbg_thread_start(pid_t pid, thid_t tid, - ea_t ea) { }; + ea_t ea) {} virtual void dbg_thread_exit(pid_t pid, thid_t tid, ea_t ea, - int exit_code) { }; + int exit_code) {} virtual void dbg_library_load(pid_t pid, thid_t tid, ea_t ea, char *name, ea_t base, - asize_t size) { }; + asize_t size) {} virtual void dbg_library_unload(pid_t pid, thid_t tid, ea_t ea, - char *libname) { }; + char *libname) {} virtual void dbg_information(pid_t pid, thid_t tid, ea_t ea, - char *info) { }; + char *info) {} virtual int dbg_exception(pid_t pid, thid_t tid, ea_t ea, int code, bool can_cont, ea_t exc_ea, - char *info) { return 0; }; - virtual void dbg_suspend_process(void) { }; - virtual int dbg_bpt(thid_t tid, ea_t breakpoint_ea) { return 0; }; - virtual int dbg_trace(thid_t tid, ea_t ip) { return 0; }; + char *info) { return 0; } + virtual void dbg_suspend_process(void) {} + virtual int dbg_bpt(thid_t tid, ea_t breakpoint_ea) { return 0; } + virtual int dbg_trace(thid_t tid, ea_t ip) { return 0; } virtual void dbg_request_error(int failed_command, - int failed_dbg_notification) { }; - virtual void dbg_step_into(void) { }; - virtual void dbg_step_over(void) { }; - virtual void dbg_run_to(pid_t pid, thid_t tid, ea_t ea) { }; - virtual void dbg_step_until_ret(void) { }; + int failed_dbg_notification) {} + virtual void dbg_step_into(void) {} + virtual void dbg_step_over(void) {} + virtual void dbg_run_to(pid_t pid, thid_t tid, ea_t ea) {} + virtual void dbg_step_until_ret(void) {} }; int idaapi DBG_Callback(void *ud, int notification_code, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + class DBG_Hooks *proxy = (class DBG_Hooks *)ud; debug_event_t *event; int code = 0; + try { switch (notification_code) diff --git a/pywraps/py_dbg.py b/pywraps/py_dbg.py new file mode 100644 index 0000000..0a033aa --- /dev/null +++ b/pywraps/py_dbg.py @@ -0,0 +1,7 @@ + +# +NO_PROCESS = 0xFFFFFFFF +NO_THREAD = 0 +# + + diff --git a/pywraps/py_diskio.hpp b/pywraps/py_diskio.hpp index 899ecd5..a20da3d 100644 --- a/pywraps/py_diskio.hpp +++ b/pywraps/py_diskio.hpp @@ -5,17 +5,17 @@ //-------------------------------------------------------------------------- int idaapi py_enumerate_files_cb(const char *file, void *ud) { - PyObject *py_file = PyString_FromString(file); - PYW_GIL_ENSURE; - PyObject *py_ret = PyObject_CallFunctionObjArgs( - (PyObject *)ud, - py_file, - NULL); - PYW_GIL_RELEASE; - int r = (py_ret == NULL || !PyNumber_Check(py_ret)) ? 1 /* stop enumeration on failure */ : PyInt_AsLong(py_ret); - Py_XDECREF(py_file); - Py_XDECREF(py_ret); - return r; + // No need to 'PYW_GIL_GET' here, as this is called synchronously + // and from the same thread as the one that executes + // 'py_enumerate_files'. + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_file(PyString_FromString(file)); + newref_t py_ret( + PyObject_CallFunctionObjArgs( + (PyObject *)ud, + py_file.o, + NULL)); + return (py_ret == NULL || !PyNumber_Check(py_ret.o)) ? 1 /* stop enum on failure */ : PyInt_AsLong(py_ret.o); } // @@ -31,7 +31,7 @@ def enumerate_files(path, fname, callback): @param callback: a callable object that takes the filename as its first argument and it returns 0 to continue enumeration or non-zero to stop enumeration. - @return: + @return: None in case of script errors tuple(code, fname) : If the callback returns non-zero """ @@ -40,6 +40,8 @@ def enumerate_files(path, fname, callback): */ PyObject *py_enumerate_files(PyObject *path, PyObject *fname, PyObject *callback) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + do { if ( !PyString_Check(path) || !PyString_Check(fname) || !PyCallable_Check(callback) ) diff --git a/pywraps/py_expr.hpp b/pywraps/py_expr.hpp index 6c0688d..06976d5 100644 --- a/pywraps/py_expr.hpp +++ b/pywraps/py_expr.hpp @@ -9,10 +9,12 @@ struct py_idcfunc_ctx_t int nargs; py_idcfunc_ctx_t(PyObject *py_func, const char *name, int nargs): py_func(py_func), name(name), nargs(nargs) { + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_INCREF(py_func); } ~py_idcfunc_ctx_t() { + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_DECREF(py_func); } }; @@ -26,25 +28,25 @@ static error_t py_call_idc_func( // Convert IDC arguments to Python list py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)_ctx; int cvt; - ppyobject_vec_t pargs; char errbuf[MAXSTR]; - if ( !pyw_convert_idc_args(argv, ctx->nargs, pargs, NULL, errbuf, sizeof(errbuf)) ) + + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_vec_t pargs; + if ( !pyw_convert_idc_args(argv, ctx->nargs, pargs, true, errbuf, sizeof(errbuf)) ) { // Error during conversion? Create an IDC exception return PyW_CreateIdcException(r, errbuf); } // Call the Python function - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallObject( - ctx->py_func, - pargs.empty() ? NULL : pargs[0]); - + newref_t py_result(PyObject_CallObject( + ctx->py_func, + pargs.empty() ? NULL : pargs[0].o)); + error_t err; if ( PyW_GetError(errbuf, sizeof(errbuf)) ) { err = PyW_CreateIdcException(r, errbuf); - Py_XDECREF(py_result); } else { @@ -55,13 +57,7 @@ static error_t py_call_idc_func( err = PyW_CreateIdcException(r, "ERROR: bad return value"); else err = eOk; - if ( cvt != CIP_OK_NODECREF ) - Py_XDECREF(py_result); } - PYW_GIL_RELEASE; - - // Free the converted args - pyw_free_idc_args(pargs); return err; } @@ -153,4 +149,4 @@ bool CompileLine_wrap(const char *line, char *errbuf, size_t errbufsize) } // -#endif \ No newline at end of file +#endif diff --git a/pywraps/py_graph.hpp b/pywraps/py_graph.hpp index b35590c..73f5c8e 100644 --- a/pywraps/py_graph.hpp +++ b/pywraps/py_graph.hpp @@ -1,20 +1,44 @@ -#ifndef __PY_IDA_GRAPH__ -#define __PY_IDA_GRAPH__ +#ifndef __PY_GRAPH__ +#define __PY_GRAPH__ // -class py_graph_t +class py_graph_t : public py_customidamemo_t { + typedef py_customidamemo_t inherited; + +protected: + + virtual void node_info_modified(int n, const node_info_t *ni, uint32 flags) + { + if ( ni == NULL ) + { + node_cache.erase(n); + } + else + { + nodetext_cache_t *c = node_cache.get(n); + if ( c != NULL ) + { + if ( (flags & NIF_TEXT) == NIF_TEXT ) + c->text = ni->text; + if ( (flags & NIF_BG_COLOR) == NIF_BG_COLOR ) + c->bgcolor = ni->bg_color; + } + } + } + + void collect_class_callbacks_ids(callbacks_ids_t *out); + private: enum { - GR_HAVE_USER_HINT = 0x00000001, - GR_HAVE_CLICKED = 0x00000002, - GR_HAVE_DBL_CLICKED = 0x00000004, - GR_HAVE_GOTFOCUS = 0x00000008, - GR_HAVE_LOSTFOCUS = 0x00000010, - GR_HAVE_CHANGED_CURRENT = 0x00000020, - GR_HAVE_CLOSE = 0x00000040, - GR_HAVE_COMMAND = 0x00000080 + GRCODE_HAVE_USER_HINT = 0x00010000, + GRCODE_HAVE_CLICKED = 0x00020000, + GRCODE_HAVE_DBL_CLICKED = 0x00040000, + GRCODE_HAVE_GOTFOCUS = 0x00080000, + GRCODE_HAVE_LOSTFOCUS = 0x00100000, + GRCODE_HAVE_CHANGED_CURRENT = 0x00200000, + GRCODE_HAVE_COMMAND = 0x00400000 }; struct nodetext_cache_t { @@ -41,20 +65,6 @@ private: } }; - class tform_pygraph_map_t: public std::map - { - public: - py_graph_t *get(TForm *form) - { - iterator it = find(form); - return it == end() ? NULL : it->second; - } - void add(TForm *form, py_graph_t *py) - { - (*this)[form] = py; - } - }; - class cmdid_map_t: public std::map { private: @@ -100,23 +110,25 @@ private: } }; - static tform_pygraph_map_t tform_pyg; static cmdid_map_t cmdid_pyg; - int cb_flags; - TForm *form; - graph_viewer_t *gv; + + // TForm *form; bool refresh_needed; - PyObject *self; nodetext_cache_map_t node_cache; + // instance callback + int gr_callback(int code, va_list va); + // static callback static int idaapi s_callback(void *obj, int code, va_list va) { + PYW_GIL_GET; return ((py_graph_t *)obj)->gr_callback(code, va); } static bool idaapi s_menucb(void *ud) { + PYW_GIL_GET; Py_ssize_t id = (Py_ssize_t)ud; py_graph_t *_this = cmdid_pyg.get(id); if ( _this != NULL ) @@ -128,212 +140,37 @@ private: void on_command(Py_ssize_t id) { // Check return value to OnRefresh() call - PYW_GIL_ENSURE; - PyObject *ret = PyObject_CallMethod(self, (char *)S_ON_COMMAND, "n", id); - PYW_GIL_RELEASE; - Py_XDECREF(ret); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_COMMAND, "n", id)); } // Refresh user-defined graph node number and edges // It calls Python method and expects that the user already filled // the nodes and edges. The nodes and edges are retrieved and passed to IDA - void on_user_refresh(mutable_graph_t *g) - { - if ( !refresh_needed ) - return; - - // Check return value to OnRefresh() call - PYW_GIL_ENSURE; - PyObject *ret = PyObject_CallMethod(self, (char *)S_ON_REFRESH, NULL); - PYW_GIL_RELEASE; - if ( ret == NULL || !PyObject_IsTrue(ret) ) - { - Py_XDECREF(ret); - return; - } - - // Refer to the nodes - PyObject *nodes = PyW_TryGetAttrString(self, S_M_NODES); - if ( ret == NULL || !PyList_Check(nodes) ) - { - Py_XDECREF(nodes); - return; - } - - // Refer to the edges - PyObject *edges = PyW_TryGetAttrString(self, S_M_EDGES); - if ( ret == NULL || !PyList_Check(edges) ) - { - Py_DECREF(nodes); - Py_XDECREF(edges); - return; - } - - // Resize the nodes - int max_nodes = abs(int(PyList_Size(nodes))); - g->clear(); - g->resize(max_nodes); - - // Mark that we refreshed already - refresh_needed = false; - - // Clear cached nodes - node_cache.clear(); - - // Get the edges - for ( int i=(int)PyList_Size(edges)-1; i>=0; i-- ) - { - // Each list item is a sequence (id1, id2) - PyObject *item = PyList_GetItem(edges, i); - if ( !PySequence_Check(item) ) - continue; - - // Get and validate each of the two elements in the sequence - int edge_ids[2]; - int j; - for ( j=0; j max_nodes ) - break; - - edge_ids[j] = v; - } - - // Incomplete? - if ( j != qnumber(edge_ids) ) - break; - - // Add the edge - g->add_edge(edge_ids[0], edge_ids[1], NULL); - } - Py_DECREF(nodes); - Py_DECREF(edges); - } + void on_user_refresh(mutable_graph_t *g); // Retrieves the text for user-defined graph node // It expects either a string or a tuple (string, bgcolor) - bool on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color) - { - // If already cached then return the value - nodetext_cache_t *c = node_cache.get(node); - if ( c != NULL ) - { - *str = c->text.c_str(); - if ( bg_color != NULL ) - *bg_color = c->bgcolor; - return true; - } - - // Not cached, call Python - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod(self, (char *)S_ON_GETTEXT, "i", node); - PYW_GIL_RELEASE; - if ( result == NULL ) - return false; - - bgcolor_t cl = bg_color == NULL ? DEFCOLOR : *bg_color; - const char *s; - - // User returned a string? - if ( PyString_Check(result) ) - { - s = PyString_AsString(result); - if ( s == NULL ) - s = ""; - c = node_cache.add(node, s, cl); - } - // User returned a sequence of text and bgcolor - else if ( PySequence_Check(result) && PySequence_Size(result) == 2 ) - { - PyObject *py_str = PySequence_GetItem(result, 0); - PyObject *py_color = PySequence_GetItem(result, 1); - - if ( py_str == NULL || !PyString_Check(py_str) || (s = PyString_AsString(py_str)) == NULL ) - s = ""; - if ( py_color != NULL && PyNumber_Check(py_color) ) - cl = bgcolor_t(PyLong_AsUnsignedLong(py_color)); - - c = node_cache.add(node, s, cl); - - Py_XDECREF(py_str); - Py_XDECREF(py_color); - } - Py_DECREF(result); - - *str = c->text.c_str(); - if ( bg_color != NULL ) - *bg_color = c->bgcolor; - - return true; - } + bool on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color); // Retrieves the hint for the user-defined graph // Calls Python and expects a string or None - int on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint) - { - // 'hint' must be allocated by qalloc() or qstrdup() - // out: 0-use default hint, 1-use proposed hint - - // We dispatch hints over nodes only - if ( mousenode == -1 ) - return 0; - - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod(self, (char *)S_ON_HINT, "i", mousenode); - PYW_GIL_RELEASE; - bool ok = result != NULL && PyString_Check(result); - if ( !ok ) - { - Py_XDECREF(result); - return 0; - } - *hint = qstrdup(PyString_AsString(result)); - Py_DECREF(result); - return 1; // use our hint - } + int on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint); // graph is being destroyed - void on_destroy(mutable_graph_t * /*g*/ = NULL) + void on_graph_destroyed(mutable_graph_t * /*g*/ = NULL) { - if ( self != NULL ) - { - if ( (cb_flags & GR_HAVE_CLOSE) != 0 ) - { - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL); - PYW_GIL_RELEASE; - - Py_XDECREF(result); - } - unbind(); - } - - // Remove the TForm from list - if ( form != NULL ) - tform_pyg.erase(form); - - // Remove all associated commands from the list - cmdid_pyg.clear(this); - - // Delete this instance - delete this; + refresh_needed = true; + node_cache.clear(); } // graph is being clicked int on_clicked( - graph_viewer_t * /*gv*/, + graph_viewer_t * /*view*/, selection_item_t * /*item1*/, graph_item_t *item2) { - // in: graph_viewer_t *gv + // in: graph_viewer_t *view // selection_item_t *current_item1 // graph_item_t *current_item2 // out: 0-ok, 1-ignore click @@ -345,29 +182,20 @@ private: if ( item2->n == -1 ) return 1; - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_CLICK, - "i", - item2->n); - PYW_GIL_RELEASE; - - if ( result == NULL || !PyObject_IsTrue(result) ) - { - Py_XDECREF(result); - return 1; - } - - Py_DECREF(result); - - return 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_CLICK, + "i", + item2->n)); + return result == NULL || !PyObject_IsTrue(result.o); } // a graph node has been double clicked - int on_dblclicked(graph_viewer_t * /*gv*/, selection_item_t *item) + int on_dblclicked(graph_viewer_t * /*view*/, selection_item_t *item) { - // in: graph_viewer_t *gv + // in: graph_viewer_t *view // selection_item_t *current_item // out: 0-ok, 1-ignore click //graph_viewer_t *v = va_arg(va, graph_viewer_t *); @@ -375,75 +203,410 @@ private: if ( item == NULL || !item->is_node ) return 1; - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_DBL_CLICK, - "i", - item->node); - PYW_GIL_RELEASE; - - if ( result == NULL || !PyObject_IsTrue(result) ) - { - Py_XDECREF(result); - return 1; - } - Py_DECREF(result); - return 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_DBL_CLICK, + "i", + item->node)); + return result == NULL || !PyObject_IsTrue(result.o); } // a graph viewer got focus - void on_gotfocus(graph_viewer_t * /*gv*/) + void on_gotfocus(graph_viewer_t * /*view*/) { - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_ACTIVATE, - NULL); - PYW_GIL_RELEASE; - Py_XDECREF(result); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_ACTIVATE, + NULL)); } // a graph viewer lost focus - void on_lostfocus(graph_viewer_t * /*gv*/) + void on_lostfocus(graph_viewer_t * /*view*/) { - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_DEACTIVATE, - NULL); - PYW_GIL_RELEASE; - - Py_XDECREF(result); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_DEACTIVATE, + NULL)); } // a new graph node became the current node - int on_changed_current(graph_viewer_t * /*gv*/, int curnode) + int on_changed_current(graph_viewer_t * /*view*/, int curnode) { - // in: graph_viewer_t *gv + // in: graph_viewer_t *view // int curnode // out: 0-ok, 1-forbid to change the current node if ( curnode < 0 ) return 0; - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_SELECT, - "i", - curnode); - PYW_GIL_RELEASE; - - bool allow = (result != NULL && PyObject_IsTrue(result)); - Py_XDECREF(result); - return allow ? 0 : 1; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_SELECT, + "i", + curnode)); + return !(result != NULL && PyObject_IsTrue(result.o)); } - int gr_callback(int code, va_list va) + // a group is being created + int on_creating_group(mutable_graph_t *my_g, intset_t *my_nodes) { - int ret; - switch ( code ) + PYW_GIL_CHECK_LOCKED_SCOPE(); + printf("my_g: %p; my_nodes: %p\n", my_g, my_nodes); + newref_t py_nodes(PyList_New(my_nodes->size())); + int i; + intset_t::const_iterator p; + for ( i = 0, p=my_nodes->begin(); p != my_nodes->end(); ++p, ++i ) + PyList_SetItem(py_nodes.o, i, PyInt_FromLong(*p)); + newref_t py_result( + PyObject_CallMethod( + self.o, + (char *)S_ON_CREATING_GROUP, + "O", + py_nodes.o)); + return (py_result == NULL || !PyInt_Check(py_result.o)) ? 1 : PyInt_AsLong(py_result.o); + } + + // a group is being deleted + int on_deleting_group(mutable_graph_t * /*g*/, int old_group) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // TODO + return 0; + } + + // a group is being collapsed/uncollapsed + int on_group_visibility(mutable_graph_t * /*g*/, int group, bool expand) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // TODO + return 0; + } + + + void show() + { + TForm *form; + if ( lookup_info.find_by_py_view(&form, NULL, this) ) + open_tform(form, FORM_TAB|FORM_MENU|FORM_QWIDGET); + } + + void jump_to_node(int nid) + { + viewer_center_on(view, nid); + int x, y; + + // will return a place only when a node was previously selected + place_t *old_pl = get_custom_viewer_place(view, false, &x, &y); + if ( old_pl != NULL ) { + user_graph_place_t *new_pl = (user_graph_place_t *) old_pl->clone(); + new_pl->node = nid; + jumpto(view, new_pl, x, y); + delete new_pl; + } + } + + virtual void refresh() + { + refresh_needed = true; + inherited::refresh(); + } + + int initialize(PyObject *self, const char *title) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( !collect_pyobject_callbacks(self) ) + return -1; + + // Create form + HWND hwnd = NULL; + TForm *form = create_tform(title, &hwnd); + if ( hwnd != NULL ) // Created new tform + { + // get a unique graph id + netnode id; + char grnode[MAXSTR]; + qsnprintf(grnode, sizeof(grnode), "$ pygraph %s", title); + id.create(grnode); + graph_viewer_t *pview = create_graph_viewer(form, id, s_callback, this, 0); + open_tform(form, FORM_TAB | FORM_MENU | FORM_QWIDGET); + if ( pview != NULL ) + viewer_fit_window(pview); + bind(self, pview); + refresh(); + // Link "form" and "py_graph" + lookup_info.add(form, view, this); + } + else + { + show(); + } + + viewer_fit_window(view); + return 0; + } + + Py_ssize_t add_command(const char *title, const char *hotkey) + { + if ( !has_callback(GRCODE_HAVE_COMMAND) || view == NULL) + return 0; + Py_ssize_t cmd_id = cmdid_pyg.id(); + bool ok = viewer_add_menu_item(view, title, s_menucb, (void *)cmd_id, hotkey, 0); + if ( !ok ) + return 0; + cmdid_pyg.add(this); + return cmd_id; + } + +public: + py_graph_t() + { + // form = NULL; + refresh_needed = true; + } + + virtual ~py_graph_t() + { + // Remove all associated commands from the list + cmdid_pyg.clear(this); + } + + static void SelectNode(PyObject *self, int /*nid*/) + { + py_graph_t *_this = view_extract_this(self); + if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) ) + return; + + _this->jump_to_node(0); + } + + static Py_ssize_t AddCommand(PyObject *self, const char *title, const char *hotkey) + { + py_graph_t *_this = view_extract_this(self); + if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) ) + return 0; + + return _this->add_command(title, hotkey); + } + + static void Close(PyObject *self) + { + TForm *form; + py_graph_t *_this = view_extract_this(self); + if ( _this == NULL || !lookup_info.find_by_py_view(&form, NULL, _this) ) + return; + close_tform(form, FORM_CLOSE_LATER); + } + + static py_graph_t *Show(PyObject *self) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + py_graph_t *py_graph = view_extract_this(self); + + // New instance? + if ( py_graph == NULL ) + { + qstring title; + if ( !PyW_GetStringAttr(self, S_M_TITLE, &title) ) + return NULL; + + // Form already created? try to get associated py_graph instance + // so that we reuse it + graph_viewer_t *found_view; + TForm *form = find_tform(title.c_str()); + if ( form != NULL ) + lookup_info.find_by_form(&found_view, (py_customidamemo_t**) &py_graph, form); + + if ( py_graph == NULL ) + { + py_graph = new py_graph_t(); + } + else + { + // unbind so we are rebound + py_graph->unbind(); + py_graph->refresh_needed = true; + } + if ( py_graph->initialize(self, title.c_str()) < 0 ) + { + delete py_graph; + py_graph = NULL; + } + } + else + { + py_graph->show(); + } + return py_graph; + } +}; + +//------------------------------------------------------------------------- +void py_graph_t::collect_class_callbacks_ids(callbacks_ids_t *out) +{ + inherited::collect_class_callbacks_ids(out); + out->add(S_ON_REFRESH, 0); + out->add(S_ON_GETTEXT, 0); + out->add(S_M_EDGES, -1); + out->add(S_M_NODES, -1); + out->add(S_ON_HINT, GRCODE_HAVE_USER_HINT); + out->add(S_ON_CLICK, GRCODE_HAVE_CLICKED); + out->add(S_ON_DBL_CLICK, GRCODE_HAVE_DBL_CLICKED); + out->add(S_ON_COMMAND, GRCODE_HAVE_COMMAND); + out->add(S_ON_SELECT, GRCODE_HAVE_CHANGED_CURRENT); + out->add(S_ON_ACTIVATE, GRCODE_HAVE_GOTFOCUS); + out->add(S_ON_DEACTIVATE, GRCODE_HAVE_LOSTFOCUS); +} + +//------------------------------------------------------------------------- +void py_graph_t::on_user_refresh(mutable_graph_t *g) +{ + if ( !refresh_needed || self == NULL /* Happens at creation-time */ ) + return; + + // Check return value to OnRefresh() call + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_REFRESH, NULL)); + if ( ret != NULL && PyObject_IsTrue(ret.o) ) + { + // Refer to the nodes + ref_t nodes(PyW_TryGetAttrString(self.o, S_M_NODES)); + if ( ret != NULL && PyList_Check(nodes.o) ) + { + // Refer to the edges + ref_t edges(PyW_TryGetAttrString(self.o, S_M_EDGES)); + if ( ret != NULL && PyList_Check(edges.o) ) + { + // Resize the nodes + int max_nodes = abs(int(PyList_Size(nodes.o))); + g->clear(); + g->resize(max_nodes); + + // Mark that we refreshed already + refresh_needed = false; + + // Clear cached nodes + node_cache.clear(); + + // Get the edges + for ( int i=(int)PyList_Size(edges.o)-1; i>=0; i-- ) + { + // Each list item is a sequence (id1, id2) + borref_t item(PyList_GetItem(edges.o, i)); + if ( !PySequence_Check(item.o) ) + continue; + + // Get and validate each of the two elements in the sequence + int edge_ids[2]; + int j; + for ( j=0; j max_nodes ) + break; + edge_ids[j] = v; + } + + // Incomplete? + if ( j != qnumber(edge_ids) ) + break; + + // Add the edge + g->add_edge(edge_ids[0], edge_ids[1], NULL); + } + } + } + } +} + +//------------------------------------------------------------------------- +bool py_graph_t::on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color) +{ + // If already cached then return the value + nodetext_cache_t *c = node_cache.get(node); + if ( c != NULL ) + { + *str = c->text.c_str(); + if ( bg_color != NULL ) + *bg_color = c->bgcolor; + return true; + } + + // Not cached, call Python + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_GETTEXT, "i", node)); + if ( result == NULL ) + return false; + + bgcolor_t cl = bg_color == NULL ? DEFCOLOR : *bg_color; + const char *s; + + // User returned a string? + if ( PyString_Check(result.o) ) + { + s = PyString_AsString(result.o); + if ( s == NULL ) + s = ""; + c = node_cache.add(node, s, cl); + } + // User returned a sequence of text and bgcolor + else if ( PySequence_Check(result.o) && PySequence_Size(result.o) == 2 ) + { + newref_t py_str(PySequence_GetItem(result.o, 0)); + newref_t py_color(PySequence_GetItem(result.o, 1)); + + if ( py_str == NULL || !PyString_Check(py_str.o) || (s = PyString_AsString(py_str.o)) == NULL ) + s = ""; + if ( py_color != NULL && PyNumber_Check(py_color.o) ) + cl = bgcolor_t(PyLong_AsUnsignedLong(py_color.o)); + + c = node_cache.add(node, s, cl); + } + + *str = c->text.c_str(); + if ( bg_color != NULL ) + *bg_color = c->bgcolor; + + return true; +} + +//------------------------------------------------------------------------- +int py_graph_t::on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint) +{ + // 'hint' must be allocated by qalloc() or qstrdup() + // out: 0-use default hint, 1-use proposed hint + + // We dispatch hints over nodes only + if ( mousenode == -1 ) + return 0; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_HINT, "i", mousenode)); + bool ok = result != NULL && PyString_Check(result.o); + if ( ok ) + *hint = qstrdup(PyString_AsString(result.o)); + return ok; // use our hint +} + + +//------------------------------------------------------------------------- +int py_graph_t::gr_callback(int code, va_list va) +{ + int ret; + switch ( code ) + { // case grcode_user_text: { @@ -454,19 +617,20 @@ private: ret = on_user_text(g, node, result, bgcolor); break; } - // + // case grcode_destroyed: - on_destroy(va_arg(va, mutable_graph_t *)); + on_graph_destroyed(va_arg(va, mutable_graph_t *)); ret = 0; break; - // + + // case grcode_clicked: - if ( cb_flags & GR_HAVE_CLICKED ) + if ( has_callback(GRCODE_HAVE_CLICKED) ) { - graph_viewer_t *gv = va_arg(va, graph_viewer_t *); + graph_viewer_t *view = va_arg(va, graph_viewer_t *); selection_item_t *item = va_arg(va, selection_item_t *); graph_item_t *gitem = va_arg(va, graph_item_t *); - ret = on_clicked(gv, item, gitem); + ret = on_clicked(view, item, gitem); } else { @@ -474,40 +638,40 @@ private: ret = 1; } break; - // + // case grcode_dblclicked: - if ( cb_flags & GR_HAVE_DBL_CLICKED ) + if ( has_callback(GRCODE_HAVE_DBL_CLICKED) ) { - graph_viewer_t *gv = va_arg(va, graph_viewer_t *); + graph_viewer_t *view = va_arg(va, graph_viewer_t *); selection_item_t *item = va_arg(va, selection_item_t *); - ret = on_dblclicked(gv, item); + ret = on_dblclicked(view, item); } else ret = 1; // ignore break; - // + // case grcode_gotfocus: - if ( cb_flags & GR_HAVE_GOTFOCUS ) + if ( has_callback(GRCODE_HAVE_GOTFOCUS) ) on_gotfocus(va_arg(va, graph_viewer_t *)); ret = 0; break; - // + // case grcode_lostfocus: - if ( cb_flags & GR_HAVE_LOSTFOCUS ) + if ( has_callback(GRCODE_HAVE_LOSTFOCUS) ) on_lostfocus(va_arg(va, graph_viewer_t *)); ret = 0; break; - // + // case grcode_user_refresh: on_user_refresh(va_arg(va, mutable_graph_t *)); ret = 1; break; - // + // case grcode_user_hint: - if ( cb_flags & GR_HAVE_USER_HINT ) + if ( has_callback(GRCODE_HAVE_USER_HINT) ) { mutable_graph_t *g = va_arg(va, mutable_graph_t *); int mousenode = va_arg(va, int); @@ -521,272 +685,62 @@ private: ret = 0; } break; - // + // case grcode_changed_current: - if ( cb_flags & GR_HAVE_CHANGED_CURRENT ) + if ( has_callback(GRCODE_HAVE_CHANGED_CURRENT) ) { - graph_viewer_t *gv = va_arg(va, graph_viewer_t *); + graph_viewer_t *view = va_arg(va, graph_viewer_t *); int cur_node = va_arg(va, int); - ret = on_changed_current(gv, cur_node); + ret = on_changed_current(view, cur_node); } else ret = 0; // allow selection change break; - // + // + case grcode_creating_group: // a group is being created + { + mutable_graph_t *g = va_arg(va, mutable_graph_t*); + intset_t *nodes = va_arg(va, intset_t*); + ret = on_creating_group(g, nodes); + } + break; + // + case grcode_deleting_group: // a group is being deleted + { + mutable_graph_t *g = va_arg(va, mutable_graph_t*); + int old_group = va_arg(va, int); + ret = on_deleting_group(g, old_group); + } + break; + // + case grcode_group_visibility: // a group is being collapsed/uncollapsed + { + mutable_graph_t *g = va_arg(va, mutable_graph_t*); + int group = va_arg(va, int); + bool expand = bool(va_arg(va, int)); + ret = on_group_visibility(g, group, expand); + } + break; + // default: ret = 0; break; - } - //grcode_changed_graph, // new graph has been set - //grcode_creating_group, // a group is being created - //grcode_deleting_group, // a group is being deleted - //grcode_group_visibility, // a group is being collapsed/uncollapsed - //grcode_user_size, // calculate node size for user-defined graph - //grcode_user_title, // render node title of a user-defined graph - //grcode_user_draw, // render node of a user-defined graph - return ret; } + //grcode_changed_graph, // new graph has been set + //grcode_user_size, // calculate node size for user-defined graph + //grcode_user_title, // render node title of a user-defined graph + //grcode_user_draw, // render node of a user-defined graph + return ret; +} - void unbind() - { - if ( self == NULL ) - return; - - // Unbind this object from the python object - PyObject *py_cobj = PyCObject_FromVoidPtr(NULL, NULL); - PyObject_SetAttrString(self, S_M_THIS, py_cobj); - Py_DECREF(py_cobj); - Py_XDECREF(self); - self = NULL; - } - - void show() - { - open_tform(form, FORM_TAB|FORM_MENU|FORM_QWIDGET); - } - - static py_graph_t *extract_this(PyObject *self) - { - // Try to extract "this" from the python object - PyObject *py_this = PyW_TryGetAttrString(self, S_M_THIS); - if ( py_this == NULL || !PyCObject_Check(py_this) ) - { - Py_XDECREF(py_this); - return NULL; - } - py_graph_t *ret = (py_graph_t *) PyCObject_AsVoidPtr(py_this); - Py_DECREF(py_this); - return ret; - } - - void jump_to_node(int nid) - { - viewer_center_on(gv, nid); - int x, y; - - // will return a place only when a node was previously selected - place_t *old_pl = get_custom_viewer_place(gv, false, &x, &y); - if ( old_pl != NULL ) - { - user_graph_place_t *new_pl = (user_graph_place_t *) old_pl->clone(); - new_pl->node = nid; - jumpto(gv, new_pl, x, y); - delete new_pl; - } - } - - void refresh() - { - refresh_needed = true; - refresh_viewer(gv); - } - - int create(PyObject *self, const char *title) - { - // check what callbacks we have - static const struct - { - const char *name; - int have; - } callbacks[] = - { - {S_ON_REFRESH, 0}, // 0 = mandatory callback - {S_ON_GETTEXT, 0}, - {S_M_EDGES, -1}, // -1 = mandatory attributes - {S_M_NODES, -1}, - {S_ON_HINT, GR_HAVE_USER_HINT}, - {S_ON_CLICK, GR_HAVE_CLICKED}, - {S_ON_DBL_CLICK, GR_HAVE_DBL_CLICKED}, - {S_ON_CLOSE, GR_HAVE_CLOSE}, - {S_ON_COMMAND, GR_HAVE_COMMAND}, - {S_ON_SELECT, GR_HAVE_CHANGED_CURRENT}, - {S_ON_ACTIVATE, GR_HAVE_GOTFOCUS}, - {S_ON_DEACTIVATE, GR_HAVE_LOSTFOCUS} - }; - cb_flags = 0; - for ( int i=0; i= 0 && PyCallable_Check(attr) == 0)) - { - Py_XDECREF(attr); - return -1; - } - if ( have > 0 && attr != NULL ) - cb_flags |= have; - Py_XDECREF(attr); - } - - - // Keep a reference - this->self = self; - Py_INCREF(self); - - // Bind py_graph_t to python object - PyObject *py_cobj = PyCObject_FromVoidPtr(this, NULL); - PyObject_SetAttrString(self, S_M_THIS, py_cobj); - Py_DECREF(py_cobj); - - // Create form - HWND hwnd = NULL; - form = create_tform(title, &hwnd); - - // Link "form" and "py_graph" - tform_pyg.add(form, this); - - if ( hwnd != NULL ) - { - // get a unique graph id - netnode id; - char grnode[MAXSTR]; - qsnprintf(grnode, sizeof(grnode), "$ pygraph %s", title); - id.create(grnode); - gv = create_graph_viewer(form, id, s_callback, this, 0); - open_tform(form, FORM_TAB | FORM_MENU | FORM_QWIDGET); - if ( gv != NULL ) - viewer_fit_window(gv); - } - else - { - show(); - } - - viewer_fit_window(gv); - return 0; - } - - Py_ssize_t add_command(const char *title, const char *hotkey) - { - if ( (cb_flags & GR_HAVE_COMMAND) == 0 || gv == NULL) - return 0; - Py_ssize_t cmd_id = cmdid_pyg.id(); - bool ok = viewer_add_menu_item(gv, title, s_menucb, (void *)cmd_id, hotkey, 0); - if ( !ok ) - return 0; - cmdid_pyg.add(this); - return cmd_id; - } - - public: - py_graph_t() - { - form = NULL; - gv = NULL; - refresh_needed = true; - self = NULL; - } - - static void SelectNode(PyObject *self, int /*nid*/) - { - py_graph_t *_this = extract_this(self); - if ( _this == NULL || _this->form == NULL ) - return; - - _this->jump_to_node(0); - } - - static Py_ssize_t AddCommand(PyObject *self, const char *title, const char *hotkey) - { - py_graph_t *_this = extract_this(self); - if ( _this == NULL || _this->form == NULL ) - return 0; - - return _this->add_command(title, hotkey); - } - - static void Close(PyObject *self) - { - py_graph_t *_this = extract_this(self); - if ( _this == NULL || _this->form == NULL ) - return; - - close_tform(_this->form, FORM_CLOSE_LATER); - } - - static void Refresh(PyObject *self) - { - py_graph_t *_this = extract_this(self); - if ( _this == NULL ) - return; - - _this->refresh(); - } - - static py_graph_t *Show(PyObject *self) - { - py_graph_t *ret = extract_this(self); - - // New instance? - if ( ret == NULL ) - { - qstring title; - if ( !PyW_GetStringAttr(self, S_M_TITLE, &title) ) - return NULL; - - // Form already created? try to get associated py_graph instance - // so that we reuse it - ret = tform_pyg.get(find_tform(title.c_str())); - - // Instance not found? create a new one - if ( ret == NULL ) - ret = new py_graph_t(); - else - { - // unbind so we are rebound - ret->unbind(); - ret->refresh_needed = true; - } - if ( ret->create(self, title.c_str()) < 0 ) - { - delete ret; - ret = NULL; - } - } - else - { - ret->show(); - } - return ret; - } -}; - -py_graph_t::tform_pygraph_map_t py_graph_t::tform_pyg; -py_graph_t::cmdid_map_t py_graph_t::cmdid_pyg; +//------------------------------------------------------------------------- +py_graph_t::cmdid_map_t py_graph_t::cmdid_pyg; bool pyg_show(PyObject *self) { return py_graph_t::Show(self) != NULL; } -void pyg_refresh(PyObject *self) -{ - py_graph_t::Refresh(self); -} - void pyg_close(PyObject *self) { py_graph_t::Close(self); @@ -794,6 +748,7 @@ void pyg_close(PyObject *self) PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey) { + PYW_GIL_CHECK_LOCKED_SCOPE(); return Py_BuildValue("n", py_graph_t::AddCommand(self, title, hotkey)); } @@ -806,9 +761,7 @@ void pyg_select_node(PyObject *self, int nid) //-------------------------------------------------------------------------- // -void pyg_refresh(PyObject *self); void pyg_close(PyObject *self); - PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey); void pyg_select_node(PyObject *self, int nid); bool pyg_show(PyObject *self); diff --git a/pywraps/py_graph.py b/pywraps/py_graph.py index 8a50b55..6de065b 100644 --- a/pywraps/py_graph.py +++ b/pywraps/py_graph.py @@ -1,5 +1,5 @@ # -class GraphViewer(object): +class GraphViewer(CustomIDAMemo): """This class wraps the user graphing facility provided by the graph.hpp file""" def __init__(self, title, close_open = False): """ @@ -52,12 +52,6 @@ class GraphViewer(object): """ _idaapi.pyg_close(self) - def Refresh(self): - """ - Refreshes the graph. This causes the OnRefresh() to be called - """ - _idaapi.pyg_refresh(self) - def Show(self): """ Shows an existing graph or creates a new one @@ -166,4 +160,4 @@ class GraphViewer(object): # """ # print "command:", cmd_id # -# \ No newline at end of file +# diff --git a/pywraps/py_idaapi.hpp b/pywraps/py_idaapi.hpp index b5c0302..6b8cc85 100644 --- a/pywraps/py_idaapi.hpp +++ b/pywraps/py_idaapi.hpp @@ -53,11 +53,23 @@ static const char S_ON_GETTEXT[] = "OnGetText"; static const char S_ON_ACTIVATE[] = "OnActivate"; static const char S_ON_DEACTIVATE[] = "OnDeactivate"; static const char S_ON_SELECT[] = "OnSelect"; +static const char S_ON_CREATING_GROUP[] = "OnCreatingGroup"; +static const char S_ON_DELETING_GROUP[] = "OnDeletingGroup"; +static const char S_ON_GROUP_VISIBILITY[] = "OnGroupVisibility"; static const char S_M_EDGES[] = "_edges"; static const char S_M_NODES[] = "_nodes"; static const char S_M_THIS[] = "_this"; static const char S_M_TITLE[] = "_title"; static const char S_CLINK_NAME[] = "__clink__"; +static const char S_ON_VIEW_ACTIVATED[] = "OnViewActivated"; +static const char S_ON_VIEW_DEACTIVATED[] = "OnViewDeactivated"; +static const char S_ON_VIEW_KEYDOWN[] = "OnViewKeydown"; +static const char S_ON_VIEW_CLICK[] = "OnViewClick"; +static const char S_ON_VIEW_DBLCLICK[] = "OnViewDblclick"; +static const char S_ON_VIEW_CURPOS[] = "OnViewCurpos"; +static const char S_ON_VIEW_SWITCHED[] = "OnViewSwitched"; +static const char S_ON_VIEW_MOUSE_OVER[] = "OnViewMouseOver"; + #ifdef __PYWRAPS__ static const char S_PY_IDAAPI_MODNAME[] = "__main__"; @@ -66,7 +78,7 @@ static const char S_PY_IDAAPI_MODNAME[] = S_IDAAPI_MODNAME; #endif //------------------------------------------------------------------------ -static PyObject *py_cvt_helper_module = NULL; +static ref_t py_cvt_helper_module; static bool pywraps_initialized = false; //--------------------------------------------------------------------------- @@ -155,19 +167,16 @@ static error_t idaapi idc_py_invoke0( idc_value_t *argv, idc_value_t *res) { + PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *pyfunc = (PyObject *) argv[0].pvoid; - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallFunctionObjArgs(pyfunc, NULL); - PYW_GIL_RELEASE; - - Py_XDECREF(py_result); + newref_t py_result(PyObject_CallFunctionObjArgs(pyfunc, NULL)); // Report Python error as IDC exception qstring err; + error_t err_code = eOk; if ( PyW_GetError(&err) ) - return PyW_CreateIdcException(res, err.c_str()); - - return eOk; + err_code = PyW_CreateIdcException(res, err.c_str()); + return err_code; } //------------------------------------------------------------------------ @@ -223,8 +232,11 @@ void deinit_pywraps() return; pywraps_initialized = false; - Py_XDECREF(py_cvt_helper_module); - py_cvt_helper_module = NULL; + + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_cvt_helper_module = ref_t(); // Deref. + } // Unregister the IDC PyInvoke0 method (helper function for add_idc_hotkey()) set_idc_func_ex(S_PYINVOKE0, NULL, idc_py_invoke0_args, 0); @@ -232,47 +244,34 @@ void deinit_pywraps() //------------------------------------------------------------------------ // Utility function to create a class instance whose constructor takes zero arguments -PyObject *create_idaapi_class_instance0(const char *clsname) +ref_t create_idaapi_class_instance0(const char *clsname) { - PyObject *py_cls = get_idaapi_attr(clsname); + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_cls(get_idaapi_attr(clsname)); if ( py_cls == NULL ) - return NULL; + return ref_t(); - PYW_GIL_ENSURE; - PyObject *py_obj = PyObject_CallFunctionObjArgs(py_cls, NULL); - PYW_GIL_RELEASE; - - Py_DECREF(py_cls); + ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, NULL)); if ( PyW_GetError() || py_obj == NULL ) - { - Py_XDECREF(py_obj); - Py_RETURN_NONE; - } + py_obj = ref_t(); return py_obj; } //------------------------------------------------------------------------ // Utility function to create linked class instances -PyObject *create_idaapi_linked_class_instance( +ref_t create_idaapi_linked_class_instance( const char *clsname, void *lnk) { - PyObject *py_cls = get_idaapi_attr(clsname); + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_cls(get_idaapi_attr(clsname)); if ( py_cls == NULL ) - return NULL; - - PyObject *py_lnk = PyCObject_FromVoidPtr(lnk, NULL); - PYW_GIL_ENSURE; - PyObject *py_obj = PyObject_CallFunctionObjArgs(py_cls, py_lnk, NULL); - PYW_GIL_RELEASE; - Py_DECREF(py_cls); - Py_DECREF(py_lnk); + return ref_t(); + newref_t py_lnk(PyCObject_FromVoidPtr(lnk, NULL)); + ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, py_lnk.o, NULL)); if ( PyW_GetError() || py_obj == NULL ) - { - Py_XDECREF(py_obj); - py_obj = NULL; - } + py_obj = ref_t(); return py_obj; } @@ -280,10 +279,10 @@ PyObject *create_idaapi_linked_class_instance( // Gets a class type reference in idaapi // With the class type reference we can create a new instance of that type // This function takes a reference to the idaapi module and keeps the reference -PyObject *get_idaapi_attr_by_id(const int class_id) +ref_t get_idaapi_attr_by_id(const int class_id) { if ( class_id >= PY_CLSID_LAST || py_cvt_helper_module == NULL ) - return NULL; + return ref_t(); // Some class names. The array is parallel with the PY_CLSID_xxx consts static const char *class_names[]= @@ -292,16 +291,18 @@ PyObject *get_idaapi_attr_by_id(const int class_id) "object_t", "PyIdc_cvt_refclass__" }; - return PyObject_GetAttrString(py_cvt_helper_module, class_names[class_id]); + PYW_GIL_CHECK_LOCKED_SCOPE(); + return newref_t(PyObject_GetAttrString(py_cvt_helper_module.o, class_names[class_id])); } //------------------------------------------------------------------------ // Gets a class reference by name -PyObject *get_idaapi_attr(const char *attrname) +ref_t get_idaapi_attr(const char *attrname) { + PYW_GIL_CHECK_LOCKED_SCOPE(); return py_cvt_helper_module == NULL - ? NULL - : PyW_TryGetAttrString(py_cvt_helper_module, attrname); + ? ref_t() + : PyW_TryGetAttrString(py_cvt_helper_module.o, attrname); } //------------------------------------------------------------------------ @@ -311,41 +312,39 @@ bool PyW_GetStringAttr( const char *attr_name, qstring *str) { - PyObject *py_attr = PyW_TryGetAttrString(py_obj, attr_name); + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_attr(PyW_TryGetAttrString(py_obj, attr_name)); if ( py_attr == NULL ) return false; - bool ok = PyString_Check(py_attr) != 0; + bool ok = PyString_Check(py_attr.o) != 0; if ( ok ) - *str = PyString_AsString(py_attr); + *str = PyString_AsString(py_attr.o); - Py_DECREF(py_attr); return ok; } //------------------------------------------------------------------------ // Returns an attribute or NULL // No errors will be set if the attribute did not exist -PyObject *PyW_TryGetAttrString(PyObject *py_obj, const char *attr) +ref_t PyW_TryGetAttrString(PyObject *py_obj, const char *attr) { - if ( !PyObject_HasAttrString(py_obj, attr) ) - return NULL; - else - return PyObject_GetAttrString(py_obj, attr); + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t o; + if ( PyObject_HasAttrString(py_obj, attr) ) + o = newref_t(PyObject_GetAttrString(py_obj, attr)); + return o; } //------------------------------------------------------------------------ // Tries to import a module and clears the exception on failure -PyObject *PyW_TryImportModule(const char *name) +ref_t PyW_TryImportModule(const char *name) { - PYW_GIL_ENSURE; - PyObject *result = PyImport_ImportModule(name); - PYW_GIL_RELEASE; - if ( result != NULL ) - return result; - if ( PyErr_Occurred() ) - PyErr_Clear(); - return NULL; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result(PyImport_ImportModule(name)); + if ( result == NULL && PyErr_Occurred() ) + PyErr_Clear(); + return result; } //------------------------------------------------------------------------- @@ -358,144 +357,158 @@ PyObject *PyW_TryImportModule(const char *name) // And because of that we are confused as to whether to convert to 32 or 64 bool PyW_GetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var) { - if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) - return false; + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = true; + do + { + if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) + { + rc = false; + break; + } - // Can we convert to C long? - long l = PyInt_AsLong(py_var); - if ( !PyErr_Occurred() ) - { - idc_var->set_long(l); - return true; - } - // Clear last error - PyErr_Clear(); - // Can be fit into a C unsigned long? - l = (long) PyLong_AsUnsignedLong(py_var); - if ( !PyErr_Occurred() ) - { - idc_var->set_long(l); - return true; - } - PyErr_Clear(); - idc_var->set_int64(PyLong_AsLongLong(py_var)); - return true; + // Can we convert to C long? + long l = PyInt_AsLong(py_var); + if ( !PyErr_Occurred() ) + { + idc_var->set_long(l); + break; + } + // Clear last error + PyErr_Clear(); + // Can be fit into a C unsigned long? + l = (long) PyLong_AsUnsignedLong(py_var); + if ( !PyErr_Occurred() ) + { + idc_var->set_long(l); + break; + } + PyErr_Clear(); + idc_var->set_int64(PyLong_AsLongLong(py_var)); + } while ( false ); + return rc; } //------------------------------------------------------------------------- // Parses a Python object as a long or long long bool PyW_GetNumber(PyObject *py_var, uint64 *num, bool *is_64) { - if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) - return false; + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = true; +#define SETNUM(numexpr, is64_expr) \ + do \ + { \ + if ( num != NULL ) \ + *num = numexpr; \ + if ( is_64 != NULL ) \ + *is_64 = is64_expr; \ + } while ( false ) - // Can we convert to C long? - long l = PyInt_AsLong(py_var); - if ( !PyErr_Occurred() ) + do { - if ( num != NULL ) - *num = uint64(l); - if ( is_64 != NULL ) - *is_64 = false; - return true; - } - - // Clear last error - PyErr_Clear(); - - // Can be fit into a C unsigned long? - unsigned long ul = PyLong_AsUnsignedLong(py_var); - if ( !PyErr_Occurred() ) - { - if ( num != NULL ) - *num = uint64(ul); - if ( is_64 != NULL ) - *is_64 = false; - return true; - } - PyErr_Clear(); - - // Try to parse as int64 - PY_LONG_LONG ll = PyLong_AsLongLong(py_var); - if ( !PyErr_Occurred() ) - { - if ( num != NULL ) - *num = uint64(ll); - if ( is_64 != NULL ) - *is_64 = true; - return true; - } - PyErr_Clear(); - - // Try to parse as uint64 - unsigned PY_LONG_LONG ull = PyLong_AsUnsignedLongLong(py_var); - PyObject *err = PyErr_Occurred(); - if ( err == NULL ) - { - if ( num != NULL ) - *num = uint64(ull); - if ( is_64 != NULL ) - *is_64 = true; - return true; - } - // Negative number? _And_ it with uint64(-1) - bool ok = false; - if ( err == PyExc_TypeError ) - { - PyObject *py_mask = Py_BuildValue("K", 0xFFFFFFFFFFFFFFFFull); - PyObject *py_num = PyNumber_And(py_var, py_mask); - if ( py_num != NULL && py_mask != NULL ) + if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) { - PyErr_Clear(); - ull = PyLong_AsUnsignedLongLong(py_num); - if ( !PyErr_Occurred() ) + rc = false; + break; + } + + // Can we convert to C long? + long l = PyInt_AsLong(py_var); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(l), false); + break; + } + + // Clear last error + PyErr_Clear(); + + // Can be fit into a C unsigned long? + unsigned long ul = PyLong_AsUnsignedLong(py_var); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(ul), false); + break; + } + PyErr_Clear(); + + // Try to parse as int64 + PY_LONG_LONG ll = PyLong_AsLongLong(py_var); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(ll), true); + break; + } + PyErr_Clear(); + + // Try to parse as uint64 + unsigned PY_LONG_LONG ull = PyLong_AsUnsignedLongLong(py_var); + PyObject *err = PyErr_Occurred(); + if ( err == NULL ) + { + SETNUM(uint64(ull), true); + break; + } + // Negative number? _And_ it with uint64(-1) + rc = false; + if ( err == PyExc_TypeError ) + { + newref_t py_mask(Py_BuildValue("K", 0xFFFFFFFFFFFFFFFFull)); + newref_t py_num(PyNumber_And(py_var, py_mask.o)); + if ( py_num != NULL && py_mask != NULL ) { - if ( num != NULL ) - *num = uint64(ull); - if ( is_64 != NULL ) - *is_64 = true; - ok = true; + PyErr_Clear(); + ull = PyLong_AsUnsignedLongLong(py_num.o); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(ull), true); + rc = true; + } } } - Py_XDECREF(py_num); - Py_XDECREF(py_mask); - } - PyErr_Clear(); - return ok; + PyErr_Clear(); + } while ( false ); + return rc; +#undef SETNUM } //------------------------------------------------------------------------- // Checks if a given object is of sequence type bool PyW_IsSequenceType(PyObject *obj) { - if ( !PySequence_Check(obj) ) - return false; - - Py_ssize_t sz = PySequence_Size(obj); - if ( sz == -1 || PyErr_Occurred() != NULL ) + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = true; + do { - PyErr_Clear(); - return false; - } - return true; + if ( !PySequence_Check(obj) ) + { + rc = false; + break; + } + + Py_ssize_t sz = PySequence_Size(obj); + if ( sz == -1 || PyErr_Occurred() != NULL ) + { + PyErr_Clear(); + rc = false; + break; + } + } while ( false ); + return rc; } //------------------------------------------------------------------------- // Returns the string representation of an object bool PyW_ObjectToString(PyObject *obj, qstring *out) { - PyObject *py_str = PyObject_Str(obj); - if ( py_str != NULL ) - { - *out = PyString_AsString(py_str); - Py_DECREF(py_str); - return true; - } + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_str(PyObject_Str(obj)); + bool ok = py_str != NULL; + if ( ok ) + *out = PyString_AsString(py_str.o); else - { out->qclear(); - return false; - } + return ok; } //-------------------------------------------------------------------------- @@ -503,6 +516,8 @@ bool PyW_ObjectToString(PyObject *obj, qstring *out) // exception string bool PyW_GetError(qstring *out, bool clear_err) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyErr_Occurred() == NULL ) return false; @@ -523,23 +538,18 @@ bool PyW_GetError(qstring *out, bool clear_err) PyErr_Restore(err_type, err_value, err_traceback); // Resolve FormatExc() - PyObject *py_fmtexc = get_idaapi_attr(S_IDAAPI_FORMATEXC); + ref_t py_fmtexc(get_idaapi_attr(S_IDAAPI_FORMATEXC)); // Helper there? if ( py_fmtexc != NULL ) { // Call helper - PYW_GIL_ENSURE; py_ret = PyObject_CallFunctionObjArgs( - py_fmtexc, + py_fmtexc.o, err_type, err_value, err_traceback, NULL); - PYW_GIL_RELEASE; - - // Dispose helper reference - Py_DECREF(py_fmtexc); } // Clear the error @@ -576,6 +586,8 @@ bool PyW_GetError(qstring *out, bool clear_err) //------------------------------------------------------------------------- bool PyW_GetError(char *buf, size_t bufsz, bool clear_err) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + qstring s; if ( !PyW_GetError(&s, clear_err) ) return false; @@ -589,6 +601,8 @@ bool PyW_GetError(char *buf, size_t bufsz, bool clear_err) // This method is used to display errors that occurred in a callback bool PyW_ShowCbErr(const char *cb_name) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + static qstring err_str; if ( !PyW_GetError(&err_str) ) return false; @@ -600,10 +614,11 @@ bool PyW_ShowCbErr(const char *cb_name) //--------------------------------------------------------------------------- void *pyobj_get_clink(PyObject *pyobj) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Try to query the link attribute - PyObject *attr = PyW_TryGetAttrString(pyobj, S_CLINK_NAME); - void *t = attr != NULL && PyCObject_Check(attr) ? PyCObject_AsVoidPtr(attr) : NULL; - Py_XDECREF(attr); + ref_t attr(PyW_TryGetAttrString(pyobj, S_CLINK_NAME)); + void *t = attr != NULL && PyCObject_Check(attr.o) ? PyCObject_AsVoidPtr(attr.o) : NULL; return t; } @@ -624,8 +639,10 @@ def parse_command_line(cmdline): */ static PyObject *py_parse_command_line(const char *cmdline) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + qstrvec_t args; - if ( parse_command_line2(cmdline, &args, NULL) == 0 ) + if ( parse_command_line3(cmdline, &args, NULL, LP_PATH_WITH_ARGS) == 0 ) Py_RETURN_NONE; PyObject *py_list = PyList_New(args.size()); @@ -727,6 +744,7 @@ static bool qstrvec_t_assign(PyObject *self, PyObject *other) static PyObject *qstrvec_t_addressof(PyObject *self, size_t idx) { + PYW_GIL_CHECK_LOCKED_SCOPE(); qstrvec_t *sv = qstrvec_t_get_clink(self); if ( sv == NULL || idx >= sv->size() ) Py_RETURN_NONE; @@ -751,6 +769,7 @@ static bool qstrvec_t_from_list( PyObject *self, PyObject *py_list) { + PYW_GIL_CHECK_LOCKED_SCOPE(); qstrvec_t *sv = qstrvec_t_get_clink(self); return sv == NULL ? false : PyW_PyListToStrVec(py_list, *sv); } @@ -763,6 +782,7 @@ static size_t qstrvec_t_size(PyObject *self) static PyObject *qstrvec_t_get(PyObject *self, size_t idx) { + PYW_GIL_CHECK_LOCKED_SCOPE(); qstrvec_t *sv = qstrvec_t_get_clink(self); if ( sv == NULL || idx >= sv->size() ) Py_RETURN_NONE; diff --git a/pywraps/py_idaapi.py b/pywraps/py_idaapi.py index 8da0f0f..1a65807 100644 --- a/pywraps/py_idaapi.py +++ b/pywraps/py_idaapi.py @@ -18,6 +18,36 @@ import os import sys import bisect import __builtin__ +import imp + +def require(modulename): + """ + Load, or reload a module. + + When under heavy development, a user's tool might consist of multiple + modules. If those are imported using the standard 'import' mechanism, + there is no guarantee that the Python implementation will re-read + and re-evaluate the module's Python code. In fact, it usually doesn't. + What should be done instead is 'reload()'-ing that module. + + This is a simple helper function that will do just that: In case the + module doesn't exist, it 'import's it, and if it does exist, + 'reload()'s it. + + For more information, see: . + """ + if modulename in sys.modules.keys(): + reload(sys.modules[modulename]) + else: + import importlib + import inspect + m = importlib.import_module(modulename) + frame_obj, filename, line_number, function_name, lines, index = inspect.stack()[1] + importer_module = inspect.getmodule(frame_obj) + if importer_module is None: # No importer module; called from command line + importer_module = sys.modules['__main__'] + setattr(importer_module, modulename, m) + sys.modules[modulename] = m # ----------------------------------------------------------------------- @@ -403,11 +433,7 @@ def IDAPython_ExecScript(script, g): if len(scriptpath) and scriptpath not in sys.path: sys.path.append(scriptpath) - # Save the argv, path, I/O and base modules for later cleanup argv = sys.argv - path = sys.path - stdio = [sys.stdin, sys.stdout, sys.stderr] - basemodules = sys.modules.copy() sys.argv = [ script ] # Adjust the __file__ path in the globals we pass to the script @@ -421,18 +447,65 @@ def IDAPython_ExecScript(script, g): PY_COMPILE_ERR = "%s\n%s" % (str(e), traceback.format_exc()) print(PY_COMPILE_ERR) finally: - # Restore the globals to the state before the script was run + # Restore state g['__file__'] = old__file__ - sys.argv = argv - sys.path = path - sys.stdin, sys.stdout, sys.stderr = stdio - # Clean up the modules loaded by the script - for module in sys.modules.keys(): - if not module in basemodules: - del sys.modules[module] + return PY_COMPILE_ERR +# ------------------------------------------------------------ +def IDAPython_LoadProcMod(script, g): + """ + Load processor module. + """ + pname = g['__name__'] if g and g.has_key("__name__") else '__main__' + parent = sys.modules[pname] + + scriptpath, scriptname = os.path.split(script) + if len(scriptpath) and scriptpath not in sys.path: + sys.path.append(scriptpath) + + procmod_name = os.path.splitext(scriptname)[0] + procobj = None + fp = None + try: + fp, pathname, description = imp.find_module(procmod_name) + procmod = imp.load_module(procmod_name, fp, pathname, description) + if parent: + setattr(parent, procmod_name, procmod) + # export attrs from parent to processor module + parent_attrs = getattr(parent, '__all__', + (attr for attr in dir(parent) if not attr.startswith('_'))) + for pa in parent_attrs: + setattr(procmod, pa, getattr(parent, pa)) + # instantiate processor object + if getattr(procmod, 'PROCESSOR_ENTRY', None): + procobj = procmod.PROCESSOR_ENTRY() + PY_COMPILE_ERR = None + except Exception as e: + PY_COMPILE_ERR = "%s\n%s" % (str(e), traceback.format_exc()) + print(PY_COMPILE_ERR) + finally: + if fp: fp.close() + + sys.path.remove(scriptpath) + + return (PY_COMPILE_ERR, procobj) + +# ------------------------------------------------------------ +def IDAPython_UnLoadProcMod(script, g): + """ + Unload processor module. + """ + pname = g['__name__'] if g and g.has_key("__name__") else '__main__' + parent = sys.modules[pname] + + scriptname = os.path.split(script)[1] + procmod_name = os.path.splitext(scriptname)[0] + if getattr(parent, procmod_name, None): + delattr(parent, procmod_name) + del sys.modules[procmod_name] + PY_COMPILE_ERR = None return PY_COMPILE_ERR # ---------------------------------------------------------------------- diff --git a/pywraps/py_idaview.hpp b/pywraps/py_idaview.hpp new file mode 100644 index 0000000..12f31ea --- /dev/null +++ b/pywraps/py_idaview.hpp @@ -0,0 +1,75 @@ +#ifndef __PY_IDA_VIEW__ +#define __PY_IDA_VIEW__ + +// +class py_idaview_t : public py_customidamemo_t +{ + typedef py_customidamemo_t inherited; + +public: + static bool Bind(PyObject *self); +}; + +//------------------------------------------------------------------------- +bool py_idaview_t::Bind(PyObject *self) +{ + // Already a py_idaview_t associated to this object? + py_idaview_t *_this = view_extract_this(self); + if ( _this != NULL ) + return false; + + qstring title; + if ( !PyW_GetStringAttr(self, S_M_TITLE, &title) ) + return false; + + // Get the IDAView associated to this TForm + TForm *tform = find_tform(title.c_str()); + if ( tform == NULL ) + return false; + TCustomControl *v = get_tform_idaview(tform); + if ( v == NULL ) + return false; + + // Get unique py_idaview_t associated to that tform + py_idaview_t *py_view; + TCustomControl *found_view; + if ( lookup_info.find_by_form(&found_view, (py_customidamemo_t**) &py_view, tform) ) + { + // If we have a py_idaview_t for that form, ensure it has + // the expected view. + QASSERT(30451, found_view == v); + } + else + { + py_view = new py_idaview_t(); + lookup_info.add(tform, v, py_view); + } + + // Finally, bind: + // py_idaview_t <=> IDAViewWrapper + // py_idaview_t => TCustomControl + bool ok = py_view->bind(self, v); + if ( ok ) + { + ok = py_view->collect_pyobject_callbacks(self); + if ( !ok ) + delete py_view; + } + return ok; +} + +//------------------------------------------------------------------------- +bool pyidag_bind(PyObject *self) +{ + return py_idaview_t::Bind(self); +} + +// + +//-------------------------------------------------------------------------- + +// +bool pyidag_bind(PyObject *self); +// + +#endif // __PY_IDA_VIEW__ diff --git a/pywraps/py_idaview.py b/pywraps/py_idaview.py new file mode 100644 index 0000000..95b606a --- /dev/null +++ b/pywraps/py_idaview.py @@ -0,0 +1,16 @@ + +# +class IDAViewWrapper(CustomIDAMemo): + """This class wraps access to native IDA views. See kernwin.hpp file""" + def __init__(self, title): + """ + Constructs the IDAViewWrapper object around the view + whose title is 'title'. + + @param title: The title of the existing IDA view. E.g., 'IDA View-A' + """ + self._title = title + + def Bind(self): + return _idaapi.pyidag_bind(self) +# diff --git a/pywraps/py_idp.hpp b/pywraps/py_idp.hpp index f8c0b82..83f9b81 100644 --- a/pywraps/py_idp.hpp +++ b/pywraps/py_idp.hpp @@ -33,12 +33,17 @@ static PyObject *AssembleLine( { int inslen; char buf[MAXSTR]; + bool ok = false; if (ph.notify != NULL && - (inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf)) > 0) + (inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf)) > 0) { - return PyString_FromStringAndSize(buf, inslen); + ok = true; } - Py_RETURN_NONE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return PyString_FromStringAndSize(buf, inslen); + else + Py_RETURN_NONE; } //--------------------------------------------------------------------------- @@ -66,17 +71,20 @@ bool assemble( { int inslen; char buf[MAXSTR]; - + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = false; + Py_BEGIN_ALLOW_THREADS; if (ph.notify != NULL) { - inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf); + inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf); if (inslen > 0) { patch_many_bytes(ea, buf, inslen); - return true; + rc = true; } } - return false; + Py_END_ALLOW_THREADS; + return rc; } //------------------------------------------------------------------------- @@ -318,6 +326,7 @@ def ph_get_instruc(): static PyObject *ph_get_instruc() { Py_ssize_t i = 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *py_result = PyTuple_New(ph.instruc_end - ph.instruc_start); for ( const instruc_t *p = ph.instruc + ph.instruc_start, *end = ph.instruc + ph.instruc_end; p != end; @@ -341,10 +350,10 @@ def ph_get_regnames(): static PyObject *ph_get_regnames() { Py_ssize_t i = 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *py_result = PyList_New(ph.regsNum); for ( Py_ssize_t i=0; icustom_outop(py_obj) ? 2 : 0; - Py_XDECREF(py_obj); + ret = proxy->custom_outop(py_obj.o) ? 2 : 0; break; } case processor_t::custom_mnem: { + PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *py_ret = proxy->custom_mnem(); if ( py_ret != NULL && PyString_Check(py_ret) ) { @@ -1009,6 +1029,7 @@ int idaapi IDP_Callback(void *ud, int notification_code, va_list va) // Extract user buffer (we hardcode the MAXSTR size limit) uchar *bin = va_arg(va, uchar *); // Call python + PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *py_buffer = proxy->assemble(ea, cs, ip, use32, line); if ( py_buffer != NULL && PyString_Check(py_buffer) ) { @@ -1328,6 +1349,7 @@ int idaapi IDP_Callback(void *ud, int notification_code, va_list va) catch (Swig::DirectorException &e) { msg("Exception in IDP Hook function: %s\n", e.getMessage()); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( PyErr_Occurred() ) PyErr_Print(); } @@ -1337,6 +1359,9 @@ int idaapi IDP_Callback(void *ud, int notification_code, va_list va) //--------------------------------------------------------------------------- int idaapi IDB_Callback(void *ud, int notification_code, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + class IDB_Hooks *proxy = (class IDB_Hooks *)ud; ea_t ea, ea2; bool repeatable_cmt; @@ -1508,10 +1533,9 @@ int idaapi IDB_Callback(void *ud, int notification_code, va_list va) catch (Swig::DirectorException &e) { msg("Exception in IDB Hook function: %s\n", e.getMessage()); + PYW_GIL_CHECK_LOCKED_SCOPE(); if (PyErr_Occurred()) - { PyErr_Print(); - } } return 0; } diff --git a/pywraps/py_kernwin.hpp b/pywraps/py_kernwin.hpp index 91517d8..b882ee2 100644 --- a/pywraps/py_kernwin.hpp +++ b/pywraps/py_kernwin.hpp @@ -24,6 +24,8 @@ def register_timer(interval, callback): */ static PyObject *py_register_timer(int interval, PyObject *py_callback) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( py_callback == NULL || !PyCallable_Check(py_callback) ) Py_RETURN_NONE; @@ -32,17 +34,15 @@ static PyObject *py_register_timer(int interval, PyObject *py_callback) { static int idaapi callback(void *ud) { + PYW_GIL_GET; py_timer_ctx_t *ctx = (py_timer_ctx_t *)ud; - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallFunctionObjArgs(ctx->pycallback, NULL); - int ret = py_result == NULL ? -1 : PyLong_AsLong(py_result); - Py_XDECREF(py_result); - PYW_GIL_RELEASE; + newref_t py_result(PyObject_CallFunctionObjArgs(ctx->pycallback, NULL)); + int ret = py_result == NULL ? -1 : PyLong_AsLong(py_result.o); // Timer has been unregistered? if ( ret == -1 ) { - // Fee the context + // Free the context Py_DECREF(ctx->pycallback); delete ctx; } @@ -83,6 +83,8 @@ def unregister_timer(timer_obj): */ static PyObject *py_unregister_timer(PyObject *py_timerctx) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( py_timerctx == NULL || !PyCObject_Check(py_timerctx) ) Py_RETURN_FALSE; @@ -111,6 +113,7 @@ def choose_idasgn(): static PyObject *py_choose_idasgn() { char *name = choose_idasgn(); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( name == NULL ) { Py_RETURN_NONE; @@ -140,6 +143,7 @@ static PyObject *py_get_highlighted_identifier(int flags = 0) { char buf[MAXSTR]; bool ok = get_highlighted_identifier(buf, sizeof(buf), flags); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !ok ) Py_RETURN_NONE; else @@ -157,6 +161,7 @@ static int py_load_custom_icon_data(PyObject *data, const char *format) { Py_ssize_t len; char *s; + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( PyString_AsStringAndSize(data, &s, &len) == -1 ) return 0; else @@ -191,6 +196,7 @@ def asktext(max_text, defval, prompt): */ PyObject *py_asktext(int max_text, const char *defval, const char *prompt) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( max_text <= 0 ) Py_RETURN_NONE; @@ -231,6 +237,26 @@ ea_t py_str2ea(const char *str, ea_t screenEA = BADADDR) return ok ? ea : BADADDR; } +//------------------------------------------------------------------------ +/* +# +def str2user(str): + """ + Insert C-style escape characters to string + + @return: new string with escape characters inserted + """ + pass +# +*/ +PyObject *py_str2user(const char *str) +{ + qstring qstr(str); + qstring retstr; + qstr2user(&retstr, qstr); + return PyString_FromString(retstr.c_str()); +} + //------------------------------------------------------------------------ /* # @@ -265,6 +291,7 @@ def del_menu_item(menu_ctx): */ static bool py_del_menu_item(PyObject *py_ctx) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(py_ctx) ) return false; @@ -297,6 +324,7 @@ def del_hotkey(ctx): */ bool py_del_hotkey(PyObject *pyctx) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(pyctx) ) return false; @@ -327,6 +355,7 @@ def add_hotkey(hotkey, pyfunc): */ PyObject *py_add_hotkey(const char *hotkey, PyObject *pyfunc) { + PYW_GIL_CHECK_LOCKED_SCOPE(); // Make sure a callable was passed if ( !PyCallable_Check(pyfunc) ) return NULL; @@ -414,6 +443,7 @@ static PyObject *py_add_menu_item( PyObject *pyfunc, PyObject *args) { + PYW_GIL_CHECK_LOCKED_SCOPE(); bool no_args; // No slash in the menu path? @@ -512,55 +542,55 @@ def execute_sync(callable, reqf): //------------------------------------------------------------------------ static int py_execute_sync(PyObject *py_callable, int reqf) { - // Not callable? - if ( !PyCallable_Check(py_callable) ) - return -1; - - struct py_exec_request_t: exec_request_t + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc = -1; + // Callable? + if ( PyCallable_Check(py_callable) ) { - PyObject *py_callable; - bool no_wait; - virtual int idaapi execute() + struct py_exec_request_t : exec_request_t { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallFunctionObjArgs(py_callable, NULL); - int r = py_result == NULL || !PyInt_Check(py_result) ? -1 : PyInt_AsLong(py_result); - Py_XDECREF(py_result); - PYW_GIL_RELEASE; + ref_t py_callable; + virtual int idaapi execute() + { + PYW_GIL_GET; + newref_t py_result(PyObject_CallFunctionObjArgs(py_callable.o, NULL)); + int ret = py_result == NULL || !PyInt_Check(py_result.o) + ? -1 + : PyInt_AsLong(py_result.o); + // if the requesting thread decided not to wait for the request to + // complete, we have to self-destroy, nobody else will do it + if ( (code & MFF_NOWAIT) != 0 ) + delete this; + return ret; + } + py_exec_request_t(PyObject *pyc) + { + // No need to GIL-ensure here, since this is created + // within the py_execute_sync() scope. + py_callable = borref_t(pyc); + } + virtual ~py_exec_request_t() + { + // Need to GIL-ensure here, since this might be called + // from the main thread. + PYW_GIL_GET; + py_callable = ref_t(); // Release callable + } + }; + py_exec_request_t *req = new py_exec_request_t(py_callable); - // Free this request - if ( no_wait ) - delete this; - - return r; - } - py_exec_request_t(PyObject *pyc, bool no_wait): - py_callable(pyc), no_wait(no_wait) - { - // Take reference to the callable - Py_INCREF(py_callable); - } - virtual ~py_exec_request_t() - { - // Release callable - Py_XDECREF(py_callable); - } - }; - - bool no_wait = (reqf & MFF_NOWAIT) != 0; - - // Allocate a request - py_exec_request_t *req = new py_exec_request_t(py_callable, no_wait); - - // Execute it - int r = execute_sync(*req, reqf); - - // Delete only if NOWAIT was not specified - // (Otherwise the request will delete itself) - if ( !no_wait ) - delete req; - - return r; + // Release GIL before executing, or if this is running in the + // non-main thread, this will wait on the req.sem, while the main + // thread might be waiting for the GIL to be available. + Py_BEGIN_ALLOW_THREADS; + rc = execute_sync(*req, reqf); + Py_END_ALLOW_THREADS; + // destroy the request once it is finished. exception: NOWAIT requests + // will be handled in the future, so do not destroy them yet! + if ( (reqf & MFF_NOWAIT) == 0 ) + delete req; + } + return rc; } //------------------------------------------------------------------------ @@ -585,23 +615,22 @@ static bool py_execute_ui_requests(PyObject *py_list) struct py_ui_request_t: public ui_request_t { private: - ppyobject_vec_t py_callables; + ref_vec_t py_callables; size_t py_callable_idx; static int idaapi s_py_list_walk_cb( - PyObject *py_item, - Py_ssize_t index, - void *ud) + const ref_t &py_item, + Py_ssize_t index, + void *ud) { + PYW_GIL_CHECK_LOCKED_SCOPE(); // Not callable? Terminate iteration - if ( !PyCallable_Check(py_item) ) + if ( !PyCallable_Check(py_item.o) ) return CIP_FAILED; // Append this callable and increment its reference py_ui_request_t *_this = (py_ui_request_t *)ud; _this->py_callables.push_back(py_item); - Py_INCREF(py_item); - return CIP_OK; } public: @@ -611,14 +640,13 @@ static bool py_execute_ui_requests(PyObject *py_list) virtual bool idaapi run() { - // Get callable - PyObject *py_callable = py_callables.at(py_callable_idx); + PYW_GIL_GET; - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallFunctionObjArgs(py_callable, NULL); - bool reschedule = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - PYW_GIL_RELEASE; + // Get callable + ref_t py_callable = py_callables.at(py_callable_idx); + bool reschedule; + newref_t py_result(PyObject_CallFunctionObjArgs(py_callable.o, NULL)); + reschedule = py_result != NULL && PyObject_IsTrue(py_result.o); // No rescheduling? Then advance to the next callable if ( !reschedule ) @@ -640,13 +668,7 @@ static bool py_execute_ui_requests(PyObject *py_list) virtual idaapi ~py_ui_request_t() { - // Release all callables - for ( ppyobject_vec_t::const_iterator it=py_callables.begin(); - it != py_callables.end(); - ++it ) - { - Py_XDECREF(*it); - } + py_callables.clear(); } }; @@ -809,6 +831,7 @@ public: virtual PyObject *get_ea_hint(ea_t /*ea*/) { + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_RETURN_NONE; }; }; @@ -821,6 +844,8 @@ public: //--------------------------------------------------------------------------- int idaapi UI_Callback(void *ud, int notification_code, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; UI_Hooks *proxy = (UI_Hooks *)ud; int ret = 0; try @@ -857,6 +882,7 @@ int idaapi UI_Callback(void *ud, int notification_code, va_list va) char *_buf; Py_ssize_t _len; + PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *py_str = proxy->get_ea_hint(ea); if ( py_str != NULL && PyString_Check(py_str) @@ -872,6 +898,7 @@ int idaapi UI_Callback(void *ud, int notification_code, va_list va) catch (Swig::DirectorException &e) { msg("Exception in UI Hook function: %s\n", e.getMessage()); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( PyErr_Occurred() ) PyErr_Print(); } @@ -881,15 +908,15 @@ int idaapi UI_Callback(void *ud, int notification_code, va_list va) //------------------------------------------------------------------------ bool idaapi py_menu_item_callback(void *userdata) { + PYW_GIL_GET; + // userdata is a tuple of ( func, args ) // func and args are borrowed references from userdata PyObject *func = PyTuple_GET_ITEM(userdata, 0); PyObject *args = PyTuple_GET_ITEM(userdata, 1); // Call the python function - PYW_GIL_ENSURE; - PyObject *result = PyEval_CallObject(func, args); - PYW_GIL_RELEASE; + newref_t result(PyEval_CallObject(func, args)); // We cannot raise an exception in the callback, just print it. if ( result == NULL ) @@ -898,10 +925,8 @@ bool idaapi py_menu_item_callback(void *userdata) return false; } - bool ret = PyObject_IsTrue(result) != 0; - Py_DECREF(result); - return ret; + return PyObject_IsTrue(result.o) != 0; } // -#endif \ No newline at end of file +#endif diff --git a/pywraps/py_lines.hpp b/pywraps/py_lines.hpp index 9c7f962..1476a4f 100644 --- a/pywraps/py_lines.hpp +++ b/pywraps/py_lines.hpp @@ -12,10 +12,12 @@ static void idaapi s_py_get_user_defined_prefix( char *buf, size_t bufsize) { - PyObject *py_ret = PyObject_CallFunction( - py_get_user_defined_prefix, - PY_FMT64 "iis" PY_FMT64, - ea, lnnum, indent, line, bufsize); + PYW_GIL_GET; + newref_t py_ret( + PyObject_CallFunction( + py_get_user_defined_prefix, + PY_FMT64 "iis" PY_FMT64, + ea, lnnum, indent, line, bufsize)); // Error? Display it // No error? Copy the buffer @@ -23,14 +25,13 @@ static void idaapi s_py_get_user_defined_prefix( { Py_ssize_t py_len; char *py_str; - if ( PyString_AsStringAndSize(py_ret, &py_str, &py_len) != -1 ) + if ( PyString_AsStringAndSize(py_ret.o, &py_str, &py_len) != -1 ) { memcpy(buf, py_str, qmin(bufsize, py_len)); if ( py_len < bufsize ) buf[py_len] = '\0'; } } - Py_XDECREF(py_ret); } // @@ -65,6 +66,7 @@ def set_user_defined_prefix(width, callback): */ static PyObject *py_set_user_defined_prefix(size_t width, PyObject *pycb) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( width == 0 || pycb == Py_None ) { // Release old callback reference @@ -110,6 +112,7 @@ def tag_remove(colstr): */ PyObject *py_tag_remove(const char *instr) { + PYW_GIL_CHECK_LOCKED_SCOPE(); size_t sz = strlen(instr); char *buf = new char[sz + 5]; if ( buf == NULL ) @@ -135,6 +138,7 @@ PyObject *py_tag_addr(ea_t ea) { char buf[100]; tag_addr(buf, buf + sizeof(buf), ea); + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyString_FromString(buf); } @@ -180,6 +184,7 @@ PyObject *py_generate_disassembly( bool as_stack, bool notags) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( max_lines <= 0 ) Py_RETURN_NONE; @@ -188,7 +193,7 @@ PyObject *py_generate_disassembly( int lnnum; int nlines = generate_disassembly(ea, lines, max_lines, &lnnum, as_stack); - PyObject *py_tuple = PyTuple_New(nlines); + newref_t py_tuple(PyTuple_New(nlines)); for ( int i=0; i #endif diff --git a/pywraps/py_linput.hpp b/pywraps/py_linput.hpp index 12a5fda..e5d000b 100644 --- a/pywraps/py_linput.hpp +++ b/pywraps/py_linput.hpp @@ -110,6 +110,7 @@ private: //-------------------------------------------------------------------------- void _from_cobject(PyObject *pycobject) { + PYW_GIL_CHECK_LOCKED_SCOPE(); this->set_linput((linput_t *)PyCObject_AsVoidPtr(pycobject)); } @@ -133,6 +134,7 @@ public: //-------------------------------------------------------------------------- loader_input_t(PyObject *pycobject = NULL): li(NULL), own(OWN_NONE), __idc_cvt_id__(PY_ICID_OPAQUE) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( pycobject != NULL && PyCObject_Check(pycobject) ) _from_cobject(pycobject); } @@ -143,11 +145,13 @@ public: if ( li == NULL ) return; + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; if ( own == OWN_CREATE ) close_linput(li); else if ( own == OWN_FROM_FP ) unmake_linput(li); - + Py_END_ALLOW_THREADS; li = NULL; own = OWN_NONE; } @@ -162,14 +166,17 @@ public: bool open(const char *filename, bool remote = false) { close(); + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; li = open_linput(filename, remote); - if ( li == NULL ) - return false; - - // Save file name - fn = filename; - own = OWN_CREATE; - return true; + if ( li != NULL ) + { + // Save file name + fn = filename; + own = OWN_CREATE; + } + Py_END_ALLOW_THREADS; + return li != NULL; } //-------------------------------------------------------------------------- @@ -193,6 +200,7 @@ public: // This method can be used to pass a linput_t* from C code static loader_input_t *from_cobject(PyObject *pycobject) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(pycobject) ) return NULL; loader_input_t *l = new loader_input_t(); @@ -203,14 +211,18 @@ public: //-------------------------------------------------------------------------- static loader_input_t *from_fp(FILE *fp) { + PYW_GIL_GET; + loader_input_t *l = NULL; + Py_BEGIN_ALLOW_THREADS; linput_t *fp_li = make_linput(fp); - if ( fp_li == NULL ) - return NULL; - - loader_input_t *l = new loader_input_t(); - l->own = OWN_FROM_FP; - l->fn.sprnt("", fp); - l->li = fp_li; + if ( fp_li != NULL ) + { + l = new loader_input_t(); + l->own = OWN_FROM_FP; + l->fn.sprnt("", fp); + l->li = fp_li; + } + Py_END_ALLOW_THREADS; return l; } @@ -223,37 +235,55 @@ public: //-------------------------------------------------------------------------- bool open_memory(ea_t start, asize_t size = 0) { - linput_t *l = create_memory_linput(start, size); - if ( l == NULL ) - return false; - close(); - li = l; - fn = ""; - own = OWN_CREATE; - return true; + PYW_GIL_GET; + linput_t *l; + Py_BEGIN_ALLOW_THREADS; + l = create_memory_linput(start, size); + if ( l != NULL ) + { + close(); + li = l; + fn = ""; + own = OWN_CREATE; + } + Py_END_ALLOW_THREADS; + return l != NULL; } //-------------------------------------------------------------------------- int32 seek(int32 pos, int whence = SEEK_SET) { - return qlseek(li, pos, whence); + int32 r; + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; + r = qlseek(li, pos, whence); + Py_END_ALLOW_THREADS; + return r; } //-------------------------------------------------------------------------- int32 tell() { - return qltell(li); + int32 r; + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; + r = qltell(li); + Py_END_ALLOW_THREADS; + return r; } //-------------------------------------------------------------------------- PyObject *getz(size_t sz, int32 fpos = -1) { + PYW_GIL_CHECK_LOCKED_SCOPE(); do { char *buf = (char *) malloc(sz + 5); if ( buf == NULL ) break; + Py_BEGIN_ALLOW_THREADS; qlgetz(li, fpos, buf, sz); + Py_END_ALLOW_THREADS; PyObject *ret = PyString_FromString(buf); free(buf); return ret; @@ -264,12 +294,17 @@ public: //-------------------------------------------------------------------------- PyObject *gets(size_t len) { + PYW_GIL_CHECK_LOCKED_SCOPE(); do { char *buf = (char *) malloc(len + 5); if ( buf == NULL ) break; - if ( qlgets(buf, len, li) == NULL ) + bool ok; + Py_BEGIN_ALLOW_THREADS; + ok = qlgets(buf, len, li) != NULL; + Py_END_ALLOW_THREADS; + if ( !ok ) { free(buf); break; @@ -284,12 +319,16 @@ public: //-------------------------------------------------------------------------- PyObject *read(size_t size) { + PYW_GIL_CHECK_LOCKED_SCOPE(); do { char *buf = (char *) malloc(size + 5); if ( buf == NULL ) break; - ssize_t r = qlread(li, buf, size); + ssize_t r; + Py_BEGIN_ALLOW_THREADS; + r = qlread(li, buf, size); + Py_END_ALLOW_THREADS; if ( r == -1 ) { free(buf); @@ -311,12 +350,16 @@ public: //-------------------------------------------------------------------------- PyObject *readbytes(size_t size, bool big_endian) { + PYW_GIL_CHECK_LOCKED_SCOPE(); do { char *buf = (char *) malloc(size + 5); if ( buf == NULL ) break; - int r = lreadbytes(li, buf, size, big_endian); + int r; + Py_BEGIN_ALLOW_THREADS; + r = lreadbytes(li, buf, size, big_endian); + Py_END_ALLOW_THREADS; if ( r == -1 ) { free(buf); @@ -332,25 +375,38 @@ public: //-------------------------------------------------------------------------- int file2base(int32 pos, ea_t ea1, ea_t ea2, int patchable) { - return ::file2base(li, pos, ea1, ea2, patchable); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = ::file2base(li, pos, ea1, ea2, patchable); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- int32 size() { - return qlsize(li); + int32 rc; + Py_BEGIN_ALLOW_THREADS; + rc = qlsize(li); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- PyObject *filename() { + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyString_FromString(fn.c_str()); } //-------------------------------------------------------------------------- PyObject *get_char() { - int ch = qlgetc(li); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int ch; + Py_BEGIN_ALLOW_THREADS; + ch = qlgetc(li); + Py_END_ALLOW_THREADS; if ( ch == EOF ) Py_RETURN_NONE; return Py_BuildValue("c", ch); diff --git a/pywraps/py_loader.hpp b/pywraps/py_loader.hpp index b122487..4eb78ff 100644 --- a/pywraps/py_loader.hpp +++ b/pywraps/py_loader.hpp @@ -25,8 +25,11 @@ static int py_mem2base(PyObject *py_mem, ea_t ea, long fpos = -1) { Py_ssize_t len; char *buf; - if ( PyString_AsStringAndSize(py_mem, &buf, &len) == -1 ) - return 0; + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyString_AsStringAndSize(py_mem, &buf, &len) == -1 ) + return 0; + } return mem2base((void *)buf, ea, ea+len, fpos); } @@ -47,6 +50,7 @@ def load_plugin(name): static PyObject *py_load_plugin(const char *name) { plugin_t *r = load_plugin(name); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( r == NULL ) Py_RETURN_NONE; else @@ -67,10 +71,20 @@ def run_plugin(plg): */ static bool py_run_plugin(PyObject *plg, int arg) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(plg) ) + { return false; + } else - return run_plugin((plugin_t *)PyCObject_AsVoidPtr(plg), arg); + { + plugin_t *p = (plugin_t *)PyCObject_AsVoidPtr(plg); + bool rc; + Py_BEGIN_ALLOW_THREADS; + rc = run_plugin(p, arg); + Py_END_ALLOW_THREADS; + return rc; + } } // diff --git a/pywraps/py_nalt.hpp b/pywraps/py_nalt.hpp index d417e9d..8d0a795 100644 --- a/pywraps/py_nalt.hpp +++ b/pywraps/py_nalt.hpp @@ -21,53 +21,35 @@ static int idaapi py_import_enum_cb( if ( name == NULL ) name = get_true_name(BADADDR, ea, name_buf, sizeof(name_buf)); - PyObject *py_name; + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_name; if ( name == NULL ) - { - py_name = Py_None; - Py_INCREF(Py_None); - } + py_name = borref_t(Py_None); else - { - py_name = PyString_FromString(name); - } + py_name = newref_t(PyString_FromString(name)); - PyObject *py_ord = Py_BuildValue(PY_FMT64, pyul_t(ord)); - PyObject *py_ea = Py_BuildValue(PY_FMT64, pyul_t(ea)); - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallFunctionObjArgs( - (PyObject *)param, - py_ea, - py_name, - py_ord, - NULL); - PYW_GIL_RELEASE; - - int r = py_result != NULL && PyObject_IsTrue(py_result) ? 1 : 0; - - Py_DECREF(py_ea); - Py_DECREF(py_name); - Py_DECREF(py_ord); - Py_XDECREF(py_result); - - return r; + newref_t py_ord(Py_BuildValue(PY_FMT64, pyul_t(ord))); + newref_t py_ea(Py_BuildValue(PY_FMT64, pyul_t(ea))); + newref_t py_result( + PyObject_CallFunctionObjArgs( + (PyObject *)param, + py_ea.o, + py_name.o, + py_ord.o, + NULL)); + return py_result != NULL && PyObject_IsTrue(py_result.o) ? 1 : 0; } //------------------------------------------------------------------------- switch_info_ex_t *switch_info_ex_t_get_clink(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyObject_HasAttrString(self, S_CLINK_NAME) ) return NULL; switch_info_ex_t *r; - PyObject *attr = PyObject_GetAttrString(self, S_CLINK_NAME); - if ( PyCObject_Check(attr) ) - r = (switch_info_ex_t *) PyCObject_AsVoidPtr(attr); - else - r = NULL; - - Py_DECREF(attr); - return r; + newref_t attr(PyObject_GetAttrString(self, S_CLINK_NAME)); + return PyCObject_Check(attr.o) ? ((switch_info_ex_t *) PyCObject_AsVoidPtr(attr.o)) : NULL; } // @@ -86,10 +68,11 @@ def get_import_module_name(path, fname, callback): */ static PyObject *py_get_import_module_name(int mod_index) { + PYW_GIL_CHECK_LOCKED_SCOPE(); char buf[MAXSTR]; if ( !get_import_module_name(mod_index, buf, sizeof(buf)) ) Py_RETURN_NONE; - + return PyString_FromString(buf); } @@ -108,14 +91,16 @@ def get_switch_info_ex(ea): PyObject *py_get_switch_info_ex(ea_t ea) { switch_info_ex_t *ex = new switch_info_ex_t(); - PyObject *py_obj; - if ( ::get_switch_info_ex(ea, ex, sizeof(switch_info_ex_t)) <= 0 + ref_t py_obj; + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ::get_switch_info_ex(ea, ex, sizeof(switch_info_ex_t)) <= 0 || (py_obj = create_idaapi_linked_class_instance(S_PY_SWIEX_CLSNAME, ex)) == NULL ) { delete ex; Py_RETURN_NONE; } - return py_obj; + py_obj.incref(); + return py_obj.o; } //------------------------------------------------------------------------- @@ -129,7 +114,7 @@ def create_switch_xrefs(insn_ea, si): will call it for switch tables Note: Custom switch information are not supported yet. - + @param insn_ea: address of the 'indirect jump' instruction @param si: switch information @@ -157,7 +142,7 @@ idaman bool ida_export py_create_switch_xrefs( def create_switch_table(insn_ea, si): """ Create switch table from the switch information - + @param insn_ea: address of the 'indirect jump' instruction @param si: switch information @@ -173,7 +158,7 @@ idaman bool ida_export py_create_switch_table( switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); if ( swi == NULL ) return false; - + create_switch_table(insn_ea, swi); return true; } @@ -232,9 +217,9 @@ def enum_import_names(mod_index, callback): */ static int py_enum_import_names(int mod_index, PyObject *py_cb) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCallable_Check(py_cb) ) return -1; - return enum_import_names(mod_index, py_import_enum_cb, py_cb); } @@ -242,12 +227,14 @@ static int py_enum_import_names(int mod_index, PyObject *py_cb) static PyObject *switch_info_ex_t_create() { switch_info_ex_t *inst = new switch_info_ex_t(); + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyCObject_FromVoidPtr(inst, NULL); } //--------------------------------------------------------------------------- static bool switch_info_ex_t_destroy(PyObject *py_obj) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(py_obj) ) return false; switch_info_ex_t *inst = (switch_info_ex_t *) PyCObject_AsVoidPtr(py_obj); @@ -273,6 +260,7 @@ static bool switch_info_ex_t_assign(PyObject *self, PyObject *other) static PyObject *switch_info_ex_t_get_regdtyp(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue("b", (char)link->regdtyp); @@ -282,12 +270,14 @@ static void switch_info_ex_t_set_regdtyp(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); link->regdtyp = (char)PyInt_AsLong(value); } static PyObject *switch_info_ex_t_get_flags2(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue("i", link->flags2); @@ -297,12 +287,14 @@ static void switch_info_ex_t_set_flags2(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); link->flags2 = (int)PyInt_AsLong(value); } static PyObject *switch_info_ex_t_get_jcases(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue("i", link->jcases); @@ -312,12 +304,14 @@ static void switch_info_ex_t_set_jcases(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); link->jcases = (int)PyInt_AsLong(value); } static PyObject *switch_info_ex_t_get_regnum(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue("i", (int)link->regnum); @@ -327,12 +321,14 @@ static void switch_info_ex_t_set_regnum(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); link->regnum = (int)PyInt_AsLong(value); } static PyObject *switch_info_ex_t_get_flags(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue("H", (ushort)link->flags); @@ -342,12 +338,14 @@ static void switch_info_ex_t_set_flags(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); link->flags = (uint16)PyInt_AsLong(value); } static PyObject *switch_info_ex_t_get_ncases(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue("H", (uint16)link->ncases); @@ -357,12 +355,14 @@ static void switch_info_ex_t_set_ncases(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); link->ncases = (ushort)PyInt_AsLong(value); } static PyObject *switch_info_ex_t_get_defjump(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue(PY_FMT64, (pyul_t)link->defjump); @@ -372,6 +372,7 @@ static void switch_info_ex_t_set_defjump(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); uint64 v(0); PyW_GetNumber(value, &v); link->defjump = (pyul_t)v; } @@ -388,6 +389,7 @@ static void switch_info_ex_t_set_jumps(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); uint64 v(0); PyW_GetNumber(value, &v); link->jumps = (pyul_t)v; } @@ -395,6 +397,7 @@ static void switch_info_ex_t_set_jumps(PyObject *self, PyObject *value) static PyObject *switch_info_ex_t_get_elbase(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue(PY_FMT64, (pyul_t)link->elbase); @@ -405,6 +408,7 @@ static void switch_info_ex_t_set_elbase(PyObject *self, PyObject *value) if ( link == NULL ) return; uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); PyW_GetNumber(value, &v); link->elbase = (pyul_t)v; } @@ -412,6 +416,7 @@ static void switch_info_ex_t_set_elbase(PyObject *self, PyObject *value) static PyObject *switch_info_ex_t_get_startea(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue(PY_FMT64, (pyul_t)link->startea); @@ -422,6 +427,7 @@ static void switch_info_ex_t_set_startea(PyObject *self, PyObject *value) if ( link == NULL ) return; uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); PyW_GetNumber(value, &v); link->startea = (pyul_t)v; } @@ -429,6 +435,7 @@ static void switch_info_ex_t_set_startea(PyObject *self, PyObject *value) static PyObject *switch_info_ex_t_get_custom(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue(PY_FMT64, (pyul_t)link->custom); @@ -439,6 +446,7 @@ static void switch_info_ex_t_set_custom(PyObject *self, PyObject *value) if ( link == NULL ) return; uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); PyW_GetNumber(value, &v); link->custom = (pyul_t)v; } @@ -446,6 +454,7 @@ static void switch_info_ex_t_set_custom(PyObject *self, PyObject *value) static PyObject *switch_info_ex_t_get_ind_lowcase(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue(PY_FMT64, (pyul_t)link->ind_lowcase); @@ -456,6 +465,7 @@ static void switch_info_ex_t_set_ind_lowcase(PyObject *self, PyObject *value) if ( link == NULL ) return; uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); PyW_GetNumber(value, &v); link->ind_lowcase = (pyul_t)v; } @@ -463,6 +473,7 @@ static void switch_info_ex_t_set_ind_lowcase(PyObject *self, PyObject *value) static PyObject *switch_info_ex_t_get_values_lowcase(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue(PY_FMT64, (pyul_t)link->values); @@ -473,6 +484,7 @@ static void switch_info_ex_t_set_values_lowcase(PyObject *self, PyObject *value) if ( link == NULL ) return; uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); PyW_GetNumber(value, &v); link->values = (pyul_t)v; } diff --git a/pywraps/py_name.hpp b/pywraps/py_name.hpp index 7f802b9..0ca1794 100644 --- a/pywraps/py_name.hpp +++ b/pywraps/py_name.hpp @@ -5,16 +5,19 @@ PyObject *py_get_debug_names(ea_t ea1, ea_t ea2) { // Get debug names ea_name_vec_t names; + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_BEGIN_ALLOW_THREADS; get_debug_names(ea1, ea2, names); + Py_END_ALLOW_THREADS; PyObject *dict = Py_BuildValue("{}"); - if (dict == NULL) - return NULL; - - for (ea_name_vec_t::iterator it=names.begin();it!=names.end();++it) + if (dict != NULL) { - PyDict_SetItem(dict, - Py_BuildValue(PY_FMT64, it->ea), - PyString_FromString(it->name.c_str())); + for (ea_name_vec_t::iterator it=names.begin();it!=names.end();++it) + { + PyDict_SetItem(dict, + Py_BuildValue(PY_FMT64, it->ea), + PyString_FromString(it->name.c_str())); + } } return dict; } diff --git a/pywraps/py_notifywhen.hpp b/pywraps/py_notifywhen.hpp index d11039a..61fef9c 100644 --- a/pywraps/py_notifywhen.hpp +++ b/pywraps/py_notifywhen.hpp @@ -8,7 +8,7 @@ //------------------------------------------------------------------------ class pywraps_notify_when_t { - ppyobject_vec_t table[NW_EVENTSCNT]; + ref_vec_t table[NW_EVENTSCNT]; qstring err; bool in_notify; struct notify_when_args_t @@ -22,6 +22,8 @@ class pywraps_notify_when_t //------------------------------------------------------------------------ static int idaapi idp_callback(void *ud, int event_id, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; pywraps_notify_when_t *_this = (pywraps_notify_when_t *)ud; switch ( event_id ) { @@ -60,32 +62,29 @@ class pywraps_notify_when_t //------------------------------------------------------------------------ void register_callback(int slot, PyObject *py_callable) { - ppyobject_vec_t &tbl = table[slot]; - ppyobject_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, py_callable); + borref_t callable_ref(py_callable); + ref_vec_t &tbl = table[slot]; + ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); // Already added if ( it != it_end ) return; - // Increment reference - Py_INCREF(py_callable); - // Insert the element - tbl.push_back(py_callable); + tbl.push_back(callable_ref); } //------------------------------------------------------------------------ void unregister_callback(int slot, PyObject *py_callable) { - ppyobject_vec_t &tbl = table[slot]; - ppyobject_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, py_callable); + borref_t callable_ref(py_callable); + ref_vec_t &tbl = table[slot]; + ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); + // Not found? if ( it == it_end ) return; - // Decrement reference - Py_DECREF(py_callable); - // Delete the element tbl.erase(it); } @@ -101,11 +100,11 @@ public: bool deinit() { // Uninstall all objects - ppyobject_vec_t::iterator it, it_end; + ref_vec_t::iterator it, it_end; for ( int slot=0; sloto); } // ...and remove the notification return unhook_from_notification_point(HT_IDP, idp_callback, this); @@ -154,6 +153,8 @@ public: //------------------------------------------------------------------------ bool notify_va(int slot, va_list va) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Sanity bounds check! if ( slot < 0 || slot >= NW_EVENTSCNT ) return false; @@ -161,42 +162,38 @@ public: bool ok = true; in_notify = true; int old = slot == NW_OPENIDB_SLOT ? va_arg(va, int) : 0; - for (ppyobject_vec_t::iterator it = table[slot].begin(), it_end = table[slot].end(); - it != it_end; - ++it) + { - // Form the notification code - PyObject *py_code = PyInt_FromLong(1 << slot); - PyObject *py_result(NULL); - switch ( slot ) + for (ref_vec_t::iterator it = table[slot].begin(), it_end = table[slot].end(); + it != it_end; + ++it) { - case NW_CLOSEIDB_SLOT: - case NW_INITIDA_SLOT: - case NW_TERMIDA_SLOT: + // Form the notification code + newref_t py_code(PyInt_FromLong(1 << slot)); + ref_t py_result; + switch ( slot ) { - PYW_GIL_ENSURE; - py_result = PyObject_CallFunctionObjArgs(*it, py_code, NULL); - PYW_GIL_RELEASE; - break; + case NW_CLOSEIDB_SLOT: + case NW_INITIDA_SLOT: + case NW_TERMIDA_SLOT: + { + py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, NULL)); + break; + } + case NW_OPENIDB_SLOT: + { + newref_t py_old(PyInt_FromLong(old)); + py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, py_old.o, NULL)); + } + break; } - case NW_OPENIDB_SLOT: + if ( PyW_GetError(&err) || py_result == NULL ) { - PyObject *py_old = PyInt_FromLong(old); - PYW_GIL_ENSURE; - py_result = PyObject_CallFunctionObjArgs(*it, py_code, py_old, NULL); - PYW_GIL_RELEASE; - Py_DECREF(py_old); + PyErr_Clear(); + warning("notify_when(): Error occured while notifying object.\n%s", err.c_str()); + ok = false; } - break; } - Py_DECREF(py_code); - if ( PyW_GetError(&err) || py_result == NULL ) - { - PyErr_Clear(); - warning("notify_when(): Error occured while notifying object.\n%s", err.c_str()); - ok = false; - } - Py_XDECREF(py_result); } in_notify = false; @@ -211,6 +208,7 @@ public: } delayed_notify_when_list.qclear(); } + return ok; } @@ -247,6 +245,10 @@ bool pywraps_nw_notify(int slot, ...) if ( g_nw == NULL ) return false; + // Appears to be called from 'driver_notifywhen.cpp', which + // itself is called from possibly non-python code. + // I.e., we must acquire the GIL. + PYW_GIL_GET; va_list va; va_start(va, slot); bool ok = g_nw->notify_va(slot, va); @@ -261,11 +263,11 @@ bool pywraps_nw_term() { if ( g_nw == NULL ) return true; - + // If could not deinitialize then return w/o stopping nw if ( !g_nw->deinit() ) return false; - + // Cleanup delete g_nw; g_nw = NULL; @@ -296,6 +298,7 @@ def notify_when(when, callback): */ static bool notify_when(int when, PyObject *py_callable) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( g_nw == NULL || !PyCallable_Check(py_callable) ) return false; return g_nw->notify_when(when, py_callable); diff --git a/pywraps/py_plgform.hpp b/pywraps/py_plgform.hpp index cd81e39..8797766 100644 --- a/pywraps/py_plgform.hpp +++ b/pywraps/py_plgform.hpp @@ -6,11 +6,14 @@ class plgform_t { private: - PyObject *py_obj; + ref_t py_obj; TForm *form; static int idaapi s_callback(void *ud, int notification_code, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + plgform_t *_this = (plgform_t *)ud; if ( notification_code == ui_tform_visible ) { @@ -21,15 +24,12 @@ private: // G: HWND // We wrap and pass as a CObject in the hope that a Python UI framework // can unwrap a CObject and get the hwnd/widget back - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_obj, - (char *)S_ON_CREATE, "O", - PyCObject_FromVoidPtr(form, NULL)); - PYW_GIL_RELEASE; - + newref_t py_result( + PyObject_CallMethod( + _this->py_obj.o, + (char *)S_ON_CREATE, "O", + PyCObject_FromVoidPtr(form, NULL))); PyW_ShowCbErr(S_ON_CREATE); - Py_XDECREF(py_result); } } else if ( notification_code == ui_tform_invisible ) @@ -37,16 +37,14 @@ private: TForm *form = va_arg(va, TForm *); if ( form == _this->form ) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_obj, - (char *)S_ON_CLOSE, "O", - PyCObject_FromVoidPtr(form, NULL)); - PYW_GIL_RELEASE; - - PyW_ShowCbErr(S_ON_CLOSE); - Py_XDECREF(py_result); - + { + newref_t py_result( + PyObject_CallMethod( + _this->py_obj.o, + (char *)S_ON_CLOSE, "O", + PyCObject_FromVoidPtr(form, NULL))); + PyW_ShowCbErr(S_ON_CLOSE); + } _this->unhook(); } } @@ -59,11 +57,12 @@ private: form = NULL; // Call DECREF at last, since it may trigger __del__ - Py_XDECREF(py_obj); + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_obj = ref_t(); } public: - plgform_t(): py_obj(NULL), form(NULL) + plgform_t(): form(NULL) { } @@ -98,8 +97,7 @@ public: return false; } - py_obj = obj; - Py_INCREF(obj); + py_obj = borref_t(obj); if ( is_idaq() ) options |= FORM_QWIDGET; @@ -117,6 +115,7 @@ public: static PyObject *create() { + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyCObject_FromVoidPtr(new plgform_t(), destroy); } @@ -129,7 +128,7 @@ public: // //--------------------------------------------------------------------------- -#define DECL_PLGFORM plgform_t *plgform = (plgform_t *) PyCObject_AsVoidPtr(py_link); +#define DECL_PLGFORM PYW_GIL_CHECK_LOCKED_SCOPE(); plgform_t *plgform = (plgform_t *) PyCObject_AsVoidPtr(py_link); static PyObject *plgform_new() { return plgform_t::create(); @@ -155,4 +154,4 @@ static void plgform_close( #undef DECL_PLGFORM // -#endif // __PY_PLGFORM__ \ No newline at end of file +#endif // __PY_PLGFORM__ diff --git a/pywraps/py_plgform.py b/pywraps/py_plgform.py index be0bcde..dc4c5e3 100644 --- a/pywraps/py_plgform.py +++ b/pywraps/py_plgform.py @@ -59,6 +59,19 @@ class PluginForm(object): @param ctx: Context. Reference to a module that already imported QtGui module """ + if form is None: + return None + if type(form).__name__ == "SwigPyObject": + # Since 'form' is a SwigPyObject, we first need to convert it to a PyCObject. + # However, there's no easy way of doing it, so we'll use a rather brutal approach: + # converting the SwigPyObject to a 'long' (will go through 'SwigPyObject_long', + # that will return the pointer's value as a long), and then convert that value + # back to a pointer into a PyCObject. + ptr_l = long(form) + from ctypes import pythonapi, c_void_p, py_object + pythonapi.PyCObject_FromVoidPtr.restype = py_object + pythonapi.PyCObject_AsVoidPtr.argtypes = [c_void_p, c_void_p] + form = pythonapi.PyCObject_FromVoidPtr(ptr_l, 0) return ctx.QtGui.QWidget.FromCObject(form) @@ -89,7 +102,7 @@ class PluginForm(object): @return: None """ - return _idaapi.plgform_close(self.__clink__) + return _idaapi.plgform_close(self.__clink__, options) FORM_SAVE = 0x1 """Save state in desktop config""" @@ -105,4 +118,4 @@ class PluginForm(object): # plg = PluginForm() -plg.Show("This is it") \ No newline at end of file +plg.Show("This is it") diff --git a/pywraps/py_qfile.hpp b/pywraps/py_qfile.hpp index fe40d34..7b993fc 100644 --- a/pywraps/py_qfile.hpp +++ b/pywraps/py_qfile.hpp @@ -102,6 +102,7 @@ private: } inline void _from_cobject(PyObject *pycobject) { + PYW_GIL_CHECK_LOCKED_SCOPE(); _from_fp((FILE *)PyCObject_AsVoidPtr(pycobject)); } public: @@ -119,7 +120,12 @@ public: own = true; fn.qclear(); __idc_cvt_id__ = PY_ICID_OPAQUE; - if ( pycobject != NULL && PyCObject_Check(pycobject) ) + bool ok; + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + ok = pycobject != NULL && PyCObject_Check(pycobject); + } + if ( ok ) _from_cobject(pycobject); } @@ -135,7 +141,11 @@ public: if ( fp == NULL ) return; if ( own ) + { + Py_BEGIN_ALLOW_THREADS; qfclose(fp); + Py_END_ALLOW_THREADS; + } fp = NULL; own = true; } @@ -150,7 +160,9 @@ public: bool open(const char *filename, const char *mode) { close(); + Py_BEGIN_ALLOW_THREADS; fp = qfopen(filename, mode); + Py_END_ALLOW_THREADS; if ( fp == NULL ) return false; // Save file name @@ -181,7 +193,11 @@ public: //-------------------------------------------------------------------------- static qfile_t *tmpfile() { - return from_fp(qtmpfile()); + FILE *fp; + Py_BEGIN_ALLOW_THREADS; + fp = qtmpfile(); + Py_END_ALLOW_THREADS; + return from_fp(fp); } //-------------------------------------------------------------------------- @@ -193,13 +209,21 @@ public: //-------------------------------------------------------------------------- int seek(int32 offset, int whence = SEEK_SET) { - return qfseek(fp, offset, whence); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfseek(fp, offset, whence); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- int32 tell() { - return qftell(fp); + int32 rc; + Py_BEGIN_ALLOW_THREADS; + rc = qftell(fp); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- @@ -210,12 +234,17 @@ public: char *buf = (char *) malloc(size + 5); if ( buf == NULL ) break; - int r = freadbytes(fp, buf, size, big_endian); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int r; + Py_BEGIN_ALLOW_THREADS; + r = freadbytes(fp, buf, size, big_endian); + Py_END_ALLOW_THREADS; if ( r != 0 ) { free(buf); break; } + PyObject *ret = PyString_FromStringAndSize(buf, r); free(buf); return ret; @@ -231,7 +260,11 @@ public: char *buf = (char *) malloc(size + 5); if ( buf == NULL ) break; - int r = qfread(fp, buf, size); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int r; + Py_BEGIN_ALLOW_THREADS; + r = qfread(fp, buf, size); + Py_END_ALLOW_THREADS; if ( r <= 0 ) { free(buf); @@ -252,7 +285,12 @@ public: char *buf = (char *) malloc(size + 5); if ( buf == NULL ) break; - if ( qfgets(buf, size, fp) == NULL ) + PYW_GIL_CHECK_LOCKED_SCOPE(); + char *p; + Py_BEGIN_ALLOW_THREADS; + p = qfgets(buf, size, fp); + Py_END_ALLOW_THREADS; + if ( p == NULL ) { free(buf); break; @@ -267,50 +305,87 @@ public: //-------------------------------------------------------------------------- int writebytes(PyObject *py_buf, bool big_endian) { - int sz = PyString_GET_SIZE(py_buf); - void *buf = (void *)PyString_AS_STRING(py_buf); - return fwritebytes(fp, buf, sz, big_endian); + Py_ssize_t sz; + void *buf; + PYW_GIL_CHECK_LOCKED_SCOPE(); + sz = PyString_GET_SIZE(py_buf); + buf = (void *)PyString_AS_STRING(py_buf); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = fwritebytes(fp, buf, int(sz), big_endian); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- int write(PyObject *py_buf) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyString_Check(py_buf) ) return 0; - return qfwrite(fp, (void *)PyString_AS_STRING(py_buf), PyString_GET_SIZE(py_buf)); + // Just so that there is no risk that the buffer returned by + // 'PyString_AS_STRING' gets deallocated within the + // Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_buf_ref(py_buf); + void *p = (void *)PyString_AS_STRING(py_buf); + Py_ssize_t sz = PyString_GET_SIZE(py_buf); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfwrite(fp, p, sz); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- int puts(const char *str) { - return qfputs(str, fp); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfputs(str, fp); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- int32 size() { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int32 r; + Py_BEGIN_ALLOW_THREADS; int pos = qfseek(fp, 0, SEEK_END); - int32 r = qftell(fp); + r = qftell(fp); qfseek(fp, pos, SEEK_SET); + Py_END_ALLOW_THREADS; return r; } //-------------------------------------------------------------------------- int flush() { - return qflush(fp); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qflush(fp); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- PyObject *filename() { + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyString_FromString(fn.c_str()); } //-------------------------------------------------------------------------- PyObject *get_char() { - int ch = qfgetc(fp); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int ch; + Py_BEGIN_ALLOW_THREADS; + ch = qfgetc(fp); + Py_END_ALLOW_THREADS; if ( ch == EOF ) Py_RETURN_NONE; return Py_BuildValue("c", ch); @@ -319,7 +394,12 @@ public: //-------------------------------------------------------------------------- int put_char(char chr) { - return qfputc(chr, fp); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfputc(chr, fp); + Py_END_ALLOW_THREADS; + return rc; } }; // diff --git a/pywraps/py_typeinf.hpp b/pywraps/py_typeinf.hpp index d7eed11..a809ebe 100644 --- a/pywraps/py_typeinf.hpp +++ b/pywraps/py_typeinf.hpp @@ -5,22 +5,26 @@ //------------------------------------------------------------------------- PyObject *idc_parse_decl(til_t *ti, const char *decl, int flags) { - qtype fields, type; + tinfo_t tif; qstring name; - bool ok = parse_decl(ti, decl, &name, &type, &fields, flags); - if ( !ok ) - Py_RETURN_NONE; + qtype fields, type; + bool ok = parse_decl2(ti, decl, &name, &tif, flags); + if ( ok ) + ok = tif.serialize(&type, &fields, NULL, SUDT_FAST); - return Py_BuildValue("(sss)", - name.c_str(), - (char *)type.c_str(), - (char *)fields.c_str()); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("(sss)", + name.c_str(), + (char *)type.c_str(), + (char *)fields.c_str()); + Py_RETURN_NONE; } //------------------------------------------------------------------------- /* # -def get_type_size0(ti, tp): +def calc_type_size(ti, tp): """ Returns the size of a type @param ti: Type info. 'idaapi.cvar.idati' can be passed. @@ -32,25 +36,69 @@ def get_type_size0(ti, tp): pass # */ -PyObject *py_get_type_size0(const til_t *ti, PyObject *tp) +PyObject *py_calc_type_size(const til_t *ti, PyObject *tp) { - if ( !PyString_Check(tp) ) + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyString_Check(tp) ) + { + // To avoid release of 'data' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t tpref(tp); + const type_t *data = (type_t *)PyString_AsString(tp); + size_t sz; + Py_BEGIN_ALLOW_THREADS; + tinfo_t tif; + tif.deserialize(ti, &data, NULL, NULL); + sz = tif.get_size(); + Py_END_ALLOW_THREADS; + if ( sz != BADSIZE ) + return PyInt_FromLong(sz); + Py_RETURN_NONE; + } + else { PyErr_SetString(PyExc_ValueError, "String expected!"); return NULL; } - - size_t sz = get_type_size0(ti, (type_t *)PyString_AsString(tp)); - if ( sz == BADSIZE ) - Py_RETURN_NONE; - - return PyInt_FromLong(sz); } //------------------------------------------------------------------------- /* # -def print_type(ea, on_line): +def apply_type(ti, ea, tp_name, py_type, py_fields, flags) + """ + Apply the specified type to the address + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param py_type: type string + @param py_fields: type fields + @param ea: the address of the object + @param flags: combination of TINFO_... constants or 0 + @return: Boolean + """ + pass +# +*/ +static bool py_apply_type(til_t *ti, PyObject *py_type, PyObject *py_fields, ea_t ea, int flags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + const type_t *type = (const type_t *) PyString_AsString(py_type); + const p_list *fields = (const p_list *) PyString_AsString(py_fields); + bool rc; + Py_BEGIN_ALLOW_THREADS; + tinfo_t tif; + rc = tif.deserialize(ti, &type, &fields, NULL) && apply_tinfo2(ea, tif, flags); + Py_END_ALLOW_THREADS; + return rc; +} + +//------------------------------------------------------------------------- +/* +# +def print_type(ea, one_line): """ Returns the type of an item @return: @@ -62,14 +110,13 @@ def print_type(ea, on_line): */ static PyObject *py_print_type(ea_t ea, bool one_line) { - char buf[MAXSTR]; - if ( print_type2(ea, buf, sizeof(buf), one_line ? PRTYPE_1LINE : PRTYPE_MULTI) ) - { - qstrncat(buf, ";", sizeof(buf)); + char buf[64*MAXSTR]; + int flags = PRTYPE_SEMI | (one_line ? PRTYPE_1LINE : PRTYPE_MULTI); + bool ok = print_type2(ea, buf, sizeof(buf), one_line ? PRTYPE_1LINE : PRTYPE_MULTI); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) return PyString_FromString(buf); - } - else - Py_RETURN_NONE; + Py_RETURN_NONE; } //------------------------------------------------------------------------- @@ -90,17 +137,24 @@ PyObject *py_unpack_object_from_idb( ea_t ea, int pio_flags = 0) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) { PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); return NULL; } + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + // Unpack type_t *type = (type_t *) PyString_AsString(py_type); p_list *fields = (p_list *) PyString_AsString(py_fields); idc_value_t idc_obj; - error_t err = unpack_object_from_idb( + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = unpack_object_from_idb( &idc_obj, ti, type, @@ -108,22 +162,21 @@ PyObject *py_unpack_object_from_idb( ea, NULL, pio_flags); + Py_END_ALLOW_THREADS; // Unpacking failed? if ( err != eOk ) return Py_BuildValue("(ii)", 0, err); // Convert - PyObject *py_ret(NULL); + ref_t py_ret; err = idcvar_to_pyvar(idc_obj, &py_ret); // Conversion failed? if ( err != CIP_OK ) return Py_BuildValue("(ii)", 0, err); - - PyObject *py_result = Py_BuildValue("(iO)", 1, py_ret); - Py_DECREF(py_ret); - return py_result; + else + return Py_BuildValue("(iO)", 1, py_ret.o); } //------------------------------------------------------------------------- @@ -152,12 +205,17 @@ PyObject *py_unpack_object_from_bv( PyObject *py_bytes, int pio_flags = 0) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyString_Check(py_type) && !PyString_Check(py_fields) && !PyString_Check(py_bytes) ) { PyErr_SetString(PyExc_ValueError, "Incorrect argument type!"); return NULL; } + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + // Get type strings type_t *type = (type_t *) PyString_AsString(py_type); p_list *fields = (p_list *) PyString_AsString(py_fields); @@ -168,29 +226,30 @@ PyObject *py_unpack_object_from_bv( memcpy(bytes.begin(), PyString_AsString(py_bytes), bytes.size()); idc_value_t idc_obj; - error_t err = unpack_object_from_bv( + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = unpack_object_from_bv( &idc_obj, ti, type, fields, bytes, pio_flags); + Py_END_ALLOW_THREADS; // Unpacking failed? if ( err != eOk ) return Py_BuildValue("(ii)", 0, err); // Convert - PyObject *py_ret(NULL); + ref_t py_ret; err = idcvar_to_pyvar(idc_obj, &py_ret); // Conversion failed? if ( err != CIP_OK ) return Py_BuildValue("(ii)", 0, err); - PyObject *py_result = Py_BuildValue("(iO)", 1, py_ret); - Py_DECREF(py_ret); - return py_result; + return Py_BuildValue("(iO)", 1, py_ret.o); } //------------------------------------------------------------------------- @@ -218,6 +277,7 @@ PyObject *py_pack_object_to_idb( ea_t ea, int pio_flags = 0) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) { PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); @@ -226,15 +286,24 @@ PyObject *py_pack_object_to_idb( // Convert Python object to IDC object idc_value_t idc_obj; - if ( !convert_pyobj_to_idc_exc(py_obj, &idc_obj) ) + borref_t py_obj_ref(py_obj); + if ( !pyvar_to_idcvar_or_error(py_obj_ref, &idc_obj) ) return NULL; + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + // Get type strings - type_t *type = (type_t *) PyString_AsString(py_type); - p_list *fields = (p_list *) PyString_AsString(py_fields); + type_t *type = (type_t *)PyString_AsString(py_type); + p_list *fields = (p_list *)PyString_AsString(py_fields); // Pack - error_t err = pack_object_to_idb(&idc_obj, ti, type, fields, ea, pio_flags); + // error_t err; + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = pack_object_to_idb(&idc_obj, ti, type, fields, ea, pio_flags); + Py_END_ALLOW_THREADS; return PyInt_FromLong(err); } @@ -265,6 +334,7 @@ PyObject *py_pack_object_to_bv( ea_t base_ea, int pio_flags=0) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) { PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); @@ -273,16 +343,23 @@ PyObject *py_pack_object_to_bv( // Convert Python object to IDC object idc_value_t idc_obj; - if ( !convert_pyobj_to_idc_exc(py_obj, &idc_obj) ) + borref_t py_obj_ref(py_obj); + if ( !pyvar_to_idcvar_or_error(py_obj_ref, &idc_obj) ) return NULL; + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + // Get type strings - type_t *type = (type_t *) PyString_AsString(py_type); - p_list *fields = (p_list *) PyString_AsString(py_fields); + type_t *type = (type_t *)PyString_AsString(py_type); + p_list *fields = (p_list *)PyString_AsString(py_fields); // Pack relobj_t bytes; - error_t err = pack_object_to_bv( + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = pack_object_to_bv( &idc_obj, ti, type, @@ -290,19 +367,172 @@ PyObject *py_pack_object_to_bv( &bytes, NULL, pio_flags); - do - { - if ( err != eOk ) - break; - if ( !bytes.relocate(base_ea, inf.mf) ) - { + if ( err == eOk && !bytes.relocate(base_ea, inf.mf) ) err = -1; - break; - } + Py_END_ALLOW_THREADS; + if ( err == eOk ) return Py_BuildValue("(is#)", 1, bytes.begin(), bytes.size()); - } while ( false ); - return Py_BuildValue("(ii)", 0, err); + else + return Py_BuildValue("(ii)", 0, err); } + +//------------------------------------------------------------------------- +/* Parse types from a string or file. See ParseTypes() in idc.py */ +int idc_parse_types(const char *input, int flags) +{ + int hti = ((flags >> 4) & 7) << HTI_PAK_SHIFT; + + if ((flags & 1) != 0) + hti |= HTI_FIL; + + return parse_decls(idati, input, (flags & 2) == 0 ? msg : NULL, hti); +} + +//------------------------------------------------------------------------- +PyObject *py_idc_get_type_raw(ea_t ea) +{ + qtype type, fields; + bool ok = get_tinfo(ea, &type, &fields); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("(ss)", (char *)type.c_str(), (char *)fields.c_str()); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +PyObject *py_idc_get_local_type_raw(int ordinal) +{ + const type_t *type; + const p_list *fields; + bool ok = get_numbered_type(idati, ordinal, &type, &fields); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("(ss)", (char *)type, (char *)fields); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +char *idc_guess_type(ea_t ea, char *buf, size_t bufsize) +{ + tinfo_t tif; + if ( guess_tinfo2(ea, &tif) ) + { + qstring out; + if ( tif.print(&out) ) + return qstrncpy(buf, out.begin(), bufsize); + } + return NULL; +} + +//------------------------------------------------------------------------- +char *idc_get_type(ea_t ea, char *buf, size_t bufsize) +{ + tinfo_t tif; + if ( get_tinfo2(ea, &tif) ) + { + qstring out; + if ( tif.print(&out) ) + { + qstrncpy(buf, out.c_str(), bufsize); + return buf; + } + } + return NULL; +} + +//------------------------------------------------------------------------- +int idc_set_local_type(int ordinal, const char *dcl, int flags) +{ + if (dcl == NULL || dcl[0] == '\0') + { + if ( !del_numbered_type(idati, ordinal) ) + return 0; + } + else + { + tinfo_t tif; + qstring name; + if ( !parse_decl2(idati, dcl, &name, &tif, flags) ) + return 0; + + if ( ordinal <= 0 ) + { + if ( !name.empty() ) + ordinal = get_type_ordinal(idati, name.begin()); + + if ( ordinal <= 0 ) + ordinal = alloc_type_ordinal(idati); + } + + if ( tif.set_numbered_type(idati, ordinal, 0, name.c_str()) != TERR_OK ) + return 0; + } + return ordinal; +} + +//------------------------------------------------------------------------- +int idc_get_local_type(int ordinal, int flags, char *buf, size_t maxsize) +{ + tinfo_t tif; + if ( !tif.get_numbered_type(idati, ordinal) ) + { + buf[0] = 0; + return false; + } + + qstring res; + const char *name = get_numbered_type_name(idati, ordinal); + if ( !tif.print(&res, name, flags, 2, 40) ) + { + buf[0] = 0; + return false; + } + + qstrncpy(buf, res.begin(), maxsize); + return true; +} + +//------------------------------------------------------------------------- +PyObject *idc_print_type(PyObject *py_type, PyObject *py_fields, const char *name, int flags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + + qstring res; + const type_t *type = (type_t *)PyString_AsString(py_type); + const p_list *fields = (p_list *)PyString_AsString(py_fields); + bool ok; + Py_BEGIN_ALLOW_THREADS; + tinfo_t tif; + ok = tif.deserialize(idati, &type, &fields, NULL) + && tif.print(&res, name, flags, 2, 40); + Py_END_ALLOW_THREADS; + if ( ok ) + return PyString_FromString(res.begin()); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +char idc_get_local_type_name(int ordinal, char *buf, size_t bufsize) +{ + const char *name = get_numbered_type_name(idati, ordinal); + if ( name == NULL ) + return false; + + qstrncpy(buf, name, bufsize); + return true; +} + // #endif diff --git a/pywraps/py_typeinf.py b/pywraps/py_typeinf.py new file mode 100644 index 0000000..c8bcc68 --- /dev/null +++ b/pywraps/py_typeinf.py @@ -0,0 +1,15 @@ +# + +def get_type_size0(ti, tp): + """ + DEPRECATED. Please use calc_type_size instead + Returns the size of a type + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @return: + - None on failure + - The size of the type + """ + return calc_type_size(ti, tp) + +# diff --git a/pywraps/py_ua.hpp b/pywraps/py_ua.hpp index aa99cb1..56d3df1 100644 --- a/pywraps/py_ua.hpp +++ b/pywraps/py_ua.hpp @@ -35,6 +35,8 @@ def init_output_buffer(size = MAXSTR): */ PyObject *py_init_output_buffer(size_t size = MAXSTR) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Let Python allocate a writable string buffer for us PyObject *py_str = PyString_FromStringAndSize(NULL, size); if ( py_str == NULL ) @@ -73,6 +75,7 @@ PyObject *py_decode_preceding_insn(ea_t ea) { bool farref; ea_t r = decode_preceding_insn(ea, &farref); + PYW_GIL_CHECK_LOCKED_SCOPE(); return Py_BuildValue("(" PY_FMT64 "i)", pyul_t(r), farref ? 1 : 0); } @@ -117,6 +120,7 @@ def get_stkvar(op, v): */ PyObject *py_get_stkvar(PyObject *py_op, PyObject *py_v) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(py_op); uint64 v; if ( op == NULL || !PyW_GetNumber(py_v, &v) ) @@ -150,6 +154,7 @@ def add_stkvar3(op, v, flags): */ bool py_add_stkvar3(PyObject *py_op, PyObject *py_v, int flags) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(py_op); uint64 v; return ( op == NULL || !PyW_GetNumber(py_v, &v) || !add_stkvar3(*op, sval_t(v), flags)) ? false : true; @@ -192,11 +197,24 @@ bool py_apply_type_to_stkarg( const char *name) { uint64 v; + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(py_op); if ( op == NULL || !PyW_GetNumber(py_uv, &v) || !PyString_Check(py_type)) + { return false; + } else - return apply_type_to_stkarg(*op, uval_t(v), (type_t *) PyString_AsString(py_type), name); + { + const type_t *t = (type_t *) PyString_AsString(py_type); + tinfo_t tif; + tif.deserialize(idati, &t); + borref_t br(py_op); + bool rc; + Py_BEGIN_ALLOW_THREADS; + rc = apply_tinfo_to_stkarg(*op, uval_t(v), tif, name); + Py_END_ALLOW_THREADS; + return rc; + } } //------------------------------------------------------------------------- @@ -213,6 +231,7 @@ def OutImmChar(op, outflags = 0): */ static void py_OutImmChar(PyObject *x) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(x); if ( op != NULL ) OutImmChar(*op); @@ -233,6 +252,7 @@ def ua_stkvar2(op, outflags = 0): */ static bool py_ua_stkvar2(PyObject *x, adiff_t v, int flags) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(x); return op == NULL ? false : ua_stkvar2(*op, v, flags); } @@ -252,6 +272,7 @@ def ua_add_off_drefs(op, type): */ ea_t py_ua_add_off_drefs(PyObject *py_op, dref_t type) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(py_op); return op == NULL ? BADADDR : ua_add_off_drefs(*op, type); } @@ -270,6 +291,7 @@ def ua_add_off_drefs2(op, type, outf): */ ea_t py_ua_add_off_drefs2(PyObject *py_op, dref_t type, int outf) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(py_op); return op == NULL ? BADADDR : ua_add_off_drefs2(*op, type, outf); } @@ -295,6 +317,7 @@ bool py_out_name_expr( ea_t ea, PyObject *py_off) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(py_op); uint64 v(0); adiff_t off; @@ -302,13 +325,14 @@ bool py_out_name_expr( off = adiff_t(v); else off = BADADDR; - + return op == NULL ? false : out_name_expr(*op, ea, off); } //------------------------------------------------------------------------- static PyObject *insn_t_get_op_link(PyObject *py_insn_lnk, int i) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( i < 0 || i >= UA_MAXOP || !PyCObject_Check(py_insn_lnk) ) Py_RETURN_NONE; @@ -322,18 +346,21 @@ static PyObject *insn_t_get_op_link(PyObject *py_insn_lnk, int i) //------------------------------------------------------------------------- static PyObject *insn_t_create() { + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyCObject_FromVoidPtr(new insn_t(), NULL); } //------------------------------------------------------------------------- static PyObject *op_t_create() { + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyCObject_FromVoidPtr(new op_t(), NULL); } //------------------------------------------------------------------------- static bool op_t_assign(PyObject *self, PyObject *other) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *lhs = op_t_get_clink(self); op_t *rhs = op_t_get_clink(other); if (lhs == NULL || rhs == NULL) @@ -346,6 +373,7 @@ static bool op_t_assign(PyObject *self, PyObject *other) //------------------------------------------------------------------------- static bool insn_t_assign(PyObject *self, PyObject *other) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *lhs = insn_t_get_clink(self); insn_t *rhs = insn_t_get_clink(other); if (lhs == NULL || rhs == NULL) @@ -358,6 +386,7 @@ static bool insn_t_assign(PyObject *self, PyObject *other) //------------------------------------------------------------------------- static bool op_t_destroy(PyObject *py_obj) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(py_obj) ) return false; @@ -370,6 +399,7 @@ static bool op_t_destroy(PyObject *py_obj) //------------------------------------------------------------------------- static bool insn_t_destroy(PyObject *py_obj) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(py_obj) ) return false; @@ -381,13 +411,16 @@ static bool insn_t_destroy(PyObject *py_obj) // Returns a C link to the global 'cmd' variable static PyObject *py_get_global_cmd_link() { + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyCObject_FromVoidPtr(&::cmd, NULL); } //------------------------------------------------------------------------- static PyObject *insn_t_is_canon_insn(int itype) { - if ( ph.is_canon_insn(itype) ) + bool ok = ph.is_canon_insn(itype); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) Py_RETURN_TRUE; else Py_RETURN_FALSE; @@ -396,13 +429,17 @@ static PyObject *insn_t_is_canon_insn(int itype) //------------------------------------------------------------------------- static PyObject *insn_t_get_canon_feature(int itype) { - return Py_BuildValue("I", ph.is_canon_insn(itype) ? ph.instruc[itype-ph.instruc_start].feature : 0); + uint32 v = ph.is_canon_insn(itype) ? ph.instruc[itype-ph.instruc_start].feature : 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); + return Py_BuildValue("I", v); } //------------------------------------------------------------------------- static PyObject *insn_t_get_canon_mnem(int itype) { - if ( ph.is_canon_insn(itype) ) + bool ok = ph.is_canon_insn(itype); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) return Py_BuildValue("s", ph.instruc[itype-ph.instruc_start].name); else Py_RETURN_NONE; @@ -411,6 +448,7 @@ static PyObject *insn_t_get_canon_mnem(int itype) //------------------------------------------------------------------------- static PyObject *insn_t_get_cs(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -419,10 +457,11 @@ static PyObject *insn_t_get_cs(PyObject *self) static void insn_t_set_cs(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; - + uint64 v(0); PyW_GetNumber(value, &v); link->cs = ea_t(v); @@ -430,6 +469,7 @@ static void insn_t_set_cs(PyObject *self, PyObject *value) static PyObject *insn_t_get_ip(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -438,6 +478,7 @@ static PyObject *insn_t_get_ip(PyObject *self) static void insn_t_set_ip(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -448,6 +489,7 @@ static void insn_t_set_ip(PyObject *self, PyObject *value) static PyObject *insn_t_get_ea(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -456,6 +498,7 @@ static PyObject *insn_t_get_ea(PyObject *self) static void insn_t_set_ea(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -466,6 +509,7 @@ static void insn_t_set_ea(PyObject *self, PyObject *value) static PyObject *insn_t_get_itype(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -474,6 +518,7 @@ static PyObject *insn_t_get_itype(PyObject *self) static void insn_t_set_itype(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -482,6 +527,7 @@ static void insn_t_set_itype(PyObject *self, PyObject *value) static PyObject *insn_t_get_size(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -490,6 +536,7 @@ static PyObject *insn_t_get_size(PyObject *self) static void insn_t_set_size(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -498,6 +545,7 @@ static void insn_t_set_size(PyObject *self, PyObject *value) static PyObject *insn_t_get_auxpref(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -506,6 +554,7 @@ static PyObject *insn_t_get_auxpref(PyObject *self) static void insn_t_set_auxpref(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -514,6 +563,7 @@ static void insn_t_set_auxpref(PyObject *self, PyObject *value) static PyObject *insn_t_get_segpref(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -522,6 +572,7 @@ static PyObject *insn_t_get_segpref(PyObject *self) static void insn_t_set_segpref(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -530,6 +581,7 @@ static void insn_t_set_segpref(PyObject *self, PyObject *value) static PyObject *insn_t_get_insnpref(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -538,6 +590,7 @@ static PyObject *insn_t_get_insnpref(PyObject *self) static void insn_t_set_insnpref(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -546,6 +599,7 @@ static void insn_t_set_insnpref(PyObject *self, PyObject *value) static PyObject *insn_t_get_flags(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -554,6 +608,7 @@ static PyObject *insn_t_get_flags(PyObject *self) static void insn_t_set_flags(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -563,6 +618,7 @@ static void insn_t_set_flags(PyObject *self, PyObject *value) //------------------------------------------------------------------------- static PyObject *op_t_get_n(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -579,6 +635,7 @@ static void op_t_set_n(PyObject *self, PyObject *value) static PyObject *op_t_get_type(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -587,6 +644,7 @@ static PyObject *op_t_get_type(PyObject *self) static void op_t_set_type(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -595,6 +653,7 @@ static void op_t_set_type(PyObject *self, PyObject *value) static PyObject *op_t_get_offb(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -603,6 +662,7 @@ static PyObject *op_t_get_offb(PyObject *self) static void op_t_set_offb(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -611,6 +671,7 @@ static void op_t_set_offb(PyObject *self, PyObject *value) static PyObject *op_t_get_offo(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -619,6 +680,7 @@ static PyObject *op_t_get_offo(PyObject *self) static void op_t_set_offo(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -627,6 +689,7 @@ static void op_t_set_offo(PyObject *self, PyObject *value) static PyObject *op_t_get_flags(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -635,6 +698,7 @@ static PyObject *op_t_get_flags(PyObject *self) static void op_t_set_flags(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -643,6 +707,7 @@ static void op_t_set_flags(PyObject *self, PyObject *value) static PyObject *op_t_get_dtyp(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -651,6 +716,7 @@ static PyObject *op_t_get_dtyp(PyObject *self) static void op_t_set_dtyp(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -659,6 +725,7 @@ static void op_t_set_dtyp(PyObject *self, PyObject *value) static PyObject *op_t_get_reg_phrase(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -666,6 +733,7 @@ static PyObject *op_t_get_reg_phrase(PyObject *self) } static void op_t_set_reg_phrase(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -674,6 +742,7 @@ static void op_t_set_reg_phrase(PyObject *self, PyObject *value) static PyObject *op_t_get_value(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -682,6 +751,7 @@ static PyObject *op_t_get_value(PyObject *self) static void op_t_set_value(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -690,6 +760,7 @@ static void op_t_set_value(PyObject *self, PyObject *value) static PyObject *op_t_get_addr(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -698,6 +769,7 @@ static PyObject *op_t_get_addr(PyObject *self) static void op_t_set_addr(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -708,6 +780,7 @@ static void op_t_set_addr(PyObject *self, PyObject *value) static PyObject *op_t_get_specval(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -716,6 +789,7 @@ static PyObject *op_t_get_specval(PyObject *self) static void op_t_set_specval(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -726,6 +800,7 @@ static void op_t_set_specval(PyObject *self, PyObject *value) static PyObject *op_t_get_specflag1(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -734,6 +809,7 @@ static PyObject *op_t_get_specflag1(PyObject *self) static void op_t_set_specflag1(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -742,6 +818,7 @@ static void op_t_set_specflag1(PyObject *self, PyObject *value) static PyObject *op_t_get_specflag2(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -750,6 +827,7 @@ static PyObject *op_t_get_specflag2(PyObject *self) static void op_t_set_specflag2(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -758,6 +836,7 @@ static void op_t_set_specflag2(PyObject *self, PyObject *value) static PyObject *op_t_get_specflag3(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -766,6 +845,7 @@ static PyObject *op_t_get_specflag3(PyObject *self) static void op_t_set_specflag3(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -774,6 +854,7 @@ static void op_t_set_specflag3(PyObject *self, PyObject *value) static PyObject *op_t_get_specflag4(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -782,6 +863,7 @@ static PyObject *op_t_get_specflag4(PyObject *self) static void op_t_set_specflag4(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; diff --git a/pywraps/py_view_base.hpp b/pywraps/py_view_base.hpp new file mode 100644 index 0000000..f98f35f --- /dev/null +++ b/pywraps/py_view_base.hpp @@ -0,0 +1,810 @@ +#ifndef __PY_VIEW_BASE__ +#define __PY_VIEW_BASE__ + +// + +//#define PYGDBG_ENABLED +#ifdef PYGDBG_ENABLED +#define PYGLOG(...) msg(__VA_ARGS__) +#else +#define PYGLOG(...) +#endif + +//------------------------------------------------------------------------- +class py_customidamemo_t; +class lookup_info_t +{ +public: + void add(TForm *form, TCustomControl *view, py_customidamemo_t *py_view) + { + QASSERT(0, form != NULL && view != NULL && py_view != NULL + && !find_by_form(NULL, NULL, form) + && !find_by_view(NULL, NULL, view) + && !find_by_py_view(NULL, NULL, py_view)); + entry_t &e = entries.push_back(); + e.form = form; + e.view = view; + e.py_view = py_view; + } + +#define FIND_BY__BODY(crit, res1, res2) \ + { \ + for ( entries_t::const_iterator it = entries.begin(); it != entries.end(); ++it ) \ + { \ + const entry_t &e = *it; \ + if ( e.crit == crit ) \ + { \ + if ( out_##res1 != NULL ) \ + *out_##res1 = e.res1; \ + if ( out_##res2 != NULL ) \ + *out_##res2 = e.res2; \ + return true; \ + } \ + } \ + return false; \ + } + bool find_by_form(TCustomControl **out_view, py_customidamemo_t **out_py_view, const TForm *form) const FIND_BY__BODY(form, view, py_view); + bool find_by_view(TForm **out_form, py_customidamemo_t **out_py_view, const TCustomControl *view) const FIND_BY__BODY(view, form, py_view); + bool find_by_py_view(TForm **out_form, TCustomControl **out_view, const py_customidamemo_t *py_view) const FIND_BY__BODY(py_view, view, form); +#undef FIND_BY__BODY + + bool del_by_py_view(const py_customidamemo_t *py_view) + { + for ( entries_t::iterator it = entries.begin(); it != entries.end(); ++it ) + { + if ( it->py_view == py_view ) + { + entries.erase(it); + return true; + } + } + return false; + } + +private: + struct entry_t + { + TForm *form; + TCustomControl *view; + py_customidamemo_t *py_view; + }; + typedef qvector entries_t; + entries_t entries; +}; + +//------------------------------------------------------------------------- +template +T *view_extract_this(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_this(PyW_TryGetAttrString(self, S_M_THIS)); + if ( py_this == NULL || !PyCObject_Check(py_this.o) ) + return NULL; + return (T*) PyCObject_AsVoidPtr(py_this.o); +} + +//------------------------------------------------------------------------- +class py_customidamemo_t +{ + void convert_node_info( + node_info_t *out, + uint32 *out_flags, + ref_t py_nodeinfo) + { + if ( out_flags != NULL ) + *out_flags = 0; +#define COPY_PROP(checker, converter, pname, flag) \ + do \ + { \ + newref_t pname(PyObject_GetAttrString(py_nodeinfo.o, #pname)); \ + if ( pname != NULL && checker(pname.o) ) \ + { \ + out->pname = converter(pname.o); \ + if ( out_flags != NULL ) \ + *out_flags |= flag; \ + } \ + } while ( false ) +#define COPY_ULONG_PROP(pname, flag) COPY_PROP(PyNumber_Check, PyLong_AsUnsignedLong, pname, flag) +#define COPY_STRING_PROP(pname, flag) COPY_PROP(PyString_Check, PyString_AsString, pname, flag) + COPY_ULONG_PROP(bg_color, NIF_BG_COLOR); + COPY_ULONG_PROP(frame_color, NIF_FRAME_COLOR); + COPY_ULONG_PROP(ea, NIF_EA); + COPY_STRING_PROP(text, NIF_TEXT); +#undef COPY_STRING_PROP +#undef COPY_ULONG_PROP +#undef COPY_PROP + } + + enum + { + GRBASE_HAVE_VIEW_ACTIVATED = 0x001, + GRBASE_HAVE_VIEW_DEACTIVATED = 0x002, + GRBASE_HAVE_KEYDOWN = 0x004, + GRBASE_HAVE_POPUP = 0x008, + GRBASE_HAVE_VIEW_CLICK = 0x010, + GRBASE_HAVE_VIEW_DBLCLICK = 0x020, + GRBASE_HAVE_VIEW_CURPOS = 0x040, + GRBASE_HAVE_CLOSE = 0x080, + GRBASE_HAVE_VIEW_SWITCHED = 0x100, + GRBASE_HAVE_VIEW_MOUSE_OVER = 0x200, + }; + + static void ensure_view_callbacks_installed(); + int cb_flags; + +protected: + ref_t self; + TCustomControl *view; + // This is called after having modified the + // node properties in the IDB. In case an + // implementation is performing some caching, + // this is a chance to update that cache. + // If 'ni' is NULL, then the node info was deleted. + virtual void node_info_modified( + int /*n*/, + const node_info_t * /*ni*/, + uint32 /*flags*/) {} + + struct callback_id_t + { + qstring name; + int have; + }; + struct callbacks_ids_t : public qvector + { + void add(const char *_n, int _h) + { + callback_id_t &o = push_back(); + o.name = _n; + o.have = _h; + } + }; + callbacks_ids_t cbids; + + bool collect_pyobject_callbacks(PyObject *self); + virtual void collect_class_callbacks_ids(callbacks_ids_t *out); + + // Bi-directionally bind/unbind the Python object and this controller. + bool bind(PyObject *_self, TCustomControl *view); + void unbind(); + + static lookup_info_t lookup_info; + +public: + py_customidamemo_t(); + virtual ~py_customidamemo_t(); + virtual void refresh() + { + refresh_viewer(view); + } + void set_node_info(PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags); + void set_nodes_infos(PyObject *dict); + PyObject *get_node_info(PyObject *py_node_idx); + void del_nodes_infos(PyObject *py_nodes); + PyObject *get_current_renderer_type(); + void set_current_renderer_type(PyObject *py_rto); + PyObject *create_groups(PyObject *groups_infos); + PyObject *delete_groups(PyObject *groups, PyObject *new_current); + PyObject *set_groups_visibility(PyObject *groups, PyObject *expand, PyObject *new_current); + + // View events + void on_view_activated(); + void on_view_deactivated(); + void on_view_keydown(int key, int state); + void on_view_popup(); + void on_view_click(const view_mouse_event_t *event); + void on_view_dblclick(const view_mouse_event_t *event); + void on_view_curpos(); + void on_view_close(); + void on_view_switched(tcc_renderer_type_t rt); + void on_view_mouse_over(const view_mouse_event_t *event); + inline bool has_callback(int flag) { return (cb_flags & flag) != 0; } +}; + +//------------------------------------------------------------------------- +py_customidamemo_t::py_customidamemo_t() + : self(newref_t(NULL)), + view(NULL) +{ + PYGLOG("%p: py_customidamemo_t()\n", this); + ensure_view_callbacks_installed(); +} + +//------------------------------------------------------------------------- +py_customidamemo_t::~py_customidamemo_t() +{ + PYGLOG("%p: ~py_customidamemo_t()\n", this); + unbind(); + lookup_info.del_by_py_view(this); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::ensure_view_callbacks_installed() +{ + static bool installed = false; + if ( !installed ) + { + struct ida_local lambda_t + { + static int idaapi callback(void * /*ud*/, int code, va_list va) + { + py_customidamemo_t *py_view; + if ( lookup_info.find_by_view(NULL, &py_view, va_arg(va, TCustomControl *)) ) + { + PYW_GIL_GET; + switch ( code ) + { + case view_activated: + py_view->on_view_activated(); + break; + case view_deactivated: + py_view->on_view_deactivated(); + break; + case view_keydown: + { + int key = va_arg(va, int); + int state = va_arg(va, int); + py_view->on_view_keydown(key, state); + } + break; + case view_popup: + py_view->on_view_popup(); + break; + case view_click: + case view_dblclick: + { + const view_mouse_event_t *event = va_arg(va, view_mouse_event_t*); + if ( code == view_click ) + py_view->on_view_click(event); + else + py_view->on_view_dblclick(event); + } + break; + case view_curpos: + py_view->on_view_curpos(); + break; + case view_close: + py_view->on_view_close(); + delete py_view; + break; + case view_switched: + { + tcc_renderer_type_t rt = (tcc_renderer_type_t) va_arg(va, int); + py_view->on_view_switched(rt); + } + break; + case view_mouse_over: + { + const view_mouse_event_t *event = va_arg(va, view_mouse_event_t*); + py_view->on_view_mouse_over(event); + } + break; + } + } + return 0; + } + }; + hook_to_notification_point(HT_VIEW, lambda_t::callback, NULL); + installed = true; + } +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::set_node_info( + PyObject *py_node_idx, + PyObject *py_node_info, + PyObject *py_flags) +{ + if ( !PyNumber_Check(py_node_idx) || !PyNumber_Check(py_flags) ) + return; + borref_t py_idx(py_node_idx); + borref_t py_ni(py_node_info); + borref_t py_fl(py_flags); + node_info_t ni; + convert_node_info(&ni, NULL, py_ni); + int idx = PyInt_AsLong(py_idx.o); + uint32 flgs = PyLong_AsLong(py_fl.o); + viewer_set_node_info(view, idx, ni, flgs); + node_info_modified(idx, &ni, flgs); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::set_nodes_infos(PyObject *dict) +{ + if ( !PyDict_Check(dict) ) + return; + Py_ssize_t pos = 0; + PyObject *o_key, *o_value; + while ( PyDict_Next(dict, &pos, &o_key, &o_value) ) + { + borref_t key(o_key); + borref_t value(o_value); + if ( !PyNumber_Check(key.o) ) + continue; + uint32 flags; + node_info_t ni; + convert_node_info(&ni, &flags, value); + int idx = PyInt_AsLong(key.o); + viewer_set_node_info(view, idx, ni, flags); + node_info_modified(idx, &ni, flags); + } +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::get_node_info(PyObject *py_node_idx) +{ + if ( !PyNumber_Check(py_node_idx) ) + Py_RETURN_NONE; + node_info_t ni; + if ( !viewer_get_node_info(view, &ni, PyInt_AsLong(py_node_idx)) ) + Py_RETURN_NONE; + return Py_BuildValue("(kkks)", ni.bg_color, ni.frame_color, ni.ea, ni.text.c_str()); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::del_nodes_infos(PyObject *py_nodes) +{ + if ( !PySequence_Check(py_nodes) ) + return; + Py_ssize_t sz = PySequence_Size(py_nodes); + for ( Py_ssize_t i = 0; i < sz; ++i ) + { + newref_t item(PySequence_GetItem(py_nodes, i)); + if ( !PyNumber_Check(item.o) ) + continue; + int idx = PyInt_AsLong(item.o); + viewer_del_node_info(view, idx); + node_info_modified(idx, NULL, 0); + } +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::get_current_renderer_type() +{ + tcc_renderer_type_t rt = get_view_renderer_type(view); + return PyLong_FromLong(long(rt)); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::set_current_renderer_type(PyObject *py_rto) +{ + tcc_renderer_type_t rt = TCCRT_INVALID; + borref_t py_rt(py_rto); + if ( PyNumber_Check(py_rt.o) ) + { + rt = tcc_renderer_type_t(PyLong_AsLong(py_rt.o)); + set_view_renderer_type(view, rt); + } +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::create_groups(PyObject *_groups_infos) +{ + if ( !PySequence_Check(_groups_infos) ) + Py_RETURN_NONE; + borref_t groups_infos(_groups_infos); + groups_crinfos_t gis; + Py_ssize_t sz = PySequence_Size(groups_infos.o); + for ( Py_ssize_t i = 0; i < sz; ++i ) + { + newref_t item(PySequence_GetItem(groups_infos.o, i)); + if ( !PyDict_Check(item.o) ) + continue; + borref_t nodes(PyDict_GetItemString(item.o, "nodes")); + if ( nodes.o == NULL || !PySequence_Check(nodes.o) ) + continue; + borref_t text(PyDict_GetItemString(item.o, "text")); + if ( text.o == NULL || !PyString_Check(text.o) ) + continue; + group_crinfo_t gi; + Py_ssize_t nodes_cnt = PySequence_Size(nodes.o); + for ( Py_ssize_t k = 0; k < nodes_cnt; ++k ) + { + newref_t node(PySequence_GetItem(nodes.o, k)); + if ( PyInt_Check(node.o) ) + gi.nodes.insert(PyInt_AsLong(node.o)); + } + if ( !gi.nodes.empty() ) + { + gi.text = PyString_AsString(text.o); + gis.push_back(gi); + } + } + intset_t groups; + if ( gis.empty() || !viewer_create_groups(view, &groups, gis) || groups.empty() ) + Py_RETURN_NONE; + + PyObject *py_groups = PyList_New(0); + for ( intset_t::const_iterator it = groups.begin(); it != groups.end(); ++it ) + PyList_Append(py_groups, PyInt_FromLong(long(*it))); + return py_groups; +} + +//------------------------------------------------------------------------- +static void pynodes_to_idanodes(intset_t *idanodes, ref_t pynodes) +{ + Py_ssize_t sz = PySequence_Size(pynodes.o); + for ( Py_ssize_t i = 0; i < sz; ++i ) + { + newref_t item(PySequence_GetItem(pynodes.o, i)); + if ( !PyInt_Check(item.o) ) + continue; + idanodes->insert(PyInt_AsLong(item.o)); + } +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::delete_groups(PyObject *_groups, PyObject *_new_current) +{ + if ( !PySequence_Check(_groups) || !PyNumber_Check(_new_current) ) + Py_RETURN_NONE; + borref_t groups(_groups); + borref_t new_current(_new_current); + intset_t ida_groups; + pynodes_to_idanodes(&ida_groups, groups); + if ( ida_groups.empty() ) + Py_RETURN_NONE; + if ( viewer_delete_groups(view, ida_groups, int(PyInt_AsLong(new_current.o))) ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::set_groups_visibility(PyObject *_groups, PyObject *_expand, PyObject *_new_current) +{ + if ( !PySequence_Check(_groups) + || !PyBool_Check(_expand) + || !PyNumber_Check(_new_current) ) + Py_RETURN_NONE; + borref_t groups(_groups); + borref_t expand(_expand); + borref_t new_current(_new_current); + intset_t ida_groups; + pynodes_to_idanodes(&ida_groups, groups); + if ( ida_groups.empty() ) + Py_RETURN_NONE; + if ( viewer_set_groups_visibility(view, ida_groups, expand.o == Py_True, int(PyInt_AsLong(new_current.o))) ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +bool py_customidamemo_t::bind(PyObject *_self, TCustomControl *view) +{ + if ( this->self != NULL || this->view != NULL ) + return false; + PYGLOG("%p: py_customidamemo_t::bind(_self=%p, view=%p)\n", this, _self, view); + PYW_GIL_CHECK_LOCKED_SCOPE(); + + newref_t py_cobj(PyCObject_FromVoidPtr(this, NULL)); + PyObject_SetAttrString(_self, S_M_THIS, py_cobj.o); + + this->self = borref_t(_self); + this->view = view; + return true; +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::unbind() +{ + if ( self == NULL ) + return; + PYGLOG("%p: py_customidamemo_t::unbind(); self.o=%p, view=%p\n", this, self.o, view); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_cobj(PyCObject_FromVoidPtr(NULL, NULL)); + PyObject_SetAttrString(self.o, S_M_THIS, py_cobj.o); + self = newref_t(NULL); + view = NULL; +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::collect_class_callbacks_ids(callbacks_ids_t *out) +{ + out->add(S_ON_VIEW_ACTIVATED, GRBASE_HAVE_VIEW_ACTIVATED); + out->add(S_ON_VIEW_DEACTIVATED, GRBASE_HAVE_VIEW_DEACTIVATED); + out->add(S_ON_VIEW_KEYDOWN, GRBASE_HAVE_KEYDOWN); + out->add(S_ON_POPUP, GRBASE_HAVE_POPUP); + out->add(S_ON_VIEW_CLICK, GRBASE_HAVE_VIEW_CLICK); + out->add(S_ON_VIEW_DBLCLICK, GRBASE_HAVE_VIEW_DBLCLICK); + out->add(S_ON_VIEW_CURPOS, GRBASE_HAVE_VIEW_CURPOS); + out->add(S_ON_CLOSE, GRBASE_HAVE_CLOSE); + out->add(S_ON_VIEW_SWITCHED, GRBASE_HAVE_VIEW_SWITCHED); + out->add(S_ON_VIEW_MOUSE_OVER, GRBASE_HAVE_VIEW_MOUSE_OVER); +} + +//------------------------------------------------------------------------- +bool py_customidamemo_t::collect_pyobject_callbacks(PyObject *o) +{ + callbacks_ids_t cbids; + collect_class_callbacks_ids(&cbids); + cb_flags = 0; + for ( callbacks_ids_t::const_iterator it = cbids.begin(); it != cbids.end(); ++it ) + { + const callback_id_t &cbid = *it; + ref_t attr(PyW_TryGetAttrString(o, cbid.name.c_str())); + int have = cbid.have; + // Mandatory fields not present? + if ( (attr == NULL && have <= 0 ) + // Mandatory callback fields present but not callable? + || (attr != NULL && have >= 0 && PyCallable_Check(attr.o) == 0)) + { + return false; + } + if ( have > 0 && attr != NULL ) + cb_flags |= have; + } + return true; +} + + +//------------------------------------------------------------------------- +#define CHK_EVT(flag_needed) \ + if ( self == NULL || !has_callback(flag_needed) ) \ + return; \ + PYW_GIL_CHECK_LOCKED_SCOPE() + +#ifdef PYGDBG_ENABLED +#define CHK_RES() PYGLOG("%s: return code: %p\n", __FUNCTION__, result.o) +#else +#define CHK_RES() +#endif + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_activated() +{ + CHK_EVT(GRBASE_HAVE_VIEW_ACTIVATED); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_ACTIVATED, + NULL)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_deactivated() +{ + CHK_EVT(GRBASE_HAVE_VIEW_DEACTIVATED); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_DEACTIVATED, + NULL)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_keydown(int key, int state) +{ + CHK_EVT(GRBASE_HAVE_KEYDOWN); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_KEYDOWN, + "ii", + key, state)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_popup() +{ + CHK_EVT(GRBASE_HAVE_POPUP); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_POPUP, + NULL)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_click(const view_mouse_event_t *event) +{ + CHK_EVT(GRBASE_HAVE_VIEW_CLICK); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_CLICK, + "iii", + event->x, event->y, event->state)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_dblclick(const view_mouse_event_t *event) +{ + CHK_EVT(GRBASE_HAVE_VIEW_DBLCLICK); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_DBLCLICK, + "iii", + event->x, event->y, event->state)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_curpos() +{ + CHK_EVT(GRBASE_HAVE_VIEW_CURPOS); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_CURPOS, + NULL)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_close() +{ + CHK_EVT(GRBASE_HAVE_CLOSE); + newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_CLOSE, NULL)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_switched(tcc_renderer_type_t rt) +{ + CHK_EVT(GRBASE_HAVE_VIEW_SWITCHED); + newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_VIEW_SWITCHED, "i", int(rt))); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event) +{ + CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_OVER); + if ( event->rtype == TCCRT_GRAPH || event->rtype == TCCRT_PROXIMITY ) + { + const selection_item_t *item = event->location.item; + int icode; + ref_t tuple; + if ( item != NULL ) + { + if ( item->is_node ) + { + icode = 1; + tuple = newref_t(Py_BuildValue("(i)", item->node)); + } + else + { + icode = 2; + tuple = newref_t(Py_BuildValue("(ii)", item->elp.e.src, item->elp.e.dst)); + } + } + else + { + icode = 0; + tuple = newref_t(Py_BuildValue("()")); + } + newref_t result(PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_MOUSE_OVER, + "iiiiO", + event->x, event->y, event->state, icode, tuple.o)); + CHK_RES(); + } +} + + +#undef CHK_RES +#undef CHK_EVT + +//------------------------------------------------------------------------- +//------------------------------------------------------------------------- + +#define GET_THIS() py_customidamemo_t *_this = view_extract_this(self) +#define CHK_THIS() \ + GET_THIS(); \ + if ( _this == NULL ) \ + return +#define CHK_THIS_OR_NONE() \ + GET_THIS(); \ + if ( _this == NULL ) \ + Py_RETURN_NONE + +//------------------------------------------------------------------------- +void pygc_refresh(PyObject *self) +{ + CHK_THIS(); + _this->refresh(); +} + +//------------------------------------------------------------------------- +void pygc_set_node_info(PyObject *self, PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags) +{ + CHK_THIS(); + _this->set_node_info(py_node_idx, py_node_info, py_flags); +} + +//------------------------------------------------------------------------- +void pygc_set_nodes_infos(PyObject *self, PyObject *values) +{ + CHK_THIS(); + _this->set_nodes_infos(values); +} + +//------------------------------------------------------------------------- +PyObject *pygc_get_node_info(PyObject *self, PyObject *py_node_idx) +{ + GET_THIS(); + if ( _this != NULL ) + return _this->get_node_info(py_node_idx); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +void pygc_del_nodes_infos(PyObject *self, PyObject *py_nodes) +{ + CHK_THIS(); + _this->del_nodes_infos(py_nodes); +} + +//------------------------------------------------------------------------- +PyObject *pygc_get_current_renderer_type(PyObject *self) +{ + GET_THIS(); + if ( _this != NULL ) + return _this->get_current_renderer_type(); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +void pygc_set_current_renderer_type(PyObject *self, PyObject *py_rt) +{ + CHK_THIS(); + _this->set_current_renderer_type(py_rt); +} + +//------------------------------------------------------------------------- +PyObject *pygc_create_groups(PyObject *self, PyObject *groups_infos) +{ + CHK_THIS_OR_NONE(); + return _this->create_groups(groups_infos); +} + +//------------------------------------------------------------------------- +PyObject *pygc_delete_groups(PyObject *self, PyObject *groups, PyObject *new_current) +{ + CHK_THIS_OR_NONE(); + return _this->delete_groups(groups, new_current); +} + +//------------------------------------------------------------------------- +PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject *expand, PyObject *new_current) +{ + CHK_THIS_OR_NONE(); + return _this->set_groups_visibility(groups, expand, new_current); +} + +#undef CHK_THIS_OR_NONE +#undef CHK_THIS +#undef GET_THIS + +//------------------------------------------------------------------------- +lookup_info_t py_customidamemo_t::lookup_info; +// + +// +void pygc_refresh(PyObject *self); +void pygc_set_node_info(PyObject *self, PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags); +void pygc_set_nodes_infos(PyObject *self, PyObject *values); +PyObject *pygc_get_node_info(PyObject *self, PyObject *py_node_idx); +void pygc_del_nodes_infos(PyObject *self, PyObject *py_nodes); +PyObject *pygc_get_current_renderer_type(PyObject *self); +void pygc_set_current_renderer_type(PyObject *self, PyObject *py_rt); +PyObject *pygc_create_groups(PyObject *self, PyObject *groups_infos); +PyObject *pygc_delete_groups(PyObject *self, PyObject *groups, PyObject *new_current); +PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject *expand, PyObject *new_current); +// + + + +#endif // __PY_VIEW_BASE__ + diff --git a/pywraps/py_view_base.py b/pywraps/py_view_base.py new file mode 100644 index 0000000..4048468 --- /dev/null +++ b/pywraps/py_view_base.py @@ -0,0 +1,107 @@ + +# +class CustomIDAMemo(object): + def Refresh(self): + """ + Refreshes the graph. This causes the OnRefresh() to be called + """ + _idaapi.pygc_refresh(self) + + def GetCurrentRendererType(self): + return _idaapi.pygc_get_current_renderer_type(self) + + def SetCurrentRendererType(self, rtype): + """ + Set the current view's renderer. + + @param rtype: The renderer type. Should be one of the idaapi.TCCRT_* values. + """ + _idaapi.pygc_set_current_renderer_type(self, rtype) + + def SetNodeInfo(self, node_index, node_info, flags): + """ + Set the properties for the given node. + + Example usage (set second nodes's bg color to red): + inst = ... + p = idaapi.node_info_t() + p.bg_color = 0x00ff0000 + inst.SetNodeInfo(1, p, idaapi.NIF_BG_COLOR) + + @param node_index: The node index. + @param node_info: An idaapi.node_info_t instance. + @param flags: An OR'ed value of NIF_* values. + """ + _idaapi.pygc_set_node_info(self, node_index, node_info, flags) + + def SetNodesInfos(self, values): + """ + Set the properties for the given nodes. + + Example usage (set first three nodes's bg color to purple): + inst = ... + p = idaapi.node_info_t() + p.bg_color = 0x00ff00ff + inst.SetNodesInfos({0 : p, 1 : p, 2 : p}) + + @param values: A dictionary of 'int -> node_info_t' objects. + """ + _idaapi.pygc_set_nodes_infos(self, values) + + def GetNodeInfo(self, node): + """ + Get the properties for the given node. + + @param node: The index of the node. + @return: A tuple (bg_color, frame_color, ea, text), or None. + """ + return _idaapi.pygc_get_node_info(self, node) + + def DelNodesInfos(self, *nodes): + """ + Delete the properties for the given node(s). + + @param nodes: A list of node IDs + """ + return _idaapi.pygc_del_nodes_infos(self, nodes) + + def CreateGroups(self, groups_infos): + """ + Send a request to modify the graph by creating a + (set of) group(s), and perform an animation. + + Each object in the 'groups_infos' list must be of the format: + { + "nodes" : [, , , ...] # The list of nodes to group + "text" : # The synthetic text for that group + } + + @param groups_infos: A list of objects that describe those groups. + @return: A [, , ...] list of group nodes, or None (failure). + """ + return _idaapi.pygc_create_groups(self, groups_infos) + + def DeleteGroups(self, groups, new_current = -1): + """ + Send a request to delete the specified groups in the graph, + and perform an animation. + + @param groups: A list of group node numbers. + @param new_current: A node to focus on after the groups have been deleted + @return: True on success, False otherwise. + """ + return _idaapi.pygc_delete_groups(self, groups, new_current) + + def SetGroupsVisibility(self, groups, expand, new_current = -1): + """ + Send a request to expand/collapse the specified groups in the graph, + and perform an animation. + + @param groups: A list of group node numbers. + @param expand: True to expand the group, False otherwise. + @param new_current: A node to focus on after the groups have been expanded/collapsed. + @return: True on success, False otherwise. + """ + return _idaapi.pygc_set_groups_visibility(self, groups, expand, new_current) + +# diff --git a/pywraps/sidaapi.py b/pywraps/sidaapi.py index 73c090a..d27b44c 100644 --- a/pywraps/sidaapi.py +++ b/pywraps/sidaapi.py @@ -90,6 +90,10 @@ class processor_t(object): # ---------------------------------------------------------------------- BADADDR = 0xFFFFFFFFFFFFFFFFL +# ---------------------------------------------------------------------- +UA_MAXOP = 6 +o_last = 14 +o_void = 0 # ---------------------------------------------------------------------- """ diff --git a/swig/area.i b/swig/area.i index 21b2c9f..591bc51 100644 --- a/swig/area.i +++ b/swig/area.i @@ -1,27 +1,17 @@ -%ignore sarray; -%ignore lastreq_t; -%ignore AREA_CACHE_SIZE; %ignore ANODE; %ignore ANODE2; %ignore AREA_LONG_COMMENT_TAG; %ignore area_visitor_t; +%ignore areacb_t_link_dont_load; +%ignore add_area_from_cache; +%ignore areacb_t_valid_push_back; // Ignore the private members in areacb_t %ignore areacb_t::areasCode; %ignore areacb_t::infosize; %ignore areacb_t::lastreq; %ignore areacb_t::reserved; -%ignore areacb_t::sa; -%ignore areacb_t::cache; -%ignore areacb_t::allocate; -%ignore areacb_t::search; -%ignore areacb_t::readArea; -%ignore areacb_t::findCache; -%ignore areacb_t::addCache; -%ignore areacb_t::delCache; -%ignore areacb_t::free_cache; -%ignore areacb_t::find_nth_start; -%ignore areacb_t::build_optimizer; +%ignore areacb_t::areas; %ignore areacb_t::move_area_comment; %ignore areacb_t::pack_and_write_area; %ignore areacb_t::move_away; diff --git a/swig/bytes.i b/swig/bytes.i index ba08b19..aed1331 100644 --- a/swig/bytes.i +++ b/swig/bytes.i @@ -102,20 +102,17 @@ //------------------------------------------------------------------------ static bool idaapi py_testf_cb(flags_t flags, void *ud) { - PyObject *py_flags = PyLong_FromUnsignedLong(flags); - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallFunctionObjArgs((PyObject *) ud, py_flags, NULL); - PYW_GIL_RELEASE; - bool ret = result != NULL && PyObject_IsTrue(result); - Py_XDECREF(result); - Py_XDECREF(py_flags); - return ret; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_flags(PyLong_FromUnsignedLong(flags)); + newref_t result(PyObject_CallFunctionObjArgs((PyObject *) ud, py_flags.o, NULL)); + return result != NULL && PyObject_IsTrue(result.o); } //------------------------------------------------------------------------ // Wraps the (next|prev)that() static ea_t py_npthat(ea_t ea, ea_t bound, PyObject *py_callable, bool next) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCallable_Check(py_callable) ) return BADADDR; else @@ -130,20 +127,17 @@ static int idaapi py_visit_patched_bytes_cb( uint32 v, void *ud) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallFunction( - (PyObject *)ud, - PY_FMT64 "iII", - pyul_t(ea), - fpos, - o, - v); - PYW_GIL_RELEASE; - + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallFunction( + (PyObject *)ud, + PY_FMT64 "iII", + pyul_t(ea), + fpos, + o, + v)); PyW_ShowCbErr("visit_patched_bytes"); - int ret = (py_result != NULL && PyInt_Check(py_result)) ? PyInt_AsLong(py_result) : 0; - Py_XDECREF(py_result); - return ret; + return (py_result != NULL && PyInt_Check(py_result.o)) ? PyInt_AsLong(py_result.o) : 0; } @@ -163,20 +157,18 @@ class py_custom_data_type_t size_t nbytes) // size of the future item { py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; - - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_self, - (char *)S_MAY_CREATE_AT, - PY_FMT64 PY_FMT64, - pyul_t(ea), - pyul_t(nbytes)); - PYW_GIL_RELEASE; + + PYW_GIL_GET; + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_MAY_CREATE_AT, + PY_FMT64 PY_FMT64, + pyul_t(ea), + pyul_t(nbytes))); PyW_ShowCbErr(S_MAY_CREATE_AT); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; + return py_result != NULL && PyObject_IsTrue(py_result.o); } // !=NULL means variable size datatype @@ -187,24 +179,23 @@ class py_custom_data_type_t ea_t ea, // address of the item asize_t maxsize) // maximal size of the item { + PYW_GIL_GET; // Returns: 0-no such item can be created/displayed // this callback is required only for varsize datatypes py_custom_data_type_t *_this = (py_custom_data_type_t *)ud; - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_self, - (char *)S_CALC_ITEM_SIZE, - PY_FMT64 PY_FMT64, - pyul_t(ea), - pyul_t(maxsize)); - PYW_GIL_RELEASE; - + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_CALC_ITEM_SIZE, + PY_FMT64 PY_FMT64, + pyul_t(ea), + pyul_t(maxsize))); + if ( PyW_ShowCbErr(S_CALC_ITEM_SIZE) || py_result == NULL ) return 0; - + uint64 num = 0; - PyW_GetNumber(py_result, &num); - Py_XDECREF(py_result); + PyW_GetNumber(py_result.o, &num); return asize_t(num); } @@ -222,6 +213,8 @@ public: int register_dt(PyObject *py_obj) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Already registered? if ( dtid >= 0 ) return dtid; @@ -230,9 +223,10 @@ public: dt.cbsize = sizeof(dt); dt.ud = this; - PyObject *py_attr = NULL; do { + ref_t py_attr; + // name if ( !PyW_GetStringAttr(py_obj, S_NAME, &dt_name) ) break; @@ -253,30 +247,27 @@ public: // value_size py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); - if ( py_attr != NULL && PyInt_Check(py_attr) ) - dt.value_size = PyInt_AsLong(py_attr); - Py_XDECREF(py_attr); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + dt.value_size = PyInt_AsLong(py_attr.o); + py_attr = ref_t(); // props py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); - if ( py_attr != NULL && PyInt_Check(py_attr) ) - dt.props = PyInt_AsLong(py_attr); - Py_XDECREF(py_attr); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + dt.props = PyInt_AsLong(py_attr.o); + py_attr = ref_t(); // may_create_at py_attr = PyW_TryGetAttrString(py_obj, S_MAY_CREATE_AT); - if ( py_attr != NULL && PyCallable_Check(py_attr) ) + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) dt.may_create_at = s_may_create_at; - Py_XDECREF(py_attr); + py_attr = ref_t(); // calc_item_size py_attr = PyW_TryGetAttrString(py_obj, S_CALC_ITEM_SIZE); - if ( py_attr != NULL && PyCallable_Check(py_attr) ) + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) dt.calc_item_size = s_calc_item_size; - Py_XDECREF(py_attr); - - // Clear attribute - py_attr = NULL; + py_attr = ref_t(); // Now try to register dtid = register_custom_data_type(&dt); @@ -287,20 +278,16 @@ public: Py_INCREF(py_obj); py_self = py_obj; - py_attr = PyInt_FromLong(dtid); - PyObject_SetAttrString(py_obj, S_ID, py_attr); - Py_DECREF(py_attr); - - // Done with attribute - py_attr = NULL; + py_attr = newref_t(PyInt_FromLong(dtid)); + PyObject_SetAttrString(py_obj, S_ID, py_attr.o); } while ( false ); - - Py_XDECREF(py_attr); return dtid; } bool unregister_dt() { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( dtid < 0 ) return true; @@ -340,44 +327,41 @@ private: int operand_num, // current operand number int dtid) // custom data type id { + PYW_GIL_GET; + // Build a string from the buffer - PyObject *py_value = PyString_FromStringAndSize( - (const char *)value, - Py_ssize_t(size)); + newref_t py_value(PyString_FromStringAndSize( + (const char *)value, + Py_ssize_t(size))); if ( py_value == NULL ) return false; py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_self, - (char *)S_PRINTF, - "O" PY_FMT64 "ii", - py_value, - pyul_t(current_ea), - operand_num, - dtid); - PYW_GIL_RELEASE; - // Done with the string - Py_DECREF(py_value); + newref_t py_result(PyObject_CallMethod( + _this->py_self, + (char *)S_PRINTF, + "O" PY_FMT64 "ii", + py_value.o, + pyul_t(current_ea), + operand_num, + dtid)); // Error while calling the function? if ( PyW_ShowCbErr(S_PRINTF) || py_result == NULL ) return false; bool ok = false; - if ( PyString_Check(py_result) ) + if ( PyString_Check(py_result.o) ) { Py_ssize_t len; char *buf; - if ( out != NULL && PyString_AsStringAndSize(py_result, &buf, &len) != -1 ) + if ( out != NULL && PyString_AsStringAndSize(py_result.o, &buf, &len) != -1 ) { out->qclear(); out->append(buf, len); } ok = true; } - Py_DECREF(py_result); return ok; } @@ -389,16 +373,17 @@ private: int operand_num, // current operand number (-1 if unknown) qstring *errstr) // buffer for error message { + PYW_GIL_GET; + py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_self, - (char *)S_SCAN, - "s" PY_FMT64, - input, - pyul_t(current_ea), - operand_num); - PYW_GIL_RELEASE; + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_SCAN, + "s" PY_FMT64, + input, + pyul_t(current_ea), + operand_num)); // Error while calling the function? if ( PyW_ShowCbErr(S_SCAN) || py_result == NULL) @@ -408,15 +393,14 @@ private: do { // We expect a tuple(bool, string|None) - if ( !PyTuple_Check(py_result) || PyTuple_Size(py_result) != 2 ) + if ( !PyTuple_Check(py_result.o) || PyTuple_Size(py_result.o) != 2 ) break; - // Borrow references - PyObject *py_bool = PyTuple_GetItem(py_result, 0); - PyObject *py_val = PyTuple_GetItem(py_result, 1); + borref_t py_bool(PyTuple_GetItem(py_result.o, 0)); + borref_t py_val(PyTuple_GetItem(py_result.o, 1)); // Get return code from Python - ok = PyObject_IsTrue(py_bool); + ok = PyObject_IsTrue(py_bool.o); // We expect None or the value (depending on probe) if ( ok ) @@ -427,7 +411,7 @@ private: Py_ssize_t len; char *buf; - if ( PyString_AsStringAndSize(py_val, &buf, &len) != -1 ) + if ( PyString_AsStringAndSize(py_val.o, &buf, &len) != -1 ) { value->qclear(); value->append(buf, len); @@ -437,16 +421,15 @@ private: else { // Make sure the user returned (False, String) - if ( py_bool != Py_False || !PyString_Check(py_val) ) + if ( py_bool.o != Py_False || !PyString_Check(py_val.o) ) { *errstr = "Invalid return value returned from the Python callback!"; break; } // Get the error message - *errstr = PyString_AsString(py_val); + *errstr = PyString_AsString(py_val.o); } } while ( false ); - Py_DECREF(py_result); return ok; } @@ -458,19 +441,18 @@ private: // xrefs from the current item. // this callback may be missing. { + PYW_GIL_GET; + py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; - - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_self, - (char *)S_ANALYZE, - PY_FMT64 "i", - pyul_t(current_ea), - operand_num); - PYW_GIL_RELEASE; - + newref_t py_result( + PyObject_CallMethod( + _this->py_self, + (char *)S_ANALYZE, + PY_FMT64 "i", + pyul_t(current_ea), + operand_num)); + PyW_ShowCbErr(S_ANALYZE); - Py_XDECREF(py_result); } public: py_custom_data_format_t() @@ -479,9 +461,9 @@ public: py_self = NULL; } - const char *get_name() const - { - return df_name.c_str(); + const char *get_name() const + { + return df_name.c_str(); } int register_df(int dtid, PyObject *py_obj) @@ -493,10 +475,12 @@ public: memset(&df, 0, sizeof(df)); df.cbsize = sizeof(df); df.ud = this; - PyObject *py_attr = NULL; + PYW_GIL_CHECK_LOCKED_SCOPE(); do { + ref_t py_attr; + // name if ( !PyW_GetStringAttr(py_obj, S_NAME, &df_name) ) break; @@ -508,9 +492,8 @@ public: // props py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); - if ( py_attr != NULL && PyInt_Check(py_attr) ) - df.props = PyInt_AsLong(py_attr); - Py_XDECREF(py_attr); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + df.props = PyInt_AsLong(py_attr.o); // hotkey if ( PyW_GetStringAttr(py_obj, S_HOTKEY, &df_hotkey) ) @@ -518,36 +501,28 @@ public: // value_size py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); - if ( py_attr != NULL && PyInt_Check(py_attr) ) - df.value_size = PyInt_AsLong(py_attr); - Py_XDECREF(py_attr); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + df.value_size = PyInt_AsLong(py_attr.o); // text_width py_attr = PyW_TryGetAttrString(py_obj, S_TEXT_WIDTH); - if ( py_attr != NULL && PyInt_Check(py_attr) ) - df.text_width = PyInt_AsLong(py_attr); - Py_XDECREF(py_attr); + if ( py_attr != NULL && PyInt_Check(py_attr.o) ) + df.text_width = PyInt_AsLong(py_attr.o); // print cb py_attr = PyW_TryGetAttrString(py_obj, S_PRINTF); - if ( py_attr != NULL && PyCallable_Check(py_attr) ) + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) df.print = s_print; - Py_XDECREF(py_attr); // scan cb py_attr = PyW_TryGetAttrString(py_obj, S_SCAN); - if ( py_attr != NULL && PyCallable_Check(py_attr) ) + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) df.scan = s_scan; - Py_XDECREF(py_attr); // analyze cb py_attr = PyW_TryGetAttrString(py_obj, S_ANALYZE); - if ( py_attr != NULL && PyCallable_Check(py_attr) ) + if ( py_attr != NULL && PyCallable_Check(py_attr.o) ) df.analyze = s_analyze; - Py_XDECREF(py_attr); - - // Done with attribute - py_attr = NULL; // Now try to register dfid = register_custom_data_format(dtid, &df); @@ -559,19 +534,16 @@ public: py_self = py_obj; // Update the format ID - py_attr = PyInt_FromLong(dfid); - PyObject_SetAttrString(py_obj, S_ID, py_attr); - Py_DECREF(py_attr); - - py_attr = NULL; + py_attr = newref_t(PyInt_FromLong(dfid)); + PyObject_SetAttrString(py_obj, S_ID, py_attr.o); } while ( false ); - - Py_XDECREF(py_attr); return dfid; } bool unregister_df(int dtid) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Never registered? if ( dfid < 0 ) return true; @@ -635,6 +607,8 @@ static py_custom_data_format_list_t py_df_list; //------------------------------------------------------------------------ static PyObject *py_data_type_to_py_dict(const data_type_t *dt) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + return Py_BuildValue("{s:" PY_FMT64 ",s:i,s:i,s:s,s:s,s:s,s:s}", S_VALUE_SIZE, pyul_t(dt->value_size), S_PROPS, dt->props, @@ -648,6 +622,8 @@ static PyObject *py_data_type_to_py_dict(const data_type_t *dt) //------------------------------------------------------------------------ static PyObject *py_data_format_to_py_dict(const data_format_t *df) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + return Py_BuildValue("{s:i,s:i,s:i,s:" PY_FMT64 ",s:s,s:s,s:s}", S_PROPS, df->props, S_CBSIZE, df->cbsize, @@ -684,6 +660,7 @@ def visit_patched_bytes(ea1, ea2, callable): */ static int py_visit_patched_bytes(ea_t ea1, ea_t ea2, PyObject *py_callable) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCallable_Check(py_callable) ) return 0; else @@ -732,29 +709,23 @@ def get_many_bytes(ea, size): */ static PyObject *py_get_many_bytes(ea_t ea, unsigned int size) { + PYW_GIL_CHECK_LOCKED_SCOPE(); do { if ( size <= 0 ) break; // Allocate memory via Python - PyObject *py_buf = PyString_FromStringAndSize(NULL, Py_ssize_t(size)); + newref_t py_buf(PyString_FromStringAndSize(NULL, Py_ssize_t(size))); if ( py_buf == NULL ) break; // Read bytes - bool ok = get_many_bytes(ea, PyString_AsString(py_buf), size); + if ( !get_many_bytes(ea, PyString_AsString(py_buf.o), size) ) + Py_RETURN_NONE; - // If failed, dispose the Python string - if ( !ok ) - { - Py_DECREF(py_buf); - - py_buf = Py_None; - Py_INCREF(py_buf); - } - - return py_buf; + py_buf.incref(); + return py_buf.o; } while ( false ); Py_RETURN_NONE; } @@ -805,9 +776,11 @@ static PyObject *py_get_ascii_contents2( } if ( type == ASCSTR_C && used_size > 0 && buf[used_size-1] == '\0' ) used_size--; - PyObject *py_buf = PyString_FromStringAndSize((const char *)buf, used_size); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_buf(PyString_FromStringAndSize((const char *)buf, used_size)); qfree(buf); - return py_buf; + py_buf.incref(); + return py_buf.o; } //--------------------------------------------------------------------------- /* @@ -987,6 +960,7 @@ def get_custom_data_format(dtid, dfid): // Get definition of a registered custom data format and returns a dictionary static PyObject *py_get_custom_data_format(int dtid, int fid) { + PYW_GIL_CHECK_LOCKED_SCOPE(); const data_format_t *df = get_custom_data_format(dtid, fid); if ( df == NULL ) Py_RETURN_NONE; @@ -1007,6 +981,7 @@ def get_custom_data_type(dtid): // Get definition of a registered custom data format and returns a dictionary static PyObject *py_get_custom_data_type(int dtid) { + PYW_GIL_CHECK_LOCKED_SCOPE(); const data_type_t *dt = get_custom_data_type(dtid); if ( dt == NULL ) Py_RETURN_NONE; diff --git a/swig/dbg.i b/swig/dbg.i index 10f5fcd..5bc8a19 100644 --- a/swig/dbg.i +++ b/swig/dbg.i @@ -25,7 +25,14 @@ typedef struct %rename (get_manual_regions) py_get_manual_regions; %ignore set_manual_regions; %ignore inform_idc_about_debthread; +%ignore is_dbgmem_valid; +// We want ALL wrappers around what is declared in dbg.hpp +// to release the GIL when calling into the IDA api: those +// might be very long operations, that even require some +// network traffic. +%thread; %include "dbg.hpp" +%nothread; %ignore DBG_Callback; %feature("director") DBG_Hooks; @@ -112,6 +119,7 @@ static PyObject *refresh_debugger_memory() // Invalidate the cache isEnabled(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_RETURN_NONE; } @@ -119,74 +127,78 @@ int idaapi DBG_Callback(void *ud, int notification_code, va_list va); class DBG_Hooks { public: - virtual ~DBG_Hooks() { unhook(); }; + virtual ~DBG_Hooks() { unhook(); } - bool hook() { return hook_to_notification_point(HT_DBG, DBG_Callback, this); }; - bool unhook() { return unhook_from_notification_point(HT_DBG, DBG_Callback, this); }; + bool hook() { return hook_to_notification_point(HT_DBG, DBG_Callback, this); } + bool unhook() { return unhook_from_notification_point(HT_DBG, DBG_Callback, this); } /* Hook functions to be overridden in Python */ virtual void dbg_process_start(pid_t pid, thid_t tid, ea_t ea, char *name, ea_t base, - asize_t size) { }; + asize_t size) {} virtual void dbg_process_exit(pid_t pid, thid_t tid, ea_t ea, - int exit_code) { }; + int exit_code) {} virtual void dbg_process_attach(pid_t pid, thid_t tid, ea_t ea, char *name, ea_t base, - asize_t size) { }; + asize_t size) {} virtual void dbg_process_detach(pid_t pid, thid_t tid, - ea_t ea) { }; + ea_t ea) {} virtual void dbg_thread_start(pid_t pid, thid_t tid, - ea_t ea) { }; + ea_t ea) {} virtual void dbg_thread_exit(pid_t pid, thid_t tid, ea_t ea, - int exit_code) { }; + int exit_code) {} virtual void dbg_library_load(pid_t pid, thid_t tid, ea_t ea, char *name, ea_t base, - asize_t size) { }; + asize_t size) {} virtual void dbg_library_unload(pid_t pid, thid_t tid, ea_t ea, - char *libname) { }; + char *libname) {} virtual void dbg_information(pid_t pid, thid_t tid, ea_t ea, - char *info) { }; + char *info) {} virtual int dbg_exception(pid_t pid, thid_t tid, ea_t ea, int code, bool can_cont, ea_t exc_ea, - char *info) { return 0; }; - virtual void dbg_suspend_process(void) { }; - virtual int dbg_bpt(thid_t tid, ea_t breakpoint_ea) { return 0; }; - virtual int dbg_trace(thid_t tid, ea_t ip) { return 0; }; + char *info) { return 0; } + virtual void dbg_suspend_process(void) {} + virtual int dbg_bpt(thid_t tid, ea_t breakpoint_ea) { return 0; } + virtual int dbg_trace(thid_t tid, ea_t ip) { return 0; } virtual void dbg_request_error(int failed_command, - int failed_dbg_notification) { }; - virtual void dbg_step_into(void) { }; - virtual void dbg_step_over(void) { }; - virtual void dbg_run_to(pid_t pid, thid_t tid, ea_t ea) { }; - virtual void dbg_step_until_ret(void) { }; + int failed_dbg_notification) {} + virtual void dbg_step_into(void) {} + virtual void dbg_step_over(void) {} + virtual void dbg_run_to(pid_t pid, thid_t tid, ea_t ea) {} + virtual void dbg_step_until_ret(void) {} }; int idaapi DBG_Callback(void *ud, int notification_code, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + class DBG_Hooks *proxy = (class DBG_Hooks *)ud; debug_event_t *event; int code = 0; + try { switch (notification_code) diff --git a/swig/diskio.i b/swig/diskio.i index 8a62dd1..500b2a8 100644 --- a/swig/diskio.i +++ b/swig/diskio.i @@ -49,17 +49,17 @@ //-------------------------------------------------------------------------- int idaapi py_enumerate_files_cb(const char *file, void *ud) { - PyObject *py_file = PyString_FromString(file); - PYW_GIL_ENSURE; - PyObject *py_ret = PyObject_CallFunctionObjArgs( - (PyObject *)ud, - py_file, - NULL); - PYW_GIL_RELEASE; - int r = (py_ret == NULL || !PyNumber_Check(py_ret)) ? 1 /* stop enumeration on failure */ : PyInt_AsLong(py_ret); - Py_XDECREF(py_file); - Py_XDECREF(py_ret); - return r; + // No need to 'PYW_GIL_GET' here, as this is called synchronously + // and from the same thread as the one that executes + // 'py_enumerate_files'. + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_file(PyString_FromString(file)); + newref_t py_ret( + PyObject_CallFunctionObjArgs( + (PyObject *)ud, + py_file.o, + NULL)); + return (py_ret == NULL || !PyNumber_Check(py_ret.o)) ? 1 /* stop enum on failure */ : PyInt_AsLong(py_ret.o); } // %} @@ -173,6 +173,7 @@ private: //-------------------------------------------------------------------------- void _from_cobject(PyObject *pycobject) { + PYW_GIL_CHECK_LOCKED_SCOPE(); this->set_linput((linput_t *)PyCObject_AsVoidPtr(pycobject)); } @@ -196,6 +197,7 @@ public: //-------------------------------------------------------------------------- loader_input_t(PyObject *pycobject = NULL): li(NULL), own(OWN_NONE), __idc_cvt_id__(PY_ICID_OPAQUE) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( pycobject != NULL && PyCObject_Check(pycobject) ) _from_cobject(pycobject); } @@ -206,11 +208,13 @@ public: if ( li == NULL ) return; + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; if ( own == OWN_CREATE ) close_linput(li); else if ( own == OWN_FROM_FP ) unmake_linput(li); - + Py_END_ALLOW_THREADS; li = NULL; own = OWN_NONE; } @@ -225,14 +229,17 @@ public: bool open(const char *filename, bool remote = false) { close(); + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; li = open_linput(filename, remote); - if ( li == NULL ) - return false; - - // Save file name - fn = filename; - own = OWN_CREATE; - return true; + if ( li != NULL ) + { + // Save file name + fn = filename; + own = OWN_CREATE; + } + Py_END_ALLOW_THREADS; + return li != NULL; } //-------------------------------------------------------------------------- @@ -256,6 +263,7 @@ public: // This method can be used to pass a linput_t* from C code static loader_input_t *from_cobject(PyObject *pycobject) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(pycobject) ) return NULL; loader_input_t *l = new loader_input_t(); @@ -266,14 +274,18 @@ public: //-------------------------------------------------------------------------- static loader_input_t *from_fp(FILE *fp) { + PYW_GIL_GET; + loader_input_t *l = NULL; + Py_BEGIN_ALLOW_THREADS; linput_t *fp_li = make_linput(fp); - if ( fp_li == NULL ) - return NULL; - - loader_input_t *l = new loader_input_t(); - l->own = OWN_FROM_FP; - l->fn.sprnt("", fp); - l->li = fp_li; + if ( fp_li != NULL ) + { + l = new loader_input_t(); + l->own = OWN_FROM_FP; + l->fn.sprnt("", fp); + l->li = fp_li; + } + Py_END_ALLOW_THREADS; return l; } @@ -286,37 +298,55 @@ public: //-------------------------------------------------------------------------- bool open_memory(ea_t start, asize_t size = 0) { - linput_t *l = create_memory_linput(start, size); - if ( l == NULL ) - return false; - close(); - li = l; - fn = ""; - own = OWN_CREATE; - return true; + PYW_GIL_GET; + linput_t *l; + Py_BEGIN_ALLOW_THREADS; + l = create_memory_linput(start, size); + if ( l != NULL ) + { + close(); + li = l; + fn = ""; + own = OWN_CREATE; + } + Py_END_ALLOW_THREADS; + return l != NULL; } //-------------------------------------------------------------------------- int32 seek(int32 pos, int whence = SEEK_SET) { - return qlseek(li, pos, whence); + int32 r; + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; + r = qlseek(li, pos, whence); + Py_END_ALLOW_THREADS; + return r; } //-------------------------------------------------------------------------- int32 tell() { - return qltell(li); + int32 r; + PYW_GIL_GET; + Py_BEGIN_ALLOW_THREADS; + r = qltell(li); + Py_END_ALLOW_THREADS; + return r; } //-------------------------------------------------------------------------- PyObject *getz(size_t sz, int32 fpos = -1) { + PYW_GIL_CHECK_LOCKED_SCOPE(); do { char *buf = (char *) malloc(sz + 5); if ( buf == NULL ) break; + Py_BEGIN_ALLOW_THREADS; qlgetz(li, fpos, buf, sz); + Py_END_ALLOW_THREADS; PyObject *ret = PyString_FromString(buf); free(buf); return ret; @@ -327,12 +357,17 @@ public: //-------------------------------------------------------------------------- PyObject *gets(size_t len) { + PYW_GIL_CHECK_LOCKED_SCOPE(); do { char *buf = (char *) malloc(len + 5); if ( buf == NULL ) break; - if ( qlgets(buf, len, li) == NULL ) + bool ok; + Py_BEGIN_ALLOW_THREADS; + ok = qlgets(buf, len, li) != NULL; + Py_END_ALLOW_THREADS; + if ( !ok ) { free(buf); break; @@ -347,12 +382,16 @@ public: //-------------------------------------------------------------------------- PyObject *read(size_t size) { + PYW_GIL_CHECK_LOCKED_SCOPE(); do { char *buf = (char *) malloc(size + 5); if ( buf == NULL ) break; - ssize_t r = qlread(li, buf, size); + ssize_t r; + Py_BEGIN_ALLOW_THREADS; + r = qlread(li, buf, size); + Py_END_ALLOW_THREADS; if ( r == -1 ) { free(buf); @@ -374,12 +413,16 @@ public: //-------------------------------------------------------------------------- PyObject *readbytes(size_t size, bool big_endian) { + PYW_GIL_CHECK_LOCKED_SCOPE(); do { char *buf = (char *) malloc(size + 5); if ( buf == NULL ) break; - int r = lreadbytes(li, buf, size, big_endian); + int r; + Py_BEGIN_ALLOW_THREADS; + r = lreadbytes(li, buf, size, big_endian); + Py_END_ALLOW_THREADS; if ( r == -1 ) { free(buf); @@ -395,25 +438,38 @@ public: //-------------------------------------------------------------------------- int file2base(int32 pos, ea_t ea1, ea_t ea2, int patchable) { - return ::file2base(li, pos, ea1, ea2, patchable); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = ::file2base(li, pos, ea1, ea2, patchable); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- int32 size() { - return qlsize(li); + int32 rc; + Py_BEGIN_ALLOW_THREADS; + rc = qlsize(li); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- PyObject *filename() { + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyString_FromString(fn.c_str()); } //-------------------------------------------------------------------------- PyObject *get_char() { - int ch = qlgetc(li); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int ch; + Py_BEGIN_ALLOW_THREADS; + ch = qlgetc(li); + Py_END_ALLOW_THREADS; if ( ch == EOF ) Py_RETURN_NONE; return Py_BuildValue("c", ch); @@ -432,7 +488,7 @@ def enumerate_files(path, fname, callback): @param callback: a callable object that takes the filename as its first argument and it returns 0 to continue enumeration or non-zero to stop enumeration. - @return: + @return: None in case of script errors tuple(code, fname) : If the callback returns non-zero """ @@ -441,6 +497,8 @@ def enumerate_files(path, fname, callback): */ PyObject *py_enumerate_files(PyObject *path, PyObject *fname, PyObject *callback) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + do { if ( !PyString_Check(path) || !PyString_Check(fname) || !PyCallable_Check(callback) ) diff --git a/swig/expr.i b/swig/expr.i index b4cacbd..14ab043 100644 --- a/swig/expr.i +++ b/swig/expr.i @@ -74,10 +74,12 @@ struct py_idcfunc_ctx_t int nargs; py_idcfunc_ctx_t(PyObject *py_func, const char *name, int nargs): py_func(py_func), name(name), nargs(nargs) { + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_INCREF(py_func); } ~py_idcfunc_ctx_t() { + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_DECREF(py_func); } }; @@ -91,25 +93,25 @@ static error_t py_call_idc_func( // Convert IDC arguments to Python list py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)_ctx; int cvt; - ppyobject_vec_t pargs; char errbuf[MAXSTR]; - if ( !pyw_convert_idc_args(argv, ctx->nargs, pargs, NULL, errbuf, sizeof(errbuf)) ) + + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_vec_t pargs; + if ( !pyw_convert_idc_args(argv, ctx->nargs, pargs, true, errbuf, sizeof(errbuf)) ) { // Error during conversion? Create an IDC exception return PyW_CreateIdcException(r, errbuf); } // Call the Python function - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallObject( - ctx->py_func, - pargs.empty() ? NULL : pargs[0]); - + newref_t py_result(PyObject_CallObject( + ctx->py_func, + pargs.empty() ? NULL : pargs[0].o)); + error_t err; if ( PyW_GetError(errbuf, sizeof(errbuf)) ) { err = PyW_CreateIdcException(r, errbuf); - Py_XDECREF(py_result); } else { @@ -120,13 +122,7 @@ static error_t py_call_idc_func( err = PyW_CreateIdcException(r, "ERROR: bad return value"); else err = eOk; - if ( cvt != CIP_OK_NODECREF ) - Py_XDECREF(py_result); } - PYW_GIL_RELEASE; - - // Free the converted args - pyw_free_idc_args(pargs); return err; } diff --git a/swig/fpro.i b/swig/fpro.i index e001f93..046eac6 100644 --- a/swig/fpro.i +++ b/swig/fpro.i @@ -100,6 +100,7 @@ private: } inline void _from_cobject(PyObject *pycobject) { + PYW_GIL_CHECK_LOCKED_SCOPE(); _from_fp((FILE *)PyCObject_AsVoidPtr(pycobject)); } public: @@ -117,7 +118,12 @@ public: own = true; fn.qclear(); __idc_cvt_id__ = PY_ICID_OPAQUE; - if ( pycobject != NULL && PyCObject_Check(pycobject) ) + bool ok; + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + ok = pycobject != NULL && PyCObject_Check(pycobject); + } + if ( ok ) _from_cobject(pycobject); } @@ -133,7 +139,11 @@ public: if ( fp == NULL ) return; if ( own ) + { + Py_BEGIN_ALLOW_THREADS; qfclose(fp); + Py_END_ALLOW_THREADS; + } fp = NULL; own = true; } @@ -148,7 +158,9 @@ public: bool open(const char *filename, const char *mode) { close(); + Py_BEGIN_ALLOW_THREADS; fp = qfopen(filename, mode); + Py_END_ALLOW_THREADS; if ( fp == NULL ) return false; // Save file name @@ -179,7 +191,11 @@ public: //-------------------------------------------------------------------------- static qfile_t *tmpfile() { - return from_fp(qtmpfile()); + FILE *fp; + Py_BEGIN_ALLOW_THREADS; + fp = qtmpfile(); + Py_END_ALLOW_THREADS; + return from_fp(fp); } //-------------------------------------------------------------------------- @@ -191,13 +207,21 @@ public: //-------------------------------------------------------------------------- int seek(int32 offset, int whence = SEEK_SET) { - return qfseek(fp, offset, whence); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfseek(fp, offset, whence); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- int32 tell() { - return qftell(fp); + int32 rc; + Py_BEGIN_ALLOW_THREADS; + rc = qftell(fp); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- @@ -208,12 +232,17 @@ public: char *buf = (char *) malloc(size + 5); if ( buf == NULL ) break; - int r = freadbytes(fp, buf, size, big_endian); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int r; + Py_BEGIN_ALLOW_THREADS; + r = freadbytes(fp, buf, size, big_endian); + Py_END_ALLOW_THREADS; if ( r != 0 ) { free(buf); break; } + PyObject *ret = PyString_FromStringAndSize(buf, r); free(buf); return ret; @@ -229,7 +258,11 @@ public: char *buf = (char *) malloc(size + 5); if ( buf == NULL ) break; - int r = qfread(fp, buf, size); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int r; + Py_BEGIN_ALLOW_THREADS; + r = qfread(fp, buf, size); + Py_END_ALLOW_THREADS; if ( r <= 0 ) { free(buf); @@ -250,7 +283,12 @@ public: char *buf = (char *) malloc(size + 5); if ( buf == NULL ) break; - if ( qfgets(buf, size, fp) == NULL ) + PYW_GIL_CHECK_LOCKED_SCOPE(); + char *p; + Py_BEGIN_ALLOW_THREADS; + p = qfgets(buf, size, fp); + Py_END_ALLOW_THREADS; + if ( p == NULL ) { free(buf); break; @@ -265,50 +303,87 @@ public: //-------------------------------------------------------------------------- int writebytes(PyObject *py_buf, bool big_endian) { - int sz = PyString_GET_SIZE(py_buf); - void *buf = (void *)PyString_AS_STRING(py_buf); - return fwritebytes(fp, buf, sz, big_endian); + Py_ssize_t sz; + void *buf; + PYW_GIL_CHECK_LOCKED_SCOPE(); + sz = PyString_GET_SIZE(py_buf); + buf = (void *)PyString_AS_STRING(py_buf); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = fwritebytes(fp, buf, int(sz), big_endian); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- int write(PyObject *py_buf) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyString_Check(py_buf) ) return 0; - return qfwrite(fp, (void *)PyString_AS_STRING(py_buf), PyString_GET_SIZE(py_buf)); + // Just so that there is no risk that the buffer returned by + // 'PyString_AS_STRING' gets deallocated within the + // Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_buf_ref(py_buf); + void *p = (void *)PyString_AS_STRING(py_buf); + Py_ssize_t sz = PyString_GET_SIZE(py_buf); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfwrite(fp, p, sz); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- int puts(const char *str) { - return qfputs(str, fp); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfputs(str, fp); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- int32 size() { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int32 r; + Py_BEGIN_ALLOW_THREADS; int pos = qfseek(fp, 0, SEEK_END); - int32 r = qftell(fp); + r = qftell(fp); qfseek(fp, pos, SEEK_SET); + Py_END_ALLOW_THREADS; return r; } //-------------------------------------------------------------------------- int flush() { - return qflush(fp); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qflush(fp); + Py_END_ALLOW_THREADS; + return rc; } //-------------------------------------------------------------------------- PyObject *filename() { + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyString_FromString(fn.c_str()); } //-------------------------------------------------------------------------- PyObject *get_char() { - int ch = qfgetc(fp); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int ch; + Py_BEGIN_ALLOW_THREADS; + ch = qfgetc(fp); + Py_END_ALLOW_THREADS; if ( ch == EOF ) Py_RETURN_NONE; return Py_BuildValue("c", ch); @@ -317,7 +392,12 @@ public: //-------------------------------------------------------------------------- int put_char(char chr) { - return qfputc(chr, fp); + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = qfputc(chr, fp); + Py_END_ALLOW_THREADS; + return rc; } }; // diff --git a/swig/frame.i b/swig/frame.i index f136507..568bfd2 100644 --- a/swig/frame.i +++ b/swig/frame.i @@ -17,6 +17,8 @@ %ignore write_stkpnts; %ignore del_stkpnts; %ignore rename_frame; +%ignore _set_frame_size; +%ignore add_empty_frame; %ignore get_stkvar; %rename (get_stkvar) py_get_stkvar; diff --git a/swig/graph.i b/swig/graph.i index 6faf7cc..1871a92 100644 --- a/swig/graph.i +++ b/swig/graph.i @@ -1,22 +1,47 @@ %ignore mutable_graph_t; %ignore graph_visitor_t; %ignore abstract_graph_t; +%include "graph.hpp" %{ // -class py_graph_t +class py_graph_t : public py_customidamemo_t { + typedef py_customidamemo_t inherited; + +protected: + + virtual void node_info_modified(int n, const node_info_t *ni, uint32 flags) + { + if ( ni == NULL ) + { + node_cache.erase(n); + } + else + { + nodetext_cache_t *c = node_cache.get(n); + if ( c != NULL ) + { + if ( (flags & NIF_TEXT) == NIF_TEXT ) + c->text = ni->text; + if ( (flags & NIF_BG_COLOR) == NIF_BG_COLOR ) + c->bgcolor = ni->bg_color; + } + } + } + + void collect_class_callbacks_ids(callbacks_ids_t *out); + private: enum { - GR_HAVE_USER_HINT = 0x00000001, - GR_HAVE_CLICKED = 0x00000002, - GR_HAVE_DBL_CLICKED = 0x00000004, - GR_HAVE_GOTFOCUS = 0x00000008, - GR_HAVE_LOSTFOCUS = 0x00000010, - GR_HAVE_CHANGED_CURRENT = 0x00000020, - GR_HAVE_CLOSE = 0x00000040, - GR_HAVE_COMMAND = 0x00000080 + GRCODE_HAVE_USER_HINT = 0x00010000, + GRCODE_HAVE_CLICKED = 0x00020000, + GRCODE_HAVE_DBL_CLICKED = 0x00040000, + GRCODE_HAVE_GOTFOCUS = 0x00080000, + GRCODE_HAVE_LOSTFOCUS = 0x00100000, + GRCODE_HAVE_CHANGED_CURRENT = 0x00200000, + GRCODE_HAVE_COMMAND = 0x00400000 }; struct nodetext_cache_t { @@ -43,20 +68,6 @@ private: } }; - class tform_pygraph_map_t: public std::map - { - public: - py_graph_t *get(TForm *form) - { - iterator it = find(form); - return it == end() ? NULL : it->second; - } - void add(TForm *form, py_graph_t *py) - { - (*this)[form] = py; - } - }; - class cmdid_map_t: public std::map { private: @@ -102,23 +113,25 @@ private: } }; - static tform_pygraph_map_t tform_pyg; static cmdid_map_t cmdid_pyg; - int cb_flags; - TForm *form; - graph_viewer_t *gv; + + // TForm *form; bool refresh_needed; - PyObject *self; nodetext_cache_map_t node_cache; + // instance callback + int gr_callback(int code, va_list va); + // static callback static int idaapi s_callback(void *obj, int code, va_list va) { + PYW_GIL_GET; return ((py_graph_t *)obj)->gr_callback(code, va); } static bool idaapi s_menucb(void *ud) { + PYW_GIL_GET; Py_ssize_t id = (Py_ssize_t)ud; py_graph_t *_this = cmdid_pyg.get(id); if ( _this != NULL ) @@ -130,212 +143,37 @@ private: void on_command(Py_ssize_t id) { // Check return value to OnRefresh() call - PYW_GIL_ENSURE; - PyObject *ret = PyObject_CallMethod(self, (char *)S_ON_COMMAND, "n", id); - PYW_GIL_RELEASE; - Py_XDECREF(ret); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_COMMAND, "n", id)); } // Refresh user-defined graph node number and edges // It calls Python method and expects that the user already filled // the nodes and edges. The nodes and edges are retrieved and passed to IDA - void on_user_refresh(mutable_graph_t *g) - { - if ( !refresh_needed ) - return; - - // Check return value to OnRefresh() call - PYW_GIL_ENSURE; - PyObject *ret = PyObject_CallMethod(self, (char *)S_ON_REFRESH, NULL); - PYW_GIL_RELEASE; - if ( ret == NULL || !PyObject_IsTrue(ret) ) - { - Py_XDECREF(ret); - return; - } - - // Refer to the nodes - PyObject *nodes = PyW_TryGetAttrString(self, S_M_NODES); - if ( ret == NULL || !PyList_Check(nodes) ) - { - Py_XDECREF(nodes); - return; - } - - // Refer to the edges - PyObject *edges = PyW_TryGetAttrString(self, S_M_EDGES); - if ( ret == NULL || !PyList_Check(edges) ) - { - Py_DECREF(nodes); - Py_XDECREF(edges); - return; - } - - // Resize the nodes - int max_nodes = abs(int(PyList_Size(nodes))); - g->clear(); - g->resize(max_nodes); - - // Mark that we refreshed already - refresh_needed = false; - - // Clear cached nodes - node_cache.clear(); - - // Get the edges - for ( int i=(int)PyList_Size(edges)-1; i>=0; i-- ) - { - // Each list item is a sequence (id1, id2) - PyObject *item = PyList_GetItem(edges, i); - if ( !PySequence_Check(item) ) - continue; - - // Get and validate each of the two elements in the sequence - int edge_ids[2]; - int j; - for ( j=0; j max_nodes ) - break; - - edge_ids[j] = v; - } - - // Incomplete? - if ( j != qnumber(edge_ids) ) - break; - - // Add the edge - g->add_edge(edge_ids[0], edge_ids[1], NULL); - } - Py_DECREF(nodes); - Py_DECREF(edges); - } + void on_user_refresh(mutable_graph_t *g); // Retrieves the text for user-defined graph node // It expects either a string or a tuple (string, bgcolor) - bool on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color) - { - // If already cached then return the value - nodetext_cache_t *c = node_cache.get(node); - if ( c != NULL ) - { - *str = c->text.c_str(); - if ( bg_color != NULL ) - *bg_color = c->bgcolor; - return true; - } - - // Not cached, call Python - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod(self, (char *)S_ON_GETTEXT, "i", node); - PYW_GIL_RELEASE; - if ( result == NULL ) - return false; - - bgcolor_t cl = bg_color == NULL ? DEFCOLOR : *bg_color; - const char *s; - - // User returned a string? - if ( PyString_Check(result) ) - { - s = PyString_AsString(result); - if ( s == NULL ) - s = ""; - c = node_cache.add(node, s, cl); - } - // User returned a sequence of text and bgcolor - else if ( PySequence_Check(result) && PySequence_Size(result) == 2 ) - { - PyObject *py_str = PySequence_GetItem(result, 0); - PyObject *py_color = PySequence_GetItem(result, 1); - - if ( py_str == NULL || !PyString_Check(py_str) || (s = PyString_AsString(py_str)) == NULL ) - s = ""; - if ( py_color != NULL && PyNumber_Check(py_color) ) - cl = bgcolor_t(PyLong_AsUnsignedLong(py_color)); - - c = node_cache.add(node, s, cl); - - Py_XDECREF(py_str); - Py_XDECREF(py_color); - } - Py_DECREF(result); - - *str = c->text.c_str(); - if ( bg_color != NULL ) - *bg_color = c->bgcolor; - - return true; - } + bool on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color); // Retrieves the hint for the user-defined graph // Calls Python and expects a string or None - int on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint) - { - // 'hint' must be allocated by qalloc() or qstrdup() - // out: 0-use default hint, 1-use proposed hint - - // We dispatch hints over nodes only - if ( mousenode == -1 ) - return 0; - - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod(self, (char *)S_ON_HINT, "i", mousenode); - PYW_GIL_RELEASE; - bool ok = result != NULL && PyString_Check(result); - if ( !ok ) - { - Py_XDECREF(result); - return 0; - } - *hint = qstrdup(PyString_AsString(result)); - Py_DECREF(result); - return 1; // use our hint - } + int on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint); // graph is being destroyed - void on_destroy(mutable_graph_t * /*g*/ = NULL) + void on_graph_destroyed(mutable_graph_t * /*g*/ = NULL) { - if ( self != NULL ) - { - if ( (cb_flags & GR_HAVE_CLOSE) != 0 ) - { - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL); - PYW_GIL_RELEASE; - - Py_XDECREF(result); - } - unbind(); - } - - // Remove the TForm from list - if ( form != NULL ) - tform_pyg.erase(form); - - // Remove all associated commands from the list - cmdid_pyg.clear(this); - - // Delete this instance - delete this; + refresh_needed = true; + node_cache.clear(); } // graph is being clicked int on_clicked( - graph_viewer_t * /*gv*/, + graph_viewer_t * /*view*/, selection_item_t * /*item1*/, graph_item_t *item2) { - // in: graph_viewer_t *gv + // in: graph_viewer_t *view // selection_item_t *current_item1 // graph_item_t *current_item2 // out: 0-ok, 1-ignore click @@ -347,29 +185,20 @@ private: if ( item2->n == -1 ) return 1; - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_CLICK, - "i", - item2->n); - PYW_GIL_RELEASE; - - if ( result == NULL || !PyObject_IsTrue(result) ) - { - Py_XDECREF(result); - return 1; - } - - Py_DECREF(result); - - return 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_CLICK, + "i", + item2->n)); + return result == NULL || !PyObject_IsTrue(result.o); } // a graph node has been double clicked - int on_dblclicked(graph_viewer_t * /*gv*/, selection_item_t *item) + int on_dblclicked(graph_viewer_t * /*view*/, selection_item_t *item) { - // in: graph_viewer_t *gv + // in: graph_viewer_t *view // selection_item_t *current_item // out: 0-ok, 1-ignore click //graph_viewer_t *v = va_arg(va, graph_viewer_t *); @@ -377,75 +206,410 @@ private: if ( item == NULL || !item->is_node ) return 1; - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_DBL_CLICK, - "i", - item->node); - PYW_GIL_RELEASE; - - if ( result == NULL || !PyObject_IsTrue(result) ) - { - Py_XDECREF(result); - return 1; - } - Py_DECREF(result); - return 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_DBL_CLICK, + "i", + item->node)); + return result == NULL || !PyObject_IsTrue(result.o); } // a graph viewer got focus - void on_gotfocus(graph_viewer_t * /*gv*/) + void on_gotfocus(graph_viewer_t * /*view*/) { - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_ACTIVATE, - NULL); - PYW_GIL_RELEASE; - Py_XDECREF(result); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_ACTIVATE, + NULL)); } // a graph viewer lost focus - void on_lostfocus(graph_viewer_t * /*gv*/) + void on_lostfocus(graph_viewer_t * /*view*/) { - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_DEACTIVATE, - NULL); - PYW_GIL_RELEASE; - - Py_XDECREF(result); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_DEACTIVATE, + NULL)); } // a new graph node became the current node - int on_changed_current(graph_viewer_t * /*gv*/, int curnode) + int on_changed_current(graph_viewer_t * /*view*/, int curnode) { - // in: graph_viewer_t *gv + // in: graph_viewer_t *view // int curnode // out: 0-ok, 1-forbid to change the current node if ( curnode < 0 ) return 0; - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_SELECT, - "i", - curnode); - PYW_GIL_RELEASE; - - bool allow = (result != NULL && PyObject_IsTrue(result)); - Py_XDECREF(result); - return allow ? 0 : 1; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_SELECT, + "i", + curnode)); + return !(result != NULL && PyObject_IsTrue(result.o)); } - int gr_callback(int code, va_list va) + // a group is being created + int on_creating_group(mutable_graph_t *my_g, intset_t *my_nodes) { - int ret; - switch ( code ) + PYW_GIL_CHECK_LOCKED_SCOPE(); + printf("my_g: %p; my_nodes: %p\n", my_g, my_nodes); + newref_t py_nodes(PyList_New(my_nodes->size())); + int i; + intset_t::const_iterator p; + for ( i = 0, p=my_nodes->begin(); p != my_nodes->end(); ++p, ++i ) + PyList_SetItem(py_nodes.o, i, PyInt_FromLong(*p)); + newref_t py_result( + PyObject_CallMethod( + self.o, + (char *)S_ON_CREATING_GROUP, + "O", + py_nodes.o)); + return (py_result == NULL || !PyInt_Check(py_result.o)) ? 1 : PyInt_AsLong(py_result.o); + } + + // a group is being deleted + int on_deleting_group(mutable_graph_t * /*g*/, int old_group) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // TODO + return 0; + } + + // a group is being collapsed/uncollapsed + int on_group_visibility(mutable_graph_t * /*g*/, int group, bool expand) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // TODO + return 0; + } + + + void show() + { + TForm *form; + if ( lookup_info.find_by_py_view(&form, NULL, this) ) + open_tform(form, FORM_TAB|FORM_MENU|FORM_QWIDGET); + } + + void jump_to_node(int nid) + { + viewer_center_on(view, nid); + int x, y; + + // will return a place only when a node was previously selected + place_t *old_pl = get_custom_viewer_place(view, false, &x, &y); + if ( old_pl != NULL ) { + user_graph_place_t *new_pl = (user_graph_place_t *) old_pl->clone(); + new_pl->node = nid; + jumpto(view, new_pl, x, y); + delete new_pl; + } + } + + virtual void refresh() + { + refresh_needed = true; + inherited::refresh(); + } + + int initialize(PyObject *self, const char *title) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + if ( !collect_pyobject_callbacks(self) ) + return -1; + + // Create form + HWND hwnd = NULL; + TForm *form = create_tform(title, &hwnd); + if ( hwnd != NULL ) // Created new tform + { + // get a unique graph id + netnode id; + char grnode[MAXSTR]; + qsnprintf(grnode, sizeof(grnode), "$ pygraph %s", title); + id.create(grnode); + graph_viewer_t *pview = create_graph_viewer(form, id, s_callback, this, 0); + open_tform(form, FORM_TAB | FORM_MENU | FORM_QWIDGET); + if ( pview != NULL ) + viewer_fit_window(pview); + bind(self, pview); + refresh(); + // Link "form" and "py_graph" + lookup_info.add(form, view, this); + } + else + { + show(); + } + + viewer_fit_window(view); + return 0; + } + + Py_ssize_t add_command(const char *title, const char *hotkey) + { + if ( !has_callback(GRCODE_HAVE_COMMAND) || view == NULL) + return 0; + Py_ssize_t cmd_id = cmdid_pyg.id(); + bool ok = viewer_add_menu_item(view, title, s_menucb, (void *)cmd_id, hotkey, 0); + if ( !ok ) + return 0; + cmdid_pyg.add(this); + return cmd_id; + } + +public: + py_graph_t() + { + // form = NULL; + refresh_needed = true; + } + + virtual ~py_graph_t() + { + // Remove all associated commands from the list + cmdid_pyg.clear(this); + } + + static void SelectNode(PyObject *self, int /*nid*/) + { + py_graph_t *_this = view_extract_this(self); + if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) ) + return; + + _this->jump_to_node(0); + } + + static Py_ssize_t AddCommand(PyObject *self, const char *title, const char *hotkey) + { + py_graph_t *_this = view_extract_this(self); + if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) ) + return 0; + + return _this->add_command(title, hotkey); + } + + static void Close(PyObject *self) + { + TForm *form; + py_graph_t *_this = view_extract_this(self); + if ( _this == NULL || !lookup_info.find_by_py_view(&form, NULL, _this) ) + return; + close_tform(form, FORM_CLOSE_LATER); + } + + static py_graph_t *Show(PyObject *self) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + + py_graph_t *py_graph = view_extract_this(self); + + // New instance? + if ( py_graph == NULL ) + { + qstring title; + if ( !PyW_GetStringAttr(self, S_M_TITLE, &title) ) + return NULL; + + // Form already created? try to get associated py_graph instance + // so that we reuse it + graph_viewer_t *found_view; + TForm *form = find_tform(title.c_str()); + if ( form != NULL ) + lookup_info.find_by_form(&found_view, (py_customidamemo_t**) &py_graph, form); + + if ( py_graph == NULL ) + { + py_graph = new py_graph_t(); + } + else + { + // unbind so we are rebound + py_graph->unbind(); + py_graph->refresh_needed = true; + } + if ( py_graph->initialize(self, title.c_str()) < 0 ) + { + delete py_graph; + py_graph = NULL; + } + } + else + { + py_graph->show(); + } + return py_graph; + } +}; + +//------------------------------------------------------------------------- +void py_graph_t::collect_class_callbacks_ids(callbacks_ids_t *out) +{ + inherited::collect_class_callbacks_ids(out); + out->add(S_ON_REFRESH, 0); + out->add(S_ON_GETTEXT, 0); + out->add(S_M_EDGES, -1); + out->add(S_M_NODES, -1); + out->add(S_ON_HINT, GRCODE_HAVE_USER_HINT); + out->add(S_ON_CLICK, GRCODE_HAVE_CLICKED); + out->add(S_ON_DBL_CLICK, GRCODE_HAVE_DBL_CLICKED); + out->add(S_ON_COMMAND, GRCODE_HAVE_COMMAND); + out->add(S_ON_SELECT, GRCODE_HAVE_CHANGED_CURRENT); + out->add(S_ON_ACTIVATE, GRCODE_HAVE_GOTFOCUS); + out->add(S_ON_DEACTIVATE, GRCODE_HAVE_LOSTFOCUS); +} + +//------------------------------------------------------------------------- +void py_graph_t::on_user_refresh(mutable_graph_t *g) +{ + if ( !refresh_needed || self == NULL /* Happens at creation-time */ ) + return; + + // Check return value to OnRefresh() call + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_REFRESH, NULL)); + if ( ret != NULL && PyObject_IsTrue(ret.o) ) + { + // Refer to the nodes + ref_t nodes(PyW_TryGetAttrString(self.o, S_M_NODES)); + if ( ret != NULL && PyList_Check(nodes.o) ) + { + // Refer to the edges + ref_t edges(PyW_TryGetAttrString(self.o, S_M_EDGES)); + if ( ret != NULL && PyList_Check(edges.o) ) + { + // Resize the nodes + int max_nodes = abs(int(PyList_Size(nodes.o))); + g->clear(); + g->resize(max_nodes); + + // Mark that we refreshed already + refresh_needed = false; + + // Clear cached nodes + node_cache.clear(); + + // Get the edges + for ( int i=(int)PyList_Size(edges.o)-1; i>=0; i-- ) + { + // Each list item is a sequence (id1, id2) + borref_t item(PyList_GetItem(edges.o, i)); + if ( !PySequence_Check(item.o) ) + continue; + + // Get and validate each of the two elements in the sequence + int edge_ids[2]; + int j; + for ( j=0; j max_nodes ) + break; + edge_ids[j] = v; + } + + // Incomplete? + if ( j != qnumber(edge_ids) ) + break; + + // Add the edge + g->add_edge(edge_ids[0], edge_ids[1], NULL); + } + } + } + } +} + +//------------------------------------------------------------------------- +bool py_graph_t::on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color) +{ + // If already cached then return the value + nodetext_cache_t *c = node_cache.get(node); + if ( c != NULL ) + { + *str = c->text.c_str(); + if ( bg_color != NULL ) + *bg_color = c->bgcolor; + return true; + } + + // Not cached, call Python + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_GETTEXT, "i", node)); + if ( result == NULL ) + return false; + + bgcolor_t cl = bg_color == NULL ? DEFCOLOR : *bg_color; + const char *s; + + // User returned a string? + if ( PyString_Check(result.o) ) + { + s = PyString_AsString(result.o); + if ( s == NULL ) + s = ""; + c = node_cache.add(node, s, cl); + } + // User returned a sequence of text and bgcolor + else if ( PySequence_Check(result.o) && PySequence_Size(result.o) == 2 ) + { + newref_t py_str(PySequence_GetItem(result.o, 0)); + newref_t py_color(PySequence_GetItem(result.o, 1)); + + if ( py_str == NULL || !PyString_Check(py_str.o) || (s = PyString_AsString(py_str.o)) == NULL ) + s = ""; + if ( py_color != NULL && PyNumber_Check(py_color.o) ) + cl = bgcolor_t(PyLong_AsUnsignedLong(py_color.o)); + + c = node_cache.add(node, s, cl); + } + + *str = c->text.c_str(); + if ( bg_color != NULL ) + *bg_color = c->bgcolor; + + return true; +} + +//------------------------------------------------------------------------- +int py_graph_t::on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint) +{ + // 'hint' must be allocated by qalloc() or qstrdup() + // out: 0-use default hint, 1-use proposed hint + + // We dispatch hints over nodes only + if ( mousenode == -1 ) + return 0; + + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_HINT, "i", mousenode)); + bool ok = result != NULL && PyString_Check(result.o); + if ( ok ) + *hint = qstrdup(PyString_AsString(result.o)); + return ok; // use our hint +} + + +//------------------------------------------------------------------------- +int py_graph_t::gr_callback(int code, va_list va) +{ + int ret; + switch ( code ) + { // case grcode_user_text: { @@ -456,19 +620,20 @@ private: ret = on_user_text(g, node, result, bgcolor); break; } - // + // case grcode_destroyed: - on_destroy(va_arg(va, mutable_graph_t *)); + on_graph_destroyed(va_arg(va, mutable_graph_t *)); ret = 0; break; - // + + // case grcode_clicked: - if ( cb_flags & GR_HAVE_CLICKED ) + if ( has_callback(GRCODE_HAVE_CLICKED) ) { - graph_viewer_t *gv = va_arg(va, graph_viewer_t *); + graph_viewer_t *view = va_arg(va, graph_viewer_t *); selection_item_t *item = va_arg(va, selection_item_t *); graph_item_t *gitem = va_arg(va, graph_item_t *); - ret = on_clicked(gv, item, gitem); + ret = on_clicked(view, item, gitem); } else { @@ -476,40 +641,40 @@ private: ret = 1; } break; - // + // case grcode_dblclicked: - if ( cb_flags & GR_HAVE_DBL_CLICKED ) + if ( has_callback(GRCODE_HAVE_DBL_CLICKED) ) { - graph_viewer_t *gv = va_arg(va, graph_viewer_t *); + graph_viewer_t *view = va_arg(va, graph_viewer_t *); selection_item_t *item = va_arg(va, selection_item_t *); - ret = on_dblclicked(gv, item); + ret = on_dblclicked(view, item); } else ret = 1; // ignore break; - // + // case grcode_gotfocus: - if ( cb_flags & GR_HAVE_GOTFOCUS ) + if ( has_callback(GRCODE_HAVE_GOTFOCUS) ) on_gotfocus(va_arg(va, graph_viewer_t *)); ret = 0; break; - // + // case grcode_lostfocus: - if ( cb_flags & GR_HAVE_LOSTFOCUS ) + if ( has_callback(GRCODE_HAVE_LOSTFOCUS) ) on_lostfocus(va_arg(va, graph_viewer_t *)); ret = 0; break; - // + // case grcode_user_refresh: on_user_refresh(va_arg(va, mutable_graph_t *)); ret = 1; break; - // + // case grcode_user_hint: - if ( cb_flags & GR_HAVE_USER_HINT ) + if ( has_callback(GRCODE_HAVE_USER_HINT) ) { mutable_graph_t *g = va_arg(va, mutable_graph_t *); int mousenode = va_arg(va, int); @@ -523,272 +688,62 @@ private: ret = 0; } break; - // + // case grcode_changed_current: - if ( cb_flags & GR_HAVE_CHANGED_CURRENT ) + if ( has_callback(GRCODE_HAVE_CHANGED_CURRENT) ) { - graph_viewer_t *gv = va_arg(va, graph_viewer_t *); + graph_viewer_t *view = va_arg(va, graph_viewer_t *); int cur_node = va_arg(va, int); - ret = on_changed_current(gv, cur_node); + ret = on_changed_current(view, cur_node); } else ret = 0; // allow selection change break; - // + // + case grcode_creating_group: // a group is being created + { + mutable_graph_t *g = va_arg(va, mutable_graph_t*); + intset_t *nodes = va_arg(va, intset_t*); + ret = on_creating_group(g, nodes); + } + break; + // + case grcode_deleting_group: // a group is being deleted + { + mutable_graph_t *g = va_arg(va, mutable_graph_t*); + int old_group = va_arg(va, int); + ret = on_deleting_group(g, old_group); + } + break; + // + case grcode_group_visibility: // a group is being collapsed/uncollapsed + { + mutable_graph_t *g = va_arg(va, mutable_graph_t*); + int group = va_arg(va, int); + bool expand = bool(va_arg(va, int)); + ret = on_group_visibility(g, group, expand); + } + break; + // default: ret = 0; break; - } - //grcode_changed_graph, // new graph has been set - //grcode_creating_group, // a group is being created - //grcode_deleting_group, // a group is being deleted - //grcode_group_visibility, // a group is being collapsed/uncollapsed - //grcode_user_size, // calculate node size for user-defined graph - //grcode_user_title, // render node title of a user-defined graph - //grcode_user_draw, // render node of a user-defined graph - return ret; } + //grcode_changed_graph, // new graph has been set + //grcode_user_size, // calculate node size for user-defined graph + //grcode_user_title, // render node title of a user-defined graph + //grcode_user_draw, // render node of a user-defined graph + return ret; +} - void unbind() - { - if ( self == NULL ) - return; - - // Unbind this object from the python object - PyObject *py_cobj = PyCObject_FromVoidPtr(NULL, NULL); - PyObject_SetAttrString(self, S_M_THIS, py_cobj); - Py_DECREF(py_cobj); - Py_XDECREF(self); - self = NULL; - } - - void show() - { - open_tform(form, FORM_TAB|FORM_MENU|FORM_QWIDGET); - } - - static py_graph_t *extract_this(PyObject *self) - { - // Try to extract "this" from the python object - PyObject *py_this = PyW_TryGetAttrString(self, S_M_THIS); - if ( py_this == NULL || !PyCObject_Check(py_this) ) - { - Py_XDECREF(py_this); - return NULL; - } - py_graph_t *ret = (py_graph_t *) PyCObject_AsVoidPtr(py_this); - Py_DECREF(py_this); - return ret; - } - - void jump_to_node(int nid) - { - viewer_center_on(gv, nid); - int x, y; - - // will return a place only when a node was previously selected - place_t *old_pl = get_custom_viewer_place(gv, false, &x, &y); - if ( old_pl != NULL ) - { - user_graph_place_t *new_pl = (user_graph_place_t *) old_pl->clone(); - new_pl->node = nid; - jumpto(gv, new_pl, x, y); - delete new_pl; - } - } - - void refresh() - { - refresh_needed = true; - refresh_viewer(gv); - } - - int create(PyObject *self, const char *title) - { - // check what callbacks we have - static const struct - { - const char *name; - int have; - } callbacks[] = - { - {S_ON_REFRESH, 0}, // 0 = mandatory callback - {S_ON_GETTEXT, 0}, - {S_M_EDGES, -1}, // -1 = mandatory attributes - {S_M_NODES, -1}, - {S_ON_HINT, GR_HAVE_USER_HINT}, - {S_ON_CLICK, GR_HAVE_CLICKED}, - {S_ON_DBL_CLICK, GR_HAVE_DBL_CLICKED}, - {S_ON_CLOSE, GR_HAVE_CLOSE}, - {S_ON_COMMAND, GR_HAVE_COMMAND}, - {S_ON_SELECT, GR_HAVE_CHANGED_CURRENT}, - {S_ON_ACTIVATE, GR_HAVE_GOTFOCUS}, - {S_ON_DEACTIVATE, GR_HAVE_LOSTFOCUS} - }; - cb_flags = 0; - for ( int i=0; i= 0 && PyCallable_Check(attr) == 0)) - { - Py_XDECREF(attr); - return -1; - } - if ( have > 0 && attr != NULL ) - cb_flags |= have; - Py_XDECREF(attr); - } - - - // Keep a reference - this->self = self; - Py_INCREF(self); - - // Bind py_graph_t to python object - PyObject *py_cobj = PyCObject_FromVoidPtr(this, NULL); - PyObject_SetAttrString(self, S_M_THIS, py_cobj); - Py_DECREF(py_cobj); - - // Create form - HWND hwnd = NULL; - form = create_tform(title, &hwnd); - - // Link "form" and "py_graph" - tform_pyg.add(form, this); - - if ( hwnd != NULL ) - { - // get a unique graph id - netnode id; - char grnode[MAXSTR]; - qsnprintf(grnode, sizeof(grnode), "$ pygraph %s", title); - id.create(grnode); - gv = create_graph_viewer(form, id, s_callback, this, 0); - open_tform(form, FORM_TAB | FORM_MENU | FORM_QWIDGET); - if ( gv != NULL ) - viewer_fit_window(gv); - } - else - { - show(); - } - - viewer_fit_window(gv); - return 0; - } - - Py_ssize_t add_command(const char *title, const char *hotkey) - { - if ( (cb_flags & GR_HAVE_COMMAND) == 0 || gv == NULL) - return 0; - Py_ssize_t cmd_id = cmdid_pyg.id(); - bool ok = viewer_add_menu_item(gv, title, s_menucb, (void *)cmd_id, hotkey, 0); - if ( !ok ) - return 0; - cmdid_pyg.add(this); - return cmd_id; - } - - public: - py_graph_t() - { - form = NULL; - gv = NULL; - refresh_needed = true; - self = NULL; - } - - static void SelectNode(PyObject *self, int /*nid*/) - { - py_graph_t *_this = extract_this(self); - if ( _this == NULL || _this->form == NULL ) - return; - - _this->jump_to_node(0); - } - - static Py_ssize_t AddCommand(PyObject *self, const char *title, const char *hotkey) - { - py_graph_t *_this = extract_this(self); - if ( _this == NULL || _this->form == NULL ) - return 0; - - return _this->add_command(title, hotkey); - } - - static void Close(PyObject *self) - { - py_graph_t *_this = extract_this(self); - if ( _this == NULL || _this->form == NULL ) - return; - - close_tform(_this->form, FORM_CLOSE_LATER); - } - - static void Refresh(PyObject *self) - { - py_graph_t *_this = extract_this(self); - if ( _this == NULL ) - return; - - _this->refresh(); - } - - static py_graph_t *Show(PyObject *self) - { - py_graph_t *ret = extract_this(self); - - // New instance? - if ( ret == NULL ) - { - qstring title; - if ( !PyW_GetStringAttr(self, S_M_TITLE, &title) ) - return NULL; - - // Form already created? try to get associated py_graph instance - // so that we reuse it - ret = tform_pyg.get(find_tform(title.c_str())); - - // Instance not found? create a new one - if ( ret == NULL ) - ret = new py_graph_t(); - else - { - // unbind so we are rebound - ret->unbind(); - ret->refresh_needed = true; - } - if ( ret->create(self, title.c_str()) < 0 ) - { - delete ret; - ret = NULL; - } - } - else - { - ret->show(); - } - return ret; - } -}; - -py_graph_t::tform_pygraph_map_t py_graph_t::tform_pyg; -py_graph_t::cmdid_map_t py_graph_t::cmdid_pyg; +//------------------------------------------------------------------------- +py_graph_t::cmdid_map_t py_graph_t::cmdid_pyg; bool pyg_show(PyObject *self) { return py_graph_t::Show(self) != NULL; } -void pyg_refresh(PyObject *self) -{ - py_graph_t::Refresh(self); -} - void pyg_close(PyObject *self) { py_graph_t::Close(self); @@ -796,6 +751,7 @@ void pyg_close(PyObject *self) PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey) { + PYW_GIL_CHECK_LOCKED_SCOPE(); return Py_BuildValue("n", py_graph_t::AddCommand(self, title, hotkey)); } @@ -808,9 +764,7 @@ void pyg_select_node(PyObject *self, int nid) %inline %{ // -void pyg_refresh(PyObject *self); void pyg_close(PyObject *self); - PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey); void pyg_select_node(PyObject *self, int nid); bool pyg_show(PyObject *self); @@ -819,7 +773,7 @@ bool pyg_show(PyObject *self); %pythoncode %{ # -class GraphViewer(object): +class GraphViewer(CustomIDAMemo): """This class wraps the user graphing facility provided by the graph.hpp file""" def __init__(self, title, close_open = False): """ @@ -872,12 +826,6 @@ class GraphViewer(object): """ _idaapi.pyg_close(self) - def Refresh(self): - """ - Refreshes the graph. This causes the OnRefresh() to be called - """ - _idaapi.pyg_refresh(self) - def Show(self): """ Shows an existing graph or creates a new one diff --git a/swig/hexrays.i b/swig/hexrays.i index 1b7818c..b99c881 100644 --- a/swig/hexrays.i +++ b/swig/hexrays.i @@ -21,7 +21,7 @@ #pragma SWIG nowarn=454 // Setting a pointer/reference variable may leak memory #define _STD_BEGIN -#define typename +#define typename %{ #include "hexrays.hpp" @@ -177,9 +177,9 @@ typedef intvec_t eavec_t;// vector of addresses %template(history_t) qstack; typedef int iterator_word; -/* no support for nested classes in swig means we need to wrap +/* no support for nested classes in swig means we need to wrap this iterator and do some magic... - + to use it, call qlist< cinsn_t >::begin() which will return the proper iterator type which can then be used to get the current item. */ @@ -246,27 +246,25 @@ void qswap(cinsn_t &a, cinsn_t &b); %{ //--------------------------------------------------------------------- -static int hexrays_python_call(PyObject *fct, PyObject *args) +static int hexrays_python_call(ref_t fct, ref_t args) { - PyObject *resultobj; + PYW_GIL_GET; + int result; int ecode1 = 0 ; - resultobj = PyEval_CallObject(fct, args); - - if (resultobj == NULL) + newref_t resultobj(PyEval_CallObject(fct.o, args.o)); + if ( resultobj == NULL ) { msg("IDAPython: Hex-rays python callback raised an exception.\n"); - + // we can't do much else than clear the exception since this was not called from Python. // XXX: print stack trace? PyErr_Clear(); return 0; } - ecode1 = SWIG_AsVal_int(resultobj, &result); - Py_DECREF(resultobj); - + ecode1 = SWIG_AsVal_int(resultobj.o, &result); if (SWIG_IsOK(ecode1)) return result; @@ -277,22 +275,22 @@ static int hexrays_python_call(PyObject *fct, PyObject *args) //--------------------------------------------------------------------- static bool idaapi __python_custom_viewer_popup_item_callback(void *ud) { + PYW_GIL_GET; + int ret; - PyObject *fct = (PyObject *)ud; - - ret = hexrays_python_call(fct, NULL); - + borref_t fct((PyObject *)ud); + newref_t nil(NULL); + ret = hexrays_python_call(fct, nil); return ret ? true : false; } //--------------------------------------------------------------------- static int idaapi __hexrays_python_callback(void *ud, hexrays_event_t event, va_list va) { + PYW_GIL_GET; + int ret; - PyObject *fct = (PyObject *)ud; - void *argp = NULL; - PyObject *args = NULL; - + borref_t fct((PyObject *)ud); switch(event) { case hxe_maturity: @@ -302,13 +300,9 @@ static int idaapi __hexrays_python_callback(void *ud, hexrays_event_t event, va_ { cfunc_t *arg0 = va_arg(va, cfunc_t *); ctree_maturity_t arg1 = va_argi(va, ctree_maturity_t); - PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, 0 ); - - args = Py_BuildValue("(iOi)", event, arg0obj, arg1); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, 0 )); + newref_t args(Py_BuildValue("(iOi)", event, arg0obj.o, arg1)); ret = hexrays_python_call(fct, args); - - Py_XDECREF(arg0obj); - Py_DECREF(args); } break; case hxe_interr: @@ -316,31 +310,23 @@ static int idaapi __hexrays_python_callback(void *ud, hexrays_event_t event, va_ ///< int errcode { int arg0 = va_argi(va, int); - - args = Py_BuildValue("(ii)", event, arg0); + newref_t args(Py_BuildValue("(ii)", event, arg0)); ret = hexrays_python_call(fct, args); - - Py_DECREF(args); } break; case hxe_print_func: ///< Printing ctree and generating text. - ///< cfunc_t *cfunc - ///< vc_printer_t *vp + ///< cfunc_t *cfunc + ///< vc_printer_t *vp ///< Returns: 1 if text has been generated by the plugin { cfunc_t *arg0 = va_arg(va, cfunc_t *); vc_printer_t *arg1 = va_arg(va, vc_printer_t *); - PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, 0 ); - PyObject *arg1obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg1), SWIGTYPE_p_vc_printer_t, 0 ); - - args = Py_BuildValue("(iOO)", event, arg0obj, arg1obj); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, 0 )); + newref_t arg1obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg1), SWIGTYPE_p_vc_printer_t, 0 )); + newref_t args(Py_BuildValue("(iOO)", event, arg0obj.o, arg1obj.o)); ret = hexrays_python_call(fct, args); - - Py_XDECREF(arg0obj); - Py_XDECREF(arg1obj); - Py_DECREF(args); } break; @@ -350,13 +336,9 @@ static int idaapi __hexrays_python_callback(void *ud, hexrays_event_t event, va_ ///< vdui_t *vu { vdui_t *arg0 = va_arg(va, vdui_t *); - PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ); - - args = Py_BuildValue("(iO)", event, arg0obj); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); ret = hexrays_python_call(fct, args); - - Py_XDECREF(arg0obj); - Py_DECREF(args); } break; case hxe_switch_pseudocode: @@ -366,28 +348,20 @@ static int idaapi __hexrays_python_callback(void *ud, hexrays_event_t event, va_ ///< vdui_t *vu { vdui_t *arg0 = va_arg(va, vdui_t *); - PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ); - - args = Py_BuildValue("(iO)", event, arg0obj); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); ret = hexrays_python_call(fct, args); - - Py_XDECREF(arg0obj); - Py_DECREF(args); } break; case hxe_refresh_pseudocode: ///< Existing pseudocode text has been refreshed. - ///< vdui_t *vu + ///< vdui_t *vu ///< See also hxe_text_ready, which happens earlier { vdui_t *arg0 = va_arg(va, vdui_t *); - PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ); - - args = Py_BuildValue("(iO)", event, arg0obj); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); ret = hexrays_python_call(fct, args); - - Py_XDECREF(arg0obj); - Py_DECREF(args); } break; case hxe_close_pseudocode: @@ -395,32 +369,24 @@ static int idaapi __hexrays_python_callback(void *ud, hexrays_event_t event, va_ ///< vdui_t *vu { vdui_t *arg0 = va_arg(va, vdui_t *); - PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ); - - args = Py_BuildValue("(iO)", event, arg0obj); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); ret = hexrays_python_call(fct, args); - - Py_XDECREF(arg0obj); - Py_DECREF(args); } break; case hxe_keyboard: ///< Keyboard has been hit. - ///< vdui_t *vu - ///< int key_code (VK_...) - ///< int shift_state + ///< vdui_t *vu + ///< int key_code (VK_...) + ///< int shift_state ///< Should return: 1 if the event has been handled { vdui_t *arg0 = va_arg(va, vdui_t *); int arg1 = va_argi(va, int); int arg2 = va_argi(va, int); - PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ); - - args = Py_BuildValue("(iOii)", event, arg0obj, arg1, arg2); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iOii)", event, arg0obj.o, arg1, arg2)); ret = hexrays_python_call(fct, args); - - Py_XDECREF(arg0obj); - Py_DECREF(args); } break; case hxe_right_click: @@ -428,30 +394,22 @@ static int idaapi __hexrays_python_callback(void *ud, hexrays_event_t event, va_ ///< vdui_t *vu { vdui_t *arg0 = va_arg(va, vdui_t *); - PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ); - - args = Py_BuildValue("(iO)", event, arg0obj); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); ret = hexrays_python_call(fct, args); - - Py_XDECREF(arg0obj); - Py_DECREF(args); } break; case hxe_double_click: ///< Mouse double click. - ///< vdui_t *vu - ///< int shift_state + ///< vdui_t *vu + ///< int shift_state ///< Should return: 1 if the event has been handled { vdui_t *arg0 = va_arg(va, vdui_t *); int arg1 = va_argi(va, int); - PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ); - - args = Py_BuildValue("(iOi)", event, arg0obj, arg1); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iOi)", event, arg0obj.o, arg1)); ret = hexrays_python_call(fct, args); - - Py_XDECREF(arg0obj); - Py_DECREF(args); } break; case hxe_curpos: @@ -460,51 +418,39 @@ static int idaapi __hexrays_python_callback(void *ud, hexrays_event_t event, va_ ///< vdui_t *vu { vdui_t *arg0 = va_arg(va, vdui_t *); - PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ); - - args = Py_BuildValue("(iO)", event, arg0obj); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); ret = hexrays_python_call(fct, args); - - Py_XDECREF(arg0obj); - Py_DECREF(args); } break; case hxe_create_hint: ///< Create a hint for the current item. - ///< vdui_t *vu - ///< qstring *result_hint - ///< int *implines - ///< Possible return values: - ///< 0: the event has not been handled + ///< vdui_t *vu + ///< qstring *result_hint + ///< int *implines + ///< Possible return values: + ///< 0: the event has not been handled ///< 1: hint has been created (should set *implines to nonzero as well) ///< 2: hint has been created but the standard hints must be ///< appended by the decompiler { vdui_t *arg0 = va_arg(va, vdui_t *); - PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ); - - args = Py_BuildValue("(iO)", event, arg0obj); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); ret = hexrays_python_call(fct, args); - - Py_XDECREF(arg0obj); - Py_DECREF(args); } break; case hxe_text_ready: ///< Decompiled text is ready. - ///< vdui_t *vu + ///< vdui_t *vu ///< This event can be used to modify the output text (sv). ///< The text uses regular color codes (see lines.hpp) ///< COLOR_ADDR is used to store pointers to ctree elements { vdui_t *arg0 = va_arg(va, vdui_t *); - PyObject *arg0obj = SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ); - - args = Py_BuildValue("(iO)", event, arg0obj); + newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 )); + newref_t args(Py_BuildValue("(iO)", event, arg0obj.o)); ret = hexrays_python_call(fct, args); - - Py_XDECREF(arg0obj); - Py_DECREF(args); } break; default: @@ -512,7 +458,7 @@ static int idaapi __hexrays_python_callback(void *ud, hexrays_event_t event, va_ ret = 0; break; } - + return ret; } @@ -550,37 +496,40 @@ void __add_custom_viewer_popup_item( const char *hotkey, PyObject *custom_viewer_popup_item_callback) { - Py_INCREF(custom_viewer_popup_item_callback); - add_custom_viewer_popup_item(custom_viewer, title, hotkey, __python_custom_viewer_popup_item_callback, custom_viewer_popup_item_callback); + PYW_GIL_GET; + Py_INCREF(custom_viewer_popup_item_callback); + add_custom_viewer_popup_item(custom_viewer, title, hotkey, __python_custom_viewer_popup_item_callback, custom_viewer_popup_item_callback); }; //--------------------------------------------------------------------- bool __install_hexrays_callback(PyObject *hx_cblist_callback) { - if (install_hexrays_callback(__hexrays_python_callback, hx_cblist_callback)) - { - Py_INCREF(hx_cblist_callback); - return true; - } - return false; + PYW_GIL_GET; + if (install_hexrays_callback(__hexrays_python_callback, hx_cblist_callback)) + { + Py_INCREF(hx_cblist_callback); + return true; + } + return false; } //--------------------------------------------------------------------- int __remove_hexrays_callback(PyObject *hx_cblist_callback) { - int result, i; - result = remove_hexrays_callback(__hexrays_python_callback, hx_cblist_callback); - for (i=0;i= cot_empty and self.op <= cot_last: return self.cexpr elif self.op >= cit_empty and self.op < cit_end: return self.cinsn - + raise RuntimeError('unknown op type %s' % (repr(self.op), )) citem_t.to_specific_type = property(citem_to_specific_type) @@ -711,129 +660,129 @@ cexpr_t.opname = property(property_op_to_typename) def cexpr_operands(self): """ return a dictionary with the operands of a cexpr_t. """ - + if self.op >= cot_comma and self.op <= cot_asgumod or \ self.op >= cot_lor and self.op <= cot_fdiv or \ self.op == cot_idx: return {'x': self.x, 'y': self.y} - + elif self.op == cot_tern: return {'x': self.x, 'y': self.y, 'z': self.z} - + elif self.op in [cot_fneg, cot_neg, cot_sizeof] or \ self.op >= cot_lnot and self.op <= cot_predec: return {'x': self.x} - + elif self.op == cot_cast: return {'type': self.type, 'x': self.x} - + elif self.op == cot_call: return {'x': self.x, 'a': self.a} - + elif self.op in [cot_memref, cot_memptr]: return {'x': self.x, 'm': self.m} - + elif self.op == cot_num: return {'n': self.n} - + elif self.op == cot_fnum: return {'fpc': self.fpc} - + elif self.op == cot_str: return {'string': self.string} - + elif self.op == cot_obj: return {'obj_ea': self.obj_ea} - + elif self.op == cot_var: return {'v': self.v} - + elif self.op == cot_helper: return {'helper': self.helper} - + raise RuntimeError('unknown op type %s' % self.opname) cexpr_t.operands = property(cexpr_operands) def cinsn_details(self): """ return the details pointer for the cinsn_t object depending on the value of its op member. \ this is one of the cblock_t, cif_t, etc. objects. """ - + if self.op not in self.op_to_typename: raise RuntimeError('unknown item->op type') - + opname = self.opname if opname == 'empty': return self - + if opname in ['break', 'continue']: return None - + return getattr(self, 'c' + opname) cinsn_t.details = property(cinsn_details) def cblock_iter(self): - + iter = self.begin() for i in range(self.size()): yield iter.cur iter.next() - + return cblock_t.__iter__ = cblock_iter cblock_t.__len__ = cblock_t.size # cblock.find(cinsn_t) -> returns the iterator positioned at the given item def cblock_find(self, item): - + iter = self.begin() for i in range(self.size()): if iter.cur == item: return iter iter.next() - + return cblock_t.find = cblock_find # cblock.index(cinsn_t) -> returns the index of the given item def cblock_index(self, item): - + iter = self.begin() for i in range(self.size()): if iter.cur == item: return i iter.next() - + return cblock_t.index = cblock_index # cblock.at(int) -> returns the item at the given index index def cblock_at(self, index): - + iter = self.begin() for i in range(self.size()): if i == index: return iter.cur iter.next() - + return cblock_t.at = cblock_at # cblock.remove(cinsn_t) def cblock_remove(self, item): - + iter = self.find(item) self.erase(iter) - + return cblock_t.remove = cblock_remove # cblock.insert(index, cinsn_t) def cblock_insert(self, index, item): - + pos = self.at(index) iter = self.find(pos) self.insert(iter, item) - + return cblock_t.insert = cblock_insert @@ -848,13 +797,13 @@ cfuncptr_t.__str__ = lambda self: str(self.__deref__()) def cfunc_typestring(self): """ Get the function's return type typestring object. The full prototype \ can be obtained via typestring._print() method. """ - + ts = typestring() qt = qtype() - + result = self.get_func_type(ts, qt) if not result: return - + return ts cfunc_t.typestring = property(cfunc_typestring) cfuncptr_t.typestring = property(lambda self: self.__deref__().typestring) @@ -1000,18 +949,18 @@ def _map_setdefault(self, key, default=None): return default def _map_as_dict(maptype, name, keytype, valuetype): - + maptype.keytype = keytype maptype.valuetype = valuetype - + for fctname in ['begin', 'end', 'first', 'second', 'next', \ 'find', 'insert', 'erase', 'clear', 'size']: fct = getattr(_idaapi, name + '_' + fctname) setattr(maptype, '__' + fctname, fct) - + maptype.__len__ = maptype.size maptype.__getitem__ = maptype.at - + maptype.begin = lambda self, *args: self.__begin(self, *args) maptype.end = lambda self, *args: self.__end(self, *args) maptype.first = lambda self, *args: self.__first(*args) diff --git a/swig/ida.i b/swig/ida.i index 7c6e417..e3576a1 100644 --- a/swig/ida.i +++ b/swig/ida.i @@ -1,9 +1,16 @@ // Ignore kernel-only symbols %ignore dual_text_options_t; %ignore idainfo::init; +%ignore idainfo::init_netnode; +%ignore idainfo::precheck_idb_version; %ignore idainfo::retrieve; %ignore idainfo::read; %ignore idainfo::write; +%ignore idainfo::convert_va_format; +%ignore idainfo::upgrade_approved; +%ignore idainfo::will_upgrade; +%ignore idainfo::approve_upgrade; +%ignore idainfo::show_progress; %ignore idainfo::align_short_demnames; %ignore idainfo::align_strtype; %ignore idainfo::align_long_demnames; diff --git a/swig/idaapi.i b/swig/idaapi.i index 2eb3df4..90e7777 100644 --- a/swig/idaapi.i +++ b/swig/idaapi.i @@ -1,4 +1,8 @@ -%module(docstring="IDA Plugin SDK API wrapper",directors="1") idaapi +%module(docstring="IDA Plugin SDK API wrapper",directors="1",threads="1") idaapi +// * http://swig.10945.n7.nabble.com/How-to-release-Python-GIL-td5027.html +// * http://stackoverflow.com/questions/1576737/releasing-python-gil-in-c-code +// * http://matt.eifelle.com/2007/11/23/enabling-thread-support-in-swig-and-python/ +%nothread; // We don't want SWIG to release the GIL for *every* IDA API call. // Suppress 'previous definition of XX' warnings #pragma SWIG nowarn=302 // and others... @@ -165,380 +169,395 @@ struct scfld_t #define FT_BAD_TYPE -2 #define FT_OK 1 -//----------------------------------------------------------------------- -class pycvt_t -{ - struct attr_t - { - qstring str; - uint64 u64; - // User is responsible to release this attribute when done - PyObject *py_obj; - }; +// //----------------------------------------------------------------------- +// class pycvt_t +// { +// struct attr_t +// { +// qstring str; +// uint64 u64; +// // User is responsible to release this attribute when done +// PyObject *py_obj; +// }; - //----------------------------------------------------------------------- - static int get_attr( - PyObject *py_obj, - const char *attrname, - int ft, - attr_t &val) - { - PyObject *py_attr; - if ( (py_attr = PyW_TryGetAttrString(py_obj, attrname)) == NULL ) - return FT_NOT_FOUND; +// //----------------------------------------------------------------------- +// static int get_attr( +// PyObject *py_obj, +// const char *attrname, +// int ft, +// attr_t &val) +// { +// ref_t py_attr(PyW_TryGetAttrString(py_obj, attrname)); +// if ( py_attr == NULL ) +// return FT_NOT_FOUND; - int cvt = FT_OK; - if ( ft == FT_STR || ft == FT_CHAR && PyString_Check(py_attr) ) - val.str = PyString_AsString(py_attr); - else if ( (ft > FT_FIRST_NUM && ft < FT_LAST_NUM) && PyW_GetNumber(py_attr, &val.u64) ) - ; // nothing to be done - // A string array? - else if ( (ft == FT_STRARR || ft == FT_NUM16ARR || ft == FT_CHRARR_STATIC ) - && (PyList_CheckExact(py_attr) || PyW_IsSequenceType(py_attr)) ) - { - // Return a reference to the attribute - val.py_obj = py_attr; - // Do not decrement the reference to this attribute - py_attr = NULL; - } - else - cvt = FT_BAD_TYPE; - Py_XDECREF(py_attr); - return cvt; - } +// int cvt = FT_OK; +// if ( ft == FT_STR || ft == FT_CHAR && PyString_Check(py_attr.o) ) +// val.str = PyString_AsString(py_attr.o); +// else if ( (ft > FT_FIRST_NUM && ft < FT_LAST_NUM) && PyW_GetNumber(py_attr.o, &val.u64) ) +// ; // nothing to be done +// // A string array? +// else if ( (ft == FT_STRARR || ft == FT_NUM16ARR || ft == FT_CHRARR_STATIC ) +// && (PyList_CheckExact(py_attr.o) || PyW_IsSequenceType(py_attr.o)) ) +// { +// // Return a reference to the attribute +// val.py_obj = py_attr.o; +// // Do not decrement the reference to this attribute +// py_attr = NULL; +// } +// else +// cvt = FT_BAD_TYPE; +// return cvt; +// } - //----------------------------------------------------------------------- - static int idaapi make_str_list_cb( - PyObject *py_item, - Py_ssize_t index, - void *ud) - { - if ( !PyString_Check(py_item) ) - return CIP_FAILED; - char **a = (char **)ud; - a[index] = qstrdup(PyString_AsString(py_item)); - return CIP_OK; - } +// //----------------------------------------------------------------------- +// static int idaapi make_str_list_cb( +// PyObject *py_item, +// Py_ssize_t index, +// void *ud) +// { +// if ( !PyString_Check(py_item) ) +// return CIP_FAILED; +// char **a = (char **)ud; +// a[index] = qstrdup(PyString_AsString(py_item)); +// return CIP_OK; +// } - //----------------------------------------------------------------------- - // Converts an IDC list of strings to a C string list - static Py_ssize_t str_list_to_str_arr( - PyObject *py_list, - char ***arr) - { - // Take the size - Py_ssize_t size = pyvar_walk_list(py_list); +// //----------------------------------------------------------------------- +// // Converts an IDC list of strings to a C string list +// static Py_ssize_t str_list_to_str_arr( +// PyObject *py_list, +// char ***arr) +// { +// // Take the size +// Py_ssize_t size = pyvar_walk_list(py_list); - // Allocate a buffer - char **a = (char **)qalloc((size + 1) * sizeof(char *)); +// // Allocate a buffer +// char **a = (char **)qalloc((size + 1) * sizeof(char *)); - // Walk and populate - size = pyvar_walk_list(py_list, make_str_list_cb, a); +// // Walk and populate +// size = pyvar_walk_list(py_list, make_str_list_cb, a); - // Make the list NULL terminated - a[size] = NULL; +// // Make the list NULL terminated +// a[size] = NULL; - // Return the list to the user - *arr = a; +// // Return the list to the user +// *arr = a; - // Return the size of items processed - return size; - } +// // Return the size of items processed +// return size; +// } - //----------------------------------------------------------------------- - typedef qvector uint64vec_t; - static int idaapi make_int_list( - PyObject *py_item, - Py_ssize_t /*index*/, - void *ud) - { - uint64 val; - if ( !PyW_GetNumber(py_item, &val) ) - return CIP_FAILED; - uint64vec_t *vec = (uint64vec_t *)ud; - vec->push_back(val); - return CIP_OK; - } +// //----------------------------------------------------------------------- +// typedef qvector uint64vec_t; +// static int idaapi make_int_list( +// PyObject *py_item, +// Py_ssize_t /*index*/, +// void *ud) +// { +// uint64 val; +// if ( !PyW_GetNumber(py_item, &val) ) +// return CIP_FAILED; +// uint64vec_t *vec = (uint64vec_t *)ud; +// vec->push_back(val); +// return CIP_OK; +// } -public: - //----------------------------------------------------------------------- - // Frees a NULL terminated list of fields - static void free_fields( - const scfld_t *fields, - void *store_area) - { - for ( int i=0; ; i++ ) - { - // End of list? - const scfld_t &fd = fields[i]; - if ( fd.field_name == NULL ) - break; +// public: +// //----------------------------------------------------------------------- +// // Frees a NULL terminated list of fields +// static void free_fields( +// const scfld_t *fields, +// void *store_area) +// { +// for ( int i=0; ; i++ ) +// { +// // End of list? +// const scfld_t &fd = fields[i]; +// if ( fd.field_name == NULL ) +// break; - void *store = (void *)((char *)store_area + fd.field_offs); - int ft = fd.field_type & ~FT_VALUE_MASK; - switch ( ft ) - { - case FT_STR: // Simple string - { - char **s = (char **)store; - if ( *s != NULL ) - { - qfree(*s); - *s = NULL; - } - } - break; +// void *store = (void *)((char *)store_area + fd.field_offs); +// int ft = fd.field_type & ~FT_VALUE_MASK; +// switch ( ft ) +// { +// case FT_STR: // Simple string +// { +// char **s = (char **)store; +// if ( *s != NULL ) +// { +// qfree(*s); +// *s = NULL; +// } +// } +// break; - case FT_STRARR: // Array of strings - { - char ***op = (char ***)store, **p = *op; - while ( *p != NULL ) - qfree((void *)*p++); - qfree(*op); - *op = NULL; - } - break; +// case FT_STRARR: // Array of strings +// { +// char ***op = (char ***)store, **p = *op; +// while ( *p != NULL ) +// qfree((void *)*p++); +// qfree(*op); +// *op = NULL; +// } +// break; - case FT_NUM16ARR: - { - uint16 **arr = (uint16 **)store; - if ( *arr != NULL ) - { - qfree(*arr); - *arr = NULL; - } - } - break; - } - } - } +// case FT_NUM16ARR: +// { +// uint16 **arr = (uint16 **)store; +// if ( *arr != NULL ) +// { +// qfree(*arr); +// *arr = NULL; +// } +// } +// break; +// } +// } +// } - //----------------------------------------------------------------------- - // Converts from a C structure to Python - static int from_c( - const scfld_t *fields, - void *read_area, - PyObject *py_obj) - { - PyObject *py_attr; - int i; - bool ok = false; - for ( i=0; ; i++ ) - { - // End of list? - const scfld_t &fd = fields[i]; - if ( fd.field_name == NULL ) - { - ok = true; - break; - } +// //----------------------------------------------------------------------- +// // Converts from a C structure to Python +// static int from_c( +// const scfld_t *fields, +// void *read_area, +// PyObject *py_obj) +// { +// PyObject *py_attr; +// int i; +// bool ok = false; +// for ( i=0; ; i++ ) +// { +// // End of list? +// const scfld_t &fd = fields[i]; +// if ( fd.field_name == NULL ) +// { +// ok = true; +// break; +// } - // Point to structure member - int ft = fd.field_type & ~FT_VALUE_MASK; - void *read = (void *)((char *)read_area + fd.field_offs); - // Create the python attribute properly - if ( ft > FT_FIRST_NUM && ft < FT_LAST_NUM ) - { - if ( ft == FT_NUM16 ) - py_attr = Py_BuildValue("H", *(uint16 *)read); - else if ( ft == FT_NUM32 ) - py_attr = Py_BuildValue("I", *(uint32 *)read); - else if ( ft == FT_INT ) - py_attr = Py_BuildValue("i", *(int *)read); - else if ( ft == FT_SIZET ) - py_attr = Py_BuildValue(PY_FMT64,*(size_t *)read); - else if ( ft == FT_SSIZET ) - py_attr = Py_BuildValue(PY_SFMT64,*(ssize_t *)read); - } - else if ( ft == FT_STR || ft == FT_CHAR ) - { - if ( ft == FT_STR ) - py_attr = PyString_FromString(*(char **)read); - else - py_attr = Py_BuildValue("c", *(char *)read); - } - else if ( ft == FT_STRARR ) - { - char **arr = *(char ***)read; - py_attr = PyList_New(0); - while ( *arr != NULL ) - PyList_Append(py_attr, PyString_FromString(*arr++)); - } - else - continue; - PyObject_SetAttrString(py_obj, fd.field_name, py_attr); - Py_XDECREF(py_attr); - } - return ok ? -1 : i; - } +// // Point to structure member +// int ft = fd.field_type & ~FT_VALUE_MASK; +// void *read = (void *)((char *)read_area + fd.field_offs); +// // Create the python attribute properly +// if ( ft > FT_FIRST_NUM && ft < FT_LAST_NUM ) +// { +// if ( ft == FT_NUM16 ) +// py_attr = Py_BuildValue("H", *(uint16 *)read); +// else if ( ft == FT_NUM32 ) +// py_attr = Py_BuildValue("I", *(uint32 *)read); +// else if ( ft == FT_INT ) +// py_attr = Py_BuildValue("i", *(int *)read); +// else if ( ft == FT_SIZET ) +// py_attr = Py_BuildValue(PY_FMT64,*(size_t *)read); +// else if ( ft == FT_SSIZET ) +// py_attr = Py_BuildValue(PY_SFMT64,*(ssize_t *)read); +// } +// else if ( ft == FT_STR || ft == FT_CHAR ) +// { +// if ( ft == FT_STR ) +// py_attr = PyString_FromString(*(char **)read); +// else +// py_attr = Py_BuildValue("c", *(char *)read); +// } +// else if ( ft == FT_STRARR ) +// { +// char **arr = *(char ***)read; +// py_attr = PyList_New(0); +// while ( *arr != NULL ) +// PyList_Append(py_attr, PyString_FromString(*arr++)); +// } +// else +// continue; +// PyObject_SetAttrString(py_obj, fd.field_name, py_attr); +// Py_XDECREF(py_attr); +// } +// return ok ? -1 : i; +// } - //----------------------------------------------------------------------- - // Converts fields from IDC and field description into a C structure - // If 'use_extlang' is specified, then the passed idc_obj is considered - // to be an opaque object and thus can be queried only through extlang - static int from_script( - const scfld_t *fields, - void *store_area, - PyObject *py_obj) - { - int i; - bool ok = false; - attr_t attr; - for ( i=0; ; i++ ) - { - // End of list? - const scfld_t &fd = fields[i]; - if ( fd.field_name == NULL ) - { - ok = true; - break; - } +// //----------------------------------------------------------------------- +// // Converts fields from IDC and field description into a C structure +// // If 'use_extlang' is specified, then the passed idc_obj is considered +// // to be an opaque object and thus can be queried only through extlang +// static int from_script( +// const scfld_t *fields, +// void *store_area, +// PyObject *py_obj) +// { +// int i; +// bool ok = false; +// attr_t attr; +// for ( i=0; ; i++ ) +// { +// // End of list? +// const scfld_t &fd = fields[i]; +// if ( fd.field_name == NULL ) +// { +// ok = true; +// break; +// } - // Get field type - int ft = fd.field_type & ~FT_VALUE_MASK; +// // Get field type +// int ft = fd.field_type & ~FT_VALUE_MASK; - // Point to structure member - void *store = (void *)((char *)store_area + fd.field_offs); +// // Point to structure member +// void *store = (void *)((char *)store_area + fd.field_offs); - // Retrieve attribute and type - int cvt = get_attr(py_obj, fd.field_name, ft, attr); +// // Retrieve attribute and type +// int cvt = get_attr(py_obj, fd.field_name, ft, attr); - // Attribute not found? - if ( cvt == FT_NOT_FOUND ) - { - // Skip optional fields - if ( fd.is_optional ) - continue; - break; - } +// // Attribute not found? +// if ( cvt == FT_NOT_FOUND ) +// { +// // Skip optional fields +// if ( fd.is_optional ) +// continue; +// break; +// } - if ( ft == FT_STR ) - *(char **)store = qstrdup(attr.str.c_str()); - else if ( ft == FT_NUM32 ) - *(uint32 *)store = uint32(attr.u64); - else if ( ft == FT_NUM16 ) - *(uint16 *)store = attr.u64 & 0xffff; - else if ( ft == FT_INT ) - *(int *)store = int(attr.u64); - else if ( ft == FT_SIZET ) - *(size_t *)store = size_t(attr.u64); - else if ( ft == FT_SSIZET ) - *(ssize_t *)store = ssize_t(attr.u64); - else if ( ft == FT_CHAR ) - *(char *)store = *attr.str.c_str(); - else if ( ft == FT_STRARR ) - { - str_list_to_str_arr(attr.py_obj, (char ***)store); - Py_DECREF(attr.py_obj); - } - else if ( ft == FT_CHRARR_STATIC ) - { - size_t sz = (fd.field_type & FT_VALUE_MASK) >> 16; - if ( sz == 0 ) - break; - uint64vec_t w; - char *a = (char *) store; - if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) ) - { - sz = qmin(w.size(), sz); - for ( size_t i=0; i < sz; i++ ) - a[i] = w[i] & 0xFF; - } - } - else if ( ft == FT_NUM16ARR ) - { - uint64vec_t w; - if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) > 0 ) - { - size_t max_sz = (fd.field_type & FT_VALUE_MASK) >> 16; - bool zero_term; - if ( max_sz == 0 ) - { - zero_term = true; - max_sz = w.size(); - } - else - { - zero_term = false; - max_sz = qmin(max_sz, w.size()); - } - // Allocate as much as we parsed elements - // Add one more element if list was zero terminated - uint16 *a = (uint16 *)qalloc(sizeof(uint16) * (max_sz + (zero_term ? 1 : 0))) ; - for ( size_t i=0; i < max_sz; i++ ) - a[i] = w[i] & 0xFF; +// if ( ft == FT_STR ) +// *(char **)store = qstrdup(attr.str.c_str()); +// else if ( ft == FT_NUM32 ) +// *(uint32 *)store = uint32(attr.u64); +// else if ( ft == FT_NUM16 ) +// *(uint16 *)store = attr.u64 & 0xffff; +// else if ( ft == FT_INT ) +// *(int *)store = int(attr.u64); +// else if ( ft == FT_SIZET ) +// *(size_t *)store = size_t(attr.u64); +// else if ( ft == FT_SSIZET ) +// *(ssize_t *)store = ssize_t(attr.u64); +// else if ( ft == FT_CHAR ) +// *(char *)store = *attr.str.c_str(); +// else if ( ft == FT_STRARR ) +// { +// str_list_to_str_arr(attr.py_obj, (char ***)store); +// Py_DECREF(attr.py_obj); +// } +// else if ( ft == FT_CHRARR_STATIC ) +// { +// size_t sz = (fd.field_type & FT_VALUE_MASK) >> 16; +// if ( sz == 0 ) +// break; +// uint64vec_t w; +// char *a = (char *) store; +// if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) ) +// { +// sz = qmin(w.size(), sz); +// for ( size_t i=0; i < sz; i++ ) +// a[i] = w[i] & 0xFF; +// } +// } +// else if ( ft == FT_NUM16ARR ) +// { +// uint64vec_t w; +// if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) > 0 ) +// { +// size_t max_sz = (fd.field_type & FT_VALUE_MASK) >> 16; +// bool zero_term; +// if ( max_sz == 0 ) +// { +// zero_term = true; +// max_sz = w.size(); +// } +// else +// { +// zero_term = false; +// max_sz = qmin(max_sz, w.size()); +// } +// // Allocate as much as we parsed elements +// // Add one more element if list was zero terminated +// uint16 *a = (uint16 *)qalloc(sizeof(uint16) * (max_sz + (zero_term ? 1 : 0))) ; +// for ( size_t i=0; i < max_sz; i++ ) +// a[i] = w[i] & 0xFF; - if ( zero_term ) - a[max_sz] = 0; - *(uint16 **)store = a; - } - } - else - { - // Unsupported field type! - break; - } - } - return ok ? -1 : i; - } -}; +// if ( zero_term ) +// a[max_sz] = 0; +// *(uint16 **)store = a; +// } +// } +// else +// { +// // Unsupported field type! +// break; +// } +// } +// return ok ? -1 : i; +// } +// }; //------------------------------------------------------------------------- Py_ssize_t pyvar_walk_list( - PyObject *py_list, - int (idaapi *cb)(PyObject *py_item, Py_ssize_t index, void *ud), - void *ud) + const ref_t &py_list, + int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud), + void *ud) { - if ( !PyList_CheckExact(py_list) && !PyW_IsSequenceType(py_list) ) - return CIP_FAILED; + PYW_GIL_CHECK_LOCKED_SCOPE(); - bool is_seq = !PyList_CheckExact(py_list); - Py_ssize_t size = is_seq ? PySequence_Size(py_list) : PyList_Size(py_list); - - if ( cb == NULL ) - return size; - - Py_ssize_t i; - for ( i=0; i= CIP_OK; + if ( !ok ) PyErr_SetString(PyExc_ValueError, "Could not convert Python object to IDC object!"); - return false; - } - return true; + return ok; } //------------------------------------------------------------------------ @@ -620,8 +634,10 @@ static idc_class_t *get_py_idc_cvt_opaque() //------------------------------------------------------------------------- // Utility function to create opaque / convertible Python <-> IDC variables // The referred Python variable will have its reference increased -static bool create_py_idc_opaque_obj(PyObject *py_var, idc_value_t *idc_var) +static bool wrap_PyObject_ptr(const ref_t &py_var, idc_value_t *idc_var) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Create an IDC object of this special helper class if ( VarObject(idc_var, get_py_idc_cvt_opaque()) != eOk ) return false; @@ -632,7 +648,8 @@ static bool create_py_idc_opaque_obj(PyObject *py_var, idc_value_t *idc_var) VarSetAttr(idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val); // Store the value as a PVOID referencing the given Python object - idc_val.set_pvoid(py_var); + py_var.incref(); + idc_val.set_pvoid(py_var.o); VarSetAttr(idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val); return true; @@ -646,6 +663,11 @@ static error_t idaapi py_idc_opaque_dtor( idc_value_t *argv, idc_value_t * /*res*/) { + // This can be called at plugin registration time, when a + // 'script_plugin_t' instance is ::free()'d. It is + // not guaranteed that we have the GIL at that point. + PYW_GIL_GET; + // Get the value from the object idc_value_t idc_val; VarGetAttr(&argv[0], S_PY_IDCCVT_VALUE_ATTR, &idc_val); @@ -663,42 +685,53 @@ static error_t idaapi py_idc_opaque_dtor( // Converts a Python variable into an IDC variable // This function returns on one CIP_XXXX int pyvar_to_idcvar( - PyObject *py_var, - idc_value_t *idc_var, - int *gvar_sn) + const ref_t &py_var, + idc_value_t *idc_var, + int *gvar_sn) { - PyObject *attr; + PYW_GIL_CHECK_LOCKED_SCOPE(); + // None / NULL - if ( py_var == NULL || py_var == Py_None ) - idc_var->set_long(0); - // Numbers? - else if ( PyW_GetNumberAsIDC(py_var, idc_var) ) - return CIP_OK; - // String - else if ( PyString_Check(py_var) ) - idc_var->_set_string(PyString_AsString(py_var), PyString_Size(py_var)); - // Float - else if ( PyBool_Check(py_var) ) - idc_var->set_long(py_var == Py_True ? 1 : 0); - // Boolean - else if ( PyFloat_Check(py_var) ) + if ( py_var == NULL || py_var.o == Py_None ) { - double dresult = PyFloat_AsDouble(py_var); + idc_var->set_long(0); + } + // Numbers? + else if ( PyW_GetNumberAsIDC(py_var.o, idc_var) ) + { + return CIP_OK; + } + // String + else if ( PyString_Check(py_var.o) ) + { + idc_var->_set_string(PyString_AsString(py_var.o), PyString_Size(py_var.o)); + } + // Boolean + else if ( PyBool_Check(py_var.o) ) + { + idc_var->set_long(py_var.o == Py_True ? 1 : 0); + } + // Float + else if ( PyFloat_Check(py_var.o) ) + { + double dresult = PyFloat_AsDouble(py_var.o); ieee_realcvt((void *)&dresult, idc_var->e, 3); idc_var->vtype = VT_FLOAT; } // void* - else if ( PyCObject_Check(py_var) ) - idc_var->set_pvoid(PyCObject_AsVoidPtr(py_var)); - // Is it a Python list? - else if ( PyList_CheckExact(py_var) || PyW_IsSequenceType(py_var) ) + else if ( PyCObject_Check(py_var.o) ) + { + idc_var->set_pvoid(PyCObject_AsVoidPtr(py_var.o)); + } + // Python list? + else if ( PyList_CheckExact(py_var.o) || PyW_IsSequenceType(py_var.o) ) { // Create the object VarObject(idc_var); // Determine list size and type - bool is_seq = !PyList_CheckExact(py_var); - Py_ssize_t size = is_seq ? PySequence_Size(py_var) : PyList_Size(py_var); + bool is_seq = !PyList_CheckExact(py_var.o); + Py_ssize_t size = is_seq ? PySequence_Size(py_var.o) : PyList_Size(py_var.o); bool ok = true; qstring attr_name; @@ -706,7 +739,11 @@ int pyvar_to_idcvar( for ( Py_ssize_t i=0; i (key, value) - PyObject *py_item = PyList_GetItem(py_items, i); + PyObject *py_item = PyList_GetItem(py_items.o, i); // Extract key/value - PyObject *key = PySequence_GetItem(py_item, 0); - PyObject *val = PySequence_GetItem(py_item, 1); + newref_t key(PySequence_GetItem(py_item, 0)); + newref_t val(PySequence_GetItem(py_item, 1)); // Get key's string representation - PyW_ObjectToString(key, &key_name); + PyW_ObjectToString(key.o, &key_name); // Convert the attribute into an IDC value idc_value_t v; @@ -763,17 +796,13 @@ int pyvar_to_idcvar( // Store the attribute VarSetAttr(idc_var, key_name.c_str(), &v); } - Py_XDECREF(key); - Py_XDECREF(val); if ( !ok ) break; } - // Decrement attribute reference - Py_DECREF(py_items); return ok ? CIP_OK : CIP_FAILED; } // Possible function? - else if ( PyCallable_Check(py_var) ) + else if ( PyCallable_Check(py_var.o) ) { idc_var->clear(); idc_var->vtype = VT_FUNC; @@ -786,20 +815,21 @@ int pyvar_to_idcvar( else { // Get the type - int cvt_id = get_pyidc_cvt_type(py_var); + int cvt_id = get_pyidc_cvt_type(py_var.o); switch ( cvt_id ) { // // INT64 // case PY_ICID_INT64: - // Get the value attribute - attr = PyW_TryGetAttrString(py_var, S_PY_IDCCVT_VALUE_ATTR); - if ( attr == NULL ) - return false; - idc_var->set_int64(PyLong_AsLongLong(attr)); - Py_DECREF(attr); - return CIP_OK; + { + // Get the value attribute + ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); + if ( attr == NULL ) + return false; + idc_var->set_int64(PyLong_AsLongLong(attr.o)); + return CIP_OK; + } // // BYREF // @@ -810,7 +840,7 @@ int pyvar_to_idcvar( return CIP_FAILED; // Get the value attribute - attr = PyW_TryGetAttrString(py_var, S_PY_IDCCVT_VALUE_ATTR); + ref_t attr(PyW_TryGetAttrString(py_var.o, S_PY_IDCCVT_VALUE_ATTR)); if ( attr == NULL ) return CIP_FAILED; @@ -826,7 +856,6 @@ int pyvar_to_idcvar( // Create a reference to this global variable VarRef(idc_var, gvar); } - Py_DECREF(attr); return ok ? CIP_OK : CIP_FAILED; } // @@ -834,28 +863,25 @@ int pyvar_to_idcvar( // case PY_ICID_OPAQUE: { - if ( !create_py_idc_opaque_obj(py_var, idc_var) ) + if ( !wrap_PyObject_ptr(py_var, idc_var) ) return CIP_FAILED; - return CIP_OK_NODECREF; + return CIP_OK_OPAQUE; } // // Other objects // default: // A normal object? - PyObject *py_dir = PyObject_Dir(py_var); - Py_ssize_t size = PyList_Size(py_dir); - if ( py_dir == NULL || !PyList_Check(py_dir) || size == 0 ) - { - Py_XDECREF(py_dir); + newref_t py_dir(PyObject_Dir(py_var.o)); + Py_ssize_t size = PyList_Size(py_dir.o); + if ( py_dir == NULL || !PyList_Check(py_dir.o) || size == 0 ) return CIP_FAILED; - } // Create the IDC object VarObject(idc_var); for ( Py_ssize_t i=0; io); if ( t != PY_ICID_INT64 ) return CIP_IMMUTABLE; // Cannot recycle immutable object // Update the attribute - PyObject_SetAttrString(*py_var, S_PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64)); + PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64)); return CIP_OK; } - PyObject *py_cls = get_idaapi_attr_by_id(PY_CLSID_CVT_INT64); + ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_INT64)); if ( py_cls == NULL ) return CIP_FAILED; - PYW_GIL_ENSURE; - *py_var = PyObject_CallFunctionObjArgs(py_cls, PyLong_FromLongLong(idc_var.i64), NULL); - PYW_GIL_RELEASE; - Py_DECREF(py_cls); + *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, PyLong_FromLongLong(idc_var.i64), NULL)); if ( PyW_GetError() || *py_var == NULL ) return CIP_FAILED; break; @@ -936,7 +972,7 @@ int idcvar_to_pyvar( #if !defined(NO_OBSOLETE_FUNCS) || defined(__EXPR_SRC) case VT_STR: - *py_var = PyString_FromString(idc_var.str); + *py_var = newref_t(PyString_FromString(idc_var.str)); break; #endif @@ -944,7 +980,7 @@ int idcvar_to_pyvar( if ( *py_var == NULL ) { const qstring &s = idc_var.qstr(); - *py_var = PyString_FromStringAndSize(s.begin(), s.length()); + *py_var = newref_t(PyString_FromStringAndSize(s.begin(), s.length())); break; } else @@ -953,11 +989,7 @@ int idcvar_to_pyvar( // Cannot recycle immutable objects if ( *py_var != NULL ) return CIP_IMMUTABLE; -#ifdef __EA64__ - *py_var = PyLong_FromLongLong(idc_var.num); -#else - *py_var = PyLong_FromLong(idc_var.num); -#endif + *py_var = newref_t(cvt_to_pylong(idc_var.num)); break; case VT_FLOAT: if ( *py_var == NULL ) @@ -966,7 +998,7 @@ int idcvar_to_pyvar( if ( ph.realcvt(&x, (uint16 *)idc_var.e, (sizeof(x)/2-1)|010) != 0 ) INTERR(30160); - *py_var = PyFloat_FromDouble(x); + *py_var = newref_t(PyFloat_FromDouble(x)); break; } else @@ -976,19 +1008,16 @@ int idcvar_to_pyvar( { if ( *py_var == NULL ) { - PyObject *py_cls = get_idaapi_attr_by_id(PY_CLSID_CVT_BYREF); + ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_CVT_BYREF)); if ( py_cls == NULL ) return CIP_FAILED; // Create a byref object with None value. We populate it later - PYW_GIL_ENSURE; - *py_var = PyObject_CallFunctionObjArgs(py_cls, Py_None, NULL); - PYW_GIL_RELEASE; - Py_DECREF(py_cls); + *py_var = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, Py_None, NULL)); if ( PyW_GetError() || *py_var == NULL ) return CIP_FAILED; } - int t = *py_var == NULL ? -1 : get_pyidc_cvt_type(*py_var); + int t = get_pyidc_cvt_type(py_var->o); if ( t != PY_ICID_BYREF ) return CIP_FAILED; @@ -999,27 +1028,25 @@ int idcvar_to_pyvar( return CIP_FAILED; // Can we recycle the object? - PyObject *new_py_val = PyW_TryGetAttrString(*py_var, S_PY_IDCCVT_VALUE_ATTR); + ref_t new_py_val(PyW_TryGetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR)); if ( new_py_val != NULL ) { // Recycle t = idcvar_to_pyvar(*dref_v, &new_py_val); - Py_XDECREF(new_py_val); // DECREF because of GetAttrStr // Success? Nothing more to be done if ( t == CIP_OK ) return CIP_OK; // Clear it so we don't recycle it - new_py_val = NULL; + new_py_val = ref_t(); } // Try to convert (not recycle) if ( idcvar_to_pyvar(*dref_v, &new_py_val) != CIP_OK ) return CIP_FAILED; // Update the attribute - PyObject_SetAttrString(*py_var, S_PY_IDCCVT_VALUE_ATTR, new_py_val); - Py_DECREF(new_py_val); + PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, new_py_val.o); break; } @@ -1033,25 +1060,22 @@ int idcvar_to_pyvar( && VarGetAttr(&idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val) == eOk ) { // Extract the object - *py_var = (PyObject *) idc_val.pvoid; - return CIP_OK_NODECREF; + *py_var = borref_t((PyObject *) idc_val.pvoid); + return CIP_OK_OPAQUE; } - PyObject *obj; + ref_t obj; bool is_dict = false; // Need to create a new object? if ( *py_var == NULL ) { // Get skeleton class reference - PyObject *py_cls = get_idaapi_attr_by_id(PY_CLSID_APPCALL_SKEL_OBJ); + ref_t py_cls(get_idaapi_attr_by_id(PY_CLSID_APPCALL_SKEL_OBJ)); if ( py_cls == NULL ) return CIP_FAILED; // Call constructor - PYW_GIL_ENSURE; - obj = PyObject_CallFunctionObjArgs(py_cls, NULL); - PYW_GIL_RELEASE; - Py_DECREF(py_cls); + obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, NULL)); if ( PyW_GetError() || obj == NULL ) return CIP_FAILED; } @@ -1059,7 +1083,7 @@ int idcvar_to_pyvar( { // Recycle existing variable obj = *py_var; - if ( PyDict_Check(obj) ) + if ( PyDict_Check(obj.o) ) is_dict = true; } @@ -1073,30 +1097,21 @@ int idcvar_to_pyvar( VarGetAttr(&idc_var, attr_name, &v, true); // Convert attribute to a python value (recursively) - PyObject *py_attr(NULL); + ref_t py_attr; int cvt = idcvar_to_pyvar(v, &py_attr); if ( cvt <= CIP_IMMUTABLE ) - { - // Delete the object (if we created it) - if ( *py_var == NULL ) - Py_DECREF(obj); - return CIP_FAILED; - } if ( is_dict ) - PyDict_SetItemString(obj, attr_name, py_attr); + PyDict_SetItemString(obj.o, attr_name, py_attr.o); else - PyObject_SetAttrString(obj, attr_name, py_attr); - - if ( cvt == CIP_OK ) - Py_XDECREF(py_attr); + PyObject_SetAttrString(obj.o, attr_name, py_attr.o); } *py_var = obj; break; } // Unhandled type default: - *py_var = NULL; + *py_var = ref_t(); return CIP_FAILED; } return CIP_OK; @@ -1108,36 +1123,31 @@ int idcvar_to_pyvar( bool pyw_convert_idc_args( const idc_value_t args[], int nargs, - ppyobject_vec_t &pargs, - boolvec_t *decref, + ref_vec_t &pargs, + bool as_tupple, char *errbuf, size_t errbufsize) { - bool as_tupple = decref == NULL; - PyObject *py_tuple(NULL); + PYW_GIL_CHECK_LOCKED_SCOPE(); + + ref_t py_tuple; pargs.qclear(); if ( as_tupple ) { - py_tuple = PyTuple_New(nargs); + py_tuple = newref_t(PyTuple_New(nargs)); if ( py_tuple == NULL ) { if ( errbuf != 0 && errbufsize > 0 ) qstrncpy(errbuf, "Failed to create a new tuple to store arguments!", errbufsize); return false; } - // Add the tuple - pargs.push_back(py_tuple); - } - else - { - decref->qclear(); } for ( int i=0; ipush_back(cvt == CIP_OK); } } - return true; -} -//------------------------------------------------------------------------- -// Frees arguments returned by pyw_convert_idc_args() -void pyw_free_idc_args( - ppyobject_vec_t &pargs, - boolvec_t *decref) -{ - if ( decref == NULL ) - { - if ( !pargs.empty() ) - Py_XDECREF(pargs[0]); - } - else - { - // free argument objects - for ( int i=(int)pargs.size()-1; i>=0; i-- ) - { - if ( decref->at(i) ) - Py_DECREF(pargs[i]); - } - decref->clear(); - } - pargs.clear(); + // Add the tuple to the list of args only now. Doing so earlier will + // cause the py_tuple.o->ob_refcnt to be 2 and not 1, and that will + // cause 'PyTuple_SetItem()' to fail. + if ( as_tupple ) + pargs.push_back(py_tuple); + + return true; } @@ -1247,11 +1233,23 @@ static const char S_ON_GETTEXT[] = "OnGetText"; static const char S_ON_ACTIVATE[] = "OnActivate"; static const char S_ON_DEACTIVATE[] = "OnDeactivate"; static const char S_ON_SELECT[] = "OnSelect"; +static const char S_ON_CREATING_GROUP[] = "OnCreatingGroup"; +static const char S_ON_DELETING_GROUP[] = "OnDeletingGroup"; +static const char S_ON_GROUP_VISIBILITY[] = "OnGroupVisibility"; static const char S_M_EDGES[] = "_edges"; static const char S_M_NODES[] = "_nodes"; static const char S_M_THIS[] = "_this"; static const char S_M_TITLE[] = "_title"; static const char S_CLINK_NAME[] = "__clink__"; +static const char S_ON_VIEW_ACTIVATED[] = "OnViewActivated"; +static const char S_ON_VIEW_DEACTIVATED[] = "OnViewDeactivated"; +static const char S_ON_VIEW_KEYDOWN[] = "OnViewKeydown"; +static const char S_ON_VIEW_CLICK[] = "OnViewClick"; +static const char S_ON_VIEW_DBLCLICK[] = "OnViewDblclick"; +static const char S_ON_VIEW_CURPOS[] = "OnViewCurpos"; +static const char S_ON_VIEW_SWITCHED[] = "OnViewSwitched"; +static const char S_ON_VIEW_MOUSE_OVER[] = "OnViewMouseOver"; + #ifdef __PYWRAPS__ static const char S_PY_IDAAPI_MODNAME[] = "__main__"; @@ -1260,7 +1258,7 @@ static const char S_PY_IDAAPI_MODNAME[] = S_IDAAPI_MODNAME; #endif //------------------------------------------------------------------------ -static PyObject *py_cvt_helper_module = NULL; +static ref_t py_cvt_helper_module; static bool pywraps_initialized = false; //--------------------------------------------------------------------------- @@ -1349,19 +1347,16 @@ static error_t idaapi idc_py_invoke0( idc_value_t *argv, idc_value_t *res) { + PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *pyfunc = (PyObject *) argv[0].pvoid; - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallFunctionObjArgs(pyfunc, NULL); - PYW_GIL_RELEASE; - - Py_XDECREF(py_result); + newref_t py_result(PyObject_CallFunctionObjArgs(pyfunc, NULL)); // Report Python error as IDC exception qstring err; + error_t err_code = eOk; if ( PyW_GetError(&err) ) - return PyW_CreateIdcException(res, err.c_str()); - - return eOk; + err_code = PyW_CreateIdcException(res, err.c_str()); + return err_code; } //------------------------------------------------------------------------ @@ -1417,8 +1412,11 @@ void deinit_pywraps() return; pywraps_initialized = false; - Py_XDECREF(py_cvt_helper_module); - py_cvt_helper_module = NULL; + + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_cvt_helper_module = ref_t(); // Deref. + } // Unregister the IDC PyInvoke0 method (helper function for add_idc_hotkey()) set_idc_func_ex(S_PYINVOKE0, NULL, idc_py_invoke0_args, 0); @@ -1426,47 +1424,34 @@ void deinit_pywraps() //------------------------------------------------------------------------ // Utility function to create a class instance whose constructor takes zero arguments -PyObject *create_idaapi_class_instance0(const char *clsname) +ref_t create_idaapi_class_instance0(const char *clsname) { - PyObject *py_cls = get_idaapi_attr(clsname); + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_cls(get_idaapi_attr(clsname)); if ( py_cls == NULL ) - return NULL; + return ref_t(); - PYW_GIL_ENSURE; - PyObject *py_obj = PyObject_CallFunctionObjArgs(py_cls, NULL); - PYW_GIL_RELEASE; - - Py_DECREF(py_cls); + ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, NULL)); if ( PyW_GetError() || py_obj == NULL ) - { - Py_XDECREF(py_obj); - Py_RETURN_NONE; - } + py_obj = ref_t(); return py_obj; } //------------------------------------------------------------------------ // Utility function to create linked class instances -PyObject *create_idaapi_linked_class_instance( +ref_t create_idaapi_linked_class_instance( const char *clsname, void *lnk) { - PyObject *py_cls = get_idaapi_attr(clsname); + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_cls(get_idaapi_attr(clsname)); if ( py_cls == NULL ) - return NULL; - - PyObject *py_lnk = PyCObject_FromVoidPtr(lnk, NULL); - PYW_GIL_ENSURE; - PyObject *py_obj = PyObject_CallFunctionObjArgs(py_cls, py_lnk, NULL); - PYW_GIL_RELEASE; - Py_DECREF(py_cls); - Py_DECREF(py_lnk); + return ref_t(); + newref_t py_lnk(PyCObject_FromVoidPtr(lnk, NULL)); + ref_t py_obj = newref_t(PyObject_CallFunctionObjArgs(py_cls.o, py_lnk.o, NULL)); if ( PyW_GetError() || py_obj == NULL ) - { - Py_XDECREF(py_obj); - py_obj = NULL; - } + py_obj = ref_t(); return py_obj; } @@ -1474,10 +1459,10 @@ PyObject *create_idaapi_linked_class_instance( // Gets a class type reference in idaapi // With the class type reference we can create a new instance of that type // This function takes a reference to the idaapi module and keeps the reference -PyObject *get_idaapi_attr_by_id(const int class_id) +ref_t get_idaapi_attr_by_id(const int class_id) { if ( class_id >= PY_CLSID_LAST || py_cvt_helper_module == NULL ) - return NULL; + return ref_t(); // Some class names. The array is parallel with the PY_CLSID_xxx consts static const char *class_names[]= @@ -1486,16 +1471,18 @@ PyObject *get_idaapi_attr_by_id(const int class_id) "object_t", "PyIdc_cvt_refclass__" }; - return PyObject_GetAttrString(py_cvt_helper_module, class_names[class_id]); + PYW_GIL_CHECK_LOCKED_SCOPE(); + return newref_t(PyObject_GetAttrString(py_cvt_helper_module.o, class_names[class_id])); } //------------------------------------------------------------------------ // Gets a class reference by name -PyObject *get_idaapi_attr(const char *attrname) +ref_t get_idaapi_attr(const char *attrname) { + PYW_GIL_CHECK_LOCKED_SCOPE(); return py_cvt_helper_module == NULL - ? NULL - : PyW_TryGetAttrString(py_cvt_helper_module, attrname); + ? ref_t() + : PyW_TryGetAttrString(py_cvt_helper_module.o, attrname); } //------------------------------------------------------------------------ @@ -1505,41 +1492,39 @@ bool PyW_GetStringAttr( const char *attr_name, qstring *str) { - PyObject *py_attr = PyW_TryGetAttrString(py_obj, attr_name); + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_attr(PyW_TryGetAttrString(py_obj, attr_name)); if ( py_attr == NULL ) return false; - bool ok = PyString_Check(py_attr) != 0; + bool ok = PyString_Check(py_attr.o) != 0; if ( ok ) - *str = PyString_AsString(py_attr); + *str = PyString_AsString(py_attr.o); - Py_DECREF(py_attr); return ok; } //------------------------------------------------------------------------ // Returns an attribute or NULL // No errors will be set if the attribute did not exist -PyObject *PyW_TryGetAttrString(PyObject *py_obj, const char *attr) +ref_t PyW_TryGetAttrString(PyObject *py_obj, const char *attr) { - if ( !PyObject_HasAttrString(py_obj, attr) ) - return NULL; - else - return PyObject_GetAttrString(py_obj, attr); + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t o; + if ( PyObject_HasAttrString(py_obj, attr) ) + o = newref_t(PyObject_GetAttrString(py_obj, attr)); + return o; } //------------------------------------------------------------------------ // Tries to import a module and clears the exception on failure -PyObject *PyW_TryImportModule(const char *name) +ref_t PyW_TryImportModule(const char *name) { - PYW_GIL_ENSURE; - PyObject *result = PyImport_ImportModule(name); - PYW_GIL_RELEASE; - if ( result != NULL ) - return result; - if ( PyErr_Occurred() ) - PyErr_Clear(); - return NULL; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t result(PyImport_ImportModule(name)); + if ( result == NULL && PyErr_Occurred() ) + PyErr_Clear(); + return result; } //------------------------------------------------------------------------- @@ -1552,144 +1537,158 @@ PyObject *PyW_TryImportModule(const char *name) // And because of that we are confused as to whether to convert to 32 or 64 bool PyW_GetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var) { - if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) - return false; + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = true; + do + { + if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) + { + rc = false; + break; + } - // Can we convert to C long? - long l = PyInt_AsLong(py_var); - if ( !PyErr_Occurred() ) - { - idc_var->set_long(l); - return true; - } - // Clear last error - PyErr_Clear(); - // Can be fit into a C unsigned long? - l = (long) PyLong_AsUnsignedLong(py_var); - if ( !PyErr_Occurred() ) - { - idc_var->set_long(l); - return true; - } - PyErr_Clear(); - idc_var->set_int64(PyLong_AsLongLong(py_var)); - return true; + // Can we convert to C long? + long l = PyInt_AsLong(py_var); + if ( !PyErr_Occurred() ) + { + idc_var->set_long(l); + break; + } + // Clear last error + PyErr_Clear(); + // Can be fit into a C unsigned long? + l = (long) PyLong_AsUnsignedLong(py_var); + if ( !PyErr_Occurred() ) + { + idc_var->set_long(l); + break; + } + PyErr_Clear(); + idc_var->set_int64(PyLong_AsLongLong(py_var)); + } while ( false ); + return rc; } //------------------------------------------------------------------------- // Parses a Python object as a long or long long bool PyW_GetNumber(PyObject *py_var, uint64 *num, bool *is_64) { - if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) - return false; + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = true; +#define SETNUM(numexpr, is64_expr) \ + do \ + { \ + if ( num != NULL ) \ + *num = numexpr; \ + if ( is_64 != NULL ) \ + *is_64 = is64_expr; \ + } while ( false ) - // Can we convert to C long? - long l = PyInt_AsLong(py_var); - if ( !PyErr_Occurred() ) + do { - if ( num != NULL ) - *num = uint64(l); - if ( is_64 != NULL ) - *is_64 = false; - return true; - } - - // Clear last error - PyErr_Clear(); - - // Can be fit into a C unsigned long? - unsigned long ul = PyLong_AsUnsignedLong(py_var); - if ( !PyErr_Occurred() ) - { - if ( num != NULL ) - *num = uint64(ul); - if ( is_64 != NULL ) - *is_64 = false; - return true; - } - PyErr_Clear(); - - // Try to parse as int64 - PY_LONG_LONG ll = PyLong_AsLongLong(py_var); - if ( !PyErr_Occurred() ) - { - if ( num != NULL ) - *num = uint64(ll); - if ( is_64 != NULL ) - *is_64 = true; - return true; - } - PyErr_Clear(); - - // Try to parse as uint64 - unsigned PY_LONG_LONG ull = PyLong_AsUnsignedLongLong(py_var); - PyObject *err = PyErr_Occurred(); - if ( err == NULL ) - { - if ( num != NULL ) - *num = uint64(ull); - if ( is_64 != NULL ) - *is_64 = true; - return true; - } - // Negative number? _And_ it with uint64(-1) - bool ok = false; - if ( err == PyExc_TypeError ) - { - PyObject *py_mask = Py_BuildValue("K", 0xFFFFFFFFFFFFFFFFull); - PyObject *py_num = PyNumber_And(py_var, py_mask); - if ( py_num != NULL && py_mask != NULL ) + if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) { - PyErr_Clear(); - ull = PyLong_AsUnsignedLongLong(py_num); - if ( !PyErr_Occurred() ) + rc = false; + break; + } + + // Can we convert to C long? + long l = PyInt_AsLong(py_var); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(l), false); + break; + } + + // Clear last error + PyErr_Clear(); + + // Can be fit into a C unsigned long? + unsigned long ul = PyLong_AsUnsignedLong(py_var); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(ul), false); + break; + } + PyErr_Clear(); + + // Try to parse as int64 + PY_LONG_LONG ll = PyLong_AsLongLong(py_var); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(ll), true); + break; + } + PyErr_Clear(); + + // Try to parse as uint64 + unsigned PY_LONG_LONG ull = PyLong_AsUnsignedLongLong(py_var); + PyObject *err = PyErr_Occurred(); + if ( err == NULL ) + { + SETNUM(uint64(ull), true); + break; + } + // Negative number? _And_ it with uint64(-1) + rc = false; + if ( err == PyExc_TypeError ) + { + newref_t py_mask(Py_BuildValue("K", 0xFFFFFFFFFFFFFFFFull)); + newref_t py_num(PyNumber_And(py_var, py_mask.o)); + if ( py_num != NULL && py_mask != NULL ) { - if ( num != NULL ) - *num = uint64(ull); - if ( is_64 != NULL ) - *is_64 = true; - ok = true; + PyErr_Clear(); + ull = PyLong_AsUnsignedLongLong(py_num.o); + if ( !PyErr_Occurred() ) + { + SETNUM(uint64(ull), true); + rc = true; + } } } - Py_XDECREF(py_num); - Py_XDECREF(py_mask); - } - PyErr_Clear(); - return ok; + PyErr_Clear(); + } while ( false ); + return rc; +#undef SETNUM } //------------------------------------------------------------------------- // Checks if a given object is of sequence type bool PyW_IsSequenceType(PyObject *obj) { - if ( !PySequence_Check(obj) ) - return false; - - Py_ssize_t sz = PySequence_Size(obj); - if ( sz == -1 || PyErr_Occurred() != NULL ) + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = true; + do { - PyErr_Clear(); - return false; - } - return true; + if ( !PySequence_Check(obj) ) + { + rc = false; + break; + } + + Py_ssize_t sz = PySequence_Size(obj); + if ( sz == -1 || PyErr_Occurred() != NULL ) + { + PyErr_Clear(); + rc = false; + break; + } + } while ( false ); + return rc; } //------------------------------------------------------------------------- // Returns the string representation of an object bool PyW_ObjectToString(PyObject *obj, qstring *out) { - PyObject *py_str = PyObject_Str(obj); - if ( py_str != NULL ) - { - *out = PyString_AsString(py_str); - Py_DECREF(py_str); - return true; - } + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_str(PyObject_Str(obj)); + bool ok = py_str != NULL; + if ( ok ) + *out = PyString_AsString(py_str.o); else - { out->qclear(); - return false; - } + return ok; } //-------------------------------------------------------------------------- @@ -1697,6 +1696,8 @@ bool PyW_ObjectToString(PyObject *obj, qstring *out) // exception string bool PyW_GetError(qstring *out, bool clear_err) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyErr_Occurred() == NULL ) return false; @@ -1717,23 +1718,18 @@ bool PyW_GetError(qstring *out, bool clear_err) PyErr_Restore(err_type, err_value, err_traceback); // Resolve FormatExc() - PyObject *py_fmtexc = get_idaapi_attr(S_IDAAPI_FORMATEXC); + ref_t py_fmtexc(get_idaapi_attr(S_IDAAPI_FORMATEXC)); // Helper there? if ( py_fmtexc != NULL ) { // Call helper - PYW_GIL_ENSURE; py_ret = PyObject_CallFunctionObjArgs( - py_fmtexc, + py_fmtexc.o, err_type, err_value, err_traceback, NULL); - PYW_GIL_RELEASE; - - // Dispose helper reference - Py_DECREF(py_fmtexc); } // Clear the error @@ -1770,6 +1766,8 @@ bool PyW_GetError(qstring *out, bool clear_err) //------------------------------------------------------------------------- bool PyW_GetError(char *buf, size_t bufsz, bool clear_err) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + qstring s; if ( !PyW_GetError(&s, clear_err) ) return false; @@ -1783,6 +1781,8 @@ bool PyW_GetError(char *buf, size_t bufsz, bool clear_err) // This method is used to display errors that occurred in a callback bool PyW_ShowCbErr(const char *cb_name) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + static qstring err_str; if ( !PyW_GetError(&err_str) ) return false; @@ -1794,10 +1794,11 @@ bool PyW_ShowCbErr(const char *cb_name) //--------------------------------------------------------------------------- void *pyobj_get_clink(PyObject *pyobj) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Try to query the link attribute - PyObject *attr = PyW_TryGetAttrString(pyobj, S_CLINK_NAME); - void *t = attr != NULL && PyCObject_Check(attr) ? PyCObject_AsVoidPtr(attr) : NULL; - Py_XDECREF(attr); + ref_t attr(PyW_TryGetAttrString(pyobj, S_CLINK_NAME)); + void *t = attr != NULL && PyCObject_Check(attr.o) ? PyCObject_AsVoidPtr(attr.o) : NULL; return t; } @@ -1808,7 +1809,7 @@ void *pyobj_get_clink(PyObject *pyobj) //------------------------------------------------------------------------ class pywraps_notify_when_t { - ppyobject_vec_t table[NW_EVENTSCNT]; + ref_vec_t table[NW_EVENTSCNT]; qstring err; bool in_notify; struct notify_when_args_t @@ -1822,6 +1823,8 @@ class pywraps_notify_when_t //------------------------------------------------------------------------ static int idaapi idp_callback(void *ud, int event_id, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; pywraps_notify_when_t *_this = (pywraps_notify_when_t *)ud; switch ( event_id ) { @@ -1860,32 +1863,29 @@ class pywraps_notify_when_t //------------------------------------------------------------------------ void register_callback(int slot, PyObject *py_callable) { - ppyobject_vec_t &tbl = table[slot]; - ppyobject_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, py_callable); + borref_t callable_ref(py_callable); + ref_vec_t &tbl = table[slot]; + ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); // Already added if ( it != it_end ) return; - // Increment reference - Py_INCREF(py_callable); - // Insert the element - tbl.push_back(py_callable); + tbl.push_back(callable_ref); } //------------------------------------------------------------------------ void unregister_callback(int slot, PyObject *py_callable) { - ppyobject_vec_t &tbl = table[slot]; - ppyobject_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, py_callable); + borref_t callable_ref(py_callable); + ref_vec_t &tbl = table[slot]; + ref_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, callable_ref); + // Not found? if ( it == it_end ) return; - // Decrement reference - Py_DECREF(py_callable); - // Delete the element tbl.erase(it); } @@ -1901,11 +1901,11 @@ public: bool deinit() { // Uninstall all objects - ppyobject_vec_t::iterator it, it_end; + ref_vec_t::iterator it, it_end; for ( int slot=0; sloto); } // ...and remove the notification return unhook_from_notification_point(HT_IDP, idp_callback, this); @@ -1954,6 +1954,8 @@ public: //------------------------------------------------------------------------ bool notify_va(int slot, va_list va) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Sanity bounds check! if ( slot < 0 || slot >= NW_EVENTSCNT ) return false; @@ -1961,42 +1963,38 @@ public: bool ok = true; in_notify = true; int old = slot == NW_OPENIDB_SLOT ? va_arg(va, int) : 0; - for (ppyobject_vec_t::iterator it = table[slot].begin(), it_end = table[slot].end(); - it != it_end; - ++it) + { - // Form the notification code - PyObject *py_code = PyInt_FromLong(1 << slot); - PyObject *py_result(NULL); - switch ( slot ) + for (ref_vec_t::iterator it = table[slot].begin(), it_end = table[slot].end(); + it != it_end; + ++it) { - case NW_CLOSEIDB_SLOT: - case NW_INITIDA_SLOT: - case NW_TERMIDA_SLOT: + // Form the notification code + newref_t py_code(PyInt_FromLong(1 << slot)); + ref_t py_result; + switch ( slot ) { - PYW_GIL_ENSURE; - py_result = PyObject_CallFunctionObjArgs(*it, py_code, NULL); - PYW_GIL_RELEASE; - break; + case NW_CLOSEIDB_SLOT: + case NW_INITIDA_SLOT: + case NW_TERMIDA_SLOT: + { + py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, NULL)); + break; + } + case NW_OPENIDB_SLOT: + { + newref_t py_old(PyInt_FromLong(old)); + py_result = newref_t(PyObject_CallFunctionObjArgs(it->o, py_code.o, py_old.o, NULL)); + } + break; } - case NW_OPENIDB_SLOT: + if ( PyW_GetError(&err) || py_result == NULL ) { - PyObject *py_old = PyInt_FromLong(old); - PYW_GIL_ENSURE; - py_result = PyObject_CallFunctionObjArgs(*it, py_code, py_old, NULL); - PYW_GIL_RELEASE; - Py_DECREF(py_old); + PyErr_Clear(); + warning("notify_when(): Error occured while notifying object.\n%s", err.c_str()); + ok = false; } - break; } - Py_DECREF(py_code); - if ( PyW_GetError(&err) || py_result == NULL ) - { - PyErr_Clear(); - warning("notify_when(): Error occured while notifying object.\n%s", err.c_str()); - ok = false; - } - Py_XDECREF(py_result); } in_notify = false; @@ -2011,6 +2009,7 @@ public: } delayed_notify_when_list.qclear(); } + return ok; } @@ -2047,6 +2046,10 @@ bool pywraps_nw_notify(int slot, ...) if ( g_nw == NULL ) return false; + // Appears to be called from 'driver_notifywhen.cpp', which + // itself is called from possibly non-python code. + // I.e., we must acquire the GIL. + PYW_GIL_GET; va_list va; va_start(va, slot); bool ok = g_nw->notify_va(slot, va); @@ -2061,11 +2064,11 @@ bool pywraps_nw_term() { if ( g_nw == NULL ) return true; - + // If could not deinitialize then return w/o stopping nw if ( !g_nw->deinit() ) return false; - + // Cleanup delete g_nw; g_nw = NULL; @@ -2078,21 +2081,9 @@ bool pywraps_nw_term() // Do not create separate wrappers for default arguments %feature("compactdefaultargs"); -#ifdef __EA64__ -#ifdef __GNUC__ -%constant ea_t BADADDR = 0xFFFFFFFFFFFFFFFFll; -%constant sel_t BADSEL = 0xFFFFFFFFFFFFFFFFll; -%constant nodeidx_t BADNODE = 0xFFFFFFFFFFFFFFFFll; -#else // __GNUC__ -%constant ea_t BADADDR = 0xFFFFFFFFFFFFFFFFui64; -%constant sel_t BADSEL = 0xFFFFFFFFFFFFFFFFui64; -%constant nodeidx_t BADNODE = 0xFFFFFFFFFFFFFFFFui64; -#endif // __GNUC__ -#else //__EA64__ -%constant ea_t BADADDR = 0xFFFFFFFFL; -%constant sel_t BADSEL = 0xFFFFFFFFL; -%constant nodeidx_t BADNODE = 0xFFFFFFFFL; -#endif +%constant ea_t BADADDR = ea_t(-1); +%constant sel_t BADSEL = sel_t(-1); +%constant nodeidx_t BADNODE = nodeidx_t(-1); // Help SWIG to figure out the ulonglong type #ifdef SWIGWIN @@ -2122,6 +2113,36 @@ import os import sys import bisect import __builtin__ +import imp + +def require(modulename): + """ + Load, or reload a module. + + When under heavy development, a user's tool might consist of multiple + modules. If those are imported using the standard 'import' mechanism, + there is no guarantee that the Python implementation will re-read + and re-evaluate the module's Python code. In fact, it usually doesn't. + What should be done instead is 'reload()'-ing that module. + + This is a simple helper function that will do just that: In case the + module doesn't exist, it 'import's it, and if it does exist, + 'reload()'s it. + + For more information, see: . + """ + if modulename in sys.modules.keys(): + reload(sys.modules[modulename]) + else: + import importlib + import inspect + m = importlib.import_module(modulename) + frame_obj, filename, line_number, function_name, lines, index = inspect.stack()[1] + importer_module = inspect.getmodule(frame_obj) + if importer_module is None: # No importer module; called from command line + importer_module = sys.modules['__main__'] + setattr(importer_module, modulename, m) + sys.modules[modulename] = m # ----------------------------------------------------------------------- @@ -2507,11 +2528,7 @@ def IDAPython_ExecScript(script, g): if len(scriptpath) and scriptpath not in sys.path: sys.path.append(scriptpath) - # Save the argv, path, I/O and base modules for later cleanup argv = sys.argv - path = sys.path - stdio = [sys.stdin, sys.stdout, sys.stderr] - basemodules = sys.modules.copy() sys.argv = [ script ] # Adjust the __file__ path in the globals we pass to the script @@ -2525,18 +2542,65 @@ def IDAPython_ExecScript(script, g): PY_COMPILE_ERR = "%s\n%s" % (str(e), traceback.format_exc()) print(PY_COMPILE_ERR) finally: - # Restore the globals to the state before the script was run + # Restore state g['__file__'] = old__file__ - sys.argv = argv - sys.path = path - sys.stdin, sys.stdout, sys.stderr = stdio - # Clean up the modules loaded by the script - for module in sys.modules.keys(): - if not module in basemodules: - del sys.modules[module] + return PY_COMPILE_ERR +# ------------------------------------------------------------ +def IDAPython_LoadProcMod(script, g): + """ + Load processor module. + """ + pname = g['__name__'] if g and g.has_key("__name__") else '__main__' + parent = sys.modules[pname] + + scriptpath, scriptname = os.path.split(script) + if len(scriptpath) and scriptpath not in sys.path: + sys.path.append(scriptpath) + + procmod_name = os.path.splitext(scriptname)[0] + procobj = None + fp = None + try: + fp, pathname, description = imp.find_module(procmod_name) + procmod = imp.load_module(procmod_name, fp, pathname, description) + if parent: + setattr(parent, procmod_name, procmod) + # export attrs from parent to processor module + parent_attrs = getattr(parent, '__all__', + (attr for attr in dir(parent) if not attr.startswith('_'))) + for pa in parent_attrs: + setattr(procmod, pa, getattr(parent, pa)) + # instantiate processor object + if getattr(procmod, 'PROCESSOR_ENTRY', None): + procobj = procmod.PROCESSOR_ENTRY() + PY_COMPILE_ERR = None + except Exception as e: + PY_COMPILE_ERR = "%s\n%s" % (str(e), traceback.format_exc()) + print(PY_COMPILE_ERR) + finally: + if fp: fp.close() + + sys.path.remove(scriptpath) + + return (PY_COMPILE_ERR, procobj) + +# ------------------------------------------------------------ +def IDAPython_UnLoadProcMod(script, g): + """ + Unload processor module. + """ + pname = g['__name__'] if g and g.has_key("__name__") else '__main__' + parent = sys.modules[pname] + + scriptname = os.path.split(script)[1] + procmod_name = os.path.splitext(scriptname)[0] + if getattr(parent, procmod_name, None): + delattr(parent, procmod_name) + del sys.modules[procmod_name] + PY_COMPILE_ERR = None return PY_COMPILE_ERR # ---------------------------------------------------------------------- @@ -2701,8 +2765,10 @@ def parse_command_line(cmdline): */ static PyObject *py_parse_command_line(const char *cmdline) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + qstrvec_t args; - if ( parse_command_line2(cmdline, &args, NULL) == 0 ) + if ( parse_command_line3(cmdline, &args, NULL, LP_PATH_WITH_ARGS) == 0 ) Py_RETURN_NONE; PyObject *py_list = PyList_New(args.size()); @@ -2804,6 +2870,7 @@ static bool qstrvec_t_assign(PyObject *self, PyObject *other) static PyObject *qstrvec_t_addressof(PyObject *self, size_t idx) { + PYW_GIL_CHECK_LOCKED_SCOPE(); qstrvec_t *sv = qstrvec_t_get_clink(self); if ( sv == NULL || idx >= sv->size() ) Py_RETURN_NONE; @@ -2828,6 +2895,7 @@ static bool qstrvec_t_from_list( PyObject *self, PyObject *py_list) { + PYW_GIL_CHECK_LOCKED_SCOPE(); qstrvec_t *sv = qstrvec_t_get_clink(self); return sv == NULL ? false : PyW_PyListToStrVec(py_list, *sv); } @@ -2840,6 +2908,7 @@ static size_t qstrvec_t_size(PyObject *self) static PyObject *qstrvec_t_get(PyObject *self, size_t idx) { + PYW_GIL_CHECK_LOCKED_SCOPE(); qstrvec_t *sv = qstrvec_t_get_clink(self); if ( sv == NULL || idx >= sv->size() ) Py_RETURN_NONE; @@ -2914,6 +2983,7 @@ def notify_when(when, callback): */ static bool notify_when(int when, PyObject *py_callable) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( g_nw == NULL || !PyCallable_Check(py_callable) ) return false; return g_nw->notify_when(when, py_callable); @@ -2939,5 +3009,6 @@ static bool notify_when(int when, PyObject *py_callable) %include "typeinf.i" %include "ua.i" %include "xref.i" +%include "view.i" %include "graph.i" %include "fpro.i" diff --git a/swig/idd.i b/swig/idd.i index 7db2323..f55489e 100644 --- a/swig/idd.i +++ b/swig/idd.i @@ -14,6 +14,12 @@ %clear(char dtyp); +%pythoncode %{ +# +NO_PROCESS = 0xFFFFFFFF +NO_THREAD = 0 +# +%} %{ // @@ -28,6 +34,8 @@ static bool dbg_can_query() //------------------------------------------------------------------------- static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + PyObject *py_list = PyList_New(areas.size()); meminfo_vec_t::const_iterator it, it_end(areas.end()); Py_ssize_t i = 0; @@ -56,6 +64,8 @@ PyObject *py_appcall( PyObject *py_fields, PyObject *arg_list) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyList_Check(arg_list) ) return NULL; @@ -71,11 +81,11 @@ PyObject *py_appcall( for ( Py_ssize_t i=0; i%s\n", int(i), s.c_str()); } // Convert it @@ -95,6 +105,10 @@ PyObject *py_appcall( return NULL; } + error_t ret; + idc_value_t idc_result; + Py_BEGIN_ALLOW_THREADS; + if ( (debug & IDA_DEBUG_APPCALL) != 0 ) { msg("input variables:\n" @@ -110,8 +124,7 @@ PyObject *py_appcall( } // Do Appcall - idc_value_t idc_result; - error_t ret = appcall( + ret = appcall( func_ea, tid, (type_t *)type, @@ -120,16 +133,17 @@ PyObject *py_appcall( idc_args.begin(), &idc_result); + Py_END_ALLOW_THREADS; + if ( ret != eOk ) { // An exception was thrown? if ( ret == eExecThrow ) { // Convert the result (which is a debug_event) into a Python object - PyObject *py_appcall_exc(NULL); + ref_t py_appcall_exc; idcvar_to_pyvar(idc_result, &py_appcall_exc); - PyErr_SetObject(PyExc_OSError, py_appcall_exc); - Py_DECREF(py_appcall_exc); + PyErr_SetObject(PyExc_OSError, py_appcall_exc.o); return NULL; } // An error in the Appcall? (or an exception but AppCallOptions/DEBEV is not set) @@ -159,7 +173,7 @@ PyObject *py_appcall( for ( Py_ssize_t i=0; iob_refcnt == 1); if ( (debug & IDA_DEBUG_APPCALL) != 0 ) { msg("return var:\n" @@ -184,7 +198,8 @@ PyObject *py_appcall( VarPrint(&s, &idc_result); msg("%s\n-----------\n", s.c_str()); } - return py_result; + py_result.incref(); + return py_result.o; } // %} @@ -212,6 +227,8 @@ def dbg_get_registers(): */ static PyObject *dbg_get_registers() { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( dbg == NULL ) Py_RETURN_NONE; @@ -270,6 +287,8 @@ def dbg_get_thread_sreg_base(tid, sreg_value): */ static PyObject *dbg_get_thread_sreg_base(PyObject *py_tid, PyObject *py_sreg_value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !dbg_can_query() || !PyInt_Check(py_tid) || !PyInt_Check(py_sreg_value) ) Py_RETURN_NONE; ea_t answer; @@ -296,6 +315,8 @@ def dbg_read_memory(ea, sz): */ static PyObject *dbg_read_memory(PyObject *py_ea, PyObject *py_sz) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + uint64 ea, sz; if ( !dbg_can_query() || !PyW_GetNumber(py_ea, &ea) || !PyW_GetNumber(py_sz, &sz) ) Py_RETURN_NONE; @@ -333,6 +354,8 @@ def dbg_write_memory(ea, buffer): */ static PyObject *dbg_write_memory(PyObject *py_ea, PyObject *py_buf) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + uint64 ea; if ( !dbg_can_query() || !PyString_Check(py_buf) || !PyW_GetNumber(py_ea, &ea) ) Py_RETURN_NONE; @@ -357,6 +380,8 @@ def dbg_get_name(): */ static PyObject *dbg_get_name() { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( dbg == NULL ) Py_RETURN_NONE; else @@ -378,15 +403,19 @@ def dbg_get_memory_info(): */ static PyObject *dbg_get_memory_info() { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !dbg_can_query() ) Py_RETURN_NONE; // Invalidate memory + meminfo_vec_t areas; + Py_BEGIN_ALLOW_THREADS; invalidate_dbgmem_config(); invalidate_dbgmem_contents(BADADDR, BADADDR); - meminfo_vec_t areas; get_dbg_memory_info(&areas); + Py_END_ALLOW_THREADS; return meminfo_vec_t_to_py(areas); } @@ -611,7 +640,7 @@ class Appcall_callable__(object): def __get_size(self): if self.__type == None: return -1 - r = _idaapi.get_type_size0(_idaapi.cvar.idati, self.__type) + r = _idaapi.calc_type_size(_idaapi.cvar.idati, self.__type) if not r: return -1 return r diff --git a/swig/idp.i b/swig/idp.i index 18dc86a..c1c4def 100644 --- a/swig/idp.i +++ b/swig/idp.i @@ -35,6 +35,7 @@ %ignore _py_getreg; %ignore free_processor_module; %ignore read_config_file; +%ignore cfg_compiler_changed; %ignore gen_idb_event; @@ -74,12 +75,17 @@ static PyObject *AssembleLine( { int inslen; char buf[MAXSTR]; + bool ok = false; if (ph.notify != NULL && - (inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf)) > 0) + (inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf)) > 0) { - return PyString_FromStringAndSize(buf, inslen); + ok = true; } - Py_RETURN_NONE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return PyString_FromStringAndSize(buf, inslen); + else + Py_RETURN_NONE; } //--------------------------------------------------------------------------- @@ -107,17 +113,20 @@ bool assemble( { int inslen; char buf[MAXSTR]; - + PYW_GIL_CHECK_LOCKED_SCOPE(); + bool rc = false; + Py_BEGIN_ALLOW_THREADS; if (ph.notify != NULL) { - inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf); + inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf); if (inslen > 0) { patch_many_bytes(ea, buf, inslen); - return true; + rc = true; } } - return false; + Py_END_ALLOW_THREADS; + return rc; } //------------------------------------------------------------------------- @@ -359,6 +368,7 @@ def ph_get_instruc(): static PyObject *ph_get_instruc() { Py_ssize_t i = 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *py_result = PyTuple_New(ph.instruc_end - ph.instruc_start); for ( const instruc_t *p = ph.instruc + ph.instruc_start, *end = ph.instruc + ph.instruc_end; p != end; @@ -382,10 +392,10 @@ def ph_get_regnames(): static PyObject *ph_get_regnames() { Py_ssize_t i = 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *py_result = PyList_New(ph.regsNum); for ( Py_ssize_t i=0; icustom_outop(py_obj) ? 2 : 0; - Py_XDECREF(py_obj); + ret = proxy->custom_outop(py_obj.o) ? 2 : 0; break; } case processor_t::custom_mnem: { + PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *py_ret = proxy->custom_mnem(); if ( py_ret != NULL && PyString_Check(py_ret) ) { @@ -1051,6 +1072,7 @@ int idaapi IDP_Callback(void *ud, int notification_code, va_list va) // Extract user buffer (we hardcode the MAXSTR size limit) uchar *bin = va_arg(va, uchar *); // Call python + PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *py_buffer = proxy->assemble(ea, cs, ip, use32, line); if ( py_buffer != NULL && PyString_Check(py_buffer) ) { @@ -1370,6 +1392,7 @@ int idaapi IDP_Callback(void *ud, int notification_code, va_list va) catch (Swig::DirectorException &e) { msg("Exception in IDP Hook function: %s\n", e.getMessage()); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( PyErr_Occurred() ) PyErr_Print(); } @@ -1379,6 +1402,9 @@ int idaapi IDP_Callback(void *ud, int notification_code, va_list va) //--------------------------------------------------------------------------- int idaapi IDB_Callback(void *ud, int notification_code, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + class IDB_Hooks *proxy = (class IDB_Hooks *)ud; ea_t ea, ea2; bool repeatable_cmt; @@ -1550,10 +1576,9 @@ int idaapi IDB_Callback(void *ud, int notification_code, va_list va) catch (Swig::DirectorException &e) { msg("Exception in IDB Hook function: %s\n", e.getMessage()); + PYW_GIL_CHECK_LOCKED_SCOPE(); if (PyErr_Occurred()) - { PyErr_Print(); - } } return 0; } diff --git a/swig/kernwin.i b/swig/kernwin.i index 949de38..f3de499 100644 --- a/swig/kernwin.i +++ b/swig/kernwin.i @@ -50,6 +50,7 @@ %ignore is_idaview; %ignore refresh_custom_viewer; %ignore set_custom_viewer_handlers; +%ignore get_viewer_name; // Ignore these string functions. There are trivial replacements in Python. %ignore addblanks; %ignore trim; @@ -70,6 +71,7 @@ %rename (asktext) py_asktext; %rename (str2ea) py_str2ea; +%rename (str2user) py_str2user; %ignore process_ui_action; %rename (process_ui_action) py_process_ui_action; %ignore execute_sync; @@ -88,33 +90,40 @@ // Make askaddr(), askseg(), and asklong() return a // tuple: (result, value) -%apply unsigned long *INOUT { sval_t *value }; %rename (_asklong) asklong; -%apply unsigned long *INOUT { ea_t *addr }; %rename (_askaddr) askaddr; -%apply unsigned long *INOUT { sel_t *sel }; %rename (_askseg) askseg; %feature("director") UI_Hooks; %inline %{ int py_msg(const char *format) { - return msg("%s", format); + int rc; + Py_BEGIN_ALLOW_THREADS; + rc = msg("%s", format); + Py_END_ALLOW_THREADS; + return rc; } void py_warning(const char *format) { + Py_BEGIN_ALLOW_THREADS; warning("%s", format); + Py_END_ALLOW_THREADS; } void py_error(const char *format) { + Py_BEGIN_ALLOW_THREADS; error("%s", format); + Py_END_ALLOW_THREADS; } void refresh_lists(void) { + Py_BEGIN_ALLOW_THREADS; callui(ui_list); + Py_END_ALLOW_THREADS; } %} @@ -149,6 +158,8 @@ def register_timer(interval, callback): */ static PyObject *py_register_timer(int interval, PyObject *py_callback) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( py_callback == NULL || !PyCallable_Check(py_callback) ) Py_RETURN_NONE; @@ -157,17 +168,15 @@ static PyObject *py_register_timer(int interval, PyObject *py_callback) { static int idaapi callback(void *ud) { + PYW_GIL_GET; py_timer_ctx_t *ctx = (py_timer_ctx_t *)ud; - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallFunctionObjArgs(ctx->pycallback, NULL); - int ret = py_result == NULL ? -1 : PyLong_AsLong(py_result); - Py_XDECREF(py_result); - PYW_GIL_RELEASE; + newref_t py_result(PyObject_CallFunctionObjArgs(ctx->pycallback, NULL)); + int ret = py_result == NULL ? -1 : PyLong_AsLong(py_result.o); // Timer has been unregistered? if ( ret == -1 ) { - // Fee the context + // Free the context Py_DECREF(ctx->pycallback); delete ctx; } @@ -208,6 +217,8 @@ def unregister_timer(timer_obj): */ static PyObject *py_unregister_timer(PyObject *py_timerctx) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( py_timerctx == NULL || !PyCObject_Check(py_timerctx) ) Py_RETURN_FALSE; @@ -236,6 +247,7 @@ def choose_idasgn(): static PyObject *py_choose_idasgn() { char *name = choose_idasgn(); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( name == NULL ) { Py_RETURN_NONE; @@ -265,6 +277,7 @@ static PyObject *py_get_highlighted_identifier(int flags = 0) { char buf[MAXSTR]; bool ok = get_highlighted_identifier(buf, sizeof(buf), flags); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !ok ) Py_RETURN_NONE; else @@ -282,6 +295,7 @@ static int py_load_custom_icon_data(PyObject *data, const char *format) { Py_ssize_t len; char *s; + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( PyString_AsStringAndSize(data, &s, &len) == -1 ) return 0; else @@ -316,6 +330,7 @@ def asktext(max_text, defval, prompt): */ PyObject *py_asktext(int max_text, const char *defval, const char *prompt) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( max_text <= 0 ) Py_RETURN_NONE; @@ -356,6 +371,26 @@ ea_t py_str2ea(const char *str, ea_t screenEA = BADADDR) return ok ? ea : BADADDR; } +//------------------------------------------------------------------------ +/* +# +def str2user(str): + """ + Insert C-style escape characters to string + + @return: new string with escape characters inserted + """ + pass +# +*/ +PyObject *py_str2user(const char *str) +{ + qstring qstr(str); + qstring retstr; + qstr2user(&retstr, qstr); + return PyString_FromString(retstr.c_str()); +} + //------------------------------------------------------------------------ /* # @@ -390,6 +425,7 @@ def del_menu_item(menu_ctx): */ static bool py_del_menu_item(PyObject *py_ctx) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(py_ctx) ) return false; @@ -422,6 +458,7 @@ def del_hotkey(ctx): */ bool py_del_hotkey(PyObject *pyctx) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(pyctx) ) return false; @@ -452,6 +489,7 @@ def add_hotkey(hotkey, pyfunc): */ PyObject *py_add_hotkey(const char *hotkey, PyObject *pyfunc) { + PYW_GIL_CHECK_LOCKED_SCOPE(); // Make sure a callable was passed if ( !PyCallable_Check(pyfunc) ) return NULL; @@ -539,6 +577,7 @@ static PyObject *py_add_menu_item( PyObject *pyfunc, PyObject *args) { + PYW_GIL_CHECK_LOCKED_SCOPE(); bool no_args; // No slash in the menu path? @@ -637,55 +676,55 @@ def execute_sync(callable, reqf): //------------------------------------------------------------------------ static int py_execute_sync(PyObject *py_callable, int reqf) { - // Not callable? - if ( !PyCallable_Check(py_callable) ) - return -1; - - struct py_exec_request_t: exec_request_t + PYW_GIL_CHECK_LOCKED_SCOPE(); + int rc = -1; + // Callable? + if ( PyCallable_Check(py_callable) ) { - PyObject *py_callable; - bool no_wait; - virtual int idaapi execute() + struct py_exec_request_t : exec_request_t { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallFunctionObjArgs(py_callable, NULL); - int r = py_result == NULL || !PyInt_Check(py_result) ? -1 : PyInt_AsLong(py_result); - Py_XDECREF(py_result); - PYW_GIL_RELEASE; + ref_t py_callable; + virtual int idaapi execute() + { + PYW_GIL_GET; + newref_t py_result(PyObject_CallFunctionObjArgs(py_callable.o, NULL)); + int ret = py_result == NULL || !PyInt_Check(py_result.o) + ? -1 + : PyInt_AsLong(py_result.o); + // if the requesting thread decided not to wait for the request to + // complete, we have to self-destroy, nobody else will do it + if ( (code & MFF_NOWAIT) != 0 ) + delete this; + return ret; + } + py_exec_request_t(PyObject *pyc) + { + // No need to GIL-ensure here, since this is created + // within the py_execute_sync() scope. + py_callable = borref_t(pyc); + } + virtual ~py_exec_request_t() + { + // Need to GIL-ensure here, since this might be called + // from the main thread. + PYW_GIL_GET; + py_callable = ref_t(); // Release callable + } + }; + py_exec_request_t *req = new py_exec_request_t(py_callable); - // Free this request - if ( no_wait ) - delete this; - - return r; - } - py_exec_request_t(PyObject *pyc, bool no_wait): - py_callable(pyc), no_wait(no_wait) - { - // Take reference to the callable - Py_INCREF(py_callable); - } - virtual ~py_exec_request_t() - { - // Release callable - Py_XDECREF(py_callable); - } - }; - - bool no_wait = (reqf & MFF_NOWAIT) != 0; - - // Allocate a request - py_exec_request_t *req = new py_exec_request_t(py_callable, no_wait); - - // Execute it - int r = execute_sync(*req, reqf); - - // Delete only if NOWAIT was not specified - // (Otherwise the request will delete itself) - if ( !no_wait ) - delete req; - - return r; + // Release GIL before executing, or if this is running in the + // non-main thread, this will wait on the req.sem, while the main + // thread might be waiting for the GIL to be available. + Py_BEGIN_ALLOW_THREADS; + rc = execute_sync(*req, reqf); + Py_END_ALLOW_THREADS; + // destroy the request once it is finished. exception: NOWAIT requests + // will be handled in the future, so do not destroy them yet! + if ( (reqf & MFF_NOWAIT) == 0 ) + delete req; + } + return rc; } //------------------------------------------------------------------------ @@ -710,23 +749,22 @@ static bool py_execute_ui_requests(PyObject *py_list) struct py_ui_request_t: public ui_request_t { private: - ppyobject_vec_t py_callables; + ref_vec_t py_callables; size_t py_callable_idx; static int idaapi s_py_list_walk_cb( - PyObject *py_item, - Py_ssize_t index, - void *ud) + const ref_t &py_item, + Py_ssize_t index, + void *ud) { + PYW_GIL_CHECK_LOCKED_SCOPE(); // Not callable? Terminate iteration - if ( !PyCallable_Check(py_item) ) + if ( !PyCallable_Check(py_item.o) ) return CIP_FAILED; // Append this callable and increment its reference py_ui_request_t *_this = (py_ui_request_t *)ud; _this->py_callables.push_back(py_item); - Py_INCREF(py_item); - return CIP_OK; } public: @@ -736,14 +774,13 @@ static bool py_execute_ui_requests(PyObject *py_list) virtual bool idaapi run() { - // Get callable - PyObject *py_callable = py_callables.at(py_callable_idx); + PYW_GIL_GET; - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallFunctionObjArgs(py_callable, NULL); - bool reschedule = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - PYW_GIL_RELEASE; + // Get callable + ref_t py_callable = py_callables.at(py_callable_idx); + bool reschedule; + newref_t py_result(PyObject_CallFunctionObjArgs(py_callable.o, NULL)); + reschedule = py_result != NULL && PyObject_IsTrue(py_result.o); // No rescheduling? Then advance to the next callable if ( !reschedule ) @@ -765,13 +802,7 @@ static bool py_execute_ui_requests(PyObject *py_list) virtual idaapi ~py_ui_request_t() { - // Release all callables - for ( ppyobject_vec_t::const_iterator it=py_callables.begin(); - it != py_callables.end(); - ++it ) - { - Py_XDECREF(*it); - } + py_callables.clear(); } }; @@ -934,6 +965,7 @@ public: virtual PyObject *get_ea_hint(ea_t /*ea*/) { + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_RETURN_NONE; }; }; @@ -942,45 +974,35 @@ public: //--------------------------------------------------------------------------- uint32 idaapi choose_sizer(void *self) { - PyObject *pyres; - uint32 res; - - PYW_GIL_ENSURE; - pyres = PyObject_CallMethod((PyObject *)self, "sizer", ""); - PYW_GIL_RELEASE; - - res = PyInt_AsLong(pyres); - Py_DECREF(pyres); - return res; + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod((PyObject *)self, "sizer", "")); + return PyInt_AsLong(pyres.o); } //--------------------------------------------------------------------------- char *idaapi choose_getl(void *self, uint32 n, char *buf) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - (PyObject *)self, - "getl", - "l", - n); - PYW_GIL_RELEASE; + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + (PyObject *)self, + "getl", + "l", + n)); const char *res; - if (pyres == NULL || (res = PyString_AsString(pyres)) == NULL ) + if (pyres == NULL || (res = PyString_AsString(pyres.o)) == NULL ) qstrncpy(buf, "", MAXSTR); else qstrncpy(buf, res, MAXSTR); - - Py_XDECREF(pyres); return buf; } //--------------------------------------------------------------------------- void idaapi choose_enter(void *self, uint32 n) { - PYW_GIL_ENSURE; - Py_XDECREF(PyObject_CallMethod((PyObject *)self, "enter", "l", n)); - PYW_GIL_RELEASE; + PYW_GIL_GET; + newref_t res(PyObject_CallMethod((PyObject *)self, "enter", "l", n)); } //--------------------------------------------------------------------------- @@ -993,8 +1015,9 @@ uint32 choose_choose( int deflt, int icon) { - PyObject *pytitle = PyObject_GetAttrString((PyObject *)self, "title"); - const char *title = pytitle != NULL ? PyString_AsString(pytitle) : "Choose"; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t pytitle(PyObject_GetAttrString((PyObject *)self, "title")); + const char *title = pytitle != NULL ? PyString_AsString(pytitle.o) : "Choose"; int r = choose( flags, @@ -1015,7 +1038,7 @@ uint32 choose_choose( NULL, /* destroy */ NULL, /* popup_names */ NULL);/* get_icon */ - Py_XDECREF(pytitle); + return r; } @@ -1166,6 +1189,7 @@ static PyObject *formchgcbfa_get_field_value( size_t sz) { DECLARE_FORM_ACTIONS; + PYW_GIL_CHECK_LOCKED_SCOPE(); switch ( ft ) { case 8: @@ -1237,7 +1261,9 @@ static PyObject *formchgcbfa_get_field_value( for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) (*it)--; - return PyW_IntVecToPyList(intvec); + ref_t l(PyW_IntVecToPyList(intvec)); + l.incref(); + return l.o; } break; } @@ -1300,6 +1326,7 @@ static bool formchgcbfa_set_field_value( PyObject *py_val) { DECLARE_FORM_ACTIONS; + PYW_GIL_CHECK_LOCKED_SCOPE(); switch ( ft ) { @@ -1371,6 +1398,11 @@ static bool formchgcbfa_set_field_value( static size_t py_get_AskUsingForm() { + // Return a pointer to the function. Note that, although + // the C implementation of AskUsingForm_cv will do some + // Qt/txt widgets generation, the Python's ctypes + // implementation through which the call well go will first + // unblock other threads. No need to do it ourselves. return (size_t)AskUsingForm_c; } @@ -1384,6 +1416,8 @@ static size_t py_get_AskUsingForm() //--------------------------------------------------------------------------- int idaapi UI_Callback(void *ud, int notification_code, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; UI_Hooks *proxy = (UI_Hooks *)ud; int ret = 0; try @@ -1420,6 +1454,7 @@ int idaapi UI_Callback(void *ud, int notification_code, va_list va) char *_buf; Py_ssize_t _len; + PYW_GIL_CHECK_LOCKED_SCOPE(); PyObject *py_str = proxy->get_ea_hint(ea); if ( py_str != NULL && PyString_Check(py_str) @@ -1435,6 +1470,7 @@ int idaapi UI_Callback(void *ud, int notification_code, va_list va) catch (Swig::DirectorException &e) { msg("Exception in UI Hook function: %s\n", e.getMessage()); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( PyErr_Occurred() ) PyErr_Print(); } @@ -1444,15 +1480,15 @@ int idaapi UI_Callback(void *ud, int notification_code, va_list va) //------------------------------------------------------------------------ bool idaapi py_menu_item_callback(void *userdata) { + PYW_GIL_GET; + // userdata is a tuple of ( func, args ) // func and args are borrowed references from userdata PyObject *func = PyTuple_GET_ITEM(userdata, 0); PyObject *args = PyTuple_GET_ITEM(userdata, 1); // Call the python function - PYW_GIL_ENSURE; - PyObject *result = PyEval_CallObject(func, args); - PYW_GIL_RELEASE; + newref_t result(PyEval_CallObject(func, args)); // We cannot raise an exception in the callback, just print it. if ( result == NULL ) @@ -1461,9 +1497,7 @@ bool idaapi py_menu_item_callback(void *userdata) return false; } - bool ret = PyObject_IsTrue(result) != 0; - Py_DECREF(result); - return ret; + return PyObject_IsTrue(result.o) != 0; } @@ -1476,8 +1510,8 @@ bool idaapi py_menu_item_callback(void *userdata) #define thisdecl py_choose2_t *_this = thisobj #define MENU_COMMAND_CB(id) \ static uint32 idaapi s_menu_command_##id(void *obj, uint32 n) \ - { \ - return thisobj->on_command(id, int(n)); \ + { \ + return thisobj->on_command(id, int(n)); \ } //------------------------------------------------------------------------ @@ -1565,6 +1599,9 @@ private: //------------------------------------------------------------------------ static int idaapi ui_cb(void *obj, int notification_code, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + // UI callback to handle chooser items with attributes if ( notification_code != ui_get_chooser_item_attrs ) return 0; @@ -1674,6 +1711,9 @@ private: void on_get_line(int lineno, char * const *line_arr) { + // Called from s_getl, which itself can be called from the kernel. Ensure GIL + PYW_GIL_GET; + // Get headers? if ( lineno == 0 ) { @@ -1689,64 +1729,51 @@ private: line_arr[i][0] = '\0'; // Call Python - PYW_GIL_ENSURE; - PyObject *list = PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1); - PYW_GIL_RELEASE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t list(PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1)); if ( list == NULL ) return; // Go over the List returned by Python and convert to C strings for ( int i=ncols-1; i>=0; i-- ) { - PyObject *item = PyList_GetItem(list, Py_ssize_t(i)); + borref_t item(PyList_GetItem(list.o, Py_ssize_t(i))); if ( item == NULL ) continue; - const char *str = PyString_AsString(item); + const char *str = PyString_AsString(item.o); if ( str != NULL ) qstrncpy(line_arr[i], str, MAXSTR); } - Py_DECREF(list); } size_t on_get_size() { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL); - PYW_GIL_RELEASE; + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL)); if ( pyres == NULL ) return 0; - size_t res = PyInt_AsLong(pyres); - Py_DECREF(pyres); - return res; + return PyInt_AsLong(pyres.o); } void on_refreshed() { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_REFRESHED, NULL); - PYW_GIL_RELEASE; - Py_XDECREF(pyres); + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_REFRESHED, NULL)); } void on_select(const intvec_t &intvec) { - PYW_GIL_ENSURE; - PyObject *py_list = PyW_IntVecToPyList(intvec); - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_SELECT, "O", py_list); - PYW_GIL_RELEASE; - Py_XDECREF(pyres); - Py_XDECREF(py_list); + PYW_GIL_GET; + ref_t py_list(PyW_IntVecToPyList(intvec)); + newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_SELECT, "O", py_list.o)); } void on_close() { - // Call Python - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL); - PYW_GIL_RELEASE; - Py_XDECREF(pyres); + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL)); // Delete this instance if none modal and not embedded if ( !is_modal() && get_embedded() == NULL ) @@ -1755,123 +1782,96 @@ private: int on_delete_line(int lineno) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - self, - (char *)S_ON_DELETE_LINE, - "i", - lineno - 1); - PYW_GIL_RELEASE; - - if ( pyres == NULL ) - return lineno; - - size_t res = PyInt_AsLong(pyres); - Py_DECREF(pyres); - return res + 1; + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_DELETE_LINE, + "i", + lineno - 1)); + return pyres == NULL ? lineno : PyInt_AsLong(pyres.o) + 1; } int on_refresh(int lineno) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - self, - (char *)S_ON_REFRESH, - "i", - lineno - 1); - PYW_GIL_RELEASE; - if ( pyres == NULL ) - return lineno; - - size_t res = PyInt_AsLong(pyres); - Py_DECREF(pyres); - return res + 1; + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_REFRESH, + "i", + lineno - 1)); + return pyres == NULL ? lineno : PyInt_AsLong(pyres.o) + 1; } void on_insert_line() { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_INSERT_LINE, NULL); - PYW_GIL_RELEASE; - Py_XDECREF(pyres); + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_INSERT_LINE, NULL)); } void on_enter(int lineno) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - self, - (char *)S_ON_SELECT_LINE, - "i", - lineno - 1); - PYW_GIL_RELEASE; - Py_XDECREF(pyres); + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_SELECT_LINE, + "i", + lineno - 1)); } void on_edit_line(int lineno) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - self, - (char *)S_ON_EDIT_LINE, - "i", - lineno - 1); - PYW_GIL_RELEASE; - Py_XDECREF(pyres); + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_EDIT_LINE, + "i", + lineno - 1)); } int on_command(int cmd_id, int lineno) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - self, - (char *)S_ON_COMMAND, - "ii", - lineno - 1, - cmd_id); - PYW_GIL_RELEASE; - - if ( pyres==NULL ) - return lineno; - - size_t res = PyInt_AsLong(pyres); - Py_XDECREF(pyres); - return res; + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_COMMAND, + "ii", + lineno - 1, + cmd_id)); + return pyres == NULL ? lineno : PyInt_AsLong(pyres.o); } int on_get_icon(int lineno) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod( - self, - (char *)S_ON_GET_ICON, - "i", - lineno - 1); - PYW_GIL_RELEASE; - - size_t res = PyInt_AsLong(pyres); - Py_XDECREF(pyres); - return res; + PYW_GIL_GET; + newref_t pyres( + PyObject_CallMethod( + self, + (char *)S_ON_GET_ICON, + "i", + lineno - 1)); + return PyInt_AsLong(pyres.o); } void on_get_line_attr(int lineno, chooser_item_attrs_t *attr) { - PYW_GIL_ENSURE; - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1); - PYW_GIL_RELEASE; - - if ( pyres == NULL ) - return; - - if ( PyList_Check(pyres) ) + PYW_GIL_GET; + newref_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1)); + if ( pyres != NULL ) { - PyObject *item; - if ( (item = PyList_GetItem(pyres, 0)) != NULL ) - attr->color = PyInt_AsLong(item); - if ( (item = PyList_GetItem(pyres, 1)) != NULL ) - attr->flags = PyInt_AsLong(item); + if ( PyList_Check(pyres.o) ) + { + PyObject *item; + if ( (item = PyList_GetItem(pyres.o, 0)) != NULL ) + attr->color = PyInt_AsLong(item); + if ( (item = PyList_GetItem(pyres.o, 1)) != NULL ) + attr->flags = PyInt_AsLong(item); + } } - Py_XDECREF(pyres); } public: @@ -1961,7 +1961,7 @@ public: // Adjust the title ptitle = title_buf; - + // Adjust the caption p = strtok(NULL, delimiter); caption += (p - temp); @@ -1972,12 +1972,12 @@ public: } if ( !add_chooser_command( - ptitle, - caption, - menu_cbs[menu_cb_idx], - menu_index, - icon, - flags)) + ptitle, + caption, + menu_cbs[menu_cb_idx], + menu_index, + icon, + flags)) { return -1; } @@ -1990,83 +1990,71 @@ public: // Otherwise the chooser window is created and displayed int create(PyObject *self) { - PyObject *attr; + PYW_GIL_CHECK_LOCKED_SCOPE(); // Get flags - attr = PyW_TryGetAttrString(self, S_FLAGS); - if ( attr == NULL ) + ref_t flags_attr(PyW_TryGetAttrString(self, S_FLAGS)); + if ( flags_attr == NULL ) return -1; - - flags = PyInt_Check(attr) != 0 ? PyInt_AsLong(attr) : 0; - Py_DECREF(attr); + flags = PyInt_Check(flags_attr.o) != 0 ? PyInt_AsLong(flags_attr.o) : 0; // Get the title if ( !PyW_GetStringAttr(self, S_TITLE, &title) ) return -1; // Get columns - attr = PyW_TryGetAttrString(self, "cols"); - if ( attr == NULL ) + ref_t cols_attr(PyW_TryGetAttrString(self, "cols")); + if ( cols_attr == NULL ) return -1; // Get col count - int ncols = int(PyList_Size(attr)); + int ncols = int(PyList_Size(cols_attr.o)); // Get cols caption and widthes cols.qclear(); for ( int i=0; irefresh = NULL; } } - Py_XDECREF(attr); // Create the chooser (if not embedded) int r; @@ -2338,6 +2322,8 @@ void choose2_activate(PyObject *self) //------------------------------------------------------------------------ PyObject *choose2_get_embedded_selection(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_choose2_t *c2 = choose2_find_instance(self); chooser_info_t *embedded; @@ -2351,13 +2337,17 @@ PyObject *choose2_get_embedded_selection(PyObject *self) for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) (*it)--; - return PyW_IntVecToPyList(intvec); + ref_t ret(PyW_IntVecToPyList(intvec)); + ret.incref(); + return ret.o; } //------------------------------------------------------------------------ // Return the C instances as 64bit numbers PyObject *choose2_get_embedded(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_choose2_t *c2 = choose2_find_instance(self); chooser_info_t *embedded; @@ -2433,17 +2423,17 @@ private: static py_cli_t *py_clis[MAX_PY_CLI]; static const py_cli_cbs_t py_cli_cbs[MAX_PY_CLI]; //-------------------------------------------------------------------------- -#define IMPL_PY_CLI_CB(CBN) \ +#define IMPL_PY_CLI_CB(CBN) \ static bool idaapi s_keydown##CBN(qstring *line, int *p_x, int *p_sellen, int *vk_key, int shift) \ - { \ + { \ return py_clis[CBN]->on_keydown(line, p_x, p_sellen, vk_key, shift); \ - } \ - static bool idaapi s_execute_line##CBN(const char *line) \ - { \ - return py_clis[CBN]->on_execute_line(line); \ - } \ + } \ + static bool idaapi s_execute_line##CBN(const char *line) \ + { \ + return py_clis[CBN]->on_execute_line(line); \ + } \ static bool idaapi s_complete_line##CBN(qstring *completion, const char *prefix, int n, const char *line, int x) \ - { \ + { \ return py_clis[CBN]->on_complete_line(completion, prefix, n, line, x); \ } @@ -2458,18 +2448,15 @@ private: // Returns: true-executed line, false-ask for more lines bool on_execute_line(const char *line) { - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_EXECUTE_LINE, - "s", - line); - PYW_GIL_RELEASE; - - bool ok = result != NULL && PyObject_IsTrue(result); + PYW_GIL_GET; + newref_t result( + PyObject_CallMethod( + self, + (char *)S_ON_EXECUTE_LINE, + "s", + line)); PyW_ShowCbErr(S_ON_EXECUTE_LINE); - Py_XDECREF(result); - return ok; + return result != NULL && PyObject_IsTrue(result.o); } //-------------------------------------------------------------------------- @@ -2491,41 +2478,45 @@ private: int *vk_key, int shift) { - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_KEYDOWN, - "siiHi", - line->c_str(), - *p_x, - *p_sellen, - *vk_key, - shift); - PYW_GIL_RELEASE; + PYW_GIL_GET; + newref_t result( + PyObject_CallMethod( + self, + (char *)S_ON_KEYDOWN, + "siiHi", + line->c_str(), + *p_x, + *p_sellen, + *vk_key, + shift)); - bool ok = result != NULL && PyTuple_Check(result); + bool ok = result != NULL && PyTuple_Check(result.o); PyW_ShowCbErr(S_ON_KEYDOWN); if ( ok ) { - Py_ssize_t sz = PyTuple_Size(result); + Py_ssize_t sz = PyTuple_Size(result.o); PyObject *item; - - if ( sz > 0 && (item = PyTuple_GetItem(result, 0)) != NULL && PyString_Check(item) ) - *line = PyString_AsString(item); - - if ( sz > 1 && (item = PyTuple_GetItem(result, 1)) != NULL && PyInt_Check(item) ) - *p_x = PyInt_AsLong(item); - - if ( sz > 2 && (item = PyTuple_GetItem(result, 2)) != NULL && PyInt_Check(item) ) - *p_sellen = PyInt_AsLong(item); - if ( sz > 3 && (item = PyTuple_GetItem(result, 3)) != NULL && PyInt_Check(item) ) - *vk_key = PyInt_AsLong(item) & 0xffff; +#define GET_TUPLE_ENTRY(col, PyThingy, AsThingy, out) \ + do \ + { \ + if ( sz > col ) \ + { \ + borref_t _r(PyTuple_GetItem(result.o, col)); \ + if ( _r != NULL && PyThingy##_Check(_r.o) ) \ + *out = PyThingy##_##AsThingy(_r.o); \ + } \ + } while ( false ) + + GET_TUPLE_ENTRY(0, PyString, AsString, line); + GET_TUPLE_ENTRY(1, PyInt, AsLong, p_x); + GET_TUPLE_ENTRY(2, PyInt, AsLong, p_sellen); + GET_TUPLE_ENTRY(3, PyInt, AsLong, vk_key); + *vk_key &= 0xffff; +#undef GET_TUPLE_ENTRY } - - Py_XDECREF(result); return ok; } @@ -2536,41 +2527,41 @@ private: // Returns: true if generated a new completion // This callback is optional bool on_complete_line( - qstring *completion, - const char *prefix, - int n, - const char *line, - int x) + qstring *completion, + const char *prefix, + int n, + const char *line, + int x) { - PYW_GIL_ENSURE; - PyObject *result = PyObject_CallMethod( - self, - (char *)S_ON_COMPLETE_LINE, - "sisi", - prefix, - n, - line, - x); - PYW_GIL_RELEASE; - - bool ok = result != NULL && PyString_Check(result); + PYW_GIL_GET; + newref_t result( + PyObject_CallMethod( + self, + (char *)S_ON_COMPLETE_LINE, + "sisi", + prefix, + n, + line, + x)); + + bool ok = result != NULL && PyString_Check(result.o); PyW_ShowCbErr(S_ON_COMPLETE_LINE); if ( ok ) - *completion = PyString_AsString(result); - - Py_XDECREF(result); + *completion = PyString_AsString(result.o); return ok; } // Private ctor (use bind()) - py_cli_t() - { + py_cli_t() + { } public: //--------------------------------------------------------------------------- static int bind(PyObject *py_obj) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + int cli_idx; // Find an empty slot for ( cli_idx = 0; cli_idx < MAX_PY_CLI; ++cli_idx ) @@ -2579,7 +2570,7 @@ public: break; } py_cli_t *py_cli = NULL; - do + do { // No free slots? if ( cli_idx >= MAX_PY_CLI ) @@ -2593,14 +2584,12 @@ public: py_cli->cli.size = sizeof(cli_t); // Store 'flags' - if ( (attr = PyW_TryGetAttrString(py_obj, S_FLAGS)) == NULL ) { - py_cli->cli.flags = 0; - } - else - { - py_cli->cli.flags = PyLong_AsLong(attr); - Py_DECREF(attr); + ref_t flags_attr(PyW_TryGetAttrString(py_obj, S_FLAGS)); + if ( flags_attr == NULL ) + py_cli->cli.flags = 0; + else + py_cli->cli.flags = PyLong_AsLong(flags_attr.o); } // Store 'sname' @@ -2652,9 +2641,12 @@ public: py_cli_t *py_cli = py_clis[cli_idx]; remove_command_interpreter(&py_cli->cli); - - Py_DECREF(py_cli->self); - delete py_cli; + + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_DECREF(py_cli->self); + delete py_cli; + } py_clis[cli_idx] = NULL; @@ -2677,11 +2669,14 @@ const py_cli_cbs_t py_cli_t::py_cli_cbs[MAX_PY_CLI] = class plgform_t { private: - PyObject *py_obj; + ref_t py_obj; TForm *form; static int idaapi s_callback(void *ud, int notification_code, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; + plgform_t *_this = (plgform_t *)ud; if ( notification_code == ui_tform_visible ) { @@ -2692,15 +2687,12 @@ private: // G: HWND // We wrap and pass as a CObject in the hope that a Python UI framework // can unwrap a CObject and get the hwnd/widget back - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_obj, - (char *)S_ON_CREATE, "O", - PyCObject_FromVoidPtr(form, NULL)); - PYW_GIL_RELEASE; - + newref_t py_result( + PyObject_CallMethod( + _this->py_obj.o, + (char *)S_ON_CREATE, "O", + PyCObject_FromVoidPtr(form, NULL))); PyW_ShowCbErr(S_ON_CREATE); - Py_XDECREF(py_result); } } else if ( notification_code == ui_tform_invisible ) @@ -2708,16 +2700,14 @@ private: TForm *form = va_arg(va, TForm *); if ( form == _this->form ) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - _this->py_obj, - (char *)S_ON_CLOSE, "O", - PyCObject_FromVoidPtr(form, NULL)); - PYW_GIL_RELEASE; - - PyW_ShowCbErr(S_ON_CLOSE); - Py_XDECREF(py_result); - + { + newref_t py_result( + PyObject_CallMethod( + _this->py_obj.o, + (char *)S_ON_CLOSE, "O", + PyCObject_FromVoidPtr(form, NULL))); + PyW_ShowCbErr(S_ON_CLOSE); + } _this->unhook(); } } @@ -2730,11 +2720,12 @@ private: form = NULL; // Call DECREF at last, since it may trigger __del__ - Py_XDECREF(py_obj); + PYW_GIL_CHECK_LOCKED_SCOPE(); + py_obj = ref_t(); } public: - plgform_t(): py_obj(NULL), form(NULL) + plgform_t(): form(NULL) { } @@ -2769,8 +2760,7 @@ public: return false; } - py_obj = obj; - Py_INCREF(obj); + py_obj = borref_t(obj); if ( is_idaq() ) options |= FORM_QWIDGET; @@ -2788,6 +2778,7 @@ public: static PyObject *create() { + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyCObject_FromVoidPtr(new plgform_t(), destroy); } @@ -2802,7 +2793,7 @@ public: %inline %{ // //--------------------------------------------------------------------------- -#define DECL_PLGFORM plgform_t *plgform = (plgform_t *) PyCObject_AsVoidPtr(py_link); +#define DECL_PLGFORM PYW_GIL_CHECK_LOCKED_SCOPE(); plgform_t *plgform = (plgform_t *) PyCObject_AsVoidPtr(py_link); static PyObject *plgform_new() { return plgform_t::create(); @@ -2998,6 +2989,7 @@ private: static bool idaapi s_popup_cb(void *ud) { + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; return _this->on_popup(); } @@ -3009,6 +3001,7 @@ private: if ( it == _global_popup_map.end() ) return false; + PYW_GIL_GET; return it->second.cv->on_popup_menu(it->second.menu_id); } @@ -3018,6 +3011,7 @@ private: int shift, void *ud) { + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; return _this->on_keydown(vk_key, shift); } @@ -3025,6 +3019,7 @@ private: // The popup menu is being constructed static void idaapi s_cv_popup(TCustomControl * /*cv*/, void *ud) { + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; _this->on_popup(); } @@ -3032,6 +3027,7 @@ private: // The user clicked static bool idaapi s_cv_click(TCustomControl * /*cv*/, int shift, void *ud) { + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; return _this->on_click(shift); } @@ -3039,6 +3035,7 @@ private: // The user double clicked static bool idaapi s_cv_dblclick(TCustomControl * /*cv*/, int shift, void *ud) { + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; return _this->on_dblclick(shift); } @@ -3046,6 +3043,7 @@ private: // Cursor position has been changed static void idaapi s_cv_curpos(TCustomControl * /*cv*/, void *ud) { + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; _this->on_curpos_changed(); } @@ -3053,6 +3051,8 @@ private: //-------------------------------------------------------------------------- static int idaapi s_ui_cb(void *ud, int code, va_list va) { + // This hook gets called from the kernel. Ensure we hold the GIL. + PYW_GIL_GET; customviewer_t *_this = (customviewer_t *)ud; switch ( code ) { @@ -3073,11 +3073,12 @@ private: TForm *form = va_arg(va, TForm *); if ( _this->_form != form ) break; - - unhook_from_notification_point(HT_UI, s_ui_cb, _this); - _this->on_close(); - _this->on_post_close(); } + // fallthrough... + case ui_term: + unhook_from_notification_point(HT_UI, s_ui_cb, _this); + _this->on_close(); + _this->on_post_close(); break; } @@ -3371,6 +3372,8 @@ private: // Convert a tuple (String, [color, [bgcolor]]) to a simpleline_t static bool py_to_simpleline(PyObject *py, simpleline_t &sl) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyString_Check(py) ) { sl.line = PyString_AsString(py); @@ -3400,37 +3403,29 @@ private: // virtual bool on_click(int shift) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CLICK, "i", shift); - PYW_GIL_RELEASE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CLICK, "i", shift)); PyW_ShowCbErr(S_ON_CLICK); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; + return py_result != NULL && PyObject_IsTrue(py_result.o); } //-------------------------------------------------------------------------- // OnDblClick virtual bool on_dblclick(int shift) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_DBL_CLICK, "i", shift); - PYW_GIL_RELEASE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_DBL_CLICK, "i", shift)); PyW_ShowCbErr(S_ON_DBL_CLICK); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; + return py_result != NULL && PyObject_IsTrue(py_result.o); } //-------------------------------------------------------------------------- // OnCurorPositionChanged virtual void on_curpos_changed() { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CURSOR_POS_CHANGED, NULL); - PYW_GIL_RELEASE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CURSOR_POS_CHANGED, NULL)); PyW_ShowCbErr(S_ON_CURSOR_POS_CHANGED); - Py_XDECREF(py_result); } //-------------------------------------------------------------------------- @@ -3440,12 +3435,9 @@ private: // Call the close method if it is there and the object is still bound if ( (features & HAVE_CLOSE) != 0 && py_self != NULL ) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CLOSE, NULL); - PYW_GIL_RELEASE; - + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result(PyObject_CallMethod(py_self, (char *)S_ON_CLOSE, NULL)); PyW_ShowCbErr(S_ON_CLOSE); - Py_XDECREF(py_result); // Cleanup Py_DECREF(py_self); @@ -3457,36 +3449,31 @@ private: // OnKeyDown virtual bool on_keydown(int vk_key, int shift) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - py_self, - (char *)S_ON_KEYDOWN, - "ii", - vk_key, - shift); - PYW_GIL_RELEASE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_KEYDOWN, + "ii", + vk_key, + shift)); PyW_ShowCbErr(S_ON_KEYDOWN); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; + return py_result != NULL && PyObject_IsTrue(py_result.o); } //-------------------------------------------------------------------------- // OnPopupShow virtual bool on_popup() { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - py_self, - (char *)S_ON_POPUP, - NULL); - PYW_GIL_RELEASE; - + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_POPUP, + NULL)); PyW_ShowCbErr(S_ON_POPUP); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; + return py_result != NULL && PyObject_IsTrue(py_result.o); } //-------------------------------------------------------------------------- @@ -3494,28 +3481,22 @@ private: virtual bool on_hint(place_t *place, int *important_lines, qstring &hint) { size_t ln = data.to_lineno(place); - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - py_self, - (char *)S_ON_HINT, - PY_FMT64, - pyul_t(ln)); - PYW_GIL_RELEASE; + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_HINT, + PY_FMT64, + pyul_t(ln))); PyW_ShowCbErr(S_ON_HINT); - bool ok = py_result != NULL && PyTuple_Check(py_result) && PyTuple_Size(py_result) == 2; + bool ok = py_result != NULL && PyTuple_Check(py_result.o) && PyTuple_Size(py_result.o) == 2; if ( ok ) { - // Borrow references - PyObject *py_nlines = PyTuple_GetItem(py_result, 0); - PyObject *py_hint = PyTuple_GetItem(py_result, 1); - if ( important_lines != NULL ) - *important_lines = PyInt_AsLong(py_nlines); - - hint = PyString_AsString(py_hint); + *important_lines = PyInt_AsLong(PyTuple_GetItem(py_result.o, 0)); + hint = PyString_AsString(PyTuple_GetItem(py_result.o, 1)); } - Py_XDECREF(py_result); return ok; } @@ -3523,18 +3504,15 @@ private: // OnPopupMenuClick virtual bool on_popup_menu(size_t menu_id) { - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallMethod( - py_self, - (char *)S_ON_POPUP_MENU, - PY_FMT64, - pyul_t(menu_id)); - PYW_GIL_RELEASE; - + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_result( + PyObject_CallMethod( + py_self, + (char *)S_ON_POPUP_MENU, + PY_FMT64, + pyul_t(menu_id))); PyW_ShowCbErr(S_ON_POPUP_MENU); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; + return py_result != NULL && PyObject_IsTrue(py_result.o); } //-------------------------------------------------------------------------- @@ -3606,6 +3584,7 @@ public: place_t *pl; int x, y; pl = get_place(mouse, &x, &y); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( pl == NULL ) Py_RETURN_NONE; return Py_BuildValue("(" PY_FMT64 "ii)", pyul_t(data.to_lineno(pl)), x, y); @@ -3616,6 +3595,7 @@ public: PyObject *get_line(size_t nline) { simpleline_t *r = data.get_line(nline); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( r == NULL ) Py_RETURN_NONE; return Py_BuildValue("(sII)", r->line.c_str(), (unsigned int)r->color, (unsigned int)r->bgcolor); @@ -3665,6 +3645,8 @@ public: {S_ON_DBL_CLICK, HAVE_DBLCLICK}, {S_ON_CURSOR_POS_CHANGED, HAVE_CURPOS} }; + + PYW_GIL_CHECK_LOCKED_SCOPE(); for ( size_t i=0; i static int py_install_command_interpreter(PyObject *py_obj) -{ +{ return py_cli_t::bind(py_obj); } static void py_remove_command_interpreter(int cli_idx) -{ +{ py_cli_t::unbind(cli_idx); } // @@ -3760,6 +3744,7 @@ static void py_remove_command_interpreter(int cli_idx) // PyObject *pyscv_init(PyObject *py_link, const char *title) { + PYW_GIL_CHECK_LOCKED_SCOPE(); py_simplecustview_t *_this = new py_simplecustview_t(); bool ok = _this->init(py_link, title); if ( !ok ) @@ -3804,6 +3789,7 @@ bool pyscv_refresh_current(PyObject *py_this) PyObject *pyscv_get_current_line(PyObject *py_this, bool mouse, bool notags) { DECL_THIS; + PYW_GIL_CHECK_LOCKED_SCOPE(); const char *line; if ( _this == NULL || (line = _this->get_current_line(mouse, notags)) == NULL ) Py_RETURN_NONE; @@ -3864,7 +3850,10 @@ PyObject *pyscv_get_line(PyObject *py_this, size_t nline) { DECL_THIS; if ( _this == NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_RETURN_NONE; + } return _this->get_line(nline); } @@ -3874,7 +3863,10 @@ PyObject *pyscv_get_pos(PyObject *py_this, bool mouse) { DECL_THIS; if ( _this == NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_RETURN_NONE; + } return _this->get_pos(mouse); } @@ -3884,6 +3876,7 @@ PyObject *pyscv_clear_lines(PyObject *py_this) DECL_THIS; if ( _this != NULL ) _this->clear(); + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_RETURN_NONE; } @@ -3921,7 +3914,10 @@ PyObject *pyscv_get_selection(PyObject *py_this) { DECL_THIS; if ( _this == NULL ) + { + PYW_GIL_CHECK_LOCKED_SCOPE(); Py_RETURN_NONE; + } return _this->py_get_selection(); } @@ -3929,6 +3925,7 @@ PyObject *pyscv_get_selection(PyObject *py_this) PyObject *pyscv_get_current_word(PyObject *py_this, bool mouse) { DECL_THIS; + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( _this != NULL ) { qstring word; @@ -5587,6 +5584,19 @@ class PluginForm(object): @param ctx: Context. Reference to a module that already imported QtGui module """ + if form is None: + return None + if type(form).__name__ == "SwigPyObject": + # Since 'form' is a SwigPyObject, we first need to convert it to a PyCObject. + # However, there's no easy way of doing it, so we'll use a rather brutal approach: + # converting the SwigPyObject to a 'long' (will go through 'SwigPyObject_long', + # that will return the pointer's value as a long), and then convert that value + # back to a pointer into a PyCObject. + ptr_l = long(form) + from ctypes import pythonapi, c_void_p, py_object + pythonapi.PyCObject_FromVoidPtr.restype = py_object + pythonapi.PyCObject_AsVoidPtr.argtypes = [c_void_p, c_void_p] + form = pythonapi.PyCObject_FromVoidPtr(ptr_l, 0) return ctx.QtGui.QWidget.FromCObject(form) @@ -5617,7 +5627,7 @@ class PluginForm(object): @return: None """ - return _idaapi.plgform_close(self.__clink__) + return _idaapi.plgform_close(self.__clink__, options) FORM_SAVE = 0x1 """Save state in desktop config""" diff --git a/swig/lines.i b/swig/lines.i index f932a82..f444322 100644 --- a/swig/lines.i +++ b/swig/lines.i @@ -85,10 +85,12 @@ static void idaapi s_py_get_user_defined_prefix( char *buf, size_t bufsize) { - PyObject *py_ret = PyObject_CallFunction( - py_get_user_defined_prefix, - PY_FMT64 "iis" PY_FMT64, - ea, lnnum, indent, line, bufsize); + PYW_GIL_GET; + newref_t py_ret( + PyObject_CallFunction( + py_get_user_defined_prefix, + PY_FMT64 "iis" PY_FMT64, + ea, lnnum, indent, line, bufsize)); // Error? Display it // No error? Copy the buffer @@ -96,14 +98,13 @@ static void idaapi s_py_get_user_defined_prefix( { Py_ssize_t py_len; char *py_str; - if ( PyString_AsStringAndSize(py_ret, &py_str, &py_len) != -1 ) + if ( PyString_AsStringAndSize(py_ret.o, &py_str, &py_len) != -1 ) { memcpy(buf, py_str, qmin(bufsize, py_len)); if ( py_len < bufsize ) buf[py_len] = '\0'; } } - Py_XDECREF(py_ret); } // %} @@ -138,6 +139,7 @@ def set_user_defined_prefix(width, callback): */ static PyObject *py_set_user_defined_prefix(size_t width, PyObject *pycb) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( width == 0 || pycb == Py_None ) { // Release old callback reference @@ -183,6 +185,7 @@ def tag_remove(colstr): */ PyObject *py_tag_remove(const char *instr) { + PYW_GIL_CHECK_LOCKED_SCOPE(); size_t sz = strlen(instr); char *buf = new char[sz + 5]; if ( buf == NULL ) @@ -208,6 +211,7 @@ PyObject *py_tag_addr(ea_t ea) { char buf[100]; tag_addr(buf, buf + sizeof(buf), ea); + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyString_FromString(buf); } @@ -253,6 +257,7 @@ PyObject *py_generate_disassembly( bool as_stack, bool notags) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( max_lines <= 0 ) Py_RETURN_NONE; @@ -261,7 +266,7 @@ PyObject *py_generate_disassembly( int lnnum; int nlines = generate_disassembly(ea, lines, max_lines, &lnnum, as_stack); - PyObject *py_tuple = PyTuple_New(nlines); + newref_t py_tuple(PyTuple_New(nlines)); for ( int i=0; i diff --git a/swig/loader.i b/swig/loader.i index cd186c4..355d7fb 100644 --- a/swig/loader.i +++ b/swig/loader.i @@ -68,6 +68,7 @@ %ignore IDP_DESC_START; %ignore IDP_DESC_END; %ignore get_idp_desc; +%ignore get_idp_descs; %ignore init_fileregions; %ignore term_fileregions; %ignore save_fileregions; @@ -108,6 +109,8 @@ %ignore is_in_loader; %ignore get_ids_filename; %ignore is_embedded_dbfile_ext; +%ignore cpp_namespaces; +%ignore max_trusted_idb_count; %ignore mem2base; %rename (mem2base) py_mem2base; @@ -147,8 +150,11 @@ static int py_mem2base(PyObject *py_mem, ea_t ea, long fpos = -1) { Py_ssize_t len; char *buf; - if ( PyString_AsStringAndSize(py_mem, &buf, &len) == -1 ) - return 0; + { + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyString_AsStringAndSize(py_mem, &buf, &len) == -1 ) + return 0; + } return mem2base((void *)buf, ea, ea+len, fpos); } @@ -169,6 +175,7 @@ def load_plugin(name): static PyObject *py_load_plugin(const char *name) { plugin_t *r = load_plugin(name); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( r == NULL ) Py_RETURN_NONE; else @@ -189,10 +196,20 @@ def run_plugin(plg): */ static bool py_run_plugin(PyObject *plg, int arg) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(plg) ) + { return false; + } else - return run_plugin((plugin_t *)PyCObject_AsVoidPtr(plg), arg); + { + plugin_t *p = (plugin_t *)PyCObject_AsVoidPtr(plg); + bool rc; + Py_BEGIN_ALLOW_THREADS; + rc = run_plugin(p, arg); + Py_END_ALLOW_THREADS; + return rc; + } } // diff --git a/swig/nalt.i b/swig/nalt.i index cf1c3ac..426c2a9 100644 --- a/swig/nalt.i +++ b/swig/nalt.i @@ -35,53 +35,35 @@ static int idaapi py_import_enum_cb( if ( name == NULL ) name = get_true_name(BADADDR, ea, name_buf, sizeof(name_buf)); - PyObject *py_name; + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_name; if ( name == NULL ) - { - py_name = Py_None; - Py_INCREF(Py_None); - } + py_name = borref_t(Py_None); else - { - py_name = PyString_FromString(name); - } + py_name = newref_t(PyString_FromString(name)); - PyObject *py_ord = Py_BuildValue(PY_FMT64, pyul_t(ord)); - PyObject *py_ea = Py_BuildValue(PY_FMT64, pyul_t(ea)); - PYW_GIL_ENSURE; - PyObject *py_result = PyObject_CallFunctionObjArgs( - (PyObject *)param, - py_ea, - py_name, - py_ord, - NULL); - PYW_GIL_RELEASE; - - int r = py_result != NULL && PyObject_IsTrue(py_result) ? 1 : 0; - - Py_DECREF(py_ea); - Py_DECREF(py_name); - Py_DECREF(py_ord); - Py_XDECREF(py_result); - - return r; + newref_t py_ord(Py_BuildValue(PY_FMT64, pyul_t(ord))); + newref_t py_ea(Py_BuildValue(PY_FMT64, pyul_t(ea))); + newref_t py_result( + PyObject_CallFunctionObjArgs( + (PyObject *)param, + py_ea.o, + py_name.o, + py_ord.o, + NULL)); + return py_result != NULL && PyObject_IsTrue(py_result.o) ? 1 : 0; } //------------------------------------------------------------------------- switch_info_ex_t *switch_info_ex_t_get_clink(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyObject_HasAttrString(self, S_CLINK_NAME) ) return NULL; switch_info_ex_t *r; - PyObject *attr = PyObject_GetAttrString(self, S_CLINK_NAME); - if ( PyCObject_Check(attr) ) - r = (switch_info_ex_t *) PyCObject_AsVoidPtr(attr); - else - r = NULL; - - Py_DECREF(attr); - return r; + newref_t attr(PyObject_GetAttrString(self, S_CLINK_NAME)); + return PyCObject_Check(attr.o) ? ((switch_info_ex_t *) PyCObject_AsVoidPtr(attr.o)) : NULL; } // %} @@ -108,10 +90,11 @@ def get_import_module_name(path, fname, callback): */ static PyObject *py_get_import_module_name(int mod_index) { + PYW_GIL_CHECK_LOCKED_SCOPE(); char buf[MAXSTR]; if ( !get_import_module_name(mod_index, buf, sizeof(buf)) ) Py_RETURN_NONE; - + return PyString_FromString(buf); } @@ -130,14 +113,16 @@ def get_switch_info_ex(ea): PyObject *py_get_switch_info_ex(ea_t ea) { switch_info_ex_t *ex = new switch_info_ex_t(); - PyObject *py_obj; - if ( ::get_switch_info_ex(ea, ex, sizeof(switch_info_ex_t)) <= 0 + ref_t py_obj; + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ::get_switch_info_ex(ea, ex, sizeof(switch_info_ex_t)) <= 0 || (py_obj = create_idaapi_linked_class_instance(S_PY_SWIEX_CLSNAME, ex)) == NULL ) { delete ex; Py_RETURN_NONE; } - return py_obj; + py_obj.incref(); + return py_obj.o; } //------------------------------------------------------------------------- @@ -151,7 +136,7 @@ def create_switch_xrefs(insn_ea, si): will call it for switch tables Note: Custom switch information are not supported yet. - + @param insn_ea: address of the 'indirect jump' instruction @param si: switch information @@ -179,7 +164,7 @@ idaman bool ida_export py_create_switch_xrefs( def create_switch_table(insn_ea, si): """ Create switch table from the switch information - + @param insn_ea: address of the 'indirect jump' instruction @param si: switch information @@ -195,7 +180,7 @@ idaman bool ida_export py_create_switch_table( switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); if ( swi == NULL ) return false; - + create_switch_table(insn_ea, swi); return true; } @@ -254,9 +239,9 @@ def enum_import_names(mod_index, callback): */ static int py_enum_import_names(int mod_index, PyObject *py_cb) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCallable_Check(py_cb) ) return -1; - return enum_import_names(mod_index, py_import_enum_cb, py_cb); } @@ -264,12 +249,14 @@ static int py_enum_import_names(int mod_index, PyObject *py_cb) static PyObject *switch_info_ex_t_create() { switch_info_ex_t *inst = new switch_info_ex_t(); + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyCObject_FromVoidPtr(inst, NULL); } //--------------------------------------------------------------------------- static bool switch_info_ex_t_destroy(PyObject *py_obj) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(py_obj) ) return false; switch_info_ex_t *inst = (switch_info_ex_t *) PyCObject_AsVoidPtr(py_obj); @@ -295,6 +282,7 @@ static bool switch_info_ex_t_assign(PyObject *self, PyObject *other) static PyObject *switch_info_ex_t_get_regdtyp(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue("b", (char)link->regdtyp); @@ -304,12 +292,14 @@ static void switch_info_ex_t_set_regdtyp(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); link->regdtyp = (char)PyInt_AsLong(value); } static PyObject *switch_info_ex_t_get_flags2(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue("i", link->flags2); @@ -319,12 +309,14 @@ static void switch_info_ex_t_set_flags2(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); link->flags2 = (int)PyInt_AsLong(value); } static PyObject *switch_info_ex_t_get_jcases(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue("i", link->jcases); @@ -334,12 +326,14 @@ static void switch_info_ex_t_set_jcases(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); link->jcases = (int)PyInt_AsLong(value); } static PyObject *switch_info_ex_t_get_regnum(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue("i", (int)link->regnum); @@ -349,12 +343,14 @@ static void switch_info_ex_t_set_regnum(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); link->regnum = (int)PyInt_AsLong(value); } static PyObject *switch_info_ex_t_get_flags(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue("H", (ushort)link->flags); @@ -364,12 +360,14 @@ static void switch_info_ex_t_set_flags(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); link->flags = (uint16)PyInt_AsLong(value); } static PyObject *switch_info_ex_t_get_ncases(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue("H", (uint16)link->ncases); @@ -379,12 +377,14 @@ static void switch_info_ex_t_set_ncases(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); link->ncases = (ushort)PyInt_AsLong(value); } static PyObject *switch_info_ex_t_get_defjump(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue(PY_FMT64, (pyul_t)link->defjump); @@ -394,6 +394,7 @@ static void switch_info_ex_t_set_defjump(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); uint64 v(0); PyW_GetNumber(value, &v); link->defjump = (pyul_t)v; } @@ -410,6 +411,7 @@ static void switch_info_ex_t_set_jumps(PyObject *self, PyObject *value) switch_info_ex_t *link = switch_info_ex_t_get_clink(self); if ( link == NULL ) return; + PYW_GIL_CHECK_LOCKED_SCOPE(); uint64 v(0); PyW_GetNumber(value, &v); link->jumps = (pyul_t)v; } @@ -417,6 +419,7 @@ static void switch_info_ex_t_set_jumps(PyObject *self, PyObject *value) static PyObject *switch_info_ex_t_get_elbase(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue(PY_FMT64, (pyul_t)link->elbase); @@ -427,6 +430,7 @@ static void switch_info_ex_t_set_elbase(PyObject *self, PyObject *value) if ( link == NULL ) return; uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); PyW_GetNumber(value, &v); link->elbase = (pyul_t)v; } @@ -434,6 +438,7 @@ static void switch_info_ex_t_set_elbase(PyObject *self, PyObject *value) static PyObject *switch_info_ex_t_get_startea(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue(PY_FMT64, (pyul_t)link->startea); @@ -444,6 +449,7 @@ static void switch_info_ex_t_set_startea(PyObject *self, PyObject *value) if ( link == NULL ) return; uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); PyW_GetNumber(value, &v); link->startea = (pyul_t)v; } @@ -451,6 +457,7 @@ static void switch_info_ex_t_set_startea(PyObject *self, PyObject *value) static PyObject *switch_info_ex_t_get_custom(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue(PY_FMT64, (pyul_t)link->custom); @@ -461,6 +468,7 @@ static void switch_info_ex_t_set_custom(PyObject *self, PyObject *value) if ( link == NULL ) return; uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); PyW_GetNumber(value, &v); link->custom = (pyul_t)v; } @@ -468,6 +476,7 @@ static void switch_info_ex_t_set_custom(PyObject *self, PyObject *value) static PyObject *switch_info_ex_t_get_ind_lowcase(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue(PY_FMT64, (pyul_t)link->ind_lowcase); @@ -478,6 +487,7 @@ static void switch_info_ex_t_set_ind_lowcase(PyObject *self, PyObject *value) if ( link == NULL ) return; uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); PyW_GetNumber(value, &v); link->ind_lowcase = (pyul_t)v; } @@ -485,6 +495,7 @@ static void switch_info_ex_t_set_ind_lowcase(PyObject *self, PyObject *value) static PyObject *switch_info_ex_t_get_values_lowcase(PyObject *self) { switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( link == NULL ) Py_RETURN_NONE; return Py_BuildValue(PY_FMT64, (pyul_t)link->values); @@ -495,6 +506,7 @@ static void switch_info_ex_t_set_values_lowcase(PyObject *self, PyObject *value) if ( link == NULL ) return; uint64 v(0); + PYW_GIL_CHECK_LOCKED_SCOPE(); PyW_GetNumber(value, &v); link->values = (pyul_t)v; } diff --git a/swig/name.i b/swig/name.i index 012b33e..1dbf34d 100644 --- a/swig/name.i +++ b/swig/name.i @@ -39,16 +39,19 @@ PyObject *py_get_debug_names(ea_t ea1, ea_t ea2) { // Get debug names ea_name_vec_t names; + PYW_GIL_CHECK_LOCKED_SCOPE(); + Py_BEGIN_ALLOW_THREADS; get_debug_names(ea1, ea2, names); + Py_END_ALLOW_THREADS; PyObject *dict = Py_BuildValue("{}"); - if (dict == NULL) - return NULL; - - for (ea_name_vec_t::iterator it=names.begin();it!=names.end();++it) + if (dict != NULL) { - PyDict_SetItem(dict, - Py_BuildValue(PY_FMT64, it->ea), - PyString_FromString(it->name.c_str())); + for (ea_name_vec_t::iterator it=names.begin();it!=names.end();++it) + { + PyDict_SetItem(dict, + Py_BuildValue(PY_FMT64, it->ea), + PyString_FromString(it->name.c_str())); + } } return dict; } diff --git a/swig/netnode.i b/swig/netnode.i index 89a1fab..f72930f 100644 --- a/swig/netnode.i +++ b/swig/netnode.i @@ -69,10 +69,12 @@ %ignore netnode::set_close_flag; %ignore netnode::reserve_nodes; %ignore netnode::validate; +%ignore netnode::upgrade16; %ignore netnode::upgrade; %ignore netnode::compress; %ignore netnode::inited; %ignore netnode::init; +%ignore netnode::can_write; %ignore netnode::flush; %ignore netnode::get_linput; %ignore netnode::term; diff --git a/swig/pro.i b/swig/pro.i index 7061265..25ada89 100644 --- a/swig/pro.i +++ b/swig/pro.i @@ -98,7 +98,8 @@ $result = PyLong_FromUnsignedLongLong((unsigned long long) $1); %ignore BELOW_NORMAL_PRIORITY_CLASS; %ignore parse_command_line; %ignore parse_command_line2; -%rename (parse_command_line2) py_parse_command_line; +%ignore parse_command_line3; +%rename (parse_command_line3) py_parse_command_line; %ignore qgetenv; %ignore qsetenv; %ignore qctime; @@ -126,13 +127,6 @@ public: const char *c_str() const { return self->c_str(); } }; -//--------------------------------------------------------------------- -// for obscure reasons swig can't get past this one on its own, it needs a dummy declaration. -class strvec_t { -public: - qstring& at(int _idx) { return self->at(_idx).line; } - size_t size() const { return self->size(); } -}; class qtype { public: diff --git a/swig/segment.i b/swig/segment.i index d543129..f6818b5 100644 --- a/swig/segment.i +++ b/swig/segment.i @@ -19,6 +19,43 @@ %ignore is_ephemeral_segm; %ignore correct_address; +%{ +void segment_t_startEA_set(segment_t *segm, ea_t newea) +{ + if ( getseg(segm->startEA) == segm ) + { + PyErr_SetString(PyExc_AttributeError, "Can't modify startEA, please use set_segm_start() instead"); + } + else + { + segm->startEA = newea; + } +} +ea_t segment_t_startEA_get(segment_t *segm) +{ + return segm->startEA; +} +void segment_t_endEA_set(segment_t *segm, ea_t newea) +{ + if ( getseg(segm->startEA) == segm ) + { + PyErr_SetString(PyExc_AttributeError, "Can't modify endEA, please use set_segm_end() instead"); + } + else + { + segm->endEA = newea; + } +} +ea_t segment_t_endEA_get(segment_t *segm) +{ + return segm->endEA; +} +%} +%extend segment_t +{ + ea_t startEA; + ea_t endEA; +} %include "segment.hpp" %inline %{ diff --git a/swig/srarea.i b/swig/srarea.i index 5dbf875..440c43c 100644 --- a/swig/srarea.i +++ b/swig/srarea.i @@ -1,14 +1,14 @@ // Ignore kernel-only symbols -%ignore createSRarea; -%ignore killSRareas; -%ignore delSRarea; -%ignore splitSRarea; -%ignore SRareaStart; -%ignore SRareaEnd; +%ignore create_srarea; +%ignore kill_srareras; +%ignore del_srarea; +%ignore break_srarea; +%ignore set_srarea_start; +%ignore set_srarea_end; %ignore repairSRarea; -%ignore SRinit; -%ignore SRterm; -%ignore SRsave; +%ignore init_srarea; +%ignore term_srarea; +%ignore save_srarea; #define R_es 29 #define R_cs 30 diff --git a/swig/typeconv.i b/swig/typeconv.i index 7193337..20bb647 100644 --- a/swig/typeconv.i +++ b/swig/typeconv.i @@ -44,6 +44,7 @@ } %typemap(argout) (TYPEMAP,SIZE) { + Py_XDECREF(resultobj); if (result > 0) { resultobj = PyString_FromString($1); @@ -91,6 +92,7 @@ /* REMOVING ssize_t return value in $symname */ } %typemap(argout) (TYPEMAP,SIZE) { + Py_XDECREF(resultobj); if (result > 0) { resultobj = PyString_FromStringAndSize((char *)$1, result); @@ -117,6 +119,7 @@ /* REMOVING ssize_t return value in $symname */ } %typemap(argout) (TYPEMAP,SIZE) { + Py_XDECREF(resultobj); if (result) { resultobj = PyString_FromStringAndSize((char *)$1, *$2); @@ -151,3 +154,32 @@ } $1 = ea_t($1_temp); } +//------------------------------------------------------------------------- +// Convert qstring +%typemap(in) qstring* +{ + char *buf; + Py_ssize_t length; + int success = PyString_AsStringAndSize($input, &buf, &length); + if ( success > -1 ) + { + $1 = new qstring(buf, length); + } +} +%typemap(freearg) qstring* +{ + delete $1; +} +%typemap(out) qstring* +{ + $result = PyString_FromStringAndSize($1->c_str(), $1->length()); +} +#ifdef __EA64__ +%apply longlong *INOUT { sval_t *value }; +%apply ulonglong *INOUT { ea_t *addr }; +%apply ulonglong *INOUT { sel_t *sel }; +#else +%apply int *INOUT { sval_t *value }; +%apply unsigned int *INOUT { ea_t *addr }; +%apply unsigned int *INOUT { sel_t *sel }; +#endif diff --git a/swig/typeinf.i b/swig/typeinf.i index b96ce07..a1f2bbc 100644 --- a/swig/typeinf.i +++ b/swig/typeinf.i @@ -49,6 +49,8 @@ %ignore til_add_macro; %ignore til_next_macro; +%ignore parse_subtype; +%ignore calc_type_size; %ignore get_type_size; %ignore get_type_size0; %ignore skip_type; @@ -62,6 +64,7 @@ %ignore print_type; %ignore show_type; %ignore show_plist; +%ignore show_bytes; %ignore skip_function_arg_names; %ignore perform_funcarg_conversion; %ignore get_argloc_info; @@ -85,11 +88,17 @@ %ignore get_named_type_size; %ignore decorate_name; +%ignore decorate_name3; %ignore gen_decorate_name; %ignore calc_bare_name; +%ignore calc_bare_name3; %ignore calc_cpp_name; %ignore calc_c_cpp_name; +%ignore calc_c_cpp_name3; %ignore predicate_t; +%ignore local_predicate_t; +%ignore tinfo_predicate_t; +%ignore local_tinfo_predicate_t; %ignore choose_named_type; %ignore get_default_align; %ignore align_size; @@ -123,6 +132,8 @@ %rename (apply_type_to_stkarg) py_apply_type_to_stkarg; %ignore print_type; %rename (print_type) py_print_type; +%rename (calc_type_size) py_calc_type_size; +%rename (apply_type) py_apply_type; %ignore use_regarg_type_cb; %ignore set_op_type_t; @@ -143,29 +154,17 @@ %ignore extend_sign; // Kernel-only symbols -%ignore init_til; -%ignore save_til; -%ignore term_til; -%ignore determine_til; -%ignore sync_from_til; -%ignore get_tilpath; -%ignore autoload_til; -%ignore get_idainfo_by_type; -%ignore apply_callee_type; -%ignore propagate_stkargs; %ignore build_anon_type_name; -%ignore type_names; -%ignore get_compiler_id; -%ignore reloc_info_t; -%ignore relobj_t; -%ignore regobj_t; -%ignore build_func_type; - -%ignore append_type_name; -%ignore for_all_types_ex; -%ignore fix_idb_type; -%ignore pdb2ti; -%ignore process_sdacl_padding; +%ignore enum_type_data_t::is_signed; +%ignore enum_type_data_t::is_unsigned; +%ignore enum_type_data_t::get_sign; +%ignore bitfield_type_data_t::serialize; +%ignore func_type_data_t::serialize; +%ignore func_type_data_t::deserialize; +%ignore enum_type_data_t::get_enum_base_type; +%ignore enum_type_data_t::deserialize_enum; +%ignore valstr_deprecated_t; +%ignore valinfo_deprecated_t; %include "typeinf.hpp" @@ -174,6 +173,7 @@ %rename (load_til) load_til_wrap; %rename (get_type_size0) py_get_type_size0; %rename (idc_get_type_raw) py_idc_get_type_raw; +%rename (idc_get_local_type_raw) py_idc_get_local_type_raw; %rename (unpack_object_from_idb) py_unpack_object_from_idb; %rename (unpack_object_from_bv) py_unpack_object_from_bv; %rename (pack_object_to_idb) py_pack_object_to_idb; @@ -183,22 +183,26 @@ //------------------------------------------------------------------------- PyObject *idc_parse_decl(til_t *ti, const char *decl, int flags) { - qtype fields, type; + tinfo_t tif; qstring name; - bool ok = parse_decl(ti, decl, &name, &type, &fields, flags); - if ( !ok ) - Py_RETURN_NONE; + qtype fields, type; + bool ok = parse_decl2(ti, decl, &name, &tif, flags); + if ( ok ) + ok = tif.serialize(&type, &fields, NULL, SUDT_FAST); - return Py_BuildValue("(sss)", - name.c_str(), - (char *)type.c_str(), - (char *)fields.c_str()); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("(sss)", + name.c_str(), + (char *)type.c_str(), + (char *)fields.c_str()); + Py_RETURN_NONE; } //------------------------------------------------------------------------- /* # -def get_type_size0(ti, tp): +def calc_type_size(ti, tp): """ Returns the size of a type @param ti: Type info. 'idaapi.cvar.idati' can be passed. @@ -210,25 +214,69 @@ def get_type_size0(ti, tp): pass # */ -PyObject *py_get_type_size0(const til_t *ti, PyObject *tp) +PyObject *py_calc_type_size(const til_t *ti, PyObject *tp) { - if ( !PyString_Check(tp) ) + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( PyString_Check(tp) ) + { + // To avoid release of 'data' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t tpref(tp); + const type_t *data = (type_t *)PyString_AsString(tp); + size_t sz; + Py_BEGIN_ALLOW_THREADS; + tinfo_t tif; + tif.deserialize(ti, &data, NULL, NULL); + sz = tif.get_size(); + Py_END_ALLOW_THREADS; + if ( sz != BADSIZE ) + return PyInt_FromLong(sz); + Py_RETURN_NONE; + } + else { PyErr_SetString(PyExc_ValueError, "String expected!"); return NULL; } - - size_t sz = get_type_size0(ti, (type_t *)PyString_AsString(tp)); - if ( sz == BADSIZE ) - Py_RETURN_NONE; - - return PyInt_FromLong(sz); } //------------------------------------------------------------------------- /* # -def print_type(ea, on_line): +def apply_type(ti, ea, tp_name, py_type, py_fields, flags) + """ + Apply the specified type to the address + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param py_type: type string + @param py_fields: type fields + @param ea: the address of the object + @param flags: combination of TINFO_... constants or 0 + @return: Boolean + """ + pass +# +*/ +static bool py_apply_type(til_t *ti, PyObject *py_type, PyObject *py_fields, ea_t ea, int flags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + const type_t *type = (const type_t *) PyString_AsString(py_type); + const p_list *fields = (const p_list *) PyString_AsString(py_fields); + bool rc; + Py_BEGIN_ALLOW_THREADS; + tinfo_t tif; + rc = tif.deserialize(ti, &type, &fields, NULL) && apply_tinfo2(ea, tif, flags); + Py_END_ALLOW_THREADS; + return rc; +} + +//------------------------------------------------------------------------- +/* +# +def print_type(ea, one_line): """ Returns the type of an item @return: @@ -240,14 +288,13 @@ def print_type(ea, on_line): */ static PyObject *py_print_type(ea_t ea, bool one_line) { - char buf[MAXSTR]; - if ( print_type2(ea, buf, sizeof(buf), one_line ? PRTYPE_1LINE : PRTYPE_MULTI) ) - { - qstrncat(buf, ";", sizeof(buf)); + char buf[64*MAXSTR]; + int flags = PRTYPE_SEMI | (one_line ? PRTYPE_1LINE : PRTYPE_MULTI); + bool ok = print_type2(ea, buf, sizeof(buf), one_line ? PRTYPE_1LINE : PRTYPE_MULTI); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) return PyString_FromString(buf); - } - else - Py_RETURN_NONE; + Py_RETURN_NONE; } //------------------------------------------------------------------------- @@ -268,17 +315,24 @@ PyObject *py_unpack_object_from_idb( ea_t ea, int pio_flags = 0) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) { PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); return NULL; } + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + // Unpack type_t *type = (type_t *) PyString_AsString(py_type); p_list *fields = (p_list *) PyString_AsString(py_fields); idc_value_t idc_obj; - error_t err = unpack_object_from_idb( + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = unpack_object_from_idb( &idc_obj, ti, type, @@ -286,22 +340,21 @@ PyObject *py_unpack_object_from_idb( ea, NULL, pio_flags); + Py_END_ALLOW_THREADS; // Unpacking failed? if ( err != eOk ) return Py_BuildValue("(ii)", 0, err); // Convert - PyObject *py_ret(NULL); + ref_t py_ret; err = idcvar_to_pyvar(idc_obj, &py_ret); // Conversion failed? if ( err != CIP_OK ) return Py_BuildValue("(ii)", 0, err); - - PyObject *py_result = Py_BuildValue("(iO)", 1, py_ret); - Py_DECREF(py_ret); - return py_result; + else + return Py_BuildValue("(iO)", 1, py_ret.o); } //------------------------------------------------------------------------- @@ -330,12 +383,17 @@ PyObject *py_unpack_object_from_bv( PyObject *py_bytes, int pio_flags = 0) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyString_Check(py_type) && !PyString_Check(py_fields) && !PyString_Check(py_bytes) ) { PyErr_SetString(PyExc_ValueError, "Incorrect argument type!"); return NULL; } + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + // Get type strings type_t *type = (type_t *) PyString_AsString(py_type); p_list *fields = (p_list *) PyString_AsString(py_fields); @@ -346,29 +404,30 @@ PyObject *py_unpack_object_from_bv( memcpy(bytes.begin(), PyString_AsString(py_bytes), bytes.size()); idc_value_t idc_obj; - error_t err = unpack_object_from_bv( + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = unpack_object_from_bv( &idc_obj, ti, type, fields, bytes, pio_flags); + Py_END_ALLOW_THREADS; // Unpacking failed? if ( err != eOk ) return Py_BuildValue("(ii)", 0, err); // Convert - PyObject *py_ret(NULL); + ref_t py_ret; err = idcvar_to_pyvar(idc_obj, &py_ret); // Conversion failed? if ( err != CIP_OK ) return Py_BuildValue("(ii)", 0, err); - PyObject *py_result = Py_BuildValue("(iO)", 1, py_ret); - Py_DECREF(py_ret); - return py_result; + return Py_BuildValue("(iO)", 1, py_ret.o); } //------------------------------------------------------------------------- @@ -396,6 +455,7 @@ PyObject *py_pack_object_to_idb( ea_t ea, int pio_flags = 0) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) { PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); @@ -404,15 +464,24 @@ PyObject *py_pack_object_to_idb( // Convert Python object to IDC object idc_value_t idc_obj; - if ( !convert_pyobj_to_idc_exc(py_obj, &idc_obj) ) + borref_t py_obj_ref(py_obj); + if ( !pyvar_to_idcvar_or_error(py_obj_ref, &idc_obj) ) return NULL; + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + // Get type strings - type_t *type = (type_t *) PyString_AsString(py_type); - p_list *fields = (p_list *) PyString_AsString(py_fields); + type_t *type = (type_t *)PyString_AsString(py_type); + p_list *fields = (p_list *)PyString_AsString(py_fields); // Pack - error_t err = pack_object_to_idb(&idc_obj, ti, type, fields, ea, pio_flags); + // error_t err; + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = pack_object_to_idb(&idc_obj, ti, type, fields, ea, pio_flags); + Py_END_ALLOW_THREADS; return PyInt_FromLong(err); } @@ -443,6 +512,7 @@ PyObject *py_pack_object_to_bv( ea_t base_ea, int pio_flags=0) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) { PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); @@ -451,16 +521,23 @@ PyObject *py_pack_object_to_bv( // Convert Python object to IDC object idc_value_t idc_obj; - if ( !convert_pyobj_to_idc_exc(py_obj, &idc_obj) ) + borref_t py_obj_ref(py_obj); + if ( !pyvar_to_idcvar_or_error(py_obj_ref, &idc_obj) ) return NULL; + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + // Get type strings - type_t *type = (type_t *) PyString_AsString(py_type); - p_list *fields = (p_list *) PyString_AsString(py_fields); + type_t *type = (type_t *)PyString_AsString(py_type); + p_list *fields = (p_list *)PyString_AsString(py_fields); // Pack relobj_t bytes; - error_t err = pack_object_to_bv( + error_t err; + Py_BEGIN_ALLOW_THREADS; + err = pack_object_to_bv( &idc_obj, ti, type, @@ -468,174 +545,212 @@ PyObject *py_pack_object_to_bv( &bytes, NULL, pio_flags); - do - { - if ( err != eOk ) - break; - if ( !bytes.relocate(base_ea, inf.mf) ) - { + if ( err == eOk && !bytes.relocate(base_ea, inf.mf) ) err = -1; - break; - } + Py_END_ALLOW_THREADS; + if ( err == eOk ) return Py_BuildValue("(is#)", 1, bytes.begin(), bytes.size()); - } while ( false ); - return Py_BuildValue("(ii)", 0, err); + else + return Py_BuildValue("(ii)", 0, err); } -// -til_t * load_til(const char *tildir, const char *name) + +//------------------------------------------------------------------------- +/* Parse types from a string or file. See ParseTypes() in idc.py */ +int idc_parse_types(const char *input, int flags) { - char errbuf[4096]; - til_t *res; + int hti = ((flags >> 4) & 7) << HTI_PAK_SHIFT; - res = load_til(tildir, name, errbuf, sizeof(errbuf)); + if ((flags & 1) != 0) + hti |= HTI_FIL; - if (!res) + return parse_decls(idati, input, (flags & 2) == 0 ? msg : NULL, hti); +} + +//------------------------------------------------------------------------- +PyObject *py_idc_get_type_raw(ea_t ea) +{ + qtype type, fields; + bool ok = get_tinfo(ea, &type, &fields); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("(ss)", (char *)type.c_str(), (char *)fields.c_str()); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +PyObject *py_idc_get_local_type_raw(int ordinal) +{ + const type_t *type; + const p_list *fields; + bool ok = get_numbered_type(idati, ordinal, &type, &fields); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) + return Py_BuildValue("(ss)", (char *)type, (char *)fields); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +char *idc_guess_type(ea_t ea, char *buf, size_t bufsize) +{ + tinfo_t tif; + if ( guess_tinfo2(ea, &tif) ) + { + qstring out; + if ( tif.print(&out) ) + return qstrncpy(buf, out.begin(), bufsize); + } + return NULL; +} + +//------------------------------------------------------------------------- +char *idc_get_type(ea_t ea, char *buf, size_t bufsize) +{ + tinfo_t tif; + if ( get_tinfo2(ea, &tif) ) + { + qstring out; + if ( tif.print(&out) ) { - PyErr_SetString(PyExc_RuntimeError, errbuf); - return NULL; + qstrncpy(buf, out.c_str(), bufsize); + return buf; + } + } + return NULL; +} + +//------------------------------------------------------------------------- +int idc_set_local_type(int ordinal, const char *dcl, int flags) +{ + if (dcl == NULL || dcl[0] == '\0') + { + if ( !del_numbered_type(idati, ordinal) ) + return 0; + } + else + { + tinfo_t tif; + qstring name; + if ( !parse_decl2(idati, dcl, &name, &tif, flags) ) + return 0; + + if ( ordinal <= 0 ) + { + if ( !name.empty() ) + ordinal = get_type_ordinal(idati, name.begin()); + + if ( ordinal <= 0 ) + ordinal = alloc_type_ordinal(idati); } - return res; + if ( tif.set_numbered_type(idati, ordinal, 0, name.c_str()) != TERR_OK ) + return 0; + } + return ordinal; +} + +//------------------------------------------------------------------------- +int idc_get_local_type(int ordinal, int flags, char *buf, size_t maxsize) +{ + tinfo_t tif; + if ( !tif.get_numbered_type(idati, ordinal) ) + { + buf[0] = 0; + return false; + } + + qstring res; + const char *name = get_numbered_type_name(idati, ordinal); + if ( !tif.print(&res, name, flags, 2, 40) ) + { + buf[0] = 0; + return false; + } + + qstrncpy(buf, res.begin(), maxsize); + return true; +} + +//------------------------------------------------------------------------- +PyObject *idc_print_type(PyObject *py_type, PyObject *py_fields, const char *name, int flags) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // To avoid release of 'type'/'fields' during Py_BEGIN|END_ALLOW_THREADS section. + borref_t py_type_ref(py_type); + borref_t py_fields_ref(py_fields); + + qstring res; + const type_t *type = (type_t *)PyString_AsString(py_type); + const p_list *fields = (p_list *)PyString_AsString(py_fields); + bool ok; + Py_BEGIN_ALLOW_THREADS; + tinfo_t tif; + ok = tif.deserialize(idati, &type, &fields, NULL) + && tif.print(&res, name, flags, 2, 40); + Py_END_ALLOW_THREADS; + if ( ok ) + return PyString_FromString(res.begin()); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +char idc_get_local_type_name(int ordinal, char *buf, size_t bufsize) +{ + const char *name = get_numbered_type_name(idati, ordinal); + if ( name == NULL ) + return false; + + qstrncpy(buf, name, bufsize); + return true; +} + +// +til_t *load_til(const char *tildir, const char *name) +{ + char errbuf[MAXSTR]; + til_t *res = load_til(tildir, name, errbuf, sizeof(errbuf)); + if ( res == NULL ) + PyErr_SetString(PyExc_RuntimeError, errbuf); + return res; } %} %rename (load_til_header) load_til_header_wrap; %inline %{ -til_t * load_til_header_wrap(const char *tildir, const char *name) +til_t *load_til_header_wrap(const char *tildir, const char *name) { - char errbuf[4096]; - til_t *res; - - res = load_til_header(tildir, name, errbuf, sizeof(errbuf));; - - if (!res) - { - PyErr_SetString(PyExc_RuntimeError, errbuf); - return NULL; - } - - return res; + char errbuf[MAXSTR]; + til_t *res = load_til_header(tildir, name, errbuf, sizeof(errbuf));; + if ( res == NULL ) + PyErr_SetString(PyExc_RuntimeError, errbuf); + return res; } %} %cstring_output_maxsize(char *buf, size_t maxsize); -%inline %{ -/* Parse types from a string or file. See ParseTypes() in idc.py */ -int idc_parse_types(const char *input, int flags) -{ - int hti = ((flags >> 4) & 7) << HTI_PAK_SHIFT; +%pythoncode %{ +# - if ((flags & 1) != 0) - hti |= HTI_FIL; +def get_type_size0(ti, tp): + """ + DEPRECATED. Please use calc_type_size instead + Returns the size of a type + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @return: + - None on failure + - The size of the type + """ + return calc_type_size(ti, tp) - return parse_decls(idati, input, (flags & 2) == 0 ? msg : NULL, hti); -} +# -PyObject *py_idc_get_type_raw(ea_t ea) -{ - qtype type, fields; - if (get_tinfo(ea, &type, &fields)) - { - return Py_BuildValue("(ss)", (char *)type.c_str(), (char *)fields.c_str()); - } - else - { - Py_RETURN_NONE; - } -} - -char *idc_get_type(ea_t ea, char *buf, size_t bufsize) -{ - qtype type, fnames; - - if (get_tinfo(ea, &type, &fnames)) - { - int code = print_type_to_one_line(buf, bufsize, idati, type.c_str(), - NULL, NULL, fnames.c_str()); - if (code == T_NORMAL) - return buf; - } - return NULL; -} - -char *idc_guess_type(ea_t ea, char *buf, size_t bufsize) -{ - qtype type, fnames; - - if (guess_tinfo(ea, &type, &fnames)) - { - int code = print_type_to_one_line(buf, bufsize, idati, type.c_str(), - NULL, NULL, fnames.c_str()); - if (code == T_NORMAL) - return buf; - } - return NULL; -} - -int idc_set_local_type(int ordinal, const char *dcl, int flags) -{ - if (dcl == NULL || dcl[0] == '\0') - { - if (!del_numbered_type(idati, ordinal)) - return 0; - } - else - { - qstring name; - qtype type; - qtype fields; - - if (!parse_decl(idati, dcl, &name, &type, &fields, flags)) - return 0; - - if (ordinal <= 0) - { - if (!name.empty()) - ordinal = get_type_ordinal(idati, name.c_str()); - - if (ordinal <= 0) - ordinal = alloc_type_ordinal(idati); - } - - if (!set_numbered_type(idati, ordinal, 0, name.c_str(), type.c_str(), fields.c_str())) - return 0; - } - return ordinal; -} - -int idc_get_local_type(int ordinal, int flags, char *buf, size_t maxsize) -{ - const type_t *type; - const p_list *fields; - - if (!get_numbered_type(idati, ordinal, &type, &fields)) - { - buf[0] = 0; - return false; - } - - qstring res; - const char *name = get_numbered_type_name(idati, ordinal); - - if (print_type_to_qstring(&res, NULL, 2, 40, flags, idati, type, name, NULL, fields) <= 0) - { - buf[0] = 0; - return false; - } - - qstrncpy(buf, res.c_str(), maxsize); - return true; -} - -char idc_get_local_type_name(int ordinal, char *buf, size_t bufsize) -{ - const char *name = get_numbered_type_name(idati, ordinal); - - if (name == NULL) - return false; - - qstrncpy(buf, name, bufsize); - return true; -} %} diff --git a/swig/ua.i b/swig/ua.i index f5622c2..96ffe05 100644 --- a/swig/ua.i +++ b/swig/ua.i @@ -60,6 +60,8 @@ def init_output_buffer(size = MAXSTR): */ PyObject *py_init_output_buffer(size_t size = MAXSTR) { + PYW_GIL_CHECK_LOCKED_SCOPE(); + // Let Python allocate a writable string buffer for us PyObject *py_str = PyString_FromStringAndSize(NULL, size); if ( py_str == NULL ) @@ -98,6 +100,7 @@ PyObject *py_decode_preceding_insn(ea_t ea) { bool farref; ea_t r = decode_preceding_insn(ea, &farref); + PYW_GIL_CHECK_LOCKED_SCOPE(); return Py_BuildValue("(" PY_FMT64 "i)", pyul_t(r), farref ? 1 : 0); } @@ -142,6 +145,7 @@ def get_stkvar(op, v): */ PyObject *py_get_stkvar(PyObject *py_op, PyObject *py_v) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(py_op); uint64 v; if ( op == NULL || !PyW_GetNumber(py_v, &v) ) @@ -175,6 +179,7 @@ def add_stkvar3(op, v, flags): */ bool py_add_stkvar3(PyObject *py_op, PyObject *py_v, int flags) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(py_op); uint64 v; return ( op == NULL || !PyW_GetNumber(py_v, &v) || !add_stkvar3(*op, sval_t(v), flags)) ? false : true; @@ -217,11 +222,24 @@ bool py_apply_type_to_stkarg( const char *name) { uint64 v; + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(py_op); if ( op == NULL || !PyW_GetNumber(py_uv, &v) || !PyString_Check(py_type)) + { return false; + } else - return apply_type_to_stkarg(*op, uval_t(v), (type_t *) PyString_AsString(py_type), name); + { + const type_t *t = (type_t *) PyString_AsString(py_type); + tinfo_t tif; + tif.deserialize(idati, &t); + borref_t br(py_op); + bool rc; + Py_BEGIN_ALLOW_THREADS; + rc = apply_tinfo_to_stkarg(*op, uval_t(v), tif, name); + Py_END_ALLOW_THREADS; + return rc; + } } //------------------------------------------------------------------------- @@ -238,6 +256,7 @@ def OutImmChar(op, outflags = 0): */ static void py_OutImmChar(PyObject *x) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(x); if ( op != NULL ) OutImmChar(*op); @@ -258,6 +277,7 @@ def ua_stkvar2(op, outflags = 0): */ static bool py_ua_stkvar2(PyObject *x, adiff_t v, int flags) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(x); return op == NULL ? false : ua_stkvar2(*op, v, flags); } @@ -277,6 +297,7 @@ def ua_add_off_drefs(op, type): */ ea_t py_ua_add_off_drefs(PyObject *py_op, dref_t type) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(py_op); return op == NULL ? BADADDR : ua_add_off_drefs(*op, type); } @@ -295,6 +316,7 @@ def ua_add_off_drefs2(op, type, outf): */ ea_t py_ua_add_off_drefs2(PyObject *py_op, dref_t type, int outf) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(py_op); return op == NULL ? BADADDR : ua_add_off_drefs2(*op, type, outf); } @@ -320,6 +342,7 @@ bool py_out_name_expr( ea_t ea, PyObject *py_off) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *op = op_t_get_clink(py_op); uint64 v(0); adiff_t off; @@ -327,13 +350,14 @@ bool py_out_name_expr( off = adiff_t(v); else off = BADADDR; - + return op == NULL ? false : out_name_expr(*op, ea, off); } //------------------------------------------------------------------------- static PyObject *insn_t_get_op_link(PyObject *py_insn_lnk, int i) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( i < 0 || i >= UA_MAXOP || !PyCObject_Check(py_insn_lnk) ) Py_RETURN_NONE; @@ -347,18 +371,21 @@ static PyObject *insn_t_get_op_link(PyObject *py_insn_lnk, int i) //------------------------------------------------------------------------- static PyObject *insn_t_create() { + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyCObject_FromVoidPtr(new insn_t(), NULL); } //------------------------------------------------------------------------- static PyObject *op_t_create() { + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyCObject_FromVoidPtr(new op_t(), NULL); } //------------------------------------------------------------------------- static bool op_t_assign(PyObject *self, PyObject *other) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *lhs = op_t_get_clink(self); op_t *rhs = op_t_get_clink(other); if (lhs == NULL || rhs == NULL) @@ -371,6 +398,7 @@ static bool op_t_assign(PyObject *self, PyObject *other) //------------------------------------------------------------------------- static bool insn_t_assign(PyObject *self, PyObject *other) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *lhs = insn_t_get_clink(self); insn_t *rhs = insn_t_get_clink(other); if (lhs == NULL || rhs == NULL) @@ -383,6 +411,7 @@ static bool insn_t_assign(PyObject *self, PyObject *other) //------------------------------------------------------------------------- static bool op_t_destroy(PyObject *py_obj) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(py_obj) ) return false; @@ -395,6 +424,7 @@ static bool op_t_destroy(PyObject *py_obj) //------------------------------------------------------------------------- static bool insn_t_destroy(PyObject *py_obj) { + PYW_GIL_CHECK_LOCKED_SCOPE(); if ( !PyCObject_Check(py_obj) ) return false; @@ -406,13 +436,16 @@ static bool insn_t_destroy(PyObject *py_obj) // Returns a C link to the global 'cmd' variable static PyObject *py_get_global_cmd_link() { + PYW_GIL_CHECK_LOCKED_SCOPE(); return PyCObject_FromVoidPtr(&::cmd, NULL); } //------------------------------------------------------------------------- static PyObject *insn_t_is_canon_insn(int itype) { - if ( ph.is_canon_insn(itype) ) + bool ok = ph.is_canon_insn(itype); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) Py_RETURN_TRUE; else Py_RETURN_FALSE; @@ -421,13 +454,17 @@ static PyObject *insn_t_is_canon_insn(int itype) //------------------------------------------------------------------------- static PyObject *insn_t_get_canon_feature(int itype) { - return Py_BuildValue("I", ph.is_canon_insn(itype) ? ph.instruc[itype-ph.instruc_start].feature : 0); + uint32 v = ph.is_canon_insn(itype) ? ph.instruc[itype-ph.instruc_start].feature : 0; + PYW_GIL_CHECK_LOCKED_SCOPE(); + return Py_BuildValue("I", v); } //------------------------------------------------------------------------- static PyObject *insn_t_get_canon_mnem(int itype) { - if ( ph.is_canon_insn(itype) ) + bool ok = ph.is_canon_insn(itype); + PYW_GIL_CHECK_LOCKED_SCOPE(); + if ( ok ) return Py_BuildValue("s", ph.instruc[itype-ph.instruc_start].name); else Py_RETURN_NONE; @@ -436,6 +473,7 @@ static PyObject *insn_t_get_canon_mnem(int itype) //------------------------------------------------------------------------- static PyObject *insn_t_get_cs(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -444,10 +482,11 @@ static PyObject *insn_t_get_cs(PyObject *self) static void insn_t_set_cs(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; - + uint64 v(0); PyW_GetNumber(value, &v); link->cs = ea_t(v); @@ -455,6 +494,7 @@ static void insn_t_set_cs(PyObject *self, PyObject *value) static PyObject *insn_t_get_ip(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -463,6 +503,7 @@ static PyObject *insn_t_get_ip(PyObject *self) static void insn_t_set_ip(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -473,6 +514,7 @@ static void insn_t_set_ip(PyObject *self, PyObject *value) static PyObject *insn_t_get_ea(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -481,6 +523,7 @@ static PyObject *insn_t_get_ea(PyObject *self) static void insn_t_set_ea(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -491,6 +534,7 @@ static void insn_t_set_ea(PyObject *self, PyObject *value) static PyObject *insn_t_get_itype(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -499,6 +543,7 @@ static PyObject *insn_t_get_itype(PyObject *self) static void insn_t_set_itype(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -507,6 +552,7 @@ static void insn_t_set_itype(PyObject *self, PyObject *value) static PyObject *insn_t_get_size(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -515,6 +561,7 @@ static PyObject *insn_t_get_size(PyObject *self) static void insn_t_set_size(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -523,6 +570,7 @@ static void insn_t_set_size(PyObject *self, PyObject *value) static PyObject *insn_t_get_auxpref(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -531,6 +579,7 @@ static PyObject *insn_t_get_auxpref(PyObject *self) static void insn_t_set_auxpref(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -539,6 +588,7 @@ static void insn_t_set_auxpref(PyObject *self, PyObject *value) static PyObject *insn_t_get_segpref(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -547,6 +597,7 @@ static PyObject *insn_t_get_segpref(PyObject *self) static void insn_t_set_segpref(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -555,6 +606,7 @@ static void insn_t_set_segpref(PyObject *self, PyObject *value) static PyObject *insn_t_get_insnpref(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -563,6 +615,7 @@ static PyObject *insn_t_get_insnpref(PyObject *self) static void insn_t_set_insnpref(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -571,6 +624,7 @@ static void insn_t_set_insnpref(PyObject *self, PyObject *value) static PyObject *insn_t_get_flags(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -579,6 +633,7 @@ static PyObject *insn_t_get_flags(PyObject *self) static void insn_t_set_flags(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); insn_t *link = insn_t_get_clink(self); if ( link == NULL ) return; @@ -588,6 +643,7 @@ static void insn_t_set_flags(PyObject *self, PyObject *value) //------------------------------------------------------------------------- static PyObject *op_t_get_n(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -604,6 +660,7 @@ static void op_t_set_n(PyObject *self, PyObject *value) static PyObject *op_t_get_type(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -612,6 +669,7 @@ static PyObject *op_t_get_type(PyObject *self) static void op_t_set_type(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -620,6 +678,7 @@ static void op_t_set_type(PyObject *self, PyObject *value) static PyObject *op_t_get_offb(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -628,6 +687,7 @@ static PyObject *op_t_get_offb(PyObject *self) static void op_t_set_offb(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -636,6 +696,7 @@ static void op_t_set_offb(PyObject *self, PyObject *value) static PyObject *op_t_get_offo(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -644,6 +705,7 @@ static PyObject *op_t_get_offo(PyObject *self) static void op_t_set_offo(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -652,6 +714,7 @@ static void op_t_set_offo(PyObject *self, PyObject *value) static PyObject *op_t_get_flags(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -660,6 +723,7 @@ static PyObject *op_t_get_flags(PyObject *self) static void op_t_set_flags(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -668,6 +732,7 @@ static void op_t_set_flags(PyObject *self, PyObject *value) static PyObject *op_t_get_dtyp(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -676,6 +741,7 @@ static PyObject *op_t_get_dtyp(PyObject *self) static void op_t_set_dtyp(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -684,6 +750,7 @@ static void op_t_set_dtyp(PyObject *self, PyObject *value) static PyObject *op_t_get_reg_phrase(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -691,6 +758,7 @@ static PyObject *op_t_get_reg_phrase(PyObject *self) } static void op_t_set_reg_phrase(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -699,6 +767,7 @@ static void op_t_set_reg_phrase(PyObject *self, PyObject *value) static PyObject *op_t_get_value(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -707,6 +776,7 @@ static PyObject *op_t_get_value(PyObject *self) static void op_t_set_value(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -715,6 +785,7 @@ static void op_t_set_value(PyObject *self, PyObject *value) static PyObject *op_t_get_addr(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -723,6 +794,7 @@ static PyObject *op_t_get_addr(PyObject *self) static void op_t_set_addr(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -733,6 +805,7 @@ static void op_t_set_addr(PyObject *self, PyObject *value) static PyObject *op_t_get_specval(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -741,6 +814,7 @@ static PyObject *op_t_get_specval(PyObject *self) static void op_t_set_specval(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -751,6 +825,7 @@ static void op_t_set_specval(PyObject *self, PyObject *value) static PyObject *op_t_get_specflag1(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -759,6 +834,7 @@ static PyObject *op_t_get_specflag1(PyObject *self) static void op_t_set_specflag1(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -767,6 +843,7 @@ static void op_t_set_specflag1(PyObject *self, PyObject *value) static PyObject *op_t_get_specflag2(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -775,6 +852,7 @@ static PyObject *op_t_get_specflag2(PyObject *self) static void op_t_set_specflag2(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -783,6 +861,7 @@ static void op_t_set_specflag2(PyObject *self, PyObject *value) static PyObject *op_t_get_specflag3(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -791,6 +870,7 @@ static PyObject *op_t_get_specflag3(PyObject *self) static void op_t_set_specflag3(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; @@ -799,6 +879,7 @@ static void op_t_set_specflag3(PyObject *self, PyObject *value) static PyObject *op_t_get_specflag4(PyObject *self) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) Py_RETURN_NONE; @@ -807,6 +888,7 @@ static PyObject *op_t_get_specflag4(PyObject *self) static void op_t_set_specflag4(PyObject *self, PyObject *value) { + PYW_GIL_CHECK_LOCKED_SCOPE(); op_t *link = op_t_get_clink(self); if ( link == NULL ) return; diff --git a/swig/view.i b/swig/view.i new file mode 100644 index 0000000..56b5d67 --- /dev/null +++ b/swig/view.i @@ -0,0 +1,1008 @@ + +%{ +// + +//#define PYGDBG_ENABLED +#ifdef PYGDBG_ENABLED +#define PYGLOG(...) msg(__VA_ARGS__) +#else +#define PYGLOG(...) +#endif + +//------------------------------------------------------------------------- +class py_customidamemo_t; +class lookup_info_t +{ +public: + void add(TForm *form, TCustomControl *view, py_customidamemo_t *py_view) + { + QASSERT(0, form != NULL && view != NULL && py_view != NULL + && !find_by_form(NULL, NULL, form) + && !find_by_view(NULL, NULL, view) + && !find_by_py_view(NULL, NULL, py_view)); + entry_t &e = entries.push_back(); + e.form = form; + e.view = view; + e.py_view = py_view; + } + +#define FIND_BY__BODY(crit, res1, res2) \ + { \ + for ( entries_t::const_iterator it = entries.begin(); it != entries.end(); ++it ) \ + { \ + const entry_t &e = *it; \ + if ( e.crit == crit ) \ + { \ + if ( out_##res1 != NULL ) \ + *out_##res1 = e.res1; \ + if ( out_##res2 != NULL ) \ + *out_##res2 = e.res2; \ + return true; \ + } \ + } \ + return false; \ + } + bool find_by_form(TCustomControl **out_view, py_customidamemo_t **out_py_view, const TForm *form) const FIND_BY__BODY(form, view, py_view); + bool find_by_view(TForm **out_form, py_customidamemo_t **out_py_view, const TCustomControl *view) const FIND_BY__BODY(view, form, py_view); + bool find_by_py_view(TForm **out_form, TCustomControl **out_view, const py_customidamemo_t *py_view) const FIND_BY__BODY(py_view, view, form); +#undef FIND_BY__BODY + + bool del_by_py_view(const py_customidamemo_t *py_view) + { + for ( entries_t::iterator it = entries.begin(); it != entries.end(); ++it ) + { + if ( it->py_view == py_view ) + { + entries.erase(it); + return true; + } + } + return false; + } + +private: + struct entry_t + { + TForm *form; + TCustomControl *view; + py_customidamemo_t *py_view; + }; + typedef qvector entries_t; + entries_t entries; +}; + +//------------------------------------------------------------------------- +template +T *view_extract_this(PyObject *self) +{ + PYW_GIL_CHECK_LOCKED_SCOPE(); + ref_t py_this(PyW_TryGetAttrString(self, S_M_THIS)); + if ( py_this == NULL || !PyCObject_Check(py_this.o) ) + return NULL; + return (T*) PyCObject_AsVoidPtr(py_this.o); +} + +//------------------------------------------------------------------------- +class py_customidamemo_t +{ + void convert_node_info( + node_info_t *out, + uint32 *out_flags, + ref_t py_nodeinfo) + { + if ( out_flags != NULL ) + *out_flags = 0; +#define COPY_PROP(checker, converter, pname, flag) \ + do \ + { \ + newref_t pname(PyObject_GetAttrString(py_nodeinfo.o, #pname)); \ + if ( pname != NULL && checker(pname.o) ) \ + { \ + out->pname = converter(pname.o); \ + if ( out_flags != NULL ) \ + *out_flags |= flag; \ + } \ + } while ( false ) +#define COPY_ULONG_PROP(pname, flag) COPY_PROP(PyNumber_Check, PyLong_AsUnsignedLong, pname, flag) +#define COPY_STRING_PROP(pname, flag) COPY_PROP(PyString_Check, PyString_AsString, pname, flag) + COPY_ULONG_PROP(bg_color, NIF_BG_COLOR); + COPY_ULONG_PROP(frame_color, NIF_FRAME_COLOR); + COPY_ULONG_PROP(ea, NIF_EA); + COPY_STRING_PROP(text, NIF_TEXT); +#undef COPY_STRING_PROP +#undef COPY_ULONG_PROP +#undef COPY_PROP + } + + enum + { + GRBASE_HAVE_VIEW_ACTIVATED = 0x001, + GRBASE_HAVE_VIEW_DEACTIVATED = 0x002, + GRBASE_HAVE_KEYDOWN = 0x004, + GRBASE_HAVE_POPUP = 0x008, + GRBASE_HAVE_VIEW_CLICK = 0x010, + GRBASE_HAVE_VIEW_DBLCLICK = 0x020, + GRBASE_HAVE_VIEW_CURPOS = 0x040, + GRBASE_HAVE_CLOSE = 0x080, + GRBASE_HAVE_VIEW_SWITCHED = 0x100, + GRBASE_HAVE_VIEW_MOUSE_OVER = 0x200, + }; + + static void ensure_view_callbacks_installed(); + int cb_flags; + +protected: + ref_t self; + TCustomControl *view; + // This is called after having modified the + // node properties in the IDB. In case an + // implementation is performing some caching, + // this is a chance to update that cache. + // If 'ni' is NULL, then the node info was deleted. + virtual void node_info_modified( + int /*n*/, + const node_info_t * /*ni*/, + uint32 /*flags*/) {} + + struct callback_id_t + { + qstring name; + int have; + }; + struct callbacks_ids_t : public qvector + { + void add(const char *_n, int _h) + { + callback_id_t &o = push_back(); + o.name = _n; + o.have = _h; + } + }; + callbacks_ids_t cbids; + + bool collect_pyobject_callbacks(PyObject *self); + virtual void collect_class_callbacks_ids(callbacks_ids_t *out); + + // Bi-directionally bind/unbind the Python object and this controller. + bool bind(PyObject *_self, TCustomControl *view); + void unbind(); + + static lookup_info_t lookup_info; + +public: + py_customidamemo_t(); + virtual ~py_customidamemo_t(); + virtual void refresh() + { + refresh_viewer(view); + } + void set_node_info(PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags); + void set_nodes_infos(PyObject *dict); + PyObject *get_node_info(PyObject *py_node_idx); + void del_nodes_infos(PyObject *py_nodes); + PyObject *get_current_renderer_type(); + void set_current_renderer_type(PyObject *py_rto); + PyObject *create_groups(PyObject *groups_infos); + PyObject *delete_groups(PyObject *groups, PyObject *new_current); + PyObject *set_groups_visibility(PyObject *groups, PyObject *expand, PyObject *new_current); + + // View events + void on_view_activated(); + void on_view_deactivated(); + void on_view_keydown(int key, int state); + void on_view_popup(); + void on_view_click(const view_mouse_event_t *event); + void on_view_dblclick(const view_mouse_event_t *event); + void on_view_curpos(); + void on_view_close(); + void on_view_switched(tcc_renderer_type_t rt); + void on_view_mouse_over(const view_mouse_event_t *event); + inline bool has_callback(int flag) { return (cb_flags & flag) != 0; } +}; + +//------------------------------------------------------------------------- +py_customidamemo_t::py_customidamemo_t() + : self(newref_t(NULL)), + view(NULL) +{ + PYGLOG("%p: py_customidamemo_t()\n", this); + ensure_view_callbacks_installed(); +} + +//------------------------------------------------------------------------- +py_customidamemo_t::~py_customidamemo_t() +{ + PYGLOG("%p: ~py_customidamemo_t()\n", this); + unbind(); + lookup_info.del_by_py_view(this); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::ensure_view_callbacks_installed() +{ + static bool installed = false; + if ( !installed ) + { + struct ida_local lambda_t + { + static int idaapi callback(void * /*ud*/, int code, va_list va) + { + py_customidamemo_t *py_view; + if ( lookup_info.find_by_view(NULL, &py_view, va_arg(va, TCustomControl *)) ) + { + PYW_GIL_GET; + switch ( code ) + { + case view_activated: + py_view->on_view_activated(); + break; + case view_deactivated: + py_view->on_view_deactivated(); + break; + case view_keydown: + { + int key = va_arg(va, int); + int state = va_arg(va, int); + py_view->on_view_keydown(key, state); + } + break; + case view_popup: + py_view->on_view_popup(); + break; + case view_click: + case view_dblclick: + { + const view_mouse_event_t *event = va_arg(va, view_mouse_event_t*); + if ( code == view_click ) + py_view->on_view_click(event); + else + py_view->on_view_dblclick(event); + } + break; + case view_curpos: + py_view->on_view_curpos(); + break; + case view_close: + py_view->on_view_close(); + delete py_view; + break; + case view_switched: + { + tcc_renderer_type_t rt = (tcc_renderer_type_t) va_arg(va, int); + py_view->on_view_switched(rt); + } + break; + case view_mouse_over: + { + const view_mouse_event_t *event = va_arg(va, view_mouse_event_t*); + py_view->on_view_mouse_over(event); + } + break; + } + } + return 0; + } + }; + hook_to_notification_point(HT_VIEW, lambda_t::callback, NULL); + installed = true; + } +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::set_node_info( + PyObject *py_node_idx, + PyObject *py_node_info, + PyObject *py_flags) +{ + if ( !PyNumber_Check(py_node_idx) || !PyNumber_Check(py_flags) ) + return; + borref_t py_idx(py_node_idx); + borref_t py_ni(py_node_info); + borref_t py_fl(py_flags); + node_info_t ni; + convert_node_info(&ni, NULL, py_ni); + int idx = PyInt_AsLong(py_idx.o); + uint32 flgs = PyLong_AsLong(py_fl.o); + viewer_set_node_info(view, idx, ni, flgs); + node_info_modified(idx, &ni, flgs); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::set_nodes_infos(PyObject *dict) +{ + if ( !PyDict_Check(dict) ) + return; + Py_ssize_t pos = 0; + PyObject *o_key, *o_value; + while ( PyDict_Next(dict, &pos, &o_key, &o_value) ) + { + borref_t key(o_key); + borref_t value(o_value); + if ( !PyNumber_Check(key.o) ) + continue; + uint32 flags; + node_info_t ni; + convert_node_info(&ni, &flags, value); + int idx = PyInt_AsLong(key.o); + viewer_set_node_info(view, idx, ni, flags); + node_info_modified(idx, &ni, flags); + } +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::get_node_info(PyObject *py_node_idx) +{ + if ( !PyNumber_Check(py_node_idx) ) + Py_RETURN_NONE; + node_info_t ni; + if ( !viewer_get_node_info(view, &ni, PyInt_AsLong(py_node_idx)) ) + Py_RETURN_NONE; + return Py_BuildValue("(kkks)", ni.bg_color, ni.frame_color, ni.ea, ni.text.c_str()); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::del_nodes_infos(PyObject *py_nodes) +{ + if ( !PySequence_Check(py_nodes) ) + return; + Py_ssize_t sz = PySequence_Size(py_nodes); + for ( Py_ssize_t i = 0; i < sz; ++i ) + { + newref_t item(PySequence_GetItem(py_nodes, i)); + if ( !PyNumber_Check(item.o) ) + continue; + int idx = PyInt_AsLong(item.o); + viewer_del_node_info(view, idx); + node_info_modified(idx, NULL, 0); + } +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::get_current_renderer_type() +{ + tcc_renderer_type_t rt = get_view_renderer_type(view); + return PyLong_FromLong(long(rt)); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::set_current_renderer_type(PyObject *py_rto) +{ + tcc_renderer_type_t rt = TCCRT_INVALID; + borref_t py_rt(py_rto); + if ( PyNumber_Check(py_rt.o) ) + { + rt = tcc_renderer_type_t(PyLong_AsLong(py_rt.o)); + set_view_renderer_type(view, rt); + } +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::create_groups(PyObject *_groups_infos) +{ + if ( !PySequence_Check(_groups_infos) ) + Py_RETURN_NONE; + borref_t groups_infos(_groups_infos); + groups_crinfos_t gis; + Py_ssize_t sz = PySequence_Size(groups_infos.o); + for ( Py_ssize_t i = 0; i < sz; ++i ) + { + newref_t item(PySequence_GetItem(groups_infos.o, i)); + if ( !PyDict_Check(item.o) ) + continue; + borref_t nodes(PyDict_GetItemString(item.o, "nodes")); + if ( nodes.o == NULL || !PySequence_Check(nodes.o) ) + continue; + borref_t text(PyDict_GetItemString(item.o, "text")); + if ( text.o == NULL || !PyString_Check(text.o) ) + continue; + group_crinfo_t gi; + Py_ssize_t nodes_cnt = PySequence_Size(nodes.o); + for ( Py_ssize_t k = 0; k < nodes_cnt; ++k ) + { + newref_t node(PySequence_GetItem(nodes.o, k)); + if ( PyInt_Check(node.o) ) + gi.nodes.insert(PyInt_AsLong(node.o)); + } + if ( !gi.nodes.empty() ) + { + gi.text = PyString_AsString(text.o); + gis.push_back(gi); + } + } + intset_t groups; + if ( gis.empty() || !viewer_create_groups(view, &groups, gis) || groups.empty() ) + Py_RETURN_NONE; + + PyObject *py_groups = PyList_New(0); + for ( intset_t::const_iterator it = groups.begin(); it != groups.end(); ++it ) + PyList_Append(py_groups, PyInt_FromLong(long(*it))); + return py_groups; +} + +//------------------------------------------------------------------------- +static void pynodes_to_idanodes(intset_t *idanodes, ref_t pynodes) +{ + Py_ssize_t sz = PySequence_Size(pynodes.o); + for ( Py_ssize_t i = 0; i < sz; ++i ) + { + newref_t item(PySequence_GetItem(pynodes.o, i)); + if ( !PyInt_Check(item.o) ) + continue; + idanodes->insert(PyInt_AsLong(item.o)); + } +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::delete_groups(PyObject *_groups, PyObject *_new_current) +{ + if ( !PySequence_Check(_groups) || !PyNumber_Check(_new_current) ) + Py_RETURN_NONE; + borref_t groups(_groups); + borref_t new_current(_new_current); + intset_t ida_groups; + pynodes_to_idanodes(&ida_groups, groups); + if ( ida_groups.empty() ) + Py_RETURN_NONE; + if ( viewer_delete_groups(view, ida_groups, int(PyInt_AsLong(new_current.o))) ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +PyObject *py_customidamemo_t::set_groups_visibility(PyObject *_groups, PyObject *_expand, PyObject *_new_current) +{ + if ( !PySequence_Check(_groups) + || !PyBool_Check(_expand) + || !PyNumber_Check(_new_current) ) + Py_RETURN_NONE; + borref_t groups(_groups); + borref_t expand(_expand); + borref_t new_current(_new_current); + intset_t ida_groups; + pynodes_to_idanodes(&ida_groups, groups); + if ( ida_groups.empty() ) + Py_RETURN_NONE; + if ( viewer_set_groups_visibility(view, ida_groups, expand.o == Py_True, int(PyInt_AsLong(new_current.o))) ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +bool py_customidamemo_t::bind(PyObject *_self, TCustomControl *view) +{ + if ( this->self != NULL || this->view != NULL ) + return false; + PYGLOG("%p: py_customidamemo_t::bind(_self=%p, view=%p)\n", this, _self, view); + PYW_GIL_CHECK_LOCKED_SCOPE(); + + newref_t py_cobj(PyCObject_FromVoidPtr(this, NULL)); + PyObject_SetAttrString(_self, S_M_THIS, py_cobj.o); + + this->self = borref_t(_self); + this->view = view; + return true; +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::unbind() +{ + if ( self == NULL ) + return; + PYGLOG("%p: py_customidamemo_t::unbind(); self.o=%p, view=%p\n", this, self.o, view); + PYW_GIL_CHECK_LOCKED_SCOPE(); + newref_t py_cobj(PyCObject_FromVoidPtr(NULL, NULL)); + PyObject_SetAttrString(self.o, S_M_THIS, py_cobj.o); + self = newref_t(NULL); + view = NULL; +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::collect_class_callbacks_ids(callbacks_ids_t *out) +{ + out->add(S_ON_VIEW_ACTIVATED, GRBASE_HAVE_VIEW_ACTIVATED); + out->add(S_ON_VIEW_DEACTIVATED, GRBASE_HAVE_VIEW_DEACTIVATED); + out->add(S_ON_VIEW_KEYDOWN, GRBASE_HAVE_KEYDOWN); + out->add(S_ON_POPUP, GRBASE_HAVE_POPUP); + out->add(S_ON_VIEW_CLICK, GRBASE_HAVE_VIEW_CLICK); + out->add(S_ON_VIEW_DBLCLICK, GRBASE_HAVE_VIEW_DBLCLICK); + out->add(S_ON_VIEW_CURPOS, GRBASE_HAVE_VIEW_CURPOS); + out->add(S_ON_CLOSE, GRBASE_HAVE_CLOSE); + out->add(S_ON_VIEW_SWITCHED, GRBASE_HAVE_VIEW_SWITCHED); + out->add(S_ON_VIEW_MOUSE_OVER, GRBASE_HAVE_VIEW_MOUSE_OVER); +} + +//------------------------------------------------------------------------- +bool py_customidamemo_t::collect_pyobject_callbacks(PyObject *o) +{ + callbacks_ids_t cbids; + collect_class_callbacks_ids(&cbids); + cb_flags = 0; + for ( callbacks_ids_t::const_iterator it = cbids.begin(); it != cbids.end(); ++it ) + { + const callback_id_t &cbid = *it; + ref_t attr(PyW_TryGetAttrString(o, cbid.name.c_str())); + int have = cbid.have; + // Mandatory fields not present? + if ( (attr == NULL && have <= 0 ) + // Mandatory callback fields present but not callable? + || (attr != NULL && have >= 0 && PyCallable_Check(attr.o) == 0)) + { + return false; + } + if ( have > 0 && attr != NULL ) + cb_flags |= have; + } + return true; +} + + +//------------------------------------------------------------------------- +#define CHK_EVT(flag_needed) \ + if ( self == NULL || !has_callback(flag_needed) ) \ + return; \ + PYW_GIL_CHECK_LOCKED_SCOPE() + +#ifdef PYGDBG_ENABLED +#define CHK_RES() PYGLOG("%s: return code: %p\n", __FUNCTION__, result.o) +#else +#define CHK_RES() +#endif + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_activated() +{ + CHK_EVT(GRBASE_HAVE_VIEW_ACTIVATED); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_ACTIVATED, + NULL)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_deactivated() +{ + CHK_EVT(GRBASE_HAVE_VIEW_DEACTIVATED); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_DEACTIVATED, + NULL)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_keydown(int key, int state) +{ + CHK_EVT(GRBASE_HAVE_KEYDOWN); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_KEYDOWN, + "ii", + key, state)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_popup() +{ + CHK_EVT(GRBASE_HAVE_POPUP); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_POPUP, + NULL)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_click(const view_mouse_event_t *event) +{ + CHK_EVT(GRBASE_HAVE_VIEW_CLICK); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_CLICK, + "iii", + event->x, event->y, event->state)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_dblclick(const view_mouse_event_t *event) +{ + CHK_EVT(GRBASE_HAVE_VIEW_DBLCLICK); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_DBLCLICK, + "iii", + event->x, event->y, event->state)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_curpos() +{ + CHK_EVT(GRBASE_HAVE_VIEW_CURPOS); + newref_t result( + PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_CURPOS, + NULL)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_close() +{ + CHK_EVT(GRBASE_HAVE_CLOSE); + newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_CLOSE, NULL)); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_switched(tcc_renderer_type_t rt) +{ + CHK_EVT(GRBASE_HAVE_VIEW_SWITCHED); + newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_VIEW_SWITCHED, "i", int(rt))); + CHK_RES(); +} + +//------------------------------------------------------------------------- +void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event) +{ + CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_OVER); + if ( event->rtype == TCCRT_GRAPH || event->rtype == TCCRT_PROXIMITY ) + { + const selection_item_t *item = event->location.item; + int icode; + ref_t tuple; + if ( item != NULL ) + { + if ( item->is_node ) + { + icode = 1; + tuple = newref_t(Py_BuildValue("(i)", item->node)); + } + else + { + icode = 2; + tuple = newref_t(Py_BuildValue("(ii)", item->elp.e.src, item->elp.e.dst)); + } + } + else + { + icode = 0; + tuple = newref_t(Py_BuildValue("()")); + } + newref_t result(PyObject_CallMethod( + self.o, + (char *)S_ON_VIEW_MOUSE_OVER, + "iiiiO", + event->x, event->y, event->state, icode, tuple.o)); + CHK_RES(); + } +} + + +#undef CHK_RES +#undef CHK_EVT + +//------------------------------------------------------------------------- +//------------------------------------------------------------------------- + +#define GET_THIS() py_customidamemo_t *_this = view_extract_this(self) +#define CHK_THIS() \ + GET_THIS(); \ + if ( _this == NULL ) \ + return +#define CHK_THIS_OR_NONE() \ + GET_THIS(); \ + if ( _this == NULL ) \ + Py_RETURN_NONE + +//------------------------------------------------------------------------- +void pygc_refresh(PyObject *self) +{ + CHK_THIS(); + _this->refresh(); +} + +//------------------------------------------------------------------------- +void pygc_set_node_info(PyObject *self, PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags) +{ + CHK_THIS(); + _this->set_node_info(py_node_idx, py_node_info, py_flags); +} + +//------------------------------------------------------------------------- +void pygc_set_nodes_infos(PyObject *self, PyObject *values) +{ + CHK_THIS(); + _this->set_nodes_infos(values); +} + +//------------------------------------------------------------------------- +PyObject *pygc_get_node_info(PyObject *self, PyObject *py_node_idx) +{ + GET_THIS(); + if ( _this != NULL ) + return _this->get_node_info(py_node_idx); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +void pygc_del_nodes_infos(PyObject *self, PyObject *py_nodes) +{ + CHK_THIS(); + _this->del_nodes_infos(py_nodes); +} + +//------------------------------------------------------------------------- +PyObject *pygc_get_current_renderer_type(PyObject *self) +{ + GET_THIS(); + if ( _this != NULL ) + return _this->get_current_renderer_type(); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +void pygc_set_current_renderer_type(PyObject *self, PyObject *py_rt) +{ + CHK_THIS(); + _this->set_current_renderer_type(py_rt); +} + +//------------------------------------------------------------------------- +PyObject *pygc_create_groups(PyObject *self, PyObject *groups_infos) +{ + CHK_THIS_OR_NONE(); + return _this->create_groups(groups_infos); +} + +//------------------------------------------------------------------------- +PyObject *pygc_delete_groups(PyObject *self, PyObject *groups, PyObject *new_current) +{ + CHK_THIS_OR_NONE(); + return _this->delete_groups(groups, new_current); +} + +//------------------------------------------------------------------------- +PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject *expand, PyObject *new_current) +{ + CHK_THIS_OR_NONE(); + return _this->set_groups_visibility(groups, expand, new_current); +} + +#undef CHK_THIS_OR_NONE +#undef CHK_THIS +#undef GET_THIS + +//------------------------------------------------------------------------- +lookup_info_t py_customidamemo_t::lookup_info; +// + +%} + +%inline %{ +// +void pygc_refresh(PyObject *self); +void pygc_set_node_info(PyObject *self, PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags); +void pygc_set_nodes_infos(PyObject *self, PyObject *values); +PyObject *pygc_get_node_info(PyObject *self, PyObject *py_node_idx); +void pygc_del_nodes_infos(PyObject *self, PyObject *py_nodes); +PyObject *pygc_get_current_renderer_type(PyObject *self); +void pygc_set_current_renderer_type(PyObject *self, PyObject *py_rt); +PyObject *pygc_create_groups(PyObject *self, PyObject *groups_infos); +PyObject *pygc_delete_groups(PyObject *self, PyObject *groups, PyObject *new_current); +PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject *expand, PyObject *new_current); +// +%} + +%pythoncode %{ +# +class CustomIDAMemo(object): + def Refresh(self): + """ + Refreshes the graph. This causes the OnRefresh() to be called + """ + _idaapi.pygc_refresh(self) + + def GetCurrentRendererType(self): + return _idaapi.pygc_get_current_renderer_type(self) + + def SetCurrentRendererType(self, rtype): + """ + Set the current view's renderer. + + @param rtype: The renderer type. Should be one of the idaapi.TCCRT_* values. + """ + _idaapi.pygc_set_current_renderer_type(self, rtype) + + def SetNodeInfo(self, node_index, node_info, flags): + """ + Set the properties for the given node. + + Example usage (set second nodes's bg color to red): + inst = ... + p = idaapi.node_info_t() + p.bg_color = 0x00ff0000 + inst.SetNodeInfo(1, p, idaapi.NIF_BG_COLOR) + + @param node_index: The node index. + @param node_info: An idaapi.node_info_t instance. + @param flags: An OR'ed value of NIF_* values. + """ + _idaapi.pygc_set_node_info(self, node_index, node_info, flags) + + def SetNodesInfos(self, values): + """ + Set the properties for the given nodes. + + Example usage (set first three nodes's bg color to purple): + inst = ... + p = idaapi.node_info_t() + p.bg_color = 0x00ff00ff + inst.SetNodesInfos({0 : p, 1 : p, 2 : p}) + + @param values: A dictionary of 'int -> node_info_t' objects. + """ + _idaapi.pygc_set_nodes_infos(self, values) + + def GetNodeInfo(self, node): + """ + Get the properties for the given node. + + @param node: The index of the node. + @return: A tuple (bg_color, frame_color, ea, text), or None. + """ + return _idaapi.pygc_get_node_info(self, node) + + def DelNodesInfos(self, *nodes): + """ + Delete the properties for the given node(s). + + @param nodes: A list of node IDs + """ + return _idaapi.pygc_del_nodes_infos(self, nodes) + + def CreateGroups(self, groups_infos): + """ + Send a request to modify the graph by creating a + (set of) group(s), and perform an animation. + + Each object in the 'groups_infos' list must be of the format: + { + "nodes" : [, , , ...] # The list of nodes to group + "text" : # The synthetic text for that group + } + + @param groups_infos: A list of objects that describe those groups. + @return: A [, , ...] list of group nodes, or None (failure). + """ + return _idaapi.pygc_create_groups(self, groups_infos) + + def DeleteGroups(self, groups, new_current = -1): + """ + Send a request to delete the specified groups in the graph, + and perform an animation. + + @param groups: A list of group node numbers. + @param new_current: A node to focus on after the groups have been deleted + @return: True on success, False otherwise. + """ + return _idaapi.pygc_delete_groups(self, groups, new_current) + + def SetGroupsVisibility(self, groups, expand, new_current = -1): + """ + Send a request to expand/collapse the specified groups in the graph, + and perform an animation. + + @param groups: A list of group node numbers. + @param expand: True to expand the group, False otherwise. + @param new_current: A node to focus on after the groups have been expanded/collapsed. + @return: True on success, False otherwise. + """ + return _idaapi.pygc_set_groups_visibility(self, groups, expand, new_current) + +# +%} + +%{ +// +class py_idaview_t : public py_customidamemo_t +{ + typedef py_customidamemo_t inherited; + +public: + static bool Bind(PyObject *self); +}; + +//------------------------------------------------------------------------- +bool py_idaview_t::Bind(PyObject *self) +{ + // Already a py_idaview_t associated to this object? + py_idaview_t *_this = view_extract_this(self); + if ( _this != NULL ) + return false; + + qstring title; + if ( !PyW_GetStringAttr(self, S_M_TITLE, &title) ) + return false; + + // Get the IDAView associated to this TForm + TForm *tform = find_tform(title.c_str()); + if ( tform == NULL ) + return false; + TCustomControl *v = get_tform_idaview(tform); + if ( v == NULL ) + return false; + + // Get unique py_idaview_t associated to that tform + py_idaview_t *py_view; + TCustomControl *found_view; + if ( lookup_info.find_by_form(&found_view, (py_customidamemo_t**) &py_view, tform) ) + { + // If we have a py_idaview_t for that form, ensure it has + // the expected view. + QASSERT(30451, found_view == v); + } + else + { + py_view = new py_idaview_t(); + lookup_info.add(tform, v, py_view); + } + + // Finally, bind: + // py_idaview_t <=> IDAViewWrapper + // py_idaview_t => TCustomControl + bool ok = py_view->bind(self, v); + if ( ok ) + { + ok = py_view->collect_pyobject_callbacks(self); + if ( !ok ) + delete py_view; + } + return ok; +} + +//------------------------------------------------------------------------- +bool pyidag_bind(PyObject *self) +{ + return py_idaview_t::Bind(self); +} + +// +%} + +%inline %{ +// +bool pyidag_bind(PyObject *self); +// +%} + +%pythoncode %{ +# +class IDAViewWrapper(CustomIDAMemo): + """This class wraps access to native IDA views. See kernwin.hpp file""" + def __init__(self, title): + """ + Constructs the IDAViewWrapper object around the view + whose title is 'title'. + + @param title: The title of the existing IDA view. E.g., 'IDA View-A' + """ + self._title = title + + def Bind(self): + return _idaapi.pyidag_bind(self) +# +%}