View Javadoc

1   /*
2    * Copyright 2005 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  
18  package org.apache.jdo.impl.enhancer.classfile;
19  
20  import java.util.ArrayList;
21  import java.util.List;
22  import java.util.Vector;
23  import java.util.Hashtable;
24  import java.util.Enumeration;
25  import java.io.*;
26  import java.security.MessageDigest;
27  import java.security.DigestOutputStream;
28  import java.security.NoSuchAlgorithmException;
29  import java.io.DataOutputStream;
30  
31  
32  /***
33   * ClassFile models the structure of a class as represented within
34   * a class file.
35   */
36  final public class ClassFile implements VMConstants, Serializable {
37  
38      /* Class file constants */
39      public static final int magic = 0xcafebabe;
40      
41      /*@craig added more flexible version checking.
42       */
43      public static final short[] [] jdkMajorMinorVersions = new short[][] {
44          new short[] {45,3}, // jdk 1.1
45          new short[] {46,0}, // jdk 1.2
46          new short[] {47,0}, // jdk 1.3
47          new short[] {48,0}  // jdk 1.4
48      };
49      public static final List jdkVersions = 
50          convertMajorMinorVersions(jdkMajorMinorVersions);
51  
52      public static final String supportedVersions = printSupportedVersions();
53      
54      private int majorVersion = 0;
55      private int minorVersion = 0;
56      
57      /* The constant pool for the class file */
58      private ConstantPool constantPool = new ConstantPool();
59  
60      /* access flag bit mask - see VMConstants */
61      private int accessFlags = 0;
62  
63      /* The name of the class */
64      private ConstClass thisClassName;
65  
66      /* The name of the super class */
67      private ConstClass superClassName;
68  
69      /* A list of the interfaces which the class implements
70       * The contents are ConstClass objects
71       */
72      private Vector classInterfaces = new Vector();
73  
74      /* A list of the fields which the class contains
75       * The contents are ClassField objects
76       */
77      private Vector classFields = new Vector();
78  
79      /* A list of the methods which the class defines
80       * The contents are ClassMethod objects
81       */
82      private Vector classMethods = new Vector();
83  
84      /* A list of the attributes associated with the class */
85      private AttributeVector classAttributes = new AttributeVector();
86  
87      /*** Static methods 
88       * Added for major.minor compatibility checking
89       */
90      private static List convertMajorMinorVersions(short[][] majorMinor) {
91          int length = majorMinor.length;
92          List result = new ArrayList(length);
93          for (int i = 0; i < length; i++) {
94              result.add(new Integer(majorMinor[i][0] * 65536 + majorMinor[i][1]));
95          }
96          return result;
97      }
98      
99      private static boolean isSupportedVersion(short major, short minor) {
100         Integer version = new Integer(major*65536 + minor);
101         return jdkVersions.contains(version);
102     }
103     
104     public static final String printSupportedVersions() {
105         StringBuffer buf = new StringBuffer("{"); //NOI18N
106         int length = jdkMajorMinorVersions.length;
107         for (int i = 0; i < length; i++) {
108             int major = jdkMajorMinorVersions[i][0];
109             int minor = jdkMajorMinorVersions[i][1];
110             buf.append("{"); buf.append(major); buf.append(","); 
111             buf.append(minor); buf.append("}"); //NOI18N
112         }
113         buf.append("}"); //NOI18N
114         return buf.toString();
115     }
116 
117     /* public accessors */
118 
119 
120 
121     /***
122      * Return the constant pool for the class file
123      */
124     public ConstantPool pool() {
125         return constantPool;
126     }
127 
128     /***
129      * Return the access flags for the class - see VMConstants
130      */
131     public int access() {
132         return accessFlags;
133     }
134 
135     /***
136      * Is the class final?
137      */
138     final public boolean isFinal() {
139         return (accessFlags & ACCFinal) != 0;
140     }
141 
142     /***
143      * Is the class an interface?
144      */
145     final public boolean isInterface() {
146         return (accessFlags & ACCInterface) != 0;
147     }
148 
149     /***
150      * Is the class public?
151      */
152     final public boolean isPublic() {
153         return (accessFlags & ACCPublic) != 0;
154     }
155 
156     /***
157      * Is the class abstract?
158      */
159     final public boolean isAbstract() {
160         return (accessFlags & ACCAbstract) != 0;
161     }
162 
163 
164     /***
165      * Set the access flags for the class - see VMConstants
166      */
167     public void setAccessFlags (int flags) {
168         accessFlags = flags;
169     }
170 
171     /***
172      * Return the name of the class
173      */
174     public ConstClass className() {
175         return thisClassName;
176     }
177 
178     /***
179      * Return the name of the class as a string
180      */
181     //@olsen: added method
182     public String classNameString() {
183         return (thisClassName == null) ? null : thisClassName.asString();
184     }
185 
186     /***
187      * Return the name of the super class
188      */
189     public ConstClass superName() {
190         return superClassName;
191     }
192 
193     /***
194      * Return the name of the super class as a string
195      */
196     public String superNameString() {
197         return (superClassName == null) ? null : superClassName.asString();
198     }
199 
200     /***
201      * Set the name of the super class
202      */
203     public void setSuperName(ConstClass superCl) {
204         superClassName = superCl;
205     }
206 
207     /***
208      * Return the list of the interfaces which the class implements
209      * The contents are ConstClass objects
210      */
211     public Vector interfaces() {
212         return classInterfaces;
213     }
214 
215     /***
216      * Add an interface to the list of the interfaces which the class implements
217      */
218     public void addInterface (ConstClass iface) {
219         classInterfaces.addElement(iface);
220     }
221 
222     /***
223      * Return the list of the fields which the class contains
224      * The contents are ClassField objects
225      */
226     public Vector fields() {
227         return classFields;
228     }
229 
230     /***
231      * Add a field to the list of the fields which the class contains
232      */
233     public void addField (ClassField field) {
234         classFields.addElement(field);
235     }
236 
237     /***
238      * Add a field to the list of the fields which the class contains,
239      * at the index'th position.
240      */
241     public void addField(ClassField field, int index) {
242         classFields.insertElementAt(field, index);
243     }
244 
245     /***
246      * Return the list of the methods which the class defines
247      * The contents are ClassMethod objects
248      */
249     public Vector methods() {
250         return classMethods;
251     }
252 
253     /***
254      * Look for a method with the specified name and type signature
255      */
256     public ClassMethod findMethod(String methodName, String methodSig) {
257         for (Enumeration e = methods().elements(); e.hasMoreElements();) {
258             ClassMethod method = (ClassMethod) e.nextElement();
259             if (method.name().asString().equals(methodName) &&
260                 method.signature().asString().equals(methodSig))
261                 return method;
262         }
263         return null;
264     }
265 
266     /***
267      * Add a method to the list of the methods which the class defines
268      */
269     public void addMethod(ClassMethod method) {
270         classMethods.addElement(method);
271     }
272 
273     /***
274      * Look for a field with the specified name
275      */
276     public ClassField findField(String fieldName) {
277         for (Enumeration e = fields().elements(); e.hasMoreElements();) {
278             ClassField field = (ClassField) e.nextElement();
279             if (field.name().asString().equals(fieldName))
280                 return field;
281         }
282         return null;
283     }
284 
285     /***
286      * Return the list of the attributes associated with the class
287      */
288     public AttributeVector attributes() {
289         return classAttributes;
290     }
291 
292     /***
293      * Returns the class name in user ('.' delimited) form.
294      */
295     //@olsen: moved from ClassControl to ClassFile
296     public String userClassName()
297     {
298         return userClassFromVMClass(classNameString());
299     }
300   
301     /***
302      * Returns the class name in user ('.' delimited) form.
303      */
304     //@olsen: moved from ClassControl to ClassFile
305     static public String userClassFromVMClass(String vmName)
306     {
307         return vmName.replace('/', '.');
308     }
309   
310     /***
311      * Returns the class name in VM ('/' delimited) form.
312      */
313     //@olsen: moved from ClassControl to ClassFile
314     static public String vmClassFromUserClass(String userName)
315     {
316         return userName.replace('.', '/');
317     }
318   
319     /***
320      * Returns the vm package name for this class.
321      */
322     //@olsen: moved from ClassControl to ClassFile
323     public String pkg()
324     {
325         return</strong> packageOf(classNameString());
326     }
327   
328     /***
329      * Returns the vm package name for the vm class name.
330      */
331     //@olsen: moved from ClassControl to ClassFile
332     staticong> public String packageOf(String vmName)
333     {
334         int last = vmName.lastIndexOf('/');
335         if (last < 0)
336             return "";
337         return vmName.substring(0, last);
338     }
339 
340 
341     /* Constructors */
342 
343     /***
344      * Construct a ClassFile from an input stream
345      */
346     public ClassFile(DataInputStream data) throws ClassFormatError {
347         this(data, true);
348     }
349 
350     public ClassFile(DataInputStream data,
351                      boolean allowJDK12ClassFiles) throws ClassFormatError {
352         try {
353             int thisMagic = data.readInt();
354             if (thisMagic != magic)
355                 throw new ClassFormatError("Bad magic value for input");
356 
357             short thisMinorVersion = data.readShort();
358             short thisMajorVersion = data.readShort();
359             /*@craig changed checking only target 1.1 and 1.2 to more
360              * general check for a list of versions.
361              */
362              if (isSupportedVersion(thisMajorVersion, thisMinorVersion)) {
363                 minorVersion = thisMinorVersion;
364                 majorVersion = thisMajorVersion;
365             } else {
366                 throw new ClassFormatError("Bad version number: {" +
367                                            thisMajorVersion + "," + 
368                                            thisMinorVersion +
369                                            "} expected one of: " +
370                                            supportedVersions);
371             }
372 
373             readConstants(data);
374             accessFlags = data.readUnsignedShort();
375             thisClassName = (ConstClass)
376                 constantPool.constantAt(data.readUnsignedShort());
377             superClassName = (ConstClass)
378                 constantPool.constantAt(data.readUnsignedShort());
379             readInterfaces(data);
380             readFields(data);
381             readMethods(data);
382             classAttributes = AttributeVector.readAttributes(data, constantPool);
383         } catch (IOException e) {
384             throw new ClassFormatError("IOException during reading: " + 
385                                        e.getMessage());
386         }
387         //@olsen: added println() for debugging
388         //System.out.println("ClassFile(): new class = " + 
389         //thisClassName.asString());
390     }
391 
392     /***
393      * Construct a bare bones class, ready for additions
394      */
395     public ClassFile(String cname, String supername) {
396         thisClassName = constantPool.addClass(cname);
397         superClassName = constantPool.addClass(supername);
398         //@olsen: added println() for debugging
399         //System.out.println("ClassFile(): new bare class file = " + 
400         //thisClassName);
401     }
402 
403     /***
404      * Write the Class file to the data output stream
405      */
406     public
407     void write (DataOutputStream buff) throws IOException {
408         buff.writeInt(magic);
409         buff.writeShort(minorVersion);
410         buff.writeShort(majorVersion);
411         constantPool.write(buff);
412         buff.writeShort(accessFlags);
413         buff.writeShort(thisClassName.getIndex());
414         //@lars: superclass may be null (java.lang.Object); 
415         //VMSpec 2nd ed., section 4.1
416         buff.writeShort(superClassName == null ? 0 : superClassName.getIndex());
417         //buff.writeShort(superClassName.getIndex());
418         writeInterfaces(buff);
419         writeFields(buff);
420         writeMethods(buff);
421         classAttributes.write(buff);
422     }
423 
424     /***
425      * Returns a byte array representation of this class.
426      */
427     public byte[] getBytes() throws java.io.IOException {
428         /* Write the class bytes to a file, for debugging. */
429 
430         String writeClassToDirectory =
431             System.getProperty("filter.writeClassToDirectory");
432         if (writeClassToDirectory != null) {
433             String filename = writeClassToDirectory + java.io.File.separator +
434                 thisClassName.asString() + ".class";
435             System.err.println("Writing class to file " + filename);
436             DataOutputStream stream = new DataOutputStream(
437                 new java.io.FileOutputStream(filename));
438             write(stream);
439             stream.close();
440         }
441 
442         /* Get the class bytes and return them. */
443 
444         ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
445         write(new DataOutputStream(byteStream));
446 
447         return byteStream.toByteArray();
448     }
449 
450     //@olsen: added method
451     public void print(PrintStream out) {
452         print(out, 0);
453     }
454     
455     //@olsen: added 'indent' parameter
456     public void print(PrintStream out, int indent) {
457         constantPool.print(out, indent);
458         out.println();
459 
460         ClassPrint.spaces(out, indent);
461         out.println("majorVersion = " + Integer.toString(majorVersion));
462         ClassPrint.spaces(out, indent);
463         out.println("minorVersion = " + Integer.toString(minorVersion));
464         ClassPrint.spaces(out, indent);
465         out.println("accessFlags = " + Integer.toString(accessFlags));
466         ClassPrint.spaces(out, indent);
467         out.println("className = " + thisClassName.asString());
468         ClassPrint.spaces(out, indent);
469         out.println("superClassName = " + superClassName.asString());
470         ClassPrint.spaces(out, indent);
471         out.print("Interfaces =");
472         for (int i=0; i<classInterfaces.size(); i++) {
473             out.print(" "
474                       + ((ConstClass)classInterfaces.elementAt(i)).asString());
475         }
476         out.println();
477 
478         ClassPrint.spaces(out, indent);
479         out.println("fields =");
480         for (int i=0; i<classFields.size(); i++) {
481             ((ClassField) classFields.elementAt(i)).print(out, indent + 3);
482         }
483 
484         ClassPrint.spaces(out, indent);
485         out.println("methods =");
486         for (int i=0; i<classMethods.size(); i++) {
487             ((ClassMethod) classMethods.elementAt(i)).print(out, indent + 3);
488         }
489 
490         ClassPrint.spaces(out, indent);
491         out.println("attributes =");
492         classAttributes.print(out, indent + 3);
493 
494     }
495 
496     //@olsen: made public
497     //@olsen: added 'out' and 'indent' parameters
498     public void summarize(PrintStream out, int indent) {
499         constantPool.summarize(out, indent);
500         int codeSize = 0;
501         for (int i=0; i<classMethods.size(); i++) {
502             codeSize += ((ClassMethod)classMethods.elementAt(i)).codeSize();
503         }
504         ClassPrint.spaces(out, indent);
505         out.println(classMethods.size() + " methods in "
506                     + codeSize + " bytes");
507         ClassPrint.spaces(out, indent);
508         out.println(classFields.size() + " fields");
509     }
510 
511     /* package local methods *//package-summary/html">class="comment"> package local methods *//package-summary.html">/* package local methods *//package-summary.html">class="comment"> package local methods */
512 
513     /*
514      * class file reading helpers
515      */
516     private void readConstants (DataInputStream data) throws IOException {
517         constantPool = new ConstantPool(data);
518     }
519 
520     private void readInterfaces(DataInputStream data) throws IOException {
521         int nInterfaces = data.readUnsignedShort();
522         while (nInterfaces-- > 0) {
523             int interfaceIndex = data.readUnsignedShort();
524             ConstClass ci = null;
525             if (interfaceIndex != 0)
526                 ci = (ConstClass) constantPool.constantAt(interfaceIndex);
527             classInterfaces.addElement(ci);
528         }
529     }
530 
531     private void writeInterfaces(DataOutputStream data) throws IOException {
532         data.writeShort(classInterfaces.size());
533         for (int i=0; i<classInterfaces.size(); i++) {
534             ConstClass ci = (ConstClass) classInterfaces.elementAt(i);
535             int interfaceIndex = 0;
536             if (ci != null)
537                 interfaceIndex = ci.getIndex();
538             data.writeShort(interfaceIndex);
539         }
540     }
541 
542     private void readFields(DataInputStream data) throws IOException {
543         int nFields = data.readUnsignedShort();
544         while (nFields-- > 0) {
545             classFields.addElement (ClassField.read(data, constantPool));
546         }
547     }
548 
549     private void writeFields (DataOutputStream data) throws IOException {
550         data.writeShort(classFields.size());
551         for (int i=0; i<classFields.size(); i++)
552             ((ClassField)classFields.elementAt(i)).write(data);
553     }
554 
555     private void readMethods (DataInputStream data) throws IOException {
556         int nMethods = data.readUnsignedShort();
557         while (nMethods-- > 0) {
558             classMethods.addElement (ClassMethod.read(data, constantPool));
559         }
560     }
561 
562     private void writeMethods (DataOutputStream data) throws IOException {
563         data.writeShort(classMethods.size());
564         for (int i=0; i<classMethods.size(); i++)
565             ((ClassMethod)classMethods.elementAt(i)).write(data);
566     }
567 
568 }
569 
570 abstract class ArraySorter {
571     protected ArraySorter() {}
572 
573     /* return the size of the array being sorted */
574     abstract int size();
575 
576     /* return -1 if o1 < o2, 0 if o1 == o2, 1 if o1 > o2 */
577     abstract int compare(int o1Index, int o2Index);
578 
579     /* Swap the elements at index o1Index and o2Index */
580     abstract void swap(int o1Index, int o2Index);
581 
582     void sortArray() {
583         sortArray(0, size()-1);
584     }
585 
586     private void sortArray(int start, int end) {
587         if (end > start) {
588             swap(start, (start+end)/2);
589             int last = start;
590             for (int i = start+1; i<=end; i++) {
591                 if (compare(i, start) < 0)
592                     swap (++last, i);
593             }
594             swap(start, last);
595             sortArray(start, last-1);
596             sortArray(last+1, end);
597         }
598     }
599 }
600 
601 class InterfaceArraySorter extends ArraySorter {
602     private ConstClass theArray[];
603 
604     InterfaceArraySorter(ConstClass[] interfaces) {
605         theArray = interfaces;
606     }
607 
608     /* return the size of the array being sorted */
609     int size() { return theArray.length; }
610 
611     /* return -1 if o1 < o2, 0 if o1 == o2, 1 if o1 > o2 */
612     int compare(int o1Index, int o2Index) {
613         return theArray[o1Index].asString().compareTo(
614             theArray[o2Index].asString());
615     }
616 
617     /* Swap the elements at index o1Index and o2Index */
618     void swap(int o1Index, int o2Index) {
619         ConstClass tmp = theArray[o1Index];
620         theArray[o1Index] = theArray[o2Index];
621         theArray[o2Index] = tmp;
622     }
623 }
624 
625 class FieldArraySorter extends ArraySorter {
626     private ClassField theArray[];
627 
628     FieldArraySorter(ClassField[] fields) {
629         theArray = fields;
630     }
631 
632     /* return the size of the array being sorted */
633     int size() { return theArray.length; }
634 
635     /* return -1 if o1 < o2, 0 if o1 == o2, 1 if o1 > o2 */
636     int compare(int o1Index, int o2Index) {
637         return theArray[o1Index].name().asString().compareTo(
638             theArray[o2Index].name().asString());
639     }
640 
641     /* Swap the elements at index o1Index and o2Index */
642     void swap(int o1Index, int o2Index) {
643         ClassField tmp = theArray[o1Index];
644         theArray[o1Index] = theArray[o2Index];
645         theArray[o2Index] = tmp;
646     }
647 }
648 
649 class MethodArraySorter extends ArraySorter {
650     private ClassMethod theArray[];
651 
652     MethodArraySorter(ClassMethod[] methods) {
653         theArray = methods;
654     }
655 
656     /* return the size of the array being sorted */
657     int size() { return theArray.length; }
658 
659     /* return -1 if o1 < o2, 0 if o1 == o2, 1 if o1 > o2 */
660     int compare(int o1Index, int o2Index) {
661         int cmp = theArray[o1Index].name().asString().compareTo(
662             theArray[o2Index].name().asString());
663         if (cmp == 0) {
664             cmp = theArray[o1Index].signature().asString().compareTo(
665                 theArray[o2Index].signature().asString());
666         }
667         return cmp;
668     }
669 
670     /* Swap the elements at index o1Index and o2Index */
671     void swap(int o1Index, int o2Index) {
672         ClassMethod tmp = theArray[o1Index];
673         theArray[o1Index] = theArray[o2Index];
674         theArray[o2Index] = tmp;
675     }
676 }