001 /*** 002 * 003 * Portions Copyright (c) 2007 Paul Hammant 004 * Portions copyright (c) 2000-2007 INRIA, France Telecom 005 * All rights reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without 008 * modification, are permitted provided that the following conditions 009 * are met: 010 * 1. Redistributions of source code must retain the above copyright 011 * notice, this list of conditions and the following disclaimer. 012 * 2. Redistributions in binary form must reproduce the above copyright 013 * notice, this list of conditions and the following disclaimer in the 014 * documentation and/or other materials provided with the distribution. 015 * 3. Neither the name of the copyright holders nor the names of its 016 * contributors may be used to endorse or promote products derived from 017 * this software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 021 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 023 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 024 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 025 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 026 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 027 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 028 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 029 * THE POSSIBILITY OF SUCH DAMAGE. 030 */ 031 032 package com.thoughtworks.paranamer; 033 034 import java.io.IOException; 035 import java.io.InputStream; 036 import java.io.File; 037 import java.lang.reflect.Constructor; 038 import java.lang.reflect.Method; 039 import java.lang.reflect.AccessibleObject; 040 import java.lang.reflect.Modifier; 041 import java.util.ArrayList; 042 import java.util.List; 043 import java.util.Map; 044 import java.util.HashMap; 045 046 047 /** 048 * An ASM-based implementation of Paranamer. It relies on debug information compiled 049 * with the "-g" javac option to retrieve parameter names. 050 * <p/> 051 * Portions of this source file are a fork of ASM. 052 * 053 * @author Guilherme Silveira 054 * @author Paul Hammant 055 */ 056 public class BytecodeReadingParanamer implements Paranamer { 057 058 private static final Map primitives = new HashMap() { 059 { 060 put("int","I"); 061 put("boolean","Z"); 062 put("char","C"); 063 put("short","B"); 064 put("float","F"); 065 put("long","J"); 066 put("double","D"); 067 } 068 }; 069 070 public String[] lookupParameterNames(AccessibleObject methodOrConstructor) { 071 return lookupParameterNames(methodOrConstructor, true); 072 } 073 074 public String[] lookupParameterNames(AccessibleObject methodOrCtor, boolean throwExceptionIfMissing) { 075 076 Class[] types = null; 077 Class declaringClass = null; 078 String name = null; 079 if (methodOrCtor instanceof Method) { 080 Method method = (Method) methodOrCtor; 081 types = method.getParameterTypes(); 082 name = method.getName(); 083 declaringClass = method.getDeclaringClass(); 084 } else { 085 Constructor constructor = (Constructor) methodOrCtor; 086 types = constructor.getParameterTypes(); 087 declaringClass = constructor.getDeclaringClass(); 088 name = "<init>"; 089 } 090 091 if (types.length == 0) { 092 return EMPTY_NAMES; 093 } 094 InputStream content = getClassAsStream(declaringClass); 095 if (content == null) { 096 if (throwExceptionIfMissing) { 097 throw new ParameterNamesNotFoundException("Unable to get class bytes"); 098 } else { 099 return Paranamer.EMPTY_NAMES; 100 } 101 } 102 try { 103 ClassReader reader = new ClassReader(content); 104 TypeCollector visitor = new TypeCollector(name, types, throwExceptionIfMissing); 105 reader.accept(visitor); 106 return visitor.getParameterNamesForMethod(); 107 } catch (IOException e) { 108 if (throwExceptionIfMissing) { 109 throw new ParameterNamesNotFoundException("IoException while reading class bytes", e); 110 } else { 111 return Paranamer.EMPTY_NAMES; 112 } 113 } 114 } 115 116 public int areParameterNamesAvailable(Class clazz, String constructorOrMethodName) { 117 InputStream content = getClassAsStream(clazz.getClassLoader(), clazz.getName()); 118 if (content == null) { 119 return NO_PARAMETER_NAMES_FOR_CLASS; 120 } 121 try { 122 ClassReader reader = new ClassReader(content); 123 TypeCollector visitor = null; 124 if (constructorOrMethodName.equals("<init>")) { 125 visitor = new TypeCollector(constructorOrMethodName, (clazz.getConstructors()[0]).getParameterTypes(), true); 126 } else { 127 List methods = getMatchingMethods(clazz.getClassLoader(), clazz.getName(), constructorOrMethodName); 128 if (methods.size() == 0) { 129 return Paranamer.NO_PARAMETER_NAMES_FOR_CLASS_AND_MEMBER; 130 } 131 visitor = new TypeCollector(constructorOrMethodName, ((Method) methods.get(0)).getParameterTypes(), true); 132 } 133 reader.accept(visitor); 134 if (visitor.isClassFound()) { 135 if (!visitor.isMethodFound() || !visitor.isDebugInfoPresent()) { 136 return Paranamer.NO_PARAMETER_NAMES_FOR_CLASS_AND_MEMBER; 137 } 138 } else { 139 return Paranamer.NO_PARAMETER_NAMES_FOR_CLASS; 140 } 141 return Paranamer.PARAMETER_NAMES_FOUND; 142 } catch (IOException e) { 143 return Paranamer.NO_PARAMETER_NAMES_FOR_CLASS; 144 } catch (ClassNotFoundException e) { 145 return NO_PARAMETER_NAMES_FOR_CLASS; 146 } 147 } 148 149 private InputStream getClassAsStream(Class clazz) { 150 ClassLoader classLoader = clazz.getClassLoader(); 151 if (classLoader == null) { 152 classLoader = ClassLoader.getSystemClassLoader(); 153 } 154 return getClassAsStream(classLoader, clazz.getName()); 155 } 156 157 private InputStream getClassAsStream(ClassLoader classLoader, String className) { 158 String name = className.replace('.', '/') + ".class"; 159 // better pre-cache all methods otherwise this content will be loaded 160 // multiple times 161 InputStream asStream = classLoader.getResourceAsStream(name); 162 if (asStream == null) { 163 asStream = BytecodeReadingParanamer.class.getResourceAsStream(name); 164 } 165 return asStream; 166 } 167 168 private List getMatchingMethods(ClassLoader classLoader, String className, String name) throws ClassNotFoundException { 169 List list = new ArrayList(); 170 Method[] methods = classLoader.loadClass(className).getMethods(); 171 for (int i = 0; i < methods.length; i++) { 172 Method method = methods[i]; 173 if (method.getName().equals(name)) { 174 list.add(method); 175 } 176 } 177 return list; 178 } 179 180 /** 181 * The type collector waits for an specific method in order to start a method 182 * collector. 183 * 184 * @author Guilherme Silveira 185 */ 186 private static class TypeCollector { 187 188 private static final String COMMA = ","; 189 190 private final String methodName; 191 192 private final Class[] parameterTypes; 193 private final boolean throwExceptionIfMissing; 194 195 private MethodCollector collector; 196 private boolean methodFound = false; 197 private boolean classFound = false; 198 199 private TypeCollector(String methodName, Class[] parameterTypes, boolean throwExceptionIfMissing) { 200 this.methodName = methodName; 201 this.parameterTypes = parameterTypes; 202 this.throwExceptionIfMissing = throwExceptionIfMissing; 203 this.collector = null; 204 } 205 206 public MethodCollector visitMethod(int access, String name, String desc) { 207 // already found the method, skip any processing 208 classFound = true; 209 if (collector != null) { 210 return null; 211 } 212 // not the same name 213 if (!name.equals(methodName)) { 214 return null; 215 } 216 methodFound = true; 217 Type[] argumentTypes = Type.getArgumentTypes(desc); 218 int longOrDoubleQuantity = 0; 219 for (int i1 = 0; i1 < argumentTypes.length; i1++) { 220 Type t = argumentTypes[i1]; 221 if (t.getClassName().equals("long") 222 || t.getClassName().equals("double")) { 223 longOrDoubleQuantity++; 224 } 225 } 226 int paramCount = argumentTypes.length; 227 // not the same quantity of parameters 228 if (paramCount != this.parameterTypes.length) { 229 return null; 230 } 231 for (int i = 0; i < argumentTypes.length; i++) { 232 if (!correctTypeName(argumentTypes, i).equals( 233 this.parameterTypes[i].getName())) { 234 return null; 235 } 236 } 237 this.collector = new MethodCollector((Modifier.isStatic(access) ? 0 : 1), 238 argumentTypes.length + longOrDoubleQuantity); 239 return collector; 240 } 241 242 private String correctTypeName(Type[] argumentTypes, int i) { 243 String s = argumentTypes[i].getClassName(); 244 // array notation needs cleanup. 245 if (s.endsWith("[]")) { 246 String prefix = s.substring(0, s.length() - 2); 247 if (primitives.containsKey(prefix)) { 248 s = "[" + primitives.get(prefix); 249 } else { 250 s = "[L" + prefix + ";"; 251 } 252 } 253 return s; 254 } 255 256 private boolean isDebugInfoPresent() { 257 return collector.isDebugInfoPresent(); 258 } 259 260 private String[] getParameterNamesForMethod() { 261 if (collector == null) { 262 return Paranamer.EMPTY_NAMES; 263 } 264 if (!collector.isDebugInfoPresent()) { 265 if (throwExceptionIfMissing) { 266 throw new ParameterNamesNotFoundException("Parameter names not found for " + methodName); 267 } else { 268 return Paranamer.EMPTY_NAMES; 269 } 270 } 271 return collector.getResult().split(COMMA); 272 } 273 274 private boolean isMethodFound() { 275 return methodFound; 276 } 277 278 private boolean isClassFound() { 279 return classFound; 280 } 281 } 282 283 /** 284 * Objects of this class collects information from a specific method. 285 * 286 * @author Guilherme Silveira 287 */ 288 private static class MethodCollector { 289 290 private final int paramCount; 291 292 private final int ignoreCount; 293 294 private int currentParameter; 295 296 private final StringBuffer result; 297 298 private boolean debugInfoPresent; 299 300 private MethodCollector(int ignoreCount, int paramCount) { 301 this.ignoreCount = ignoreCount; 302 this.paramCount = paramCount; 303 this.result = new StringBuffer(); 304 this.currentParameter = 0; 305 // if there are 0 parameters, there is no need for debug info 306 this.debugInfoPresent = paramCount == 0 ? true : false; 307 } 308 309 public void visitLocalVariable(String name, int index) { 310 if (index >= ignoreCount && index < ignoreCount + paramCount) { 311 if (!name.equals("arg" + currentParameter)) { 312 debugInfoPresent = true; 313 } 314 result.append(','); 315 result.append(name); 316 currentParameter++; 317 } 318 } 319 320 private String getResult() { 321 return result.length() != 0 ? result.substring(1) : ""; 322 } 323 324 private boolean isDebugInfoPresent() { 325 return debugInfoPresent; 326 } 327 328 } 329 330 /*** 331 * Portions Copyright (c) 2007 Paul Hammant 332 * Portions copyright (c) 2000-2007 INRIA, France Telecom 333 * All rights reserved. 334 * 335 * Redistribution and use in source and binary forms, with or without 336 * modification, are permitted provided that the following conditions 337 * are met: 338 * 1. Redistributions of source code must retain the above copyright 339 * notice, this list of conditions and the following disclaimer. 340 * 2. Redistributions in binary form must reproduce the above copyright 341 * notice, this list of conditions and the following disclaimer in the 342 * documentation and/or other materials provided with the distribution. 343 * 3. Neither the name of the copyright holders nor the names of its 344 * contributors may be used to endorse or promote products derived from 345 * this software without specific prior written permission. 346 * 347 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 348 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 349 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 350 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 351 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 352 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 353 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 354 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 355 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 356 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 357 * THE POSSIBILITY OF SUCH DAMAGE. 358 */ 359 360 361 /** 362 * A Java class parser to make a Class Visitor visit an existing class. 363 * This class parses a byte array conforming to the Java class file format and 364 * calls the appropriate visit methods of a given class visitor for each field, 365 * method and bytecode instruction encountered. 366 * 367 * @author Eric Bruneton 368 * @author Eugene Kuleshov 369 */ 370 private static class ClassReader { 371 372 /** 373 * The class to be parsed. <i>The content of this array must not be 374 * modified. This field is intended for Attribute sub classes, and 375 * is normally not needed by class generators or adapters.</i> 376 */ 377 public final byte[] b; 378 379 /** 380 * The start index of each constant pool item in {@link #b b}, plus one. 381 * The one byte offset skips the constant pool item tag that indicates its 382 * type. 383 */ 384 private final int[] items; 385 386 /** 387 * The String objects corresponding to the CONSTANT_Utf8 items. This cache 388 * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item, 389 * which GREATLY improves performances (by a factor 2 to 3). This caching 390 * strategy could be extended to all constant pool items, but its benefit 391 * would not be so great for these items (because they are much less 392 * expensive to parse than CONSTANT_Utf8 items). 393 */ 394 private final String[] strings; 395 396 /** 397 * Maximum length of the strings contained in the constant pool of the 398 * class. 399 */ 400 private final int maxStringLength; 401 402 /** 403 * Start index of the class header information (access, name...) in 404 * {@link #b b}. 405 */ 406 public final int header; 407 408 409 /** 410 * The type of CONSTANT_Fieldref constant pool items. 411 */ 412 final static int FIELD = 9; 413 414 /** 415 * The type of CONSTANT_Methodref constant pool items. 416 */ 417 final static int METH = 10; 418 419 /** 420 * The type of CONSTANT_InterfaceMethodref constant pool items. 421 */ 422 final static int IMETH = 11; 423 424 /** 425 * The type of CONSTANT_Integer constant pool items. 426 */ 427 final static int INT = 3; 428 429 /** 430 * The type of CONSTANT_Float constant pool items. 431 */ 432 final static int FLOAT = 4; 433 434 /** 435 * The type of CONSTANT_Long constant pool items. 436 */ 437 final static int LONG = 5; 438 439 /** 440 * The type of CONSTANT_Double constant pool items. 441 */ 442 final static int DOUBLE = 6; 443 444 /** 445 * The type of CONSTANT_NameAndType constant pool items. 446 */ 447 final static int NAME_TYPE = 12; 448 449 /** 450 * The type of CONSTANT_Utf8 constant pool items. 451 */ 452 final static int UTF8 = 1; 453 454 // ------------------------------------------------------------------------ 455 // Constructors 456 // ------------------------------------------------------------------------ 457 458 /** 459 * Constructs a new {@link ClassReader} object. 460 * 461 * @param b the bytecode of the class to be read. 462 */ 463 private ClassReader(final byte[] b) { 464 this(b, 0); 465 } 466 467 /** 468 * Constructs a new {@link ClassReader} object. 469 * 470 * @param b the bytecode of the class to be read. 471 * @param off the start offset of the class data. 472 */ 473 private ClassReader(final byte[] b, final int off) { 474 this.b = b; 475 // parses the constant pool 476 items = new int[readUnsignedShort(off + 8)]; 477 int n = items.length; 478 strings = new String[n]; 479 int max = 0; 480 int index = off + 10; 481 for (int i = 1; i < n; ++i) { 482 items[i] = index + 1; 483 int size; 484 switch (b[index]) { 485 case FIELD: 486 case METH: 487 case IMETH: 488 case INT: 489 case FLOAT: 490 case NAME_TYPE: 491 size = 5; 492 break; 493 case LONG: 494 case DOUBLE: 495 size = 9; 496 ++i; 497 break; 498 case UTF8: 499 size = 3 + readUnsignedShort(index + 1); 500 if (size > max) { 501 max = size; 502 } 503 break; 504 // case HamConstants.CLASS: 505 // case HamConstants.STR: 506 default: 507 size = 3; 508 break; 509 } 510 index += size; 511 } 512 maxStringLength = max; 513 // the class header information starts just after the constant pool 514 header = index; 515 } 516 517 518 /** 519 * Constructs a new {@link ClassReader} object. 520 * 521 * @param is an input stream from which to read the class. 522 * @throws IOException if a problem occurs during reading. 523 */ 524 private ClassReader(final InputStream is) throws IOException { 525 this(readClass(is)); 526 } 527 528 /** 529 * Reads the bytecode of a class. 530 * 531 * @param is an input stream from which to read the class. 532 * @return the bytecode read from the given input stream. 533 * @throws IOException if a problem occurs during reading. 534 */ 535 private static byte[] readClass(final InputStream is) throws IOException { 536 if (is == null) { 537 throw new IOException("Class not found"); 538 } 539 byte[] b = new byte[is.available()]; 540 int len = 0; 541 while (true) { 542 int n = is.read(b, len, b.length - len); 543 if (n == -1) { 544 if (len < b.length) { 545 byte[] c = new byte[len]; 546 System.arraycopy(b, 0, c, 0, len); 547 b = c; 548 } 549 return b; 550 } 551 len += n; 552 if (len == b.length) { 553 byte[] c = new byte[b.length + 1000]; 554 System.arraycopy(b, 0, c, 0, len); 555 b = c; 556 } 557 } 558 } 559 560 // ------------------------------------------------------------------------ 561 // Public methods 562 // ------------------------------------------------------------------------ 563 564 /** 565 * Makes the given visitor visit the Java class of this {@link ClassReader}. 566 * This class is the one specified in the constructor (see 567 * {@link #ClassReader(byte[]) ClassReader}). 568 * 569 * @param classVisitor the visitor that must visit this class. 570 */ 571 private void accept(final TypeCollector classVisitor) { 572 char[] c = new char[maxStringLength]; // buffer used to read strings 573 int i, j, k; // loop variables 574 int u, v, w; // indexes in b 575 576 String attrName; 577 int anns = 0; 578 int ianns = 0; 579 580 // visits the header 581 u = header; 582 v = items[readUnsignedShort(u + 4)]; 583 int len = readUnsignedShort(u + 6); 584 w = 0; 585 u += 8; 586 for (i = 0; i < len; ++i) { 587 u += 2; 588 } 589 v = u; 590 i = readUnsignedShort(v); 591 v += 2; 592 for (; i > 0; --i) { 593 j = readUnsignedShort(v + 6); 594 v += 8; 595 for (; j > 0; --j) { 596 v += 6 + readInt(v + 2); 597 } 598 } 599 i = readUnsignedShort(v); 600 v += 2; 601 for (; i > 0; --i) { 602 j = readUnsignedShort(v + 6); 603 v += 8; 604 for (; j > 0; --j) { 605 v += 6 + readInt(v + 2); 606 } 607 } 608 609 i = readUnsignedShort(v); 610 v += 2; 611 for (; i > 0; --i) { 612 v += 6 + readInt(v + 2); 613 } 614 615 // visits the class annotations 616 for (i = 1; i >= 0; --i) { 617 v = i == 0 ? ianns : anns; 618 if (v != 0) { 619 v += 2; 620 } 621 } 622 623 // visits the fields 624 i = readUnsignedShort(u); 625 u += 2; 626 for (; i > 0; --i) { 627 j = readUnsignedShort(u + 6); 628 u += 8; 629 for (; j > 0; --j) { 630 u += 6 + readInt(u + 2); 631 } 632 } 633 634 // visits the methods 635 i = readUnsignedShort(u); 636 u += 2; 637 for (; i > 0; --i) { 638 u = readMethod(classVisitor, c, u); 639 } 640 641 } 642 643 private int readMethod(TypeCollector classVisitor, char[] c, int u) { 644 int v; 645 int w; 646 int j; 647 String attrName; 648 int k; 649 int access = readUnsignedShort(u); 650 String name = readUTF8(u + 2, c); 651 String desc = readUTF8(u + 4, c); 652 v = 0; 653 w = 0; 654 655 // looks for Code and Exceptions attributes 656 j = readUnsignedShort(u + 6); 657 u += 8; 658 for (; j > 0; --j) { 659 attrName = readUTF8(u, c); 660 int attrSize = readInt(u + 2); 661 u += 6; 662 // tests are sorted in decreasing frequency order 663 // (based on frequencies observed on typical classes) 664 if (attrName.equals("Code")) { 665 v = u; 666 } 667 u += attrSize; 668 } 669 // reads declared exceptions 670 if (w == 0) { 671 } else { 672 w += 2; 673 for (j = 0; j < readUnsignedShort(w); ++j) { 674 w += 2; 675 } 676 } 677 678 // visits the method's code, if any 679 MethodCollector mv = classVisitor.visitMethod(access, name, desc); 680 681 if (mv != null && v != 0) { 682 int codeLength = readInt(v + 4); 683 v += 8; 684 685 int codeStart = v; 686 int codeEnd = v + codeLength; 687 v = codeEnd; 688 689 j = readUnsignedShort(v); 690 v += 2; 691 for (; j > 0; --j) { 692 v += 8; 693 } 694 // parses the local variable, line number tables, and code 695 // attributes 696 int varTable = 0; 697 int varTypeTable = 0; 698 j = readUnsignedShort(v); 699 v += 2; 700 for (; j > 0; --j) { 701 attrName = readUTF8(v, c); 702 if (attrName.equals("LocalVariableTable")) { 703 varTable = v + 6; 704 } else if (attrName.equals("LocalVariableTypeTable")) { 705 varTypeTable = v + 6; 706 } 707 v += 6 + readInt(v + 2); 708 } 709 710 v = codeStart; 711 // visits the local variable tables 712 if (varTable != 0) { 713 if (varTypeTable != 0) { 714 k = readUnsignedShort(varTypeTable) * 3; 715 w = varTypeTable + 2; 716 int[] typeTable = new int[k]; 717 while (k > 0) { 718 typeTable[--k] = w + 6; // signature 719 typeTable[--k] = readUnsignedShort(w + 8); // index 720 typeTable[--k] = readUnsignedShort(w); // start 721 w += 10; 722 } 723 } 724 k = readUnsignedShort(varTable); 725 w = varTable + 2; 726 for (; k > 0; --k) { 727 int index = readUnsignedShort(w + 8); 728 mv.visitLocalVariable(readUTF8(w + 4, c), index); 729 w += 10; 730 } 731 } 732 } 733 return u; 734 } 735 736 /** 737 * Reads an unsigned short value in {@link #b b}. <i>This method is 738 * intended for Attribute sub classes, and is normally not needed by 739 * class generators or adapters.</i> 740 * 741 * @param index the start index of the value to be read in {@link #b b}. 742 * @return the read value. 743 */ 744 private int readUnsignedShort(final int index) { 745 byte[] b = this.b; 746 return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); 747 } 748 749 /** 750 * Reads a signed int value in {@link #b b}. <i>This method is intended for 751 * Attribute sub classes, and is normally not needed by class 752 * generators or adapters.</i> 753 * 754 * @param index the start index of the value to be read in {@link #b b}. 755 * @return the read value. 756 */ 757 private int readInt(final int index) { 758 byte[] b = this.b; 759 return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) 760 | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); 761 } 762 763 /** 764 * Reads an UTF8 string constant pool item in {@link #b b}. <i>This method 765 * is intended for Attribute sub classes, and is normally not needed 766 * by class generators or adapters.</i> 767 * 768 * @param index the start index of an unsigned short value in {@link #b b}, 769 * whose value is the index of an UTF8 constant pool item. 770 * @param buf buffer to be used to read the item. This buffer must be 771 * sufficiently large. It is not automatically resized. 772 * @return the String corresponding to the specified UTF8 item. 773 */ 774 private String readUTF8(int index, final char[] buf) { 775 int item = readUnsignedShort(index); 776 String s = strings[item]; 777 if (s != null) { 778 return s; 779 } 780 index = items[item]; 781 return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf); 782 } 783 784 /** 785 * Reads UTF8 string in {@link #b b}. 786 * 787 * @param index start offset of the UTF8 string to be read. 788 * @param utfLen length of the UTF8 string to be read. 789 * @param buf buffer to be used to read the string. This buffer must be 790 * sufficiently large. It is not automatically resized. 791 * @return the String corresponding to the specified UTF8 string. 792 */ 793 private String readUTF(int index, final int utfLen, final char[] buf) { 794 int endIndex = index + utfLen; 795 byte[] b = this.b; 796 int strLen = 0; 797 int c, d, e; 798 while (index < endIndex) { 799 c = b[index++] & 0xFF; 800 switch (c >> 4) { 801 case 0: 802 case 1: 803 case 2: 804 case 3: 805 case 4: 806 case 5: 807 case 6: 808 case 7: 809 // 0xxxxxxx 810 buf[strLen++] = (char) c; 811 break; 812 case 12: 813 case 13: 814 // 110x xxxx 10xx xxxx 815 d = b[index++]; 816 buf[strLen++] = (char) (((c & 0x1F) << 6) | (d & 0x3F)); 817 break; 818 default: 819 // 1110 xxxx 10xx xxxx 10xx xxxx 820 d = b[index++]; 821 e = b[index++]; 822 buf[strLen++] = (char) (((c & 0x0F) << 12) 823 | ((d & 0x3F) << 6) | (e & 0x3F)); 824 break; 825 } 826 } 827 return new String(buf, 0, strLen); 828 } 829 830 } 831 832 /*** 833 * Portions Copyright (c) 2007 Paul Hammant 834 * Portions copyright (c) 2000-2007 INRIA, France Telecom 835 * All rights reserved. 836 * 837 * Redistribution and use in source and binary forms, with or without 838 * modification, are permitted provided that the following conditions 839 * are met: 840 * 1. Redistributions of source code must retain the above copyright 841 * notice, this list of conditions and the following disclaimer. 842 * 2. Redistributions in binary form must reproduce the above copyright 843 * notice, this list of conditions and the following disclaimer in the 844 * documentation and/or other materials provided with the distribution. 845 * 3. Neither the name of the copyright holders nor the names of its 846 * contributors may be used to endorse or promote products derived from 847 * this software without specific prior written permission. 848 * 849 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 850 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 851 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 852 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 853 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 854 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 855 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 856 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 857 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 858 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 859 * THE POSSIBILITY OF SUCH DAMAGE. 860 */ 861 862 /** 863 * A Java type. This class can be used to make it easier to manipulate type and 864 * method descriptors. 865 * 866 * @author Eric Bruneton 867 * @author Chris Nokleberg 868 */ 869 private static class Type { 870 871 /** 872 * The sort of the <tt>void</tt> type. 873 */ 874 private final static int VOID = 0; 875 876 /** 877 * The sort of the <tt>boolean</tt> type. 878 */ 879 private final static int BOOLEAN = 1; 880 881 /** 882 * The sort of the <tt>char</tt> type. 883 */ 884 private final static int CHAR = 2; 885 886 /** 887 * The sort of the <tt>byte</tt> type. 888 */ 889 private final static int BYTE = 3; 890 891 /** 892 * The sort of the <tt>short</tt> type. 893 */ 894 private final static int SHORT = 4; 895 896 /** 897 * The sort of the <tt>int</tt> type. 898 */ 899 private final static int INT = 5; 900 901 /** 902 * The sort of the <tt>float</tt> type. 903 */ 904 private final static int FLOAT = 6; 905 906 /** 907 * The sort of the <tt>long</tt> type. 908 */ 909 private final static int LONG = 7; 910 911 /** 912 * The sort of the <tt>double</tt> type. 913 */ 914 private final static int DOUBLE = 8; 915 916 /** 917 * The sort of array reference types. 918 */ 919 private final static int ARRAY = 9; 920 921 /** 922 * The sort of object reference type. 923 */ 924 private final static int OBJECT = 10; 925 926 /** 927 * The <tt>void</tt> type. 928 */ 929 private final static Type VOID_TYPE = new Type(VOID); 930 931 /** 932 * The <tt>boolean</tt> type. 933 */ 934 private final static Type BOOLEAN_TYPE = new Type(BOOLEAN); 935 936 /** 937 * The <tt>char</tt> type. 938 */ 939 private final static Type CHAR_TYPE = new Type(CHAR); 940 941 /** 942 * The <tt>byte</tt> type. 943 */ 944 private final static Type BYTE_TYPE = new Type(BYTE); 945 946 /** 947 * The <tt>short</tt> type. 948 */ 949 private final static Type SHORT_TYPE = new Type(SHORT); 950 951 /** 952 * The <tt>int</tt> type. 953 */ 954 private final static Type INT_TYPE = new Type(INT); 955 956 /** 957 * The <tt>float</tt> type. 958 */ 959 private final static Type FLOAT_TYPE = new Type(FLOAT); 960 961 /** 962 * The <tt>long</tt> type. 963 */ 964 private final static Type LONG_TYPE = new Type(LONG); 965 966 /** 967 * The <tt>double</tt> type. 968 */ 969 private final static Type DOUBLE_TYPE = new Type(DOUBLE); 970 971 // ------------------------------------------------------------------------ 972 // Fields 973 // ------------------------------------------------------------------------ 974 975 /** 976 * The sort of this Java type. 977 */ 978 private final int sort; 979 980 /** 981 * A buffer containing the internal name of this Java type. This field is 982 * only used for reference types. 983 */ 984 private char[] buf; 985 986 /** 987 * The offset of the internal name of this Java type in {@link #buf buf}. 988 * This field is only used for reference types. 989 */ 990 private int off; 991 992 /** 993 * The length of the internal name of this Java type. This field is only 994 * used for reference types. 995 */ 996 private int len; 997 998 // ------------------------------------------------------------------------ 999 // Constructors 1000 // ------------------------------------------------------------------------ 1001 1002 /** 1003 * Constructs a primitive type. 1004 * 1005 * @param sort the sort of the primitive type to be constructed. 1006 */ 1007 private Type(final int sort) { 1008 this.sort = sort; 1009 this.len = 1; 1010 } 1011 1012 /** 1013 * Constructs a reference type. 1014 * 1015 * @param sort the sort of the reference type to be constructed. 1016 * @param buf a buffer containing the descriptor of the previous type. 1017 * @param off the offset of this descriptor in the previous buffer. 1018 * @param len the length of this descriptor. 1019 */ 1020 private Type(final int sort, final char[] buf, final int off, final int len) { 1021 this.sort = sort; 1022 this.buf = buf; 1023 this.off = off; 1024 this.len = len; 1025 } 1026 1027 1028 /** 1029 * Returns the Java types corresponding to the argument types of the given 1030 * method descriptor. 1031 * 1032 * @param methodDescriptor a method descriptor. 1033 * @return the Java types corresponding to the argument types of the given 1034 * method descriptor. 1035 */ 1036 private static Type[] getArgumentTypes(final String methodDescriptor) { 1037 char[] buf = methodDescriptor.toCharArray(); 1038 int off = 1; 1039 int size = 0; 1040 while (true) { 1041 char car = buf[off++]; 1042 if (car == ')') { 1043 break; 1044 } else if (car == 'L') { 1045 while (buf[off++] != ';') { 1046 } 1047 ++size; 1048 } else if (car != '[') { 1049 ++size; 1050 } 1051 } 1052 1053 Type[] args = new Type[size]; 1054 off = 1; 1055 size = 0; 1056 while (buf[off] != ')') { 1057 args[size] = getType(buf, off); 1058 off += args[size].len + (args[size].sort == OBJECT ? 2 : 0); 1059 size += 1; 1060 } 1061 return args; 1062 } 1063 1064 1065 /** 1066 * Returns the Java type corresponding to the given type descriptor. 1067 * 1068 * @param buf a buffer containing a type descriptor. 1069 * @param off the offset of this descriptor in the previous buffer. 1070 * @return the Java type corresponding to the given type descriptor. 1071 */ 1072 private static Type getType(final char[] buf, final int off) { 1073 int len; 1074 switch (buf[off]) { 1075 case 'V': 1076 return VOID_TYPE; 1077 case 'Z': 1078 return BOOLEAN_TYPE; 1079 case 'C': 1080 return CHAR_TYPE; 1081 case 'B': 1082 return BYTE_TYPE; 1083 case 'S': 1084 return SHORT_TYPE; 1085 case 'I': 1086 return INT_TYPE; 1087 case 'F': 1088 return FLOAT_TYPE; 1089 case 'J': 1090 return LONG_TYPE; 1091 case 'D': 1092 return DOUBLE_TYPE; 1093 case '[': 1094 len = 1; 1095 while (buf[off + len] == '[') { 1096 ++len; 1097 } 1098 if (buf[off + len] == 'L') { 1099 ++len; 1100 while (buf[off + len] != ';') { 1101 ++len; 1102 } 1103 } 1104 return new Type(ARRAY, buf, off, len + 1); 1105 // case 'L': 1106 default: 1107 len = 1; 1108 while (buf[off + len] != ';') { 1109 ++len; 1110 } 1111 return new Type(OBJECT, buf, off + 1, len - 1); 1112 } 1113 } 1114 1115 // ------------------------------------------------------------------------ 1116 // Accessors 1117 // ------------------------------------------------------------------------ 1118 1119 /** 1120 * Returns the number of dimensions of this array type. This method should 1121 * only be used for an array type. 1122 * 1123 * @return the number of dimensions of this array type. 1124 */ 1125 private int getDimensions() { 1126 int i = 1; 1127 while (buf[off + i] == '[') { 1128 ++i; 1129 } 1130 return i; 1131 } 1132 1133 /** 1134 * Returns the type of the elements of this array type. This method should 1135 * only be used for an array type. 1136 * 1137 * @return Returns the type of the elements of this array type. 1138 */ 1139 private Type getElementType() { 1140 return getType(buf, off + getDimensions()); 1141 } 1142 1143 /** 1144 * Returns the name of the class corresponding to this type. 1145 * 1146 * @return the fully qualified name of the class corresponding to this type. 1147 */ 1148 private String getClassName() { 1149 switch (sort) { 1150 case VOID: 1151 return "void"; 1152 case BOOLEAN: 1153 return "boolean"; 1154 case CHAR: 1155 return "char"; 1156 case BYTE: 1157 return "byte"; 1158 case SHORT: 1159 return "short"; 1160 case INT: 1161 return "int"; 1162 case FLOAT: 1163 return "float"; 1164 case LONG: 1165 return "long"; 1166 case DOUBLE: 1167 return "double"; 1168 case ARRAY: 1169 StringBuffer b = new StringBuffer(getElementType().getClassName()); 1170 for (int i = getDimensions(); i > 0; --i) { 1171 b.append("[]"); 1172 } 1173 return b.toString(); 1174 // case OBJECT: 1175 default: 1176 return new String(buf, off, len).replace('/', '.'); 1177 } 1178 } 1179 } 1180 1181 1182 }