// $Id: modifier.cpp,v 1.33 2004/03/25 13:32:28 ericb 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 "platform.h"
#include "semantic.h"
#include "option.h"
#include "javasym.h"
#include "stream.h"

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


//
// Process modifiers, returning the AccessFlags they represent.
//
// Diagnostics are emitted in the following cases:
// . Invalid modifiers based on context.
// . Duplicated mutually exclusive modifiers (public, protected, private).
// . Duplicated general modifiers.
// . Modifier combinations not legal (such as final abstract).
// . Modifiers not in the recommended order (controlled by +Pmodifier-order).
// . Explicit specification of implicit modifier (controlled by
//   +Predundant-modifiers).
//
AccessFlags Semantic::ProcessModifiers(AstModifiers* modifiers,
                                       const wchar_t* declaration_kind_name,
                                       u2 valid_flags, u2 implicit_flags)
{
    if (! modifiers)
        return AccessFlags(implicit_flags);
    AccessFlags access_flags;
    bool seen_keyword = false;
    for (unsigned i = 0; i < modifiers -> NumModifiers(); i++)
    {
        Ast* mod = modifiers -> Modifier(i);
        AstAnnotation* annotation = mod -> AnnotationCast();
        AstModifierKeyword* keyword = mod -> ModifierKeywordCast();
        if (annotation)
        {
            if (seen_keyword)
            {
                ReportSemError(SemanticError::RECOMMENDED_ANNOTATION_ORDER,
                               mod,
                               lex_stream -> NameString(annotation -> name ->
                                                        identifier_token));
            }
            // TODO: Add annotation support for 1.5.
            ReportSemError(SemanticError::ANNOTATION_MODIFIER_UNSUPPORTED,
                           mod);
            continue;
        }
        assert(keyword);
        seen_keyword = true;
        u2 flag;
        switch (lex_stream -> Kind(keyword -> modifier_token))
        {
        case TK_abstract: flag = AccessFlags::ACCESS_ABSTRACT; break;
        case TK_final: flag = AccessFlags::ACCESS_FINAL; break;
        case TK_native: flag = AccessFlags::ACCESS_NATIVE; break;
        case TK_public: flag = AccessFlags::ACCESS_PUBLIC; break;
        case TK_protected: flag = AccessFlags::ACCESS_PROTECTED; break;
        case TK_private: flag = AccessFlags::ACCESS_PRIVATE; break;
        case TK_static: flag = AccessFlags::ACCESS_STATIC; break;
        case TK_strictfp: flag = AccessFlags::ACCESS_STRICTFP; break;
        case TK_synchronized: flag = AccessFlags::ACCESS_SYNCHRONIZED; break;
        case TK_transient: flag = AccessFlags::ACCESS_TRANSIENT; break;
        case TK_volatile: flag = AccessFlags::ACCESS_VOLATILE; break;
        default:
            assert(false && "token was not modifier");
        }
        if ((flag & valid_flags) == 0)
        {
            ReportSemError(SemanticError::INVALID_MODIFIER, mod,
                           lex_stream -> NameString(keyword -> modifier_token),
                           declaration_kind_name);
        }
        else if ((flag & AccessFlags::ACCESS_ACCESS) &&
                 (access_flags.Flags() & AccessFlags::ACCESS_ACCESS))
        {
            ReportSemError(SemanticError::DUPLICATE_ACCESS_MODIFIER, mod);
        }
        else if (access_flags.IsSet(flag))
        {
            ReportSemError(SemanticError::DUPLICATE_MODIFIER, mod,
                           lex_stream -> NameString(keyword ->
                                                    modifier_token));
        }
        else
        {
            // We have a valid flag if it is alone.
            if ((flag & implicit_flags) != 0)
            {
                ReportSemError(SemanticError::REDUNDANT_MODIFIER, mod,
                               lex_stream -> NameString(keyword ->
                                                        modifier_token),
                               declaration_kind_name);
            }
            if (! access_flags.RecommendedOrder(flag))
            {
                ReportSemError(SemanticError::RECOMMENDED_MODIFIER_ORDER, mod,
                               lex_stream -> NameString(keyword ->
                                                        modifier_token));
            }
            access_flags.SetFlags(flag);
            if (access_flags.ACC_FINAL())
            {
                if (access_flags.ACC_VOLATILE())
                {
                    // We know it's a field because of volatile.
                    ReportSemError(SemanticError::VOLATILE_FINAL_FIELD, mod,
                                   lex_stream -> NameString(keyword ->
                                                            modifier_token));
                    access_flags.ResetFlags(flag);
                }
                else if (access_flags.ACC_ABSTRACT())
                {
                    ReportSemError(SemanticError::FINAL_ABSTRACT_ENTITY, mod,
                                   declaration_kind_name);
                    access_flags.ResetFlags(flag);
                }
            }
            else if (access_flags.ACC_ABSTRACT() &&
                     (valid_flags & AccessFlags::ACCESS_NATIVE))
            {
                //
                // Classes can be private static abstract strictfp, but methods
                // cannot. Hence, we checked that we are dealing with a method
                // by looking for NATIVE in the legal flags.
                //
                if (access_flags.ACC_SYNCHRONIZED() ||
                    access_flags.ACC_NATIVE() || access_flags.ACC_PRIVATE() ||
                    access_flags.ACC_STATIC() || access_flags.ACC_STRICTFP())
                {
                    ReportSemError(SemanticError::BAD_ABSTRACT_METHOD_MODIFIER,
                                   mod);
                    access_flags.ResetFlags(flag);
                }
            }
            else if (access_flags.ACC_STRICTFP() && access_flags.ACC_NATIVE())
            {
                ReportSemError(SemanticError::STRICTFP_NATIVE_METHOD, mod);
                access_flags.ResetFlags(flag);
            }
        }
    }
    access_flags.SetFlags(implicit_flags);
    return access_flags;
}

//
// Process modifiers of packages (annotations only, added in 1.5).
//
AccessFlags Semantic::ProcessPackageModifiers(AstPackageDeclaration* package)
{
    return ProcessModifiers(package -> modifiers_opt, L"a package", 0);
}


//
// Process modifiers of top-level types.
//
AccessFlags Semantic::ProcessTopLevelTypeModifiers(AstDeclaredType* decl)
{
    AstClassDeclaration* class_decl = decl -> ClassDeclarationCast();
    AstEnumDeclaration* enum_decl = decl -> EnumDeclarationCast();
    AstInterfaceDeclaration* interface_decl =
        decl -> InterfaceDeclarationCast();
    AstAnnotationDeclaration* annotation_decl =
        decl -> AnnotationDeclarationCast();
    u2 valid_flags;
    u2 implicit_flags = 0;
    const wchar_t* context;
    if (class_decl)
    {
        context = L"a top-level class";
        valid_flags = AccessFlags::ACCESS_ABSTRACT |
            AccessFlags::ACCESS_FINAL | AccessFlags::ACCESS_PUBLIC |
            AccessFlags::ACCESS_STRICTFP;
    }
    else if (enum_decl)
    {
        valid_flags = AccessFlags::ACCESS_ABSTRACT |
            AccessFlags::ACCESS_FINAL | AccessFlags::ACCESS_PUBLIC |
            AccessFlags::ACCESS_STRICTFP;
        implicit_flags = AccessFlags::ACCESS_FINAL;
        for (unsigned i = 0; i < enum_decl -> NumEnumConstants(); i++)
        {
            if (enum_decl -> EnumConstant(i) -> class_body_opt)
            {
                valid_flags &= ~ AccessFlags::ACCESS_FINAL;
                implicit_flags = 0;
                break;
            }
        }
        context = implicit_flags
            ? L"a top-level enumeration (all enum constants without a body)"
            : L"a top-level enumeration (some enum constants with a body)";
    }
    else
    {
        valid_flags = AccessFlags::ACCESS_ABSTRACT |
            AccessFlags::ACCESS_PUBLIC | AccessFlags::ACCESS_STRICTFP;
        implicit_flags = AccessFlags::ACCESS_INTERFACE |
            AccessFlags::ACCESS_ABSTRACT;
        if (interface_decl)
            context = L"a top-level interface";
        else
        {
            assert(annotation_decl);
            context = L"a top-level annotation type";
            implicit_flags |= AccessFlags::ACCESS_ANNOTATION;
        }
    }
    return ProcessModifiers(decl -> modifiers_opt, context, valid_flags,
                            implicit_flags);
}


//
// Process modifiers of local classes declared as a statement in a method.
//
AccessFlags Semantic::ProcessLocalClassModifiers(AstDeclaredType* decl)
{
    AstClassDeclaration* class_decl = decl -> ClassDeclarationCast();
    AstEnumDeclaration* enum_decl = decl -> EnumDeclarationCast();
    u2 valid_flags = AccessFlags::ACCESS_ABSTRACT |
        AccessFlags::ACCESS_FINAL | AccessFlags::ACCESS_STRICTFP;
    u2 implicit_flags = 0;
    const wchar_t* context;
    if (class_decl)
        context = L"a local inner class";
    else
    {
        assert(enum_decl);
        implicit_flags = AccessFlags::ACCESS_FINAL;
        for (unsigned i = 0; i < enum_decl -> NumEnumConstants(); i++)
        {
            if (enum_decl -> EnumConstant(i) -> class_body_opt)
            {
                valid_flags &= ~ AccessFlags::ACCESS_FINAL;
                implicit_flags = 0;
                break;
            }
        }
        context = implicit_flags
            ? L"a local enumeration (all enum constants without a body)"
            : L"a local enumeration (some enum constants with a body)";
    }
    return ProcessModifiers(decl -> modifiers_opt, context, valid_flags,
                            implicit_flags);
}


//
// Process modifiers of nested and inner types.
//
AccessFlags Semantic::ProcessNestedTypeModifiers(TypeSymbol* containing_type,
                                                 AstDeclaredType* decl)
{
    AstClassDeclaration* class_decl = decl -> ClassDeclarationCast();
    AstEnumDeclaration* enum_decl = decl -> EnumDeclarationCast();
    AstInterfaceDeclaration* interface_decl =
        decl -> InterfaceDeclarationCast();
    AstAnnotationDeclaration* annotation_decl =
        decl -> AnnotationDeclarationCast();
    u2 valid_flags;
    u2 implicit_flags = 0;
    const wchar_t* context;
    if (class_decl || enum_decl)
    {
        if (containing_type -> ACC_INTERFACE())
        {
            valid_flags = AccessFlags::ACCESS_ABSTRACT |
                AccessFlags::ACCESS_FINAL | AccessFlags::ACCESS_PUBLIC |
                AccessFlags::ACCESS_STATIC | AccessFlags::ACCESS_STRICTFP;
            implicit_flags = AccessFlags::ACCESS_STATIC |
                AccessFlags::ACCESS_PUBLIC;
        }
        else
        {
            valid_flags = AccessFlags::ACCESS_ABSTRACT |
                AccessFlags::ACCESS_FINAL | AccessFlags::ACCESS_ACCESS |
                AccessFlags::ACCESS_STATIC | AccessFlags::ACCESS_STRICTFP;
        }
        if (class_decl)
            context = L"a member class";
        else
        {
            implicit_flags |= AccessFlags::ACCESS_FINAL |
                AccessFlags::ACCESS_STATIC;
            for (unsigned i = 0; i < enum_decl -> NumEnumConstants(); i++)
            {
                if (enum_decl -> EnumConstant(i) -> class_body_opt)
                {
                    valid_flags &= ~ AccessFlags::ACCESS_FINAL;
                    implicit_flags &= ~ AccessFlags::ACCESS_FINAL;
                    break;
                }
            }
            context = (implicit_flags & AccessFlags::ACCESS_FINAL)
                ? L"a member enumeration (all enum constants without a body)"
                : L"a member enumeration (some enum constants with a body)";
        }
    }
    else
    {
        if (containing_type -> ACC_INTERFACE())
        {
            valid_flags = AccessFlags::ACCESS_ABSTRACT |
                AccessFlags::ACCESS_PUBLIC | AccessFlags::ACCESS_STATIC |
                AccessFlags::ACCESS_STRICTFP;
            implicit_flags = AccessFlags::ACCESS_INTERFACE |
                AccessFlags::ACCESS_ABSTRACT | AccessFlags::ACCESS_STATIC |
                AccessFlags::ACCESS_PUBLIC;
        }
        else
        {
            valid_flags = AccessFlags::ACCESS_ABSTRACT |
                AccessFlags::ACCESS_ACCESS | AccessFlags::ACCESS_STATIC |
                AccessFlags::ACCESS_STRICTFP;
            implicit_flags = AccessFlags::ACCESS_INTERFACE |
                AccessFlags::ACCESS_ABSTRACT | AccessFlags::ACCESS_STATIC;
        }
        if (interface_decl)
            context = L"a member interface";
        else
        {
            assert(annotation_decl);
            context = L"a member annotation type";
            implicit_flags |= AccessFlags::ACCESS_ANNOTATION;
        }
    }
    return ProcessModifiers(decl -> modifiers_opt, context, valid_flags,
                            implicit_flags);
}


//
// Process modifiers of fields declared in classes.
//
AccessFlags Semantic::ProcessFieldModifiers(AstFieldDeclaration* field_declaration)
{
    return ProcessModifiers(field_declaration -> modifiers_opt,
                            L"a class's member field",
                            (AccessFlags::ACCESS_ACCESS |
                             AccessFlags::ACCESS_STATIC |
                             AccessFlags::ACCESS_FINAL |
                             AccessFlags::ACCESS_TRANSIENT |
                             AccessFlags::ACCESS_VOLATILE));
}


//
// Process modifiers of local variables.
//
AccessFlags Semantic::ProcessLocalModifiers(AstLocalVariableStatement* decl)
{
    return ProcessModifiers(decl -> modifiers_opt, L"a local variable",
                            AccessFlags::ACCESS_FINAL);
}


//
// Process modifiers of parameters.
//
AccessFlags Semantic::ProcessFormalModifiers(AstFormalParameter* decl)
{
    return ProcessModifiers(decl -> modifiers_opt, L"a formal parameter",
                            AccessFlags::ACCESS_FINAL);
}


//
// Process modifiers of methods declared in classes.
//
AccessFlags Semantic::ProcessMethodModifiers(AstMethodDeclaration* decl)
{
    return ProcessModifiers(decl -> modifiers_opt, L"a class's member method",
                            (AccessFlags::ACCESS_ACCESS |
                             AccessFlags::ACCESS_STATIC |
                             AccessFlags::ACCESS_STRICTFP |
                             AccessFlags::ACCESS_ABSTRACT |
                             AccessFlags::ACCESS_FINAL |
                             AccessFlags::ACCESS_NATIVE |
                             AccessFlags::ACCESS_SYNCHRONIZED));
}


//
// Process modifiers of methods declared in interfaces.
//
AccessFlags Semantic::ProcessInterfaceMethodModifiers(AstMethodDeclaration* method_declaration)
{
    return ProcessModifiers(method_declaration -> modifiers_opt,
                            L"an interface's member method",
                            (AccessFlags::ACCESS_PUBLIC |
                             AccessFlags::ACCESS_ABSTRACT),
                            (AccessFlags::ACCESS_PUBLIC |
                             AccessFlags::ACCESS_ABSTRACT));
}


//
// Process modifiers of constructors.
//
AccessFlags Semantic::ProcessConstructorModifiers(AstConstructorDeclaration* constructor_declaration)
{
    return ProcessModifiers(constructor_declaration -> modifiers_opt,
                            L"a constructor", AccessFlags::ACCESS_ACCESS);
}


//
// Process modifiers of fields declared in interfaces.
//
AccessFlags Semantic::ProcessInterfaceFieldModifiers(AstFieldDeclaration* decl)
{
    return ProcessModifiers(decl -> modifiers_opt,
                            L"an interface's member field",
                            (AccessFlags::ACCESS_PUBLIC |
                             AccessFlags::ACCESS_STATIC |
                             AccessFlags::ACCESS_FINAL),
                            (AccessFlags::ACCESS_PUBLIC |
                             AccessFlags::ACCESS_STATIC |
                             AccessFlags::ACCESS_FINAL));
}


//
// Process static and instance initializer modifiers.
//
AccessFlags Semantic::ProcessInitializerModifiers(AstInitializerDeclaration* initializer)
{
    return ProcessModifiers(initializer -> modifiers_opt,
                            L"an initializer block",
                            AccessFlags::ACCESS_STATIC);
}


//
// Process static and instance initializer modifiers.
//
AccessFlags Semantic::ProcessEnumConstantModifiers(AstEnumConstant* decl)
{
    return ProcessModifiers(decl -> modifiers_opt, L"an enum constant", 0,
                            (AccessFlags::ACCESS_PUBLIC |
                             AccessFlags::ACCESS_STATIC |
                             AccessFlags::ACCESS_FINAL |
                             AccessFlags::ACCESS_ENUM));
}

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