javap/src/main/java/org/apidesign/javap/ClassData.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 05 Feb 2013 10:19:10 +0100
changeset 668 b4354a30d4dd
parent 303 c12342170235
permissions -rw-r--r--
David pointed out that it is milli, micro, nano - e.g. we need to multiply by 10^6
     1 /*
     2  * Copyright (c) 2002, 2004, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    25 
    26 
    27 package org.apidesign.javap;
    28 
    29 import java.io.*;
    30 
    31 /**
    32  * Central data repository of the Java Disassembler.
    33  * Stores all the information in java class file.
    34  *
    35  * @author  Sucheta Dambalkar (Adopted code from jdis)
    36  */
    37 public final class ClassData implements RuntimeConstants {
    38 
    39     private int magic;
    40     private int minor_version;
    41     private int major_version;
    42     private int cpool_count;
    43     private Object cpool[];
    44     private int access;
    45     private int this_class = 0;;
    46     private int super_class;
    47     private int interfaces_count;
    48     private int[] interfaces = new int[0];;
    49     private int fields_count;
    50     private FieldData[] fields;
    51     private int methods_count;
    52     private MethodData[] methods;
    53     private InnerClassData[] innerClasses;
    54     private int attributes_count;
    55     private AttrData[] attrs;
    56     private String classname;
    57     private String superclassname;
    58     private int source_cpx=0;
    59     private byte tags[];
    60     private Hashtable indexHashAscii = new Hashtable();
    61     private String pkgPrefix="";
    62     private int pkgPrefixLen=0;
    63 
    64     /**
    65      * Read classfile to disassemble.
    66      */
    67     public ClassData(InputStream infile) throws IOException {
    68         this.read(new DataInputStream(infile));
    69     }
    70 
    71     /**
    72      * Reads and stores class file information.
    73      */
    74     public void read(DataInputStream in) throws IOException {
    75         // Read the header
    76         magic = in.readInt();
    77         if (magic != JAVA_MAGIC) {
    78             throw new ClassFormatError("wrong magic: " +
    79                                        toHex(magic) + ", expected " +
    80                                        toHex(JAVA_MAGIC));
    81         }
    82         minor_version = in.readShort();
    83         major_version = in.readShort();
    84         if (major_version != JAVA_VERSION) {
    85         }
    86 
    87         // Read the constant pool
    88         readCP(in);
    89         access = in.readUnsignedShort();
    90         this_class = in.readUnsignedShort();
    91         super_class = in.readUnsignedShort();
    92 
    93         //Read interfaces.
    94         interfaces_count = in.readUnsignedShort();
    95         if(interfaces_count > 0){
    96             interfaces = new int[interfaces_count];
    97         }
    98         for (int i = 0; i < interfaces_count; i++) {
    99             interfaces[i]=in.readShort();
   100         }
   101 
   102         // Read the fields
   103         readFields(in);
   104 
   105         // Read the methods
   106         readMethods(in);
   107 
   108         // Read the attributes
   109         attributes_count = in.readUnsignedShort();
   110         attrs=new AttrData[attributes_count];
   111         for (int k = 0; k < attributes_count; k++) {
   112             int name_cpx=in.readUnsignedShort();
   113             if (getTag(name_cpx)==CONSTANT_UTF8
   114                 && getString(name_cpx).equals("SourceFile")
   115                 ){      if (in.readInt()!=2)
   116                     throw new ClassFormatError("invalid attr length");
   117                 source_cpx=in.readUnsignedShort();
   118                 AttrData attr=new AttrData(this);
   119                 attr.read(name_cpx);
   120                 attrs[k]=attr;
   121 
   122             } else if (getTag(name_cpx)==CONSTANT_UTF8
   123                        && getString(name_cpx).equals("InnerClasses")
   124                        ){       int length=in.readInt();
   125                        int num=in.readUnsignedShort();
   126                        if (2+num*8 != length)
   127                            throw new ClassFormatError("invalid attr length");
   128                        innerClasses=new InnerClassData[num];
   129                        for (int j = 0; j < num; j++) {
   130                            InnerClassData innerClass=new InnerClassData(this);
   131                            innerClass.read(in);
   132                            innerClasses[j]=innerClass;
   133                        }
   134                        AttrData attr=new AttrData(this);
   135                        attr.read(name_cpx);
   136                        attrs[k]=attr;
   137             } else {
   138                 AttrData attr=new AttrData(this);
   139                 attr.read(name_cpx, in);
   140                 attrs[k]=attr;
   141             }
   142         }
   143         in.close();
   144     } // end ClassData.read()
   145 
   146     /**
   147      * Reads and stores constant pool info.
   148      */
   149     void readCP(DataInputStream in) throws IOException {
   150         cpool_count = in.readUnsignedShort();
   151         tags = new byte[cpool_count];
   152         cpool = new Object[cpool_count];
   153         for (int i = 1; i < cpool_count; i++) {
   154             byte tag = in.readByte();
   155 
   156             switch(tags[i] = tag) {
   157             case CONSTANT_UTF8:
   158                 String str=in.readUTF();
   159                 indexHashAscii.put(cpool[i] = str, new Integer(i));
   160                 break;
   161             case CONSTANT_INTEGER:
   162                 cpool[i] = new Integer(in.readInt());
   163                 break;
   164             case CONSTANT_FLOAT:
   165                 cpool[i] = new Float(in.readFloat());
   166                 break;
   167             case CONSTANT_LONG:
   168                 cpool[i++] = new Long(in.readLong());
   169                 break;
   170             case CONSTANT_DOUBLE:
   171                 cpool[i++] = new Double(in.readDouble());
   172                 break;
   173             case CONSTANT_CLASS:
   174             case CONSTANT_STRING:
   175                 cpool[i] = new CPX(in.readUnsignedShort());
   176                 break;
   177 
   178             case CONSTANT_FIELD:
   179             case CONSTANT_METHOD:
   180             case CONSTANT_INTERFACEMETHOD:
   181             case CONSTANT_NAMEANDTYPE:
   182                 cpool[i] = new CPX2(in.readUnsignedShort(), in.readUnsignedShort());
   183                 break;
   184 
   185             case 0:
   186             default:
   187                 throw new ClassFormatError("invalid constant type: " + (int)tags[i]);
   188             }
   189         }
   190     }
   191 
   192     /**
   193      * Reads and strores field info.
   194      */
   195     protected void readFields(DataInputStream in) throws IOException {
   196         int fields_count = in.readUnsignedShort();
   197         fields=new FieldData[fields_count];
   198         for (int k = 0; k < fields_count; k++) {
   199             FieldData field=new FieldData(this);
   200             field.read(in);
   201             fields[k]=field;
   202         }
   203     }
   204 
   205     /**
   206      * Reads and strores Method info.
   207      */
   208     protected void readMethods(DataInputStream in) throws IOException {
   209         int methods_count = in.readUnsignedShort();
   210         methods=new MethodData[methods_count];
   211         for (int k = 0; k < methods_count ; k++) {
   212             MethodData method=new MethodData(this);
   213             method.read(in);
   214             methods[k]=method;
   215         }
   216     }
   217 
   218     /**
   219      * get a string
   220      */
   221     public String getString(int n) {
   222         if (n == 0) {
   223             return null; 
   224         } else {
   225             return (String)cpool[n];
   226         }
   227     }
   228 
   229     /**
   230      * get the type of constant given an index
   231      */
   232     public byte getTag(int n) {
   233         try{
   234             return tags[n];
   235         } catch (ArrayIndexOutOfBoundsException e) {
   236             return (byte)100;
   237         }
   238     }
   239 
   240     static final String hexString="0123456789ABCDEF";
   241 
   242     public static char hexTable[]=hexString.toCharArray();
   243 
   244     static String toHex(long val, int width) {
   245         StringBuffer s = new StringBuffer();
   246         for (int i=width-1; i>=0; i--)
   247             s.append(hexTable[((int)(val>>(4*i)))&0xF]);
   248         return "0x"+s.toString();
   249     }
   250 
   251     static String toHex(long val) {
   252         int width;
   253         for (width=16; width>0; width--) {
   254             if ((val>>(width-1)*4)!=0) break;
   255         }
   256         return toHex(val, width);
   257     }
   258 
   259     static String toHex(int val) {
   260         int width;
   261         for (width=8; width>0; width--) {
   262             if ((val>>(width-1)*4)!=0) break;
   263         }
   264         return toHex(val, width);
   265     }
   266 
   267     /**
   268      * Returns the name of this class.
   269      */
   270     public String getClassName() {
   271         String res=null;
   272         if (this_class==0) {
   273             return res;
   274         }
   275         int tcpx;
   276         try {
   277             if (tags[this_class]!=CONSTANT_CLASS) {
   278                 return res; //"<CP["+cpx+"] is not a Class> ";
   279             }
   280             tcpx=((CPX)cpool[this_class]).cpx;
   281         } catch (ArrayIndexOutOfBoundsException e) {
   282             return res; // "#"+cpx+"// invalid constant pool index";
   283         } catch (Throwable e) {
   284             return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
   285         }
   286 
   287         try {
   288             return (String)(cpool[tcpx]);
   289         } catch (ArrayIndexOutOfBoundsException e) {
   290             return  res; // "class #"+scpx+"// invalid constant pool index";
   291         } catch (ClassCastException e) {
   292             return  res; // "class #"+scpx+"// invalid constant pool reference";
   293         } catch (Throwable e) {
   294             return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
   295         }
   296 
   297     }
   298 
   299     /**
   300      * Returns the name of class at perticular index.
   301      */
   302     public String getClassName(int cpx) {
   303         String res="#"+cpx;
   304         if (cpx==0) {
   305             return res;
   306         }
   307         int scpx;
   308         try {
   309             if (tags[cpx]!=CONSTANT_CLASS) {
   310                 return res; //"<CP["+cpx+"] is not a Class> ";
   311             }
   312             scpx=((CPX)cpool[cpx]).cpx;
   313         } catch (ArrayIndexOutOfBoundsException e) {
   314             return res; // "#"+cpx+"// invalid constant pool index";
   315         } catch (Throwable e) {
   316             return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
   317         }
   318         res="#"+scpx;
   319         try {
   320             return (String)(cpool[scpx]);
   321         } catch (ArrayIndexOutOfBoundsException e) {
   322             return  res; // "class #"+scpx+"// invalid constant pool index";
   323         } catch (ClassCastException e) {
   324             return  res; // "class #"+scpx+"// invalid constant pool reference";
   325         } catch (Throwable e) {
   326             return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
   327         }
   328     }
   329     
   330     public int getAccessFlags() {
   331         return access;
   332     }
   333 
   334     /**
   335      * Returns true if it is a class
   336      */
   337     public boolean isClass() {
   338         if((access & ACC_INTERFACE) == 0) return true;
   339         return false;
   340     }
   341 
   342     /**
   343      * Returns true if it is a interface.
   344      */
   345     public boolean isInterface(){
   346         if((access & ACC_INTERFACE) != 0) return true;
   347         return false;
   348     }
   349 
   350     /**
   351      * Returns true if this member is public, false otherwise.
   352      */
   353     public boolean isPublic(){
   354         return (access & ACC_PUBLIC) != 0;
   355     }
   356 
   357     /**
   358      * Returns the access of this class or interface.
   359      */
   360     public String[] getAccess(){
   361         Vector v = new Vector();
   362         if ((access & ACC_PUBLIC)   !=0) v.addElement("public");
   363         if ((access & ACC_FINAL)    !=0) v.addElement("final");
   364         if ((access & ACC_ABSTRACT) !=0) v.addElement("abstract");
   365         String[] accflags = new String[v.size()];
   366         v.copyInto(accflags);
   367         return accflags;
   368     }
   369 
   370     /**
   371      * Returns list of innerclasses.
   372      */
   373     public InnerClassData[] getInnerClasses(){
   374         return innerClasses;
   375     }
   376 
   377     /**
   378      * Returns list of attributes.
   379      */
   380     final AttrData[] getAttributes(){
   381         return attrs;
   382     }
   383     
   384     public byte[] findAnnotationData(boolean classRetention) {
   385         String n = classRetention ?
   386             "RuntimeInvisibleAnnotations" : // NOI18N
   387             "RuntimeVisibleAnnotations"; // NOI18N
   388         return findAttr(n, attrs);
   389     }
   390 
   391     /**
   392      * Returns true if superbit is set.
   393      */
   394     public boolean isSuperSet(){
   395         if ((access & ACC_SUPER)   !=0) return true;
   396         return false;
   397     }
   398 
   399     /**
   400      * Returns super class name.
   401      */
   402     public String getSuperClassName(){
   403         String res=null;
   404         if (super_class==0) {
   405             return res;
   406         }
   407         int scpx;
   408         try {
   409             if (tags[super_class]!=CONSTANT_CLASS) {
   410                 return res; //"<CP["+cpx+"] is not a Class> ";
   411             }
   412             scpx=((CPX)cpool[super_class]).cpx;
   413         } catch (ArrayIndexOutOfBoundsException e) {
   414             return res; // "#"+cpx+"// invalid constant pool index";
   415         } catch (Throwable e) {
   416             return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
   417         }
   418 
   419         try {
   420             return (String)(cpool[scpx]);
   421         } catch (ArrayIndexOutOfBoundsException e) {
   422             return  res; // "class #"+scpx+"// invalid constant pool index";
   423         } catch (ClassCastException e) {
   424             return  res; // "class #"+scpx+"// invalid constant pool reference";
   425         } catch (Throwable e) {
   426             return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
   427         }
   428     }
   429 
   430     /**
   431      * Returns list of super interfaces.
   432      */
   433     public String[] getSuperInterfaces(){
   434         String interfacenames[] = new String[interfaces.length];
   435         int interfacecpx = -1;
   436         for(int i = 0; i < interfaces.length; i++){
   437             interfacecpx=((CPX)cpool[interfaces[i]]).cpx;
   438             interfacenames[i] = (String)(cpool[interfacecpx]);
   439         }
   440         return interfacenames;
   441     }
   442 
   443     /**
   444      * Returns string at prticular constant pool index.
   445      */
   446     public String getStringValue(int cpoolx) {
   447         try {
   448             return ((String)cpool[cpoolx]);
   449         } catch (ArrayIndexOutOfBoundsException e) {
   450             return "//invalid constant pool index:"+cpoolx;
   451         } catch (ClassCastException e) {
   452             return "//invalid constant pool ref:"+cpoolx;
   453         }
   454     }
   455 
   456     /**
   457      * Returns list of field info.
   458      */
   459     public  FieldData[] getFields(){
   460         return fields;
   461     }
   462 
   463     /**
   464      * Returns list of method info.
   465      */
   466     public  MethodData[] getMethods(){
   467         return methods;
   468     }
   469 
   470     /**
   471      * Returns constant pool entry at that index.
   472      */
   473     public CPX2 getCpoolEntry(int cpx){
   474         return ((CPX2)(cpool[cpx]));
   475     }
   476 
   477     public Object getCpoolEntryobj(int cpx){
   478         return (cpool[cpx]);
   479     }
   480 
   481     /**
   482      * Returns index of this class.
   483      */
   484     public int getthis_cpx(){
   485         return this_class;
   486     }
   487 
   488     /**
   489      * Returns string at that index.
   490      */
   491     public String StringValue(int cpx) {
   492         return stringValue(cpx, false);
   493     }
   494     public String stringValue(int cpx, boolean textual) {
   495         return stringValue(cpx, textual, null);
   496     }
   497     public String stringValue(int cpx, String[] classRefs) {
   498         return stringValue(cpx, true, classRefs);
   499     }
   500     private String stringValue(int cpx, boolean textual, String[] refs) {
   501         if (cpx==0) return "#0";
   502         int tag;
   503         Object x;
   504         String suffix="";
   505         try {
   506             tag=tags[cpx];
   507             x=cpool[cpx];
   508         } catch (IndexOutOfBoundsException e) {
   509             return "<Incorrect CP index:"+cpx+">";
   510         }
   511 
   512         if (x==null) return "<NULL>";
   513         switch (tag) {
   514         case CONSTANT_UTF8: {
   515             if (!textual) {
   516                 return (String)x;
   517             }
   518             StringBuilder sb=new StringBuilder();
   519             String s=(String)x;
   520             for (int k=0; k<s.length(); k++) {
   521                 char c=s.charAt(k);
   522                 switch (c) {
   523                 case '\\': sb.append('\\').append('\\'); break;
   524                 case '\t': sb.append('\\').append('t'); break;
   525                 case '\n': sb.append('\\').append('n'); break;
   526                 case '\r': sb.append('\\').append('r'); break;
   527                 case '\"': sb.append('\\').append('\"'); break;
   528                 default: sb.append(c);
   529                 }
   530             }
   531             return sb.toString();
   532         }
   533         case CONSTANT_DOUBLE: {
   534             Double d=(Double)x;
   535             String sd=d.toString();
   536             if (textual) {
   537                 return sd;
   538             }
   539             return sd+"d";
   540         }
   541         case CONSTANT_FLOAT: {
   542             Float f=(Float)x;
   543             String sf=(f).toString();
   544             if (textual) {
   545                 return sf;
   546             }
   547             return sf+"f";
   548         }
   549         case CONSTANT_LONG: {
   550             Long ln = (Long)x;
   551             if (textual) {
   552                 return ln.toString();
   553             }
   554             return ln.toString()+'l';
   555         }
   556         case CONSTANT_INTEGER: {
   557             Integer in = (Integer)x;
   558             return in.toString();
   559         }
   560         case CONSTANT_CLASS:
   561             String jn = getClassName(cpx);
   562             if (textual) {
   563                 if (refs != null) {
   564                     refs[0] = jn;
   565                 }
   566                 return jn;
   567             }
   568             return javaName(jn);
   569         case CONSTANT_STRING:
   570             String sv = stringValue(((CPX)x).cpx, textual);
   571             if (textual) {
   572                 return '"' + sv + '"';
   573             } else {
   574                 return sv;
   575             }
   576         case CONSTANT_FIELD:
   577         case CONSTANT_METHOD:
   578         case CONSTANT_INTERFACEMETHOD:
   579             //return getShortClassName(((CPX2)x).cpx1)+"."+StringValue(((CPX2)x).cpx2);
   580              return javaName(getClassName(((CPX2)x).cpx1))+"."+StringValue(((CPX2)x).cpx2);
   581 
   582         case CONSTANT_NAMEANDTYPE:
   583             return getName(((CPX2)x).cpx1)+":"+StringValue(((CPX2)x).cpx2);
   584         default:
   585             return "UnknownTag"; //TBD
   586         }
   587     }
   588 
   589     /**
   590      * Returns resolved java type name.
   591      */
   592     public String javaName(String name) {
   593         if( name==null) return "null";
   594         int len=name.length();
   595         if (len==0) return "\"\"";
   596         int cc='/';
   597     fullname: { // xxx/yyy/zzz
   598             int cp;
   599             for (int k=0; k<len; k += Character.charCount(cp)) {
   600                 cp=name.codePointAt(k);
   601                 if (cc=='/') {
   602                     if (!isJavaIdentifierStart(cp)) break fullname;
   603                 } else if (cp!='/') {
   604                     if (!isJavaIdentifierPart(cp)) break fullname;
   605                 }
   606                 cc=cp;
   607             }
   608             return name;
   609         }
   610         return "\""+name+"\"";
   611     }
   612 
   613     public String getName(int cpx) {
   614         String res;
   615         try {
   616             return javaName((String)cpool[cpx]); //.replace('/','.');
   617         } catch (ArrayIndexOutOfBoundsException e) {
   618             return "<invalid constant pool index:"+cpx+">";
   619         } catch (ClassCastException e) {
   620             return "<invalid constant pool ref:"+cpx+">";
   621         }
   622     }
   623 
   624     /**
   625      * Returns unqualified class name.
   626      */
   627     public String getShortClassName(int cpx) {
   628         String classname=javaName(getClassName(cpx));
   629         pkgPrefixLen=classname.lastIndexOf("/")+1;
   630         if (pkgPrefixLen!=0) {
   631             pkgPrefix=classname.substring(0,pkgPrefixLen);
   632             if (classname.startsWith(pkgPrefix)) {
   633                 return classname.substring(pkgPrefixLen);
   634             }
   635         }
   636         return classname;
   637     }
   638 
   639     /**
   640      * Returns source file name.
   641      */
   642     public String getSourceName(){
   643         return getName(source_cpx);
   644     }
   645 
   646     /**
   647      * Returns package name.
   648      */
   649     public String getPkgName(){
   650         String classname=getClassName(this_class);
   651         pkgPrefixLen=classname.lastIndexOf("/")+1;
   652         if (pkgPrefixLen!=0) {
   653             pkgPrefix=classname.substring(0,pkgPrefixLen);
   654             return("package  "+pkgPrefix.substring(0,pkgPrefixLen-1)+";\n");
   655         }else return null;
   656     }
   657 
   658     /**
   659      * Returns total constant pool entry count.
   660      */
   661     public int getCpoolCount(){
   662         return cpool_count;
   663     }
   664 
   665     /**
   666      * Returns minor version of class file.
   667      */
   668     public int getMinor_version(){
   669         return minor_version;
   670     }
   671 
   672     /**
   673      * Returns major version of class file.
   674      */
   675     public int getMajor_version(){
   676         return major_version;
   677     }
   678 
   679     private boolean isJavaIdentifierStart(int cp) {
   680         return ('a' <= cp && cp <= 'z') || ('A' <= cp && cp <= 'Z');
   681     }
   682 
   683     private boolean isJavaIdentifierPart(int cp) {
   684         return isJavaIdentifierStart(cp) || ('0' <= cp && cp <= '9');
   685     }
   686 
   687     public String[] getNameAndType(int indx) {
   688         return getNameAndType(indx, 0, new String[2]);
   689     }
   690     
   691     private String[] getNameAndType(int indx, int at, String[] arr) {
   692         CPX2 c2 = getCpoolEntry(indx);
   693         arr[at] = StringValue(c2.cpx1);
   694         arr[at + 1] = StringValue(c2.cpx2);
   695         return arr;
   696     }
   697 
   698     public String[] getFieldInfoName(int indx) {
   699         CPX2 c2 = getCpoolEntry(indx);
   700         String[] arr = new String[3];
   701         arr[0] = getClassName(c2.cpx1);
   702         return getNameAndType(c2.cpx2, 1, arr);
   703     }
   704 
   705     static byte[] findAttr(String n, AttrData[] attrs) {
   706         for (AttrData ad : attrs) {
   707             if (n.equals(ad.getAttrName())) {
   708                 return ad.getData();
   709             }
   710         }
   711         return null;
   712     }
   713 }