javap is work-in-progress

--HG--
branch : pmacs2
This commit is contained in:
Erik Osheim 2011-12-15 23:51:11 -05:00
parent c3e1f583a4
commit 87e96d3ed1
3 changed files with 285 additions and 148 deletions

View File

@ -170,7 +170,7 @@ class Application(object):
mode.install(self)
names = (
'blame', 'c', 'console', 'consolemini', 'css', 'diff', 'dir',
'elisp', 'hex', 'html', 'java', 'javascript', 'lisp', 'make',
'elisp', 'hex', 'html', 'java', 'javap', 'javascript', 'lisp', 'make',
'mini', 'mutt', 'nasm', 'ocaml', 'perl', 'python', 'replace', 'rst',
'scheme', 'search', 'sh', 'sql', 'tt', 'text', 'text2', 'which',
'xml', 'cheetah', 'colortext', 'latex', 'insertmini', 'conf',

127
mode/javap.py Normal file
View File

@ -0,0 +1,127 @@
from mode import Fundamental
from lex import Grammar, PatternRule, RegionRule, PatternMatchRule
name = '[a-zA-Z0-9_$.]+'
class JavapGrammar(Grammar):
rules = [
#PatternMatchRule('x', r'^( )(\d+:)([ \t]+)([a-z0-9_]+)',
# 'spaces', 'javap.linenum', 'spaces', 'javap.ins'),
#PatternMatchRule('x', r'^( )(\d+:)([ \t]+)([a-z0-9_]+)',
# 'spaces', 'javap.linenum', 'spaces', 'javap.ins'),
#PatternMatchRule('x', r'^( )(\d+:)([ \t]+)([a-z0-9_]+)',
# 'spaces', 'javap.linenum', 'spaces', 'javap.ins'),
PatternMatchRule('x', '^(package)( )(.+)$',
'javap.keyword', 'spaces', 'javap.package'),
PatternRule('javap.keyword', '(?:static|public|protected|private|final|abstract)'),
PatternMatchRule('x', '(--class)( +)([^ ]+)',
'javap.pseudo', 'spaces', 'javap.type'),
PatternMatchRule('x', '(--field)( +)([^ ]+)( +)([^ ]+)',
'javap.pseudo', 'spaces', 'javap.type', 'spaces', 'javap.type'),
PatternRule('javap.pseudo', '--(?:method|iface|string|field)'),
PatternMatchRule('x', '([^ ]+)( +)(\()',
'javap.type', 'spaces', 'javap.delim'),
PatternMatchRule('x', '(class|extends|implements)( )([^ ]+)',
'javap.keyword', 'spaces', 'javap.type'),
PatternMatchRule('x', '([^ ]+)( )([^ ]+)( )(\()',
'javap.type', 'spaces', 'javap.method', 'spaces', 'javap.delim'),
PatternMatchRule('x', '([^ ]+)( )([^ ]+)( )({)(})',
'javap.type', 'spaces', 'javap.method', 'spaces', 'javap.delim', 'javap.delim'),
PatternMatchRule('x', '([^ ]+)(,)( )',
'javap.type', 'javap.delim', 'spaces'),
PatternMatchRule('x', '([^ ]+)(\))',
'javap.type', 'javap.delim'),
#PatternMatchRule('x', '(//class )(.+)', 'javap.zcomment', 'javap.class'),
#PatternMatchRule('x', '(//InterfaceMethod )([^:]+)(:)(.+)',
# 'javap.zcomment', 'javap.method', 'javap.zcomment', 'javap.sig'),
#PatternMatchRule('x', '(//Method )([^:]+)(:)(.+)',
# 'javap.zcomment', 'javap.method', 'javap.zcomment', 'javap.sig'),
PatternMatchRule('x', '^( +)(\d+)( +)([^ ]+)',
'spaces', 'javap.byte', 'spaces', 'javap.ins'),
PatternRule('javap.const', r'#\d+'),
PatternRule('javap.int', r'\d+'),
#PatternRule('javap.zcomment', r'^Compiled from .*$'),
#PatternRule('javap.zcomment', r'Code:'),
#PatternMatchRule('x', r'(class)( )(' + name + ')', 'javap.zkeyword', 'spaces', 'javap.class'),
#PatternMatchRule('x', r'(extends)( )(' + name + ')', 'javap.zkeyword', 'spaces', 'javap.class'),
#PatternMatchRule('x', r'(public)( )(static)( )(' + name + ')( )(' + name + ')',
# 'javap.zkeyword', 'spaces', 'javap.zkeyword', 'spaces',
# 'javap.class', 'spaces', 'javap.method'),
#PatternRule(r'javap.zkeyword', r'(?:static|public|protected|private|extends|class|abstract)'),
#PatternRule(r'javap.cls', r'L[^;]+'),
#PatternRule(r'javap.cls2', r'B|C|D|F|I|J|S|Z|\['),
#PatternRule(r'javap.ins', r'(?<= )\d+:'),
#PatternRule(r'javap.class', name),
PatternRule('spaces', r' +'),
PatternRule('eol', r'\n'),
]
# white is for delimiters, operators, numbers
c_default = ('default', 'default')
# magenta is for keywords/builtins, translation, globs
lo_magenta = ('magenta202', 'default')
hi_magenta = ('magenta505', 'default')
# red is for comments, pods, endblocks
lo_red = ('red300', 'default')
hi_red = ('red511', 'default')
# orange are for arrays and hashes
hi_orange = ('yellow531', 'default')
lo_orange = ('yellow520', 'default')
# yellow is for scalars and prototypes
hi_yellow = ('yellow551', 'default')
lo_yellow = ('yellow330', 'default')
# green is for strings and hash keys
lo_green = ('green030', 'default')
hi_green = ('green050', 'default')
# cyan is for quotes, evals, regexes, subs
lo_cyan = ('cyan033', 'default')
hi_cyan = ('cyan155', 'default')
# blue is unused
lo_blue = ('blue113', 'default')
hi_blue = ('blue225', 'default')
class Javap(Fundamental):
name = 'Javap'
grammar = JavapGrammar()
colors = {
###'javap.string': ('green', 'default'),
##'javap.label': ('blue', 'default', 'bold'),
#'javap.class': ('cyan', 'default', 'bold'),
'javap.package': hi_yellow,
'javap.type': hi_cyan,
'javap.method': hi_blue,
##'javap.abbrev': ('green', 'default', 'bold'),
#'javap.name': ('yellow', 'default', 'bold'),
#'javap.linenum': ('green', 'default', 'bold'),
'javap.byte': lo_orange,
'javap.ins': hi_orange,
#'javap.cls': ('cyan', 'default', 'bold'),
#'javap.int': ('green', 'default', 'bold'),
'javap.const': hi_yellow,
'javap.keyword': hi_magenta,
'javap.pseudo': hi_magenta,
}
_bindings = {
'ipython-start': ('M-e',),
}
install = Javap.install

View File

@ -10,6 +10,7 @@ import default
import urllib2
import os
import re
from subprocess import Popen, PIPE, STDOUT
chr1 = '[a-zA-Z_]'
chr2 = '[a-zA-Z_0-9]'
@ -131,164 +132,172 @@ class ScalaStart(Interact):
def _execute(self, w, **vargs):
Interact._execute(self, w, bname='*Scala*', cmd='scala')
class ScalaDocBrowse(Method):
def _execute(self, w, **vargs):
a = w.application
url = a.config['scala.api']
Interact().execute(w, bname='*Scala-Doc*', cmd='links "%s"' % url)
class ScalaDecompile(Method):
args = [arg('classname', t='string', p='Class: ', h='The class to decompile')]
class ScalaDocLookup(Method):
args = [arg('name', t='string', p='Name: ', dv=default.current_word,
ld=True, h='The Scala name to get help on')]
def _get_path(self, w, name):
return w.application.getpath('cache', 'scala', name)
def _get_url(self, w, name, url):
path = self._get_path(w, name)
w.application.mkdirs('cache', 'scala')
ins_re = re.compile(r'^(\d+):')
if not os.path.exists(path):
open(path, 'w').write(urllib2.urlopen(url).read())
def _abbrev(self, s):
s = s.replace('/', '.')
s = s.replace('java.lang.', '')
s = s.replace('scala.', '')
if self.pkg is not None:
s = s.replace(self.pkg, '')
return s
html = open(path, 'r').read()
return html
pkg = None
obj_re = re.compile(r'^.+ (?:class|interface) ([^ ]+) ')
def _parse_obj(self, line):
s = self._abbrev(line[:-1])
m = self.obj_re.match(line)
obj = m.group(1)
toks = obj.split('.')
def _execute(self, w, **vargs):
try:
from BeautifulSoup import BeautifulSoup
except ImportError:
w.set_error('BeautifulSoup is not installed...')
return
a = w.application
name = vargs.get('name')
html = self._get_url(w, 'api.html', a.config['scala.api'])
soup = BeautifulSoup(html)
tags = soup.findAll('li')
for li in tags:
if li['title'].endswith(name):
frag = li.contents[0]['href']
url2 = a.config['scala.api-base'] + '/' + frag
a.run_external('links', url2)
return
w.set_error('error looking up %s...' % name)
class ScalaXRayBase(Method):
_is_method = False
pkg_re = re.compile('^package (.+)$')
def find_pkg(self, w):
for line in w.buffer.lines:
m = self.pkg_re.match(line)
if m:
return m.group(1)
raise Exception("no package found")
def get_sxr_dir(self, w):
# TODO: fixme fixme
path = 'target/scala_2.8.0/classes.sxr'
if os.path.exists(path):
return path
if len(toks) > 1:
self.pkg = '.'.join(toks[:-1]) + '.'
else:
raise Exception("classes.sxr not found")
self.pkg = None
def get_pkg_tags(self, w, pkg, base):
return os.path.join(base, pkg.replace('.', '/') + '.scala.txt')
def get_public_tags(self, w, base):
return os.path.join(base, 'public-tags')
return self._abbrev(line[:-1])
def term_def_lookup(self, w):
pkg = self.find_pkg(w)
base = self.get_sxr_dir(w)
tags = self.get_public_tags
def _parse_method(self, line):
return self._abbrev(line[:-1]).replace("(", " (")
def type_def_lookup(self, t, w):
base = self.get_sxr_dir(w)
tags = self.get_public_tags(w, base)
f = open(tags, 'r')
for line in f:
toks = line.split()
if toks[0] != 'type': continue
if toks[1] != t: continue
path = toks[2]
n = int(toks[4])
return (path, n)
return (None, None)
def type_lookup(self, n, w):
pkg = self.find_pkg(w)
base = self.get_sxr_dir(w)
path = w.buffer.path
r = re.compile('^.*src/main/scala/(.+)$')
m = r.match(path)
tags = os.path.join(base, m.group(1) + '.txt')
f = open(tags, 'r')
for line in f:
toks = line.split('\t')
i = int(toks[0])
j = int(toks[1])
if i <= n and n <= j:
return toks[2]
return None
class ScalaGetType(ScalaXRayBase):
type_re = re.compile('(?:[^ \[\]{}()\.,;:]+\.)*([^ \[\]{}()\.,;:]+)')
ret_re = re.compile('\)(?=[a-zA-Z0-9_])')
def _execute(self, w, **vargs):
word = w.get_token().string
if word is None or word.strip() == "":
w.set_error('no word selected')
return
n = w.cursor_byte_offset()
t = self.type_lookup(n, w)
if not t:
w.set_error('%s has unknown type' % word)
return
if w.application.config['scala.type-abbrev']:
t = self.type_re.sub(lambda m: m.group(1), t)
#t = t.replace(')', ') => ')
t = self.ret_re.sub(') => ', t)
t = t.replace(': ', ':')
t = t.replace(',', ', ')
w.set_error(t)
class ScalaGotoDefinition(ScalaXRayBase):
generic_re = re.compile('\[.+\]')
def _execute(self, w, **vargs):
word = w.get_token().string
if word is None or word.strip() == "":
w.set_error('no word selected')
return
n = w.cursor_byte_offset()
t = self.type_lookup(n, w)
if not t:
w.set_error('%s has unknown type' % word)
return
# remove generics
t = self.generic_re.sub('', t)
path, n = self.type_def_lookup(t, w)
if not path:
w.set_error('%s not found' % t)
return
b = w.buffer
a = w.application
b2 = a.open_path(path)
a.switch_buffer(b2)
if b == b2:
a.methods['goto-char'].execute(w, charno=n - 1)
a.methods['center-view'].execute(w)
prims = {
'B': 'byte',
'C': 'char',
'D': 'double',
'F': 'float',
'I': 'int',
'J': 'long',
'S': 'short',
'V': 'void',
'Z': 'bool',
}
def _parse_args(self, line):
args = []
i = 0
arr = ""
while i < len(line):
if line[i] in self.prims:
args.append(self.prims[line[i]] + arr)
arr = ""
elif line[i] == "[":
arr += "[]"
elif line[i] == "L":
j = i + 1
while line[j] != ';': j += 1
args.append(self._abbrev(line[i + 1:j]) + arr)
arr = ""
i = j
else:
a.methods['goto-char'].execute(b2.windows[0], charno=n - 1)
a.methods['center-view'].execute(b2.windows[0])
w.set_error('opening %s...' % path)
raise Exception("huh? saw %r (%r)" % (line[i:], line))
i += 1
return args
typs = {
'InterfaceMethod': 'iface ',
'Method': 'method',
}
field_re = re.compile('^Field\s+([^:]+):(.+)$')
sig_re = re.compile('^(\w+)\s+([^:]+):\(([^)]*)\)(.+)$')
def _parse_sig(self, line):
if line.startswith('class'):
_, name = line.split()
return "class " + self._abbrev(name)
elif line.startswith('String'):
return 'string'
elif line.startswith('Field'):
m = self.field_re.match(line)
if not m: raise Exception("failed to match %r" % line)
name = self._abbrev(m.group(1))
result = self._parse_args(m.group(2))[0]
#return "field %s -> %s" % (name, result)
return "field %s %s" % (result, name)
else:
m = self.sig_re.match(line)
if not m: raise Exception("failed to match %r" % line)
typ = self.typs[m.group(1)]
name = self._abbrev(m.group(2))
args = self._parse_args(m.group(3))
result = self._parse_args(m.group(4))[0]
#return "%s %s (%s) -> %s" % (typ, name, ', '.join(args), result)
return "%s %s %s (%s)" % (typ, result, name, ', '.join(args))
def _parse_instruction(self, line):
if '//' in line:
real, x = line.split('//')
sig = self._parse_sig(x)
else:
real, sig = line, None
toks = real.split()
n = toks[0][:-1]
ins = toks[1]
rest = [t[:-1] for t in toks[2:]]
args = " ".join(rest)
if sig:
#return "%4s %-22s -- %s" % (n, ins + " " + args, sig)
return "%4s %-22s --%s" % (n, ins + " " + args, sig)
else:
return "%4s %-22s" % (n, ins + " " + args)
def _execute(self, w, **vargs):
cp = 'target/scala-2.9.1.final/classes' #FIXME
argv = ['javap', '-classpath', cp, '-c', vargs['classname']]
p = Popen(argv, stdout=PIPE, stderr=STDOUT)
lines = p.stdout.readlines()
obj = None
methods = []
curr = None
instructions = []
for i, line in enumerate(lines):
line = line.strip()
if i == 0:
continue
elif i == 1:
obj = self._parse_obj(line)
elif line == "Code:":
continue
elif line == "":
if curr:
methods.append((curr, instructions))
curr = None
instructions = []
elif self.ins_re.match(line):
instructions.append(self._parse_instruction(line))
else:
curr = self._parse_method(line)
outlines = []
if self.pkg:
outlines.append('package ' + self.pkg[:-1])
outlines.append('')
outlines.append(obj + " {")
for method, instructions in methods:
if instructions:
outlines.append(" " + method + " {")
for ins in instructions:
outlines.append(" " + ins)
outlines.append(" }")
else:
outlines.append(" " + method + " {}")
outlines.append("}")
output = "\n".join(outlines)
#w.application.data_buffer("*Javap*", output, switch_to=True)
w.application.data_buffer("*Javap*", output, modename='javap', switch_to=True)
# white is for delimiters, operators, numbers
default = ('default', 'default')
@ -328,7 +337,8 @@ class Scala(Fundamental):
tabbercls = ScalaTabber
grammar = ScalaGrammar
commentc = '//'
actions = [ScalaStart, ScalaDocBrowse, ScalaDocLookup, ScalaGetType, ScalaGotoDefinition]
#actions = [ScalaStart, ScalaDocBrowse, ScalaDocLookup, ScalaGetType, ScalaGotoDefinition]
actions = [ScalaStart, ScalaDecompile]
opentokens = ('delimiter', 'sub.start', 'sub.sub.start', 'sub.sub.sub.start')
opentags = {'(': ')', '[': ']', '{': '}'}
closetokens = ('delimiter', 'sub.end', 'sub.sub.end', 'sub.sub.sub.end')
@ -365,8 +375,8 @@ class Scala(Fundamental):
}
_bindings = {
'scala-get-type': ('M-,',),
'scala-goto-definition': ('C-c ,',),
#'scala-get-type': ('M-,',),
#'scala-goto-definition': ('C-c ,',),
'close-paren': (')',),
'close-brace': ('}',),
'close-bracket': (']',),