From 188fa298c78404f0cb1ed5291cfa53e5be0fb4b2 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Sat, 22 Oct 2016 07:54:58 +0200 Subject: [PATCH 1/3] Handle the invokedynamic-related constant-pool entries. --- classfile/file.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/classfile/file.py b/classfile/file.py index 91f83f7..d361b47 100644 --- a/classfile/file.py +++ b/classfile/file.py @@ -101,6 +101,32 @@ class sig(object): def __eq__(s, o): return isinstance(o, sig) and o.nm == s.nm and o.tp == s.tp +class methodhandle(object): + def __init__(self, kind, ref): + self.kind = kind + self.ref = ref + def __hash__(self): + return hash(methodhandle) + self.kind * 31 + self.ref + def __eq__(s, o): + return isinstance(o, methodhandle) and o.kind == s.kind and o.ref == s.ref + +class methodtype(object): + def __init__(self, desc): + self.desc = desc + def __hash__(self): + return hash(methodhandle) + self.desc + def __eq__(s, o): + return isinstance(o, methodtype) and o.desc == s.desc + +class callsite(object): + def __init__(self, boot, sig): + self.boot = boot + self.sig = sig + def __hash__(self): + return hash(callsite) + self.boot * 31 + self.sig + def __eq__(s, o): + return isinstance(o, callsite) and o.boot == s.boot and o.sig == s.sig + class fieldref(object): def __init__(self, cls, sig): self.cls = cls @@ -235,6 +261,12 @@ class classfile(object): return imethodref(buf.uint16(), buf.uint16()), False elif t == CONSTANT_NameAndType: return sig(buf.uint16(), buf.uint16()), False + elif t == CONSTANT_MethodHandle: + return methodhandle(buf.uint8(), buf.uint16()), False + elif t == CONSTANT_MethodType: + return methodtype(buf.uint16()), False + elif t == CONSTANT_InvokeDynamic: + return callsite(buf.uint16(), buf.uint16()), False else: raise binfmt.fmterror("unknown constant tag: " + str(t)) @@ -262,6 +294,12 @@ class classfile(object): buf.uint8(CONSTANT_InterfaceMethodref).uint16(const.cls).uint16(const.sig) elif isinstance(const, sig): buf.uint8(CONSTANT_NameAndType).uint16(const.nm).uint16(const.tp) + elif isinstance(const, methodhandle): + buf.uint8(CONSTANT_MethodHandle).uint8(const.kind).uint16(const.ref) + elif isinstance(const, methodtype): + buf.uint8(CONSTANT_MethodType).uint16(const.desc) + elif isinstance(const, callsite): + buf.uint8(CONSTANT_InvokeDynamic).uint16(const.boot).uint16(const.sig) else: raise Exception("unexpected object type in constant pool: " + const) -- 2.11.0 From 9d8b614097cc94a30b894d88fefb37195fbffcbf Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Mon, 10 Apr 2023 18:01:47 +0200 Subject: [PATCH 2/3] Handle classes with nil superclass. Ie. java.lang.Object. --- classfile/file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classfile/file.py b/classfile/file.py index d361b47..819c6a4 100644 --- a/classfile/file.py +++ b/classfile/file.py @@ -637,7 +637,7 @@ class classfile(object): self.super = buf.uint16() if not self.checkcp(self.this, classref): raise binfmt.fmterror("invalid class name reference") - if not self.checkcp(self.super, classref): + if not self.checkcp(self.super, classref) and self.cp[self.super] is not None: raise binfmt.fmterror("invalid super-class reference") iflen = buf.uint16() while len(self.ifaces) < iflen: -- 2.11.0 From 4c04aee9f8d854260261f8b971c3e71ea33810da Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Mon, 10 Apr 2023 18:02:12 +0200 Subject: [PATCH 3/3] Add a class-loading module. --- classfile/classpath.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 classfile/classpath.py diff --git a/classfile/classpath.py b/classfile/classpath.py new file mode 100644 index 0000000..2b82406 --- /dev/null +++ b/classfile/classpath.py @@ -0,0 +1,48 @@ +import os, zipfile +from . import file + +class dir(object): + def __init__(self, path): + self.path = path + + def get(self, name): + if '.' in name: + raise FileNotFoundError(name) + els = name.split('/') + fn = os.path.join(self.path, *els[:-1], els[-1] + ".class") + with open(fn, "rb") as fp: + return file.classfile.load(fp) + +class jar(object): + def __init__(self, filename): + self.filename = filename + + def get(self, name): + with zipfile.ZipFile(self.filename) as jar: + fn = name + ".class" + try: + fp = jar.open(fn, "r") + except KeyError: + raise FileNotFoundError(name) + with fp: + return file.classfile.load(fp) + +class path(object): + def __init__(self, *ents, caching=True): + self.ents = ents + self.cache = {} if caching else None + + def get(self, name): + if self.cache is not None and name in self.cache: + return self.cache[name] + for ent in self.ents: + try: + ret = ent.get(name) + break; + except FileNotFoundError: + pass + else: + raise FileNotFoundError(name) + if self.cache is not None: + self.cache[name] = ret + return ret -- 2.11.0