/Users/lyon/j4p/src/javassist/bytecode/ClassFile.java

1    /* 
2     * Javassist, a Java-bytecode translator toolkit. 
3     * Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved. 
4     * 
5     * The contents of this file are subject to the Mozilla Public License Version 
6     * 1.1 (the "License"); you may not use this file except in compliance with 
7     * the License.  Alternatively, the contents of this file may be used under 
8     * the terms of the GNU Lesser General Public License Version 2.1 or later. 
9     * 
10    * Software distributed under the License is distributed on an "AS IS" basis, 
11    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
12    * for the specific language governing rights and limitations under the 
13    * License. 
14    */ 
15    
16   package javassist.bytecode; 
17    
18   import java.io.DataInputStream; 
19   import java.io.DataOutputStream; 
20   import java.io.IOException; 
21   import java.util.Map; 
22   import java.util.LinkedList; 
23   import java.util.List; 
24    
25   import javassist.CannotCompileException; 
26    
27   /** 
28    * <code>ClassFile</code> represents a Java <code>.class</code> file, 
29    * which consists of a constant pool, methods, fields, and attributes. 
30    * 
31    * @see javassist.CtClass#getClassFile() 
32    */ 
33   public final class ClassFile { 
34       ConstPool constPool; 
35       int thisClass; 
36       int accessFlags; 
37       int superClass; 
38       int[] interfaces; 
39       LinkedList fields; 
40       LinkedList methods; 
41       LinkedList attributes; 
42    
43       String thisclassname;       // not JVM-internal name 
44    
45       /** 
46        * Constructs a class file from a byte stream. 
47        */ 
48       public ClassFile(DataInputStream in) throws IOException { 
49           read(in); 
50       } 
51    
52       /** 
53        * Constructs a class file including no members. 
54        * 
55        * @param isInterface       true if this is an interface. 
56        *                          false if this is a class. 
57        * @param classname         a fully-qualified class name 
58        * @param superclass        a fully-qualified super class name 
59        */ 
60       public ClassFile(boolean isInterface, 
61                        String classname, String superclass) { 
62           constPool = new ConstPool(classname); 
63           thisClass = constPool.getThisClassInfo(); 
64           if (isInterface) 
65               accessFlags = AccessFlag.SUPER | AccessFlag.INTERFACE 
66                       | AccessFlag.ABSTRACT; 
67           else 
68               accessFlags = AccessFlag.SUPER; 
69    
70           initSuperclass(superclass); 
71           interfaces = null; 
72           fields = new LinkedList(); 
73           methods = new LinkedList(); 
74           thisclassname = classname; 
75    
76           attributes = new LinkedList(); 
77           attributes.add(new SourceFileAttribute(constPool, 
78                   getSourcefileName(thisclassname))); 
79       } 
80    
81       private void initSuperclass(String superclass) { 
82           if (superclass != null) 
83               superClass = constPool.addClassInfo(superclass); 
84           else 
85               superClass = constPool.addClassInfo("java.lang.Object"); 
86       } 
87    
88       private static String getSourcefileName(String qname) { 
89           int index = qname.lastIndexOf('.'); 
90           if (index >= 0) 
91               qname = qname.substring(index + 1); 
92    
93           return qname + ".java"; 
94       } 
95    
96       /** 
97        * Returns a constant pool table. 
98        */ 
99       public ConstPool getConstPool() { 
100          return constPool; 
101      } 
102   
103      /** 
104       * Returns true if this is an interface. 
105       */ 
106      public boolean isInterface() { 
107          return (accessFlags & AccessFlag.INTERFACE) != 0; 
108      } 
109   
110      /** 
111       * Returns true if this is a final class or interface. 
112       */ 
113      public boolean isFinal() { 
114          return (accessFlags & AccessFlag.FINAL) != 0; 
115      } 
116   
117      /** 
118       * Returns true if this is an abstract class or an interface. 
119       */ 
120      public boolean isAbstract() { 
121          return (accessFlags & AccessFlag.ABSTRACT) != 0; 
122      } 
123   
124      /** 
125       * Returns access flags. 
126       * 
127       * @see javassist.bytecode.AccessFlag 
128       */ 
129      public int getAccessFlags() { 
130          return accessFlags; 
131      } 
132   
133      /** 
134       * Changes access flags. 
135       * 
136       * @see javassist.bytecode.AccessFlag 
137       */ 
138      public void setAccessFlags(int acc) { 
139          accessFlags = acc | AccessFlag.SUPER; 
140      } 
141   
142      /** 
143       * Returns the class name. 
144       */ 
145      public String getName() { 
146          return thisclassname; 
147      } 
148   
149      /** 
150       * Sets the class name.  This method substitutes the new name 
151       * for all occurrences of the old class name in the class file. 
152       */ 
153      public void setName(String name) { 
154          renameClass(thisclassname, name); 
155      } 
156   
157      /** 
158       * Returns the super class name. 
159       */ 
160      public String getSuperclass() { 
161          return constPool.getClassInfo(superClass); 
162      } 
163   
164      /** 
165       * Returns the index of the constant pool entry representing 
166       * the super class. 
167       */ 
168      public int getSuperclassId() { 
169          return superClass; 
170      } 
171   
172      /** 
173       * Sets the super class. 
174       * 
175       * <p>This method modifies constructors so that they call 
176       * constructors declared in the new super class. 
177       */ 
178      public void setSuperclass(String superclass) 
179              throws CannotCompileException { 
180          if (superclass == null) 
181              superclass = "java.lang.Object"; 
182   
183          try { 
184              superClass = constPool.addClassInfo(superclass); 
185              LinkedList list = methods; 
186              int n = list.size(); 
187              for (int i = 0; i < n; ++i) { 
188                  MethodInfo minfo = (MethodInfo) list.get(i); 
189                  minfo.setSuperclass(superclass); 
190              } 
191          } catch (BadBytecode e) { 
192              throw new CannotCompileException(e); 
193          } 
194      } 
195   
196      /** 
197       * Replaces all occurrences of a class name in the class file. 
198       * 
199       * <p>If class X is substituted for class Y in the class file, 
200       * X and Y must have the same signature.  If Y provides a method 
201       * m(), X must provide it even if X inherits m() from the super class. 
202       * If this fact is not guaranteed, the bytecode verifier may cause 
203       * an error. 
204       * 
205       * @param oldname           the replaced class name 
206       * @param newname           the substituted class name 
207       */ 
208      public final void renameClass(String oldname, String newname) { 
209          LinkedList list; 
210          int n; 
211   
212          if (oldname.equals(newname)) 
213              return; 
214   
215          if (oldname.equals(thisclassname)) 
216              thisclassname = newname; 
217   
218          oldname = Descriptor.toJvmName(oldname); 
219          newname = Descriptor.toJvmName(newname); 
220          constPool.renameClass(oldname, newname); 
221   
222          list = methods; 
223          n = list.size(); 
224          for (int i = 0; i < n; ++i) { 
225              MethodInfo minfo = (MethodInfo) list.get(i); 
226              String desc = minfo.getDescriptor(); 
227              minfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); 
228          } 
229   
230          list = fields; 
231          n = list.size(); 
232          for (int i = 0; i < n; ++i) { 
233              FieldInfo finfo = (FieldInfo) list.get(i); 
234              String desc = finfo.getDescriptor(); 
235              finfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); 
236          } 
237      } 
238   
239      /** 
240       * Replaces all occurrences of several class names in the class file. 
241       * 
242       * @param classnames        specifies which class name is replaced 
243       *                          with which new name.  Class names must 
244       *                          be described with the JVM-internal 
245       *                          representation like 
246       *                          <code>java/lang/Object</code>. 
247       * 
248       * @see #renameClass(String,String) 
249       */ 
250      public final void renameClass(Map classnames) { 
251          String jvmNewThisName 
252                  = (String) classnames.get(Descriptor.toJvmName(thisclassname)); 
253          if (jvmNewThisName != null) 
254              thisclassname = Descriptor.toJavaName(jvmNewThisName); 
255   
256          constPool.renameClass(classnames); 
257   
258          LinkedList list = methods; 
259          int n = list.size(); 
260          for (int i = 0; i < n; ++i) { 
261              MethodInfo minfo = (MethodInfo) list.get(i); 
262              String desc = minfo.getDescriptor(); 
263              minfo.setDescriptor(Descriptor.rename(desc, classnames)); 
264          } 
265   
266          list = fields; 
267          n = list.size(); 
268          for (int i = 0; i < n; ++i) { 
269              FieldInfo finfo = (FieldInfo) list.get(i); 
270              String desc = finfo.getDescriptor(); 
271              finfo.setDescriptor(Descriptor.rename(desc, classnames)); 
272          } 
273      } 
274   
275      /** 
276       * Returns the names of the interfaces implemented by the class. 
277       */ 
278      public String[] getInterfaces() { 
279          if (interfaces == null) 
280              return new String[0]; 
281          else { 
282              int n = interfaces.length; 
283              String[] list = new String[n]; 
284              for (int i = 0; i < n; ++i) 
285                  list[i] = constPool.getClassInfo(interfaces[i]); 
286   
287              return list; 
288          } 
289      } 
290   
291      /** 
292       * Sets the interfaces. 
293       * 
294       * @param nameList          the names of the interfaces. 
295       */ 
296      public void setInterfaces(String[] nameList) { 
297          if (nameList != null) { 
298              int n = nameList.length; 
299              interfaces = new int[n]; 
300              for (int i = 0; i < n; ++i) 
301                  interfaces[i] = constPool.addClassInfo(nameList[i]); 
302          } 
303      } 
304   
305      /** 
306       * Appends an interface to the 
307       * interfaces implemented by the class. 
308       */ 
309      public void addInterface(String name) { 
310          int info = constPool.addClassInfo(name); 
311          if (interfaces == null) { 
312              interfaces = new int[1]; 
313              interfaces[0] = info; 
314          } else { 
315              int n = interfaces.length; 
316              int[] newarray = new int[n + 1]; 
317              System.arraycopy(interfaces, 0, newarray, 0, n); 
318              newarray[n] = info; 
319              interfaces = newarray; 
320          } 
321      } 
322   
323      /** 
324       * Returns all the fields declared in the class. 
325       * 
326       * @return a list of <code>FieldInfo</code>. 
327       * @see FieldInfo 
328       */ 
329      public List getFields() { 
330          return fields; 
331      } 
332   
333      /** 
334       * Appends a field to the class. 
335       */ 
336      public void addField(FieldInfo finfo) { 
337          fields.add(finfo); 
338      } 
339   
340      /** 
341       * Returns all the methods declared in the class. 
342       * 
343       * @return a list of <code>MethodInfo</code>. 
344       * @see MethodInfo 
345       */ 
346      public List getMethods() { 
347          return methods; 
348      } 
349   
350      /** 
351       * Returns the method with the specified name.  If there are multiple 
352       * methods with that name, this method returns one of them. 
353       * 
354       * @return null             if no such a method is found. 
355       */ 
356      public MethodInfo getMethod(String name) { 
357          LinkedList list = methods; 
358          int n = list.size(); 
359          for (int i = 0; i < n; ++i) { 
360              MethodInfo minfo = (MethodInfo) list.get(i); 
361              if (minfo.getName().equals(name)) 
362                  return minfo; 
363          } 
364   
365          return null; 
366      } 
367   
368      /** 
369       * Returns a static initializer (class initializer), or null if 
370       * it does not exist. 
371       */ 
372      public MethodInfo getStaticInitializer() { 
373          return getMethod(MethodInfo.nameClinit); 
374      } 
375   
376      /** 
377       * Appends a method to the class. 
378       */ 
379      public void addMethod(MethodInfo minfo) { 
380          methods.add(minfo); 
381      } 
382   
383      /** 
384       * Returns all the attributes. 
385       * 
386       * @return a list of <code>AttributeInfo</code> objects. 
387       * @see AttributeInfo 
388       */ 
389      public List getAttributes() { 
390          return attributes; 
391      } 
392   
393      /** 
394       * Returns the attribute with the specified name. 
395       * 
396       * @param name      attribute name 
397       */ 
398      public AttributeInfo getAttribute(String name) { 
399          LinkedList list = attributes; 
400          int n = list.size(); 
401          for (int i = 0; i < n; ++i) { 
402              AttributeInfo ai = (AttributeInfo) list.get(i); 
403              if (ai.getName().equals(name)) 
404                  return ai; 
405          } 
406   
407          return null; 
408      } 
409   
410      /** 
411       * Appends an attribute.  If there is already an attribute with 
412       * the same name, the new one substitutes for it. 
413       */ 
414      public void addAttribute(AttributeInfo info) { 
415          AttributeInfo.remove(attributes, info.getName()); 
416          attributes.add(info); 
417      } 
418   
419      /** 
420       * Returns the source file containing this class. 
421       * 
422       * @return null     if this information is not available. 
423       */ 
424      public String getSourceFile() { 
425          SourceFileAttribute sf 
426                  = (SourceFileAttribute) getAttribute(SourceFileAttribute.tag); 
427          if (sf == null) 
428              return null; 
429          else 
430              return sf.getFileName(); 
431      } 
432   
433      private void read(DataInputStream in) throws IOException { 
434          int i, n; 
435          int magic = in.readInt(); 
436          if (magic != 0xCAFEBABE) 
437              throw new IOException("non class file"); 
438   
439          int major = in.readUnsignedShort(); 
440          int minor = in.readUnsignedShort(); 
441          constPool = new ConstPool(in); 
442          accessFlags = in.readUnsignedShort(); 
443          thisClass = in.readUnsignedShort(); 
444          constPool.setThisClassInfo(thisClass); 
445          superClass = in.readUnsignedShort(); 
446          n = in.readUnsignedShort(); 
447          if (n == 0) 
448              interfaces = null; 
449          else { 
450              interfaces = new int[n]; 
451              for (i = 0; i < n; ++i) 
452                  interfaces[i] = in.readUnsignedShort(); 
453          } 
454   
455          ConstPool cp = constPool; 
456          n = in.readUnsignedShort(); 
457          fields = new LinkedList(); 
458          for (i = 0; i < n; ++i) 
459              addField(new FieldInfo(cp, in)); 
460   
461          n = in.readUnsignedShort(); 
462          methods = new LinkedList(); 
463          for (i = 0; i < n; ++i) 
464              addMethod(new MethodInfo(cp, in)); 
465   
466          attributes = new LinkedList(); 
467          n = in.readUnsignedShort(); 
468          for (i = 0; i < n; ++i) 
469              addAttribute(AttributeInfo.read(cp, in)); 
470   
471          thisclassname = constPool.getClassInfo(thisClass); 
472      } 
473   
474      /** 
475       * Writes a class file represened by this object 
476       * into an output stream. 
477       */ 
478      public void write(DataOutputStream out) throws IOException { 
479          int i, n; 
480   
481          out.writeInt(0xCAFEBABE);       // magic 
482          out.writeShort(3);              // major version 
483          out.writeShort(45);             // minor version 
484          constPool.write(out);           // constant pool 
485          out.writeShort(accessFlags); 
486          out.writeShort(thisClass); 
487          out.writeShort(superClass); 
488   
489          if (interfaces == null) 
490              n = 0; 
491          else 
492              n = interfaces.length; 
493   
494          out.writeShort(n); 
495          for (i = 0; i < n; ++i) 
496              out.writeShort(interfaces[i]); 
497   
498          LinkedList list = fields; 
499          n = list.size(); 
500          out.writeShort(n); 
501          for (i = 0; i < n; ++i) { 
502              FieldInfo finfo = (FieldInfo) list.get(i); 
503              finfo.write(out); 
504          } 
505   
506          list = methods; 
507          n = list.size(); 
508          out.writeShort(n); 
509          for (i = 0; i < n; ++i) { 
510              MethodInfo minfo = (MethodInfo) list.get(i); 
511              minfo.write(out); 
512          } 
513   
514          out.writeShort(attributes.size()); 
515          AttributeInfo.writeAll(attributes, out); 
516      } 
517  } 
518