mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GP-4659: Renaming "Python" module to "Jython"
This commit is contained in:
parent
9840eee937
commit
fb1f725f5b
46 changed files with 285 additions and 284 deletions
190
Ghidra/Features/Jython/jython-src/ghidradoc.py
Normal file
190
Ghidra/Features/Jython/jython-src/ghidradoc.py
Normal file
|
@ -0,0 +1,190 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
"""
|
||||
Ties the Ghidra documentation into the builtin Python help.
|
||||
"""
|
||||
|
||||
import __builtin__
|
||||
import java
|
||||
import re
|
||||
import json
|
||||
import os
|
||||
import zipfile
|
||||
|
||||
from ghidra.framework import Application
|
||||
from ghidra.util import SystemUtilities
|
||||
|
||||
class _Helper:
|
||||
def __init__(self):
|
||||
self.orig_help = __builtin__.help
|
||||
if SystemUtilities.isInHeadlessMode():
|
||||
# ./pythonRun scenario
|
||||
self.msg = "\nExample workflow:\n"
|
||||
self.msg += " # Import headless analyzer\n"
|
||||
self.msg += " from ghidra.app.util.headless import HeadlessAnalyzer\n\n"
|
||||
self.msg += " # View HeadlessAnalyzer API\n"
|
||||
self.msg += " help(HeadlessAnalyzer)\n\n"
|
||||
self.msg += " # Get a HeadlessAnalyzer instance\n"
|
||||
self.msg += " headless = HeadlessAnalyzer.getInstance()\n\n"
|
||||
self.msg += " # Get headless options\n"
|
||||
self.msg += " options = headless.getOptions()\n\n"
|
||||
self.msg += " # View HeadlessOptions API and set options accordingly\n"
|
||||
self.msg += " help(options)\n\n"
|
||||
self.msg += " # View processLocal method API\n"
|
||||
self.msg += " help(headless.processLocal)\n\n"
|
||||
self.msg += " # Perform headless processing\n"
|
||||
self.msg += " headless.processLocal(...)\n\n"
|
||||
else:
|
||||
# PythonPlugin scenario
|
||||
self.msg = "Press 'F1' for usage instructions"
|
||||
|
||||
def __call__(self, param=None):
|
||||
|
||||
def get_class_and_method(param):
|
||||
if param is None and not SystemUtilities.isInHeadlessMode():
|
||||
# Enable help() in PythonPlugin scenario to show help for GhidraScript
|
||||
return "ghidra.app.script.GhidraScript", None
|
||||
class_name = None
|
||||
method_name = None
|
||||
if type(param) in [type(1), type(1j), type(1L), type(1.0), type(None), type(True), type([]), type({}), type(()), type({1})]:
|
||||
# These are instances of builtin types, so skip
|
||||
pass
|
||||
elif type(param) == type(str):
|
||||
# These are builtin Python types, so skip
|
||||
pass
|
||||
elif type(param) == type(str.split):
|
||||
# These are python functions, so skip
|
||||
pass
|
||||
elif type(param) == type(java):
|
||||
# These are java packages, which we don't don't document, so skip
|
||||
pass
|
||||
elif type(param) == type(java.lang.Object):
|
||||
# This is a java class, so extract its class name
|
||||
match = re.search("'(.*)'", str(param))
|
||||
if match is not None:
|
||||
class_name = match.group(1)
|
||||
elif type(param) == type(java.lang.Object().toString):
|
||||
# This is a java method, so extract its class name and method name
|
||||
tokens = str(param).split(" ")[2].split(".")
|
||||
class_name = ".".join(tokens[:-1])
|
||||
method_name = tokens[-1]
|
||||
else:
|
||||
# Assuming this is a java object, so extract its class name
|
||||
match = re.search("'(.*)'", str(type(param)))
|
||||
if match is not None:
|
||||
class_name = match.group(1)
|
||||
return class_name, method_name
|
||||
|
||||
def get_jsondoc(class_name):
|
||||
jsondoc = None
|
||||
try:
|
||||
root = Application.getApplicationRootDirectory().getFile(False).getParentFile().getAbsolutePath()
|
||||
javadoc_zip_name = "GhidraAPI_javadoc.zip"
|
||||
if SystemUtilities.isInDevelopmentMode():
|
||||
javadoc_zip = root + "/build/tmp/" + javadoc_zip_name
|
||||
else:
|
||||
javadoc_zip = root + "/docs/" + javadoc_zip_name
|
||||
if os.path.exists(javadoc_zip):
|
||||
json_path = "api/" + class_name.replace('.', '/') + '.json'
|
||||
with zipfile.ZipFile(javadoc_zip, "r").open(json_path) as f:
|
||||
jsondoc = json.load(f)
|
||||
except (IOError, KeyError) as e:
|
||||
pass
|
||||
return jsondoc
|
||||
|
||||
def format_class(cls):
|
||||
sig = "class " + cls["name"] + "\n"
|
||||
if "extends" in cls:
|
||||
sig += " extends " + cls["extends"] + "\n"
|
||||
implements = ""
|
||||
for interface in cls["implements"]:
|
||||
if len(implements) > 0:
|
||||
implements += ", "
|
||||
implements += interface
|
||||
if len(implements) > 0:
|
||||
sig += " implements " + implements + " \n"
|
||||
sig += "\n" + cls["comment"]
|
||||
return sig
|
||||
|
||||
def format_field(field):
|
||||
sig = "%s %s" % (field["type_long"], field["name"])
|
||||
if field["static"]:
|
||||
sig = "static " + sig
|
||||
if field["constant_value"]:
|
||||
sig += " = " + field["constant_value"]
|
||||
sig += "\n"
|
||||
desc = " %s\n" % (field["comment"]) if len(field["comment"]) > 0 else ""
|
||||
return sig + desc
|
||||
|
||||
def format_method(method):
|
||||
paramsig = ""
|
||||
args = ""
|
||||
for param in method["params"]:
|
||||
if len(paramsig) > 0:
|
||||
paramsig += ", "
|
||||
paramsig += "%s %s" % (param["type_short"], param["name"])
|
||||
args += " @param %s (%s): %s\n" % (param["name"], param["type_long"], param["comment"])
|
||||
throws = ""
|
||||
for exception in method["throws"]:
|
||||
throws += " @throws %s: %s\n" % (exception["type_short"], exception["comment"])
|
||||
sig = "%s %s(%s)\n" % (method["return"]["type_short"], method["name"], paramsig)
|
||||
if method["static"]:
|
||||
sig = "static " + sig
|
||||
desc = " %s\n\n" % (method["comment"]) if len(method["comment"]) > 0 else ""
|
||||
ret = ""
|
||||
if method["return"]["type_short"] != "void":
|
||||
ret = " @return %s: %s\n" % (method["return"]["type_long"], method["return"]["comment"])
|
||||
return sig + desc + args + ret + throws
|
||||
|
||||
class_name, method_name = get_class_and_method(param)
|
||||
if class_name is None:
|
||||
self.orig_help(param)
|
||||
else:
|
||||
try_again = True
|
||||
while try_again:
|
||||
try_again = False
|
||||
print "Searching API for " + class_name + ("" if method_name is None else "." + method_name + "()") + "..."
|
||||
jsondoc = get_jsondoc(class_name)
|
||||
if jsondoc is None:
|
||||
print "No API found for " + class_name
|
||||
elif method_name is None:
|
||||
print "#####################################################"
|
||||
print format_class(jsondoc)
|
||||
print "#####################################################\n"
|
||||
for field in jsondoc["fields"]:
|
||||
print format_field(field)
|
||||
print "-----------------------------------------------------"
|
||||
for method in jsondoc["methods"]:
|
||||
print format_method(method)
|
||||
print "-----------------------------------------------------"
|
||||
else:
|
||||
found_method = False
|
||||
for method in jsondoc["methods"]:
|
||||
if method["name"] == method_name:
|
||||
print "-----------------------------------------------------"
|
||||
print format_method(method)
|
||||
print "-----------------------------------------------------"
|
||||
found_method = True
|
||||
if not found_method:
|
||||
# The method may be inherited, so check for a super class and try again
|
||||
if "extends" in jsondoc:
|
||||
class_name = jsondoc["extends"]
|
||||
try_again = True
|
||||
|
||||
def __repr__(self):
|
||||
return self.msg
|
||||
|
||||
__builtin__.help = _Helper()
|
530
Ghidra/Features/Jython/jython-src/introspect.py
Normal file
530
Ghidra/Features/Jython/jython-src/introspect.py
Normal file
|
@ -0,0 +1,530 @@
|
|||
## ###
|
||||
# IP: LGPL 2.1
|
||||
##
|
||||
"""
|
||||
Provides a variety of introspective-type support functions for
|
||||
things like call tips and command auto completion.
|
||||
|
||||
NOTE: this file is a modification of Patrick O'Brien's version 1.62
|
||||
"""
|
||||
|
||||
from __future__ import nested_scopes
|
||||
|
||||
import cStringIO
|
||||
import inspect
|
||||
import sys
|
||||
import tokenize
|
||||
import types
|
||||
import __main__
|
||||
from ghidra.jython import JythonCodeCompletionFactory
|
||||
import java # needed for java.lang.Class
|
||||
|
||||
try:
|
||||
True
|
||||
except NameError:
|
||||
True = 1==1
|
||||
False = 1==0
|
||||
|
||||
#from java.lang.System.out import println
|
||||
|
||||
def getAutoCompleteList(command='', locals=None, includeMagic=1,
|
||||
includeSingle=1, includeDouble=1):
|
||||
"""Return list of auto-completion tuples for command.
|
||||
|
||||
First entry is the possible completions, and second entry is the
|
||||
of actual string that should be added to do the completion.
|
||||
|
||||
The list of options will be based on the locals namespace."""
|
||||
attributes = []
|
||||
object = None
|
||||
# Get the proper chunk of code from the command.
|
||||
#root = getRoot(command, terminator='.')
|
||||
# and get the part of the completion we should filter on
|
||||
(root, filter) = getRootAndFilter(command, terminator='.')
|
||||
if root:
|
||||
jump_past_period = 1
|
||||
else:
|
||||
jump_past_period = 0
|
||||
|
||||
#println("root='" + root + "'")
|
||||
#println("filter='" + filter + "'")
|
||||
|
||||
if not root:
|
||||
# top-level?
|
||||
attributes = locals
|
||||
else:
|
||||
try:
|
||||
if locals is not None:
|
||||
object = eval(root, locals)
|
||||
else:
|
||||
object = eval(root)
|
||||
except:
|
||||
#print "could not eval(", root, "):", sys.exc_info()[0]
|
||||
pass
|
||||
else:
|
||||
attributes = getAttributeNames(object, includeMagic,
|
||||
includeSingle, includeDouble)
|
||||
completion_list = []
|
||||
for attribute in attributes:
|
||||
if attribute.lower().startswith(filter.lower()):
|
||||
try:
|
||||
if object is not None:
|
||||
pyObj = getattr(object, attribute)
|
||||
else:
|
||||
pyObj = locals[attribute]
|
||||
completion_list.append(JythonCodeCompletionFactory.
|
||||
newCodeCompletion(attribute,
|
||||
attribute,
|
||||
pyObj,
|
||||
filter))
|
||||
except:
|
||||
# hmm, problem evaluating? Examples of this include
|
||||
# inner classes, e.g. access$0, which aren't valid Python
|
||||
# anyway
|
||||
pass
|
||||
completion_list.sort(compare_completions)
|
||||
return completion_list
|
||||
|
||||
def compare_completions(comp1, comp2):
|
||||
return cmp(comp1.description, comp2.description)
|
||||
|
||||
def getAttributeNames(object, includeMagic=1, includeSingle=1,
|
||||
includeDouble=1):
|
||||
"""Return list of unique attributes, including inherited, for object."""
|
||||
attributes = []
|
||||
dict = {}
|
||||
if not hasattrAlwaysReturnsTrue(object):
|
||||
# Add some attributes that don't always get picked up. If
|
||||
# they don't apply, they'll get filtered out at the end
|
||||
attributes += ['__bases__', '__class__', '__dict__', '__name__',
|
||||
'func_closure', 'func_code', 'func_defaults',
|
||||
'func_dict', 'func_doc', 'func_globals', 'func_name']
|
||||
if includeMagic:
|
||||
try: attributes += object._getAttributeNames()
|
||||
except: pass
|
||||
# Get all attribute names.
|
||||
attrdict = getAllAttributeNames(object)
|
||||
for attrlist in attrdict.values():
|
||||
attributes += attrlist
|
||||
# Remove duplicates from the attribute list.
|
||||
for item in attributes:
|
||||
dict[item] = None
|
||||
attributes = dict.keys()
|
||||
attributes.sort(lambda x, y: cmp(x.upper(), y.upper()))
|
||||
if not includeSingle:
|
||||
attributes = filter(lambda item: item[0]!='_' \
|
||||
or item[1]=='_', attributes)
|
||||
if not includeDouble:
|
||||
attributes = filter(lambda item: item[:2]!='__', attributes)
|
||||
#print "attributes currently", attributes
|
||||
# Make sure we haven't picked up any bogus attributes somehow.
|
||||
#attributes = [attribute for attribute in attributes \
|
||||
# if hasattr(object, attribute)]
|
||||
retval = []
|
||||
for attribute in attributes:
|
||||
#print "checking", attribute
|
||||
try:
|
||||
if hasattr(object, attribute):
|
||||
retval += [attribute]
|
||||
except:
|
||||
# yes Virginia, hasattr() can fail in Jython 2.2 because trying
|
||||
# to get __doc__ on a builtin class results in NullPointerException
|
||||
pass
|
||||
return retval
|
||||
|
||||
def hasattrAlwaysReturnsTrue(object):
|
||||
return hasattr(object, 'bogu5_123_aTTri8ute')
|
||||
|
||||
def getAllAttributeNames(object):
|
||||
"""Return dict of all attributes, including inherited, for an object.
|
||||
|
||||
Recursively walk through a class and all base classes.
|
||||
"""
|
||||
attrdict = {} # (object, technique, count): [list of attributes]
|
||||
# !!!
|
||||
# Do Not use hasattr() as a test anywhere in this function,
|
||||
# because it is unreliable with the remote objects: xmlrpc, soap, etc.
|
||||
# They always return true for hasattr().
|
||||
# !!!
|
||||
try:
|
||||
# Yes, this can fail if object is an instance of a class with
|
||||
# __str__ (or __repr__) having a bug or raising an
|
||||
# exception. :-(
|
||||
key = str(object)
|
||||
except:
|
||||
key = 'anonymous'
|
||||
# Wake up sleepy object - a hack for ZODB objects in "ghost" state.
|
||||
try:
|
||||
wakeupcall = dir(object)
|
||||
del wakeupcall
|
||||
except:
|
||||
pass
|
||||
# Get attributes available through the normal convention
|
||||
try:
|
||||
attributes = dir(object)
|
||||
attrdict[(key, 'dir', len(attributes))] = attributes
|
||||
except:
|
||||
# sadly, this fails for PyReflectedFunctions
|
||||
pass
|
||||
# Get attributes rom the object's dictionary, if it has one.
|
||||
try:
|
||||
attributes = object.__dict__.keys()
|
||||
attributes.sort()
|
||||
except: # Must catch all because object might have __getattr__.
|
||||
pass
|
||||
else:
|
||||
attrdict[(key, '__dict__', len(attributes))] = attributes
|
||||
# For a class instance, get the attributes for the class.
|
||||
try:
|
||||
klass = object.__class__
|
||||
except: # Must catch all because object might have __getattr__.
|
||||
pass
|
||||
else:
|
||||
if klass is object:
|
||||
# Break a circular reference. This happens with extension
|
||||
# classes.
|
||||
#print "breaking circular reference to self"
|
||||
pass
|
||||
# this extra check added for Jython 2.2.1 to break circular recursion
|
||||
elif klass is not java.lang.Class:
|
||||
# print "calling update from", object, "with", klass
|
||||
attrdict.update(getAllAttributeNames(klass))
|
||||
# Also get attributes from any and all parent classes.
|
||||
try:
|
||||
bases = object.__bases__
|
||||
except: # Must catch all because object might have __getattr__.
|
||||
pass
|
||||
else:
|
||||
if isinstance(bases, types.TupleType):
|
||||
# needed for Jython 2.2?
|
||||
halt_type = type(types.TypeType)
|
||||
for base in bases:
|
||||
if type(base) is types.TypeType \
|
||||
or type(base) is halt_type:
|
||||
# Break a circular reference. Happens in Python 2.2.
|
||||
#print "breaking TypeType circular reference"
|
||||
pass
|
||||
else:
|
||||
# print "calling update (better not be 'type') with", base
|
||||
attrdict.update(getAllAttributeNames(base))
|
||||
return attrdict
|
||||
|
||||
def getCallTip(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 = 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 calltip
|
||||
name = ''
|
||||
object, dropSelf = getBaseObject(object)
|
||||
try:
|
||||
name = object.__name__
|
||||
except AttributeError:
|
||||
pass
|
||||
tip1 = ''
|
||||
argspec = ''
|
||||
if inspect.isbuiltin(object):
|
||||
# Builtin function don't have an argspec that we can get.
|
||||
pass
|
||||
elif inspect.isfunction(object):
|
||||
# tip1 is a string like: "getCallTip(command='', locals=None)"
|
||||
argspec = apply(inspect.formatargspec, inspect.getargspec(object))
|
||||
if dropSelf:
|
||||
# Thh first parameter to a method is a reference to an
|
||||
# instance, usually coded as "self", and is usually passed
|
||||
# automatically by Python; therefore we want to drop it.
|
||||
temp = argspec.split(',')
|
||||
if len(temp) == 1: # No other arguments.
|
||||
argspec = '()'
|
||||
else: # Drop the first argument.
|
||||
argspec = '(' + ','.join(temp[1:]).lstrip()
|
||||
tip1 = name + argspec
|
||||
doc = ''
|
||||
if callable(object):
|
||||
try:
|
||||
doc = inspect.getdoc(object)
|
||||
except:
|
||||
# Jython 2.2a1 could throw and exception with getdoc()
|
||||
pass
|
||||
if doc:
|
||||
# tip2 is the first separated line of the docstring, like:
|
||||
# "Return call tip text for a command."
|
||||
# tip3 is the rest of the docstring, like:
|
||||
# "The call tip information will be based on ... <snip>
|
||||
firstline = doc.split('\n')[0].lstrip()
|
||||
if tip1 == firstline:
|
||||
tip1 = ''
|
||||
else:
|
||||
tip1 += '\n\n'
|
||||
docpieces = doc.split('\n\n')
|
||||
tip2 = docpieces[0]
|
||||
tip3 = '\n\n'.join(docpieces[1:])
|
||||
tip = '%s%s\n\n%s' % (tip1, tip2, tip3)
|
||||
else:
|
||||
tip = tip1
|
||||
calltip = (name, argspec[1:-1], tip.strip())
|
||||
return calltip
|
||||
|
||||
def getRoot(command, terminator=None):
|
||||
"""Return the rightmost root portion of an arbitrary Python command.
|
||||
|
||||
Return only the root portion that can be eval()ed without side
|
||||
effect. The command would normally terminate with a '(' or
|
||||
'.'. The terminator and anything after the terminator will be
|
||||
dropped."""
|
||||
command = command.split('\n')[-1]
|
||||
if command.startswith(sys.ps2):
|
||||
command = command[len(sys.ps2):]
|
||||
command = command.lstrip()
|
||||
command = rtrimTerminus(command, terminator)
|
||||
tokens = getTokens(command)
|
||||
if not tokens:
|
||||
return ''
|
||||
if tokens[-1][0] is tokenize.ENDMARKER:
|
||||
# Remove the end marker.
|
||||
del tokens[-1]
|
||||
if not tokens:
|
||||
return ''
|
||||
if terminator == '.' and \
|
||||
(tokens[-1][1] <> '.' or tokens[-1][0] is not tokenize.OP):
|
||||
# Trap decimals in numbers, versus the dot operator
|
||||
return ''
|
||||
else:
|
||||
# Strip off the terminator.
|
||||
if terminator and command.endswith(terminator):
|
||||
size = 0 - len(terminator)
|
||||
command = command[:size]
|
||||
command = command.rstrip()
|
||||
tokens = getTokens(command)
|
||||
tokens.reverse()
|
||||
line = ''
|
||||
start = None
|
||||
prefix = ''
|
||||
laststring = '.'
|
||||
emptyTypes = ('[]', '()', '{}')
|
||||
for token in tokens:
|
||||
tokentype = token[0]
|
||||
tokenstring = token[1]
|
||||
line = token[4]
|
||||
if tokentype is tokenize.ENDMARKER:
|
||||
continue
|
||||
if tokentype in (tokenize.NAME, tokenize.STRING, tokenize.NUMBER) \
|
||||
and laststring != '.':
|
||||
# We've reached something that's not part of the root.
|
||||
if prefix and line[token[3][1]] != ' ':
|
||||
# If it doesn't have a space after it, remove the prefix.
|
||||
prefix = ''
|
||||
break
|
||||
if tokentype in (tokenize.NAME, tokenize.STRING, tokenize.NUMBER) \
|
||||
or (tokentype is tokenize.OP and tokenstring == '.'):
|
||||
if prefix:
|
||||
# The prefix isn't valid because it comes after a dot.
|
||||
prefix = ''
|
||||
break
|
||||
else:
|
||||
# start represents the last known good point in the line
|
||||
start = token[2][1]
|
||||
elif len(tokenstring) == 1 and tokenstring in ('[({])}'):
|
||||
# Remember, we're working backwards.
|
||||
# So prefix += tokenstring would be wrong.
|
||||
if prefix in emptyTypes and tokenstring in ('[({'):
|
||||
# We've already got an empty type identified so now we
|
||||
# are in a nested situation and we can break out with
|
||||
# what we've got
|
||||
break
|
||||
else:
|
||||
prefix = tokenstring = prefix
|
||||
else:
|
||||
# We've reached something that's not part of the root
|
||||
break
|
||||
laststring= tokenstring
|
||||
if start is None:
|
||||
start = len(line)
|
||||
root = line[start:]
|
||||
if prefix in emptyTypes:
|
||||
# Empty types are safe to be eval()'d and introspected.
|
||||
root = prefix + root
|
||||
return root
|
||||
|
||||
def getRootAndFilter(command, terminator=None):
|
||||
"""Return the rightmost root portion of an arbitrary Python command.
|
||||
Also returns the filter, which is the fragment after the root and
|
||||
terminator.
|
||||
|
||||
Return only the root portion that can be eval()ed without side
|
||||
effect. The command would normally terminate with a '(' or
|
||||
'.'. The terminator and anything after the terminator will be
|
||||
dropped."""
|
||||
command = command.split('\n')[-1]
|
||||
if command.startswith(sys.ps2):
|
||||
command = command[len(sys.ps2):]
|
||||
command = command.lstrip()
|
||||
tokens = getTokens(command)
|
||||
if not tokens:
|
||||
return ('', '')
|
||||
tokens.reverse()
|
||||
root = ""
|
||||
filter = ""
|
||||
terminator_seen = False
|
||||
# initialize matching bracket/paren sets
|
||||
op_mate = {'}': '{',
|
||||
']': '[',
|
||||
')': '('}
|
||||
op_stack = []
|
||||
while len(tokens) > 0:
|
||||
# work backwards through the tokens, starting at the end of the string
|
||||
token = tokens[0]
|
||||
del tokens[0]
|
||||
tokentype = token[0]
|
||||
tokenstring = token[1]
|
||||
line = token[4]
|
||||
if tokentype is tokenize.ENDMARKER:
|
||||
|
||||
#println("Hit end marker; continuing")
|
||||
|
||||
continue
|
||||
if not terminator_seen:
|
||||
if not filter and tokentype in (tokenize.NAME, tokenize.STRING):
|
||||
# okay, we think we've found our filter string
|
||||
filter = tokenstring
|
||||
|
||||
#println("hit filter string '" + filter + "'")
|
||||
|
||||
elif tokenstring == terminator:
|
||||
# hooray, our terminator!
|
||||
terminator_seen = True
|
||||
else:
|
||||
# either we found another token before our already-set
|
||||
# filter string, or we just found a bad token
|
||||
|
||||
#println("No valid tokens found after terminator (at '" +
|
||||
# tokenstring + "'")
|
||||
|
||||
break
|
||||
else:
|
||||
# we've seen the terminator -- continue adding valid tokens
|
||||
# until we hit a token that stops us
|
||||
if tokentype in (tokenize.NAME, tokenize.STRING, tokenize.NUMBER):
|
||||
root = tokenstring + root
|
||||
|
||||
#println("Added to root: '" + root + "'")
|
||||
|
||||
elif tokentype is tokenize.OP:
|
||||
if tokenstring in op_mate.keys():
|
||||
# found a closing bracket/paren
|
||||
op_stack.append(tokenstring)
|
||||
elif tokenstring in op_mate.values():
|
||||
if len(op_stack) < 1 or \
|
||||
tokenstring != op_mate[op_stack.pop()]:
|
||||
# uh-oh, non-matching brackets/parens!
|
||||
break
|
||||
elif len(op_stack) > 0:
|
||||
# weird tokens are okay inside brackets/parens
|
||||
pass
|
||||
elif tokenstring == '.':
|
||||
# dots are okay too!
|
||||
pass
|
||||
else:
|
||||
break
|
||||
|
||||
root = tokenstring + root
|
||||
|
||||
#println("Added to root: '" + root + "'")
|
||||
else:
|
||||
# hit a terminating token
|
||||
break
|
||||
return (root, filter)
|
||||
|
||||
def getTokens(command):
|
||||
"""Return list of token tuples for command."""
|
||||
command = str(command) # In case the command is unicode, which fails.
|
||||
f = cStringIO.StringIO(command)
|
||||
# tokens is a list of token tuples, each looking like:
|
||||
# (type, string, (srow, scol), (erow, ecol), line)
|
||||
tokens = []
|
||||
# Can't use list comprehension:
|
||||
# tokens = [token for token in tokenize.generate_tokens(f.readline)]
|
||||
# because of need to append as much as possible before TokenError.
|
||||
try:
|
||||
# This works with Python 2.1.3 (with nested_scopes)
|
||||
def eater(*args):
|
||||
tokens.append(args)
|
||||
tokenize.tokenize_loop(f.readline, eater)
|
||||
except tokenize.TokenError:
|
||||
# This is due to a premature EOF, which we expect since we are
|
||||
# feeding in fragments of Python code
|
||||
pass
|
||||
return tokens
|
||||
|
||||
def rtrimTerminus(command, terminator=None):
|
||||
"""Return command minus anything that follows the final terminator."""
|
||||
if terminator:
|
||||
pieces = command.split(terminator)
|
||||
if len(pieces) > 1:
|
||||
command = terminator.join(pieces[:-1]) + terminator
|
||||
return command
|
||||
|
||||
def getBaseObject(object):
|
||||
"""Return base object and dropSelf indicator for an object."""
|
||||
if inspect.isbuiltin(object):
|
||||
# Builtin function don't have an argspec that we can get.
|
||||
dropSelf = 0
|
||||
elif inspect.ismethod(object):
|
||||
# Get the function from the object otherwise
|
||||
# inspect.getargspec() complains that the object isn't a
|
||||
# Python funciton.
|
||||
try:
|
||||
if object.im_self is None:
|
||||
# This is an unbound method so we do not drop self
|
||||
# from the argpec, sinc ean instance must be passed
|
||||
# as the first arg.
|
||||
dropSelf = 0
|
||||
else:
|
||||
dropSelf = 1
|
||||
object = object.im_func
|
||||
except AttributeError:
|
||||
dropSelf = 0
|
||||
elif inspect.isclass(object):
|
||||
# Get the __init__ method function for the class
|
||||
constructor = getConstructor(object)
|
||||
if constructor is not None:
|
||||
object = constructor
|
||||
dropSelf = 1
|
||||
else:
|
||||
dropSelf = 0
|
||||
elif callable(object):
|
||||
# Get the __call__ method instead
|
||||
try:
|
||||
call_method = object.__call__.im_func
|
||||
if call_method.__name__ == '__call__':
|
||||
# unbound jython method end up here, example: string.index(
|
||||
dropSelf = 0
|
||||
else:
|
||||
object = call_method
|
||||
dropSelf = 1
|
||||
except AttributeError:
|
||||
# unbound python methods end up here
|
||||
dropSelf = 0
|
||||
else:
|
||||
dropSelf = 0
|
||||
return object, dropSelf
|
||||
|
||||
def getConstructor(object):
|
||||
"""Return constructor for class object, or None if there isn't one."""
|
||||
try:
|
||||
return object.__init__.im_func
|
||||
except AttributeError:
|
||||
for base in object.__bases__:
|
||||
constructor = getConstructor(base)
|
||||
if constructor is not None:
|
||||
return constructor
|
||||
return None
|
237
Ghidra/Features/Jython/jython-src/jintrospect.py
Normal file
237
Ghidra/Features/Jython/jython-src/jintrospect.py
Normal file
|
@ -0,0 +1,237 @@
|
|||
## ###
|
||||
# IP: LGPL 2.1
|
||||
##
|
||||
"""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 JythonCodeCompletionFactory
|
||||
|
||||
__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.method))
|
||||
# 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
|
48
Ghidra/Features/Jython/jython-src/sitecustomize.py
Normal file
48
Ghidra/Features/Jython/jython-src/sitecustomize.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
"""
|
||||
User-supplied customizations go here.
|
||||
"""
|
||||
|
||||
# nice-to-have: place 'java' and 'ghidra' into the local namespace, so you
|
||||
# can do fun things like "dir(java.lang)"
|
||||
import java
|
||||
import ghidra
|
||||
import __main__
|
||||
__main__.java = java
|
||||
__main__.ghidra = ghidra
|
||||
|
||||
# fix Jython "bug": unknown type 'javainstance' or 'javapackage' even though
|
||||
# that is the type Jython gives us if we ask type(<someObject>) or
|
||||
# type(ghidra) (respectively)
|
||||
import __builtin__
|
||||
import org.python.core
|
||||
# changed by Jim 20090528 for Jython 2.5
|
||||
# not sure why I even put these here... the first one might be troublesome
|
||||
#__builtin__.javainstance = org.python.core.PyJavaInstance
|
||||
#__builtin__.javapackage = org.python.core.PyJavaPackage
|
||||
#__builtin__.javaclass = org.python.core.PyJavaClass
|
||||
__builtin__.javainstance = org.python.core.PyObjectDerived
|
||||
__builtin__.javapackage = org.python.core.PyJavaPackage
|
||||
__builtin__.javaclass = org.python.core.PyJavaType
|
||||
|
||||
# changed by Jim 20090528 for Jython 2.5
|
||||
# REMOVED collections stuff
|
||||
# OOPS still need this
|
||||
import sys
|
||||
|
||||
# Ghidra documentation
|
||||
import ghidradoc
|
Loading…
Add table
Add a link
Reference in a new issue