All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.org.codehaus.janino.util.ClassFile Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version

/*
 * 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. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
 *       products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 com.hazelcast.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.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.hazelcast.org.codehaus.commons.nullanalysis.Nullable;
import com.hazelcast.org.codehaus.janino.Descriptor;
import com.hazelcast.org.codehaus.janino.MethodDescriptor;
import com.hazelcast.org.codehaus.janino.Mod;

/**
 * An object that implements the Java "class file" format.
 * 

* {@link #ClassFile(InputStream)} reads bytecode from an {@link InputStream} and creates a {@link ClassFile} object * from it. *

*

* {@link #store(OutputStream)} generates JVM bytecode which is suitable for being processed by a Java * virtual machine, and writes it to an {@link OutputStream}. *

*/ public class ClassFile implements Annotatable { /** * Unchecked exception that represents an error condition that could occur during processing of class files, e.g. * reading a corrupt class file, or an overflow of the constant pool. */ public static class ClassFileException extends RuntimeException { public ClassFileException(String message) { super(message); } public ClassFileException(String message, Throwable cause) { super(message, cause); } } /** * Constructs a class with no fields and methods. * An application would typically add fields and methods before saving it. *

* By default, the .class file major and minor version are that of the currently executing JVM (system property * {@code "java.class.version"}). *

* * @param accessFlags As defined by {@link com.hazelcast.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;"); {@code null} for * {@link Object} * @param interfaceFds The field descriptors for the implemented interfaces * @see ClassFile#setVersion(short, short) * @see ClassFile#addFieldInfo(short, String, String, Object) * @see ClassFile#addMethodInfo(short, String, MethodDescriptor) */ public ClassFile(short accessFlags, String thisClassFd, @Nullable String superclassFd, String[] interfaceFds) { // Compute the .class file major and minor version. { String jcv = System.getProperty("java.class.version"); Matcher m = Pattern.compile("(\\d+)\\.(\\d+)").matcher(jcv); if (!m.matches()) throw new AssertionError("Unrecognized JVM class file version \"" + jcv + "\""); this.majorVersion = Short.parseShort(m.group(1)); this.minorVersion = Short.parseShort(m.group(2)); } this.constantPool = new ArrayList(); this.constantPool.add(null); // Add fake "0" index entry. this.constantPoolMap = new HashMap(); // Some sanity checks on the access flags, according to JVMS8 4.1. if ((accessFlags & Mod.INTERFACE) != 0) { assert ( accessFlags & (Mod.ABSTRACT | Mod.FINAL | Mod.SUPER | Mod.ENUM) ) == Mod.ABSTRACT : Integer.toString(accessFlags & 0xffff, 16); } if ((accessFlags & Mod.INTERFACE) == 0) { assert ( (accessFlags & Mod.ANNOTATION) == 0 && (accessFlags & (Mod.FINAL | Mod.ABSTRACT)) != (Mod.FINAL | Mod.ABSTRACT) ) : Integer.toString(accessFlags & 0xffff, 16); } this.accessFlags = accessFlags; this.thisClass = this.addConstantClassInfo(thisClassFd); this.superclass = superclassFd == null ? (short) 0 : 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 {@code 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 {@code Deprecated} attribute to this class. */ public void addDeprecatedAttribute() { this.attributes.add(new DeprecatedAttribute(this.addConstantUtf8Info("Deprecated"))); } /** * Finds the {@code InnerClasses} attribute of this class file. * * @return {@code null} if this class has no "InnerClasses" attribute */ @Nullable public InnerClassesAttribute getInnerClassesAttribute() { return (InnerClassesAttribute) this.findAttribute(this.attributes, "InnerClasses"); } /** * Finds the named attribute in the attributes. * * @return {@code null} iff the attributes constain no attribute with that name * @throws ClassFileException attributes contains more than one attribute with that name */ @Nullable private AttributeInfo findAttribute(List attributes, String attributeName) throws ClassFormatError { Short nameIndex = (Short) this.constantPoolMap.get(new ConstantUtf8Info(attributeName)); if (nameIndex == null) return null; AttributeInfo result = null; for (AttributeInfo ai : attributes) { if (ai.nameIndex == nameIndex) { if (result != null) throw new ClassFileException("Duplicate \"" + attributeName + "\" attribute"); result = ai; } } return result; } /** * Creates an {@code InnerClasses} attribute if it does not exist, then adds the entry to the {@code * InnerClasses} attribute. */ public void addInnerClassesAttributeEntry(InnerClassesAttribute.Entry entry) { InnerClassesAttribute ica = this.getInnerClassesAttribute(); if (ica == null) { ica = new InnerClassesAttribute(this.addConstantUtf8Info("InnerClasses")); this.attributes.add(ica); } ica.getEntries().add(entry); } /** * Finds the {@code Runtime[In]visibleAnnotations} attribute in the attributes. * * @return {@code null} if attributes constains no such attribute */ @Nullable private AnnotationsAttribute getAnnotationsAttribute(boolean runtimeVisible, List attributes) { String attributeName = runtimeVisible ? "RuntimeVisibleAnnotations" : "RuntimeInvisibleAnnotations"; return (AnnotationsAttribute) this.findAttribute(attributes, attributeName); } @Override public Annotation[] getAnnotations(boolean runtimeVisible) { AnnotationsAttribute aa = this.getAnnotationsAttribute(runtimeVisible, this.attributes); if (aa == null) return new Annotation[0]; return (Annotation[]) aa.annotations.toArray( new Annotation[aa.annotations.size()] ); } /** * Creates a {@code Runtime[In]visibleAnnotations} attribute on the class (if it does not yet exist) and adds an * entry to it. * * @param elementValuePairs Maps element-name constant-pool-index ({@link ConstantUtf8Info}) to element value */ @Override public void addAnnotationsAttributeEntry( boolean runtimeVisible, String fieldDescriptor, Map elementValuePairs ) { this.addAnnotationsAttributeEntry(runtimeVisible, fieldDescriptor, elementValuePairs, this.attributes); } /** * Adds a {@code Runtime[In]visibleAnnotations} attribute to the target (if it does not yet exist) and * adds an entry to it. * * @param elementValuePairs Maps "elemant_name_index" ({@link ConstantUtf8Info}) to "element_value", see JVMS8 * 4.7.16 */ private void addAnnotationsAttributeEntry( boolean runtimeVisible, String fieldDescriptor, Map elementValuePairs, List target ) { // Find or create the "Runtime[In]visibleAnnotations" attribute. AnnotationsAttribute aa = this.getAnnotationsAttribute(runtimeVisible, target); if (aa == null) { String attributeName = runtimeVisible ? "RuntimeVisibleAnnotations" : "RuntimeInvisibleAnnotations"; aa = new AnnotationsAttribute(this.addConstantUtf8Info(attributeName)); target.add(aa); } // Add the new annotation. aa.getAnnotations().add( new Annotation(this.addConstantUtf8Info(fieldDescriptor), elementValuePairs) ); } /** * Reads "class file" data from the inputStream and construct a {@link ClassFile} object from it. *

* 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 ClassFileException("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 ORACLE increase them with // each platform update while keeping them backwards compatible. // if (!ClassFile.isRecognizedVersion(this.majorVersion, this.minorVersion)) { // throw new ClassFileException( // "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() { return this.getConstantClassInfo(this.thisClass).getName(this).replace('/', '.'); } /** * Sets the major and minor class file version numbers (JVMS 4.1). *

* {@link ClassFile} declares a set of valid major-minor version number pairs, e.g. {@link * #MAJOR_VERSION_JDK_1_6} and {@link #MINOR_VERSION_JDK_1_6}. *

*/ 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; } /** * Returns the constant index number for a "CONSTANT_Class_info" structure to the class file. If the * class hasn't been added before, adds it to the constant pool. Otherwise returns 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 ClassFileException("\"" + Descriptor.toString(typeFd) + "\" is neither a class nor an array"); } return this.addToConstantPool(new ConstantClassInfo(this.addConstantUtf8Info(s))); } /** * Adds 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) )); } /** * Adds 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) )); } /** * Adds 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) )); } /** * Adds 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))); } /** * Adds 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)); } /** * Adds 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)); } /** * Adds 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)); } /** * Adds 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)); } /** * Adds 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) { 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 ClassFileException("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) { // Check whether an equal entry already exists. Short index = (Short) this.constantPoolMap.get(cpi); if (index != null) return index.shortValue(); // The current size of the constant pool is the index of the new entry. final short res = (short) this.constantPool.size(); // Add one or two entries to the constant pool. this.constantPool.add(cpi); if (cpi.isWide()) this.constantPool.add(null); // Check for constant pool overflow. if (this.constantPool.size() > 0xFFFF) { throw new ClassFileException( "Constant pool for class " + this.getThisClassName() + " has grown past JVM limit of 0xFFFF" ); } // Also put the new entry into the "constantPoolMap" for fast access. this.constantPoolMap.put(cpi, res); return 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( short accessFlags, String fieldName, String fieldTypeFd, @Nullable Object constantValue ) { List attributes = new ArrayList(); if (constantValue != null) { attributes.add(new ConstantValueAttribute( this.addConstantUtf8Info("ConstantValue"), this.addConstantSifldInfo(constantValue) )); } FieldInfo fi = new FieldInfo( accessFlags, // accessFlags 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(short accessFlags, String methodName, MethodDescriptor methodMd) { int parameterCount = Mod.isStatic(accessFlags) ? 0 : 1; for (String fd : methodMd.parameterFds) parameterCount += Descriptor.size(fd); // JVMS8 4.3.3 // See https://github.com/janino-compiler/janino/pull/46 if (parameterCount > 255) { throw new ClassFileException(( "Method \"" + methodName + "\" has too many parameters (" + parameterCount + ")" )); } MethodInfo mi = new MethodInfo( accessFlags, // accessFlags this.addConstantUtf8Info(methodName), // nameIndex this.addConstantUtf8Info(methodMd.toString()), // desriptorIndex new ArrayList() // attributes ); this.methodInfos.add(mi); return mi; } /** * @return The (read-only) constant pool entry indexed by index * @throws ClassFileException index is invalid */ public ConstantPoolInfo getConstantPoolInfo(short index) { final ConstantPoolInfo result = (ConstantPoolInfo) this.constantPool.get(0xffff & index); if (result == null) throw new ClassFileException("Invalid constant pool index " + index); return result; } /** * @return The (read-only) constant class info indexed by index */ public ConstantClassInfo getConstantClassInfo(short index) { return (ConstantClassInfo) this.getConstantPoolInfo(index); } /** * @return The (read-only) constant fieldref info indexed by index */ public ConstantFieldrefInfo getConstantFieldrefInfo(short index) { return (ConstantFieldrefInfo) this.getConstantPoolInfo(index); } /** * @return The (read-only) constant interface methodref info indexed by index */ public ConstantInterfaceMethodrefInfo getConstantInterfaceMethodrefInfo(short index) { return (ConstantInterfaceMethodrefInfo) this.getConstantPoolInfo(index); } /** * @return The (read-only) constant invoke dynamic info indexed by index */ public ConstantInvokeDynamicInfo getConstantInvokeDynamicInfo(short index) { return (ConstantInvokeDynamicInfo) this.getConstantPoolInfo(index); } /** * @return The (read-only) constant method handle info indexed by index */ public ConstantMethodHandleInfo getConstantMethodHandleInfo(short index) { return (ConstantMethodHandleInfo) this.getConstantPoolInfo(index); } /** * @return The (read-only) constant methodref info indexed by index */ public ConstantMethodrefInfo getConstantMethodrefInfo(short index) { return (ConstantMethodrefInfo) this.getConstantPoolInfo(index); } /** * @return The (read-only) constant method type info indexed by index */ public ConstantMethodTypeInfo getConstantMethodTypeInfo(short index) { return (ConstantMethodTypeInfo) this.getConstantPoolInfo(index); } /** * @return The (read-only) constant name and type info indexed by index */ public ConstantNameAndTypeInfo getConstantNameAndTypeInfo(short index) { return (ConstantNameAndTypeInfo) this.getConstantPoolInfo(index); } /** * @return The (read-only) constant utf8 info indexed by index */ public ConstantUtf8Info getConstantUtf8Info(short index) { return (ConstantUtf8Info) this.getConstantPoolInfo(index); } /** * @return The (read-only) constant value pool info indexed by index */ public ConstantValuePoolInfo getConstantValuePoolInfo(short index) { return (ConstantValuePoolInfo) this.getConstantPoolInfo(index); } /** * @return The size of the constant pool */ public int getConstantPoolSize() { return this.constantPool.size(); } /** * Shorthand for {@code getConstantUtf8Info(index).s}. * * @param index Index to a {@code CONSTANT_Utf8_info} in the constant pool * @return The string represented by the structure */ public String getConstantUtf8(short index) { return this.getConstantUtf8Info(index).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[] result = new short[dis.readUnsignedShort()]; for (int i = 0; i < result.length; ++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(); int constantPoolCount = dis.readUnsignedShort(); // constant_pool_count this.constantPool.add(null); for (int i = 1; i < constantPoolCount; ++i) { ConstantPoolInfo cpi = ConstantPoolInfo.loadConstantPoolInfo(dis); this.constantPool.add(cpi); this.constantPoolMap.put(cpi, (short) i); if (cpi.isWide()) { this.constantPool.add(null); ++i; } } } /** * u2 fields_count, fields[fields_count] */ private List loadFields(DataInputStream dis) throws IOException { List result = new ArrayList(); for (int i = dis.readUnsignedShort(); i > 0; i--) { // fields_count result.add(new FieldInfo( // fields[field_count] dis.readShort(), // access_flags dis.readShort(), // name_index dis.readShort(), // descriptor_index this.loadAttributes(dis) // attributes_count, attributes[attributes_count] )); } return result; } /** * u2 methods_count, methods[methods_count] */ private List loadMethods(DataInputStream dis) throws IOException { int methodsCount = dis.readUnsignedShort(); List 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 loadAttributes(DataInputStream dis) throws IOException { int attributesCount = dis.readUnsignedShort(); List attributes = new ArrayList(attributesCount); for (int i = 0; i < attributesCount; ++i) attributes.add(this.loadAttribute(dis)); return attributes; } /** * Writes {@link ClassFile} to an {@link OutputStream}, in "class file" format. *

* 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. *

*/ 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[constant_pool_count] 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[interfaces_count] ClassFile.storeFields(dos, this.fieldInfos); // fields_count, fields[fields_count] ClassFile.storeMethods(dos, this.methodInfos); // methods_count, methods[methods_count] ClassFile.storeAttributes(dos, this.attributes); // attributes_count, attributes[attributes_count] } /** * u2 constant_pool_count, constant_pool[constant_pool_count - 1] */ private static void storeConstantPool(DataOutputStream dos, List 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 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 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 attributeInfos) throws IOException { dos.writeShort(attributeInfos.size()); for (AttributeInfo attributeInfo : attributeInfos) attributeInfo.store(dos); } /** * Constructs the name of a resource that could contain the source code of the class with the className. *

* 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. {@code "pkg1.pkg2.Outer$Inner"} * @return the name of the resource, e.g. {@code "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"; } /** * Constructs the name of a resource that could contain the class file of the class with the className. * * @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"; } /** * Returns 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 ClassFileException(ex.toString(), ex); } return baos.toByteArray(); } private static final int CLASS_FILE_MAGIC = 0xcafebabe; /** Major version number of a class file that was generated by a Java 1.1-compliant compiler. */ public static final short MAJOR_VERSION_JDK_1_1 = 45; /** Minor version number of a class file that was generated by a Java 1.1-compliant compiler. */ public static final short MINOR_VERSION_JDK_1_1 = 3; /** Major version number of a class file that was generated by a Java 1.2-compliant compiler. */ public static final short MAJOR_VERSION_JDK_1_2 = 46; /** Minor version number of a class file that was generated by a Java 1.2-compliant compiler. */ public static final short MINOR_VERSION_JDK_1_2 = 0; /** Major version number of a class file that was generated by a Java 1.3-compliant compiler. */ public static final short MAJOR_VERSION_JDK_1_3 = 47; /** Minor version number of a class file that was generated by a Java 1.3-compliant compiler. */ public static final short MINOR_VERSION_JDK_1_3 = 0; /** Major version number of a class file that was generated by a Java 1.4-compliant compiler. */ public static final short MAJOR_VERSION_JDK_1_4 = 48; /** Minor version number of a class file that was generated by a Java 1.4-compliant compiler. */ public static final short MINOR_VERSION_JDK_1_4 = 0; /** Major version number of a class file that was generated by a Java 1.5-compliant compiler. */ public static final short MAJOR_VERSION_JDK_1_5 = 49; /** Minor version number of a class file that was generated by a Java 1.5-compliant compiler. */ public static final short MINOR_VERSION_JDK_1_5 = 0; /** Major version number of a class file that was generated by a Java 1.6-compliant compiler. */ public static final short MAJOR_VERSION_JDK_1_6 = 50; /** Minor version number of a class file that was generated by a Java 1.6-compliant compiler. */ public static final short MINOR_VERSION_JDK_1_6 = 0; /** Major version number of a class file that was generated by a Java 1.7-compliant compiler. */ public static final short MAJOR_VERSION_JDK_1_7 = 51; /** Minor version number of a class file that was generated by a Java 1.7-compliant compiler. */ public static final short MINOR_VERSION_JDK_1_7 = 0; /** Major version number of a class file that was generated by a Java 1.8-compliant compiler. */ public static final short MAJOR_VERSION_JDK_1_8 = 52; /** Minor version number of a class file that was generated by a Java 1.8-compliant compiler. */ public static final short MINOR_VERSION_JDK_1_8 = 0; /** Major version number of a class file that was generated by a Java 1.9-compliant compiler. */ public static final short MAJOR_VERSION_JDK_1_9 = 53; /** Minor version number of a class file that was generated by a Java 1.9-compliant compiler. */ public static final short MINOR_VERSION_JDK_1_9 = 0; /** Major version number of a class file that was generated by a Java 1.10-compliant compiler. */ public static final short MAJOR_VERSION_JDK_1_10 = 54; /** Minor version number of a class file that was generated by a Java 1.10-compliant compiler. */ public static final short MINOR_VERSION_JDK_1_10 = 0; /** Major version number of a class file that was generated by a Java 1.11-compliant compiler. */ public static final short MAJOR_VERSION_JDK_1_11 = 55; /** Minor version number of a class file that was generated by a Java 1.11-compliant compiler. */ public static final short MINOR_VERSION_JDK_1_11 = 0; /** Major version number of a class file that was generated by a Java 1.12-compliant compiler. */ public static final short MAJOR_VERSION_JDK_1_12 = 56; /** Minor version number of a class file that was generated by a Java 1.12-compliant compiler. */ public static final short MINOR_VERSION_JDK_1_12 = 0; private short majorVersion; private short minorVersion; private final List 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. * * @see "JVMS11 4.1, The ClassFile Structure" */ public final short superclass; /** * The constant pool indexes of {@link ConstantClassInfo} which describe 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 fieldInfos; /** * The {@link MethodInfo}s of the methods of this class or interface. */ public final List methodInfos; /** * The {@link AttributeInfo}s of the attributes of this class or interface. */ private final List attributes; // Convenience. private final Map constantPoolMap; /** * Base for various the constant pool table entry types. */ public abstract static class ConstantPoolInfo { /** * Stores this CP entry into a {@link DataOutputStream}. *

* 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 */ public 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()); case 15: return new ConstantMethodHandleInfo(dis.readByte(), dis.readShort()); case 16: return new ConstantMethodTypeInfo(dis.readShort()); case 18: return new ConstantInvokeDynamicInfo(dis.readShort(), dis.readShort()); default: throw new ClassFileException("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 String toString() { return "CONSTANT_Class_info(" + this.nameIndex + ")"; } @Override public boolean equals(@Nullable 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 ConstantClassInfo} of this {@link ConstantFieldrefInfo} */ public ConstantClassInfo getClassInfo(ClassFile classFile) { return classFile.getConstantClassInfo(this.classIndex); } /** * @return The {@link ConstantNameAndTypeInfo} of this {@link ConstantFieldrefInfo} */ public ConstantNameAndTypeInfo getNameAndType(ClassFile classFile) { return classFile.getConstantNameAndTypeInfo(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 String toString() { return "CONSTANT_Fieldref_info(" + this.classIndex + ", " + this.nameAndTypeIndex + ")"; } @Override public boolean equals(@Nullable 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 ConstantClassInfo} of this {@link ConstantMethodrefInfo} */ public ConstantClassInfo getClassInfo(ClassFile classFile) { return classFile.getConstantClassInfo(this.classIndex); } /** * @return The {@link ConstantNameAndTypeInfo} of this {@link ConstantMethodrefInfo} */ public ConstantNameAndTypeInfo getNameAndType(ClassFile classFile) { return classFile.getConstantNameAndTypeInfo(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 String toString() { return "CONSTANT_Methodref_info(" + this.classIndex + ", " + this.nameAndTypeIndex + ")"; } @Override public boolean equals(@Nullable 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 ConstantClassInfo} of this {@link ConstantInterfaceMethodrefInfo} */ public ConstantClassInfo getClassInfo(ClassFile classFile) { return classFile.getConstantClassInfo(this.classIndex); } /** * @return The {@link ConstantNameAndTypeInfo} of this {@link ConstantInterfaceMethodrefInfo} */ public ConstantNameAndTypeInfo getNameAndType(ClassFile classFile) { return classFile.getConstantNameAndTypeInfo(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 String toString() { return "CONSTANT_InterfaceMethodref_info(" + this.classIndex + ", " + this.nameAndTypeIndex + ")"; } @Override public boolean equals(@Nullable 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; 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 String toString() { return "CONSTANT_String_info(" + this.stringIndex + ")"; } @Override public boolean equals(@Nullable 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; 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 String toString() { return "CONSTANT_Integer_info(" + this.value + ")"; } @Override public boolean equals(@Nullable 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; 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 String toString() { return "CONSTANT_Float_info(" + this.value + ")"; } @Override public boolean equals(@Nullable 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; 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 String toString() { return "CONSTANT_Long_info(" + this.value + ")"; } @Override public boolean equals(@Nullable 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; 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 String toString() { return "CONSTANT_Double_info(" + this.value + ")"; } @Override public boolean equals(@Nullable 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 name of the field or method */ public String getName(ClassFile classFile) { return classFile.getConstantUtf8(this.nameIndex); } /** * @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 String toString() { return "CONSTANT_NameAndType_info(" + this.nameIndex + ", " + this.descriptorIndex + ")"; } @Override public boolean equals(@Nullable 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 ConstantValuePoolInfo { private final String s; public ConstantUtf8Info(String s) { assert s != null; this.s = s; } // Implement ConstantValuePoolInfo. @Override public Object getValue(ClassFile classFile) { return this.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 ClassFileException("String constant too long to store in class file"); } } @Override public String toString() { return "CONSTANT_Utf8_info(\"" + this.s + "\")"; } @Override public boolean equals(@Nullable Object o) { return o instanceof ConstantUtf8Info && ((ConstantUtf8Info) o).s.equals(this.s); } @Override public int hashCode() { return this.s.hashCode(); } } /** * See JVMS7 4.4.8. */ public static class ConstantMethodHandleInfo extends ConstantPoolInfo { private final byte referenceKind; private final short referenceIndex; public ConstantMethodHandleInfo(byte referenceKind, short referenceIndex) { this.referenceKind = referenceKind; this.referenceIndex = referenceIndex; } public byte getReferenceKind() { return this.referenceKind; } public short getReferenceIndex() { return this.referenceIndex; } // Implement ConstantPoolInfo. @Override public boolean isWide() { return false; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(15); dos.writeByte(this.referenceKind); dos.writeShort(this.referenceIndex); } @Override public String toString() { return "CONSTANT_MethodHandle_info(" + this.referenceKind + ", " + this.referenceIndex + ")"; } @Override public boolean equals(@Nullable Object o) { return ( o instanceof ConstantMethodHandleInfo && ((ConstantMethodHandleInfo) o).referenceKind == this.referenceKind && ((ConstantMethodHandleInfo) o).referenceIndex == this.referenceIndex ); } @Override public int hashCode() { return this.referenceKind + (this.referenceIndex << 16); } } /** * See JVMS7 4.4.9. */ public static class ConstantMethodTypeInfo extends ConstantPoolInfo { private final short descriptorIndex; public ConstantMethodTypeInfo(short descriptorIndex) { this.descriptorIndex = descriptorIndex; } public short getDescriptorIndex() { return this.descriptorIndex; } // Implement ConstantPoolInfo. @Override public boolean isWide() { return false; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(16); dos.writeShort(this.descriptorIndex); } @Override public String toString() { return "CONSTANT_MethodType_info(" + this.descriptorIndex + ")"; } @Override public boolean equals(@Nullable Object o) { return ( o instanceof ConstantMethodTypeInfo && ((ConstantMethodTypeInfo) o).descriptorIndex == this.descriptorIndex ); } @Override public int hashCode() { return this.descriptorIndex; } } /** * See JVMS7 4.4.10. */ public static class ConstantInvokeDynamicInfo extends ConstantPoolInfo { private final short bootstrapMethodAttrIndex; private final short nameAndTypeIndex; public ConstantInvokeDynamicInfo(short bootstrapMethodAttrIndex, short nameAndTypeIndex) { this.bootstrapMethodAttrIndex = bootstrapMethodAttrIndex; this.nameAndTypeIndex = nameAndTypeIndex; } public short getBootstrapMethodAttrIndex() { return this.bootstrapMethodAttrIndex; } public short getNameAndTypeIndex() { return this.nameAndTypeIndex; } // Implement ConstantPoolInfo. @Override public boolean isWide() { return false; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(18); dos.writeShort(this.bootstrapMethodAttrIndex); dos.writeShort(this.nameAndTypeIndex); } @Override public String toString() { return "CONSTANT_InvokeDynamic_info(" + this.bootstrapMethodAttrIndex + ", " + this.nameAndTypeIndex + ")"; } @Override public boolean equals(@Nullable Object o) { return ( o instanceof ConstantInvokeDynamicInfo && ((ConstantInvokeDynamicInfo) o).bootstrapMethodAttrIndex == this.bootstrapMethodAttrIndex && ((ConstantInvokeDynamicInfo) o).nameAndTypeIndex == this.nameAndTypeIndex ); } @Override public int hashCode() { return this.bootstrapMethodAttrIndex + (this.nameAndTypeIndex << 16); } } /** * Representation of a "method_info" structure, as defined by JVMS7 4.6. */ public class MethodInfo implements Annotatable { private final short accessFlags; private final short nameIndex; private final short descriptorIndex; private final List attributes; /** * Initializes the "method_info" structure. */ public MethodInfo( short accessFlags, short nameIndex, short descriptorIndex, List attributes ) { this.accessFlags = accessFlags; 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 access flags of this method; or'ed values are the constants declared in {@link Mod}. */ public short getAccessFlags() { return this.accessFlags; } /** * @return The annotations of this method */ @Override public Annotation[] getAnnotations(boolean runtimeVisible) { AnnotationsAttribute aa = ClassFile.this.getAnnotationsAttribute(runtimeVisible, this.attributes); if (aa == null) return new Annotation[0]; return (Annotation[]) aa.annotations.toArray(new Annotation[aa.annotations.size()]); } /** * @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 attribute to this method. */ public void addAttribute(AttributeInfo attribute) { this.attributes.add(attribute); } @Override public void addAnnotationsAttributeEntry( boolean runtimeVisible, String fieldDescriptor, Map elementValuePairs ) { ClassFile.this.addAnnotationsAttributeEntry( runtimeVisible, fieldDescriptor, elementValuePairs, this.attributes ); } /** * Writes this object to a {@link DataOutputStream}, in the format described inJVMS7 4.6. */ public void store(DataOutputStream dos) throws IOException { dos.writeShort(this.accessFlags); // access_flags dos.writeShort(this.nameIndex); // name_index dos.writeShort(this.descriptorIndex); // descriptor_index ClassFile.storeAttributes(dos, this.attributes); // attributes_count, attributes[attributes_count] } @Override public String toString() { try { return ClassFile.this + "." + ClassFile.this.getConstantUtf8(this.nameIndex) + "(...)"; } catch (Exception e) { return super.toString(); } } } private MethodInfo loadMethodInfo(DataInputStream dis) throws IOException { return new MethodInfo( dis.readShort(), // access_flags dis.readShort(), // name_index dis.readShort(), // descriptor_index this.loadAttributes(dis) // attributes_count, attributes[attributes_count] ); } /** * Representation of a "method_info" structure, as defined by JVMS7 4.5. */ public class FieldInfo implements Annotatable { public FieldInfo( short accessFlags, short nameIndex, short descriptorIndex, List attributes ) { this.accessFlags = accessFlags; 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 getAccessFlags() { return this.accessFlags; } /** * @return The annotations of this field */ @Override public Annotation[] getAnnotations(boolean runtimeVisible) { AnnotationsAttribute aa = ClassFile.this.getAnnotationsAttribute(runtimeVisible, this.attributes); if (aa == null) return new Annotation[0]; return (Annotation[]) aa.annotations.toArray(new Annotation[aa.annotations.size()]); } /** * @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 attribute to this field. */ public void addAttribute(AttributeInfo attribute) { this.attributes.add(attribute); } @Override public void addAnnotationsAttributeEntry( boolean runtimeVisible, String fieldDescriptor, Map elementValuePairs ) { ClassFile.this.addAnnotationsAttributeEntry( runtimeVisible, fieldDescriptor, elementValuePairs, this.attributes ); } /** * Writes this object to a {@link DataOutputStream}, in the format described inJVMS7 4.5. */ public void store(DataOutputStream dos) throws IOException { dos.writeShort(this.accessFlags); // access_flags dos.writeShort(this.nameIndex); // name_index dos.writeShort(this.descriptorIndex); // descriptor_index ClassFile.storeAttributes(dos, this.attributes); // attibutes_count, attributes } private final short accessFlags; private final short nameIndex; private final short descriptorIndex; private final List 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; } /** * Loads 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 {@code "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 ("StackMapTable".equals(attributeName)) { result = StackMapTableAttribute.loadBody(attributeNameIndex, bdis, this); } 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 if ("AnnotationDefault".equals(attributeName)) { result = AnnotationDefaultAttribute.loadBody(attributeNameIndex, bdis); } else if ("RuntimeVisibleAnnotations".equals(attributeName)) { result = AnnotationsAttribute.loadBody(attributeNameIndex, bdis); } else if ("RuntimeInvisibleAnnotations".equals(attributeName)) { result = AnnotationsAttribute.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 ClassFileException( (ba.length - bais.available()) + " bytes of trailing garbage in body of attribute \"" + attributeName + "\"" ); } return result; } /** * Representation of a {@code 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 classFile.getConstantValuePoolInfo(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 {@code 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] = classFile.getConstantClassInfo(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 {@code InnerClasses} attribute (see JVMS 4.7.5). */ public static class InnerClassesAttribute extends AttributeInfo { private final List 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 The {@link Entry}s contained in this {@link InnerClassesAttribute}, see JVMS7 4.7.6 */ public List getEntries() { return this.entries; } private static AttributeInfo loadBody(short attributeNameIndex, DataInputStream dis) throws IOException { Entry[] ics = new Entry[dis.readUnsignedShort()]; // 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 {@code Runtime[In]visibleAnnotations} attribute (see JVMS8 4.7.16/17). */ public static class AnnotationsAttribute extends AttributeInfo { private final List annotations; AnnotationsAttribute(short attributeNameIndex) { super(attributeNameIndex); this.annotations = new ArrayList(); } AnnotationsAttribute(short attributeNameIndex, Annotation[] annotations) { super(attributeNameIndex); this.annotations = new ArrayList(Arrays.asList(annotations)); } /** * @return The {@link Annotation}s contained in this {@link AnnotationsAttribute}, see JVMS8 4.7.16/17 */ public List getAnnotations() { return this.annotations; } private static AttributeInfo loadBody(short attributeNameIndex, DataInputStream dis) throws IOException { Annotation[] as = new Annotation[dis.readUnsignedShort()]; // num_annotations for (short i = 0; i < as.length; ++i) { // annotations[num_annotations] as[i] = AnnotationsAttribute.loadAnnotation(dis); } return new AnnotationsAttribute(attributeNameIndex, as); } private static Annotation loadAnnotation(DataInputStream dis) throws IOException { return new Annotation( dis.readShort(), // type_index AnnotationsAttribute.loadElementValuePairs(dis) // num_element_value_pairs, element_value_pairs ); } private static Map loadElementValuePairs(DataInputStream dis) throws IOException { int numElementaluePairs = dis.readUnsignedShort(); // nul_element_value_pairs if (numElementaluePairs == 0) return Collections.emptyMap(); Map result = new HashMap(); for (int i = 0; i < numElementaluePairs; i++) { result.put( dis.readShort(), // element_name_index ClassFile.loadElementValue(dis) // value ); } return result; } // Implement "AttributeInfo". @Override protected void storeBody(DataOutputStream dos) throws IOException { dos.writeShort(this.annotations.size()); // num_annotations for (Annotation a : this.annotations) a.store(dos); } } /** * Representation of a {@code 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 {@code 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 {@code 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.readUnsignedShort()]; // 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 short startPc, lineNumber; /** * @param lineNumber 1...65535 */ public Entry(short startPc, short lineNumber) { this.startPc = startPc; this.lineNumber = lineNumber; } } } /** * Representation of a {@code 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 { Entry[] lvtes = new Entry[dis.readUnsignedShort()]; // local_variable_table_length for (short i = 0; i < lvtes.length; ++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 {@code 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 {@code AnnotationDefault} attribute (see JVMS8 4.7.22). */ public static class AnnotationDefaultAttribute extends AttributeInfo { private final ElementValue elementValue; public AnnotationDefaultAttribute(short attributeNameIndex, ElementValue elementValue) { super(attributeNameIndex); this.elementValue = elementValue; } private static AttributeInfo loadBody(short attributeNameIndex, DataInputStream dis) throws IOException { return new AnnotationDefaultAttribute( attributeNameIndex, ClassFile.loadElementValue(dis) ); } // Implement "AttributeInfo". @Override protected void storeBody(DataOutputStream dos) throws IOException { dos.writeByte(this.elementValue.getTag()); this.elementValue.store(dos); } } /** * Representation of an unmodifiable {@code Code} attribute, as read from a class file. */ public 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; } private 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.readUnsignedShort()]; // 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.readUnsignedShort()]; // 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; ExceptionTableEntry(short startPc, short endPc, short handlerPc, short catchType) { this.startPc = startPc; this.endPc = endPc; this.handlerPc = handlerPc; this.catchType = catchType; } } } /** * Representation of an unmodifiable {@code StackMapTable} attribute, as read from a class file. */ public static class StackMapTableAttribute extends AttributeInfo { private final StackMapFrame[] entries; public StackMapTableAttribute(short attributeNameIndex, StackMapFrame[] entries) { super(attributeNameIndex); this.entries = entries; } /** * Representation of an entry in the {@link StackMapTableAttribute}. */ public abstract static class StackMapFrame { /** * The {@code offset_delta} value that is implicit to all stack map frames; see JVMS8 4.7.4. */ final int offsetDelta; public StackMapFrame(int offsetDelta) { this.offsetDelta = offsetDelta; } /** * Invokes the "right" {@code visit...()} method of the {@link StackMapFrameVisitor}. */ public abstract T accept(StackMapFrameVisitor smfv); /** * Serializes this record and writes it to the given {@link DataOutputStream}. */ public abstract void store(DataOutputStream dos) throws IOException; } /** * @param The return type of {@link * StackMapFrame#accept(ClassFile.StackMapTableAttribute.StackMapFrameVisitor)} * @see StackMapFrame#accept(ClassFile.StackMapTableAttribute.StackMapFrameVisitor) */ public interface StackMapFrameVisitor { T visitSameFrame(SameFrame sf); // SUPPRESS CHECKSTYLE JavadocMethod:6 T visitSameLocals1StackItemFrame(SameLocals1StackItemFrame sl1sif); T visitSameLocals1StackItemFrameExtended(SameLocals1StackItemFrameExtended sl1sife); T visitChopFrame(ChopFrame cf); T visitSameFrameExtended(SameFrameExtended sfe); T visitAppendFrame(AppendFrame af); T visitFullFrame(FullFrame ff); } /** * Representation of the {@code same_frame} structure; see JVMS8 4.7.4. */ public static class SameFrame extends StackMapFrame { public SameFrame(int offsetDelta) { super(offsetDelta); } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(this.offsetDelta); } @Override public T accept(StackMapFrameVisitor smfv) { return smfv.visitSameFrame(this); } @Override public String toString() { return "same_frame (offsetDelta=" + this.offsetDelta + ")"; } } /** * Representation of the {@code same_locals_1_stack_item_frame} structure; see JVMS8 4.7.4. */ public static class SameLocals1StackItemFrame extends StackMapFrame { private final VerificationTypeInfo stack; public SameLocals1StackItemFrame(int offsetDelta, VerificationTypeInfo stack) { super(offsetDelta); this.stack = stack; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(this.offsetDelta + 64); this.stack.store(dos); } @Override public T accept(StackMapFrameVisitor smfv) { return smfv.visitSameLocals1StackItemFrame(this); } @Override public String toString() { return ( "same_locals_1_stack_item_frame(offsetDelta=" + this.offsetDelta + ", stack=[" + this.stack + "])" ); } } /** * Representation of the {@code same_locals_1_stack_item_frame_extended} structure; see JVMS8 4.7.4. */ public static class SameLocals1StackItemFrameExtended extends StackMapFrame { private final VerificationTypeInfo stack; public SameLocals1StackItemFrameExtended(int offsetDelta, VerificationTypeInfo stack) { super(offsetDelta); this.stack = stack; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(247); dos.writeShort(this.offsetDelta); this.stack.store(dos); } @Override public T accept(StackMapFrameVisitor smfv) { return smfv.visitSameLocals1StackItemFrameExtended(this); } @Override public String toString() { return ( "same_locals_1_stack_item_frame_extended(offsetDelta=" + this.offsetDelta + ", stack=[" + this.stack + "])" ); } } /** * Representation of the {@code chop_frame} structure; see JVMS8 4.7.4. */ public static class ChopFrame extends StackMapFrame { private final int k; public ChopFrame(int offsetDelta, int k) { super(offsetDelta); assert k >= 1 && k <= 3 : k; this.k = k; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(251 - this.k); // k = 3, 2, 1 => frame_type = 248, 249, 250 dos.writeShort(this.offsetDelta); } @Override public T accept(StackMapFrameVisitor smfv) { return smfv.visitChopFrame(this); } @Override public String toString() { return "chop_frame(offsetDelta=" + this.offsetDelta + ", locals-=" + this.k + ", stack=[])"; } } /** * Representation of the {@code same_frame_extended} structure; see JVMS8 4.7.4. */ public static class SameFrameExtended extends StackMapFrame { public SameFrameExtended(int offsetDelta) { super(offsetDelta); } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(251); dos.writeShort(this.offsetDelta); } @Override public T accept(StackMapFrameVisitor smfv) { return smfv.visitSameFrameExtended(this); } @Override public String toString() { return "same_frame_extended(offsetDelta=" + this.offsetDelta + ", stack=[])"; } } /** * Representation of the {@code append_frame} structure; see JVMS8 4.7.4. */ public static class AppendFrame extends StackMapFrame { private final VerificationTypeInfo[] locals; public AppendFrame(int offsetDelta, VerificationTypeInfo[] locals) { super(offsetDelta); assert locals.length >= 1 && locals.length <= 3; this.locals = locals; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(this.locals.length + 251); dos.writeShort(this.offsetDelta); StackMapTableAttribute.storeVerificationTypeInfos(this.locals, dos); } @Override public T accept(StackMapFrameVisitor smfv) { return smfv.visitAppendFrame(this); } @Override public String toString() { return "append_frame(locals+=" + Arrays.toString(this.locals) + ", stack=[])"; } } /** * Representation of the {@code full_frame} structure; see JVMS8 4.7.4. */ public static class FullFrame extends StackMapFrame { private final VerificationTypeInfo[] locals; private final VerificationTypeInfo[] stack; public FullFrame(int offsetDelta, VerificationTypeInfo[] locals, VerificationTypeInfo[] stack) { super(offsetDelta); this.locals = locals; this.stack = stack; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(255); dos.writeShort(this.offsetDelta); dos.writeShort(this.locals.length); StackMapTableAttribute.storeVerificationTypeInfos(this.locals, dos); dos.writeShort(this.stack.length); StackMapTableAttribute.storeVerificationTypeInfos(this.stack, dos); } @Override public T accept(StackMapFrameVisitor smfv) { return smfv.visitFullFrame(this); } @Override public String toString() { return ( "full_frame(offsetDelta=" + this.offsetDelta + ", locals=" + Arrays.toString(this.locals) + ", stack=" + Arrays.toString(this.stack) + ")" ); } } /** * Representation of the {@code verification_type_info} union; see JVMS8 4.7.4. */ public interface VerificationTypeInfo { /** * @return The category of the type (1 or 2) (JVMS11 2.11.1) */ int category(); /** * Writes this object to an {@link OutputStream}, in "class file" format. */ void store(DataOutputStream dos) throws IOException; } /** * Representation of the {@code top_variable_info} structure; see JVMS8 4.7.4. */ public static final VerificationTypeInfo TOP_VARIABLE_INFO = new VerificationTypeInfo() { @Override public int category() { return 1; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(0); } @Override public String toString() { return "top"; } @Override public int hashCode() { return 0; } @Override public boolean equals(Object obj) { return obj == this; } }; /** * Representation of the {@code integer_variable_info} structure; see JVMS8 4.7.4. */ public static final VerificationTypeInfo INTEGER_VARIABLE_INFO = new VerificationTypeInfo() { @Override public int category() { return 1; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(1); } @Override public String toString() { return "int"; } @Override public int hashCode() { return 1; } @Override public boolean equals(Object obj) { return obj == this; } }; /** * Representation of the {@code float_variable_info} structure; see JVMS8 4.7.4. */ public static final VerificationTypeInfo FLOAT_VARIABLE_INFO = new VerificationTypeInfo() { @Override public int category() { return 1; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(2); } @Override public String toString() { return "float"; } @Override public int hashCode() { return 2; } @Override public boolean equals(Object obj) { return obj == this; } }; /** * Representation of the {@code double_variable_info} structure; see JVMS8 4.7.4. */ public static final VerificationTypeInfo DOUBLE_VARIABLE_INFO = new VerificationTypeInfo() { @Override public int category() { return 2; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(3); } @Override public String toString() { return "double"; } @Override public int hashCode() { return 3; } @Override public boolean equals(Object obj) { return obj == this; } }; /** * Representation of the {@code long_variable_info} structure; see JVMS8 4.7.4. */ public static final VerificationTypeInfo LONG_VARIABLE_INFO = new VerificationTypeInfo() { @Override public int category() { return 2; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(4); } @Override public String toString() { return "long"; } @Override public int hashCode() { return 4; } @Override public boolean equals(Object obj) { return obj == this; } }; /** * Representation of the {@code null_variable_info} structure; see JVMS8 4.7.4. */ public static final VerificationTypeInfo NULL_VARIABLE_INFO = new VerificationTypeInfo() { @Override public int category() { return 1; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(5); } @Override public String toString() { return "null"; } @Override public int hashCode() { return 5; } @Override public boolean equals(Object obj) { return obj == this; } }; /** * Representation of the {@code uninitialized_this_variable_info} structure; see JVMS8 4.7.4. */ public static final VerificationTypeInfo UNINITIALIZED_THIS_VARIABLE_INFO = new VerificationTypeInfo() { @Override public int category() { return 1; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(6); } @Override public String toString() { return "uninitializedThis"; } @Override public int hashCode() { return 6; } @Override public boolean equals(Object obj) { return obj == this; } }; /** * Representation of the {@code object_variable_info} structure; see JVMS8 4.7.4. */ public static class ObjectVariableInfo implements VerificationTypeInfo { private final short constantClassInfoIndex; private final String fieldDescriptor; public ObjectVariableInfo(short constantClassInfoIndex, String fieldDescriptor) { this.constantClassInfoIndex = constantClassInfoIndex; this.fieldDescriptor = fieldDescriptor; } public short getConstantClassInfoIndex() { return this.constantClassInfoIndex; } @Override public int category() { return 1; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(7); dos.writeShort(this.constantClassInfoIndex); } @Override public String toString() { return "object(" + Descriptor.toString(this.fieldDescriptor) + ")"; } @Override public int hashCode() { return 7 ^ this.constantClassInfoIndex; } @Override public boolean equals(Object obj) { return ( obj instanceof ObjectVariableInfo && ((ObjectVariableInfo) obj).constantClassInfoIndex == this.constantClassInfoIndex ); } } /** * Representation of the {@code uninitialized_variable_info} structure; see JVMS8 4.7.4. */ public static class UninitializedVariableInfo implements VerificationTypeInfo { /** The code offset where the variable is declared. */ public short offset; public UninitializedVariableInfo(short offset) { this.offset = offset; } @Override public int category() { return 1; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeByte(8); dos.writeShort(this.offset); } @Override public String toString() { return "uninitialized(offset=" + this.offset + ")"; } @Override public int hashCode() { return 8 ^ this.offset; } @Override public boolean equals(Object obj) { return ( obj instanceof UninitializedVariableInfo && ((UninitializedVariableInfo) obj).offset == this.offset ); } } private static AttributeInfo loadBody(short attributeNameIndex, DataInputStream dis, ClassFile classFile) throws IOException { StackMapFrame[] entries = new StackMapFrame[dis.readUnsignedShort()]; // number_of_entries for (int i = 0; i < entries.length; ++i) { // entries int frameType = dis.readUnsignedByte(); StackMapFrame e = ( frameType <= 63 ? new SameFrame( frameType // offsetDelta ) : frameType <= 127 ? new SameLocals1StackItemFrame( frameType - 64, // offsetDelta StackMapTableAttribute.loadVerificationTypeInfo(dis, classFile) // stack ) : frameType <= 246 ? null : frameType == 247 ? new SameLocals1StackItemFrameExtended( dis.readUnsignedShort(), // offsetDelta StackMapTableAttribute.loadVerificationTypeInfo(dis, classFile) // stack ) : frameType <= 250 ? new ChopFrame( dis.readUnsignedShort(), // offsetDelta 251 - frameType // k ) : frameType == 251 ? new SameFrameExtended( dis.readUnsignedShort() // offsetDelta ) : frameType <= 254 ? new AppendFrame( dis.readUnsignedShort(), // offsetDelta StackMapTableAttribute.loadVerificationTypeInfos(dis, frameType - 251, classFile) // locals ) : frameType == 255 ? new FullFrame( dis.readUnsignedShort(), // offsetDelta SUPPRESS CHECKSTYLE LineLength:2 StackMapTableAttribute.loadVerificationTypeInfos(dis, dis.readUnsignedShort(), classFile), // locals StackMapTableAttribute.loadVerificationTypeInfos(dis, dis.readUnsignedShort(), classFile) // stack ) : null ); if (e == null) throw new ClassFileException("Invalid stack_map_frame frame_type " + frameType); entries[i] = e; } return new StackMapTableAttribute( attributeNameIndex, // attributeNameIndex entries // entries ); } private static void storeVerificationTypeInfos(VerificationTypeInfo[] vtis, DataOutputStream dos) throws IOException { for (VerificationTypeInfo vti : vtis) vti.store(dos); } private static VerificationTypeInfo[] loadVerificationTypeInfos(DataInputStream dis, int number, ClassFile classFile) throws IOException { VerificationTypeInfo[] result = new VerificationTypeInfo[number]; for (int i = 0; i < number; i++) result[i] = StackMapTableAttribute.loadVerificationTypeInfo(dis, classFile); return result; } private static VerificationTypeInfo loadVerificationTypeInfo(DataInputStream dis, ClassFile classFile) throws IOException { int tag = 0xff & dis.readByte(); switch (tag) { case 0: return StackMapTableAttribute.TOP_VARIABLE_INFO; case 1: return StackMapTableAttribute.INTEGER_VARIABLE_INFO; case 2: return StackMapTableAttribute.FLOAT_VARIABLE_INFO; case 3: return StackMapTableAttribute.DOUBLE_VARIABLE_INFO; case 4: return StackMapTableAttribute.LONG_VARIABLE_INFO; case 5: return StackMapTableAttribute.NULL_VARIABLE_INFO; case 6: return StackMapTableAttribute.UNINITIALIZED_THIS_VARIABLE_INFO; case 7: { short constantClassInfoIndex = dis.readShort(); return new ObjectVariableInfo( constantClassInfoIndex, classFile.getConstantClassInfo(constantClassInfoIndex).getName(classFile) ); } case 8: return new UninitializedVariableInfo(dis.readShort()); default: throw new ClassFileException("Invalid verification_type_info tag " + tag); } } @Override protected void storeBody(DataOutputStream dos) throws IOException { dos.writeShort(this.entries.length); // number_of_entries for (StackMapFrame smf : this.entries) smf.store(dos); // entries } } /** * Representation of the "element_value" structure (see JVMS8 4.7.16.1). */ public interface ElementValue { /** * @return The "tag" byte to use when storing this "value" in an "element_value" structure */ byte getTag(); /** * Writes this element value in an element-value-type dependent way; see JVMS8 4.7.16.1. The "tag" byte is * snot part of this writing! */ void store(DataOutputStream dos) throws IOException; /** * Invokes the respective method of the {@link ClassFile.ElementValue.Visitor}. */ @Nullable R accept(Visitor visitor) throws EX; /** * The visitor interface for the implementation of the "visitor" pattern. * * @param The type of the object that the "{@code visit*()}" methods return * @param The type of the exception that the "{@code visit*()}" methods may throw */ public interface Visitor extends ClassFile.ConstantElementValue.Visitor { // SUPPRESS CHECKSTYLE JavadocMethod:3 R visitAnnotation(Annotation subject) throws EX; R visitArrayElementValue(ArrayElementValue subject) throws EX; R visitEnumConstValue(EnumConstValue subject) throws EX; } } /** * Convenience class for element values that are constants (as opposed to annotations, enum constants and * arrays). */ public abstract static class ConstantElementValue implements ClassFile.ElementValue { private final byte tag; /** * The index of the constant pool entry that holds the constant value for this annotation element. */ public final short constantValueIndex; public ConstantElementValue(byte tag, short constantValueIndex) { this.tag = tag; this.constantValueIndex = constantValueIndex; } @Override public byte getTag() { return this.tag; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeShort(this.constantValueIndex); // const_value_index } @Override @Nullable public R accept(ClassFile.ElementValue.Visitor visitor) throws EX { return this.accept((ConstantElementValue.Visitor) visitor); } /** * Invokes the respective method of the {@link ConstantElementValue.Visitor}. */ @Nullable protected abstract R accept(ConstantElementValue.Visitor visitor) throws EX; /** * The visitor interface for the implementation of the "visitor" pattern. * * @param The type of the object that the "{@code visit*()}" methods return * @param The type of the exception that the "{@code visit*()}" methods may throw */ public interface Visitor { // SUPPRESS CHECKSTYLE JavadocMethod:10 R visitBooleanElementValue(BooleanElementValue subject) throws EX; R visitByteElementValue(ByteElementValue subject) throws EX; R visitCharElementValue(CharElementValue subject) throws EX; R visitClassElementValue(ClassElementValue subject) throws EX; R visitDoubleElementValue(DoubleElementValue subject) throws EX; R visitFloatElementValue(FloatElementValue subject) throws EX; R visitIntElementValue(IntElementValue subject) throws EX; R visitLongElementValue(LongElementValue subject) throws EX; R visitShortElementValue(ShortElementValue subject) throws EX; R visitStringElementValue(StringElementValue subject) throws EX; } } // SUPPRESS CHECKSTYLE LineLength|JavadocType:50 public static final class ByteElementValue extends ConstantElementValue { public ByteElementValue(short constantValueIndex) { super((byte) 'B', constantValueIndex); } @Override protected R accept(Visitor visitor) throws EX { return visitor.visitByteElementValue(this); } } public static final class CharElementValue extends ConstantElementValue { public CharElementValue(short constantValueIndex) { super((byte) 'C', constantValueIndex); } @Override protected R accept(Visitor visitor) throws EX { return visitor.visitCharElementValue(this); } } public static final class DoubleElementValue extends ConstantElementValue { public DoubleElementValue(short constantValueIndex) { super((byte) 'D', constantValueIndex); } @Override protected R accept(Visitor visitor) throws EX { return visitor.visitDoubleElementValue(this); } } public static final class FloatElementValue extends ConstantElementValue { public FloatElementValue(short constantValueIndex) { super((byte) 'F', constantValueIndex); } @Override protected R accept(Visitor visitor) throws EX { return visitor.visitFloatElementValue(this); } } public static final class IntElementValue extends ConstantElementValue { public IntElementValue(short constantValueIndex) { super((byte) 'I', constantValueIndex); } @Override protected R accept(Visitor visitor) throws EX { return visitor.visitIntElementValue(this); } } public static final class LongElementValue extends ConstantElementValue { public LongElementValue(short constantValueIndex) { super((byte) 'J', constantValueIndex); } @Override protected R accept(Visitor visitor) throws EX { return visitor.visitLongElementValue(this); } } public static final class ShortElementValue extends ConstantElementValue { public ShortElementValue(short constantValueIndex) { super((byte) 'S', constantValueIndex); } @Override protected R accept(Visitor visitor) throws EX { return visitor.visitShortElementValue(this); } } public static final class BooleanElementValue extends ConstantElementValue { public BooleanElementValue(short constantValueIndex) { super((byte) 'Z', constantValueIndex); } @Override protected R accept(Visitor visitor) throws EX { return visitor.visitBooleanElementValue(this); } } public static final class StringElementValue extends ConstantElementValue { public StringElementValue(short constantValueIndex) { super((byte) 's', constantValueIndex); } @Override protected R accept(Visitor visitor) throws EX { return visitor.visitStringElementValue(this); } } public static final class ClassElementValue extends ConstantElementValue { /** * @param constantValueIndex Index of a constant pool entry that is a CONSTANT_Utf8_info structure * representing a return descriptor */ public ClassElementValue(short constantValueIndex) { super((byte) 'c', constantValueIndex); } @Override protected R accept(Visitor visitor) throws EX { return visitor.visitClassElementValue(this); } } /** * Representation of the "enum_const_value" element in the "element_value" structure. */ public static final class EnumConstValue implements ClassFile.ElementValue { /** * {@code type_name_index}; index of a {@link ConstantUtf8Info} representing a field descriptor. */ public final short typeNameIndex; /** * {@code const_name_index}; index of a {@link ConstantUtf8Info} giveing the simple name of the enum * constant represented by this {@code element_value} structure. */ public final short constNameIndex; /** * @param typeNameIndex {@code type_name_index}; index of a {@link ConstantUtf8Info} representing a field * descriptor * @param constNameIndex {@code const_name_index}; index of a {@link ConstantUtf8Info} giveing the simple * name of the enum constant represented by this {@code element_value} structure */ public EnumConstValue(short typeNameIndex, short constNameIndex) { this.typeNameIndex = typeNameIndex; this.constNameIndex = constNameIndex; } @Override public byte getTag() { return 'e'; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeShort(this.typeNameIndex); // type_name_index dos.writeShort(this.constNameIndex); // const_name_index } @Override @Nullable public R accept(ClassFile.ElementValue.Visitor visitor) throws EX { return visitor.visitEnumConstValue(this); } } /** * Representation of the "array_value" structure. */ public static final class ArrayElementValue implements ClassFile.ElementValue { /** * The values of the elements of this array element value. */ public final ClassFile.ElementValue[] values; public ArrayElementValue(ClassFile.ElementValue[] values) { this.values = values; } @Override public byte getTag() { return '['; } @Override public void store(DataOutputStream dos) throws IOException { dos.writeShort(this.values.length); // num_values for (ClassFile.ElementValue ev : this.values) { // values[num_values] dos.writeByte(ev.getTag()); // tag ev.store(dos); // value } } @Override @Nullable public R accept(ClassFile.ElementValue.Visitor visitor) throws EX { return visitor.visitArrayElementValue(this); } } /** * The structure of the {@code annotations} array as described in JVMS8 4.7.16. */ public static class Annotation implements ClassFile.ElementValue { /** * The "type_index" field of the {@code annotation} type as described in JVMS8 4.7.16. The constant pool * entry at that index must be a CONSTANT_Utf8_info structure representing a field descriptor. */ public final short typeIndex; /** * The "element_value_pairs" field of the {@code annotation} type as described in JVMS8 4.7.16. * Key is the "{@code element_name_index}" (a constant pool index to a {@link ConstantUtf8Info}); * value is an {@link ClassFile.ElementValue}. */ public final Map elementValuePairs; /** * @param typeIndex UTF 8 constant pool entry index; field descriptor * @param elementValuePairs Maps element name index ({@link ConstantUtf8Info}) to {@link * ClassFile.ElementValue}s */ public Annotation(short typeIndex, Map elementValuePairs) { this.typeIndex = typeIndex; this.elementValuePairs = elementValuePairs; } @Override public byte getTag() { return '@'; } @Override public void store(DataOutputStream dos) throws IOException { // SUPPRESS CHECKSTYLE LineLength:4 dos.writeShort(this.typeIndex); // type_index dos.writeShort(this.elementValuePairs.size()); // num_element_value_pairs for ( // element_value_pairs[num_element_value_pairs] Map.Entry evps : this.elementValuePairs.entrySet() ) { Short elementNameIndex = (Short) evps.getKey(); ClassFile.ElementValue elementValue = (ClassFile.ElementValue) evps.getValue(); dos.writeShort(elementNameIndex); // element_name_index dos.writeByte(elementValue.getTag()); // value.tag elementValue.store(dos); // value.value } } @Override @Nullable public R accept(ClassFile.ElementValue.Visitor visitor) throws EX { return visitor.visitAnnotation(this); } } private static ClassFile.ElementValue loadElementValue(DataInputStream dis) throws IOException { byte tag = dis.readByte(); // tag switch (tag) { case 'B': return new ByteElementValue(dis.readShort()); case 'C': return new CharElementValue(dis.readShort()); case 'D': return new DoubleElementValue(dis.readShort()); case 'F': return new FloatElementValue(dis.readShort()); case 'I': return new IntElementValue(dis.readShort()); case 'J': return new LongElementValue(dis.readShort()); case 'S': return new ShortElementValue(dis.readShort()); case 'Z': return new BooleanElementValue(dis.readShort()); case 's': return new StringElementValue(dis.readShort()); case 'e': return new EnumConstValue(dis.readShort(), dis.readShort()); case 'c': return new ClassElementValue(dis.readShort()); case '@': return AnnotationsAttribute.loadAnnotation(dis); case '[': ClassFile.ElementValue[] values = new ClassFile.ElementValue[dis.readUnsignedShort()]; // num_values for (int i = 0; i < values.length; i++) values[i] = ClassFile.loadElementValue(dis); // values[num_values] return new ArrayElementValue(values); default: throw new ClassFileException("Invalid element-value-pair tag '" + (char) tag + "'"); } } public StackMapTableAttribute.ObjectVariableInfo newObjectVariableInfo(String fieldDescriptor) { return new StackMapTableAttribute.ObjectVariableInfo(this.addConstantClassInfo(fieldDescriptor), fieldDescriptor); } public StackMapTableAttribute.UninitializedVariableInfo newUninitializedVariableInfo(short offset) { return new StackMapTableAttribute.UninitializedVariableInfo(offset); } @Override public String toString() { try { return this.getConstantUtf8(this.getConstantClassInfo(this.thisClass).nameIndex); } catch (Exception e) { return super.toString(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy