0001: /*
0002: * Janino - An embedded Java[TM] compiler
0003: *
0004: * Copyright (c) 2006, Arno Unkrig
0005: * All rights reserved.
0006: *
0007: * Redistribution and use in source and binary forms, with or without
0008: * modification, are permitted provided that the following conditions
0009: * are met:
0010: *
0011: * 1. Redistributions of source code must retain the above copyright
0012: * notice, this list of conditions and the following disclaimer.
0013: * 2. Redistributions in binary form must reproduce the above
0014: * copyright notice, this list of conditions and the following
0015: * disclaimer in the documentation and/or other materials
0016: * provided with the distribution.
0017: * 3. The name of the author may not be used to endorse or promote
0018: * products derived from this software without specific prior
0019: * written permission.
0020: *
0021: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0022: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
0023: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0024: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
0025: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0026: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
0027: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0028: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
0029: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
0030: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
0031: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0032: */
0033:
0034: package org.codehaus.janino.util;
0035:
0036: import java.io.*;
0037: import java.util.*;
0038:
0039: import org.codehaus.janino.Descriptor;
0040:
0041: /**
0042: * An object that represents the Java<sup>TM</sup> "class file" format.
0043: * <p>
0044: * {@link #ClassFile(InputStream)} creates a {@link ClassFile} object from the bytecode
0045: * read from the given {@link InputStream}.
0046: * <p>
0047: * {@link #store(OutputStream)} generates Java<sup>TM</sup> bytecode
0048: * which is suitable for being processed by a Java<sup>TM</sup> virtual
0049: * machine.
0050: */
0051: public class ClassFile {
0052:
0053: /**
0054: * Construct from parsed components.
0055: * @param accessFlags as defined by {@link org.codehaus.janino.Mod}
0056: * @param thisClassFD the field descriptor for this class
0057: * @param superclassFD the field descriptor for the extended class (e.g. "Ljava/lang/Object;")
0058: * @param interfaceFDs the field descriptors for the implemented interfaces
0059: */
0060: public ClassFile(short accessFlags, String this ClassFD,
0061: String super classFD, String[] interfaceFDs) {
0062: this .majorVersion = ClassFile.MAJOR_VERSION_JDK_1_1;
0063: this .minorVersion = ClassFile.MINOR_VERSION_JDK_1_1;
0064:
0065: this .constantPool = new ArrayList();
0066: this .constantPool.add(null); // Add fake "0" index entry.
0067: this .constantPoolMap = new HashMap();
0068:
0069: this .accessFlags = accessFlags;
0070: this .this Class = this .addConstantClassInfo(this ClassFD);
0071: this .super class = this .addConstantClassInfo(super classFD);
0072: this .interfaces = new short[interfaceFDs.length];
0073: for (int i = 0; i < interfaceFDs.length; ++i) {
0074: this .interfaces[i] = this
0075: .addConstantClassInfo(interfaceFDs[i]);
0076: }
0077:
0078: this .fieldInfos = new ArrayList();
0079: this .methodInfos = new ArrayList();
0080: this .attributes = new ArrayList();
0081: }
0082:
0083: /**
0084: * Adds a "SourceFile" attribute to this class file. (Does not check whether one exists already.)
0085: * @param sourceFileName
0086: */
0087: public void addSourceFileAttribute(String sourceFileName) {
0088: this .attributes.add(new SourceFileAttribute(this
0089: .addConstantUtf8Info("SourceFile"), // attributeNameIndex
0090: this .addConstantUtf8Info(sourceFileName) // sourceFileIndex
0091: ));
0092: }
0093:
0094: public void addDeprecatedAttribute() {
0095: this .attributes.add(new DeprecatedAttribute(this
0096: .addConstantUtf8Info("Deprecated")));
0097: }
0098:
0099: /**
0100: * Find the "InnerClasses" attribute of this class file
0101: * @return <code>null</code> if this class has no "InnerClasses" attribute
0102: */
0103: public InnerClassesAttribute getInnerClassesAttribute() {
0104: Short ni = (Short) this .constantPoolMap
0105: .get(new ConstantUtf8Info("InnerClasses"));
0106: if (ni == null)
0107: return null;
0108:
0109: for (Iterator it = this .attributes.iterator(); it.hasNext();) {
0110: AttributeInfo ai = (AttributeInfo) it.next();
0111: if (ai.nameIndex == ni.shortValue()
0112: && ai instanceof InnerClassesAttribute)
0113: return (InnerClassesAttribute) ai;
0114: }
0115: return null;
0116: }
0117:
0118: /**
0119: * Create an "InnerClasses" attribute if it does not exist, then add the given entry
0120: * to the "InnerClasses" attribute.
0121: * @param e
0122: */
0123: public void addInnerClassesAttributeEntry(
0124: InnerClassesAttribute.Entry e) {
0125: InnerClassesAttribute ica = this .getInnerClassesAttribute();
0126: if (ica == null) {
0127: ica = new InnerClassesAttribute(this
0128: .addConstantUtf8Info("InnerClasses"));
0129: this .attributes.add(ica);
0130: }
0131: ica.getEntries().add(e);
0132: return;
0133: }
0134:
0135: /**
0136: * Read "class file" data from a {@link InputStream} and construct a
0137: * {@link ClassFile} object from it.
0138: * <p>
0139: * If the {@link ClassFile} is created with this constructor, then most modifying operations
0140: * lead to a {@link UnsupportedOperationException}; only fields, methods and
0141: * attributes can be added.
0142: * @param inputStream
0143: * @throws IOException
0144: * @throws ClassFormatError
0145: */
0146: public ClassFile(InputStream inputStream) throws IOException,
0147: ClassFormatError {
0148: DataInputStream dis = inputStream instanceof DataInputStream ? (DataInputStream) inputStream
0149: : new DataInputStream(inputStream);
0150:
0151: int magic = dis.readInt(); // magic
0152: if (magic != ClassFile.CLASS_FILE_MAGIC)
0153: throw new ClassFormatError("Invalid magic number");
0154:
0155: this .minorVersion = dis.readShort(); // minor_version
0156: this .majorVersion = dis.readShort(); // major_version
0157: if (!ClassFile.isRecognizedVersion(this .majorVersion,
0158: this .minorVersion))
0159: throw new ClassFormatError(
0160: "Unrecognized class file format version "
0161: + this .majorVersion + "/"
0162: + this .minorVersion);
0163:
0164: this .constantPool = new ArrayList();
0165: this .constantPoolMap = new HashMap();
0166: this .loadConstantPool(dis); // constant_pool_count, constant_pool
0167:
0168: this .accessFlags = dis.readShort(); // access_flags
0169: this .this Class = dis.readShort(); // this_class
0170: this .super class = dis.readShort(); // super_class
0171: this .interfaces = ClassFile.readShortArray(dis); // interfaces_count, interfaces
0172:
0173: this .fieldInfos = Collections.unmodifiableList(this
0174: .loadFields(dis)); // fields_count, fields
0175: this .methodInfos = Collections.unmodifiableList(this
0176: .loadMethods(dis)); // methods_count, methods
0177: this .attributes = Collections.unmodifiableList(this
0178: .loadAttributes(dis)); // attributes_count, attributes
0179: }
0180:
0181: /**
0182: * @return The fully qualified name of this class, e.g. "pkg1.pkg2.Outer$Inner"
0183: */
0184: public String getThisClassName() {
0185: return this .getConstantClassName(this .this Class).replace('/',
0186: '.');
0187: }
0188:
0189: /**
0190: * Sets the major and minor class file version numbers (JVMS 4.1). The class file version
0191: * defaults to the JDK 1.1 values (45.3) which execute on virtually every JVM.
0192: * @param majorVersion
0193: * @param minorVersion
0194: */
0195: public void setVersion(short majorVersion, short minorVersion) {
0196: this .majorVersion = majorVersion;
0197: this .minorVersion = minorVersion;
0198: }
0199:
0200: /**
0201: * Returns the current major class file version number.
0202: */
0203: public short getMajorVersion() {
0204: return this .majorVersion;
0205: }
0206:
0207: /**
0208: * Returns the current minor class file version number.
0209: */
0210: public short getMinorVersion() {
0211: return this .minorVersion;
0212: }
0213:
0214: public static boolean isRecognizedVersion(short majorVersion,
0215: short minorVersion) {
0216: return ((majorVersion == ClassFile.MAJOR_VERSION_JDK_1_1 && minorVersion == ClassFile.MINOR_VERSION_JDK_1_1)
0217: || (majorVersion == ClassFile.MAJOR_VERSION_JDK_1_2 && minorVersion == ClassFile.MINOR_VERSION_JDK_1_2)
0218: || (majorVersion == ClassFile.MAJOR_VERSION_JDK_1_3 && minorVersion == ClassFile.MINOR_VERSION_JDK_1_3)
0219: || (majorVersion == ClassFile.MAJOR_VERSION_JDK_1_4 && minorVersion == ClassFile.MINOR_VERSION_JDK_1_4) || (majorVersion == ClassFile.MAJOR_VERSION_JDK_1_5 && minorVersion == ClassFile.MINOR_VERSION_JDK_1_5));
0220: }
0221:
0222: /**
0223: * Add a "CONSTANT_Class_info" structure to the class file.
0224: *
0225: * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#1221">JVM specification, section 4.4.1</a>
0226: */
0227: public short addConstantClassInfo(String typeFD) {
0228: String s;
0229: if (Descriptor.isClassOrInterfaceReference(typeFD)) {
0230: s = Descriptor.toInternalForm(typeFD);
0231: } else if (Descriptor.isArrayReference(typeFD)) {
0232: s = typeFD;
0233: } else {
0234: throw new RuntimeException("\""
0235: + Descriptor.toString(typeFD)
0236: + "\" is neither a class nor an array");
0237: }
0238:
0239: return this .addToConstantPool(new ConstantClassInfo(this
0240: .addConstantUtf8Info(s)));
0241: }
0242:
0243: /**
0244: * Add a "CONSTANT_Fieldref_info" structure to the class file.
0245: *
0246: * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#42041">JVM specification, section 4.4.2</a>
0247: */
0248: public short addConstantFieldrefInfo(String classFD,
0249: String fieldName, String fieldFD) {
0250: return this .addToConstantPool(new ConstantFieldrefInfo(this
0251: .addConstantClassInfo(classFD), this
0252: .addConstantNameAndTypeInfo(fieldName, fieldFD)));
0253: }
0254:
0255: /**
0256: * Add a "CONSTANT_Methodref_info" structure to the class file.
0257: *
0258: * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#42041">JVM specification, section 4.4.2</a>
0259: */
0260: public short addConstantMethodrefInfo(String classFD,
0261: String methodName, String methodMD) {
0262: return this .addToConstantPool(new ConstantMethodrefInfo(this
0263: .addConstantClassInfo(classFD), this
0264: .addConstantNameAndTypeInfo(methodName, methodMD)));
0265: }
0266:
0267: /**
0268: * Add a "CONSTANT_InterfaceMethodref_info" structure to the class file.
0269: *
0270: * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#42041">JVM specification, section 4.4.2</a>
0271: */
0272: public short addConstantInterfaceMethodrefInfo(String classFD,
0273: String methodName, String methodMD) {
0274: return this
0275: .addToConstantPool(new ConstantInterfaceMethodrefInfo(
0276: this .addConstantClassInfo(classFD), this
0277: .addConstantNameAndTypeInfo(methodName,
0278: methodMD)));
0279: }
0280:
0281: /**
0282: * Add a "CONSTANT_String_info" structure to the class file.
0283: *
0284: * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#29297">JVM specification, section 4.4.3</a>
0285: */
0286: public short addConstantStringInfo(String string) {
0287: return this .addToConstantPool(new ConstantStringInfo(this
0288: .addConstantUtf8Info(string)));
0289: }
0290:
0291: /**
0292: * Add a "CONSTANT_Integer_info" structure to the class file.
0293: *
0294: * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#21942">JVM specification, section 4.4.4</a>
0295: */
0296: public short addConstantIntegerInfo(final int value) {
0297: return this .addToConstantPool(new ConstantIntegerInfo(value));
0298: }
0299:
0300: /**
0301: * Add a "CONSTANT_Float_info" structure to the class file.
0302: *
0303: * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#21942">JVM specification, section 4.4.4</a>
0304: */
0305: public short addConstantFloatInfo(final float value) {
0306: return this .addToConstantPool(new ConstantFloatInfo(value));
0307: }
0308:
0309: /**
0310: * Add a "CONSTANT_Long_info" structure to the class file.
0311: *
0312: * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#1348">JVM specification, section 4.4.5</a>
0313: */
0314: public short addConstantLongInfo(final long value) {
0315: return this .addToConstantPool(new ConstantLongInfo(value));
0316: }
0317:
0318: /**
0319: * Add a "CONSTANT_Double_info" structure to the class file.
0320: *
0321: * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#1348">JVM specification, section 4.4.5</a>
0322: */
0323: public short addConstantDoubleInfo(final double value) {
0324: return this .addToConstantPool(new ConstantDoubleInfo(value));
0325: }
0326:
0327: /**
0328: * Add a "CONSTANT_NameAndType_info" structure to the class file.
0329: *
0330: * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#1327">JVM specification, section 4.4.6</a>
0331: */
0332: private short addConstantNameAndTypeInfo(String name,
0333: String descriptor) {
0334: return this .addToConstantPool(new ConstantNameAndTypeInfo(this
0335: .addConstantUtf8Info(name), this
0336: .addConstantUtf8Info(descriptor)));
0337: }
0338:
0339: /**
0340: * Add a "CONSTANT_Utf8_info" structure to the class file.
0341: *
0342: * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#7963">JVM specification, section 4.4.7</a>
0343: */
0344: public short addConstantUtf8Info(final String s) {
0345: return this .addToConstantPool(new ConstantUtf8Info(s));
0346: }
0347:
0348: /**
0349: * Convenience method that adds a String, Integer, Float, Long or Double ConstantInfo.
0350: */
0351: private short addConstantSIFLDInfo(Object cv) {
0352: if (cv instanceof String) {
0353: return this .addConstantStringInfo((String) cv);
0354: } else if (cv instanceof Byte || cv instanceof Short
0355: || cv instanceof Integer) {
0356: return this
0357: .addConstantIntegerInfo(((Number) cv).intValue());
0358: } else if (cv instanceof Boolean) {
0359: return this .addConstantIntegerInfo(((Boolean) cv)
0360: .booleanValue() ? 1 : 0);
0361: } else if (cv instanceof Character) {
0362: return this .addConstantIntegerInfo(((Character) cv)
0363: .charValue());
0364: } else if (cv instanceof Float) {
0365: return this .addConstantFloatInfo(((Float) cv).floatValue());
0366: } else if (cv instanceof Long) {
0367: return this .addConstantLongInfo(((Long) cv).longValue());
0368: } else if (cv instanceof Double) {
0369: return this .addConstantDoubleInfo(((Double) cv)
0370: .doubleValue());
0371: } else {
0372: throw new RuntimeException(
0373: "Unexpected constant value type \""
0374: + cv.getClass().getName() + "\"");
0375: }
0376: }
0377:
0378: private short addToConstantPool(ConstantPoolInfo cpi) {
0379: Short index = (Short) this .constantPoolMap.get(cpi);
0380: if (index != null)
0381: return index.shortValue();
0382:
0383: short res = (short) this .constantPool.size();
0384: this .constantPool.add(cpi);
0385: if (cpi.isWide())
0386: this .constantPool.add(null);
0387:
0388: this .constantPoolMap.put(cpi, new Short(res));
0389: return res;
0390: }
0391:
0392: public FieldInfo addFieldInfo(short accessFlags, String fieldName,
0393: String fieldTypeFD, Object optionalConstantValue) {
0394: List attributes = new ArrayList();
0395: if (optionalConstantValue != null) {
0396: attributes.add(new ConstantValueAttribute(this
0397: .addConstantUtf8Info("ConstantValue"), this
0398: .addConstantSIFLDInfo(optionalConstantValue)));
0399: }
0400: FieldInfo fi = new FieldInfo(accessFlags, // accessFlags
0401: this .addConstantUtf8Info(fieldName), // nameIndex
0402: this .addConstantUtf8Info(fieldTypeFD), // descriptorIndex
0403: attributes // attributes
0404: );
0405: this .fieldInfos.add(fi);
0406: return fi;
0407: }
0408:
0409: public MethodInfo addMethodInfo(short accessFlags,
0410: String methodName, String methodMD) {
0411: MethodInfo mi = new MethodInfo(accessFlags, // accessFlags
0412: this .addConstantUtf8Info(methodName), // nameIndex
0413: this .addConstantUtf8Info(methodMD), // desriptorIndex
0414: new ArrayList() // attributes
0415: );
0416: this .methodInfos.add(mi);
0417: return mi;
0418: }
0419:
0420: public ConstantPoolInfo getConstantPoolInfo(short index) {
0421: return (ConstantPoolInfo) this .constantPool.get(index);
0422: }
0423:
0424: /**
0425: * @param index Index to a <code>CONSTANT_Class_info</code> in the constant pool
0426: * @return The name of the denoted class in "internal form" (see JVMS 4.2)
0427: */
0428: public String getConstantClassName(short index) {
0429: ConstantClassInfo cci = (ConstantClassInfo) this
0430: .getConstantPoolInfo(index);
0431: ConstantUtf8Info cui = (ConstantUtf8Info) this
0432: .getConstantPoolInfo(cci.nameIndex);
0433: return cui.s;
0434: }
0435:
0436: /**
0437: * @param index Index to a <code>CONSTANT_Utf8_info</code> in the constant pool
0438: * @return The string represented by the structure
0439: */
0440: public String getConstantUtf8(short index) {
0441: ConstantUtf8Info cui = (ConstantUtf8Info) this
0442: .getConstantPoolInfo(index);
0443: return cui.s;
0444: }
0445:
0446: /**
0447: * u4 length, u1[length]
0448: */
0449: private static byte[] readLengthAndBytes(DataInputStream dis)
0450: throws IOException {
0451: byte[] ba = new byte[dis.readInt()];
0452: dis.readFully(ba);
0453: return ba;
0454: }
0455:
0456: /**
0457: * u2 length, u2[length]
0458: */
0459: private static short[] readShortArray(DataInputStream dis)
0460: throws IOException {
0461: short count = dis.readShort();
0462: short[] result = new short[count];
0463: for (int i = 0; i < count; ++i)
0464: result[i] = dis.readShort();
0465: return result;
0466: }
0467:
0468: /**
0469: * u2 constant_pool_count, constant_pool[constant_pool_count]
0470: */
0471: private void loadConstantPool(DataInputStream dis)
0472: throws IOException {
0473: this .constantPool.clear();
0474: this .constantPoolMap.clear();
0475:
0476: short constantPoolCount = dis.readShort(); // constant_pool_count
0477: this .constantPool.add(null);
0478: for (short i = 1; i < constantPoolCount; ++i) {
0479: ConstantPoolInfo cpi = ConstantPoolInfo
0480: .loadConstantPoolInfo(dis);
0481: this .constantPool.add(cpi);
0482: this .constantPoolMap.put(cpi, new Short(i));
0483: if (cpi instanceof ConstantLongInfo
0484: || cpi instanceof ConstantDoubleInfo) {
0485: this .constantPool.add(null);
0486: ++i;
0487: }
0488: }
0489: }
0490:
0491: /**
0492: * u2 fields_count, fields[fields_count]
0493: */
0494: private List loadFields(DataInputStream dis) throws IOException {
0495: short fieldsCount = dis.readShort();
0496: List fields = new ArrayList(fieldsCount);
0497: for (int i = 0; i < fieldsCount; ++i) {
0498: fields.add(new FieldInfo(dis.readShort(), // accessFlags
0499: dis.readShort(), // nameIndex
0500: dis.readShort(), // descriptorIndex
0501: this .loadAttributes(dis) // attributes
0502: ));
0503: }
0504: return fields;
0505: }
0506:
0507: /**
0508: * u2 methods_count, methods[methods_count]
0509: */
0510: private List loadMethods(DataInputStream dis) throws IOException {
0511: short methodsCount = dis.readShort();
0512: List methods = new ArrayList(methodsCount);
0513: for (int i = 0; i < methodsCount; ++i)
0514: methods.add(this .loadMethodInfo(dis));
0515: return methods;
0516: }
0517:
0518: /**
0519: * u2 attributes_count, attributes[attributes_count]
0520: */
0521: private List loadAttributes(DataInputStream dis) throws IOException {
0522: short attributesCount = dis.readShort();
0523: List attributes = new ArrayList(attributesCount);
0524: for (int i = 0; i < attributesCount; ++i)
0525: attributes.add(this .loadAttribute(dis));
0526: return attributes;
0527: }
0528:
0529: /**
0530: * Write {@link ClassFile} to an {@link OutputStream}, in "class file" format.
0531: * <p>
0532: * Notice that if an {@link IOException} is thrown, the class file is
0533: * probably written incompletely and thus invalid. The calling method must take
0534: * care of this situation, e.g. by closing the output stream and then deleting the
0535: * file.
0536: * @param os
0537: * @throws IOException
0538: */
0539: public void store(OutputStream os) throws IOException {
0540: DataOutputStream dos = os instanceof DataOutputStream ? (DataOutputStream) os
0541: : new DataOutputStream(os);
0542:
0543: dos.writeInt(ClassFile.CLASS_FILE_MAGIC); // magic
0544: dos.writeShort(this .minorVersion); // minor_version
0545: dos.writeShort(this .majorVersion); // major_version
0546: ClassFile.storeConstantPool(dos, this .constantPool); // constant_pool_count, constant_pool
0547: dos.writeShort(this .accessFlags); // access_flags
0548: dos.writeShort(this .this Class); // this_class
0549: dos.writeShort(this .super class); // super_class
0550: ClassFile.storeShortArray(dos, this .interfaces); // interfaces_count, interfaces
0551: ClassFile.storeFields(dos, this .fieldInfos); // fields_count, fields
0552: ClassFile.storeMethods(dos, this .methodInfos); // methods_count, methods
0553: ClassFile.storeAttributes(dos, this .attributes); // attributes_count, attributes
0554: }
0555:
0556: /**
0557: * u2 constant_pool_count, constant_pool[constant_pool_count - 1]
0558: */
0559: private static void storeConstantPool(DataOutputStream dos,
0560: List constantPool) throws IOException {
0561: dos.writeShort(constantPool.size());
0562: for (int i = 1; i < constantPool.size(); ++i) {
0563: ConstantPoolInfo cpi = (ConstantPoolInfo) constantPool
0564: .get(i);
0565: if (cpi == null)
0566: continue; // (Double or Long CPI.)
0567: cpi.store(dos);
0568: }
0569: }
0570:
0571: /**
0572: * u2 count, u2[count]
0573: */
0574: private static void storeShortArray(DataOutputStream dos, short[] sa)
0575: throws IOException {
0576: dos.writeShort(sa.length);
0577: for (int i = 0; i < sa.length; ++i)
0578: dos.writeShort(sa[i]);
0579: }
0580:
0581: /**
0582: * u2 fields_count, fields[fields_count]
0583: */
0584: private static void storeFields(DataOutputStream dos,
0585: List fieldInfos) throws IOException {
0586: dos.writeShort(fieldInfos.size());
0587: for (int i = 0; i < fieldInfos.size(); ++i)
0588: ((FieldInfo) fieldInfos.get(i)).store(dos);
0589: }
0590:
0591: /**
0592: * u2 methods_count, methods[methods_count]
0593: */
0594: private static void storeMethods(DataOutputStream dos,
0595: List methodInfos) throws IOException {
0596: dos.writeShort(methodInfos.size());
0597: for (int i = 0; i < methodInfos.size(); ++i)
0598: ((MethodInfo) methodInfos.get(i)).store(dos);
0599: }
0600:
0601: /**
0602: * u2 attributes_count, attributes[attributes_count]
0603: */
0604: private static void storeAttributes(DataOutputStream dos,
0605: List attributeInfos) throws IOException {
0606: dos.writeShort(attributeInfos.size());
0607: for (int i = 0; i < attributeInfos.size(); ++i)
0608: ((AttributeInfo) attributeInfos.get(i)).store(dos);
0609: }
0610:
0611: /**
0612: * Construct the name of a resource that could contain the source code of
0613: * the class with the given name.
0614: * <p>
0615: * Notice that member types are declared inside a different type, so the relevant source file
0616: * is that of the outermost declaring class.
0617: *
0618: * @param className Fully qualified class name, e.g. "pkg1.pkg2.Outer$Inner"
0619: * @return the name of the resource, e.g. "pkg1/pkg2/Outer.java"
0620: */
0621: public static String getSourceResourceName(String className) {
0622:
0623: // Strip nested type suffixes.
0624: {
0625: int idx = className.lastIndexOf('.') + 1;
0626: idx = className.indexOf('$', idx);
0627: if (idx != -1)
0628: className = className.substring(0, idx);
0629: }
0630:
0631: return className.replace('.', '/') + ".java";
0632: }
0633:
0634: /**
0635: * Construct the name of a resource that could contain the class file of the
0636: * class with the given name.
0637: *
0638: * @param className Fully qualified class name, e.g. "pkg1.pkg2.Outer$Inner"
0639: * @return the name of the resource, e.g. "pkg1/pkg2/Outer$Inner.class"
0640: */
0641: public static String getClassFileResourceName(String className) {
0642: return className.replace('.', '/') + ".class";
0643: }
0644:
0645: /**
0646: * Return the byte code of this {@link ClassFile} as a byte array.
0647: */
0648: public byte[] toByteArray() {
0649: ByteArrayOutputStream baos = new ByteArrayOutputStream();
0650: try {
0651: this .store(baos);
0652: } catch (IOException ex) {
0653: // ByteArrayOutputStream should never throw IOExceptions.
0654: throw new RuntimeException(ex.toString());
0655: }
0656: return baos.toByteArray();
0657: }
0658:
0659: private static final int CLASS_FILE_MAGIC = 0xcafebabe;
0660:
0661: public final static short MAJOR_VERSION_JDK_1_1 = 45;
0662: public final static short MINOR_VERSION_JDK_1_1 = 3;
0663: public final static short MAJOR_VERSION_JDK_1_2 = 46;
0664: public final static short MINOR_VERSION_JDK_1_2 = 0;
0665: public final static short MAJOR_VERSION_JDK_1_3 = 47;
0666: public final static short MINOR_VERSION_JDK_1_3 = 0;
0667: public final static short MAJOR_VERSION_JDK_1_4 = 48;
0668: public final static short MINOR_VERSION_JDK_1_4 = 0;
0669: public final static short MAJOR_VERSION_JDK_1_5 = 49;
0670: public final static short MINOR_VERSION_JDK_1_5 = 0;
0671:
0672: private short majorVersion;
0673: private short minorVersion;
0674: public/*final*/List constantPool; // ConstantPoolInfo
0675: public/*final*/short accessFlags;
0676: public/*final*/short this Class;
0677: public/*final*/short super class;
0678: public/*final*/short[] interfaces;
0679: public/*final*/List fieldInfos; // FieldInfo
0680: public/*final*/List methodInfos; // MethodInfo
0681: private/*final*/List attributes; // AttributeInfo
0682:
0683: // Convenience.
0684: private/*final*/Map constantPoolMap; // ConstantPoolInfo => Short
0685:
0686: public static abstract class ConstantPoolInfo {
0687: abstract public void store(DataOutputStream dos)
0688: throws IOException;
0689:
0690: abstract public boolean isWide();
0691:
0692: private static ConstantPoolInfo loadConstantPoolInfo(
0693: DataInputStream dis) throws IOException {
0694: byte tag = dis.readByte();
0695: //System.out.println("tag=" + tag);
0696: switch (tag) {
0697:
0698: case 7:
0699: return new ConstantClassInfo(dis.readShort());
0700:
0701: case 9:
0702: return new ConstantFieldrefInfo(dis.readShort(), dis
0703: .readShort());
0704:
0705: case 10:
0706: return new ConstantMethodrefInfo(dis.readShort(), dis
0707: .readShort());
0708:
0709: case 11:
0710: return new ConstantInterfaceMethodrefInfo(dis
0711: .readShort(), dis.readShort());
0712:
0713: case 8:
0714: return new ConstantStringInfo(dis.readShort());
0715:
0716: case 3:
0717: return new ConstantIntegerInfo(dis.readInt());
0718:
0719: case 4:
0720: return new ConstantFloatInfo(dis.readFloat());
0721:
0722: case 5:
0723: return new ConstantLongInfo(dis.readLong());
0724:
0725: case 6:
0726: return new ConstantDoubleInfo(dis.readDouble());
0727:
0728: case 12:
0729: return new ConstantNameAndTypeInfo(dis.readShort(), dis
0730: .readShort());
0731:
0732: case 1:
0733: return new ConstantUtf8Info(dis.readUTF());
0734:
0735: default:
0736: throw new ClassFormatError("Invalid constant pool tag "
0737: + tag);
0738: }
0739: }
0740: }
0741:
0742: public static abstract class ConstantValuePoolInfo extends
0743: ConstantPoolInfo {
0744: public abstract Object getValue(ClassFile classFile);
0745: }
0746:
0747: public static class ConstantClassInfo extends ConstantPoolInfo {
0748: private final short nameIndex;
0749:
0750: public ConstantClassInfo(short nameIndex) {
0751: this .nameIndex = nameIndex;
0752: }
0753:
0754: // Implement ConstantPoolInfo.
0755: public boolean isWide() {
0756: return false;
0757: }
0758:
0759: public void store(DataOutputStream dos) throws IOException {
0760: dos.writeByte(7);
0761: dos.writeShort(this .nameIndex);
0762: }
0763:
0764: public boolean equals(Object o) {
0765: return o instanceof ConstantClassInfo
0766: && ((ConstantClassInfo) o).nameIndex == this .nameIndex;
0767: }
0768:
0769: public int hashCode() {
0770: return this .nameIndex;
0771: }
0772: }
0773:
0774: public static class ConstantFieldrefInfo extends ConstantPoolInfo {
0775: private final short classIndex;
0776: private final short nameAndTypeIndex;
0777:
0778: public ConstantFieldrefInfo(short classIndex,
0779: short nameAndTypeIndex) {
0780: this .classIndex = classIndex;
0781: this .nameAndTypeIndex = nameAndTypeIndex;
0782: }
0783:
0784: public short getNameAndTypeIndex() {
0785: return this .nameAndTypeIndex;
0786: }
0787:
0788: // Implement ConstantPoolInfo.
0789: public boolean isWide() {
0790: return false;
0791: }
0792:
0793: public void store(DataOutputStream dos) throws IOException {
0794: dos.writeByte(9);
0795: dos.writeShort(this .classIndex);
0796: dos.writeShort(this .nameAndTypeIndex);
0797: }
0798:
0799: public boolean equals(Object o) {
0800: return (o instanceof ConstantFieldrefInfo
0801: && ((ConstantFieldrefInfo) o).classIndex == this .classIndex && ((ConstantFieldrefInfo) o).nameAndTypeIndex == this .nameAndTypeIndex);
0802: }
0803:
0804: public int hashCode() {
0805: return this .classIndex + (this .nameAndTypeIndex << 16);
0806: }
0807: }
0808:
0809: public static class ConstantMethodrefInfo extends ConstantPoolInfo {
0810: private final short classIndex;
0811: private final short nameAndTypeIndex;
0812:
0813: public ConstantMethodrefInfo(short classIndex,
0814: short nameAndTypeIndex) {
0815: this .classIndex = classIndex;
0816: this .nameAndTypeIndex = nameAndTypeIndex;
0817: }
0818:
0819: public short getNameAndTypeIndex() {
0820: return this .nameAndTypeIndex;
0821: }
0822:
0823: // Implement ConstantPoolInfo.
0824: public boolean isWide() {
0825: return false;
0826: }
0827:
0828: public void store(DataOutputStream dos) throws IOException {
0829: dos.writeByte(10);
0830: dos.writeShort(this .classIndex);
0831: dos.writeShort(this .nameAndTypeIndex);
0832: }
0833:
0834: public boolean equals(Object o) {
0835: return (o instanceof ConstantMethodrefInfo
0836: && ((ConstantMethodrefInfo) o).classIndex == this .classIndex && ((ConstantMethodrefInfo) o).nameAndTypeIndex == this .nameAndTypeIndex);
0837: }
0838:
0839: public int hashCode() {
0840: return this .classIndex + (this .nameAndTypeIndex << 16);
0841: }
0842: }
0843:
0844: public static class ConstantInterfaceMethodrefInfo extends
0845: ConstantPoolInfo {
0846: private final short classIndex;
0847: private final short nameAndTypeIndex;
0848:
0849: public ConstantInterfaceMethodrefInfo(short classIndex,
0850: short nameAndTypeIndex) {
0851: this .classIndex = classIndex;
0852: this .nameAndTypeIndex = nameAndTypeIndex;
0853: }
0854:
0855: public short getNameAndTypeIndex() {
0856: return this .nameAndTypeIndex;
0857: }
0858:
0859: // Implement ConstantPoolInfo.
0860: public boolean isWide() {
0861: return false;
0862: }
0863:
0864: public void store(DataOutputStream dos) throws IOException {
0865: dos.writeByte(11);
0866: dos.writeShort(this .classIndex);
0867: dos.writeShort(this .nameAndTypeIndex);
0868: }
0869:
0870: public boolean equals(Object o) {
0871: return (o instanceof ConstantInterfaceMethodrefInfo
0872: && ((ConstantInterfaceMethodrefInfo) o).classIndex == this .classIndex && ((ConstantInterfaceMethodrefInfo) o).nameAndTypeIndex == this .nameAndTypeIndex);
0873: }
0874:
0875: public int hashCode() {
0876: return this .classIndex + (this .nameAndTypeIndex << 16);
0877: }
0878: }
0879:
0880: static class ConstantStringInfo extends ConstantValuePoolInfo {
0881: private final short stringIndex;
0882:
0883: public ConstantStringInfo(short stringIndex) {
0884: this .stringIndex = stringIndex;
0885: }
0886:
0887: // Implement ConstantValuePoolInfo.
0888: public Object getValue(ClassFile classFile) {
0889: return classFile.getConstantUtf8(this .stringIndex);
0890: }
0891:
0892: // Implement ConstantPoolInfo.
0893: public boolean isWide() {
0894: return false;
0895: }
0896:
0897: public void store(DataOutputStream dos) throws IOException {
0898: dos.writeByte(8);
0899: dos.writeShort(this .stringIndex);
0900: }
0901:
0902: public boolean equals(Object o) {
0903: return o instanceof ConstantStringInfo
0904: && ((ConstantStringInfo) o).stringIndex == this .stringIndex;
0905: }
0906:
0907: public int hashCode() {
0908: return this .stringIndex;
0909: }
0910: }
0911:
0912: private static class ConstantIntegerInfo extends
0913: ConstantValuePoolInfo {
0914: private final int value;
0915:
0916: public ConstantIntegerInfo(int value) {
0917: this .value = value;
0918: }
0919:
0920: // Implement ConstantValuePoolInfo.
0921: public Object getValue(ClassFile classFile) {
0922: return new Integer(this .value);
0923: }
0924:
0925: // Implement ConstantPoolInfo.
0926: public boolean isWide() {
0927: return false;
0928: }
0929:
0930: public void store(DataOutputStream dos) throws IOException {
0931: dos.writeByte(3);
0932: dos.writeInt(this .value);
0933: }
0934:
0935: public boolean equals(Object o) {
0936: return o instanceof ConstantIntegerInfo
0937: && ((ConstantIntegerInfo) o).value == this .value;
0938: }
0939:
0940: public int hashCode() {
0941: return this .value;
0942: }
0943: }
0944:
0945: private static class ConstantFloatInfo extends
0946: ConstantValuePoolInfo {
0947: private final float value;
0948:
0949: public ConstantFloatInfo(float value) {
0950: this .value = value;
0951: }
0952:
0953: // Implement ConstantValuePoolInfo.
0954: public Object getValue(ClassFile classFile) {
0955: return new Float(this .value);
0956: }
0957:
0958: // Implement ConstantPoolInfo.
0959: public boolean isWide() {
0960: return false;
0961: }
0962:
0963: public void store(DataOutputStream dos) throws IOException {
0964: dos.writeByte(4);
0965: dos.writeFloat(this .value);
0966: }
0967:
0968: public boolean equals(Object o) {
0969: return o instanceof ConstantFloatInfo
0970: && ((ConstantFloatInfo) o).value == this .value;
0971: }
0972:
0973: public int hashCode() {
0974: return Float.floatToIntBits(this .value);
0975: }
0976: }
0977:
0978: private static class ConstantLongInfo extends ConstantValuePoolInfo {
0979: private final long value;
0980:
0981: public ConstantLongInfo(long value) {
0982: this .value = value;
0983: }
0984:
0985: // Implement ConstantValuePoolInfo.
0986: public Object getValue(ClassFile classFile) {
0987: return new Long(this .value);
0988: }
0989:
0990: // Implement ConstantPoolInfo.
0991: public boolean isWide() {
0992: return true;
0993: }
0994:
0995: public void store(DataOutputStream dos) throws IOException {
0996: dos.writeByte(5);
0997: dos.writeLong(this .value);
0998: }
0999:
1000: public boolean equals(Object o) {
1001: return o instanceof ConstantLongInfo
1002: && ((ConstantLongInfo) o).value == this .value;
1003: }
1004:
1005: public int hashCode() {
1006: return (int) this .value ^ (int) (this .value >> 32);
1007: }
1008: }
1009:
1010: private static class ConstantDoubleInfo extends
1011: ConstantValuePoolInfo {
1012: private final double value;
1013:
1014: public ConstantDoubleInfo(double value) {
1015: this .value = value;
1016: }
1017:
1018: // Implement ConstantValuePoolInfo.
1019: public Object getValue(ClassFile classFile) {
1020: return new Double(this .value);
1021: }
1022:
1023: // Implement ConstantPoolInfo.
1024: public boolean isWide() {
1025: return true;
1026: }
1027:
1028: public void store(DataOutputStream dos) throws IOException {
1029: dos.writeByte(6);
1030: dos.writeDouble(this .value);
1031: }
1032:
1033: public boolean equals(Object o) {
1034: return o instanceof ConstantDoubleInfo
1035: && ((ConstantDoubleInfo) o).value == this .value;
1036: }
1037:
1038: public int hashCode() {
1039: long bits = Double.doubleToLongBits(this .value);
1040: return (int) bits ^ (int) (bits >> 32);
1041: }
1042: }
1043:
1044: public static class ConstantNameAndTypeInfo extends
1045: ConstantPoolInfo {
1046: private final short nameIndex;
1047: private final short descriptorIndex;
1048:
1049: public ConstantNameAndTypeInfo(short nameIndex,
1050: short descriptorIndex) {
1051: this .nameIndex = nameIndex;
1052: this .descriptorIndex = descriptorIndex;
1053: }
1054:
1055: public short getDescriptorIndex() {
1056: return this .descriptorIndex;
1057: }
1058:
1059: // Implement ConstantPoolInfo.
1060: public boolean isWide() {
1061: return false;
1062: }
1063:
1064: public void store(DataOutputStream dos) throws IOException {
1065: dos.writeByte(12);
1066: dos.writeShort(this .nameIndex);
1067: dos.writeShort(this .descriptorIndex);
1068: }
1069:
1070: public boolean equals(Object o) {
1071: return (o instanceof ConstantNameAndTypeInfo
1072: && ((ConstantNameAndTypeInfo) o).nameIndex == this .nameIndex && ((ConstantNameAndTypeInfo) o).descriptorIndex == this .descriptorIndex);
1073: }
1074:
1075: public int hashCode() {
1076: return this .nameIndex + (this .descriptorIndex << 16);
1077: }
1078: }
1079:
1080: public static class ConstantUtf8Info extends ConstantPoolInfo {
1081: private final String s;
1082:
1083: public ConstantUtf8Info(String s) {
1084: if (s == null)
1085: throw new RuntimeException();
1086: this .s = s;
1087: }
1088:
1089: public String getString() {
1090: return this .s;
1091: }
1092:
1093: // Implement ConstantPoolInfo.
1094: public boolean isWide() {
1095: return false;
1096: }
1097:
1098: public void store(DataOutputStream dos) throws IOException {
1099: dos.writeByte(1);
1100: try {
1101: dos.writeUTF(this .s);
1102: } catch (UTFDataFormatException e) {
1103: throw new ClassFormatError(
1104: "String constant too long to store in class file");
1105: }
1106: }
1107:
1108: public boolean equals(Object o) {
1109: return o instanceof ConstantUtf8Info
1110: && ((ConstantUtf8Info) o).s.equals(this .s);
1111: }
1112:
1113: public int hashCode() {
1114: return this .s.hashCode();
1115: }
1116: }
1117:
1118: /**
1119: * This class represents a "method_info" structure, as defined by the
1120: * JVM specification.
1121: */
1122: public class MethodInfo {
1123: private final short accessFlags;
1124: private final short nameIndex;
1125: private final short descriptorIndex;
1126: private final List attributes; // AttributeInfo
1127:
1128: /**
1129: * Initialize the "method_info" structure.
1130: */
1131: public MethodInfo(short accessFlags, short nameIndex,
1132: short descriptorIndex, List attributes) {
1133: this .accessFlags = accessFlags;
1134: this .nameIndex = nameIndex;
1135: this .descriptorIndex = descriptorIndex;
1136: this .attributes = attributes;
1137: }
1138:
1139: public ClassFile getClassFile() {
1140: return ClassFile.this ;
1141: }
1142:
1143: public short getAccessFlags() {
1144: return this .accessFlags;
1145: }
1146:
1147: public short getNameIndex() {
1148: return this .nameIndex;
1149: }
1150:
1151: public short getDescriptorIndex() {
1152: return this .descriptorIndex;
1153: }
1154:
1155: public AttributeInfo[] getAttributes() {
1156: return (AttributeInfo[]) this .attributes
1157: .toArray(new AttributeInfo[this .attributes.size()]);
1158: }
1159:
1160: public void addAttribute(AttributeInfo attribute) {
1161: this .attributes.add(attribute);
1162: }
1163:
1164: /**
1165: * Write this object to a {@link DataOutputStream}, in the format
1166: * defined by the JVM specification.
1167: */
1168: public void store(DataOutputStream dos) throws IOException {
1169: dos.writeShort(this .accessFlags); // access_flags
1170: dos.writeShort(this .nameIndex); // name_index
1171: dos.writeShort(this .descriptorIndex); // descriptor_index
1172: ClassFile.storeAttributes(dos, this .attributes); // attributes_count, attributes
1173: }
1174: }
1175:
1176: private MethodInfo loadMethodInfo(DataInputStream dis)
1177: throws IOException {
1178: return new MethodInfo(dis.readShort(), // accessFlags
1179: dis.readShort(), // nameIndex
1180: dis.readShort(), // descriptorIndex
1181: this .loadAttributes(dis) // attributes
1182: );
1183: }
1184:
1185: public static class FieldInfo {
1186: public FieldInfo(short accessFlags, short nameIndex,
1187: short descriptorIndex, List attributes) {
1188: this .accessFlags = accessFlags;
1189: this .nameIndex = nameIndex;
1190: this .descriptorIndex = descriptorIndex;
1191: this .attributes = attributes;
1192: }
1193:
1194: public short getAccessFlags() {
1195: return this .accessFlags;
1196: }
1197:
1198: public short getNameIndex() {
1199: return this .nameIndex;
1200: }
1201:
1202: public short getDescriptorIndex() {
1203: return this .descriptorIndex;
1204: }
1205:
1206: public AttributeInfo[] getAttributes() {
1207: return (AttributeInfo[]) this .attributes
1208: .toArray(new AttributeInfo[this .attributes.size()]);
1209: }
1210:
1211: public void addAttribute(AttributeInfo attribute) {
1212: this .attributes.add(attribute);
1213: }
1214:
1215: public void store(DataOutputStream dos) throws IOException {
1216: dos.writeShort(this .accessFlags); // access_flags
1217: dos.writeShort(this .nameIndex); // name_index
1218: dos.writeShort(this .descriptorIndex); // descriptor_index
1219: ClassFile.storeAttributes(dos, this .attributes); // attibutes_count, attributes
1220: }
1221:
1222: private final short accessFlags;
1223: private final short nameIndex;
1224: private final short descriptorIndex;
1225: private final List attributes; // AttributeInfo
1226: }
1227:
1228: /**
1229: * Representation of a class file attribute (see JVMS 4.7).
1230: */
1231: public abstract static class AttributeInfo {
1232: public AttributeInfo(short nameIndex) {
1233: this .nameIndex = nameIndex;
1234: }
1235:
1236: public void store(DataOutputStream dos) throws IOException {
1237: ByteArrayOutputStream baos = new ByteArrayOutputStream();
1238: this .storeBody(new DataOutputStream(baos));
1239:
1240: dos.writeShort(this .nameIndex); // attribute_name_index;
1241: dos.writeInt(baos.size()); // attribute_length
1242: baos.writeTo(dos); // info
1243: }
1244:
1245: protected abstract void storeBody(DataOutputStream dos)
1246: throws IOException;
1247:
1248: private final short nameIndex;
1249: }
1250:
1251: /**
1252: * Load one class file attribute. The returned object will be of
1253: * {@link AttributeInfo}-derived type, depending on the attribute's name; e.g. if the
1254: * name of the attribute is "SourceFile", then the returned object will be of type
1255: * {@link SourceFileAttribute}.
1256: */
1257: private AttributeInfo loadAttribute(DataInputStream dis)
1258: throws IOException {
1259: short attributeNameIndex = dis.readShort(); // attribute_name_index
1260: int attributeLength = dis.readInt(); // attribute_length
1261:
1262: final byte[] ba = new byte[attributeLength];
1263: dis.readFully(ba);
1264: ByteArrayInputStream bais = new ByteArrayInputStream(ba);
1265: DataInputStream bdis = new DataInputStream(bais);
1266:
1267: String attributeName = this .getConstantUtf8(attributeNameIndex);
1268: AttributeInfo result;
1269: if ("ConstantValue".equals(attributeName)) {
1270: result = ConstantValueAttribute.loadBody(
1271: attributeNameIndex, bdis);
1272: } else if ("Code".equals(attributeName)) {
1273: result = CodeAttribute.loadBody(attributeNameIndex, this ,
1274: bdis);
1275: } else if ("Exceptions".equals(attributeName)) {
1276: result = ExceptionsAttribute.loadBody(attributeNameIndex,
1277: bdis);
1278: } else if ("InnerClasses".equals(attributeName)) {
1279: result = InnerClassesAttribute.loadBody(attributeNameIndex,
1280: bdis);
1281: } else if ("Synthetic".equals(attributeName)) {
1282: result = SyntheticAttribute.loadBody(attributeNameIndex,
1283: bdis);
1284: } else if ("SourceFile".equals(attributeName)) {
1285: result = SourceFileAttribute.loadBody(attributeNameIndex,
1286: bdis);
1287: } else if ("LineNumberTable".equals(attributeName)) {
1288: result = LineNumberTableAttribute.loadBody(
1289: attributeNameIndex, bdis);
1290: } else if ("LocalVariableTable".equals(attributeName)) {
1291: result = LocalVariableTableAttribute.loadBody(
1292: attributeNameIndex, bdis);
1293: } else if ("Deprecated".equals(attributeName)) {
1294: result = DeprecatedAttribute.loadBody(attributeNameIndex,
1295: bdis);
1296: } else {
1297: return new AttributeInfo(attributeNameIndex) {
1298: protected void storeBody(DataOutputStream dos)
1299: throws IOException {
1300: dos.write(ba);
1301: }
1302: };
1303: }
1304:
1305: if (bais.available() > 0)
1306: throw new ClassFormatError(
1307: (ba.length - bais.available())
1308: + " bytes of trailing garbage in body of attribute \""
1309: + attributeName + "\"");
1310:
1311: return result;
1312: }
1313:
1314: /**
1315: * Representation of a "ConstantValue" attribute (see JVMS 4.7.2).
1316: */
1317: public static class ConstantValueAttribute extends AttributeInfo {
1318: private final short constantValueIndex;
1319:
1320: ConstantValueAttribute(short attributeNameIndex,
1321: short constantValueIndex) {
1322: super (attributeNameIndex);
1323: this .constantValueIndex = constantValueIndex;
1324: }
1325:
1326: public short getConstantValueIndex() {
1327: return this .constantValueIndex;
1328: }
1329:
1330: private static AttributeInfo loadBody(short attributeNameIndex,
1331: DataInputStream dis) throws IOException {
1332: return new ConstantValueAttribute(attributeNameIndex, // attributeNameIndex
1333: dis.readShort() // constantValueIndex
1334: );
1335: }
1336:
1337: // Implement "AttributeInfo".
1338: protected void storeBody(DataOutputStream dos)
1339: throws IOException {
1340: dos.writeShort(this .constantValueIndex);
1341: }
1342: }
1343:
1344: /**
1345: * Representation of an "Exceptions" attribute (see JVMS 4.7.4).
1346: */
1347: public static class ExceptionsAttribute extends AttributeInfo {
1348: private final short[] exceptionIndexes;
1349:
1350: public ExceptionsAttribute(short attributeNameIndex,
1351: short[] exceptionIndexes) {
1352: super (attributeNameIndex);
1353: this .exceptionIndexes = exceptionIndexes;
1354: }
1355:
1356: public short[] getExceptionIndexes() {
1357: short[] eis = new short[this .exceptionIndexes.length];
1358: System.arraycopy(this .exceptionIndexes, 0, eis, 0,
1359: eis.length);
1360: return eis;
1361: }
1362:
1363: private static AttributeInfo loadBody(short attributeNameIndex,
1364: DataInputStream dis) throws IOException {
1365: return new ExceptionsAttribute(attributeNameIndex, // attributeNameIndex
1366: ClassFile.readShortArray(dis) // exceptionIndexes
1367: );
1368: }
1369:
1370: // Implement "AttributeInfo".
1371: protected void storeBody(DataOutputStream dos)
1372: throws IOException {
1373: ClassFile.storeShortArray(dos, this .exceptionIndexes);
1374: }
1375: }
1376:
1377: /**
1378: * Representation of an "InnerClasses" attribute (see JVMS 4.7.5).
1379: */
1380: public static class InnerClassesAttribute extends AttributeInfo {
1381: private final List entries; // InnerClassesAttribute.Entry
1382:
1383: InnerClassesAttribute(short attributeNameIndex) {
1384: super (attributeNameIndex);
1385: this .entries = new ArrayList();
1386: }
1387:
1388: InnerClassesAttribute(short attributeNameIndex, Entry[] entries) {
1389: super (attributeNameIndex);
1390: this .entries = new ArrayList(Arrays.asList(entries));
1391: }
1392:
1393: public List getEntries() {
1394: return this .entries;
1395: }
1396:
1397: private static AttributeInfo loadBody(short attributeNameIndex,
1398: DataInputStream dis) throws IOException {
1399: Entry[] ics = new Entry[dis.readShort()]; // number_of_classes
1400: for (short i = 0; i < ics.length; ++i) { // classes
1401: ics[i] = new InnerClassesAttribute.Entry(dis
1402: .readShort(), // innerClassInfoIndex
1403: dis.readShort(), // outerClassInfoIndex
1404: dis.readShort(), // innerNameIndex
1405: dis.readShort() // innerClassAccessFlags
1406: );
1407: }
1408: return new InnerClassesAttribute(attributeNameIndex, ics);
1409: }
1410:
1411: // Implement "AttributeInfo".
1412: protected void storeBody(DataOutputStream dos)
1413: throws IOException {
1414: dos.writeShort(this .entries.size());
1415: for (Iterator it = this .entries.iterator(); it.hasNext();) {
1416: Entry e = (Entry) it.next();
1417: dos.writeShort(e.innerClassInfoIndex);
1418: dos.writeShort(e.outerClassInfoIndex);
1419: dos.writeShort(e.innerNameIndex);
1420: dos.writeShort(e.innerClassAccessFlags);
1421: }
1422: }
1423:
1424: public static class Entry {
1425: public final short innerClassInfoIndex,
1426: outerClassInfoIndex, innerNameIndex,
1427: innerClassAccessFlags;
1428:
1429: public Entry(short innerClassInfoIndex,
1430: short outerClassInfoIndex, short innerNameIndex,
1431: short innerClassAccessFlags) {
1432: this .innerClassInfoIndex = innerClassInfoIndex;
1433: this .outerClassInfoIndex = outerClassInfoIndex;
1434: this .innerNameIndex = innerNameIndex;
1435: this .innerClassAccessFlags = innerClassAccessFlags;
1436: }
1437: }
1438: }
1439:
1440: /**
1441: * Representation of a "Synthetic" attribute (see JVMS 4.7.6).
1442: */
1443: public static class SyntheticAttribute extends AttributeInfo {
1444: SyntheticAttribute(short attributeNameIndex) {
1445: super (attributeNameIndex);
1446: }
1447:
1448: private static AttributeInfo loadBody(short attributeNameIndex,
1449: DataInputStream dis) {
1450: return new SyntheticAttribute(attributeNameIndex // attributeNameIndex
1451: );
1452: }
1453:
1454: // Implement "AttributeInfo".
1455: protected void storeBody(DataOutputStream dos)
1456: throws IOException {
1457: }
1458: }
1459:
1460: /**
1461: * Representation of a "SourceFile" attribute (see JVMS 4.7.7).
1462: */
1463: public static class SourceFileAttribute extends AttributeInfo {
1464: private final short sourceFileIndex;
1465:
1466: public SourceFileAttribute(short attributeNameIndex,
1467: short sourceFileIndex) {
1468: super (attributeNameIndex);
1469: this .sourceFileIndex = sourceFileIndex;
1470: }
1471:
1472: private static AttributeInfo loadBody(short attributeNameIndex,
1473: DataInputStream dis) throws IOException {
1474: return new SourceFileAttribute(attributeNameIndex, // attributeNameIndex
1475: dis.readShort() // sourceFileNameIndex
1476: );
1477: }
1478:
1479: // Implement "AttributeInfo".
1480: protected void storeBody(DataOutputStream dos)
1481: throws IOException {
1482: dos.writeShort(this .sourceFileIndex);
1483: }
1484: }
1485:
1486: /**
1487: * Representation of a "LineNumberTable" attribute (see JVMS 4.7.8).
1488: */
1489: public static class LineNumberTableAttribute extends AttributeInfo {
1490: private final Entry[] entries;
1491:
1492: public LineNumberTableAttribute(short attributeNameIndex,
1493: Entry[] entries) {
1494: super (attributeNameIndex);
1495: this .entries = entries;
1496: }
1497:
1498: private static AttributeInfo loadBody(short attributeNameIndex,
1499: DataInputStream dis) throws IOException {
1500: Entry[] lntes = new Entry[dis.readShort()]; // line_number_table_length
1501: for (short i = 0; i < lntes.length; ++i) { // line_number_table
1502: lntes[i] = new LineNumberTableAttribute.Entry(dis
1503: .readShort(), // startPC
1504: dis.readShort() // lineNumber
1505: );
1506: }
1507: return new LineNumberTableAttribute(attributeNameIndex,
1508: lntes);
1509: }
1510:
1511: // Implement "AttributeInfo".
1512: protected void storeBody(DataOutputStream dos)
1513: throws IOException {
1514: dos.writeShort(this .entries.length); // line_number_table_length
1515: for (int i = 0; i < this .entries.length; ++i) {
1516: dos.writeShort(this .entries[i].startPC);
1517: dos.writeShort(this .entries[i].lineNumber);
1518: }
1519: }
1520:
1521: public static class Entry {
1522: public final short startPC, lineNumber;
1523:
1524: public Entry(short startPC, short lineNumber) {
1525: this .startPC = startPC;
1526: this .lineNumber = lineNumber;
1527: }
1528: }
1529: }
1530:
1531: /**
1532: * Representation of a "LocalVariableTable" attribute (see JVMS 4.7.9).
1533: */
1534: public static class LocalVariableTableAttribute extends
1535: AttributeInfo {
1536: private final Entry[] entries;
1537:
1538: LocalVariableTableAttribute(short attributeNameIndex,
1539: Entry[] entries) {
1540: super (attributeNameIndex);
1541: this .entries = entries;
1542: }
1543:
1544: private static AttributeInfo loadBody(short attributeNameIndex,
1545: DataInputStream dis) throws IOException {
1546: short localVariableTableLength = dis.readShort();
1547: Entry[] lvtes = new Entry[localVariableTableLength]; // local_variable_table_length
1548: for (short i = 0; i < localVariableTableLength; ++i) { // local_variable_table
1549: lvtes[i] = new LocalVariableTableAttribute.Entry(dis
1550: .readShort(), // startPC
1551: dis.readShort(), // length
1552: dis.readShort(), // nameIndex
1553: dis.readShort(), // descriptorIndex
1554: dis.readShort() // index
1555: );
1556: }
1557: return new LocalVariableTableAttribute(attributeNameIndex,
1558: lvtes);
1559: }
1560:
1561: // Implement "AttributeInfo".
1562: protected void storeBody(DataOutputStream dos)
1563: throws IOException {
1564: dos.writeShort(this .entries.length); // local_variable_table_length
1565: for (int i = 0; i < this .entries.length; ++i) { // local_variable_table
1566: Entry lnte = this .entries[i];
1567: dos.writeShort(lnte.startPC); // start_pc;
1568: dos.writeShort(lnte.length); // length
1569: dos.writeShort(lnte.nameIndex); // name_index
1570: dos.writeShort(lnte.descriptorIndex); // descriptor_index
1571: dos.writeShort(lnte.index); // index
1572: }
1573: }
1574:
1575: public static class Entry {
1576: public final short startPC, length, nameIndex,
1577: descriptorIndex, index;
1578:
1579: public Entry(short startPC, short length, short nameIndex,
1580: short descriptorIndex, short index) {
1581: this .startPC = startPC;
1582: this .length = length;
1583: this .nameIndex = nameIndex;
1584: this .descriptorIndex = descriptorIndex;
1585: this .index = index;
1586: }
1587: }
1588: }
1589:
1590: /**
1591: * Representation of a "Deprecated" attribute (see JVMS 4.7.10).
1592: */
1593: public static class DeprecatedAttribute extends AttributeInfo {
1594: public DeprecatedAttribute(short attributeNameIndex) {
1595: super (attributeNameIndex);
1596: }
1597:
1598: private static AttributeInfo loadBody(short attributeNameIndex,
1599: DataInputStream dis) {
1600: return new DeprecatedAttribute(attributeNameIndex);
1601: }
1602:
1603: // Implement "AttributeInfo".
1604: protected void storeBody(DataOutputStream dos)
1605: throws IOException {
1606: }
1607: }
1608:
1609: /**
1610: * Representation of an unmodifiable "Code" attribute, as read from a class file.
1611: */
1612: private static class CodeAttribute extends AttributeInfo {
1613: private final short maxStack;
1614: private final short maxLocals;
1615: private final byte[] code;
1616: private final ExceptionTableEntry[] exceptionTableEntries;
1617: private final AttributeInfo[] attributes;
1618:
1619: private CodeAttribute(short attributeNameIndex, short maxStack,
1620: short maxLocals, byte[] code,
1621: ExceptionTableEntry[] exceptionTableEntries,
1622: AttributeInfo[] attributes) {
1623: super (attributeNameIndex);
1624: this .maxStack = maxStack;
1625: this .maxLocals = maxLocals;
1626: this .code = code;
1627: this .exceptionTableEntries = exceptionTableEntries;
1628: this .attributes = attributes;
1629: }
1630:
1631: public static AttributeInfo loadBody(short attributeNameIndex,
1632: ClassFile classFile, DataInputStream dis)
1633: throws IOException {
1634: short maxStack = dis.readShort(); // max_stack
1635: short maxLocals = dis.readShort(); // max_locals
1636: byte[] code = ClassFile.readLengthAndBytes(dis); // code_length, code
1637:
1638: ExceptionTableEntry[] etes = new ExceptionTableEntry[dis
1639: .readShort()]; // exception_table_length
1640: for (int i = 0; i < etes.length; ++i) { // exception_table
1641: etes[i] = new ExceptionTableEntry(dis.readShort(), // startPC
1642: dis.readShort(), // endPC
1643: dis.readShort(), // handlerPC
1644: dis.readShort() // catchType
1645: );
1646: }
1647:
1648: AttributeInfo[] attributes = new AttributeInfo[dis
1649: .readShort()]; // attributes_count
1650: for (int i = 0; i < attributes.length; ++i) { // attributes
1651: attributes[i] = classFile.loadAttribute(dis);
1652: }
1653:
1654: return new CodeAttribute(attributeNameIndex, // attributeNameIndex
1655: maxStack, // maxStack
1656: maxLocals, // maxLocals
1657: code, // code
1658: etes, // exceptionTableEntries
1659: attributes // attributes
1660: );
1661: }
1662:
1663: protected void storeBody(DataOutputStream dos)
1664: throws IOException {
1665: dos.writeShort(this .maxStack); // max_stack
1666: dos.writeShort(this .maxLocals); // max_locals
1667: dos.writeInt(this .code.length); // code_length
1668: dos.write(this .code); // code
1669: dos.writeShort(this .exceptionTableEntries.length); // exception_table_length
1670: for (int i = 0; i < this .exceptionTableEntries.length; ++i) { // exception_table
1671: ExceptionTableEntry ete = this .exceptionTableEntries[i];
1672: dos.writeShort(ete.startPC); // start_pc
1673: dos.writeShort(ete.endPC); // end_pc
1674: dos.writeShort(ete.handlerPC); // handler_pc
1675: dos.writeShort(ete.catchType); // catch_type
1676: }
1677: dos.writeShort(this .attributes.length); // attributes_count
1678: for (int i = 0; i < this .attributes.length; ++i) { // attributes
1679: this .attributes[i].store(dos);
1680: }
1681: }
1682:
1683: /**
1684: * Representation of an entry in the "exception_table" of a "Code" attribute (see JVMS
1685: * 4.7.3).
1686: */
1687: private static class ExceptionTableEntry {
1688: private final short startPC, endPC, handlerPC, catchType;
1689:
1690: public ExceptionTableEntry(short startPC, short endPC,
1691: short handlerPC, short catchType) {
1692: this.startPC = startPC;
1693: this.endPC = endPC;
1694: this.handlerPC = handlerPC;
1695: this.catchType = catchType;
1696: }
1697: }
1698: }
1699: }
|