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