Add a class-loading module. master
authorFredrik Tolf <fredrik@dolda2000.com>
Mon, 10 Apr 2023 16:02:12 +0000 (18:02 +0200)
committerFredrik Tolf <fredrik@dolda2000.com>
Mon, 10 Apr 2023 16:02:12 +0000 (18:02 +0200)
classfile/classpath.py [new file with mode: 0644]
classfile/file.py

diff --git a/classfile/classpath.py b/classfile/classpath.py
new file mode 100644 (file)
index 0000000..2b82406
--- /dev/null
@@ -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
index 91f83f7..819c6a4 100644 (file)
@@ -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)
 
@@ -599,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: