Mercurial > lasercutter
comparison src/clojure/asm/ClassReader.java @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
1 /*** | |
2 * ASM: a very small and fast Java bytecode manipulation framework | |
3 * Copyright (c) 2000-2005 INRIA, France Telecom | |
4 * All rights reserved. | |
5 * | |
6 * Redistribution and use in source and binary forms, with or without | |
7 * modification, are permitted provided that the following conditions | |
8 * are met: | |
9 * 1. Redistributions of source code must retain the above copyright | |
10 * notice, this list of conditions and the following disclaimer. | |
11 * 2. Redistributions in binary form must reproduce the above copyright | |
12 * notice, this list of conditions and the following disclaimer in the | |
13 * documentation and/or other materials provided with the distribution. | |
14 * 3. Neither the name of the copyright holders nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
28 * THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 package clojure.asm; | |
31 | |
32 import java.io.InputStream; | |
33 import java.io.IOException; | |
34 | |
35 /** | |
36 * A Java class parser to make a {@link ClassVisitor} visit an existing class. | |
37 * This class parses a byte array conforming to the Java class file format and | |
38 * calls the appropriate visit methods of a given class visitor for each field, | |
39 * method and bytecode instruction encountered. | |
40 * | |
41 * @author Eric Bruneton | |
42 * @author Eugene Kuleshov | |
43 */ | |
44 public class ClassReader{ | |
45 | |
46 /** | |
47 * Flag to skip method code. If this class is set <code>CODE</code> | |
48 * attribute won't be visited. This can be used, for example, to retrieve | |
49 * annotations for methods and method parameters. | |
50 */ | |
51 public final static int SKIP_CODE = 1; | |
52 | |
53 /** | |
54 * Flag to skip the debug information in the class. If this flag is set the | |
55 * debug information of the class is not visited, i.e. the | |
56 * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and | |
57 * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be | |
58 * called. | |
59 */ | |
60 public final static int SKIP_DEBUG = 2; | |
61 | |
62 /** | |
63 * Flag to skip the stack map frames in the class. If this flag is set the | |
64 * stack map frames of the class is not visited, i.e. the | |
65 * {@link MethodVisitor#visitFrame visitFrame} method will not be called. | |
66 * This flag is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is | |
67 * used: it avoids visiting frames that will be ignored and recomputed from | |
68 * scratch in the class writer. | |
69 */ | |
70 public final static int SKIP_FRAMES = 4; | |
71 | |
72 /** | |
73 * Flag to expand the stack map frames. By default stack map frames are | |
74 * visited in their original format (i.e. "expanded" for classes whose | |
75 * version is less than V1_6, and "compressed" for the other classes). If | |
76 * this flag is set, stack map frames are always visited in expanded format | |
77 * (this option adds a decompression/recompression step in ClassReader and | |
78 * ClassWriter which degrades performances quite a lot). | |
79 */ | |
80 public final static int EXPAND_FRAMES = 8; | |
81 | |
82 /** | |
83 * The class to be parsed. <i>The content of this array must not be | |
84 * modified. This field is intended for {@link Attribute} sub classes, and | |
85 * is normally not needed by class generators or adapters.</i> | |
86 */ | |
87 public final byte[] b; | |
88 | |
89 /** | |
90 * The start index of each constant pool item in {@link #b b}, plus one. | |
91 * The one byte offset skips the constant pool item tag that indicates its | |
92 * type. | |
93 */ | |
94 private final int[] items; | |
95 | |
96 /** | |
97 * The String objects corresponding to the CONSTANT_Utf8 items. This cache | |
98 * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item, | |
99 * which GREATLY improves performances (by a factor 2 to 3). This caching | |
100 * strategy could be extended to all constant pool items, but its benefit | |
101 * would not be so great for these items (because they are much less | |
102 * expensive to parse than CONSTANT_Utf8 items). | |
103 */ | |
104 private final String[] strings; | |
105 | |
106 /** | |
107 * Maximum length of the strings contained in the constant pool of the | |
108 * class. | |
109 */ | |
110 private final int maxStringLength; | |
111 | |
112 /** | |
113 * Start index of the class header information (access, name...) in | |
114 * {@link #b b}. | |
115 */ | |
116 public final int header; | |
117 | |
118 // ------------------------------------------------------------------------ | |
119 // Constructors | |
120 // ------------------------------------------------------------------------ | |
121 | |
122 /** | |
123 * Constructs a new {@link ClassReader} object. | |
124 * | |
125 * @param b the bytecode of the class to be read. | |
126 */ | |
127 public ClassReader(final byte[] b){ | |
128 this(b, 0, b.length); | |
129 } | |
130 | |
131 /** | |
132 * Constructs a new {@link ClassReader} object. | |
133 * | |
134 * @param b the bytecode of the class to be read. | |
135 * @param off the start offset of the class data. | |
136 * @param len the length of the class data. | |
137 */ | |
138 public ClassReader(final byte[] b, final int off, final int len){ | |
139 this.b = b; | |
140 // parses the constant pool | |
141 items = new int[readUnsignedShort(off + 8)]; | |
142 int n = items.length; | |
143 strings = new String[n]; | |
144 int max = 0; | |
145 int index = off + 10; | |
146 for(int i = 1; i < n; ++i) | |
147 { | |
148 items[i] = index + 1; | |
149 int size; | |
150 switch(b[index]) | |
151 { | |
152 case ClassWriter.FIELD: | |
153 case ClassWriter.METH: | |
154 case ClassWriter.IMETH: | |
155 case ClassWriter.INT: | |
156 case ClassWriter.FLOAT: | |
157 case ClassWriter.NAME_TYPE: | |
158 size = 5; | |
159 break; | |
160 case ClassWriter.LONG: | |
161 case ClassWriter.DOUBLE: | |
162 size = 9; | |
163 ++i; | |
164 break; | |
165 case ClassWriter.UTF8: | |
166 size = 3 + readUnsignedShort(index + 1); | |
167 if(size > max) | |
168 { | |
169 max = size; | |
170 } | |
171 break; | |
172 // case ClassWriter.CLASS: | |
173 // case ClassWriter.STR: | |
174 default: | |
175 size = 3; | |
176 break; | |
177 } | |
178 index += size; | |
179 } | |
180 maxStringLength = max; | |
181 // the class header information starts just after the constant pool | |
182 header = index; | |
183 } | |
184 | |
185 /** | |
186 * Returns the class's access flags (see {@link Opcodes}). This value may | |
187 * not reflect Deprecated and Synthetic flags when bytecode is before 1.5 | |
188 * and those flags are represented by attributes. | |
189 * | |
190 * @return the class access flags | |
191 * @see ClassVisitor#visit(int,int,String,String,String,String[]) | |
192 */ | |
193 public int getAccess(){ | |
194 return readUnsignedShort(header); | |
195 } | |
196 | |
197 /** | |
198 * Returns the internal name of the class (see | |
199 * {@link Type#getInternalName() getInternalName}). | |
200 * | |
201 * @return the internal class name | |
202 * @see ClassVisitor#visit(int,int,String,String,String,String[]) | |
203 */ | |
204 public String getClassName(){ | |
205 return readClass(header + 2, new char[maxStringLength]); | |
206 } | |
207 | |
208 /** | |
209 * Returns the internal of name of the super class (see | |
210 * {@link Type#getInternalName() getInternalName}). For interfaces, the | |
211 * super class is {@link Object}. | |
212 * | |
213 * @return the internal name of super class, or <tt>null</tt> for | |
214 * {@link Object} class. | |
215 * @see ClassVisitor#visit(int,int,String,String,String,String[]) | |
216 */ | |
217 public String getSuperName(){ | |
218 int n = items[readUnsignedShort(header + 4)]; | |
219 return n == 0 ? null : readUTF8(n, new char[maxStringLength]); | |
220 } | |
221 | |
222 /** | |
223 * Returns the internal names of the class's interfaces (see | |
224 * {@link Type#getInternalName() getInternalName}). | |
225 * | |
226 * @return the array of internal names for all implemented interfaces or | |
227 * <tt>null</tt>. | |
228 * @see ClassVisitor#visit(int,int,String,String,String,String[]) | |
229 */ | |
230 public String[] getInterfaces(){ | |
231 int index = header + 6; | |
232 int n = readUnsignedShort(index); | |
233 String[] interfaces = new String[n]; | |
234 if(n > 0) | |
235 { | |
236 char[] buf = new char[maxStringLength]; | |
237 for(int i = 0; i < n; ++i) | |
238 { | |
239 index += 2; | |
240 interfaces[i] = readClass(index, buf); | |
241 } | |
242 } | |
243 return interfaces; | |
244 } | |
245 | |
246 /** | |
247 * Copies the constant pool data into the given {@link ClassWriter}. Should | |
248 * be called before the {@link #accept(ClassVisitor,int)} method. | |
249 * | |
250 * @param classWriter the {@link ClassWriter} to copy constant pool into. | |
251 */ | |
252 void copyPool(final ClassWriter classWriter){ | |
253 char[] buf = new char[maxStringLength]; | |
254 int ll = items.length; | |
255 Item[] items2 = new Item[ll]; | |
256 for(int i = 1; i < ll; i++) | |
257 { | |
258 int index = items[i]; | |
259 int tag = b[index - 1]; | |
260 Item item = new Item(i); | |
261 int nameType; | |
262 switch(tag) | |
263 { | |
264 case ClassWriter.FIELD: | |
265 case ClassWriter.METH: | |
266 case ClassWriter.IMETH: | |
267 nameType = items[readUnsignedShort(index + 2)]; | |
268 item.set(tag, | |
269 readClass(index, buf), | |
270 readUTF8(nameType, buf), | |
271 readUTF8(nameType + 2, buf)); | |
272 break; | |
273 | |
274 case ClassWriter.INT: | |
275 item.set(readInt(index)); | |
276 break; | |
277 | |
278 case ClassWriter.FLOAT: | |
279 item.set(Float.intBitsToFloat(readInt(index))); | |
280 break; | |
281 | |
282 case ClassWriter.NAME_TYPE: | |
283 item.set(tag, | |
284 readUTF8(index, buf), | |
285 readUTF8(index + 2, buf), | |
286 null); | |
287 break; | |
288 | |
289 case ClassWriter.LONG: | |
290 item.set(readLong(index)); | |
291 ++i; | |
292 break; | |
293 | |
294 case ClassWriter.DOUBLE: | |
295 item.set(Double.longBitsToDouble(readLong(index))); | |
296 ++i; | |
297 break; | |
298 | |
299 case ClassWriter.UTF8: | |
300 { | |
301 String s = strings[i]; | |
302 if(s == null) | |
303 { | |
304 index = items[i]; | |
305 s = strings[i] = readUTF(index + 2, | |
306 readUnsignedShort(index), | |
307 buf); | |
308 } | |
309 item.set(tag, s, null, null); | |
310 } | |
311 break; | |
312 | |
313 // case ClassWriter.STR: | |
314 // case ClassWriter.CLASS: | |
315 default: | |
316 item.set(tag, readUTF8(index, buf), null, null); | |
317 break; | |
318 } | |
319 | |
320 int index2 = item.hashCode % items2.length; | |
321 item.next = items2[index2]; | |
322 items2[index2] = item; | |
323 } | |
324 | |
325 int off = items[1] - 1; | |
326 classWriter.pool.putByteArray(b, off, header - off); | |
327 classWriter.items = items2; | |
328 classWriter.threshold = (int) (0.75d * ll); | |
329 classWriter.index = ll; | |
330 } | |
331 | |
332 /** | |
333 * Constructs a new {@link ClassReader} object. | |
334 * | |
335 * @param is an input stream from which to read the class. | |
336 * @throws IOException if a problem occurs during reading. | |
337 */ | |
338 public ClassReader(final InputStream is) throws IOException{ | |
339 this(readClass(is)); | |
340 } | |
341 | |
342 /** | |
343 * Constructs a new {@link ClassReader} object. | |
344 * | |
345 * @param name the fully qualified name of the class to be read. | |
346 * @throws IOException if an exception occurs during reading. | |
347 */ | |
348 public ClassReader(final String name) throws IOException{ | |
349 this(ClassLoader.getSystemResourceAsStream(name.replace('.', '/') | |
350 + ".class")); | |
351 } | |
352 | |
353 /** | |
354 * Reads the bytecode of a class. | |
355 * | |
356 * @param is an input stream from which to read the class. | |
357 * @return the bytecode read from the given input stream. | |
358 * @throws IOException if a problem occurs during reading. | |
359 */ | |
360 private static byte[] readClass(final InputStream is) throws IOException{ | |
361 if(is == null) | |
362 { | |
363 throw new IOException("Class not found"); | |
364 } | |
365 byte[] b = new byte[is.available()]; | |
366 int len = 0; | |
367 while(true) | |
368 { | |
369 int n = is.read(b, len, b.length - len); | |
370 if(n == -1) | |
371 { | |
372 if(len < b.length) | |
373 { | |
374 byte[] c = new byte[len]; | |
375 System.arraycopy(b, 0, c, 0, len); | |
376 b = c; | |
377 } | |
378 return b; | |
379 } | |
380 len += n; | |
381 if(len == b.length) | |
382 { | |
383 byte[] c = new byte[b.length + 1000]; | |
384 System.arraycopy(b, 0, c, 0, len); | |
385 b = c; | |
386 } | |
387 } | |
388 } | |
389 | |
390 // ------------------------------------------------------------------------ | |
391 // Public methods | |
392 // ------------------------------------------------------------------------ | |
393 | |
394 /** | |
395 * Makes the given visitor visit the Java class of this {@link ClassReader}. | |
396 * This class is the one specified in the constructor (see | |
397 * {@link #ClassReader(byte[]) ClassReader}). | |
398 * | |
399 * @param classVisitor the visitor that must visit this class. | |
400 * @param flags option flags that can be used to modify the default behavior | |
401 * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}. | |
402 */ | |
403 public void accept(final ClassVisitor classVisitor, final int flags){ | |
404 accept(classVisitor, new Attribute[0], flags); | |
405 } | |
406 | |
407 /** | |
408 * Makes the given visitor visit the Java class of this {@link ClassReader}. | |
409 * This class is the one specified in the constructor (see | |
410 * {@link #ClassReader(byte[]) ClassReader}). | |
411 * | |
412 * @param classVisitor the visitor that must visit this class. | |
413 * @param attrs prototypes of the attributes that must be parsed during the | |
414 * visit of the class. Any attribute whose type is not equal to the | |
415 * type of one the prototypes will not be parsed: its byte array | |
416 * value will be passed unchanged to the ClassWriter. <i>This may | |
417 * corrupt it if this value contains references to the constant pool, | |
418 * or has syntactic or semantic links with a class element that has | |
419 * been transformed by a class adapter between the reader and the | |
420 * writer</i>. | |
421 * @param flags option flags that can be used to modify the default behavior | |
422 * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}. | |
423 */ | |
424 public void accept( | |
425 final ClassVisitor classVisitor, | |
426 final Attribute[] attrs, | |
427 final int flags){ | |
428 byte[] b = this.b; // the bytecode array | |
429 char[] c = new char[maxStringLength]; // buffer used to read strings | |
430 int i, j, k; // loop variables | |
431 int u, v, w; // indexes in b | |
432 Attribute attr; | |
433 | |
434 int access; | |
435 String name; | |
436 String desc; | |
437 String attrName; | |
438 String signature; | |
439 int anns = 0; | |
440 int ianns = 0; | |
441 Attribute cattrs = null; | |
442 | |
443 // visits the header | |
444 u = header; | |
445 access = readUnsignedShort(u); | |
446 name = readClass(u + 2, c); | |
447 v = items[readUnsignedShort(u + 4)]; | |
448 String superClassName = v == 0 ? null : readUTF8(v, c); | |
449 String[] implementedItfs = new String[readUnsignedShort(u + 6)]; | |
450 w = 0; | |
451 u += 8; | |
452 for(i = 0; i < implementedItfs.length; ++i) | |
453 { | |
454 implementedItfs[i] = readClass(u, c); | |
455 u += 2; | |
456 } | |
457 | |
458 boolean skipCode = (flags & SKIP_CODE) != 0; | |
459 boolean skipDebug = (flags & SKIP_DEBUG) != 0; | |
460 boolean unzip = (flags & EXPAND_FRAMES) != 0; | |
461 | |
462 // skips fields and methods | |
463 v = u; | |
464 i = readUnsignedShort(v); | |
465 v += 2; | |
466 for(; i > 0; --i) | |
467 { | |
468 j = readUnsignedShort(v + 6); | |
469 v += 8; | |
470 for(; j > 0; --j) | |
471 { | |
472 v += 6 + readInt(v + 2); | |
473 } | |
474 } | |
475 i = readUnsignedShort(v); | |
476 v += 2; | |
477 for(; i > 0; --i) | |
478 { | |
479 j = readUnsignedShort(v + 6); | |
480 v += 8; | |
481 for(; j > 0; --j) | |
482 { | |
483 v += 6 + readInt(v + 2); | |
484 } | |
485 } | |
486 // reads the class's attributes | |
487 signature = null; | |
488 String sourceFile = null; | |
489 String sourceDebug = null; | |
490 String enclosingOwner = null; | |
491 String enclosingName = null; | |
492 String enclosingDesc = null; | |
493 | |
494 i = readUnsignedShort(v); | |
495 v += 2; | |
496 for(; i > 0; --i) | |
497 { | |
498 attrName = readUTF8(v, c); | |
499 // tests are sorted in decreasing frequency order | |
500 // (based on frequencies observed on typical classes) | |
501 if(attrName.equals("SourceFile")) | |
502 { | |
503 sourceFile = readUTF8(v + 6, c); | |
504 } | |
505 else if(attrName.equals("InnerClasses")) | |
506 { | |
507 w = v + 6; | |
508 } | |
509 else if(attrName.equals("EnclosingMethod")) | |
510 { | |
511 enclosingOwner = readClass(v + 6, c); | |
512 int item = readUnsignedShort(v + 8); | |
513 if(item != 0) | |
514 { | |
515 enclosingName = readUTF8(items[item], c); | |
516 enclosingDesc = readUTF8(items[item] + 2, c); | |
517 } | |
518 } | |
519 else if(attrName.equals("Signature")) | |
520 { | |
521 signature = readUTF8(v + 6, c); | |
522 } | |
523 else if(attrName.equals("RuntimeVisibleAnnotations")) | |
524 { | |
525 anns = v + 6; | |
526 } | |
527 else if(attrName.equals("Deprecated")) | |
528 { | |
529 access |= Opcodes.ACC_DEPRECATED; | |
530 } | |
531 else if(attrName.equals("Synthetic")) | |
532 { | |
533 access |= Opcodes.ACC_SYNTHETIC; | |
534 } | |
535 else if(attrName.equals("SourceDebugExtension")) | |
536 { | |
537 int len = readInt(v + 2); | |
538 sourceDebug = readUTF(v + 6, len, new char[len]); | |
539 } | |
540 else if(attrName.equals("RuntimeInvisibleAnnotations")) | |
541 { | |
542 ianns = v + 6; | |
543 } | |
544 else | |
545 { | |
546 attr = readAttribute(attrs, | |
547 attrName, | |
548 v + 6, | |
549 readInt(v + 2), | |
550 c, | |
551 -1, | |
552 null); | |
553 if(attr != null) | |
554 { | |
555 attr.next = cattrs; | |
556 cattrs = attr; | |
557 } | |
558 } | |
559 v += 6 + readInt(v + 2); | |
560 } | |
561 // calls the visit method | |
562 classVisitor.visit(readInt(4), | |
563 access, | |
564 name, | |
565 signature, | |
566 superClassName, | |
567 implementedItfs); | |
568 | |
569 // calls the visitSource method | |
570 if(!skipDebug && (sourceFile != null || sourceDebug != null)) | |
571 { | |
572 classVisitor.visitSource(sourceFile, sourceDebug); | |
573 } | |
574 | |
575 // calls the visitOuterClass method | |
576 if(enclosingOwner != null) | |
577 { | |
578 classVisitor.visitOuterClass(enclosingOwner, | |
579 enclosingName, | |
580 enclosingDesc); | |
581 } | |
582 | |
583 // visits the class annotations | |
584 for(i = 1; i >= 0; --i) | |
585 { | |
586 v = i == 0 ? ianns : anns; | |
587 if(v != 0) | |
588 { | |
589 j = readUnsignedShort(v); | |
590 v += 2; | |
591 for(; j > 0; --j) | |
592 { | |
593 v = readAnnotationValues(v + 2, | |
594 c, | |
595 true, | |
596 classVisitor.visitAnnotation(readUTF8(v, c), i != 0)); | |
597 } | |
598 } | |
599 } | |
600 | |
601 // visits the class attributes | |
602 while(cattrs != null) | |
603 { | |
604 attr = cattrs.next; | |
605 cattrs.next = null; | |
606 classVisitor.visitAttribute(cattrs); | |
607 cattrs = attr; | |
608 } | |
609 | |
610 // calls the visitInnerClass method | |
611 if(w != 0) | |
612 { | |
613 i = readUnsignedShort(w); | |
614 w += 2; | |
615 for(; i > 0; --i) | |
616 { | |
617 classVisitor.visitInnerClass(readUnsignedShort(w) == 0 | |
618 ? null | |
619 : readClass(w, c), readUnsignedShort(w + 2) == 0 | |
620 ? null | |
621 : readClass(w + 2, c), readUnsignedShort(w + 4) == 0 | |
622 ? null | |
623 : readUTF8(w + 4, c), | |
624 readUnsignedShort(w + 6)); | |
625 w += 8; | |
626 } | |
627 } | |
628 | |
629 // visits the fields | |
630 i = readUnsignedShort(u); | |
631 u += 2; | |
632 for(; i > 0; --i) | |
633 { | |
634 access = readUnsignedShort(u); | |
635 name = readUTF8(u + 2, c); | |
636 desc = readUTF8(u + 4, c); | |
637 // visits the field's attributes and looks for a ConstantValue | |
638 // attribute | |
639 int fieldValueItem = 0; | |
640 signature = null; | |
641 anns = 0; | |
642 ianns = 0; | |
643 cattrs = null; | |
644 | |
645 j = readUnsignedShort(u + 6); | |
646 u += 8; | |
647 for(; j > 0; --j) | |
648 { | |
649 attrName = readUTF8(u, c); | |
650 // tests are sorted in decreasing frequency order | |
651 // (based on frequencies observed on typical classes) | |
652 if(attrName.equals("ConstantValue")) | |
653 { | |
654 fieldValueItem = readUnsignedShort(u + 6); | |
655 } | |
656 else if(attrName.equals("Signature")) | |
657 { | |
658 signature = readUTF8(u + 6, c); | |
659 } | |
660 else if(attrName.equals("Deprecated")) | |
661 { | |
662 access |= Opcodes.ACC_DEPRECATED; | |
663 } | |
664 else if(attrName.equals("Synthetic")) | |
665 { | |
666 access |= Opcodes.ACC_SYNTHETIC; | |
667 } | |
668 else if(attrName.equals("RuntimeVisibleAnnotations")) | |
669 { | |
670 anns = u + 6; | |
671 } | |
672 else if(attrName.equals("RuntimeInvisibleAnnotations")) | |
673 { | |
674 ianns = u + 6; | |
675 } | |
676 else | |
677 { | |
678 attr = readAttribute(attrs, | |
679 attrName, | |
680 u + 6, | |
681 readInt(u + 2), | |
682 c, | |
683 -1, | |
684 null); | |
685 if(attr != null) | |
686 { | |
687 attr.next = cattrs; | |
688 cattrs = attr; | |
689 } | |
690 } | |
691 u += 6 + readInt(u + 2); | |
692 } | |
693 // visits the field | |
694 FieldVisitor fv = classVisitor.visitField(access, | |
695 name, | |
696 desc, | |
697 signature, | |
698 fieldValueItem == 0 ? null : readConst(fieldValueItem, c)); | |
699 // visits the field annotations and attributes | |
700 if(fv != null) | |
701 { | |
702 for(j = 1; j >= 0; --j) | |
703 { | |
704 v = j == 0 ? ianns : anns; | |
705 if(v != 0) | |
706 { | |
707 k = readUnsignedShort(v); | |
708 v += 2; | |
709 for(; k > 0; --k) | |
710 { | |
711 v = readAnnotationValues(v + 2, | |
712 c, | |
713 true, | |
714 fv.visitAnnotation(readUTF8(v, c), j != 0)); | |
715 } | |
716 } | |
717 } | |
718 while(cattrs != null) | |
719 { | |
720 attr = cattrs.next; | |
721 cattrs.next = null; | |
722 fv.visitAttribute(cattrs); | |
723 cattrs = attr; | |
724 } | |
725 fv.visitEnd(); | |
726 } | |
727 } | |
728 | |
729 // visits the methods | |
730 i = readUnsignedShort(u); | |
731 u += 2; | |
732 for(; i > 0; --i) | |
733 { | |
734 int u0 = u + 6; | |
735 access = readUnsignedShort(u); | |
736 name = readUTF8(u + 2, c); | |
737 desc = readUTF8(u + 4, c); | |
738 signature = null; | |
739 anns = 0; | |
740 ianns = 0; | |
741 int dann = 0; | |
742 int mpanns = 0; | |
743 int impanns = 0; | |
744 cattrs = null; | |
745 v = 0; | |
746 w = 0; | |
747 | |
748 // looks for Code and Exceptions attributes | |
749 j = readUnsignedShort(u + 6); | |
750 u += 8; | |
751 for(; j > 0; --j) | |
752 { | |
753 attrName = readUTF8(u, c); | |
754 int attrSize = readInt(u + 2); | |
755 u += 6; | |
756 // tests are sorted in decreasing frequency order | |
757 // (based on frequencies observed on typical classes) | |
758 if(attrName.equals("Code")) | |
759 { | |
760 if(!skipCode) | |
761 { | |
762 v = u; | |
763 } | |
764 } | |
765 else if(attrName.equals("Exceptions")) | |
766 { | |
767 w = u; | |
768 } | |
769 else if(attrName.equals("Signature")) | |
770 { | |
771 signature = readUTF8(u, c); | |
772 } | |
773 else if(attrName.equals("Deprecated")) | |
774 { | |
775 access |= Opcodes.ACC_DEPRECATED; | |
776 } | |
777 else if(attrName.equals("RuntimeVisibleAnnotations")) | |
778 { | |
779 anns = u; | |
780 } | |
781 else if(attrName.equals("AnnotationDefault")) | |
782 { | |
783 dann = u; | |
784 } | |
785 else if(attrName.equals("Synthetic")) | |
786 { | |
787 access |= Opcodes.ACC_SYNTHETIC; | |
788 } | |
789 else if(attrName.equals("RuntimeInvisibleAnnotations")) | |
790 { | |
791 ianns = u; | |
792 } | |
793 else if(attrName.equals("RuntimeVisibleParameterAnnotations")) | |
794 { | |
795 mpanns = u; | |
796 } | |
797 else if(attrName.equals("RuntimeInvisibleParameterAnnotations")) | |
798 { | |
799 impanns = u; | |
800 } | |
801 else | |
802 { | |
803 attr = readAttribute(attrs, | |
804 attrName, | |
805 u, | |
806 attrSize, | |
807 c, | |
808 -1, | |
809 null); | |
810 if(attr != null) | |
811 { | |
812 attr.next = cattrs; | |
813 cattrs = attr; | |
814 } | |
815 } | |
816 u += attrSize; | |
817 } | |
818 // reads declared exceptions | |
819 String[] exceptions; | |
820 if(w == 0) | |
821 { | |
822 exceptions = null; | |
823 } | |
824 else | |
825 { | |
826 exceptions = new String[readUnsignedShort(w)]; | |
827 w += 2; | |
828 for(j = 0; j < exceptions.length; ++j) | |
829 { | |
830 exceptions[j] = readClass(w, c); | |
831 w += 2; | |
832 } | |
833 } | |
834 | |
835 // visits the method's code, if any | |
836 MethodVisitor mv = classVisitor.visitMethod(access, | |
837 name, | |
838 desc, | |
839 signature, | |
840 exceptions); | |
841 | |
842 if(mv != null) | |
843 { | |
844 /* | |
845 * if the returned MethodVisitor is in fact a MethodWriter, it | |
846 * means there is no method adapter between the reader and the | |
847 * writer. If, in addition, the writer's constant pool was | |
848 * copied from this reader (mw.cw.cr == this), and the signature | |
849 * and exceptions of the method have not been changed, then it | |
850 * is possible to skip all visit events and just copy the | |
851 * original code of the method to the writer (the access, name | |
852 * and descriptor can have been changed, this is not important | |
853 * since they are not copied as is from the reader). | |
854 */ | |
855 if(mv instanceof MethodWriter) | |
856 { | |
857 MethodWriter mw = (MethodWriter) mv; | |
858 if(mw.cw.cr == this) | |
859 { | |
860 if(signature == mw.signature) | |
861 { | |
862 boolean sameExceptions = false; | |
863 if(exceptions == null) | |
864 { | |
865 sameExceptions = mw.exceptionCount == 0; | |
866 } | |
867 else | |
868 { | |
869 if(exceptions.length == mw.exceptionCount) | |
870 { | |
871 sameExceptions = true; | |
872 for(j = exceptions.length - 1; j >= 0; --j) | |
873 { | |
874 w -= 2; | |
875 if(mw.exceptions[j] != readUnsignedShort(w)) | |
876 { | |
877 sameExceptions = false; | |
878 break; | |
879 } | |
880 } | |
881 } | |
882 } | |
883 if(sameExceptions) | |
884 { | |
885 /* | |
886 * we do not copy directly the code into | |
887 * MethodWriter to save a byte array copy | |
888 * operation. The real copy will be done in | |
889 * ClassWriter.toByteArray(). | |
890 */ | |
891 mw.classReaderOffset = u0; | |
892 mw.classReaderLength = u - u0; | |
893 continue; | |
894 } | |
895 } | |
896 } | |
897 } | |
898 | |
899 if(dann != 0) | |
900 { | |
901 AnnotationVisitor dv = mv.visitAnnotationDefault(); | |
902 readAnnotationValue(dann, c, null, dv); | |
903 if(dv != null) | |
904 { | |
905 dv.visitEnd(); | |
906 } | |
907 } | |
908 for(j = 1; j >= 0; --j) | |
909 { | |
910 w = j == 0 ? ianns : anns; | |
911 if(w != 0) | |
912 { | |
913 k = readUnsignedShort(w); | |
914 w += 2; | |
915 for(; k > 0; --k) | |
916 { | |
917 w = readAnnotationValues(w + 2, | |
918 c, | |
919 true, | |
920 mv.visitAnnotation(readUTF8(w, c), j != 0)); | |
921 } | |
922 } | |
923 } | |
924 if(mpanns != 0) | |
925 { | |
926 readParameterAnnotations(mpanns, c, true, mv); | |
927 } | |
928 if(impanns != 0) | |
929 { | |
930 readParameterAnnotations(impanns, c, false, mv); | |
931 } | |
932 while(cattrs != null) | |
933 { | |
934 attr = cattrs.next; | |
935 cattrs.next = null; | |
936 mv.visitAttribute(cattrs); | |
937 cattrs = attr; | |
938 } | |
939 } | |
940 | |
941 if(mv != null && v != 0) | |
942 { | |
943 int maxStack = readUnsignedShort(v); | |
944 int maxLocals = readUnsignedShort(v + 2); | |
945 int codeLength = readInt(v + 4); | |
946 v += 8; | |
947 | |
948 int codeStart = v; | |
949 int codeEnd = v + codeLength; | |
950 | |
951 mv.visitCode(); | |
952 | |
953 // 1st phase: finds the labels | |
954 int label; | |
955 Label[] labels = new Label[codeLength + 1]; | |
956 while(v < codeEnd) | |
957 { | |
958 int opcode = b[v] & 0xFF; | |
959 switch(ClassWriter.TYPE[opcode]) | |
960 { | |
961 case ClassWriter.NOARG_INSN: | |
962 case ClassWriter.IMPLVAR_INSN: | |
963 v += 1; | |
964 break; | |
965 case ClassWriter.LABEL_INSN: | |
966 label = v - codeStart + readShort(v + 1); | |
967 if(labels[label] == null) | |
968 { | |
969 labels[label] = new Label(); | |
970 } | |
971 v += 3; | |
972 break; | |
973 case ClassWriter.LABELW_INSN: | |
974 label = v - codeStart + readInt(v + 1); | |
975 if(labels[label] == null) | |
976 { | |
977 labels[label] = new Label(); | |
978 } | |
979 v += 5; | |
980 break; | |
981 case ClassWriter.WIDE_INSN: | |
982 opcode = b[v + 1] & 0xFF; | |
983 if(opcode == Opcodes.IINC) | |
984 { | |
985 v += 6; | |
986 } | |
987 else | |
988 { | |
989 v += 4; | |
990 } | |
991 break; | |
992 case ClassWriter.TABL_INSN: | |
993 // skips 0 to 3 padding bytes | |
994 w = v - codeStart; | |
995 v = v + 4 - (w & 3); | |
996 // reads instruction | |
997 label = w + readInt(v); | |
998 if(labels[label] == null) | |
999 { | |
1000 labels[label] = new Label(); | |
1001 } | |
1002 j = readInt(v + 8) - readInt(v + 4) + 1; | |
1003 v += 12; | |
1004 for(; j > 0; --j) | |
1005 { | |
1006 label = w + readInt(v); | |
1007 v += 4; | |
1008 if(labels[label] == null) | |
1009 { | |
1010 labels[label] = new Label(); | |
1011 } | |
1012 } | |
1013 break; | |
1014 case ClassWriter.LOOK_INSN: | |
1015 // skips 0 to 3 padding bytes | |
1016 w = v - codeStart; | |
1017 v = v + 4 - (w & 3); | |
1018 // reads instruction | |
1019 label = w + readInt(v); | |
1020 if(labels[label] == null) | |
1021 { | |
1022 labels[label] = new Label(); | |
1023 } | |
1024 j = readInt(v + 4); | |
1025 v += 8; | |
1026 for(; j > 0; --j) | |
1027 { | |
1028 label = w + readInt(v + 4); | |
1029 v += 8; | |
1030 if(labels[label] == null) | |
1031 { | |
1032 labels[label] = new Label(); | |
1033 } | |
1034 } | |
1035 break; | |
1036 case ClassWriter.VAR_INSN: | |
1037 case ClassWriter.SBYTE_INSN: | |
1038 case ClassWriter.LDC_INSN: | |
1039 v += 2; | |
1040 break; | |
1041 case ClassWriter.SHORT_INSN: | |
1042 case ClassWriter.LDCW_INSN: | |
1043 case ClassWriter.FIELDORMETH_INSN: | |
1044 case ClassWriter.TYPE_INSN: | |
1045 case ClassWriter.IINC_INSN: | |
1046 v += 3; | |
1047 break; | |
1048 case ClassWriter.ITFMETH_INSN: | |
1049 v += 5; | |
1050 break; | |
1051 // case MANA_INSN: | |
1052 default: | |
1053 v += 4; | |
1054 break; | |
1055 } | |
1056 } | |
1057 // parses the try catch entries | |
1058 j = readUnsignedShort(v); | |
1059 v += 2; | |
1060 for(; j > 0; --j) | |
1061 { | |
1062 label = readUnsignedShort(v); | |
1063 Label start = labels[label]; | |
1064 if(start == null) | |
1065 { | |
1066 labels[label] = start = new Label(); | |
1067 } | |
1068 label = readUnsignedShort(v + 2); | |
1069 Label end = labels[label]; | |
1070 if(end == null) | |
1071 { | |
1072 labels[label] = end = new Label(); | |
1073 } | |
1074 label = readUnsignedShort(v + 4); | |
1075 Label handler = labels[label]; | |
1076 if(handler == null) | |
1077 { | |
1078 labels[label] = handler = new Label(); | |
1079 } | |
1080 int type = readUnsignedShort(v + 6); | |
1081 if(type == 0) | |
1082 { | |
1083 mv.visitTryCatchBlock(start, end, handler, null); | |
1084 } | |
1085 else | |
1086 { | |
1087 mv.visitTryCatchBlock(start, | |
1088 end, | |
1089 handler, | |
1090 readUTF8(items[type], c)); | |
1091 } | |
1092 v += 8; | |
1093 } | |
1094 // parses the local variable, line number tables, and code | |
1095 // attributes | |
1096 int varTable = 0; | |
1097 int varTypeTable = 0; | |
1098 int stackMap = 0; | |
1099 int frameCount = 0; | |
1100 int frameMode = 0; | |
1101 int frameOffset = 0; | |
1102 int frameLocalCount = 0; | |
1103 int frameLocalDiff = 0; | |
1104 int frameStackCount = 0; | |
1105 Object[] frameLocal = null; | |
1106 Object[] frameStack = null; | |
1107 boolean zip = true; | |
1108 cattrs = null; | |
1109 j = readUnsignedShort(v); | |
1110 v += 2; | |
1111 for(; j > 0; --j) | |
1112 { | |
1113 attrName = readUTF8(v, c); | |
1114 if(attrName.equals("LocalVariableTable")) | |
1115 { | |
1116 if(!skipDebug) | |
1117 { | |
1118 varTable = v + 6; | |
1119 k = readUnsignedShort(v + 6); | |
1120 w = v + 8; | |
1121 for(; k > 0; --k) | |
1122 { | |
1123 label = readUnsignedShort(w); | |
1124 if(labels[label] == null) | |
1125 { | |
1126 labels[label] = new Label(true); | |
1127 } | |
1128 label += readUnsignedShort(w + 2); | |
1129 if(labels[label] == null) | |
1130 { | |
1131 labels[label] = new Label(true); | |
1132 } | |
1133 w += 10; | |
1134 } | |
1135 } | |
1136 } | |
1137 else if(attrName.equals("LocalVariableTypeTable")) | |
1138 { | |
1139 varTypeTable = v + 6; | |
1140 } | |
1141 else if(attrName.equals("LineNumberTable")) | |
1142 { | |
1143 if(!skipDebug) | |
1144 { | |
1145 k = readUnsignedShort(v + 6); | |
1146 w = v + 8; | |
1147 for(; k > 0; --k) | |
1148 { | |
1149 label = readUnsignedShort(w); | |
1150 if(labels[label] == null) | |
1151 { | |
1152 labels[label] = new Label(true); | |
1153 } | |
1154 labels[label].line = readUnsignedShort(w + 2); | |
1155 w += 4; | |
1156 } | |
1157 } | |
1158 } | |
1159 else if(attrName.equals("StackMapTable")) | |
1160 { | |
1161 if((flags & SKIP_FRAMES) == 0) | |
1162 { | |
1163 stackMap = v + 8; | |
1164 frameCount = readUnsignedShort(v + 6); | |
1165 } | |
1166 /* | |
1167 * here we do not extract the labels corresponding to | |
1168 * the attribute content. This would require a full | |
1169 * parsing of the attribute, which would need to be | |
1170 * repeated in the second phase (see below). Instead the | |
1171 * content of the attribute is read one frame at a time | |
1172 * (i.e. after a frame has been visited, the next frame | |
1173 * is read), and the labels it contains are also | |
1174 * extracted one frame at a time. Thanks to the ordering | |
1175 * of frames, having only a "one frame lookahead" is not | |
1176 * a problem, i.e. it is not possible to see an offset | |
1177 * smaller than the offset of the current insn and for | |
1178 * which no Label exist. | |
1179 */ | |
1180 // TODO true for frame offsets, | |
1181 // but for UNINITIALIZED type offsets? | |
1182 } | |
1183 else if(attrName.equals("StackMap")) | |
1184 { | |
1185 if((flags & SKIP_FRAMES) == 0) | |
1186 { | |
1187 stackMap = v + 8; | |
1188 frameCount = readUnsignedShort(v + 6); | |
1189 zip = false; | |
1190 } | |
1191 /* | |
1192 * IMPORTANT! here we assume that the frames are | |
1193 * ordered, as in the StackMapTable attribute, although | |
1194 * this is not guaranteed by the attribute format. | |
1195 */ | |
1196 } | |
1197 else | |
1198 { | |
1199 for(k = 0; k < attrs.length; ++k) | |
1200 { | |
1201 if(attrs[k].type.equals(attrName)) | |
1202 { | |
1203 attr = attrs[k].read(this, | |
1204 v + 6, | |
1205 readInt(v + 2), | |
1206 c, | |
1207 codeStart - 8, | |
1208 labels); | |
1209 if(attr != null) | |
1210 { | |
1211 attr.next = cattrs; | |
1212 cattrs = attr; | |
1213 } | |
1214 } | |
1215 } | |
1216 } | |
1217 v += 6 + readInt(v + 2); | |
1218 } | |
1219 | |
1220 // 2nd phase: visits each instruction | |
1221 if(stackMap != 0) | |
1222 { | |
1223 // creates the very first (implicit) frame from the method | |
1224 // descriptor | |
1225 frameLocal = new Object[maxLocals]; | |
1226 frameStack = new Object[maxStack]; | |
1227 if(unzip) | |
1228 { | |
1229 int local = 0; | |
1230 if((access & Opcodes.ACC_STATIC) == 0) | |
1231 { | |
1232 if(name.equals("<init>")) | |
1233 { | |
1234 frameLocal[local++] = Opcodes.UNINITIALIZED_THIS; | |
1235 } | |
1236 else | |
1237 { | |
1238 frameLocal[local++] = readClass(header + 2, c); | |
1239 } | |
1240 } | |
1241 j = 1; | |
1242 loop: | |
1243 while(true) | |
1244 { | |
1245 k = j; | |
1246 switch(desc.charAt(j++)) | |
1247 { | |
1248 case'Z': | |
1249 case'C': | |
1250 case'B': | |
1251 case'S': | |
1252 case'I': | |
1253 frameLocal[local++] = Opcodes.INTEGER; | |
1254 break; | |
1255 case'F': | |
1256 frameLocal[local++] = Opcodes.FLOAT; | |
1257 break; | |
1258 case'J': | |
1259 frameLocal[local++] = Opcodes.LONG; | |
1260 break; | |
1261 case'D': | |
1262 frameLocal[local++] = Opcodes.DOUBLE; | |
1263 break; | |
1264 case'[': | |
1265 while(desc.charAt(j) == '[') | |
1266 { | |
1267 ++j; | |
1268 } | |
1269 if(desc.charAt(j) == 'L') | |
1270 { | |
1271 ++j; | |
1272 while(desc.charAt(j) != ';') | |
1273 { | |
1274 ++j; | |
1275 } | |
1276 } | |
1277 frameLocal[local++] = desc.substring(k, ++j); | |
1278 break; | |
1279 case'L': | |
1280 while(desc.charAt(j) != ';') | |
1281 { | |
1282 ++j; | |
1283 } | |
1284 frameLocal[local++] = desc.substring(k + 1, | |
1285 j++); | |
1286 break; | |
1287 default: | |
1288 break loop; | |
1289 } | |
1290 } | |
1291 frameLocalCount = local; | |
1292 } | |
1293 /* | |
1294 * for the first explicit frame the offset is not | |
1295 * offset_delta + 1 but only offset_delta; setting the | |
1296 * implicit frame offset to -1 allow the use of the | |
1297 * "offset_delta + 1" rule in all cases | |
1298 */ | |
1299 frameOffset = -1; | |
1300 } | |
1301 v = codeStart; | |
1302 Label l; | |
1303 while(v < codeEnd) | |
1304 { | |
1305 w = v - codeStart; | |
1306 | |
1307 l = labels[w]; | |
1308 if(l != null) | |
1309 { | |
1310 mv.visitLabel(l); | |
1311 if(!skipDebug && l.line > 0) | |
1312 { | |
1313 mv.visitLineNumber(l.line, l); | |
1314 } | |
1315 } | |
1316 | |
1317 while(frameLocal != null | |
1318 && (frameOffset == w || frameOffset == -1)) | |
1319 { | |
1320 // if there is a frame for this offset, | |
1321 // makes the visitor visit it, | |
1322 // and reads the next frame if there is one. | |
1323 if(!zip || unzip) | |
1324 { | |
1325 mv.visitFrame(Opcodes.F_NEW, | |
1326 frameLocalCount, | |
1327 frameLocal, | |
1328 frameStackCount, | |
1329 frameStack); | |
1330 } | |
1331 else if(frameOffset != -1) | |
1332 { | |
1333 mv.visitFrame(frameMode, | |
1334 frameLocalDiff, | |
1335 frameLocal, | |
1336 frameStackCount, | |
1337 frameStack); | |
1338 } | |
1339 | |
1340 if(frameCount > 0) | |
1341 { | |
1342 int tag, delta, n; | |
1343 if(zip) | |
1344 { | |
1345 tag = b[stackMap++] & 0xFF; | |
1346 } | |
1347 else | |
1348 { | |
1349 tag = MethodWriter.FULL_FRAME; | |
1350 frameOffset = -1; | |
1351 } | |
1352 frameLocalDiff = 0; | |
1353 if(tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME) | |
1354 { | |
1355 delta = tag; | |
1356 frameMode = Opcodes.F_SAME; | |
1357 frameStackCount = 0; | |
1358 } | |
1359 else if(tag < MethodWriter.RESERVED) | |
1360 { | |
1361 delta = tag | |
1362 - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME; | |
1363 stackMap = readFrameType(frameStack, | |
1364 0, | |
1365 stackMap, | |
1366 c, | |
1367 labels); | |
1368 frameMode = Opcodes.F_SAME1; | |
1369 frameStackCount = 1; | |
1370 } | |
1371 else | |
1372 { | |
1373 delta = readUnsignedShort(stackMap); | |
1374 stackMap += 2; | |
1375 if(tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) | |
1376 { | |
1377 stackMap = readFrameType(frameStack, | |
1378 0, | |
1379 stackMap, | |
1380 c, | |
1381 labels); | |
1382 frameMode = Opcodes.F_SAME1; | |
1383 frameStackCount = 1; | |
1384 } | |
1385 else if(tag >= MethodWriter.CHOP_FRAME | |
1386 && tag < MethodWriter.SAME_FRAME_EXTENDED) | |
1387 { | |
1388 frameMode = Opcodes.F_CHOP; | |
1389 frameLocalDiff = MethodWriter.SAME_FRAME_EXTENDED | |
1390 - tag; | |
1391 frameLocalCount -= frameLocalDiff; | |
1392 frameStackCount = 0; | |
1393 } | |
1394 else if(tag == MethodWriter.SAME_FRAME_EXTENDED) | |
1395 { | |
1396 frameMode = Opcodes.F_SAME; | |
1397 frameStackCount = 0; | |
1398 } | |
1399 else if(tag < MethodWriter.FULL_FRAME) | |
1400 { | |
1401 j = unzip ? frameLocalCount : 0; | |
1402 for(k = tag | |
1403 - MethodWriter.SAME_FRAME_EXTENDED; k > 0; k--) | |
1404 { | |
1405 stackMap = readFrameType(frameLocal, | |
1406 j++, | |
1407 stackMap, | |
1408 c, | |
1409 labels); | |
1410 } | |
1411 frameMode = Opcodes.F_APPEND; | |
1412 frameLocalDiff = tag | |
1413 - MethodWriter.SAME_FRAME_EXTENDED; | |
1414 frameLocalCount += frameLocalDiff; | |
1415 frameStackCount = 0; | |
1416 } | |
1417 else | |
1418 { // if (tag == FULL_FRAME) { | |
1419 frameMode = Opcodes.F_FULL; | |
1420 n = frameLocalDiff = frameLocalCount = readUnsignedShort(stackMap); | |
1421 stackMap += 2; | |
1422 for(j = 0; n > 0; n--) | |
1423 { | |
1424 stackMap = readFrameType(frameLocal, | |
1425 j++, | |
1426 stackMap, | |
1427 c, | |
1428 labels); | |
1429 } | |
1430 n = frameStackCount = readUnsignedShort(stackMap); | |
1431 stackMap += 2; | |
1432 for(j = 0; n > 0; n--) | |
1433 { | |
1434 stackMap = readFrameType(frameStack, | |
1435 j++, | |
1436 stackMap, | |
1437 c, | |
1438 labels); | |
1439 } | |
1440 } | |
1441 } | |
1442 frameOffset += delta + 1; | |
1443 if(labels[frameOffset] == null) | |
1444 { | |
1445 labels[frameOffset] = new Label(); | |
1446 } | |
1447 | |
1448 --frameCount; | |
1449 } | |
1450 else | |
1451 { | |
1452 frameLocal = null; | |
1453 } | |
1454 } | |
1455 | |
1456 int opcode = b[v] & 0xFF; | |
1457 switch(ClassWriter.TYPE[opcode]) | |
1458 { | |
1459 case ClassWriter.NOARG_INSN: | |
1460 mv.visitInsn(opcode); | |
1461 v += 1; | |
1462 break; | |
1463 case ClassWriter.IMPLVAR_INSN: | |
1464 if(opcode > Opcodes.ISTORE) | |
1465 { | |
1466 opcode -= 59; // ISTORE_0 | |
1467 mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), | |
1468 opcode & 0x3); | |
1469 } | |
1470 else | |
1471 { | |
1472 opcode -= 26; // ILOAD_0 | |
1473 mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), | |
1474 opcode & 0x3); | |
1475 } | |
1476 v += 1; | |
1477 break; | |
1478 case ClassWriter.LABEL_INSN: | |
1479 mv.visitJumpInsn(opcode, labels[w | |
1480 + readShort(v + 1)]); | |
1481 v += 3; | |
1482 break; | |
1483 case ClassWriter.LABELW_INSN: | |
1484 mv.visitJumpInsn(opcode - 33, labels[w | |
1485 + readInt(v + 1)]); | |
1486 v += 5; | |
1487 break; | |
1488 case ClassWriter.WIDE_INSN: | |
1489 opcode = b[v + 1] & 0xFF; | |
1490 if(opcode == Opcodes.IINC) | |
1491 { | |
1492 mv.visitIincInsn(readUnsignedShort(v + 2), | |
1493 readShort(v + 4)); | |
1494 v += 6; | |
1495 } | |
1496 else | |
1497 { | |
1498 mv.visitVarInsn(opcode, | |
1499 readUnsignedShort(v + 2)); | |
1500 v += 4; | |
1501 } | |
1502 break; | |
1503 case ClassWriter.TABL_INSN: | |
1504 // skips 0 to 3 padding bytes | |
1505 v = v + 4 - (w & 3); | |
1506 // reads instruction | |
1507 label = w + readInt(v); | |
1508 int min = readInt(v + 4); | |
1509 int max = readInt(v + 8); | |
1510 v += 12; | |
1511 Label[] table = new Label[max - min + 1]; | |
1512 for(j = 0; j < table.length; ++j) | |
1513 { | |
1514 table[j] = labels[w + readInt(v)]; | |
1515 v += 4; | |
1516 } | |
1517 mv.visitTableSwitchInsn(min, | |
1518 max, | |
1519 labels[label], | |
1520 table); | |
1521 break; | |
1522 case ClassWriter.LOOK_INSN: | |
1523 // skips 0 to 3 padding bytes | |
1524 v = v + 4 - (w & 3); | |
1525 // reads instruction | |
1526 label = w + readInt(v); | |
1527 j = readInt(v + 4); | |
1528 v += 8; | |
1529 int[] keys = new int[j]; | |
1530 Label[] values = new Label[j]; | |
1531 for(j = 0; j < keys.length; ++j) | |
1532 { | |
1533 keys[j] = readInt(v); | |
1534 values[j] = labels[w + readInt(v + 4)]; | |
1535 v += 8; | |
1536 } | |
1537 mv.visitLookupSwitchInsn(labels[label], | |
1538 keys, | |
1539 values); | |
1540 break; | |
1541 case ClassWriter.VAR_INSN: | |
1542 mv.visitVarInsn(opcode, b[v + 1] & 0xFF); | |
1543 v += 2; | |
1544 break; | |
1545 case ClassWriter.SBYTE_INSN: | |
1546 mv.visitIntInsn(opcode, b[v + 1]); | |
1547 v += 2; | |
1548 break; | |
1549 case ClassWriter.SHORT_INSN: | |
1550 mv.visitIntInsn(opcode, readShort(v + 1)); | |
1551 v += 3; | |
1552 break; | |
1553 case ClassWriter.LDC_INSN: | |
1554 mv.visitLdcInsn(readConst(b[v + 1] & 0xFF, c)); | |
1555 v += 2; | |
1556 break; | |
1557 case ClassWriter.LDCW_INSN: | |
1558 mv.visitLdcInsn(readConst(readUnsignedShort(v + 1), | |
1559 c)); | |
1560 v += 3; | |
1561 break; | |
1562 case ClassWriter.FIELDORMETH_INSN: | |
1563 case ClassWriter.ITFMETH_INSN: | |
1564 int cpIndex = items[readUnsignedShort(v + 1)]; | |
1565 String iowner = readClass(cpIndex, c); | |
1566 cpIndex = items[readUnsignedShort(cpIndex + 2)]; | |
1567 String iname = readUTF8(cpIndex, c); | |
1568 String idesc = readUTF8(cpIndex + 2, c); | |
1569 if(opcode < Opcodes.INVOKEVIRTUAL) | |
1570 { | |
1571 mv.visitFieldInsn(opcode, iowner, iname, idesc); | |
1572 } | |
1573 else | |
1574 { | |
1575 mv.visitMethodInsn(opcode, iowner, iname, idesc); | |
1576 } | |
1577 if(opcode == Opcodes.INVOKEINTERFACE) | |
1578 { | |
1579 v += 5; | |
1580 } | |
1581 else | |
1582 { | |
1583 v += 3; | |
1584 } | |
1585 break; | |
1586 case ClassWriter.TYPE_INSN: | |
1587 mv.visitTypeInsn(opcode, readClass(v + 1, c)); | |
1588 v += 3; | |
1589 break; | |
1590 case ClassWriter.IINC_INSN: | |
1591 mv.visitIincInsn(b[v + 1] & 0xFF, b[v + 2]); | |
1592 v += 3; | |
1593 break; | |
1594 // case MANA_INSN: | |
1595 default: | |
1596 mv.visitMultiANewArrayInsn(readClass(v + 1, c), | |
1597 b[v + 3] & 0xFF); | |
1598 v += 4; | |
1599 break; | |
1600 } | |
1601 } | |
1602 l = labels[codeEnd - codeStart]; | |
1603 if(l != null) | |
1604 { | |
1605 mv.visitLabel(l); | |
1606 } | |
1607 // visits the local variable tables | |
1608 if(!skipDebug && varTable != 0) | |
1609 { | |
1610 int[] typeTable = null; | |
1611 if(varTypeTable != 0) | |
1612 { | |
1613 k = readUnsignedShort(varTypeTable) * 3; | |
1614 w = varTypeTable + 2; | |
1615 typeTable = new int[k]; | |
1616 while(k > 0) | |
1617 { | |
1618 typeTable[--k] = w + 6; // signature | |
1619 typeTable[--k] = readUnsignedShort(w + 8); // index | |
1620 typeTable[--k] = readUnsignedShort(w); // start | |
1621 w += 10; | |
1622 } | |
1623 } | |
1624 k = readUnsignedShort(varTable); | |
1625 w = varTable + 2; | |
1626 for(; k > 0; --k) | |
1627 { | |
1628 int start = readUnsignedShort(w); | |
1629 int length = readUnsignedShort(w + 2); | |
1630 int index = readUnsignedShort(w + 8); | |
1631 String vsignature = null; | |
1632 if(typeTable != null) | |
1633 { | |
1634 for(int a = 0; a < typeTable.length; a += 3) | |
1635 { | |
1636 if(typeTable[a] == start | |
1637 && typeTable[a + 1] == index) | |
1638 { | |
1639 vsignature = readUTF8(typeTable[a + 2], c); | |
1640 break; | |
1641 } | |
1642 } | |
1643 } | |
1644 mv.visitLocalVariable(readUTF8(w + 4, c), | |
1645 readUTF8(w + 6, c), | |
1646 vsignature, | |
1647 labels[start], | |
1648 labels[start + length], | |
1649 index); | |
1650 w += 10; | |
1651 } | |
1652 } | |
1653 // visits the other attributes | |
1654 while(cattrs != null) | |
1655 { | |
1656 attr = cattrs.next; | |
1657 cattrs.next = null; | |
1658 mv.visitAttribute(cattrs); | |
1659 cattrs = attr; | |
1660 } | |
1661 // visits the max stack and max locals values | |
1662 mv.visitMaxs(maxStack, maxLocals); | |
1663 } | |
1664 | |
1665 if(mv != null) | |
1666 { | |
1667 mv.visitEnd(); | |
1668 } | |
1669 } | |
1670 | |
1671 // visits the end of the class | |
1672 classVisitor.visitEnd(); | |
1673 } | |
1674 | |
1675 /** | |
1676 * Reads parameter annotations and makes the given visitor visit them. | |
1677 * | |
1678 * @param v start offset in {@link #b b} of the annotations to be read. | |
1679 * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, | |
1680 * {@link #readClass(int,char[]) readClass} or | |
1681 * {@link #readConst readConst}. | |
1682 * @param visible <tt>true</tt> if the annotations to be read are visible | |
1683 * at runtime. | |
1684 * @param mv the visitor that must visit the annotations. | |
1685 */ | |
1686 private void readParameterAnnotations( | |
1687 int v, | |
1688 final char[] buf, | |
1689 final boolean visible, | |
1690 final MethodVisitor mv){ | |
1691 int n = b[v++] & 0xFF; | |
1692 for(int i = 0; i < n; ++i) | |
1693 { | |
1694 int j = readUnsignedShort(v); | |
1695 v += 2; | |
1696 for(; j > 0; --j) | |
1697 { | |
1698 v = readAnnotationValues(v + 2, | |
1699 buf, | |
1700 true, | |
1701 mv.visitParameterAnnotation(i, | |
1702 readUTF8(v, buf), | |
1703 visible)); | |
1704 } | |
1705 } | |
1706 } | |
1707 | |
1708 /** | |
1709 * Reads the values of an annotation and makes the given visitor visit them. | |
1710 * | |
1711 * @param v the start offset in {@link #b b} of the values to be read | |
1712 * (including the unsigned short that gives the number of values). | |
1713 * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, | |
1714 * {@link #readClass(int,char[]) readClass} or | |
1715 * {@link #readConst readConst}. | |
1716 * @param named if the annotation values are named or not. | |
1717 * @param av the visitor that must visit the values. | |
1718 * @return the end offset of the annotation values. | |
1719 */ | |
1720 private int readAnnotationValues( | |
1721 int v, | |
1722 final char[] buf, | |
1723 final boolean named, | |
1724 final AnnotationVisitor av){ | |
1725 int i = readUnsignedShort(v); | |
1726 v += 2; | |
1727 if(named) | |
1728 { | |
1729 for(; i > 0; --i) | |
1730 { | |
1731 v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av); | |
1732 } | |
1733 } | |
1734 else | |
1735 { | |
1736 for(; i > 0; --i) | |
1737 { | |
1738 v = readAnnotationValue(v, buf, null, av); | |
1739 } | |
1740 } | |
1741 if(av != null) | |
1742 { | |
1743 av.visitEnd(); | |
1744 } | |
1745 return v; | |
1746 } | |
1747 | |
1748 /** | |
1749 * Reads a value of an annotation and makes the given visitor visit it. | |
1750 * | |
1751 * @param v the start offset in {@link #b b} of the value to be read (<i>not | |
1752 * including the value name constant pool index</i>). | |
1753 * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, | |
1754 * {@link #readClass(int,char[]) readClass} or | |
1755 * {@link #readConst readConst}. | |
1756 * @param name the name of the value to be read. | |
1757 * @param av the visitor that must visit the value. | |
1758 * @return the end offset of the annotation value. | |
1759 */ | |
1760 private int readAnnotationValue( | |
1761 int v, | |
1762 final char[] buf, | |
1763 final String name, | |
1764 final AnnotationVisitor av){ | |
1765 int i; | |
1766 if(av == null) | |
1767 { | |
1768 switch(b[v] & 0xFF) | |
1769 { | |
1770 case'e': // enum_const_value | |
1771 return v + 5; | |
1772 case'@': // annotation_value | |
1773 return readAnnotationValues(v + 3, buf, true, null); | |
1774 case'[': // array_value | |
1775 return readAnnotationValues(v + 1, buf, false, null); | |
1776 default: | |
1777 return v + 3; | |
1778 } | |
1779 } | |
1780 switch(b[v++] & 0xFF) | |
1781 { | |
1782 case'I': // pointer to CONSTANT_Integer | |
1783 case'J': // pointer to CONSTANT_Long | |
1784 case'F': // pointer to CONSTANT_Float | |
1785 case'D': // pointer to CONSTANT_Double | |
1786 av.visit(name, readConst(readUnsignedShort(v), buf)); | |
1787 v += 2; | |
1788 break; | |
1789 case'B': // pointer to CONSTANT_Byte | |
1790 av.visit(name, | |
1791 new Byte((byte) readInt(items[readUnsignedShort(v)]))); | |
1792 v += 2; | |
1793 break; | |
1794 case'Z': // pointer to CONSTANT_Boolean | |
1795 av.visit(name, readInt(items[readUnsignedShort(v)]) == 0 | |
1796 ? Boolean.FALSE | |
1797 : Boolean.TRUE); | |
1798 v += 2; | |
1799 break; | |
1800 case'S': // pointer to CONSTANT_Short | |
1801 av.visit(name, | |
1802 new Short((short) readInt(items[readUnsignedShort(v)]))); | |
1803 v += 2; | |
1804 break; | |
1805 case'C': // pointer to CONSTANT_Char | |
1806 av.visit(name, | |
1807 new Character((char) readInt(items[readUnsignedShort(v)]))); | |
1808 v += 2; | |
1809 break; | |
1810 case's': // pointer to CONSTANT_Utf8 | |
1811 av.visit(name, readUTF8(v, buf)); | |
1812 v += 2; | |
1813 break; | |
1814 case'e': // enum_const_value | |
1815 av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf)); | |
1816 v += 4; | |
1817 break; | |
1818 case'c': // class_info | |
1819 av.visit(name, Type.getType(readUTF8(v, buf))); | |
1820 v += 2; | |
1821 break; | |
1822 case'@': // annotation_value | |
1823 v = readAnnotationValues(v + 2, | |
1824 buf, | |
1825 true, | |
1826 av.visitAnnotation(name, readUTF8(v, buf))); | |
1827 break; | |
1828 case'[': // array_value | |
1829 int size = readUnsignedShort(v); | |
1830 v += 2; | |
1831 if(size == 0) | |
1832 { | |
1833 return readAnnotationValues(v - 2, | |
1834 buf, | |
1835 false, | |
1836 av.visitArray(name)); | |
1837 } | |
1838 switch(this.b[v++] & 0xFF) | |
1839 { | |
1840 case'B': | |
1841 byte[] bv = new byte[size]; | |
1842 for(i = 0; i < size; i++) | |
1843 { | |
1844 bv[i] = (byte) readInt(items[readUnsignedShort(v)]); | |
1845 v += 3; | |
1846 } | |
1847 av.visit(name, bv); | |
1848 --v; | |
1849 break; | |
1850 case'Z': | |
1851 boolean[] zv = new boolean[size]; | |
1852 for(i = 0; i < size; i++) | |
1853 { | |
1854 zv[i] = readInt(items[readUnsignedShort(v)]) != 0; | |
1855 v += 3; | |
1856 } | |
1857 av.visit(name, zv); | |
1858 --v; | |
1859 break; | |
1860 case'S': | |
1861 short[] sv = new short[size]; | |
1862 for(i = 0; i < size; i++) | |
1863 { | |
1864 sv[i] = (short) readInt(items[readUnsignedShort(v)]); | |
1865 v += 3; | |
1866 } | |
1867 av.visit(name, sv); | |
1868 --v; | |
1869 break; | |
1870 case'C': | |
1871 char[] cv = new char[size]; | |
1872 for(i = 0; i < size; i++) | |
1873 { | |
1874 cv[i] = (char) readInt(items[readUnsignedShort(v)]); | |
1875 v += 3; | |
1876 } | |
1877 av.visit(name, cv); | |
1878 --v; | |
1879 break; | |
1880 case'I': | |
1881 int[] iv = new int[size]; | |
1882 for(i = 0; i < size; i++) | |
1883 { | |
1884 iv[i] = readInt(items[readUnsignedShort(v)]); | |
1885 v += 3; | |
1886 } | |
1887 av.visit(name, iv); | |
1888 --v; | |
1889 break; | |
1890 case'J': | |
1891 long[] lv = new long[size]; | |
1892 for(i = 0; i < size; i++) | |
1893 { | |
1894 lv[i] = readLong(items[readUnsignedShort(v)]); | |
1895 v += 3; | |
1896 } | |
1897 av.visit(name, lv); | |
1898 --v; | |
1899 break; | |
1900 case'F': | |
1901 float[] fv = new float[size]; | |
1902 for(i = 0; i < size; i++) | |
1903 { | |
1904 fv[i] = Float.intBitsToFloat(readInt(items[readUnsignedShort(v)])); | |
1905 v += 3; | |
1906 } | |
1907 av.visit(name, fv); | |
1908 --v; | |
1909 break; | |
1910 case'D': | |
1911 double[] dv = new double[size]; | |
1912 for(i = 0; i < size; i++) | |
1913 { | |
1914 dv[i] = Double.longBitsToDouble(readLong(items[readUnsignedShort(v)])); | |
1915 v += 3; | |
1916 } | |
1917 av.visit(name, dv); | |
1918 --v; | |
1919 break; | |
1920 default: | |
1921 v = readAnnotationValues(v - 3, | |
1922 buf, | |
1923 false, | |
1924 av.visitArray(name)); | |
1925 } | |
1926 } | |
1927 return v; | |
1928 } | |
1929 | |
1930 private int readFrameType( | |
1931 final Object[] frame, | |
1932 final int index, | |
1933 int v, | |
1934 final char[] buf, | |
1935 final Label[] labels){ | |
1936 int type = b[v++] & 0xFF; | |
1937 switch(type) | |
1938 { | |
1939 case 0: | |
1940 frame[index] = Opcodes.TOP; | |
1941 break; | |
1942 case 1: | |
1943 frame[index] = Opcodes.INTEGER; | |
1944 break; | |
1945 case 2: | |
1946 frame[index] = Opcodes.FLOAT; | |
1947 break; | |
1948 case 3: | |
1949 frame[index] = Opcodes.DOUBLE; | |
1950 break; | |
1951 case 4: | |
1952 frame[index] = Opcodes.LONG; | |
1953 break; | |
1954 case 5: | |
1955 frame[index] = Opcodes.NULL; | |
1956 break; | |
1957 case 6: | |
1958 frame[index] = Opcodes.UNINITIALIZED_THIS; | |
1959 break; | |
1960 case 7: // Object | |
1961 frame[index] = readClass(v, buf); | |
1962 v += 2; | |
1963 break; | |
1964 default: // Uninitialized | |
1965 int offset = readUnsignedShort(v); | |
1966 if(labels[offset] == null) | |
1967 { | |
1968 labels[offset] = new Label(); | |
1969 } | |
1970 frame[index] = labels[offset]; | |
1971 v += 2; | |
1972 } | |
1973 return v; | |
1974 } | |
1975 | |
1976 /** | |
1977 * Reads an attribute in {@link #b b}. | |
1978 * | |
1979 * @param attrs prototypes of the attributes that must be parsed during the | |
1980 * visit of the class. Any attribute whose type is not equal to the | |
1981 * type of one the prototypes is ignored (i.e. an empty | |
1982 * {@link Attribute} instance is returned). | |
1983 * @param type the type of the attribute. | |
1984 * @param off index of the first byte of the attribute's content in | |
1985 * {@link #b b}. The 6 attribute header bytes, containing the type | |
1986 * and the length of the attribute, are not taken into account here | |
1987 * (they have already been read). | |
1988 * @param len the length of the attribute's content. | |
1989 * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, | |
1990 * {@link #readClass(int,char[]) readClass} or | |
1991 * {@link #readConst readConst}. | |
1992 * @param codeOff index of the first byte of code's attribute content in | |
1993 * {@link #b b}, or -1 if the attribute to be read is not a code | |
1994 * attribute. The 6 attribute header bytes, containing the type and | |
1995 * the length of the attribute, are not taken into account here. | |
1996 * @param labels the labels of the method's code, or <tt>null</tt> if the | |
1997 * attribute to be read is not a code attribute. | |
1998 * @return the attribute that has been read, or <tt>null</tt> to skip this | |
1999 * attribute. | |
2000 */ | |
2001 private Attribute readAttribute( | |
2002 final Attribute[] attrs, | |
2003 final String type, | |
2004 final int off, | |
2005 final int len, | |
2006 final char[] buf, | |
2007 final int codeOff, | |
2008 final Label[] labels){ | |
2009 for(int i = 0; i < attrs.length; ++i) | |
2010 { | |
2011 if(attrs[i].type.equals(type)) | |
2012 { | |
2013 return attrs[i].read(this, off, len, buf, codeOff, labels); | |
2014 } | |
2015 } | |
2016 return new Attribute(type).read(this, off, len, null, -1, null); | |
2017 } | |
2018 | |
2019 // ------------------------------------------------------------------------ | |
2020 // Utility methods: low level parsing | |
2021 // ------------------------------------------------------------------------ | |
2022 | |
2023 /** | |
2024 * Returns the start index of the constant pool item in {@link #b b}, plus | |
2025 * one. <i>This method is intended for {@link Attribute} sub classes, and is | |
2026 * normally not needed by class generators or adapters.</i> | |
2027 * | |
2028 * @param item the index a constant pool item. | |
2029 * @return the start index of the constant pool item in {@link #b b}, plus | |
2030 * one. | |
2031 */ | |
2032 public int getItem(final int item){ | |
2033 return items[item]; | |
2034 } | |
2035 | |
2036 /** | |
2037 * Reads a byte value in {@link #b b}. <i>This method is intended for | |
2038 * {@link Attribute} sub classes, and is normally not needed by class | |
2039 * generators or adapters.</i> | |
2040 * | |
2041 * @param index the start index of the value to be read in {@link #b b}. | |
2042 * @return the read value. | |
2043 */ | |
2044 public int readByte(final int index){ | |
2045 return b[index] & 0xFF; | |
2046 } | |
2047 | |
2048 /** | |
2049 * Reads an unsigned short value in {@link #b b}. <i>This method is | |
2050 * intended for {@link Attribute} sub classes, and is normally not needed by | |
2051 * class generators or adapters.</i> | |
2052 * | |
2053 * @param index the start index of the value to be read in {@link #b b}. | |
2054 * @return the read value. | |
2055 */ | |
2056 public int readUnsignedShort(final int index){ | |
2057 byte[] b = this.b; | |
2058 return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); | |
2059 } | |
2060 | |
2061 /** | |
2062 * Reads a signed short value in {@link #b b}. <i>This method is intended | |
2063 * for {@link Attribute} sub classes, and is normally not needed by class | |
2064 * generators or adapters.</i> | |
2065 * | |
2066 * @param index the start index of the value to be read in {@link #b b}. | |
2067 * @return the read value. | |
2068 */ | |
2069 public short readShort(final int index){ | |
2070 byte[] b = this.b; | |
2071 return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); | |
2072 } | |
2073 | |
2074 /** | |
2075 * Reads a signed int value in {@link #b b}. <i>This method is intended for | |
2076 * {@link Attribute} sub classes, and is normally not needed by class | |
2077 * generators or adapters.</i> | |
2078 * | |
2079 * @param index the start index of the value to be read in {@link #b b}. | |
2080 * @return the read value. | |
2081 */ | |
2082 public int readInt(final int index){ | |
2083 byte[] b = this.b; | |
2084 return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) | |
2085 | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); | |
2086 } | |
2087 | |
2088 /** | |
2089 * Reads a signed long value in {@link #b b}. <i>This method is intended | |
2090 * for {@link Attribute} sub classes, and is normally not needed by class | |
2091 * generators or adapters.</i> | |
2092 * | |
2093 * @param index the start index of the value to be read in {@link #b b}. | |
2094 * @return the read value. | |
2095 */ | |
2096 public long readLong(final int index){ | |
2097 long l1 = readInt(index); | |
2098 long l0 = readInt(index + 4) & 0xFFFFFFFFL; | |
2099 return (l1 << 32) | l0; | |
2100 } | |
2101 | |
2102 /** | |
2103 * Reads an UTF8 string constant pool item in {@link #b b}. <i>This method | |
2104 * is intended for {@link Attribute} sub classes, and is normally not needed | |
2105 * by class generators or adapters.</i> | |
2106 * | |
2107 * @param index the start index of an unsigned short value in {@link #b b}, | |
2108 * whose value is the index of an UTF8 constant pool item. | |
2109 * @param buf buffer to be used to read the item. This buffer must be | |
2110 * sufficiently large. It is not automatically resized. | |
2111 * @return the String corresponding to the specified UTF8 item. | |
2112 */ | |
2113 public String readUTF8(int index, final char[] buf){ | |
2114 int item = readUnsignedShort(index); | |
2115 String s = strings[item]; | |
2116 if(s != null) | |
2117 { | |
2118 return s; | |
2119 } | |
2120 index = items[item]; | |
2121 return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf); | |
2122 } | |
2123 | |
2124 /** | |
2125 * Reads UTF8 string in {@link #b b}. | |
2126 * | |
2127 * @param index start offset of the UTF8 string to be read. | |
2128 * @param utfLen length of the UTF8 string to be read. | |
2129 * @param buf buffer to be used to read the string. This buffer must be | |
2130 * sufficiently large. It is not automatically resized. | |
2131 * @return the String corresponding to the specified UTF8 string. | |
2132 */ | |
2133 private String readUTF(int index, final int utfLen, final char[] buf){ | |
2134 int endIndex = index + utfLen; | |
2135 byte[] b = this.b; | |
2136 int strLen = 0; | |
2137 int c, d, e; | |
2138 while(index < endIndex) | |
2139 { | |
2140 c = b[index++] & 0xFF; | |
2141 switch(c >> 4) | |
2142 { | |
2143 case 0: | |
2144 case 1: | |
2145 case 2: | |
2146 case 3: | |
2147 case 4: | |
2148 case 5: | |
2149 case 6: | |
2150 case 7: | |
2151 // 0xxxxxxx | |
2152 buf[strLen++] = (char) c; | |
2153 break; | |
2154 case 12: | |
2155 case 13: | |
2156 // 110x xxxx 10xx xxxx | |
2157 d = b[index++]; | |
2158 buf[strLen++] = (char) (((c & 0x1F) << 6) | (d & 0x3F)); | |
2159 break; | |
2160 default: | |
2161 // 1110 xxxx 10xx xxxx 10xx xxxx | |
2162 d = b[index++]; | |
2163 e = b[index++]; | |
2164 buf[strLen++] = (char) (((c & 0x0F) << 12) | |
2165 | ((d & 0x3F) << 6) | (e & 0x3F)); | |
2166 break; | |
2167 } | |
2168 } | |
2169 return new String(buf, 0, strLen); | |
2170 } | |
2171 | |
2172 /** | |
2173 * Reads a class constant pool item in {@link #b b}. <i>This method is | |
2174 * intended for {@link Attribute} sub classes, and is normally not needed by | |
2175 * class generators or adapters.</i> | |
2176 * | |
2177 * @param index the start index of an unsigned short value in {@link #b b}, | |
2178 * whose value is the index of a class constant pool item. | |
2179 * @param buf buffer to be used to read the item. This buffer must be | |
2180 * sufficiently large. It is not automatically resized. | |
2181 * @return the String corresponding to the specified class item. | |
2182 */ | |
2183 public String readClass(final int index, final char[] buf){ | |
2184 // computes the start index of the CONSTANT_Class item in b | |
2185 // and reads the CONSTANT_Utf8 item designated by | |
2186 // the first two bytes of this CONSTANT_Class item | |
2187 return readUTF8(items[readUnsignedShort(index)], buf); | |
2188 } | |
2189 | |
2190 /** | |
2191 * Reads a numeric or string constant pool item in {@link #b b}. <i>This | |
2192 * method is intended for {@link Attribute} sub classes, and is normally not | |
2193 * needed by class generators or adapters.</i> | |
2194 * | |
2195 * @param item the index of a constant pool item. | |
2196 * @param buf buffer to be used to read the item. This buffer must be | |
2197 * sufficiently large. It is not automatically resized. | |
2198 * @return the {@link Integer}, {@link Float}, {@link Long}, | |
2199 * {@link Double}, {@link String} or {@link Type} corresponding to | |
2200 * the given constant pool item. | |
2201 */ | |
2202 public Object readConst(final int item, final char[] buf){ | |
2203 int index = items[item]; | |
2204 switch(b[index - 1]) | |
2205 { | |
2206 case ClassWriter.INT: | |
2207 return new Integer(readInt(index)); | |
2208 case ClassWriter.FLOAT: | |
2209 return new Float(Float.intBitsToFloat(readInt(index))); | |
2210 case ClassWriter.LONG: | |
2211 return new Long(readLong(index)); | |
2212 case ClassWriter.DOUBLE: | |
2213 return new Double(Double.longBitsToDouble(readLong(index))); | |
2214 case ClassWriter.CLASS: | |
2215 String s = readUTF8(index, buf); | |
2216 return s.charAt(0) == '[' | |
2217 ? Type.getType(s) | |
2218 : Type.getObjectType(s); | |
2219 // case ClassWriter.STR: | |
2220 default: | |
2221 return readUTF8(index, buf); | |
2222 } | |
2223 } | |
2224 } |