ghidra/Ghidra/Features/Python/python-src/jintrospect.py

235 lines
9.2 KiB
Python

"""Extend introspect.py for Java based Jython classes."""
from introspect import *
import string
import __builtin__
import java # needed for java.lang.Class
import org # for org.python.core
import ghidra # for PythonCodeCompletionFactory
__author__ = "Don Coleman <dcoleman@chariotsolutions.com>"
#def getAutoCompleteList(command='', locals=None, includeMagic=1,
# includeSingle=1, includeDouble=1):
# """Return list of auto-completion options for command.
#
# The list of options will be based on the locals namespace."""
# attributes = []
# # Get the proper chunk of code from the command.
# root = getRoot(command, terminator='.')
# try:
# if locals is not None:
# object = eval(root, locals)
# else:
# object = eval(root)
# except:
# #print "could not eval(", root, "):", sys.exc_info()[0]
# return attributes
#
# if ispython(object):
# # use existing code
# attributes = getAttributeNames(object, includeMagic, includeSingle,
# includeDouble)
# else:
# methods = methodsOf(object.__class__)
# attributes = [eachMethod.__name__ for eachMethod in methods]
#
# return attributes
#
#def methodsOf(clazz):
# """Return a list of all the methods in a class"""
# classMembers = vars(clazz).values()
# methods = [eachMember for eachMember in classMembers
# if callable(eachMember)]
# for eachBase in clazz.__bases__:
# methods.extend(methodsOf(eachBase))
# return methods
def getCallTipJava(command='', locals=None):
"""For a command, return a tuple of object name, argspec, tip text.
The call tip information will be based on the locals namespace."""
calltip = ('', '', '') # object name, argspec, tip text.
# Get the proper chunk of code from the command.
(root, filter) = getRootAndFilter(command, terminator='(')
#java.lang.System.out.println("root=" + root)
try:
if locals is not None:
object = eval(root, locals)
else:
object = eval(root)
except:
#java.lang.System.err.println("could not eval(" + root + "):" +
# str(sys.exc_info()[0]))
return calltip
if ispython(object):
# Patrick's code handles Python code
# TODO fix in future because getCallTip runs eval() again
#java.lang.System.out.println("is a Python object")
calltip = getCallTip(command, locals)
if not calltip[1] and not calltip[2]:
# either it's a pure Java object, or we didn't get much from Python's
# getCallTip
name = ''
try:
name = object.__name__
except AttributeError:
pass
tipList = []
argspec = '' # not using argspec for Java
# if inspect.isbuiltin(object):
# # inspect.isbuiltin() fails for Jython
# # Can we get the argspec for Jython builtins? We can't in Python.
# # YES!
# print "is a builtin"
# pass
# elif inspect.isclass(object):
if inspect.isclass(object):
# get the constructor(s)
# TODO consider getting modifiers since Jython can access
# private methods
#java.lang.System.out.println("is a class")
try:
# this will likely fail for pure Python classes
constructors = object.getConstructors()
for constructor in constructors:
paramList = []
paramTypes = constructor.getParameterTypes()
# paramTypes is an array of classes; we need Strings
# TODO consider list comprehension
for param in paramTypes:
# TODO translate [B to byte[], C to char[], etc.
paramList.append(param.__name__)
paramString = string.join(paramList, ', ')
tip = "%s(%s)" % (constructor.name, paramString)
tipList.append(tip)
if len(constructors) == 1:
plural = ""
else:
plural = "s"
name = "Constructor" + plural + " for " + name + ":"
except:
pass
# if callable(object):
# # some Python types are function names as well, like
# # type() and file()
# argspec = str(object.__call__)
# # these don't seem to be very accurate
# if hasattr(object.__call__, "maxargs"):
# tipList.append("maxargs?: " +
# str(object.__call__.maxargs))
# if hasattr(object.__call__, "minargs"):
# tipList.append("minargs?: " +
# str(object.__call__.minargs))
# elif inspect.ismethod(object):
elif inspect.isroutine(object):
#java.lang.System.out.println("is a routine")
# method = object
# object = method.im_class
#
# # Java allows overloading so we may have more than one method
# methodArray = object.getMethods()
#
# for eachMethod in methodArray:
# if eachMethod.name == method.__name__:
# paramList = []
# for eachParam in eachMethod.parameterTypes:
# paramList.append(eachParam.__name__)
#
# paramString = string.join(paramList, ', ')
#
# # create a Python style string a la PyCrust
# # we're showing the parameter type rather than the
# # parameter name, since that's all we can get
# # we need to show multiple methods for overloading
# # TODO improve message format
# # do we want to show the method visibility?
# # how about exceptions?
# # note: name, return type and exceptions same for
# # EVERY overloaded method
#
#
# tip = "%s(%s) -> %s" % (eachMethod.name, paramString,
# eachMethod.returnType)
# tipList.append(tip)
if hasattr(object, "argslist"):
for args in object.argslist:
if args is not None:
# for now
tipList.append(str(args.data))
# elif callable(object):
# argspec = str(object.__call__)
# # these don't seem to be very accurate
# if hasattr(object.__call__, "maxargs"):
# tipList.append("maxargs?: " + str(object.__call__.maxargs))
# if hasattr(object.__call__, "minargs"):
# tipList.append("minargs?: " + str(object.__call__.minargs))
# elif inspect.isfunction(object):
# print "is function"
if (len(tipList) == 0):
if hasattr(object, "__name__") and \
hasattr(__builtin__, object.__name__):
# try to get arguments for any other "old-style" builtin
# functions (see __builtin__.java, classDictInit() method)
methods = \
java.lang.Class.getMethods(org.python.core.__builtin__)
for method in methods:
if method.name == object.__name__:
tipList.append(str(method))
argspec = "a built-in Python function"
else:
# last-ditch: try possible __call__ methods of new-style
# objects
for possible_call_method in \
ghidra.python.PythonCodeCompletionFactory.getCallMethods(object):
signature = str(possible_call_method)
# clean up the method signature a bit, so it looks sane
signature = \
signature.replace("$1exposed_", ".").replace(".__call__", "")
tipList.append(signature)
calltip = (name, argspec, string.join(tipList, "\n"))
return calltip
def ispython(object):
"""
Figure out if this is Python code or Java code..
"""
pyclass = 0
pycode = 0
pyinstance = 0
if inspect.isclass(object):
try:
object.__doc__
pyclass = 1
except AttributeError:
pyclass = 0
elif inspect.ismethod(object):
try:
# changed for Jython 2.2a1
#object.__dict__
object.__str__
pycode = 1
except AttributeError:
pycode = 0
else: # I guess an instance of an object falls here
try:
# changed for Jython 2.2a1
#object.__dict__
object.__str__
pyinstance = 1
except AttributeError:
pyinstance = 0
return pyclass | pycode | pyinstance