/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