// $Id: class.cpp,v 1.16 2004/09/14 05:49:05 elliott-oss Exp $
//
// This software is subject to the terms of the IBM Jikes Compiler
// License Agreement available at the following URL:
// http://ibm.com/developerworks/opensource/jikes.
// Copyright (C) 1996, 2004 IBM Corporation and others.  All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//

#include "class.h"
#include "code.h"
#include "control.h"
#include "op.h"
#include "option.h"
#include "semantic.h"
#include "zip.h"

#ifdef HAVE_JIKES_NAMESPACE
namespace Jikes { // Open namespace Jikes block
#endif // HAVE_JIKES_NAMESPACE


void ConstantPool::SetNext(CPInfo* constant)
{
    Next() = constant;
    if (constant -> Large())
        Next() = NULL; // entries following Long or Double are unused
}

bool ConstantPool::Check() const
{
    for (unsigned i = 1; i < top; i++)
    {
        CPInfo* entry = Tuple<CPInfo*>::operator[](i);
        if (entry && ! entry -> Check(*this))
            return false;
    }
    return true;
}


CPInfo* CPInfo::AllocateCPInfo(ClassFile& buffer)
{
    u1 tag = buffer.GetU1();
    switch (tag)
    {
    case CONSTANT_Utf8:
        return new CPUtf8Info(buffer);
    case CONSTANT_Integer:
        return new CPIntegerInfo(buffer);
    case CONSTANT_Float:
        return new CPFloatInfo(buffer);
    case CONSTANT_Long:
        return new CPLongInfo(buffer);
    case CONSTANT_Double:
        return new CPDoubleInfo(buffer);
    case CONSTANT_Class:
        return new CPClassInfo(buffer);
    case CONSTANT_String:
        return new CPStringInfo(buffer);
    case CONSTANT_Fieldref:
    case CONSTANT_Methodref:
    case CONSTANT_InterfaceMethodref:
        return new CPMemberInfo((ConstantPoolTag) tag, buffer);
    case CONSTANT_NameAndType:
        return new CPNameAndTypeInfo(buffer);
    default:
        return new CPInfo((ConstantPoolTag) tag);
    }
}


CPClassInfo::CPClassInfo(ClassFile& buffer)
    : CPInfo(CONSTANT_Class)
    , name_index(buffer.GetU2())
    , type(NULL)
{}


CPMemberInfo::CPMemberInfo(ConstantPoolTag _tag, ClassFile& buffer)
    : CPInfo(_tag)
    , class_index(buffer.GetU2())
    , name_and_type_index(buffer.GetU2())
{}


CPStringInfo::CPStringInfo(ClassFile& buffer)
    : CPInfo(CONSTANT_String)
    , string_index(buffer.GetU2())
{}


CPIntegerInfo::CPIntegerInfo(ClassFile& buffer)
    : CPInfo(CONSTANT_Integer)
    , bytes(buffer.GetU4())
{}


CPFloatInfo::CPFloatInfo(ClassFile& buffer)
    : CPInfo(CONSTANT_Float)
    , value(buffer.GetU4())
{}


CPLongInfo::CPLongInfo(ClassFile& buffer)
    : CPInfo(CONSTANT_Long)
    , value(buffer.GetU8())
{}


CPDoubleInfo::CPDoubleInfo(ClassFile& buffer)
    : CPInfo(CONSTANT_Double)
    , value(buffer.GetU8())
{}


CPNameAndTypeInfo::CPNameAndTypeInfo(ClassFile& buffer)
    : CPInfo(CONSTANT_NameAndType)
    , name_index(buffer.GetU2())
    , descriptor_index(buffer.GetU2())
{}


CPUtf8Info::CPUtf8Info(ClassFile& buffer)
    : CPInfo(CONSTANT_Utf8)
    , len(buffer.GetU2())
    , valid(true)
{
    u2 size = (u2) buffer.GetN(bytes, len);
    if (size != len)
        valid = false;
    Init(size);
    if (! valid)
        buffer.MarkInvalid("bad CPUtf8Info");
}

void CPUtf8Info::Init(u2 size)
{
#ifdef JIKES_DEBUG
    const char* tmp;
#endif // JIKES_DEBUG
    for (u2 i = 0; i < size; i++)
    {
        switch (bytes[i])
        {
        case 0x00:
        case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5:
        case 0xf6: case 0xf7: case 0xf8: case 0xf9: case 0xfa: case 0xfb:
        case 0xfc: case 0xfd: case 0xfe: case 0xff: // invalid
            valid = false;
#ifdef JIKES_DEBUG
            contents.Next() = '\\';
            contents.Next() = 'x';
            tmp = IntToString(bytes[i], 2).String();
            contents.Next() = tmp[0];
            contents.Next() = tmp[1];
#endif // JIKES_DEBUG
            break;
#ifdef JIKES_DEBUG
        case 0x09:
            contents.Next() = '\\';
            contents.Next() = 't';
            break;
        case 0x0a:
            contents.Next() = '\\';
            contents.Next() = 'n';
            break;
        case 0x0d:
            contents.Next() = '\\';
            contents.Next() = 'r';
            break;
        case 0x22:
            contents.Next() = '\\';
            contents.Next() = '"';
            break;
        case 0x5c:
            contents.Next() = '\\';
            contents.Next() = '\\';
            break;
        case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06:
        case 0x07: case 0x08: case 0x0b: case 0x0c: case 0x0e: case 0x0f:
        case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15:
        case 0x16: case 0x17: case 0x18: case 0x19: case 0x1a: case 0x1b:
        case 0x1c: case 0x1d: case 0x1e: case 0x1f: case 0x7f:
            // non-printing ASCII
            contents.Next() = '\\';
            contents.Next() = 'u';
            tmp = IntToString(bytes[i], 4).String();
            contents.Next() = tmp[0];
            contents.Next() = tmp[1];
            contents.Next() = tmp[2];
            contents.Next() = tmp[3];
            break;
#endif // JIKES_DEBUG
        default:
            if (bytes[i] <= 0x7f) // 1-byte (printing ASCII, if JIKES_DEBUG)
            {
#ifdef JIKES_DEBUG
                contents.Next() = bytes[i];
#endif // JIKES_DEBUG
            }
            else if (bytes[i] <= 0xdf) // 2-byte source
            {
                if (i + 1 == size || (bytes[i + 1] & 0xc0) != 0x80)
                {
                    valid = false;
#ifdef JIKES_DEBUG
                    contents.Next() = '\\';
                    contents.Next() = 'x';
                    tmp = IntToString(bytes[i], 2).String();
                    contents.Next() = tmp[0];
                    contents.Next() = tmp[1];
#endif // JIKES_DEBUG
                    break;
                }
                ++i;
#ifdef JIKES_DEBUG
                u2 value = (bytes[i - 1] & 0x1f) << 6;
                value |= bytes[i] & 0x3f;
                contents.Next() = '\\';
                contents.Next() = 'u';
                tmp = IntToString(value, 4).String();
                contents.Next() = tmp[0];
                contents.Next() = tmp[1];
                contents.Next() = tmp[2];
                contents.Next() = tmp[3];
#endif // JIKES_DEBUG
            }
            else // 3-byte source
            {
                assert((bytes[i] & 0xf0) == 0xe0);
                if (i + 2 >= size ||
                    (bytes[i + 1] & 0xc0) != 0x80 ||
                    (bytes[i + 2] & 0xc0) != 0x80)
                {
                    valid = false;
#ifdef JIKES_DEBUG
                    contents.Next() = '\\';
                    contents.Next() = 'x';
                    tmp = IntToString(bytes[i], 2).String();
                    contents.Next() = tmp[0];
                    contents.Next() = tmp[1];
#endif // JIKES_DEBUG
                    break;
                }
                i += 2;
#ifdef JIKES_DEBUG
                u2 value = (bytes[i - 2] & 0x0f) << 12;
                value |= (bytes[i - 1] & 0x3f) << 6;
                value |= bytes[i] & 0x3f;
                contents.Next() = '\\';
                contents.Next() = 'u';
                tmp = IntToString(value, 4).String();
                contents.Next() = tmp[0];
                contents.Next() = tmp[1];
                contents.Next() = tmp[2];
                contents.Next() = tmp[3];
#endif // JIKES_DEBUG
            }
        }
    }
#ifdef JIKES_DEBUG
    if (! valid)
    {
        contents.Next() = '\\';
        contents.Next() = 'i';
        contents.Next() = 'n';
        contents.Next() = 'v';
        contents.Next() = 'a';
        contents.Next() = 'l';
        contents.Next() = 'i';
        contents.Next() = 'd';
        contents.Next() = '\\';
    }
    contents.Next() = 0;
#endif // JIKES_DEBUG
}


FieldInfo::FieldInfo(ClassFile& buffer)
    : AccessFlags(buffer.GetU2())
    , name_index(buffer.GetU2())
    , descriptor_index(buffer.GetU2())
    , attr_synthetic(NULL)
    , attr_deprecated(NULL)
    , attr_signature(NULL)
    , attr_constantvalue(NULL)
    , attr_visible_annotations(NULL)
    , attr_invisible_annotations(NULL)
{
    unsigned count = buffer.GetU2();
    while (count--)
    {
        AttributeInfo* attr = AttributeInfo::AllocateAttributeInfo(buffer);
        attributes.Next() = attr;
        switch (attr -> Tag())
        {
        case AttributeInfo::ATTRIBUTE_Synthetic:
            if (attr_synthetic)
                buffer.MarkInvalid("duplicate synthetic attribute");
            attr_synthetic = (SyntheticAttribute*) attr;
            SetACC_SYNTHETIC();
            break;
        case AttributeInfo::ATTRIBUTE_Deprecated:
            if (attr_deprecated)
                buffer.MarkInvalid("duplicate deprecated attribute");
            attr_deprecated = (DeprecatedAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_ConstantValue:
            if (attr_constantvalue)
                buffer.MarkInvalid("duplicate ConstantValue attribute");
            if (! ACC_FINAL())
                buffer.MarkInvalid("ConstantValue attribute without final");
            attr_constantvalue = (ConstantValueAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_Signature:
            if (attr_signature)
                buffer.MarkInvalid("duplicate signature attribute");
            attr_signature = (SignatureAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_RuntimeVisibleAnnotations:
            if (attr_visible_annotations)
                buffer.MarkInvalid("duplicate signature attribute");
            attr_visible_annotations = (AnnotationsAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_RuntimeInvisibleAnnotations:
            if (attr_invisible_annotations)
                buffer.MarkInvalid("duplicate invisible attribute");
            attr_invisible_annotations = (AnnotationsAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_Generic:
            // ignore
            break;
        default:
            // invalid field attribute
            buffer.MarkInvalid("invalid field attribute");
        }
    }
}

const char* FieldInfo::Signature(const ConstantPool& pool,
                                 const Control& /*control*/) const
{
    assert(pool[descriptor_index] -> Tag() == CPInfo::CONSTANT_Utf8);
    const CPUtf8Info* sig =
        /*(control.option.source >= JikesOption::SDK1_5 && attr_signature)
        ? attr_signature -> Signature(pool)
        :*/ (const CPUtf8Info*) pool[descriptor_index];
    return sig -> Bytes();
}

u2 FieldInfo::SignatureLength(const ConstantPool& pool,
                              const Control& /*control*/) const
{
    assert(pool[descriptor_index] -> Tag() == CPInfo::CONSTANT_Utf8);
    const CPUtf8Info* sig =
        /*(control.option.source >= JikesOption::SDK1_5 && attr_signature)
        ? attr_signature -> Signature(pool)
        :*/ (const CPUtf8Info*) pool[descriptor_index];
    return sig -> Length();
}


MethodInfo::MethodInfo(ClassFile& buffer)
    : AccessFlags(buffer.GetU2())
    , name_index(buffer.GetU2())
    , descriptor_index(buffer.GetU2())
    , attr_synthetic(NULL)
    , attr_deprecated(NULL)
    , attr_signature(NULL)
    , attr_bridge(NULL)
    , attr_code(NULL)
    , attr_exceptions(NULL)
    , attr_visible_annotations(NULL)
    , attr_invisible_annotations(NULL)
    , attr_param_visible_annotations(NULL)
    , attr_param_invisible_annotations(NULL)
    , attr_annotation_default(NULL)
{
    unsigned count = buffer.GetU2();
    while (count--)
    {
        AttributeInfo* attr = AttributeInfo::AllocateAttributeInfo(buffer);
        attributes.Next() = attr;
        switch (attr -> Tag())
        {
        case AttributeInfo::ATTRIBUTE_Synthetic:
            if (attr_synthetic)
                buffer.MarkInvalid("duplicate synthetic attribute");
            attr_synthetic = (SyntheticAttribute*) attr;
            SetACC_SYNTHETIC();
            break;
        case AttributeInfo::ATTRIBUTE_Deprecated:
            if (attr_deprecated)
                buffer.MarkInvalid("duplicate deprecated attribute");
            attr_deprecated = (DeprecatedAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_Code:
            if (attr_code)
                buffer.MarkInvalid("duplicate code attribute");
            if (ACC_NATIVE() || ACC_ABSTRACT())
                buffer.MarkInvalid("code for native or abstract method");
            attr_code = (CodeAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_Signature:
            if (attr_signature)
                buffer.MarkInvalid("duplicate signature attribute");
            attr_signature = (SignatureAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_Bridge:
            if (attr_bridge)
                buffer.MarkInvalid("duplicate bridge attribute");
            attr_bridge = (BridgeAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_Exceptions:
            if (attr_exceptions)
                buffer.MarkInvalid("duplicate exceptions attribute");
            attr_exceptions = (ExceptionsAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_RuntimeVisibleAnnotations:
            if (attr_visible_annotations)
                buffer.MarkInvalid("duplicate visible attribute");
            attr_visible_annotations = (AnnotationsAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_RuntimeInvisibleAnnotations:
            if (attr_invisible_annotations)
                buffer.MarkInvalid("duplicate invisible attribute");
            attr_invisible_annotations = (AnnotationsAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_RuntimeVisibleParameterAnnotations:
            if (attr_param_visible_annotations)
                buffer.MarkInvalid("duplicate param visible attribute");
            attr_param_visible_annotations =
                (ParameterAnnotationsAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_RuntimeInvisibleParameterAnnotations:
            if (attr_param_invisible_annotations)
                buffer.MarkInvalid("duplicate param invisible attribute");
            attr_param_invisible_annotations =
                (ParameterAnnotationsAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_AnnotationDefault:
            if (attr_annotation_default)
                buffer.MarkInvalid("duplicate annotation default attribute");
            if (! ACC_ABSTRACT() || ! ACC_PUBLIC())
                buffer.MarkInvalid("annotation default on non-abstract or non-public method");
            attr_annotation_default = (AnnotationDefaultAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_Generic:
            // ignore
            break;
        default:
            // invalid method attribute
            buffer.MarkInvalid("invalid method attribute");
        }
    }
    if (! ACC_NATIVE() && ! ACC_ABSTRACT() && ! attr_code)
        buffer.MarkInvalid("no code for non-native, non-abstract method");
}

const char* MethodInfo::Signature(const ConstantPool& pool,
                                  const Control& /*control*/) const
{
    assert(pool[descriptor_index] -> Tag() == CPInfo::CONSTANT_Utf8);
    const CPUtf8Info* sig =
        /*(control.option.source >= JikesOption::SDK1_5 && attr_signature)
        ? attr_signature -> Signature(pool)
        :*/ (const CPUtf8Info*) pool[descriptor_index];
    return sig -> Bytes();
}

u2 MethodInfo::SignatureLength(const ConstantPool& pool,
                               const Control& /*control*/) const
{
    assert(pool[descriptor_index] -> Tag() == CPInfo::CONSTANT_Utf8);
    const CPUtf8Info* sig =
        /*(control.option.source >= JikesOption::SDK1_5 && attr_signature)
        ? attr_signature -> Signature(pool)
        :*/ (const CPUtf8Info*) pool[descriptor_index];
    return sig -> Length();
}


AttributeInfo::AttributeInfo(AttributeInfoTag _tag, ClassFile& buffer)
    : tag(_tag)
    , attribute_name_index(buffer.GetU2())
    , attribute_length(buffer.GetU4())
{}

AttributeInfo::AttributeInfoTag AttributeInfo::Tag(const CPUtf8Info* name)
{
    switch (name -> Length())
    {
    case 4:
        if (! strcmp(name -> Bytes(), StringConstant::U8S_Code))
            return ATTRIBUTE_Code;
        break;
    case 8:
        if (! strcmp(name -> Bytes(), StringConstant::U8S_StackMap))
            return ATTRIBUTE_StackMap;
        break;
    case 9:
        if (! strcmp(name -> Bytes(), StringConstant::U8S_Signature))
            return ATTRIBUTE_Signature;
        if (! strcmp(name -> Bytes(), StringConstant::U8S_Synthetic))
            return ATTRIBUTE_Synthetic;
        break;
    case 10:
        if (! strcmp(name -> Bytes(), StringConstant::U8S_Deprecated))
            return ATTRIBUTE_Deprecated;
        if (! strcmp(name -> Bytes(), StringConstant::U8S_Exceptions))
            return ATTRIBUTE_Exceptions;
        if (! strcmp(name -> Bytes(), StringConstant::U8S_SourceFile))
            return ATTRIBUTE_SourceFile;
        break;
    case 12:
        if (! strcmp(name -> Bytes(), StringConstant::U8S_InnerClasses))
            return ATTRIBUTE_InnerClasses;
        break;
    case 13:
        if (! strcmp(name -> Bytes(), StringConstant::U8S_ConstantValue))
            return ATTRIBUTE_ConstantValue;
        break;
    case 15:
        if (! strcmp(name -> Bytes(), StringConstant::U8S_LineNumberTable))
            return ATTRIBUTE_LineNumberTable;
        if (! strcmp(name -> Bytes(), StringConstant::U8S_EnclosingMethod))
            return ATTRIBUTE_EnclosingMethod;
        break;
    case 17:
        if (! strcmp(name -> Bytes(), StringConstant::U8S_AnnotationDefault))
            return ATTRIBUTE_AnnotationDefault;
        break;
    case 18:
        if (! strcmp(name -> Bytes(), StringConstant::U8S_LocalVariableTable))
            return ATTRIBUTE_LocalVariableTable;
        break;
    case 22:
        if (! strcmp(name -> Bytes(),
                     StringConstant::U8S_LocalVariableTypeTable))
            return ATTRIBUTE_LocalVariableTypeTable;
        break;
    case 25:
        if (! strcmp(name -> Bytes(),
                     StringConstant::U8S_RuntimeVisibleAnnotations))
            return ATTRIBUTE_RuntimeVisibleAnnotations;
        break;
    case 27:
        if (! strcmp(name -> Bytes(),
                     StringConstant::U8S_RuntimeInvisibleAnnotations))
            return ATTRIBUTE_RuntimeInvisibleAnnotations;
        break;
    case 34:
        if (! strcmp(name -> Bytes(),
                     StringConstant::U8S_RuntimeVisibleParameterAnnotations))
            return ATTRIBUTE_RuntimeVisibleParameterAnnotations;
        break;
    case 36:
        if (! strcmp(name -> Bytes(),
                     StringConstant::U8S_RuntimeInvisibleParameterAnnotations))
            return ATTRIBUTE_RuntimeInvisibleParameterAnnotations;
    }
    return ATTRIBUTE_Generic;
}

AttributeInfo* AttributeInfo::AllocateAttributeInfo(ClassFile& buffer)
{
    u2 index = buffer.PeekU2();
    if (buffer.Pool()[index] -> Tag() != CPInfo::CONSTANT_Utf8)
    {
        buffer.MarkInvalid("attribute name not utf8 constant");
        return new UnknownAttribute(buffer);
    }
    switch (Tag((CPUtf8Info*) buffer.Pool()[index]))
    {
    case ATTRIBUTE_ConstantValue:
        return new ConstantValueAttribute(buffer);
    case ATTRIBUTE_Code:
        return new CodeAttribute(buffer);
    case ATTRIBUTE_Exceptions:
        return new ExceptionsAttribute(buffer);
    case ATTRIBUTE_InnerClasses:
        return new InnerClassesAttribute(buffer);
    case ATTRIBUTE_SourceFile:
        return new SourceFileAttribute(buffer);
    case ATTRIBUTE_Synthetic:
        return new SyntheticAttribute(buffer);
    case ATTRIBUTE_LineNumberTable:
        return new LineNumberTableAttribute(buffer);
    case ATTRIBUTE_LocalVariableTable:
        return new LocalVariableTableAttribute(buffer, false);
    case ATTRIBUTE_Deprecated:
        return new DeprecatedAttribute(buffer);
    case ATTRIBUTE_Signature:
        return new SignatureAttribute(buffer);
    case ATTRIBUTE_Bridge:
        return new BridgeAttribute(buffer);
    case ATTRIBUTE_EnclosingMethod:
        return new EnclosingMethodAttribute(buffer);
    case ATTRIBUTE_LocalVariableTypeTable:
        return new LocalVariableTableAttribute(buffer, true);
    case ATTRIBUTE_StackMap:
        return new StackMapAttribute(buffer);
    case ATTRIBUTE_RuntimeVisibleAnnotations:
        return new AnnotationsAttribute(buffer, true);
    case ATTRIBUTE_RuntimeInvisibleAnnotations:
        return new AnnotationsAttribute(buffer, false);
    case ATTRIBUTE_RuntimeVisibleParameterAnnotations:
        return new ParameterAnnotationsAttribute(buffer, true);
    case ATTRIBUTE_RuntimeInvisibleParameterAnnotations:
        return new ParameterAnnotationsAttribute(buffer, false);
    case ATTRIBUTE_AnnotationDefault:
        return new AnnotationDefaultAttribute(buffer);
    default:
        return new UnknownAttribute(buffer);
    }
}


UnknownAttribute::UnknownAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_Generic, buffer)
{
    info_length = buffer.GetN(info, attribute_length);
}


ConstantValueAttribute::ConstantValueAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_ConstantValue, buffer)
    , constantvalue_index(buffer.GetU2())
{
    if (attribute_length != 2)
        buffer.MarkInvalid("bad constant value attribute length");
    if (! buffer.Pool()[constantvalue_index] -> Constant())
        buffer.MarkInvalid("bad constant value attribute type");
}


CodeAttribute::CodeAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_Code, buffer)
    , max_stack(buffer.GetU2())
    , max_locals(buffer.GetU2())
    , code(8, 4)
    , exception_table(6, 16)
    , attributes(6, 16)
    , attr_linenumbers(NULL)
    , attr_locals(NULL)
    , attr_stackmap(NULL)
{
    unsigned remaining = attribute_length - 12;
    // +2 for max_stack, +2 for max_locals, +4 for code_length,
    // +2 for exception_table_length, +2 for attributes_count
    u4 code_length = buffer.GetU4();
    remaining -= code_length;
    buffer.SkipN(code_length);

    u2 exception_table_length = buffer.GetU2();
    remaining -= exception_table_length * 8;
    while (exception_table_length--)
    {
        ExceptionElement& entry = exception_table.Next();
        entry.start_pc = buffer.GetU2();
        entry.end_pc = buffer.GetU2();
        entry.handler_pc = buffer.GetU2();
        entry.catch_type = buffer.GetU2();
        if (entry.catch_type &&
            buffer.Pool()[entry.catch_type] -> Tag() != CPInfo::CONSTANT_Class)
        {
            buffer.MarkInvalid("bad type for exception table catch type");
        }
    }

    u2 attributes_count = buffer.GetU2();
    while (attributes_count--)
    {
        AttributeInfo* attr = AllocateAttributeInfo(buffer);
        remaining -= 6 + attr -> AttributeLength();
        attributes.Next() = attr;
        switch (attr -> Tag())
        {
        case AttributeInfo::ATTRIBUTE_LineNumberTable:
            if (attr_linenumbers)
                buffer.MarkInvalid("duplicate line number table");
            attr_linenumbers = (LineNumberTableAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_LocalVariableTable:
            if (attr_locals)
                buffer.MarkInvalid("duplicate local variable table");
            attr_locals = (LocalVariableTableAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_LocalVariableTypeTable:
            if (attr_local_types)
                buffer.MarkInvalid("duplicate local variable type table");
            attr_local_types = (LocalVariableTableAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_StackMap:
            if (attr_stackmap)
                buffer.MarkInvalid("duplicate stack map");
            attr_stackmap = (StackMapAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_Generic:
            // ignore
            break;
        default:
            buffer.MarkInvalid("invalid code attribute");
        }
    }
    if (remaining)
        buffer.MarkInvalid("bytes remaining at end of code attribute");
}


#ifdef JIKES_DEBUG
void CodeAttribute::Print(const ConstantPool& constant_pool, int fill) const
{
    assert(! fill);
    PrintPrefix("Code", constant_pool, 0);
    Coutput << endl << " max_stack: " << (unsigned) max_stack
            << ", max_locals: " << (unsigned) max_locals
            << ", code_length: " << (unsigned) code.Length() << endl;
    Operators::OpDmp(constant_pool, code);
    Coutput << " exception_table_length: "
            << (unsigned) exception_table.Length() << endl;
    for (unsigned i = 0; i < exception_table.Length(); i++)
    {
        Coutput << "  start " << (unsigned) exception_table[i].start_pc
                << ", end " << (unsigned) exception_table[i].end_pc
                << ", handler " << (unsigned) exception_table[i].handler_pc
                << ", catch_type " << (unsigned) exception_table[i].catch_type;
        if (! exception_table[i].catch_type)
            Coutput << " (all)";
        else if (constant_pool[exception_table[i].catch_type] -> Tag() ==
                 CPInfo::CONSTANT_Class)
        {
            Coutput << '=';
            constant_pool[exception_table[i].catch_type] ->
                Describe(constant_pool);
        }
        else Coutput << "(invalid)";
        Coutput << endl;
    }
    Coutput << " attribute_count: " << (unsigned) attributes.Length() << endl;
    for (unsigned j = 0; j < attributes.Length(); j++)
        attributes[j] -> Print(constant_pool, 2);
}
#endif // JIKES_DEBUG


ExceptionsAttribute::ExceptionsAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_Exceptions, buffer)
    , exception_index_table(6, 16)
{
    unsigned count = buffer.GetU2();
    if (attribute_length != count * 2 + 2)
        buffer.MarkInvalid("bad exceptions attribute length");
    while (count--)
    {
        u2 index = buffer.GetU2();
        exception_index_table.Next() = index;
        if (buffer.Pool()[index] -> Tag() != CPInfo::CONSTANT_Class)
            buffer.MarkInvalid("bad type for exceptions attribute entry");
    }
}


InnerClassesAttribute::InnerClassesAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_InnerClasses, buffer)
    , classes(6, 16)
{
    unsigned count = buffer.GetU2();
    if (attribute_length != count * 8 + 2)
        buffer.MarkInvalid("bad inner classes attribute length");
    while (count--)
    {
        InnerClassesElement& entry = classes.Next();
        entry.inner_class_info_index = buffer.GetU2();
        entry.outer_class_info_index = buffer.GetU2();
        entry.inner_name_index = buffer.GetU2();
        entry.inner_class_access_flags.SetFlags(buffer.GetU2());
        if ((buffer.Pool()[entry.inner_class_info_index] -> Tag() !=
             CPInfo::CONSTANT_Class) ||
            (entry.outer_class_info_index &&
             (buffer.Pool()[entry.outer_class_info_index] -> Tag() !=
              CPInfo::CONSTANT_Class)) ||
            (entry.inner_name_index &&
             (buffer.Pool()[entry.inner_name_index] -> Tag() !=
              CPInfo::CONSTANT_Utf8)) ||
            ! entry.inner_class_access_flags.LegalAccess())
        {
            buffer.MarkInvalid("bad type in inner classes attribute");
        }
    }
}


SyntheticAttribute::SyntheticAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_Synthetic, buffer)
{
    if (attribute_length)
        buffer.MarkInvalid("bad synthetic attribute length");
}


SourceFileAttribute::SourceFileAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_SourceFile, buffer)
    , sourcefile_index(buffer.GetU2())
{
    if (attribute_length != 2)
        buffer.MarkInvalid("bad source file attribute length");
    if (buffer.Pool()[sourcefile_index] -> Tag() != CPInfo::CONSTANT_Utf8)
        buffer.MarkInvalid("bad type for source file attribute");
}


LineNumberTableAttribute::LineNumberTableAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_LineNumberTable, buffer)
    , line_number_table(6, 16)
{
    unsigned count = buffer.GetU2();
    if(attribute_length != count * 4 + 2)
        buffer.MarkInvalid("bad line number table length");
    while (count--)
    {
        LineNumberElement& entry = line_number_table.Next();
        entry.start_pc = buffer.GetU2();
        entry.line_number = buffer.GetU2();
    }
}


LocalVariableTableAttribute::LocalVariableTableAttribute(ClassFile& buffer,
                                                         bool generic)
    : AttributeInfo(generic ? ATTRIBUTE_LocalVariableTypeTable
                    : ATTRIBUTE_LocalVariableTable, buffer)
    , local_variable_table(6, 16)
{
    unsigned count = buffer.GetU2();
    if (attribute_length != count * 10 + 2)
        buffer.MarkInvalid("bad local variable table length");
    while (count--)
    {
        LocalVariableElement& entry = local_variable_table.Next();
        entry.start_pc = buffer.GetU2();
        entry.length = buffer.GetU2();
        entry.name_index = buffer.GetU2();
        entry.descriptor_index = buffer.GetU2();
        entry.index = buffer.GetU2();
        if ((buffer.Pool()[entry.name_index] -> Tag() !=
             CPInfo::CONSTANT_Utf8) ||
            (buffer.Pool()[entry.descriptor_index] -> Tag() !=
             CPInfo::CONSTANT_Utf8))
        {
            buffer.MarkInvalid("bad type for local variable table entry");
        }
    }
}


DeprecatedAttribute::DeprecatedAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_Deprecated, buffer)
{
    if (attribute_length)
        buffer.MarkInvalid("bad deprecated attribute length");
}


SignatureAttribute::SignatureAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_Signature, buffer)
    , signature_index(buffer.GetU2())
{
    if (attribute_length != 2)
        buffer.MarkInvalid("bad signature attribute length");
    if (buffer.Pool()[signature_index] -> Tag() != CPInfo::CONSTANT_Utf8)
        buffer.MarkInvalid("bad type for signature attribute");
}


BridgeAttribute::BridgeAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_Bridge, buffer)
{
    if (attribute_length)
        buffer.MarkInvalid("bad bridge attribute length");
}


void StackMapAttribute::StackMapFrame::VerificationTypeInfo::Read(ClassFile& buffer)
{
    int type = buffer.GetU1();
    tag = (VerificationTypeInfoTag) type;
    if (type < TYPE_Top || type > TYPE_Uninitialized)
    {
        type = TYPE_Top;
        buffer.MarkInvalid("bad stack map type");
    }
    if (type >= TYPE_Object)
    {
        info = buffer.GetU2();
        if (type == TYPE_Object &&
            buffer.Pool()[info] -> Tag() != CPInfo::CONSTANT_Class)
        {
            buffer.MarkInvalid("bad stack map info");
        }
    }
}

StackMapAttribute::StackMapFrame::StackMapFrame(ClassFile& buffer)
    : offset(buffer.GetU2())
    , locals(6, 16)
    , stack(6, 16)
    , frame_size(6)
    // +2 for offset, +2 for locals_size, +2 for stack_size
{
    unsigned count = buffer.GetU2();
    while (count--)
    {
        unsigned index = locals.NextIndex();
        locals[index].Read(buffer);
        frame_size += locals[index].Size();
    }
    count = buffer.GetU2();
    while (count--)
    {
        unsigned index = stack.NextIndex();
        stack[index].Read(buffer);
        frame_size += stack[index].Size();
    }
}

StackMapAttribute::StackMapAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_StackMap, buffer)
    , frames(6, 16)
{
    unsigned remaining = attribute_length - 2; // -2 for frame_count
    unsigned count = buffer.GetU2();
    while (count--)
    {
        unsigned index = frames.NextIndex();
        frames[index] = new StackMapFrame(buffer);
        remaining -= frames[index] -> FrameSize();
    }
    if (remaining)
        buffer.MarkInvalid("bytes remaining at end of stack map attribute");
}


AnnotationComponentValue* AnnotationComponentValue::AllocateAnnotationComponentValue(ClassFile& buffer)
{
    AnnotationComponentValueTag tag =
        (AnnotationComponentValueTag) buffer.GetU1();
    switch (tag)
    {
    case COMPONENT_boolean: case COMPONENT_byte: case COMPONENT_char:
    case COMPONENT_short: case COMPONENT_int: case COMPONENT_long:
    case COMPONENT_float: case COMPONENT_double: case COMPONENT_string:
    case COMPONENT_class:
        return new AnnotationComponentConstant(buffer, tag);
    case COMPONENT_enum:
        return new AnnotationComponentEnum(buffer);
    case COMPONENT_annotation:
        return new AnnotationComponentAnnotation(buffer);
    case COMPONENT_array:
        return new AnnotationComponentArray(buffer);
    default:
        buffer.MarkInvalid("unknown annotation component value");
        return new AnnotationComponentValue(tag);
    }
}


AnnotationComponentConstant::AnnotationComponentConstant(ClassFile& buffer,
                                                         AnnotationComponentValueTag _tag)
    : AnnotationComponentValue(_tag)
    , index(buffer.GetU2())
{
    switch (tag)
    {
    case COMPONENT_boolean: case COMPONENT_byte: case COMPONENT_char:
    case COMPONENT_short: case COMPONENT_int:
        if (buffer.Pool()[index] -> Tag() != CPInfo::CONSTANT_Integer)
            buffer.MarkInvalid("bad int-like annotation constant");
        break;
    case COMPONENT_long:
        if (buffer.Pool()[index] -> Tag() != CPInfo::CONSTANT_Long)
            buffer.MarkInvalid("bad long annotation constant");
        break;
    case COMPONENT_float:
        if (buffer.Pool()[index] -> Tag() != CPInfo::CONSTANT_Float)
            buffer.MarkInvalid("bad float annotation constant");
        break;
    case COMPONENT_double:
        if (buffer.Pool()[index] -> Tag() != CPInfo::CONSTANT_Double)
            buffer.MarkInvalid("bad double annotation constant");
        break;
    case COMPONENT_string:
        if (buffer.Pool()[index] -> Tag() != CPInfo::CONSTANT_String)
            buffer.MarkInvalid("bad string annotation constant");
        break;
    case COMPONENT_class:
        if (buffer.Pool()[index] -> Tag() != CPInfo::CONSTANT_Class)
            buffer.MarkInvalid("bad class annotation constant");
        break;
    default:
        assert(false && "invalid annotation constant");
    }
}


AnnotationComponentEnum::AnnotationComponentEnum(ClassFile& buffer)
    : AnnotationComponentValue(COMPONENT_enum)
    , type_name_index(buffer.GetU2())
    , const_name_index(buffer.GetU2())
{
    if (buffer.Pool()[type_name_index] -> Tag() != CPInfo::CONSTANT_Class ||
        buffer.Pool()[const_name_index] -> Tag() != CPInfo::CONSTANT_Utf8)
    {
        buffer.MarkInvalid("bad type for annotation component enum");
    }
}


AnnotationComponentAnnotation::AnnotationComponentAnnotation(ClassFile& buffer)
    : AnnotationComponentValue(COMPONENT_annotation)
{
    attr_value = new Annotation(buffer);
}

AnnotationComponentAnnotation::~AnnotationComponentAnnotation()
{
    delete attr_value;
}

u2 AnnotationComponentAnnotation::Length() const
{
    return 1 + attr_value -> Length(); // +1 tag
}

void AnnotationComponentAnnotation::Put(OutputBuffer& out) const
{
    AnnotationComponentValue::Put(out);
    attr_value -> Put(out);
}

#ifdef JIKES_DEBUG
void AnnotationComponentAnnotation::Print(const ConstantPool& pool) const
{
    attr_value -> Print(pool);
}
#endif // JIKES_DEBUG


AnnotationComponentArray::AnnotationComponentArray(ClassFile& buffer)
    : AnnotationComponentValue(COMPONENT_array)
    , values(6, 16)
    , len(3) // +1 tag, +2 num_values
{
    unsigned count = buffer.GetU2();
    while (count--)
        AddValue(AllocateAnnotationComponentValue(buffer));
}


Annotation::Annotation(ClassFile& buffer)
    : type_index(buffer.GetU2())
    , components(6, 16)
{
    if (buffer.Pool()[type_index] -> Tag() != CPInfo::CONSTANT_Utf8)
        buffer.MarkInvalid("bad type for annotation");
    unsigned i = buffer.GetU2();
    while (i--)
    {
        Component& component = components.Next();
        u2 index = buffer.GetU2();
        component.component_name_index = index;
        if (buffer.Pool()[index] -> Tag() != CPInfo::CONSTANT_Utf8)
            buffer.MarkInvalid("bad type for annotation component name");
        component.component_value =
            AnnotationComponentValue::AllocateAnnotationComponentValue(buffer);
    }
}


AnnotationsAttribute::AnnotationsAttribute(ClassFile& buffer, bool visible)
    : AttributeInfo((visible ? ATTRIBUTE_RuntimeVisibleAnnotations
                     : ATTRIBUTE_RuntimeInvisibleAnnotations), buffer)
    , annotations(6, 16)
{
    unsigned count = buffer.GetU2();
    unsigned length = 2; // +2 num_annotations
    while (count--)
    {
        Annotation* value = new Annotation(buffer);
        annotations.Next() = value;
        length += value -> Length();
    }
    if (length != attribute_length)
        buffer.MarkInvalid("bad annotations attribute length");
}


ParameterAnnotationsAttribute::ParameterAnnotationsAttribute(ClassFile& buffer,
                                                             bool visible)
    : AttributeInfo((visible ? ATTRIBUTE_RuntimeVisibleParameterAnnotations
                     : ATTRIBUTE_RuntimeInvisibleParameterAnnotations), buffer)
    , num_parameters(buffer.GetU1())
    , parameters(NULL)
{
    unsigned length = 1 + 2 * num_parameters;
    // +1 num_parameters, +2 num_annotations * num_parameters
    if (num_parameters)
        parameters = new Tuple<Annotation*>[num_parameters];
    for (unsigned i = 0; i < num_parameters; i++)
    {
        unsigned count = buffer.GetU2();
        while (count--)
        {
            Annotation* value = new Annotation(buffer);
            parameters[i].Next() = value;
            length += value -> Length();
        }
    }
    if (length != attribute_length)
        buffer.MarkInvalid("bad parameter annotations attribute length");
}


AnnotationDefaultAttribute::AnnotationDefaultAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_AnnotationDefault, buffer)
{
    default_value =
        AnnotationComponentValue::AllocateAnnotationComponentValue(buffer);
    if (default_value -> Length() != attribute_length)
        buffer.MarkInvalid("bad annotation default attribute length");
}


EnclosingMethodAttribute::EnclosingMethodAttribute(ClassFile& buffer)
    : AttributeInfo(ATTRIBUTE_EnclosingMethod, buffer)
    , class_index(buffer.GetU2())
    , name_and_type_index(buffer.GetU2())
{
    if (attribute_length != 4)
        buffer.MarkInvalid("bad enclosing method attribute length");
    if (buffer.Pool()[class_index] -> Tag() != CPInfo::CONSTANT_Class ||
        (name_and_type_index &&
         (buffer.Pool()[name_and_type_index] -> Tag() !=
          CPInfo::CONSTANT_NameAndType)))
    {
        buffer.MarkInvalid("bad type for enclosing method attribute");
    }
}


ClassFile::ClassFile(const char* buf, unsigned buf_size)
    : problem(NULL)
    , buffer(buf)
    , buffer_tail(buf + buf_size)
    , magic(GetU4())
    , minor_version(GetU2())
    , major_version(GetU2())
    , constant_pool(8, 4)
    , interfaces(6, 16)
    , fields(6, 16)
    , methods(6, 16)
    , attributes(6, 16)
    , attr_synthetic(NULL)
    , attr_deprecated(NULL)
    , attr_signature(NULL)
    , attr_sourcefile(NULL)
    , attr_innerclasses(NULL)
    , attr_visible_annotations(NULL)
    , attr_invisible_annotations(NULL)
    , attr_enclosing_method(NULL)
{
    if (magic != MAGIC || major_version < 45)
        MarkInvalid("unknown class format");
    u2 count = GetU2();
    if (! count)
        MarkInvalid("empty constant pool");
    else
    {
        while (--count) // skip entry 0
        {
            CPInfo* entry = CPInfo::AllocateCPInfo(*this);
            if (entry -> Large())
                count--; // skip next entry for eight-byte constants
            constant_pool.SetNext(entry);
        }
    }
    if (! constant_pool.Check())
        MarkInvalid("invalid constant pool");
    access_flags = GetU2();
    if (! LegalAccess())
        MarkInvalid("illegal access");
    this_class = GetU2();
    if (constant_pool[this_class] -> Tag() != CPInfo::CONSTANT_Class)
        MarkInvalid("illegal this class");
    super_class = GetU2();
    if (super_class &&
        constant_pool[super_class] -> Tag() != CPInfo::CONSTANT_Class)
    {
        MarkInvalid("illegal super class");
    }
    count = GetU2();
    while (count--)
    {
        u2 inter = GetU2();
        if (constant_pool[inter] -> Tag() != CPInfo::CONSTANT_Class)
            MarkInvalid("illegal interface");
        interfaces.Next() = inter;
    }

    count = GetU2();
    while (count--)
        fields.Next() = new FieldInfo(*this);
    count = GetU2();
    while (count--)
        methods.Next() = new MethodInfo(*this);
    count = GetU2();
    while (count--)
    {
        AttributeInfo* attr = AttributeInfo::AllocateAttributeInfo(*this);
        attributes.Next() = attr;
        switch (attr -> Tag())
        {
        case AttributeInfo::ATTRIBUTE_Synthetic:
            if (attr_synthetic)
                MarkInvalid("duplicate synthetic attribute");
            attr_synthetic = (SyntheticAttribute*) attr;
            SetACC_SYNTHETIC();
            break;
        case AttributeInfo::ATTRIBUTE_Deprecated:
            if (attr_deprecated)
                MarkInvalid("duplicate deprecated attribute");
            attr_deprecated = (DeprecatedAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_Signature:
            if (attr_signature)
                MarkInvalid("duplicate signature attribute");
            attr_signature = (SignatureAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_SourceFile:
            if (attr_sourcefile)
                MarkInvalid("duplicate source file attribute");
            attr_sourcefile = (SourceFileAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_InnerClasses:
            if (attr_innerclasses)
                MarkInvalid("duplicate inner classes attribute");
            attr_innerclasses = (InnerClassesAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_RuntimeVisibleAnnotations:
            if (attr_visible_annotations)
                MarkInvalid("duplicate visible annotations attribute");
            attr_visible_annotations = (AnnotationsAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_RuntimeInvisibleAnnotations:
            if (attr_invisible_annotations)
                MarkInvalid("duplicate invisible annotations attribute");
            attr_invisible_annotations = (AnnotationsAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_EnclosingMethod:
            if (attr_enclosing_method)
                MarkInvalid("duplicate enclosing method attribute");
            attr_enclosing_method = (EnclosingMethodAttribute*) attr;
            break;
        case AttributeInfo::ATTRIBUTE_Generic:
            // ignore
            break;
        default:
            MarkInvalid("invalid method attribute");
        }
    }
}

void ClassFile::Write(TypeSymbol* unit_type) const
{
    Semantic* sem = unit_type -> semantic_environment -> sem;
    Control& control = sem -> control;
    OutputBuffer output_buffer;

    const char* class_file_name = unit_type -> ClassName();
    if (control.option.verbose)
        Coutput << "[write " << class_file_name << "]" << endl;
    assert (Valid());
    if (control.option.nowrite)
        return;

    unsigned i;
    output_buffer.PutU4(MAGIC);
    output_buffer.PutU2(minor_version);
    output_buffer.PutU2(major_version);

    output_buffer.PutU2(constant_pool.Length());
    assert(constant_pool.Check());
    for (i = 1; i < constant_pool.Length(); i++)
    {
        constant_pool[i] -> Put(output_buffer);
        if (constant_pool[i] -> Large())
            i++; // skip the next entry for eight-byte constants
    }

    output_buffer.PutU2(access_flags);
    output_buffer.PutU2(this_class);
    output_buffer.PutU2(super_class);
    output_buffer.PutU2(interfaces.Length());
    for (i = 0; i < interfaces.Length(); i++)
        output_buffer.PutU2(interfaces[i]);
    output_buffer.PutU2(fields.Length());
    for (i = 0; i < fields.Length(); i++)
        fields[i] -> Put(output_buffer);
    output_buffer.PutU2(methods.Length());
    for (i = 0; i < methods.Length(); i++)
        methods[i] -> Put(output_buffer);
    output_buffer.PutU2(attributes.Length());
    for (i = 0; i < attributes.Length(); i++)
        attributes[i] -> Put(output_buffer);

    // Now output to file
    if (! output_buffer.WriteToFile(class_file_name))
    {
        int length = strlen(class_file_name);
        wchar_t* name = new wchar_t[length + 1];
        for (int j = 0; j < length; j++)
            name[j] = class_file_name[j];
        name[length] = U_NULL;

        sem -> ReportSemError(SemanticError::CANNOT_WRITE_FILE,
                              unit_type -> declaration, name);
        delete [] name;
    }
}


//
// This processes a descriptor, and returns the associated type, or else
// control.no_type if the descriptor is bad. Signature is assumed to be null
// terminated, and this adjusts the pointer to the spot just after the type
// parsed.
//
TypeSymbol* Semantic::ProcessSignature(TypeSymbol* base_type,
                                       const char*& signature, TokenIndex tok)
{
    TypeSymbol* type;
    int num_dimensions = 0;
    for ( ; *signature == U_LEFT_BRACKET; signature++)
        num_dimensions++;
    switch (*signature++)
    {
    case U_B:
        type = control.byte_type;
        break;
    case U_C:
        type = control.char_type;
        break;
    case U_D:
        type = control.double_type;
        break;
    case U_F:
        type = control.float_type;
        break;
    case U_I:
        type = control.int_type;
        break;
    case U_J:
        type = control.long_type;
        break;
    case U_S:
        type = control.short_type;
        break;
    case U_Z:
        type = control.boolean_type;
        break;
    case U_V:
        if (num_dimensions)
            return control.no_type;
        type = control.void_type;
        break;
    case U_L:
        {
            const char* str = signature;
            while (*str && *str != U_SEMICOLON)
                str++;
            if (! *str)
            {
                signature = str;
                return control.no_type;
            }
            type = ReadTypeFromSignature(base_type, signature,
                                         str - signature, tok);
            signature = str + 1;
        }
        break;
    case U_T:
        assert(false && "generics not implemented yet");
    case U_NULL: // oops, already exceeded string
        signature--;
        // fallthrough
    default:
        return control.no_type;
    }
    return type -> GetArrayType(this, num_dimensions);
}

//
// This returns the type associated with a CONSTANT_Class entry, or
// control.no_type if there was an error. Unless the type is bad, it is
// necessarily a subtype of java.lang.Object.
//
TypeSymbol* Semantic::GetType(TypeSymbol* base_type, CPClassInfo* class_info,
                              const ConstantPool& constant_pool,
                              TokenIndex tok)
{
    if (! class_info -> Type())
    {
        const char* str = class_info -> TypeName(constant_pool);
        if (*str == U_LEFT_BRACKET)
            class_info -> SetType(ProcessSignature(base_type, str, tok));
        else
        {
            u2 length = class_info -> TypeNameLength(constant_pool);
            class_info -> SetType(ReadTypeFromSignature(base_type, str,
                                                        length, tok));
        }
    }
    return class_info -> Type();
}

//
// Searches for a nested type with the simple name name_symbol located within
// base_type, when base_type was loaded from a .class file.
//
TypeSymbol* Semantic::ProcessNestedType(TypeSymbol* base_type,
                                        NameSymbol* name_symbol,
                                        TokenIndex tok)
{
    TypeSymbol* inner_type = base_type -> FindTypeSymbol(name_symbol);
    if (! inner_type)
    {
        int length = base_type -> ExternalNameLength() + 1 +
            name_symbol -> NameLength(); // +1 for $,... +1 for $
        wchar_t* external_name = new wchar_t[length + 1]; // +1 for '\0';
        wcscpy(external_name, base_type -> ExternalName());
        wcscat(external_name, StringConstant::US_DS);
        wcscat(external_name, name_symbol -> Name());
        NameSymbol* external_name_symbol =
            control.FindOrInsertName(external_name, length);

        delete [] external_name;

        inner_type = base_type -> InsertNestedTypeSymbol(name_symbol);
        inner_type -> outermost_type = base_type -> outermost_type;
        inner_type -> supertypes_closure = new SymbolSet;
        inner_type -> subtypes = new SymbolSet;
        inner_type -> SetExternalIdentity(external_name_symbol);
        inner_type -> SetOwner(base_type);
        inner_type -> SetSignature(control);

        FileSymbol* file_symbol =
            Control::GetFile(control, base_type -> ContainingPackage(),
                             external_name_symbol);
        if (file_symbol)
        {
            inner_type -> file_symbol = file_symbol;
            inner_type -> SetLocation();

            ReadClassFile(inner_type, tok);
        }
        else
        {
            // this symbol table will only contain a default constructor
            inner_type -> SetSymbolTable(1);
            inner_type -> super = control.Object();
            inner_type -> MarkBad();
            AddDefaultConstructor(inner_type);
            ReportSemError(SemanticError::TYPE_NOT_FOUND, tok,
                           inner_type -> ContainingPackageName(),
                           inner_type -> ExternalName());
        }
    }

    return inner_type;
}

//
// Retrieves the innermost nested type from a signature containing '$' as
// the type nesting separator.
//
TypeSymbol* Semantic::RetrieveNestedTypes(TypeSymbol* base_type,
                                          wchar_t* signature, TokenIndex tok)
{
    int len;
    for (len = 0;
         signature[len] != U_NULL && signature[len] != U_DOLLAR; len++)
        ;
    NameSymbol* name_symbol = control.FindOrInsertName(signature, len);
    TypeSymbol* inner_type = ProcessNestedType(base_type, name_symbol, tok);

    return (signature[len] == U_DOLLAR
            ? RetrieveNestedTypes(inner_type, &signature[len + 1], tok)
            : inner_type);
}

//
// Given a UTF8 signature, this finds the type that it refers to, loading
// the type into memory or causing a type not found error.
//
TypeSymbol* Semantic::ReadTypeFromSignature(TypeSymbol* base_type,
                                            const char* utf8_signature,
                                            int length, TokenIndex tok)
{
    TypeSymbol* type = control.type_table.FindType(utf8_signature, length);

    if (type)
    {
        if (type -> SourcePending())
            control.ProcessHeaders(type -> file_symbol);
    }
    else
    {
        wchar_t* signature = new wchar_t[length + 1];
        Control::ConvertUtf8ToUnicode(signature, utf8_signature, length);

        int total_length;
        for (total_length = 0;
             signature[total_length] != U_NULL &&
                 signature[total_length] != U_DOLLAR; total_length++)
            ;

        if (signature[total_length] != U_NULL &&
            Code::IsDecimalDigit(signature[total_length + 1]))
        {
            // an anonymous or a local type?
            for (total_length += 2;
                 Code::IsDecimalDigit(signature[total_length]); total_length++)
                // will stop at next '$' or '\0' !!!
                ;
            if (signature[total_length] != U_NULL)
            {
                // not an anonymous type? then scan local type name
                for (total_length++;
                     signature[total_length] != U_NULL &&
                         signature[total_length] != U_DOLLAR; total_length++)
                    ;
            }
        }

        int len;
        for (len = total_length - 1;
             len >= 0 && signature[len] != U_SLASH; len--)
            ;

        wchar_t* name = &(signature[len + 1]);

        //
        // When a package name is specified in the signature, we look for the
        // type in question in that package, i.e., we redefine package.
        // Otherwise, we search for the type in the unnamed package.
        //
        PackageSymbol* package = NULL;

        //
        // Which package?
        //
        if (len >= 0)
        {
            wchar_t* package_name = new wchar_t[len + 1];
            for (int i = 0; i < len; i++)
                package_name[i] = signature[i];
            package_name[len] = U_NULL;
            package = control.ProcessPackage(package_name);

            if (package -> directory.Length() == 0)
            {
                ReportSemError(SemanticError::PACKAGE_NOT_FOUND, tok,
                               package -> PackageName());
            }
            delete [] package_name;
        }
        else package = control.UnnamedPackage();

        //
        // Process type
        //
        NameSymbol* name_symbol =
            control.FindOrInsertName(name, total_length - (len + 1));
        type = package -> FindTypeSymbol(name_symbol);
        if (type)
        {
            if (type -> SourcePending())
                control.ProcessHeaders(type -> file_symbol);
        }
        else
        {
            FileSymbol* file_symbol = Control::GetFile(control, package,
                                                       name_symbol);
            //
            // If a file_symbol was not found, ReadType will issue an error
            // message
            //
            type = ReadType(file_symbol, package, name_symbol, tok);

            //
            // If we have to do a full check and we have a case where a
            // ".class" file depends on a ".java" file then we should signal
            // that the ".java" file associated with the ".class" file should
            // be recompiled.
            //
            if (control.option.full_check && ! control.option.depend &&
                file_symbol && file_symbol -> IsJava() &&
                ! file_symbol -> IsZip())
            {
                control.recompilation_file_set.AddElement(file_symbol);
                if (! control.option.incremental && control.option.pedantic)
                {
                    ReportSemError(SemanticError::RECOMPILATION, tok,
                                   base_type -> ContainingPackageName(),
                                   base_type -> ExternalName(),
                                   type -> ContainingPackageName(),
                                   type -> ExternalName());
                }
            }
        }

        if (signature[total_length] == U_DOLLAR)
            type = RetrieveNestedTypes(type, &signature[total_length + 1],
                                       tok);

        delete [] signature;
    }

    //
    // Establish a dependence from base_type (read from a class file) to type.
    //
    AddDependence(base_type, type);

    return type;
}

//
// This is called when a type needs to be read from a .class file. It reads
// the file and fills in the symbol table of type.
//
void Semantic::ReadClassFile(TypeSymbol* type, TokenIndex tok)
{
#ifdef JIKES_DEBUG
    control.class_files_read++;
#endif // JIKES_DEBUG

    FileSymbol* file_symbol = type -> file_symbol;

    if (control.option.verbose)  {
        Coutput << "[read "
                << file_symbol -> FileName()
                << "]" << endl;
    }

    if (file_symbol -> IsZip())
    {
        ZipFile* zipfile = new ZipFile(file_symbol);

        if (zipfile -> Buffer() == NULL)
        {
            // this symbol table will only contain a default constructor
            type -> SetSymbolTable(1);
            if (type != control.Object())
                type -> super = (type == control.Throwable()
                                 ? control.Object() : control.Throwable());
            type -> MarkBad();
            AddDefaultConstructor(type);

            ReportSemError(SemanticError::COMPRESSED_ZIP_FILE, tok,
                           file_symbol -> PathSym() -> Name(),
                           type -> ContainingPackageName(),
                           type -> ExternalName());
        }
        else ProcessClassFile(type, zipfile -> Buffer(),
                              file_symbol -> uncompressed_size, tok);
        delete zipfile;
    }
    else
    {
        // Get a ReadObject from the API that contains the file's data.
        JikesAPI::FileReader* classFile =
            JikesAPI::getInstance() -> read(file_symbol -> FileName());
        if (classFile == NULL)
        {
            // this symbol table will only contain a default constructor
            type -> SetSymbolTable(1);
            if (type != control.Object())
                type -> super = (type == control.Throwable()
                                 ? control.Object() : control.Throwable());
            type -> MarkBad();
            AddDefaultConstructor(type);

            ReportSemError(SemanticError::CANNOT_OPEN_CLASS_FILE, tok,
                           type -> ContainingPackageName(),
                           type -> ExternalName());
        }

        else
        {
            // Process the file data.
            unsigned size = classFile -> getBufferSize();

#if defined(WIN32_FILE_SYSTEM)
            size = ((0xFFFFFFFF && GetLastError()) != NO_ERROR) ? 0 : size;
#endif
            ProcessClassFile(type, classFile -> getBuffer(), size, tok);
            delete classFile;
        }
    }
}

//
// Attempts to read in a type from a buffer representing a .class file.
//
void Semantic::ProcessClassFile(TypeSymbol* type, const char* buffer,
                                unsigned buffer_size, TokenIndex tok)
{
    assert(! type -> HeaderProcessed());
    ClassFile* class_data = new ClassFile(buffer, buffer_size);
    if (! class_data -> Valid())
    {
        const char* problem = class_data -> DescribeProblem();
        int length = strlen(problem);
        wchar_t* unicode_problem = new wchar_t[length + 1];
        Control::ConvertUtf8ToUnicode(unicode_problem, problem, length);

        ReportSemError(SemanticError::INVALID_CLASS_FILE, tok,
                       type -> ExternalName(),
                       type -> file_symbol -> PathSym() -> Name(),
                       type -> ContainingPackageName(),
                       unicode_problem);
        type -> MarkBad();
        delete class_data;
        delete unicode_problem;
        return;
    }

    //
    // See if we read the expected type.
    //
    const ConstantPool& pool = class_data -> Pool();
    CPClassInfo* class_info = class_data -> ThisClass();
    if (strcmp(type -> fully_qualified_name -> value,
               class_info -> TypeName(pool)))
    {
        wchar_t* str = new wchar_t[class_info -> TypeNameLength(pool) + 1];
        control.ConvertUtf8ToUnicode(str, class_info -> TypeName(pool),
                                     class_info -> TypeNameLength(pool));
        // Passing NULL as the filename allows the error format to use the
        // platform dependent directory delimiter.
        ReportSemError(SemanticError::WRONG_TYPE_IN_CLASSFILE, tok,
                       type -> ExternalName(),
                       type -> file_symbol -> PathSym() -> Name(),
                       NULL, str);
        type -> MarkBad();
        delete [] str;
        delete class_data;
        return;
    }

    type -> MarkHeaderProcessed();
    type -> MarkConstructorMembersProcessed();
    type -> MarkMethodMembersProcessed();
    type -> MarkFieldMembersProcessed();
    type -> MarkLocalClassProcessingCompleted();
    type -> MarkSourceNoLongerPending();

    //
    // Since the unnamed packages includes all directories on the CLASSPATH,
    // check that types are not duplicated between directories.
    //
    if (! type -> IsNested() &&
        type -> ContainingPackage() == control.UnnamedPackage())
    {
        TypeSymbol* old_type = (TypeSymbol*)
            control.unnamed_package_types.Image(type -> Identity());
        if (! old_type)
            control.unnamed_package_types.AddElement(type);
        else
        {
            ReportSemError(SemanticError::DUPLICATE_TYPE_DECLARATION,
                           tok, type -> Name(), old_type -> FileLoc());
        }
    }

    //
    // On systems such as NT and Win-95 that are not case-sensitive,
    // we need to confirm that the type name specified matches the name
    // in the class file.
    // TODO: Is this necessary, or did the previous check filter this?
//      assert(type_name_length - (n + 1) == type -> ExternalNameLength());
//      int i;
//      for (i = 0; i < type -> ExternalNameLength(); i++)
//      {
//          if (type_name[n + 1 + i] != type -> ExternalName()[i])
//              break;
//      }
//      if (i < type -> ExternalNameLength())
//      {
//          wchar_t* name = new wchar_t[type_name_length + 1];
//          for (int k = 0; k < type_name_length; k++)
//              name[k] = type_name[k];
//          name[type_name_length] = U_NULL;
//          ReportSemError(SemanticError::TYPE_NAME_MISMATCH, tok,
//                         type -> ContainingPackageName(),
//                         type -> ExternalName(), name);
//          delete [] name;
//      }

    //
    // We now have enough information to make a good estimate for the
    // size of the symbol table we need for this class.
    //
    int i;
    const InnerClassesAttribute* inner_classes = class_data -> InnerClasses();
    type -> SetSymbolTable(class_data -> FieldsCount() +
                           class_data -> MethodsCount() +
                           (inner_classes
                            ? inner_classes -> InnerClassesLength() : 0));

    //
    // Read the attributes. We do this now to learn about inner classes and
    // type parameterization, since this information may be needed when
    // processing supertypes, field signatures, and method signatures. Also,
    // we learn the access flags of this type.
    //
    type -> SetFlags(class_data -> Flags());
    type -> ResetACC_SUPER();
    if (class_data -> Deprecated())
        type -> MarkDeprecated();
    if (inner_classes)
    {
        for (i = inner_classes -> InnerClassesLength() - 1; i >= 0; i--)
        {
            const CPClassInfo* inner = inner_classes -> Inner(i, pool);
            const CPClassInfo* outer = inner_classes -> Outer(i, pool);
            if (inner == class_info)
            {
                type -> SetFlags(inner_classes -> Flags(i));
                if (! outer)
                {
                    //
                    // This type is local or anonymous, so we shouldn't be
                    // reading it from the .class file. Marking it anonymous
                    // is our clue to report an error for trying to use the
                    // type by qualified name.
                    //
                    type -> MarkAnonymous();
                }
            }
            else if (outer == class_info && inner_classes -> Name(i, pool) &&
                     inner_classes -> NameLength(i, pool))
            {
                //
                // Some idiot compilers give anonymous classes the name "" and
                // an outer class, rather than obeying JVMS 4.7.5.  For
                // example, mail.jar in Sun's javamail 1.3.1 includes
                // javax.mail.Service with this property. The check for
                // NameLength above skips those invalid entries.
                //
                type -> AddNestedTypeSignature((inner_classes ->
                                                Name(i, pool)),
                                               (inner_classes ->
                                                NameLength(i, pool)));
            }
        }
    }
    if (control.option.full_check &&
        (control.option.unzip || ! type -> file_symbol -> IsZip()))
    {
        type -> ProcessNestedTypeSignatures(this, tok);
    }

    //
    // Tie this type to its supertypes.
    // FIXME: for sdk1_5, read attr_signature
    //
    class_info = class_data -> SuperClass();
    if ((type == control.Object()) != (class_info == NULL))
        type -> MarkBad();
    if (class_info)
    {
        type -> super = GetType(type, class_info, pool, tok);
        if (type -> super -> ACC_INTERFACE() || type -> super -> IsArray() ||
            type -> super -> Bad())
        {
            type -> MarkBad();
        }
        else
        {
            type -> supertypes_closure -> AddElement(type -> super);
            type -> supertypes_closure -> Union(*type -> super ->
                                                supertypes_closure);
        }
    }
    for (i = class_data -> InterfacesCount() - 1; i >= 0; i--)
    {
        class_info = class_data -> Interface(i);
        assert(class_info);
        TypeSymbol* interf = GetType(type, class_info, pool, tok);
        type -> AddInterface(interf);
        type -> supertypes_closure -> AddElement(interf);
        type -> supertypes_closure -> Union(*interf -> supertypes_closure);
        if (! interf -> ACC_INTERFACE())
            type -> MarkBad();
    }

    //
    // Read the fields.
    //
    for (i = class_data -> FieldsCount() - 1; i >= 0; i--)
    {
        const FieldInfo* field = class_data -> Field(i);
        if (field -> ACC_SYNTHETIC())
            continue; // No point reading these - the user can't access them.
        NameSymbol* name_symbol =
            control.ConvertUtf8ToUnicode(field -> Name(pool),
                                         field -> NameLength(pool));
        VariableSymbol* symbol = new VariableSymbol(name_symbol);
        symbol -> SetOwner(type);
        symbol -> MarkComplete();
        symbol -> MarkInitialized();
        symbol -> SetFlags(field -> Flags());
        symbol -> SetSignatureString(field -> Signature(pool, control),
                                     field -> SignatureLength(pool, control));
        if (field -> Deprecated())
            symbol -> MarkDeprecated();
        const CPInfo* value = field -> ConstantValue(pool);
        if (value)
        {
            switch (value -> Tag())
            {
            case CPInfo::CONSTANT_Integer:
                symbol -> initial_value = control.int_pool.
                    FindOrInsert(((CPIntegerInfo*) value) -> Value());
                break;
            case CPInfo::CONSTANT_Long:
                symbol -> initial_value = control.long_pool.
                    FindOrInsert(((CPLongInfo*) value) -> Value());
                break;
            case CPInfo::CONSTANT_Float:
                symbol -> initial_value = control.float_pool.
                    FindOrInsert(((CPFloatInfo*) value) -> Value());
                break;
            case CPInfo::CONSTANT_Double:
                symbol -> initial_value = control.double_pool.
                    FindOrInsert(((CPDoubleInfo*) value) -> Value());
                break;
            case CPInfo::CONSTANT_String:
                {
                    CPStringInfo* str_value = (CPStringInfo*) value;
                    symbol -> initial_value = control.Utf8_pool.
                        FindOrInsert(str_value -> Bytes(pool),
                                     str_value -> Length(pool));
                }
                break;
            default:
                assert(false && "unexpected constant pool entry");
            }
        }
        type -> InsertVariableSymbol(symbol);
        if (control.option.full_check &&
            (control.option.unzip || ! type -> file_symbol -> IsZip()))
        {
            symbol -> ProcessVariableSignature(this, tok);
        }
    }

    //
    // Read the methods.
    //
    for (i = class_data -> MethodsCount() - 1; i >= 0; i--)
    {
        const MethodInfo* method = class_data -> Method(i);
        NameSymbol* name_symbol =
            control.ConvertUtf8ToUnicode(method -> Name(pool),
                                         method -> NameLength(pool));
        if (method -> ACC_SYNTHETIC() ||
            name_symbol == control.clinit_name_symbol)
        {
            continue; // No point reading these - the user can't access them.
        }
        MethodSymbol* symbol = new MethodSymbol(name_symbol);
        symbol -> SetContainingType(type);
        symbol -> SetFlags(method -> Flags());
        Utf8LiteralValue* sig = control.Utf8_pool.
            FindOrInsert(method -> Signature(pool, control),
                         method -> SignatureLength(pool, control));
        symbol -> SetSignature(sig);
        if (method -> Deprecated())
            symbol -> MarkDeprecated();
        const ExceptionsAttribute* throws_clause = method -> Exceptions();
        if (throws_clause)
        {
            for (int j = throws_clause -> NumberOfExceptions() - 1;
                 j >= 0; j--)
            {
                const CPClassInfo* exception =
                    throws_clause -> Exception(j, pool);
                symbol ->
                    AddThrowsSignature(exception -> TypeName(pool),
                                       exception -> TypeNameLength(pool));
            }
        }

        type -> InsertMethodSymbol(symbol);
        if (control.option.full_check &&
            (control.option.unzip || ! type -> file_symbol -> IsZip()))
        {
            symbol -> ProcessMethodSignature(this, tok);
        }
    }

    //
    // If requested by +F, suck in all types referred to in the constant pool
    // (both in CONSTANT_Class and in descriptors of CONSTANT_NameAndType).
    //
    if (control.option.full_check &&
        (control.option.unzip || ! type -> file_symbol -> IsZip()))
    {
        for (i = pool.Length() - 1; i > 0; i--)
        {
            if (pool[i] -> Tag() == CPInfo::CONSTANT_Class)
                GetType(type, (CPClassInfo*) pool[i], pool, tok);
            else if (pool[i] -> Tag() == CPInfo::CONSTANT_NameAndType)
            {
                const char* signature =
                    ((CPNameAndTypeInfo*) pool[i]) -> Signature(pool);
                if (*signature != U_LEFT_PARENTHESIS)
                    // no '(' indicates a field descriptor
                    ProcessSignature(type, signature, tok);
                else // a method descriptor
                {
                    while (*signature && *signature++ != U_RIGHT_PARENTHESIS);
                    ProcessSignature(type, signature, tok);
                }
            }
        }
    }

    delete class_data;
    type -> CompressSpace();
}


#ifdef HAVE_JIKES_NAMESPACE
} // Close namespace Jikes block
#endif
