/* * Janino - An embedded Java[TM] compiler * * Copyright (c) 2001-2010, Arno Unkrig * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the * following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.codehaus.janino.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UTFDataFormatException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.codehaus.janino.Descriptor; import org.codehaus.janino.JaninoRuntimeException; import org.codehaus.janino.Java; import org.codehaus.janino.Mod; /** * An object that represents the Java™ "class file" format. *
* {@link #ClassFile(InputStream)} creates a {@link ClassFile} object from the bytecode * read from the given {@link InputStream}. *
* {@link #store(OutputStream)} generates Java™ bytecode
* which is suitable for being processed by a Java™ virtual
* machine.
*/
@SuppressWarnings({ "rawtypes", "unchecked" }) public
class ClassFile {
/**
* Construct from parsed components.
* @param accessFlags as defined by {@link org.codehaus.janino.Mod}
* @param thisClassFd the field descriptor for this class
* @param superclassFd the field descriptor for the extended class (e.g. "Ljava/lang/Object;")
* @param interfaceFds the field descriptors for the implemented interfaces
*/
public
ClassFile(short accessFlags, String thisClassFd, String superclassFd, String[] interfaceFds) {
this.majorVersion = ClassFile.MAJOR_VERSION_JDK_1_1;
this.minorVersion = ClassFile.MINOR_VERSION_JDK_1_1;
this.constantPool = new ArrayList();
this.constantPool.add(null); // Add fake "0" index entry.
this.constantPoolMap = new HashMap();
this.accessFlags = accessFlags;
this.thisClass = this.addConstantClassInfo(thisClassFd);
this.superclass = this.addConstantClassInfo(superclassFd);
this.interfaces = new short[interfaceFds.length];
for (int i = 0; i < interfaceFds.length; ++i) {
this.interfaces[i] = this.addConstantClassInfo(interfaceFds[i]);
}
this.fieldInfos = new ArrayList();
this.methodInfos = new ArrayList();
this.attributes = new ArrayList();
}
/**
* Adds a "SourceFile" attribute to this class file. (Does not check whether one exists already.)
* @param sourceFileName
*/
public void
addSourceFileAttribute(String sourceFileName) {
this.attributes.add(new SourceFileAttribute(
this.addConstantUtf8Info("SourceFile"), // attributeNameIndex
this.addConstantUtf8Info(sourceFileName) // sourceFileIndex
));
}
/** Adds the "Deprecated" attribute to this class. */
public void
addDeprecatedAttribute() {
this.attributes.add(new DeprecatedAttribute(this.addConstantUtf8Info("Deprecated")));
}
/**
* Find the "InnerClasses" attribute of this class file
* @return
* If the {@link ClassFile} is created with this constructor, then most modifying operations lead to a {@link
* UnsupportedOperationException}; only fields, methods and attributes can be added.
*/
public
ClassFile(InputStream inputStream) throws IOException {
DataInputStream dis = (
inputStream instanceof DataInputStream
? (DataInputStream) inputStream :
new DataInputStream(inputStream)
);
int magic = dis.readInt(); // magic
if (magic != ClassFile.CLASS_FILE_MAGIC) throw new ClassFormatError("Invalid magic number");
this.minorVersion = dis.readShort(); // minor_version
this.majorVersion = dis.readShort(); // major_version
// Explicitly DO NOT CHECK the major and minor version of the CLASS file, because SUN increase them with each
// platform update while keeping them backwards compatible.
// if (!ClassFile.isRecognizedVersion(this.majorVersion, this.minorVersion)) {
// throw new ClassFormatError(
// "Unrecognized class file format version "
// + this.majorVersion
// + "/"
// + this.minorVersion
// );
// }
this.constantPool = new ArrayList();
this.constantPoolMap = new HashMap();
this.loadConstantPool(dis); // constant_pool_count, constant_pool
this.accessFlags = dis.readShort(); // access_flags
this.thisClass = dis.readShort(); // this_class
this.superclass = dis.readShort(); // super_class
this.interfaces = ClassFile.readShortArray(dis); // interfaces_count, interfaces
this.fieldInfos = Collections.unmodifiableList(this.loadFields(dis)); // fields_count, fields
this.methodInfos = Collections.unmodifiableList(this.loadMethods(dis)); // methods_count, methods
this.attributes = Collections.unmodifiableList(this.loadAttributes(dis)); // attributes_count, attributes
}
/** @return The fully qualified name of this class, e.g. "pkg1.pkg2.Outer$Inner" */
public String
getThisClassName() {
ConstantClassInfo cci = (ConstantClassInfo) this.getConstantPoolInfo(this.thisClass);
return cci.getName(this).replace('/', '.');
}
/**
* Sets the major and minor class file version numbers (JVMS 4.1). The class file version defaults to the JDK 1.1
* values (45.3) which execute on virtually every JVM.
*/
public void
setVersion(short majorVersion, short minorVersion) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
}
/** @return The current major class file version number */
public short
getMajorVersion() { return this.majorVersion; }
/** @return The current minor class file version number */
public short
getMinorVersion() { return this.minorVersion; }
/**
* Return the constant index number for a "CONSTANT_Class_info" structure to the class file. If the
* class hasn't been added before, add it to the constant pool. Otherwise return the constant number for
* that element of the pool.
*
* @see JVM specification,
* section 4.4.1
*/
public short
addConstantClassInfo(String typeFd) {
String s;
if (Descriptor.isClassOrInterfaceReference(typeFd)) {
s = Descriptor.toInternalForm(typeFd);
} else
if (Descriptor.isArrayReference(typeFd)) {
s = typeFd;
} else
{
throw new JaninoRuntimeException("\"" + Descriptor.toString(typeFd) + "\" is neither a class nor an array");
}
return this.addToConstantPool(new ConstantClassInfo(this.addConstantUtf8Info(s)));
}
/**
* Add a "CONSTANT_Fieldref_info" structure to the class file.
*
* @see JVM specification,
* section 4.4.2
*/
public short
addConstantFieldrefInfo(String classFd, String fieldName, String fieldFd) {
return this.addToConstantPool(new ConstantFieldrefInfo(
this.addConstantClassInfo(classFd),
this.addConstantNameAndTypeInfo(fieldName, fieldFd)
));
}
/**
* Add a "CONSTANT_Methodref_info" structure to the class file.
*
* @see JVM specification,
* section 4.4.2
*/
public short
addConstantMethodrefInfo(String classFd, String methodName, String methodMd) {
return this.addToConstantPool(new ConstantMethodrefInfo(
this.addConstantClassInfo(classFd),
this.addConstantNameAndTypeInfo(methodName, methodMd)
));
}
/**
* Add a "CONSTANT_InterfaceMethodref_info" structure to the class file.
*
* @see JVM specification,
* section 4.4.2
*/
public short
addConstantInterfaceMethodrefInfo(String classFd, String methodName, String methodMd) {
return this.addToConstantPool(new ConstantInterfaceMethodrefInfo(
this.addConstantClassInfo(classFd),
this.addConstantNameAndTypeInfo(methodName, methodMd)
));
}
/**
* Add a "CONSTANT_String_info" structure to the class file.
*
* @see JVM specification,
* section 4.4.3
*/
public short
addConstantStringInfo(String string) {
return this.addToConstantPool(new ConstantStringInfo(this.addConstantUtf8Info(string)));
}
/**
* Add a "CONSTANT_Integer_info" structure to the class file.
*
* @see JVM specification,
* section 4.4.4
*/
public short
addConstantIntegerInfo(final int value) {
return this.addToConstantPool(new ConstantIntegerInfo(value));
}
/**
* Add a "CONSTANT_Float_info" structure to the class file.
*
* @see JVM specification,
* section 4.4.4
*/
public short
addConstantFloatInfo(final float value) {
return this.addToConstantPool(new ConstantFloatInfo(value));
}
/**
* Add a "CONSTANT_Long_info" structure to the class file.
*
* @see JVM specification,
* section 4.4.5
*/
public short
addConstantLongInfo(final long value) {
return this.addToConstantPool(new ConstantLongInfo(value));
}
/**
* Add a "CONSTANT_Double_info" structure to the class file.
*
* @see JVM specification,
* section 4.4.5
*/
public short
addConstantDoubleInfo(final double value) {
return this.addToConstantPool(new ConstantDoubleInfo(value));
}
/**
* Add a "CONSTANT_NameAndType_info" structure to the class file.
*
* @see JVM specification,
* section 4.4.6
*/
private short
addConstantNameAndTypeInfo(String name, String descriptor) {
return this.addToConstantPool(new ConstantNameAndTypeInfo(
this.addConstantUtf8Info(name),
this.addConstantUtf8Info(descriptor)
));
}
/**
* Adds a "CONSTANT_Utf8_info" structure to the class file if no equal entry exists.
*
* @return The index of the already existing or newly created entry
* @see JVM
* specification, section 4.4.7
*/
public short
addConstantUtf8Info(final String s) {
return this.addToConstantPool(new ConstantUtf8Info(s));
}
/** Convenience method that adds a String, Integer, Float, Long or Double ConstantInfo. */
private short
addConstantSIFLDInfo(Object cv) { // SUPPRESS CHECKSTYLE AbbreviationAsWord
if (cv instanceof String) {
return this.addConstantStringInfo((String) cv);
} else
if (cv instanceof Byte || cv instanceof Short || cv instanceof Integer) {
return this.addConstantIntegerInfo(((Number) cv).intValue());
} else
if (cv instanceof Boolean) {
return this.addConstantIntegerInfo(((Boolean) cv).booleanValue() ? 1 : 0);
} else
if (cv instanceof Character) {
return this.addConstantIntegerInfo(((Character) cv).charValue());
} else
if (cv instanceof Float) {
return this.addConstantFloatInfo(((Float) cv).floatValue());
} else
if (cv instanceof Long) {
return this.addConstantLongInfo(((Long) cv).longValue());
} else
if (cv instanceof Double) {
return this.addConstantDoubleInfo(((Double) cv).doubleValue());
} else
{
throw new JaninoRuntimeException("Unexpected constant value type \"" + cv.getClass().getName() + "\"");
}
}
/**
* Adds an entry to the constant pool and returns its index, or, if an equal entry already exists in the constant
* pool, returns the index of that entry.
*/
private short
addToConstantPool(ConstantPoolInfo cpi) {
Short index = (Short) this.constantPoolMap.get(cpi);
if (index != null) return index.shortValue();
int res = this.constantPool.size();
if (res > 0xFFFF) {
throw new JaninoRuntimeException("Constant pool has grown past JVM limit of 0xFFFF");
}
this.constantPool.add(cpi);
if (cpi.isWide()) this.constantPool.add(null);
this.constantPoolMap.put(cpi, new Short((short) res));
return (short) res;
}
/**
* Creates a {@link FieldInfo} and adds it to this class. The return value can be used e.g. to add attributes
* ({@code Deprecated}, ...) to the field.
*/
public FieldInfo
addFieldInfo(
Java.Modifiers modifiers,
String fieldName,
String fieldTypeFd,
Object optionalConstantValue
) {
List
* Notice that if an {@link IOException} is thrown, the class file is
* probably written incompletely and thus invalid. The calling method must take
* care of this situation, e.g. by closing the output stream and then deleting the
* file.
* @param os
* @throws IOException
*/
public void
store(OutputStream os) throws IOException {
DataOutputStream dos = os instanceof DataOutputStream ? (DataOutputStream) os : new DataOutputStream(os);
dos.writeInt(ClassFile.CLASS_FILE_MAGIC); // magic
dos.writeShort(this.minorVersion); // minor_version
dos.writeShort(this.majorVersion); // major_version
ClassFile.storeConstantPool(dos, this.constantPool); // constant_pool_count, constant_pool
dos.writeShort(this.accessFlags); // access_flags
dos.writeShort(this.thisClass); // this_class
dos.writeShort(this.superclass); // super_class
ClassFile.storeShortArray(dos, this.interfaces); // interfaces_count, interfaces
ClassFile.storeFields(dos, this.fieldInfos); // fields_count, fields
ClassFile.storeMethods(dos, this.methodInfos); // methods_count, methods
//TODO: need annotation(s) for classes?
ClassFile.storeAttributes(dos, this.attributes, null); // attributes_count, attributes
}
/** u2 constant_pool_count, constant_pool[constant_pool_count - 1] */
private static void
storeConstantPool(DataOutputStream dos, List
* Notice that member types are declared inside a different type, so the relevant source file
* is that of the outermost declaring class.
*
* @param className Fully qualified class name, e.g. "pkg1.pkg2.Outer$Inner"
* @return the name of the resource, e.g. "pkg1/pkg2/Outer.java"
*/
public static String
getSourceResourceName(String className) {
// Strip nested type suffixes.
{
int idx = className.lastIndexOf('.') + 1;
idx = className.indexOf('$', idx);
if (idx != -1) className = className.substring(0, idx);
}
return className.replace('.', '/') + ".java";
}
/**
* Construct the name of a resource that could contain the class file of the
* class with the given name.
*
* @param className Fully qualified class name, e.g. "pkg1.pkg2.Outer$Inner"
* @return the name of the resource, e.g. "pkg1/pkg2/Outer$Inner.class"
*/
public static String
getClassFileResourceName(String className) { return className.replace('.', '/') + ".class"; }
/** Return the byte code of this {@link ClassFile} as a byte array. */
public byte[]
toByteArray() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
this.store(baos);
} catch (IOException ex) {
// ByteArrayOutputStream should never throw IOExceptions.
throw new JaninoRuntimeException(ex.toString(), ex);
}
return baos.toByteArray();
}
private static final int CLASS_FILE_MAGIC = 0xcafebabe;
// CHECKSTYLE JavadocVariable:OFF
public static final short MAJOR_VERSION_JDK_1_1 = 45;
public static final short MINOR_VERSION_JDK_1_1 = 3;
public static final short MAJOR_VERSION_JDK_1_2 = 46;
public static final short MINOR_VERSION_JDK_1_2 = 0;
public static final short MAJOR_VERSION_JDK_1_3 = 47;
public static final short MINOR_VERSION_JDK_1_3 = 0;
public static final short MAJOR_VERSION_JDK_1_4 = 48;
public static final short MINOR_VERSION_JDK_1_4 = 0;
public static final short MAJOR_VERSION_JDK_1_5 = 49;
public static final short MINOR_VERSION_JDK_1_5 = 0;
// CHECKSTYLE JavadocVariable:ON
private short majorVersion;
private short minorVersion;
private final List
* See JVMS7 4.4.1 and following
*/
protected abstract void store(DataOutputStream dos) throws IOException;
/** @return Whether this CP entry is "wide" in the sense of JVMS7 4.4.5 */
protected abstract boolean isWide();
private static ConstantPoolInfo
loadConstantPoolInfo(DataInputStream dis) throws IOException {
byte tag = dis.readByte();
//System.out.println("tag=" + tag);
switch (tag) {
case 7:
return new ConstantClassInfo(dis.readShort());
case 9:
return new ConstantFieldrefInfo(dis.readShort(), dis.readShort());
case 10:
return new ConstantMethodrefInfo(dis.readShort(), dis.readShort());
case 11:
return new ConstantInterfaceMethodrefInfo(dis.readShort(), dis.readShort());
case 8:
return new ConstantStringInfo(dis.readShort());
case 3:
return new ConstantIntegerInfo(dis.readInt());
case 4:
return new ConstantFloatInfo(dis.readFloat());
case 5:
return new ConstantLongInfo(dis.readLong());
case 6:
return new ConstantDoubleInfo(dis.readDouble());
case 12:
return new ConstantNameAndTypeInfo(dis.readShort(), dis.readShort());
case 1:
return new ConstantUtf8Info(dis.readUTF());
default:
throw new ClassFormatError("Invalid constant pool tag " + tag);
}
}
}
/**
* Intermediate base class for constant pool table entry types that have 'value' semantics: Double, Float,
* Integer, Long, String
*/
public abstract static
class ConstantValuePoolInfo extends ConstantPoolInfo {
/**
* @return The value that this constant pool table entry represents; the actual type is {@link Double}, {@link
* Float}, {@link Integer}, {@link Long} or {@link String}
*/
public abstract Object getValue(ClassFile classFile);
}
/** See JVMS7 4.4.1. */
public static
class ConstantClassInfo extends ConstantPoolInfo {
private final short nameIndex;
public ConstantClassInfo(short nameIndex) { this.nameIndex = nameIndex; }
/** @return The class's or interface's name in "internal form" (JVMS7 4.2.1) */
public String
getName(ClassFile classFile) { return classFile.getConstantUtf8(this.nameIndex); }
// Implement ConstantPoolInfo.
@Override public boolean
isWide() { return false; }
@Override public void
store(DataOutputStream dos) throws IOException {
dos.writeByte(7);
dos.writeShort(this.nameIndex);
}
@Override public boolean
equals(Object o) {
return o instanceof ConstantClassInfo && ((ConstantClassInfo) o).nameIndex == this.nameIndex;
}
@Override public int
hashCode() { return this.nameIndex; }
}
/** See JVMS7 4.4.2. */
public static
class ConstantFieldrefInfo extends ConstantPoolInfo {
private final short classIndex;
private final short nameAndTypeIndex;
public
ConstantFieldrefInfo(short classIndex, short nameAndTypeIndex) {
this.classIndex = classIndex;
this.nameAndTypeIndex = nameAndTypeIndex;
}
/** @return The {@link ConstantNameAndTypeInfo} of this {@link ConstantFieldrefInfo} */
public ConstantNameAndTypeInfo
getNameAndType(ClassFile classFile) {
return (ClassFile.ConstantNameAndTypeInfo) classFile.getConstantPoolInfo(this.nameAndTypeIndex);
}
// Implement ConstantPoolInfo.
@Override public boolean
isWide() { return false; }
@Override public void
store(DataOutputStream dos) throws IOException {
dos.writeByte(9);
dos.writeShort(this.classIndex);
dos.writeShort(this.nameAndTypeIndex);
}
@Override public boolean
equals(Object o) {
return (
o instanceof ConstantFieldrefInfo
&& ((ConstantFieldrefInfo) o).classIndex == this.classIndex
&& ((ConstantFieldrefInfo) o).nameAndTypeIndex == this.nameAndTypeIndex
);
}
@Override public int
hashCode() { return this.classIndex + (this.nameAndTypeIndex << 16); }
}
/** See JVMS7 4.4.2. */
public static
class ConstantMethodrefInfo extends ConstantPoolInfo {
private final short classIndex;
private final short nameAndTypeIndex;
public
ConstantMethodrefInfo(short classIndex, short nameAndTypeIndex) {
this.classIndex = classIndex;
this.nameAndTypeIndex = nameAndTypeIndex;
}
/** @return The {@link ConstantNameAndTypeInfo} of this {@link ConstantMethodrefInfo} */
public ConstantNameAndTypeInfo
getNameAndType(ClassFile classFile) {
return (ClassFile.ConstantNameAndTypeInfo) classFile.getConstantPoolInfo(this.nameAndTypeIndex);
}
// Implement ConstantPoolInfo.
@Override public boolean
isWide() { return false; }
@Override public void
store(DataOutputStream dos) throws IOException {
dos.writeByte(10);
dos.writeShort(this.classIndex);
dos.writeShort(this.nameAndTypeIndex);
}
@Override public boolean
equals(Object o) {
return (
o instanceof ConstantMethodrefInfo
&& ((ConstantMethodrefInfo) o).classIndex == this.classIndex
&& ((ConstantMethodrefInfo) o).nameAndTypeIndex == this.nameAndTypeIndex
);
}
@Override public int
hashCode() { return this.classIndex + (this.nameAndTypeIndex << 16); }
}
/** See JVMS7 4.4.2. */
public static
class ConstantInterfaceMethodrefInfo extends ConstantPoolInfo {
private final short classIndex;
private final short nameAndTypeIndex;
public
ConstantInterfaceMethodrefInfo(short classIndex, short nameAndTypeIndex) {
this.classIndex = classIndex;
this.nameAndTypeIndex = nameAndTypeIndex;
}
/** @return The {@link ConstantNameAndTypeInfo} of this {@link ConstantInterfaceMethodrefInfo} */
public ConstantNameAndTypeInfo
getNameAndType(ClassFile classFile) {
return (ClassFile.ConstantNameAndTypeInfo) classFile.getConstantPoolInfo(this.nameAndTypeIndex);
}
// Implement ConstantPoolInfo.
@Override public boolean
isWide() { return false; }
@Override public void
store(DataOutputStream dos) throws IOException {
dos.writeByte(11);
dos.writeShort(this.classIndex);
dos.writeShort(this.nameAndTypeIndex);
}
@Override public boolean
equals(Object o) {
return (
o instanceof ConstantInterfaceMethodrefInfo
&& ((ConstantInterfaceMethodrefInfo) o).classIndex == this.classIndex
&& ((ConstantInterfaceMethodrefInfo) o).nameAndTypeIndex == this.nameAndTypeIndex
);
}
@Override public int
hashCode() { return this.classIndex + (this.nameAndTypeIndex << 16); }
}
/** See JVMS7 4.4.3. */
static
class ConstantStringInfo extends ConstantValuePoolInfo {
private final short stringIndex;
public
ConstantStringInfo(short stringIndex) { this.stringIndex = stringIndex; }
// Implement ConstantValuePoolInfo.
@Override public Object
getValue(ClassFile classFile) { return classFile.getConstantUtf8(this.stringIndex); }
// Implement ConstantPoolInfo.
@Override public boolean
isWide() { return false; }
@Override public void
store(DataOutputStream dos) throws IOException {
dos.writeByte(8);
dos.writeShort(this.stringIndex);
}
@Override public boolean
equals(Object o) {
return o instanceof ConstantStringInfo && ((ConstantStringInfo) o).stringIndex == this.stringIndex;
}
@Override public int
hashCode() { return this.stringIndex; }
}
/** See JVMS7 4.4.4. */
private static
class ConstantIntegerInfo extends ConstantValuePoolInfo {
private final int value;
public
ConstantIntegerInfo(int value) { this.value = value; }
// Implement ConstantValuePoolInfo.
@Override public Object
getValue(ClassFile classFile) { return new Integer(this.value); }
// Implement ConstantPoolInfo.
@Override public boolean
isWide() { return false; }
@Override public void
store(DataOutputStream dos) throws IOException {
dos.writeByte(3);
dos.writeInt(this.value);
}
@Override public boolean
equals(Object o) { return o instanceof ConstantIntegerInfo && ((ConstantIntegerInfo) o).value == this.value; }
@Override public int
hashCode() { return this.value; }
}
/** See JVMS7 4.4.4. */
private static
class ConstantFloatInfo extends ConstantValuePoolInfo {
private final float value;
public
ConstantFloatInfo(float value) { this.value = value; }
// Implement ConstantValuePoolInfo.
@Override public Object getValue(ClassFile classFile) { return new Float(this.value); }
// Implement ConstantPoolInfo.
@Override public boolean
isWide() { return false; }
@Override public void
store(DataOutputStream dos) throws IOException {
dos.writeByte(4);
dos.writeFloat(this.value);
}
@Override public boolean
equals(Object o) {
return o instanceof ConstantFloatInfo && ((ConstantFloatInfo) o).value == this.value;
}
@Override public int
hashCode() { return Float.floatToIntBits(this.value); }
}
/** See JVMS7 4.4.5. */
private static
class ConstantLongInfo extends ConstantValuePoolInfo {
private final long value;
public
ConstantLongInfo(long value) { this.value = value; }
// Implement ConstantValuePoolInfo.
@Override public Object getValue(ClassFile classFile) { return this.value; }
// Implement ConstantPoolInfo.
@Override public boolean
isWide() { return true; }
@Override public void
store(DataOutputStream dos) throws IOException {
dos.writeByte(5);
dos.writeLong(this.value);
}
@Override public boolean
equals(Object o) {
return o instanceof ConstantLongInfo && ((ConstantLongInfo) o).value == this.value;
}
@Override public int
hashCode() { return (int) this.value ^ (int) (this.value >> 32); }
}
/** See JVMS7 4.4.5. */
private static
class ConstantDoubleInfo extends ConstantValuePoolInfo {
private final double value;
public
ConstantDoubleInfo(double value) { this.value = value; }
// Implement ConstantValuePoolInfo.
@Override public Object getValue(ClassFile classFile) { return new Double(this.value); }
// Implement ConstantPoolInfo.
@Override public boolean
isWide() { return true; }
@Override public void
store(DataOutputStream dos) throws IOException {
dos.writeByte(6);
dos.writeDouble(this.value);
}
@Override public boolean
equals(Object o) { return o instanceof ConstantDoubleInfo && ((ConstantDoubleInfo) o).value == this.value; }
@Override public int
hashCode() {
long bits = Double.doubleToLongBits(this.value);
return (int) bits ^ (int) (bits >> 32);
}
}
/** See JVMS7 4.4.6. */
public static
class ConstantNameAndTypeInfo extends ConstantPoolInfo {
private final short nameIndex;
private final short descriptorIndex;
public
ConstantNameAndTypeInfo(short nameIndex, short descriptorIndex) {
this.nameIndex = nameIndex;
this.descriptorIndex = descriptorIndex;
}
/** @return The (field or method) descriptor related to the name */
public String
getDescriptor(ClassFile classFile) {
return classFile.getConstantUtf8(this.descriptorIndex);
}
// Implement ConstantPoolInfo.
@Override public boolean
isWide() { return false; }
@Override public void
store(DataOutputStream dos) throws IOException {
dos.writeByte(12);
dos.writeShort(this.nameIndex);
dos.writeShort(this.descriptorIndex);
}
@Override public boolean
equals(Object o) {
return (
o instanceof ConstantNameAndTypeInfo
&& ((ConstantNameAndTypeInfo) o).nameIndex == this.nameIndex
&& ((ConstantNameAndTypeInfo) o).descriptorIndex == this.descriptorIndex
);
}
@Override public int
hashCode() { return this.nameIndex + (this.descriptorIndex << 16); }
}
/** See JVMS7 4.4.7. */
public static
class ConstantUtf8Info extends ConstantPoolInfo {
private final String s;
public
ConstantUtf8Info(String s) {
if (s == null) throw new JaninoRuntimeException();
this.s = s;
}
/** @return The string contained in this {@link ConstantUtf8Info} */
public String
getString() { return this.s; }
// Implement ConstantPoolInfo.
@Override public boolean
isWide() { return false; }
@Override public void
store(DataOutputStream dos) throws IOException {
dos.writeByte(1);
try {
dos.writeUTF(this.s);
} catch (UTFDataFormatException e) {
// SUPPRESS CHECKSTYLE AvoidHidingCause
throw new ClassFormatError("String constant too long to store in class file");
}
}
@Override public boolean
equals(Object o) {
return o instanceof ConstantUtf8Info && ((ConstantUtf8Info) o).s.equals(this.s);
}
@Override public int
hashCode() { return this.s.hashCode(); }
}
/** Representation of a "method_info" structure, as defined by JVMS7 4.6. */
public
class MethodInfo {
private final Java.Modifiers modifiers;
private final short nameIndex;
private final short descriptorIndex;
private final Listnull
if this class has no "InnerClasses" attribute
*/
public InnerClassesAttribute
getInnerClassesAttribute() {
Short ni = (Short) this.constantPoolMap.get(new ConstantUtf8Info("InnerClasses"));
if (ni == null) return null;
for (IteratorCONSTANT_Utf8_info
in the constant pool
* @return The string represented by the structure
*/
public String
getConstantUtf8(short index) {
ConstantUtf8Info cui = (ConstantUtf8Info) this.getConstantPoolInfo(index);
return cui.s;
}
/** u4 length, u1[length] */
private static byte[]
readLengthAndBytes(DataInputStream dis) throws IOException {
byte[] ba = new byte[dis.readInt()];
dis.readFully(ba);
return ba;
}
/** u2 length, u2[length] */
private static short[]
readShortArray(DataInputStream dis) throws IOException {
short count = dis.readShort();
short[] result = new short[count];
for (int i = 0; i < count; ++i) result[i] = dis.readShort();
return result;
}
/** u2 constant_pool_count, constant_pool[constant_pool_count] */
private void
loadConstantPool(DataInputStream dis) throws IOException {
this.constantPool.clear();
this.constantPoolMap.clear();
short constantPoolCount = dis.readShort(); // constant_pool_count
this.constantPool.add(null);
for (short i = 1; i < constantPoolCount; ++i) {
ConstantPoolInfo cpi = ConstantPoolInfo.loadConstantPoolInfo(dis);
this.constantPool.add(cpi);
this.constantPoolMap.put(cpi, new Short(i));
if (cpi.isWide()) {
this.constantPool.add(null);
++i;
}
}
}
/** u2 fields_count, fields[fields_count] */
private List