Add a class-loading module.
[pycfml.git] / classfile / file.py
1 import collections
2 from . import binfmt
3
4 ACC_PUBLIC       = 0x0001
5 ACC_PRIVATE      = 0x0002
6 ACC_PROTECTED    = 0x0004
7 ACC_STATIC       = 0x0008
8 ACC_FINAL        = 0x0010
9 ACC_SUPER        = 0x0020
10 ACC_SYNCHRONIZED = 0x0020
11 ACC_VOLATILE     = 0x0040
12 ACC_BRIDGE       = 0x0040
13 ACC_TRANSIENT    = 0x0080
14 ACC_VARARGS      = 0x0080
15 ACC_NATIVE       = 0x0100
16 ACC_INTERFACE    = 0x0200
17 ACC_ABSTRACT     = 0x0400
18 ACC_STRICT       = 0x0800
19 ACC_SYNTHETIC    = 0x1000
20 ACC_ANNOTATION   = 0x2000
21 ACC_ENUM         = 0x4000
22
23 CONSTANT_Class              = 7
24 CONSTANT_Fieldref           = 9
25 CONSTANT_Methodref          = 10
26 CONSTANT_InterfaceMethodref = 11
27 CONSTANT_String             = 8
28 CONSTANT_Integer            = 3
29 CONSTANT_Float              = 4
30 CONSTANT_Long               = 5
31 CONSTANT_Double             = 6
32 CONSTANT_NameAndType        = 12
33 CONSTANT_Utf8               = 1
34 CONSTANT_MethodHandle       = 15
35 CONSTANT_MethodType         = 16
36 CONSTANT_InvokeDynamic      = 18
37
38 version = collections.namedtuple("version", ["major", "minor"])
39 version.__eq__ = lambda s, o: s.major == o.major and s.minor == o.minor
40 version.__ne__ = lambda s, o: s.major != o.major or s.minor != o.minor
41 version.__lt__ = lambda s, o: (s.major < o.major) or (s.major == o.major and s.minor < o.minor)
42 version.__gt__ = lambda s, o: (s.major > o.major) or (s.major == o.major and s.minor > o.minor)
43 version.__le__ = lambda s, o: (s.major < o.major) or (s.major == o.major and s.minor <= o.minor)
44 version.__ge__ = lambda s, o: (s.major > o.major) or (s.major == o.major and s.minor >= o.minor)
45 version.J5 = version(49, 0)
46 version.J6 = version(50, 0)
47 version.J7 = version(51, 0)
48 version.J8 = version(52, 0)
49
50 class constint(object):
51     def __init__(self, val):
52         self.val = val
53     def __hash__(self):
54         return hash(constint) + self.val
55     def __eq__(s, o):
56         return isinstance(o, constint) and o.val == s.val
57 class constfloat(object):
58     def __init__(self, val):
59         self.val = val
60     def __hash__(self):
61         return hash(constfloat) + self.val
62     def __eq__(s, o):
63         return isinstance(o, constfloat) and o.val == s.val
64 class constlong(object):
65     def __init__(self, val):
66         self.val = val
67     def __hash__(self):
68         return hash(constlong) + self.val
69     def __eq__(s, o):
70         return isinstance(o, constlong) and o.val == s.val
71 class constdouble(object):
72     def __init__(self, val):
73         self.val = val
74     def __hash__(self):
75         return hash(constdouble) + self.val
76     def __eq__(s, o):
77         return isinstance(o, constdouble) and o.val == s.val
78
79 class conststr(object):
80     def __init__(self, idx):
81         self.idx = idx
82     def __hash__(self):
83         return hash(conststr) + self.idx
84     def __eq__(s, o):
85         return isinstance(o, conststr) and o.idx == s.idx
86
87 class classref(object):
88     def __init__(self, nm):
89         self.nm = nm
90     def __hash__(self):
91         return hash(classref) + self.nm
92     def __eq__(s, o):
93         return isinstance(o, classref) and o.nm == s.nm
94
95 class sig(object):
96     def __init__(self, nm, tp):
97         self.nm = nm
98         self.tp = tp
99     def __hash__(self):
100         return hash(sig) + self.nm * 31 + self.tp
101     def __eq__(s, o):
102         return isinstance(o, sig) and o.nm == s.nm and o.tp == s.tp
103
104 class methodhandle(object):
105     def __init__(self, kind, ref):
106         self.kind = kind
107         self.ref = ref
108     def __hash__(self):
109         return hash(methodhandle) + self.kind * 31 + self.ref
110     def __eq__(s, o):
111         return isinstance(o, methodhandle) and o.kind == s.kind and o.ref == s.ref
112
113 class methodtype(object):
114     def __init__(self, desc):
115         self.desc = desc
116     def __hash__(self):
117         return hash(methodhandle) + self.desc
118     def __eq__(s, o):
119         return isinstance(o, methodtype) and o.desc == s.desc
120
121 class callsite(object):
122     def __init__(self, boot, sig):
123         self.boot = boot
124         self.sig = sig
125     def __hash__(self):
126         return hash(callsite) + self.boot * 31 + self.sig
127     def __eq__(s, o):
128         return isinstance(o, callsite) and o.boot == s.boot and o.sig == s.sig
129
130 class fieldref(object):
131     def __init__(self, cls, sig):
132         self.cls = cls
133         self.sig = sig
134     def __hash__(self):
135         return hash(fieldref) + self.cls * 31 + self.sig
136     def __eq__(s, o):
137         return isinstance(o, fieldref) and o.cls == s.cls and o.sig == s.sig
138
139 class methodref(object):
140     def __init__(self, cls, sig):
141         self.cls = cls
142         self.sig = sig
143     def __hash__(self):
144         return hash(methodref) + self.cls * 31 + self.sig
145     def __eq__(s, o):
146         return isinstance(o, methodref) and o.cls == s.cls and o.sig == s.sig
147
148 class imethodref(object):
149     def __init__(self, cls, sig):
150         self.cls = cls
151         self.sig = sig
152     def __hash__(self):
153         return hash(imethodref) + self.cls * 31 + self.sig
154     def __eq__(s, o):
155         return isinstance(o, imethodref) and o.cls == s.cls and o.sig == s.sig
156
157 class field(object):
158     def __init__(self, acc, nm, descr):
159         self.acc = acc
160         self.nm = nm
161         self.descr = descr
162         self.const = None
163         self.syn = False
164         self.sig = None
165         self.deprecated = False
166         self.rtann = []
167         self.cpann = []
168         self.attrs = []
169
170 class localdef(object):
171     def __init__(self, start, end, nm, descr, reg):
172         self.start = start
173         self.end = end
174         self.nm = nm
175         self.descr = descr
176         self.reg = reg
177
178 class code(object):
179     def __init__(self):
180         self.maxstack = 0
181         self.maxlocals = 0
182         self.code = b""
183         self.exctab = []
184         self.lintab = None
185         self.locals = None
186         self.tlocals = None
187         self.attrs = []
188
189 class method(object):
190     def __init__(self, acc, nm, descr):
191         self.acc = acc
192         self.nm = nm
193         self.descr = descr
194         self.code = None
195         self.throws = []
196         self.syn = False
197         self.sig = None
198         self.deprecated = False
199         self.rtann = []
200         self.cpann = []
201         self.prtann = None
202         self.pcpann = None
203         self.anndef = None
204         self.attrs = []
205
206 class annotation(object):
207     def __init__(self, tp):
208         self.tp = tp
209         self.vals = {}
210
211 class innerclass(object):
212     def __init__(self, cls, outer, nm, acc):
213         self.cls = cls
214         self.outer = outer
215         self.nm = nm
216         self.acc = acc
217
218 class classfile(object):
219     MAGIC = 0xCAFEBABE
220
221     def __init__(self, ver, access=None):
222         self.ver = ver
223         self.cp = []
224         self.access = access
225         self.this = None
226         self.super = None
227         self.ifaces = []
228         self.fields = []
229         self.methods = []
230         self.srcfile = None
231         self.innerclasses = []
232         self.enclosingmethod = None
233         self.syn = False
234         self.sig = None
235         self.deprecated = False
236         self.rtann = []
237         self.cpann = []
238         self.attrs = []
239
240     def loadconstant(self, buf):
241         t = buf.uint8()
242         if t == CONSTANT_Utf8:
243             return binfmt.mutf8dec(buf.splice(buf.uint16())), False
244         elif t == CONSTANT_Class:
245             return classref(buf.uint16()), False
246         elif t == CONSTANT_String:
247             return conststr(buf.uint16()), False
248         elif t == CONSTANT_Integer:
249             return constint(buf.int32()), False
250         elif t == CONSTANT_Float:
251             return constfloat(buf.float32()), False
252         elif t == CONSTANT_Long:
253             return constlong(buf.int64()), True
254         elif t == CONSTANT_Double:
255             return constdouble(buf.float64()), True
256         elif t == CONSTANT_Fieldref:
257             return fieldref(buf.uint16(), buf.uint16()), False
258         elif t == CONSTANT_Methodref:
259             return methodref(buf.uint16(), buf.uint16()), False
260         elif t == CONSTANT_InterfaceMethodref:
261             return imethodref(buf.uint16(), buf.uint16()), False
262         elif t == CONSTANT_NameAndType:
263             return sig(buf.uint16(), buf.uint16()), False
264         elif t == CONSTANT_MethodHandle:
265             return methodhandle(buf.uint8(), buf.uint16()), False
266         elif t == CONSTANT_MethodType:
267             return methodtype(buf.uint16()), False
268         elif t == CONSTANT_InvokeDynamic:
269             return callsite(buf.uint16(), buf.uint16()), False
270         else:
271             raise binfmt.fmterror("unknown constant tag: " + str(t))
272
273     def saveconstant(self, buf, const):
274         if isinstance(const, str):
275             enc = binfmt.mutf8enc(const)
276             buf.uint8(CONSTANT_Utf8).uint16(len(enc)).extend(enc)
277         elif isinstance(const, classref):
278             buf.uint8(CONSTANT_Class).uint16(const.nm)
279         elif isinstance(const, conststr):
280             buf.uint8(CONSTANT_String).uint16(const.idx)
281         elif isinstance(const, constint):
282             buf.uint8(CONSTANT_Integer).int32(const.val)
283         elif isinstance(const, constfloat):
284             buf.uint8(CONSTANT_Float).float32(const.val)
285         elif isinstance(const, constlong):
286             buf.uint8(CONSTANT_Long).int64(const.val)
287         elif isinstance(const, constdouble):
288             buf.uint8(CONSTANT_Double).float64(const.val)
289         elif isinstance(const, fieldref):
290             buf.uint8(CONSTANT_Fieldref).uint16(const.cls).uint16(const.sig)
291         elif isinstance(const, methodref):
292             buf.uint8(CONSTANT_Methodref).uint16(const.cls).uint16(const.sig)
293         elif isinstance(const, imethodref):
294             buf.uint8(CONSTANT_InterfaceMethodref).uint16(const.cls).uint16(const.sig)
295         elif isinstance(const, sig):
296             buf.uint8(CONSTANT_NameAndType).uint16(const.nm).uint16(const.tp)
297         elif isinstance(const, methodhandle):
298             buf.uint8(CONSTANT_MethodHandle).uint8(const.kind).uint16(const.ref)
299         elif isinstance(const, methodtype):
300             buf.uint8(CONSTANT_MethodType).uint16(const.desc)
301         elif isinstance(const, callsite):
302             buf.uint8(CONSTANT_InvokeDynamic).uint16(const.boot).uint16(const.sig)
303         else:
304             raise Exception("unexpected object type in constant pool: " + const)
305
306     def checkcp(self, idx, tp):
307         return 0 <= idx < len(self.cp) and isinstance(self.cp[idx], tp)
308
309     def intern(self, const, new=Exception):
310         for i, cur in enumerate(self.cp):
311             if cur == const:
312                 return i
313         if new == Exception:
314             raise Exception("constant not present in pool: " + const)
315         if new:
316             self.cp.append(const)
317             return len(self.cp) - 1
318         else:
319             return None
320
321     def loadattr(self, buf):
322         nm = buf.uint16()
323         if not self.checkcp(nm, str):
324             raise binfmt.fmterror("invalid attribute name reference")
325         return nm, binfmt.decbuf(buf.splice(buf.uint32()))
326
327     def saveattrs(self, buf, attrs):
328         buf.uint16(len(attrs))
329         for nm, data in attrs:
330             buf.uint16(nm).uint32(len(data)).extend(data)
331
332     def loadannval(self, buf):
333         t = chr(buf.uint8())
334         if t in "BCDFIJSZs":
335             return buf.uint16()
336         elif t == "e":
337             return (buf.uint16(), buf.uint16())
338         elif t == "c":
339             return classref(buf.uint16()) # XXX, but meh
340         elif t == "@":
341             return loadannotation(buf)
342         elif t == "[":
343             return [self.loadannval(buf) for i in range(buf.uint16())]
344         else:
345             raise binfmt.fmterror("unknown annotation-value type tag: " + t)
346
347     def saveannval(self, buf, val):
348         if isinstance(val, int):
349             const = self.cp[val]
350             if isinstance(const, str):
351                 buf.uint8(ord('s')).uint16(val)
352             else:
353                 raise Exception("unexpected constant type in annotation value: " + const)
354         elif isinstance(val, tuple) and len(val) == 2:
355             buf.uint8(ord('e')).uint16(val[0]).uint16(val[1])
356         elif isinstance(val, classref):
357             buf.uint8(ord('c')).uint16(val.nm)
358         elif isinstance(val, annotation):
359             buf.uint8(ord('@'))
360             saveannotation(buf, val)
361         elif isinstance(val, list):
362             buf.uint8(ord('['))
363             for sval in val: self.saveannval(buf, sval)
364         else:
365             raise Exception("unexpected annotation value type: " + val)
366
367     def loadannotation(self, buf):
368         tp = buf.uint16()
369         if not self.checkcp(tp, str):
370             raise binfmt.fmterror("invalid annotation type reference")
371         ret = annotation(tp)
372         nval = buf.uint16()
373         for i in range(nval):
374             nm = buf.uint16()
375             if not self.checkcp(nm, str):
376                 raise binfmt.fmterror("invalid annotation-value name reference")
377             ret.vals[nm] = self.loadannval(buf)
378         return ret
379
380     def saveannotation(self, buf, ann):
381         buf.uint16(ann.tp)
382         buf.uint16(len(ann.vals))
383         for key, val in ann.vals.items():
384             buf.uint16(key)
385             self.saveannval(buf, val)
386
387     def loadfield(self, buf):
388         acc = buf.uint16()
389         nm = buf.uint16()
390         if not self.checkcp(nm, str):
391             raise binfmt.fmterror("invalid field name reference")
392         descr = buf.uint16()
393         if not self.checkcp(descr, str):
394             raise binfmt.fmterror("invalid field descriptor reference")
395         ret = field(acc, nm, descr)
396         nattr = buf.uint16()
397         for i in range(nattr):
398             nm, data = self.loadattr(buf)
399             pnm = self.cp[nm]
400             if pnm == "ConstantValue":
401                 ret.const = data.uint16()
402             elif pnm == "Synthetic":
403                 ret.syn = True
404             elif pnm == "Signature":
405                 ret.sig = data.uint16()
406             elif pnm == "Deprecated":
407                 ret.deprecated = True
408             elif pnm == "RuntimeVisibleAnnotations":
409                 for o in range(data.uint16()):
410                     ret.rtann.append(self.loadannotation(data))
411             elif pnm == "RuntimeInvisibleAnnotations":
412                 for o in range(data.uint16()):
413                     ret.cpann.append(self.loadannotation(data))
414             else:
415                 ret.attrs.append((nm, data.splice()))
416         return ret
417
418     def savefield(self, buf, field):
419         buf.uint16(field.acc)
420         buf.uint16(field.nm).uint16(field.descr)
421         attrs = list(field.attrs)
422         enc = binfmt.encbuf
423         if field.const is not None:
424             attrs.append((self.intern("ConstantValue"), enc().uint16(field.const)))
425         if field.syn:
426             attrs.append((self.intern("Synthetic"), b""))
427         if field.sig is not None:
428             attrs.append((self.intern("Signature"), enc().uint16(field.sig)))
429         if field.deprecated:
430             attrs.append((self.intern("Deprecated"), b""))
431         if len(field.rtann) > 0:
432             data = enc()
433             data.uint16(len(field.rtann))
434             for ann in field.rtann: self.saveannotation(data, ann)
435             attrs.append((self.intern("RuntimeVisibleAnnotations"), data))
436         if len(field.cpann) > 0:
437             data = enc()
438             data.uint16(len(field.cpann))
439             for ann in field.cpann: self.saveannotation(data, ann)
440             attrs.append((self.intern("RuntimeInvisibleAnnotations"), data))
441         self.saveattrs(buf, attrs)
442
443     def loadcode(self, buf):
444         ret = code()
445         ret.maxstack = buf.uint16()
446         ret.maxlocals = buf.uint16()
447         ret.code = buf.splice(buf.uint32())
448         for i in range(buf.uint16()):
449             estart = buf.uint16()
450             eend = buf.uint16()
451             ehnd = buf.uint16()
452             ctp = buf.uint16()
453             if not (ctp == 0 or self.checkcp(ctp, classref)):
454                 raise binfmt.fmterror("invalid exception-catch reference")
455             ret.exctab.append((estart, eend, ehnd, ctp))
456         nattr = buf.uint16()
457         for i in range(nattr):
458             nm, data = self.loadattr(buf)
459             pnm = self.cp[nm]
460             if pnm == "LineNumberTable":
461                 lintab = []
462                 for o in range(data.uint16()):
463                     pc = data.uint16()
464                     ln = data.uint16()
465                     lintab.append((pc, ln))
466                 ret.lintab = lintab
467             elif pnm in ("LocalVariableTable", "LocalVariableTypeTable"):
468                 locals = []
469                 for o in range(data.uint16()):
470                     start = data.uint16()
471                     ln = data.uint16()
472                     nm = data.uint16()
473                     descr = data.uint16()
474                     reg = data.uint16()
475                     if not self.checkcp(nm, str):
476                         raise binfmt.fmterror("invalid local variable name reference")
477                     if not self.checkcp(descr, str):
478                         raise binfmt.fmterror("invalid local variable descriptor reference")
479                     locals.append(localdef(start, start + ln, nm, descr, reg))
480                 if nm == "LocalVariableTypeTable":
481                     ret.tlocals = locals
482                 else:
483                     ret.locals = locals
484             else:
485                 ret.attrs.append((nm, data.splice()))
486         return ret
487
488     def savecode(self, buf, code):
489         buf.uint16(code.maxstack).uint16(code.maxlocals)
490         buf.uint32(len(code.code)).extend(code.code)
491         buf.uint16(len(code.exctab))
492         for estart, eend, ehnd, ctp in code.exctab:
493             buf.uint16(estart).uint16(eend).uint16(ehnd).uint16(ctp)
494         attrs = list(code.attrs)
495         enc = binfmt.encbuf
496         if code.lintab is not None:
497             data = enc()
498             data.uint16(len(code.lintab))
499             for pc, ln in code.lintab:
500                 data.uint16(pc).uint16(ln)
501             attrs.append((self.intern("LineNumberTable"), data))
502         def savelocals(ltab):
503             data = enc()
504             data.uint16(len(ltab))
505             for local in ltab:
506                 data.uint16(local.start).uint16(local.end - local.start).uint16(local.nm).uint16(local.descr).uint16(local.reg)
507             return data
508         if code.locals is not None:
509             attrs.append((self.intern("LocalVariableTable"), savelocals(code.locals)))
510         if code.tlocals is not None:
511             attrs.append((self.intern("LocalVariableTypeTable"), savelocals(code.tlocals)))
512         self.saveattrs(buf, attrs)
513
514     def loadmethod(self, buf):
515         acc = buf.uint16()
516         nm = buf.uint16()
517         if not self.checkcp(nm, str):
518             raise binfmt.fmterror("invalid field name reference")
519         descr = buf.uint16()
520         if not self.checkcp(descr, str):
521             raise binfmt.fmterror("invalid field descriptor reference")
522         ret = method(acc, nm, descr)
523         nattr = buf.uint16()
524         for i in range(nattr):
525             nm, data = self.loadattr(buf)
526             pnm = self.cp[nm]
527             if pnm == "Code":
528                 ret.code = self.loadcode(data)
529             elif pnm == "Exceptions":
530                 for o in range(data.uint16()):
531                     eref = data.uint16()
532                     if not self.checkcp(eref, classref):
533                         raise binfmt.fmterror("invalid exception reference")
534                     ret.throws.append(eref)
535             elif pnm == "Synthetic":
536                 ret.syn = True
537             elif pnm == "Signature":
538                 ret.sig = data.uint16()
539             elif pnm == "Deprecated":
540                 ret.deprecated = True
541             elif pnm == "RuntimeVisibleAnnotations":
542                 for o in range(data.uint16()):
543                     ret.rtann.append(self.loadannotation(data))
544             elif pnm == "RuntimeInvisibleAnnotations":
545                 for o in range(data.uint16()):
546                     ret.cpann.append(self.loadannotation(data))
547             elif pnm == "RuntimeVisibleParameterAnnotations":
548                 ret.prtann = []
549                 for o in range(data.uint8()):
550                     abuf = []
551                     for u in range(data.uint16()):
552                         abuf.append(self.loadannotation(data))
553                     ret.prtann.append(abuf)
554             elif pnm == "RuntimeInvisibleParameterAnnotations":
555                 ret.pcpann = []
556                 for o in range(data.uint8()):
557                     abuf = []
558                     for u in range(data.uint16()):
559                         abuf.append(self.loadannotation(data))
560                     ret.pcpann.append(abuf)
561             elif pnm == "AnnotationDefault":
562                 ret.anndef = self.loadannval(data)
563             else:
564                 ret.attrs.append((nm, data.splice()))
565         return ret
566
567     def savemethod(self, buf, method):
568         buf.uint16(method.acc)
569         buf.uint16(method.nm).uint16(method.descr)
570         attrs = list(method.attrs)
571         enc = binfmt.encbuf
572         if method.code:
573             data = enc()
574             self.savecode(data, method.code)
575             attrs.append((self.intern("Code"), data))
576         if len(method.throws) > 0:
577             data = enc()
578             data.uint16(len(method.throws))
579             for eref in method.throws: data.uint16(eref)
580             attrs.append((self.intern("Exceptions"), data))
581         if method.syn:
582             attrs.append((self.intern("Synthetic"), b""))
583         if method.sig is not None:
584             attrs.append((self.intern("Signature"), enc().uint16(method.sig)))
585         if method.deprecated:
586             attrs.append((self.intern("Deprecated"), b""))
587         if len(method.rtann) > 0:
588             data = enc()
589             data.uint16(len(method.rtann))
590             for ann in method.rtann: self.saveannotation(data, ann)
591             attrs.append((self.intern("RuntimeVisibleAnnotations"), data))
592         if len(method.cpann) > 0:
593             data = enc()
594             data.uint16(len(method.cpann))
595             for ann in method.cpann: self.saveannotation(data, ann)
596             attrs.append((self.intern("RuntimeInvisibleAnnotations"), data))
597         if method.prtann is not None:
598             data = enc()
599             data.uint8(len(method.prtann))
600             for par in method.prtann:
601                 buf.uint16(len(par))
602                 for ann in par: self.saveannotation(data, ann)
603             attrs.append((self.intern("RuntimeVisibleParameterAnnotations"), data))
604         if method.pcpann is not None:
605             data = enc()
606             data.uint8(len(method.pcpann))
607             for par in method.pcpann:
608                 buf.uint16(len(par))
609                 for ann in par: self.saveannotation(data, ann)
610             attrs.append((self.intern("RuntimeInvisibleParameterAnnotations"), data))
611         if method.anndef is not None:
612             data = enc()
613             self.saveannval(data, method.anndef)
614             attrs.append((self.intern("AnnotationDefault"), data))
615         self.saveattrs(buf, attrs)
616
617     @classmethod
618     def load(cls, fp):
619         buf = binfmt.decstream(fp)
620         if buf.uint32() != cls.MAGIC:
621             raise binfmt.fmterror("invalid magic number")
622         minor, major = buf.uint16(), buf.uint16()
623         self = cls(version(major, minor))
624
625         cplen = buf.uint16()
626         if cplen < 1:
627             raise binfmt.fmterror("invalid constant-pool length")
628         self.cp.append(None)
629         while len(self.cp) < cplen:
630             loaded, dbl = self.loadconstant(buf)
631             self.cp.append(loaded)
632             if dbl:
633                 self.cp.append(None)
634
635         self.acc = buf.uint16()
636         self.this = buf.uint16()
637         self.super = buf.uint16()
638         if not self.checkcp(self.this, classref):
639             raise binfmt.fmterror("invalid class name reference")
640         if not self.checkcp(self.super, classref) and self.cp[self.super] is not None:
641             raise binfmt.fmterror("invalid super-class reference")
642         iflen = buf.uint16()
643         while len(self.ifaces) < iflen:
644             iref = buf.uint16()
645             if not self.checkcp(iref, classref):
646                 raise binfmt.fmterror("invalid interface reference")
647             self.ifaces.append(iref)
648
649         nfields = buf.uint16()
650         while len(self.fields) < nfields:
651             self.fields.append(self.loadfield(buf))
652         nmethods = buf.uint16()
653         while len(self.methods) < nmethods:
654             self.methods.append(self.loadmethod(buf))
655
656         nattrs = buf.uint16()
657         for i in range(nattrs):
658             nm, data = self.loadattr(buf)
659             pnm = self.cp[nm]
660             if pnm == "SourceFile":
661                 self.srcfile = data.uint16()
662             elif pnm == "Signature":
663                 self.sig = data.uint16()
664             elif pnm == "Synthetic":
665                 self.syn = True
666             elif pnm == "Deprecated":
667                 self.deprecated = True
668             elif pnm == "InnerClasses":
669                 for o in range(data.uint16()):
670                     cref = data.uint16()
671                     outer = data.uint16()
672                     cnm = data.uint16()
673                     acc = data.uint16()
674                     if not self.checkcp(cref, classref):
675                         raise binfmt.fmterror("invalid inner-class reference")
676                     if not (outer == 0 or self.checkcp(outer, classref)):
677                         raise binfmt.fmterror("invalid inner-class outer reference")
678                     if not (cnm == 0 or self.checkcp(cnm, str)):
679                         raise binfmt.fmterror("invalid inner-class name reference")
680                     self.innerclasses.append(innerclass(cref, outer, cnm, acc))
681             elif pnm == "EnclosingMethod":
682                 self.enclosingmethod = (data.uint16(), data.uint16())
683                 if not self.checkcp(self.enclosingmethod[0], classref):
684                     raise binfmt.fmterror("invalid enclosing-method class reference")
685                 if not (self.enclosingmethod[1] == 0 or self.checkcp(self.enclosingmethod[1], sig)):
686                     raise binfmt.fmterror("invalid enclosing-method method reference")
687             elif pnm == "RuntimeVisibleAnnotations":
688                 for o in range(data.uint16()):
689                     self.rtann.append(self.loadannotation(data))
690             elif pnm == "RuntimeInvisibleAnnotations":
691                 for o in range(data.uint16()):
692                     self.cpann.append(self.loadannotation(data))
693             else:
694                 self.attrs.append((nm, data.splice()))
695
696         return self
697
698     def _save(self, buf):
699         buf.uint32(self.MAGIC)
700         buf.uint16(self.ver.minor).uint16(self.ver.major)
701
702         buf.uint16(len(self.cp))
703         for const in self.cp:
704             if const is not None:
705                 self.saveconstant(buf, const)
706
707         buf.uint16(self.acc)
708         buf.uint16(self.this).uint16(self.super)
709         buf.uint16(len(self.ifaces))
710         for iref in self.ifaces: buf.uint16(iref)
711
712         buf.uint16(len(self.fields))
713         for field in self.fields:
714             self.savefield(buf, field)
715
716         buf.uint16(len(self.methods))
717         for method in self.methods:
718             self.savemethod(buf, method)
719
720         enc = binfmt.encbuf
721         attrs = list(self.attrs)
722         if self.srcfile is not None:
723             attrs.append((self.intern("SourceFile"), enc().uint16(self.srcfile)))
724         if self.syn:
725             attrs.append((self.intern("Synthetic"), b""))
726         if self.deprecated:
727             attrs.append((self.intern("Deprecated"), b""))
728         if self.sig is not None:
729             attrs.append((self.intern("Signature"), enc().uint16(self.sig)))
730         if len(self.innerclasses) > 0:
731             data = enc()
732             data.uint16(len(self.innerclasses))
733             for inner in self.innerclasses: data.uint16(inner.cls).uint16(inner.outer).uint16(inner.nm).uint16(inner.acc)
734             attrs.append((self.intern("InnerClasses"), data))
735         if self.enclosingmethod is not None:
736             attrs.append((self.intern("EnclosingMethod"), enc().uint16(self.enclosingmethod[0]).uint16(self.enclosingmethod[1])))
737         if len(self.rtann) > 0:
738             data = enc()
739             data.uint16(len(self.rtann))
740             for ann in self.rtann: self.saveannotation(data, ann)
741             attrs.append((self.intern("RuntimeVisibleAnnotations"), data))
742         if len(self.cpann) > 0:
743             data = enc()
744             data.uint16(len(self.cpann))
745             for ann in self.cpann: self.saveannotation(data, ann)
746             attrs.append((self.intern("RuntimeInvisibleAnnotations"), data))
747         self.saveattrs(buf, attrs)
748
749     def save(self, fp):
750         return self._save(binfmt.encstream(fp))
751
752     @classmethod
753     def fromfile(cls, fn):
754         with open(fn, "rb") as fp:
755             return cls.load(fp)
756
757     def tofile(self, fn):
758         with open(fn, "wb") as fp:
759             return self.save(fp)