001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist.bytecode;
017:
018: import java.io.DataInputStream;
019: import java.io.DataOutputStream;
020: import java.io.IOException;
021: import java.util.ArrayList;
022: import java.util.LinkedList;
023: import java.util.List;
024: import java.util.ListIterator;
025: import java.util.Map;
026: import javassist.CannotCompileException;
027:
028: /**
029: * <code>ClassFile</code> represents a Java <code>.class</code> file, which
030: * consists of a constant pool, methods, fields, and attributes.
031: *
032: * @see javassist.CtClass#getClassFile()
033: */
034: public final class ClassFile {
035: int major, minor; // version number
036: ConstPool constPool;
037: int this Class;
038: int accessFlags;
039: int super Class;
040: int[] interfaces;
041: ArrayList fields;
042: ArrayList methods;
043: LinkedList attributes;
044: String this classname; // not JVM-internal name
045: String[] cachedInterfaces;
046: String cachedSuperclass;
047:
048: /**
049: * Constructs a class file from a byte stream.
050: */
051: public ClassFile(DataInputStream in) throws IOException {
052: read(in);
053: }
054:
055: /**
056: * Constructs a class file including no members.
057: *
058: * @param isInterface
059: * true if this is an interface. false if this is a class.
060: * @param classname
061: * a fully-qualified class name
062: * @param superclass
063: * a fully-qualified super class name
064: */
065: public ClassFile(boolean isInterface, String classname,
066: String super class) {
067: major = 45;
068: minor = 3; // JDK 1.1 or later
069: constPool = new ConstPool(classname);
070: this Class = constPool.getThisClassInfo();
071: if (isInterface)
072: accessFlags = AccessFlag.SUPER | AccessFlag.INTERFACE
073: | AccessFlag.ABSTRACT;
074: else
075: accessFlags = AccessFlag.SUPER;
076:
077: initSuperclass(super class);
078: interfaces = null;
079: fields = new ArrayList();
080: methods = new ArrayList();
081: this classname = classname;
082:
083: attributes = new LinkedList();
084: attributes.add(new SourceFileAttribute(constPool,
085: getSourcefileName(this classname)));
086: }
087:
088: private void initSuperclass(String super class) {
089: if (super class != null) {
090: this .super Class = constPool.addClassInfo(super class);
091: cachedSuperclass = super class;
092: } else {
093: this .super Class = constPool
094: .addClassInfo("java.lang.Object");
095: cachedSuperclass = "java.lang.Object";
096: }
097: }
098:
099: private static String getSourcefileName(String qname) {
100: int index = qname.lastIndexOf('.');
101: if (index >= 0)
102: qname = qname.substring(index + 1);
103:
104: return qname + ".java";
105: }
106:
107: /**
108: * Eliminates dead constant pool items. If a method or a field is removed,
109: * the constant pool items used by that method/field become dead items. This
110: * method recreates a constant pool.
111: */
112: public void compact() {
113: ConstPool cp = compact0();
114: ArrayList list = methods;
115: int n = list.size();
116: for (int i = 0; i < n; ++i) {
117: MethodInfo minfo = (MethodInfo) list.get(i);
118: minfo.compact(cp);
119: }
120:
121: list = fields;
122: n = list.size();
123: for (int i = 0; i < n; ++i) {
124: FieldInfo finfo = (FieldInfo) list.get(i);
125: finfo.compact(cp);
126: }
127:
128: attributes = AttributeInfo.copyAll(attributes, cp);
129: constPool = cp;
130: }
131:
132: private ConstPool compact0() {
133: ConstPool cp = new ConstPool(this classname);
134: this Class = cp.getThisClassInfo();
135: String sc = getSuperclass();
136: if (sc != null)
137: super Class = cp.addClassInfo(getSuperclass());
138:
139: if (interfaces != null) {
140: int n = interfaces.length;
141: for (int i = 0; i < n; ++i)
142: interfaces[i] = cp.addClassInfo(constPool
143: .getClassInfo(interfaces[i]));
144: }
145:
146: return cp;
147: }
148:
149: /**
150: * Discards all attributes, associated with both the class file and the
151: * members such as a code attribute and exceptions attribute. The unused
152: * constant pool entries are also discarded (a new packed constant pool is
153: * constructed).
154: */
155: public void prune() {
156: ConstPool cp = compact0();
157: LinkedList newAttributes = new LinkedList();
158: AttributeInfo invisibleAnnotations = getAttribute(AnnotationsAttribute.invisibleTag);
159: if (invisibleAnnotations != null) {
160: invisibleAnnotations = invisibleAnnotations.copy(cp, null);
161: newAttributes.add(invisibleAnnotations);
162: }
163:
164: AttributeInfo visibleAnnotations = getAttribute(AnnotationsAttribute.visibleTag);
165: if (visibleAnnotations != null) {
166: visibleAnnotations = visibleAnnotations.copy(cp, null);
167: newAttributes.add(visibleAnnotations);
168: }
169:
170: ArrayList list = methods;
171: int n = list.size();
172: for (int i = 0; i < n; ++i) {
173: MethodInfo minfo = (MethodInfo) list.get(i);
174: minfo.prune(cp);
175: }
176:
177: list = fields;
178: n = list.size();
179: for (int i = 0; i < n; ++i) {
180: FieldInfo finfo = (FieldInfo) list.get(i);
181: finfo.prune(cp);
182: }
183:
184: attributes = newAttributes;
185: cp.prune();
186: constPool = cp;
187: }
188:
189: /**
190: * Returns a constant pool table.
191: */
192: public ConstPool getConstPool() {
193: return constPool;
194: }
195:
196: /**
197: * Returns true if this is an interface.
198: */
199: public boolean isInterface() {
200: return (accessFlags & AccessFlag.INTERFACE) != 0;
201: }
202:
203: /**
204: * Returns true if this is a final class or interface.
205: */
206: public boolean isFinal() {
207: return (accessFlags & AccessFlag.FINAL) != 0;
208: }
209:
210: /**
211: * Returns true if this is an abstract class or an interface.
212: */
213: public boolean isAbstract() {
214: return (accessFlags & AccessFlag.ABSTRACT) != 0;
215: }
216:
217: /**
218: * Returns access flags.
219: *
220: * @see javassist.bytecode.AccessFlag
221: */
222: public int getAccessFlags() {
223: return accessFlags;
224: }
225:
226: /**
227: * Changes access flags.
228: *
229: * @see javassist.bytecode.AccessFlag
230: */
231: public void setAccessFlags(int acc) {
232: accessFlags = acc | AccessFlag.SUPER;
233: }
234:
235: /**
236: * Returns access and property flags of this nested class.
237: * This method returns -1 if the class is not a nested class.
238: *
239: * <p>The returned value is obtained from <code>inner_class_access_flags</code>
240: * of the entry representing this nested class itself
241: * in <code>InnerClasses_attribute</code>>.
242: */
243: public int getInnerAccessFlags() {
244: InnerClassesAttribute ica = (InnerClassesAttribute) getAttribute(InnerClassesAttribute.tag);
245: if (ica == null)
246: return -1;
247:
248: String name = getName();
249: int n = ica.tableLength();
250: for (int i = 0; i < n; ++i)
251: if (name.equals(ica.innerClass(i)))
252: return ica.accessFlags(i);
253:
254: return -1;
255: }
256:
257: /**
258: * Returns the class name.
259: */
260: public String getName() {
261: return this classname;
262: }
263:
264: /**
265: * Sets the class name. This method substitutes the new name for all
266: * occurrences of the old class name in the class file.
267: */
268: public void setName(String name) {
269: renameClass(this classname, name);
270: }
271:
272: /**
273: * Returns the super class name.
274: */
275: public String getSuperclass() {
276: if (cachedSuperclass == null)
277: cachedSuperclass = constPool.getClassInfo(super Class);
278:
279: return cachedSuperclass;
280: }
281:
282: /**
283: * Returns the index of the constant pool entry representing the super
284: * class.
285: */
286: public int getSuperclassId() {
287: return super Class;
288: }
289:
290: /**
291: * Sets the super class.
292: *
293: * <p>
294: * This method modifies constructors so that they call constructors declared
295: * in the new super class.
296: */
297: public void setSuperclass(String super class)
298: throws CannotCompileException {
299: if (super class == null)
300: super class = "java.lang.Object";
301:
302: try {
303: this .super Class = constPool.addClassInfo(super class);
304: ArrayList list = methods;
305: int n = list.size();
306: for (int i = 0; i < n; ++i) {
307: MethodInfo minfo = (MethodInfo) list.get(i);
308: minfo.setSuperclass(super class);
309: }
310: } catch (BadBytecode e) {
311: throw new CannotCompileException(e);
312: }
313: cachedSuperclass = super class;
314: }
315:
316: /**
317: * Replaces all occurrences of a class name in the class file.
318: *
319: * <p>
320: * If class X is substituted for class Y in the class file, X and Y must
321: * have the same signature. If Y provides a method m(), X must provide it
322: * even if X inherits m() from the super class. If this fact is not
323: * guaranteed, the bytecode verifier may cause an error.
324: *
325: * @param oldname
326: * the replaced class name
327: * @param newname
328: * the substituted class name
329: */
330: public final void renameClass(String oldname, String newname) {
331: ArrayList list;
332: int n;
333:
334: if (oldname.equals(newname))
335: return;
336:
337: if (oldname.equals(this classname))
338: this classname = newname;
339:
340: oldname = Descriptor.toJvmName(oldname);
341: newname = Descriptor.toJvmName(newname);
342: constPool.renameClass(oldname, newname);
343:
344: list = methods;
345: n = list.size();
346: for (int i = 0; i < n; ++i) {
347: MethodInfo minfo = (MethodInfo) list.get(i);
348: String desc = minfo.getDescriptor();
349: minfo.setDescriptor(Descriptor.rename(desc, oldname,
350: newname));
351: }
352:
353: list = fields;
354: n = list.size();
355: for (int i = 0; i < n; ++i) {
356: FieldInfo finfo = (FieldInfo) list.get(i);
357: String desc = finfo.getDescriptor();
358: finfo.setDescriptor(Descriptor.rename(desc, oldname,
359: newname));
360: }
361: }
362:
363: /**
364: * Replaces all occurrences of several class names in the class file.
365: *
366: * @param classnames
367: * specifies which class name is replaced with which new name.
368: * Class names must be described with the JVM-internal
369: * representation like <code>java/lang/Object</code>.
370: * @see #renameClass(String,String)
371: */
372: public final void renameClass(Map classnames) {
373: String jvmNewThisName = (String) classnames.get(Descriptor
374: .toJvmName(this classname));
375: if (jvmNewThisName != null)
376: this classname = Descriptor.toJavaName(jvmNewThisName);
377:
378: constPool.renameClass(classnames);
379:
380: ArrayList list = methods;
381: int n = list.size();
382: for (int i = 0; i < n; ++i) {
383: MethodInfo minfo = (MethodInfo) list.get(i);
384: String desc = minfo.getDescriptor();
385: minfo.setDescriptor(Descriptor.rename(desc, classnames));
386: }
387:
388: list = fields;
389: n = list.size();
390: for (int i = 0; i < n; ++i) {
391: FieldInfo finfo = (FieldInfo) list.get(i);
392: String desc = finfo.getDescriptor();
393: finfo.setDescriptor(Descriptor.rename(desc, classnames));
394: }
395: }
396:
397: /**
398: * Returns the names of the interfaces implemented by the class.
399: * The returned array is read only.
400: */
401: public String[] getInterfaces() {
402: if (cachedInterfaces != null)
403: return cachedInterfaces;
404:
405: String[] rtn = null;
406: if (interfaces == null)
407: rtn = new String[0];
408: else {
409: int n = interfaces.length;
410: String[] list = new String[n];
411: for (int i = 0; i < n; ++i)
412: list[i] = constPool.getClassInfo(interfaces[i]);
413:
414: rtn = list;
415: }
416:
417: cachedInterfaces = rtn;
418: return rtn;
419: }
420:
421: /**
422: * Sets the interfaces.
423: *
424: * @param nameList
425: * the names of the interfaces.
426: */
427: public void setInterfaces(String[] nameList) {
428: cachedInterfaces = null;
429: if (nameList != null) {
430: int n = nameList.length;
431: interfaces = new int[n];
432: for (int i = 0; i < n; ++i)
433: interfaces[i] = constPool.addClassInfo(nameList[i]);
434: }
435: }
436:
437: /**
438: * Appends an interface to the interfaces implemented by the class.
439: */
440: public void addInterface(String name) {
441: cachedInterfaces = null;
442: int info = constPool.addClassInfo(name);
443: if (interfaces == null) {
444: interfaces = new int[1];
445: interfaces[0] = info;
446: } else {
447: int n = interfaces.length;
448: int[] newarray = new int[n + 1];
449: System.arraycopy(interfaces, 0, newarray, 0, n);
450: newarray[n] = info;
451: interfaces = newarray;
452: }
453: }
454:
455: /**
456: * Returns all the fields declared in the class.
457: *
458: * @return a list of <code>FieldInfo</code>.
459: * @see FieldInfo
460: */
461: public List getFields() {
462: return fields;
463: }
464:
465: /**
466: * Appends a field to the class.
467: *
468: * @throws DuplicateMemberException when the field is already included.
469: */
470: public void addField(FieldInfo finfo)
471: throws DuplicateMemberException {
472: testExistingField(finfo.getName(), finfo.getDescriptor());
473: fields.add(finfo);
474: }
475:
476: private void addField0(FieldInfo finfo) {
477: fields.add(finfo);
478: }
479:
480: private void testExistingField(String name, String descriptor)
481: throws DuplicateMemberException {
482: ListIterator it = fields.listIterator(0);
483: while (it.hasNext()) {
484: FieldInfo minfo = (FieldInfo) it.next();
485: if (minfo.getName().equals(name))
486: throw new DuplicateMemberException("duplicate field: "
487: + name);
488: }
489: }
490:
491: /**
492: * Returns all the methods declared in the class.
493: *
494: * @return a list of <code>MethodInfo</code>.
495: * @see MethodInfo
496: */
497: public List getMethods() {
498: return methods;
499: }
500:
501: /**
502: * Returns the method with the specified name. If there are multiple methods
503: * with that name, this method returns one of them.
504: *
505: * @return null if no such a method is found.
506: */
507: public MethodInfo getMethod(String name) {
508: ArrayList list = methods;
509: int n = list.size();
510: for (int i = 0; i < n; ++i) {
511: MethodInfo minfo = (MethodInfo) list.get(i);
512: if (minfo.getName().equals(name))
513: return minfo;
514: }
515:
516: return null;
517: }
518:
519: /**
520: * Returns a static initializer (class initializer), or null if it does not
521: * exist.
522: */
523: public MethodInfo getStaticInitializer() {
524: return getMethod(MethodInfo.nameClinit);
525: }
526:
527: /**
528: * Appends a method to the class.
529: *
530: * @throws DuplicateMemberException when the method is already included.
531: */
532: public void addMethod(MethodInfo minfo)
533: throws DuplicateMemberException {
534: testExistingMethod(minfo);
535: methods.add(minfo);
536: }
537:
538: private void addMethod0(MethodInfo minfo) {
539: methods.add(minfo);
540: }
541:
542: private void testExistingMethod(MethodInfo newMinfo)
543: throws DuplicateMemberException {
544: String name = newMinfo.getName();
545: String descriptor = newMinfo.getDescriptor();
546: ListIterator it = methods.listIterator(0);
547: while (it.hasNext()) {
548: MethodInfo minfo = (MethodInfo) it.next();
549: if (minfo.getName().equals(name)
550: && notBridgeMethod(minfo)
551: && notBridgeMethod(newMinfo)
552: && Descriptor.eqParamTypes(minfo.getDescriptor(),
553: descriptor))
554: throw new DuplicateMemberException("duplicate method: "
555: + name + " in " + this .getName());
556: }
557: }
558:
559: /* For a bridge method, see Sec. 15.12.4.5 of JLS 3rd Ed.
560: */
561: private boolean notBridgeMethod(MethodInfo minfo) {
562: return (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0;
563: }
564:
565: /**
566: * Returns all the attributes. The returned <code>List</code> object
567: * is shared with this object. If you add a new attribute to the list,
568: * the attribute is also added to the classs file represented by this
569: * object. If you remove an attribute from the list, it is also removed
570: * from the class file.
571: *
572: * @return a list of <code>AttributeInfo</code> objects.
573: * @see AttributeInfo
574: */
575: public List getAttributes() {
576: return attributes;
577: }
578:
579: /**
580: * Returns the attribute with the specified name. If there are multiple
581: * attributes with that name, this method returns either of them. It
582: * returns null if the specified attributed is not found.
583: *
584: * @param name attribute name
585: * @see #getAttributes()
586: */
587: public AttributeInfo getAttribute(String name) {
588: LinkedList list = attributes;
589: int n = list.size();
590: for (int i = 0; i < n; ++i) {
591: AttributeInfo ai = (AttributeInfo) list.get(i);
592: if (ai.getName().equals(name))
593: return ai;
594: }
595:
596: return null;
597: }
598:
599: /**
600: * Appends an attribute. If there is already an attribute with the same
601: * name, the new one substitutes for it.
602: *
603: * @see #getAttributes()
604: */
605: public void addAttribute(AttributeInfo info) {
606: AttributeInfo.remove(attributes, info.getName());
607: attributes.add(info);
608: }
609:
610: /**
611: * Returns the source file containing this class.
612: *
613: * @return null if this information is not available.
614: */
615: public String getSourceFile() {
616: SourceFileAttribute sf = (SourceFileAttribute) getAttribute(SourceFileAttribute.tag);
617: if (sf == null)
618: return null;
619: else
620: return sf.getFileName();
621: }
622:
623: private void read(DataInputStream in) throws IOException {
624: int i, n;
625: int magic = in.readInt();
626: if (magic != 0xCAFEBABE)
627: throw new IOException("non class file");
628:
629: minor = in.readUnsignedShort();
630: major = in.readUnsignedShort();
631: constPool = new ConstPool(in);
632: accessFlags = in.readUnsignedShort();
633: this Class = in.readUnsignedShort();
634: constPool.setThisClassInfo(this Class);
635: super Class = in.readUnsignedShort();
636: n = in.readUnsignedShort();
637: if (n == 0)
638: interfaces = null;
639: else {
640: interfaces = new int[n];
641: for (i = 0; i < n; ++i)
642: interfaces[i] = in.readUnsignedShort();
643: }
644:
645: ConstPool cp = constPool;
646: n = in.readUnsignedShort();
647: fields = new ArrayList();
648: for (i = 0; i < n; ++i)
649: addField0(new FieldInfo(cp, in));
650:
651: n = in.readUnsignedShort();
652: methods = new ArrayList();
653: for (i = 0; i < n; ++i)
654: addMethod0(new MethodInfo(cp, in));
655:
656: attributes = new LinkedList();
657: n = in.readUnsignedShort();
658: for (i = 0; i < n; ++i)
659: addAttribute(AttributeInfo.read(cp, in));
660:
661: this classname = constPool.getClassInfo(this Class);
662: }
663:
664: /**
665: * Writes a class file represened by this object into an output stream.
666: */
667: public void write(DataOutputStream out) throws IOException {
668: int i, n;
669:
670: out.writeInt(0xCAFEBABE); // magic
671: out.writeShort(minor); // minor version
672: out.writeShort(major); // major version
673: constPool.write(out); // constant pool
674: out.writeShort(accessFlags);
675: out.writeShort(this Class);
676: out.writeShort(super Class);
677:
678: if (interfaces == null)
679: n = 0;
680: else
681: n = interfaces.length;
682:
683: out.writeShort(n);
684: for (i = 0; i < n; ++i)
685: out.writeShort(interfaces[i]);
686:
687: ArrayList list = fields;
688: n = list.size();
689: out.writeShort(n);
690: for (i = 0; i < n; ++i) {
691: FieldInfo finfo = (FieldInfo) list.get(i);
692: finfo.write(out);
693: }
694:
695: list = methods;
696: n = list.size();
697: out.writeShort(n);
698: for (i = 0; i < n; ++i) {
699: MethodInfo minfo = (MethodInfo) list.get(i);
700: minfo.write(out);
701: }
702:
703: out.writeShort(attributes.size());
704: AttributeInfo.writeAll(attributes, out);
705: }
706:
707: /**
708: * Get the Major version.
709: *
710: * @return the major version
711: */
712: public int getMajorVersion() {
713: return major;
714: }
715:
716: /**
717: * Set the major version.
718: *
719: * @param major
720: * the major version
721: */
722: public void setMajorVersion(int major) {
723: this .major = major;
724: }
725:
726: /**
727: * Get the minor version.
728: *
729: * @return the minor version
730: */
731: public int getMinorVersion() {
732: return minor;
733: }
734:
735: /**
736: * Set the minor version.
737: *
738: * @param minor
739: * the minor version
740: */
741: public void setMinorVersion(int minor) {
742: this .minor = minor;
743: }
744:
745: /**
746: * Sets the major and minor version to Java 5.
747: *
748: * If the major version is older than 49, Java 5
749: * extensions such as annotations are ignored
750: * by the JVM.
751: */
752: public void setVersionToJava5() {
753: this .major = 49;
754: this .minor = 0;
755: }
756: }
|