package s3.core.domain;
import ovm.core.domain.Blueprint;
import ovm.core.domain.ConstantResolvedInstanceFieldref;
import ovm.core.domain.ConstantResolvedInstanceMethodref;
import ovm.core.domain.ConstantResolvedInterfaceMethodref;
import ovm.core.domain.ConstantResolvedStaticFieldref;
import ovm.core.domain.ConstantResolvedStaticMethodref;
import ovm.core.domain.DomainDirectory;
import ovm.core.domain.LinkageException;
import ovm.core.domain.MemberResolver;
import ovm.core.domain.Method;
import ovm.core.domain.Oop;
import ovm.core.domain.ResolutionInfo;
import ovm.core.domain.ResolvedConstant;
import ovm.core.domain.Type;
import ovm.core.repository.ConstantClass;
import ovm.core.repository.ConstantFieldref;
import ovm.core.repository.ConstantMethodref;
import ovm.core.repository.ConstantPool;
import ovm.core.repository.Constants;
import ovm.core.repository.ConstantsEditor;
import ovm.core.repository.RepositoryString;
import ovm.core.repository.Selector;
import ovm.core.repository.TypeName;
import ovm.core.services.memory.MemoryPolicy;
import ovm.core.services.memory.VM_Address;
import ovm.services.bytecode.JVMConstants;
import ovm.services.bytecode.reader.ByteCodeConstants;
import ovm.util.ByteBuffer;
import ovm.util.OVMError;
import ovm.util.PragmaLiveClass;
import ovm.util.UnsafeAccess;
import s3.core.S3Base;

/**
 * @author Christian Grothoff
 **/
public class S3Constants
    extends S3Base
    implements Constants, 
               PragmaLiveClass,
               UnsafeAccess {

    /**
     * For every entry in the constant pool, we keep its tag.  Tags are
     * constants which indicate the type of constant pool entry that
     * follows. See JVM Spec 4.4.
     **/
    protected byte[] tags_;
    protected Object[] constants; /* prims are boxed */
    protected final Type.Compound type_;

    S3Constants(Type.Compound type,
		byte[] tags,
		Object[] values) {
	this.type_ = type;
	this.tags_ = tags;
	this.constants = values;
    }

    protected int set(int index, int tag, Object val) {
	tags_[index] = (byte) tag;
	VM_Address.uncheckedAASTORE(constants, index, val);
	return index;
    }

    protected final int set(int index, ResolvedConstant val) {
	return set(index, val.getTag(), val);
    }

    /** Runtime resolution code (w/ initializeBlueprint) */
    public ConstantResolvedStaticMethodref resolveStaticMethodAtRuntime(int index) 
	throws LinkageException {
	ConstantResolvedStaticMethodref smi = resolveStaticMethod(index);
	S3Domain dom = (S3Domain) type_.getDomain();
	Oop shst = smi.getSharedState();
	if (! dom.isExecutive())
	    dom.getCoreServicesAccess().initializeBlueprint(shst);
	return smi;
    }
    public ConstantResolvedStaticFieldref resolveStaticFieldAtRuntime(int index) 
	throws LinkageException {
	ConstantResolvedStaticFieldref sfi = resolveStaticField(index);
	S3Domain dom = (S3Domain) type_.getDomain();
	Oop shst = sfi.getSharedState();
	if (! dom.isExecutive())
	    dom.getCoreServicesAccess().initializeBlueprint(shst);
	return sfi;
    }

    public boolean isStaticMethodResolved(int index) {
	return tags_[index] == JVMConstants.CONSTANT_ResolvedStaticMethod;
    }
    public boolean isInstanceMethodResolved(int index) {
	return tags_[index] == JVMConstants.CONSTANT_ResolvedInstanceField;
    }
    public boolean isInterfaceMethodResolved(int index) {
	return tags_[index] == JVMConstants.CONSTANT_ResolvedInterfaceMethod;
    }
    public boolean isStaticFieldResolved(int index) {
	return tags_[index] == JVMConstants.CONSTANT_ResolvedStaticField;
    }
    public boolean isInstanceFieldResolved(int index) {
	return tags_[index] == JVMConstants.CONSTANT_ResolvedInstanceField;
    }
    public boolean isConstantResolved(int index) {
	return tags_[index] == JVMConstants.CONSTANT_Reference;
    }
    public boolean isClassResolved(int index) {
	return tags_[index] == JVMConstants.CONSTANT_ResolvedClass;
    }

    private Object enterMetaDataArea() {
	return MemoryPolicy.the().enterMetaDataArea(type_.getContext());
    }

    /**
     * Resolve a constant pool CONSTANT_Methodref referred to by an
     * INVOKESTATIC.
     */
    public ConstantResolvedStaticMethodref resolveStaticMethod(int index) 
	throws LinkageException {
	byte tag = tags_[index];
	// Already resolved ? //
	if (tag == JVMConstants.CONSTANT_ResolvedStaticMethod) {
	    if (constants[index] == null)
		throw new NullPointerException();
	    return (ConstantResolvedStaticMethodref)constants[index];
	}
	// Tag check //
	if (tag != JVMConstants.CONSTANT_Methodref) {
	    d("FATAL: tried to call S3Constants.resolveStaticMethod() "
	      + "on the entry of wrong type " + tag);
	    throw new OVMError("aborting");
	}
	// Resolution //
	S3Domain dom = (S3Domain) type_.getDomain();
	Object value = constants[index];
	if (! (value instanceof Selector.Method)) {
	    d("FATAL: S3Constants has a constant of wrong type"
	      + "in the slog tagged as CONSTANT_Methodref");
	    d(value.toString());
	    throw new OVMError("aborting");
	}
	Selector.Method sel = (Selector.Method)value;
	Type.Context ctx = dom.getApplicationTypeContext();
	Blueprint bp = dom.blueprintFor(ctx.typeFor(sel.getDefiningClass()));
	MemberResolver resolver = dom.getMemberResolver();
	Method method = resolver
	    .resolveStaticMethod(bp.getType().asCompound(),
				 sel.getUnboundSelector(),
				 null);
	if (method == null) {
	    throw new LinkageException("method " +
				       sel.getUnboundSelector() +
				       " not found in " +
				       bp.getType());
	}
	// bp may not be the declaringBP 
	bp = dom.blueprintFor(method.getDeclaringType());
	Object r = enterMetaDataArea();
	try {
	    ResolutionInfo rinfo = resolver
		.resolveStaticMethod(bp,
				     sel.getUnboundSelector(),
				     type_);
	    if (rinfo == null) {
		throw new LinkageException(sel + " not found 2");
	    }
	    Oop shst = bp.getInstanceBlueprint().getSharedState();
	    if (shst == null)
		throw new Error("shst null");
	    ConstantResolvedStaticMethodref smi = 
		 ConstantResolvedStaticMethodref.make(method,
						    rinfo.getOffset(),
						    shst);
	    set(index, smi);
	    return smi;
	} finally {
	    MemoryPolicy.the().leave(r);
	}
    }

    /**
     * Resolve a constant pool CONSTANT_Methodref referred to by an
     * INVOKESPECIAL or INVOKEVIRTUAL for user-domain to
     * executive-domain call.
     */
    public ConstantResolvedInstanceMethodref resolveExecutiveInstanceMethod(int index)
	throws LinkageException {
	byte tag = tags_[index];
	// Already resolved ? //
	if (tag == JVMConstants.CONSTANT_ResolvedInstanceMethod) {
	    if (constants[index] == null)
		throw new NullPointerException();
	    return (ConstantResolvedInstanceMethodref)constants[index];
	}
	// Tag check //
	if (tag != JVMConstants.CONSTANT_Methodref) {
	    d("FATAL: tried to call S3Constants.resolveExecutiveInstanceMethod() "
	      + "on the entry of wrong type " + tag);
	    throw new OVMError("aborting");
	}
	// Resolution //
	S3Domain dom = (S3Domain)DomainDirectory.getExecutiveDomain();
	Object value = constants[index];
	if (! (value instanceof Selector.Method)) {
	    d("FATAL: S3Constants has a constant of wrong type"
	      + "in the slog tagged as CONSTANT_Methodref");
	    d(value.toString());
	    throw new OVMError("aborting");
	}
	Selector.Method sel = (Selector.Method)value;
	MemberResolver resolver = dom.getMemberResolver();
	Type.Context ctx = dom.getApplicationTypeContext();
	Blueprint bp;
	try {
	    bp = dom.blueprintFor(ctx.typeFor(sel.getDefiningClass()));
	} catch(LinkageException le) {
	    throw le;
	}
	ResolutionInfo rinfo = null;
	ConstantResolvedInstanceMethodref smi = null;
	rinfo = resolver
	    .resolveSpecialMethod(bp,
				  sel,
				  type_);
	if (rinfo == null) {
	    try {
		rinfo = resolver
		    .resolveVirtualMethod(bp,
					  sel.getUnboundSelector(),
					  type_);
	    } catch (MemberResolver.IllegalAccessException e) {
		throw e.unchecked();
	    }
	    if (rinfo == null)
		throw new LinkageException(sel + " not found");
	    else { // resolved as a virtual call
		Method method = resolver
		    .resolveInstanceMethod(bp.getType().asCompound(),
					   sel.getUnboundSelector(),
					   null);
		Object r = enterMetaDataArea();
		try {
		    smi = ConstantResolvedInstanceMethodref.make(method, rinfo.getOffset(), 
		                                                 bp);
		} finally {
		    MemoryPolicy.the().leave(r);
		}
	    }
	} else { // resolved as a special call
	    Method method = resolver
		.resolveInstanceMethod(bp.getType().asCompound(),
				       sel.getUnboundSelector(),
				       null);
	    Object r = enterMetaDataArea();
	    try {
		smi = ConstantResolvedInstanceMethodref.make(method, rinfo.getOffset(), bp);
	    } finally {
		MemoryPolicy.the().leave(r);
	    }
	}
	if (smi == null)
	    throw new Error();
	set(index, smi);
	return smi;
    }

    /**
     * Resolve a constant pool CONSTANT_Methodref referred to by an
     * INVOKESPECIAL or INVOKEVIRTUAL.
     */
    public ConstantResolvedInstanceMethodref resolveInstanceMethod(int index)
	throws LinkageException {
	byte tag = tags_[index];
	// Already resolved ? //
	if (tag == JVMConstants.CONSTANT_ResolvedInstanceMethod) {
	    if (constants[index] == null)
		throw new NullPointerException();
	    return (ConstantResolvedInstanceMethodref)constants[index];
	}
	// Tag check //
	if (tag != JVMConstants.CONSTANT_Methodref) {
	    d("FATAL: tried to call S3Constants.resolveInstanceMethod() "
	      + "on the entry of wrong type " + tag);
	    throw new OVMError("aborting");
	}
	// Resolution //
	S3Domain dom = (S3Domain) type_.getDomain();
	Object value = constants[index];
	if (! (value instanceof Selector.Method)) {
	    d("FATAL: S3Constants has a constant of wrong type"
	      + "in the slog tagged as CONSTANT_Methodref");
	    d(value.toString());
	    throw new OVMError("aborting");
	}
	Selector.Method sel = (Selector.Method)value;
	MemberResolver resolver = dom.getMemberResolver();
	Type.Context ctx = dom.getApplicationTypeContext();
	Blueprint bp;
	try {
	    bp = dom.blueprintFor(ctx.typeFor(sel.getDefiningClass()));
	} catch(LinkageException le) {
	    throw le;
	}
	Object r = enterMetaDataArea();
	try {
	    ResolutionInfo rinfo = null;
	    ConstantResolvedInstanceMethodref smi = null;
	    rinfo = resolver.resolveSpecialMethod(bp, sel, type_);
	    if (rinfo == null) {
		try {
		    rinfo = resolver.resolveVirtualMethod(bp, sel.getUnboundSelector(), type_);
		} catch (MemberResolver.IllegalAccessException e) {
		    throw e.unchecked();
		}
		if (rinfo == null) throw new LinkageException(sel + " not found");
		else { // resolved as a virtual call
                    Method method = resolver.resolveInstanceMethod(bp.getType().asCompound(), 
                                                                   sel.getUnboundSelector(), null);
		    smi = ConstantResolvedInstanceMethodref.make(method, rinfo.getOffset(), bp);
		}
	    } else { // resolved as a special call
                Method method = resolver.resolveInstanceMethod(bp.getType().asCompound(),
                                                               sel.getUnboundSelector(), null);
		smi = ConstantResolvedInstanceMethodref.make(method, rinfo.getOffset(), bp);
	    }
	    set(index, smi);
	    return smi;
	} finally {
	    MemoryPolicy.the().leave(r);
	}
    }

    /**
     * Resolve a constant pool CONSTANT_Methodref referred to by an
     * INVOKEINTERFACE.
     */
    public ConstantResolvedInterfaceMethodref resolveInterfaceMethod(int index) 
	throws LinkageException {
	byte tag = tags_[index];
	// Already resolved ? //
	if (tag == JVMConstants.CONSTANT_ResolvedInterfaceMethod) {
	    if (constants[index] == null)
		throw new NullPointerException();
	    return (ConstantResolvedInterfaceMethodref)constants[index];
	}
	// Tag check //
	if (tag != JVMConstants.CONSTANT_InterfaceMethodref) {
	    d("FATAL: tried to call S3Constants.resolveInterfaceMethod()"
	      + "on the entry of wrong type");
	    throw new OVMError("aborting");
	}
	// Resolution //
	S3Domain dom = (S3Domain) type_.getDomain();
	Object value = constants[index];
	if (! (value instanceof Selector.Method)) {
	    d("FATAL: S3Constants has a constant of wrong type"
	      + "in the slog tagged as CONSTANT_InterfaceMethodref");
	    d(value.toString());
	    throw new OVMError("aborting");
	}
	Selector.Method sel = (Selector.Method)value;
	MemberResolver resolver = dom.getMemberResolver();
	Type.Context ctx = dom.getApplicationTypeContext();
	Blueprint bp;
	try {
	    bp = dom.blueprintFor(ctx.typeFor(sel.getDefiningClass()));
	} catch(LinkageException le) {
	    throw le;
	}
	Object r = enterMetaDataArea();
	try {
	    ResolutionInfo rinfo = resolver
		.resolveInterfaceMethod(bp,
					sel.getUnboundSelector());
	    if (rinfo == null) {
		throw new LinkageException();
	    }
	    //Method m = bp.getType().asScalar().getMethod(sel);
	    Method m = resolver.resolveInstanceMethod
		(bp.getType().asCompound(),
		 sel.getUnboundSelector(),
		 null);
	    if (m == null) {
		//d("resolveInterfaceMethod : fatal method null ");
		//d("sel = " + sel + ", bp = " + bp);
		throw new LinkageException();
	    }
	    ConstantResolvedInterfaceMethodref imi = 
	        ConstantResolvedInterfaceMethodref.make(m, rinfo.getOffset(), bp);
	    set(index, imi);
	    return imi;
	} finally {
	    MemoryPolicy.the().leave(r);
	}
    }

    /**
     * Resolve a constant pool CONSTANT_Fieldref referred to by an
     * GETSTATIC or PUTSTATIC.
     */
    public ConstantResolvedStaticFieldref resolveStaticField(int index) 
	throws LinkageException {
	byte tag = tags_[index];
	// Already resolved ? //
	if (tag == JVMConstants.CONSTANT_ResolvedStaticField) {
	    if (constants[index] == null)
		throw new NullPointerException();
	    return (ConstantResolvedStaticFieldref)constants[index];
	}
	// Tag check //
	if (tag != JVMConstants.CONSTANT_Fieldref) {
	    d("FATAL: tried to call S3Constants.resolveStaticField()"
	      + "on the entry of wrong type");
	    throw new OVMError("aborting");
	}
	// Resolution //
	S3Domain dom = (S3Domain) type_.getDomain();
	Object value = constants[index];
	if (! (value instanceof Selector.Field)) {
	    d("FATAL: S3Constants has a constant of wrong type"
	      + "in the slog tagged as CONSTANT_Fieldref");
	    d(value.toString());
	    throw new OVMError("aborting");
	}
	Selector.Field sel = (Selector.Field)value;
	Type.Context ctx = dom.getApplicationTypeContext();
	Blueprint bp =
	    dom.blueprintFor(ctx.typeFor(sel.getDefiningClass()));
	MemberResolver resolver = dom.getMemberResolver();
	S3Field field = (S3Field)resolver
	    .resolveStaticField((Type.Class)bp.getType(),
				sel.getUnboundSelector(),
				null);
	if (field == null) {
	    throw new LinkageException(sel + " not found");
	}
	// bp may not be the declaringBP 
	bp = dom.blueprintFor(field.getDeclaringType());
	Oop shst = bp.getInstanceBlueprint().getSharedState();
	if (shst == null)
	    throw new Error("shst null");
	Object r = enterMetaDataArea();
	try {
	    ConstantResolvedStaticFieldref sfi = ConstantResolvedStaticFieldref.make(field, field.getOffset(), shst);
	    set(index, sfi);
	    return sfi;
	} finally {
	    MemoryPolicy.the().leave(r);
	}
    }

    /**
     * Resolve a constant pool CONSTANT_Fieldref referred to by an
     * GETFIELD or PUTFIELD.
     */
    public ConstantResolvedInstanceFieldref resolveInstanceField(int index) 
	throws LinkageException {
	byte tag = tags_[index];
	// Already resolved ? //
	if (tag == JVMConstants.CONSTANT_ResolvedInstanceField) {
	    if (constants[index] == null)
		throw new NullPointerException();
	    return (ConstantResolvedInstanceFieldref)constants[index];
	}
	// Tag check //
	if (tag != JVMConstants.CONSTANT_Fieldref) {
	    d("FATAL: tried to call S3Constants.resolveInstanceField()"
	      + "on the entry of wrong type");
	    throw new OVMError("aborting");
	}
	// Resolution //
	S3Domain dom = (S3Domain) type_.getDomain();
	Object value = constants[index];
	if (! (value instanceof Selector.Field)) {
	    d("FATAL: S3Constants has a constant of wrong type"
	      + "in the slog tagged as CONSTANT_Fieldref");
	    d(value.toString());
	    throw new OVMError("aborting");
	}
	Selector.Field sel = (Selector.Field)value;
	Type.Context ctx = dom.getApplicationTypeContext();
	Blueprint bp; // shstBP
	try {
	    bp = dom.blueprintFor(ctx.typeFor(sel.getDefiningClass()));
	} catch(LinkageException le) {
	    throw le;
	}
	MemberResolver resolver = dom.getMemberResolver();
	S3Field field = (S3Field)resolver
	    .resolveInstanceField((Type.Class)bp.getType(),
				  sel.getUnboundSelector(),
				  null);
	if (field == null) {
	    throw new LinkageException();
	}
	Object r = enterMetaDataArea();
	try {
	    ConstantResolvedInstanceFieldref ifi = 
	        ConstantResolvedInstanceFieldref.make(field, field.getOffset(), bp);
	    set(index, ifi);
	    return ifi;
	} finally {
	    MemoryPolicy.the().leave(r);
	}
    }

    /**
     * Resolve a constant pool CONSTANT_Class entry (holding a
     * TypeName) and overwrite the entry with a Blueprint. This is
     * true of Gemeinsam TypeNames and Shared-state Blueprints,
     * too. -HY
     */
    public S3Blueprint resolveClassAt(int index) throws LinkageException {
	byte tag = tags_[index];
	// Already resolved ? //
	if (tag == JVMConstants.CONSTANT_ResolvedClass) {
	    if (constants[index] == null)
		throw new NullPointerException();
	    return (S3Blueprint)constants[index];
	}
	// Tag check //
	if (tag != JVMConstants.CONSTANT_Class) {
	    d("FATAL: tried to call resolveClassAt()"
	      + "on the entry of wrong type");
	    throw new OVMError("aborting");
	}
	// Resolution //
	S3Domain dom = (S3Domain) type_.getDomain();
	Object value = constants[index];
	if (! (value instanceof TypeName.Compound)) {
	    d("FATAL: S3Constants has a constant of wrong type"
	      + "in the slog tagged as CONSTANT_Class");
	    d(value.toString());
	    throw new OVMError("aborting");
	}
	TypeName.Compound tn = (TypeName.Compound)value;
	Type.Context ctx = dom.getApplicationTypeContext();
	Blueprint bp;
	try {
	    bp = dom.blueprintFor(ctx.typeFor(tn));
	} catch(LinkageException le) {
// 	    pln("resolveClassAt: Can't resolve constant " + tn);
	    throw le;
	}
	set(index, JVMConstants.CONSTANT_ResolvedClass, bp);
	return (S3Blueprint)bp;
    }

    /**
     * Resolve the object at the given index and return the corresponding
     * revolved instance.
     */
    public Oop resolveConstantAt(int index) {
	if (tags_[index] != JVMConstants.CONSTANT_Reference) {
	    S3Domain dom = (S3Domain) type_.getDomain();
	    switch (tags_[index]) {
	    case JVMConstants.CONSTANT_String :
		if (! (constants[index] instanceof RepositoryString)) {
		    d("FATAL: S3ConstantPool has entry of wrong type tagged as CONSTANT_String!");
		    d(constants[index].toString());
		    throw new OVMError("aborting");
		}
		RepositoryString s = (RepositoryString) constants[index];
		set(index, JVMConstants.CONSTANT_Reference, 
            dom.internString(s.getUtf8Index()));
		if (constants[index] == null)
		    throw new NullPointerException("Resolving RepositoryString " + s + " resulted in null!");
		tags_[index] = JVMConstants.CONSTANT_Reference;
		break;
	    case JVMConstants.CONSTANT_SharedState:
		if (! (constants[index] instanceof TypeName.Gemeinsam) ) {
		    d("FATAL: S3ConstantPool has entry of wrong type tagged as CONSTANT_SharedState!");
		    d(constants[index].toString());
		    throw new OVMError("aborting");
		}
		TypeName.Gemeinsam tn = (TypeName.Gemeinsam) constants[index];
		Type.Context tc = type_.getContext();
		Blueprint.Factory bf = tc.getDomain();
		TypeName.Compound bpn = tn.getInstanceTypeName().asCompound();
		try {
		    Blueprint ibp =
			bf.blueprintFor(tc.typeFor(bpn));
		    Object o =  ibp.getSharedState();
		    if (o == null)
			throw new NullPointerException("Resolving shared state of " + constants[index] + 
						       " resulted in null");
		    set(index, JVMConstants.CONSTANT_Reference, o);
		    // also: trigger static initialization (if needed)
		    if (! dom.isExecutive()) // don't do this at bootimage-build time; -- and at RT, we never hit this case for ED resolution :-)
			dom.getCoreServicesAccess().initializeBlueprint(VM_Address.fromObject(o).asOop());
// 		} catch (LinkageException.NoClassDef e) {
		    // FIXME: Why in the hell are we doing THIS?  If
		    // the class isn't found the the right place, why
		    // are we looking in the wrong place?
// 		    tc = type_.getDomain().getApplicationTypeContext();
// 		    try {
// 			Blueprint ibp =
// 			    bf.blueprintFor(tc.typeFor(bpn));
// 			Object o = ibp.getSharedState();	    
// 			if (o == null)
// 			    throw new NullPointerException("Resolving shared state of " + constants[index] + " resulted in null");
// 			VM_Address.uncheckedAASTORE(constants, index, o);
// 			// constants[index] = o;
// 			tags_[index] = JVMConstants.CONSTANT_Reference;
// 			// also: trigger static initialization (if needed)
// 			if (! dom.isExecutive()) // don't do this at bootimage-build time; -- and at RT, we never hit this case for ED resolution :-)
// 			    dom.getCoreServicesAccess().initializeBlueprint(VM_Address.fromObject(o).asOop());
// 			pln("resolved " + ibp + " in application tc");
			
//  		    } catch (LinkageException le) {
// 			pln("CP PUNT SharedState " + tn);
// 			throw new OVMError(le);
// 		    }
		} catch ( LinkageException le ) {
		    pln( "CP PUNT SharedState " + tn);
		    throw new OVMError(le);
		}	     
		break;	      
	    case JVMConstants.CONSTANT_Binder:
	    default :
		fail( "constant type " + getTagAt(index) + " not supported");
	    }
	}
	if (constants[index] == null)
	    throw new NullPointerException();
        return VM_Address.fromObject(constants[index]).asOop();
    }

    public byte getTagAt(int offset) {
	return tags_[offset];
    }

    public int getConstantCount() {
	return constants.length; 
    }

    public Object getConstantAt(int offset) {
        if ((offset >= 0)
            && (offset < constants.length)
            && ((offset > 0) || (tags_[offset] != 0)))
            return constants[offset];
        // offset 0 is ok if tags_[0] != 0 because then we
        // are the upper part of a diff-constant pool!
        throw failure("Illegal tag for access to getConstantAt: "
		      + offset
		      + " valid range is [1,"
		      + constants.length
		      + ")");
    }

    public void checkTagAt(byte expectedTag, int offset)
        throws ConstantPool.AccessException {
        if (tags_[offset] != expectedTag) {
            String message =
                "Invalid Constant Pool Entry "
		+ ByteCodeConstants.CONSTANT_NAMES[tags_[offset]]
                    + " instead of "
                    + ByteCodeConstants.CONSTANT_NAMES[expectedTag]
                    + ", CP index "
                    + offset
		+ ", was "
                    + getContents(offset);
            throw new ConstantPool.AccessException(message);
        }
    }

    
    /**
     * Return the typename at the given index.
     * See JVM Spec 4.4.1.
     * @param cpIndex the index of the TypeName to be retrieved
     * @return the TypeName at the given index
     * @throws Constants.AccessException if the type of the constant does not match
     *         the type that is requested 
     **/
    public ConstantClass getClassAt(int cpIndex)
	throws Constants.AccessException {
	try {
	    return (ConstantClass) constants[cpIndex];
	} catch (ClassCastException e) {
	    throw new Constants.AccessException("At CP index " + cpIndex + ": " + e);
	} catch (ArrayIndexOutOfBoundsException e) {
	    throw new Constants.AccessException(
				      "Invalid CP index " + cpIndex + " >= " + constants.length);
	}
    }

    public Type getType() {
	return type_;
    }
   
    /**
     * Return a builder based on this constant pool.
     * @return a <code>ConstantPool.Builder</code> for this
     *         constant pool
     */
    public ConstantsEditor getBuilder() {
        return new S3ConstantsBuilder(type_,
				      this);
    }

    public ConstantFieldref getFieldrefAt(int cpIndex)
	throws ConstantPool.AccessException {
	try {
	    return (ConstantFieldref) constants[cpIndex];
	} catch (ClassCastException e) {
	    throw new ConstantPool.AccessException("At CP index " + cpIndex + ": " + e);
	} catch (ArrayIndexOutOfBoundsException e) {
	    throw new ConstantPool.AccessException(
				      "Invalid CP index " + cpIndex + " >= " + constants.length);
	}
    }

    public ConstantMethodref getMethodrefAt(int cpIndex)
	throws ConstantPool.AccessException {
	try {
	    return (ConstantMethodref) constants[cpIndex];
	} catch (ClassCastException e) {
	    throw new ConstantPool.AccessException("At CP index " + cpIndex + ": " + e + " have: " + constants[cpIndex].getClass());
	} catch (ArrayIndexOutOfBoundsException e) {
	    throw new ConstantPool.AccessException("Invalid CP index " + cpIndex + " >= " + constants.length);
	}
    }


    public int getValueAt(int cpIndex) throws ConstantPool.AccessException {
	if ((cpIndex < 0) || (cpIndex >= tags_.length))
	    throw new ConstantPool.AccessException("CP index " + cpIndex + " out of bounds");
	byte t = tags_[cpIndex];
	switch (t) {
	case JVMConstants.CONSTANT_Long :
	case JVMConstants.CONSTANT_Double :
	    throw new ConstantPool.AccessException("use getWideValueAt for that");
	case JVMConstants.CONSTANT_Integer :
	    return ((Integer) constants[cpIndex]).intValue();
	case JVMConstants.CONSTANT_Float :
	    ByteBuffer conv_ = ByteBuffer.allocate(4);
	    conv_.putFloat(0, ((Float) constants[cpIndex]).floatValue());
	    return conv_.getInt(0);
	default :
	    throw new ConstantPool.AccessException("getValueAt only works for int or float!");
	}
    }

    public long getWideValueAt(int cpIndex) throws ConstantPool.AccessException {
	if ((cpIndex < 0) || (cpIndex >= tags_.length))
	    throw new ConstantPool.AccessException("CP index " + cpIndex + " out of bounds");
	byte t = tags_[cpIndex];
	switch (t) {
	case JVMConstants.CONSTANT_Integer :
	case JVMConstants.CONSTANT_Float :
	    throw new ConstantPool.AccessException("use getValueAt for that");
	case JVMConstants.CONSTANT_Long :
	    return ((Long) constants[cpIndex]).longValue();
	case JVMConstants.CONSTANT_Double :
	    ByteBuffer conv_ = ByteBuffer.allocate(8);
	    conv_.putDouble(0, ((Double) constants[cpIndex]).doubleValue());
	    return conv_.getLong(0);
	default :
	    throw new ConstantPool.AccessException("getWideValueAt only works for long or double!");
	}
    }
    

    /**
     * Debug method. Get the contents of the constant pool at index
     * <code>cpIndex</code> as a <code>String</code>.
     */
    String getContents(int cpIndex) {
        String contents = "\"(" + tags_[cpIndex] + ")";
        switch (tags_[cpIndex]) {
            case JVMConstants.CONSTANT_Utf8 :
                contents += "Utf8: " + constants[cpIndex];
                break;
            case JVMConstants.CONSTANT_NameAndType :
                contents += "NameAndType";
                break;
            case JVMConstants.CONSTANT_Integer :
                contents += "INTEGER: " + constants[cpIndex];
                break;
            case JVMConstants.CONSTANT_Float :
                contents += "FLOAT:" + constants[cpIndex];
                break;
            case JVMConstants.CONSTANT_Double :
                contents += "DOUBLE" + constants[cpIndex];
                cpIndex++;
                break;
            case JVMConstants.CONSTANT_Long :
                contents += "LONG" + constants[cpIndex];
                cpIndex++;
                break;
            case 0 :
                contents += "<EMPTY>";
                break;
            default :
                contents += "OTHER"
                    + constants[cpIndex]
                    + ", "
                    + constants[cpIndex].getClass();
        }
        return contents + "\"";
    }

    /* ******************** S3ConstantsBuilder support **************** */

    /**
     * Return the tag-array of this constant pool.
     * (used by the <code>ConstantPoolBuilder</code> when extending
     * this CP).
     * @return the tag-array of this constant pool
     */
    public byte[] getTags() {
        return (byte[]) tags_.clone();
    }

    /**
     * Return the values-array of this constant pool.  Used by the
     * ConstantPoolBuilder when extending this CP.
     **/
    public Object[] getValues() {
        return (Object[]) constants.clone();
    }

} // end of S3Constants
