/* * Copyright (c) 2007, 2012, 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 com.sun.max.vm.classfile.constant; import static com.sun.cri.bytecode.Bytecodes.*; import static com.sun.max.vm.classfile.ErrorContext.*; import static com.sun.max.vm.classfile.constant.ConstantPool.Tag.*; import static com.sun.max.vm.classfile.constant.PoolConstantFactory.*; import java.util.*; import com.sun.cri.ci.*; import com.sun.cri.ri.*; import com.sun.max.annotate.*; import com.sun.max.program.*; import com.sun.max.vm.actor.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.classfile.*; import com.sun.max.vm.runtime.*; import com.sun.max.vm.type.*; import com.sun.max.vm.value.*; /** * Holds the constants found in a constant pool. A constant pool can be {@linkplain ConstantPoolEditor edited} to add new entries. * * @see <a href="http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#20080">4.4 The Constant Pool</a> */ public final class ConstantPool implements RiConstantPool { /** * The initial capacity of a {@linkplain #ConstantPool(ClassLoader) generated} pool. */ private static final int INITIAL_CAPACITY = 10; /** * Table 4.3 in #4.4. */ public enum Tag { CLASS(7) { @Override public Kind valueKind() { return Kind.REFERENCE; } }, FIELD_REF(9), METHOD_REF(10), INTERFACE_METHOD_REF(11), STRING(8) { @Override public Kind valueKind() { return Kind.REFERENCE; } }, INTEGER(3) { @Override public Kind valueKind() { return Kind.INT; } }, FLOAT(4) { @Override public Kind valueKind() { return Kind.FLOAT; } }, LONG(5) { @Override public Kind valueKind() { return Kind.LONG; } }, DOUBLE(6) { @Override public Kind valueKind() { return Kind.DOUBLE; } }, NAME_AND_TYPE(12), UTF8(1), OBJECT(-1) { @Override public Kind valueKind() { return Kind.REFERENCE; } }, INVALID(0); /** * A negative value indicates a non-standard constant type only used internally. */ private final byte classfileTag; /** * @param classfileTag */ private Tag(int classfileTag) { assert (byte) classfileTag == classfileTag; this.classfileTag = (byte) classfileTag; } /** * Gets the kind of the Java program accessible constant value denoted by this tag. * * @throws ClassFormatError if the pool constant type denoted by this tag is not accessible as a Java program value */ public Kind valueKind() { throw classFormatError(this + " pool constants are not accessible as Java program values"); } public byte classfileTag() { return classfileTag; } /** * Decodes a tag from a classfile to the canonical Tag value representing an entry of the appropriate type. * * Constant Type | Value * ----------------------------+-------- * CONSTANT_Class | 7 * CONSTANT_Fieldref | 9 * CONSTANT_Methodref | 10 * CONSTANT_InterfaceMethodref | 11 * CONSTANT_String | 8 * CONSTANT_Integer | 3 * CONSTANT_Float | 4 * CONSTANT_Long | 5 * CONSTANT_Double | 6 * CONSTANT_NameAndType | 12 * CONSTANT_Utf8 | 1 */ static Tag fromClassfile(int tag) { switch (tag) { case 7: return CLASS; case 9: return FIELD_REF; case 10: return METHOD_REF; case 11: return INTERFACE_METHOD_REF; case 8: return STRING; case 3: return INTEGER; case 4: return FLOAT; case 5: return LONG; case 6: return DOUBLE; case 12: return NAME_AND_TYPE; case 1: return UTF8; default: throw classFormatError("Invalid constant pool entry tag " + tag); } } public static final List<Tag> VALUES = Arrays.asList(values()); } /** * Must only be updated by {@link ConstantPoolEditor#append(PoolConstant)}. */ @INSPECTED private PoolConstant[] constants; public PoolConstant[] constants() { return constants; } public void setConstants(PoolConstant[] constants) { this.constants = constants; } public void setConstant(int index, PoolConstant constant) { constants[index] = constant; } /** * Must only be updated by {@link ConstantPoolEditor#append(PoolConstant)}. */ int length; private final ClassLoader classLoader; /** * Creates a constant pool from a class file. */ public ConstantPool(ClassLoader classLoader, ClassfileStream classfileStream) { final int poolLength = classfileStream.readUnsigned2(); if (poolLength < 1) { throw classFormatError("Invalid constant pool size (" + poolLength + ")"); } final Tag[] tags = new Tag[poolLength]; final int[] rawEntries = new int[poolLength]; final PoolConstant[] poolConstants = new PoolConstant[poolLength]; poolConstants[0] = InvalidConstant.VALUE; this.classLoader = classLoader; this.length = poolLength; // Pass 1: read in the primitive values int i = 1; while (i < poolLength) { final int tagByte = classfileStream.readUnsigned1(); final Tag tag = Tag.fromClassfile(tagByte); tags[i] = tag; switch (tag) { case CLASS: { rawEntries[i] = classfileStream.readUnsigned2(); break; } case STRING: { rawEntries[i] = classfileStream.readUnsigned2(); break; } case FIELD_REF: case METHOD_REF: case INTERFACE_METHOD_REF: { final int classIndex = classfileStream.readUnsigned2(); final int nameAndTypeIndex = classfileStream.readUnsigned2(); rawEntries[i] = (classIndex << 16) | (nameAndTypeIndex & 0xFFFF); break; } case NAME_AND_TYPE: { final int nameIndex = classfileStream.readUnsigned2(); final int descriptorIndex = classfileStream.readUnsigned2(); rawEntries[i] = (nameIndex << 16) | (descriptorIndex & 0xFFFF); break; } case INTEGER: { poolConstants[i] = createIntegerConstant(classfileStream.readInt()); break; } case FLOAT: { poolConstants[i] = createFloatConstant(classfileStream.readFloat()); break; } case LONG: { poolConstants[i] = createLongConstant(classfileStream.readLong()); ++i; try { tags[i] = INVALID; poolConstants[i] = InvalidConstant.VALUE; } catch (ArrayIndexOutOfBoundsException e) { throw classFormatError("Invalid long constant index " + (i - 1)); } break; } case DOUBLE: { poolConstants[i] = createDoubleConstant(classfileStream.readDouble()); ++i; try { tags[i] = INVALID; poolConstants[i] = InvalidConstant.VALUE; } catch (ArrayIndexOutOfBoundsException e) { throw classFormatError("Invalid double constant index " + (i - 1)); } break; } case UTF8: { poolConstants[i] = makeUtf8Constant(classfileStream.readUtf8String()); break; } default: { throw ProgramError.unexpected(); } } i++; } // Pass 2: first verification pass - validate cross references and fixup class and string constants i = 1; while (i < poolLength) { try { final Tag tag = tags[i]; switch (tag) { case CLASS: { final int nameIndex = rawEntries[i]; final Utf8Constant utf8Constant = (Utf8Constant) poolConstants[nameIndex]; final String name = utf8Constant.toString(); if (name.charAt(0) == '[') { poolConstants[i] = createClassConstant(JavaTypeDescriptor.parseTypeDescriptor(name)); } else { poolConstants[i] = createClassConstant(JavaTypeDescriptor.parseTypeDescriptor('L' + name + ';')); } break; } case STRING: { final int stringIndex = rawEntries[i]; final Utf8Constant utf8Constant = (Utf8Constant) poolConstants[stringIndex]; final String string = utf8Constant.toString(); poolConstants[i] = createStringConstant(string); break; } case NAME_AND_TYPE: { final int nameAndType = rawEntries[i]; final int nameIndex = nameAndType >>> 16; final int descriptorIndex = nameAndType & 0xffff; final Utf8Constant name = (Utf8Constant) poolConstants[nameIndex]; final Utf8Constant descriptor = (Utf8Constant) poolConstants[descriptorIndex]; poolConstants[i] = new NameAndTypeConstant(name, descriptor); break; } case FIELD_REF: { final int classNameAndType = rawEntries[i]; final int classIndex = classNameAndType >> 16; final int nameAndTypeIndex = classNameAndType & 0xFFFF; final FieldRefConstant.UnresolvedIndices fieldRef = new FieldRefConstant.UnresolvedIndices(classIndex, nameAndTypeIndex, tags); poolConstants[i] = fieldRef; break; } case METHOD_REF: { final int classNameAndType = rawEntries[i]; final int classIndex = classNameAndType >> 16; final int nameAndTypeIndex = classNameAndType & 0xFFFF; final ClassMethodRefConstant.UnresolvedIndices methodRef = new ClassMethodRefConstant.UnresolvedIndices(classIndex, nameAndTypeIndex, tags); poolConstants[i] = methodRef; break; } case INTERFACE_METHOD_REF: { final int classNameAndType = rawEntries[i]; final int classIndex = classNameAndType >> 16; final int nameAndTypeIndex = classNameAndType & 0xFFFF; final InterfaceMethodRefConstant.UnresolvedIndices methodRef = new InterfaceMethodRefConstant.UnresolvedIndices(classIndex, nameAndTypeIndex, tags); poolConstants[i] = methodRef; break; } default: break; } } catch (NullPointerException e) { throw classFormatError("Invalid constant pool entry type at index " + i); } catch (ClassCastException e) { throw classFormatError("Invalid constant pool entry type at index " + i); } catch (ArrayIndexOutOfBoundsException e) { throw classFormatError("Invalid constant pool index in entry at index " + i); } ++i; } this.constants = poolConstants; // Pass 3: second verification pass - checks the strings are of the right format i = 1; while (i < poolLength) { try { final Tag tag = tags[i]; switch (tag) { case FIELD_REF: { final FieldRefConstant fieldRef = fieldAt(i); fieldRef.type(this); ClassfileReader.verifyFieldName(fieldRef.name(this)); break; } case INTERFACE_METHOD_REF: case METHOD_REF: { final MethodRefConstant methodRef = methodAt(i); methodRef.signature(this); ClassfileReader.verifyMethodName(methodRef.name(this), false); break; } default: break; } } catch (NullPointerException e) { throw classFormatError("Invalid constant pool entry type at index " + i); } catch (ClassCastException e) { throw classFormatError("Invalid constant pool entry type at index " + i); } catch (ArrayIndexOutOfBoundsException e) { throw classFormatError("Invalid constant pool index in entry at index " + i); } ++i; } } /** * Creates a constant pool for a generated class. */ public ConstantPool(ClassLoader classLoader) { this.classLoader = classLoader; this.constants = new PoolConstant[INITIAL_CAPACITY]; // Index 0 is always invalid this.constants[length++] = InvalidConstant.VALUE; } /** * Creates a constant pool for a generated class. */ public ConstantPool(ClassLoader classLoader, PoolConstant[] constants, int length) { this.classLoader = classLoader; if (length < 1 || length > constants.length) { throw new IllegalArgumentException("length < 1 || length > constants.length"); } if (constants[0] != InvalidConstant.VALUE) { throw new IllegalArgumentException("constants make have " + Tag.INVALID + " entry at index 0"); } this.constants = constants; this.length = length; } /** * Creates an object that wraps the result of resolving a resolvable constant for a particular reason. * * @param index the index of a resolvable entry in this constant pool */ public ResolutionGuard.InPool makeResolutionGuard(int index) { return new ResolutionGuard.InPool(this, index); } public ClassLoader classLoader() { return classLoader; } public int numberOfConstants() { return length; } @INSPECTED @CONSTANT private ClassActor holder; public void setHolder(ClassActor holder) { assert this.holder == null; this.holder = holder; } public ClassActor holder() { return holder; } static ClassFormatError unexpectedEntry(int index, Tag tag, String description, Tag... expected) { throw verifyError("Constant pool entry" + (description == null ? "" : " for " + description) + " at " + index + " is a " + tag + ", expected " + com.sun.max.Utils.toString(expected, " or ")); } private ClassFormatError unexpectedEntry(int index, String description, Tag... expected) { throw unexpectedEntry(index, tagAt(index), description, expected); } public PoolConstant at(int index) { return at(index, null); } public PoolConstant at(int index, String description) { try { return constants[index]; } catch (IndexOutOfBoundsException exception) { throw verifyError("Constant pool index (" + index + ")" + (description == null ? "" : " for " + description) + " is out of range"); } } /** * Updates the constant entry at a given index. * * This may overwrite another resolved constant in the situation of two threads racing to resolve a constant. * However, the two resolved constants are equivalent and so which one actually gets written to the pool * does not matter. This means that two pool constants for the same index cannot be compared for equality with '==' * but that's true anyway given that there's no guarantee of the two constants both being the resolved version. * * @param constant the resolved constant to write at {@code index} */ void updateAt(int index, ResolvableConstant constant) { assert constant.isResolved(); assert constants[index].tag() == constant.tag(); constants[index] = constant; } /** * Gets the tag at a given index. If {@code index == 0} or there is no valid entry at {@code index} (e.g. it denotes * the slot following a double or long entry), then {@link Tag#INVALID} is returned. */ public Tag tagAt(int index) { try { return constants[index].tag(); } catch (IndexOutOfBoundsException exception) { throw verifyError("Constant pool index " + index + " is out of range"); } } public ResolvableConstant resolvableAt(int index) { try { return (ResolvableConstant) at(index); } catch (ClassCastException e) { throw unexpectedEntry(index, null, CLASS, FIELD_REF, METHOD_REF, INTERFACE_METHOD_REF); } } public ValueConstant valueConstantAt(int index) { try { return (ValueConstant) at(index); } catch (ClassCastException e) { throw unexpectedEntry(index, null, INTEGER, FLOAT, LONG, DOUBLE, STRING, CLASS); } } public Value valueAt(int index) { return valueConstantAt(index).value(this, index); } public int intAt(int index) { return intAt(index, null); } public int intAt(int index, String description) { try { final IntegerConstant constant = (IntegerConstant) at(index); return constant.value(); } catch (ClassCastException e) { throw unexpectedEntry(index, description, INTEGER); } } public long longAt(int index) { return longAt(index, null); } public long longAt(int index, String description) { try { final LongConstant constant = (LongConstant) at(index); return constant.value(); } catch (ClassCastException e) { throw unexpectedEntry(index, description, LONG); } } public float floatAt(int index) { return floatAt(index, null); } public float floatAt(int index, String description) { try { final FloatConstant constant = (FloatConstant) at(index); return constant.value(); } catch (ClassCastException e) { throw unexpectedEntry(index, description, FLOAT); } } public double doubleAt(int index) { return doubleAt(index, null); } public double doubleAt(int index, String description) { try { final DoubleConstant constant = (DoubleConstant) at(index); return constant.value(); } catch (ClassCastException e) { throw unexpectedEntry(index, description, DOUBLE); } } public NameAndTypeConstant nameAndTypeAt(int index) { return nameAndTypeAt(index, null); } public NameAndTypeConstant nameAndTypeAt(int index, String description) { try { return (NameAndTypeConstant) at(index); } catch (ClassCastException e) { throw unexpectedEntry(index, description, NAME_AND_TYPE); } } public Utf8Constant utf8At(int index, String description) { return utf8ConstantAt(index, description); } public Utf8Constant utf8At(int index) { return utf8ConstantAt(index, null); } public ClassConstant classAt(int index) { return classAt(index, null); } public ClassConstant classAt(int index, String description) { try { return (ClassConstant) at(index); } catch (ClassCastException e) { throw unexpectedEntry(index, description, CLASS); } } public ObjectConstant objectAt(int index) { try { return (ObjectConstant) at(index); } catch (ClassCastException e) { throw unexpectedEntry(index, null, OBJECT); } } public MemberRefConstant memberAt(int index) { return memberAt(index, null); } public MemberRefConstant memberAt(int index, String description) { try { return (MemberRefConstant) at(index); } catch (ClassCastException e) { throw unexpectedEntry(index, description, METHOD_REF, INTERFACE_METHOD_REF, FIELD_REF); } } public MethodRefConstant methodAt(int index) { try { return (MethodRefConstant) at(index); } catch (ClassCastException e) { throw unexpectedEntry(index, null, METHOD_REF, INTERFACE_METHOD_REF); } } public ClassMethodRefConstant classMethodAt(int index) { try { return (ClassMethodRefConstant) at(index); } catch (ClassCastException e) { throw unexpectedEntry(index, null, METHOD_REF); } } public InterfaceMethodRefConstant interfaceMethodAt(int index) { try { return (InterfaceMethodRefConstant) at(index); } catch (ClassCastException e) { throw unexpectedEntry(index, null, INTERFACE_METHOD_REF); } } public FieldRefConstant fieldAt(int index) { try { return (FieldRefConstant) at(index); } catch (ClassCastException e) { throw unexpectedEntry(index, null, FIELD_REF); } } public Utf8Constant utf8ConstantAt(int index, String description) { try { return (Utf8Constant) at(index); } catch (ClassCastException e) { throw unexpectedEntry(index, description, UTF8); } } public StringConstant stringConstantAt(int index) { try { return (StringConstant) at(index); } catch (ClassCastException e) { throw unexpectedEntry(index, null, STRING); } } public String stringAt(int index) { return stringConstantAt(index).value; } ConstantPoolEditor editor; /** * Invoking this method is equivalent to {@link #edit(boolean) edit(true)}. */ public ConstantPoolEditor edit() { return edit(true); } /** * Gets an object that can be used to look up and/or append new entries to this pool. * * At most one thread can be editing a constant pool instance. As such, the current thread * is blocked if another thread is currently editing this constant pool and it is only * woken up once the other thread {@linkplain ConstantPoolEditor#release() releases} the editor. * * @param allowAppending * specifies if the client is allowed to append entries to the pool */ public synchronized ConstantPoolEditor edit(boolean allowAppending) { if (editor == null || editor.owner() != Thread.currentThread()) { while (editor != null) { try { wait(); } catch (InterruptedException e) { // do nothing } } editor = new ConstantPoolEditor(this, allowAppending); } else { editor.acquire(); } return editor; } /** * Invoking this method is equivalent to {@link #edit(ConstantPoolEditorClient, boolean)}. */ public void edit(ConstantPoolEditorClient client) { edit(client, true); } /** * Runs a supplied piece of code that looks up and/or appends entries to this pool. The call back mechanism used for * doing the editing ensures that no other thread can be editing the pool concurrently. * * @param allowAppending * specifies if the client is allowed to append entries to the pool */ public synchronized void edit(ConstantPoolEditorClient client, boolean allowAppending) { final ConstantPoolEditor constantPoolEditor = edit(); try { client.edit(constantPoolEditor); } finally { constantPoolEditor.release(); } } @Override public String toString() { return "ConstantPool[" + holder() + "]"; } // --- Implementation of RiConstantPool --- public MethodActor resolveInvokeVirtual(int cpi) { final VirtualMethodActor virtualMethodActor = classMethodAt(cpi).resolveVirtual(this, cpi); if (virtualMethodActor.isInitializer()) { throw new VerifyError("<init> must be invoked with invokespecial"); } return virtualMethodActor; } public static boolean isSpecial(MethodActor declaredMethod, ClassActor currentClassActor) { final ClassActor holder = declaredMethod.holder(); return (currentClassActor.flags() & Actor.ACC_SUPER) != 0 && currentClassActor != holder && currentClassActor.hasSuperClass(holder) && !declaredMethod.isInstanceInitializer(); } public MethodActor resolveInvokeSpecial(int cpi) { MethodActor methodActor = classMethodAt(cpi).resolveVirtual(this, cpi); if (isSpecial(methodActor, holder())) { methodActor = holder().superClassActor.findVirtualMethodActor(methodActor); if (methodActor == null) { throw new AbstractMethodError(); } } if (methodActor.isAbstract()) { throw new AbstractMethodError(); } return methodActor; } public MethodActor resolveInvokeInterface(int cpi) { final InterfaceMethodRefConstant methodConstant = interfaceMethodAt(cpi); final MethodActor declaredMethod = methodConstant.resolve(this, cpi); if (!(declaredMethod instanceof InterfaceMethodActor)) { throw new InternalError("Use of INVOKEINTERFACE for a method in java.lang.Object should have been rewritten"); } return declaredMethod; } public MethodActor resolveInvokeStatic(int cpi) { final StaticMethodActor staticMethodActor = classMethodAt(cpi).resolveStatic(this, cpi); if (staticMethodActor.isConstructor()) { throw new VerifyError("<init> must be invoked with invokespecial"); } return staticMethodActor; } private FieldActor checkResolvedFieldAccess(FieldActor field, int opcode) { if (opcode >= 0) { if (ALIAS.Static.isAliased(field)) { // The use of an aliased field is always ok. return field; } switch (opcode) { case GETSTATIC: { if (!field.isStatic()) { // IncompatibleClassChangeError return null; } break; } case PUTSTATIC: { if (!field.isStatic()) { // IncompatibleClassChangeError return null; } if (field.isFinal() && field.holder() != holder()) { // IllegalAccessError return null; } break; } case GETFIELD: { if (field.isStatic()) { // IncompatibleClassChangeError return null; } break; } case PUTFIELD: { if (field.isStatic()) { // IncompatibleClassChangeError return null; } if (field.isFinal() && field.holder() != holder()) { // IllegalAccessError return null; } break; } } } return field; } public RiField lookupField(int cpi, int opcode) { FieldRefConstant constant = fieldAt(cpi); if (constant.isResolvableWithoutClassLoading(this)) { // the resolution can occur without side effects try { FieldActor field = checkResolvedFieldAccess(constant.resolve(this, cpi), opcode); if (field != null) { return field; } } catch (LinkageError error) { // Treat as unresolved so that error is thrown later at run time } } RiType holder = UnresolvedType.toRiType(constant.holder(this), holder()); RiType type = UnresolvedType.toRiType(constant.type(this), holder()); String name = constant.name(this).string; return new UnresolvedField(this, cpi, holder, name, type); } private MethodActor checkResolvedMethodAccess(MethodActor method, int opcode) { if (opcode >= 0) { if (ALIAS.Static.isAliased(method)) { // The use of an aliased method is always ok. return method; } switch (opcode) { case INVOKESTATIC: { if (!method.isStatic()) { // IncompatibleClassChangeError return null; } if (method.isInitializer()) { // VerifyError: <init> must be invoked with INVOKESPECIAL return null; } break; } case INVOKEINTERFACE: case INVOKESPECIAL: case INVOKEVIRTUAL: { if (method.isStatic()) { // IncompatibleClassChangeError return null; } if (method.isInitializer() && opcode != INVOKESPECIAL) { // VerifyError: <init> must be invoked with INVOKESPECIAL return null; } break; } } } return method; } public RiMethod lookupMethod(int cpi, int opcode) { MethodRefConstant constant = methodAt(cpi); if (constant.isResolvableWithoutClassLoading(this)) { // the resolution can occur without side effects try { MethodActor method = checkResolvedMethodAccess(constant.resolve(this, cpi), opcode); if (method != null) { return method; } } catch (LinkageError error) { // Treat as unresolved } } RiType holder = UnresolvedType.toRiType(constant.holder(this), holder()); RiSignature signature = constant.signature(this); String name = constant.name(this).string; return new UnresolvedMethod(this, cpi, holder, name, signature); } public RiType lookupType(int cpi, int opcode) { return typeFrom(classAt(cpi), cpi, opcode); } public RiSignature lookupSignature(int cpi) { return SignatureDescriptor.create(utf8At(cpi).string); } public Object lookupConstant(int cpi) { switch (tagAt(cpi)) { case CLASS: { RiType type = typeFrom(classAt(cpi), cpi, LDC); if (type instanceof RiResolvedType) { ClassActor classActor = (ClassActor) type; return CiConstant.forObject(classActor.javaClass()); } return type; } case INTEGER: { return CiConstant.forInt(intAt(cpi)); } case FLOAT: { return CiConstant.forFloat(floatAt(cpi)); } case STRING: { return CiConstant.forObject(stringAt(cpi)); } case LONG: { return CiConstant.forLong(longAt(cpi)); } case DOUBLE: { return CiConstant.forDouble(doubleAt(cpi)); } case OBJECT: { return CiConstant.forObject(objectAt(cpi).value()); } default: throw ProgramError.unexpected("unknown constant type"); } } private RiType typeFrom(ClassConstant constant, int cpi, int opcode) { if (constant.isResolvableWithoutClassLoading(this)) { try { // the resolution can occur without side effects ClassActor type = checkResolvedTypeAccess(constant.resolve(this, cpi), opcode); if (type != null) { return type; } } catch (LinkageError error) { // Treat as unresolved so that error is thrown later at run time } } return new UnresolvedType.InPool(constant.typeDescriptor(), this, cpi); } private ClassActor checkResolvedTypeAccess(ClassActor classActor, int opcode) { if (opcode >= 0) { switch (opcode) { case NEW: { if (classActor.isAbstract() || classActor.isArrayClass()) { // InstantiationError return null; } break; } case MULTIANEWARRAY: case ANEWARRAY: case CHECKCAST: case INSTANCEOF: case LDC: { // No extra checks required break; } } } return classActor; } public CiConstant encoding() { return CiConstant.forObject(this); } @Override public void loadReferencedType(int cpi, int bytecode) { // TODO(tw): Implement this for Maxine. } }