javap/src/main/java/org/apidesign/javap/ClassData.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 07 Feb 2013 12:58:12 +0100
branchemul
changeset 694 0d277415ed02
parent 303 c12342170235
permissions -rw-r--r--
Rebasing the Inflater support on jzlib which, unlike GNU ClassPath, has correct implementation of Huffman code. Making the implementation more easily testable by turning Inflater and ZipInputStream into pure delegates. Current implementation is going to need proper long support.
     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 }