1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 package com.sun.org.apache.bcel.internal.classfile; 6 7 /* ==================================================================== 8 * The Apache Software License, Version 1.1 9 * 10 * Copyright (c) 2001 The Apache Software Foundation. All rights 11 * reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in 22 * the documentation and/or other materials provided with the 23 * distribution. 24 * 25 * 3. The end-user documentation included with the redistribution, 26 * if any, must include the following acknowledgment: 27 * "This product includes software developed by the 28 * Apache Software Foundation (http://www.apache.org/)." 29 * Alternately, this acknowledgment may appear in the software itself, 30 * if and wherever such third-party acknowledgments normally appear. 31 * 32 * 4. The names "Apache" and "Apache Software Foundation" and 33 * "Apache BCEL" must not be used to endorse or promote products 34 * derived from this software without prior written permission. For 35 * written permission, please contact apache@apache.org. 36 * 37 * 5. Products derived from this software may not be called "Apache", 38 * "Apache BCEL", nor may "Apache" appear in their name, without 39 * prior written permission of the Apache Software Foundation. 40 * 41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 42 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 44 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 45 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 48 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 49 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 51 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52 * SUCH DAMAGE. 53 * ==================================================================== 54 * 55 * This software consists of voluntary contributions made by many 56 * individuals on behalf of the Apache Software Foundation. For more 57 * information on the Apache Software Foundation, please see 58 * <http://www.apache.org/>. 59 */ 60 61 import com.sun.org.apache.bcel.internal.Constants; 62 import com.sun.org.apache.bcel.internal.util.SyntheticRepository; 63 import com.sun.org.apache.bcel.internal.util.ClassVector; 64 import com.sun.org.apache.bcel.internal.util.ClassQueue; 65 import com.sun.org.apache.bcel.internal.generic.Type; 66 import com.sun.org.apache.xalan.internal.utils.SecuritySupport; 67 68 import java.io.*; 69 import java.util.StringTokenizer; 70 71 /** 72 * Represents a Java class, i.e., the data structures, constant pool, 73 * fields, methods and commands contained in a Java .class file. 74 * See <a href="ftp://java.sun.com/docs/specs/">JVM 75 * specification</a> for details. 76 77 * The intent of this class is to represent a parsed or otherwise existing 78 * class file. Those interested in programatically generating classes 79 * should see the <a href="../generic/ClassGen.html">ClassGen</a> class. 80 81 * @see com.sun.org.apache.bcel.internal.generic.ClassGen 82 * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> 83 */ 84 public class JavaClass extends AccessFlags implements Cloneable, Node { 85 private String file_name; 86 private String package_name; 87 private String source_file_name = "<Unknown>"; 88 private int class_name_index; 89 private int superclass_name_index; 90 private String class_name; 91 private String superclass_name; 92 private int major, minor; // Compiler version 93 private ConstantPool constant_pool; // Constant pool 94 private int[] interfaces; // implemented interfaces 95 private String[] interface_names; 96 private Field[] fields; // Fields, i.e., variables of class 97 private Method[] methods; // methods defined in the class 98 private Attribute[] attributes; // attributes defined in the class 99 private byte source = HEAP; // Generated in memory 100 101 public static final byte HEAP = 1; 102 public static final byte FILE = 2; 103 public static final byte ZIP = 3; 104 105 static boolean debug = false; // Debugging on/off 106 static char sep = '/'; // directory separator 107 108 /** 109 * In cases where we go ahead and create something, 110 * use the default SyntheticRepository, because we 111 * don't know any better. 112 */ 113 private transient com.sun.org.apache.bcel.internal.util.Repository repository = 114 SyntheticRepository.getInstance(); 115 116 /** 117 * Constructor gets all contents as arguments. 118 * 119 * @param class_name_index Index into constant pool referencing a 120 * ConstantClass that represents this class. 121 * @param superclass_name_index Index into constant pool referencing a 122 * ConstantClass that represents this class's superclass. 123 * @param file_name File name 124 * @param major Major compiler version 125 * @param minor Minor compiler version 126 * @param access_flags Access rights defined by bit flags 127 * @param constant_pool Array of constants 128 * @param interfaces Implemented interfaces 129 * @param fields Class fields 130 * @param methods Class methods 131 * @param attributes Class attributes 132 * @param source Read from file or generated in memory? 133 */ 134 public JavaClass(int class_name_index, 135 int superclass_name_index, 136 String file_name, 137 int major, 138 int minor, 139 int access_flags, 140 ConstantPool constant_pool, 141 int[] interfaces, 142 Field[] fields, 143 Method[] methods, 144 Attribute[] attributes, 145 byte source) 146 { 147 if(interfaces == null) // Allowed for backward compatibility 148 interfaces = new int[0]; 149 if(attributes == null) 150 this.attributes = new Attribute[0]; 151 if(fields == null) 152 fields = new Field[0]; 153 if(methods == null) 154 methods = new Method[0]; 155 156 this.class_name_index = class_name_index; 157 this.superclass_name_index = superclass_name_index; 158 this.file_name = file_name; 159 this.major = major; 160 this.minor = minor; 161 this.access_flags = access_flags; 162 this.constant_pool = constant_pool; 163 this.interfaces = interfaces; 164 this.fields = fields; 165 this.methods = methods; 166 this.attributes = attributes; 167 this.source = source; 168 169 // Get source file name if available 170 for(int i=0; i < attributes.length; i++) { 171 if(attributes[i] instanceof SourceFile) { 172 source_file_name = ((SourceFile)attributes[i]).getSourceFileName(); 173 break; 174 } 175 } 176 177 /* According to the specification the following entries must be of type 178 * `ConstantClass' but we check that anyway via the 179 * `ConstPool.getConstant' method. 180 */ 181 class_name = constant_pool.getConstantString(class_name_index, 182 Constants.CONSTANT_Class); 183 class_name = Utility.compactClassName(class_name, false); 184 185 int index = class_name.lastIndexOf('.'); 186 if(index < 0) 187 package_name = ""; 188 else 189 package_name = class_name.substring(0, index); 190 191 if(superclass_name_index > 0) { // May be zero -> class is java.lang.Object 192 superclass_name = constant_pool.getConstantString(superclass_name_index, 193 Constants.CONSTANT_Class); 194 superclass_name = Utility.compactClassName(superclass_name, false); 195 } 196 else 197 superclass_name = "java.lang.Object"; 198 199 interface_names = new String[interfaces.length]; 200 for(int i=0; i < interfaces.length; i++) { 201 String str = constant_pool.getConstantString(interfaces[i], Constants.CONSTANT_Class); 202 interface_names[i] = Utility.compactClassName(str, false); 203 } 204 } 205 206 /** 207 * Constructor gets all contents as arguments. 208 * 209 * @param class_name_index Class name 210 * @param superclass_name_index Superclass name 211 * @param file_name File name 212 * @param major Major compiler version 213 * @param minor Minor compiler version 214 * @param access_flags Access rights defined by bit flags 215 * @param constant_pool Array of constants 216 * @param interfaces Implemented interfaces 217 * @param fields Class fields 218 * @param methods Class methods 219 * @param attributes Class attributes 220 */ 221 public JavaClass(int class_name_index, 222 int superclass_name_index, 223 String file_name, 224 int major, 225 int minor, 226 int access_flags, 227 ConstantPool constant_pool, 228 int[] interfaces, 229 Field[] fields, 230 Method[] methods, 231 Attribute[] attributes) { 232 this(class_name_index, superclass_name_index, file_name, major, minor, access_flags, 233 constant_pool, interfaces, fields, methods, attributes, HEAP); 234 } 235 236 237 /** 238 * Called by objects that are traversing the nodes of the tree implicitely 239 * defined by the contents of a Java class. I.e., the hierarchy of methods, 240 * fields, attributes, etc. spawns a tree of objects. 241 * 242 * @param v Visitor object 243 */ 244 public void accept(Visitor v) { 245 v.visitJavaClass(this); 246 } 247 248 /* Print debug information depending on `JavaClass.debug' 249 */ 250 static final void Debug(String str) { 251 if(debug) 252 System.out.println(str); 253 } 254 255 /** 256 * Dump class to a file. 257 * 258 * @param file Output file 259 * @throws IOException 260 */ 261 public void dump(File file) throws IOException 262 { 263 String parent = file.getParent(); 264 265 if(parent != null) { 266 File dir = new File(parent); 267 268 if(dir != null) 269 dir.mkdirs(); 270 } 271 272 dump(new DataOutputStream(new FileOutputStream(file))); 273 } 274 275 /** 276 * Dump class to a file named file_name. 277 * 278 * @param file_name Output file name 279 * @exception IOException 280 */ 281 public void dump(String file_name) throws IOException 282 { 283 dump(new File(file_name)); 284 } 285 286 /** 287 * @return class in binary format 288 */ 289 public byte[] getBytes() { 290 ByteArrayOutputStream s = new ByteArrayOutputStream(); 291 DataOutputStream ds = new DataOutputStream(s); 292 293 try { 294 dump(ds); 295 } catch(IOException e) { 296 e.printStackTrace(); 297 } finally { 298 try { ds.close(); } catch(IOException e2) { e2.printStackTrace(); } 299 } 300 301 return s.toByteArray(); 302 } 303 304 /** 305 * Dump Java class to output stream in binary format. 306 * 307 * @param file Output stream 308 * @exception IOException 309 */ 310 public void dump(OutputStream file) throws IOException { 311 dump(new DataOutputStream(file)); 312 } 313 314 /** 315 * Dump Java class to output stream in binary format. 316 * 317 * @param file Output stream 318 * @exception IOException 319 */ 320 public void dump(DataOutputStream file) throws IOException 321 { 322 file.writeInt(0xcafebabe); 323 file.writeShort(minor); 324 file.writeShort(major); 325 326 constant_pool.dump(file); 327 328 file.writeShort(access_flags); 329 file.writeShort(class_name_index); 330 file.writeShort(superclass_name_index); 331 332 file.writeShort(interfaces.length); 333 for(int i=0; i < interfaces.length; i++) 334 file.writeShort(interfaces[i]); 335 336 file.writeShort(fields.length); 337 for(int i=0; i < fields.length; i++) 338 fields[i].dump(file); 339 340 file.writeShort(methods.length); 341 for(int i=0; i < methods.length; i++) 342 methods[i].dump(file); 343 344 if(attributes != null) { 345 file.writeShort(attributes.length); 346 for(int i=0; i < attributes.length; i++) 347 attributes[i].dump(file); 348 } 349 else 350 file.writeShort(0); 351 352 file.close(); 353 } 354 355 /** 356 * @return Attributes of the class. 357 */ 358 public Attribute[] getAttributes() { return attributes; } 359 360 /** 361 * @return Class name. 362 */ 363 public String getClassName() { return class_name; } 364 365 /** 366 * @return Package name. 367 */ 368 public String getPackageName() { return package_name; } 369 370 /** 371 * @return Class name index. 372 */ 373 public int getClassNameIndex() { return class_name_index; } 374 375 /** 376 * @return Constant pool. 377 */ 378 public ConstantPool getConstantPool() { return constant_pool; } 379 380 /** 381 * @return Fields, i.e., variables of the class. Like the JVM spec 382 * mandates for the classfile format, these fields are those specific to 383 * this class, and not those of the superclass or superinterfaces. 384 */ 385 public Field[] getFields() { return fields; } 386 387 /** 388 * @return File name of class, aka SourceFile attribute value 389 */ 390 public String getFileName() { return file_name; } 391 392 /** 393 * @return Names of implemented interfaces. 394 */ 395 public String[] getInterfaceNames() { return interface_names; } 396 397 /** 398 * @return Indices in constant pool of implemented interfaces. 399 */ 400 public int[] getInterfaceIndices() { return interfaces; } 401 402 /** 403 * @return Major number of class file version. 404 */ 405 public int getMajor() { return major; } 406 407 /** 408 * @return Methods of the class. 409 */ 410 public Method[] getMethods() { return methods; } 411 412 /** 413 * @return A com.sun.org.apache.bcel.internal.classfile.Method corresponding to 414 * java.lang.reflect.Method if any 415 */ 416 public Method getMethod(java.lang.reflect.Method m) { 417 for(int i = 0; i < methods.length; i++) { 418 Method method = methods[i]; 419 420 if(m.getName().equals(method.getName()) && 421 (m.getModifiers() == method.getModifiers()) && 422 Type.getSignature(m).equals(method.getSignature())) { 423 return method; 424 } 425 } 426 427 return null; 428 } 429 430 /** 431 * @return Minor number of class file version. 432 */ 433 public int getMinor() { return minor; } 434 435 /** 436 * @return sbsolute path to file where this class was read from 437 */ 438 public String getSourceFileName() { return source_file_name; } 439 440 /** 441 * @return Superclass name. 442 */ 443 public String getSuperclassName() { return superclass_name; } 444 445 /** 446 * @return Class name index. 447 */ 448 public int getSuperclassNameIndex() { return superclass_name_index; } 449 450 static { 451 // Debugging ... on/off 452 String debug = null, sep = null; 453 454 try { 455 debug = SecuritySupport.getSystemProperty("JavaClass.debug"); 456 // Get path separator either / or \ usually 457 sep = SecuritySupport.getSystemProperty("file.separator"); 458 } 459 catch (SecurityException e) { 460 // falls through 461 } 462 463 if(debug != null) 464 JavaClass.debug = Boolean.parseBoolean(debug); 465 466 if(sep != null) 467 try { 468 JavaClass.sep = sep.charAt(0); 469 } catch(StringIndexOutOfBoundsException e) {} // Never reached 470 } 471 472 /** 473 * @param attributes . 474 */ 475 public void setAttributes(Attribute[] attributes) { 476 this.attributes = attributes; 477 } 478 479 /** 480 * @param class_name . 481 */ 482 public void setClassName(String class_name) { 483 this.class_name = class_name; 484 } 485 486 /** 487 * @param class_name_index . 488 */ 489 public void setClassNameIndex(int class_name_index) { 490 this.class_name_index = class_name_index; 491 } 492 493 /** 494 * @param constant_pool . 495 */ 496 public void setConstantPool(ConstantPool constant_pool) { 497 this.constant_pool = constant_pool; 498 } 499 500 /** 501 * @param fields . 502 */ 503 public void setFields(Field[] fields) { 504 this.fields = fields; 505 } 506 507 /** 508 * Set File name of class, aka SourceFile attribute value 509 */ 510 public void setFileName(String file_name) { 511 this.file_name = file_name; 512 } 513 514 /** 515 * @param interface_names . 516 */ 517 public void setInterfaceNames(String[] interface_names) { 518 this.interface_names = interface_names; 519 } 520 521 /** 522 * @param interfaces . 523 */ 524 public void setInterfaces(int[] interfaces) { 525 this.interfaces = interfaces; 526 } 527 528 /** 529 * @param major . 530 */ 531 public void setMajor(int major) { 532 this.major = major; 533 } 534 535 /** 536 * @param methods . 537 */ 538 public void setMethods(Method[] methods) { 539 this.methods = methods; 540 } 541 542 /** 543 * @param minor . 544 */ 545 public void setMinor(int minor) { 546 this.minor = minor; 547 } 548 549 /** 550 * Set absolute path to file this class was read from. 551 */ 552 public void setSourceFileName(String source_file_name) { 553 this.source_file_name = source_file_name; 554 } 555 556 /** 557 * @param superclass_name . 558 */ 559 public void setSuperclassName(String superclass_name) { 560 this.superclass_name = superclass_name; 561 } 562 563 /** 564 * @param superclass_name_index . 565 */ 566 public void setSuperclassNameIndex(int superclass_name_index) { 567 this.superclass_name_index = superclass_name_index; 568 } 569 570 /** 571 * @return String representing class contents. 572 */ 573 public String toString() { 574 String access = Utility.accessToString(access_flags, true); 575 access = access.equals("")? "" : (access + " "); 576 577 StringBuffer buf = new StringBuffer(access + 578 Utility.classOrInterface(access_flags) + 579 " " + 580 class_name + " extends " + 581 Utility.compactClassName(superclass_name, 582 false) + '\n'); 583 int size = interfaces.length; 584 585 if(size > 0) { 586 buf.append("implements\t\t"); 587 588 for(int i=0; i < size; i++) { 589 buf.append(interface_names[i]); 590 if(i < size - 1) 591 buf.append(", "); 592 } 593 594 buf.append('\n'); 595 } 596 597 buf.append("filename\t\t" + file_name + '\n'); 598 buf.append("compiled from\t\t" + source_file_name + '\n'); 599 buf.append("compiler version\t" + major + "." + minor + '\n'); 600 buf.append("access flags\t\t" + access_flags + '\n'); 601 buf.append("constant pool\t\t" + constant_pool.getLength() + " entries\n"); 602 buf.append("ACC_SUPER flag\t\t" + isSuper() + "\n"); 603 604 if(attributes.length > 0) { 605 buf.append("\nAttribute(s):\n"); 606 for(int i=0; i < attributes.length; i++) 607 buf.append(indent(attributes[i])); 608 } 609 610 if(fields.length > 0) { 611 buf.append("\n" + fields.length + " fields:\n"); 612 for(int i=0; i < fields.length; i++) 613 buf.append("\t" + fields[i] + '\n'); 614 } 615 616 if(methods.length > 0) { 617 buf.append("\n" + methods.length + " methods:\n"); 618 for(int i=0; i < methods.length; i++) 619 buf.append("\t" + methods[i] + '\n'); 620 } 621 622 return buf.toString(); 623 } 624 625 private static final String indent(Object obj) { 626 StringTokenizer tok = new StringTokenizer(obj.toString(), "\n"); 627 StringBuffer buf = new StringBuffer(); 628 629 while(tok.hasMoreTokens()) 630 buf.append("\t" + tok.nextToken() + "\n"); 631 632 return buf.toString(); 633 } 634 635 /** 636 * @return deep copy of this class 637 */ 638 public JavaClass copy() { 639 JavaClass c = null; 640 641 try { 642 c = (JavaClass)clone(); 643 } catch(CloneNotSupportedException e) {} 644 645 c.constant_pool = constant_pool.copy(); 646 c.interfaces = (int[])interfaces.clone(); 647 c.interface_names = (String[])interface_names.clone(); 648 649 c.fields = new Field[fields.length]; 650 for(int i=0; i < fields.length; i++) 651 c.fields[i] = fields[i].copy(c.constant_pool); 652 653 c.methods = new Method[methods.length]; 654 for(int i=0; i < methods.length; i++) 655 c.methods[i] = methods[i].copy(c.constant_pool); 656 657 c.attributes = new Attribute[attributes.length]; 658 for(int i=0; i < attributes.length; i++) 659 c.attributes[i] = attributes[i].copy(c.constant_pool); 660 661 return c; 662 } 663 664 public final boolean isSuper() { 665 return (access_flags & Constants.ACC_SUPER) != 0; 666 } 667 668 public final boolean isClass() { 669 return (access_flags & Constants.ACC_INTERFACE) == 0; 670 } 671 672 /** @return returns either HEAP (generated), FILE, or ZIP 673 */ 674 public final byte getSource() { 675 return source; 676 } 677 678 /********************* New repository functionality *********************/ 679 680 /** 681 * Gets the ClassRepository which holds its definition. By default 682 * this is the same as SyntheticRepository.getInstance(); 683 */ 684 public com.sun.org.apache.bcel.internal.util.Repository getRepository() { 685 return repository; 686 } 687 688 /** 689 * Sets the ClassRepository which loaded the JavaClass. 690 * Should be called immediately after parsing is done. 691 */ 692 public void setRepository(com.sun.org.apache.bcel.internal.util.Repository repository) { 693 this.repository = repository; 694 } 695 696 /** Equivalent to runtime "instanceof" operator. 697 * 698 * @return true if this JavaClass is derived from teh super class 699 */ 700 public final boolean instanceOf(JavaClass super_class) { 701 if(this.equals(super_class)) 702 return true; 703 704 JavaClass[] super_classes = getSuperClasses(); 705 706 for(int i=0; i < super_classes.length; i++) { 707 if(super_classes[i].equals(super_class)) { 708 return true; 709 } 710 } 711 712 if(super_class.isInterface()) { 713 return implementationOf(super_class); 714 } 715 716 return false; 717 } 718 719 /** 720 * @return true, if clazz is an implementation of interface inter 721 */ 722 public boolean implementationOf(JavaClass inter) { 723 if(!inter.isInterface()) { 724 throw new IllegalArgumentException(inter.getClassName() + " is no interface"); 725 } 726 727 if(this.equals(inter)) { 728 return true; 729 } 730 731 JavaClass[] super_interfaces = getAllInterfaces(); 732 733 for(int i=0; i < super_interfaces.length; i++) { 734 if(super_interfaces[i].equals(inter)) { 735 return true; 736 } 737 } 738 739 return false; 740 } 741 742 /** 743 * @return the superclass for this JavaClass object, or null if this 744 * is java.lang.Object 745 */ 746 public JavaClass getSuperClass() { 747 if("java.lang.Object".equals(getClassName())) { 748 return null; 749 } 750 751 try { 752 return repository.loadClass(getSuperclassName()); 753 } catch(ClassNotFoundException e) { 754 System.err.println(e); 755 return null; 756 } 757 } 758 759 /** 760 * @return list of super classes of this class in ascending order, i.e., 761 * java.lang.Object is always the last element 762 */ 763 public JavaClass[] getSuperClasses() { 764 JavaClass clazz = this; 765 ClassVector vec = new ClassVector(); 766 767 for(clazz = clazz.getSuperClass(); clazz != null; 768 clazz = clazz.getSuperClass()) 769 { 770 vec.addElement(clazz); 771 } 772 773 return vec.toArray(); 774 } 775 776 /** 777 * Get interfaces directly implemented by this JavaClass. 778 */ 779 public JavaClass[] getInterfaces() { 780 String[] interfaces = getInterfaceNames(); 781 JavaClass[] classes = new JavaClass[interfaces.length]; 782 783 try { 784 for(int i = 0; i < interfaces.length; i++) { 785 classes[i] = repository.loadClass(interfaces[i]); 786 } 787 } catch(ClassNotFoundException e) { 788 System.err.println(e); 789 return null; 790 } 791 792 return classes; 793 } 794 795 /** 796 * Get all interfaces implemented by this JavaClass (transitively). 797 */ 798 public JavaClass[] getAllInterfaces() { 799 ClassQueue queue = new ClassQueue(); 800 ClassVector vec = new ClassVector(); 801 802 queue.enqueue(this); 803 804 while(!queue.empty()) { 805 JavaClass clazz = queue.dequeue(); 806 807 JavaClass souper = clazz.getSuperClass(); 808 JavaClass[] interfaces = clazz.getInterfaces(); 809 810 if(clazz.isInterface()) { 811 vec.addElement(clazz); 812 } else { 813 if(souper != null) { 814 queue.enqueue(souper); 815 } 816 } 817 818 for(int i = 0; i < interfaces.length; i++) { 819 queue.enqueue(interfaces[i]); 820 } 821 } 822 823 return vec.toArray(); 824 } 825 }