001    /*
002     * Copyright 2002-2008 Sun Microsystems, Inc.  All Rights Reserved.
003     * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004     *
005     * This code is free software; you can redistribute it and/or modify it
006     * under the terms of the GNU General Public License version 2 only, as
007     * published by the Free Software Foundation.  Sun designates this
008     * particular file as subject to the "Classpath" exception as provided
009     * by Sun in the LICENSE file that accompanied this code.
010     *
011     * This code is distributed in the hope that it will be useful, but WITHOUT
012     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014     * version 2 for more details (a copy is included in the LICENSE file that
015     * accompanied this code).
016     *
017     * You should have received a copy of the GNU General Public License version
018     * 2 along with this work; if not, write to the Free Software Foundation,
019     * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020     *
021     * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022     * CA 95054 USA or visit www.sun.com if you need additional information or
023     * have any questions.
024     */
025    
026    
027    package sun.tools.javap;
028    
029    import java.util.*;
030    import java.io.*;
031    
032    /**
033     * Central data repository of the Java Disassembler.
034     * Stores all the information in java class file.
035     *
036     * @author  Sucheta Dambalkar (Adopted code from jdis)
037     */
038    public class ClassData implements RuntimeConstants {
039    
040        private int magic;
041        private int minor_version;
042        private int major_version;
043        private int cpool_count;
044        private Object cpool[];
045        private int access;
046        private int this_class = 0;;
047        private int super_class;
048        private int interfaces_count;
049        private int[] interfaces = new int[0];;
050        private int fields_count;
051        private FieldData[] fields;
052        private int methods_count;
053        private MethodData[] methods;
054        private InnerClassData[] innerClasses;
055        private int attributes_count;
056        private AttrData[] attrs;
057        private String classname;
058        private String superclassname;
059        private int source_cpx=0;
060        private byte tags[];
061        private Hashtable<Object,Integer> indexHashAscii = new Hashtable<Object,Integer>();
062        private String pkgPrefix="";
063        private int pkgPrefixLen=0;
064    
065        /**
066         * Read classfile to disassemble.
067         */
068        public ClassData(InputStream infile){
069            try{
070                this.read(new DataInputStream(infile));
071            }catch (FileNotFoundException ee) {
072                error("cant read file");
073            }catch (Error ee) {
074                ee.printStackTrace();
075                error("fatal error");
076            } catch (Exception ee) {
077                ee.printStackTrace();
078                error("fatal exception");
079            }
080        }
081    
082        /**
083         * Reads and stores class file information.
084         */
085        public void read(DataInputStream in) throws IOException {
086            // Read the header
087            magic = in.readInt();
088            if (magic != JAVA_MAGIC) {
089                throw new ClassFormatError("wrong magic: " +
090                                           toHex(magic) + ", expected " +
091                                           toHex(JAVA_MAGIC));
092            }
093            minor_version = in.readShort();
094            major_version = in.readShort();
095            if (major_version != JAVA_VERSION) {
096            }
097    
098            // Read the constant pool
099            readCP(in);
100            access = in.readUnsignedShort();
101            this_class = in.readUnsignedShort();
102            super_class = in.readUnsignedShort();
103    
104            //Read interfaces.
105            interfaces_count = in.readUnsignedShort();
106            if(interfaces_count > 0){
107                interfaces = new int[interfaces_count];
108            }
109            for (int i = 0; i < interfaces_count; i++) {
110                interfaces[i]=in.readShort();
111            }
112    
113            // Read the fields
114            readFields(in);
115    
116            // Read the methods
117            readMethods(in);
118    
119            // Read the attributes
120            attributes_count = in.readUnsignedShort();
121            attrs=new AttrData[attributes_count];
122            for (int k = 0; k < attributes_count; k++) {
123                int name_cpx=in.readUnsignedShort();
124                if (getTag(name_cpx)==CONSTANT_UTF8
125                    && getString(name_cpx).equals("SourceFile")
126                    ){      if (in.readInt()!=2)
127                        throw new ClassFormatError("invalid attr length");
128                    source_cpx=in.readUnsignedShort();
129                    AttrData attr=new AttrData(this);
130                    attr.read(name_cpx);
131                    attrs[k]=attr;
132    
133                } else if (getTag(name_cpx)==CONSTANT_UTF8
134                           && getString(name_cpx).equals("InnerClasses")
135                           ){       int length=in.readInt();
136                           int num=in.readUnsignedShort();
137                           if (2+num*8 != length)
138                               throw new ClassFormatError("invalid attr length");
139                           innerClasses=new InnerClassData[num];
140                           for (int j = 0; j < num; j++) {
141                               InnerClassData innerClass=new InnerClassData(this);
142                               innerClass.read(in);
143                               innerClasses[j]=innerClass;
144                           }
145                           AttrData attr=new AttrData(this);
146                           attr.read(name_cpx);
147                           attrs[k]=attr;
148                } else {
149                    AttrData attr=new AttrData(this);
150                    attr.read(name_cpx, in);
151                    attrs[k]=attr;
152                }
153            }
154            in.close();
155        } // end ClassData.read()
156    
157        /**
158         * Reads and stores constant pool info.
159         */
160        void readCP(DataInputStream in) throws IOException {
161            cpool_count = in.readUnsignedShort();
162            tags = new byte[cpool_count];
163            cpool = new Object[cpool_count];
164            for (int i = 1; i < cpool_count; i++) {
165                byte tag = in.readByte();
166    
167                switch(tags[i] = tag) {
168                case CONSTANT_UTF8:
169                    String str=in.readUTF();
170                    indexHashAscii.put(cpool[i] = str, i);
171                    break;
172                case CONSTANT_INTEGER:
173                    cpool[i] = Integer.valueOf(in.readInt());
174                    break;
175                case CONSTANT_FLOAT:
176                    cpool[i] = Float.valueOf(in.readFloat());
177                    break;
178                case CONSTANT_LONG:
179                    cpool[i++] = Long.valueOf(in.readLong());
180                    break;
181                case CONSTANT_DOUBLE:
182                    cpool[i++] = Double.valueOf(in.readDouble());
183                    break;
184                case CONSTANT_CLASS:
185                case CONSTANT_STRING:
186                    cpool[i] = new CPX(in.readUnsignedShort());
187                    break;
188    
189                case CONSTANT_FIELD:
190                case CONSTANT_METHOD:
191                case CONSTANT_INTERFACEMETHOD:
192                case CONSTANT_NAMEANDTYPE:
193                    cpool[i] = new CPX2(in.readUnsignedShort(), in.readUnsignedShort());
194                    break;
195    
196                case 0:
197                default:
198                    throw new ClassFormatError("invalid constant type: " + (int)tags[i]);
199                }
200            }
201        }
202    
203        /**
204         * Reads and strores field info.
205         */
206        protected void readFields(DataInputStream in) throws IOException {
207            int fields_count = in.readUnsignedShort();
208            fields=new FieldData[fields_count];
209            for (int k = 0; k < fields_count; k++) {
210                FieldData field=new FieldData(this);
211                field.read(in);
212                fields[k]=field;
213            }
214        }
215    
216        /**
217         * Reads and strores Method info.
218         */
219        protected void readMethods(DataInputStream in) throws IOException {
220            int methods_count = in.readUnsignedShort();
221            methods=new MethodData[methods_count];
222            for (int k = 0; k < methods_count ; k++) {
223                MethodData method=new MethodData(this);
224                method.read(in);
225                methods[k]=method;
226            }
227        }
228    
229        /**
230         * get a string
231         */
232        public String getString(int n) {
233            return (n == 0) ? null : (String)cpool[n];
234        }
235    
236        /**
237         * get the type of constant given an index
238         */
239        public byte getTag(int n) {
240            try{
241                return tags[n];
242            } catch (ArrayIndexOutOfBoundsException e) {
243                return (byte)100;
244            }
245        }
246    
247        static final String hexString="0123456789ABCDEF";
248    
249        public static char hexTable[]=hexString.toCharArray();
250    
251        static String toHex(long val, int width) {
252            StringBuffer s = new StringBuffer();
253            for (int i=width-1; i>=0; i--)
254                s.append(hexTable[((int)(val>>(4*i)))&0xF]);
255            return "0x"+s.toString();
256        }
257    
258        static String toHex(long val) {
259            int width;
260            for (width=16; width>0; width--) {
261                if ((val>>(width-1)*4)!=0) break;
262            }
263            return toHex(val, width);
264        }
265    
266        static String toHex(int val) {
267            int width;
268            for (width=8; width>0; width--) {
269                if ((val>>(width-1)*4)!=0) break;
270            }
271            return toHex(val, width);
272        }
273    
274        public void error(String msg) {
275            System.err.println("ERROR:" +msg);
276        }
277    
278        /**
279         * Returns the name of this class.
280         */
281        public String getClassName() {
282            String res=null;
283            if (this_class==0) {
284                return res;
285            }
286            int tcpx;
287            try {
288                if (tags[this_class]!=CONSTANT_CLASS) {
289                    return res; //"<CP["+cpx+"] is not a Class> ";
290                }
291                tcpx=((CPX)cpool[this_class]).cpx;
292            } catch (ArrayIndexOutOfBoundsException e) {
293                return res; // "#"+cpx+"// invalid constant pool index";
294            } catch (Throwable e) {
295                return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
296            }
297    
298            try {
299                return (String)(cpool[tcpx]);
300            } catch (ArrayIndexOutOfBoundsException e) {
301                return  res; // "class #"+scpx+"// invalid constant pool index";
302            } catch (ClassCastException e) {
303                return  res; // "class #"+scpx+"// invalid constant pool reference";
304            } catch (Throwable e) {
305                return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
306            }
307    
308        }
309    
310        /**
311         * Returns the name of class at perticular index.
312         */
313        public String getClassName(int cpx) {
314            String res="#"+cpx;
315            if (cpx==0) {
316                return res;
317            }
318            int scpx;
319            try {
320                if (tags[cpx]!=CONSTANT_CLASS) {
321                    return res; //"<CP["+cpx+"] is not a Class> ";
322                }
323                scpx=((CPX)cpool[cpx]).cpx;
324            } catch (ArrayIndexOutOfBoundsException e) {
325                return res; // "#"+cpx+"// invalid constant pool index";
326            } catch (Throwable e) {
327                return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
328            }
329            res="#"+scpx;
330            try {
331                return (String)(cpool[scpx]);
332            } catch (ArrayIndexOutOfBoundsException e) {
333                return  res; // "class #"+scpx+"// invalid constant pool index";
334            } catch (ClassCastException e) {
335                return  res; // "class #"+scpx+"// invalid constant pool reference";
336            } catch (Throwable e) {
337                return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
338            }
339        }
340    
341        /**
342         * Returns true if it is a class
343         */
344        public boolean isClass() {
345            if((access & ACC_INTERFACE) == 0) return true;
346            return false;
347        }
348    
349        /**
350         * Returns true if it is a interface.
351         */
352        public boolean isInterface(){
353            if((access & ACC_INTERFACE) != 0) return true;
354            return false;
355        }
356    
357        /**
358         * Returns true if this member is public, false otherwise.
359         */
360        public boolean isPublic(){
361            return (access & ACC_PUBLIC) != 0;
362        }
363    
364        /**
365         * Returns the access of this class or interface.
366         */
367        public String[] getAccess(){
368            Vector<String> v = new Vector<String>();
369            if ((access & ACC_PUBLIC)   !=0) v.addElement("public");
370            if ((access & ACC_FINAL)    !=0) v.addElement("final");
371            if ((access & ACC_ABSTRACT) !=0) v.addElement("abstract");
372            String[] accflags = new String[v.size()];
373            v.copyInto(accflags);
374            return accflags;
375        }
376    
377        /**
378         * Returns list of innerclasses.
379         */
380        public InnerClassData[] getInnerClasses(){
381            return innerClasses;
382        }
383    
384        /**
385         * Returns list of attributes.
386         */
387        public AttrData[] getAttributes(){
388            return 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        public String TagString (int tag) {
489            String res=Tables.tagName(tag);
490            if (res==null)  return "BOGUS_TAG:"+tag;
491            return res;
492        }
493    
494        /**
495         * Returns string at that index.
496         */
497        public String StringValue(int cpx) {
498            if (cpx==0) return "#0";
499            int tag;
500            Object x;
501            String suffix="";
502            try {
503                tag=tags[cpx];
504                x=cpool[cpx];
505            } catch (IndexOutOfBoundsException e) {
506                return "<Incorrect CP index:"+cpx+">";
507            }
508    
509            if (x==null) return "<NULL>";
510            switch (tag) {
511            case CONSTANT_UTF8: {
512                StringBuffer sb=new StringBuffer();
513                String s=(String)x;
514                for (int k=0; k<s.length(); k++) {
515                    char c=s.charAt(k);
516                    switch (c) {
517                    case '\t': sb.append('\\').append('t'); break;
518                    case '\n': sb.append('\\').append('n'); break;
519                    case '\r': sb.append('\\').append('r'); break;
520                    case '\"': sb.append('\\').append('\"'); break;
521                    default: sb.append(c);
522                    }
523                }
524                return sb.toString();
525            }
526            case CONSTANT_DOUBLE: {
527                Double d=(Double)x;
528                String sd=d.toString();
529                return sd+"d";
530            }
531            case CONSTANT_FLOAT: {
532                Float f=(Float)x;
533                String sf=(f).toString();
534                return sf+"f";
535            }
536            case CONSTANT_LONG: {
537                Long ln = (Long)x;
538                return ln.toString()+'l';
539            }
540            case CONSTANT_INTEGER: {
541                Integer in = (Integer)x;
542                return in.toString();
543            }
544            case CONSTANT_CLASS:
545                return javaName(getClassName(cpx));
546            case CONSTANT_STRING:
547                return StringValue(((CPX)x).cpx);
548            case CONSTANT_FIELD:
549            case CONSTANT_METHOD:
550            case CONSTANT_INTERFACEMETHOD:
551                //return getShortClassName(((CPX2)x).cpx1)+"."+StringValue(((CPX2)x).cpx2);
552                 return javaName(getClassName(((CPX2)x).cpx1))+"."+StringValue(((CPX2)x).cpx2);
553    
554            case CONSTANT_NAMEANDTYPE:
555                return getName(((CPX2)x).cpx1)+":"+StringValue(((CPX2)x).cpx2);
556            default:
557                return "UnknownTag"; //TBD
558            }
559        }
560    
561        /**
562         * Returns resolved java type name.
563         */
564        public String javaName(String name) {
565            if( name==null) return "null";
566            int len=name.length();
567            if (len==0) return "\"\"";
568            int cc='/';
569        fullname: { // xxx/yyy/zzz
570                int cp;
571                for (int k=0; k<len; k += Character.charCount(cp)) {
572                    cp=name.codePointAt(k);
573                    if (cc=='/') {
574                        if (!Character.isJavaIdentifierStart(cp)) break fullname;
575                    } else if (cp!='/') {
576                        if (!Character.isJavaIdentifierPart(cp)) break fullname;
577                    }
578                    cc=cp;
579                }
580                return name;
581            }
582            return "\""+name+"\"";
583        }
584    
585        public String getName(int cpx) {
586            String res;
587            try {
588                return javaName((String)cpool[cpx]); //.replace('/','.');
589            } catch (ArrayIndexOutOfBoundsException e) {
590                return "<invalid constant pool index:"+cpx+">";
591            } catch (ClassCastException e) {
592                return "<invalid constant pool ref:"+cpx+">";
593            }
594        }
595    
596        /**
597         * Returns unqualified class name.
598         */
599        public String getShortClassName(int cpx) {
600            String classname=javaName(getClassName(cpx));
601            pkgPrefixLen=classname.lastIndexOf("/")+1;
602            if (pkgPrefixLen!=0) {
603                pkgPrefix=classname.substring(0,pkgPrefixLen);
604                if (classname.startsWith(pkgPrefix)) {
605                    return classname.substring(pkgPrefixLen);
606                }
607            }
608            return classname;
609        }
610    
611        /**
612         * Returns source file name.
613         */
614        public String getSourceName(){
615            return getName(source_cpx);
616        }
617    
618        /**
619         * Returns package name.
620         */
621        public String getPkgName(){
622            String classname=getClassName(this_class);
623            pkgPrefixLen=classname.lastIndexOf("/")+1;
624            if (pkgPrefixLen!=0) {
625                pkgPrefix=classname.substring(0,pkgPrefixLen);
626                return("package  "+pkgPrefix.substring(0,pkgPrefixLen-1)+";\n");
627            }else return null;
628        }
629    
630        /**
631         * Returns total constant pool entry count.
632         */
633        public int getCpoolCount(){
634            return cpool_count;
635        }
636    
637        public String StringTag(int cpx) {
638            byte tag=0;
639            String str=null;
640            try {
641                if (cpx==0) throw new IndexOutOfBoundsException();
642                tag=tags[cpx];
643                return      TagString(tag);
644            } catch (IndexOutOfBoundsException e) {
645                str="Incorrect CP index:"+cpx;
646            }
647            return str;
648        }
649    
650        /**
651         * Returns minor version of class file.
652         */
653        public int getMinor_version(){
654            return minor_version;
655        }
656    
657        /**
658         * Returns major version of class file.
659         */
660        public int getMajor_version(){
661            return major_version;
662        }
663    }