IMPLEMENTATION MODULE YaflMetImplementation;

IMPORT ArrayList;
FROM Conversions IMPORT IntConversions;
FROM YaflLex IMPORT LexicalAnalyzer;   
FROM List IMPORT List;
FROM YaflParser IMPORT NonTerminal, LimitedWalker;
FROM YaflSymbols IMPORT SymbolTable;
FROM YaflDeclarations IMPORT SingleDataItem, Declaration, 
                             ConstDeclaration, InheritsClause;
FROM YaflModules IMPORT CompilationUnit; 
FROM YaflNTList IMPORT DeclList;
FROM YaflParamClasses IMPORT ClassActualSet;
FROM YaflPragmas IMPORT Pragma;
FROM YaflPredefined IMPORT PredefItems, PredefMethod, PredefClass, 
                           ResultDataItem;
FROM YaflQuantifiers IMPORT SetSpecification;                           
FROM YaflType IMPORT Type;
FROM YaflCfg IMPORT CurrentSpot, YaflCfg;
FROM YaflClasses IMPORT ClassDeclaration, ConstrainedClassDecl,
                        VirtualClassDecl;
IMPORT LookAhead;
FROM Streams IMPORT StdOut;
FROM YaflCreator IMPORT Creators;
FROM YaflDictionary IMPORT EntryReference, MethodReference, EntryDictionary,
                        CallReferenceList, CallReference, AttributeReference;
FROM YaflDesignator  IMPORT Desig, DesigElement;
FROM YaflType IMPORT TypedNonTerminal;
FROM YaflLiteral IMPORT Literal;
FROM YaflStatements IMPORT Assignment, DebugStatement, AssertStatement,
                           Statement, MethodInvStatement;
FROM YaflNTList IMPORT NTList;
FROM YaflMethods IMPORT Formal;
FROM YaflExpressions IMPORT ActualList;

           
IMPORT SYSTEM;

  CLASS MethodImplementation;
    INHERITS MethodDeclaration(MethImplCodeGenerator);

    VAR
      TheLocalsList: DeclList (SingleDataItem);
      TheMethodsList: DeclList (MethodImplementation);
      TheConstsList: DeclList (ConstDeclaration);
      ThePragmasList: DeclList (Pragma);
      TheStmtList: StatementList;
      TheTagDecl: SingleDataItem;
      TheDefinition: MethodDefinition;
      TheContextNr, TheStructNr: INTEGER;
      EndId: Ident; -- this variable was local to the Parse method;
                    -- it was used to check the matching of begin
                    -- and end identifiers;we moved it here so we may
                    -- know the range of text belonging to the method;
                    -- this is useful for the debugger or other tools;
      MethodNumberInDual: INTEGER;

    REDEFINE METHOD CREATE (LineNr, ColNr: INTEGER);
      BEGIN
      BASE (LineNr, ColNr);
      TheLocalsList.CREATE;
      TheMethodsList.CREATE;
      TheConstsList.CREATE;
      ThePragmasList.CREATE;
      SubDecls.AppendList (TheLocalsList);
      SubDecls.AppendList (TheMethodsList);
      SubDecls.AppendList (TheConstsList);
      SubDecls.AppendList (ThePragmasList);
      MethodNumberInDual := -1;
      END CREATE;

    METHOD EndIdent:Ident; -- made for the debugger
      BEGIN
      RESULT := EndId;
      END EndIdent;

    METHOD ClassImpl: ClassImplementation;
      BEGIN
      WHAT Class OF
        IN ClassImplementation:
          RESULT := TAG;
          END;
        END;
      END ClassImpl;

    METHOD AttachDefinition (Def: MethodDefinition);
      BEGIN
      TheDefinition := Def;
      END AttachDefinition;

    REDEFINE METHOD SubTree: ARRAY OF NonTerminal;
      BEGIN
      RESULT.CREATE (1);
      RESULT[0] := TheStmtList;
      RESULT := BASE + RESULT;
      END SubTree;

    REDEFINE METHOD Parse(Lkh: LookAhead);
      VAR
        DList: DeclList;
      BEGIN
      ParseDeclaration (Lkh);
      IF NOT Deferred THEN
        DList := Lkh.AcceptDeclarationList(TRUE, LookAhead.MethodContext);
        IF DList <> VOID THEN
          DList.SetFather (THIS);
          END;
        SubDecls.AppendFromList (DList);
        IF Lkh.CurrentToken = Lkh.Begin THEN
          Lkh.Accept (LexicalAnalyzer.Begin);
          TheStmtList := Lkh.AcceptStatementList;
          SetSon (TheStmtList);
          END;
        Lkh.Accept (LexicalAnalyzer.End);
        EndId := Lkh.AcceptIdent;
        IF (EndId <> VOID) AND (Id <> VOID) AND (Id.Data <> EndId.Data) THEN
          Error ("Non matching closing identifier");
          END;
        Lkh.Accept (LexicalAnalyzer.SemiColon);
        END;
      END Parse;

    VAR
      TheEnclosing: MethodImplementation;
      EnclosingAsked: BOOLEAN;

    REDEFINE METHOD Enclosing: MethodImplementation;
      VAR
        p: NonTerminal;
        Ok: BOOLEAN;
      BEGIN
      IF NOT EnclosingAsked THEN
        EnclosingAsked := TRUE;
        p := Father;  
        WHILE NOT Ok DO
          WHAT p OF
            IN ClassImplementation:
              Ok := TRUE;
              END;
            IN MethodImplementation:
              TheEnclosing := TAG;
              Ok := TRUE;
              END;
           ELSE
            p := p.Father;
            ASSERT p <> VOID;
            END;
          END;
        END;
      RESULT := TheEnclosing;
      END Enclosing;
      
    METHOD Definition: MethodDefinition;
      BEGIN
      RESULT := TheDefinition;
      END Definition;

    REDEFINE METHOD WhatAmI: ARRAY OF CHAR;
      BEGIN
      RESULT := "MethodImplementation";
      END WhatAmI;

    METHOD EnterTag;
      BEGIN
      TheTagDecl := PredefItems.Tag.CLONE;
      ---------------------------------
      -- When entering a method, TAG
      -- holds no type, and is not
      -- used.
      ---------------------------------
      ASSERT NOT TheTagDecl.UsedRead;
      ASSERT NOT TheTagDecl.UsedWrite;
      ASSERT TheTagDecl.GetType = VOID;
      SetSon (TheTagDecl);
      SymbolTable.Enter (TheTagDecl.Id.Data, TheTagDecl);
      END EnterTag;

    METHOD TagDecl: SingleDataItem;
      BEGIN
      RESULT := TheTagDecl;
      END TagDecl;


    METHOD Result: PredefDataItem;
      BEGIN
      RESULT := LocalResult;
      END Result;
    
    ---------------------------
    -- Enter THIS and its type.
    ---------------------------
    METHOD EnterThis;
      BEGIN
      This.Enter;
      END EnterThis;
           
    -------------------------------
    -- Enter the BASE predefined method in the symbol
    -- table, if and only if the THIS redefines a previously
    -- defined version of the same method.
    -------------------------------
    METHOD EnterBase;
      BEGIN
      SymbolTable.Enter (PredefItems.Base.Id.Data, PredefItems.Base);
      END EnterBase;
            
    
    REDEFINE METHOD SetAssertionsVisibility;
      BEGIN
      BASE;
      --EnterThis;
      WHAT Class OF
        IN ClassImplementation:
          TAG.Attributes.Enter;
          END;
        END;    
      END SetAssertionsVisibility;
      
    REDEFINE METHOD Tag;
      BEGIN
      ---------------------------------------------
      -- Memorize the current Method Implementation
      ---------------------------------------------
--      IF NOT IsLocal AND NOT Redefine THEN
--        VOID := ContextNr;
--        VOID := StructNr;
--        END;
      IF YaflCfg.VerboseLevel > 1 THEN
        StdOut.WriteLine ("Tagging method " + Class.Id.Data + "." + Id.Data);
        END;
      ASSERT CurrentSpot.CurrentClass <> VOID;
      CurrentSpot.PushCurrentMethod (THIS);
      SymbolTable.PushLevel;
      EnterFormals;
      EnterThis;
      EnterTag;
--------------------------------------------------------------------
-- patched by bernard
-- enter RESULT before further declarations, in the case where RESULT
-- is referenced by a VarType pragma
--------------------------------------------------------------------
      IF Return <> VOID THEN
        EnterResult;
        END;
      SubDecls.Enter;
      SubDecls.UniqueTag;
      ---------------------------------------------
      -- Reset the current method reference, as
      -- it might have been modified by the Tag of
      -- local methods.
      ---------------------------------------------
--      IF Return <> VOID THEN
--        EnterResult;
--        END;
      IF Redefine THEN
        IF IsLocal THEN
          Error ("A local method cannot REDEFINE");
         ELSE
          EnterBase;
          END;
       ELSIF IsCreate THEN
        Error ("CREATE must be redefined");
        END;
      IF IsCreate THEN
        IF (Definition = VOID) THEN
          IF (ClassImpl.Definition <> VOID) THEN
            Error ("CREATE must be redefined public[1]");
            END;
         ELSIF Definition.ClassDef <> ClassImpl.Definition THEN
          Error ("CREATE must be redefined public[2]");
          END;
        END;
      IF TheStmtList <> VOID THEN
        TheStmtList.UniqueTag;
        END;
      SymbolTable.PopLevel;
      CurrentSpot.PopCurrentMethod;
      END Tag;

    REDEFINE METHOD Enter;
      VAR
        Decl: Declaration;
        Cl: ClassImplementation;
      BEGIN
      IF Definition = VOID THEN 
        ----------------------------------
        -- As a valuable side effect, we'll now consider the
        -- possibility of attaching the corresponding
        -- method definition to THIS.
        ----------------------------------
        Cl := ClassImpl;
        IF Cl.Definition <> VOID THEN
          Decl := Cl.Definition.GetLocalDecl(Id.Data);
          IF Decl <> VOID THEN
            WHAT Decl OF
              IN MethodDefinition:
                IF TAG.Class = THIS.ClassImpl.Definition THEN
                  AttachDefinition (TAG);
                  END;
                END;
              IN MethodImplementation:
                END;
              IN PredefMethod:
                END;
              END;
            END;
          END;
        END;
      -------------------------
      -- If there is a matching declaration in the definition
      -- module, use it. Otherwise, us the method header
      -------------------------
      IF NOT IsPredefined THEN
        IF NOT Redefine THEN
          IF Definition <> VOID THEN
            Definition.Enter;
           ELSE
            BASE;
            END;
          END;
        END;
      END Enter;

    -----------------------------
    -- Check type usage
    -----------------------------
    REDEFINE METHOD CheckType;
      VAR
        RedefMethod: MethodDeclaration;
        TheId: ARRAY OF CHAR;
        Cl: ClassDeclaration;
      BEGIN
      CurrentSpot.PushCurrentMethod (THIS);
      BASE;
      --------------------------------------------
      -- Check the conformity of the method header
      -- with the original declaration if it is a
      -- non predefined Redefined method.
      --------------------------------------------
      IF YaflCfg.VerboseLevel > 1 THEN
        StdOut.WriteLine ("Checking type " + Class.Id.Data + "." + Id.Data);
        END;
      TheId := Id.Data;
      IF (NOT IsCreate) AND
         (TheId <> PredefItems.Clone.Id.Data) AND
         (TheId <> PredefItems.Kill.Id.Data) AND
         (TheId <> PredefItems.Base.Id.Data) THEN
        RedefMethod := Redefined(Original := TRUE);
        IF RedefMethod <> VOID THEN
          CheckConformity (RedefMethod);
         -----------------------
         -- If I don't redefine anything, I must make sure there
         -- is no method or constant with the same name earlier
         -- in the inheritance chain.
         -----------------------
         ELSIF (NOT Redefine) AND (Class.Inherits <> VOID) THEN
          Cl := Class.Inherits.Class;
          IF Cl <> VOID THEN
            IF Cl.GetDecl(TheId) <> VOID THEN
              Error ("Name clash with " + Cl.Id.Data + "." + TheId);
              END;
            END;
          END;
        -------------
        -- Issue at least a warning message if the
        -- method does not redefine anything; while 
        -- it is marked as REDEFINE
        -------------  
        DEBUG
          IF Redefine AND (RedefMethod = VOID) THEN
            Warning ("Strange, strange...");
            END;
          END;
        END;
      -----------------------------------
      -- Check the Method's as well as the Locals's declarations.
      -----------------------------------       
      TheMethodsList.UniqueCheckType;
      TheLocalsList.UniqueCheckType;
      ----------------------------
      -- Check the method's body.
      ----------------------------      
      IF TheStmtList <> VOID THEN
        ---------------------------------------------
        -- Memorize the current Method Implementation
        ---------------------------------------------
        TheStmtList.UniqueCheckType;
        END;
      -------------------------------------
      -- If the method contains no INLINE statement,
      -- some semantic warning may be issued.
      -------------------------------------        
      IF NOT UsesInline THEN
        ----------------------
        -- Check if locals are used reasonably, and
        -- issue warning messages if they are not.
        ----------------------
        FOR Loc IN TheLocalsList DO
          Loc.CheckUsage;
          END;      
        --------------------------------
        -- Check if RESULT is assigned; issue a warning
        -- if it is not and if there is at least a valid
        -- statement in the method.
        --------------------------------
        IF Return <> VOID THEN
          ASSERT LocalResult <> VOID;
          IF (TheStmtList <> VOID) AND NOT TheStmtList.IsEmpty THEN
            IF NOT LocalResult.UsedWrite THEN
              Warning ("RESULT is never given a value");
              END;
            END;
          END;
        END;
      CurrentSpot.PopCurrentMethod;
      END CheckType;
      
    REDEFINE METHOD SetNoChangeRequested;
      BEGIN
      BASE;
      IF (Definition <> VOID) AND (NOT Definition.NoChangeRequested) THEN
        Definition.SetNoChangeRequested;
        IF Public THEN
          Warning("NO_CHANGE must be declared in definition module");
          END;
        END;
      END SetNoChangeRequested;

    REDEFINE METHOD SetNoChangeHCRequested;
      BEGIN
      BASE;
      IF (Definition <> VOID) AND (NOT Definition.NoChangeHCRequested) THEN
        Definition.SetNoChangeHCRequested;
        IF Public THEN
          Warning("NO_CHANGE_HC must be declared in definition module");
          END;
        END;
      END SetNoChangeHCRequested;

    REDEFINE METHOD NoChangeRequested: BOOLEAN;
      BEGIN
      RESULT := BASE;
      IF NOT RESULT THEN
        IF Definition <> VOID THEN
          RESULT := Definition.NoChangeRequested;
          END;
	  ELSE
        IF Public AND (NOT Definition.NoChangeRequested) THEN
          Warning("NO_CHANGE must be declared in definition module");
          END;
        END;
      END NoChangeRequested;

    REDEFINE METHOD NoChangeHCRequested: BOOLEAN;
      BEGIN
      RESULT := BASE;
      IF NOT RESULT THEN
        IF Definition <> VOID THEN
          RESULT := Definition.NoChangeHCRequested;
          END;
	  ELSE
        IF Public AND (NOT Definition.NoChangeHCRequested) THEN
          Warning("NO_CHANGE_HC must be declared in definition module");
          END;
        END;
      END NoChangeHCRequested;

    REDEFINE METHOD ComputeNoChangeStatus;
      VAR
        Assignments: LimitedWalker(Assignment);
        Assign: Assignment;
        Designators: LimitedWalker(Desig);
        Desi: Desig;
        ChangeFlag: BOOLEAN;
      BEGIN
      IF NOT NoChangeHCRequested THEN
  	 Assignments.CREATE(THIS);
        Assign := Assignments.Next;
        WHILE (Assign <> VOID) AND NOT ChangeFlag DO
          IF Assign.IsChange THEN
            ChangeFlag := TRUE;
          ELSE
            Assign := Assignments.Next;
            END;
          END;
        IF ChangeFlag THEN
          SetNoChangeStatus(MethIsChange);
        ELSE
          Designators.CREATE(THIS);
          Desi := Designators.Next;
          WHILE (Desi <> VOID) AND NOT ChangeFlag DO
            IF Desi.IsChange THEN
              ChangeFlag := TRUE;
            ELSE
              Desi := Designators.Next;
              END;
            END;
          IF ChangeFlag THEN
            SetNoChangeStatus(MethCallChangeMeth);
          ELSE
            IF UsesInline THEN
              SetNoChangeStatus(MethUsesInLine);
            ELSE
              IF NoChangeRequested THEN
                SetNoChangeStatus(MethIsNoChange);
              ELSE
                SetNoChangeStatus(MethIsNoChangeDem);
                END;
              END;
            END;
          END;
      ELSE
        SetNoChangeStatus(MethIsNoChangeHC);
        END;

      CASE NoChangeStatus OF
        MethIsNoChangeDem,
        MethIsNoChange:
          -- nothing to do.
          END;
        MethIsNoChangeHC:
          -- nothing to do.
          END;
        MethCallChangeMeth:
          IF NoChangeRequested THEN
            Warning("NO_CHANGE method calls CHANGE methods");
            ASSERT Desi <> VOID;
            Desi.Warning(" not allowed in NO_CHANGE method");
            END;
          END;
        MethUsesInLine:
          IF NoChangeRequested THEN
            Warning("NO_CHANGE method use INLINE statements");
            END;
          END;
        MethIsChange:
          IF NoChangeRequested THEN
            Warning("NO_CHANGE method contains CHANGE statements");
            ASSERT Assign <> VOID;
            Assign.Warning(" not allowed in NO_CHANGE method");
            END;
          END;
        END;
      END ComputeNoChangeStatus;

    --------------------------------------------------
    -- This method Compare the definition of a
    -- Redefined method with its original declaration.
    --------------------------------------------------
    METHOD CheckConformity(Original: MethodDeclaration);
      VAR
        InheritsCl: InheritsClause;
        Ctx: ConstrainedClassDecl;
        Actuals: ClassActualSet;
        OtherReturn: Type;
      BEGIN
      ---------------------------------------------------------
      -- Check the conformity of the Formal parameters
      -- of the redefined method with the original declaration.
      ---------------------------------------------------------
      InheritsCl := CurrentSpot.CurrentClass.Inherits;
      ASSERT InheritsCl <> VOID;
      Actuals := InheritsCl.Actuals;
      Ctx := InheritsCl.Class.MakeConstrainedClass(Actuals);
      IF NOT (Formals = VOID IFF Original.Formals = VOID) THEN
        Error ("Parameters mismatch[1]");
       ELSIF Formals <> VOID THEN
        VOID := Formals.Match(THIS, Original.Formals, Ctx);
        END;
      --------------------------------------------
      -- Check the conformity of the Return types.
      --------------------------------------------
      IF ((Return = VOID) AND (Original.Return <> VOID))
        OR ((Return <> VOID) AND (Original.Return = VOID)) THEN
        Error ("Return value mismatch");
       ELSIF Return <> VOID THEN
        OtherReturn := Original.Return.BuildContextual (Ctx);
        IF NOT OtherReturn.Match (Return) THEN
          IF NOT OtherReturn.Compatible(Return) THEN
            Return.Error("Return type mismatch (" + Return.Image + "/" +
                                                    OtherReturn.Image + ")");
           ELSE
            ASSERT ClassImpl <> VOID;
            ---------------------------------------
            -- If a constrained redefinition is used for a public
            -- class method, the redefinition must be public.
            ---------------------------------------
            IF (ClassImpl.Definition <> VOID) AND (Definition = VOID) AND
               (Original.Public) THEN
              Error ("Private constrained redefinition");
              END;                                                                
            END;                                                    
          END;
        END;
      END CheckConformity;

    REDEFINE METHOD IsLocal: BOOLEAN;
      BEGIN
      RESULT := Enclosing <> VOID;
      END IsLocal;

    METHOD UsesValueStack: BOOLEAN;
      BEGIN
      RESULT := (TheStmtList <> VOID) AND TheStmtList.UsesValueStack;
      END UsesValueStack;
      
    METHOD CallsOtherMethods: BOOLEAN;
      BEGIN
      RESULT := (TheStmtList <> VOID) AND TheStmtList.CallsMethod;
      END CallsOtherMethods;

    REDEFINE METHOD NeedsSeparateAnchor: BOOLEAN;
      BEGIN
      IF Publish THEN
        RESULT := TRUE;
       ELSE
        RESULT := BASE;
        IF RESULT THEN
          RESULT := (Definition <> VOID) OR BeenRedefined OR 
                     Deferred;
          END;        
        IF RESULT AND YaflCfg.PleaseGlobalOptimize THEN
          RESULT := EntryRef.IsPolymorphic;
          END;
        END;
      END NeedsSeparateAnchor;
      
    VAR
      AnchorAsked: BOOLEAN;
      AnchorNumber: INTEGER;
      
    REDEFINE METHOD UseStaticAnchor: BOOLEAN;
      BEGIN         
      IF YaflCfg.PleaseGlobalOptimize THEN
        RESULT := FALSE;
       ELSE
        IF NOT AnchorAsked THEN
          AnchorAsked := TRUE;
          IF (Definition = VOID) AND NOT IsPredefined THEN
            AnchorNumber := YaflCfg.UniqueNumber;
            END;
          END;
        RESULT := AnchorNumber <> 0;
        END;
      END UseStaticAnchor;
      
    METHOD GetAnchorNumber: INTEGER;
      BEGIN
      RESULT := AnchorNumber;
      END GetAnchorNumber;  
      
    REDEFINE METHOD TagHeader;
      BEGIN
      BASE;
      FOR Meth IN TheMethodsList DO
        Meth.TagHeader;
        END;
      END TagHeader;

    METHOD StructNr: INTEGER;
      BEGIN
      IF TheStructNr = 0 THEN
        TheStructNr := YaflCfg.UniqueNumber;
        END;
      RESULT := TheStructNr;
      END StructNr;

    METHOD ContextNr: INTEGER;
      BEGIN
      IF TheContextNr = 0 THEN
        TheContextNr := YaflCfg.UniqueNumber;
        END;
      RESULT := TheContextNr;
      END ContextNr;

    VAR
      StructAsked, StructAnswer: BOOLEAN;

    METHOD UseStruct: BOOLEAN;
      BEGIN
      IF StructAsked THEN
        RESULT := StructAnswer;
       ELSE
        IF YaflCfg.ForceStruct OR (YaflCfg.TraceModeLevel = 2) THEN
          IF UsesInline THEN
            RESULT := FALSE;
           ELSE
            RESULT := TRUE;
            END;
         ELSE
          RESULT := TheMethodsList.Size > 0;
          StructAnswer := RESULT;
          StructAsked := TRUE;
          END;
        END;
      END UseStruct;

    METHOD StmtList: StatementList;
      BEGIN
      RESULT := TheStmtList;
      END StmtList;

    METHOD FirstStatement: INTEGER;
      BEGIN
      IF TheStmtList <> VOID THEN
        RESULT := TheStmtList.LineNr;
        END;
      END FirstStatement;

    METHOD LastStatement: INTEGER;
      VAR
        i: INTEGER;
      BEGIN
      IF (TheStmtList <> VOID) THEN
        i := TheStmtList.Size;
        IF i > 0 THEN
          RESULT := TheStmtList.GetList.Get(i-1).LineNr;
          END;
        END;
      END LastStatement;

    VAR
      TheNestingAsked: BOOLEAN;
      TheNesting: INTEGER;

    REDEFINE METHOD Nesting: INTEGER;
      BEGIN
      IF NOT TheNestingAsked THEN
        ASSERT TheNesting = 0;
        TheNestingAsked := TRUE;
        IF Enclosing <> VOID THEN
          TheNesting := Enclosing.Nesting+1;
          END;
        END;
      RESULT := TheNesting;
      END Nesting;      
      
    REDEFINE METHOD Public: BOOLEAN;
      BEGIN
      RESULT := Definition <> VOID;
      END Public;

    REDEFINE METHOD CallBackName: ARRAY OF CHAR;
      BEGIN            
      RESULT := BASE;
      IF (RESULT = VOID) AND (Definition <> VOID) THEN
        RESULT := Definition.CallBackName;
        END;
      END CallBackName;
      
    -----------------------------
    -- The Locals method returns an array
    -- which contains the local variable
    -- declarations attached to THIS.
    -----------------------------
    METHOD Locals: DeclList(SingleDataItem);
      BEGIN
      RESULT := TheLocalsList;
      END Locals;
      
    METHOD Methods: DeclList(MethodImplementation);
      BEGIN
      RESULT := TheMethodsList;
      END Methods;
      
    METHOD UseStaticBodyFunction: BOOLEAN;
      BEGIN
      IF YaflCfg.PleaseGlobalOptimize THEN
        RESULT := FALSE;
       ELSE
        RESULT := TRUE;
        END;
      END UseStaticBodyFunction;
    -----------------------------
    -- The two following methods are used
    -- to mark and query a method for containing
    -- at least an inline statement; in which case
    -- most of the warnings related to locals usage
    -- should be discarded because irrelevant.
    -----------------------------
    VAR 
      InlineUsed: BOOLEAN;
   
    METHOD SetUseInline;
      BEGIN
      InlineUsed := TRUE;
      IF IsLocal THEN 
        Error ("Local method including INLINE statements");
        END;
      END SetUseInline;
      
    METHOD UsesInline: BOOLEAN;
      BEGIN
      RESULT := InlineUsed;
      END UsesInline;
      
    METHOD CyclomaticComplexity: INTEGER;
      BEGIN
      IF TheStmtList <> VOID THEN
        RESULT := TheStmtList.CyclomaticComplexity;
        END;
      END CyclomaticComplexity;
---------------------------      
    METHOD MergeAnchor: BOOLEAN;
      BEGIN               
      IF Publish THEN
        RESULT := FALSE;
       ELSIF YaflCfg.PleaseGlobalOptimize THEN
        IF NOT Deferred THEN 
          RESULT := Reference.EntryRef.IsMonomorphic;
          END;
       ELSE
        RESULT := IsCreate OR ((NOT NeedsSeparateAnchor) AND NOT Redefine);
        END;
      END MergeAnchor;

-- Global Optimization
                                                
    -----------------------------------
    -- Removes the DEBUG and ASSERT statements 
    -- from the body of THIS.
    -----------------------------------
    METHOD RemoveDebug;
      VAR
        l    : List(StatementList);
        Cur  : NTList(Statement);
      BEGIN
      l.CREATE;
      GrabSubNodes (l);
      FOR El IN l DO
        Cur := El.GetList;
        FOR j IN Cur.Size - 1 TO 0 BY -1 DO
          WHAT Cur.Get(j) OF
            IN DebugStatement:
              Cur.Delete(j);
              END;
            IN AssertStatement:
              Cur.Delete(j);
              END;
           ELSE
            -- That's OK
            END;
          END;
        END;      
      END RemoveDebug;

    METHOD Useful: BOOLEAN;
      BEGIN        
      RESULT := Reference.Useful;
      END Useful;
           
    METHOD UsefulRootMethod : BOOLEAN;
      BEGIN
      RESULT := Reference.UsefulRootMethod;
      END UsefulRootMethod;
      
    ---------------------------     
    -- Removes a local method from THIS
    ---------------------------
    METHOD RemoveMethod (Meth : MethodImplementation);
      BEGIN
      SubDecls.DeleteElement(Meth);
      END RemoveMethod;

    ---------------------------
    -- Remove the method from its class
    ---------------------------
    METHOD Remove;
      BEGIN       
      ASSERT NOT Useful AND NOT UsefulRootMethod AND NOT Publish; 
      IF Enclosing <> VOID THEN
        Enclosing.RemoveMethod(THIS);
       ELSE
        ClassImpl.DeleteMethod(THIS); 
        END;
      END Remove;              
                 
    METHOD RemoveStatements;
      BEGIN
      ASSERT UsefulRootMethod AND NOT Useful;  
      IF StmtList <> VOID THEN
        IF StmtList.GetList <> VOID THEN
          StmtList.GetList.Purge;
          END;
        END;
      END RemoveStatements;
      
    --------------------------------
    -- Check if the Desig given is RESULT  
    --------------------------------
    METHOD IsResult (Des : Desig) : BOOLEAN;
      BEGIN
      IF Des.Elements.Size = 1 THEN
        IF Des.First.Id.GetRef <> VOID THEN
          WHAT Des.First.Id.GetRef OF
            IN ResultDataItem:
              RESULT := TRUE;
              END;
           ELSE
            -- Don't abort
            END; -- WHAT
          END; -- IF  
        END; -- IF
      END IsResult;         

    METHOD FindAttributeDecl (Name : ARRAY OF CHAR) : SingleDataItem;
      VAR
        Decl : Declaration;
      BEGIN
      Decl := Class.SubDecls.Find (Name);
      IF Decl <> VOID THEN
        WHAT Decl OF
          IN SingleDataItem:
            IF NOT TAG.Once THEN
              RESULT := TAG;
              END;
            END;
         ELSE
          END;
        END;
      END FindAttributeDecl; 
                        
    ---------------------------------
    -- Finds the declaration of a DesigElement if
    -- that DesigElement is a Formal
    ---------------------------------
    METHOD FindFormalDecl (Des : DesigElement) : SingleDataItem;
      VAR
        Nt : NonTerminal;
      BEGIN              
      Nt := Des.Id.GetRef;
      IF Nt <> VOID THEN
        WHAT Nt OF 
          IN Formal:
            RESULT := TAG;
            END;
         ELSE
          -- Don't abord
          END;
        END;                                           
      END FindFormalDecl;
      
    --------------------------------------
    -- RESULT = TRUE if Nt is a desig that refers to a formal
    -- RESULT = VOID otherwise
    --------------------------------------      
    METHOD IsFormal (Nt : TypedNonTerminal) : BOOLEAN;
      BEGIN
      WHAT Nt OF
        IN Desig:
          IF TAG.Elements.Size = 1 THEN
            RESULT := FindFormalDecl(TAG.First) <> VOID;
            END;
          END;
       ELSE
        END;
      END IsFormal;    
      
    ------------------------------
    -- RESULT = Literal if Nt is a literal
    -- RESULT = VOID otherwise
    ------------------------------
    METHOD IsLiteral (Nt : TypedNonTerminal) : Literal;
      BEGIN         
      WHAT Nt OF
        IN Literal:
          RESULT := TAG;
          END;
       ELSE
        END;
      END IsLiteral;                         

    ---------------------------------
    -- RESULT = VOID if Des is not a non ONCE attribute of the class.
    -- RESULT = Declaration of the attribute otherwise.
    ---------------------------------
    METHOD IsAttribute (Nt : TypedNonTerminal; Only : BOOLEAN)
                                                        : SingleDataItem;
      VAR
        CurElement   : DesigElement;
      BEGIN
      ASSERT Nt <> VOID;
      WHAT Nt OF
        IN Desig:
          ASSERT TAG.Elements <> VOID;
          CurElement := TAG.First;
          END;
        IN DesigElement:
          CurElement := TAG;
          END;
       ELSE
        END;
      IF CurElement<> VOID THEN        
        IF CurElement.Id.Data = PredefItems.This.Id.Data THEN
          CurElement := CurElement.NextElement;
	  END;
	IF CurElement <> VOID THEN
          IF (NOT Only) OR (CurElement.NextElement=VOID) THEN 
	    IF (CurElement.BrExpr = VOID) OR
	       (CurElement.BrExpr.Size = 0) THEN
              RESULT := FindAttributeDecl (CurElement.Id.Data);
	      END;
            END;
          END;
        END;
      END IsAttribute;
                                           
    ----------------------------------
    -- Checks if the assignment is
    --      'Attribute := Formal'
    --   or 'RESULT := Attribute'
    --   or 'RESULT := Literal'
    ----------------------------------
    METHOD CheckSimpleAssignment (Assign : Assignment): BOOLEAN;
      VAR
        s : SingleDataItem;
        l : Literal;
      BEGIN
      IF IsResult (Assign.LeftExpr) THEN
        IF (Formals = VOID) OR (Formals.Size = 0) THEN
          s := IsAttribute(Assign.RightExpr, Only := TRUE);
          l := IsLiteral(Assign.RightExpr);
          END;
        IF s <> VOID THEN
          Reference.SetSimpleQuery (s.Id.Data);
          RESULT := TRUE;
          END;
        IF l <> VOID THEN
          RESULT := TRUE;
          -- Reference.SetSimpleLiteral(l.Id.Data);
          END;
       ELSE
        IF (Formals <> VOID) AND (Formals.Size = 1) THEN
          s := IsAttribute (Assign.LeftExpr, Only := FALSE);
          END;
        IF NOT IsFormal(Assign.RightExpr) THEN
          s := VOID;
          END;
        IF s <> VOID THEN
          Reference.SetSimpleAssignment (s.Id.Data);
          RESULT := TRUE;
          END;
        END;
      END CheckSimpleAssignment;

    ---------------------------                                               
    -- Returns the method reference if the call is transitive
    ---------------------------                                               
    METHOD CheckTransitiveCall(TheDesig : Desig): MethodReference;
      VAR
        El           : DesigElement;
        s            : SingleDataItem;
        TheCall      : CallReference;
        TheAttribute : AttributeReference;
        TheActuals   : ActualList;
      BEGIN
      El := TheDesig.First;
      IF (El <> VOID) AND (El.Id.Data = PredefItems.This.Id.Data) THEN
        El := El.NextElement;
        END;       
      IF (El <> VOID) THEN
        s := IsAttribute(El, FALSE); 
        IF s<>VOID THEN
          TheAttribute := El.AttributeRef(Class);
          El := El.NextElement;
          END;
        IF El<>VOID THEN 
          TheActuals := El.Actuals;
          IF ((TheActuals=VOID) OR (TheActuals.Size=0)) AND 
                           ((Formals=VOID) OR (Formals.Size=0)) THEN
            TheCall := El.CallRef;
           ELSIF (TheActuals<>VOID) AND (TheActuals.SimpleMatch(Formals)) THEN
            TheCall := El.CallRef;
            END;
          IF TheCall<>VOID THEN 
            Reference.SetSimpleTransfer(TheAttribute,TheCall);              
            RESULT := Reference;
            END;
          END;
        END;
      END CheckTransitiveCall;  
      
    METHOD CheckTransitiveAssignment(Assign : Assignment);
      VAR
        Nt     : TypedNonTerminal;
        MethRef: MethodReference;
      BEGIN
      IF IsResult(Assign.LeftExpr) THEN
        Nt := Assign.RightExpr; 
        WHAT Nt OF
          IN Desig:
            MethRef := CheckTransitiveCall(TAG); 
            IF MethRef<>VOID THEN
              MethRef.SetAssignToResult;
              END;
            END;
         ELSE
          -- Don't abord
          END;
        END;
      END CheckTransitiveAssignment;

    METHOD CheckSimpleCases;
      VAR
        StmtList: StatementList;
        Stmt: Statement;
      BEGIN
      IF NOT PredefinedMethod THEN
        StmtList := THIS.StmtList;
        IF (StmtList = VOID) OR (StmtList.Size = 0) THEN
          Reference.SetDoesNothing;
         ELSIF StmtList.Size = 1 THEN
          Stmt := StmtList.GetList.Get(0);
          ASSERT Stmt <> VOID;
          WHAT Stmt OF 
            IN Assignment:              
              IF NOT CheckSimpleAssignment(TAG) THEN
                CheckTransitiveAssignment(TAG);
                END;
              END;
            IN MethodInvStatement:
              VOID := CheckTransitiveCall(TAG.NTDesignator);
              END;
           ELSE
            END;
          END; 
        END;
      END CheckSimpleCases;
      
    REDEFINE METHOD Publish: BOOLEAN;
      BEGIN
      RESULT := BASE OR 
                ((Definition <> VOID) AND Definition.Publish);
      END Publish;
   
    METHOD PredefinedMethod : BOOLEAN;
      BEGIN
      RESULT := (Id.Data = PredefItems.Create.Id.Data) OR
                (Id.Data = PredefItems.Clone.Id.Data) OR
                (Id.Data = PredefItems.Kill.Id.Data);
      END PredefinedMethod;

    REDEFINE METHOD EnterInDictionary;
           
      METHOD BuildCallTable;
        VAR
          DesigList  : ArrayList(DesigElement);
          SetSpecList: ArrayList(SetSpecification);
          CallList   : CallReferenceList;
          CRef       : CallReference;
          CallRow    : ARRAY OF CallReference;
          CallsBase  : BOOLEAN;
        BEGIN
        ASSERT IsTypeChecked;
        CurrentSpot.PushCurrentMethod (THIS);
        IF StmtList <> VOID THEN
          DesigList.CREATE;
          StmtList.GrabSubNodes (DesigList);
          
          CallList.CREATE; 
          FOR DesigElt IN DesigList DO
            CRef := DesigElt.FirstPassCallRef;
            IF CRef <> VOID THEN
              IF DesigElt.IsBase THEN
                CallsBase := TRUE;
               ELSE
                CallList.Append (CRef);
                END;
              END;
            END;                   
            
          SetSpecList.CREATE;
          StmtList.GrabSubNodes (SetSpecList);
          FOR SetSpec IN SetSpecList DO
            SetSpec.AddMethodCalls (CallList);
            END;
            
          IF CallList.Size > 0 THEN  
            CallList.Compress;
            CallRow := CallList.Row;
            END;
          Reference.SetCalls (CallRow, CallsBase);
          END;      
        CurrentSpot.PopCurrentMethod;
        END BuildCallTable;
        
        
    BEGIN 
    IF NOT Deferred THEN

      BASE;            
      IF IsTagged THEN
        ASSERT IsTypeChecked;
        CheckSimpleCases;
        BuildCallTable;    
        Reference.SetUsesValueStack (Val := UsesValueStack);
        END; -- IF
        
      Reference.SetImplPos (Id.ColNr, Id.LineNr);
      Reference.SetCyclomaticComplexity (CyclomaticComplexity);
      IF Enclosing <> VOID THEN
        Reference.SetEnclosing (Enclosing.Reference);
        ASSERT Reference.Enclosing = Enclosing.Reference;
        END; -- IF
      FOR Meth IN Methods DO
        Meth.EnterInDictionary;       
        END; -- FOR    
             
      END; -- IF    
      
    END EnterInDictionary;

  END MethodImplementation;

END YaflMetImplementation;
