/* * Copyright (c) 2000, 2011, 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. * * 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 sun.jvm.hotspot.oops; import java.io.*; import java.util.*; import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.runtime.*; import sun.jvm.hotspot.types.*; import sun.jvm.hotspot.utilities.*; // A ConstantPool is an oop containing class constants // as described in the class file public class ConstantPool extends Oop implements ClassConstants { public class CPSlot { private Address ptr; CPSlot(Address ptr) { this.ptr = ptr; } CPSlot(Symbol sym) { this.ptr = sym.getAddress().orWithMask(1); } public boolean isOop() { return (ptr.minus(null) & 1) == 0; } public boolean isMetaData() { return (ptr.minus(null) & 1) == 1; } public Symbol getSymbol() { if (isMetaData()) { return Symbol.create(ptr.xorWithMask(1)); } throw new InternalError("not a symbol"); } public Oop getOop() { if (isOop()) { return VM.getVM().getObjectHeap().newOop(ptr.addOffsetToAsOopHandle(0)); } throw new InternalError("not an oop"); } } // Used for debugging this code private static final boolean DEBUG = false; protected void debugMessage(String message) { System.out.println(message); } static { VM.registerVMInitializedObserver(new Observer() { public void update(Observable o, Object data) { initialize(VM.getVM().getTypeDataBase()); } }); } private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { Type type = db.lookupType("constantPoolOopDesc"); tags = new OopField(type.getOopField("_tags"), 0); operands = new OopField(type.getOopField("_operands"), 0); cache = new OopField(type.getOopField("_cache"), 0); poolHolder = new OopField(type.getOopField("_pool_holder"), 0); length = new CIntField(type.getCIntegerField("_length"), 0); headerSize = type.getSize(); elementSize = 0; // fetch constants: INDY_BSM_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_indy_bsm_offset").intValue(); INDY_ARGC_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_indy_argc_offset").intValue(); INDY_ARGV_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_indy_argv_offset").intValue(); } ConstantPool(OopHandle handle, ObjectHeap heap) { super(handle, heap); } public boolean isConstantPool() { return true; } private static OopField tags; private static OopField operands; private static OopField cache; private static OopField poolHolder; private static CIntField length; // number of elements in oop private static long headerSize; private static long elementSize; private static int INDY_BSM_OFFSET; private static int INDY_ARGC_OFFSET; private static int INDY_ARGV_OFFSET; public TypeArray getTags() { return (TypeArray) tags.getValue(this); } public TypeArray getOperands() { return (TypeArray) operands.getValue(this); } public ConstantPoolCache getCache() { return (ConstantPoolCache) cache.getValue(this); } public Klass getPoolHolder() { return (Klass) poolHolder.getValue(this); } public int getLength() { return (int)length.getValue(this); } private long getElementSize() { if (elementSize !=0 ) { return elementSize; } else { elementSize = VM.getVM().getOopSize(); } return elementSize; } private long indexOffset(long index) { if (Assert.ASSERTS_ENABLED) { Assert.that(index > 0 && index < getLength(), "invalid cp index " + index + " " + getLength()); } return (index * getElementSize()) + headerSize; } public ConstantTag getTagAt(long index) { return new ConstantTag(getTags().getByteAt((int) index)); } public CPSlot getSlotAt(long index) { return new CPSlot(getHandle().getAddressAt(indexOffset(index))); } public Oop getObjAtRaw(long index){ return getHeap().newOop(getHandle().getOopHandleAt(indexOffset(index))); } public Symbol getSymbolAt(long index) { CPSlot slot = getSlotAt(index); return slot.getSymbol(); } public int getIntAt(long index){ return getHandle().getJIntAt(indexOffset(index)); } public float getFloatAt(long index){ return getHandle().getJFloatAt(indexOffset(index)); } public long getLongAt(long index) { int oneHalf = getHandle().getJIntAt(indexOffset(index + 1)); int otherHalf = getHandle().getJIntAt(indexOffset(index)); // buildLongFromIntsPD accepts higher address value, lower address value // in that order. return VM.getVM().buildLongFromIntsPD(oneHalf, otherHalf); } public double getDoubleAt(long index) { return Double.longBitsToDouble(getLongAt(index)); } public int getFieldOrMethodAt(int which) { if (DEBUG) { System.err.print("ConstantPool.getFieldOrMethodAt(" + which + "): new index = "); } int i = -1; ConstantPoolCache cache = getCache(); if (cache == null) { i = which; } else { // change byte-ordering and go via cache i = cache.getEntryAt(0xFFFF & VM.getVM().getBytes().swapShort((short) which)).getConstantPoolIndex(); } if (Assert.ASSERTS_ENABLED) { Assert.that(getTagAt(i).isFieldOrMethod(), "Corrupted constant pool"); } if (DEBUG) { System.err.println(i); } int res = getIntAt(i); if (DEBUG) { System.err.println("ConstantPool.getFieldOrMethodAt(" + i + "): result = " + res); } return res; } public int[] getNameAndTypeAt(int which) { if (Assert.ASSERTS_ENABLED) { Assert.that(getTagAt(which).isNameAndType(), "Corrupted constant pool"); } int i = getIntAt(which); if (DEBUG) { System.err.println("ConstantPool.getNameAndTypeAt(" + which + "): result = " + i); } return new int[] { extractLowShortFromInt(i), extractHighShortFromInt(i) }; } public Symbol getNameRefAt(int which) { return implGetNameRefAt(which, false); } private Symbol implGetNameRefAt(int which, boolean uncached) { int signatureIndex = getNameRefIndexAt(implNameAndTypeRefIndexAt(which, uncached)); return getSymbolAt(signatureIndex); } public Symbol getSignatureRefAt(int which) { return implGetSignatureRefAt(which, false); } private Symbol implGetSignatureRefAt(int which, boolean uncached) { int signatureIndex = getSignatureRefIndexAt(implNameAndTypeRefIndexAt(which, uncached)); return getSymbolAt(signatureIndex); } private int implNameAndTypeRefIndexAt(int which, boolean uncached) { int i = which; if (!uncached && getCache() != null) { if (ConstantPoolCache.isSecondaryIndex(which)) { // Invokedynamic index. int pool_index = getCache().getMainEntryAt(which).getConstantPoolIndex(); pool_index = invokeDynamicNameAndTypeRefIndexAt(pool_index); // assert(tagAt(pool_index).isNameAndType(), ""); return pool_index; } // change byte-ordering and go via cache i = remapInstructionOperandFromCache(which); } else { if (getTagAt(which).isInvokeDynamic()) { int pool_index = invokeDynamicNameAndTypeRefIndexAt(which); // assert(tag_at(pool_index).is_name_and_type(), ""); return pool_index; } } // assert(tag_at(i).is_field_or_method(), "Corrupted constant pool"); // assert(!tag_at(i).is_invoke_dynamic(), "Must be handled above"); int ref_index = getIntAt(i); return extractHighShortFromInt(ref_index); } private int remapInstructionOperandFromCache(int operand) { int cpc_index = operand; // DEBUG_ONLY(cpc_index -= CPCACHE_INDEX_TAG); // assert((int)(u2)cpc_index == cpc_index, "clean u2"); int member_index = getCache().getEntryAt(cpc_index).getConstantPoolIndex(); return member_index; } int invokeDynamicNameAndTypeRefIndexAt(int which) { // assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool"); return extractHighShortFromInt(getIntAt(which)); } // returns null, if not resolved. public Klass getKlassRefAt(int which) { if( ! getTagAt(which).isKlass()) return null; return (Klass) getObjAtRaw(which); } // returns null, if not resolved. public InstanceKlass getFieldOrMethodKlassRefAt(int which) { int refIndex = getFieldOrMethodAt(which); int klassIndex = extractLowShortFromInt(refIndex); return (InstanceKlass) getKlassRefAt(klassIndex); } // returns null, if not resolved. public Method getMethodRefAt(int which) { InstanceKlass klass = getFieldOrMethodKlassRefAt(which); if (klass == null) return null; Symbol name = getNameRefAt(which); Symbol sig = getSignatureRefAt(which); return klass.findMethod(name, sig); } // returns null, if not resolved. public Field getFieldRefAt(int which) { InstanceKlass klass = getFieldOrMethodKlassRefAt(which); if (klass == null) return null; Symbol name = getNameRefAt(which); Symbol sig = getSignatureRefAt(which); return klass.findField(name, sig); } public int getNameAndTypeRefIndexAt(int index) { return implNameAndTypeRefIndexAt(index, false); } /** Lookup for entries consisting of (name_index, signature_index) */ public int getNameRefIndexAt(int index) { int[] refIndex = getNameAndTypeAt(index); if (DEBUG) { System.err.println("ConstantPool.getNameRefIndexAt(" + index + "): refIndex = " + refIndex[0]+"/"+refIndex[1]); } int i = refIndex[0]; if (DEBUG) { System.err.println("ConstantPool.getNameRefIndexAt(" + index + "): result = " + i); } return i; } /** Lookup for entries consisting of (name_index, signature_index) */ public int getSignatureRefIndexAt(int index) { int[] refIndex = getNameAndTypeAt(index); if (DEBUG) { System.err.println("ConstantPool.getSignatureRefIndexAt(" + index + "): refIndex = " + refIndex[0]+"/"+refIndex[1]); } int i = refIndex[1]; if (DEBUG) { System.err.println("ConstantPool.getSignatureRefIndexAt(" + index + "): result = " + i); } return i; } /** Lookup for MethodHandle entries. */ public int getMethodHandleIndexAt(int i) { if (Assert.ASSERTS_ENABLED) { Assert.that(getTagAt(i).isMethodHandle(), "Corrupted constant pool"); } int res = extractHighShortFromInt(getIntAt(i)); if (DEBUG) { System.err.println("ConstantPool.getMethodHandleIndexAt(" + i + "): result = " + res); } return res; } /** Lookup for MethodHandle entries. */ public int getMethodHandleRefKindAt(int i) { if (Assert.ASSERTS_ENABLED) { Assert.that(getTagAt(i).isMethodHandle(), "Corrupted constant pool"); } int res = extractLowShortFromInt(getIntAt(i)); if (DEBUG) { System.err.println("ConstantPool.getMethodHandleRefKindAt(" + i + "): result = " + res); } return res; } /** Lookup for MethodType entries. */ public int getMethodTypeIndexAt(int i) { if (Assert.ASSERTS_ENABLED) { Assert.that(getTagAt(i).isMethodType(), "Corrupted constant pool"); } int res = getIntAt(i); if (DEBUG) { System.err.println("ConstantPool.getMethodHandleTypeAt(" + i + "): result = " + res); } return res; } /** Lookup for multi-operand (InvokeDynamic) entries. */ public short[] getBootstrapSpecifierAt(int i) { if (Assert.ASSERTS_ENABLED) { Assert.that(getTagAt(i).isInvokeDynamic(), "Corrupted constant pool"); } int bsmSpec = extractLowShortFromInt(this.getIntAt(i)); TypeArray operands = getOperands(); if (operands == null) return null; // safety first int basePos = VM.getVM().buildIntFromShorts(operands.getShortAt(bsmSpec * 2 + 0), operands.getShortAt(bsmSpec * 2 + 1)); int argv = basePos + INDY_ARGV_OFFSET; int argc = operands.getShortAt(basePos + INDY_ARGC_OFFSET); int endPos = argv + argc; short[] values = new short[endPos - basePos]; for (int j = 0; j < values.length; j++) { values[j] = operands.getShortAt(basePos+j); } return values; } final private static String[] nameForTag = new String[] { }; private String nameForTag(int tag) { switch (tag) { case JVM_CONSTANT_Utf8: return "JVM_CONSTANT_Utf8"; case JVM_CONSTANT_Unicode: return "JVM_CONSTANT_Unicode"; case JVM_CONSTANT_Integer: return "JVM_CONSTANT_Integer"; case JVM_CONSTANT_Float: return "JVM_CONSTANT_Float"; case JVM_CONSTANT_Long: return "JVM_CONSTANT_Long"; case JVM_CONSTANT_Double: return "JVM_CONSTANT_Double"; case JVM_CONSTANT_Class: return "JVM_CONSTANT_Class"; case JVM_CONSTANT_String: return "JVM_CONSTANT_String"; case JVM_CONSTANT_Fieldref: return "JVM_CONSTANT_Fieldref"; case JVM_CONSTANT_Methodref: return "JVM_CONSTANT_Methodref"; case JVM_CONSTANT_InterfaceMethodref: return "JVM_CONSTANT_InterfaceMethodref"; case JVM_CONSTANT_NameAndType: return "JVM_CONSTANT_NameAndType"; case JVM_CONSTANT_MethodHandle: return "JVM_CONSTANT_MethodHandle"; case JVM_CONSTANT_MethodType: return "JVM_CONSTANT_MethodType"; case JVM_CONSTANT_InvokeDynamic: return "JVM_CONSTANT_InvokeDynamic"; case JVM_CONSTANT_Invalid: return "JVM_CONSTANT_Invalid"; case JVM_CONSTANT_UnresolvedClass: return "JVM_CONSTANT_UnresolvedClass"; case JVM_CONSTANT_UnresolvedClassInError: return "JVM_CONSTANT_UnresolvedClassInError"; case JVM_CONSTANT_ClassIndex: return "JVM_CONSTANT_ClassIndex"; case JVM_CONSTANT_UnresolvedString: return "JVM_CONSTANT_UnresolvedString"; case JVM_CONSTANT_StringIndex: return "JVM_CONSTANT_StringIndex"; } throw new InternalError("Unknown tag: " + tag); } public void iterateFields(OopVisitor visitor, boolean doVMFields) { super.iterateFields(visitor, doVMFields); if (doVMFields) { visitor.doOop(tags, true); visitor.doOop(cache, true); visitor.doOop(poolHolder, true); final int length = (int) getLength(); // zero'th pool entry is always invalid. ignore it. for (int index = 1; index < length; index++) { int ctag = (int) getTags().getByteAt((int) index); switch (ctag) { case JVM_CONSTANT_ClassIndex: case JVM_CONSTANT_StringIndex: case JVM_CONSTANT_Integer: visitor.doInt(new IntField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true); break; case JVM_CONSTANT_Float: visitor.doFloat(new FloatField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true); break; case JVM_CONSTANT_Long: visitor.doLong(new LongField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true); // long entries occupy two slots index++; break; case JVM_CONSTANT_Double: visitor.doDouble(new DoubleField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true); // double entries occupy two slots index++; break; case JVM_CONSTANT_UnresolvedClassInError: case JVM_CONSTANT_UnresolvedClass: case JVM_CONSTANT_Class: case JVM_CONSTANT_UnresolvedString: case JVM_CONSTANT_Utf8: visitor.doOop(new OopField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true); break; case JVM_CONSTANT_Fieldref: case JVM_CONSTANT_Methodref: case JVM_CONSTANT_InterfaceMethodref: case JVM_CONSTANT_NameAndType: case JVM_CONSTANT_MethodHandle: case JVM_CONSTANT_MethodType: case JVM_CONSTANT_InvokeDynamic: visitor.doInt(new IntField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true); break; } } } /* int length = getLength(); for (int index = 0; index < length; index++) { long offset = baseOffset + (index + typeDataBase.getOopSize()); visitor.doOop(new IndexableField(index, offset, false), getObjAt(index)); } */ } public void writeBytes(OutputStream os) throws IOException { // Map between any modified UTF-8 and it's constant pool index. Map utf8ToIndex = new HashMap(); DataOutputStream dos = new DataOutputStream(os); TypeArray tags = getTags(); int len = (int)getLength(); int ci = 0; // constant pool index // collect all modified UTF-8 Strings from Constant Pool for (ci = 1; ci < len; ci++) { byte cpConstType = tags.getByteAt(ci); if(cpConstType == JVM_CONSTANT_Utf8) { Symbol sym = getSymbolAt(ci); utf8ToIndex.put(sym.asString(), new Short((short) ci)); } else if(cpConstType == JVM_CONSTANT_Long || cpConstType == JVM_CONSTANT_Double) { ci++; } } for(ci = 1; ci < len; ci++) { int cpConstType = (int)tags.getByteAt(ci); // write cp_info // write constant type switch(cpConstType) { case JVM_CONSTANT_Utf8: { dos.writeByte(cpConstType); Symbol sym = getSymbolAt(ci); dos.writeShort((short)sym.getLength()); dos.write(sym.asByteArray()); if (DEBUG) debugMessage("CP[" + ci + "] = modified UTF-8 " + sym.asString()); break; } case JVM_CONSTANT_Unicode: throw new IllegalArgumentException("Unicode constant!"); case JVM_CONSTANT_Integer: dos.writeByte(cpConstType); dos.writeInt(getIntAt(ci)); if (DEBUG) debugMessage("CP[" + ci + "] = int " + getIntAt(ci)); break; case JVM_CONSTANT_Float: dos.writeByte(cpConstType); dos.writeFloat(getFloatAt(ci)); if (DEBUG) debugMessage("CP[" + ci + "] = float " + getFloatAt(ci)); break; case JVM_CONSTANT_Long: { dos.writeByte(cpConstType); long l = getLongAt(ci); // long entries occupy two pool entries ci++; dos.writeLong(l); break; } case JVM_CONSTANT_Double: dos.writeByte(cpConstType); dos.writeDouble(getDoubleAt(ci)); // double entries occupy two pool entries ci++; break; case JVM_CONSTANT_Class: { dos.writeByte(cpConstType); // Klass already resolved. ConstantPool constains klassOop. Klass refKls = (Klass) getObjAtRaw(ci); String klassName = refKls.getName().asString(); Short s = (Short) utf8ToIndex.get(klassName); dos.writeShort(s.shortValue()); if (DEBUG) debugMessage("CP[" + ci + "] = class " + s); break; } // case JVM_CONSTANT_ClassIndex: case JVM_CONSTANT_UnresolvedClassInError: case JVM_CONSTANT_UnresolvedClass: { dos.writeByte(JVM_CONSTANT_Class); String klassName = getSymbolAt(ci).asString(); Short s = (Short) utf8ToIndex.get(klassName); dos.writeShort(s.shortValue()); if (DEBUG) debugMessage("CP[" + ci + "] = class " + s); break; } case JVM_CONSTANT_String: { dos.writeByte(cpConstType); String str = OopUtilities.stringOopToString(getObjAtRaw(ci)); Short s = (Short) utf8ToIndex.get(str); dos.writeShort(s.shortValue()); if (DEBUG) debugMessage("CP[" + ci + "] = string " + s); break; } // case JVM_CONSTANT_StringIndex: case JVM_CONSTANT_UnresolvedString: { dos.writeByte(JVM_CONSTANT_String); String val = getSymbolAt(ci).asString(); Short s = (Short) utf8ToIndex.get(val); dos.writeShort(s.shortValue()); if (DEBUG) debugMessage("CP[" + ci + "] = string " + s); break; } // all external, internal method/field references case JVM_CONSTANT_Fieldref: case JVM_CONSTANT_Methodref: case JVM_CONSTANT_InterfaceMethodref: { dos.writeByte(cpConstType); int value = getIntAt(ci); short klassIndex = (short) extractLowShortFromInt(value); short nameAndTypeIndex = (short) extractHighShortFromInt(value); dos.writeShort(klassIndex); dos.writeShort(nameAndTypeIndex); if (DEBUG) debugMessage("CP[" + ci + "] = ref klass = " + klassIndex + ", N&T = " + nameAndTypeIndex); break; } case JVM_CONSTANT_NameAndType: { dos.writeByte(cpConstType); int value = getIntAt(ci); short nameIndex = (short) extractLowShortFromInt(value); short signatureIndex = (short) extractHighShortFromInt(value); dos.writeShort(nameIndex); dos.writeShort(signatureIndex); if (DEBUG) debugMessage("CP[" + ci + "] = N&T name = " + nameIndex + ", type = " + signatureIndex); break; } case JVM_CONSTANT_MethodHandle: { dos.writeByte(cpConstType); int value = getIntAt(ci); short nameIndex = (short) extractLowShortFromInt(value); short signatureIndex = (short) extractHighShortFromInt(value); dos.writeShort(nameIndex); dos.writeShort(signatureIndex); if (DEBUG) debugMessage("CP[" + ci + "] = N&T name = " + nameIndex + ", type = " + signatureIndex); break; } case JVM_CONSTANT_InvokeDynamic: { dos.writeByte(cpConstType); int value = getIntAt(ci); short bsmIndex = (short) extractLowShortFromInt(value); short nameAndTypeIndex = (short) extractHighShortFromInt(value); dos.writeShort(bsmIndex); dos.writeShort(nameAndTypeIndex); if (DEBUG) debugMessage("CP[" + ci + "] = indy BSM = " + bsmIndex + ", N&T = " + nameAndTypeIndex); break; } default: throw new InternalError("unknown tag: " + cpConstType); } // switch } dos.flush(); return; } public void printValueOn(PrintStream tty) { tty.print("ConstantPool for " + getPoolHolder().getName().asString()); } public long getObjectSize() { return alignObjectSize(headerSize + (getLength() * getElementSize())); } //---------------------------------------------------------------------- // Internals only below this point // private static int extractHighShortFromInt(int val) { // must stay in sync with constantPoolOopDesc::name_and_type_at_put, method_at_put, etc. return (val >> 16) & 0xFFFF; } private static int extractLowShortFromInt(int val) { // must stay in sync with constantPoolOopDesc::name_and_type_at_put, method_at_put, etc. return val & 0xFFFF; } }