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