Created
February 20, 2012 23:04
-
-
Save SpaceManiac/1872149 to your computer and use it in GitHub Desktop.
A Python/Solum script to check Bukkit plugins against a given Bukkit or CraftBukkit jar.
This file contains 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/python | |
# compatchecker.py | |
# | |
# Scans .jar files passed on command line for compatability with a given | |
# Bukkit or CraftBukkit jar. | |
# | |
# Requires Solum (https://github.com/TkTech/Solum) | |
# sudo pip install git+git://github.com/TkTech/Solum.git | |
import sys, urllib2, re | |
from glob import glob | |
from solum import JarFile, ClassFile, ConstantType | |
from os import path | |
# finding things | |
craftbukkit = False | |
def find(name): | |
if craftbukkit: | |
return name.startswith('org/bukkit/') or name.startswith('net/minecraft/') | |
else: | |
return name.startswith('org/bukkit') and not name.startswith('org/bukkit/craftbukkit/') | |
def findClass(constant): | |
return find(constant['name']['value']) | |
def findReference(constant): | |
return find(constant['class']['name']['value']) | |
def recurseMethod(jar, bCf, bMethod, signature): | |
if bCf.methods.find_one(name=bMethod, args=signature[0], returns=signature[1]): | |
return True | |
for interface in bCf.interfaces: | |
interface = interface['name']['value'] | |
if find(interface) and recurseMethod(jar, jar.open_class(interface), bMethod, signature): | |
return True | |
return find(bCf.superclass) and recurseMethod(jar, jar.open_class(bCf.superclass), bMethod, signature) | |
# formatting things | |
typeMap = { | |
'V': 'void', | |
'Z': 'boolean', | |
'I': 'int', | |
'D': 'double', | |
'J': 'long', | |
'F': 'float', | |
'S': 'short', | |
'B': 'byte', | |
'C': 'char' | |
} | |
def formatType(type): | |
if type in typeMap: | |
return typeMap[type] | |
elif type.startswith("["): | |
return formatType(type[1:]) + '[]' | |
elif type.startswith("L"): | |
return type[1:-1].replace('/', '.') | |
elif type == "": | |
return "" | |
else: | |
print "Unmappable type '%s'" % type | |
return type | |
def makeSignature(raw): | |
i = raw.find(')') | |
rawArgs = raw[1:i] | |
rawRet = raw[i+1:] | |
ret = formatType(rawRet) | |
args = [] | |
i = 0 | |
isArray = "" | |
while i < len(rawArgs): | |
if rawArgs[i] == 'L': | |
i2 = rawArgs.find(';', i) | |
args.append(formatType(rawArgs[i:i2+1]) + isArray) | |
isArray = "" | |
i = i2 + 1 | |
elif rawArgs[i] == '[': | |
isArray = "[]" | |
i += 1 | |
else: | |
args.append(formatType(rawArgs[i]) + isArray) | |
isArray = "" | |
i += 1 | |
return (tuple(args), ret) | |
def printableArgs(args): | |
return '(' + ', '.join(args) + ')' | |
def prettyName(cname): | |
return cname[:-6].replace("/", ".") | |
# main | |
if __name__ == "__main__": | |
verbose = quiet = False | |
files = [] | |
cb = "" | |
for arg in sys.argv[1:]: | |
if arg == "-v": | |
verbose = True | |
quiet = False | |
elif arg == "-q": | |
quiet = True | |
verbose = False | |
elif cb == "": | |
cb = arg | |
else: | |
files.append(arg) | |
if len(files) == 1 and files[0].find('*') != -1: | |
files = glob(files[0]) | |
if len(files) == 0 or cb == "": | |
print "usage: %s [-vq] <bukkit.jar or craftbukkit.jar> <plugin.jar> [plugin.jar ...]" % sys.argv[0] | |
print "ex: %s craftbukkit-1.1-R5-SNAPHOST.jar plugins/*.jar" % sys.argv[0] | |
sys.exit(0) | |
bukkit = JarFile(cb) | |
try: | |
bukkit.open_class('org/bukkit/Bukkit.class') | |
except KeyError: | |
print 'First argument must be a valid bukkit.jar or craftbukkit.jar' | |
sys.exit(0) | |
try: | |
bukkit.open_class('org/bukkit/craftbukkit/CraftServer.class') | |
craftbukkit = True | |
print 'You have passed a craftbukkit.jar, CB and NMS references will be checked' | |
except KeyError: | |
craftbukkit = False | |
print 'You have passed a bukkit.jar, CB and NMS references will not be checked' | |
total = numBad = 0 | |
for filename in files: | |
total += 1 | |
print "Scanning %s..." % filename | |
jar = JarFile(filename) | |
jarBadrefs = 0 | |
for cname in jar.class_list: | |
cf = jar.open_class(cname) | |
badClasses = [] | |
badRefs = [] | |
# classes | |
for bClass in cf.constants.find(ConstantType.CLASS, f=findClass): | |
bClass = bClass["name"]["value"] | |
try: | |
bukkit.open_class(bClass) | |
except KeyError: | |
badClasses.append(bClass) | |
badRefs.append("class " + bClass.replace('/', '.')) | |
# methods | |
temp = cf.constants.find(ConstantType.METHOD_REF, f=findReference) | |
temp += cf.constants.find(ConstantType.INTERFACE_METHOD_REF, f=findReference) | |
for bMethod in temp: | |
signature = makeSignature(bMethod["name_and_type"]["descriptor"]["value"]) | |
bClass = bMethod["class"]["name"]["value"] | |
bMethod = bMethod["name_and_type"]["name"]["value"] | |
if not bClass in badClasses: | |
if not recurseMethod(bukkit, bukkit.open_class(bClass), bMethod, signature): | |
badRefs.append("method %s %s%s in %s" % | |
(signature[1], bMethod, printableArgs(signature[0]), bClass.replace('/', '.'))) | |
# fields | |
for bField in cf.constants.find(ConstantType.FIELD_REF, f=findReference): | |
bClass = bField["class"]["name"]["value"] | |
bField = bField["name_and_type"]["name"]["value"] | |
if not bClass in badClasses: | |
bCf = bukkit.open_class(bClass) | |
try: | |
bCf.fields[bField] | |
except KeyError: | |
badRefs.append("field %s in %s" % (bField, bClass.replace('/', '.'))) | |
jarBadrefs += len(badRefs) | |
if len(badRefs) > 0: | |
if verbose: | |
print " %s has %d bad references:" % (prettyName(cname), len(badRefs)) | |
for r in badRefs: | |
print " " + r | |
elif not quiet: | |
print " %s has %d bad references" % (prettyName(cname), len(badRefs)) | |
if quiet and jarBadrefs > 0: | |
print " %s has a total of %d bad references" % (path.basename(filename), jarBadrefs) | |
elif not quiet and jarBadrefs == 0: | |
print " %s has no bad references!" % path.basename(filename) | |
if jarBadrefs > 0: | |
numBad += 1 | |
print "%d of %d plugins had bad references (%d%%)" % (numBad, total, numBad * 100 / total) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment