View Javadoc

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