diff options
Diffstat (limited to 'lldb/bindings/python')
-rw-r--r-- | lldb/bindings/python/createPythonInit.py | 17 | ||||
-rw-r--r-- | lldb/bindings/python/python-extensions.swig | 592 | ||||
-rw-r--r-- | lldb/bindings/python/python-swigsafecast.swig | 154 | ||||
-rw-r--r-- | lldb/bindings/python/python-typemaps.swig | 532 | ||||
-rw-r--r-- | lldb/bindings/python/python-wrapper.swig | 1067 |
5 files changed, 2362 insertions, 0 deletions
diff --git a/lldb/bindings/python/createPythonInit.py b/lldb/bindings/python/createPythonInit.py new file mode 100644 index 000000000000..3deb9159b702 --- /dev/null +++ b/lldb/bindings/python/createPythonInit.py @@ -0,0 +1,17 @@ +import os +import sys + +pkgRelDir = sys.argv[1] +pkgFiles = sys.argv[2:] + +getFileName = lambda f: os.path.splitext(os.path.basename(f))[0] +importNames = ', '.join('"{}"'.format(getFileName(f)) for f in pkgFiles) + +script = """__all__ = [{import_names}] +for x in __all__: + __import__('lldb.{pkg_name}.' + x) +""".format(import_names=importNames, pkg_name=pkgRelDir.replace("/", ".")) + +pkgIniFile = os.path.normpath(os.path.join(pkgRelDir, "__init__.py")) +with open(pkgIniFile, "w") as f: + f.write(script) diff --git a/lldb/bindings/python/python-extensions.swig b/lldb/bindings/python/python-extensions.swig new file mode 100644 index 000000000000..0b23fdd40006 --- /dev/null +++ b/lldb/bindings/python/python-extensions.swig @@ -0,0 +1,592 @@ +%extend lldb::SBBreakpoint { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBBroadcaster { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBCommandReturnObject { + /* the write() and flush() calls are not part of the SB API proper, and are solely for Python usage + they are meant to make an SBCommandReturnObject into a file-like object so that instructions of the sort + print >>sb_command_return_object, "something" + will work correctly */ + + void lldb::SBCommandReturnObject::write (const char* str) + { + if (str) + $self->Printf("%s",str); + } + void lldb::SBCommandReturnObject::flush () + {} +} + +%extend lldb::SBCompileUnit { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBDeclaration { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBFunction { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBLineEntry { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBModule { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBSection { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} +%extend lldb::SBStream { + /* the write() and flush() calls are not part of the SB API proper, and are solely for Python usage + they are meant to make an SBStream into a file-like object so that instructions of the sort + print >>sb_stream, "something" + will work correctly */ + + void lldb::SBStream::write (const char* str) + { + if (str) + $self->Printf("%s",str); + } + void lldb::SBStream::flush () + {} +} +%extend lldb::SBSymbol { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBTarget { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBTypeFilter { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBTypeNameSpecifier { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBTypeSummary { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBTypeSynthetic { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBThread { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%pythoncode %{ + +def command(command_name=None, doc=None): + import lldb + """A decorator function that registers an LLDB command line + command that is bound to the function it is attached to.""" + def callable(function): + """Registers an lldb command for the decorated function.""" + command = "command script add -f %s.%s %s" % (function.__module__, function.__name__, command_name or function.__name__) + lldb.debugger.HandleCommand(command) + if doc: + function.__doc__ = doc + return function + + return callable + +class declaration(object): + '''A class that represents a source declaration location with file, line and column.''' + def __init__(self, file, line, col): + self.file = file + self.line = line + self.col = col + +class value_iter(object): + def __iter__(self): + return self + + def __next__(self): + if self.index >= self.length: + raise StopIteration() + child_sbvalue = self.sbvalue.GetChildAtIndex(self.index) + self.index += 1 + return value(child_sbvalue) + + def next(self): + return self.__next__() + + def __init__(self,value): + self.index = 0 + self.sbvalue = value + if type(self.sbvalue) is value: + self.sbvalue = self.sbvalue.sbvalue + self.length = self.sbvalue.GetNumChildren() + +class value(object): + '''A class designed to wrap lldb.SBValue() objects so the resulting object + can be used as a variable would be in code. So if you have a Point structure + variable in your code in the current frame named "pt", you can initialize an instance + of this class with it: + + pt = lldb.value(lldb.frame.FindVariable("pt")) + print pt + print pt.x + print pt.y + + pt = lldb.value(lldb.frame.FindVariable("rectangle_array")) + print rectangle_array[12] + print rectangle_array[5].origin.x''' + def __init__(self, sbvalue): + self.sbvalue = sbvalue + + def __nonzero__(self): + return self.sbvalue.__nonzero__() + + def __bool__(self): + return self.sbvalue.__bool__() + + def __str__(self): + return self.sbvalue.__str__() + + def __getitem__(self, key): + # Allow array access if this value has children... + if type(key) is value: + key = int(key) + if type(key) is int: + child_sbvalue = (self.sbvalue.GetValueForExpressionPath("[%i]" % key)) + if child_sbvalue and child_sbvalue.IsValid(): + return value(child_sbvalue) + raise IndexError("Index '%d' is out of range" % key) + raise TypeError("No array item of type %s" % str(type(key))) + + def __iter__(self): + return value_iter(self.sbvalue) + + def __getattr__(self, name): + child_sbvalue = self.sbvalue.GetChildMemberWithName (name) + if child_sbvalue and child_sbvalue.IsValid(): + return value(child_sbvalue) + raise AttributeError("Attribute '%s' is not defined" % name) + + def __add__(self, other): + return int(self) + int(other) + + def __sub__(self, other): + return int(self) - int(other) + + def __mul__(self, other): + return int(self) * int(other) + + def __floordiv__(self, other): + return int(self) // int(other) + + def __mod__(self, other): + return int(self) % int(other) + + def __divmod__(self, other): + return int(self) % int(other) + + def __pow__(self, other): + return int(self) ** int(other) + + def __lshift__(self, other): + return int(self) << int(other) + + def __rshift__(self, other): + return int(self) >> int(other) + + def __and__(self, other): + return int(self) & int(other) + + def __xor__(self, other): + return int(self) ^ int(other) + + def __or__(self, other): + return int(self) | int(other) + + def __div__(self, other): + return int(self) / int(other) + + def __truediv__(self, other): + return int(self) / int(other) + + def __iadd__(self, other): + result = self.__add__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __isub__(self, other): + result = self.__sub__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __imul__(self, other): + result = self.__mul__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __idiv__(self, other): + result = self.__div__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __itruediv__(self, other): + result = self.__truediv__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ifloordiv__(self, other): + result = self.__floordiv__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __imod__(self, other): + result = self.__and__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ipow__(self, other): + result = self.__pow__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ipow__(self, other, modulo): + result = self.__pow__(self, other, modulo) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ilshift__(self, other): + result = self.__lshift__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __irshift__(self, other): + result = self.__rshift__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __iand__(self, other): + result = self.__and__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ixor__(self, other): + result = self.__xor__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ior__(self, other): + result = self.__ior__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __neg__(self): + return -int(self) + + def __pos__(self): + return +int(self) + + def __abs__(self): + return abs(int(self)) + + def __invert__(self): + return ~int(self) + + def __complex__(self): + return complex (int(self)) + + def __int__(self): + is_num,is_sign = is_numeric_type(self.sbvalue.GetType().GetCanonicalType().GetBasicType()) + if is_num and not is_sign: return self.sbvalue.GetValueAsUnsigned() + return self.sbvalue.GetValueAsSigned() + + def __long__(self): + return self.__int__() + + def __float__(self): + return float (self.sbvalue.GetValueAsSigned()) + + def __oct__(self): + return '0%o' % self.sbvalue.GetValueAsUnsigned() + + def __hex__(self): + return '0x%x' % self.sbvalue.GetValueAsUnsigned() + + def __len__(self): + return self.sbvalue.GetNumChildren() + + def __eq__(self, other): + if type(other) is int: + return int(self) == other + elif type(other) is str: + return str(self) == other + elif type(other) is value: + self_err = SBError() + other_err = SBError() + self_val = self.sbvalue.GetValueAsUnsigned(self_err) + if self_err.fail: + raise ValueError("unable to extract value of self") + other_val = other.sbvalue.GetValueAsUnsigned(other_err) + if other_err.fail: + raise ValueError("unable to extract value of other") + return self_val == other_val + raise TypeError("Unknown type %s, No equality operation defined." % str(type(other))) + + def __ne__(self, other): + return not self.__eq__(other) +%} + +%pythoncode %{ + +class SBSyntheticValueProvider(object): + def __init__(self,valobj): + pass + + def num_children(self): + return 0 + + def get_child_index(self,name): + return None + + def get_child_at_index(self,idx): + return None + + def update(self): + pass + + def has_children(self): + return False + + +%} + +%pythoncode %{ + +# given an lldb.SBBasicType it returns a tuple +# (is_numeric, is_signed) +# the value of is_signed is undefined if is_numeric == false +def is_numeric_type(basic_type): + if basic_type == eBasicTypeInvalid: return (False,False) + if basic_type == eBasicTypeVoid: return (False,False) + if basic_type == eBasicTypeChar: return (True,False) + if basic_type == eBasicTypeSignedChar: return (True,True) + if basic_type == eBasicTypeUnsignedChar: return (True,False) + if basic_type == eBasicTypeWChar: return (True,False) + if basic_type == eBasicTypeSignedWChar: return (True,True) + if basic_type == eBasicTypeUnsignedWChar: return (True,False) + if basic_type == eBasicTypeChar16: return (True,False) + if basic_type == eBasicTypeChar32: return (True,False) + if basic_type == eBasicTypeShort: return (True,True) + if basic_type == eBasicTypeUnsignedShort: return (True,False) + if basic_type == eBasicTypeInt: return (True,True) + if basic_type == eBasicTypeUnsignedInt: return (True,False) + if basic_type == eBasicTypeLong: return (True,True) + if basic_type == eBasicTypeUnsignedLong: return (True,False) + if basic_type == eBasicTypeLongLong: return (True,True) + if basic_type == eBasicTypeUnsignedLongLong: return (True,False) + if basic_type == eBasicTypeInt128: return (True,True) + if basic_type == eBasicTypeUnsignedInt128: return (True,False) + if basic_type == eBasicTypeBool: return (False,False) + if basic_type == eBasicTypeHalf: return (True,True) + if basic_type == eBasicTypeFloat: return (True,True) + if basic_type == eBasicTypeDouble: return (True,True) + if basic_type == eBasicTypeLongDouble: return (True,True) + if basic_type == eBasicTypeFloatComplex: return (True,True) + if basic_type == eBasicTypeDoubleComplex: return (True,True) + if basic_type == eBasicTypeLongDoubleComplex: return (True,True) + if basic_type == eBasicTypeObjCID: return (False,False) + if basic_type == eBasicTypeObjCClass: return (False,False) + if basic_type == eBasicTypeObjCSel: return (False,False) + if basic_type == eBasicTypeNullPtr: return (False,False) + #if basic_type == eBasicTypeOther: + return (False,False) + +%} diff --git a/lldb/bindings/python/python-swigsafecast.swig b/lldb/bindings/python/python-swigsafecast.swig new file mode 100644 index 000000000000..d5cafbfa67cb --- /dev/null +++ b/lldb/bindings/python/python-swigsafecast.swig @@ -0,0 +1,154 @@ +// leaving this undefined ensures we will get a linker error if we try to use SBTypeToSWIGWrapper() +// for a type for which we did not specialze this function +template <typename SBClass> +PyObject* +SBTypeToSWIGWrapper (SBClass* sb_object); + +template <typename SBClass> +PyObject* +SBTypeToSWIGWrapper (SBClass& sb_object) +{ + return SBTypeToSWIGWrapper(&sb_object); +} + +template <typename SBClass> +PyObject* +SBTypeToSWIGWrapper (const SBClass& sb_object) +{ + return SBTypeToSWIGWrapper(&sb_object); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (PyObject* py_object) +{ + return py_object; +} + +template <> +PyObject* +SBTypeToSWIGWrapper (unsigned int* c_int) +{ + if (!c_int) + return NULL; + return PyInt_FromLong(*c_int); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBEvent* event_sb) +{ + return SWIG_NewPointerObj((void *) event_sb, SWIGTYPE_p_lldb__SBEvent, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBProcess* process_sb) +{ + return SWIG_NewPointerObj((void *) process_sb, SWIGTYPE_p_lldb__SBProcess, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBThread* thread_sb) +{ + return SWIG_NewPointerObj((void *) thread_sb, SWIGTYPE_p_lldb__SBThread, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBThreadPlan* thread_plan_sb) +{ + return SWIG_NewPointerObj((void *) thread_plan_sb, SWIGTYPE_p_lldb__SBThreadPlan, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBTarget* target_sb) +{ + return SWIG_NewPointerObj((void *) target_sb, SWIGTYPE_p_lldb__SBTarget, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBFrame* frame_sb) +{ + return SWIG_NewPointerObj((void *) frame_sb, SWIGTYPE_p_lldb__SBFrame, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBDebugger* debugger_sb) +{ + return SWIG_NewPointerObj((void *) debugger_sb, SWIGTYPE_p_lldb__SBDebugger, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBBreakpoint* breakpoint_sb) +{ + return SWIG_NewPointerObj((void *) breakpoint_sb, SWIGTYPE_p_lldb__SBBreakpoint, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBWatchpoint* watchpoint_sb) +{ + return SWIG_NewPointerObj((void *) watchpoint_sb, SWIGTYPE_p_lldb__SBWatchpoint, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBBreakpointLocation* breakpoint_location_sb) +{ + return SWIG_NewPointerObj((void *) breakpoint_location_sb, SWIGTYPE_p_lldb__SBBreakpointLocation, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBBreakpointName* breakpoint_name_sb) +{ + return SWIG_NewPointerObj((void *) breakpoint_name_sb, SWIGTYPE_p_lldb__SBBreakpointName, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBValue* value_sb) +{ + return SWIG_NewPointerObj((void *) value_sb, SWIGTYPE_p_lldb__SBValue, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBCommandReturnObject* cmd_ret_obj_sb) +{ + return SWIG_NewPointerObj((void *) cmd_ret_obj_sb, SWIGTYPE_p_lldb__SBCommandReturnObject, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBExecutionContext* ctx_sb) +{ + return SWIG_NewPointerObj((void *) ctx_sb, SWIGTYPE_p_lldb__SBExecutionContext, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBTypeSummaryOptions* summary_options_sb) +{ + return SWIG_NewPointerObj((void *) summary_options_sb, SWIGTYPE_p_lldb__SBTypeSummaryOptions, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBStructuredData* structured_data_sb) +{ + return SWIG_NewPointerObj((void *) structured_data_sb, SWIGTYPE_p_lldb__SBStructuredData, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBSymbolContext* sym_ctx_sb) +{ + return SWIG_NewPointerObj((void *) sym_ctx_sb, SWIGTYPE_p_lldb__SBSymbolContext, 0); +} diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig new file mode 100644 index 000000000000..c08aeab71f78 --- /dev/null +++ b/lldb/bindings/python/python-typemaps.swig @@ -0,0 +1,532 @@ +/* Typemap definitions, to allow SWIG to properly handle 'char**' data types. */ + +%typemap(in) char ** { + /* Check if is a list */ + if (PythonList::Check($input)) { + PythonList list(PyRefType::Borrowed, $input); + int size = list.GetSize(); + int i = 0; + $1 = (char**)malloc((size+1)*sizeof(char*)); + for (i = 0; i < size; i++) { + PythonString py_str = list.GetItemAtIndex(i).AsType<PythonString>(); + if (!py_str.IsAllocated()) { + PyErr_SetString(PyExc_TypeError,"list must contain strings"); + free($1); + return nullptr; + } + + $1[i] = const_cast<char*>(py_str.GetString().data()); + } + $1[i] = 0; + } else if ($input == Py_None) { + $1 = NULL; + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + return NULL; + } +} + +%typemap(typecheck) char ** { + /* Check if is a list */ + $1 = 1; + if (PythonList::Check($input)) { + PythonList list(PyRefType::Borrowed, $input); + int size = list.GetSize(); + int i = 0; + for (i = 0; i < size; i++) { + PythonString s = list.GetItemAtIndex(i).AsType<PythonString>(); + if (!s.IsAllocated()) { $1 = 0; } + } + } + else + { + $1 = ( ($input == Py_None) ? 1 : 0); + } +} + +%typemap(freearg) char** { + free((char *) $1); +} + +%typemap(out) char** { + int len; + int i; + len = 0; + while ($1[len]) len++; + PythonList list(len); + for (i = 0; i < len; i++) + list.SetItemAtIndex(i, PythonString($1[i])); + $result = list.release(); +} + +%typemap(in) lldb::tid_t { + PythonObject obj = Retain<PythonObject>($input); + lldb::tid_t value = unwrapOrSetPythonException(As<unsigned long long>(obj)); + if (PyErr_Occurred()) + return nullptr; + $1 = value; +} + +%typemap(in) lldb::StateType { + PythonObject obj = Retain<PythonObject>($input); + unsigned long long state_type_value = + unwrapOrSetPythonException(As<unsigned long long>(obj)); + if (PyErr_Occurred()) + return nullptr; + if (state_type_value > lldb::StateType::kLastStateType) { + PyErr_SetString(PyExc_ValueError, "Not a valid StateType value"); + return nullptr; + } + $1 = static_cast<lldb::StateType>(state_type_value); +} + +/* Typemap definitions to allow SWIG to properly handle char buffer. */ + +// typemap for a char buffer +%typemap(in) (char *dst, size_t dst_len) { + if (!PyInt_Check($input)) { + PyErr_SetString(PyExc_ValueError, "Expecting an integer"); + return NULL; + } + $2 = PyInt_AsLong($input); + if ($2 <= 0) { + PyErr_SetString(PyExc_ValueError, "Positive integer expected"); + return NULL; + } + $1 = (char *) malloc($2); +} +// SBProcess::ReadCStringFromMemory() uses a void*, but needs to be treated +// as char data instead of byte data. +%typemap(in) (void *char_buf, size_t size) = (char *dst, size_t dst_len); + +// Return the char buffer. Discarding any previous return result +%typemap(argout) (char *dst, size_t dst_len) { + Py_XDECREF($result); /* Blow away any previous result */ + if (result == 0) { + PythonString string(""); + $result = string.release(); + Py_INCREF($result); + } else { + llvm::StringRef ref(static_cast<const char*>($1), result); + PythonString string(ref); + $result = string.release(); + } + free($1); +} +// SBProcess::ReadCStringFromMemory() uses a void*, but needs to be treated +// as char data instead of byte data. +%typemap(argout) (void *char_buf, size_t size) = (char *dst, size_t dst_len); + + +// typemap for handling an snprintf-like API like SBThread::GetStopDescription. +%typemap(in) (char *dst_or_null, size_t dst_len) { + if (!PyInt_Check($input)) { + PyErr_SetString(PyExc_ValueError, "Expecting an integer"); + return NULL; + } + $2 = PyInt_AsLong($input); + if ($2 <= 0) { + PyErr_SetString(PyExc_ValueError, "Positive integer expected"); + return NULL; + } + $1 = (char *) malloc($2); +} +%typemap(argout) (char *dst_or_null, size_t dst_len) { + Py_XDECREF($result); /* Blow away any previous result */ + llvm::StringRef ref($1); + PythonString string(ref); + $result = string.release(); + free($1); +} + + +// typemap for an outgoing buffer +// See also SBEvent::SBEvent(uint32_t event, const char *cstr, uint32_t cstr_len). +// Ditto for SBProcess::PutSTDIN(const char *src, size_t src_len). +%typemap(in) (const char *cstr, uint32_t cstr_len), + (const char *src, size_t src_len) { + if (PythonString::Check($input)) { + PythonString str(PyRefType::Borrowed, $input); + $1 = (char*)str.GetString().data(); + $2 = str.GetSize(); + } + else if(PythonByteArray::Check($input)) { + PythonByteArray bytearray(PyRefType::Borrowed, $input); + $1 = (char*)bytearray.GetBytes().data(); + $2 = bytearray.GetSize(); + } + else if (PythonBytes::Check($input)) { + PythonBytes bytes(PyRefType::Borrowed, $input); + $1 = (char*)bytes.GetBytes().data(); + $2 = bytes.GetSize(); + } + else { + PyErr_SetString(PyExc_ValueError, "Expecting a string"); + return NULL; + } +} +// For SBProcess::WriteMemory, SBTarget::GetInstructions and SBDebugger::DispatchInput. +%typemap(in) (const void *buf, size_t size), + (const void *data, size_t data_len) { + if (PythonString::Check($input)) { + PythonString str(PyRefType::Borrowed, $input); + $1 = (void*)str.GetString().data(); + $2 = str.GetSize(); + } + else if(PythonByteArray::Check($input)) { + PythonByteArray bytearray(PyRefType::Borrowed, $input); + $1 = (void*)bytearray.GetBytes().data(); + $2 = bytearray.GetSize(); + } + else if (PythonBytes::Check($input)) { + PythonBytes bytes(PyRefType::Borrowed, $input); + $1 = (void*)bytes.GetBytes().data(); + $2 = bytes.GetSize(); + } + else { + PyErr_SetString(PyExc_ValueError, "Expecting a buffer"); + return NULL; + } +} + +// typemap for an incoming buffer +// See also SBProcess::ReadMemory. +%typemap(in) (void *buf, size_t size) { + if (PyInt_Check($input)) { + $2 = PyInt_AsLong($input); + } else if (PyLong_Check($input)) { + $2 = PyLong_AsLong($input); + } else { + PyErr_SetString(PyExc_ValueError, "Expecting an integer or long object"); + return NULL; + } + if ($2 <= 0) { + PyErr_SetString(PyExc_ValueError, "Positive integer expected"); + return NULL; + } + $1 = (void *) malloc($2); +} + +// Return the buffer. Discarding any previous return result +// See also SBProcess::ReadMemory. +%typemap(argout) (void *buf, size_t size) { + Py_XDECREF($result); /* Blow away any previous result */ + if (result == 0) { + $result = Py_None; + Py_INCREF($result); + } else { + PythonBytes bytes(static_cast<const uint8_t*>($1), result); + $result = bytes.release(); + } + free($1); +} + +%{ +namespace { +template <class T> +T PyLongAsT(PyObject *obj) { + static_assert(true, "unsupported type"); +} + +template <> uint64_t PyLongAsT<uint64_t>(PyObject *obj) { + return static_cast<uint64_t>(PyLong_AsUnsignedLongLong(obj)); +} + +template <> uint32_t PyLongAsT<uint32_t>(PyObject *obj) { + return static_cast<uint32_t>(PyLong_AsUnsignedLong(obj)); +} + +template <> int64_t PyLongAsT<int64_t>(PyObject *obj) { + return static_cast<int64_t>(PyLong_AsLongLong(obj)); +} + +template <> int32_t PyLongAsT<int32_t>(PyObject *obj) { + return static_cast<int32_t>(PyLong_AsLong(obj)); +} + +template <class T> +bool SetNumberFromPyObject(T &number, PyObject *obj) { + if (PyInt_Check(obj)) + number = static_cast<T>(PyInt_AsLong(obj)); + else if (PyLong_Check(obj)) + number = PyLongAsT<T>(obj); + else return false; + + return true; +} + +template <> +bool SetNumberFromPyObject<double>(double &number, PyObject *obj) { + if (PyFloat_Check(obj)) { + number = PyFloat_AsDouble(obj); + return true; + } + + return false; +} + +} // namespace +%} + +// these typemaps allow Python users to pass list objects +// and have them turn into C++ arrays (this is useful, for instance +// when creating SBData objects from lists of numbers) +%typemap(in) (uint64_t* array, size_t array_len), + (uint32_t* array, size_t array_len), + (int64_t* array, size_t array_len), + (int32_t* array, size_t array_len), + (double* array, size_t array_len) { + /* Check if is a list */ + if (PyList_Check($input)) { + int size = PyList_Size($input); + int i = 0; + $2 = size; + $1 = ($1_type) malloc(size * sizeof($*1_type)); + for (i = 0; i < size; i++) { + PyObject *o = PyList_GetItem($input,i); + if (!SetNumberFromPyObject($1[i], o)) { + PyErr_SetString(PyExc_TypeError,"list must contain numbers"); + free($1); + return NULL; + } + + if (PyErr_Occurred()) { + free($1); + return NULL; + } + } + } else if ($input == Py_None) { + $1 = NULL; + $2 = 0; + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + return NULL; + } +} + +%typemap(freearg) (uint64_t* array, size_t array_len), + (uint32_t* array, size_t array_len), + (int64_t* array, size_t array_len), + (int32_t* array, size_t array_len), + (double* array, size_t array_len) { + free($1); +} + +// these typemaps wrap SBModule::GetVersion() from requiring a memory buffer +// to the more Pythonic style where a list is returned and no previous allocation +// is necessary - this will break if more than 50 versions are ever returned +%typemap(typecheck) (uint32_t *versions, uint32_t num_versions) { + $1 = ($input == Py_None ? 1 : 0); +} + +%typemap(in, numinputs=0) (uint32_t *versions) { + $1 = (uint32_t*)malloc(sizeof(uint32_t) * 50); +} + +%typemap(in, numinputs=0) (uint32_t num_versions) { + $1 = 50; +} + +%typemap(argout) (uint32_t *versions, uint32_t num_versions) { + uint32_t count = result; + if (count >= $2) + count = $2; + PyObject* list = PyList_New(count); + for (uint32_t j = 0; j < count; j++) + { + PyObject* item = PyInt_FromLong($1[j]); + int ok = PyList_SetItem(list,j,item); + if (ok != 0) + { + $result = Py_None; + break; + } + } + $result = list; +} + +%typemap(freearg) (uint32_t *versions) { + free($1); +} + + +// For Log::LogOutputCallback +%typemap(in) (lldb::LogOutputCallback log_callback, void *baton) { + if (!($input == Py_None || PyCallable_Check(reinterpret_cast<PyObject*>($input)))) { + PyErr_SetString(PyExc_TypeError, "Need a callable object or None!"); + return NULL; + } + + // FIXME (filcab): We can't currently check if our callback is already + // LLDBSwigPythonCallPythonLogOutputCallback (to DECREF the previous + // baton) nor can we just remove all traces of a callback, if we want to + // revert to a file logging mechanism. + + // Don't lose the callback reference + Py_INCREF($input); + $1 = LLDBSwigPythonCallPythonLogOutputCallback; + $2 = $input; +} + +%typemap(typecheck) (lldb::LogOutputCallback log_callback, void *baton) { + $1 = $input == Py_None; + $1 = $1 || PyCallable_Check(reinterpret_cast<PyObject*>($input)); +} + + +%typemap(in) lldb::FileSP { + PythonFile py_file(PyRefType::Borrowed, $input); + if (!py_file) { + PyErr_SetString(PyExc_TypeError, "not a file"); + return nullptr; + } + auto sp = unwrapOrSetPythonException(py_file.ConvertToFile()); + if (!sp) + return nullptr; + $1 = sp; +} + +%typemap(in) lldb::FileSP FORCE_IO_METHODS { + PythonFile py_file(PyRefType::Borrowed, $input); + if (!py_file) { + PyErr_SetString(PyExc_TypeError, "not a file"); + return nullptr; + } + auto sp = unwrapOrSetPythonException(py_file.ConvertToFileForcingUseOfScriptingIOMethods()); + if (!sp) + return nullptr; + $1 = sp; +} + +%typemap(in) lldb::FileSP BORROWED { + PythonFile py_file(PyRefType::Borrowed, $input); + if (!py_file) { + PyErr_SetString(PyExc_TypeError, "not a file"); + return nullptr; + } + auto sp = unwrapOrSetPythonException(py_file.ConvertToFile(/*borrowed=*/true)); + if (!sp) + return nullptr; + $1 = sp; +} + +%typemap(in) lldb::FileSP BORROWED_FORCE_IO_METHODS { + PythonFile py_file(PyRefType::Borrowed, $input); + if (!py_file) { + PyErr_SetString(PyExc_TypeError, "not a file"); + return nullptr; + } + auto sp = unwrapOrSetPythonException(py_file.ConvertToFileForcingUseOfScriptingIOMethods(/*borrowed=*/true)); + if (!sp) + return nullptr; + $1 = sp; +} + +%typecheck(SWIG_TYPECHECK_POINTER) lldb::FileSP { + if (PythonFile::Check($input)) { + $1 = 1; + } else { + PyErr_Clear(); + $1 = 0; + } +} + +%typemap(out) lldb::FileSP { + $result = nullptr; + lldb::FileSP &sp = $1; + if (sp) { + PythonFile pyfile = unwrapOrSetPythonException(PythonFile::FromFile(*sp)); + if (!pyfile.IsValid()) + return nullptr; + $result = pyfile.release(); + } + if (!$result) + { + $result = Py_None; + Py_INCREF(Py_None); + } +} + +%typemap(in) (const char* string, int len) { + if ($input == Py_None) + { + $1 = NULL; + $2 = 0; + } + else if (PythonString::Check($input)) + { + PythonString py_str(PyRefType::Borrowed, $input); + llvm::StringRef str = py_str.GetString(); + $1 = const_cast<char*>(str.data()); + $2 = str.size(); + // In Python 2, if $input is a PyUnicode object then this + // will trigger a Unicode -> String conversion, in which + // case the `PythonString` will now own the PyString. Thus + // if it goes out of scope, the data will be deleted. The + // only way to avoid this is to leak the Python object in + // that case. Note that if there was no conversion, then + // releasing the string will not leak anything, since we + // created this as a borrowed reference. + py_str.release(); + } + else + { + PyErr_SetString(PyExc_TypeError,"not a string-like object"); + return NULL; + } +} + +%inline %{ + +struct Py_buffer_RAII { + Py_buffer buffer = {}; + Py_buffer_RAII() {}; + Py_buffer &operator=(const Py_buffer_RAII &) = delete; + Py_buffer_RAII(const Py_buffer_RAII &) = delete; + ~Py_buffer_RAII() { + if (buffer.obj) + PyBuffer_Release(&buffer); + } +}; + +%} + +// These two pybuffer macros are copied out of swig/Lib/python/pybuffer.i, +// and fixed so they will not crash if PyObject_GetBuffer fails. +// https://github.com/swig/swig/issues/1640 +// +// I've also moved the call to PyBuffer_Release to the end of the SWIG wrapper, +// doing it right away is not legal according to the python buffer protocol. + +%define %pybuffer_mutable_binary(TYPEMAP, SIZE) +%typemap(in) (TYPEMAP, SIZE) (Py_buffer_RAII view) { + int res; Py_ssize_t size = 0; void *buf = 0; + res = PyObject_GetBuffer($input, &view.buffer, PyBUF_WRITABLE); + if (res < 0) { + PyErr_Clear(); + %argument_fail(res, "(TYPEMAP, SIZE)", $symname, $argnum); + } + size = view.buffer.len; + buf = view.buffer.buf; + $1 = ($1_ltype) buf; + $2 = ($2_ltype) (size/sizeof($*1_type)); +} +%enddef + +%define %pybuffer_binary(TYPEMAP, SIZE) +%typemap(in) (TYPEMAP, SIZE) (Py_buffer_RAII view) { + int res; Py_ssize_t size = 0; const void *buf = 0; + res = PyObject_GetBuffer($input, &view.buffer, PyBUF_CONTIG_RO); + if (res < 0) { + PyErr_Clear(); + %argument_fail(res, "(TYPEMAP, SIZE)", $symname, $argnum); + } + size = view.buffer.len; + buf = view.buffer.buf; + $1 = ($1_ltype) buf; + $2 = ($2_ltype) (size / sizeof($*1_type)); +} +%enddef + +%pybuffer_binary(const uint8_t *buf, size_t num_bytes); +%pybuffer_mutable_binary(uint8_t *buf, size_t num_bytes); diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig new file mode 100644 index 000000000000..f9e89373fe25 --- /dev/null +++ b/lldb/bindings/python/python-wrapper.swig @@ -0,0 +1,1067 @@ +%header %{ + +template <typename T> +PyObject * +SBTypeToSWIGWrapper (T* item); + +class PyErr_Cleaner +{ +public: + PyErr_Cleaner(bool print=false) : + m_print(print) + { + } + + ~PyErr_Cleaner() + { + if (PyErr_Occurred()) + { + if(m_print && !PyErr_ExceptionMatches(PyExc_SystemExit)) + PyErr_Print(); + PyErr_Clear(); + } + } + +private: + bool m_print; +}; + +%} + +%wrapper %{ + +// resolve a dotted Python name in the form +// foo.bar.baz.Foobar to an actual Python object +// if pmodule is NULL, the __main__ module will be used +// as the starting point for the search + + +// This function is called by lldb_private::ScriptInterpreterPython::BreakpointCallbackFunction(...) +// and is used when a script command is attached to a breakpoint for execution. + +SWIGEXPORT llvm::Expected<bool> +LLDBSwigPythonBreakpointCallbackFunction +( + const char *python_function_name, + const char *session_dictionary_name, + const lldb::StackFrameSP& frame_sp, + const lldb::BreakpointLocationSP& bp_loc_sp, + lldb_private::StructuredDataImpl *args_impl +) +{ + using namespace llvm; + + lldb::SBFrame sb_frame (frame_sp); + lldb::SBBreakpointLocation sb_bp_loc(bp_loc_sp); + + PyErr_Cleaner py_err_cleaner(true); + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_function_name, dict); + + unsigned max_positional_args; + if (auto arg_info = pfunc.GetArgInfo()) + max_positional_args = arg_info.get().max_positional_args; + else + return arg_info.takeError(); + + PythonObject frame_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_frame)); + PythonObject bp_loc_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_bp_loc)); + + auto result = [&] () -> Expected<PythonObject> { + // If the called function doesn't take extra_args, drop them here: + if (max_positional_args < 4) { + return pfunc.Call(frame_arg, bp_loc_arg, dict); + } else { + lldb::SBStructuredData *args_value = new lldb::SBStructuredData(args_impl); + PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(args_value)); + return pfunc.Call(frame_arg, bp_loc_arg, args_arg, dict); + } + } (); + + if (!result) + return result.takeError(); + + // Only False counts as false! + return result.get().get() != Py_False; +} + +// This function is called by lldb_private::ScriptInterpreterPython::WatchpointCallbackFunction(...) +// and is used when a script command is attached to a watchpoint for execution. + +SWIGEXPORT bool +LLDBSwigPythonWatchpointCallbackFunction +( + const char *python_function_name, + const char *session_dictionary_name, + const lldb::StackFrameSP& frame_sp, + const lldb::WatchpointSP& wp_sp +) +{ + lldb::SBFrame sb_frame (frame_sp); + lldb::SBWatchpoint sb_wp(wp_sp); + + bool stop_at_watchpoint = true; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_function_name, dict); + + if (!pfunc.IsAllocated()) + return stop_at_watchpoint; + + PythonObject frame_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_frame)); + PythonObject wp_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_wp)); + PythonObject result = pfunc(frame_arg, wp_arg, dict); + + if (result.get() == Py_False) + stop_at_watchpoint = false; + + return stop_at_watchpoint; +} + +SWIGEXPORT bool +LLDBSwigPythonCallTypeScript +( + const char *python_function_name, + const void *session_dictionary, + const lldb::ValueObjectSP& valobj_sp, + void** pyfunct_wrapper, + const lldb::TypeSummaryOptionsSP& options_sp, + std::string& retval +) +{ + lldb::SBValue sb_value (valobj_sp); + lldb::SBTypeSummaryOptions sb_options(options_sp.get()); + + retval.clear(); + + if (!python_function_name || !session_dictionary) + return false; + + PyObject *pfunc_impl = nullptr; + + if (pyfunct_wrapper && *pyfunct_wrapper && PyFunction_Check (*pyfunct_wrapper)) + { + pfunc_impl = (PyObject*)(*pyfunct_wrapper); + if (pfunc_impl->ob_refcnt == 1) + { + Py_XDECREF(pfunc_impl); + pfunc_impl = NULL; + } + } + + PyObject *py_dict = (PyObject*)session_dictionary; + if (!PythonDictionary::Check(py_dict)) + return true; + + PythonDictionary dict(PyRefType::Borrowed, py_dict); + + PyErr_Cleaner pyerr_cleanup(true); // show Python errors + + PythonCallable pfunc(PyRefType::Borrowed, pfunc_impl); + + if (!pfunc.IsAllocated()) + { + pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_function_name, dict); + if (!pfunc.IsAllocated()) + return false; + + if (pyfunct_wrapper) + { + *pyfunct_wrapper = pfunc.get(); + Py_XINCREF(pfunc.get()); + } + } + + PythonObject result; + auto argc = pfunc.GetArgInfo(); + if (!argc) { + llvm::consumeError(argc.takeError()); + return false; + } + + PythonObject value_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_value)); + PythonObject options_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_options)); + + if (argc.get().max_positional_args < 3) + result = pfunc(value_arg,dict); + else + result = pfunc(value_arg,dict,options_arg); + + retval = result.Str().GetString().str(); + + return true; +} + +SWIGEXPORT void* +LLDBSwigPythonCreateSyntheticProvider +( + const char *python_class_name, + const char *session_dictionary_name, + const lldb::ValueObjectSP& valobj_sp +) +{ + if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name) + Py_RETURN_NONE; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_class_name,dict); + + if (!pfunc.IsAllocated()) + Py_RETURN_NONE; + + // I do not want the SBValue to be deallocated when going out of scope because python + // has ownership of it and will manage memory for this object by itself + lldb::SBValue *sb_value = new lldb::SBValue(valobj_sp); + sb_value->SetPreferSyntheticValue(false); + + PythonObject val_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_value)); + if (!val_arg.IsAllocated()) + Py_RETURN_NONE; + + PythonObject result = pfunc(val_arg, dict); + + if (result.IsAllocated()) + return result.release(); + + Py_RETURN_NONE; +} + +SWIGEXPORT void* +LLDBSwigPythonCreateCommandObject +( + const char *python_class_name, + const char *session_dictionary_name, + const lldb::DebuggerSP debugger_sp +) +{ + if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name) + Py_RETURN_NONE; + + PyErr_Cleaner py_err_cleaner(true); + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_class_name, dict); + + if (!pfunc.IsAllocated()) + return nullptr; + + lldb::SBDebugger debugger_sb(debugger_sp); + PythonObject debugger_arg(PyRefType::Owned, SBTypeToSWIGWrapper(debugger_sb)); + PythonObject result = pfunc(debugger_arg, dict); + + if (result.IsAllocated()) + return result.release(); + + Py_RETURN_NONE; +} + +SWIGEXPORT void* +LLDBSwigPythonCreateScriptedThreadPlan +( + const char *python_class_name, + const char *session_dictionary_name, + lldb_private::StructuredDataImpl *args_impl, + std::string &error_string, + const lldb::ThreadPlanSP& thread_plan_sp +) +{ + if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name) + Py_RETURN_NONE; + + // I do not want the SBThreadPlan to be deallocated when going out of scope because python + // has ownership of it and will manage memory for this object by itself + lldb::SBThreadPlan *tp_value = new lldb::SBThreadPlan(thread_plan_sp); + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_class_name, dict); + + if (!pfunc.IsAllocated()) { + error_string.append("could not find script class: "); + error_string.append(python_class_name); + return nullptr; + } + + PythonObject tp_arg(PyRefType::Owned, SBTypeToSWIGWrapper(tp_value)); + + if (!tp_arg.IsAllocated()) + Py_RETURN_NONE; + + llvm::Expected<PythonCallable::ArgInfo> arg_info = pfunc.GetArgInfo(); + if (!arg_info) { + llvm::handleAllErrors( + arg_info.takeError(), + [&](PythonException &E) { + error_string.append(E.ReadBacktrace()); + }, + [&](const llvm::ErrorInfoBase &E) { + error_string.append(E.message()); + }); + Py_RETURN_NONE; + } + + PythonObject result = {}; + if (arg_info.get().max_positional_args == 2) { + if (args_impl != nullptr) { + error_string.assign("args passed, but __init__ does not take an args dictionary"); + Py_RETURN_NONE; + } + result = pfunc(tp_arg, dict); + } else if (arg_info.get().max_positional_args >= 3) { + lldb::SBStructuredData *args_value = new lldb::SBStructuredData(args_impl); + PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(args_value)); + result = pfunc(tp_arg, args_arg, dict); + } else { + error_string.assign("wrong number of arguments in __init__, should be 2 or 3 (not including self)"); + Py_RETURN_NONE; + } + + // FIXME: At this point we should check that the class we found supports all the methods + // that we need. + + if (result.IsAllocated()) + return result.release(); + Py_RETURN_NONE; +} + +SWIGEXPORT bool +LLDBSWIGPythonCallThreadPlan +( + void *implementor, + const char *method_name, + lldb_private::Event *event, + bool &got_error +) +{ + got_error = false; + + PyErr_Cleaner py_err_cleaner(false); + PythonObject self(PyRefType::Borrowed, static_cast<PyObject*>(implementor)); + auto pfunc = self.ResolveName<PythonCallable>(method_name); + + if (!pfunc.IsAllocated()) + return false; + + PythonObject result; + if (event != nullptr) + { + lldb::SBEvent sb_event(event); + PythonObject event_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_event)); + result = pfunc(event_arg); + } + else + result = pfunc(); + + if (PyErr_Occurred()) + { + got_error = true; + printf ("Return value was neither false nor true for call to %s.\n", method_name); + PyErr_Print(); + return false; + } + + if (result.get() == Py_True) + return true; + else if (result.get() == Py_False) + return false; + + // Somebody returned the wrong thing... + got_error = true; + printf ("Wrong return value type for call to %s.\n", method_name); + return false; +} + +SWIGEXPORT void * +LLDBSwigPythonCreateScriptedBreakpointResolver +( + const char *python_class_name, + const char *session_dictionary_name, + lldb_private::StructuredDataImpl *args_impl, + lldb::BreakpointSP &breakpoint_sp +) +{ + if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name) + Py_RETURN_NONE; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_class_name, dict); + + if (!pfunc.IsAllocated()) + return nullptr; + + lldb::SBBreakpoint *bkpt_value = new lldb::SBBreakpoint(breakpoint_sp); + + PythonObject bkpt_arg(PyRefType::Owned, SBTypeToSWIGWrapper(bkpt_value)); + + lldb::SBStructuredData *args_value = new lldb::SBStructuredData(args_impl); + PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(args_value)); + + PythonObject result = pfunc(bkpt_arg, args_arg, dict); + // FIXME: At this point we should check that the class we found supports all the methods + // that we need. + + if (result.IsAllocated()) + { + // Check that __callback__ is defined: + auto callback_func = result.ResolveName<PythonCallable>("__callback__"); + if (callback_func.IsAllocated()) + return result.release(); + else + result.release(); + } + Py_RETURN_NONE; +} + +SWIGEXPORT unsigned int +LLDBSwigPythonCallBreakpointResolver +( + void *implementor, + const char *method_name, + lldb_private::SymbolContext *sym_ctx +) +{ + PyErr_Cleaner py_err_cleaner(false); + PythonObject self(PyRefType::Borrowed, static_cast<PyObject*>(implementor)); + auto pfunc = self.ResolveName<PythonCallable>(method_name); + + if (!pfunc.IsAllocated()) + return 0; + + PythonObject result; + if (sym_ctx != nullptr) { + lldb::SBSymbolContext sb_sym_ctx(sym_ctx); + PythonObject sym_ctx_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_sym_ctx)); + result = pfunc(sym_ctx_arg); + } else + result = pfunc(); + + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + return 0; + } + + // The callback will return a bool, but we're need to also return ints + // so we're squirrelling the bool through as an int... And if you return + // nothing, we'll continue. + if (strcmp(method_name, "__callback__") == 0) { + if (result.get() == Py_False) + return 0; + else + return 1; + } + + long long ret_val = unwrapOrSetPythonException(As<long long>(result)); + + if (PyErr_Occurred()) { + PyErr_Print(); + PyErr_Clear(); + return 0; + } + + return ret_val; +} + +// wrapper that calls an optional instance member of an object taking no arguments +static PyObject* +LLDBSwigPython_CallOptionalMember +( + PyObject* implementor, + char* callee_name, + PyObject* ret_if_not_found = Py_None, + bool* was_found = NULL +) +{ + PyErr_Cleaner py_err_cleaner(false); + + PythonObject self(PyRefType::Borrowed, static_cast<PyObject*>(implementor)); + auto pfunc = self.ResolveName<PythonCallable>(callee_name); + + if (!pfunc.IsAllocated()) + { + if (was_found) + *was_found = false; + Py_XINCREF(ret_if_not_found); + return ret_if_not_found; + } + + if (was_found) + *was_found = true; + + PythonObject result = pfunc(); + return result.release(); +} + +SWIGEXPORT size_t +LLDBSwigPython_CalculateNumChildren +( + PyObject *implementor, + uint32_t max +) +{ + PythonObject self(PyRefType::Borrowed, implementor); + auto pfunc = self.ResolveName<PythonCallable>("num_children"); + + if (!pfunc.IsAllocated()) + return 0; + + auto arg_info = pfunc.GetArgInfo(); + if (!arg_info) { + llvm::consumeError(arg_info.takeError()); + return 0; + } + + size_t ret_val; + if (arg_info.get().max_positional_args < 1) + ret_val = unwrapOrSetPythonException(As<long long>(pfunc.Call())); + else + ret_val = unwrapOrSetPythonException(As<long long>(pfunc.Call(PythonInteger(max)))); + + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + return 0; + } + + if (arg_info.get().max_positional_args < 1) + ret_val = std::min(ret_val, static_cast<size_t>(max)); + + return ret_val; +} + +SWIGEXPORT PyObject* +LLDBSwigPython_GetChildAtIndex +( + PyObject *implementor, + uint32_t idx +) +{ + PyErr_Cleaner py_err_cleaner(true); + + PythonObject self(PyRefType::Borrowed, implementor); + auto pfunc = self.ResolveName<PythonCallable>("get_child_at_index"); + + if (!pfunc.IsAllocated()) + return nullptr; + + PythonObject result = pfunc(PythonInteger(idx)); + + if (!result.IsAllocated()) + return nullptr; + + lldb::SBValue* sbvalue_ptr = nullptr; + if (SWIG_ConvertPtr(result.get(), (void**)&sbvalue_ptr, SWIGTYPE_p_lldb__SBValue, 0) == -1) + return nullptr; + + if (sbvalue_ptr == nullptr) + return nullptr; + + return result.release(); +} + +SWIGEXPORT int +LLDBSwigPython_GetIndexOfChildWithName +( + PyObject *implementor, + const char* child_name +) +{ + PyErr_Cleaner py_err_cleaner(true); + + PythonObject self(PyRefType::Borrowed, implementor); + auto pfunc = self.ResolveName<PythonCallable>("get_child_index"); + + if (!pfunc.IsAllocated()) + return UINT32_MAX; + + llvm::Expected<PythonObject> result = pfunc.Call(PythonString(child_name)); + + long long retval = unwrapOrSetPythonException(As<long long>(std::move(result))); + + if (PyErr_Occurred()) { + PyErr_Clear(); // FIXME print this? do something else + return UINT32_MAX; + } + + if (retval >= 0) + return (uint32_t)retval; + + return UINT32_MAX; +} + +SWIGEXPORT bool +LLDBSwigPython_UpdateSynthProviderInstance +( + PyObject *implementor +) +{ + bool ret_val = false; + + static char callee_name[] = "update"; + + PyObject* py_return = LLDBSwigPython_CallOptionalMember(implementor,callee_name); + + if (py_return == Py_True) + ret_val = true; + + Py_XDECREF(py_return); + + return ret_val; +} + +SWIGEXPORT bool +LLDBSwigPython_MightHaveChildrenSynthProviderInstance +( + PyObject *implementor +) +{ + bool ret_val = false; + + static char callee_name[] = "has_children"; + + PyObject* py_return = LLDBSwigPython_CallOptionalMember(implementor,callee_name, Py_True); + + if (py_return == Py_True) + ret_val = true; + + Py_XDECREF(py_return); + + return ret_val; +} + +SWIGEXPORT PyObject* +LLDBSwigPython_GetValueSynthProviderInstance +( + PyObject *implementor +) +{ + PyObject* ret_val = nullptr; + + static char callee_name[] = "get_value"; + + PyObject* py_return = LLDBSwigPython_CallOptionalMember(implementor,callee_name, Py_None); + + if (py_return == Py_None || py_return == nullptr) + ret_val = nullptr; + + lldb::SBValue* sbvalue_ptr = NULL; + + if (SWIG_ConvertPtr(py_return, (void**)&sbvalue_ptr, SWIGTYPE_p_lldb__SBValue, 0) == -1) + ret_val = nullptr; + else if (sbvalue_ptr == NULL) + ret_val = nullptr; + else + ret_val = py_return; + + Py_XDECREF(py_return); + return ret_val; +} + +SWIGEXPORT void* +LLDBSWIGPython_CastPyObjectToSBValue +( + PyObject* data +) +{ + lldb::SBValue* sb_ptr = NULL; + + int valid_cast = SWIG_ConvertPtr(data, (void**)&sb_ptr, SWIGTYPE_p_lldb__SBValue, 0); + + if (valid_cast == -1) + return NULL; + + return sb_ptr; +} + +SWIGEXPORT bool +LLDBSwigPythonCallCommand +( + const char *python_function_name, + const char *session_dictionary_name, + lldb::DebuggerSP& debugger, + const char* args, + lldb_private::CommandReturnObject& cmd_retobj, + lldb::ExecutionContextRefSP exe_ctx_ref_sp +) +{ + lldb::SBCommandReturnObject cmd_retobj_sb(cmd_retobj); + lldb::SBDebugger debugger_sb(debugger); + lldb::SBExecutionContext exe_ctx_sb(exe_ctx_ref_sp); + + PyErr_Cleaner py_err_cleaner(true); + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_function_name, dict); + + if (!pfunc.IsAllocated()) + return false; + + // pass the pointer-to cmd_retobj_sb or watch the underlying object disappear from under you + // see comment above for SBCommandReturnObjectReleaser for further details + auto argc = pfunc.GetArgInfo(); + if (!argc) { + llvm::consumeError(argc.takeError()); + return false; + } + PythonObject debugger_arg(PyRefType::Owned, SBTypeToSWIGWrapper(debugger_sb)); + PythonObject exe_ctx_arg(PyRefType::Owned, SBTypeToSWIGWrapper(exe_ctx_sb)); + PythonObject cmd_retobj_arg(PyRefType::Owned, SBTypeToSWIGWrapper(&cmd_retobj_sb)); + + if (argc.get().max_positional_args < 5u) + pfunc(debugger_arg, PythonString(args), cmd_retobj_arg, dict); + else + pfunc(debugger_arg, PythonString(args), exe_ctx_arg, cmd_retobj_arg, dict); + + return true; +} + +SWIGEXPORT bool +LLDBSwigPythonCallCommandObject +( + PyObject *implementor, + lldb::DebuggerSP& debugger, + const char* args, + lldb_private::CommandReturnObject& cmd_retobj, + lldb::ExecutionContextRefSP exe_ctx_ref_sp +) +{ + lldb::SBCommandReturnObject cmd_retobj_sb(cmd_retobj); + lldb::SBDebugger debugger_sb(debugger); + lldb::SBExecutionContext exe_ctx_sb(exe_ctx_ref_sp); + + PyErr_Cleaner py_err_cleaner(true); + + PythonObject self(PyRefType::Borrowed, implementor); + auto pfunc = self.ResolveName<PythonCallable>("__call__"); + + if (!pfunc.IsAllocated()) + return false; + + // pass the pointer-to cmd_retobj_sb or watch the underlying object disappear from under you + // see comment above for SBCommandReturnObjectReleaser for further details + PythonObject debugger_arg(PyRefType::Owned, SBTypeToSWIGWrapper(debugger_sb)); + PythonObject exe_ctx_arg(PyRefType::Owned, SBTypeToSWIGWrapper(exe_ctx_sb)); + PythonObject cmd_retobj_arg(PyRefType::Owned, SBTypeToSWIGWrapper(&cmd_retobj_sb)); + + pfunc(debugger_arg, PythonString(args), exe_ctx_arg, cmd_retobj_arg); + + return true; +} + +SWIGEXPORT void* +LLDBSWIGPythonCreateOSPlugin +( + const char *python_class_name, + const char *session_dictionary_name, + const lldb::ProcessSP& process_sp +) +{ + if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name) + Py_RETURN_NONE; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_class_name, dict); + + if (!pfunc.IsAllocated()) + Py_RETURN_NONE; + + // I do not want the SBProcess to be deallocated when going out of scope because python + // has ownership of it and will manage memory for this object by itself + lldb::SBProcess *process_sb = new lldb::SBProcess(process_sp); + PythonObject process_arg(PyRefType::Owned, SBTypeToSWIGWrapper(process_sb)); + if (!process_arg.IsAllocated()) + Py_RETURN_NONE; + + auto result = pfunc(process_arg); + + if (result.IsAllocated()) + return result.release(); + + Py_RETURN_NONE; +} + +SWIGEXPORT void* +LLDBSWIGPython_CreateFrameRecognizer +( + const char *python_class_name, + const char *session_dictionary_name +) +{ + if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name) + Py_RETURN_NONE; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_class_name, dict); + + if (!pfunc.IsAllocated()) + Py_RETURN_NONE; + + auto result = pfunc(); + + if (result.IsAllocated()) + return result.release(); + + Py_RETURN_NONE; +} + +SWIGEXPORT PyObject* +LLDBSwigPython_GetRecognizedArguments +( + PyObject *implementor, + const lldb::StackFrameSP& frame_sp +) +{ + static char callee_name[] = "get_recognized_arguments"; + + lldb::SBFrame frame_sb(frame_sp); + PyObject *arg = SBTypeToSWIGWrapper(frame_sb); + + PythonString str(callee_name); + PyObject* result = PyObject_CallMethodObjArgs(implementor, str.get(), arg, + NULL); + return result; +} + +SWIGEXPORT void* +LLDBSWIGPython_GetDynamicSetting (void* module, const char* setting, const lldb::TargetSP& target_sp) +{ + if (!module || !setting) + Py_RETURN_NONE; + + PyErr_Cleaner py_err_cleaner(true); + PythonObject py_module(PyRefType::Borrowed, (PyObject *)module); + auto pfunc = py_module.ResolveName<PythonCallable>("get_dynamic_setting"); + + if (!pfunc.IsAllocated()) + Py_RETURN_NONE; + + lldb::SBTarget target_sb(target_sp); + PythonObject target_arg(PyRefType::Owned, SBTypeToSWIGWrapper(target_sb)); + auto result = pfunc(target_arg, PythonString(setting)); + + return result.release(); +} + +SWIGEXPORT bool +LLDBSWIGPythonRunScriptKeywordProcess +(const char* python_function_name, +const char* session_dictionary_name, +lldb::ProcessSP& process, +std::string& output) + +{ + if (python_function_name == NULL || python_function_name[0] == '\0' || !session_dictionary_name) + return false; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_function_name, dict); + + if (!pfunc.IsAllocated()) + return false; + + lldb::SBProcess process_sb(process); + PythonObject process_arg(PyRefType::Owned, SBTypeToSWIGWrapper(process_sb)); + auto result = pfunc(process_arg, dict); + + output = result.Str().GetString().str(); + + return true; +} + +SWIGEXPORT bool +LLDBSWIGPythonRunScriptKeywordThread +(const char* python_function_name, +const char* session_dictionary_name, +lldb::ThreadSP& thread, +std::string& output) + +{ + if (python_function_name == NULL || python_function_name[0] == '\0' || !session_dictionary_name) + return false; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_function_name, dict); + + if (!pfunc.IsAllocated()) + return false; + + lldb::SBThread thread_sb(thread); + PythonObject thread_arg(PyRefType::Owned, SBTypeToSWIGWrapper(thread_sb)); + auto result = pfunc(thread_arg, dict); + + output = result.Str().GetString().str(); + + return true; +} + +SWIGEXPORT bool +LLDBSWIGPythonRunScriptKeywordTarget +(const char* python_function_name, +const char* session_dictionary_name, +lldb::TargetSP& target, +std::string& output) + +{ + if (python_function_name == NULL || python_function_name[0] == '\0' || !session_dictionary_name) + return false; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_function_name,dict); + + if (!pfunc.IsAllocated()) + return false; + + lldb::SBTarget target_sb(target); + PythonObject target_arg(PyRefType::Owned, SBTypeToSWIGWrapper(target_sb)); + auto result = pfunc(target_arg, dict); + + output = result.Str().GetString().str(); + + return true; +} + +SWIGEXPORT bool +LLDBSWIGPythonRunScriptKeywordFrame +(const char* python_function_name, +const char* session_dictionary_name, +lldb::StackFrameSP& frame, +std::string& output) + +{ + if (python_function_name == NULL || python_function_name[0] == '\0' || !session_dictionary_name) + return false; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_function_name,dict); + + if (!pfunc.IsAllocated()) + return false; + + lldb::SBFrame frame_sb(frame); + PythonObject frame_arg(PyRefType::Owned, SBTypeToSWIGWrapper(frame_sb)); + auto result = pfunc(frame_arg, dict); + + output = result.Str().GetString().str(); + + return true; +} + +SWIGEXPORT bool +LLDBSWIGPythonRunScriptKeywordValue +(const char* python_function_name, +const char* session_dictionary_name, +lldb::ValueObjectSP& value, +std::string& output) + +{ + if (python_function_name == NULL || python_function_name[0] == '\0' || !session_dictionary_name) + return false; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_function_name, dict); + + if (!pfunc.IsAllocated()) + return false; + + lldb::SBValue value_sb(value); + PythonObject value_arg(PyRefType::Owned, SBTypeToSWIGWrapper(value_sb)); + auto result = pfunc(value_arg, dict); + + output = result.Str().GetString().str(); + + return true; +} + +SWIGEXPORT bool +LLDBSwigPythonCallModuleInit +( + const char *python_module_name, + const char *session_dictionary_name, + lldb::DebuggerSP& debugger +) +{ + std::string python_function_name_string = python_module_name; + python_function_name_string += ".__lldb_init_module"; + const char* python_function_name = python_function_name_string.c_str(); + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_function_name, dict); + + // This method is optional and need not exist. So if we don't find it, + // it's actually a success, not a failure. + if (!pfunc.IsAllocated()) + return true; + + lldb::SBDebugger debugger_sb(debugger); + PythonObject debugger_arg(PyRefType::Owned, SBTypeToSWIGWrapper(debugger_sb)); + pfunc(debugger_arg, dict); + + return true; +} +%} + + +%runtime %{ +// Forward declaration to be inserted at the start of LLDBWrapPython.h +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBValue.h" + +SWIGEXPORT lldb::ValueObjectSP +LLDBSWIGPython_GetValueObjectSPFromSBValue (void* data) +{ + lldb::ValueObjectSP valobj_sp; + if (data) + { + lldb::SBValue* sb_ptr = (lldb::SBValue *)data; + valobj_sp = sb_ptr->GetSP(); + } + return valobj_sp; +} + +#ifdef __cplusplus +extern "C" { +#endif + +void LLDBSwigPythonCallPythonLogOutputCallback(const char *str, void *baton); + +#ifdef __cplusplus +} +#endif +%} + +%wrapper %{ + + +// For the LogOutputCallback functions +void LLDBSwigPythonCallPythonLogOutputCallback(const char *str, void *baton) { + if (baton != Py_None) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + PyObject *result = PyObject_CallFunction(reinterpret_cast<PyObject*>(baton), const_cast<char*>("s"), str); + Py_XDECREF(result); + SWIG_PYTHON_THREAD_END_BLOCK; + } +} +%} |