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 }