1839 lines
68 KiB
Java
1839 lines
68 KiB
Java
|
|
/*
|
|
* 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.
|
|
* <p>
|
|
* {@link #ClassFile(InputStream)} creates a {@link ClassFile} object from the bytecode
|
|
* read from the given {@link InputStream}.
|
|
* <p>
|
|
* {@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 <code>null</code> 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 (Iterator<AttributeInfo> it = this.attributes.iterator(); it.hasNext();) {
|
|
AttributeInfo ai = (AttributeInfo) it.next();
|
|
if (ai.nameIndex == ni.shortValue() && ai instanceof InnerClassesAttribute) {
|
|
return (InnerClassesAttribute) ai;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Create an "InnerClasses" attribute if it does not exist, then add the given entry
|
|
* to the "InnerClasses" attribute.
|
|
* @param e
|
|
*/
|
|
public void
|
|
addInnerClassesAttributeEntry(InnerClassesAttribute.Entry e) {
|
|
InnerClassesAttribute ica = this.getInnerClassesAttribute();
|
|
if (ica == null) {
|
|
ica = new InnerClassesAttribute(this.addConstantUtf8Info("InnerClasses"));
|
|
this.attributes.add(ica);
|
|
}
|
|
ica.getEntries().add(e);
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Read "class file" data from a {@link InputStream} and construct a {@link ClassFile} object from it.
|
|
* <p>
|
|
* 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 <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#1221">JVM specification,
|
|
* section 4.4.1</a>
|
|
*/
|
|
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 <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#42041">JVM specification,
|
|
* section 4.4.2</a>
|
|
*/
|
|
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 <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#42041">JVM specification,
|
|
* section 4.4.2</a>
|
|
*/
|
|
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 <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#42041">JVM specification,
|
|
* section 4.4.2</a>
|
|
*/
|
|
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 <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#29297">JVM specification,
|
|
* section 4.4.3</a>
|
|
*/
|
|
public short
|
|
addConstantStringInfo(String string) {
|
|
return this.addToConstantPool(new ConstantStringInfo(this.addConstantUtf8Info(string)));
|
|
}
|
|
|
|
/**
|
|
* Add a "CONSTANT_Integer_info" structure to the class file.
|
|
*
|
|
* @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#21942">JVM specification,
|
|
* section 4.4.4</a>
|
|
*/
|
|
public short
|
|
addConstantIntegerInfo(final int value) {
|
|
return this.addToConstantPool(new ConstantIntegerInfo(value));
|
|
}
|
|
|
|
/**
|
|
* Add a "CONSTANT_Float_info" structure to the class file.
|
|
*
|
|
* @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#21942">JVM specification,
|
|
* section 4.4.4</a>
|
|
*/
|
|
public short
|
|
addConstantFloatInfo(final float value) {
|
|
return this.addToConstantPool(new ConstantFloatInfo(value));
|
|
}
|
|
|
|
/**
|
|
* Add a "CONSTANT_Long_info" structure to the class file.
|
|
*
|
|
* @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#1348">JVM specification,
|
|
* section 4.4.5</a>
|
|
*/
|
|
public short
|
|
addConstantLongInfo(final long value) {
|
|
return this.addToConstantPool(new ConstantLongInfo(value));
|
|
}
|
|
|
|
/**
|
|
* Add a "CONSTANT_Double_info" structure to the class file.
|
|
*
|
|
* @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#1348">JVM specification,
|
|
* section 4.4.5</a>
|
|
*/
|
|
public short
|
|
addConstantDoubleInfo(final double value) {
|
|
return this.addToConstantPool(new ConstantDoubleInfo(value));
|
|
}
|
|
|
|
/**
|
|
* Add a "CONSTANT_NameAndType_info" structure to the class file.
|
|
*
|
|
* @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#1327">JVM specification,
|
|
* section 4.4.6</a>
|
|
*/
|
|
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 <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#7963">JVM
|
|
* specification, section 4.4.7</a>
|
|
*/
|
|
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<AttributeInfo> attributes = new ArrayList();
|
|
if (optionalConstantValue != null) {
|
|
attributes.add(new ConstantValueAttribute(
|
|
this.addConstantUtf8Info("ConstantValue"),
|
|
this.addConstantSIFLDInfo(optionalConstantValue)
|
|
));
|
|
}
|
|
FieldInfo fi = new FieldInfo(
|
|
modifiers, // modifierAndAnnotations
|
|
this.addConstantUtf8Info(fieldName), // nameIndex
|
|
this.addConstantUtf8Info(fieldTypeFd), // descriptorIndex
|
|
attributes // attributes
|
|
);
|
|
this.fieldInfos.add(fi);
|
|
return fi;
|
|
}
|
|
|
|
/**
|
|
* Creates a {@link MethodInfo} and adds it to this class. The return value can be used e.g. to add attributes
|
|
* ({@code Code}, {@code Deprecated}, {@code Exceptions}, ...) to the method.
|
|
*/
|
|
public MethodInfo
|
|
addMethodInfo(Java.Modifiers modifiers, String methodName, String methodMd) {
|
|
MethodInfo mi = new MethodInfo(
|
|
modifiers, // modifiers
|
|
this.addConstantUtf8Info(methodName), // nameIndex
|
|
this.addConstantUtf8Info(methodMd), // desriptorIndex
|
|
new ArrayList() // attributes
|
|
);
|
|
this.methodInfos.add(mi);
|
|
return mi;
|
|
}
|
|
|
|
/** @return The (read-only) constant pool entry indexed by {@code index} */
|
|
public ConstantPoolInfo
|
|
getConstantPoolInfo(short index) { return (ConstantPoolInfo) this.constantPool.get(0xffff & index); }
|
|
|
|
/** @return The size of the constant pool */
|
|
public int
|
|
getConstantPoolSize() { return this.constantPool.size(); }
|
|
|
|
/**
|
|
* @param index Index to a <code>CONSTANT_Utf8_info</code> 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<FieldInfo>
|
|
loadFields(DataInputStream dis) throws IOException {
|
|
short fieldsCount = dis.readShort();
|
|
List<FieldInfo> fields = new ArrayList(fieldsCount);
|
|
for (int i = 0; i < fieldsCount; ++i) {
|
|
short mods = dis.readShort();
|
|
short nameIndex = dis.readShort();
|
|
short descIndex = dis.readShort();
|
|
List<AttributeInfo> atts = this.loadAttributes(dis);
|
|
fields.add(new FieldInfo(
|
|
ClassFile.buildModsAndAnns(mods, atts),
|
|
nameIndex,
|
|
descIndex,
|
|
atts
|
|
));
|
|
}
|
|
return fields;
|
|
}
|
|
|
|
/**
|
|
* Extract annotations from list of attributes
|
|
* and build a ModifiersAndAnnotations object
|
|
* @param mods
|
|
* @param attributes
|
|
*/
|
|
private static Java.Modifiers
|
|
buildModsAndAnns(short mods, List attributes) {
|
|
//TODO: extract the annotations
|
|
return new Java.Modifiers(mods);
|
|
}
|
|
|
|
/** u2 methods_count, methods[methods_count] */
|
|
private List<MethodInfo>
|
|
loadMethods(DataInputStream dis) throws IOException {
|
|
short methodsCount = dis.readShort();
|
|
List<MethodInfo> methods = new ArrayList(methodsCount);
|
|
for (int i = 0; i < methodsCount; ++i) methods.add(this.loadMethodInfo(dis));
|
|
return methods;
|
|
}
|
|
|
|
/** u2 attributes_count, attributes[attributes_count] */
|
|
private List<AttributeInfo>
|
|
loadAttributes(DataInputStream dis) throws IOException {
|
|
short attributesCount = dis.readShort();
|
|
List<AttributeInfo> attributes = new ArrayList(attributesCount);
|
|
for (int i = 0; i < attributesCount; ++i) attributes.add(this.loadAttribute(dis));
|
|
return attributes;
|
|
}
|
|
|
|
/**
|
|
* Write {@link ClassFile} to an {@link OutputStream}, in "class file" format.
|
|
* <p>
|
|
* 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<ConstantPoolInfo> constantPool) throws IOException {
|
|
dos.writeShort(constantPool.size());
|
|
for (int i = 1; i < constantPool.size(); ++i) {
|
|
ConstantPoolInfo cpi = (ConstantPoolInfo) constantPool.get(i);
|
|
if (cpi == null) continue; // (Double or Long CPI.)
|
|
cpi.store(dos);
|
|
}
|
|
}
|
|
|
|
/** u2 count, u2[count] */
|
|
private static void
|
|
storeShortArray(DataOutputStream dos, short[] sa) throws IOException {
|
|
dos.writeShort(sa.length);
|
|
for (short s : sa) dos.writeShort(s);
|
|
}
|
|
|
|
/** u2 fields_count, fields[fields_count] */
|
|
private static void
|
|
storeFields(DataOutputStream dos, List<FieldInfo> fieldInfos) throws IOException {
|
|
dos.writeShort(fieldInfos.size());
|
|
for (FieldInfo fieldInfo : fieldInfos) fieldInfo.store(dos);
|
|
}
|
|
|
|
/** u2 methods_count, methods[methods_count] */
|
|
private static void
|
|
storeMethods(DataOutputStream dos, List<MethodInfo> methodInfos) throws IOException {
|
|
dos.writeShort(methodInfos.size());
|
|
for (MethodInfo methodInfo : methodInfos) methodInfo.store(dos);
|
|
}
|
|
|
|
/** u2 attributes_count, attributes[attributes_count] */
|
|
private static void
|
|
storeAttributes(DataOutputStream dos, List<AttributeInfo> attributeInfos, Java.Annotation[] annotations)
|
|
throws IOException {
|
|
//TODO: write annotation attributes
|
|
dos.writeShort(attributeInfos.size());
|
|
for (AttributeInfo attributeInfo : attributeInfos) attributeInfo.store(dos);
|
|
}
|
|
|
|
/**
|
|
* Construct the name of a resource that could contain the source code of
|
|
* the class with the given name.
|
|
* <p>
|
|
* 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<ConstantPoolInfo> constantPool;
|
|
|
|
/**
|
|
* The access flags of the class.
|
|
*
|
|
* @see Mod#PUBLIC and consorts
|
|
*/
|
|
public final short accessFlags;
|
|
|
|
/** The constant pool index of the {@link ConstantClassInfo} that describes THIS class. */
|
|
public final short thisClass;
|
|
|
|
/**
|
|
* The constant pool index of the {@link ConstantClassInfo} that describes the superclass of THIS class. Zero
|
|
* for class {@link Object}, {@link Object} for interfaces.
|
|
*/
|
|
public final short superclass;
|
|
|
|
/**
|
|
* The constant pool indexes of {@link ConstantClassInfo} which describes the interfaces that this class
|
|
* implements, resp. that this interface extends.
|
|
*/
|
|
public final short[] interfaces;
|
|
|
|
/** The {@link FieldInfo}s of the field members of this class or interface. */
|
|
public final List<FieldInfo> fieldInfos;
|
|
|
|
/** The {@link MethodInfo}s of the methods of this class or interface. */
|
|
public final List<MethodInfo> methodInfos;
|
|
|
|
/** The {@link AttributeInfo}s of the attributes of this class or interface. */
|
|
private final List<AttributeInfo> attributes;
|
|
|
|
// Convenience.
|
|
private final Map<ConstantPoolInfo, Short> constantPoolMap;
|
|
|
|
/** Base for various the constant pool table entry types. */
|
|
public abstract static
|
|
class ConstantPoolInfo {
|
|
|
|
/**
|
|
* Stores this CP entry into the given {@link DataOutputStream}.
|
|
* <p>
|
|
* 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 List<AttributeInfo> attributes;
|
|
|
|
/** Initializes the "method_info" structure. */
|
|
public
|
|
MethodInfo(
|
|
Java.Modifiers modifiers,
|
|
short nameIndex,
|
|
short descriptorIndex,
|
|
List<AttributeInfo> attributes
|
|
) {
|
|
this.modifiers = modifiers;
|
|
this.nameIndex = nameIndex;
|
|
this.descriptorIndex = descriptorIndex;
|
|
this.attributes = attributes;
|
|
}
|
|
|
|
/** @return The {@link ClassFile} that contains this {@link MethodInfo} object */
|
|
public ClassFile
|
|
getClassFile() { return ClassFile.this; }
|
|
|
|
/** @return The modifier flags of this method; or'ed values are the constants declared in {@link Mod}. */
|
|
public short getModifierFlags() { return this.modifiers.flags; }
|
|
|
|
/** @return The annotations of this method */
|
|
public Java.Annotation[] getAnnotations() { return this.modifiers.annotations; }
|
|
|
|
/** @return The method's name */
|
|
public String
|
|
getName() { return ClassFile.this.getConstantUtf8(this.nameIndex); }
|
|
|
|
/** @return The method descriptor describing this method */
|
|
public String
|
|
getDescriptor() { return ClassFile.this.getConstantUtf8(this.descriptorIndex); }
|
|
|
|
/** @return The attributes of this method */
|
|
public AttributeInfo[]
|
|
getAttributes() {
|
|
return (AttributeInfo[]) this.attributes.toArray(new AttributeInfo[this.attributes.size()]);
|
|
}
|
|
|
|
/** Adds the given {@code attribute} to this method. */
|
|
public void
|
|
addAttribute(AttributeInfo attribute) { this.attributes.add(attribute); }
|
|
|
|
/** Writes this object to a {@link DataOutputStream}, in the format described inJVMS7 4.6. */
|
|
public void
|
|
store(DataOutputStream dos) throws IOException {
|
|
dos.writeShort(this.modifiers.flags); // access_flags
|
|
dos.writeShort(this.nameIndex); // name_index
|
|
dos.writeShort(this.descriptorIndex); // descriptor_index
|
|
ClassFile.storeAttributes( // attributes_count, attributes
|
|
dos,
|
|
this.attributes,
|
|
this.modifiers.annotations
|
|
);
|
|
}
|
|
}
|
|
|
|
private MethodInfo
|
|
loadMethodInfo(DataInputStream dis) throws IOException {
|
|
short mods = dis.readShort();
|
|
short nameIndex = dis.readShort();
|
|
short descIndex = dis.readShort();
|
|
List<AttributeInfo> attributes = this.loadAttributes(dis);
|
|
return new MethodInfo(
|
|
ClassFile.buildModsAndAnns(mods, attributes),
|
|
nameIndex,
|
|
descIndex,
|
|
attributes
|
|
);
|
|
}
|
|
|
|
/** Representation of a "method_info" structure, as defined by JVMS7 4.5. */
|
|
public static
|
|
class FieldInfo {
|
|
|
|
public
|
|
FieldInfo(
|
|
Java.Modifiers modifiers,
|
|
short nameIndex,
|
|
short descriptorIndex,
|
|
List<AttributeInfo> attributes
|
|
) {
|
|
this.modifiers = modifiers;
|
|
this.nameIndex = nameIndex;
|
|
this.descriptorIndex = descriptorIndex;
|
|
this.attributes = attributes;
|
|
}
|
|
|
|
/** @return The modifier flags of the field; or'ed values are the constants declared in {@link Mod} */
|
|
public short getModifierFlags() { return this.modifiers.flags; }
|
|
|
|
/** @return The annotations of this field */
|
|
public Java.Annotation[] getAnnotations() { return this.modifiers.annotations; }
|
|
|
|
/** @return The field's name */
|
|
public String
|
|
getName(ClassFile classFile) { return classFile.getConstantUtf8(this.nameIndex); }
|
|
|
|
/** @return The field descriptor describing this field */
|
|
public String
|
|
getDescriptor(ClassFile classFile) { return classFile.getConstantUtf8(this.descriptorIndex); }
|
|
|
|
/** @return The attributes of this field */
|
|
public AttributeInfo[]
|
|
getAttributes() {
|
|
return (AttributeInfo[]) this.attributes.toArray(new AttributeInfo[this.attributes.size()]);
|
|
}
|
|
|
|
/** Adds the given {@code attribute} to this field. */
|
|
public void
|
|
addAttribute(AttributeInfo attribute) { this.attributes.add(attribute); }
|
|
|
|
/** Writes this object to a {@link DataOutputStream}, in the format described inJVMS7 4.5. */
|
|
public void
|
|
store(DataOutputStream dos) throws IOException {
|
|
dos.writeShort(this.modifiers.flags); // access_flags
|
|
dos.writeShort(this.nameIndex); // name_index
|
|
dos.writeShort(this.descriptorIndex); // descriptor_index
|
|
ClassFile.storeAttributes( // attibutes_count, attributes
|
|
dos,
|
|
this.attributes,
|
|
this.modifiers.annotations
|
|
);
|
|
}
|
|
|
|
private final Java.Modifiers modifiers;
|
|
private final short nameIndex;
|
|
private final short descriptorIndex;
|
|
private final List<AttributeInfo> attributes;
|
|
}
|
|
|
|
/** Representation of a class file attribute (see JVMS7 4.7). */
|
|
public abstract static
|
|
class AttributeInfo {
|
|
|
|
public
|
|
AttributeInfo(short nameIndex) { this.nameIndex = nameIndex; }
|
|
|
|
/** Writes this attribute to a {@link DataOutputStream}, in the format described in JVMS7 4.7. */
|
|
public void
|
|
store(DataOutputStream dos) throws IOException {
|
|
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
this.storeBody(new DataOutputStream(baos));
|
|
|
|
dos.writeShort(this.nameIndex); // attribute_name_index;
|
|
dos.writeInt(baos.size()); // attribute_length
|
|
baos.writeTo(dos); // info
|
|
}
|
|
|
|
/** Writes the body of this attribute in an attribute-type dependent way; see JVMS7 4.7.2 and following. */
|
|
protected abstract void
|
|
storeBody(DataOutputStream dos) throws IOException;
|
|
|
|
private final short nameIndex;
|
|
}
|
|
|
|
/**
|
|
* Load one class file attribute. The returned object will be of
|
|
* {@link AttributeInfo}-derived type, depending on the attribute's name; e.g. if the
|
|
* name of the attribute is "SourceFile", then the returned object will be of type
|
|
* {@link SourceFileAttribute}.
|
|
*/
|
|
private AttributeInfo
|
|
loadAttribute(DataInputStream dis) throws IOException {
|
|
|
|
short attributeNameIndex = dis.readShort(); // attribute_name_index
|
|
int attributeLength = dis.readInt(); // attribute_length
|
|
|
|
final byte[] ba = new byte[attributeLength];
|
|
dis.readFully(ba);
|
|
ByteArrayInputStream bais = new ByteArrayInputStream(ba);
|
|
DataInputStream bdis = new DataInputStream(bais);
|
|
|
|
String attributeName = this.getConstantUtf8(attributeNameIndex);
|
|
AttributeInfo result;
|
|
if ("ConstantValue".equals(attributeName)) {
|
|
result = ConstantValueAttribute.loadBody(attributeNameIndex, bdis);
|
|
} else
|
|
if ("Code".equals(attributeName)) {
|
|
result = CodeAttribute.loadBody(attributeNameIndex, this, bdis);
|
|
} else
|
|
if ("Exceptions".equals(attributeName)) {
|
|
result = ExceptionsAttribute.loadBody(attributeNameIndex, bdis);
|
|
} else
|
|
if ("InnerClasses".equals(attributeName)) {
|
|
result = InnerClassesAttribute.loadBody(attributeNameIndex, bdis);
|
|
} else
|
|
if ("Synthetic".equals(attributeName)) {
|
|
result = SyntheticAttribute.loadBody(attributeNameIndex, bdis);
|
|
} else
|
|
if ("SourceFile".equals(attributeName)) {
|
|
result = SourceFileAttribute.loadBody(attributeNameIndex, bdis);
|
|
} else
|
|
if ("LineNumberTable".equals(attributeName)) {
|
|
result = LineNumberTableAttribute.loadBody(attributeNameIndex, bdis);
|
|
} else
|
|
if ("LocalVariableTable".equals(attributeName)) {
|
|
result = LocalVariableTableAttribute.loadBody(attributeNameIndex, bdis);
|
|
} else
|
|
if ("Deprecated".equals(attributeName)) {
|
|
result = DeprecatedAttribute.loadBody(attributeNameIndex, bdis);
|
|
} else
|
|
{
|
|
return new AttributeInfo(attributeNameIndex) {
|
|
@Override protected void storeBody(DataOutputStream dos) throws IOException { dos.write(ba); }
|
|
};
|
|
}
|
|
|
|
if (bais.available() > 0) {
|
|
throw new ClassFormatError(
|
|
(ba.length - bais.available())
|
|
+ " bytes of trailing garbage in body of attribute \""
|
|
+ attributeName
|
|
+ "\""
|
|
);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Representation of a "ConstantValue" attribute (see JVMS 4.7.2). */
|
|
public static
|
|
class ConstantValueAttribute extends AttributeInfo {
|
|
|
|
private final short constantValueIndex;
|
|
|
|
ConstantValueAttribute(short attributeNameIndex, short constantValueIndex) {
|
|
super(attributeNameIndex);
|
|
this.constantValueIndex = constantValueIndex;
|
|
}
|
|
|
|
/** @return The constant value contained in this attribute */
|
|
public ConstantValuePoolInfo
|
|
getConstantValue(ClassFile classFile) {
|
|
return (ConstantValuePoolInfo) classFile.getConstantPoolInfo(this.constantValueIndex);
|
|
}
|
|
|
|
private static AttributeInfo
|
|
loadBody(short attributeNameIndex, DataInputStream dis) throws IOException {
|
|
return new ConstantValueAttribute(
|
|
attributeNameIndex, // attributeNameIndex
|
|
dis.readShort() // constantValueIndex
|
|
);
|
|
}
|
|
|
|
// Implement "AttributeInfo".
|
|
@Override protected void
|
|
storeBody(DataOutputStream dos) throws IOException {
|
|
dos.writeShort(this.constantValueIndex);
|
|
}
|
|
}
|
|
|
|
/** Representation of an "Exceptions" attribute (see JVMS 4.7.4). */
|
|
public static
|
|
class ExceptionsAttribute extends AttributeInfo {
|
|
|
|
private final short[] exceptionIndexes;
|
|
|
|
public
|
|
ExceptionsAttribute(short attributeNameIndex, short[] exceptionIndexes) {
|
|
super(attributeNameIndex);
|
|
this.exceptionIndexes = exceptionIndexes;
|
|
}
|
|
|
|
/** @return The exception types contained in this {@link ExceptionsAttribute} */
|
|
public ConstantClassInfo[]
|
|
getExceptions(ClassFile classFile) {
|
|
ConstantClassInfo[] es = new ConstantClassInfo[this.exceptionIndexes.length];
|
|
for (int i = 0; i < es.length; i++) {
|
|
es[i] = (ConstantClassInfo) classFile.getConstantPoolInfo(this.exceptionIndexes[i]);
|
|
}
|
|
return es;
|
|
}
|
|
|
|
private static AttributeInfo
|
|
loadBody(short attributeNameIndex, DataInputStream dis) throws IOException {
|
|
return new ExceptionsAttribute(
|
|
attributeNameIndex, // attributeNameIndex
|
|
ClassFile.readShortArray(dis) // exceptionIndexes
|
|
);
|
|
}
|
|
|
|
// Implement "AttributeInfo".
|
|
@Override protected void
|
|
storeBody(DataOutputStream dos) throws IOException {
|
|
ClassFile.storeShortArray(dos, this.exceptionIndexes);
|
|
}
|
|
}
|
|
|
|
/** Representation of an "InnerClasses" attribute (see JVMS 4.7.5). */
|
|
public static
|
|
class InnerClassesAttribute extends AttributeInfo {
|
|
|
|
private final List<InnerClassesAttribute.Entry> entries;
|
|
|
|
InnerClassesAttribute(short attributeNameIndex) {
|
|
super(attributeNameIndex);
|
|
this.entries = new ArrayList();
|
|
}
|
|
InnerClassesAttribute(short attributeNameIndex, Entry[] entries) {
|
|
super(attributeNameIndex);
|
|
this.entries = new ArrayList(Arrays.asList(entries));
|
|
}
|
|
|
|
/**
|
|
* @return A {@code List<InnerClassesAttribute.Entry>}: The {@link Entry}s contained in this {@link
|
|
* InnerClassesAttribute}, see JVMS7 4.7.6
|
|
*/
|
|
public List<InnerClassesAttribute.Entry>
|
|
getEntries() { return this.entries; }
|
|
|
|
private static AttributeInfo
|
|
loadBody(short attributeNameIndex, DataInputStream dis) throws IOException {
|
|
|
|
Entry[] ics = new Entry[dis.readShort()]; // number_of_classes
|
|
for (short i = 0; i < ics.length; ++i) { // classes
|
|
ics[i] = new InnerClassesAttribute.Entry(
|
|
dis.readShort(), // innerClassInfoIndex
|
|
dis.readShort(), // outerClassInfoIndex
|
|
dis.readShort(), // innerNameIndex
|
|
dis.readShort() // innerClassAccessFlags
|
|
);
|
|
}
|
|
return new InnerClassesAttribute(attributeNameIndex, ics);
|
|
}
|
|
|
|
// Implement "AttributeInfo".
|
|
@Override protected void
|
|
storeBody(DataOutputStream dos) throws IOException {
|
|
|
|
dos.writeShort(this.entries.size());
|
|
for (InnerClassesAttribute.Entry e : this.entries) {
|
|
dos.writeShort(e.innerClassInfoIndex);
|
|
dos.writeShort(e.outerClassInfoIndex);
|
|
dos.writeShort(e.innerNameIndex);
|
|
dos.writeShort(e.innerClassAccessFlags);
|
|
}
|
|
}
|
|
|
|
/** The structure of the {@code classes} array as described in JVMS7 4.7.6. */
|
|
public static
|
|
class Entry {
|
|
|
|
/** The fields of the {@code classes} array as described in JVMS7 4.7.6. */
|
|
public final short innerClassInfoIndex, outerClassInfoIndex, innerNameIndex, innerClassAccessFlags;
|
|
|
|
public
|
|
Entry(
|
|
short innerClassInfoIndex,
|
|
short outerClassInfoIndex,
|
|
short innerNameIndex,
|
|
short innerClassAccessFlags
|
|
) {
|
|
this.innerClassInfoIndex = innerClassInfoIndex;
|
|
this.outerClassInfoIndex = outerClassInfoIndex;
|
|
this.innerNameIndex = innerNameIndex;
|
|
this.innerClassAccessFlags = innerClassAccessFlags;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Representation of a "Synthetic" attribute (see JVMS 4.7.6). */
|
|
public static
|
|
class SyntheticAttribute extends AttributeInfo {
|
|
|
|
SyntheticAttribute(short attributeNameIndex) {
|
|
super(attributeNameIndex);
|
|
}
|
|
|
|
private static AttributeInfo
|
|
loadBody(short attributeNameIndex, DataInputStream dis) {
|
|
return new SyntheticAttribute(attributeNameIndex);
|
|
}
|
|
|
|
// Implement "AttributeInfo".
|
|
@Override protected void
|
|
storeBody(DataOutputStream dos) {
|
|
;
|
|
}
|
|
}
|
|
|
|
/** Representation of a "SourceFile" attribute (see JVMS 4.7.7). */
|
|
public static
|
|
class SourceFileAttribute extends AttributeInfo {
|
|
|
|
private final short sourceFileIndex;
|
|
|
|
public
|
|
SourceFileAttribute(short attributeNameIndex, short sourceFileIndex) {
|
|
super(attributeNameIndex);
|
|
this.sourceFileIndex = sourceFileIndex;
|
|
}
|
|
|
|
private static AttributeInfo
|
|
loadBody(short attributeNameIndex, DataInputStream dis) throws IOException {
|
|
return new SourceFileAttribute(
|
|
attributeNameIndex, // attributeNameIndex
|
|
dis.readShort() // sourceFileNameIndex
|
|
);
|
|
}
|
|
|
|
// Implement "AttributeInfo".
|
|
@Override protected void
|
|
storeBody(DataOutputStream dos) throws IOException { dos.writeShort(this.sourceFileIndex); }
|
|
}
|
|
|
|
/** Representation of a "LineNumberTable" attribute (see JVMS 4.7.8). */
|
|
public static
|
|
class LineNumberTableAttribute extends AttributeInfo {
|
|
|
|
private final Entry[] entries;
|
|
|
|
public
|
|
LineNumberTableAttribute(short attributeNameIndex, Entry[] entries) {
|
|
super(attributeNameIndex);
|
|
this.entries = entries;
|
|
}
|
|
|
|
private static AttributeInfo
|
|
loadBody(short attributeNameIndex, DataInputStream dis) throws IOException {
|
|
|
|
Entry[] lntes = new Entry[dis.readShort()]; // line_number_table_length
|
|
for (short i = 0; i < lntes.length; ++i) { // line_number_table
|
|
lntes[i] = new LineNumberTableAttribute.Entry(
|
|
dis.readShort(), // startPC
|
|
dis.readShort() // lineNumber
|
|
);
|
|
}
|
|
return new LineNumberTableAttribute(attributeNameIndex, lntes);
|
|
}
|
|
|
|
// Implement "AttributeInfo".
|
|
@Override protected void
|
|
storeBody(DataOutputStream dos) throws IOException {
|
|
dos.writeShort(this.entries.length); // line_number_table_length
|
|
for (Entry entry : this.entries) {
|
|
dos.writeShort(entry.startPC);
|
|
dos.writeShort(entry.lineNumber);
|
|
}
|
|
}
|
|
|
|
/** The structure of the entries in the {@code line_number_table}, as described in JVMS7 4.7.12. */
|
|
public static
|
|
class Entry {
|
|
|
|
/** The fields of the entries in the {@code line_number_table}, as described in JVMS7 4.7.12. */
|
|
public final int startPC, lineNumber;
|
|
|
|
public
|
|
Entry(int startPc, int lineNumber) {
|
|
this.startPC = startPc;
|
|
this.lineNumber = lineNumber;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Representation of a "LocalVariableTable" attribute (see JVMS 4.7.9). */
|
|
public static
|
|
class LocalVariableTableAttribute extends AttributeInfo {
|
|
|
|
private final Entry[] entries;
|
|
|
|
public
|
|
LocalVariableTableAttribute(short attributeNameIndex, Entry[] entries) {
|
|
super(attributeNameIndex);
|
|
this.entries = entries;
|
|
}
|
|
|
|
private static AttributeInfo
|
|
loadBody(short attributeNameIndex, DataInputStream dis) throws IOException {
|
|
short localVariableTableLength = dis.readShort(); // local_variable_table_length
|
|
Entry[] lvtes = new Entry[localVariableTableLength];
|
|
for (short i = 0; i < localVariableTableLength; ++i) { // local_variable_table
|
|
lvtes[i] = new LocalVariableTableAttribute.Entry(
|
|
dis.readShort(), // startPC
|
|
dis.readShort(), // length
|
|
dis.readShort(), // nameIndex
|
|
dis.readShort(), // descriptorIndex
|
|
dis.readShort() // index
|
|
);
|
|
}
|
|
return new LocalVariableTableAttribute(attributeNameIndex, lvtes);
|
|
}
|
|
|
|
// Implement "AttributeInfo".
|
|
|
|
@Override protected void
|
|
storeBody(DataOutputStream dos) throws IOException {
|
|
dos.writeShort(this.entries.length); // local_variable_table_length
|
|
for (Entry lnte : this.entries) { // local_variable_table
|
|
dos.writeShort(lnte.startPC); // start_pc;
|
|
dos.writeShort(lnte.length); // length
|
|
dos.writeShort(lnte.nameIndex); // name_index
|
|
dos.writeShort(lnte.descriptorIndex); // descriptor_index
|
|
dos.writeShort(lnte.index); // index
|
|
}
|
|
}
|
|
|
|
/** The structure of the entries in the {@code local_variable_table}, as described in JVMS7 4.7.13. */
|
|
public static
|
|
class Entry {
|
|
|
|
/** The fields of the entries in the {@code local_variable_table}, as described in JVMS7 4.7.13. */
|
|
public final short startPC, length, nameIndex, descriptorIndex, index;
|
|
|
|
public
|
|
Entry(short startPc, short length, short nameIndex, short descriptorIndex, short index) {
|
|
this.startPC = startPc;
|
|
this.length = length;
|
|
this.nameIndex = nameIndex;
|
|
this.descriptorIndex = descriptorIndex;
|
|
this.index = index;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Representation of a "Deprecated" attribute (see JVMS 4.7.10). */
|
|
public static
|
|
class DeprecatedAttribute extends AttributeInfo {
|
|
|
|
public
|
|
DeprecatedAttribute(short attributeNameIndex) { super(attributeNameIndex); }
|
|
|
|
private static AttributeInfo
|
|
loadBody(short attributeNameIndex, DataInputStream dis) {
|
|
return new DeprecatedAttribute(attributeNameIndex);
|
|
}
|
|
|
|
// Implement "AttributeInfo".
|
|
@Override protected void
|
|
storeBody(DataOutputStream dos) {
|
|
;
|
|
}
|
|
}
|
|
|
|
/** Representation of an unmodifiable "Code" attribute, as read from a class file. */
|
|
private static
|
|
class CodeAttribute extends AttributeInfo {
|
|
|
|
private final short maxStack;
|
|
private final short maxLocals;
|
|
private final byte[] code;
|
|
private final ExceptionTableEntry[] exceptionTableEntries;
|
|
private final AttributeInfo[] attributes;
|
|
|
|
CodeAttribute(
|
|
short attributeNameIndex,
|
|
short maxStack,
|
|
short maxLocals,
|
|
byte[] code,
|
|
ExceptionTableEntry[] exceptionTableEntries,
|
|
AttributeInfo[] attributes
|
|
) {
|
|
super(attributeNameIndex);
|
|
this.maxStack = maxStack;
|
|
this.maxLocals = maxLocals;
|
|
this.code = code;
|
|
this.exceptionTableEntries = exceptionTableEntries;
|
|
this.attributes = attributes;
|
|
}
|
|
|
|
public static AttributeInfo
|
|
loadBody(short attributeNameIndex, ClassFile classFile, DataInputStream dis) throws IOException {
|
|
final short maxStack = dis.readShort(); // max_stack
|
|
final short maxLocals = dis.readShort(); // max_locals
|
|
final byte[] code = ClassFile.readLengthAndBytes(dis); // code_length, code
|
|
|
|
ExceptionTableEntry[] etes = new ExceptionTableEntry[dis.readShort()]; // exception_table_length
|
|
for (int i = 0; i < etes.length; ++i) { // exception_table
|
|
etes[i] = new ExceptionTableEntry(
|
|
dis.readShort(), // startPC
|
|
dis.readShort(), // endPC
|
|
dis.readShort(), // handlerPC
|
|
dis.readShort() // catchType
|
|
);
|
|
}
|
|
|
|
AttributeInfo[] attributes = new AttributeInfo[dis.readShort()]; // attributes_count
|
|
for (int i = 0; i < attributes.length; ++i) { // attributes
|
|
attributes[i] = classFile.loadAttribute(dis);
|
|
}
|
|
|
|
return new CodeAttribute(
|
|
attributeNameIndex, // attributeNameIndex
|
|
maxStack, // maxStack
|
|
maxLocals, // maxLocals
|
|
code, // code
|
|
etes, // exceptionTableEntries
|
|
attributes // attributes
|
|
);
|
|
}
|
|
|
|
@Override protected void
|
|
storeBody(DataOutputStream dos) throws IOException {
|
|
dos.writeShort(this.maxStack); // max_stack
|
|
dos.writeShort(this.maxLocals); // max_locals
|
|
dos.writeInt(this.code.length); // code_length
|
|
dos.write(this.code); // code
|
|
dos.writeShort(this.exceptionTableEntries.length); // exception_table_length
|
|
for (ExceptionTableEntry ete : this.exceptionTableEntries) { // exception_table
|
|
dos.writeShort(ete.startPc); // start_pc
|
|
dos.writeShort(ete.endPc); // end_pc
|
|
dos.writeShort(ete.handlerPc); // handler_pc
|
|
dos.writeShort(ete.catchType); // catch_type
|
|
}
|
|
dos.writeShort(this.attributes.length); // attributes_count
|
|
for (AttributeInfo ai : this.attributes) ai.store(dos); // attributes
|
|
}
|
|
|
|
/** Representation of an entry in the "exception_table" of a "Code" attribute (see JVMS 4.7.3). */
|
|
private static
|
|
class ExceptionTableEntry {
|
|
|
|
final short startPc, endPc, handlerPc, catchType;
|
|
|
|
public
|
|
ExceptionTableEntry(short startPc, short endPc, short handlerPc, short catchType) {
|
|
this.startPc = startPc;
|
|
this.endPc = endPc;
|
|
this.handlerPc = handlerPc;
|
|
this.catchType = catchType;
|
|
}
|
|
}
|
|
}
|
|
}
|