/* * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.classfile; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; /** * See JVMS, section 4.5. * * <p><b>This is NOT part of any supported API. * If you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or * deletion without notice.</b> */ public class ConstantPool { public static class InvalidIndex extends ConstantPoolException { private static final long serialVersionUID = -4350294289300939730L; InvalidIndex(int index) { super(index); } @Override public String getMessage() { // i18n return "invalid index #" + index; } } public static class UnexpectedEntry extends ConstantPoolException { private static final long serialVersionUID = 6986335935377933211L; UnexpectedEntry(int index, int expected_tag, int found_tag) { super(index); this.expected_tag = expected_tag; this.found_tag = found_tag; } @Override public String getMessage() { // i18n? return "unexpected entry at #" + index + " -- expected tag " + expected_tag + ", found " + found_tag; } public final int expected_tag; public final int found_tag; } public static class InvalidEntry extends ConstantPoolException { private static final long serialVersionUID = 1000087545585204447L; InvalidEntry(int index, int tag) { super(index); this.tag = tag; } @Override public String getMessage() { // i18n? return "unexpected tag at #" + index + ": " + tag; } public final int tag; } public static class EntryNotFound extends ConstantPoolException { private static final long serialVersionUID = 2885537606468581850L; EntryNotFound(Object value) { super(-1); this.value = value; } @Override public String getMessage() { // i18n? return "value not found: " + value; } public final Object value; } public static final int CONSTANT_Utf8 = 1; public static final int CONSTANT_Integer = 3; public static final int CONSTANT_Float = 4; public static final int CONSTANT_Long = 5; public static final int CONSTANT_Double = 6; public static final int CONSTANT_Class = 7; public static final int CONSTANT_String = 8; public static final int CONSTANT_Fieldref = 9; public static final int CONSTANT_Methodref = 10; public static final int CONSTANT_InterfaceMethodref = 11; public static final int CONSTANT_NameAndType = 12; public static final int CONSTANT_MethodHandle = 15; public static final int CONSTANT_MethodType = 16; public static final int CONSTANT_InvokeDynamic = 18; public static enum RefKind { REF_getField(1), REF_getStatic(2), REF_putField(3), REF_putStatic(4), REF_invokeVirtual(5), REF_invokeStatic(6), REF_invokeSpecial(7), REF_newInvokeSpecial(8), REF_invokeInterface(9); public final int tag; RefKind(int tag) { this.tag = tag; } static RefKind getRefkind(int tag) { switch(tag) { case 1: return REF_getField; case 2: return REF_getStatic; case 3: return REF_putField; case 4: return REF_putStatic; case 5: return REF_invokeVirtual; case 6: return REF_invokeStatic; case 7: return REF_invokeSpecial; case 8: return REF_newInvokeSpecial; case 9: return REF_invokeInterface; default: return null; } } } ConstantPool(ClassReader cr) throws IOException, InvalidEntry { int count = cr.readUnsignedShort(); pool = new CPInfo[count]; for (int i = 1; i < count; i++) { int tag = cr.readUnsignedByte(); switch (tag) { case CONSTANT_Class: pool[i] = new CONSTANT_Class_info(this, cr); break; case CONSTANT_Double: pool[i] = new CONSTANT_Double_info(cr); i++; break; case CONSTANT_Fieldref: pool[i] = new CONSTANT_Fieldref_info(this, cr); break; case CONSTANT_Float: pool[i] = new CONSTANT_Float_info(cr); break; case CONSTANT_Integer: pool[i] = new CONSTANT_Integer_info(cr); break; case CONSTANT_InterfaceMethodref: pool[i] = new CONSTANT_InterfaceMethodref_info(this, cr); break; case CONSTANT_InvokeDynamic: pool[i] = new CONSTANT_InvokeDynamic_info(this, cr); break; case CONSTANT_Long: pool[i] = new CONSTANT_Long_info(cr); i++; break; case CONSTANT_MethodHandle: pool[i] = new CONSTANT_MethodHandle_info(this, cr); break; case CONSTANT_MethodType: pool[i] = new CONSTANT_MethodType_info(this, cr); break; case CONSTANT_Methodref: pool[i] = new CONSTANT_Methodref_info(this, cr); break; case CONSTANT_NameAndType: pool[i] = new CONSTANT_NameAndType_info(this, cr); break; case CONSTANT_String: pool[i] = new CONSTANT_String_info(this, cr); break; case CONSTANT_Utf8: pool[i] = new CONSTANT_Utf8_info(cr); break; default: throw new InvalidEntry(i, tag); } } } public ConstantPool(CPInfo[] pool) { this.pool = pool; } public int size() { return pool.length; } public int byteLength() { int length = 2; for (int i = 1; i < size(); ) { CPInfo cpInfo = pool[i]; length += cpInfo.byteLength(); i += cpInfo.size(); } return length; } public CPInfo get(int index) throws InvalidIndex { if (index <= 0 || index >= pool.length) throw new InvalidIndex(index); CPInfo info = pool[index]; if (info == null) { // this occurs for indices referencing the "second half" of an // 8 byte constant, such as CONSTANT_Double or CONSTANT_Long throw new InvalidIndex(index); } return pool[index]; } private CPInfo get(int index, int expected_type) throws InvalidIndex, UnexpectedEntry { CPInfo info = get(index); if (info.getTag() != expected_type) throw new UnexpectedEntry(index, expected_type, info.getTag()); return info; } public CONSTANT_Utf8_info getUTF8Info(int index) throws InvalidIndex, UnexpectedEntry { return ((CONSTANT_Utf8_info) get(index, CONSTANT_Utf8)); } public CONSTANT_Class_info getClassInfo(int index) throws InvalidIndex, UnexpectedEntry { return ((CONSTANT_Class_info) get(index, CONSTANT_Class)); } public CONSTANT_NameAndType_info getNameAndTypeInfo(int index) throws InvalidIndex, UnexpectedEntry { return ((CONSTANT_NameAndType_info) get(index, CONSTANT_NameAndType)); } public String getUTF8Value(int index) throws InvalidIndex, UnexpectedEntry { return getUTF8Info(index).value; } public int getUTF8Index(String value) throws EntryNotFound { for (int i = 1; i < pool.length; i++) { CPInfo info = pool[i]; if (info instanceof CONSTANT_Utf8_info && ((CONSTANT_Utf8_info) info).value.equals(value)) return i; } throw new EntryNotFound(value); } public Iterable<CPInfo> entries() { return new Iterable<CPInfo>() { public Iterator<CPInfo> iterator() { return new Iterator<CPInfo>() { public boolean hasNext() { return next < pool.length; } public CPInfo next() { current = pool[next]; switch (current.getTag()) { case CONSTANT_Double: case CONSTANT_Long: next += 2; break; default: next += 1; } return current; } public void remove() { throw new UnsupportedOperationException(); } private CPInfo current; private int next = 1; }; } }; } private CPInfo[] pool; public interface Visitor<R,P> { R visitClass(CONSTANT_Class_info info, P p); R visitDouble(CONSTANT_Double_info info, P p); R visitFieldref(CONSTANT_Fieldref_info info, P p); R visitFloat(CONSTANT_Float_info info, P p); R visitInteger(CONSTANT_Integer_info info, P p); R visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, P p); R visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, P p); R visitLong(CONSTANT_Long_info info, P p); R visitNameAndType(CONSTANT_NameAndType_info info, P p); R visitMethodref(CONSTANT_Methodref_info info, P p); R visitMethodHandle(CONSTANT_MethodHandle_info info, P p); R visitMethodType(CONSTANT_MethodType_info info, P p); R visitString(CONSTANT_String_info info, P p); R visitUtf8(CONSTANT_Utf8_info info, P p); } public static abstract class CPInfo { CPInfo() { this.cp = null; } CPInfo(ConstantPool cp) { this.cp = cp; } public abstract int getTag(); /** The number of slots in the constant pool used by this entry. * 2 for CONSTANT_Double and CONSTANT_Long; 1 for everything else. */ public int size() { return 1; } public abstract int byteLength(); public abstract <R,D> R accept(Visitor<R,D> visitor, D data); protected final ConstantPool cp; } public static abstract class CPRefInfo extends CPInfo { protected CPRefInfo(ConstantPool cp, ClassReader cr, int tag) throws IOException { super(cp); this.tag = tag; class_index = cr.readUnsignedShort(); name_and_type_index = cr.readUnsignedShort(); } protected CPRefInfo(ConstantPool cp, int tag, int class_index, int name_and_type_index) { super(cp); this.tag = tag; this.class_index = class_index; this.name_and_type_index = name_and_type_index; } public int getTag() { return tag; } public int byteLength() { return 5; } public CONSTANT_Class_info getClassInfo() throws ConstantPoolException { return cp.getClassInfo(class_index); } public String getClassName() throws ConstantPoolException { return cp.getClassInfo(class_index).getName(); } public CONSTANT_NameAndType_info getNameAndTypeInfo() throws ConstantPoolException { return cp.getNameAndTypeInfo(name_and_type_index); } public final int tag; public final int class_index; public final int name_and_type_index; } public static class CONSTANT_Class_info extends CPInfo { CONSTANT_Class_info(ConstantPool cp, ClassReader cr) throws IOException { super(cp); name_index = cr.readUnsignedShort(); } public CONSTANT_Class_info(ConstantPool cp, int name_index) { super(cp); this.name_index = name_index; } public int getTag() { return CONSTANT_Class; } public int byteLength() { return 3; } /** * Get the raw value of the class referenced by this constant pool entry. * This will either be the name of the class, in internal form, or a * descriptor for an array class. * @return the raw value of the class */ public String getName() throws ConstantPoolException { return cp.getUTF8Value(name_index); } /** * If this constant pool entry identifies either a class or interface type, * or a possibly multi-dimensional array of a class of interface type, * return the name of the class or interface in internal form. Otherwise, * (i.e. if this is a possibly multi-dimensional array of a primitive type), * return null. * @return the base class or interface name */ public String getBaseName() throws ConstantPoolException { String name = getName(); if (name.startsWith("[")) { int index = name.indexOf("[L"); if (index == -1) return null; return name.substring(index + 2, name.length() - 1); } else return name; } public int getDimensionCount() throws ConstantPoolException { String name = getName(); int count = 0; while (name.charAt(count) == '[') count++; return count; } @Override public String toString() { return "CONSTANT_Class_info[name_index: " + name_index + "]"; } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitClass(this, data); } public final int name_index; } public static class CONSTANT_Double_info extends CPInfo { CONSTANT_Double_info(ClassReader cr) throws IOException { value = cr.readDouble(); } public CONSTANT_Double_info(double value) { this.value = value; } public int getTag() { return CONSTANT_Double; } public int byteLength() { return 9; } @Override public int size() { return 2; } @Override public String toString() { return "CONSTANT_Double_info[value: " + value + "]"; } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitDouble(this, data); } public final double value; } public static class CONSTANT_Fieldref_info extends CPRefInfo { CONSTANT_Fieldref_info(ConstantPool cp, ClassReader cr) throws IOException { super(cp, cr, CONSTANT_Fieldref); } public CONSTANT_Fieldref_info(ConstantPool cp, int class_index, int name_and_type_index) { super(cp, CONSTANT_Fieldref, class_index, name_and_type_index); } @Override public String toString() { return "CONSTANT_Fieldref_info[class_index: " + class_index + ", name_and_type_index: " + name_and_type_index + "]"; } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitFieldref(this, data); } } public static class CONSTANT_Float_info extends CPInfo { CONSTANT_Float_info(ClassReader cr) throws IOException { value = cr.readFloat(); } public CONSTANT_Float_info(float value) { this.value = value; } public int getTag() { return CONSTANT_Float; } public int byteLength() { return 5; } @Override public String toString() { return "CONSTANT_Float_info[value: " + value + "]"; } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitFloat(this, data); } public final float value; } public static class CONSTANT_Integer_info extends CPInfo { CONSTANT_Integer_info(ClassReader cr) throws IOException { value = cr.readInt(); } public CONSTANT_Integer_info(int value) { this.value = value; } public int getTag() { return CONSTANT_Integer; } public int byteLength() { return 5; } @Override public String toString() { return "CONSTANT_Integer_info[value: " + value + "]"; } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitInteger(this, data); } public final int value; } public static class CONSTANT_InterfaceMethodref_info extends CPRefInfo { CONSTANT_InterfaceMethodref_info(ConstantPool cp, ClassReader cr) throws IOException { super(cp, cr, CONSTANT_InterfaceMethodref); } public CONSTANT_InterfaceMethodref_info(ConstantPool cp, int class_index, int name_and_type_index) { super(cp, CONSTANT_InterfaceMethodref, class_index, name_and_type_index); } @Override public String toString() { return "CONSTANT_InterfaceMethodref_info[class_index: " + class_index + ", name_and_type_index: " + name_and_type_index + "]"; } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitInterfaceMethodref(this, data); } } public static class CONSTANT_InvokeDynamic_info extends CPInfo { CONSTANT_InvokeDynamic_info(ConstantPool cp, ClassReader cr) throws IOException { super(cp); bootstrap_method_attr_index = cr.readUnsignedShort(); name_and_type_index = cr.readUnsignedShort(); } public CONSTANT_InvokeDynamic_info(ConstantPool cp, int bootstrap_method_index, int name_and_type_index) { super(cp); this.bootstrap_method_attr_index = bootstrap_method_index; this.name_and_type_index = name_and_type_index; } public int getTag() { return CONSTANT_InvokeDynamic; } public int byteLength() { return 5; } @Override public String toString() { return "CONSTANT_InvokeDynamic_info[bootstrap_method_index: " + bootstrap_method_attr_index + ", name_and_type_index: " + name_and_type_index + "]"; } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitInvokeDynamic(this, data); } public CONSTANT_NameAndType_info getNameAndTypeInfo() throws ConstantPoolException { return cp.getNameAndTypeInfo(name_and_type_index); } public final int bootstrap_method_attr_index; public final int name_and_type_index; } public static class CONSTANT_Long_info extends CPInfo { CONSTANT_Long_info(ClassReader cr) throws IOException { value = cr.readLong(); } public CONSTANT_Long_info(long value) { this.value = value; } public int getTag() { return CONSTANT_Long; } @Override public int size() { return 2; } public int byteLength() { return 9; } @Override public String toString() { return "CONSTANT_Long_info[value: " + value + "]"; } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitLong(this, data); } public final long value; } public static class CONSTANT_MethodHandle_info extends CPInfo { CONSTANT_MethodHandle_info(ConstantPool cp, ClassReader cr) throws IOException { super(cp); reference_kind = RefKind.getRefkind(cr.readUnsignedByte()); reference_index = cr.readUnsignedShort(); } public CONSTANT_MethodHandle_info(ConstantPool cp, RefKind ref_kind, int member_index) { super(cp); this.reference_kind = ref_kind; this.reference_index = member_index; } public int getTag() { return CONSTANT_MethodHandle; } public int byteLength() { return 4; } @Override public String toString() { return "CONSTANT_MethodHandle_info[ref_kind: " + reference_kind + ", member_index: " + reference_index + "]"; } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitMethodHandle(this, data); } public CPRefInfo getCPRefInfo() throws ConstantPoolException { int expected = CONSTANT_Methodref; int actual = cp.get(reference_index).getTag(); // allow these tag types also: switch (actual) { case CONSTANT_Fieldref: case CONSTANT_InterfaceMethodref: expected = actual; } return (CPRefInfo) cp.get(reference_index, expected); } public final RefKind reference_kind; public final int reference_index; } public static class CONSTANT_MethodType_info extends CPInfo { CONSTANT_MethodType_info(ConstantPool cp, ClassReader cr) throws IOException { super(cp); descriptor_index = cr.readUnsignedShort(); } public CONSTANT_MethodType_info(ConstantPool cp, int signature_index) { super(cp); this.descriptor_index = signature_index; } public int getTag() { return CONSTANT_MethodType; } public int byteLength() { return 3; } @Override public String toString() { return "CONSTANT_MethodType_info[signature_index: " + descriptor_index + "]"; } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitMethodType(this, data); } public String getType() throws ConstantPoolException { return cp.getUTF8Value(descriptor_index); } public final int descriptor_index; } public static class CONSTANT_Methodref_info extends CPRefInfo { CONSTANT_Methodref_info(ConstantPool cp, ClassReader cr) throws IOException { super(cp, cr, CONSTANT_Methodref); } public CONSTANT_Methodref_info(ConstantPool cp, int class_index, int name_and_type_index) { super(cp, CONSTANT_Methodref, class_index, name_and_type_index); } @Override public String toString() { return "CONSTANT_Methodref_info[class_index: " + class_index + ", name_and_type_index: " + name_and_type_index + "]"; } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitMethodref(this, data); } } public static class CONSTANT_NameAndType_info extends CPInfo { CONSTANT_NameAndType_info(ConstantPool cp, ClassReader cr) throws IOException { super(cp); name_index = cr.readUnsignedShort(); type_index = cr.readUnsignedShort(); } public CONSTANT_NameAndType_info(ConstantPool cp, int name_index, int type_index) { super(cp); this.name_index = name_index; this.type_index = type_index; } public int getTag() { return CONSTANT_NameAndType; } public int byteLength() { return 5; } public String getName() throws ConstantPoolException { return cp.getUTF8Value(name_index); } public String getType() throws ConstantPoolException { return cp.getUTF8Value(type_index); } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitNameAndType(this, data); } @Override public String toString() { return "CONSTANT_NameAndType_info[name_index: " + name_index + ", type_index: " + type_index + "]"; } public final int name_index; public final int type_index; } public static class CONSTANT_String_info extends CPInfo { CONSTANT_String_info(ConstantPool cp, ClassReader cr) throws IOException { super(cp); string_index = cr.readUnsignedShort(); } public CONSTANT_String_info(ConstantPool cp, int string_index) { super(cp); this.string_index = string_index; } public int getTag() { return CONSTANT_String; } public int byteLength() { return 3; } public String getString() throws ConstantPoolException { return cp.getUTF8Value(string_index); } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitString(this, data); } @Override public String toString() { return "CONSTANT_String_info[class_index: " + string_index + "]"; } public final int string_index; } public static class CONSTANT_Utf8_info extends CPInfo { CONSTANT_Utf8_info(ClassReader cr) throws IOException { value = cr.readUTF(); } public CONSTANT_Utf8_info(String value) { this.value = value; } public int getTag() { return CONSTANT_Utf8; } public int byteLength() { class SizeOutputStream extends OutputStream { @Override public void write(int b) { size++; } int size; } SizeOutputStream sizeOut = new SizeOutputStream(); DataOutputStream out = new DataOutputStream(sizeOut); try { out.writeUTF(value); } catch (IOException ignore) { } return 1 + sizeOut.size; } @Override public String toString() { if (value.length() < 32 && isPrintableAscii(value)) return "CONSTANT_Utf8_info[value: \"" + value + "\"]"; else return "CONSTANT_Utf8_info[value: (" + value.length() + " chars)]"; } static boolean isPrintableAscii(String s) { for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c < 32 || c >= 127) return false; } return true; } public <R, D> R accept(Visitor<R, D> visitor, D data) { return visitor.visitUtf8(this, data); } public final String value; } }