* section 3.6.1. * * Note that the original version of the JVMS contained a definition of * this abstract notion of a "word" in section 3.4, but that definition * was removed for the second edition. */ private static int getWordsPerType(Class type) { if (type == long.class || type == double.class) { return 2; } else { return 1; } } /** * Add to the given list all of the types in the "from" array that * are not already contained in the list and are assignable to at * least one of the types in the "with" array. * * This method is useful for computing the greatest common set of * declared exceptions from duplicate methods inherited from * different interfaces. */ private static void collectCompatibleTypes(Class<?>[] from, Class<?>[] with, List<Class<?>> list) { for (int i = 0; i < from.length; i++) { if (!list.contains(from[i])) { for (int j = 0; j < with.length; j++) { if (with[j].isAssignableFrom(from[i])) { list.add(from[i]); break; } } } } } /** * Given the exceptions declared in the throws clause of a proxy method, * compute the exceptions that need to be caught from the invocation * handler's invoke method and rethrown intact in the method's * implementation before catching other Throwables and wrapping them * in UndeclaredThrowableExceptions. * * The exceptions to be caught are returned in a List object. Each * exception in the returned list is guaranteed to not be a subclass of * any of the other exceptions in the list, so the catch blocks for * these exceptions may be generated in any order relative to each other. * * Error and RuntimeException are each always contained by the returned * list (if none of their superclasses are contained), since those * unchecked exceptions should always be rethrown intact, and thus their * subclasses will never appear in the returned list. * * The returned List will be empty if java.lang.Throwable is in the * given list of declared exceptions, indicating that no exceptions * need to be caught. */ private static List<Class<?>> computeUniqueCatchList(Class<?>[] exceptions) { List<Class<?>> uniqueList = new ArrayList<Class<?>>(); // unique exceptions to catch uniqueList.add(Error.class); // always catch/rethrow these uniqueList.add(RuntimeException.class); nextException: for (int i = 0; i < exceptions.length; i++) { Class<?> ex = exceptions[i]; if (ex.isAssignableFrom(Throwable.class)) { /* * If Throwable is declared to be thrown by the proxy method, * then no catch blocks are necessary, because the invoke * can, at most, throw Throwable anyway. */ uniqueList.clear(); break; } else if (!Throwable.class.isAssignableFrom(ex)) { /* * Ignore types that cannot be thrown by the invoke method. */ continue; } /* * Compare this exception against the current list of * exceptions that need to be caught: */ for (int j = 0; j < uniqueList.size();) { Class<?> ex2 = uniqueList.get(j); if (ex2.isAssignableFrom(ex)) { /* * if a superclass of this exception is already on * the list to catch, then ignore this one and continue; */ continue nextException; } else if (ex.isAssignableFrom(ex2)) { /* * if a subclass of this exception is on the list * to catch, then remove it; */ uniqueList.remove(j); } else { j++; // else continue comparing. } } // This exception is unique (so far): add it to the list to catch. uniqueList.add(ex); } return uniqueList; } /** * A PrimitiveTypeInfo object contains assorted information about * a primitive type in its public fields. The struct for a particular * primitive type can be obtained using the static "get" method. */ private static class PrimitiveTypeInfo { /** "base type" used in various descriptors (see JVMS section 4.3.2) */ public String baseTypeString; /** name of corresponding wrapper class */ public String wrapperClassName; /** method descriptor for wrapper class "valueOf" factory method */ public String wrapperValueOfDesc; /** name of wrapper class method for retrieving primitive value */ public String unwrapMethodName; /** descriptor of same method */ public String unwrapMethodDesc; private static Map<Class,PrimitiveTypeInfo> table = new HashMap<Class,PrimitiveTypeInfo>(); static { add(byte.class, Byte.class); add(char.class, Character.class); add(double.class, Double.class); add(float.class, Float.class); add(int.class, Integer.class); add(long.class, Long.class); add(short.class, Short.class); add(boolean.class, Boolean.class); } private static void add(Class primitiveClass, Class wrapperClass) { table.put(primitiveClass, new PrimitiveTypeInfo(primitiveClass, wrapperClass)); } private PrimitiveTypeInfo(Class primitiveClass, Class wrapperClass) { assert primitiveClass.isPrimitive(); baseTypeString = Array.newInstance(primitiveClass, 0) .getClass().getName().substring(1); wrapperClassName = dotToSlash(wrapperClass.getName()); wrapperValueOfDesc = "(" + baseTypeString + ")L" + wrapperClassName + ";"; unwrapMethodName = primitiveClass.getName() + "Value"; unwrapMethodDesc = "()" + baseTypeString; } public static PrimitiveTypeInfo get(Class cl) { return table.get(cl); } } /** * A ConstantPool object represents the constant pool of a class file * being generated. This representation of a constant pool is designed * specifically for use by ProxyGenerator; in particular, it assumes * that constant pool entries will not need to be resorted (for example, * by their type, as the Java compiler does), so that the final index * value can be assigned and used when an entry is first created. * * Note that new entries cannot be created after the constant pool has * been written to a class file. To prevent such logic errors, a * ConstantPool instance can be marked "read only", so that further * attempts to add new entries will fail with a runtime exception. * * See JVMS section 4.4 for more information about the constant pool * of a class file. */ private static class ConstantPool { /** * list of constant pool entries, in constant pool index order. * * This list is used when writing the constant pool to a stream * and for assigning the next index value. Note that element 0 * of this list corresponds to constant pool index 1. */ private List<Entry> pool = new ArrayList<Entry>(32); /** * maps constant pool data of all types to constant pool indexes. * * This map is used to look up the index of an existing entry for * values of all types. */ private Map<Object,Short> map = new HashMap<Object,Short>(16); /** true if no new constant pool entries may be added */ private boolean readOnly = false; /** * Get or assign the index for a CONSTANT_Utf8 entry. */ public short getUtf8(String s) { if (s == null) { throw new NullPointerException(); } return getValue(s); } /** * Get or assign the index for a CONSTANT_Integer entry. */ public short getInteger(int i) { return getValue(new Integer(i)); } /** * Get or assign the index for a CONSTANT_Float entry. */ public short getFloat(float f) { return getValue(new Float(f)); } /** * Get or assign the index for a CONSTANT_Class entry. */ public short getClass(String name) { short utf8Index = getUtf8(name); return getIndirect(new IndirectEntry( CONSTANT_CLASS, utf8Index)); } /** * Get or assign the index for a CONSTANT_String entry. */ public short getString(String s) { short utf8Index = getUtf8(s); return getIndirect(new IndirectEntry( CONSTANT_STRING, utf8Index)); } /** * Get or assign the index for a CONSTANT_FieldRef entry. */ public short getFieldRef(String className, String name, String descriptor) { short classIndex = getClass(className); short nameAndTypeIndex = getNameAndType(name, descriptor); return getIndirect(new IndirectEntry( CONSTANT_FIELD, classIndex, nameAndTypeIndex)); } /** * Get or assign the index for a CONSTANT_MethodRef entry. */ public short getMethodRef(String className, String name, String descriptor) { short classIndex = getClass(className); short nameAndTypeIndex = getNameAndType(name, descriptor); return getIndirect(new IndirectEntry( CONSTANT_METHOD, classIndex, nameAndTypeIndex)); } /** * Get or assign the index for a CONSTANT_InterfaceMethodRef entry. */ public short getInterfaceMethodRef(String className, String name, String descriptor) { short classIndex = getClass(className); short nameAndTypeIndex = getNameAndType(name, descriptor); return getIndirect(new IndirectEntry( CONSTANT_INTERFACEMETHOD, classIndex, nameAndTypeIndex)); } /** * Get or assign the index for a CONSTANT_NameAndType entry. */ public short getNameAndType(String name, String descriptor) { short nameIndex = getUtf8(name); short descriptorIndex = getUtf8(descriptor); return getIndirect(new IndirectEntry( CONSTANT_NAMEANDTYPE, nameIndex, descriptorIndex)); } /** * Set this ConstantPool instance to be "read only". * * After this method has been called, further requests to get * an index for a non-existent entry will cause an InternalError * to be thrown instead of creating of the entry. */ public void setReadOnly() { readOnly = true; } /** * Write this constant pool to a stream as part of * the class file format. * * This consists of writing the "constant_pool_count" and * "constant_pool[]" items of the "ClassFile" structure, as * described in JVMS section 4.1. */ public void write(OutputStream out) throws IOException { DataOutputStream dataOut = new DataOutputStream(out); // constant_pool_count: number of entries plus one dataOut.writeShort(pool.size() + 1); for (Entry e : pool) { e.write(dataOut); } } /** * Add a new constant pool entry and return its index. */ private short addEntry(Entry entry) { pool.add(entry); /* * Note that this way of determining the index of the * added entry is wrong if this pool supports * CONSTANT_Long or CONSTANT_Double entries. */ if (pool.size() >= 65535) { throw new IllegalArgumentException( "constant pool size limit exceeded"); } return (short) pool.size(); } /** * Get or assign the index for an entry of a type that contains * a direct value. The type of the given object determines the * type of the desired entry as follows: * * java.lang.String CONSTANT_Utf8 * java.lang.Integer CONSTANT_Integer * java.lang.Float CONSTANT_Float * java.lang.Long CONSTANT_Long * java.lang.Double CONSTANT_DOUBLE */ private short getValue(Object key) { Short index = map.get(key); if (index != null) { return index.shortValue(); } else { if (readOnly) { throw new InternalError( "late constant pool addition: " + key); } short i = addEntry(new ValueEntry(key)); map.put(key, new Short(i)); return i; } } /** * Get or assign the index for an entry of a type that contains * references to other constant pool entries. */ private short getIndirect(IndirectEntry e) { Short index = map.get(e); if (index != null) { return index.shortValue(); } else { if (readOnly) { throw new InternalError("late constant pool addition"); } short i = addEntry(e); map.put(e, new Short(i)); return i; } } /** * Entry is the abstact superclass of all constant pool entry types * that can be stored in the "pool" list; its purpose is to define a * common method for writing constant pool entries to a class file. */ private static abstract class Entry { public abstract void write(DataOutputStream out) throws IOException; } /** * ValueEntry represents a constant pool entry of a type that * contains a direct value (see the comments for the "getValue" * method for a list of such types). * * ValueEntry objects are not used as keys for their entries in the * Map "map", so no useful hashCode or equals methods are defined. */ private static class ValueEntry extends Entry { private Object value; public ValueEntry(Object value) { this.value = value; } public void write(DataOutputStream out) throws IOException { if (value instanceof String) { out.writeByte(CONSTANT_UTF8); out.writeUTF((String) value); } else if (value instanceof Integer) { out.writeByte(CONSTANT_INTEGER); out.writeInt(((Integer) value).intValue()); } else if (value instanceof Float) { out.writeByte(CONSTANT_FLOAT); out.writeFloat(((Float) value).floatValue()); } else if (value instanceof Long) { out.writeByte(CONSTANT_LONG); out.writeLong(((Long) value).longValue()); } else if (value instanceof Double) { out.writeDouble(CONSTANT_DOUBLE); out.writeDouble(((Double) value).doubleValue()); } else { throw new InternalError("bogus value entry: " + value); } } } /** * IndirectEntry represents a constant pool entry of a type that * references other constant pool entries, i.e., the following types: * * CONSTANT_Class, CONSTANT_String, CONSTANT_Fieldref, * CONSTANT_Methodref, CONSTANT_InterfaceMethodref, and * CONSTANT_NameAndType. * * Each of these entry types contains either one or two indexes of * other constant pool entries. * * IndirectEntry objects are used as the keys for their entries in * the Map "map", so the hashCode and equals methods are overridden * to allow matching. */ private static class IndirectEntry extends Entry { private int tag; private short index0; private short index1; /** * Construct an IndirectEntry for a constant pool entry type * that contains one index of another entry. */ public IndirectEntry(int tag, short index) { this.tag = tag; this.index0 = index; this.index1 = 0; } /** * Construct an IndirectEntry for a constant pool entry type * that contains two indexes for other entries. */ public IndirectEntry(int tag, short index0, short index1) { this.tag = tag; this.index0 = index0; this.index1 = index1; } public void write(DataOutputStream out) throws IOException { out.writeByte(tag); out.writeShort(index0); /* * If this entry type contains two indexes, write * out the second, too. */ if (tag == CONSTANT_FIELD || tag == CONSTANT_METHOD || tag == CONSTANT_INTERFACEMETHOD || tag == CONSTANT_NAMEANDTYPE) { out.writeShort(index1); } } public int hashCode() { return tag + index0 + index1; } public boolean equals(Object obj) { if (obj instanceof IndirectEntry) { IndirectEntry other = (IndirectEntry) obj; if (tag == other.tag && index0 == other.index0 && index1 == other.index1)Lines 1501–2000 of 2009. Previous lines Load more
* section 3.6.1. * * Note that the original version of the JVMS contained a definition of * this abstract notion of a "word" in section 3.4, but that definition * was removed for the second edition. */ private static int getWordsPerType(Class type) { if (type == long.class || type == double.class) { return 2; } else { return 1; } } /** * Add to the given list all of the types in the "from" array that * are not already contained in the list and are assignable to at * least one of the types in the "with" array. * * This method is useful for computing the greatest common set of * declared exceptions from duplicate methods inherited from * different interfaces. */ private static void collectCompatibleTypes(Class<?>[] from, Class<?>[] with, List<Class<?>> list) { for (int i = 0; i < from.length; i++) { if (!list.contains(from[i])) { for (int j = 0; j < with.length; j++) { if (with[j].isAssignableFrom(from[i])) { list.add(from[i]); break; } } } } } /** * Given the exceptions declared in the throws clause of a proxy method, * compute the exceptions that need to be caught from the invocation * handler's invoke method and rethrown intact in the method's * implementation before catching other Throwables and wrapping them * in UndeclaredThrowableExceptions. * * The exceptions to be caught are returned in a List object. Each * exception in the returned list is guaranteed to not be a subclass of * any of the other exceptions in the list, so the catch blocks for * these exceptions may be generated in any order relative to each other. * * Error and RuntimeException are each always contained by the returned * list (if none of their superclasses are contained), since those * unchecked exceptions should always be rethrown intact, and thus their * subclasses will never appear in the returned list. * * The returned List will be empty if java.lang.Throwable is in the * given list of declared exceptions, indicating that no exceptions * need to be caught. */ private static List<Class<?>> computeUniqueCatchList(Class<?>[] exceptions) { List<Class<?>> uniqueList = new ArrayList<Class<?>>(); // unique exceptions to catch uniqueList.add(Error.class); // always catch/rethrow these uniqueList.add(RuntimeException.class); nextException: for (int i = 0; i < exceptions.length; i++) { Class<?> ex = exceptions[i]; if (ex.isAssignableFrom(Throwable.class)) { /* * If Throwable is declared to be thrown by the proxy method, * then no catch blocks are necessary, because the invoke * can, at most, throw Throwable anyway. */ uniqueList.clear(); break; } else if (!Throwable.class.isAssignableFrom(ex)) { /* * Ignore types that cannot be thrown by the invoke method. */ continue; } /* * Compare this exception against the current list of * exceptions that need to be caught: */ for (int j = 0; j < uniqueList.size();) { Class<?> ex2 = uniqueList.get(j); if (ex2.isAssignableFrom(ex)) { /* * if a superclass of this exception is already on * the list to catch, then ignore this one and continue; */ continue nextException; } else if (ex.isAssignableFrom(ex2)) { /* * if a subclass of this exception is on the list * to catch, then remove it; */ uniqueList.remove(j); } else { j++; // else continue comparing. } } // This exception is unique (so far): add it to the list to catch. uniqueList.add(ex); } return uniqueList; } /** * A PrimitiveTypeInfo object contains assorted information about * a primitive type in its public fields. The struct for a particular * primitive type can be obtained using the static "get" method. */ private static class PrimitiveTypeInfo { /** "base type" used in various descriptors (see JVMS section 4.3.2) */ public String baseTypeString; /** name of corresponding wrapper class */ public String wrapperClassName; /** method descriptor for wrapper class "valueOf" factory method */ public String wrapperValueOfDesc; /** name of wrapper class method for retrieving primitive value */ public String unwrapMethodName; /** descriptor of same method */ public String unwrapMethodDesc; private static Map<Class,PrimitiveTypeInfo> table = new HashMap<Class,PrimitiveTypeInfo>(); static { add(byte.class, Byte.class); add(char.class, Character.class); add(double.class, Double.class); add(float.class, Float.class); add(int.class, Integer.class); add(long.class, Long.class); add(short.class, Short.class); add(boolean.class, Boolean.class); } private static void add(Class primitiveClass, Class wrapperClass) { table.put(primitiveClass, new PrimitiveTypeInfo(primitiveClass, wrapperClass)); } private PrimitiveTypeInfo(Class primitiveClass, Class wrapperClass) { assert primitiveClass.isPrimitive(); baseTypeString = Array.newInstance(primitiveClass, 0) .getClass().getName().substring(1); wrapperClassName = dotToSlash(wrapperClass.getName()); wrapperValueOfDesc = "(" + baseTypeString + ")L" + wrapperClassName + ";"; unwrapMethodName = primitiveClass.getName() + "Value"; unwrapMethodDesc = "()" + baseTypeString; } public static PrimitiveTypeInfo get(Class cl) { return table.get(cl); } } /** * A ConstantPool object represents the constant pool of a class file * being generated. This representation of a constant pool is designed * specifically for use by ProxyGenerator; in particular, it assumes * that constant pool entries will not need to be resorted (for example, * by their type, as the Java compiler does), so that the final index * value can be assigned and used when an entry is first created. * * Note that new entries cannot be created after the constant pool has * been written to a class file. To prevent such logic errors, a * ConstantPool instance can be marked "read only", so that further * attempts to add new entries will fail with a runtime exception. * * See JVMS section 4.4 for more information about the constant pool * of a class file. */ private static class ConstantPool { /** * list of constant pool entries, in constant pool index order. * * This list is used when writing the constant pool to a stream * and for assigning the next index value. Note that element 0 * of this list corresponds to constant pool index 1. */ private List<Entry> pool = new ArrayList<Entry>(32); /** * maps constant pool data of all types to constant pool indexes. * * This map is used to look up the index of an existing entry for * values of all types. */ private Map<Object,Short> map = new HashMap<Object,Short>(16); /** true if no new constant pool entries may be added */ private boolean readOnly = false; /** * Get or assign the index for a CONSTANT_Utf8 entry. */ public short getUtf8(String s) { if (s == null) { throw new NullPointerException(); } return getValue(s); } /** * Get or assign the index for a CONSTANT_Integer entry. */ public short getInteger(int i) { return getValue(new Integer(i)); } /** * Get or assign the index for a CONSTANT_Float entry. */ public short getFloat(float f) { return getValue(new Float(f)); } /** * Get or assign the index for a CONSTANT_Class entry. */ public short getClass(String name) { short utf8Index = getUtf8(name); return getIndirect(new IndirectEntry( CONSTANT_CLASS, utf8Index)); } /** * Get or assign the index for a CONSTANT_String entry. */ public short getString(String s) { short utf8Index = getUtf8(s); return getIndirect(new IndirectEntry( CONSTANT_STRING, utf8Index)); } /** * Get or assign the index for a CONSTANT_FieldRef entry. */ public short getFieldRef(String className, String name, String descriptor) { short classIndex = getClass(className); short nameAndTypeIndex = getNameAndType(name, descriptor); return getIndirect(new IndirectEntry( CONSTANT_FIELD, classIndex, nameAndTypeIndex)); } /** * Get or assign the index for a CONSTANT_MethodRef entry. */ public short getMethodRef(String className, String name, String descriptor) { short classIndex = getClass(className); short nameAndTypeIndex = getNameAndType(name, descriptor); return getIndirect(new IndirectEntry( CONSTANT_METHOD, classIndex, nameAndTypeIndex)); } /** * Get or assign the index for a CONSTANT_InterfaceMethodRef entry. */ public short getInterfaceMethodRef(String className, String name, String descriptor) { short classIndex = getClass(className); short nameAndTypeIndex = getNameAndType(name, descriptor); return getIndirect(new IndirectEntry( CONSTANT_INTERFACEMETHOD, classIndex, nameAndTypeIndex)); } /** * Get or assign the index for a CONSTANT_NameAndType entry. */ public short getNameAndType(String name, String descriptor) { short nameIndex = getUtf8(name); short descriptorIndex = getUtf8(descriptor); return getIndirect(new IndirectEntry( CONSTANT_NAMEANDTYPE, nameIndex, descriptorIndex)); } /** * Set this ConstantPool instance to be "read only". * * After this method has been called, further requests to get * an index for a non-existent entry will cause an InternalError * to be thrown instead of creating of the entry. */ public void setReadOnly() { readOnly = true; } /** * Write this constant pool to a stream as part of * the class file format. * * This consists of writing the "constant_pool_count" and * "constant_pool[]" items of the "ClassFile" structure, as * described in JVMS section 4.1. */ public void write(OutputStream out) throws IOException { DataOutputStream dataOut = new DataOutputStream(out); // constant_pool_count: number of entries plus one dataOut.writeShort(pool.size() + 1); for (Entry e : pool) { e.write(dataOut); } } /** * Add a new constant pool entry and return its index. */ private short addEntry(Entry entry) { pool.add(entry); /* * Note that this way of determining the index of the * added entry is wrong if this pool supports * CONSTANT_Long or CONSTANT_Double entries. */ if (pool.size() >= 65535) { throw new IllegalArgumentException( "constant pool size limit exceeded"); } return (short) pool.size(); } /** * Get or assign the index for an entry of a type that contains * a direct value. The type of the given object determines the * type of the desired entry as follows: * * java.lang.String CONSTANT_Utf8 * java.lang.Integer CONSTANT_Integer * java.lang.Float CONSTANT_Float * java.lang.Long CONSTANT_Long * java.lang.Double CONSTANT_DOUBLE */ private short getValue(Object key) { Short index = map.get(key); if (index != null) { return index.shortValue(); } else { if (readOnly) { throw new InternalError( "late constant pool addition: " + key); } short i = addEntry(new ValueEntry(key)); map.put(key, new Short(i)); return i; } } /** * Get or assign the index for an entry of a type that contains * references to other constant pool entries. */ private short getIndirect(IndirectEntry e) { Short index = map.get(e); if (index != null) { return index.shortValue(); } else { if (readOnly) { throw new InternalError("late constant pool addition"); } short i = addEntry(e); map.put(e, new Short(i)); return i; } } /** * Entry is the abstact superclass of all constant pool entry types * that can be stored in the "pool" list; its purpose is to define a * common method for writing constant pool entries to a class file. */ private static abstract class Entry { public abstract void write(DataOutputStream out) throws IOException; } /** * ValueEntry represents a constant pool entry of a type that * contains a direct value (see the comments for the "getValue" * method for a list of such types). * * ValueEntry objects are not used as keys for their entries in the * Map "map", so no useful hashCode or equals methods are defined. */ private static class ValueEntry extends Entry { private Object value; public ValueEntry(Object value) { this.value = value; } public void write(DataOutputStream out) throws IOException { if (value instanceof String) { out.writeByte(CONSTANT_UTF8); out.writeUTF((String) value); } else if (value instanceof Integer) { out.writeByte(CONSTANT_INTEGER); out.writeInt(((Integer) value).intValue()); } else if (value instanceof Float) { out.writeByte(CONSTANT_FLOAT); out.writeFloat(((Float) value).floatValue()); } else if (value instanceof Long) { out.writeByte(CONSTANT_LONG); out.writeLong(((Long) value).longValue()); } else if (value instanceof Double) { out.writeDouble(CONSTANT_DOUBLE); out.writeDouble(((Double) value).doubleValue()); } else { throw new InternalError("bogus value entry: " + value); } } } /** * IndirectEntry represents a constant pool entry of a type that * references other constant pool entries, i.e., the following types: * * CONSTANT_Class, CONSTANT_String, CONSTANT_Fieldref, * CONSTANT_Methodref, CONSTANT_InterfaceMethodref, and * CONSTANT_NameAndType. * * Each of these entry types contains either one or two indexes of * other constant pool entries. * * IndirectEntry objects are used as the keys for their entries in * the Map "map", so the hashCode and equals methods are overridden * to allow matching. */ private static class IndirectEntry extends Entry { private int tag; private short index0; private short index1; /** * Construct an IndirectEntry for a constant pool entry type * that contains one index of another entry. */ public IndirectEntry(int tag, short index) { this.tag = tag; this.index0 = index; this.index1 = 0; } /** * Construct an IndirectEntry for a constant pool entry type * that contains two indexes for other entries. */ public IndirectEntry(int tag, short index0, short index1) { this.tag = tag; this.index0 = index0; this.index1 = index1; } public void write(DataOutputStream out) throws IOException { out.writeByte(tag); out.writeShort(index0); /* * If this entry type contains two indexes, write * out the second, too. */ if (tag == CONSTANT_FIELD || tag == CONSTANT_METHOD || tag == CONSTANT_INTERFACEMETHOD || tag == CONSTANT_NAMEANDTYPE) { out.writeShort(index1); } } public int hashCode() { return tag + index0 + index1; } public boolean equals(Object obj) { if (obj instanceof IndirectEntry) { IndirectEntry other = (IndirectEntry) obj; if (tag == other.tag && index0 == other.index0 && index1 == other.index1)