|
1 /* |
|
2 * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 package com.sun.java.util.jar.pack; |
|
27 |
|
28 import com.sun.java.util.jar.pack.ConstantPool.ClassEntry; |
|
29 import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry; |
|
30 import com.sun.java.util.jar.pack.ConstantPool.Entry; |
|
31 import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry; |
|
32 import com.sun.java.util.jar.pack.ConstantPool.MemberEntry; |
|
33 import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry; |
|
34 import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry; |
|
35 import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry; |
|
36 import com.sun.java.util.jar.pack.Package.Class; |
|
37 import com.sun.java.util.jar.pack.Package.InnerClass; |
|
38 import java.io.DataInputStream; |
|
39 import java.io.FilterInputStream; |
|
40 import java.io.IOException; |
|
41 import java.io.InputStream; |
|
42 import java.util.ArrayList; |
|
43 import java.util.Arrays; |
|
44 import java.util.Map; |
|
45 import static com.sun.java.util.jar.pack.Constants.*; |
|
46 |
|
47 /** |
|
48 * Reader for a class file that is being incorporated into a package. |
|
49 * @author John Rose |
|
50 */ |
|
51 class ClassReader { |
|
52 int verbose; |
|
53 |
|
54 Package pkg; |
|
55 Class cls; |
|
56 long inPos; |
|
57 long constantPoolLimit = -1; |
|
58 DataInputStream in; |
|
59 Map<Attribute.Layout, Attribute> attrDefs; |
|
60 Map<Attribute.Layout, String> attrCommands; |
|
61 String unknownAttrCommand = "error";; |
|
62 |
|
63 ClassReader(Class cls, InputStream in) throws IOException { |
|
64 this.pkg = cls.getPackage(); |
|
65 this.cls = cls; |
|
66 this.verbose = pkg.verbose; |
|
67 this.in = new DataInputStream(new FilterInputStream(in) { |
|
68 public int read(byte b[], int off, int len) throws IOException { |
|
69 int nr = super.read(b, off, len); |
|
70 if (nr >= 0) inPos += nr; |
|
71 return nr; |
|
72 } |
|
73 public int read() throws IOException { |
|
74 int ch = super.read(); |
|
75 if (ch >= 0) inPos += 1; |
|
76 return ch; |
|
77 } |
|
78 public long skip(long n) throws IOException { |
|
79 long ns = super.skip(n); |
|
80 if (ns >= 0) inPos += ns; |
|
81 return ns; |
|
82 } |
|
83 }); |
|
84 } |
|
85 |
|
86 public void setAttrDefs(Map<Attribute.Layout, Attribute> attrDefs) { |
|
87 this.attrDefs = attrDefs; |
|
88 } |
|
89 |
|
90 public void setAttrCommands(Map<Attribute.Layout, String> attrCommands) { |
|
91 this.attrCommands = attrCommands; |
|
92 } |
|
93 |
|
94 private void skip(int n, String what) throws IOException { |
|
95 Utils.log.warning("skipping "+n+" bytes of "+what); |
|
96 long skipped = 0; |
|
97 while (skipped < n) { |
|
98 long j = in.skip(n - skipped); |
|
99 assert(j > 0); |
|
100 skipped += j; |
|
101 } |
|
102 assert(skipped == n); |
|
103 } |
|
104 |
|
105 private int readUnsignedShort() throws IOException { |
|
106 return in.readUnsignedShort(); |
|
107 } |
|
108 |
|
109 private int readInt() throws IOException { |
|
110 return in.readInt(); |
|
111 } |
|
112 |
|
113 /** Read a 2-byte int, and return the <em>global</em> CP entry for it. */ |
|
114 private Entry readRef() throws IOException { |
|
115 int i = in.readUnsignedShort(); |
|
116 return i == 0 ? null : cls.cpMap[i]; |
|
117 } |
|
118 |
|
119 private Entry readRef(byte tag) throws IOException { |
|
120 Entry e = readRef(); |
|
121 assert(!(e instanceof UnresolvedEntry)); |
|
122 checkTag(e, tag); |
|
123 return e; |
|
124 } |
|
125 |
|
126 /** Throw a ClassFormatException if the entry does not match the expected tag type. */ |
|
127 private Entry checkTag(Entry e, byte tag) throws ClassFormatException { |
|
128 if (e == null || !e.tagMatches(tag)) { |
|
129 String where = (inPos == constantPoolLimit |
|
130 ? " in constant pool" |
|
131 : " at pos: " + inPos); |
|
132 String got = (e == null |
|
133 ? "null CP index" |
|
134 : "type=" + ConstantPool.tagName(e.tag)); |
|
135 throw new ClassFormatException("Bad constant, expected type=" + |
|
136 ConstantPool.tagName(tag) + |
|
137 " got "+ got + ", in File: " + cls.file.nameString + where); |
|
138 } |
|
139 return e; |
|
140 } |
|
141 private Entry checkTag(Entry e, byte tag, boolean nullOK) throws ClassFormatException { |
|
142 return nullOK && e == null ? null : checkTag(e, tag); |
|
143 } |
|
144 |
|
145 private Entry readRefOrNull(byte tag) throws IOException { |
|
146 Entry e = readRef(); |
|
147 checkTag(e, tag, true); |
|
148 return e; |
|
149 } |
|
150 |
|
151 private Utf8Entry readUtf8Ref() throws IOException { |
|
152 return (Utf8Entry) readRef(CONSTANT_Utf8); |
|
153 } |
|
154 |
|
155 private ClassEntry readClassRef() throws IOException { |
|
156 return (ClassEntry) readRef(CONSTANT_Class); |
|
157 } |
|
158 |
|
159 private ClassEntry readClassRefOrNull() throws IOException { |
|
160 return (ClassEntry) readRefOrNull(CONSTANT_Class); |
|
161 } |
|
162 |
|
163 private SignatureEntry readSignatureRef() throws IOException { |
|
164 // The class file stores a Utf8, but we want a Signature. |
|
165 Entry e = readRef(CONSTANT_Signature); |
|
166 return (e != null && e.getTag() == CONSTANT_Utf8) |
|
167 ? ConstantPool.getSignatureEntry(e.stringValue()) |
|
168 : (SignatureEntry) e; |
|
169 } |
|
170 |
|
171 void read() throws IOException { |
|
172 boolean ok = false; |
|
173 try { |
|
174 readMagicNumbers(); |
|
175 readConstantPool(); |
|
176 readHeader(); |
|
177 readMembers(false); // fields |
|
178 readMembers(true); // methods |
|
179 readAttributes(ATTR_CONTEXT_CLASS, cls); |
|
180 fixUnresolvedEntries(); |
|
181 cls.finishReading(); |
|
182 assert(0 >= in.read(new byte[1])); |
|
183 ok = true; |
|
184 } finally { |
|
185 if (!ok) { |
|
186 if (verbose > 0) Utils.log.warning("Erroneous data at input offset "+inPos+" of "+cls.file); |
|
187 } |
|
188 } |
|
189 } |
|
190 |
|
191 void readMagicNumbers() throws IOException { |
|
192 cls.magic = in.readInt(); |
|
193 if (cls.magic != JAVA_MAGIC) |
|
194 throw new Attribute.FormatException |
|
195 ("Bad magic number in class file " |
|
196 +Integer.toHexString(cls.magic), |
|
197 ATTR_CONTEXT_CLASS, "magic-number", "pass"); |
|
198 int minver = (short) readUnsignedShort(); |
|
199 int majver = (short) readUnsignedShort(); |
|
200 cls.version = Package.Version.of(majver, minver); |
|
201 |
|
202 //System.out.println("ClassFile.version="+cls.majver+"."+cls.minver); |
|
203 String bad = checkVersion(cls.version); |
|
204 if (bad != null) { |
|
205 throw new Attribute.FormatException |
|
206 ("classfile version too "+bad+": " |
|
207 +cls.version+" in "+cls.file, |
|
208 ATTR_CONTEXT_CLASS, "version", "pass"); |
|
209 } |
|
210 } |
|
211 |
|
212 private String checkVersion(Package.Version ver) { |
|
213 int majver = ver.major; |
|
214 int minver = ver.minor; |
|
215 if (majver < pkg.minClassVersion.major || |
|
216 (majver == pkg.minClassVersion.major && |
|
217 minver < pkg.minClassVersion.minor)) { |
|
218 return "small"; |
|
219 } |
|
220 if (majver > pkg.maxClassVersion.major || |
|
221 (majver == pkg.maxClassVersion.major && |
|
222 minver > pkg.maxClassVersion.minor)) { |
|
223 return "large"; |
|
224 } |
|
225 return null; // OK |
|
226 } |
|
227 |
|
228 void readConstantPool() throws IOException { |
|
229 int length = in.readUnsignedShort(); |
|
230 //System.err.println("reading CP, length="+length); |
|
231 |
|
232 int[] fixups = new int[length*4]; |
|
233 int fptr = 0; |
|
234 |
|
235 Entry[] cpMap = new Entry[length]; |
|
236 cpMap[0] = null; |
|
237 for (int i = 1; i < length; i++) { |
|
238 //System.err.println("reading CP elt, i="+i); |
|
239 int tag = in.readByte(); |
|
240 switch (tag) { |
|
241 case CONSTANT_Utf8: |
|
242 cpMap[i] = ConstantPool.getUtf8Entry(in.readUTF()); |
|
243 break; |
|
244 case CONSTANT_Integer: |
|
245 { |
|
246 cpMap[i] = ConstantPool.getLiteralEntry(in.readInt()); |
|
247 } |
|
248 break; |
|
249 case CONSTANT_Float: |
|
250 { |
|
251 cpMap[i] = ConstantPool.getLiteralEntry(in.readFloat()); |
|
252 } |
|
253 break; |
|
254 case CONSTANT_Long: |
|
255 { |
|
256 cpMap[i] = ConstantPool.getLiteralEntry(in.readLong()); |
|
257 cpMap[++i] = null; |
|
258 } |
|
259 break; |
|
260 case CONSTANT_Double: |
|
261 { |
|
262 cpMap[i] = ConstantPool.getLiteralEntry(in.readDouble()); |
|
263 cpMap[++i] = null; |
|
264 } |
|
265 break; |
|
266 |
|
267 // just read the refs; do not attempt to resolve while reading |
|
268 case CONSTANT_Class: |
|
269 case CONSTANT_String: |
|
270 case CONSTANT_MethodType: |
|
271 fixups[fptr++] = i; |
|
272 fixups[fptr++] = tag; |
|
273 fixups[fptr++] = in.readUnsignedShort(); |
|
274 fixups[fptr++] = -1; // empty ref2 |
|
275 break; |
|
276 case CONSTANT_Fieldref: |
|
277 case CONSTANT_Methodref: |
|
278 case CONSTANT_InterfaceMethodref: |
|
279 case CONSTANT_NameandType: |
|
280 fixups[fptr++] = i; |
|
281 fixups[fptr++] = tag; |
|
282 fixups[fptr++] = in.readUnsignedShort(); |
|
283 fixups[fptr++] = in.readUnsignedShort(); |
|
284 break; |
|
285 case CONSTANT_InvokeDynamic: |
|
286 fixups[fptr++] = i; |
|
287 fixups[fptr++] = tag; |
|
288 fixups[fptr++] = -1 ^ in.readUnsignedShort(); // not a ref |
|
289 fixups[fptr++] = in.readUnsignedShort(); |
|
290 break; |
|
291 case CONSTANT_MethodHandle: |
|
292 fixups[fptr++] = i; |
|
293 fixups[fptr++] = tag; |
|
294 fixups[fptr++] = -1 ^ in.readUnsignedByte(); |
|
295 fixups[fptr++] = in.readUnsignedShort(); |
|
296 break; |
|
297 default: |
|
298 throw new ClassFormatException("Bad constant pool tag " + |
|
299 tag + " in File: " + cls.file.nameString + |
|
300 " at pos: " + inPos); |
|
301 } |
|
302 } |
|
303 constantPoolLimit = inPos; |
|
304 |
|
305 // Fix up refs, which might be out of order. |
|
306 while (fptr > 0) { |
|
307 if (verbose > 3) |
|
308 Utils.log.fine("CP fixups ["+fptr/4+"]"); |
|
309 int flimit = fptr; |
|
310 fptr = 0; |
|
311 for (int fi = 0; fi < flimit; ) { |
|
312 int cpi = fixups[fi++]; |
|
313 int tag = fixups[fi++]; |
|
314 int ref = fixups[fi++]; |
|
315 int ref2 = fixups[fi++]; |
|
316 if (verbose > 3) |
|
317 Utils.log.fine(" cp["+cpi+"] = "+ConstantPool.tagName(tag)+"{"+ref+","+ref2+"}"); |
|
318 if (ref >= 0 && cpMap[ref] == null || ref2 >= 0 && cpMap[ref2] == null) { |
|
319 // Defer. |
|
320 fixups[fptr++] = cpi; |
|
321 fixups[fptr++] = tag; |
|
322 fixups[fptr++] = ref; |
|
323 fixups[fptr++] = ref2; |
|
324 continue; |
|
325 } |
|
326 switch (tag) { |
|
327 case CONSTANT_Class: |
|
328 cpMap[cpi] = ConstantPool.getClassEntry(cpMap[ref].stringValue()); |
|
329 break; |
|
330 case CONSTANT_String: |
|
331 cpMap[cpi] = ConstantPool.getStringEntry(cpMap[ref].stringValue()); |
|
332 break; |
|
333 case CONSTANT_Fieldref: |
|
334 case CONSTANT_Methodref: |
|
335 case CONSTANT_InterfaceMethodref: |
|
336 ClassEntry mclass = (ClassEntry) checkTag(cpMap[ref], CONSTANT_Class); |
|
337 DescriptorEntry mdescr = (DescriptorEntry) checkTag(cpMap[ref2], CONSTANT_NameandType); |
|
338 cpMap[cpi] = ConstantPool.getMemberEntry((byte)tag, mclass, mdescr); |
|
339 break; |
|
340 case CONSTANT_NameandType: |
|
341 Utf8Entry mname = (Utf8Entry) checkTag(cpMap[ref], CONSTANT_Utf8); |
|
342 Utf8Entry mtype = (Utf8Entry) checkTag(cpMap[ref2], CONSTANT_Signature); |
|
343 cpMap[cpi] = ConstantPool.getDescriptorEntry(mname, mtype); |
|
344 break; |
|
345 case CONSTANT_MethodType: |
|
346 cpMap[cpi] = ConstantPool.getMethodTypeEntry((Utf8Entry) checkTag(cpMap[ref], CONSTANT_Signature)); |
|
347 break; |
|
348 case CONSTANT_MethodHandle: |
|
349 byte refKind = (byte)(-1 ^ ref); |
|
350 MemberEntry memRef = (MemberEntry) checkTag(cpMap[ref2], CONSTANT_AnyMember); |
|
351 cpMap[cpi] = ConstantPool.getMethodHandleEntry(refKind, memRef); |
|
352 break; |
|
353 case CONSTANT_InvokeDynamic: |
|
354 DescriptorEntry idescr = (DescriptorEntry) checkTag(cpMap[ref2], CONSTANT_NameandType); |
|
355 cpMap[cpi] = new UnresolvedEntry((byte)tag, (-1 ^ ref), idescr); |
|
356 // Note that ref must be resolved later, using the BootstrapMethods attribute. |
|
357 break; |
|
358 default: |
|
359 assert(false); |
|
360 } |
|
361 } |
|
362 assert(fptr < flimit); // Must make progress. |
|
363 } |
|
364 |
|
365 cls.cpMap = cpMap; |
|
366 } |
|
367 |
|
368 private /*non-static*/ |
|
369 class UnresolvedEntry extends Entry { |
|
370 final Object[] refsOrIndexes; |
|
371 UnresolvedEntry(byte tag, Object... refsOrIndexes) { |
|
372 super(tag); |
|
373 this.refsOrIndexes = refsOrIndexes; |
|
374 ClassReader.this.haveUnresolvedEntry = true; |
|
375 } |
|
376 Entry resolve() { |
|
377 Class cls = ClassReader.this.cls; |
|
378 Entry res; |
|
379 switch (tag) { |
|
380 case CONSTANT_InvokeDynamic: |
|
381 BootstrapMethodEntry iboots = cls.bootstrapMethods.get((Integer) refsOrIndexes[0]); |
|
382 DescriptorEntry idescr = (DescriptorEntry) refsOrIndexes[1]; |
|
383 res = ConstantPool.getInvokeDynamicEntry(iboots, idescr); |
|
384 break; |
|
385 default: |
|
386 throw new AssertionError(); |
|
387 } |
|
388 return res; |
|
389 } |
|
390 private void unresolved() { throw new RuntimeException("unresolved entry has no string"); } |
|
391 public int compareTo(Object x) { unresolved(); return 0; } |
|
392 public boolean equals(Object x) { unresolved(); return false; } |
|
393 protected int computeValueHash() { unresolved(); return 0; } |
|
394 public String stringValue() { unresolved(); return toString(); } |
|
395 public String toString() { return "(unresolved "+ConstantPool.tagName(tag)+")"; } |
|
396 } |
|
397 |
|
398 boolean haveUnresolvedEntry; |
|
399 private void fixUnresolvedEntries() { |
|
400 if (!haveUnresolvedEntry) return; |
|
401 Entry[] cpMap = cls.getCPMap(); |
|
402 for (int i = 0; i < cpMap.length; i++) { |
|
403 Entry e = cpMap[i]; |
|
404 if (e instanceof UnresolvedEntry) { |
|
405 cpMap[i] = e = ((UnresolvedEntry)e).resolve(); |
|
406 assert(!(e instanceof UnresolvedEntry)); |
|
407 } |
|
408 } |
|
409 haveUnresolvedEntry = false; |
|
410 } |
|
411 |
|
412 void readHeader() throws IOException { |
|
413 cls.flags = readUnsignedShort(); |
|
414 cls.thisClass = readClassRef(); |
|
415 cls.superClass = readClassRefOrNull(); |
|
416 int ni = readUnsignedShort(); |
|
417 cls.interfaces = new ClassEntry[ni]; |
|
418 for (int i = 0; i < ni; i++) { |
|
419 cls.interfaces[i] = readClassRef(); |
|
420 } |
|
421 } |
|
422 |
|
423 void readMembers(boolean doMethods) throws IOException { |
|
424 int nm = readUnsignedShort(); |
|
425 for (int i = 0; i < nm; i++) { |
|
426 readMember(doMethods); |
|
427 } |
|
428 } |
|
429 |
|
430 void readMember(boolean doMethod) throws IOException { |
|
431 int mflags = readUnsignedShort(); |
|
432 Utf8Entry mname = readUtf8Ref(); |
|
433 SignatureEntry mtype = readSignatureRef(); |
|
434 DescriptorEntry descr = ConstantPool.getDescriptorEntry(mname, mtype); |
|
435 Class.Member m; |
|
436 if (!doMethod) |
|
437 m = cls.new Field(mflags, descr); |
|
438 else |
|
439 m = cls.new Method(mflags, descr); |
|
440 readAttributes(!doMethod ? ATTR_CONTEXT_FIELD : ATTR_CONTEXT_METHOD, |
|
441 m); |
|
442 } |
|
443 void readAttributes(int ctype, Attribute.Holder h) throws IOException { |
|
444 int na = readUnsignedShort(); |
|
445 if (na == 0) return; // nothing to do here |
|
446 if (verbose > 3) |
|
447 Utils.log.fine("readAttributes "+h+" ["+na+"]"); |
|
448 for (int i = 0; i < na; i++) { |
|
449 String name = readUtf8Ref().stringValue(); |
|
450 int length = readInt(); |
|
451 // See if there is a special command that applies. |
|
452 if (attrCommands != null) { |
|
453 Attribute.Layout lkey = Attribute.keyForLookup(ctype, name); |
|
454 String cmd = attrCommands.get(lkey); |
|
455 if (cmd != null) { |
|
456 switch (cmd) { |
|
457 case "pass": |
|
458 String message1 = "passing attribute bitwise in " + h; |
|
459 throw new Attribute.FormatException(message1, ctype, name, cmd); |
|
460 case "error": |
|
461 String message2 = "attribute not allowed in " + h; |
|
462 throw new Attribute.FormatException(message2, ctype, name, cmd); |
|
463 case "strip": |
|
464 skip(length, name + " attribute in " + h); |
|
465 continue; |
|
466 } |
|
467 } |
|
468 } |
|
469 // Find canonical instance of the requested attribute. |
|
470 Attribute a = Attribute.lookup(Package.attrDefs, ctype, name); |
|
471 if (verbose > 4 && a != null) |
|
472 Utils.log.fine("pkg_attribute_lookup "+name+" = "+a); |
|
473 if (a == null) { |
|
474 a = Attribute.lookup(this.attrDefs, ctype, name); |
|
475 if (verbose > 4 && a != null) |
|
476 Utils.log.fine("this "+name+" = "+a); |
|
477 } |
|
478 if (a == null) { |
|
479 a = Attribute.lookup(null, ctype, name); |
|
480 if (verbose > 4 && a != null) |
|
481 Utils.log.fine("null_attribute_lookup "+name+" = "+a); |
|
482 } |
|
483 if (a == null && length == 0) { |
|
484 // Any zero-length attr is "known"... |
|
485 // We can assume an empty attr. has an empty layout. |
|
486 // Handles markers like Enum, Bridge, Synthetic, Deprecated. |
|
487 a = Attribute.find(ctype, name, ""); |
|
488 } |
|
489 boolean isStackMap = (ctype == ATTR_CONTEXT_CODE |
|
490 && (name.equals("StackMap") || |
|
491 name.equals("StackMapX"))); |
|
492 if (isStackMap) { |
|
493 // Known attribute but with a corner case format, "pass" it. |
|
494 Code code = (Code) h; |
|
495 final int TOO_BIG = 0x10000; |
|
496 if (code.max_stack >= TOO_BIG || |
|
497 code.max_locals >= TOO_BIG || |
|
498 code.getLength() >= TOO_BIG || |
|
499 name.endsWith("X")) { |
|
500 // No, we don't really know what to do with this one. |
|
501 // Do not compress the rare and strange "u4" and "X" cases. |
|
502 a = null; |
|
503 } |
|
504 } |
|
505 if (a == null) { |
|
506 if (isStackMap) { |
|
507 // Known attribute but w/o a format; pass it. |
|
508 String message = "unsupported StackMap variant in "+h; |
|
509 throw new Attribute.FormatException(message, ctype, name, |
|
510 "pass"); |
|
511 } else if ("strip".equals(unknownAttrCommand)) { |
|
512 // Skip the unknown attribute. |
|
513 skip(length, "unknown "+name+" attribute in "+h); |
|
514 continue; |
|
515 } else { |
|
516 String message = " is unknown attribute in class " + h; |
|
517 throw new Attribute.FormatException(message, ctype, name, |
|
518 unknownAttrCommand); |
|
519 } |
|
520 } |
|
521 long pos0 = inPos; // in case we want to check it |
|
522 if (a.layout() == Package.attrCodeEmpty) { |
|
523 // These are hardwired. |
|
524 Class.Method m = (Class.Method) h; |
|
525 m.code = new Code(m); |
|
526 try { |
|
527 readCode(m.code); |
|
528 } catch (Instruction.FormatException iie) { |
|
529 String message = iie.getMessage() + " in " + h; |
|
530 throw new ClassReader.ClassFormatException(message, iie); |
|
531 } |
|
532 assert(length == inPos - pos0); |
|
533 // Keep empty attribute a... |
|
534 } else if (a.layout() == Package.attrBootstrapMethodsEmpty) { |
|
535 assert(h == cls); |
|
536 readBootstrapMethods(cls); |
|
537 assert(length == inPos - pos0); |
|
538 // Delete the attribute; it is logically part of the constant pool. |
|
539 continue; |
|
540 } else if (a.layout() == Package.attrInnerClassesEmpty) { |
|
541 // These are hardwired also. |
|
542 assert(h == cls); |
|
543 readInnerClasses(cls); |
|
544 assert(length == inPos - pos0); |
|
545 // Keep empty attribute a... |
|
546 } else if (length > 0) { |
|
547 byte[] bytes = new byte[length]; |
|
548 in.readFully(bytes); |
|
549 a = a.addContent(bytes); |
|
550 } |
|
551 if (a.size() == 0 && !a.layout().isEmpty()) { |
|
552 throw new ClassFormatException(name + |
|
553 ": attribute length cannot be zero, in " + h); |
|
554 } |
|
555 h.addAttribute(a); |
|
556 if (verbose > 2) |
|
557 Utils.log.fine("read "+a); |
|
558 } |
|
559 } |
|
560 |
|
561 void readCode(Code code) throws IOException { |
|
562 code.max_stack = readUnsignedShort(); |
|
563 code.max_locals = readUnsignedShort(); |
|
564 code.bytes = new byte[readInt()]; |
|
565 in.readFully(code.bytes); |
|
566 Entry[] cpMap = cls.getCPMap(); |
|
567 Instruction.opcodeChecker(code.bytes, cpMap, this.cls.version); |
|
568 int nh = readUnsignedShort(); |
|
569 code.setHandlerCount(nh); |
|
570 for (int i = 0; i < nh; i++) { |
|
571 code.handler_start[i] = readUnsignedShort(); |
|
572 code.handler_end[i] = readUnsignedShort(); |
|
573 code.handler_catch[i] = readUnsignedShort(); |
|
574 code.handler_class[i] = readClassRefOrNull(); |
|
575 } |
|
576 readAttributes(ATTR_CONTEXT_CODE, code); |
|
577 } |
|
578 |
|
579 void readBootstrapMethods(Class cls) throws IOException { |
|
580 BootstrapMethodEntry[] bsms = new BootstrapMethodEntry[readUnsignedShort()]; |
|
581 for (int i = 0; i < bsms.length; i++) { |
|
582 MethodHandleEntry bsmRef = (MethodHandleEntry) readRef(CONSTANT_MethodHandle); |
|
583 Entry[] argRefs = new Entry[readUnsignedShort()]; |
|
584 for (int j = 0; j < argRefs.length; j++) { |
|
585 argRefs[j] = readRef(CONSTANT_LoadableValue); |
|
586 } |
|
587 bsms[i] = ConstantPool.getBootstrapMethodEntry(bsmRef, argRefs); |
|
588 } |
|
589 cls.setBootstrapMethods(Arrays.asList(bsms)); |
|
590 } |
|
591 |
|
592 void readInnerClasses(Class cls) throws IOException { |
|
593 int nc = readUnsignedShort(); |
|
594 ArrayList<InnerClass> ics = new ArrayList<>(nc); |
|
595 for (int i = 0; i < nc; i++) { |
|
596 InnerClass ic = |
|
597 new InnerClass(readClassRef(), |
|
598 readClassRefOrNull(), |
|
599 (Utf8Entry)readRefOrNull(CONSTANT_Utf8), |
|
600 readUnsignedShort()); |
|
601 ics.add(ic); |
|
602 } |
|
603 cls.innerClasses = ics; // set directly; do not use setInnerClasses. |
|
604 // (Later, ics may be transferred to the pkg.) |
|
605 } |
|
606 |
|
607 static class ClassFormatException extends IOException { |
|
608 private static final long serialVersionUID = -3564121733989501833L; |
|
609 |
|
610 public ClassFormatException(String message) { |
|
611 super(message); |
|
612 } |
|
613 |
|
614 public ClassFormatException(String message, Throwable cause) { |
|
615 super(message, cause); |
|
616 } |
|
617 } |
|
618 } |