/* * Copyright (c) 2003, 2018, 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 jdk.internal.reflect; import jdk.internal.vm.annotation.Stable; import java.lang.constant.*; import java.lang.constant.DirectMethodHandleDesc.Kind; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.util.Arrays; /** Provides reflective access to the constant pools of classes. Currently this is needed to provide reflective access to annotations and bootstrap methods but may be used by other internal subsystems in the future.

When applying this API to internal subsystems, take special account of interactions with the JVMTI class redefinition features. If a class reflects on its constant pool, and then is redefined to have a different constant pool, the data reported by the reflected constant pool may become out of date. */ public class ConstantPool { // Number of entries in this constant pool (= maximum valid constant pool index) public int getSize() { return getSize1(); } // Class holding this CP public Class getHolder() { return getHolder1(); } public Class getClassAt (int index) { return (Class) getRefAt(index, Tag.CLASS, true); } public Class getClassAtIfLoaded (int index) { return (Class) getRefAt(index, Tag.CLASS, false); } // Returns a class reference index for a method or a field. public int getClassRefIndexAt(int index) { return getWordAt1(index, 0); } // Returns either a Method or Constructor. // Static initializers are returned as Method objects. public Member getMethodAt (int index) { return (Member) getRefAt(index, Tag.METHODREF, true); } public Member getMethodAtIfLoaded(int index) { return (Member) getRefAt(index, Tag.METHODREF, false); } public Field getFieldAt (int index) { return (Field) getRefAt(index, Tag.FIELDREF, true); } public Field getFieldAtIfLoaded (int index) { return (Field) getRefAt(index, Tag.FIELDREF, false); } // Fetches the class name, member (field, method or interface // method) name, and type descriptor as an array of three Strings public String[] getMemberRefInfoAt (int index) { return new String[] { getUtf8At(index, 2), getUtf8At(index, 3), getUtf8At(index, 4) }; } // Returns a name and type reference index for a method, a field or an invokedynamic. public int getNameAndTypeRefIndexAt(int index) { return getWordAt1(index, 1); } // Fetches the name and type from name_and_type index as an array of two Strings public String[] getNameAndTypeRefInfoAt(int index) { return (String[]) getRefAt(index, Tag.NAMEANDTYPE, true); } public int getIntAt (int index) { return getIntAt1 (index); } public long getLongAt (int index) { return getLongAt1 (index); } public float getFloatAt (int index) { return getFloatAt1 (index); } public double getDoubleAt (int index) { return getDoubleAt1 (index); } public String getStringAt (int index) { return (String) getRefAt(index, Tag.STRING, true); } public String getUTF8At (int index) { return (String) getRefAt1(index, 0, BAR_SYMREF, null); } public Tag getTagAt (int index) { return Tag.valueOf(getCachedTagAt(index)); } public static enum Tag { UTF8(1), INTEGER(3), FLOAT(4), LONG(5), DOUBLE(6), CLASS(7), STRING(8), FIELDREF(9), METHODREF(10), INTERFACEMETHODREF(11), NAMEANDTYPE(12), METHODHANDLE(15), METHODTYPE(16), DYNAMIC(17), // condy INVOKEDYNAMIC(18), // indy INVALID(0); private final int tagCode; private Tag(int tagCode) { this.tagCode = tagCode; } private static @Stable final Tag TAG_TABLE[] = new Tag[20]; static { for (Tag tag : values()) { assert(TAG_TABLE[tag.tagCode] == null); // unique TAG_TABLE[tag.tagCode] = tag; } } private static Tag valueOf(byte v) { Tag tag = (v >= 0 && v < TAG_TABLE.length) ? TAG_TABLE[v] : null; if (tag != null) return tag; throw new IllegalArgumentException("Unknown constant pool tag code " + v); } } public Object getConstantAt(int index) { return getRefAt1(index, -1, BAR_FORCE, null); } public Object getConstantAt(int index, Object ifNotPresent) { return getRefAt1(index, -1, BAR_IFPRESENT, ifNotPresent); } private static final byte BAR_IFPRESENT = 0, BAR_FORCE = 1, BAR_SYMREF = 2; public ConstantDesc getConstantDescAt(int index) { Tag tag = getTagAt(index); switch (tag) { case UTF8: return getUTF8At(index); case INTEGER: return getIntAt(index); case LONG: return getLongAt(index); case FLOAT: return getFloatAt(index); case DOUBLE: return getDoubleAt(index); case CLASS: return classDescOf(getUTF8At(index)); case STRING: return getStringAt(index); //case FIELDREF: //case METHODREF: //case INTERFACEMETHODREF: //case NAMEANDTYPE: case METHODHANDLE: { int w0 = getWordAt1(index, 0); // refKind int w1 = getWordAt1(index, 1); // member boolean isInterface = getTagAt(w1) == Tag.INTERFACEMETHODREF; Kind kind = Kind.valueOf(w0, isInterface); // Note: Even if isInterface is true Kind.valueOf may discard it. ClassDesc clazz = classDescOf(getUtf8At(w1, 2)); return MethodHandleDesc.of(kind, clazz, getUtf8At(w1, 3), getUtf8At(w1, 4)); } case METHODTYPE: return MethodTypeDesc.ofDescriptor(getUTF8At(index)); //case INVOKEDYNAMIC: case DYNAMIC: return (DynamicConstantDesc) getBootstrappedEntryAt(index); } throw new IllegalArgumentException("no ConstantDesc representation for "+tag+" at CP["+index+"]"); } public DynamicCallSiteDesc getDynamicCallSiteDescAt(int index) { checkTag(index, Tag.INVOKEDYNAMIC); return (DynamicCallSiteDesc) getBootstrappedEntryAt(index); } /** * Get bootstrap arguments for the indy or condy at the given index. * @param index CP index of the selected indy or condy constant * @param start which bootstrap argument to start copying * @param end which bootstrap argument (exclusive) to stop copying * @param buf where to put the copied arguments (must be int[] for BAR_SYMREF, Object[] for other modes) * @param pos where to store arguments into the buf * @param resolving resolution mode: 0=BAR_IFPRESENT, 1=BAR_FORCE, 2=BAR_SYMREF * @param ifNotPresent in BAR_IFPRESENT mode, sentinel to store for values not yet resolved * @param ifNullConstant in BAR_IFPRESENT or BAR_FORCE mode, sentinel to store for an actual {@code null} * @param skipNonNull BAR_IFPRESENT or BAR_FORCE mode, do not store over elements of buf which are non-{@code null} */ public void copyOutBootstrapArgumentsAt(int index, int start, int end, Object buf, int pos, byte resolving, Object ifNotPresent, Object ifNullConstant, boolean skipNonNull) { if (start < 0 || end < start) throw new IllegalArgumentException(); copyOutRefsAt1(index, start + BSS_HEADER_WORDS, end + BSS_HEADER_WORDS, buf, pos, resolving, ifNotPresent, ifNullConstant, skipNonNull); } //--------------------------------------------------------------------------- // Internals only below this point // private @Stable byte[] tags; private byte getCachedTagAt(int index) { if (tags == null) tags = getTags1(); if (index >= 0 && index < tags.length) { return tags[index]; } // constant pool can grow over time return getTagAt1(index); } private static final int BSS_HEADER_WORDS = 6, // { attrIndex, natIndex, bsm, name, type, argc } BSS_HEADER_BSM = 2, BSS_HEADER_NAME = 3, BSS_HEADER_TYPE = 4, BSS_HEADER_ARGC = 5; private Object getBootstrappedEntryAt(int index) { int argc = getWordAt1(index, BSS_HEADER_ARGC); int[] argIndexes = copyOutWordsAt(index, BSS_HEADER_WORDS, BSS_HEADER_WORDS+argc); int bsmIndex = getWordAt1(index, BSS_HEADER_BSM); DirectMethodHandleDesc bsm = (DirectMethodHandleDesc) getConstantDescAt(bsmIndex); String name = (String) getRefAt1(index, BSS_HEADER_NAME, BAR_FORCE, null); String type = (String) getRefAt1(index, BSS_HEADER_TYPE, BAR_FORCE, null); ConstantDesc[] args = new ConstantDesc[argc]; for (int i = 0; i < argc; i++) { args[i] = getConstantDescAt(argIndexes[i]); } if (getTagAt(index) == Tag.DYNAMIC) return DynamicConstantDesc.ofNamed(bsm, name, ClassDesc.ofDescriptor(type), args); else return DynamicCallSiteDesc.of(bsm, name, MethodTypeDesc.ofDescriptor(type), args); } private ClassDesc classDescOf(String name) { return ClassDesc.of(name.replace('/', '.')); } private Object getRefAt(int index, Tag tag, boolean forceResolution) { return getRefAt1(index, -1, forceResolution ? BAR_FORCE : BAR_IFPRESENT, null); } private int getWordAt(int index, Tag tag, int word) { checkTag(index, tag); return getWordAt1(index, word); } private void checkTag(int index, Tag needTag) { Tag haveTag = getTagAt(index); if (haveTag == needTag) return; switch (needTag) { case METHODREF: if (haveTag == Tag.INTERFACEMETHODREF) return; break; } throw new IllegalArgumentException("expected "+needTag+" but found "+haveTag); } private String getUtf8At(int index, int word) { return (String) getRefAt1(index, word, BAR_SYMREF, null); } private native Class getHolder1 (); private native int getSize1 (); private native int getWordCountAt1 (int index); private native int getWordAt1 (int index, int word); private native Object getRefAt1 (int index, int word, byte resolving, Object ifNotPresent); private native int getIntAt1 (int index); private native long getLongAt1 (int index); private native float getFloatAt1 (int index); private native double getDoubleAt1 (int index); private native byte getTagAt1 (int index); private native byte[] getTags1 (); /** * @param index which CP entry to process * @param startWord which word to start copying * @param endWord which word (exclusive) to stop copying * @param buf where to put the copied arguments (must be int[] for BAR_SYMREF, Object[] for other modes) * @param bufPos where to store arguments into the buf * @param resolving resolution mode: 0=BAR_IFPRESENT, 1=BAR_FORCE, 2=BAR_SYMREF * @param ifNotPresent in BAR_IFPRESENT mode, sentinel to store for values not yet resolved * @param ifNullConstant in BAR_IFPRESENT or BAR_FORCE mode, sentinel to store for an actual {@code null} * @param skipNonNull BAR_IFPRESENT or BAR_FORCE mode, do not store over elements of buf which are non-{@code null} */ private native void copyOutRefsAt1(int index, int startWord, int endWord, Object buf, int bufPos, // destination byte resolving, Object ifNotPresent, Object ifNullConstant, boolean skipNonNull); private int[] copyOutWordsAt(int index, int startWord, int endWord) { int[] buf = new int[endWord - startWord]; copyOutRefsAt1(index, startWord, endWord, buf, 0, BAR_SYMREF, null, null, false); return buf; } private Object[] copyOutRefsAt(int index, int startWord, int endWord, boolean forceResolution, Object ifNotPresent, Object ifNullConstant) { Object[] buf = new Object[endWord - startWord]; copyOutRefsAt1(index, startWord, endWord, buf, 0, forceResolution ? BAR_FORCE : BAR_IFPRESENT, ifNotPresent, ifNullConstant, false); return buf; } }