Function.java

/*
** Module   : Function.java
** Abstract : contains the data needed to define a Progress function
**
** Copyright (c) 2005-2022, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ----------------------------------Description------------------------------------
** 001 GES 20050413   @20719 First version, simple bean-like approach
**                           including a temporary index to allow the
**                           linking of a function call to its original
**                           definition at parse time. This is needed
**                           because project-unique IDs can't be easily
**                           generated at parse time, but are only added
**                           after the entire tree is complete.
** 002 GES 20050608   @21451 Improvement in annotations for better
**                           function definition conversion.
** 003 GES 20070919   @35135 Added support for the fully qualified name
**                           of the class represented by a FUNC_CLASS.
** 004 ECF 20080704   @39144 Minor memory optimization. Use Boolean
**                           constants instead of instantiating new
**                           Boolean objects.
** 005 GES 20090518   @42407 Import change.
** 006 GES 20110901          Added some class annotations.
** 007 CA  20121212          tempidx for functions is no longer needed, as the
**                           refid is computed during conversion.
** 008 GES 20181228          Added return value extent annotation.
** 009 CA  20191002          Annotate function call parameters with their index.
** 010 CA  20210203          Fixed conversion issues with OUTPUT/INPUT-OUTPUT for extent or scalar parameters,
**                           involved in dynamic or static OO calls, functions or procedure calls.  This 
**                           includes mostly cases for OO properties/variables (static and non-static).
**     CA  20210216          Fix for the 'cast-to-object' case for a single extent argument in a dynamic call,
**                           when INPUT modifier exists or not.
**     TJD 20220504          Java 11 compatibility minor changes
*/

/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
** 
** Additional terms under GNU Affero GPL version 3 section 7:
** 
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
**   terms apply to the works covered under the License.  These additional terms
**   are non-permissive additional terms allowed under Section 7 of the GNU
**   Affero GPL version 3 and may not be removed by you.
** 
**   0. Attribution Requirement.
** 
**     You must preserve all legal notices or author attributions in the covered
**     work or Appropriate Legal Notices displayed by works containing the covered
**     work.  You may not remove from the covered work any author or developer
**     credit already included within the covered work.
** 
**   1. No License To Use Trademarks.
** 
**     This license does not grant any license or rights to use the trademarks
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
**     of Golden Code Development Corporation. You are not authorized to use the
**     name Golden Code, FWD, or the names of any author or contributor, for
**     publicity purposes without written authorization.
** 
**   2. No Misrepresentation of Affiliation.
** 
**     You may not represent yourself as Golden Code Development Corporation or FWD.
** 
**     You may not represent yourself for publicity purposes as associated with
**     Golden Code Development Corporation, FWD, or any author or contributor to
**     the covered work, without written authorization.
** 
**   3. No Misrepresentation of Source or Origin.
** 
**     You may not represent the covered work as solely your work.  All modified
**     versions of the covered work must be marked in a reasonable way to make it
**     clear that the modified work is not originating from Golden Code Development
**     Corporation or FWD.  All modified versions must contain the notices of
**     attribution required in this license.
*/

package com.goldencode.p2j.uast;

import com.goldencode.ast.*;

/**
 * A simple wrapper class to contain all related data about a function.
 */
class Function
implements ProgressParserTokenTypes,
           TokenDataWrapper
{
   /** The full text name of the function. */
   private String name = null;
   
   /** Parser defined integer constant matching the token type of the function. */
   private int tokenType = -1;
   
   /** Return value extent (defaults to 0, which is scalar). */
   private int extent = 0;
   
   /** Defines if this function definition is user-defined or builtin. */
   private boolean builtin = false;
   
   /**
    * Defines if this function can return the unknown value. If set to
    * <code>null</code>, then no information on this topic is known.
    */
   private Boolean returnsUnknown = null;
   
   /** Fully qualified class name of the object instance returned. */
   private String cls = null;
   
   /**
    * Constructs an instance with the required data to be contained.  By
    * default builtin functions have their returns unknown state set to
    * <code>false</code>.  This can be overridden by a subsequent method
    * call to {@link #setReturnsUnknown}.
    *
    * @param    name
    *           The full name of the function.
    * @param    tokenType
    *           The token type (as defined by the parser) of the function.
    * @param    builtin
    *           <code>true</code> if this function is 'built in' to the
    *           Progress runtime.
    * @param    cls
    *           Fully qualified class name of the object instance returned
    *           or <code>null</code> if this function does not return an
    *           object instance.
    */
   public Function(String name, int tokenType, boolean builtin, String cls)
   throws IllegalArgumentException
   {
      if (name == null || tokenType == -1)
         throw new IllegalArgumentException("Invalid name or type.");
      
      this.name      = name;
      this.tokenType = tokenType;
      this.builtin   = builtin;
      this.cls       = cls;
      
      if (builtin)
      {
         returnsUnknown = Boolean.FALSE;
      }
   }
   
   /**
    * Returns the full name of the function.
    *
    * @return   The full name of the function.
    */
   public String getName()
   {
      return name;
   }
   
   /**
    * Sets the full name of the function.
    *
    * @param    name
    *           The full name of the function.
    */
   public void setName(String name)
   {
      this.name = name;
   }
   
   /**
    * Returns the parser-defined token type of the function.
    *
    * @return   The token type of the function.
    */
   public int getTokenType()
   {
      return tokenType;
   }   
   
   /**
    * Sets the parser-defined token type of the function.
    *
    * @param    tokenType
    *           The token type of the function.
    */
   public void setTokenType(int tokenType)
   {
      this.tokenType = tokenType;
   }   
   
   /**
    * This is a no-op, as for function references the associated function
    * definition is computed during conversion.
    *
    * @return   Always returns -1.
    */
   public int getTempIndex()
   {
      return -1;
   }   
   
   /**
    * This is a no-op, as for function references the associated function
    * definition is computed during conversion.
    *
    * @param    tempIdx
    *           The temporary index.
    */
   public void setTempIndex(int tempIdx)
   {
      // no-op
   }
   
   /**
    * Determines if this function definition is user-defined or builtin.
    *
    * @return   <code>true</code> if the function is builtin.
    */
   public boolean isBuiltin()
   {
      return builtin;
   }
   
   /**
    * Sets whether this function definition is user-defined or builtin.
    *
    * @param    builtin
    *           <code>true</code> if the function is builtin.
    */
   public void setBuiltin(boolean builtin)
   {
      this.builtin = builtin;
   }
   
   /**
    * Obtains the fully qualified name of the class of the object instance
    * returned by this function.
    *
    * @return   The fully qualified name of the class of the object instance
    *           returned by this function or <code>null</code> if this
    *           function does not return an object instance.
    */
   public String getClassName()
   {
      return cls;
   }
   
   /**
    * Sets the fully qualified name of the class of the object instance
    * returned by this function.
    *
    * @param    cls
    *           The fully qualified name of the class of the object instance
    *           returned by this function or <code>null</code> if this
    *           function does not return an object instance.
    */
   public void setClassName(String cls)
   {
      this.cls = cls;
   }
   
   /**
    * Determines if this function can return the unknown value.
    *
    * @return   <code>true</code> if the function can return the unknown
    *           value, <code>false</code> if not and <code>null</code> if
    *           this fact is not known.
    */
   public Boolean returnsUnknown()
   {
      return returnsUnknown;
   }
   
   /**
    * Sets whether or not this function can return the unknown value.
    *
    * @param    unknown
    *           <code>true</code> if the function can return the unknown
    *           value, <code>false</code> if not and <code>null</code> if
    *           this fact is not known.
    */
   public void setReturnsUnknown(Boolean unknown)
   {
      this.returnsUnknown = unknown;
   }
   
   /**
    * Stores all non-standard options as annotations to the AST passed as
    * a parameter if this is the original definition, otherwise it stores
    * a subset of values, most important of which is the cross-reference
    * index to allow the original definition to be easily identified and
    * accessed from a reference.  If an option has the default value, it is
    * not saved as an annotation.
    *
    * @param    ast
    *           The AST to annotate.    
    * @param    original
    *           <code>true</code> if this is to annotate the AST of the
    *           original definition, <code>false</code> if the AST is only
    *           a reference.
    * @param    sym
    *           The current symbol resolver.
    */
   public void annotateOptions(Aast ast, boolean original, SymbolResolver sym)
   {
      if (ast == null)
      {
         // nothing to do
         return;
      }
      
      if (original)
      {
         this.extent = SymbolResolver.readExtent(ast);
      }
      
      if (this.extent != 0)
      {
         ast.putAnnotation("extent", Long.valueOf(this.extent));
      }
      
      if (tokenType == FUNC_CLASS && cls != null)
      {
         sym.annotateClassRef(cls, ast);
      }
      
      // the following annotations are only stored in the original defining
      // AST rather than in references, unless the reference is to a builtin
      // function which has no defining reference
      if (original || builtin)
      {
         if (original)
         {
            // storing the name and type in annotations makes function
            // definitions easier to generate at conversion time
            ast.putAnnotation("name", name);      
            ast.putAnnotation("type", Long.valueOf(tokenType));
         }      
         
         ast.putAnnotation("builtin", builtin ? Boolean.TRUE : Boolean.FALSE);
         
         if (returnsUnknown != null)
         {
            ast.putAnnotation("returnsunknown", returnsUnknown);
         }
      }
      
      if (!original)
      {
         int callType = ast.getType();
         long callOldType = ast.isAnnotation("oldtype") ? (long) ast.getAnnotation("oldtype") : -1;

         // walk the direct children and annotate their parameter index
         Aast parm = (Aast) ast.getFirstChild();
         if (callOldType == KW_NEW && ast.getType() == FUNC_CLASS)
         {
            parm = (Aast) ast.getImmediateChild(LPARENS, null);
            parm = (Aast) parm.getFirstChild();
         }

         int parIdx = 0;
         while (parm != null)
         {
            int type = parm.getType();
            
            int pos = parm.getIndexPos();
            
            if (parm.getParent() == ast)
            {
               // skip the STRING argument for DYNAMIC-FUNCTION
               if (pos == 0 && callType == FUNC_POLY && callOldType == KW_DYN_FUNC)
               {
                  parm = (Aast) parm.getNextSibling();
                  continue;
               }

               // skip the STRING argument for DYNAMIC-NEW
               if (pos == 0 && callType == FUNC_CLASS && callOldType == KW_DYN_NEW)
               {
                  parm = (Aast) parm.getNextSibling();
                  continue;
               }
               
               // skip the OO reference and STRING argument for DYNAMIC-INVOKE
               if (pos <= 1 && callType == FUNC_POLY && callOldType == KW_DYN_INVK)
               {
                  parm = (Aast) parm.getNextSibling();
                  continue;
               }
               
               // skip the CLASS_NAME argument for NEW
               if (pos == 0 && callType == FUNC_CLASS && callOldType == KW_NEW)
               {
                  parm = (Aast) parm.getNextSibling();
                  continue;
               }
            }

            // avoid the optional mode or table parm modifiers
            if (type != KW_INPUT   &&
                type != KW_IN_OUT  &&
                type != KW_OUTPUT  &&
                type != KW_APPEND  && 
                type != KW_BIND    &&
                type != KW_BY_REF  &&
                type != KW_BY_VALUE)
            {
               parm.putAnnotation("param_index", (long) parIdx);
               parIdx = parIdx + 1;
            }
            parm = (Aast) parm.getNextSibling();
         }
      }
   }   
}