Created
March 4, 2017 02:25
-
-
Save nrosner/70bed930684f467dddd5fc68cbf39e82 to your computer and use it in GitHub Desktop.
A Python script to count the number of classes and methods in a .jar file using javap.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import sys, os, re, zipfile, subprocess | |
# Rudimentary parsing of javap output | |
regex_type = r'[a-zA-Z0-9.<>, ?$[\]]+' | |
regex_class = r'(?:(public|private|protected) )?((?:(?:static|abstract|final) ?)*)(class|interface) (' + regex_type + ') (?:extends ((?:' + regex_type +'),?)+ )?(?:implements ((?:[a-zA-Z0-9\\.<>\\?\\$])+,?)+ )?{([^}]+)}' | |
regex_method = r'(?:(public|private|protected) )?((?:static|abstract|final) ?)*(?:(' + regex_type + ') )?([a-zA-Z]+)\\(([^\\)]*)\\)' | |
regex_field = r'(?:(public|private|protected) )?((?:(?:static|abstract|final) ?)*)(' + regex_type + ') ([a-zA-Z0-9]+)' | |
RE_TYPE = re.compile(regex_type) | |
RE_CLASS = re.compile(regex_class) | |
RE_METHOD = re.compile(regex_method) | |
RE_FIELD = re.compile(regex_field) | |
def classnamesfromjar(jarfilepath): | |
'''Return list of fully-qualified classnames.''' | |
with zipfile.ZipFile(jarfilepath, 'r') as zf: | |
for zi in zf.infolist(): | |
fn = zi.filename | |
if fn.endswith('.class'): | |
dotted = fn.replace('/', '.') | |
assert dotted.endswith('.class') | |
yield dotted[:len(dotted)-len('.class')] | |
def runjavap(jarfilepath): | |
'''Run javap on this jar for all the classes in the jar. Return the output of the javap command.''' | |
classnames = classnamesfromjar(jarfilepath) | |
return runjavap_aux(classpath=jarfilepath, classnames=classnames) | |
def runjavap_aux(classpath, classnames): | |
'''Run javap on the given classpath for the given classnames. Return the output of the javap command.''' | |
cmdarglist = ['javap', '-classpath', classpath] + list(classnames) | |
p = subprocess.Popen(cmdarglist, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
tup_output = p.communicate() | |
stdout = tup_output[0] | |
stderr = tup_output[1] | |
return stdout | |
def countclassesandmethodsinjarfile(jarfilepath): | |
'''Return a tuple (nc, nm) with the number of classes and number of methods in the jarfile.''' | |
javap_output = runjavap(jarfilepath) | |
nc = len(RE_CLASS.findall(javap_output)) | |
nm = len(RE_METHOD.findall(javap_output)) | |
return nc, nm | |
if __name__ == '__main__': | |
for jarpathname in sys.argv[1:]: | |
nc, nm = countclassesandmethodsinjarfile(jarpathname) | |
print("%s\t%d\t%d" % (jarpathname, nc, nm)) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment